@tulip-systems/create-app 0.12.3 → 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.
Files changed (57) hide show
  1. package/dist/templates/basic/package.json +5 -5
  2. package/dist/templates/code/package.json +16 -14
  3. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-black-italic.otf +0 -0
  4. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-black.otf +0 -0
  5. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-bold-italic.otf +0 -0
  6. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-bold.otf +0 -0
  7. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-extra-light-italic.otf +0 -0
  8. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-extra-light.otf +0 -0
  9. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-light-italic.otf +0 -0
  10. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-light.otf +0 -0
  11. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-medium-italic.otf +0 -0
  12. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-medium.otf +0 -0
  13. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-regular-italic.otf +0 -0
  14. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-regular.otf +0 -0
  15. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-thin-italic.otf +0 -0
  16. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-thin.otf +0 -0
  17. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-ultra-thin-italic.otf +0 -0
  18. package/dist/templates/code/public/fonts/neue-haas-grotesk/neue-haas-grotesk-ultra-thin.otf +0 -0
  19. package/dist/templates/code/public/images/preview/hero-home.webp +0 -0
  20. package/dist/templates/code/src/app/admin/quotations/(list)/layout.tsx +24 -0
  21. package/dist/templates/code/src/app/admin/quotations/(list)/page.tsx +23 -0
  22. package/dist/templates/code/src/app/admin/quotations/[id]/(dashboard)/layout.tsx +40 -0
  23. package/dist/templates/code/src/app/admin/quotations/[id]/(dashboard)/page.tsx +56 -0
  24. package/dist/templates/code/src/app/admin/quotations/[id]/preview/page.tsx +17 -0
  25. package/dist/templates/code/src/app/admin/quotations/_blocks/commands.tsx +24 -0
  26. package/dist/templates/code/src/app/admin/quotations/_blocks/hero/config.ts +14 -0
  27. package/dist/templates/code/src/app/admin/quotations/_blocks/hero/create.tsx +152 -0
  28. package/dist/templates/code/src/app/admin/quotations/_blocks/hero/inline.tsx +90 -0
  29. package/dist/templates/code/src/app/admin/quotations/_blocks/hero/preview.tsx +90 -0
  30. package/dist/templates/code/src/app/admin/quotations/_blocks/registry.ts +13 -0
  31. package/dist/templates/code/src/app/admin/quotations/_blocks/render.tsx +190 -0
  32. package/dist/templates/code/src/app/admin/quotations/_blocks/styles.ts +24 -0
  33. package/dist/templates/code/src/app/admin/quotations/_blocks/summary/config.ts +13 -0
  34. package/dist/templates/code/src/app/admin/quotations/_blocks/summary/create.tsx +175 -0
  35. package/dist/templates/code/src/app/admin/quotations/_blocks/summary/inline.tsx +93 -0
  36. package/dist/templates/code/src/app/admin/quotations/_blocks/summary/preview.tsx +25 -0
  37. package/dist/templates/code/src/app/admin/quotations/_blocks/totals/config.ts +18 -0
  38. package/dist/templates/code/src/app/admin/quotations/_blocks/totals/create.tsx +177 -0
  39. package/dist/templates/code/src/app/admin/quotations/_blocks/totals/inline.tsx +96 -0
  40. package/dist/templates/code/src/app/admin/quotations/_blocks/totals/preview.tsx +32 -0
  41. package/dist/templates/code/src/app/admin/quotations/_components/create-command.tsx +97 -0
  42. package/dist/templates/code/src/app/admin/quotations/_components/tables.tsx +68 -0
  43. package/dist/templates/code/src/app/admin/quotations/_components/toolbars.tsx +18 -0
  44. package/dist/templates/code/src/app/admin/quotations/_config/columns-data.tsx +35 -0
  45. package/dist/templates/code/src/app/admin/quotations/_config/commands.tsx +71 -0
  46. package/dist/templates/code/src/app/admin/quotations/_config/filters.ts +22 -0
  47. package/dist/templates/code/src/app/admin/quotations/_config/sorting.ts +23 -0
  48. package/dist/templates/code/src/app/admin/quotations/_lib/router.ts +260 -0
  49. package/dist/templates/code/src/app/admin/quotations/_lib/schema.ts +42 -0
  50. package/dist/templates/code/src/app/admin/quotations/_lib/validators.ts +20 -0
  51. package/dist/templates/code/src/lib/config/paths.tsx +8 -0
  52. package/dist/templates/code/src/lib/utils/fonts.ts +102 -0
  53. package/dist/templates/code/src/server/auth/permissions.ts +4 -0
  54. package/dist/templates/code/src/server/db/schema.ts +1 -0
  55. package/dist/templates/code/src/server/router/router.ts +10 -0
  56. package/dist/templates/spark/package.json +13 -13
  57. package/package.json +1 -1
