Ola Malmgren 287e9a2a0e
All checks were successful
Build and deploy / build-deploy (push) Successful in 1m17s
fix my page header
2026-05-22 23:20:56 +02:00
2026-05-22 22:05:16 +02:00
2026-05-22 22:07:22 +02:00
2026-05-22 10:50:48 +02:00
2026-05-22 23:20:56 +02:00
2026-05-22 10:50:48 +02:00
2026-05-22 10:50:48 +02:00
2026-05-22 20:38:46 +02:00
2026-05-22 20:33:21 +02:00
2026-05-22 10:50:48 +02:00
2026-05-22 10:50:48 +02:00
2026-05-22 10:50:48 +02:00
2026-05-22 20:33:21 +02:00
2026-05-22 10:50:48 +02:00
2026-05-22 10:50:48 +02:00
2026-05-22 10:50:48 +02:00
2026-05-22 10:50:48 +02:00

boka.gasol247

Beställningsplattform för gasoltuber till ett enskilt scoutläger (Jamboree). Riktar sig till organisationer (scoutkårer, föreningar) som beställer för leverans/upphämtning på plats. Bokningen genererar ett faktureringsunderlag som administratören exporterar till CSV — själva faktureringen sker utanför systemet.

Tekniskt frikopplad från gasol247.com men ärver dess färgspråk. Tänkt att deployas på egen subdomän.

Funktioner i MVP

  • Publikt bokningsformulär med fyra steg: produkter → uppgifter → upphämtning → granska
  • Språkstöd: svenska (default) och engelska, växlas i header
  • Organisationsbeställning med Luhn-validering av svenskt organisationsnummer
  • Upphämtningstider (slots) — kunden väljer tid, kapacitet per slot
  • Bekräftelse-mejl via Mailjet med responsiv HTML-template (mobil + desktop)
  • Admin-panel bakom lösen: lista bokningar, filter, status, detaljvy, ändra status
  • CSV-export av faktureringsunderlag (Excel-vänlig, semikolon + UTF-8 BOM)
  • Prissnapshot: priser & namn fryses i bokningen när den skapas
  • Bokningsnummer i formatet JAM-YY-XXXX (läsbart över telefon)

Teknisk stack

Lager Val Varför
Frontend + Backend Next.js 15 (App Router) En process, enkel deploy
Språk TypeScript Typsäkerhet över hela kodbasen
Styling Tailwind CSS Snabb iteration, små bundles
Databas SQLite via Prisma 5 En fil, ingen separat container för MVP
Auth Auth.js v5 (NextAuth) Credentials + bcrypt Lokal admin-tabell, ingen extern beroende
i18n next-intl Server- + client-translations, prefix-routing
Mejl Mailjet (node-mailjet) Bra leverans, designat HTML-mejl
Deploy Docker (Next standalone) Körs bakom egen reverse proxy

Snabbstart utveckling

cp .env.example .env
# Generera AUTH_SECRET:
#   openssl rand -base64 32

npm install
npx prisma db push       # skapa SQLite-schemat
npm run db:seed          # admin + produkter + upphämtningstider
npm run dev              # http://localhost:3000

Default-admin (från .env): admin@example.com / change-me — ändra direkt i .env innan deploy.

Användbara kommandon

npm run dev              # dev-server med hot reload
npm run build            # produktionsbygge
npm run start            # produktionsserver (kräver byggd app)
npm run db:studio        # Prisma Studio — visuell DB-editor
npm run db:seed          # idempotent seed (kör om för uppdaterade priser)
npx prisma db push       # synka schemat till SQLite efter ändring i prisma/schema.prisma

Projektstruktur

