@flowselections/layout-maker 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist-lib/_core-safelist.d.ts +2 -0
- package/dist-lib/_core-safelist.d.ts.map +1 -0
- package/dist-lib/_core-safelist.js +18 -0
- package/dist-lib/components/AfdrukKnop.d.ts +13 -0
- package/dist-lib/components/AfdrukKnop.d.ts.map +1 -0
- package/dist-lib/components/AfdrukKnop.js +38 -0
- package/dist-lib/components/AiLayoutImportDialog.d.ts +6 -0
- package/dist-lib/components/AiLayoutImportDialog.d.ts.map +1 -0
- package/dist-lib/components/AiLayoutImportDialog.js +133 -0
- package/dist-lib/components/BlokOntwerperPage.d.ts +5 -0
- package/dist-lib/components/BlokOntwerperPage.d.ts.map +1 -0
- package/dist-lib/components/BlokOntwerperPage.js +67 -0
- package/dist-lib/components/DocumentSettingsPanel.d.ts +13 -0
- package/dist-lib/components/DocumentSettingsPanel.d.ts.map +1 -0
- package/dist-lib/components/DocumentSettingsPanel.js +65 -0
- package/dist-lib/components/FormaatOrientatieBar.d.ts +10 -0
- package/dist-lib/components/FormaatOrientatieBar.d.ts.map +1 -0
- package/dist-lib/components/FormaatOrientatieBar.js +51 -0
- package/dist-lib/components/LayoutsPage.d.ts +2 -0
- package/dist-lib/components/LayoutsPage.d.ts.map +1 -0
- package/dist-lib/components/LayoutsPage.js +196 -0
- package/dist-lib/components/LogoBibliotheekDialog.d.ts +7 -0
- package/dist-lib/components/LogoBibliotheekDialog.d.ts.map +1 -0
- package/dist-lib/components/LogoBibliotheekDialog.js +27 -0
- package/dist-lib/components/LogosPage.d.ts +7 -0
- package/dist-lib/components/LogosPage.d.ts.map +1 -0
- package/dist-lib/components/LogosPage.js +131 -0
- package/dist-lib/components/PaginaDocument.d.ts +22 -0
- package/dist-lib/components/PaginaDocument.d.ts.map +1 -0
- package/dist-lib/components/PaginaDocument.js +80 -0
- package/dist-lib/components/PakbonBuilderPage.d.ts +5 -0
- package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -0
- package/dist-lib/components/PakbonBuilderPage.js +69 -0
- package/dist-lib/components/PakbonPreview.d.ts +13 -0
- package/dist-lib/components/PakbonPreview.d.ts.map +1 -0
- package/dist-lib/components/PakbonPreview.js +423 -0
- package/dist-lib/components/PakbonSectiesPanel.d.ts +18 -0
- package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -0
- package/dist-lib/components/PakbonSectiesPanel.js +156 -0
- package/dist-lib/components/PreviewFit.d.ts +9 -0
- package/dist-lib/components/PreviewFit.d.ts.map +1 -0
- package/dist-lib/components/PreviewFit.js +38 -0
- package/dist-lib/components/PrintQueuePage.d.ts +2 -0
- package/dist-lib/components/PrintQueuePage.d.ts.map +1 -0
- package/dist-lib/components/PrintQueuePage.js +59 -0
- package/dist-lib/components/PrinterSetupWizard.d.ts +6 -0
- package/dist-lib/components/PrinterSetupWizard.d.ts.map +1 -0
- package/dist-lib/components/PrinterSetupWizard.js +119 -0
- package/dist-lib/components/PrintersPage.d.ts +2 -0
- package/dist-lib/components/PrintersPage.d.ts.map +1 -0
- package/dist-lib/components/PrintersPage.js +60 -0
- package/dist-lib/components/ProductiebonBlokkenPanel.d.ts +7 -0
- package/dist-lib/components/ProductiebonBlokkenPanel.d.ts.map +1 -0
- package/dist-lib/components/ProductiebonBlokkenPanel.js +85 -0
- package/dist-lib/components/ProductiebonBuilderPage.d.ts +5 -0
- package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -0
- package/dist-lib/components/ProductiebonBuilderPage.js +48 -0
- package/dist-lib/components/ProductiebonPreview.d.ts +12 -0
- package/dist-lib/components/ProductiebonPreview.d.ts.map +1 -0
- package/dist-lib/components/ProductiebonPreview.js +237 -0
- package/dist-lib/components/TemplateModuleLogo.d.ts +6 -0
- package/dist-lib/components/TemplateModuleLogo.d.ts.map +1 -0
- package/dist-lib/components/TemplateModuleLogo.js +4 -0
- package/dist-lib/components/settings/MijnModuleSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/MijnModuleSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/MijnModuleSettingsCard.js +42 -0
- package/dist-lib/hooks/useMijnModuleData.d.ts +13 -0
- package/dist-lib/hooks/useMijnModuleData.d.ts.map +1 -0
- package/dist-lib/hooks/useMijnModuleData.js +49 -0
- package/dist-lib/index.d.ts +38 -0
- package/dist-lib/index.d.ts.map +1 -0
- package/dist-lib/index.js +56 -0
- package/dist-lib/integrations/supabase/auth-attacher.d.ts +2 -0
- package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/auth-attacher.js +12 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +19847 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/auth-middleware.js +52 -0
- package/dist-lib/integrations/supabase/client.d.ts +2 -0
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.js +3 -0
- package/dist-lib/integrations/supabase/client.server.d.ts +19843 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.server.js +30 -0
- package/dist-lib/integrations/supabase/types.d.ts +20423 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/types.js +78 -0
- package/dist-lib/lib/aiLayout.functions.d.ts +19864 -0
- package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -0
- package/dist-lib/lib/aiLayout.functions.js +28 -0
- package/dist-lib/lib/aiLayout.server.d.ts +14 -0
- package/dist-lib/lib/aiLayout.server.d.ts.map +1 -0
- package/dist-lib/lib/aiLayout.server.js +197 -0
- package/dist-lib/lib/browserPrint.d.ts +38 -0
- package/dist-lib/lib/browserPrint.d.ts.map +1 -0
- package/dist-lib/lib/browserPrint.js +104 -0
- package/dist-lib/lib/documentProfiles.d.ts +50 -0
- package/dist-lib/lib/documentProfiles.d.ts.map +1 -0
- package/dist-lib/lib/documentProfiles.js +458 -0
- package/dist-lib/lib/documentTypes.d.ts +123 -0
- package/dist-lib/lib/documentTypes.d.ts.map +1 -0
- package/dist-lib/lib/documentTypes.js +74 -0
- package/dist-lib/lib/fieldRegistry.d.ts +20 -0
- package/dist-lib/lib/fieldRegistry.d.ts.map +1 -0
- package/dist-lib/lib/fieldRegistry.js +123 -0
- package/dist-lib/lib/formaatPrinters.d.ts +14 -0
- package/dist-lib/lib/formaatPrinters.d.ts.map +1 -0
- package/dist-lib/lib/formaatPrinters.js +39 -0
- package/dist-lib/lib/layoutPreview.d.ts +18 -0
- package/dist-lib/lib/layoutPreview.d.ts.map +1 -0
- package/dist-lib/lib/layoutPreview.js +134 -0
- package/dist-lib/lib/logoLibrary.d.ts +80 -0
- package/dist-lib/lib/logoLibrary.d.ts.map +1 -0
- package/dist-lib/lib/logoLibrary.js +264 -0
- package/dist-lib/lib/pakbonBuilder.d.ts +210 -0
- package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -0
- package/dist-lib/lib/pakbonBuilder.js +910 -0
- package/dist-lib/lib/pakbonHtml.d.ts +48 -0
- package/dist-lib/lib/pakbonHtml.d.ts.map +1 -0
- package/dist-lib/lib/pakbonHtml.js +754 -0
- package/dist-lib/lib/printApi.d.ts +45 -0
- package/dist-lib/lib/printApi.d.ts.map +1 -0
- package/dist-lib/lib/printApi.js +98 -0
- package/dist-lib/lib/productiebonBuilder.d.ts +137 -0
- package/dist-lib/lib/productiebonBuilder.d.ts.map +1 -0
- package/dist-lib/lib/productiebonBuilder.js +661 -0
- package/dist-lib/lib/supabaseClient.d.ts +4 -0
- package/dist-lib/lib/supabaseClient.d.ts.map +1 -0
- package/dist-lib/lib/supabaseClient.js +10 -0
- package/dist-lib/lib/useDocumentData.d.ts +97 -0
- package/dist-lib/lib/useDocumentData.d.ts.map +1 -0
- package/dist-lib/lib/useDocumentData.js +286 -0
- package/dist-lib/lib/useLayoutIdParam.d.ts +7 -0
- package/dist-lib/lib/useLayoutIdParam.d.ts.map +1 -0
- package/dist-lib/lib/useLayoutIdParam.js +10 -0
- package/dist-lib/lib/utils.d.ts +3 -0
- package/dist-lib/lib/utils.d.ts.map +1 -0
- package/dist-lib/lib/utils.js +5 -0
- package/dist-lib/lib/validationSchemas.d.ts +15 -0
- package/dist-lib/lib/validationSchemas.d.ts.map +1 -0
- package/dist-lib/lib/validationSchemas.js +25 -0
- package/dist-lib/styles.css +1 -0
- package/package.json +117 -0
- package/public/logo.png +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Layout maker (pakbonnen, verzendbrieven, ectra)
|
|
2
|
+
|
|
3
|
+
blank project start
|
|
4
|
+
|
|
5
|
+
This project was built with [Lovable](https://lovable.dev).
|
|
6
|
+
|
|
7
|
+
## Build with Lovable
|
|
8
|
+
|
|
9
|
+
Continue developing this project in the [Lovable editor](https://lovable.dev/projects/4735fccb-b385-4b48-b4f8-ef135f63f4f7).
|
|
10
|
+
|
|
11
|
+
- **Ship faster**: describe what you want to build and Lovable handles the code.
|
|
12
|
+
- **Stay in sync**: every change made in Lovable is committed straight to this repository.
|
|
13
|
+
- **Full ownership**: this code is yours. Push to `main` on GitHub and your changes sync back into Lovable, ready for your next prompt.
|
|
14
|
+
|
|
15
|
+
## Development
|
|
16
|
+
|
|
17
|
+
Prefer working locally? You need Node.js and npm — [install with nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
git clone <this-repository-url>
|
|
21
|
+
cd <repository-name>
|
|
22
|
+
npm i
|
|
23
|
+
npm run dev
|
|
24
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_core-safelist.d.ts","sourceRoot":"","sources":["../src/_core-safelist.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Forceert Tailwind classes die uit @flowselections/core komen maar niet
|
|
2
|
+
// voorkomen in de eigen broncode van deze module.
|
|
3
|
+
// Zonder dit bestand worden deze classes weggeoptimaliseerd op de
|
|
4
|
+
// Lovable productie build.
|
|
5
|
+
const _safelist = [
|
|
6
|
+
// Layout — Sidebar en shell structuur
|
|
7
|
+
'h-full', 'flex-col', 'flex-1', 'min-h-screen', 'ml-64', 'pt-16',
|
|
8
|
+
'items-start', 'items-center', 'justify-between', 'border-t',
|
|
9
|
+
'w-64', 'h-screen', 'h-20', 'overflow-y-auto',
|
|
10
|
+
// Grid — InstellingenPage
|
|
11
|
+
'grid', 'lg:grid-cols-2', 'gap-6', 'p-6', 'space-y-6',
|
|
12
|
+
// Toggle/Switch — aan/uit zichtbaarheid
|
|
13
|
+
'data-[state=checked]:bg-primary', 'data-[state=unchecked]:bg-input',
|
|
14
|
+
// Layout-maker — ontwerpvlak en tabellen
|
|
15
|
+
'lg:grid-cols-[260px_1fr_300px]', 'lg:grid-cols-[240px_1fr_320px]', 'lg:grid-cols-[1fr_360px]', 'md:grid-cols-4', 'grid-cols-4', 'md:col-span-2', 'border-dashed', 'border-primary',
|
|
16
|
+
'bg-primary/10', 'bg-muted/30', 'bg-muted/50', 'shadow-sm', 'overflow-auto', 'w-10',
|
|
17
|
+
];
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Orientatie } from "../lib/browserPrint";
|
|
2
|
+
export interface AfdrukKnopProps {
|
|
3
|
+
documenttype: string;
|
|
4
|
+
formaatCode: string | null;
|
|
5
|
+
orientatie: Orientatie;
|
|
6
|
+
/** Levert de HTML van het document aan op het moment van afdrukken. */
|
|
7
|
+
getHtml: () => string;
|
|
8
|
+
css?: string;
|
|
9
|
+
documentnaam?: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function AfdrukKnop({ documenttype, formaatCode, orientatie, getHtml, css, documentnaam, label, }: AfdrukKnopProps): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=AfdrukKnop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AfdrukKnop.d.ts","sourceRoot":"","sources":["../../src/components/AfdrukKnop.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAU,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE9D,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,uEAAuE;IACvE,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,GAAG,EACH,YAAY,EACZ,KAAmB,GACpB,EAAE,eAAe,+BAgCjB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/AfdrukKnop.tsx — afdrukken met automatisch ingesteld
|
|
4
|
+
// papierformaat, stand en de bedoelde printer.
|
|
5
|
+
// ============================================================================
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { Button, toast } from "@flowselections/core";
|
|
8
|
+
import { Loader2, Printer } from "lucide-react";
|
|
9
|
+
import { drukAf } from "../lib/browserPrint";
|
|
10
|
+
export function AfdrukKnop({ documenttype, formaatCode, orientatie, getHtml, css, documentnaam, label = "Afdrukken", }) {
|
|
11
|
+
const [bezig, setBezig] = useState(false);
|
|
12
|
+
const afdrukken = async () => {
|
|
13
|
+
setBezig(true);
|
|
14
|
+
try {
|
|
15
|
+
const { printer } = await drukAf({
|
|
16
|
+
documenttype,
|
|
17
|
+
formaatCode,
|
|
18
|
+
orientatie,
|
|
19
|
+
html: getHtml(),
|
|
20
|
+
css,
|
|
21
|
+
documentnaam,
|
|
22
|
+
});
|
|
23
|
+
if (printer) {
|
|
24
|
+
toast.info(`Kies in het printvenster: ${printer.systeemnaam ?? printer.naam}`);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
toast.info("Er is nog geen vaste printer ingesteld voor dit document.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
toast.error("Afdrukken mislukt");
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
setBezig(false);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
return (_jsxs(Button, { variant: "outline", disabled: bezig, onClick: () => void afdrukken(), children: [bezig ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(Printer, { className: "mr-2 h-4 w-4" }), label] }));
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AiLayoutImportDialog.d.ts","sourceRoot":"","sources":["../../src/components/AiLayoutImportDialog.tsx"],"names":[],"mappings":"AAiCA,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,YAAY,EACZ,OAAO,GACR,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,+BAwMA"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
4
|
+
import { Badge, Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, toast, } from "@flowselections/core";
|
|
5
|
+
import { Sparkles, Upload } from "lucide-react";
|
|
6
|
+
import { analyseerDocumentLayout } from "../lib/aiLayout.functions";
|
|
7
|
+
import { supabase } from "../lib/supabaseClient";
|
|
8
|
+
import { useLogos } from "../lib/logoLibrary";
|
|
9
|
+
import { documentProfiel, standaardConfig } from "../lib/documentProfiles";
|
|
10
|
+
export function AiLayoutImportDialog({ open, onOpenChange, onKlaar, }) {
|
|
11
|
+
const navigate = useNavigate();
|
|
12
|
+
const { logos } = useLogos();
|
|
13
|
+
const [nieuweLogos, setNieuweLogos] = useState([]);
|
|
14
|
+
const [bezig, setBezig] = useState(false);
|
|
15
|
+
const [bestandsnaam, setBestandsnaam] = useState("");
|
|
16
|
+
const [voorstel, setVoorstel] = useState(null);
|
|
17
|
+
const kiesBestand = async (file) => {
|
|
18
|
+
if (file.size > 8 * 1024 * 1024) {
|
|
19
|
+
toast.error("Bestand is te groot (max 8 MB)");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
23
|
+
const reader = new FileReader();
|
|
24
|
+
reader.onload = () => resolve(String(reader.result));
|
|
25
|
+
reader.onerror = () => reject(new Error("Lezen mislukt"));
|
|
26
|
+
reader.readAsDataURL(file);
|
|
27
|
+
});
|
|
28
|
+
setBestandsnaam(file.name);
|
|
29
|
+
setBezig(true);
|
|
30
|
+
try {
|
|
31
|
+
const res = await analyseerDocumentLayout({
|
|
32
|
+
data: {
|
|
33
|
+
bestandsnaam: file.name,
|
|
34
|
+
mimeType: file.type || "application/pdf",
|
|
35
|
+
dataUrl,
|
|
36
|
+
bekendeLogos: logos.map((l) => l.naam),
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
toast.error(res.fout);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const v = res.voorstel;
|
|
44
|
+
const onbekend = [];
|
|
45
|
+
v.elementen = v.elementen.map((e) => {
|
|
46
|
+
if (e.type !== "logo")
|
|
47
|
+
return e;
|
|
48
|
+
const treffer = logos.find((l) => l.naam.toLowerCase() === (e.content ?? "").trim().toLowerCase());
|
|
49
|
+
if (treffer)
|
|
50
|
+
return { ...e, logoId: treffer.id, config: { ...e.config, bronUrl: treffer.viewUrl ?? "" } };
|
|
51
|
+
if (e.content)
|
|
52
|
+
onbekend.push(e.content);
|
|
53
|
+
return e;
|
|
54
|
+
});
|
|
55
|
+
setNieuweLogos(onbekend);
|
|
56
|
+
setVoorstel(v);
|
|
57
|
+
toast.success("Analyse gereed — controleer het voorstel");
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
toast.error("AI-analyse mislukt");
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
setBezig(false);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const bevestig = async () => {
|
|
67
|
+
if (!voorstel)
|
|
68
|
+
return;
|
|
69
|
+
setBezig(true);
|
|
70
|
+
try {
|
|
71
|
+
const { data, error } = await supabase
|
|
72
|
+
.from("document_layouts")
|
|
73
|
+
.insert({
|
|
74
|
+
naam: voorstel.naam,
|
|
75
|
+
documenttype: voorstel.documenttype,
|
|
76
|
+
omschrijving: `Automatisch herkend uit ${bestandsnaam}`,
|
|
77
|
+
papierformaat: voorstel.papierformaat,
|
|
78
|
+
breedte_mm: voorstel.breedte_mm,
|
|
79
|
+
hoogte_mm: voorstel.hoogte_mm,
|
|
80
|
+
orientatie: voorstel.orientatie,
|
|
81
|
+
config: standaardConfig(documentProfiel(voorstel.documenttype)),
|
|
82
|
+
})
|
|
83
|
+
.select("id")
|
|
84
|
+
.single();
|
|
85
|
+
if (error)
|
|
86
|
+
throw error;
|
|
87
|
+
const layoutId = data.id;
|
|
88
|
+
if (voorstel.elementen.length) {
|
|
89
|
+
await supabase.from("document_layout_elements").insert(voorstel.elementen.map((e, i) => ({
|
|
90
|
+
layout_id: layoutId,
|
|
91
|
+
element_key: e.key,
|
|
92
|
+
type: e.type,
|
|
93
|
+
volgorde: i,
|
|
94
|
+
x: e.x,
|
|
95
|
+
y: e.y,
|
|
96
|
+
breedte: e.breedte,
|
|
97
|
+
hoogte: e.hoogte,
|
|
98
|
+
rotatie: 0,
|
|
99
|
+
z_index: i,
|
|
100
|
+
content: e.content,
|
|
101
|
+
logo_id: e.logoId ?? null,
|
|
102
|
+
stijl: e.stijl,
|
|
103
|
+
config: e.config,
|
|
104
|
+
})));
|
|
105
|
+
}
|
|
106
|
+
await supabase.from("ai_layout_imports").insert({
|
|
107
|
+
bestandsnaam,
|
|
108
|
+
mime_type: "onbekend",
|
|
109
|
+
status: "klaar",
|
|
110
|
+
voorstel: voorstel,
|
|
111
|
+
onzekere_velden: voorstel.onzekere_velden,
|
|
112
|
+
bevestigd: true,
|
|
113
|
+
layout_id: layoutId,
|
|
114
|
+
});
|
|
115
|
+
toast.success("Layout aangemaakt uit document");
|
|
116
|
+
onOpenChange(false);
|
|
117
|
+
setVoorstel(null);
|
|
118
|
+
onKlaar();
|
|
119
|
+
void navigate({ to: "/layouts-printers/pakbon/$layoutId", params: { layoutId } });
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
toast.error("Opslaan mislukt");
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
setBezig(false);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(Sparkles, { className: "h-4 w-4" }), " AI-layoutgenerator"] }), _jsx(DialogDescription, { children: "Upload een bestaande PDF of foto van een document. De AI herkent tekst, tabellen, logo's en barcodes en zet die om naar een bewerkbare layout met veldcodes." })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "ai-bestand", children: "Document" }), _jsx(Input, { id: "ai-bestand", type: "file", accept: "application/pdf,image/png,image/jpeg,image/webp", disabled: bezig, onChange: (e) => {
|
|
129
|
+
const f = e.target.files?.[0];
|
|
130
|
+
if (f)
|
|
131
|
+
void kiesBestand(f);
|
|
132
|
+
} }), bezig && _jsx("p", { className: "text-sm text-muted-foreground", children: "Document wordt geanalyseerd\u2026" })] }), voorstel && (_jsxs("div", { className: "space-y-3 rounded-md border p-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "font-medium", children: voorstel.naam }), _jsxs(Badge, { variant: "secondary", children: [voorstel.elementen.length, " elementen"] })] }), _jsxs("p", { className: "text-sm text-muted-foreground", children: [voorstel.papierformaat, " \u00B7 ", voorstel.breedte_mm, " \u00D7 ", voorstel.hoogte_mm, " mm \u00B7", " ", voorstel.orientatie === "portrait" ? "staand" : "liggend"] }), nieuweLogos.length > 0 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Nieuwe logo's \u2014 nog niet in de bibliotheek" }), _jsx("div", { className: "flex flex-wrap gap-1", children: nieuweLogos.map((n) => (_jsx(Badge, { variant: "outline", children: n }, n))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Voeg deze toe via Logo's en koppel ze daarna in de designer. Herkende bestaande logo's zijn al automatisch gekoppeld." })] })), voorstel.onzekere_velden.length > 0 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Onzekere velden \u2014 controleer deze na import" }), _jsx("div", { className: "flex flex-wrap gap-1", children: voorstel.onzekere_velden.map((v) => (_jsx(Badge, { variant: "outline", children: v }, v))) })] }))] }))] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsxs(Button, { onClick: bevestig, disabled: !voorstel || bezig, children: [_jsx(Upload, { className: "mr-2 h-4 w-4" }), " Layout aanmaken"] })] })] }) }));
|
|
133
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlokOntwerperPage.d.ts","sourceRoot":"","sources":["../../src/components/BlokOntwerperPage.tsx"],"names":[],"mappings":"AAiCA,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,+BA6LvF"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { Link } from "@tanstack/react-router";
|
|
4
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, toast, } from "@flowselections/core";
|
|
5
|
+
import { ArrowLeft, Plus, Save } from "lucide-react";
|
|
6
|
+
import { PakbonSectieEditor, PakbonBlokKaderEditor } from "./PakbonSectiesPanel";
|
|
7
|
+
import { PakbonSectiePreview } from "./PakbonPreview";
|
|
8
|
+
import { SECTIE_REGISTER, leesPakbonConfig, nieuweSectie, sectieDef, } from "../lib/pakbonBuilder";
|
|
9
|
+
import { updateLayout, useLayoutDetail } from "../lib/useDocumentData";
|
|
10
|
+
import { useLayoutIdParam } from "../lib/useLayoutIdParam";
|
|
11
|
+
/** Ontwerp elk pakbon-blok afzonderlijk, los van de volledige pagina. */
|
|
12
|
+
export function BlokOntwerperPage({ layoutId: layoutIdProp } = {}) {
|
|
13
|
+
const layoutId = useLayoutIdParam(layoutIdProp);
|
|
14
|
+
const { layout, loading } = useLayoutDetail(layoutId ?? "");
|
|
15
|
+
const [config, setConfig] = useState(null);
|
|
16
|
+
const [actief, setActief] = useState(null);
|
|
17
|
+
const [toevoegen, setToevoegen] = useState("orderregels");
|
|
18
|
+
const [zoom, setZoom] = useState(1.4);
|
|
19
|
+
const [bezig, setBezig] = useState(false);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!layout)
|
|
22
|
+
return;
|
|
23
|
+
const c = leesPakbonConfig(layout.config);
|
|
24
|
+
setConfig(c);
|
|
25
|
+
setActief((huidig) => huidig ?? c.secties[0]?.id ?? null);
|
|
26
|
+
}, [layout?.id]);
|
|
27
|
+
const secties = useMemo(() => (Array.isArray(config?.secties) ? config.secties : []), [config]);
|
|
28
|
+
const sectie = secties.find((s) => s.id === actief) ?? null;
|
|
29
|
+
const patch = (p) => {
|
|
30
|
+
if (!config || !sectie)
|
|
31
|
+
return;
|
|
32
|
+
setConfig({
|
|
33
|
+
...config,
|
|
34
|
+
secties: secties.map((s) => (s.id === sectie.id ? { ...s, ...p } : s)),
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
const opslaan = async () => {
|
|
38
|
+
if (!layout || !config)
|
|
39
|
+
return;
|
|
40
|
+
setBezig(true);
|
|
41
|
+
try {
|
|
42
|
+
await updateLayout(layout.id, { config: { ...(layout.config ?? {}), pakbon: config } });
|
|
43
|
+
toast.success("Blokontwerp opgeslagen");
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
toast.error("Opslaan mislukt");
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
setBezig(false);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
if (!layoutId)
|
|
53
|
+
return (_jsxs("main", { className: "p-6 text-sm text-muted-foreground", children: ["Kies eerst een layout via ", _jsx(Link, { to: "/layouts-printers", className: "underline", children: "Layouts" }), "."] }));
|
|
54
|
+
if (loading)
|
|
55
|
+
return _jsx("main", { className: "p-6 text-sm text-muted-foreground", children: "Layout laden\u2026" });
|
|
56
|
+
if (!layout || !config)
|
|
57
|
+
return _jsx("main", { className: "p-6 text-sm text-muted-foreground", children: "Layout niet gevonden." });
|
|
58
|
+
return (_jsxs("main", { className: "space-y-4 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsxs(Link, { to: "/layouts-printers/pakbon/$layoutId", params: { layoutId }, children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Pakbonopbouw"] }) }), _jsxs("div", { children: [_jsx("h1", { className: "text-lg font-semibold", children: "Blokken ontwerpen" }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [layout.naam, " \u00B7 elk blok afzonderlijk vormgeven"] })] })] }), _jsxs(Button, { size: "sm", onClick: opslaan, disabled: bezig, children: [_jsx(Save, { className: "mr-2 h-4 w-4" }), " Opslaan"] })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-[240px_1fr_360px]", children: [_jsxs(Card, { className: "h-fit", children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Blokken" }), _jsx(CardDescription, { children: "Kies welk blok je wilt ontwerpen." })] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [secties.map((s) => (_jsxs("button", { type: "button", onClick: () => setActief(s.id), className: `flex w-full items-center justify-between rounded-md border px-2 py-1.5 text-left text-sm ${s.id === actief ? "border-primary bg-accent" : "hover:bg-accent/50"}`, children: [_jsx("span", { className: "truncate", children: s.titel }), !s.zichtbaar && _jsx(Badge, { variant: "outline", children: "verborgen" })] }, s.id))), secties.length === 0 && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen blokken." }))] }), _jsx(Separator, {}), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Select, { value: toevoegen, onValueChange: (v) => setToevoegen(v), children: [_jsx(SelectTrigger, { className: "h-8", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: SECTIE_REGISTER.map((d) => (_jsx(SelectItem, { value: d.type, children: d.label }, d.type))) })] }), _jsx(Button, { size: "sm", onClick: () => {
|
|
59
|
+
const s = nieuweSectie(toevoegen);
|
|
60
|
+
setConfig({ ...config, secties: [...secties, s] });
|
|
61
|
+
setActief(s.id);
|
|
62
|
+
}, children: _jsx(Plus, { className: "h-4 w-4" }) })] })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex-row items-center justify-between space-y-0 pb-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-base", children: sectie ? sectie.titel : "Geen blok gekozen" }), _jsx(CardDescription, { children: sectie ? sectieDef(sectie.type).omschrijving : "Kies links een blok." })] }), _jsx("div", { className: "flex items-center gap-1", children: [1, 1.4, 2].map((z) => (_jsxs(Button, { size: "sm", variant: zoom === z ? "secondary" : "ghost", onClick: () => setZoom(z), children: [z, "\u00D7"] }, z))) })] }), _jsx(CardContent, { children: _jsx("div", { className: "max-h-[70vh] overflow-auto rounded-md border bg-muted/30 p-6", children: sectie ? (_jsx("div", { style: {
|
|
63
|
+
transform: `scale(${zoom})`,
|
|
64
|
+
transformOrigin: "top left",
|
|
65
|
+
width: `${100 / zoom}%`,
|
|
66
|
+
}, children: _jsx(PakbonSectiePreview, { sectie: sectie, config: config }) })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen blok geselecteerd." })) }) })] }), _jsxs(Card, { className: "h-fit", children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Instellingen" }), _jsx(CardDescription, { children: "Inhoud, opmaak en kader van dit blok." })] }), _jsx(CardContent, { className: "space-y-4", children: sectie ? (_jsxs(_Fragment, { children: [_jsx(PakbonBlokKaderEditor, { sectie: sectie, onChange: patch }), _jsx(Separator, {}), _jsx(PakbonSectieEditor, { sectie: sectie, onChange: patch })] })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "Kies eerst een blok." })) })] })] })] }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type DocumentConfig } from "../lib/documentProfiles";
|
|
2
|
+
import type { LayoutModel } from "../lib/documentTypes";
|
|
3
|
+
/**
|
|
4
|
+
* Documentspecifieke instellingen. Wat hier verschijnt komt volledig uit het
|
|
5
|
+
* profiel van het documenttype — nieuwe documentsoorten werken automatisch.
|
|
6
|
+
*/
|
|
7
|
+
export declare function DocumentSettingsPanel({ layout, onOpgeslagen, }: {
|
|
8
|
+
layout: LayoutModel & {
|
|
9
|
+
config?: DocumentConfig;
|
|
10
|
+
};
|
|
11
|
+
onOpgeslagen?: (config: DocumentConfig) => void;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=DocumentSettingsPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentSettingsPanel.d.ts","sourceRoot":"","sources":["../../src/components/DocumentSettingsPanel.tsx"],"names":[],"mappings":"AAeA,OAAO,EAIL,KAAK,cAAc,EAGpB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,YAAY,GACb,EAAE;IACD,MAAM,EAAE,WAAW,GAAG;QAAE,MAAM,CAAC,EAAE,cAAc,CAAA;KAAE,CAAC;IAClD,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;CACjD,+BA+DA"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { Badge, Button, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Switch, toast, } from "@flowselections/core";
|
|
4
|
+
import { Save } from "lucide-react";
|
|
5
|
+
import { configWaarde, documentProfiel, standaardConfig, } from "../lib/documentProfiles";
|
|
6
|
+
import { updateLayout, usePrinters } from "../lib/useDocumentData";
|
|
7
|
+
import { useFormaatPrinters, zetFormaatPrinter } from "../lib/formaatPrinters";
|
|
8
|
+
/**
|
|
9
|
+
* Documentspecifieke instellingen. Wat hier verschijnt komt volledig uit het
|
|
10
|
+
* profiel van het documenttype — nieuwe documentsoorten werken automatisch.
|
|
11
|
+
*/
|
|
12
|
+
export function DocumentSettingsPanel({ layout, onOpgeslagen, }) {
|
|
13
|
+
const profiel = useMemo(() => documentProfiel(layout.documenttype), [layout.documenttype]);
|
|
14
|
+
const [config, setConfig] = useState(() => layout.config ?? standaardConfig(profiel));
|
|
15
|
+
const [bezig, setBezig] = useState(false);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
setConfig(layout.config ?? standaardConfig(profiel));
|
|
18
|
+
}, [layout.id, layout.config, profiel]);
|
|
19
|
+
const zet = (groep, key, waarde) => setConfig((c) => ({ ...c, [groep]: { ...(c[groep] ?? {}), [key]: waarde } }));
|
|
20
|
+
const opslaan = async () => {
|
|
21
|
+
setBezig(true);
|
|
22
|
+
try {
|
|
23
|
+
await updateLayout(layout.id, { config });
|
|
24
|
+
toast.success("Documentinstellingen opgeslagen");
|
|
25
|
+
onOpgeslagen?.(config);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
toast.error("Opslaan mislukt");
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
setBezig(false);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
return (_jsxs("div", { className: "space-y-5", children: [_jsx("div", { className: "rounded-md border bg-muted/40 p-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Badge, { variant: "secondary", children: profiel.label }), _jsx("span", { className: "text-xs text-muted-foreground", children: profiel.omschrijving })] }) }), profiel.groepen.map((groep) => (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx(Label, { className: "text-xs uppercase text-muted-foreground", children: groep.label }), groep.omschrijving && (_jsx("p", { className: "text-xs text-muted-foreground", children: groep.omschrijving }))] }), groep.instellingen.map((inst) => (_jsx(Instelling, { inst: inst, waarde: configWaarde(config, groep, inst), onChange: (v) => zet(groep.key, inst.key, v) }, inst.key))), _jsx(Separator, {})] }, groep.key))), profiel.printerPerFormaat && _jsx(FormaatPrinters, { documenttype: profiel.id }), _jsxs(Button, { className: "w-full", onClick: opslaan, disabled: bezig, children: [_jsx(Save, { className: "mr-2 h-4 w-4" }), " Instellingen opslaan"] })] }));
|
|
35
|
+
}
|
|
36
|
+
function Instelling({ inst, waarde, onChange, }) {
|
|
37
|
+
if (inst.type === "schakelaar") {
|
|
38
|
+
return (_jsxs("div", { className: "flex items-center justify-between rounded border px-3 py-2", children: [_jsx("span", { className: "text-sm", children: inst.label }), _jsx(Switch, { checked: !!waarde, onCheckedChange: onChange })] }));
|
|
39
|
+
}
|
|
40
|
+
if (inst.type === "keuze") {
|
|
41
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: inst.label }), _jsxs(Select, { value: String(waarde ?? ""), onValueChange: onChange, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: (inst.opties ?? []).map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] })] }));
|
|
42
|
+
}
|
|
43
|
+
if (inst.type === "meerkeuze") {
|
|
44
|
+
const gekozen = Array.isArray(waarde) ? waarde : [];
|
|
45
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-xs", children: inst.label }), _jsx("div", { className: "space-y-1", children: (inst.opties ?? []).map((o) => (_jsxs("div", { className: "flex items-center justify-between rounded border px-3 py-1.5", children: [_jsx("span", { className: "text-sm", children: o.label }), _jsx(Switch, { checked: gekozen.includes(o.value), onCheckedChange: (aan) => onChange(aan ? [...gekozen, o.value] : gekozen.filter((v) => v !== o.value)) })] }, o.value))) })] }));
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function FormaatPrinters({ documenttype }) {
|
|
50
|
+
const profiel = documentProfiel(documenttype);
|
|
51
|
+
const { printers } = usePrinters();
|
|
52
|
+
const { koppelingen, reload } = useFormaatPrinters(documenttype);
|
|
53
|
+
const huidige = (code) => koppelingen.find((k) => k.formaat_code === code)?.printer_id ?? "geen";
|
|
54
|
+
const kies = async (code, printerId) => {
|
|
55
|
+
try {
|
|
56
|
+
await zetFormaatPrinter(documenttype, code, printerId === "geen" ? null : printerId);
|
|
57
|
+
await reload();
|
|
58
|
+
toast.success("Standaardprinter bijgewerkt");
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
toast.error("Opslaan mislukt");
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx(Label, { className: "text-xs uppercase text-muted-foreground", children: "Standaardprinter per formaat" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Bij afdrukken kiest het systeem automatisch de printer die bij het formaat hoort." })] }), profiel.formaten.map((f) => (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: f.label }), _jsxs(Select, { value: huidige(f.code), onValueChange: (v) => void kies(f.code, v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Geen printer" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "geen", children: "Geen printer" }), printers.map((p) => (_jsx(SelectItem, { value: p.id, children: p.naam }, p.id)))] })] })] }, f.code))), _jsx(Separator, {})] }));
|
|
65
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type LayoutModel } from "../lib/documentTypes";
|
|
2
|
+
/**
|
|
3
|
+
* Formaat- en oriëntatiekeuze die in elke ontwerp-editor beschikbaar is.
|
|
4
|
+
* Wijzigingen worden direct opgeslagen en lokaal doorgegeven aan de preview.
|
|
5
|
+
*/
|
|
6
|
+
export declare function FormaatOrientatieBar({ layout, onChange, }: {
|
|
7
|
+
layout: LayoutModel;
|
|
8
|
+
onChange: (patch: Partial<LayoutModel>) => void;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=FormaatOrientatieBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormaatOrientatieBar.d.ts","sourceRoot":"","sources":["../../src/components/FormaatOrientatieBar.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxE;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;CACjD,+BAgHA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Button, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
|
|
4
|
+
import { RectangleHorizontal, RectangleVertical } from "lucide-react";
|
|
5
|
+
import { PAPIERFORMATEN } from "../lib/documentTypes";
|
|
6
|
+
import { updateLayout } from "../lib/useDocumentData";
|
|
7
|
+
/**
|
|
8
|
+
* Formaat- en oriëntatiekeuze die in elke ontwerp-editor beschikbaar is.
|
|
9
|
+
* Wijzigingen worden direct opgeslagen en lokaal doorgegeven aan de preview.
|
|
10
|
+
*/
|
|
11
|
+
export function FormaatOrientatieBar({ layout, onChange, }) {
|
|
12
|
+
const [bezig, setBezig] = useState(false);
|
|
13
|
+
const vrij = layout.papierformaat === "VRIJ";
|
|
14
|
+
const bewaar = async (patch) => {
|
|
15
|
+
onChange(patch);
|
|
16
|
+
setBezig(true);
|
|
17
|
+
try {
|
|
18
|
+
await updateLayout(layout.id, patch);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
toast.error("Formaat opslaan mislukt");
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
setBezig(false);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const kiesFormaat = (code) => {
|
|
28
|
+
const def = PAPIERFORMATEN.find((p) => p.code === code);
|
|
29
|
+
if (!def)
|
|
30
|
+
return;
|
|
31
|
+
void bewaar({
|
|
32
|
+
papierformaat: code,
|
|
33
|
+
breedte_mm: def.breedte,
|
|
34
|
+
hoogte_mm: def.hoogte,
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
const kiesOrientatie = (orientatie) => {
|
|
38
|
+
if (orientatie === layout.orientatie)
|
|
39
|
+
return;
|
|
40
|
+
void bewaar({ orientatie });
|
|
41
|
+
};
|
|
42
|
+
const maat = (veld, waarde) => {
|
|
43
|
+
const n = Number(waarde);
|
|
44
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
45
|
+
return;
|
|
46
|
+
void bewaar({ [veld]: n });
|
|
47
|
+
};
|
|
48
|
+
return (_jsxs("div", { className: "flex flex-wrap items-end gap-3 rounded-md border bg-muted/30 p-3", children: [_jsxs("div", { className: "w-52 space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Papierformaat" }), _jsxs(Select, { value: layout.papierformaat, onValueChange: kiesFormaat, disabled: bezig, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies formaat" }) }), _jsx(SelectContent, { children: PAPIERFORMATEN.map((p) => (_jsx(SelectItem, { value: p.code, children: p.label }, p.code))) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Breedte (mm)" }), _jsx(Input, { type: "number", className: "w-28", value: layout.breedte_mm, disabled: !vrij || bezig, onChange: (e) => onChange({ breedte_mm: Number(e.target.value) }), onBlur: (e) => maat("breedte_mm", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Hoogte (mm)" }), _jsx(Input, { type: "number", className: "w-28", value: layout.hoogte_mm, disabled: !vrij || bezig, onChange: (e) => onChange({ hoogte_mm: Number(e.target.value) }), onBlur: (e) => maat("hoogte_mm", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Ori\u00EBntatie" }), _jsxs("div", { className: "flex gap-1", children: [_jsxs(Button, { type: "button", size: "sm", variant: layout.orientatie === "portrait" ? "default" : "outline", onClick: () => kiesOrientatie("portrait"), disabled: bezig, children: [_jsx(RectangleVertical, { className: "mr-2 h-4 w-4" }), " Staand"] }), _jsxs(Button, { type: "button", size: "sm", variant: layout.orientatie === "landscape" ? "default" : "outline", onClick: () => kiesOrientatie("landscape"), disabled: bezig, children: [_jsx(RectangleHorizontal, { className: "mr-2 h-4 w-4" }), " Liggend"] })] })] }), _jsxs("p", { className: "ml-auto text-xs text-muted-foreground", children: ["Afdrukgebied:", " ", layout.orientatie === "landscape"
|
|
49
|
+
? `${layout.hoogte_mm} × ${layout.breedte_mm}`
|
|
50
|
+
: `${layout.breedte_mm} × ${layout.hoogte_mm}`, " ", "mm"] })] }));
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LayoutsPage.d.ts","sourceRoot":"","sources":["../../src/components/LayoutsPage.tsx"],"names":[],"mappings":"AAyDA,wBAAgB,WAAW,gCAijB1B"}
|