@happyvertical/smrt-products 0.37.2 → 0.37.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/lib/chunks/ProductAssetCollection-TbeaVxzc.js +38 -0
- package/dist/lib/chunks/ProductAssetCollection-TbeaVxzc.js.map +1 -0
- package/dist/lib/chunks/ProductCollection-8QlfZ9ez.js +28 -0
- package/dist/lib/chunks/ProductCollection-8QlfZ9ez.js.map +1 -0
- package/dist/lib/chunks/Sku-B0w2dfHb.js +467 -0
- package/dist/lib/chunks/Sku-B0w2dfHb.js.map +1 -0
- package/dist/lib/chunks/__smrt-register__-DAo7IPPx.js +22 -0
- package/dist/lib/chunks/__smrt-register__-DAo7IPPx.js.map +1 -0
- package/dist/lib/chunks/collections-6PSqhUmZ.js +162 -0
- package/dist/lib/chunks/collections-6PSqhUmZ.js.map +1 -0
- package/dist/lib/chunks/components-C6VpNVcg.js +398 -0
- package/dist/lib/chunks/components-C6VpNVcg.js.map +1 -0
- package/dist/lib/chunks/decorate-C25O10I-.js +9 -0
- package/dist/lib/chunks/generated-CM3rwUcw.js +7 -0
- package/dist/lib/chunks/generated-CM3rwUcw.js.map +1 -0
- package/dist/lib/chunks/models-JPB-sld2.js +2 -0
- package/dist/lib/chunks/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/lib/chunks/stores-CB0SI1c5.js +119 -0
- package/dist/lib/chunks/stores-CB0SI1c5.js.map +1 -0
- package/dist/lib/chunks/utils-BgTqlQoH.js +30 -0
- package/dist/lib/chunks/utils-BgTqlQoH.js.map +1 -0
- package/dist/lib/collections.js +4 -12
- package/dist/lib/components.js +2 -6
- package/dist/lib/generated.js +2 -5
- package/dist/lib/index.js +191 -216
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/lib/i18n.js +82 -91
- package/dist/lib/lib/i18n.js.map +1 -1
- package/dist/lib/lib/mock-smrt-client.js +116 -129
- package/dist/lib/lib/mock-smrt-client.js.map +1 -1
- package/dist/lib/lib/stores/product-store.svelte.d.ts.map +1 -1
- package/dist/lib/manifest.json +2 -2
- package/dist/lib/models.js +3 -12
- package/dist/lib/smrt-knowledge.json +14 -14
- package/dist/lib/smrt-products.css +1 -0
- package/dist/lib/stores.js +2 -6
- package/dist/lib/utils.js +2 -27
- package/package.json +23 -23
- package/dist/lib/chunks/ProductAssetCollection-B93HdSlX.js +0 -64
- package/dist/lib/chunks/ProductAssetCollection-B93HdSlX.js.map +0 -1
- package/dist/lib/chunks/ProductForm-CU0UvKEF.js +0 -430
- package/dist/lib/chunks/ProductForm-CU0UvKEF.js.map +0 -1
- package/dist/lib/chunks/Sku-sl6xf1PR.js +0 -494
- package/dist/lib/chunks/Sku-sl6xf1PR.js.map +0 -1
- package/dist/lib/chunks/SkuCollection-f_a3k93c.js +0 -160
- package/dist/lib/chunks/SkuCollection-f_a3k93c.js.map +0 -1
- package/dist/lib/chunks/__smrt-register__-BIgFaVKn.js +0 -5
- package/dist/lib/chunks/__smrt-register__-BIgFaVKn.js.map +0 -1
- package/dist/lib/chunks/index-i3-ci1FB.js +0 -6
- package/dist/lib/chunks/index-i3-ci1FB.js.map +0 -1
- package/dist/lib/chunks/product-store.svelte-Dayd5n3W.js +0 -132
- package/dist/lib/chunks/product-store.svelte-Dayd5n3W.js.map +0 -1
- package/dist/lib/collections.js.map +0 -1
- package/dist/lib/components.js.map +0 -1
- package/dist/lib/generated.js.map +0 -1
- package/dist/lib/models.js.map +0 -1
- package/dist/lib/stores.js.map +0 -1
- package/dist/lib/utils.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"__smrt-register__-DAo7IPPx.js","names":[],"sources":["../../../src/__smrt-register__.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// `new URL('./manifest.json', import.meta.url)` resolves at runtime to the\n// manifest sitting next to this module's compiled output. Vite warns at build\n// time that it cannot pre-resolve the URL; that is the intended behavior —\n// the URL must resolve to dist/manifest.json at runtime, not be inlined.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;AAsBA,eAAe,wBACb,IAAA,IAAA,mBAAA,KAAA,OAAA,KAAA,GAAA,CACF"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import "./__smrt-register__-DAo7IPPx.js";
|
|
2
|
+
import { i as Material, n as ProductVariant, s as Category, t as Sku } from "./Sku-B0w2dfHb.js";
|
|
3
|
+
import { t as ProductCollection } from "./ProductCollection-8QlfZ9ez.js";
|
|
4
|
+
import "./ProductAssetCollection-TbeaVxzc.js";
|
|
5
|
+
import { SmrtCollection } from "@happyvertical/smrt-core";
|
|
6
|
+
//#region src/lib/collections/CategoryCollection.ts
|
|
7
|
+
/**
|
|
8
|
+
* CategoryCollection — Collection manager for {@link Category} objects.
|
|
9
|
+
*/
|
|
10
|
+
var CategoryCollection = class extends SmrtCollection {
|
|
11
|
+
static _itemClass = Category;
|
|
12
|
+
/**
|
|
13
|
+
* Return every top-level category. `parentId` on `Category` is typed
|
|
14
|
+
* `string | undefined`; SMRT serializes undefined string fields as `''`
|
|
15
|
+
* rather than `NULL`, so a category created without an explicit
|
|
16
|
+
* `parentId` lands in the DB with `parent_id = ''`, not `parent_id IS NULL`.
|
|
17
|
+
*
|
|
18
|
+
* We accept both shapes — historical data, migrations, and direct DB
|
|
19
|
+
* imports can produce either — so consumers don't have to normalize
|
|
20
|
+
* `parentId` on write to be discoverable as a root. SQL's `IN` operator
|
|
21
|
+
* does NOT match `NULL` (NULL is not equal to anything, including itself
|
|
22
|
+
* inside an IN list), so the empty-string and NULL cases must run as
|
|
23
|
+
* separate queries and merge.
|
|
24
|
+
*/
|
|
25
|
+
async getRootCategories() {
|
|
26
|
+
const [emptyParents, nullParents] = await Promise.all([this.list({ where: { parentId: "" } }), this.list({ where: { parentId: null } })]);
|
|
27
|
+
if (emptyParents.length === 0) return nullParents;
|
|
28
|
+
if (nullParents.length === 0) return emptyParents;
|
|
29
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30
|
+
const merged = [];
|
|
31
|
+
for (const row of [...emptyParents, ...nullParents]) {
|
|
32
|
+
const id = row.id ?? "";
|
|
33
|
+
if (!id || seen.has(id)) continue;
|
|
34
|
+
seen.add(id);
|
|
35
|
+
merged.push(row);
|
|
36
|
+
}
|
|
37
|
+
return merged;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/lib/collections/MaterialCollection.ts
|
|
42
|
+
/**
|
|
43
|
+
* MaterialCollection — Collection manager for {@link Material} objects.
|
|
44
|
+
*
|
|
45
|
+
* STI framework auto-filters by `_meta_type` to return only Material rows
|
|
46
|
+
* from the shared `products` table.
|
|
47
|
+
*/
|
|
48
|
+
var MaterialCollection = class extends ProductCollection {
|
|
49
|
+
static _itemClass = Material;
|
|
50
|
+
/**
|
|
51
|
+
* Find every {@link Material} with the given kind.
|
|
52
|
+
*
|
|
53
|
+
* `materialKind` is an `@meta()` field — it lives inside the shared
|
|
54
|
+
* `_meta_data` JSON column on `products`, not as its own SQL column.
|
|
55
|
+
* That means a naive `list({ where: { materialKind } })` would generate
|
|
56
|
+
* a WHERE on a non-existent column. We list every Material STI row and
|
|
57
|
+
* filter the hydrated instances in JS.
|
|
58
|
+
*
|
|
59
|
+
* For very large catalogs, consumers should add a generated column or
|
|
60
|
+
* an expression index over the JSON path and override this helper.
|
|
61
|
+
*/
|
|
62
|
+
async findByKind(materialKind) {
|
|
63
|
+
return (await this.list({})).filter((m) => m.materialKind === materialKind);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/lib/collections/ProductVariantCollection.ts
|
|
68
|
+
/**
|
|
69
|
+
* ProductVariantCollection — read helpers for {@link ProductVariant} rows.
|
|
70
|
+
*
|
|
71
|
+
* {@link ProductVariant} is a standalone model (not a Product STI subtype),
|
|
72
|
+
* so this collection drives its own table (`product_variants`). Helper
|
|
73
|
+
* methods cover the two common query patterns: "what axes does this
|
|
74
|
+
* product vary along?" and "tell me about the size axis specifically."
|
|
75
|
+
*/
|
|
76
|
+
var ProductVariantCollection = class extends SmrtCollection {
|
|
77
|
+
static _itemClass = ProductVariant;
|
|
78
|
+
/** Every axis declaration for a given product, in display order. */
|
|
79
|
+
async findForProduct(productId) {
|
|
80
|
+
return this.list({
|
|
81
|
+
where: { productId },
|
|
82
|
+
orderBy: "sortOrder ASC"
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/** The axis declaration for a `(productId, axisName)` pair, or null. */
|
|
86
|
+
async findAxis(productId, axisName) {
|
|
87
|
+
return (await this.list({
|
|
88
|
+
where: {
|
|
89
|
+
productId,
|
|
90
|
+
axisName
|
|
91
|
+
},
|
|
92
|
+
limit: 1
|
|
93
|
+
}))[0] ?? null;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/lib/collections/SkuCollection.ts
|
|
98
|
+
/**
|
|
99
|
+
* SkuCollection — query helpers for {@link Sku} rows.
|
|
100
|
+
*
|
|
101
|
+
* @packageDocumentation
|
|
102
|
+
*/
|
|
103
|
+
var SkuCollection = class extends SmrtCollection {
|
|
104
|
+
static _itemClass = Sku;
|
|
105
|
+
/**
|
|
106
|
+
* Look up a SKU by its tenant-scoped `code` (UPC, internal part
|
|
107
|
+
* number, etc.). Returns `null` when no row matches.
|
|
108
|
+
*/
|
|
109
|
+
async findByCode(code) {
|
|
110
|
+
return (await this.list({
|
|
111
|
+
where: { code },
|
|
112
|
+
limit: 1
|
|
113
|
+
}))[0] ?? null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Look up a SKU by its scannable barcode. Returns `null` when no row
|
|
117
|
+
* matches; pass `''` to skip the query early.
|
|
118
|
+
*/
|
|
119
|
+
async findByBarcode(barcode) {
|
|
120
|
+
if (!barcode) return null;
|
|
121
|
+
return (await this.list({
|
|
122
|
+
where: { barcode },
|
|
123
|
+
limit: 1
|
|
124
|
+
}))[0] ?? null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Find every SKU pointing at the given `Product` id (or any Product
|
|
128
|
+
* STI subtype id — `Material` upstream, vertical subtypes defined in
|
|
129
|
+
* templates). Useful for listing the SKUs that back a single product
|
|
130
|
+
* card.
|
|
131
|
+
*/
|
|
132
|
+
async findByProduct(productId) {
|
|
133
|
+
return this.list({
|
|
134
|
+
where: { productId },
|
|
135
|
+
orderBy: "code ASC"
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Find every SKU that is a component of the given parent SKU (for
|
|
140
|
+
* bundles / kits). Returns an empty array when the parent has no
|
|
141
|
+
* children.
|
|
142
|
+
*/
|
|
143
|
+
async findByParent(parentSkuId) {
|
|
144
|
+
return this.list({
|
|
145
|
+
where: { parentSkuId },
|
|
146
|
+
orderBy: "code ASC"
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/** Find every active SKU, optionally narrowed by parent product id. */
|
|
150
|
+
async findActive(productId) {
|
|
151
|
+
const where = { active: true };
|
|
152
|
+
if (productId) where.productId = productId;
|
|
153
|
+
return this.list({
|
|
154
|
+
where,
|
|
155
|
+
orderBy: "code ASC"
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
//#endregion
|
|
160
|
+
export { CategoryCollection as i, ProductVariantCollection as n, MaterialCollection as r, SkuCollection as t };
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=collections-6PSqhUmZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections-6PSqhUmZ.js","names":[],"sources":["../../../src/lib/collections/CategoryCollection.ts","../../../src/lib/collections/MaterialCollection.ts","../../../src/lib/collections/ProductVariantCollection.ts","../../../src/lib/collections/SkuCollection.ts"],"sourcesContent":["/**\n * CategoryCollection — Collection manager for {@link Category} objects.\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Category } from '../models/Category';\n\nexport class CategoryCollection extends SmrtCollection<Category> {\n static readonly _itemClass = Category;\n\n /**\n * Return every top-level category. `parentId` on `Category` is typed\n * `string | undefined`; SMRT serializes undefined string fields as `''`\n * rather than `NULL`, so a category created without an explicit\n * `parentId` lands in the DB with `parent_id = ''`, not `parent_id IS NULL`.\n *\n * We accept both shapes — historical data, migrations, and direct DB\n * imports can produce either — so consumers don't have to normalize\n * `parentId` on write to be discoverable as a root. SQL's `IN` operator\n * does NOT match `NULL` (NULL is not equal to anything, including itself\n * inside an IN list), so the empty-string and NULL cases must run as\n * separate queries and merge.\n */\n async getRootCategories(): Promise<Category[]> {\n const [emptyParents, nullParents] = await Promise.all([\n this.list({ where: { parentId: '' } }),\n this.list({ where: { parentId: null } }),\n ]);\n\n if (emptyParents.length === 0) return nullParents;\n if (nullParents.length === 0) return emptyParents;\n\n // De-dupe by id in case a row somehow shows up in both lists.\n const seen = new Set<string>();\n const merged: Category[] = [];\n for (const row of [...emptyParents, ...nullParents]) {\n const id = row.id ?? '';\n if (!id || seen.has(id)) continue;\n seen.add(id);\n merged.push(row);\n }\n return merged;\n }\n}\n","/**\n * MaterialCollection — Collection manager for {@link Material} objects.\n *\n * STI framework auto-filters by `_meta_type` to return only Material rows\n * from the shared `products` table.\n */\n\nimport { Material } from '../models/Material';\nimport type { MaterialKind } from '../models/types';\nimport { ProductCollection } from './ProductCollection';\n\nexport class MaterialCollection extends ProductCollection {\n static override readonly _itemClass = Material;\n\n /**\n * Find every {@link Material} with the given kind.\n *\n * `materialKind` is an `@meta()` field — it lives inside the shared\n * `_meta_data` JSON column on `products`, not as its own SQL column.\n * That means a naive `list({ where: { materialKind } })` would generate\n * a WHERE on a non-existent column. We list every Material STI row and\n * filter the hydrated instances in JS.\n *\n * For very large catalogs, consumers should add a generated column or\n * an expression index over the JSON path and override this helper.\n */\n async findByKind(materialKind: MaterialKind): Promise<Material[]> {\n const items = (await this.list({})) as Material[];\n return items.filter((m) => m.materialKind === materialKind);\n }\n}\n","/**\n * ProductVariantCollection — read helpers for {@link ProductVariant} rows.\n *\n * {@link ProductVariant} is a standalone model (not a Product STI subtype),\n * so this collection drives its own table (`product_variants`). Helper\n * methods cover the two common query patterns: \"what axes does this\n * product vary along?\" and \"tell me about the size axis specifically.\"\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ProductVariant } from '../models/ProductVariant';\n\nexport class ProductVariantCollection extends SmrtCollection<ProductVariant> {\n static readonly _itemClass = ProductVariant;\n\n /** Every axis declaration for a given product, in display order. */\n async findForProduct(productId: string): Promise<ProductVariant[]> {\n return this.list({\n where: { productId },\n orderBy: 'sortOrder ASC',\n });\n }\n\n /** The axis declaration for a `(productId, axisName)` pair, or null. */\n async findAxis(\n productId: string,\n axisName: string,\n ): Promise<ProductVariant | null> {\n const matches = await this.list({\n where: { productId, axisName },\n limit: 1,\n });\n return matches[0] ?? null;\n }\n}\n","/**\n * SkuCollection — query helpers for {@link Sku} rows.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Sku } from '../models/Sku';\n\nexport class SkuCollection extends SmrtCollection<Sku> {\n static readonly _itemClass = Sku;\n\n /**\n * Look up a SKU by its tenant-scoped `code` (UPC, internal part\n * number, etc.). Returns `null` when no row matches.\n */\n async findByCode(code: string): Promise<Sku | null> {\n const matches = await this.list({ where: { code }, limit: 1 });\n return matches[0] ?? null;\n }\n\n /**\n * Look up a SKU by its scannable barcode. Returns `null` when no row\n * matches; pass `''` to skip the query early.\n */\n async findByBarcode(barcode: string): Promise<Sku | null> {\n if (!barcode) return null;\n const matches = await this.list({ where: { barcode }, limit: 1 });\n return matches[0] ?? null;\n }\n\n /**\n * Find every SKU pointing at the given `Product` id (or any Product\n * STI subtype id — `Material` upstream, vertical subtypes defined in\n * templates). Useful for listing the SKUs that back a single product\n * card.\n */\n async findByProduct(productId: string): Promise<Sku[]> {\n return this.list({ where: { productId }, orderBy: 'code ASC' });\n }\n\n /**\n * Find every SKU that is a component of the given parent SKU (for\n * bundles / kits). Returns an empty array when the parent has no\n * children.\n */\n async findByParent(parentSkuId: string): Promise<Sku[]> {\n return this.list({ where: { parentSkuId }, orderBy: 'code ASC' });\n }\n\n /** Find every active SKU, optionally narrowed by parent product id. */\n async findActive(productId?: string): Promise<Sku[]> {\n const where: Record<string, unknown> = { active: true };\n if (productId) where.productId = productId;\n return this.list({ where, orderBy: 'code ASC' });\n }\n}\n"],"mappings":";;;;;;;;;AAOA,IAAa,qBAAb,cAAwC,eAAyB;CAC/D,OAAgB,aAAa;;;;;;;;;;;;;;CAe7B,MAAM,oBAAyC;EAC7C,MAAM,CAAC,cAAc,eAAe,MAAM,QAAQ,IAAI,CACpD,KAAK,KAAK,EAAE,OAAO,EAAE,UAAU,GAAG,EAAE,CAAC,GACrC,KAAK,KAAK,EAAE,OAAO,EAAE,UAAU,KAAK,EAAE,CAAC,CACzC,CAAC;EAED,IAAI,aAAa,WAAW,GAAG,OAAO;EACtC,IAAI,YAAY,WAAW,GAAG,OAAO;EAGrC,MAAM,uBAAO,IAAI,IAAY;EAC7B,MAAM,SAAqB,CAAC;EAC5B,KAAK,MAAM,OAAO,CAAC,GAAG,cAAc,GAAG,WAAW,GAAG;GACnD,MAAM,KAAK,IAAI,MAAM;GACrB,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,GAAG;GACzB,KAAK,IAAI,EAAE;GACX,OAAO,KAAK,GAAG;EACjB;EACA,OAAO;CACT;AACF;;;;;;;;;AChCA,IAAa,qBAAb,cAAwC,kBAAkB;CACxD,OAAyB,aAAa;;;;;;;;;;;;;CActC,MAAM,WAAW,cAAiD;EAEhE,QAAO,MADc,KAAK,KAAK,CAAC,CAAC,EAAA,CACpB,QAAQ,MAAM,EAAE,iBAAiB,YAAY;CAC5D;AACF;;;;;;;;;;;AClBA,IAAa,2BAAb,cAA8C,eAA+B;CAC3E,OAAgB,aAAa;;CAG7B,MAAM,eAAe,WAA8C;EACjE,OAAO,KAAK,KAAK;GACf,OAAO,EAAE,UAAU;GACnB,SAAS;EACX,CAAC;CACH;;CAGA,MAAM,SACJ,WACA,UACgC;EAKhC,QAAO,MAJe,KAAK,KAAK;GAC9B,OAAO;IAAE;IAAW;GAAS;GAC7B,OAAO;EACT,CAAC,EAAA,CACc,MAAM;CACvB;AACF;;;;;;;;ACzBA,IAAa,gBAAb,cAAmC,eAAoB;CACrD,OAAgB,aAAa;;;;;CAM7B,MAAM,WAAW,MAAmC;EAElD,QAAO,MADe,KAAK,KAAK;GAAE,OAAO,EAAE,KAAK;GAAG,OAAO;EAAE,CAAC,EAAA,CAC9C,MAAM;CACvB;;;;;CAMA,MAAM,cAAc,SAAsC;EACxD,IAAI,CAAC,SAAS,OAAO;EAErB,QAAO,MADe,KAAK,KAAK;GAAE,OAAO,EAAE,QAAQ;GAAG,OAAO;EAAE,CAAC,EAAA,CACjD,MAAM;CACvB;;;;;;;CAQA,MAAM,cAAc,WAAmC;EACrD,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,UAAU;GAAG,SAAS;EAAW,CAAC;CAChE;;;;;;CAOA,MAAM,aAAa,aAAqC;EACtD,OAAO,KAAK,KAAK;GAAE,OAAO,EAAE,YAAY;GAAG,SAAS;EAAW,CAAC;CAClE;;CAGA,MAAM,WAAW,WAAoC;EACnD,MAAM,QAAiC,EAAE,QAAQ,KAAK;EACtD,IAAI,WAAW,MAAM,YAAY;EACjC,OAAO,KAAK,KAAK;GAAE;GAAO,SAAS;EAAW,CAAC;CACjD;AACF"}
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { M } from "../lib/i18n.js";
|
|
2
|
+
import "svelte/internal/disclose-version";
|
|
3
|
+
import * as $ from "svelte/internal/client";
|
|
4
|
+
import { Button } from "@happyvertical/smrt-ui/ui";
|
|
5
|
+
import { Form, Input, Textarea } from "@happyvertical/smrt-ui/forms";
|
|
6
|
+
import { useI18n } from "@happyvertical/smrt-ui/i18n";
|
|
7
|
+
//#region src/lib/components/ProductCard.svelte
|
|
8
|
+
var root$1 = $.from_html(`<div class="product-manufacturer svelte-11ja2cl"> </div>`);
|
|
9
|
+
var root_1$1 = $.from_html(`<div class="product-model svelte-11ja2cl"> </div>`);
|
|
10
|
+
var root_2$1 = $.from_html(`<p class="product-description svelte-11ja2cl"> </p>`);
|
|
11
|
+
var root_3 = $.from_html(`<div class="product-category svelte-11ja2cl"> </div>`);
|
|
12
|
+
var root_4 = $.from_html(`<span class="tag svelte-11ja2cl"> </span>`);
|
|
13
|
+
var root_5 = $.from_html(`<div class="product-tags svelte-11ja2cl"></div>`);
|
|
14
|
+
var root_6 = $.from_html(`<div class="product-card svelte-11ja2cl"><div class="product-header svelte-11ja2cl"><h3 class="product-name svelte-11ja2cl"> </h3> <!></div> <!> <!> <div class="product-meta svelte-11ja2cl"><!> <!></div> <div class="product-actions svelte-11ja2cl"><!> <!></div></div>`);
|
|
15
|
+
function ProductCard($$anchor, $$props) {
|
|
16
|
+
$.push($$props, true);
|
|
17
|
+
var div = root_6();
|
|
18
|
+
var div_1 = $.child(div);
|
|
19
|
+
var h3 = $.child(div_1);
|
|
20
|
+
var text = $.child(h3, true);
|
|
21
|
+
$.reset(h3);
|
|
22
|
+
var node = $.sibling(h3, 2);
|
|
23
|
+
var consequent = ($$anchor) => {
|
|
24
|
+
var div_2 = root$1();
|
|
25
|
+
var text_1 = $.child(div_2, true);
|
|
26
|
+
$.reset(div_2);
|
|
27
|
+
$.template_effect(() => $.set_text(text_1, $$props.product.manufacturer));
|
|
28
|
+
$.append($$anchor, div_2);
|
|
29
|
+
};
|
|
30
|
+
$.if(node, ($$render) => {
|
|
31
|
+
if ($$props.product.manufacturer) $$render(consequent);
|
|
32
|
+
});
|
|
33
|
+
$.reset(div_1);
|
|
34
|
+
var node_1 = $.sibling(div_1, 2);
|
|
35
|
+
var consequent_1 = ($$anchor) => {
|
|
36
|
+
var div_3 = root_1$1();
|
|
37
|
+
var text_2 = $.child(div_3);
|
|
38
|
+
$.reset(div_3);
|
|
39
|
+
$.template_effect(() => $.set_text(text_2, `Model: ${$$props.product.model ?? ""}`));
|
|
40
|
+
$.append($$anchor, div_3);
|
|
41
|
+
};
|
|
42
|
+
$.if(node_1, ($$render) => {
|
|
43
|
+
if ($$props.product.model) $$render(consequent_1);
|
|
44
|
+
});
|
|
45
|
+
var node_2 = $.sibling(node_1, 2);
|
|
46
|
+
var consequent_2 = ($$anchor) => {
|
|
47
|
+
var p = root_2$1();
|
|
48
|
+
var text_3 = $.child(p, true);
|
|
49
|
+
$.reset(p);
|
|
50
|
+
$.template_effect(() => $.set_text(text_3, $$props.product.description));
|
|
51
|
+
$.append($$anchor, p);
|
|
52
|
+
};
|
|
53
|
+
$.if(node_2, ($$render) => {
|
|
54
|
+
if ($$props.product.description) $$render(consequent_2);
|
|
55
|
+
});
|
|
56
|
+
var div_4 = $.sibling(node_2, 2);
|
|
57
|
+
var node_3 = $.child(div_4);
|
|
58
|
+
var consequent_3 = ($$anchor) => {
|
|
59
|
+
var div_5 = root_3();
|
|
60
|
+
var text_4 = $.child(div_5);
|
|
61
|
+
$.reset(div_5);
|
|
62
|
+
$.template_effect(() => $.set_text(text_4, `Category: ${$$props.product.category ?? ""}`));
|
|
63
|
+
$.append($$anchor, div_5);
|
|
64
|
+
};
|
|
65
|
+
$.if(node_3, ($$render) => {
|
|
66
|
+
if ($$props.product.category) $$render(consequent_3);
|
|
67
|
+
});
|
|
68
|
+
var node_4 = $.sibling(node_3, 2);
|
|
69
|
+
var consequent_4 = ($$anchor) => {
|
|
70
|
+
var div_6 = root_5();
|
|
71
|
+
$.each(div_6, 21, () => $$props.product.tags, $.index, ($$anchor, tag) => {
|
|
72
|
+
var span = root_4();
|
|
73
|
+
var text_5 = $.child(span, true);
|
|
74
|
+
$.reset(span);
|
|
75
|
+
$.template_effect(() => $.set_text(text_5, $.get(tag)));
|
|
76
|
+
$.append($$anchor, span);
|
|
77
|
+
});
|
|
78
|
+
$.reset(div_6);
|
|
79
|
+
$.append($$anchor, div_6);
|
|
80
|
+
};
|
|
81
|
+
$.if(node_4, ($$render) => {
|
|
82
|
+
if ($$props.product.tags && $$props.product.tags.length > 0) $$render(consequent_4);
|
|
83
|
+
});
|
|
84
|
+
$.reset(div_4);
|
|
85
|
+
var div_7 = $.sibling(div_4, 2);
|
|
86
|
+
var node_5 = $.child(div_7);
|
|
87
|
+
var consequent_5 = ($$anchor) => {
|
|
88
|
+
Button($$anchor, {
|
|
89
|
+
type: "button",
|
|
90
|
+
variant: "secondary",
|
|
91
|
+
size: "sm",
|
|
92
|
+
onclick: () => $$props.onEdit?.($$props.product),
|
|
93
|
+
children: ($$anchor, $$slotProps) => {
|
|
94
|
+
$.next();
|
|
95
|
+
var text_6 = $.text("Edit");
|
|
96
|
+
$.append($$anchor, text_6);
|
|
97
|
+
},
|
|
98
|
+
$$slots: { default: true }
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
$.if(node_5, ($$render) => {
|
|
102
|
+
if ($$props.onEdit) $$render(consequent_5);
|
|
103
|
+
});
|
|
104
|
+
var node_6 = $.sibling(node_5, 2);
|
|
105
|
+
var consequent_6 = ($$anchor) => {
|
|
106
|
+
Button($$anchor, {
|
|
107
|
+
type: "button",
|
|
108
|
+
variant: "danger",
|
|
109
|
+
size: "sm",
|
|
110
|
+
onclick: () => $$props.onDelete?.($$props.product.id),
|
|
111
|
+
children: ($$anchor, $$slotProps) => {
|
|
112
|
+
$.next();
|
|
113
|
+
var text_7 = $.text("Delete");
|
|
114
|
+
$.append($$anchor, text_7);
|
|
115
|
+
},
|
|
116
|
+
$$slots: { default: true }
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
$.if(node_6, ($$render) => {
|
|
120
|
+
if ($$props.onDelete) $$render(consequent_6);
|
|
121
|
+
});
|
|
122
|
+
$.reset(div_7);
|
|
123
|
+
$.reset(div);
|
|
124
|
+
$.template_effect(() => $.set_text(text, $$props.product.name));
|
|
125
|
+
$.append($$anchor, div);
|
|
126
|
+
$.pop();
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/lib/components/ProductForm.svelte
|
|
130
|
+
var root = $.from_html(`<span class="error-message svelte-1hh5ovx"> </span>`);
|
|
131
|
+
var root_1 = $.from_html(`<div class="form-group svelte-1hh5ovx"><label for="name" class="svelte-1hh5ovx"> </label> <!> <!></div> <div class="form-group svelte-1hh5ovx"><label for="description" class="svelte-1hh5ovx">Description</label> <!></div> <div class="form-row svelte-1hh5ovx"><div class="form-group svelte-1hh5ovx"><label for="price" class="svelte-1hh5ovx">Price *</label> <!> <!></div> <div class="form-group svelte-1hh5ovx"><label for="category" class="svelte-1hh5ovx">Category</label> <!></div></div> <div class="form-group svelte-1hh5ovx"><label for="tags" class="svelte-1hh5ovx">Tags</label> <!> <small class="form-hint svelte-1hh5ovx"> </small></div> <div class="form-group svelte-1hh5ovx"><label class="checkbox-label svelte-1hh5ovx"><input type="checkbox" class="form-checkbox svelte-1hh5ovx"/> </label></div> <div class="form-actions svelte-1hh5ovx"><!> <!></div>`, 1);
|
|
132
|
+
var root_2 = $.from_html(`<div class="product-form-shell svelte-1hh5ovx"><!></div>`);
|
|
133
|
+
function ProductForm($$anchor, $$props) {
|
|
134
|
+
$.push($$props, true);
|
|
135
|
+
const { t } = useI18n();
|
|
136
|
+
const product = $.prop($$props, "product", 19, () => ({})), loading = $.prop($$props, "loading", 3, false);
|
|
137
|
+
const formData = $.proxy({
|
|
138
|
+
name: product().name || "",
|
|
139
|
+
description: product().description || "",
|
|
140
|
+
price: product().price || 0,
|
|
141
|
+
inStock: product().inStock ?? true,
|
|
142
|
+
category: product().category || "",
|
|
143
|
+
tags: product().tags?.join(", ") || ""
|
|
144
|
+
});
|
|
145
|
+
let errors = $.state($.proxy({}));
|
|
146
|
+
function validateForm() {
|
|
147
|
+
$.set(errors, {}, true);
|
|
148
|
+
if (!formData.name.trim()) $.get(errors).name = "Product name is required";
|
|
149
|
+
if (formData.price < 0) $.get(errors).price = "Price must be non-negative";
|
|
150
|
+
return Object.keys($.get(errors)).length === 0;
|
|
151
|
+
}
|
|
152
|
+
function handleSubmit(event) {
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
if (!validateForm()) return;
|
|
155
|
+
const productData = {
|
|
156
|
+
...product(),
|
|
157
|
+
name: formData.name.trim(),
|
|
158
|
+
description: formData.description.trim() || void 0,
|
|
159
|
+
price: formData.price,
|
|
160
|
+
inStock: formData.inStock,
|
|
161
|
+
category: formData.category.trim(),
|
|
162
|
+
tags: formData.tags ? formData.tags.split(",").map((tag) => tag.trim()).filter(Boolean) : []
|
|
163
|
+
};
|
|
164
|
+
$$props.onSubmit(productData);
|
|
165
|
+
}
|
|
166
|
+
var div = root_2();
|
|
167
|
+
Form($.child(div), {
|
|
168
|
+
onsubmit: handleSubmit,
|
|
169
|
+
class: "product-form",
|
|
170
|
+
children: ($$anchor, $$slotProps) => {
|
|
171
|
+
var fragment = root_1();
|
|
172
|
+
var div_1 = $.first_child(fragment);
|
|
173
|
+
var label = $.child(div_1);
|
|
174
|
+
var text = $.child(label, true);
|
|
175
|
+
$.reset(label);
|
|
176
|
+
var node_1 = $.sibling(label, 2);
|
|
177
|
+
{
|
|
178
|
+
let $0 = $.derived(() => $.get(errors).name ? "error" : "");
|
|
179
|
+
let $1 = $.derived(() => t(M["products.product_form.name_placeholder"]));
|
|
180
|
+
Input(node_1, {
|
|
181
|
+
id: "name",
|
|
182
|
+
type: "text",
|
|
183
|
+
get disabled() {
|
|
184
|
+
return loading();
|
|
185
|
+
},
|
|
186
|
+
get class() {
|
|
187
|
+
return $.get($0);
|
|
188
|
+
},
|
|
189
|
+
get placeholder() {
|
|
190
|
+
return $.get($1);
|
|
191
|
+
},
|
|
192
|
+
get value() {
|
|
193
|
+
return formData.name;
|
|
194
|
+
},
|
|
195
|
+
set value($$value) {
|
|
196
|
+
formData.name = $$value;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
var node_2 = $.sibling(node_1, 2);
|
|
201
|
+
var consequent = ($$anchor) => {
|
|
202
|
+
var span = root();
|
|
203
|
+
var text_1 = $.child(span, true);
|
|
204
|
+
$.reset(span);
|
|
205
|
+
$.template_effect(() => $.set_text(text_1, $.get(errors).name));
|
|
206
|
+
$.append($$anchor, span);
|
|
207
|
+
};
|
|
208
|
+
$.if(node_2, ($$render) => {
|
|
209
|
+
if ($.get(errors).name) $$render(consequent);
|
|
210
|
+
});
|
|
211
|
+
$.reset(div_1);
|
|
212
|
+
var div_2 = $.sibling(div_1, 2);
|
|
213
|
+
var node_3 = $.sibling($.child(div_2), 2);
|
|
214
|
+
{
|
|
215
|
+
let $0 = $.derived(() => t(M["products.product_form.description_placeholder"]));
|
|
216
|
+
Textarea(node_3, {
|
|
217
|
+
id: "description",
|
|
218
|
+
get disabled() {
|
|
219
|
+
return loading();
|
|
220
|
+
},
|
|
221
|
+
get placeholder() {
|
|
222
|
+
return $.get($0);
|
|
223
|
+
},
|
|
224
|
+
rows: 3,
|
|
225
|
+
get value() {
|
|
226
|
+
return formData.description;
|
|
227
|
+
},
|
|
228
|
+
set value($$value) {
|
|
229
|
+
formData.description = $$value;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
$.reset(div_2);
|
|
234
|
+
var div_3 = $.sibling(div_2, 2);
|
|
235
|
+
var div_4 = $.child(div_3);
|
|
236
|
+
var node_4 = $.sibling($.child(div_4), 2);
|
|
237
|
+
{
|
|
238
|
+
let $0 = $.derived(() => $.get(errors).price ? "error" : "");
|
|
239
|
+
Input(node_4, {
|
|
240
|
+
id: "price",
|
|
241
|
+
type: "number",
|
|
242
|
+
step: "0.01",
|
|
243
|
+
min: "0",
|
|
244
|
+
get disabled() {
|
|
245
|
+
return loading();
|
|
246
|
+
},
|
|
247
|
+
get class() {
|
|
248
|
+
return $.get($0);
|
|
249
|
+
},
|
|
250
|
+
placeholder: "0.00",
|
|
251
|
+
get value() {
|
|
252
|
+
return formData.price;
|
|
253
|
+
},
|
|
254
|
+
set value($$value) {
|
|
255
|
+
formData.price = $$value;
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
var node_5 = $.sibling(node_4, 2);
|
|
260
|
+
var consequent_1 = ($$anchor) => {
|
|
261
|
+
var span_1 = root();
|
|
262
|
+
var text_2 = $.child(span_1, true);
|
|
263
|
+
$.reset(span_1);
|
|
264
|
+
$.template_effect(() => $.set_text(text_2, $.get(errors).price));
|
|
265
|
+
$.append($$anchor, span_1);
|
|
266
|
+
};
|
|
267
|
+
$.if(node_5, ($$render) => {
|
|
268
|
+
if ($.get(errors).price) $$render(consequent_1);
|
|
269
|
+
});
|
|
270
|
+
$.reset(div_4);
|
|
271
|
+
var div_5 = $.sibling(div_4, 2);
|
|
272
|
+
var node_6 = $.sibling($.child(div_5), 2);
|
|
273
|
+
{
|
|
274
|
+
let $0 = $.derived(() => t(M["products.product_form.category_placeholder"]));
|
|
275
|
+
Input(node_6, {
|
|
276
|
+
id: "category",
|
|
277
|
+
type: "text",
|
|
278
|
+
get disabled() {
|
|
279
|
+
return loading();
|
|
280
|
+
},
|
|
281
|
+
get placeholder() {
|
|
282
|
+
return $.get($0);
|
|
283
|
+
},
|
|
284
|
+
get value() {
|
|
285
|
+
return formData.category;
|
|
286
|
+
},
|
|
287
|
+
set value($$value) {
|
|
288
|
+
formData.category = $$value;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
$.reset(div_5);
|
|
293
|
+
$.reset(div_3);
|
|
294
|
+
var div_6 = $.sibling(div_3, 2);
|
|
295
|
+
var node_7 = $.sibling($.child(div_6), 2);
|
|
296
|
+
{
|
|
297
|
+
let $0 = $.derived(() => t(M["products.product_form.tags_placeholder"]));
|
|
298
|
+
Input(node_7, {
|
|
299
|
+
id: "tags",
|
|
300
|
+
type: "text",
|
|
301
|
+
get disabled() {
|
|
302
|
+
return loading();
|
|
303
|
+
},
|
|
304
|
+
get placeholder() {
|
|
305
|
+
return $.get($0);
|
|
306
|
+
},
|
|
307
|
+
get value() {
|
|
308
|
+
return formData.tags;
|
|
309
|
+
},
|
|
310
|
+
set value($$value) {
|
|
311
|
+
formData.tags = $$value;
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
var small = $.sibling(node_7, 2);
|
|
316
|
+
var text_3 = $.child(small, true);
|
|
317
|
+
$.reset(small);
|
|
318
|
+
$.reset(div_6);
|
|
319
|
+
var div_7 = $.sibling(div_6, 2);
|
|
320
|
+
var label_1 = $.child(div_7);
|
|
321
|
+
var input = $.child(label_1);
|
|
322
|
+
$.remove_input_defaults(input);
|
|
323
|
+
var text_4 = $.sibling(input);
|
|
324
|
+
$.reset(label_1);
|
|
325
|
+
$.reset(div_7);
|
|
326
|
+
var div_8 = $.sibling(div_7, 2);
|
|
327
|
+
var node_8 = $.child(div_8);
|
|
328
|
+
var consequent_2 = ($$anchor) => {
|
|
329
|
+
Button($$anchor, {
|
|
330
|
+
type: "button",
|
|
331
|
+
variant: "secondary",
|
|
332
|
+
get onclick() {
|
|
333
|
+
return $$props.onCancel;
|
|
334
|
+
},
|
|
335
|
+
get disabled() {
|
|
336
|
+
return loading();
|
|
337
|
+
},
|
|
338
|
+
children: ($$anchor, $$slotProps) => {
|
|
339
|
+
$.next();
|
|
340
|
+
var text_5 = $.text("Cancel");
|
|
341
|
+
$.append($$anchor, text_5);
|
|
342
|
+
},
|
|
343
|
+
$$slots: { default: true }
|
|
344
|
+
});
|
|
345
|
+
};
|
|
346
|
+
$.if(node_8, ($$render) => {
|
|
347
|
+
if ($$props.onCancel) $$render(consequent_2);
|
|
348
|
+
});
|
|
349
|
+
Button($.sibling(node_8, 2), {
|
|
350
|
+
type: "submit",
|
|
351
|
+
variant: "primary",
|
|
352
|
+
get disabled() {
|
|
353
|
+
return loading();
|
|
354
|
+
},
|
|
355
|
+
children: ($$anchor, $$slotProps) => {
|
|
356
|
+
var fragment_2 = $.comment();
|
|
357
|
+
var node_10 = $.first_child(fragment_2);
|
|
358
|
+
var consequent_3 = ($$anchor) => {
|
|
359
|
+
var text_6 = $.text("Saving...");
|
|
360
|
+
$.append($$anchor, text_6);
|
|
361
|
+
};
|
|
362
|
+
var alternate = ($$anchor) => {
|
|
363
|
+
var text_7 = $.text();
|
|
364
|
+
$.template_effect(() => $.set_text(text_7, product().id ? "Update Product" : "Create Product"));
|
|
365
|
+
$.append($$anchor, text_7);
|
|
366
|
+
};
|
|
367
|
+
$.if(node_10, ($$render) => {
|
|
368
|
+
if (loading()) $$render(consequent_3);
|
|
369
|
+
else $$render(alternate, -1);
|
|
370
|
+
});
|
|
371
|
+
$.append($$anchor, fragment_2);
|
|
372
|
+
},
|
|
373
|
+
$$slots: { default: true }
|
|
374
|
+
});
|
|
375
|
+
$.reset(div_8);
|
|
376
|
+
$.template_effect(($0, $1, $2) => {
|
|
377
|
+
$.set_text(text, $0);
|
|
378
|
+
$.set_text(text_3, $1);
|
|
379
|
+
input.disabled = loading();
|
|
380
|
+
$.set_text(text_4, ` ${$2 ?? ""}`);
|
|
381
|
+
}, [
|
|
382
|
+
() => t(M["products.product_form.name_label"]),
|
|
383
|
+
() => t(M["products.product_form.tags_hint"]),
|
|
384
|
+
() => t(M["products.product_form.in_stock_label"])
|
|
385
|
+
]);
|
|
386
|
+
$.bind_checked(input, () => formData.inStock, ($$value) => formData.inStock = $$value);
|
|
387
|
+
$.append($$anchor, fragment);
|
|
388
|
+
},
|
|
389
|
+
$$slots: { default: true }
|
|
390
|
+
});
|
|
391
|
+
$.reset(div);
|
|
392
|
+
$.append($$anchor, div);
|
|
393
|
+
$.pop();
|
|
394
|
+
}
|
|
395
|
+
//#endregion
|
|
396
|
+
export { ProductCard as n, ProductForm as t };
|
|
397
|
+
|
|
398
|
+
//# sourceMappingURL=components-C6VpNVcg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components-C6VpNVcg.js","names":[],"sources":["../../../src/lib/components/ProductCard.svelte","../../../src/lib/components/ProductForm.svelte"],"sourcesContent":["<script lang=\"ts\">\nimport { Button } from '@happyvertical/smrt-ui/ui';\nimport type { ProductData } from '../types';\n\ninterface Props {\n product: ProductData;\n onEdit?: (product: ProductData) => void;\n onDelete?: (id: string) => void;\n}\n\nconst { product, onEdit, onDelete }: Props = $props();\n</script>\n\n<div class=\"product-card\">\n <div class=\"product-header\">\n <h3 class=\"product-name\">{product.name}</h3>\n {#if product.manufacturer}\n <div class=\"product-manufacturer\">{product.manufacturer}</div>\n {/if}\n </div>\n\n {#if product.model}\n <div class=\"product-model\">Model: {product.model}</div>\n {/if}\n\n {#if product.description}\n <p class=\"product-description\">{product.description}</p>\n {/if}\n\n <div class=\"product-meta\">\n {#if product.category}\n <div class=\"product-category\">Category: {product.category}</div>\n {/if}\n \n {#if product.tags && product.tags.length > 0}\n <div class=\"product-tags\">\n {#each product.tags as tag}\n <span class=\"tag\">{tag}</span>\n {/each}\n </div>\n {/if}\n </div>\n \n <div class=\"product-actions\">\n {#if onEdit}\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onclick={() => onEdit?.(product)}>\n Edit\n </Button>\n {/if}\n\n {#if onDelete}\n <Button type=\"button\" variant=\"danger\" size=\"sm\" onclick={() => onDelete?.(product.id)}>\n Delete\n </Button>\n {/if}\n </div>\n</div>\n\n<style>\n .product-card {\n border: 1px solid var(--smrt-color-outline-variant, #e2e8f0);\n border-radius: var(--smrt-radius-md, 8px);\n padding: 1rem;\n background: var(--smrt-color-surface, #fff);\n box-shadow: var(--smrt-elevation-1, 0 1px 3px color-mix(in srgb, var(--smrt-color-shadow, #000) 10%, transparent));\n transition: box-shadow 0.2s;\n }\n\n .product-card:hover {\n box-shadow: var(--smrt-elevation-2, 0 4px 6px color-mix(in srgb, var(--smrt-color-shadow, #000) 10%, transparent));\n }\n \n .product-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 0.5rem;\n }\n \n .product-name {\n margin: 0;\n font-size: var(--smrt-typography-title-medium-size, 1.125rem);\n font-weight: var(--smrt-typography-weight-semibold, 600);\n color: var(--smrt-color-on-surface, #1f2937);\n }\n\n .product-manufacturer {\n font-size: var(--smrt-typography-title-small-size, 0.875rem);\n font-weight: var(--smrt-typography-weight-medium, 500);\n color: var(--smrt-color-on-surface-variant, #6b7280);\n }\n\n .product-model {\n font-size: var(--smrt-typography-body-medium-size, 0.875rem);\n color: var(--smrt-color-on-surface-variant, #6b7280);\n margin-bottom: 0.5rem;\n }\n\n .product-category {\n font-size: var(--smrt-typography-label-medium-size, 0.75rem);\n font-weight: var(--smrt-typography-weight-medium, 500);\n color: var(--smrt-color-on-surface, #374151);\n background: var(--smrt-color-surface-container, #f3f4f6);\n padding: 0.25rem 0.5rem;\n border-radius: var(--smrt-radius-sm, 4px);\n display: inline-block;\n margin-bottom: 0.5rem;\n }\n \n .product-description {\n margin: 0.5rem 0;\n color: var(--smrt-color-on-surface-variant, #6b7280);\n font-size: var(--smrt-typography-body-medium-size, 0.875rem);\n line-height: var(--smrt-typography-body-medium-line-height, 1.4);\n }\n \n .product-meta {\n margin: 0.75rem 0;\n }\n \n \n .product-tags {\n margin-top: 0.5rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n }\n \n .tag {\n background: var(--smrt-color-surface-container, #f3f4f6);\n color: var(--smrt-color-on-surface, #374151);\n padding: 0.125rem 0.5rem;\n border-radius: var(--smrt-radius-full, 9999px);\n font-size: var(--smrt-typography-label-medium-size, 0.75rem);\n }\n\n .product-actions {\n display: flex;\n gap: 0.5rem;\n margin-top: 1rem;\n padding-top: 0.75rem;\n border-top: 1px solid var(--smrt-color-outline-variant, #f3f4f6);\n }\n</style>","<script lang=\"ts\">\nimport { Form, Input, Textarea } from '@happyvertical/smrt-ui/forms';\nimport { useI18n } from '@happyvertical/smrt-ui/i18n';\nimport { Button } from '@happyvertical/smrt-ui/ui';\nimport { M } from '../i18n.js';\nimport type { ProductData } from '../types';\n\nconst { t } = useI18n();\n\ninterface Props {\n product?: Partial<ProductData>;\n onSubmit: (product: Partial<ProductData>) => void;\n onCancel?: () => void;\n loading?: boolean;\n}\n\nconst { product = {}, onSubmit, onCancel, loading = false }: Props = $props();\n\nconst formData = $state({\n name: product.name || '',\n description: product.description || '',\n price: product.price || 0,\n inStock: product.inStock ?? true,\n category: product.category || '',\n tags: product.tags?.join(', ') || '',\n});\n\nlet errors = $state<Record<string, string>>({});\n\nfunction validateForm() {\n errors = {};\n\n if (!formData.name.trim()) {\n errors.name = 'Product name is required';\n }\n\n if (formData.price < 0) {\n errors.price = 'Price must be non-negative';\n }\n\n return Object.keys(errors).length === 0;\n}\n\nfunction handleSubmit(event: Event) {\n event.preventDefault();\n\n if (!validateForm()) {\n return;\n }\n\n const productData: Partial<ProductData> = {\n ...product,\n name: formData.name.trim(),\n description: formData.description.trim() || undefined,\n price: formData.price,\n inStock: formData.inStock,\n category: formData.category.trim(),\n tags: formData.tags\n ? formData.tags\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean)\n : [],\n };\n\n onSubmit(productData);\n}\n</script>\n\n<div class=\"product-form-shell\">\n <Form onsubmit={handleSubmit} class=\"product-form\">\n <div class=\"form-group\">\n <label for=\"name\">{t(M['products.product_form.name_label'])}</label>\n <Input\n id=\"name\"\n type=\"text\"\n bind:value={formData.name}\n disabled={loading}\n class={errors.name ? 'error' : ''}\n placeholder={t(M['products.product_form.name_placeholder'])}\n />\n {#if errors.name}\n <span class=\"error-message\">{errors.name}</span>\n {/if}\n </div>\n\n <div class=\"form-group\">\n <label for=\"description\">Description</label>\n <Textarea\n id=\"description\"\n bind:value={formData.description}\n disabled={loading}\n placeholder={t(M['products.product_form.description_placeholder'])}\n rows={3}\n ></Textarea>\n </div>\n\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label for=\"price\">Price *</label>\n <Input\n id=\"price\"\n type=\"number\"\n step=\"0.01\"\n min=\"0\"\n bind:value={formData.price}\n disabled={loading}\n class={errors.price ? 'error' : ''}\n placeholder=\"0.00\"\n />\n {#if errors.price}\n <span class=\"error-message\">{errors.price}</span>\n {/if}\n </div>\n\n <div class=\"form-group\">\n <label for=\"category\">Category</label>\n <Input\n id=\"category\"\n type=\"text\"\n bind:value={formData.category}\n disabled={loading}\n placeholder={t(M['products.product_form.category_placeholder'])}\n />\n </div>\n </div>\n\n <div class=\"form-group\">\n <label for=\"tags\">Tags</label>\n <Input\n id=\"tags\"\n type=\"text\"\n bind:value={formData.tags}\n disabled={loading}\n placeholder={t(M['products.product_form.tags_placeholder'])}\n />\n <small class=\"form-hint\">{t(M['products.product_form.tags_hint'])}</small>\n </div>\n\n <div class=\"form-group\">\n <label class=\"checkbox-label\">\n <!-- raw-primitive-allow: native checkbox; no Provider-free checkbox primitive (Toggle is a switch with different semantics, CheckboxInput requires a Provider) -->\n <input\n type=\"checkbox\"\n bind:checked={formData.inStock}\n disabled={loading}\n class=\"form-checkbox\"\n />\n {t(M['products.product_form.in_stock_label'])}\n </label>\n </div>\n\n <div class=\"form-actions\">\n {#if onCancel}\n <Button type=\"button\" variant=\"secondary\" onclick={onCancel} disabled={loading}>\n Cancel\n </Button>\n {/if}\n\n <Button type=\"submit\" variant=\"primary\" disabled={loading}>\n {#if loading}\n Saving...\n {:else}\n {product.id ? 'Update Product' : 'Create Product'}\n {/if}\n </Button>\n </div>\n </Form>\n</div>\n\n<style>\n .product-form-shell :global(.product-form) {\n max-width: 500px;\n padding: 1.5rem;\n background: var(--smrt-color-surface, #fff);\n border-radius: var(--smrt-radius-md, 8px);\n border: 1px solid var(--smrt-color-outline-variant, #e2e8f0);\n }\n\n .form-group {\n margin-bottom: 1rem;\n }\n\n .form-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1rem;\n }\n\n label {\n display: block;\n margin-bottom: 0.25rem;\n font-weight: var(--smrt-typography-weight-medium, 500);\n color: var(--smrt-color-on-surface, #374151);\n font-size: var(--smrt-typography-label-large-size, 0.875rem);\n }\n\n /* Error border on the migrated <Input>. The primitive renders the inner\n <input class=\"input error\"> inside its own component, so the scoped class\n can't reach it without :global (#1589). */\n .product-form-shell :global(.input.error) {\n border-color: var(--smrt-color-error, #dc2626);\n }\n\n .checkbox-label {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n cursor: pointer;\n }\n \n .form-checkbox {\n width: auto;\n }\n \n .form-hint {\n color: var(--smrt-color-on-surface-variant, #6b7280);\n font-size: var(--smrt-typography-body-small-size, 0.75rem);\n margin-top: 0.25rem;\n }\n\n .error-message {\n color: var(--smrt-color-error, #dc2626);\n font-size: var(--smrt-typography-label-medium-size, 0.75rem);\n margin-top: 0.25rem;\n display: block;\n }\n\n .form-actions {\n display: flex;\n gap: 0.75rem;\n justify-content: flex-end;\n margin-top: 1.5rem;\n padding-top: 1rem;\n border-top: 1px solid var(--smrt-color-outline-variant, #f3f4f6);\n }\n</style>"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/decorate.js
|
|
2
|
+
function __decorate(decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { __decorate as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generated-CM3rwUcw.js","names":[],"sources":["../../../src/lib/generated/index.ts"],"sourcesContent":["/**\n * Auto-generated SMRT Components\n *\n * This file is automatically generated by the SMRT Vite plugin.\n * It exports UI components that are auto-generated from @smrt() decorated classes.\n *\n * DO NOT EDIT THIS FILE MANUALLY - it will be overwritten.\n */\n\n// Self-register this package's manifest for consumers that import via this\n// subpath without the main entry. See src/__smrt-register__.ts (issue #1132).\nimport '../../__smrt-register__.js';\n\n// Auto-generated components will be exported here by the SMRT Vite plugin\n// Examples:\n// export { default as SmrtProductForm } from './SmrtProductForm.svelte';\n// export { default as SmrtProductTable } from './SmrtProductTable.svelte';\n// export { default as SmrtCategoryForm } from './SmrtCategoryForm.svelte';\n\n// For now, export an empty object to prevent import errors\nexport const autoGeneratedComponents = {};\n"],"mappings":";;AAoBA,IAAa,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll as t };
|