boka.gasol247/
├── prisma/
│   ├── schema.prisma             Admin, Product, PickupSlot, Booking, BookingItem
│   └── seed.ts                   Första admin + P6/P11/P19 + exempel-slots
├── src/
│   ├── app/
│   │   ├── [locale]/             sv default, /en/* för engelska
│   │   │   ├── page.tsx          Publikt bokningsformulär
│   │   │   ├── booking/[number]/ Bekräftelsesida
│   │   │   └── admin/            Login + dashboard + detaljvy
│   │   └── api/
│   │       ├── bookings/         POST: skapa bokning + skicka mejl
│   │       ├── admin/export/     GET: CSV-faktureringsunderlag
│   │       └── auth/[...nextauth]/
│   ├── components/               BookingForm, Header, LanguageSwitcher, StatusBadge
│   ├── lib/                      prisma, money, mailjet, bookingNumber, orgNumber, validation
│   ├── i18n/                     next-intl routing + request config
│   ├── auth.ts                   Auth.js v5 setup
│   └── middleware.ts             Locale-routing
├── messages/{sv,en}.json         Översättningar (UI + e-post)
├── docker/entrypoint.sh          Kör prisma db push + ev. seed vid start
├── Dockerfile                    Multi-stage, Next standalone, non-root
├── docker-compose.yml            Volym för SQLite
└── .env.example

Datamodell snabb översikt

  • Adminemail, passwordHash (bcrypt), name. Krediter för login.
  • Productsku, lokaliserade namn/beskrivningar, priceOre (öre, inte SEK), vatBp (basis-punkter), active, sortOrder.
  • PickupSlotlabelSv/labelEn, startsAt/endsAt, capacity, active.
  • Booking — bokningsnr, status, kontaktuppgifter, organisationsuppgifter, fakturaadress, snapshot av subtotal/vat/total, locale, valfri pickupSlotId.
  • BookingItem — snapshot av sku/nameSv/nameEn/unitPriceOre/vatBp/quantity/lineTotalOre. Frysta vid bokningstillfället så priser kan ändras i Product utan att gamla bokningar påverkas.

Bokningsstatus

PENDINGCONFIRMEDDELIVEREDINVOICED (eller CANCELLED när som helst). Lagras som sträng eftersom SQLite inte stöder enums.

Priser i öre

All prismatte sker i heltal (öre). Konvertering till SEK vid presentation eller export via src/lib/money.ts. Undviker float-drift.

Produktion deploy med Docker

# 1. Skapa .env för produktion (på servern)
cp .env.example .env
# - Sätt AUTH_SECRET (openssl rand -base64 32)
# - Sätt MAILJET_API_KEY / SECRET / MAIL_FROM_EMAIL
# - Sätt AUTH_URL och NEXT_PUBLIC_SITE_URL till din domän (https://...)
# - Sätt SEED_ADMIN_EMAIL / PASSWORD för första admin

# 2. Bygg och starta
docker compose build
RUN_SEED=true docker compose up -d    # första gången
# Vid omstarter: docker compose up -d  (RUN_SEED är false som default)

# 3. Logga in
# https://din-domän/admin/login

Bakom reverse proxy

docker-compose.yml exponerar port 3000 endast på 127.0.0.1 som standard. Om du kör Caddy/Traefik/nginx som separat stack:

  1. Ta bort ports: i docker-compose.yml
  2. Anslut containern till proxyns Docker-nätverk med networks: i compose-filen
  3. Pekande proxy → boka-gasol247:3000

Sätt AUTH_TRUST_HOST=true (redan satt) så Auth.js godkänner proxyns host-header.

SQLite-volym & backup

Databasen ligger i Docker-volymen app-data (monterad som /app/data/app.db).

# Backup
docker run --rm \
  -v boka.gasol247_app-data:/data \
  -v "$PWD":/out alpine \
  cp /data/app.db /out/app.backup-$(date +%F).db

# Restore (stoppa appen först)
docker compose down
docker run --rm \
  -v boka.gasol247_app-data:/data \
  -v "$PWD":/in alpine \
  cp /in/app.backup-2026-05-22.db /data/app.db
docker compose up -d

Kör backup minst dagligen vid skarp användning.

Konfiguration miljövariabler

Variabel Krav Beskrivning
DATABASE_URL T.ex. file:./data/app.db lokalt, file:/app/data/app.db i Docker
AUTH_SECRET openssl rand -base64 32
AUTH_URL T.ex. https://boka.gasol247.com i produktion
AUTH_TRUST_HOST true bakom proxy
MAILJET_API_KEY ⚠️ Krävs för att skicka mejl. Utan: bokning skapas, mejl skippas
MAILJET_API_SECRET ⚠️ Som ovan
MAIL_FROM_EMAIL ⚠️ Verifierad avsändaradress i Mailjet
MAIL_FROM_NAME Visningsnamn
NEXT_PUBLIC_SITE_URL För absoluta länkar i framtida e-post
NEXT_PUBLIC_EVENT_NAME Visas i header och e-postmall
SEED_ADMIN_EMAIL/PASSWORD/NAME Bara för npm run db:seed / RUN_SEED=true

Aktuell status och kända begränsningar

Verifierat 2026-05-22: next build grönt, prisma db push skapar schemat, seed körs, GET /sv|/en|/admin/login 200, POST /api/bookings validerar och skapar bokning.

  • Inga migrationer — vi kör prisma db push istället för prisma migrate dev. Bra för MVP, men byt till migrate dev när schemat är stabilt så historiken finns versionerad.
  • Mailjet-mall är inline HTML i src/lib/mailjet.ts. Funkar och är responsiv, men för att kunna ändra design utan deploy bör vi flytta till Mailjet Template-ID.
  • Ingen lagerbegränsning — boka.gasol247 förhindrar inte överbokning av en produkt. Kapacitet hanteras bara per upphämtnings-slot.
  • Ingen kund-avbokning — admin kan markera CANCELLED men kunden själv har ingen länk i mejlet.
  • Admin-tabellen finns men det finns inget UI för att skapa nya admins. Lägg till manuellt via Prisma Studio eller kör seed med andra SEED_ADMIN_*-variabler.
  • Produkter & slots redigeras genom seed-filen — inget admin-UI för det än.
  • Ingen rate-limit eller CAPTCHA på publika POST /api/bookings. Lägg till om eventet annonseras brett.
  • Prisma VS Code-tillägget visar Prisma 7-varningar för datasource.url — kosmetiskt; projektet kör Prisma 5.22 där det fortfarande är obligatoriskt.

Backlog och prioriterade förbättringar finns i BACKLOG.md.

Felsökning

"Cannot find module './globals.css'" — Cleared via src/types/css.d.ts. Om felet kommer tillbaka, kontrollera att filen finns kvar.

Mejl skickas inte — Kolla logs: docker compose logs app | grep mailjet. Vanliga orsaker: avsändaradress inte verifierad i Mailjet, fel API-key, eller MAILJET_API_KEY saknas (då skippas sändningen tyst och loggas).

Bokning skapas inte — Kolla validation: POST /api/bookings returnerar {error: "validation", issues: {...}} med fältnivå-detaljer. Org.nummer måste passera Luhn-check.

Admin kan inte logga in — Kör npm run db:seed igen, eller skapa admin manuellt via Prisma Studio. Lösenord bcrypt-hashas i seed-scriptet.

Licens

Privat — för intern användning på Jamboree-eventet.

Description
Bokningsplattform för Jamboree-lägret
Readme 822 KiB
Languages
TypeScript 97.6%
Dockerfile 1%
CSS 0.8%
Shell 0.3%
JavaScript 0.3%