@open-mercato/core 0.4.2-canary-cae9dafa24 → 0.4.2-canary-f6b7824b47

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 (54) hide show
  1. package/dist/modules/api_docs/backend/docs/page.js +1 -4
  2. package/dist/modules/api_docs/backend/docs/page.js.map +2 -2
  3. package/dist/modules/auth/lib/setup-app.js +0 -4
  4. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  5. package/dist/modules/sales/acl.js +1 -3
  6. package/dist/modules/sales/acl.js.map +2 -2
  7. package/package.json +2 -2
  8. package/src/modules/api_docs/backend/docs/page.tsx +1 -2
  9. package/src/modules/auth/lib/setup-app.ts +0 -4
  10. package/src/modules/customers/README.md +1 -2
  11. package/src/modules/entities/README.md +1 -1
  12. package/src/modules/sales/acl.ts +0 -2
  13. package/src/modules/sales/i18n/de.json +1 -32
  14. package/src/modules/sales/i18n/en.json +1 -32
  15. package/src/modules/sales/i18n/es.json +1 -32
  16. package/src/modules/sales/i18n/pl.json +1 -32
  17. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js +0 -163
  18. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js.map +0 -7
  19. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js +0 -165
  20. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js.map +0 -7
  21. package/dist/modules/sales/api/dashboard/widgets/utils.js +0 -38
  22. package/dist/modules/sales/api/dashboard/widgets/utils.js.map +0 -7
  23. package/dist/modules/sales/lib/customerSnapshot.js +0 -21
  24. package/dist/modules/sales/lib/customerSnapshot.js.map +0 -7
  25. package/dist/modules/sales/lib/dateRange.js +0 -39
  26. package/dist/modules/sales/lib/dateRange.js.map +0 -7
  27. package/dist/modules/sales/widgets/dashboard/new-orders/config.js +0 -32
  28. package/dist/modules/sales/widgets/dashboard/new-orders/config.js.map +0 -7
  29. package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js +0 -252
  30. package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js.map +0 -7
  31. package/dist/modules/sales/widgets/dashboard/new-orders/widget.js +0 -33
  32. package/dist/modules/sales/widgets/dashboard/new-orders/widget.js.map +0 -7
  33. package/dist/modules/sales/widgets/dashboard/new-quotes/config.js +0 -32
  34. package/dist/modules/sales/widgets/dashboard/new-quotes/config.js.map +0 -7
  35. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js +0 -272
  36. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js.map +0 -7
  37. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.js +0 -33
  38. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.js.map +0 -7
  39. package/src/modules/sales/api/dashboard/widgets/new-orders/__tests__/route.test.ts +0 -60
  40. package/src/modules/sales/api/dashboard/widgets/new-orders/route.ts +0 -192
  41. package/src/modules/sales/api/dashboard/widgets/new-quotes/__tests__/route.test.ts +0 -61
  42. package/src/modules/sales/api/dashboard/widgets/new-quotes/route.ts +0 -194
  43. package/src/modules/sales/api/dashboard/widgets/utils.ts +0 -53
  44. package/src/modules/sales/lib/__tests__/dateRange.test.ts +0 -26
  45. package/src/modules/sales/lib/customerSnapshot.ts +0 -17
  46. package/src/modules/sales/lib/dateRange.ts +0 -42
  47. package/src/modules/sales/widgets/dashboard/new-orders/__tests__/config.test.ts +0 -28
  48. package/src/modules/sales/widgets/dashboard/new-orders/config.ts +0 -48
  49. package/src/modules/sales/widgets/dashboard/new-orders/widget.client.tsx +0 -295
  50. package/src/modules/sales/widgets/dashboard/new-orders/widget.ts +0 -33
  51. package/src/modules/sales/widgets/dashboard/new-quotes/__tests__/config.test.ts +0 -28
  52. package/src/modules/sales/widgets/dashboard/new-quotes/config.ts +0 -48
  53. package/src/modules/sales/widgets/dashboard/new-quotes/widget.client.tsx +0 -322
  54. package/src/modules/sales/widgets/dashboard/new-quotes/widget.ts +0 -33
