51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "golden-plate-map",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "format": "prettier --write .",
11 | "data:download": "node scripts/download-data.js"
12 | },
13 | "dependencies": {
14 | "@mapbox/mapbox-gl-geocoder": "^5.0.2",
15 | "@turf/bbox": "7.0.0-alpha.114",
16 | "@turf/helpers": "7.0.0-alpha.114",
17 | "mapbox-gl": "^3.2.0",
18 | "next": "14.2.0-canary.42",
19 | "react": "^18",
20 | "react-dom": "^18",
21 | "react-map-gl": "^7.1.7",
22 | "react-map-gl-geocoder": "^2.2.0"
23 | },
24 | "devDependencies": {
25 | "@ianvs/prettier-plugin-sort-imports": "^4.1.1",
26 | "@types/eslint": "^8.56.5",
27 | "@types/mapbox__mapbox-gl-geocoder": "^5.0.0",
28 | "@types/node": "^20",
29 | "@types/react": "^18",
30 | "@types/react-dom": "^18",
31 | "@typescript-eslint/eslint-plugin": "^7.1.1",
32 | "@typescript-eslint/parser": "^7.1.1",
33 | "autoprefixer": "^10.0.1",
34 | "axios": "^1.6.8",
35 | "cli-progress": "^3.12.0",
36 | "eslint": "^8",
37 | "eslint-config-next": "14.1.0",
38 | "eslint-config-prettier": "^9.1.0",
39 | "eslint-plugin-hooks": "^0.4.3",
40 | "eslint-plugin-import": "^2.29.1",
41 | "eslint-plugin-jsx-a11y": "^6.8.0",
42 | "eslint-plugin-react": "^7.34.0",
43 | "prettier": "^3.2.5",
44 | "prettier-plugin-tailwindcss": "^0.5.13",
45 | "postcss": "^8",
46 | "tailwindcss": "^3.4.3",
47 | "typescript": "^5",
48 | "zod": "^3.22.4"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/app/restaurant/[restaurantId]/_map.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * This module defines a component for map showing a restaurant's location.
3 | *
4 | * @see RestaurantDetailMap
5 | * @see https://visgl.github.io/react-map-gl/
6 | */
7 |
8 | "use client";
9 |
10 | import { useMemo, useRef } from "react";
11 | import Map, {
12 | GeolocateControl,
13 | Marker,
14 | NavigationControl,
15 | type MapRef,
16 | } from "react-map-gl";
17 |
18 | import GeocoderControl from "@/components/map/geocoder-control";
19 |
20 | import "mapbox-gl/dist/mapbox-gl.css";
21 |
22 | type RestaurantDetailMapProps = {
23 | latitude: number;
24 | longitude: number;
25 | };
26 |
27 | /**
28 | * A component for a map showing a restaurant's location.
29 | */
30 | export function RestaurantDetailMap({
31 | latitude,
32 | longitude,
33 | }: RestaurantDetailMapProps) {
34 | const markers = useMemo(() => {
35 | return [
36 | {
37 | latitude,
38 | longitude,
39 | },
40 | ];
41 | }, [latitude, longitude]);
42 |
43 | const mapRef = useRef(null);
44 |
45 | return (
46 |
78 | );
79 | }
80 |
--------------------------------------------------------------------------------
/scripts/download-data.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | const fs = require("fs");
3 | const axios = require("axios");
4 | const cliProgress = require("cli-progress");
5 | const path = require("path");
6 |
7 | const restaurantsDataUrl =
8 | "https://dotlas-marketing.s3.amazonaws.com/interviews/california_restaurants_2024.json";
9 | const dataSavePath = path.join(process.cwd(), "src", "data");
10 | const restaurantsDataSavePath = path.join(dataSavePath, "restaurants.json");
11 |
12 | async function downloadRestaurantsData() {
13 | try {
14 | const response = await axios.get(restaurantsDataUrl, {
15 | responseType: "stream",
16 | });
17 |
18 | if (response.status !== 200) {
19 | throw new Error(`Failed to download data: ${response.statusText}`);
20 | }
21 |
22 | const totalBytes = parseInt(response.headers["content-length"], 10);
23 | const progressBar = new cliProgress.SingleBar({
24 | format: "Downloading restaurants data | {bar} | {percentage}%",
25 | barCompleteChar: "\u2588",
26 | barIncompleteChar: "\u2591",
27 | hideCursor: true,
28 | });
29 |
30 | progressBar.start(totalBytes, 0);
31 |
32 | // Create the folder if it doesn't exist
33 | if (!fs.existsSync(dataSavePath)) {
34 | fs.mkdirSync(dataSavePath);
35 | }
36 |
37 | const writeStream = fs.createWriteStream(restaurantsDataSavePath);
38 | let downloadedBytes = 0;
39 |
40 | writeStream.on("drain", () => {
41 | progressBar.update(downloadedBytes);
42 | });
43 |
44 | writeStream.on("finish", () => {
45 | progressBar.stop();
46 | console.log("Restaurants data downloaded successfully!");
47 | });
48 |
49 | writeStream.on("error", (error) => {
50 | progressBar.stop();
51 | console.error("Error downloading data:", error);
52 | });
53 |
54 | response.data.pipe(writeStream);
55 | response.data.on("data", (chunk) => {
56 | downloadedBytes += chunk.length;
57 | });
58 | } catch (error) {
59 | console.error("Error downloading data:", error);
60 | }
61 | }
62 |
63 | downloadRestaurantsData();
64 |
--------------------------------------------------------------------------------
/src/components/map/geocoder-control.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * This module defines a geocoder control for Mapbox.
3 | *
4 | * @see GeocoderControl
5 | * @see https://github.com/visgl/react-map-gl/blob/master/examples/geocoder/src/geocoder-control.tsx
6 | */
7 |
8 | import MapboxGeocoder, {
9 | type GeocoderOptions,
10 | } from "@mapbox/mapbox-gl-geocoder";
11 | import { useState } from "react";
12 | import {
13 | Marker,
14 | useControl,
15 | type ControlPosition,
16 | type MarkerProps,
17 | } from "react-map-gl";
18 |
19 | import "@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css";
20 |
21 | type GeocoderControlProps = Omit<
22 | GeocoderOptions,
23 | "accessToken" | "mapboxgl" | "marker"
24 | > & {
25 | mapboxAccessToken: string;
26 | marker?: boolean | Omit;
27 |
28 | position: ControlPosition;
29 |
30 | onLoading?: (e: object) => void;
31 | onResults?: (e: object) => void;
32 | onResult?: (e: object) => void;
33 | onError?: (e: object) => void;
34 | };
35 |
36 | /**
37 | * A component that renders a geocoder control.
38 | * It allows the user to search for places and navigate to them.
39 | */
40 | export default function GeocoderControl({
41 | marker: markerProp = true,
42 |
43 | onLoading = () => {},
44 | onResults = () => {},
45 | onResult = () => {},
46 | onError = () => {},
47 | ...props
48 | }: GeocoderControlProps) {
49 | const [marker, setMarker] = useState(null);
50 |
51 | const geocoder = useControl(
52 | () => {
53 | const ctrl = new MapboxGeocoder({
54 | ...props,
55 | marker: false,
56 | accessToken: props.mapboxAccessToken,
57 | });
58 | ctrl.on("loading", onLoading);
59 | ctrl.on("results", onResults);
60 | ctrl.on("result", (evt) => {
61 | onResult(evt);
62 |
63 | const { result } = evt;
64 | const location =
65 | result &&
66 | (result.center ||
67 | (result.geometry?.type === "Point" && result.geometry.coordinates));
68 | if (location && markerProp) {
69 | setMarker(
70 | ,
77 | );
78 | } else {
79 | setMarker(null);
80 | }
81 | });
82 | ctrl.on("error", onError);
83 | return ctrl;
84 | },
85 | {
86 | position: props.position,
87 | },
88 | );
89 |
90 | // @ts-expect-error (TS2339) private member
91 | if (geocoder._map) {
92 | if (
93 | geocoder.getProximity() !== props.proximity &&
94 | props.proximity !== undefined
95 | ) {
96 | geocoder.setProximity(props.proximity);
97 | }
98 | if (
99 | geocoder.getRenderFunction() !== props.render &&
100 | props.render !== undefined
101 | ) {
102 | geocoder.setRenderFunction(props.render);
103 | }
104 | if (
105 | geocoder.getLanguage() !== props.language &&
106 | props.language !== undefined
107 | ) {
108 | geocoder.setLanguage(props.language);
109 | }
110 | if (geocoder.getZoom() !== props.zoom && props.zoom !== undefined) {
111 | geocoder.setZoom(props.zoom);
112 | }
113 | if (geocoder.getFlyTo() !== props.flyTo && props.flyTo !== undefined) {
114 | geocoder.setFlyTo(props.flyTo);
115 | }
116 | if (
117 | geocoder.getPlaceholder() !== props.placeholder &&
118 | props.placeholder !== undefined
119 | ) {
120 | geocoder.setPlaceholder(props.placeholder);
121 | }
122 | if (
123 | geocoder.getCountries() !== props.countries &&
124 | props.countries !== undefined
125 | ) {
126 | geocoder.setCountries(props.countries);
127 | }
128 | if (geocoder.getTypes() !== props.types && props.types !== undefined) {
129 | geocoder.setTypes(props.types);
130 | }
131 | if (
132 | geocoder.getMinLength() !== props.minLength &&
133 | props.minLength !== undefined
134 | ) {
135 | geocoder.setMinLength(props.minLength);
136 | }
137 | if (geocoder.getLimit() !== props.limit && props.limit !== undefined) {
138 | geocoder.setLimit(props.limit);
139 | }
140 | if (geocoder.getFilter() !== props.filter && props.filter !== undefined) {
141 | geocoder.setFilter(props.filter);
142 | }
143 | if (geocoder.getOrigin() !== props.origin && props.origin !== undefined) {
144 | geocoder.setOrigin(props.origin);
145 | }
146 | // Types missing from @types/mapbox__mapbox-gl-geocoder
147 | if (
148 | geocoder.getAutocomplete() !== props.autocomplete &&
149 | props.autocomplete !== undefined
150 | ) {
151 | geocoder.setAutocomplete(props.autocomplete);
152 | }
153 | if (
154 | geocoder.getFuzzyMatch() !== props.fuzzyMatch &&
155 | props.fuzzyMatch !== undefined
156 | ) {
157 | geocoder.setFuzzyMatch(props.fuzzyMatch);
158 | }
159 | if (
160 | geocoder.getRouting() !== props.routing &&
161 | props.routing !== undefined
162 | ) {
163 | geocoder.setRouting(props.routing);
164 | }
165 | if (
166 | geocoder.getWorldview() !== props.worldview &&
167 | props.worldview !== undefined
168 | ) {
169 | geocoder.setWorldview(props.worldview);
170 | }
171 | }
172 | return marker;
173 | }
174 |
--------------------------------------------------------------------------------
/src/services/restaurants/schema.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This module contains the schema for the restaurant data.
3 | *
4 | * It is defined using the Zod library.
5 | *
6 | * @see https://zod.dev/
7 | * @see Restaurant
8 | * @see restaurantSchema
9 | */
10 |
11 | import { z } from "zod";
12 |
13 | const diningStyles = [
14 | "Casual Dining",
15 | "Casual Elegant",
16 | "Elegant Dining",
17 | "Fine Dining",
18 | "Home Style",
19 | ] as const;
20 |
21 | const dressCodes = [
22 | "Business Casual",
23 | "Casual Dress",
24 | "Formal Attire",
25 | "Jacket Preferred",
26 | "Jacket Required",
27 | "Resort Casual",
28 | "Smart Casual",
29 | ] as const;
30 |
31 | const parkingInfos = [
32 | "Hotel Parking",
33 | "None",
34 | "Private Lot",
35 | "Public Lot",
36 | "Street Parking",
37 | "Valet",
38 | ] as const;
39 |
40 | const paymentOptions = [
41 | "AMEX",
42 | "Carte Blanche",
43 | "Cash not accepted",
44 | "Cash Only",
45 | "Cheque Gourmet",
46 | "Contactless Payment",
47 | "Diners Club",
48 | "Discover",
49 | "Eurocheque Card",
50 | "JCB",
51 | "MasterCard",
52 | "Mastercard",
53 | "Pay with OpenTable",
54 | "Sodexo Pass",
55 | "Visa",
56 | ] as const;
57 |
58 | const priceRanges = ["$30 and under", "$31 to $50", "$50 and over"] as const;
59 |
60 | const reviewTopics = [
61 | "Charming",
62 | "Dog-friendly",
63 | "Fancy",
64 | "Gluten-free-friendly",
65 | "Good for business meals",
66 | "Good for groups",
67 | "Good for special occasions",
68 | "Great for brunch",
69 | "Great for craft beers",
70 | "Great for creative cocktails",
71 | "Great for fine wines",
72 | "Great for happy hour",
73 | "Great for live music",
74 | "Great for outdoor dining",
75 | "Great for scenic views",
76 | "Healthy",
77 | "Hot spot",
78 | "Innovative",
79 | "Kid-friendly",
80 | "Lively",
81 | "Neighborhood gem",
82 | "Outstanding value",
83 | "Romantic",
84 | "Vegan-friendly",
85 | "Vegetarian-friendly",
86 | ] as const;
87 |
88 | const tags = [
89 | "BYO Liquor",
90 | "BYO Wine",
91 | "Banquet",
92 | "Bar Dining",
93 | "Bar/Lounge",
94 | "Beer",
95 | "Beer Garden",
96 | "Cafe",
97 | "Chef's Table",
98 | "Cigar Room",
99 | "Cocktails",
100 | "Corkage Fee",
101 | "Counter Seating",
102 | "Dancing",
103 | "Delivery",
104 | "Dog Friendly",
105 | "Entertainment",
106 | "Farm to Table",
107 | "Full Bar",
108 | ] as const;
109 |
110 | const deliveryPartners = ["CHOW_NOW", "POSTMATES", "UBER_EATS"] as const;
111 |
112 | /**
113 | * The schema for a single restaurant.
114 | */
115 | const restaurantSchema = z.object({
116 | country: z.literal("United States"),
117 | Subregion: z.literal("California"),
118 | city: z.string(),
119 | brand_name: z.string(),
120 | categories: z.string().array(),
121 | latitude: z.number(),
122 | longitude: z.number(),
123 | area: z.string(),
124 | address: z.string(),
125 | description: z.string(),
126 | public_transit: z.string().nullable(),
127 | cross_street: z.string().nullable(),
128 | restaurant_website: z.string().nullable(),
129 | phone_number: z.string(),
130 | dining_style: z.enum(diningStyles),
131 | executive_chef_name: z.string().nullable(),
132 | parking_info: z.enum(parkingInfos),
133 | dress_code: z.enum(dressCodes),
134 | entertainment: z.string().nullable(),
135 | operating_hours: z.string(),
136 | price_range_id: z.number(),
137 | price_range: z.enum(priceRanges),
138 | payment_options: z.enum(paymentOptions).array(),
139 | maximum_days_advance_for_reservation: z.number(),
140 | rating: z.number(),
141 | rating_count: z.number(),
142 | atmosphere_rating: z.number(),
143 | noise_rating: z.number(),
144 | food_rating: z.number(),
145 | service_rating: z.number(),
146 | value_rating: z.number(),
147 | terrible_review_count: z.number(),
148 | poor_review_count: z.number(),
149 | average_review_count: z.number(),
150 | very_good_review_count: z.number(),
151 | excellent_review_count: z.number(),
152 | review_count: z.number(),
153 | review_topics: z.enum(reviewTopics).array(),
154 | tags: z.enum(tags).array(),
155 | has_clean_menus: z.boolean(),
156 | has_common_area_cleaning: z.boolean(),
157 | has_common_area_distancing: z.boolean(),
158 | has_contact_tracing_collected: z.boolean(),
159 | has_contactless_payment: z.boolean(),
160 | requires_diner_temperature_check: z.boolean(),
161 | has_limited_seating: z.boolean(),
162 | prohibits_sick_staff: z.boolean(),
163 | has_proof_of_vaccination_outdoor: z.boolean(),
164 | requires_proof_of_vaccination: z.boolean(),
165 | requires_diner_masks: z.boolean(),
166 | requires_wait_staff_masks: z.boolean(),
167 | has_sanitized_surfaces: z.boolean(),
168 | provides_sanitizer_for_customers: z.boolean(),
169 | has_sealed_utensils: z.boolean(),
170 | has_vaccinated_staff: z.boolean(),
171 | requires_staff_temp_checks: z.boolean(),
172 | has_table_layout_with_extra_space: z.boolean(),
173 | is_permanently_closed: z.boolean(),
174 | is_waitlist_only: z.boolean(),
175 | has_waitlist: z.boolean(),
176 | has_bar: z.boolean(),
177 | has_counter: z.boolean(),
178 | has_high_top_seating: z.boolean(),
179 | has_outdoor_seating: z.boolean(),
180 | has_priority_seating: z.boolean(),
181 | has_private_dining: z.boolean(),
182 | has_takeout: z.boolean(),
183 | has_delivery_partners: z.boolean(),
184 | has_pickup: z.boolean(),
185 | has_gifting: z.boolean(),
186 | delivery_partners: z.enum(deliveryPartners).array(),
187 | facebook: z.string().nullable(),
188 | menu_url: z.string().nullable(),
189 | daily_reservation_count: z.number().nullable(),
190 | meal_cost: z.number(),
191 | restaurant_id: z.number(),
192 | });
193 |
194 | /**
195 | * A single restaurant.
196 | */
197 | export type Restaurant = z.infer;
198 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🍽️ Golden Plate Map
2 |
3 | Welcome to the Golden Plate Map project!
4 |
5 | Your task is to build a **web application** that allows users to **explore restaurants in California**.
6 |
7 | - [🍽️ Golden Plate Map](#️-golden-plate-map)
8 | - [Getting Started](#getting-started)
9 | - [Pre-requisites](#pre-requisites)
10 | - [Create a Private fork](#create-a-private-fork)
11 | - [Install Dependencies](#install-dependencies)
12 | - [Download the Dataset](#download-the-dataset)
13 | - [Load Environment Variables](#load-environment-variables)
14 | - [Start the Development Server](#start-the-development-server)
15 | - [Codebase](#codebase)
16 | - [Goals](#goals)
17 | - [Home Page (`/`)](#home-page-)
18 | - [Restaurant Page (`/restaurant/[restaurantId]`)](#restaurant-page-restaurantrestaurantid)
19 | - [Miscellaneous](#miscellaneous)
20 | - [Guidelines](#guidelines)
21 | - [Evaluation](#evaluation)
22 | - [Criteria](#criteria)
23 | - [Submission](#submission)
24 | - [Help](#help)
25 | - [GitHub Issues](#github-issues)
26 | - [Contact](#contact)
27 |
28 | ## Getting Started
29 |
30 | ### Pre-requisites
31 |
32 | - [![Node.js]](https://nodejs.org/en/download) `v18` on your development machine.
33 | - The [![pnpm]](https://pnpm.io/) package manager.
34 | - A [![GitHub]](https://github.com) account.
35 | - A [![Vercel]](https://vercel.com/home) account.
36 |
37 | ### Create a Private fork
38 |
39 | Create a private fork of this repository:
40 |
41 | - [Go to the "Import a repository" page on GitHub](https://github.com/new/import)
42 | - Specify the url for this repository
43 | - Click on the "Begin import" button
44 |
45 | Once the repository is created on GitHub, clone it onto your local system!
46 |
47 | ### Install Dependencies
48 |
49 | To install all necessary dependencies, run:
50 |
51 | ```bash
52 | pnpm install
53 | ```
54 |
55 | ### Download the Dataset
56 |
57 | Use the following command to download the dataset of restaurants in California:
58 |
59 | ```bash
60 | pnpm data:download
61 | ```
62 |
63 | ### Load Environment Variables
64 |
65 | Create a `.env` file based on the [template](./.env.template) and fill in the necessary values.
66 |
67 | ### Start the Development Server
68 |
69 | Begin interacting with the app by starting the development server:
70 |
71 | ```bash
72 | pnpm dev
73 | ```
74 |
75 | If everything is set up correctly, you should see a prompt to visit `http://localhost:3000` in your browser.
76 |
77 | > **Note**
78 | > The development server is configured to automatically reload the application when changes are made to the codebase.
79 | > As such, it can be a bit slow when using the app.
80 | >
81 | > To experience a much more performant version of the app, you can build the app using the following commands:
82 | >
83 | > ```bash
84 | > pnpm build
85 | > pnpm start
86 | > ```
87 |
88 | ## Codebase
89 |
90 | This codebase serves as a starter template for the app.
91 | It is built using the following technologies:
92 |
93 | - [![Next.js]](https://nextjs.org) - a powerful app framework for the web
94 | - [![React]](https://react.dev) - a JavaScript library for building user interfaces
95 | - [![Tailwind]](https://tailwindcss.com) - a utility-first CSS framework for styling
96 |
97 | ## Goals
98 |
99 | Your primary goal is to create a web application that allows users to explore restaurants in California.
100 |
101 | ### Home Page (`/`)
102 |
103 | The primary goal of the [home page](./src/app/page.tsx) is to provide users with the ability to find restaurants.
104 |
105 | It should include the following features:
106 |
107 | - [A search input](./src/app/_search.tsx) to find restaurants by name or cuisine:
108 |
109 | 
110 |
111 | - Based on the results returned by the search, a list of preview cards for the restaurants.
112 |
113 | - When no search query is provided, the list should display the top restaurants in California, determined by you.
114 | - Each card must contain the following details for each restaurant:
115 | - Name
116 | - Cuisine
117 | - Rating & Review count
118 | - Location / Area
119 | - Price range
120 | - Interacting with the card must allow the user to navigate to the restaurant's page.
121 |
122 |
123 |
124 |
Example of a restaurant preview card on UberEats
125 |
126 |
127 | > **Note**
128 | > Do not imitate the above design from UberEats. You are free to design the preview card as you see fit.
129 |
130 | - Display a map with markers for each restaurant returned by the search.
131 |
132 |
133 |
134 |
A map of restaurants in San Francisco, California on Google Maps
135 |
136 |
137 | > **Note**
138 | > Do not imitate the above design from Google Maps. You are free to design the map as you see fit.
139 |
140 | - Clicking on a marker should zoom in on the restaurant's location and highlight/bring into view the preview card for that restaurant. (**BONUS**)
141 |
142 | ### Restaurant Page (`/restaurant/[restaurantId]`)
143 |
144 | This page serves as [the detailed view for a specific restaurant](./src/app/restaurant/[restaurantId]/page.tsx).
145 |
146 | Include as many relevant details from the data set as possible, such as:
147 |
148 | - Address
149 | - Description
150 | - Social Media
151 | - Contact information
152 | - Amenities
153 | - Operating hours
154 |
155 |
156 |
157 |
An example restaurant page on OpenTable
158 |
159 |
160 | > **Note**
161 | > Do not imitate the above design from OpenTable. You are free to design the restaurant page as you see fit.
162 |
163 | ### Miscellaneous
164 |
165 | - An "I'm feeling lucky" button that randomly selects a restaurant for the user.
166 | - A logo for the application. (**BONUS**)
167 |
168 | ## Guidelines
169 |
170 | - You are free to use any libraries or tools that you are comfortable with, as long as they are compatible with the template's initial stack.
171 | - The following UI component libraries (all compatible with Tailwind) are highly recommended for quick prototyping and development:
172 | - [`shadcn/ui`](https://ui.shadcn.com/)
173 | - [`headlessui`](https://headlessui.com/)
174 | - [`daisyUI`](https://daisyui.com/)
175 | - [`flowbite`](https://flowbite.com/)
176 | - and any other libraries you find useful.
177 | - It is highly recommended to use the [![mapbox]](https://www.mapbox.com) package for maps.
178 |
179 | A React compatible library is already included in the project dependencies. Find more information on how use it [here](https://visgl.github.io/.react-map-gl/).
180 |
181 | You will need [an access token](https://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens) from Mapbox to use the library.
182 |
183 | Once you have an access token, set the `NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN` environment variable in the `.env` file you created earlier.
184 |
185 | > **Note**
186 | > This `.env` file is already included in the `.gitignore` file, so you don't have to worry about accidentally committing it to the repository.
187 |
188 | - If you are using VS Code as your text editor, consider installing the [recommended extensions](.vscode/extensions.json) for this project.
189 | - Always keep your code well formatted. Your IDE should be configured to automatically format your code using [![Prettier]](https://prettier.io) but you can also run the following command:
190 | ```bash
191 | pnpm format
192 | ```
193 | - Ensure that your code is free from common programming style issues by running the following command:
194 | ```bash
195 | pnpm lint
196 | ```
197 | This command runs [![ESLint]](https://eslint.org) on the codebase to catch any issues.
198 | Your IDE should also be configured to show ESLint warnings and errors.
199 | - It is highly recommended to use services like GitHub Copilot, ChatGPT, etc., to speed up your development process.
200 |
201 | ## Evaluation
202 |
203 | ### Criteria
204 |
205 | For the application:
206 |
207 | - **Functionality**: Does the application meet the requirements/goals?
208 | - **Familiarity**: Is the UI intuitive and easy to use? Is the application accessible?
209 | - **Performance**: Does the application perform well? Are there any interactions that make the user wait a long time?
210 | - **Creativity**: Does the application have a unique look and feel? Does it stand out from other similar applications?
211 |
212 | For the codebase:
213 |
214 | - **Readability**: Is the codebase well-structured and easy to understand?
215 | - **Maintainability**: Is the codebase easy to maintain and extend by other developers?
216 | - **Documentation**: Is the codebase well-documented? Are there comments where necessary?
217 | - **Best Practices**: Does the codebase follow best practices for the technologies used?
218 |
219 | ### Submission
220 |
221 | - Sync all changes to **your private fork** on GitHub.
222 | - Deploy the app on [![Vercel]](https://vercel.com/new/):
223 | - Link your GitHub repository
224 | - Add the necessary environment variables
225 | - Set the deployed app's link (assigned by Vercel) as the website for your GitHub repository (the About section on the repository's homepage).
226 |
227 | This link should be of the form `.vercel.app`.
228 |
229 | - [Invite us](#contact) as [collaborators to your private fork](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-access-to-your-personal-repositories/inviting-collaborators-to-a-personal-repository).
230 |
231 | ## Help
232 |
233 | ### GitHub Issues
234 |
235 | When you encounter issues while working on the project, please attempt to resolve them on your own first with the help of the internet.
236 | If you are still unable to resolve the issue, please create [a new issue](https://github.com/dotlas/golden-plate-map/issues/new/choose) on this repository.
237 |
238 | > **Note**
239 | > Before creating a new issue, please check the [existing issues](https://github.com/dotlas/golden-plate-map/issues) to see if a similar one has already been reported.
240 | > It is possible that someone else has already encountered the same issue and found a solution.
241 |
242 | ### Contact
243 |
244 | Please reach out to us should you have any questions.
245 |
246 | | Name | Contact |
247 | | :-------------- | :------------------------------------------------------------------------------------------------- |
248 | | Kelvin DeCosta | [![GitHub]](https://github.com/kelvindecosta) [![LinkedIn]](https://linkedin.com/in/kelvindecosta) |
249 | | Eshwaran Venkat | [![GitHub]](https://github.com/cricksmaidiene) [![LinkedIn]](https://linkedin.com/in/eshwaranv98) |
250 |
251 | > Feel free to ping us anytime for support.
252 |
253 | [Next.js]: https://img.shields.io/badge/next.js-000000?logo=nextdotjs&logoColor=white "Next.js"
254 | [React]: https://img.shields.io/badge/react-20232A?logo=react&logoColor=61DAFB "React"
255 | [Vercel]: https://img.shields.io/badge/vercel-000000?logo=vercel&logoColor=white "Vercel"
256 | [TypeScript]: https://img.shields.io/badge/typescript-007ACC?logo=typescript&logoColor=white "TypeScript"
257 | [ESLint]: https://img.shields.io/badge/eslint-3A33D1?logo=eslint&logoColor=white "ESLint"
258 | [Prettier]: https://img.shields.io/badge/prettier-1A2C34?logo=prettier&logoColor=F7BA3E "Prettier"
259 | [pnpm]: https://img.shields.io/badge/pnpm-F69220?logo=pnpm&logoColor=white "pnpm"
260 | [Node.js]: https://img.shields.io/badge/node.js-339933?logo=node.js&logoColor=white "Node.js"
261 | [GitHub]: https://img.shields.io/badge/github-181717?logo=github&logoColor=white "GitHub"
262 | [Tailwind]: https://img.shields.io/badge/tailwind-38B2AC?logo=tailwind-css&logoColor=white "Tailwind CSS"
263 | [mapbox]: https://img.shields.io/badge/mapbox-000000?logo=mapbox&logoColor=white "Mapbox"
264 | [LinkedIn]: https://img.shields.io/badge/linkedin-0A66C2?logo=linkedin&logoColor=white "LinkedIn"
265 |
--------------------------------------------------------------------------------