Legt obligatorisk adress

X-Lovable-Edit-ID: edt-26169ea6-0c33-440a-a5e3-2aacf6f1d0cc
Co-authored-by: wolfoftyreso-debug <250630591+wolfoftyreso-debug@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-07-11 13:57:56 +00:00
3 changed files with 83 additions and 34 deletions
+32 -8
View File
@@ -24,13 +24,13 @@ const formSchema = z.object({
företag: z.string().trim().min(1, "Fyll i företagsnamn").max(100), företag: z.string().trim().min(1, "Fyll i företagsnamn").max(100),
epost: z.string().trim().email("Ange en giltig e-postadress").max(255), epost: z.string().trim().email("Ange en giltig e-postadress").max(255),
telefon: z.string().trim().max(30).optional(), telefon: z.string().trim().max(30).optional(),
adress: z.string().trim().max(200).optional(), adress: z.string().trim().min(1, "Fyll i leveransadress").max(200),
postnummer: z postnummer: z
.string() .string()
.trim() .trim()
.min(1, "Fyll i postnummer") .min(1, "Fyll i postnummer")
.refine(isStockholmPostalCode, "Vi levererar endast inom Storstockholm (100 00 199 99)"), .refine(isStockholmPostalCode, "Vi levererar endast inom Storstockholm (100 00 199 99)"),
stad: z.string().trim().max(100).optional(), stad: z.string().trim().min(1, "Fyll i stad").max(100),
kommentarer: z.string().trim().max(1000).optional(), kommentarer: z.string().trim().max(1000).optional(),
}); });
@@ -67,7 +67,7 @@ const PlansSection = () => {
(async () => { (async () => {
const { data } = await supabase const { data } = await supabase
.from("profiles") .from("profiles")
.select("company_name, phone, street_address, postal_code") .select("company_name, phone, street_address, postal_code, city")
.eq("user_id", user.id) .eq("user_id", user.id)
.maybeSingle(); .maybeSingle();
setForm((prev) => ({ setForm((prev) => ({
@@ -77,6 +77,7 @@ const PlansSection = () => {
telefon: prev.telefon || data?.phone || "", telefon: prev.telefon || data?.phone || "",
adress: prev.adress || data?.street_address || "", adress: prev.adress || data?.street_address || "",
postnummer: prev.postnummer || data?.postal_code || "", postnummer: prev.postnummer || data?.postal_code || "",
stad: prev.stad || data?.city || "",
})); }));
})(); })();
}, [user, showForm]); }, [user, showForm]);
@@ -118,9 +119,9 @@ const PlansSection = () => {
company: result.data.företag, company: result.data.företag,
email: result.data.epost, email: result.data.epost,
phone: result.data.telefon ?? "", phone: result.data.telefon ?? "",
address: result.data.adress ?? "", address: result.data.adress,
postal_code: result.data.postnummer, postal_code: result.data.postnummer,
city: "Storstockholm", city: result.data.stad,
comments: result.data.kommentarer ?? "", comments: result.data.kommentarer ?? "",
return_url: `${window.location.origin}/order-confirmation?session_id={CHECKOUT_SESSION_ID}&method=card&kg=${recommendedKg}&email=${encodeURIComponent(result.data.epost)}`, return_url: `${window.location.origin}/order-confirmation?session_id={CHECKOUT_SESSION_ID}&method=card&kg=${recommendedKg}&email=${encodeURIComponent(result.data.epost)}`,
environment: getStripeEnvironment(), environment: getStripeEnvironment(),
@@ -371,16 +372,23 @@ const PlansSection = () => {
</div> </div>
<div> <div>
<label className="block mb-1.5 text-sm font-semibold">Leveransadress</label> <label className="block mb-1.5 text-sm font-semibold">
Leveransadress <span className="text-primary">*</span>
</label>
<input <input
name="adress" name="adress"
placeholder="Gatuadress, t.ex. Kungsgatan 12"
value={form.adress} value={form.adress}
onChange={handleChange} onChange={handleChange}
aria-invalid={!!errors.adress}
className="w-full px-4 py-3 rounded-sm bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary/30" className="w-full px-4 py-3 rounded-sm bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary/30"
/> />
{errors.adress && (
<p className="text-destructive text-xs mt-1">{errors.adress}</p>
)}
</div> </div>
<div> <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div> <div>
<label className="block mb-1.5 text-sm font-semibold"> <label className="block mb-1.5 text-sm font-semibold">
Postnummer <span className="text-primary">*</span> Postnummer <span className="text-primary">*</span>
@@ -397,9 +405,25 @@ const PlansSection = () => {
<p className="text-destructive text-xs mt-1">{errors.postnummer}</p> <p className="text-destructive text-xs mt-1">{errors.postnummer}</p>
)} )}
<p className="text-muted-foreground text-xs mt-1"> <p className="text-muted-foreground text-xs mt-1">
Beställningen kan endast fortsätta för postnummer inom Storstockholm. Endast postnummer inom Storstockholm.
</p> </p>
</div> </div>
<div>
<label className="block mb-1.5 text-sm font-semibold">
Stad <span className="text-primary">*</span>
</label>
<input
name="stad"
placeholder="t.ex. Stockholm"
value={form.stad}
onChange={handleChange}
aria-invalid={!!errors.stad}
className="w-full px-4 py-3 rounded-sm bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary/30"
/>
{errors.stad && (
<p className="text-destructive text-xs mt-1">{errors.stad}</p>
)}
</div>
</div> </div>
<div> <div>
+48 -23
View File
@@ -93,6 +93,7 @@ export function OrdersTable() {
<TableRow> <TableRow>
<TableHead>Ordernummer</TableHead> <TableHead>Ordernummer</TableHead>
<TableHead>Kund</TableHead> <TableHead>Kund</TableHead>
<TableHead>Leveransadress</TableHead>
<TableHead>Datum</TableHead> <TableHead>Datum</TableHead>
<TableHead>Status</TableHead> <TableHead>Status</TableHead>
<TableHead className="text-right">Belopp</TableHead> <TableHead className="text-right">Belopp</TableHead>
@@ -101,33 +102,57 @@ export function OrdersTable() {
<TableBody> <TableBody>
{filteredOrders?.length === 0 ? ( {filteredOrders?.length === 0 ? (
<TableRow> <TableRow>
<TableCell colSpan={5} className="text-center py-8 text-muted-foreground"> <TableCell colSpan={6} className="text-center py-8 text-muted-foreground">
Inga ordrar hittades Inga ordrar hittades
</TableCell> </TableCell>
</TableRow> </TableRow>
) : ( ) : (
filteredOrders?.map((order) => ( filteredOrders?.map((order) => {
<TableRow key={order.id}> const item = Array.isArray(order.items) ? order.items[0] : null;
<TableCell className="font-medium">{order.order_number}</TableCell> const lev = item?.leverans;
<TableCell> const phone = item?.telefon;
<div> const comments = item?.kommentarer;
<div className="font-medium">{order.customer_name || "—"}</div> return (
<div className="text-sm text-muted-foreground">{order.customer_email}</div> <TableRow key={order.id}>
</div> <TableCell className="font-medium align-top">{order.order_number}</TableCell>
</TableCell> <TableCell className="align-top">
<TableCell> <div>
{format(new Date(order.created_at), "d MMM yyyy, HH:mm", { locale: sv })} <div className="font-medium">{order.customer_name || "—"}</div>
</TableCell> <div className="text-sm text-muted-foreground">{order.customer_email}</div>
<TableCell> {phone && <div className="text-sm text-muted-foreground">{phone}</div>}
<Badge variant="outline" className={statusColors[order.status] || ""}> </div>
{statusLabels[order.status] || order.status} </TableCell>
</Badge> <TableCell className="align-top">
</TableCell> {lev ? (
<TableCell className="text-right font-medium"> <div className="text-sm">
{Number(order.total_amount).toLocaleString("sv-SE")} {order.currency} <div>{lev.adress || "—"}</div>
</TableCell> <div className="text-muted-foreground">
</TableRow> {[lev.postnummer, lev.stad].filter(Boolean).join(" ")}
)) </div>
{comments && (
<div className="text-xs text-muted-foreground mt-1 italic">
"{comments}"
</div>
)}
</div>
) : (
<span className="text-muted-foreground text-sm"></span>
)}
</TableCell>
<TableCell className="align-top">
{format(new Date(order.created_at), "d MMM yyyy, HH:mm", { locale: sv })}
</TableCell>
<TableCell className="align-top">
<Badge variant="outline" className={statusColors[order.status] || ""}>
{statusLabels[order.status] || order.status}
</Badge>
</TableCell>
<TableCell className="text-right font-medium align-top">
{Number(order.total_amount).toLocaleString("sv-SE")} {order.currency}
</TableCell>
</TableRow>
);
})
)} )}
</TableBody> </TableBody>
</Table> </Table>
+3 -3
View File
@@ -56,7 +56,7 @@ async function insertOrder(row: {
leverans: { leverans: {
adress: b.address, adress: b.address,
postnummer: cleaned, postnummer: cleaned,
stad: "Storstockholm", stad: b.city,
}, },
telefon: b.phone, telefon: b.phone,
kommentarer: b.comments, kommentarer: b.comments,
@@ -100,9 +100,9 @@ const BodySchema = z.object({
company: z.string().trim().min(1).max(200), company: z.string().trim().min(1).max(200),
email: z.string().trim().email().max(255), email: z.string().trim().email().max(255),
phone: z.string().trim().max(30).optional().default(""), phone: z.string().trim().max(30).optional().default(""),
address: z.string().trim().max(200).optional().default(""), address: z.string().trim().min(1).max(200),
postal_code: z.string().trim().min(1).max(20), postal_code: z.string().trim().min(1).max(20),
city: z.string().trim().max(100).optional().default(""), city: z.string().trim().min(1).max(100),
comments: z.string().trim().max(1000).optional().default(""), comments: z.string().trim().max(1000).optional().default(""),
return_url: z.string().url(), return_url: z.string().url(),
environment: z.enum(["sandbox", "live"]), environment: z.enum(["sandbox", "live"]),