@@ -1,163 +0,0 @@
1
- import { NextResponse } from "next/server";
2
- import { z } from "zod";
3
- import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
4
- import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
5
- import { findAndCountWithDecryption } from "@open-mercato/shared/lib/encryption/find";
6
- import { SalesOrder } from "../../../../data/entities.js";
7
- import { resolveWidgetScope } from "../utils.js";
8
- import { extractCustomerName } from "../../../../lib/customerSnapshot.js";
9
- import { parseDateInput, resolveDateRange } from "../../../../lib/dateRange.js";
10
- const querySchema = z.object({
11
- limit: z.coerce.number().min(1).max(20).default(5),
12
- datePeriod: z.enum(["last24h", "last7d", "last30d", "custom"]).default("last24h"),
13
- customFrom: z.string().optional(),
14
- customTo: z.string().optional(),
15
- tenantId: z.string().uuid().optional(),
16
- organizationId: z.string().uuid().optional()
17
- });
18
- const metadata = {
19
- GET: { requireAuth: true, requireFeatures: ["dashboards.view", "sales.widgets.new-orders"] }
20
- };
21
- async function resolveContext(req, translate) {
22
- const url = new URL(req.url);
23
- const rawQuery = {};
24
- for (const [key, value] of url.searchParams.entries()) {
25
- rawQuery[key] = value;
26
- }
27
- const parsed = querySchema.safeParse(rawQuery);
28
- if (!parsed.success) {
29
- throw new CrudHttpError(400, { error: translate("sales.errors.invalid_query", "Invalid query parameters") });
30
- }
31
- const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {
32
- tenantId: parsed.data.tenantId ?? null,
33
- organizationId: parsed.data.organizationId ?? null
34
- });
35
- return {
36
- container,
37
- em,
38
- tenantId,
39
- organizationIds,
40
- limit: parsed.data.limit,
41
- datePeriod: parsed.data.datePeriod,
42
- customFrom: parsed.data.customFrom,
43
- customTo: parsed.data.customTo
44
- };
45
- }
46
- function resolveDateRangeOrThrow(period, customFrom, customTo, translate) {
47
- const parsedFrom = parseDateInput(customFrom);
48
- const parsedTo = parseDateInput(customTo);
49
- if (customFrom && !parsedFrom) {
50
- throw new CrudHttpError(400, { error: translate("sales.errors.invalid_date", "Invalid date range") });
51
- }
52
- if (customTo && !parsedTo) {
53
- throw new CrudHttpError(400, { error: translate("sales.errors.invalid_date", "Invalid date range") });
54
- }
55
- return resolveDateRange(period, parsedFrom, parsedTo);
56
- }
57
- async function GET(req) {
58
- const { translate } = await resolveTranslations();
59
- try {
60
- const { em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(
61
- req,
62
- translate
63
- );
64
- const { from, to } = resolveDateRangeOrThrow(datePeriod, customFrom, customTo, translate);
65
- const where = {
66
- tenantId,
67
- deletedAt: null,
68
- createdAt: { $gte: from, $lte: to }
69
- };
70
- if (Array.isArray(organizationIds)) {
71
- where.organizationId = organizationIds.length === 1 ? organizationIds[0] : { $in: Array.from(new Set(organizationIds)) };
72
- }
73
- const [items, total] = await findAndCountWithDecryption(
74
- em,
75
- SalesOrder,
76
- where,
77
- {
78
- limit,
79
- orderBy: { createdAt: "desc" }
80
- },
81
- { tenantId }
82
- );
83
- const responseItems = items.map((order) => ({
84
- id: order.id,
85
- orderNumber: order.orderNumber,
86
- status: order.status ?? null,
87
- fulfillmentStatus: order.fulfillmentStatus ?? null,
88
- paymentStatus: order.paymentStatus ?? null,
89
- customerName: extractCustomerName(order.customerSnapshot) ?? null,
90
- customerEntityId: order.customerEntityId ?? null,
91
- netAmount: order.grandTotalNetAmount,
92
- grossAmount: order.grandTotalGrossAmount,
93
- currency: order.currencyCode ?? null,
94
- createdAt: order.createdAt.toISOString()
95
- }));
96
- return NextResponse.json({
97
- items: responseItems,
98
- total,
99
- dateRange: {
100
- from: from.toISOString(),
101
- to: to.toISOString()
102
- }
103
- });
104
- } catch (err) {
105
- if (err instanceof CrudHttpError) {
106
- return NextResponse.json(err.body, { status: err.status });
107
- }
108
- console.error("sales.widgets.newOrders failed", err);
109
- return NextResponse.json(
110
- { error: translate("sales.widgets.newOrders.error", "Failed to load recent orders") },
111
- { status: 500 }
112
- );
113
- }
114
- }
115
- const orderItemSchema = z.object({
116
- id: z.string().uuid(),
117
- orderNumber: z.string(),
118
- status: z.string().nullable(),
119
- fulfillmentStatus: z.string().nullable(),
120
- paymentStatus: z.string().nullable(),
121
- customerName: z.string().nullable(),
122
- customerEntityId: z.string().uuid().nullable(),
123
- netAmount: z.string(),
124
- grossAmount: z.string(),
125
- currency: z.string().nullable(),
126
- createdAt: z.string()
127
- });
128
- const responseSchema = z.object({
129
- items: z.array(orderItemSchema),
130
- total: z.number(),
131
- dateRange: z.object({
132
- from: z.string(),
133
- to: z.string()
134
- })
135
- });
136
- const widgetErrorSchema = z.object({
137
- error: z.string()
138
- });
139
- const openApi = {
140
- tag: "Sales",
141
- summary: "New orders widget",
142
- methods: {
143
- GET: {
144
- summary: "Fetch recently created sales orders",
145
- description: "Returns the most recent sales orders within the scoped tenant/organization.",
146
- query: querySchema,
147
- responses: [
148
- { status: 200, description: "Widget payload", schema: responseSchema }
149
- ],
150
- errors: [
151
- { status: 400, description: "Invalid query parameters", schema: widgetErrorSchema },
152
- { status: 401, description: "Unauthorized", schema: widgetErrorSchema },
153
- { status: 500, description: "Widget failed to load", schema: widgetErrorSchema }
154
- ]
155
- }
156
- }
157
- };
158
- export {
159
- GET,
160
- metadata,
161
- openApi
162
- };
163
- //# sourceMappingURL=route.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../../../src/modules/sales/api/dashboard/widgets/new-orders/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findAndCountWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SalesOrder } from '../../../../data/entities'\nimport { resolveWidgetScope, type WidgetScopeContext } from '../utils'\nimport { extractCustomerName } from '../../../../lib/customerSnapshot'\nimport { parseDateInput, resolveDateRange, type DatePeriodOption } from '../../../../lib/dateRange'\n\nconst querySchema = z.object({\n limit: z.coerce.number().min(1).max(20).default(5),\n datePeriod: z.enum(['last24h', 'last7d', 'last30d', 'custom']).default('last24h'),\n customFrom: z.string().optional(),\n customTo: z.string().optional(),\n tenantId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['dashboards.view', 'sales.widgets.new-orders'] },\n}\n\ntype WidgetContext = WidgetScopeContext & {\n limit: number\n datePeriod: DatePeriodOption\n customFrom?: string\n customTo?: string\n}\n\nasync function resolveContext(\n req: Request,\n translate: (key: string, fallback?: string) => string,\n): Promise<WidgetContext> {\n const url = new URL(req.url)\n const rawQuery: Record<string, string> = {}\n for (const [key, value] of url.searchParams.entries()) {\n rawQuery[key] = value\n }\n const parsed = querySchema.safeParse(rawQuery)\n if (!parsed.success) {\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_query', 'Invalid query parameters') })\n }\n\n const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {\n tenantId: parsed.data.tenantId ?? null,\n organizationId: parsed.data.organizationId ?? null,\n })\n\n return {\n container,\n em,\n tenantId,\n organizationIds,\n limit: parsed.data.limit,\n datePeriod: parsed.data.datePeriod,\n customFrom: parsed.data.customFrom,\n customTo: parsed.data.customTo,\n }\n}\n\nfunction resolveDateRangeOrThrow(\n period: DatePeriodOption,\n customFrom: string | undefined,\n customTo: string | undefined,\n translate: (key: string, fallback?: string) => string,\n): { from: Date; to: Date } {\n const parsedFrom = parseDateInput(customFrom)\n const parsedTo = parseDateInput(customTo)\n if (customFrom && !parsedFrom) {\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_date', 'Invalid date range') })\n }\n if (customTo && !parsedTo) {\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_date', 'Invalid date range') })\n }\n return resolveDateRange(period, parsedFrom, parsedTo)\n}\n\nexport async function GET(req: Request) {\n const { translate } = await resolveTranslations()\n try {\n const { em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(\n req,\n translate,\n )\n\n const { from, to } = resolveDateRangeOrThrow(datePeriod, customFrom, customTo, translate)\n\n const where: FilterQuery<SalesOrder> = {\n tenantId,\n deletedAt: null,\n createdAt: { $gte: from, $lte: to },\n }\n\n if (Array.isArray(organizationIds)) {\n where.organizationId =\n organizationIds.length === 1 ? organizationIds[0] : { $in: Array.from(new Set(organizationIds)) }\n }\n\n const [items, total] = await findAndCountWithDecryption(\n em,\n SalesOrder,\n where,\n {\n limit,\n orderBy: { createdAt: 'desc' as const },\n },\n { tenantId },\n )\n\n const responseItems = items.map((order) => ({\n id: order.id,\n orderNumber: order.orderNumber,\n status: order.status ?? null,\n fulfillmentStatus: order.fulfillmentStatus ?? null,\n paymentStatus: order.paymentStatus ?? null,\n customerName: extractCustomerName(order.customerSnapshot) ?? null,\n customerEntityId: order.customerEntityId ?? null,\n netAmount: order.grandTotalNetAmount,\n grossAmount: order.grandTotalGrossAmount,\n currency: order.currencyCode ?? null,\n createdAt: order.createdAt.toISOString(),\n }))\n\n return NextResponse.json({\n items: responseItems,\n total,\n dateRange: {\n from: from.toISOString(),\n to: to.toISOString(),\n },\n })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('sales.widgets.newOrders failed', err)\n return NextResponse.json(\n { error: translate('sales.widgets.newOrders.error', 'Failed to load recent orders') },\n { status: 500 },\n )\n }\n}\n\nconst orderItemSchema = z.object({\n id: z.string().uuid(),\n orderNumber: z.string(),\n status: z.string().nullable(),\n fulfillmentStatus: z.string().nullable(),\n paymentStatus: z.string().nullable(),\n customerName: z.string().nullable(),\n customerEntityId: z.string().uuid().nullable(),\n netAmount: z.string(),\n grossAmount: z.string(),\n currency: z.string().nullable(),\n createdAt: z.string(),\n})\n\nconst responseSchema = z.object({\n items: z.array(orderItemSchema),\n total: z.number(),\n dateRange: z.object({\n from: z.string(),\n to: z.string(),\n }),\n})\n\nconst widgetErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Sales',\n summary: 'New orders widget',\n methods: {\n GET: {\n summary: 'Fetch recently created sales orders',\n description: 'Returns the most recent sales orders within the scoped tenant/organization.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Widget payload', schema: responseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid query parameters', schema: widgetErrorSchema },\n { status: 401, description: 'Unauthorized', schema: widgetErrorSchema },\n { status: 500, description: 'Widget failed to load', schema: widgetErrorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,165 +0,0 @@
1
- import { NextResponse } from "next/server";
2
- import { z } from "zod";
3
- import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
4
- import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
5
- import { findAndCountWithDecryption } from "@open-mercato/shared/lib/encryption/find";
6
- import { SalesQuote } from "../../../../data/entities.js";
7
- import { resolveWidgetScope } from "../utils.js";
8
- import { extractCustomerName } from "../../../../lib/customerSnapshot.js";
9
- import { parseDateInput, resolveDateRange } from "../../../../lib/dateRange.js";
10
- const querySchema = z.object({
11
- limit: z.coerce.number().min(1).max(20).default(5),
12
- datePeriod: z.enum(["last24h", "last7d", "last30d", "custom"]).default("last24h"),
13
- customFrom: z.string().optional(),
14
- customTo: z.string().optional(),
15
- tenantId: z.string().uuid().optional(),
16
- organizationId: z.string().uuid().optional()
17
- });
18
- const metadata = {
19
- GET: { requireAuth: true, requireFeatures: ["dashboards.view", "sales.widgets.new-quotes"] }
20
- };
21
- async function resolveContext(req, translate) {
22
- const url = new URL(req.url);
23
- const rawQuery = {};
24
- for (const [key, value] of url.searchParams.entries()) {
25
- rawQuery[key] = value;
26
- }
27
- const parsed = querySchema.safeParse(rawQuery);
28
- if (!parsed.success) {
29
- throw new CrudHttpError(400, { error: translate("sales.errors.invalid_query", "Invalid query parameters") });
30
- }
31
- const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {
32
- tenantId: parsed.data.tenantId ?? null,
33
- organizationId: parsed.data.organizationId ?? null
34
- });
35
- return {
36
- container,
37
- em,
38
- tenantId,
39
- organizationIds,
40
- limit: parsed.data.limit,
41
- datePeriod: parsed.data.datePeriod,
42
- customFrom: parsed.data.customFrom,
43
- customTo: parsed.data.customTo
44
- };
45
- }
46
- function resolveDateRangeOrThrow(period, customFrom, customTo, translate) {
47
- const parsedFrom = parseDateInput(customFrom);
48
- const parsedTo = parseDateInput(customTo);
49
- if (customFrom && !parsedFrom) {
50
- throw new CrudHttpError(400, { error: translate("sales.errors.invalid_date", "Invalid date range") });
51
- }
52
- if (customTo && !parsedTo) {
53
- throw new CrudHttpError(400, { error: translate("sales.errors.invalid_date", "Invalid date range") });
54
- }
55
- return resolveDateRange(period, parsedFrom, parsedTo);
56
- }
57
- async function GET(req) {
58
- const { translate } = await resolveTranslations();
59
- try {
60
- const { em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(
61
- req,
62
- translate
63
- );
64
- const { from, to } = resolveDateRangeOrThrow(datePeriod, customFrom, customTo, translate);
65
- const where = {
66
- tenantId,
67
- deletedAt: null,
68
- createdAt: { $gte: from, $lte: to }
69
- };
70
- if (Array.isArray(organizationIds)) {
71
- where.organizationId = organizationIds.length === 1 ? organizationIds[0] : { $in: Array.from(new Set(organizationIds)) };
72
- }
73
- const [items, total] = await findAndCountWithDecryption(
74
- em,
75
- SalesQuote,
76
- where,
77
- {
78
- limit,
79
- orderBy: { createdAt: "desc" }
80
- },
81
- { tenantId }
82
- );
83
- const responseItems = items.map((quote) => ({
84
- id: quote.id,
85
- quoteNumber: quote.quoteNumber,
86
- status: quote.status ?? null,
87
- customerName: extractCustomerName(quote.customerSnapshot) ?? null,
88
- customerEntityId: quote.customerEntityId ?? null,
89
- validFrom: quote.validFrom ? quote.validFrom.toISOString() : null,
90
- validUntil: quote.validUntil ? quote.validUntil.toISOString() : null,
91
- netAmount: quote.grandTotalNetAmount,
92
- grossAmount: quote.grandTotalGrossAmount,
93
- currency: quote.currencyCode ?? null,
94
- createdAt: quote.createdAt.toISOString(),
95
- convertedOrderId: quote.convertedOrderId ?? null
96
- }));
97
- return NextResponse.json({
98
- items: responseItems,
99
- total,
100
- dateRange: {
101
- from: from.toISOString(),
102
- to: to.toISOString()
103
- }
104
- });
105
- } catch (err) {
106
- if (err instanceof CrudHttpError) {
107
- return NextResponse.json(err.body, { status: err.status });
108
- }
109
- console.error("sales.widgets.newQuotes failed", err);
110
- return NextResponse.json(
111
- { error: translate("sales.widgets.newQuotes.error", "Failed to load recent quotes") },
112
- { status: 500 }
113
- );
114
- }
115
- }
116
- const quoteItemSchema = z.object({
117
- id: z.string().uuid(),
118
- quoteNumber: z.string(),
119
- status: z.string().nullable(),
120
- customerName: z.string().nullable(),
121
- customerEntityId: z.string().uuid().nullable(),
122
- validFrom: z.string().nullable(),
123
- validUntil: z.string().nullable(),
124
- netAmount: z.string(),
125
- grossAmount: z.string(),
126
- currency: z.string().nullable(),
127
- createdAt: z.string(),
128
- convertedOrderId: z.string().uuid().nullable()
129
- });
130
- const responseSchema = z.object({
131
- items: z.array(quoteItemSchema),
132
- total: z.number(),
133
- dateRange: z.object({
134
- from: z.string(),
135
- to: z.string()
136
- })
137
- });
138
- const widgetErrorSchema = z.object({
139
- error: z.string()
140
- });
141
- const openApi = {
142
- tag: "Sales",
143
- summary: "New quotes widget",
144
- methods: {
145
- GET: {
146
- summary: "Fetch recently created sales quotes",
147
- description: "Returns the most recent sales quotes within the scoped tenant/organization.",
148
- query: querySchema,
149
- responses: [
150
- { status: 200, description: "Widget payload", schema: responseSchema }
151
- ],
152
- errors: [
153
- { status: 400, description: "Invalid query parameters", schema: widgetErrorSchema },
154
- { status: 401, description: "Unauthorized", schema: widgetErrorSchema },
155
- { status: 500, description: "Widget failed to load", schema: widgetErrorSchema }
156
- ]
157
- }
158
- }
159
- };
160
- export {
161
- GET,
162
- metadata,
163
- openApi
164
- };
165
- //# sourceMappingURL=route.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../../../src/modules/sales/api/dashboard/widgets/new-quotes/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { FilterQuery } from '@mikro-orm/core'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findAndCountWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SalesQuote } from '../../../../data/entities'\nimport { resolveWidgetScope, type WidgetScopeContext } from '../utils'\nimport { extractCustomerName } from '../../../../lib/customerSnapshot'\nimport { parseDateInput, resolveDateRange, type DatePeriodOption } from '../../../../lib/dateRange'\n\nconst querySchema = z.object({\n limit: z.coerce.number().min(1).max(20).default(5),\n datePeriod: z.enum(['last24h', 'last7d', 'last30d', 'custom']).default('last24h'),\n customFrom: z.string().optional(),\n customTo: z.string().optional(),\n tenantId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['dashboards.view', 'sales.widgets.new-quotes'] },\n}\n\ntype WidgetContext = WidgetScopeContext & {\n limit: number\n datePeriod: DatePeriodOption\n customFrom?: string\n customTo?: string\n}\n\nasync function resolveContext(\n req: Request,\n translate: (key: string, fallback?: string) => string,\n): Promise<WidgetContext> {\n const url = new URL(req.url)\n const rawQuery: Record<string, string> = {}\n for (const [key, value] of url.searchParams.entries()) {\n rawQuery[key] = value\n }\n const parsed = querySchema.safeParse(rawQuery)\n if (!parsed.success) {\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_query', 'Invalid query parameters') })\n }\n\n const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {\n tenantId: parsed.data.tenantId ?? null,\n organizationId: parsed.data.organizationId ?? null,\n })\n\n return {\n container,\n em,\n tenantId,\n organizationIds,\n limit: parsed.data.limit,\n datePeriod: parsed.data.datePeriod,\n customFrom: parsed.data.customFrom,\n customTo: parsed.data.customTo,\n }\n}\n\nfunction resolveDateRangeOrThrow(\n period: DatePeriodOption,\n customFrom: string | undefined,\n customTo: string | undefined,\n translate: (key: string, fallback?: string) => string,\n): { from: Date; to: Date } {\n const parsedFrom = parseDateInput(customFrom)\n const parsedTo = parseDateInput(customTo)\n if (customFrom && !parsedFrom) {\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_date', 'Invalid date range') })\n }\n if (customTo && !parsedTo) {\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_date', 'Invalid date range') })\n }\n return resolveDateRange(period, parsedFrom, parsedTo)\n}\n\nexport async function GET(req: Request) {\n const { translate } = await resolveTranslations()\n try {\n const { em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(\n req,\n translate,\n )\n\n const { from, to } = resolveDateRangeOrThrow(datePeriod, customFrom, customTo, translate)\n\n const where: FilterQuery<SalesQuote> = {\n tenantId,\n deletedAt: null,\n createdAt: { $gte: from, $lte: to },\n }\n\n if (Array.isArray(organizationIds)) {\n where.organizationId =\n organizationIds.length === 1 ? organizationIds[0] : { $in: Array.from(new Set(organizationIds)) }\n }\n\n const [items, total] = await findAndCountWithDecryption(\n em,\n SalesQuote,\n where,\n {\n limit,\n orderBy: { createdAt: 'desc' as const },\n },\n { tenantId },\n )\n\n const responseItems = items.map((quote) => ({\n id: quote.id,\n quoteNumber: quote.quoteNumber,\n status: quote.status ?? null,\n customerName: extractCustomerName(quote.customerSnapshot) ?? null,\n customerEntityId: quote.customerEntityId ?? null,\n validFrom: quote.validFrom ? quote.validFrom.toISOString() : null,\n validUntil: quote.validUntil ? quote.validUntil.toISOString() : null,\n netAmount: quote.grandTotalNetAmount,\n grossAmount: quote.grandTotalGrossAmount,\n currency: quote.currencyCode ?? null,\n createdAt: quote.createdAt.toISOString(),\n convertedOrderId: quote.convertedOrderId ?? null,\n }))\n\n return NextResponse.json({\n items: responseItems,\n total,\n dateRange: {\n from: from.toISOString(),\n to: to.toISOString(),\n },\n })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('sales.widgets.newQuotes failed', err)\n return NextResponse.json(\n { error: translate('sales.widgets.newQuotes.error', 'Failed to load recent quotes') },\n { status: 500 },\n )\n }\n}\n\nconst quoteItemSchema = z.object({\n id: z.string().uuid(),\n quoteNumber: z.string(),\n status: z.string().nullable(),\n customerName: z.string().nullable(),\n customerEntityId: z.string().uuid().nullable(),\n validFrom: z.string().nullable(),\n validUntil: z.string().nullable(),\n netAmount: z.string(),\n grossAmount: z.string(),\n currency: z.string().nullable(),\n createdAt: z.string(),\n convertedOrderId: z.string().uuid().nullable(),\n})\n\nconst responseSchema = z.object({\n items: z.array(quoteItemSchema),\n total: z.number(),\n dateRange: z.object({\n from: z.string(),\n to: z.string(),\n }),\n})\n\nconst widgetErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Sales',\n summary: 'New quotes widget',\n methods: {\n GET: {\n summary: 'Fetch recently created sales quotes',\n description: 'Returns the most recent sales quotes within the scoped tenant/organization.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Widget payload', schema: responseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid query parameters', schema: widgetErrorSchema },\n { status: 401, description: 'Unauthorized', schema: widgetErrorSchema },\n { status: 500, description: 'Widget failed to load', schema: widgetErrorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,38 +0,0 @@
1
- import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
2
- import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
- import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
4
- import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
5
- async function resolveWidgetScope(req, translate, overrides) {
6
- const auth = await getAuthFromRequest(req);
7
- if (!auth) {
8
- throw new CrudHttpError(401, { error: translate("sales.errors.unauthorized", "Unauthorized") });
9
- }
10
- const container = await createRequestContainer();
11
- const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
12
- const tenantId = overrides?.tenantId ?? auth.tenantId ?? null;
13
- if (!tenantId) {
14
- throw new CrudHttpError(400, { error: translate("sales.errors.tenant_required", "Tenant context is required") });
15
- }
16
- const organizationIds = (() => {
17
- if (overrides?.organizationId) return [overrides.organizationId];
18
- if (scope?.selectedId) return [scope.selectedId];
19
- if (Array.isArray(scope?.filterIds) && scope.filterIds.length > 0) return scope.filterIds;
20
- if (scope?.allowedIds === null) return null;
21
- if (auth.orgId) return [auth.orgId];
22
- return [];
23
- })();
24
- if (organizationIds !== null && organizationIds.length === 0) {
25
- throw new CrudHttpError(400, { error: translate("sales.errors.organization_required", "Organization context is required") });
26
- }
27
- const em = container.resolve("em");
28
- return {
29
- container,
30
- em,
31
- tenantId,
32
- organizationIds
33
- };
34
- }
35
- export {
36
- resolveWidgetScope
37
- };
38
- //# sourceMappingURL=utils.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../../src/modules/sales/api/dashboard/widgets/utils.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer, type AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\n\nexport type WidgetScopeContext = {\n container: AppContainer\n em: EntityManager\n tenantId: string\n organizationIds: string[] | null\n}\n\nexport async function resolveWidgetScope(\n req: Request,\n translate: (key: string, fallback?: string) => string,\n overrides?: { tenantId?: string | null; organizationId?: string | null },\n): Promise<WidgetScopeContext> {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('sales.errors.unauthorized', 'Unauthorized') })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n\n const tenantId = overrides?.tenantId ?? auth.tenantId ?? null\n if (!tenantId) {\n throw new CrudHttpError(400, { error: translate('sales.errors.tenant_required', 'Tenant context is required') })\n }\n\n const organizationIds = (() => {\n if (overrides?.organizationId) return [overrides.organizationId]\n if (scope?.selectedId) return [scope.selectedId]\n if (Array.isArray(scope?.filterIds) && scope.filterIds.length > 0) return scope.filterIds\n if (scope?.allowedIds === null) return null\n if (auth.orgId) return [auth.orgId]\n return []\n })()\n\n if (organizationIds !== null && organizationIds.length === 0) {\n throw new CrudHttpError(400, { error: translate('sales.errors.organization_required', 'Organization context is required') })\n }\n\n const em = container.resolve('em') as EntityManager\n\n return {\n container,\n em,\n tenantId,\n organizationIds,\n }\n}\n"],
5
- "mappings": "AACA,SAAS,8BAAiD;AAC1D,SAAS,0BAA0B;AACnC,SAAS,qBAAqB;AAC9B,SAAS,0CAA0C;AASnD,eAAsB,mBACpB,KACA,WACA,WAC6B;AAC7B,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,6BAA6B,cAAc,EAAE,CAAC;AAAA,EAChG;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AAExF,QAAM,WAAW,WAAW,YAAY,KAAK,YAAY;AACzD,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,gCAAgC,4BAA4B,EAAE,CAAC;AAAA,EACjH;AAEA,QAAM,mBAAmB,MAAM;AAC7B,QAAI,WAAW,eAAgB,QAAO,CAAC,UAAU,cAAc;AAC/D,QAAI,OAAO,WAAY,QAAO,CAAC,MAAM,UAAU;AAC/C,QAAI,MAAM,QAAQ,OAAO,SAAS,KAAK,MAAM,UAAU,SAAS,EAAG,QAAO,MAAM;AAChF,QAAI,OAAO,eAAe,KAAM,QAAO;AACvC,QAAI,KAAK,MAAO,QAAO,CAAC,KAAK,KAAK;AAClC,WAAO,CAAC;AAAA,EACV,GAAG;AAEH,MAAI,oBAAoB,QAAQ,gBAAgB,WAAW,GAAG;AAC5D,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,sCAAsC,kCAAkC,EAAE,CAAC;AAAA,EAC7H;AAEA,QAAM,KAAK,UAAU,QAAQ,IAAI;AAEjC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
- "names": []
7
- }
@@ -1,21 +0,0 @@
1
- function extractCustomerName(snapshot) {
2
- if (!snapshot || typeof snapshot !== "object") return null;
3
- const data = snapshot;
4
- const candidates = [
5
- data.display_name,
6
- data.displayName,
7
- data.name,
8
- data.company_name,
9
- data.companyName,
10
- data.full_name,
11
- data.fullName
12
- ];
13
- for (const candidate of candidates) {
14
- if (typeof candidate === "string" && candidate.trim().length > 0) return candidate;
15
- }
16
- return null;
17
- }
18
- export {
19
- extractCustomerName
20
- };
21
- //# sourceMappingURL=customerSnapshot.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/modules/sales/lib/customerSnapshot.ts"],
4
- "sourcesContent": ["export function extractCustomerName(snapshot: unknown): string | null {\n if (!snapshot || typeof snapshot !== 'object') return null\n const data = snapshot as Record<string, unknown>\n const candidates = [\n data.display_name,\n data.displayName,\n data.name,\n data.company_name,\n data.companyName,\n data.full_name,\n data.fullName,\n ]\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.trim().length > 0) return candidate\n }\n return null\n}\n"],
5
- "mappings": "AAAO,SAAS,oBAAoB,UAAkC;AACpE,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU,QAAO;AACtD,QAAM,OAAO;AACb,QAAM,aAAa;AAAA,IACjB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACA,aAAW,aAAa,YAAY;AAClC,QAAI,OAAO,cAAc,YAAY,UAAU,KAAK,EAAE,SAAS,EAAG,QAAO;AAAA,EAC3E;AACA,SAAO;AACT;",
6
- "names": []
7
- }
@@ -1,39 +0,0 @@
1
- const DATE_PERIOD_OPTIONS = ["last24h", "last7d", "last30d", "custom"];
2
- function parseDateInput(value) {
3
- if (!value || typeof value !== "string") return null;
4
- const date = new Date(value);
5
- if (Number.isNaN(date.getTime())) return null;
6
- return date;
7
- }
8
- function resolveDateRange(period, customFrom, customTo, now = /* @__PURE__ */ new Date()) {
9
- const to = now;
10
- switch (period) {
11
- case "last24h": {
12
- const from = new Date(now.getTime() - 24 * 60 * 60 * 1e3);
13
- return { from, to };
14
- }
15
- case "last7d": {
16
- const from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
17
- return { from, to };
18
- }
19
- case "last30d": {
20
- const from = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
21
- return { from, to };
22
- }
23
- case "custom": {
24
- const from = customFrom ?? /* @__PURE__ */ new Date(0);
25
- const boundedTo = customTo ?? now;
26
- return { from, to: boundedTo };
27
- }
28
- default: {
29
- const from = new Date(now.getTime() - 24 * 60 * 60 * 1e3);
30
- return { from, to };
31
- }
32
- }
33
- }
34
- export {
35
- DATE_PERIOD_OPTIONS,
36
- parseDateInput,
37
- resolveDateRange
38
- };
39
- //# sourceMappingURL=dateRange.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/modules/sales/lib/dateRange.ts"],
4
- "sourcesContent": ["export type DatePeriodOption = 'last24h' | 'last7d' | 'last30d' | 'custom'\n\nexport const DATE_PERIOD_OPTIONS: DatePeriodOption[] = ['last24h', 'last7d', 'last30d', 'custom']\n\nexport function parseDateInput(value?: string | null): Date | null {\n if (!value || typeof value !== 'string') return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return date\n}\n\nexport function resolveDateRange(\n period: DatePeriodOption,\n customFrom?: Date | null,\n customTo?: Date | null,\n now: Date = new Date(),\n): { from: Date; to: Date } {\n const to = now\n switch (period) {\n case 'last24h': {\n const from = new Date(now.getTime() - 24 * 60 * 60 * 1000)\n return { from, to }\n }\n case 'last7d': {\n const from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)\n return { from, to }\n }\n case 'last30d': {\n const from = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000)\n return { from, to }\n }\n case 'custom': {\n const from = customFrom ?? new Date(0)\n const boundedTo = customTo ?? now\n return { from, to: boundedTo }\n }\n default: {\n const from = new Date(now.getTime() - 24 * 60 * 60 * 1000)\n return { from, to }\n }\n }\n}\n"],
5
- "mappings": "AAEO,MAAM,sBAA0C,CAAC,WAAW,UAAU,WAAW,QAAQ;AAEzF,SAAS,eAAe,OAAoC;AACjE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO;AACzC,SAAO;AACT;AAEO,SAAS,iBACd,QACA,YACA,UACA,MAAY,oBAAI,KAAK,GACK;AAC1B,QAAM,KAAK;AACX,UAAQ,QAAQ;AAAA,IACd,KAAK,WAAW;AACd,YAAM,OAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,KAAK,KAAK,GAAI;AACzD,aAAO,EAAE,MAAM,GAAG;AAAA,IACpB;AAAA,IACA,KAAK,UAAU;AACb,YAAM,OAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAC7D,aAAO,EAAE,MAAM,GAAG;AAAA,IACpB;AAAA,IACA,KAAK,WAAW;AACd,YAAM,OAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,KAAK,KAAK,KAAK,GAAI;AAC9D,aAAO,EAAE,MAAM,GAAG;AAAA,IACpB;AAAA,IACA,KAAK,UAAU;AACb,YAAM,OAAO,cAAc,oBAAI,KAAK,CAAC;AACrC,YAAM,YAAY,YAAY;AAC9B,aAAO,EAAE,MAAM,IAAI,UAAU;AAAA,IAC/B;AAAA,IACA,SAAS;AACP,YAAM,OAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,KAAK,KAAK,GAAI;AACzD,aAAO,EAAE,MAAM,GAAG;AAAA,IACpB;AAAA,EACF;AACF;",
6
- "names": []
7
- }
@@ -1,32 +0,0 @@
1
- const DEFAULT_SETTINGS = {
2
- pageSize: 5,
3
- datePeriod: "last24h"
4
- };
5
- function hydrateSalesNewOrdersSettings(raw) {
6
- if (!raw || typeof raw !== "object") return { ...DEFAULT_SETTINGS };
7
- const input = raw;
8
- const parsedPageSize = Number(input.pageSize);
9
- const pageSize = Number.isFinite(parsedPageSize) ? Math.min(20, Math.max(1, Math.floor(parsedPageSize))) : DEFAULT_SETTINGS.pageSize;
10
- const datePeriod = input.datePeriod === "last24h" || input.datePeriod === "last7d" || input.datePeriod === "last30d" || input.datePeriod === "custom" ? input.datePeriod : DEFAULT_SETTINGS.datePeriod;
11
- let customFrom;
12
- let customTo;
13
- if (datePeriod === "custom") {
14
- if (typeof input.customFrom === "string" && !Number.isNaN(new Date(input.customFrom).getTime())) {
15
- customFrom = input.customFrom;
16
- }
17
- if (typeof input.customTo === "string" && !Number.isNaN(new Date(input.customTo).getTime())) {
18
- customTo = input.customTo;
19
- }
20
- }
21
- return {
22
- pageSize,
23
- datePeriod,
24
- customFrom,
25
- customTo
26
- };
27
- }
28
- export {
29
- DEFAULT_SETTINGS,
30
- hydrateSalesNewOrdersSettings
31
- };
32
- //# sourceMappingURL=config.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../../src/modules/sales/widgets/dashboard/new-orders/config.ts"],
4
- "sourcesContent": ["import type { DatePeriodOption } from '../../../lib/dateRange'\n\nexport type SalesNewOrdersSettings = {\n pageSize: number\n datePeriod: DatePeriodOption\n customFrom?: string\n customTo?: string\n}\n\nexport const DEFAULT_SETTINGS: SalesNewOrdersSettings = {\n pageSize: 5,\n datePeriod: 'last24h',\n}\n\nexport function hydrateSalesNewOrdersSettings(raw: unknown): SalesNewOrdersSettings {\n if (!raw || typeof raw !== 'object') return { ...DEFAULT_SETTINGS }\n const input = raw as Partial<SalesNewOrdersSettings>\n const parsedPageSize = Number(input.pageSize)\n const pageSize = Number.isFinite(parsedPageSize)\n ? Math.min(20, Math.max(1, Math.floor(parsedPageSize)))\n : DEFAULT_SETTINGS.pageSize\n\n const datePeriod: DatePeriodOption =\n input.datePeriod === 'last24h' ||\n input.datePeriod === 'last7d' ||\n input.datePeriod === 'last30d' ||\n input.datePeriod === 'custom'\n ? input.datePeriod\n : DEFAULT_SETTINGS.datePeriod\n\n let customFrom: string | undefined\n let customTo: string | undefined\n if (datePeriod === 'custom') {\n if (typeof input.customFrom === 'string' && !Number.isNaN(new Date(input.customFrom).getTime())) {\n customFrom = input.customFrom\n }\n if (typeof input.customTo === 'string' && !Number.isNaN(new Date(input.customTo).getTime())) {\n customTo = input.customTo\n }\n }\n\n return {\n pageSize,\n datePeriod,\n customFrom,\n customTo,\n }\n}\n"],
5
- "mappings": "AASO,MAAM,mBAA2C;AAAA,EACtD,UAAU;AAAA,EACV,YAAY;AACd;AAEO,SAAS,8BAA8B,KAAsC;AAClF,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,EAAE,GAAG,iBAAiB;AAClE,QAAM,QAAQ;AACd,QAAM,iBAAiB,OAAO,MAAM,QAAQ;AAC5C,QAAM,WAAW,OAAO,SAAS,cAAc,IAC3C,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,cAAc,CAAC,CAAC,IACpD,iBAAiB;AAErB,QAAM,aACJ,MAAM,eAAe,aACrB,MAAM,eAAe,YACrB,MAAM,eAAe,aACrB,MAAM,eAAe,WACjB,MAAM,aACN,iBAAiB;AAEvB,MAAI;AACJ,MAAI;AACJ,MAAI,eAAe,UAAU;AAC3B,QAAI,OAAO,MAAM,eAAe,YAAY,CAAC,OAAO,MAAM,IAAI,KAAK,MAAM,UAAU,EAAE,QAAQ,CAAC,GAAG;AAC/F,mBAAa,MAAM;AAAA,IACrB;AACA,QAAI,OAAO,MAAM,aAAa,YAAY,CAAC,OAAO,MAAM,IAAI,KAAK,MAAM,QAAQ,EAAE,QAAQ,CAAC,GAAG;AAC3F,iBAAW,MAAM;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
- "names": []
7
- }