@tulip-systems/create-app 0.12.2 → 0.12.4
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/dist/templates/basic/package.json +5 -5
- package/dist/templates/code/package.json +33 -14
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-black-italic.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-black.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-bold-italic.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-bold.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-extra-light-italic.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-extra-light.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-light-italic.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-light.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-medium-italic.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-medium.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-regular-italic.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-regular.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-thin-italic.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-thin.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-ultra-thin-italic.otf +0 -0
- package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-ultra-thin.otf +0 -0
- package/dist/templates/code/public/images/preview/hero-home.webp +0 -0
- package/dist/templates/code/src/app/admin/customers/[id]/(default)/@dashboard/page.tsx +2 -0
- package/dist/templates/code/src/app/admin/projects/[id]/(default)/page.tsx +2 -0
- package/dist/templates/code/src/app/admin/prospects/[id]/(default)/@dashboard/page.tsx +2 -0
- package/dist/templates/code/src/app/admin/quotations/(list)/layout.tsx +24 -0
- package/dist/templates/code/src/app/admin/quotations/(list)/page.tsx +23 -0
- package/dist/templates/code/src/app/admin/quotations/[id]/(dashboard)/layout.tsx +40 -0
- package/dist/templates/code/src/app/admin/quotations/[id]/(dashboard)/page.tsx +56 -0
- package/dist/templates/code/src/app/admin/quotations/[id]/preview/page.tsx +17 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/commands.tsx +24 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/hero/config.ts +14 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/hero/create.tsx +152 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/hero/inline.tsx +90 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/hero/preview.tsx +90 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/registry.ts +13 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/render.tsx +190 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/styles.ts +24 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/summary/config.ts +13 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/summary/create.tsx +175 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/summary/inline.tsx +93 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/summary/preview.tsx +25 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/totals/config.ts +18 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/totals/create.tsx +177 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/totals/inline.tsx +96 -0
- package/dist/templates/code/src/app/admin/quotations/_blocks/totals/preview.tsx +32 -0
- package/dist/templates/code/src/app/admin/quotations/_components/create-command.tsx +97 -0
- package/dist/templates/code/src/app/admin/quotations/_components/tables.tsx +68 -0
- package/dist/templates/code/src/app/admin/quotations/_components/toolbars.tsx +18 -0
- package/dist/templates/code/src/app/admin/quotations/_config/columns-data.tsx +35 -0
- package/dist/templates/code/src/app/admin/quotations/_config/commands.tsx +71 -0
- package/dist/templates/code/src/app/admin/quotations/_config/filters.ts +22 -0
- package/dist/templates/code/src/app/admin/quotations/_config/sorting.ts +23 -0
- package/dist/templates/code/src/app/admin/quotations/_lib/router.ts +260 -0
- package/dist/templates/code/src/app/admin/quotations/_lib/schema.ts +42 -0
- package/dist/templates/code/src/app/admin/quotations/_lib/validators.ts +20 -0
- package/dist/templates/code/src/app/admin/tasks/[id]/(default)/@dashboard/page.tsx +6 -4
- package/dist/templates/code/src/lib/config/editor.ts +54 -0
- package/dist/templates/code/src/lib/config/paths.tsx +8 -0
- package/dist/templates/code/src/lib/utils/fonts.ts +102 -0
- package/dist/templates/code/src/server/auth/permissions.ts +4 -0
- package/dist/templates/code/src/server/db/schema.ts +1 -0
- package/dist/templates/code/src/server/router/router.ts +10 -0
- package/dist/templates/spark/package.json +30 -13
- package/dist/templates/spark/src/app/admin/assignments/[id]/(default)/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/companies/[id]/(default)/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/customers/[id]/(default)/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/emails/_components/send-email.tsx +5 -6
- package/dist/templates/spark/src/app/admin/executions/[id]/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/products/[id]/(preview)/page.tsx +2 -2
- package/dist/templates/spark/src/app/admin/products/[id]/dashboard/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/products/[id]/dashboard/editor/page.tsx +6 -8
- package/dist/templates/spark/src/app/admin/projects/[id]/(dashboard)/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/projects/[id]/messages/page.tsx +7 -1
- package/dist/templates/spark/src/app/admin/purchase-orders/[id]/(default)/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/purchase-orders/_config/columns-data.tsx +7 -1
- package/dist/templates/spark/src/app/admin/quotation-items/_config/columns-data.tsx +7 -1
- package/dist/templates/spark/src/app/admin/quotation-items/_config/columns-inline.tsx +2 -0
- package/dist/templates/spark/src/app/admin/quotations/[id]/(default)/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/run-tasks/_config/columns-data.tsx +7 -1
- package/dist/templates/spark/src/app/admin/runs/[id]/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/runs/[id]/reports/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/sales-invoice-items/_config/columns-data.tsx +7 -1
- package/dist/templates/spark/src/app/admin/sales-invoice-items/_config/columns-inline.tsx +2 -0
- package/dist/templates/spark/src/app/admin/sales-order-items/_config/columns-data.tsx +7 -1
- package/dist/templates/spark/src/app/admin/sales-order-items/_config/columns-inline.tsx +2 -0
- package/dist/templates/spark/src/app/admin/sales-orders/[id]/(default)/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/suppliers/[id]/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/tasks/_config/columns-data.tsx +7 -1
- package/dist/templates/spark/src/app/admin/tasks/_config/columns-inline.tsx +2 -0
- package/dist/templates/spark/src/app/admin/technical-sheets/[id]/page.tsx +2 -0
- package/dist/templates/spark/src/app/admin/technical-sheets/_config/columns-data.tsx +13 -2
- package/dist/templates/spark/src/app/admin/template-tasks/_config/columns-data.tsx +7 -1
- package/dist/templates/spark/src/app/admin/template-tasks/_config/columns-inline.tsx +2 -0
- package/dist/templates/spark/src/app/admin/templates/[id]/@dashboard/page.tsx +2 -0
- package/dist/templates/spark/src/lib/config/editor.ts +54 -0
- package/package.json +1 -1
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
"@orpc/tanstack-query": "^1.14.15",
|
|
26
26
|
"@tailwindcss/typography": "^0.5.20",
|
|
27
27
|
"@tanstack/react-query": "^5.101.4",
|
|
28
|
-
"@tulip-systems/app": "0.12.
|
|
29
|
-
"@tulip-systems/auth": "0.12.
|
|
30
|
-
"@tulip-systems/commands": "0.12.
|
|
31
|
-
"@tulip-systems/core": "0.12.
|
|
32
|
-
"@tulip-systems/ui": "0.12.
|
|
28
|
+
"@tulip-systems/app": "0.12.4",
|
|
29
|
+
"@tulip-systems/auth": "0.12.4",
|
|
30
|
+
"@tulip-systems/commands": "0.12.4",
|
|
31
|
+
"@tulip-systems/core": "0.12.4",
|
|
32
|
+
"@tulip-systems/ui": "0.12.4",
|
|
33
33
|
"@vercel/functions": "^3.9.0",
|
|
34
34
|
"better-auth": "^1.6.26",
|
|
35
35
|
"class-variance-authority": "^0.7.1",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@dnd-kit/helpers": "^0.5.0",
|
|
31
31
|
"@dnd-kit/modifiers": "^9.0.0",
|
|
32
32
|
"@dnd-kit/react": "^0.5.0",
|
|
33
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
33
34
|
"@hookform/resolvers": "^5.7.1",
|
|
34
35
|
"@js-temporal/polyfill": "^0.5.1",
|
|
35
36
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
@@ -49,20 +50,38 @@
|
|
|
49
50
|
"@tanstack/react-query": "^5.101.4",
|
|
50
51
|
"@tanstack/react-table": "^8.21.3",
|
|
51
52
|
"@tanstack/react-virtual": "^3.14.9",
|
|
52
|
-
"@
|
|
53
|
-
"@
|
|
54
|
-
"@
|
|
55
|
-
"@
|
|
56
|
-
"@
|
|
57
|
-
"@
|
|
58
|
-
"@
|
|
59
|
-
"@
|
|
60
|
-
"@
|
|
61
|
-
"@
|
|
62
|
-
"@
|
|
63
|
-
"@
|
|
64
|
-
"@
|
|
65
|
-
"@
|
|
53
|
+
"@tiptap/core": "^3.29.2",
|
|
54
|
+
"@tiptap/extension-blockquote": "^3.29.2",
|
|
55
|
+
"@tiptap/extension-bold": "^3.29.2",
|
|
56
|
+
"@tiptap/extension-document": "^3.29.2",
|
|
57
|
+
"@tiptap/extension-hard-break": "^3.29.2",
|
|
58
|
+
"@tiptap/extension-heading": "^3.29.2",
|
|
59
|
+
"@tiptap/extension-highlight": "^3.29.2",
|
|
60
|
+
"@tiptap/extension-horizontal-rule": "^3.29.2",
|
|
61
|
+
"@tiptap/extension-italic": "^3.29.2",
|
|
62
|
+
"@tiptap/extension-link": "^3.29.2",
|
|
63
|
+
"@tiptap/extension-list": "^3.29.2",
|
|
64
|
+
"@tiptap/extension-paragraph": "^3.29.2",
|
|
65
|
+
"@tiptap/extension-strike": "^3.29.2",
|
|
66
|
+
"@tiptap/extension-text": "^3.29.2",
|
|
67
|
+
"@tiptap/extension-text-style": "^3.29.2",
|
|
68
|
+
"@tiptap/extension-underline": "^3.29.2",
|
|
69
|
+
"@tiptap/extensions": "^3.29.2",
|
|
70
|
+
"@tulip-systems/ai": "0.12.4",
|
|
71
|
+
"@tulip-systems/app": "0.12.4",
|
|
72
|
+
"@tulip-systems/auth": "0.12.4",
|
|
73
|
+
"@tulip-systems/blocks": "0.12.4",
|
|
74
|
+
"@tulip-systems/commands": "0.12.4",
|
|
75
|
+
"@tulip-systems/core": "0.12.4",
|
|
76
|
+
"@tulip-systems/data-tables": "0.12.4",
|
|
77
|
+
"@tulip-systems/drive": "0.12.4",
|
|
78
|
+
"@tulip-systems/drive-google": "0.12.4",
|
|
79
|
+
"@tulip-systems/editor": "0.12.4",
|
|
80
|
+
"@tulip-systems/editor-storage": "0.12.4",
|
|
81
|
+
"@tulip-systems/inline": "0.12.4",
|
|
82
|
+
"@tulip-systems/query": "0.12.4",
|
|
83
|
+
"@tulip-systems/storage": "0.12.4",
|
|
84
|
+
"@tulip-systems/ui": "0.12.4",
|
|
66
85
|
"@uidotdev/usehooks": "^2.4.1",
|
|
67
86
|
"@vercel/functions": "^3.9.0",
|
|
68
87
|
"@vercel/otel": "^2.1.3",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-extra-light-italic.otf
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-medium-italic.otf
ADDED
|
Binary file
|
|
Binary file
|
package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-regular-italic.otf
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-ultra-thin-italic.otf
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { DataList, DataListItem, DataListTitle, DataListValue } from "@tulip-systems/ui/data-list";
|
|
12
12
|
import { use } from "react";
|
|
13
13
|
import { z } from "zod";
|
|
14
|
+
import { richTextEditorPreset } from "@/lib/config/editor";
|
|
14
15
|
import { orpc } from "@/server/router/client";
|
|
15
16
|
|
|
16
17
|
const customerMutation = orpc.customers.update.mutationOptions();
|
|
@@ -130,6 +131,7 @@ export default function CustomerDetailPage(props: { params: Promise<{ id: string
|
|
|
130
131
|
mutation={customerMutation}
|
|
131
132
|
strategy={{ mutate: { on: "blur" }, validate: { blur: z.any().nullable() } }}
|
|
132
133
|
className="min-h-40"
|
|
134
|
+
extensions={richTextEditorPreset}
|
|
133
135
|
>
|
|
134
136
|
<EditorMenuBubble />
|
|
135
137
|
<EditorContent />
|
|
@@ -26,6 +26,7 @@ import { use } from "react";
|
|
|
26
26
|
import { z } from "zod";
|
|
27
27
|
import { InitiativeStatusField } from "@/app/admin/initiatives/_config/status";
|
|
28
28
|
import { Icons } from "@/components/icons";
|
|
29
|
+
import { richTextEditorPreset } from "@/lib/config/editor";
|
|
29
30
|
import { orpc } from "@/server/router/client";
|
|
30
31
|
import { projectKeySchema } from "../../_lib/validators";
|
|
31
32
|
|
|
@@ -144,6 +145,7 @@ export default function ProjectDetailPage(props: PageProps<"/admin/projects/[id]
|
|
|
144
145
|
mutation={projectMutation}
|
|
145
146
|
strategy={{ mutate: { on: "change", debounceMs: 700 }, validate: { change: z.any() } }}
|
|
146
147
|
className="min-h-80"
|
|
148
|
+
extensions={richTextEditorPreset}
|
|
147
149
|
>
|
|
148
150
|
<EditorMenuFixed />
|
|
149
151
|
<EditorContent />
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { DataList, DataListItem, DataListTitle, DataListValue } from "@tulip-systems/ui/data-list";
|
|
12
12
|
import { notFound } from "next/navigation";
|
|
13
13
|
import { use } from "react";
|
|
14
|
+
import { richTextEditorPreset } from "@/lib/config/editor";
|
|
14
15
|
import { orpc } from "@/server/router/client";
|
|
15
16
|
import { updateProspectSchema } from "../../../_lib/validators";
|
|
16
17
|
|
|
@@ -164,6 +165,7 @@ export default function ProspectDashboardPage(props: { params: Promise<{ id: str
|
|
|
164
165
|
validate: { change: updateProspectSchema.shape.notes },
|
|
165
166
|
}}
|
|
166
167
|
className="min-h-40"
|
|
168
|
+
extensions={richTextEditorPreset}
|
|
167
169
|
>
|
|
168
170
|
<EditorMenuBubble />
|
|
169
171
|
<EditorContent />
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Header, Topbar, TopbarTitle, TopbarTools } from "@tulip-systems/app/components/client";
|
|
2
|
+
import { InlineCommandMenu, InlineCommandMenuLoading } from "@tulip-systems/commands/client";
|
|
3
|
+
import { type PropsWithChildren, Suspense } from "react";
|
|
4
|
+
import { quotationCreateCommand } from "../_config/commands";
|
|
5
|
+
|
|
6
|
+
export default function QuotationsLayout({ children }: PropsWithChildren) {
|
|
7
|
+
return (
|
|
8
|
+
<>
|
|
9
|
+
<Header>
|
|
10
|
+
<Topbar>
|
|
11
|
+
<TopbarTitle breadcrumbs={[{ label: "Offertes" }]} />
|
|
12
|
+
|
|
13
|
+
<TopbarTools>
|
|
14
|
+
<Suspense fallback={<InlineCommandMenuLoading />}>
|
|
15
|
+
<InlineCommandMenu data={{}} commands={[quotationCreateCommand]} />
|
|
16
|
+
</Suspense>
|
|
17
|
+
</TopbarTools>
|
|
18
|
+
</Topbar>
|
|
19
|
+
</Header>
|
|
20
|
+
|
|
21
|
+
{children}
|
|
22
|
+
</>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Allowed } from "@tulip-systems/auth/permissions/client";
|
|
2
|
+
import { TableLayout } from "@tulip-systems/data-tables";
|
|
3
|
+
import { DataTable } from "@tulip-systems/data-tables/client";
|
|
4
|
+
import { QuotationPaginationTableProvider } from "../_components/tables";
|
|
5
|
+
import { QuotationTableToolbar } from "../_components/toolbars";
|
|
6
|
+
import { quotationColumns } from "../_config/columns-data";
|
|
7
|
+
import { quotationDeleteCommand } from "../_config/commands";
|
|
8
|
+
|
|
9
|
+
export default function QuotationsPage() {
|
|
10
|
+
return (
|
|
11
|
+
<Allowed permission={{ quotation: ["view"] }}>
|
|
12
|
+
<TableLayout className="p-content">
|
|
13
|
+
<QuotationPaginationTableProvider
|
|
14
|
+
columns={quotationColumns}
|
|
15
|
+
commands={[quotationDeleteCommand]}
|
|
16
|
+
>
|
|
17
|
+
<QuotationTableToolbar />
|
|
18
|
+
<DataTable />
|
|
19
|
+
</QuotationPaginationTableProvider>
|
|
20
|
+
</TableLayout>
|
|
21
|
+
</Allowed>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
4
|
+
import { Header, Topbar, TopbarTitle, TopbarTools } from "@tulip-systems/app/components/client";
|
|
5
|
+
import { InlineCommandMenu, InlineCommandMenuLoading } from "@tulip-systems/commands/client";
|
|
6
|
+
import { notFound } from "next/navigation";
|
|
7
|
+
import { Suspense, use } from "react";
|
|
8
|
+
import { orpc } from "@/server/router/client";
|
|
9
|
+
import { quotationDeleteCommand, quotationPreviewCommand } from "../../_config/commands";
|
|
10
|
+
|
|
11
|
+
export default function QuotationsLayout(props: LayoutProps<"/admin/quotations/[id]">) {
|
|
12
|
+
const params = use(props.params);
|
|
13
|
+
const id = params.id?.toString();
|
|
14
|
+
|
|
15
|
+
const { data } = useSuspenseQuery(orpc.quotations.getById.queryOptions({ input: { id } }));
|
|
16
|
+
if (!data) notFound();
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
<Header>
|
|
21
|
+
<Topbar>
|
|
22
|
+
<TopbarTitle
|
|
23
|
+
breadcrumbs={[{ label: "Offertes", href: "/admin/quotations" }, { label: data.title }]}
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
<TopbarTools>
|
|
27
|
+
<Suspense fallback={<InlineCommandMenuLoading />}>
|
|
28
|
+
<InlineCommandMenu
|
|
29
|
+
data={data}
|
|
30
|
+
commands={[quotationPreviewCommand, quotationDeleteCommand]}
|
|
31
|
+
/>
|
|
32
|
+
</Suspense>
|
|
33
|
+
</TopbarTools>
|
|
34
|
+
</Topbar>
|
|
35
|
+
</Header>
|
|
36
|
+
|
|
37
|
+
{props.children}
|
|
38
|
+
</>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
4
|
+
import { Allowed } from "@tulip-systems/auth/permissions/client";
|
|
5
|
+
import {
|
|
6
|
+
InlineField,
|
|
7
|
+
InlineFieldError,
|
|
8
|
+
InlineFieldLabel,
|
|
9
|
+
InlineStringInput,
|
|
10
|
+
} from "@tulip-systems/inline/client";
|
|
11
|
+
import { DataStackLayout } from "@tulip-systems/ui/data-stack";
|
|
12
|
+
import { notFound } from "next/navigation";
|
|
13
|
+
import { use } from "react";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { orpc } from "@/server/router/client";
|
|
16
|
+
import { CreateQuotationBlock, RenderQuotationInlineBlocks } from "../../_blocks/render";
|
|
17
|
+
|
|
18
|
+
const quotationMutation = orpc.quotations.update.mutationOptions({
|
|
19
|
+
meta: { invalidates: [orpc.quotations.key()] },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export default function QuotationDetailPage(props: PageProps<"/admin/quotations/[id]">) {
|
|
23
|
+
const params = use(props.params);
|
|
24
|
+
const id = params.id?.toString();
|
|
25
|
+
|
|
26
|
+
const { data } = useSuspenseQuery(orpc.quotations.getById.queryOptions({ input: { id } }));
|
|
27
|
+
if (!data) notFound();
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Allowed permission={{ quotation: ["view"] }}>
|
|
31
|
+
<DataStackLayout className="p-content">
|
|
32
|
+
<InlineField>
|
|
33
|
+
<InlineFieldLabel>Titel</InlineFieldLabel>
|
|
34
|
+
<InlineStringInput
|
|
35
|
+
initialValue={data.title}
|
|
36
|
+
variables={(value) => ({ id, data: { title: value ?? "" } })}
|
|
37
|
+
mutation={quotationMutation}
|
|
38
|
+
strategy={{ mutate: { on: "blur" }, validate: { blur: z.string().min(1) } }}
|
|
39
|
+
/>
|
|
40
|
+
<InlineFieldError />
|
|
41
|
+
</InlineField>
|
|
42
|
+
|
|
43
|
+
<InlineField>
|
|
44
|
+
<InlineFieldLabel>Blokken</InlineFieldLabel>
|
|
45
|
+
|
|
46
|
+
<div className="flex flex-col gap-2">
|
|
47
|
+
<RenderQuotationInlineBlocks quotationId={id} />
|
|
48
|
+
<CreateQuotationBlock quotationId={id} />
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<InlineFieldError />
|
|
52
|
+
</InlineField>
|
|
53
|
+
</DataStackLayout>
|
|
54
|
+
</Allowed>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
4
|
+
import { notFound } from "next/navigation";
|
|
5
|
+
import { use } from "react";
|
|
6
|
+
import { orpc } from "@/server/router/client";
|
|
7
|
+
import { RenderQuotationPreview } from "../../_blocks/render";
|
|
8
|
+
|
|
9
|
+
export default function QuotationPreviewPage(props: PageProps<"/admin/quotations/[id]/preview">) {
|
|
10
|
+
const params = use(props.params);
|
|
11
|
+
const id = params.id?.toString();
|
|
12
|
+
|
|
13
|
+
const { data } = useSuspenseQuery(orpc.quotations.getById.queryOptions({ input: { id } }));
|
|
14
|
+
if (!data) notFound();
|
|
15
|
+
|
|
16
|
+
return <RenderQuotationPreview quotationId={id} />;
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { commandBuilder } from "@tulip-systems/commands";
|
|
4
|
+
import { DeleteCommand } from "@tulip-systems/commands/client";
|
|
5
|
+
import { toast } from "@tulip-systems/ui/sonner";
|
|
6
|
+
import { orpc } from "@/server/router/client";
|
|
7
|
+
import type { QuotationBlock } from "./registry";
|
|
8
|
+
|
|
9
|
+
type QuotationBlockCommandItem = Pick<QuotationBlock, "id">;
|
|
10
|
+
|
|
11
|
+
export const quotationBlockDeleteCommand = commandBuilder
|
|
12
|
+
.$type<QuotationBlockCommandItem>()
|
|
13
|
+
.permission({ quotation: ["update"] })
|
|
14
|
+
.render(({ data }) => (
|
|
15
|
+
<DeleteCommand
|
|
16
|
+
variables={{ id: data.id }}
|
|
17
|
+
mutation={orpc.quotationBlocks.delete.mutationOptions({
|
|
18
|
+
meta: { invalidates: [orpc.quotationBlocks.key()] },
|
|
19
|
+
onSuccess: () => toast.success("Blok succesvol verwijderd"),
|
|
20
|
+
})}
|
|
21
|
+
/>
|
|
22
|
+
));
|
|
23
|
+
|
|
24
|
+
export const quotationInlineBlockCommands = [quotationBlockDeleteCommand];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BlockDefinition } from "@tulip-systems/blocks";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export const quotationHeroBlock = {
|
|
5
|
+
slug: "hero",
|
|
6
|
+
label: "Hero",
|
|
7
|
+
description: "Homepage hero title, subtitle, and supporting copy.",
|
|
8
|
+
schema: z.object({
|
|
9
|
+
media: z.unknown().optional(),
|
|
10
|
+
titlePart1: z.string().min(1),
|
|
11
|
+
titlePart2: z.string().optional(),
|
|
12
|
+
description: z.string().optional(),
|
|
13
|
+
}),
|
|
14
|
+
} as const satisfies BlockDefinition;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
|
|
4
|
+
import type { DefaultError } from "@tanstack/react-query";
|
|
5
|
+
import type { BlockCreateComponentProps } from "@tulip-systems/blocks";
|
|
6
|
+
import {
|
|
7
|
+
CreateBlock,
|
|
8
|
+
CreateBlockCancel,
|
|
9
|
+
CreateBlockContent,
|
|
10
|
+
CreateBlockDescription,
|
|
11
|
+
CreateBlockFields,
|
|
12
|
+
CreateBlockFooter,
|
|
13
|
+
CreateBlockHeader,
|
|
14
|
+
CreateBlockSubmit,
|
|
15
|
+
CreateBlockTitle,
|
|
16
|
+
CreateBlockTrigger,
|
|
17
|
+
CreateBlockTriggerContent,
|
|
18
|
+
CreateBlockTriggerDescription,
|
|
19
|
+
CreateBlockTriggerIcon,
|
|
20
|
+
CreateBlockTriggerTitle,
|
|
21
|
+
} from "@tulip-systems/blocks/client";
|
|
22
|
+
import { Input } from "@tulip-systems/ui/input";
|
|
23
|
+
import {
|
|
24
|
+
Form,
|
|
25
|
+
FormControl,
|
|
26
|
+
FormField,
|
|
27
|
+
FormItem,
|
|
28
|
+
FormLabel,
|
|
29
|
+
FormMessage,
|
|
30
|
+
} from "@tulip-systems/ui/react-hook-form";
|
|
31
|
+
import { Textarea } from "@tulip-systems/ui/textarea";
|
|
32
|
+
import { BlocksIcon } from "lucide-react";
|
|
33
|
+
import { useForm } from "react-hook-form";
|
|
34
|
+
import type { z } from "zod";
|
|
35
|
+
import { quotationHeroBlock } from "./config";
|
|
36
|
+
|
|
37
|
+
export type QuotationHeroCreateValues = z.input<typeof quotationHeroBlock.schema>;
|
|
38
|
+
|
|
39
|
+
type QuotationHeroCreateBlockInput = {
|
|
40
|
+
slug: typeof quotationHeroBlock.slug;
|
|
41
|
+
data: QuotationHeroCreateValues;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type QuotationHeroCreateBlockProps<
|
|
45
|
+
TData = unknown,
|
|
46
|
+
TError = DefaultError,
|
|
47
|
+
TVariables = undefined,
|
|
48
|
+
TOnMutateResult = unknown,
|
|
49
|
+
> = BlockCreateComponentProps<
|
|
50
|
+
QuotationHeroCreateBlockInput,
|
|
51
|
+
TData,
|
|
52
|
+
TError,
|
|
53
|
+
TVariables,
|
|
54
|
+
TOnMutateResult
|
|
55
|
+
>;
|
|
56
|
+
|
|
57
|
+
export function QuotationHeroCreateBlock<
|
|
58
|
+
TData = unknown,
|
|
59
|
+
TError = DefaultError,
|
|
60
|
+
TVariables = undefined,
|
|
61
|
+
TOnMutateResult = unknown,
|
|
62
|
+
>({
|
|
63
|
+
variables,
|
|
64
|
+
mutation,
|
|
65
|
+
}: QuotationHeroCreateBlockProps<TData, TError, TVariables, TOnMutateResult>) {
|
|
66
|
+
const form = useForm<QuotationHeroCreateValues>({
|
|
67
|
+
resolver: standardSchemaResolver(quotationHeroBlock.schema),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<CreateBlock>
|
|
72
|
+
<CreateBlockTrigger>
|
|
73
|
+
<CreateBlockTriggerIcon>
|
|
74
|
+
<BlocksIcon />
|
|
75
|
+
</CreateBlockTriggerIcon>
|
|
76
|
+
|
|
77
|
+
<CreateBlockTriggerContent>
|
|
78
|
+
<CreateBlockTriggerTitle>{quotationHeroBlock.label}</CreateBlockTriggerTitle>
|
|
79
|
+
<CreateBlockTriggerDescription>
|
|
80
|
+
{quotationHeroBlock.description}
|
|
81
|
+
</CreateBlockTriggerDescription>
|
|
82
|
+
</CreateBlockTriggerContent>
|
|
83
|
+
</CreateBlockTrigger>
|
|
84
|
+
|
|
85
|
+
<Form {...form}>
|
|
86
|
+
<CreateBlockContent
|
|
87
|
+
className="sm:max-w-xl"
|
|
88
|
+
variables={(data: QuotationHeroCreateValues) =>
|
|
89
|
+
variables({ slug: quotationHeroBlock.slug, data })
|
|
90
|
+
}
|
|
91
|
+
mutation={mutation}
|
|
92
|
+
>
|
|
93
|
+
<CreateBlockHeader>
|
|
94
|
+
<CreateBlockTitle>{quotationHeroBlock.label} toevoegen</CreateBlockTitle>
|
|
95
|
+
<CreateBlockDescription>
|
|
96
|
+
Vul de verplichte velden in om het blok toe te voegen.
|
|
97
|
+
</CreateBlockDescription>
|
|
98
|
+
</CreateBlockHeader>
|
|
99
|
+
|
|
100
|
+
<CreateBlockFields className="grid gap-4 md:grid-cols-2">
|
|
101
|
+
<FormField
|
|
102
|
+
control={form.control}
|
|
103
|
+
name="titlePart1"
|
|
104
|
+
render={({ field }) => (
|
|
105
|
+
<FormItem>
|
|
106
|
+
<FormLabel>Titel deel 1</FormLabel>
|
|
107
|
+
<FormControl>
|
|
108
|
+
<Input {...field} />
|
|
109
|
+
</FormControl>
|
|
110
|
+
<FormMessage />
|
|
111
|
+
</FormItem>
|
|
112
|
+
)}
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
<FormField
|
|
116
|
+
control={form.control}
|
|
117
|
+
name="titlePart2"
|
|
118
|
+
render={({ field }) => (
|
|
119
|
+
<FormItem>
|
|
120
|
+
<FormLabel>Titel deel 2</FormLabel>
|
|
121
|
+
<FormControl>
|
|
122
|
+
<Input {...field} />
|
|
123
|
+
</FormControl>
|
|
124
|
+
<FormMessage />
|
|
125
|
+
</FormItem>
|
|
126
|
+
)}
|
|
127
|
+
/>
|
|
128
|
+
|
|
129
|
+
<FormField
|
|
130
|
+
control={form.control}
|
|
131
|
+
name="description"
|
|
132
|
+
render={({ field }) => (
|
|
133
|
+
<FormItem className="md:col-span-2">
|
|
134
|
+
<FormLabel>Beschrijving</FormLabel>
|
|
135
|
+
<FormControl>
|
|
136
|
+
<Textarea {...field} />
|
|
137
|
+
</FormControl>
|
|
138
|
+
<FormMessage />
|
|
139
|
+
</FormItem>
|
|
140
|
+
)}
|
|
141
|
+
/>
|
|
142
|
+
</CreateBlockFields>
|
|
143
|
+
|
|
144
|
+
<CreateBlockFooter>
|
|
145
|
+
<CreateBlockCancel>Annuleren</CreateBlockCancel>
|
|
146
|
+
<CreateBlockSubmit>Blok toevoegen</CreateBlockSubmit>
|
|
147
|
+
</CreateBlockFooter>
|
|
148
|
+
</CreateBlockContent>
|
|
149
|
+
</Form>
|
|
150
|
+
</CreateBlock>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { BlockComponentProps } from "@tulip-systems/blocks";
|
|
4
|
+
import {
|
|
5
|
+
InlineBlock,
|
|
6
|
+
InlineBlockContent,
|
|
7
|
+
InlineBlockDragHandle,
|
|
8
|
+
InlineBlockHeader,
|
|
9
|
+
InlineBlockSortable,
|
|
10
|
+
InlineBlockTrigger,
|
|
11
|
+
} from "@tulip-systems/blocks/client";
|
|
12
|
+
import { DropdownCommandMenu } from "@tulip-systems/commands/client";
|
|
13
|
+
import {
|
|
14
|
+
InlineField,
|
|
15
|
+
InlineFieldError,
|
|
16
|
+
InlineFieldLabel,
|
|
17
|
+
InlineTextarea,
|
|
18
|
+
} from "@tulip-systems/inline/client";
|
|
19
|
+
import { DataStackLayout, DataStackRow } from "@tulip-systems/ui/data-stack";
|
|
20
|
+
import { GripIcon } from "lucide-react";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
import { orpc } from "@/server/router/client";
|
|
23
|
+
import { quotationInlineBlockCommands } from "../commands";
|
|
24
|
+
import { quotationHeroBlock } from "./config";
|
|
25
|
+
|
|
26
|
+
const heroMutation = orpc.quotationBlocks.update.mutationOptions({
|
|
27
|
+
meta: { invalidates: [orpc.quotationBlocks.key()] },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export function QuotationHeroInlineBlock({
|
|
31
|
+
block,
|
|
32
|
+
}: BlockComponentProps<typeof quotationHeroBlock>) {
|
|
33
|
+
return (
|
|
34
|
+
<InlineBlockSortable id={block.id}>
|
|
35
|
+
<InlineBlock>
|
|
36
|
+
<InlineBlockHeader>
|
|
37
|
+
<InlineBlockDragHandle>
|
|
38
|
+
<GripIcon className="h-4 w-4" />
|
|
39
|
+
<span className="sr-only">Blok verplaatsen</span>
|
|
40
|
+
</InlineBlockDragHandle>
|
|
41
|
+
<InlineBlockTrigger>{quotationHeroBlock.label}</InlineBlockTrigger>
|
|
42
|
+
<DropdownCommandMenu
|
|
43
|
+
data={{ id: block.id }}
|
|
44
|
+
commands={quotationInlineBlockCommands}
|
|
45
|
+
className="ml-2 size-7"
|
|
46
|
+
/>
|
|
47
|
+
</InlineBlockHeader>
|
|
48
|
+
|
|
49
|
+
<InlineBlockContent>
|
|
50
|
+
<DataStackLayout>
|
|
51
|
+
<DataStackRow>
|
|
52
|
+
<InlineField>
|
|
53
|
+
<InlineFieldLabel>Titel deel 1</InlineFieldLabel>
|
|
54
|
+
<InlineTextarea
|
|
55
|
+
initialValue={block.data.titlePart1}
|
|
56
|
+
variables={(value) => ({ id: block.id, data: { titlePart1: value ?? "" } })}
|
|
57
|
+
mutation={heroMutation}
|
|
58
|
+
strategy={{ mutate: { on: "blur" }, validate: { blur: z.string().min(1) } }}
|
|
59
|
+
/>
|
|
60
|
+
<InlineFieldError />
|
|
61
|
+
</InlineField>
|
|
62
|
+
|
|
63
|
+
<InlineField>
|
|
64
|
+
<InlineFieldLabel>Titel deel 2</InlineFieldLabel>
|
|
65
|
+
<InlineTextarea
|
|
66
|
+
initialValue={block.data.titlePart2 ?? ""}
|
|
67
|
+
variables={(value) => ({ id: block.id, data: { titlePart2: value ?? "" } })}
|
|
68
|
+
mutation={heroMutation}
|
|
69
|
+
strategy={{ mutate: { on: "blur" }, validate: { blur: z.string() } }}
|
|
70
|
+
/>
|
|
71
|
+
<InlineFieldError />
|
|
72
|
+
</InlineField>
|
|
73
|
+
</DataStackRow>
|
|
74
|
+
|
|
75
|
+
<InlineField className="md:col-span-2">
|
|
76
|
+
<InlineFieldLabel>Beschrijving</InlineFieldLabel>
|
|
77
|
+
<InlineTextarea
|
|
78
|
+
initialValue={block.data.description ?? ""}
|
|
79
|
+
variables={(value) => ({ id: block.id, data: { description: value ?? "" } })}
|
|
80
|
+
mutation={heroMutation}
|
|
81
|
+
strategy={{ mutate: { on: "blur" }, validate: { blur: z.string() } }}
|
|
82
|
+
/>
|
|
83
|
+
<InlineFieldError />
|
|
84
|
+
</InlineField>
|
|
85
|
+
</DataStackLayout>
|
|
86
|
+
</InlineBlockContent>
|
|
87
|
+
</InlineBlock>
|
|
88
|
+
</InlineBlockSortable>
|
|
89
|
+
);
|
|
90
|
+
}
|