{"id":51731130,"url":"https://github.com/cosimochellini/ub-dragon-hackaton","last_synced_at":"2026-07-18T02:47:13.509Z","repository":{"id":364330731,"uuid":"1267391444","full_name":"cosimochellini/ub-dragon-hackaton","owner":"cosimochellini","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-16T13:52:31.000Z","size":552,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-13T02:18:34.362Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cosimochellini.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-12T13:52:12.000Z","updated_at":"2026-06-16T12:51:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cosimochellini/ub-dragon-hackaton","commit_stats":null,"previous_names":["cosimochellini/ub-dragon-hackaton"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cosimochellini/ub-dragon-hackaton","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosimochellini%2Fub-dragon-hackaton","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosimochellini%2Fub-dragon-hackaton/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosimochellini%2Fub-dragon-hackaton/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosimochellini%2Fub-dragon-hackaton/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cosimochellini","download_url":"https://codeload.github.com/cosimochellini/ub-dragon-hackaton/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cosimochellini%2Fub-dragon-hackaton/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35602804,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-18T02:00:07.223Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-18T02:47:12.827Z","updated_at":"2026-07-18T02:47:13.497Z","avatar_url":"https://github.com/cosimochellini.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Therapists in Milan — direct booking\n\nAn advanced POC: a patient browses in‑person therapists in Milan and books a free\nintro call **straight from the directory card** — tap a slot → booking sheet →\nconfirmed. Built to be wired to a real booking backend (Calendly) later.\n\n## Core loop \u0026 scope\n\n- **List ↔ stylized‑map toggle** — a directory list and a stylized SVG map (fuzzy\n  radius per studio, no map API key). Shared Unobravo studios cluster; private\n  studios stand alone. Exact address only after booking.\n- **Availability lives on the card** — tap a day, tap a slot, book. No profile detour.\n- **Two filters** — service type (Individual / Couples) and gender (Any / Female /\n  Male), with a live count.\n\n## Stack\n\nReact 19 · TanStack Start (SSR + file routing) · TanStack Query · Tailwind CSS v4 ·\nTypeScript · ESLint (hardened — see [Linting](#linting)) · Vitest + Testing\nLibrary. Design tokens from Unobravo's **Zenit 2.0** system.\n\n## Requirements\n\nNode **≥ 22.12** (see `.nvmrc`).\n\n### Environment\n\n| Variable        | Default                        | Purpose                                                                       |\n| --------------- | ------------------------------ | ----------------------------------------------------------------------------- |\n| `VITE_SITE_URL` | `https://ub-dragon.netlify.app` | Absolute origin for the canonical URL and Open Graph / Twitter image. Set it in the Netlify site environment to match the deployed domain. |\n\n## SEO \u0026 favicon\n\nThe HTML `\u003chead\u003e` (in `src/routes/__root.tsx`) carries the title, description,\ncanonical URL, favicons, theme color, web-app manifest, and the full Open\nGraph + Twitter card set. The favicon (`favicon.svg` / `favicon.ico`),\nPWA/touch icons (`logo192.png`, `logo512.png`, `apple-touch-icon.png`), and the\n1200×630 social-share image (`og-image.png`) all live in `public/` and are\ngenerated from the Unobravo brand mark in `public/logo-unobravo-black.svg`.\n\nBecause this POC is seeded with **mock** therapist data, it is marked\n`noindex, nofollow` via a page-level robots `\u003cmeta\u003e` tag so it is not indexed\nunder the real Unobravo brand. (A Netlify `X-Robots-Tag` header is deliberately\navoided: `[[headers]]` rules don't apply to the catch-all-redirect HTML served\nby the SSR function, so the meta tag — rendered into every SSR response — is the\nauthoritative signal.) `robots.txt` stays crawlable on purpose so the `noindex`\ndirective is actually read; social link previews still work.\n\n## Scripts\n\n```bash\nnpm install\nnpm run dev        # dev server at http://localhost:3000\nnpm run build      # production build (client + SSR)\nnpm run typecheck  # tsc --noEmit\nnpm run lint       # eslint (see Linting below)\nnpm run test       # vitest (unit + booking-flow integration)\nnpm run react-doctor  # full React Doctor scan (scoring + report), on demand\n```\n\n## Linting\n\nESLint is configured for **strong, React-aware correctness**, not just style. On\ntop of the TanStack base config (`typescript-eslint`, `import-x`, `n`,\n`@stylistic`) the flat config (`eslint.config.js`) layers four React-focused\nplugins, each at its recommended preset:\n\n| Plugin | What it enforces |\n| --- | --- |\n| [`eslint-plugin-react-doctor`](https://react.doctor) | Security / performance / a11y / architecture, plus the **TanStack Start** and **TanStack Query** rule packs. |\n| [`eslint-plugin-react-hooks`](https://react.dev/reference/eslint-plugin-react-hooks) (v7) | Rules of Hooks + the React Compiler lints. |\n| [`@eslint-react/eslint-plugin`](https://eslint-react.xyz) | Deep, type-aware React 19 correctness rules. |\n| [`eslint-plugin-unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn) | Strong, framework-agnostic JS/TS hardening. |\n\nA small set of rules is deliberately turned off (each with an inline rationale in\n`eslint.config.js`) where it fights this project's idioms — e.g. `unicorn/no-null`\n(`null` is idiomatic for refs/DOM/JSON), `unicorn/no-nested-ternary` (Prettier owns\nternary formatting), and the React-Compiler memoization rule (the compiler isn't\nenabled here). React rules lint `src/**/*.{ts,tsx}`; the generated `routeTree.gen.ts`\nis ignored.\n\n`react-doctor` is the same rule set as a full CLI scan (scoring + report);\n`npm run react-doctor` runs it on demand. CI (`.github/workflows/ci.yml`) gates every\nPR on `lint` + `typecheck` + `test`.\n\n## Architecture\n\n- `src/data/therapists.json` — **mock data** (4 studios, 6 therapists). Availability\n  is stored as a 7‑day offset pattern (`dayOffset 0..6 → [\"HH:MM\", …]`). **This is\n  the production‑swap point** — replace the read in `src/lib/therapists-data.ts`\n  with a real API/DB call returning the same `DirectoryData` shape.\n- `src/lib/therapists-data.ts` — a TanStack Start `createServerFn` that reads the\n  JSON and resolves availability into concrete dates **once, on the server**, so\n  SSR markup and client hydration share identical date strings.\n- `src/lib/availability.ts` + `src/lib/time.ts` — date resolution. \"Today\" is the\n  current calendar day **in Europe/Rome** (timezone‑correct, no midnight‑UTC\n  off‑by‑one); the reference date is injectable for deterministic tests.\n- `src/lib/{filter,studios}.ts` — pure filtering and studio grouping.\n- `src/query/therapists.ts` — `queryOptions`; prefetched in the route loader via\n  `ensureQueryData` and consumed with `useSuspenseQuery`.\n- `src/components/` — `ui/` Zenit atoms, `therapist/` cards + availability,\n  `map/` stylized map, `booking/` bottom sheet, `shell/` header/frame, and\n  `MilanApp.tsx` (state container, decoupled from the data layer for testing).\n- `src/components/icon/` — only the ~10 Zenit glyphs the app uses, as inline SVG\n  (no `dangerouslySetInnerHTML`, no 134KB registry).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcosimochellini%2Fub-dragon-hackaton","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcosimochellini%2Fub-dragon-hackaton","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcosimochellini%2Fub-dragon-hackaton/lists"}