Synaplan
Open-source AI platform for companies: self-hosted AI agents, GDPR-compliant chatbot widget, RAG knowledge base, WhatsApp and Outlook AI, MCP, multi-model routing. Docker, Kubernetes or hosted in Germ
Report hazards, accidents and near-misses (tillbud) for Swedish construction sites and plants. SAM / Arbetsmiljöverket aligned.
Translation pending
BusinessTypeScript · PLpgSQLGitHub @thealbayrakOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Mobile-first PWA for Swedish construction sites and plants: workers report near-misses (tillbud), hazards and accidents in three taps; the safety organisation follows up with CAPA, weekly skyddsronder (AFS 2001:1) and AML 3:3a notices to Arbetsmiljöverket.
npm install
npm run dev # http://localhost:3000 — demo mode until Supabase is configured
npm test # vitest unit tests
npm run lint
npm run typecheck
Copy .env.local.example to .env.local and set NEXT_PUBLIC_SUPABASE_URL and
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY (or ..._ANON_KEY). Both are public values.
Apply the schema (tables, RLS policies, RPCs, storage bucket, realtime):
npx supabase login
npx supabase link --project-ref <your-project-ref>
npx supabase db push
npx supabase db query -f supabase/seed.sql # optional demo sites and locations
Without the CLI, paste supabase/apply-all.sql (migrations + seed in one script) into the
SQL editor of the Supabase dashboard and run it. Regenerate it after editing a migration:
node scripts/build-apply-all.mjs
Create the safety officers' accounts under Authentication → Users (email + password, or invite by e-mail). Field workers never need an account.
| Role | Can |
|---|---|
| Anonymous visitor (field worker) | read sites and locations, submit a report through the submit_incident RPC, upload photos / voice notes |
| Signed-in user (safety officer) | read and update incidents, CAPA actions, skyddsronder and media |
The dashboard and /skyddsrond redirect to /login when Supabase is configured. Media lives in
the private incident-media bucket and is served through short-lived signed URLs.
Workers never sign in. Each company and each place has a report key; the QR code carries it (/report?k=<key>).
The submit_incident RPC refuses an anonymous report without the current key of that company or place, so the
form cannot be used for another company. A manager (or the owner) replaces a key in Settings / Projects and the
old printed code stops working at once. The RPC also limits reports per phone, per network and per company per
hour, and flags a report sent from outside the place (a flag, not a block). Signed-in people of the company
report without a key. Optional: Cloudflare Turnstile (NEXT_PUBLIC_TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY).
Staff see only the places a manager ticks for them (People page); managers see their whole company.
With NEXT_PUBLIC_ROOT_DOMAIN set, each company gets <slug>.<root domain> (add a wildcard domain in Vercel).
Set RESEND_API_KEY, SAFETY_ALERT_RECIPIENT (comma-separated allowed), optionally RESEND_FROM
and NEXT_PUBLIC_APP_URL. The browser pings /api/alerts/critical-incident after a report; the
route calls the claim_incident_alert RPC, which returns the incident only if it is critical and
has not been alerted before, so the e-mail can be neither forged nor duplicated.
For delivery that does not depend on the reporter's browser, add a Database Webhook in
Supabase (table incidents, event INSERT, URL https://<your-app>/api/alerts/critical-incident,
HTTP header x-alert-secret: <ALERT_WEBHOOK_SECRET>) and set ALERT_WEBHOOK_SECRET in the app.
app/ routes (report, dashboard, skyddsrond, login, api/alerts)
components/ UI: report wizard, dashboard, skyddsrond checklist, auth, shell
lib/data/ repository contract + Supabase and in-memory implementations
lib/supabase/ client/server factories, row mappers, hand-maintained DB types
lib/offline/ IndexedDB queue for reports written without a connection
lib/alerts/ critical-incident payload, parsing and e-mail template
lib/compliance/ AML 3:3a notice model and print template
supabase/ migrations, seed data, CLI config
tests/ vitest unit tests for the pure modules
public/sw.js caches the app shell (network-first for pages, cache-first for static assets) and is
registered in production builds only. iOS splash screens are generated by
node scripts/generate-ios-assets.mjs.