@@ -0,0 +1,260 @@
1
+ import "server-cli-only";
2
+
3
+ import { ORPCError } from "@orpc/server";
4
+ import { moveBlock, reindexBlocks } from "@tulip-systems/blocks";
5
+ import {
6
+ bulkActionSchema,
7
+ offsetPaginationResponse,
8
+ offsetPaginationSchema,
9
+ } from "@tulip-systems/query";
10
+ import { asc, desc, eq, inArray } from "drizzle-orm";
11
+ import { z } from "zod";
12
+ import { getTableData } from "@/app/lib/utils/table-data.server";
13
+ import { permissionMiddleware } from "@/server/router/middleware";
14
+ import { protectedProcedure } from "@/server/router/procedures";
15
+ import {
16
+ type QuotationBlock,
17
+ type QuotationBlockInput,
18
+ quotationBlocks,
19
+ } from "../_blocks/registry";
20
+ import {
21
+ quotationDatabaseFiltersCompiler,
22
+ quotationFiltersParser,
23
+ quotationFiltersSchema,
24
+ } from "../_config/filters";
25
+ import {
26
+ quotationTableDatabaseSortsCompiler,
27
+ quotationTableSortsParser,
28
+ quotationTableSortsSchema,
29
+ } from "../_config/sorting";
30
+ import { quotationBlocks as quotationBlocksTable, quotations } from "./schema";
31
+ import { createQuotationSchema, updateQuotationSchema } from "./validators";
32
+
33
+ /**
34
+ * Router for handling operations related to quotations, including listing, retrieving by ID, creating, updating, and deleting quotations.
35
+ */
36
+ export const quotationsRouter = {
37
+ list: protectedProcedure
38
+ .use(permissionMiddleware({ quotation: ["view"] }))
39
+ .input(
40
+ z.object({
41
+ pagination: offsetPaginationSchema,
42
+ filters: quotationFiltersSchema,
43
+ sorts: quotationTableSortsSchema,
44
+ }),
45
+ )
46
+ .handler(async ({ context, input }) => {
47
+ const parsedSorts = quotationTableSortsParser.parse(input.sorts);
48
+ const orderBy = quotationTableDatabaseSortsCompiler(quotations).parse(parsedSorts);
49
+
50
+ const parsedFilters = quotationFiltersParser.parse(input.filters);
51
+ const where = quotationDatabaseFiltersCompiler(quotations).parse(parsedFilters);
52
+
53
+ const { data, total } = await getTableData(context, quotations, {
54
+ pagination: input.pagination,
55
+ where,
56
+ orderBy,
57
+ });
58
+
59
+ return offsetPaginationResponse({ data, pagination: input.pagination, total });
60
+ }),
61
+ getById: protectedProcedure
62
+ .use(permissionMiddleware({ quotation: ["view"] }))
63
+ .input(z.object({ id: z.string() }))
64
+ .handler(async ({ context, input }) => {
65
+ const data = await context.db.query.quotations.findFirst({
66
+ where: eq(quotations.id, input.id),
67
+ });
68
+
69
+ return data ?? null;
70
+ }),
71
+ create: protectedProcedure
72
+ .use(permissionMiddleware({ quotation: ["create"] }))
73
+ .input(createQuotationSchema)
74
+ .handler(async ({ context, input }) => {
75
+ return context.db.transaction(async (tx) => {
76
+ const [quotation] = await tx.insert(quotations).values(input).returning();
77
+
78
+ if (!quotation) {
79
+ throw new ORPCError("INTERNAL_SERVER_ERROR", {
80
+ message: "Offerte kon niet worden aangemaakt",
81
+ });
82
+ }
83
+
84
+ return quotation;
85
+ });
86
+ }),
87
+ update: protectedProcedure
88
+ .use(permissionMiddleware({ quotation: ["update"] }))
89
+ .input(z.object({ id: z.string(), data: updateQuotationSchema.partial() }))
90
+ .handler(async ({ context, input }) => {
91
+ const [result] = await context.db
92
+ .update(quotations)
93
+ .set(input.data)
94
+ .where(eq(quotations.id, input.id))
95
+ .returning();
96
+
97
+ if (!result) {
98
+ throw new ORPCError("NOT_FOUND", {
99
+ message: "Offerte kon niet worden gewijzigd",
100
+ });
101
+ }
102
+
103
+ return result;
104
+ }),
105
+
106
+ delete: protectedProcedure
107
+ .use(permissionMiddleware({ quotation: ["delete"] }))
108
+ .input(bulkActionSchema)
109
+ .handler(async ({ context, input }) => {
110
+ await context.db.delete(quotations).where(inArray(quotations.id, input.ids));
111
+ }),
112
+ };
113
+
114
+ /**
115
+ * Quotation blocks router that handles operations related to quotation blocks, including listing, creating, updating, and moving blocks.
116
+ */
117
+ export const quotationBlocksRouter = {
118
+ list: protectedProcedure
119
+ .use(permissionMiddleware({ quotation: ["view"] }))
120
+ .input(z.object({ quotationId: z.string() }))
121
+ .handler(async ({ context, input }) => {
122
+ const blocks = await context.db.query.quotationBlocks.findMany({
123
+ where: eq(quotationBlocksTable.quotationId, input.quotationId),
124
+ orderBy: asc(quotationBlocksTable.index),
125
+ });
126
+
127
+ return blocks;
128
+ }),
129
+ create: protectedProcedure
130
+ .use(permissionMiddleware({ quotation: ["update"] }))
131
+ .input(z.object({ quotationId: z.string(), block: quotationBlocks.createSchema }))
132
+ .handler(async ({ context, input }) => {
133
+ const block = input.block as QuotationBlockInput;
134
+ const lastBlock = await context.db
135
+ .select({ index: quotationBlocksTable.index })
136
+ .from(quotationBlocksTable)
137
+ .where(eq(quotationBlocksTable.quotationId, input.quotationId))
138
+ .orderBy(desc(quotationBlocksTable.index))
139
+ .limit(1);
140
+
141
+ const [created] = await context.db
142
+ .insert(quotationBlocksTable)
143
+ .values({
144
+ quotationId: input.quotationId,
145
+ slug: block.slug,
146
+ data: block.data,
147
+ index: (lastBlock[0]?.index ?? -1) + 1,
148
+ })
149
+ .returning();
150
+
151
+ if (!created) {
152
+ throw new ORPCError("INTERNAL_SERVER_ERROR", {
153
+ message: "Blok kon niet worden aangemaakt",
154
+ });
155
+ }
156
+
157
+ return quotationBlocks.selectSchema.parse(created) as QuotationBlock;
158
+ }),
159
+ update: protectedProcedure
160
+ .use(permissionMiddleware({ quotation: ["update"] }))
161
+ .input(z.object({ id: z.string(), data: z.record(z.string(), z.unknown()) }))
162
+ .handler(async ({ context, input }) => {
163
+ const existing = await context.db.query.quotationBlocks.findFirst({
164
+ where: eq(quotationBlocksTable.id, input.id),
165
+ });
166
+
167
+ if (!existing) {
168
+ throw new ORPCError("NOT_FOUND", {
169
+ message: "Blok kon niet worden gevonden",
170
+ });
171
+ }
172
+
173
+ const patchedData = quotationBlocks.patchData({ block: existing, patch: input.data });
174
+
175
+ if (!patchedData.success) {
176
+ throw new ORPCError("BAD_REQUEST", {
177
+ message: "Onbekend bloktype",
178
+ });
179
+ }
180
+
181
+ const [updated] = await context.db
182
+ .update(quotationBlocksTable)
183
+ .set({ data: patchedData.data })
184
+ .where(eq(quotationBlocksTable.id, input.id))
185
+ .returning();
186
+
187
+ if (!updated) {
188
+ throw new ORPCError("INTERNAL_SERVER_ERROR", {
189
+ message: "Blok kon niet worden gewijzigd",
190
+ });
191
+ }
192
+
193
+ return quotationBlocks.selectSchema.parse(updated) as QuotationBlock;
194
+ }),
195
+ delete: protectedProcedure
196
+ .use(permissionMiddleware({ quotation: ["update"] }))
197
+ .input(z.object({ id: z.string() }))
198
+ .handler(async ({ context, input }) => {
199
+ const existing = await context.db.query.quotationBlocks.findFirst({
200
+ where: eq(quotationBlocksTable.id, input.id),
201
+ });
202
+
203
+ if (!existing) {
204
+ throw new ORPCError("NOT_FOUND", {
205
+ message: "Blok kon niet worden gevonden",
206
+ });
207
+ }
208
+
209
+ await context.db.transaction(async (tx) => {
210
+ await tx.delete(quotationBlocksTable).where(eq(quotationBlocksTable.id, input.id));
211
+
212
+ const blocks = await tx.query.quotationBlocks.findMany({
213
+ where: eq(quotationBlocksTable.quotationId, existing.quotationId),
214
+ orderBy: asc(quotationBlocksTable.index),
215
+ });
216
+ const reindexedBlocks = reindexBlocks(blocks);
217
+
218
+ await Promise.all(
219
+ reindexedBlocks.map((block) =>
220
+ tx
221
+ .update(quotationBlocksTable)
222
+ .set({ index: block.index })
223
+ .where(eq(quotationBlocksTable.id, block.id)),
224
+ ),
225
+ );
226
+ });
227
+
228
+ return { success: true };
229
+ }),
230
+ move: protectedProcedure
231
+ .use(permissionMiddleware({ quotation: ["update"] }))
232
+ .input(z.object({ quotationId: z.string(), id: z.string(), toIndex: z.number().int().min(0) }))
233
+ .handler(async ({ context, input }) => {
234
+ const blocks = await context.db.query.quotationBlocks.findMany({
235
+ where: eq(quotationBlocksTable.quotationId, input.quotationId),
236
+ orderBy: asc(quotationBlocksTable.index),
237
+ });
238
+
239
+ const result = moveBlock(blocks, { id: input.id, toIndex: input.toIndex });
240
+
241
+ if (!result) {
242
+ throw new ORPCError("NOT_FOUND", {
243
+ message: "Blok kon niet worden gevonden",
244
+ });
245
+ }
246
+
247
+ await context.db.transaction(async (tx) => {
248
+ await Promise.all(
249
+ result.blocks.map((block) =>
250
+ tx
251
+ .update(quotationBlocksTable)
252
+ .set({ index: block.index })
253
+ .where(eq(quotationBlocksTable.id, block.id)),
254
+ ),
255
+ );
256
+ });
257
+
258
+ return z.array(quotationBlocks.selectSchema).parse(result.blocks) as QuotationBlock[];
259
+ }),
260
+ };
@@ -0,0 +1,42 @@
1
+ import { relations } from "drizzle-orm";
2
+ import { index, pgTable } from "drizzle-orm/pg-core";
3
+ import { baseColumns } from "@/server/db/helpers";
4
+
5
+ export const quotations = pgTable("quotations", (t) => ({
6
+ ...baseColumns,
7
+ title: t.varchar().notNull(),
8
+ }));
9
+
10
+ export const quotationsRelations = relations(quotations, ({ many }) => ({
11
+ blocks: many(quotationBlocks),
12
+ }));
13
+
14
+ /**
15
+ *
16
+ *
17
+ * Quotations Blocks
18
+ */
19
+ export const quotationBlocks = pgTable(
20
+ "quotation_blocks",
21
+ (t) => ({
22
+ ...baseColumns,
23
+ slug: t.varchar().notNull(),
24
+ data: t.jsonb().notNull(),
25
+ index: t.doublePrecision().notNull(),
26
+ quotationId: t
27
+ .uuid()
28
+ .notNull()
29
+ .references(() => quotations.id, { onDelete: "cascade" }),
30
+ }),
31
+ (table) => [
32
+ index("quotation_blocks_quotation_order_idx").on(table.quotationId, table.index),
33
+ index("quotation_blocks_quotation_slug_idx").on(table.quotationId, table.slug),
34
+ ],
35
+ );
36
+
37
+ export const quotationBlocksRelations = relations(quotationBlocks, ({ one }) => ({
38
+ quotation: one(quotations, {
39
+ fields: [quotationBlocks.quotationId],
40
+ references: [quotations.id],
41
+ }),
42
+ }));
@@ -0,0 +1,20 @@
1
+ import { createInsertSchema, createSelectSchema } from "drizzle-zod";
2
+ import { z } from "zod";
3
+ import { quotations } from "./schema";
4
+
5
+ export const quotationSchema = createSelectSchema(quotations);
6
+ export type Quotation = z.infer<typeof quotationSchema>;
7
+
8
+ export const createQuotationSchema = createInsertSchema(quotations, {
9
+ title: z.string().min(1, "Titel is verplicht"),
10
+ }).omit({ id: true, createdAt: true, updatedAt: true });
11
+
12
+ export const updateQuotationSchema = createInsertSchema(quotations, {
13
+ title: z.string().min(1, "Titel is verplicht"),
14
+ }).omit({ id: true, createdAt: true, updatedAt: true });
15
+
16
+ export type CreateQuotationInput = z.input<typeof createQuotationSchema>;
17
+ export type CreateQuotationSchema = z.infer<typeof createQuotationSchema>;
18
+
19
+ export type UpdateQuotationInput = z.input<typeof updateQuotationSchema>;
20
+ export type UpdateQuotationSchema = z.infer<typeof updateQuotationSchema>;
@@ -15,6 +15,7 @@ import {
15
15
  FoldersIcon,
16
16
  HomeIcon,
17
17
  ListTodoIcon,
18
+ ReceiptTextIcon,
18
19
  Table2Icon,
19
20
  TimerIcon,
20
21
  UserSearchIcon,
@@ -140,6 +141,13 @@ export function AdminSidebar() {
140
141
  Prospects
141
142
  </PathLink>
142
143
  </Path>
144
+
145
+ <Path segment="quotations">
146
+ <PathLink href="/admin/quotations">
147
+ <ReceiptTextIcon />
148
+ Offertes
149
+ </PathLink>
150
+ </Path>
143
151
  </PathGroupItems>
144
152
  </PathGroup>
145
153
 
@@ -0,0 +1,102 @@
1
+ import localFont from "next/font/local";
2
+
3
+ /**
4
+ * Neue Haas Grotesk Display Pro — de sans-font van de app (`--font-sans`).
5
+ *
6
+ * Belangrijk: deze font gebruikt een niet-standaard weight-schaal. Elke `weight`
7
+ * hieronder is gelijkgesteld aan de échte `usWeightClass` van het bestand, zodat
8
+ * numerieke gewichten in de browser exact zo resolven als in Figma:
9
+ *
10
+ * 250 UltraThin · 300 Thin · 350 XLight · 400 Light · 450 Roman ·
11
+ * 500 Medium · 700 Bold · 900 Black
12
+ *
13
+ * De typography-schaal gebruikt 400 (→ Light) en 500 (→ Medium) — dit matcht
14
+ * `font-weight: 400/500` uit de Figma-specs (waar 400 óók de Light-cut is, niet
15
+ * de Roman). De Roman-cut (55) zit bewust op 450 en is dus niet `font-normal`.
16
+ */
17
+ export const fontNeueHaas = localFont({
18
+ variable: "--font-sans",
19
+ display: "swap",
20
+ src: [
21
+ {
22
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-ultra-thin.otf",
23
+ weight: "250",
24
+ style: "normal",
25
+ },
26
+ {
27
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-ultra-thin-italic.otf",
28
+ weight: "250",
29
+ style: "italic",
30
+ },
31
+ {
32
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-thin.otf",
33
+ weight: "300",
34
+ style: "normal",
35
+ },
36
+ {
37
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-thin-italic.otf",
38
+ weight: "300",
39
+ style: "italic",
40
+ },
41
+ {
42
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-extra-light.otf",
43
+ weight: "350",
44
+ style: "normal",
45
+ },
46
+ {
47
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-extra-light-italic.otf",
48
+ weight: "350",
49
+ style: "italic",
50
+ },
51
+ {
52
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-light.otf",
53
+ weight: "400",
54
+ style: "normal",
55
+ },
56
+ {
57
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-light-italic.otf",
58
+ weight: "400",
59
+ style: "italic",
60
+ },
61
+ {
62
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-regular.otf",
63
+ weight: "450",
64
+ style: "normal",
65
+ },
66
+ {
67
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-regular-italic.otf",
68
+ weight: "450",
69
+ style: "italic",
70
+ },
71
+ {
72
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-medium.otf",
73
+ weight: "500",
74
+ style: "normal",
75
+ },
76
+ {
77
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-medium-italic.otf",
78
+ weight: "500",
79
+ style: "italic",
80
+ },
81
+ {
82
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-bold.otf",
83
+ weight: "700",
84
+ style: "normal",
85
+ },
86
+ {
87
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-bold-italic.otf",
88
+ weight: "700",
89
+ style: "italic",
90
+ },
91
+ {
92
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-black.otf",
93
+ weight: "900",
94
+ style: "normal",
95
+ },
96
+ {
97
+ path: "../../../public/fonts/neue-haas-grotesk/neue-haas-grotesk-black-italic.otf",
98
+ weight: "900",
99
+ style: "italic",
100
+ },
101
+ ],
102
+ });
@@ -11,6 +11,7 @@ const statement = createStatements({
11
11
  customer: ["view", "create", "update", "archive", "delete"],
12
12
  task: ["view", "create", "update", "delete"],
13
13
  project: ["view", "view.budget", "view.drive", "create", "update", "archive", "delete"],
14
+ quotation: ["view", "create", "update", "delete"],
14
15
  initiative: ["view", "view.budget", "view.stats", "create", "update", "delete"],
15
16
  domain: ["view", "create", "update", "delete"],
16
17
  time: ["view", "create", "update", "delete"],
@@ -33,6 +34,7 @@ const developer = ac.newRole({
33
34
  customer: ["view", "create", "update", "archive", "delete"],
34
35
  task: ["view", "create", "update", "delete"],
35
36
  project: ["view", "view.budget", "view.drive", "create", "update", "archive", "delete"],
37
+ quotation: ["view", "create", "update", "delete"],
36
38
  initiative: ["view", "view.budget", "view.stats", "create", "update", "delete"],
37
39
  domain: ["view", "create", "update", "delete"],
38
40
  time: ["view", "create", "update", "delete"],
@@ -50,6 +52,7 @@ const admin = ac.newRole({
50
52
  customer: ["view", "create", "update", "archive", "delete"],
51
53
  task: ["view", "create", "update", "delete"],
52
54
  project: ["view", "view.budget", "view.drive", "create", "update", "archive", "delete"],
55
+ quotation: ["view", "create", "update", "delete"],
53
56
  initiative: ["view", "view.budget", "view.stats", "create", "update", "delete"],
54
57
  domain: ["view", "create", "update", "delete"],
55
58
  time: ["view", "create", "update", "delete"],
@@ -65,6 +68,7 @@ const worker = ac.newRole({
65
68
  customer: ["view"],
66
69
  task: ["view", "create", "update", "delete"],
67
70
  project: ["view", "view.drive", "create", "update"],
71
+ quotation: ["view", "create", "update"],
68
72
  initiative: ["view", "view.stats", "update"],
69
73
  domain: ["view", "create", "update", "delete"],
70
74
  time: ["view", "create", "update", "delete"],
@@ -13,6 +13,7 @@ export * from "@/app/admin/domains/_lib/schema";
13
13
  export * from "@/app/admin/employees/_lib/schema";
14
14
  export * from "@/app/admin/initiatives/_lib/schema";
15
15
  export * from "@/app/admin/projects/_lib/schema";
16
+ export * from "@/app/admin/quotations/_lib/schema";
16
17
  export * from "@/app/admin/tasks/_lib/schema";
17
18
  export * from "@/app/admin/time/_lib/schema";
18
19
  export * from "@/app/admin/prospects/_lib/schema";
@@ -66,6 +66,16 @@ export const appRouter = {
66
66
  default: module.prospectsRouter,
67
67
  })),
68
68
  ),
69
+ quotations: lazy(() =>
70
+ import("@/app/admin/quotations/_lib/router").then((module) => ({
71
+ default: module.quotationsRouter,
72
+ })),
73
+ ),
74
+ quotationBlocks: lazy(() =>
75
+ import("@/app/admin/quotations/_lib/router").then((module) => ({
76
+ default: module.quotationBlocksRouter,
77
+ })),
78
+ ),
69
79
  };
70
80
 
71
81
  // export type definition of API
@@ -52,19 +52,19 @@
52
52
  "@tiptap/extension-text-style": "^3.29.2",
53
53
  "@tiptap/extension-underline": "^3.29.2",
54
54
  "@tiptap/extensions": "^3.29.2",
55
- "@tulip-systems/app": "0.12.3",
56
- "@tulip-systems/auth": "0.12.3",
57
- "@tulip-systems/commands": "0.12.3",
58
- "@tulip-systems/core": "0.12.3",
59
- "@tulip-systems/data-tables": "0.12.3",
60
- "@tulip-systems/drive": "0.12.3",
61
- "@tulip-systems/drive-local": "0.12.3",
62
- "@tulip-systems/editor": "0.12.3",
63
- "@tulip-systems/editor-storage": "0.12.3",
64
- "@tulip-systems/inline": "0.12.3",
65
- "@tulip-systems/query": "0.12.3",
66
- "@tulip-systems/storage": "0.12.3",
67
- "@tulip-systems/ui": "0.12.3",
55
+ "@tulip-systems/app": "0.12.4",
56
+ "@tulip-systems/auth": "0.12.4",
57
+ "@tulip-systems/commands": "0.12.4",
58
+ "@tulip-systems/core": "0.12.4",
59
+ "@tulip-systems/data-tables": "0.12.4",
60
+ "@tulip-systems/drive": "0.12.4",
61
+ "@tulip-systems/drive-local": "0.12.4",
62
+ "@tulip-systems/editor": "0.12.4",
63
+ "@tulip-systems/editor-storage": "0.12.4",
64
+ "@tulip-systems/inline": "0.12.4",
65
+ "@tulip-systems/query": "0.12.4",
66
+ "@tulip-systems/storage": "0.12.4",
67
+ "@tulip-systems/ui": "0.12.4",
68
68
  "@vercel/functions": "^3.9.0",
69
69
  "@zip.js/zip.js": "^2.8.34",
70
70
  "better-auth": "^1.6.26",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tulip-systems/create-app",
3
- "version": "0.12.3",
3
+ "version": "0.12.4",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "AGPL-3.0",