diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d44bc22..fbe8f08 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -78,21 +78,33 @@ that pair is the source of truth, not this file. The `PlaceType` union and Next.js App Router. Each folder is a route segment. -| Route | File | Purpose | -| --------------------------- | ------------------------ | ----------------------------------------------------- | -| `/` | `page.tsx` | Homepage: hero, feature grid, CTA | -| `/map` | `map/page.tsx` | Full interactive map | -| `/calendar` | `calendar/page.tsx` | Exam calendar (SAT, IB, IGCSE) | -| `/docs` | `docs/page.tsx` | Docs index linking to guides | -| `/docs/contributing` | | How to add a place via PR | -| `/docs/exam-centres` | | How to use the map for exam centres | -| `/docs/github-student-pack` | | GitHub Student Pack guide | -| `/contribute` | `contribute/page.tsx` | Contribution hub (add place / bug / question) | -| `/about` | `about/page.tsx` | Principles, stats, maintainers | -| `/legal/*` | | Privacy, terms, disclaimer | -| `/offline` | `offline/page.tsx` | PWA offline fallback | -| `/login` | `login/page.tsx` | Optional sign-in (Google OAuth, email) | -| `/auth/callback` | `auth/callback/route.ts` | OAuth code exchange (dynamic, not static-export-safe) | +| Route | File | Purpose | +| ----------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------- | +| `/` | `page.tsx` | Homepage: hero, feature grid, CTA | +| `/map` | `map/page.tsx` | Full interactive map | +| `/calendar` | `calendar/page.tsx` | Exam calendar (SAT, IB, IGCSE) | +| `/docs` | `docs/page.tsx` | Docs index linking to guides | +| `/docs/contributing` | | How to add a place via PR | +| `/docs/exam-centres` | | How to use the map for exam centres | +| `/docs/github-student-pack` | | GitHub Student Pack guide | +| `/docs/*` | `docs/(article)/*/page.tsx` | The other guides, reference pages and synced awesome lists; `lib/docs-nav.ts` lists every one | +| `/competitions` | `competitions/page.tsx` | Browse and filter competitions, as cards or a list | +| `/competitions/[slug]` | `competitions/[slug]/page.tsx` | One competition: dates, fee, format and country tracks | +| `/competitions/saved` | `competitions/saved/page.tsx` | Signed-in saved competitions and the calendar feed link | +| `/city/[slug]` | `city/[slug]/page.tsx` | Landing page for one city: every place in it | +| `/account` | `account/page.tsx` | Signed-in account: saved places, home, calendar events | +| `/onboarding` | `onboarding/page.tsx` | First-run questionnaire, shown once after first sign-in | +| `/contribute` | `contribute/page.tsx` | Contribution hub (add place / bug / question) | +| `/about` | `about/page.tsx` | Principles, stats, maintainers | +| `/legal/*` | | Privacy, terms, disclaimer | +| `/offline` | `offline/page.tsx` | PWA offline fallback | +| `/login` | `login/page.tsx` | Optional sign-in (Google OAuth, email) | +| `/auth/callback` | `auth/callback/route.ts` | OAuth code exchange (dynamic, not static-export-safe) | +| `/api/places` | `api/places/route.ts` | Public JSON API for places | +| `/api/competitions` | `api/competitions/route.ts` | Public JSON API for competitions | +| `/api/competitions/saved.ics` | `api/competitions/saved.ics/route.ts` | Calendar feed (ICS) of a user's saved competitions, read with a token | +| `/api/auth/forgot-password` | `api/auth/forgot-password/route.ts` | Rate-limit gate for password-reset requests | +| `sitemap.xml`, `robots.txt` | `sitemap.ts`, `robots.ts` | Generated sitemap and crawl rules | `layout.tsx` (root) wraps every page with `Navbar`, `Footer`, and the theme provider. @@ -124,11 +136,25 @@ components/ │ └── filters.ts # `PlaceFilters` type ├── calendar/ │ └── personal-event-dialog.tsx # add/edit/delete a signed-in user's calendar event +├── competitions/ +│ ├── competitions-browser.tsx # top-level browse view: filters, sorting, card or list layout +│ ├── competition-filters.tsx # subject, format, participation, region, fee, age and deadline filters +│ ├── filters.ts # `CompetitionFilterState` and the deadline window type +│ ├── competition-card.tsx # one competition in the browse view +│ ├── competition-detail.tsx # the full competition page body +│ ├── country-track-list.tsx # per-country eligibility and dates on a competition +│ ├── deadline-countdown.tsx # time left until the next date +│ ├── save-button.tsx # save or unsave (signed in) +│ ├── calendar-feed-card.tsx # the personal calendar feed link on /competitions/saved +│ └── view-toggle.tsx # switch between card and list layout +├── onboarding/ +│ └── onboarding-flow.tsx # first-run questionnaire (board, field, country, how you found us) ├── pins/ │ └── pin-popup.tsx # popup on marker click (directions, share, added_by) ├── layout/ │ ├── navbar.tsx # nav links + sign-in/sign-out │ ├── footer.tsx +│ ├── site-search.tsx # site-wide search dialog over places, competitions and docs, loaded on first open │ ├── page-container.tsx # max-width wrapper used by most pages │ ├── theme-provider.tsx │ └── theme-toggle.tsx @@ -137,6 +163,7 @@ components/ ├── docs/ │ ├── docs-header.tsx # resolves the current /docs route against lib/docs-nav.ts, renders DocsPageHeader │ ├── docs-page-header.tsx # full-bleed graph-paper header band (kicker + H1 + description) +│ ├── docs-index.tsx # the /docs index, with a grid and a list view (docs-index-grid.tsx, docs-index-list.tsx) │ ├── docs-sidebar.tsx # DocsDesktopSidebar (persistent, lg+) + DocsMobileNav (Sheet drawer, --`. Prefix is a short slug for the city (e.g. `mum`, `thane`, `navi`, `ldn`, `jkt`). Increment from the highest existing number in the file for that prefix. - `city`: lowercase, underscore-separated slug (e.g. `mumbai`, `navi_mumbai`, `jakarta`). Any city worldwide is welcome, not just Mumbai/Thane/Navi Mumbai. - `type`: must match the filename exactly -- `lat`/`lng`: from Google Maps (right-click pin, "What's here?"). Range: lat 18-20, lng 72-73. +- `lat`/`lng`: from Google Maps (right-click pin, "What's here?"). Any real-world coordinate works: `places.schema.json` checks latitude is between -90 and 90 and longitude between -180 and 180. - `address`: optional, short, human-readable - `gmaps_link`: Google Maps share link (Share -> Copy link) - Do not add rating, review count, or verified date to the JSON. Those go in the PR. diff --git a/package-lock.json b/package-lock.json index 819dfe6..4a80085 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "jsdom": "^29.1.1", "prettier": "^3.6.2", "shadcn": "^4.10.0", + "sharp": "^0.34.5", "tailwindcss": "^4", "typescript": "^5", "vitest": "^4.1.9" diff --git a/package.json b/package.json index f1bd895..9f1204b 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "jsdom": "^29.1.1", "prettier": "^3.6.2", "shadcn": "^4.10.0", + "sharp": "^0.34.5", "tailwindcss": "^4", "typescript": "^5", "vitest": "^4.1.9" diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest index 92bda15..39d1133 100644 --- a/public/manifest.webmanifest +++ b/public/manifest.webmanifest @@ -1,7 +1,7 @@ { "name": "StudyMap", "short_name": "StudyMap", - "description": "Crowdsourced study resource map for students worldwide. Find libraries, exam centres, stationery shops, and more.", + "description": "Crowdsourced study resource map for students worldwide. Find libraries, exam centres, airports, government offices, and more.", "start_url": "/", "display": "standalone", "orientation": "portrait-primary", @@ -41,7 +41,7 @@ "sizes": "1200x630", "type": "image/png", "form_factor": "wide", - "label": "StudyMap — resource map for Mumbai" + "label": "StudyMap - resource map for students worldwide" } ], "related_applications": [],