301c477e25
Main är sedan den här grenen skapades en helt annan produkt — Semantika,
en mobilapp med egen CDK-infrastruktur. Den äger nu repots rot: en
npm-workspaces-monorepo med apps/mobile, services/api och infra.
För att båda ska rymmas i samma repo flyttar Guidad Felsökning in i en
egen katalog i stället för att göra anspråk på roten:
felsokning/app webbklienten (Vite, egen package.json och
eslint-/vitest-konfiguration)
felsokning/services plattformstjänsten och AI-orkestern
felsokning/infra Terraform och databasschemat
felsokning/docs vision, moduler, drift
felsokning/supabase edge-funktion och migrationer
Merge:n hade tagit bort 128 filer som Guidad Felsökning bygger på —
värdapplikationens komponenter, Supabase-klienten, tillgångar — eftersom
main raderat dem och den här grenen inte råkat ändra just dem. De är
återställda på sin nya plats. Utan dem gick varken bygget eller
testerna: ai.ts och synk.ts importerar Supabase-klienten.
Semantikas rotfiler är orörda: package.json, eslint.config.js och
.github/workflows/ är deras. Guidad Felsökning har egna motsvarigheter i
sin katalog.
CI flyttar samtidigt från GitHub Actions till .gitea/workflows — samma
syntax, egna runners. .github/workflows/ tillhör Semantika härefter.
Verifierat på den nya platsen: 96 vitest-tester, typkontroll, eslint på
både klient och tjänster, bygge, och integrationstest mot riktig Postgres.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
102 lines
3.1 KiB
TypeScript
102 lines
3.1 KiB
TypeScript
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.0";
|
||
import { corsHeaders } from "npm:@supabase/supabase-js@2/cors";
|
||
import { z } from "https://esm.sh/zod@3.23.8";
|
||
|
||
const BodySchema = z.object({
|
||
företag: z.string().trim().min(1).max(100),
|
||
epost: z.string().trim().email().max(255),
|
||
telefon: z.string().trim().max(30).optional().default(""),
|
||
adress: z.string().trim().max(200).optional().default(""),
|
||
postnummer: z
|
||
.string()
|
||
.trim()
|
||
.regex(/^\s*\d{3}\s*\d{2}\s*$/, "Ogiltigt postnummer")
|
||
.refine((v) => {
|
||
const n = parseInt(v.replace(/\s/g, ""), 10);
|
||
return n >= 10000 && n <= 19999;
|
||
}, "Vi levererar endast inom Storstockholm (100 00 – 199 99)"),
|
||
stad: z.string().trim().max(100).optional().default(""),
|
||
kommentarer: z.string().trim().max(1000).optional().default(""),
|
||
employees: z.number().int().min(1).max(1000),
|
||
kg_per_vecka: z.number().min(1).max(500),
|
||
pris_per_vecka: z.number().min(0).max(1000000),
|
||
});
|
||
|
||
Deno.serve(async (req) => {
|
||
if (req.method === "OPTIONS") {
|
||
return new Response("ok", { headers: corsHeaders });
|
||
}
|
||
|
||
try {
|
||
const parsed = BodySchema.safeParse(await req.json());
|
||
if (!parsed.success) {
|
||
return new Response(
|
||
JSON.stringify({ error: parsed.error.flatten().fieldErrors }),
|
||
{
|
||
status: 400,
|
||
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||
}
|
||
);
|
||
}
|
||
const d = parsed.data;
|
||
|
||
const supabase = createClient(
|
||
Deno.env.get("SUPABASE_URL")!,
|
||
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
|
||
);
|
||
|
||
const orderNumber = `ABO-${Date.now().toString(36).toUpperCase()}`;
|
||
|
||
const { data, error } = await supabase
|
||
.from("orders")
|
||
.insert({
|
||
order_number: orderNumber,
|
||
customer_email: d.epost,
|
||
customer_name: d.företag,
|
||
items: [
|
||
{
|
||
typ: "Kakabonnemang",
|
||
antal_anställda: d.employees,
|
||
kg_per_vecka: d.kg_per_vecka,
|
||
pris_per_vecka_exkl_moms: d.pris_per_vecka,
|
||
valuta: "SEK",
|
||
leverans: {
|
||
adress: d.adress,
|
||
postnummer: d.postnummer,
|
||
stad: d.stad,
|
||
},
|
||
telefon: d.telefon,
|
||
kommentarer: d.kommentarer,
|
||
},
|
||
],
|
||
total_amount: d.pris_per_vecka,
|
||
currency: "SEK",
|
||
status: "pending",
|
||
})
|
||
.select("id, order_number")
|
||
.single();
|
||
|
||
if (error) {
|
||
console.error("Order insert failed:", error);
|
||
return new Response(
|
||
JSON.stringify({ error: "Kunde inte spara beställning", details: error.message }),
|
||
{
|
||
status: 500,
|
||
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||
}
|
||
);
|
||
}
|
||
|
||
return new Response(JSON.stringify({ ok: true, order: data }), {
|
||
status: 200,
|
||
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||
});
|
||
} catch (e) {
|
||
console.error("submit-order error:", e);
|
||
return new Response(JSON.stringify({ error: (e as Error).message }), {
|
||
status: 500,
|
||
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||
});
|
||
}
|
||
});
|