Lalabase
Tickets, Doku, Zeiterfassung und Kundengespräche an einem Ort, durchsuchbar per KI. Für Softwarefirmen mit Kundenprojekten. Open Source: auf deinem eigenen Server oder in unserer Cloud in Deutschland.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Bilingual (Arabic RTL / English LTR) marketing site for Circle — AI automation for dental clinics.
• Stack: Next.js (App Router) + Tailwind CSS v4
• Routes: /ar (default), /en
• Texts: src/i18n/ar.ts, src/i18n/en.ts
• Demo form (3 steps, clinic-specific) → POST /api/lead → Supabase leads table (+ optional n8n webhook)
• Contact number and WhatsApp links: src/lib/contact.ts檢視完整 README ↗Bilingual (Arabic RTL / English LTR) marketing site for Circle — AI automation for dental clinics.
/ar (default), /ensrc/i18n/ar.ts, src/i18n/en.tsPOST /api/lead → Supabase leads table (+ optional n8n webhook)src/lib/contact.ts| Variable | Purpose |
|---|---|
SUPABASE_URL | Supabase project URL |
SUPABASE_ANON_KEY | Supabase anon key (used server-side only; the browser never talks to Supabase) |
SUPABASE_SERVICE_ROLE_KEY | Server-only key: inserts leads and creates accounts for accepted invitations. Never expose it |
N8N_BOOKING_WEBHOOK_URL | Optional n8n webhook called after each booking (website or API, event appointment.booked, includes when and manage_url) — see n8n/ |
SITE_URL | Public site URL, e.g. https://circle.sa. Required in production: links and redirects are built from it (behind the proxy the app cannot see its public address) |
N8N_LEAD_WEBHOOK_URL | Optional n8n webhook notified of each new lead |
N8N_WEBHOOK_SECRET | Optional, sent as x-circle-secret header |
RATE_LIMIT_SALT | Random secret used to hash client IPs for rate limiting (falls back to the service key) |
npm install
npm run dev
Deploy: patient data must stay in Saudi Arabia (PDPL), so production runs self-hosted Supabase + this app +
n8n on one VM in Oracle Cloud Jeddah (a Saudi region). Everything is scripted in deploy/ (install,
updates, encrypted backups, restore, monitoring). Don't use hosted services outside KSA for patient data.
npm run preflight -- --live https://your-site # env, security, PDPL, cost caps + read-only probes
Every push runs CI (.github/workflows/ci.yml): types, lint, unit tests, SQL security tests, build.
Vercel (demo, fictional data only): import the repo, set the variables from .env.example
(plus CRON_SECRET; vercel.json runs the daily ops check), and run supabase/setup.sql on the
database. Production with real patients: the self-hosted kit in deploy/ (Jeddah), because
patient data must stay in Saudi Arabia (PDPL).
Run supabase/setup.sql (all migrations concatenated) in the SQL Editor, or the files in
supabase/migrations/ in order. Upgrading an existing database: run only the migrations added since
(every migration is safe to re-run).
Then in Supabase:
insert into public.platform_admins (user_id)
select id from auth.users where email = '[email protected]';
/ar/forgot): configure SMTP for Auth, and add {SITE_URL}/auth/callback to the
allowed redirect URLs. The email link lands on /auth/callback, which opens /{lang}/reset-password./ar/portal, /en/portal)n8n/README.md.is_clinic_member /
can_manage_clinic / can_assign_role, so a bug in the app cannot leak another clinic's data.audit_log.HttpOnly, Secure (in production), SameSite=Lax
cookies; there is no Supabase client in the browser. src/proxy.ts refreshes sessions and guards
/portal. Portal pages send Cache-Control: private, no-store and noindex.rate_limit_hit in Postgres (src/lib/rate-limit.ts).X-Frame-Options: DENY, nosniff, strict referrer and permissions policies.20260926000000_clinic_data.sql adds each clinic's public site settings (clinic_sites: slug,
template, brand, booking rules), branches, doctors, services, weekly working hours, time off,
patients, clinical notes and appointments.
(clinic_id, id) on every reference: a row can never point at another
clinic's doctor, service or patient.patient_clinical) are readable by owners, managers and the treating doctor —
never reception.public_site(slug), available_slots(slug, service, day, doctor?, branch?) and
book_appointment(...), which re-checks the slot, reuses the patient by phone and allows at most
3 open website bookings per phone. A Postgres exclusion constraint makes double-booking a doctor
impossible, even under concurrent requests./ar/c/{slug})Each clinic's public site is rendered from public_site(slug) by one of the templates in
src/templates:
modern — the clinic's own brand colours.founding_day (يوم التأسيس) — sand, mud-brick brown and gold with a Sadu-inspired band.national_day (اليوم الوطني) — deep night with Saudi green and an eight-pointed-star pattern.Occasion themes fix the colours and add a greeting ribbon; the clinic keeps its logo and font. They use original geometric motifs only — no flag, emblem or official occasion logos (the flag carries the Shahada and must not be used as commercial decoration). The brand settings:
{ "primary": "#0e7490", "accent": "#14b8a6", "font": "tajawal", "logo_url": "https://…", "hero_image_url": "https://…" }
Fonts: plex, tajawal, cairo, readex. Colours are validated, button text colour is chosen by
WCAG contrast, and content.sections can hide services / doctors / branches. Pages include
schema.org Dentist data for search engines and are regenerated at most once a minute.
Demo clinic: run supabase/seed/demo_clinic.sql to get a published sample at /ar/c/noor
(no real patient data) — useful for sales demos. Then supabase/seed/demo_activity.sql adds fictional
patients, six weeks of visits, the coming week and a knowledge base, so the board and reports have data.
/ar/c/{slug}/book)Four steps: service → doctor (or "any available") → day & time → details. The page loads 14 days of
availability in one call (GET /api/sites/{slug}/availability) so empty days are disabled up front.
POST /api/sites/{slug}/book validates input, calls book_appointment (which re-checks the slot),
and notifies N8N_BOOKING_WEBHOOK_URL if set. If someone else takes the slot first, the patient is
told and sent back to a refreshed list of times. The confirmation offers an .ics calendar file.
/ar/portal/clinic/{id})| Page | Owner / manager | Reception | Doctor |
|---|---|---|---|
| Today — KPIs, today's list, pending requests | ✓ | ✓ | own appointments |
| Appointments — week view, doctor filter, manual booking | ✓ | ✓ | own (status only) |
| Patients — search, details, visit history | ✓ | ✓ | patients they treat |
| Medical record (allergies, history) | ✓ | — | patients they treat |
| Services · Doctors & hours · Branches | ✓ | — | — |
| Website & brand — template, colours, font, content, booking rules, publish, draft preview | ✓ | — | — |
| Team & settings — members, invitations, activity log | ✓ | — | — |
Every page and action runs as the signed-in user, so these limits are enforced by the RLS policies
(the menu only hides what the database would refuse anyway). Double-booking from the dashboard is
blocked by the same exclusion constraint as online booking. Draft previews live at /ar/preview/{id}.
/ar/portal/clinic/{id}/automation)ck_…): shown once, stored as SHA-256, revocable. A key only reaches its
own clinic — every /api/v1/* route calls an api_* SQL function that resolves the clinic from
the key hash. Endpoints: clinic, slots, appointments (book / find by phone),
appointments/{id}/reschedule|cancel (phone must match), reminders, reminders/{id}/sent.GET /api/v1/reminders returns due visits with a
fresh self-service link, a formatted when, and a ready message; they are never returned twice./ar/a/{token}: confirm attendance, cancel, or pick a new time,
until the clinic's cutoff. Tokens are 256-bit, stored hashed, and replaced on each reminder.n8n/.supabase/tests/run.sh loads the migrations into a throwaway Postgres (with a stub of Supabase's
auth schema) and runs every *_test.sql — 146 tenant-isolation, permission, booking and automation checks:
PGHOST=localhost PGUSER=postgres supabase/tests/run.sh
Logos, hero images, doctor photos and before/after cases are uploaded from the dashboard
(الموقع والهوية, الأطباء والدوام). The browser shrinks photos, then a server action checks
the user may manage the clinic, identifies the file from its bytes (PNG/JPEG/WebP, max 3 MB) and
stores it in the public Storage bucket clinic-media under <clinic id>/… with the service key.
Migration 20260930000000_media.sql creates the bucket. Demo images live in public/demo.
/ar/privacy and /ar/terms (src/legal/content.ts) are a draft that needs lawyer review. Fill in
the business details in src/lib/company.ts (freelance document number, privacy email) before launch.
src/i18n/*.ts → testimonials currently holds illustrative examples, labelled as such on the page.
Replace them with real, approved customer quotes, then set IS_SAMPLE = false in src/components/Testimonials.tsx.