smooth-app
전 세계 식품의 성분·영양·정보를 자유롭게 협업하고 공개하는 데이터베이스
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Next.js 15 (App Router) frontend for HealthClouda, an API-first, multi-tenant EHR/EMR platform for Nigerian healthcare organisations. Hospitals and clinics store patient records with us, and those records move with the patient between facilities. ⚠️ This application handles real patient data (PHI). Before you write code here, read CLAUDE.md — it is the operating manual for humans and AI assistants alike — and ONBOARDING.md if you are new. The rules in §5 of CLAUDE.md are not style preferences; several of them exist because something broke without them.README 전문 보기 ↗
Next.js 15 (App Router) frontend for HealthClouda, an API-first, multi-tenant EHR/EMR platform for Nigerian healthcare organisations. Hospitals and clinics store patient records with us, and those records move with the patient between facilities.
⚠️ This application handles real patient data (PHI). Before you write code here, read
CLAUDE.md— it is the operating manual for humans and AI assistants alike — andONBOARDING.mdif you are new. The rules in §5 ofCLAUDE.mdare not style preferences; several of them exist because something broke without them.
If you remember nothing else from this file:
/api/data for reads, /api/action for writes — which attach the JWT server-side.
Never fetch() the backend from a component; use useApi() / dataGet() / dataAction().localStorage, no sessionStorage, no non-httpOnly cookie. The browser cannot read the token,
which is the entire point./api/v1/schema/ is the single source of truth for the API — not this file, not a
committed doc, not what a component currently assumes. It needs no authentication, so a
contract question is never blocked on credentials. If an endpoint or field is missing, open a
GitHub issue on the backend repo tagged api-request. Never guess, never silently work around it.| Layer | Choice |
|---|---|
| Framework | Next.js 15 (App Router) · React 19 · TypeScript (strict) |
| Styling | Tailwind CSS v4 (via @tailwindcss/postcss) |
| Forms | react-hook-form + zod (@hookform/resolvers) |
| Client state | zustand |
| Unit tests | Vitest + Testing Library (jsdom) |
| Browser tests | Playwright (e2e/) |
| Hosting | Vercel (this repo) · Railway (the Django/DRF backend) |
The backend lives in a separate repo, healthclouda-backend (Django REST Framework).
Prerequisites: Node.js 20+ and npm.
git clone https://github.com/HealthClouda/healthclouda-frontend.git
cd healthclouda-frontend
npm install
cp .env.example .env.local # then edit — see below
npm run dev # http://localhost:3000
.env.example is the documentation for this — read it, it explains the reasoning, not just the
names.
| Variable | What it is |
|---|---|
NEXT_PUBLIC_API_URL | Backend base URL including the /api/v1 suffix |
NEXT_PUBLIC_SITE_URL | This deployment's own public origin (metadata, canonical URLs) |
🔴 One build serves exactly one backend tier. NEXT_PUBLIC_API_URL is baked in at build time,
so a deployment cannot be re-pointed without rebuilding. There is no fallback outside local
development — if it is unset, the build fails deliberately (src/lib/config.ts), because a
deployed build silently pointing at localhost presents as "the backend is down".
🔴 Do not set a cookie domain. There is deliberately no COOKIE_DOMAIN variable: a dot-prefixed
parent (.healthclouda.com) would share session cookies across every subdomain, so a dev session
cookie would be sent to beta and to production.
| Git branch | Frontend | Backend API |
|---|---|---|
main | healthclouda.com + www | (unset — marketing only for now) |
staging | beta.healthclouda.com | https://api-beta.healthclouda.com/api/v1 |
develop | dev.healthclouda.com | https://api-dev.healthclouda.com/api/v1 |
Non-negotiable, and there is no CI — these are honour-system and local (FLAG-006):
npx tsc --noEmit # must be clean
npm test # must be green
npm run build # must be green
npm run build needs NEXT_PUBLIC_API_URL set, by design. Report failures honestly, with output.
Other scripts: npm run test:watch, npm run test:e2e (Playwright), npm run test:e2e:ui,
npm run lint.
component
└─ useApi() / dataGet() / dataAction() src/hooks/use-api.ts, src/lib/client-api.ts
└─ /api/data · /api/action our own route handlers (same origin)
└─ attach JWT from httpOnly cookie src/lib/auth.ts
└─ DRF backend NEXT_PUBLIC_API_URL
Server Components fetch through serverFetch() / serverFetchResult() (src/lib/server-fetch.ts),
which reads the same httpOnly cookie. On a 401 the client layer refreshes once, single-flight,
and retries — SimpleJWT rotates and blacklists refresh tokens, so concurrent refreshes log the
user out. That single-flight is load-bearing; don't "simplify" it.
Two more conventions with teeth:
ENDPOINTS (src/lib/config.ts). Never hardcode one in a component.src/lib/router.ts. Never hand-write a path. RESERVED_PATHS in
config.ts must stay in sync with src/app/, or an org slug will shadow a real route.DRF shapes: list endpoints return {count, next, previous, results}, not bare arrays — use
usePaginatedList(). Pagination params are ?page= and ?page_size=; ?limit= is silently
ignored. Invented query params are a recurring bug class here — DRF ignores an unknown filter
silently and the UI shows wrong data with no error, so verify against the live schema first.
| Portal | Endpoint | Who |
|---|---|---|
| General | POST /auth/login/ | Patients only |
| Org | POST /auth/login/<org_slug>/ | Staff and patients |
| Superadmin | POST /auth/login/admin/ | Superadmin only |
Staff who try the general portal get a 400 carrying org_slug + redirect_url — we use it to
redirect them to their org portal automatically.
Roles: SUPERADMIN, ORGANIZATION_ADMIN, DOCTOR, NURSE, RECEPTIONIST, PATIENT.
src/
├── app/ routes (App Router)
│ ├── [slug]/ org-scoped: signin, five staff dashboards, password flows
│ ├── api/ our proxy + auth route handlers — the ONLY caller of the backend
│ ├── superadmin/ superadmin portal (no org slug)
│ └── … landing, signin, set-password, access-request/respond
├── components/
│ ├── dashboard/ one folder per role
│ ├── layout/ DashboardShell, Sidebar, DashboardHeader, SmallScreenGate
│ ├── ui/ shared primitives (DataTable, Modal, SlidePanel, Toaster, …)
│ ├── forms/ auth + password-flow forms
│ ├── landing/ marketing page sections
│ └── access/ cross-org consent respond screens
├── hooks/ useApi, useAllPages, usePaginatedList, apiAction
├── lib/ config (ENDPOINTS, ROLES), router, auth, client-api, server-fetch, utils
├── types/ auth.ts, dashboard.ts
└── middleware.ts route gating + server-side session resume
design_handoff_*/ hold the design sources the dashboards and pre-login screens are built from;
they are referenced from the components and stay where they are.
develop → demo-functional (shortcuts allowed only if logged in
CODEBASE_FLAGS.md) · staging → production-grade · main → production.
Cut feat/* / fix/* from an up-to-date develop, rebase onto it (never merge it in), and open
a PR back into develop. main, staging and develop are protected by a GitHub ruleset: a PR is
required, one approving review is required, and force-pushes and deletion are blocked, with no
bypass for anyone. Assign the reviewer when you open the PR — it is always the other developer.
| File | What it is |
|---|---|
CLAUDE.md | The operating manual. Binding on humans and AI assistants |
ONBOARDING.md | Read this first if you are new — glossary, setup, conventions |
HANDOFF.md | Durable shared state: In Flight claims, backend contract notes, gates |
HANDOFF-<Name>.md | Per-developer session logs. Never edit someone else's |
CODEBASE_FLAGS.md | Known issues and logged shortcuts |
docs/ARCHITECTURE.md | What is actually built |
docs/SECURITY_BASELINE.md | The PHI security baseline |
docs/FRONTEND_SPRINT_PLAN.md | The dated plan to beta onboarding |
docs/DESIGN-VERIFICATION.md | How to verify a screen against its design |
Questions go to the team, early — thirty minutes stuck, not two hours. This is a multi-tenant medical records system; nobody reads it once and gets it.
docs/archive/| Historical documents, kept for context, not current |