@mostfeatured/dbi 0.2.13 → 0.2.15
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/src/types/Components/HTMLComponentsV2/index.d.ts +33 -1
- package/dist/src/types/Components/HTMLComponentsV2/index.d.ts.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/index.js +408 -82
- package/dist/src/types/Components/HTMLComponentsV2/index.js.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/parser.d.ts +52 -0
- package/dist/src/types/Components/HTMLComponentsV2/parser.d.ts.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/parser.js +275 -0
- package/dist/src/types/Components/HTMLComponentsV2/parser.js.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/svelteParser.d.ts +26 -0
- package/dist/src/types/Components/HTMLComponentsV2/svelteParser.d.ts.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/svelteParser.js +509 -34
- package/dist/src/types/Components/HTMLComponentsV2/svelteParser.js.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.d.ts +10 -0
- package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.d.ts.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.js +76 -11
- package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.js.map +1 -1
- package/dist/test/index.js +76 -3
- package/dist/test/index.js.map +1 -1
- package/docs/ADVANCED_FEATURES.md +4 -0
- package/docs/API_REFERENCE.md +4 -0
- package/docs/CHAT_INPUT.md +4 -0
- package/docs/COMPONENTS.md +4 -0
- package/docs/EVENTS.md +4 -0
- package/docs/GETTING_STARTED.md +4 -0
- package/docs/LOCALIZATION.md +4 -0
- package/docs/README.md +4 -0
- package/docs/SVELTE_COMPONENTS.md +162 -6
- package/docs/llm/ADVANCED_FEATURES.txt +521 -0
- package/docs/llm/API_REFERENCE.txt +659 -0
- package/docs/llm/CHAT_INPUT.txt +514 -0
- package/docs/llm/COMPONENTS.txt +595 -0
- package/docs/llm/EVENTS.txt +449 -0
- package/docs/llm/GETTING_STARTED.txt +296 -0
- package/docs/llm/LOCALIZATION.txt +501 -0
- package/docs/llm/README.txt +193 -0
- package/docs/llm/SVELTE_COMPONENTS.txt +566 -0
- package/generated/svelte-dbi.d.ts +122 -0
- package/package.json +1 -1
- package/src/types/Components/HTMLComponentsV2/index.ts +466 -94
- package/src/types/Components/HTMLComponentsV2/parser.ts +317 -0
- package/src/types/Components/HTMLComponentsV2/svelteParser.ts +567 -35
- package/src/types/Components/HTMLComponentsV2/svelteRenderer.ts +91 -13
- package/test/index.ts +76 -3
- package/test/product-showcase.svelte +380 -24
- package/llm.txt +0 -1088
|
@@ -6,26 +6,126 @@
|
|
|
6
6
|
products = [],
|
|
7
7
|
currentIndex = 0,
|
|
8
8
|
cart = [],
|
|
9
|
-
view = "browse", // 'browse' | 'cart' | 'details'
|
|
9
|
+
view = "browse", // 'browse' | 'cart' | 'details' | 'reviews'
|
|
10
10
|
elapsedTime = 0,
|
|
11
|
+
editingProduct = null, // Product being edited in modal
|
|
12
|
+
reviewText = "", // Review text from modal
|
|
13
|
+
reviews = [], // Array of { productId, productName, rating, review, date }
|
|
14
|
+
selectedCategory = "all", // Category filter
|
|
15
|
+
sortBy = "default", // Sorting option
|
|
11
16
|
} = $props();
|
|
12
17
|
|
|
18
|
+
// Get unique categories from products
|
|
19
|
+
// Note: Uses local variables for SSR template rendering
|
|
20
|
+
function getCategories() {
|
|
21
|
+
const cats = [...new Set(products.map((p) => p.category))];
|
|
22
|
+
return cats;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Filter and sort products
|
|
26
|
+
// Note: Uses local variables for SSR template rendering
|
|
27
|
+
function getFilteredProducts() {
|
|
28
|
+
let filtered = products;
|
|
29
|
+
|
|
30
|
+
// Apply category filter
|
|
31
|
+
if (selectedCategory !== "all") {
|
|
32
|
+
filtered = filtered.filter((p) => p.category === selectedCategory);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Apply sorting
|
|
36
|
+
if (sortBy === "price-low") {
|
|
37
|
+
filtered = [...filtered].sort((a, b) => a.price - b.price);
|
|
38
|
+
} else if (sortBy === "price-high") {
|
|
39
|
+
filtered = [...filtered].sort((a, b) => b.price - a.price);
|
|
40
|
+
} else if (sortBy === "name") {
|
|
41
|
+
filtered = [...filtered].sort((a, b) => a.name.localeCompare(b.name));
|
|
42
|
+
} else if (sortBy === "rating") {
|
|
43
|
+
filtered = [...filtered].sort((a, b) => b.rating - a.rating);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return filtered;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Get current product from filtered list
|
|
50
|
+
// Note: Uses local variables for SSR template rendering
|
|
51
|
+
function getCurrentProduct() {
|
|
52
|
+
const filtered = getFilteredProducts();
|
|
53
|
+
if (filtered.length === 0) return null;
|
|
54
|
+
const idx = Math.min(currentIndex, filtered.length - 1);
|
|
55
|
+
return filtered[idx];
|
|
56
|
+
}
|
|
57
|
+
|
|
13
58
|
// Format elapsed time using stuffs
|
|
14
59
|
function formatTime(seconds) {
|
|
15
60
|
return stuffs.formatSeconds(seconds);
|
|
16
61
|
}
|
|
17
62
|
|
|
63
|
+
// Handle category selection
|
|
64
|
+
function onCategoryChange(ctx) {
|
|
65
|
+
const selected = ctx.interaction.values[0];
|
|
66
|
+
data.selectedCategory = selected;
|
|
67
|
+
data.currentIndex = 0; // Reset to first product when filter changes
|
|
68
|
+
ctx.interaction.deferUpdate();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Handle sort selection
|
|
72
|
+
function onSortChange(ctx) {
|
|
73
|
+
const selected = ctx.interaction.values[0];
|
|
74
|
+
data.sortBy = selected;
|
|
75
|
+
data.currentIndex = 0; // Reset to first product when sort changes
|
|
76
|
+
ctx.interaction.deferUpdate();
|
|
77
|
+
}
|
|
78
|
+
|
|
18
79
|
function nextProduct() {
|
|
19
|
-
|
|
80
|
+
// Get filtered length using data context
|
|
81
|
+
let filtered = data.products || [];
|
|
82
|
+
const category = data.selectedCategory ?? "all";
|
|
83
|
+
if (category !== "all") {
|
|
84
|
+
filtered = filtered.filter((p) => p.category === category);
|
|
85
|
+
}
|
|
86
|
+
const currentIdx = data.currentIndex ?? 0;
|
|
87
|
+
data.currentIndex = (currentIdx + 1) % filtered.length;
|
|
20
88
|
}
|
|
21
89
|
|
|
22
90
|
function prevProduct() {
|
|
23
|
-
|
|
91
|
+
// Get filtered length using data context
|
|
92
|
+
let filtered = data.products || [];
|
|
93
|
+
const category = data.selectedCategory ?? "all";
|
|
94
|
+
if (category !== "all") {
|
|
95
|
+
filtered = filtered.filter((p) => p.category === category);
|
|
96
|
+
}
|
|
97
|
+
const currentIdx = data.currentIndex ?? 0;
|
|
98
|
+
data.currentIndex = (currentIdx - 1 + filtered.length) % filtered.length;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Helper to get current product in handler context
|
|
102
|
+
function getProductInHandler() {
|
|
103
|
+
let filtered = data.products || [];
|
|
104
|
+
const category = data.selectedCategory ?? "all";
|
|
105
|
+
const sort = data.sortBy ?? "default";
|
|
106
|
+
|
|
107
|
+
if (category !== "all") {
|
|
108
|
+
filtered = filtered.filter((p) => p.category === category);
|
|
109
|
+
}
|
|
110
|
+
// Apply sorting
|
|
111
|
+
if (sort === "price-low") {
|
|
112
|
+
filtered = [...filtered].sort((a, b) => a.price - b.price);
|
|
113
|
+
} else if (sort === "price-high") {
|
|
114
|
+
filtered = [...filtered].sort((a, b) => b.price - a.price);
|
|
115
|
+
} else if (sort === "name") {
|
|
116
|
+
filtered = [...filtered].sort((a, b) => a.name.localeCompare(b.name));
|
|
117
|
+
} else if (sort === "rating") {
|
|
118
|
+
filtered = [...filtered].sort((a, b) => b.rating - a.rating);
|
|
119
|
+
}
|
|
120
|
+
if (filtered.length === 0) return null;
|
|
121
|
+
const idx = Math.min(data.currentIndex ?? 0, filtered.length - 1);
|
|
122
|
+
return filtered[idx];
|
|
24
123
|
}
|
|
25
124
|
|
|
26
125
|
function addToCart(ctx) {
|
|
27
|
-
const product =
|
|
28
|
-
|
|
126
|
+
const product = getProductInHandler();
|
|
127
|
+
if (!product) return;
|
|
128
|
+
data.cart = [...data.cart, product];
|
|
29
129
|
ctx.interaction.reply({
|
|
30
130
|
content:
|
|
31
131
|
"✅ Added **" +
|
|
@@ -49,6 +149,10 @@
|
|
|
49
149
|
data.view = "details";
|
|
50
150
|
}
|
|
51
151
|
|
|
152
|
+
function showReviews() {
|
|
153
|
+
data.view = "reviews";
|
|
154
|
+
}
|
|
155
|
+
|
|
52
156
|
function clearCart(ctx) {
|
|
53
157
|
data.cart = [];
|
|
54
158
|
ctx.interaction.reply({
|
|
@@ -58,7 +162,7 @@
|
|
|
58
162
|
}
|
|
59
163
|
|
|
60
164
|
function checkout(ctx) {
|
|
61
|
-
if (cart.length === 0) {
|
|
165
|
+
if (data.cart.length === 0) {
|
|
62
166
|
ctx.interaction.reply({
|
|
63
167
|
content: "Cart is empty!",
|
|
64
168
|
flags: ["Ephemeral"],
|
|
@@ -66,20 +170,100 @@
|
|
|
66
170
|
noRender(); // Cart boşsa UI güncellemeye gerek yok
|
|
67
171
|
return;
|
|
68
172
|
}
|
|
69
|
-
const total = cart.reduce((sum, p) => sum + p.price, 0);
|
|
173
|
+
const total = data.cart.reduce((sum, p) => sum + p.price, 0);
|
|
70
174
|
ctx.interaction.reply({
|
|
71
175
|
content:
|
|
72
|
-
"💳 **Order Placed!**\\nItems: " +
|
|
176
|
+
"💳 **Order Placed!**\\nItems: " +
|
|
177
|
+
data.cart.length +
|
|
178
|
+
"\\nTotal: $" +
|
|
179
|
+
total,
|
|
73
180
|
flags: ["Ephemeral"],
|
|
74
181
|
});
|
|
75
182
|
data.cart = [];
|
|
76
183
|
data.view = "browse";
|
|
77
184
|
}
|
|
78
185
|
|
|
186
|
+
// Open the review modal for current product
|
|
187
|
+
async function openReviewModal(ctx) {
|
|
188
|
+
// Store which product we're reviewing
|
|
189
|
+
const product = getProductInHandler();
|
|
190
|
+
if (!product) {
|
|
191
|
+
ctx.interaction.reply({
|
|
192
|
+
content: "No product selected!",
|
|
193
|
+
flags: ["Ephemeral"],
|
|
194
|
+
});
|
|
195
|
+
noRender();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
data.editingProduct = product;
|
|
199
|
+
|
|
200
|
+
// Show the modal and await the response
|
|
201
|
+
const { fields, interaction } = await showModal("review-modal");
|
|
202
|
+
|
|
203
|
+
// Handle the response - rating now comes from string-select as an array
|
|
204
|
+
const ratingArray = fields.rating || ["5"]; // string-select returns array
|
|
205
|
+
const rating = Array.isArray(ratingArray) ? ratingArray[0] : ratingArray;
|
|
206
|
+
const review = fields.review || "";
|
|
207
|
+
|
|
208
|
+
// Add review to the reviews array (use data.reviews for current value after await)
|
|
209
|
+
data.reviews = [
|
|
210
|
+
...data.reviews,
|
|
211
|
+
{
|
|
212
|
+
productId: currentIndex,
|
|
213
|
+
productName: product?.name,
|
|
214
|
+
rating: parseInt(rating),
|
|
215
|
+
review: review,
|
|
216
|
+
date: new Date().toLocaleDateString(),
|
|
217
|
+
},
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
interaction.reply({
|
|
221
|
+
content: `⭐ **Review Submitted!**\n**Product:** ${product?.name}\n**Rating:** ${rating}/5\n**Review:** ${review}`,
|
|
222
|
+
flags: ["Ephemeral"],
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Clear editing state
|
|
226
|
+
data.editingProduct = null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Open quantity modal for adding to cart
|
|
230
|
+
async function openQuantityModal(ctx) {
|
|
231
|
+
const product = getProductInHandler();
|
|
232
|
+
if (!product) {
|
|
233
|
+
ctx.interaction.reply({
|
|
234
|
+
content: "No product selected!",
|
|
235
|
+
flags: ["Ephemeral"],
|
|
236
|
+
});
|
|
237
|
+
noRender();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
data.editingProduct = product;
|
|
241
|
+
|
|
242
|
+
// Show the modal and await the response
|
|
243
|
+
const { fields, interaction } = await showModal("quantity-modal");
|
|
244
|
+
|
|
245
|
+
const quantity = parseInt(fields.quantity) || 1;
|
|
246
|
+
|
|
247
|
+
// Add multiple of the same product
|
|
248
|
+
for (let i = 0; i < quantity; i++) {
|
|
249
|
+
data.cart = [...data.cart, product];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
interaction.reply({
|
|
253
|
+
content: `✅ Added **${quantity}x ${product?.name}** to cart! (Cart: ${data.cart.length} items)`,
|
|
254
|
+
flags: ["Ephemeral"],
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
data.editingProduct = null;
|
|
258
|
+
}
|
|
259
|
+
|
|
79
260
|
onMount(() => {
|
|
80
261
|
data.elapsedTime = 0;
|
|
81
262
|
const interval = setInterval(() => {
|
|
82
|
-
|
|
263
|
+
// Use lowPriorityUpdate to skip render if a user interaction is in progress
|
|
264
|
+
lowPriorityUpdate(() => {
|
|
265
|
+
data.elapsedTime += 1;
|
|
266
|
+
});
|
|
83
267
|
}, 1000);
|
|
84
268
|
|
|
85
269
|
return () => {
|
|
@@ -88,6 +272,7 @@
|
|
|
88
272
|
});
|
|
89
273
|
</script>
|
|
90
274
|
|
|
275
|
+
<!-- Main UI Components -->
|
|
91
276
|
<components>
|
|
92
277
|
{#if view === "browse"}
|
|
93
278
|
<container accent-color="5865F2">
|
|
@@ -96,32 +281,115 @@
|
|
|
96
281
|
<components>
|
|
97
282
|
<text-display>## 🛍️ Product Showcase</text-display>
|
|
98
283
|
<text-display
|
|
99
|
-
>**{
|
|
100
|
-
{
|
|
284
|
+
>**{getCurrentProduct()?.name || "No products"}**
|
|
285
|
+
{getCurrentProduct()?.description || ""}</text-display
|
|
101
286
|
>
|
|
102
|
-
<text-display
|
|
287
|
+
<text-display
|
|
288
|
+
>💰 **\${getCurrentProduct()?.price || 0}**</text-display
|
|
103
289
|
>
|
|
104
290
|
</components>
|
|
105
|
-
<thumbnail media={
|
|
291
|
+
<thumbnail media={getCurrentProduct()?.image}></thumbnail>
|
|
106
292
|
</section>
|
|
107
293
|
|
|
108
294
|
<separator></separator>
|
|
109
295
|
|
|
296
|
+
<!-- Category Filter -->
|
|
297
|
+
<action-row>
|
|
298
|
+
<string-select
|
|
299
|
+
placeholder="🏷️ Filter by Category"
|
|
300
|
+
onchange={onCategoryChange}
|
|
301
|
+
>
|
|
302
|
+
<option
|
|
303
|
+
value="all"
|
|
304
|
+
description="Show all products"
|
|
305
|
+
default={selectedCategory === "all"}
|
|
306
|
+
>
|
|
307
|
+
📦 All Categories
|
|
308
|
+
</option>
|
|
309
|
+
{#each getCategories() as cat}
|
|
310
|
+
<option
|
|
311
|
+
value={cat}
|
|
312
|
+
description="Filter by {cat}"
|
|
313
|
+
default={selectedCategory === cat}
|
|
314
|
+
>
|
|
315
|
+
🏷️ {cat}
|
|
316
|
+
</option>
|
|
317
|
+
{/each}
|
|
318
|
+
</string-select>
|
|
319
|
+
</action-row>
|
|
320
|
+
|
|
321
|
+
<!-- Sort Options -->
|
|
322
|
+
<action-row>
|
|
323
|
+
<string-select placeholder="📊 Sort Products" onchange={onSortChange}>
|
|
324
|
+
<option
|
|
325
|
+
value="default"
|
|
326
|
+
description="Original order"
|
|
327
|
+
default={sortBy === "default"}
|
|
328
|
+
>
|
|
329
|
+
📋 Default Order
|
|
330
|
+
</option>
|
|
331
|
+
<option
|
|
332
|
+
value="price-low"
|
|
333
|
+
description="Cheapest first"
|
|
334
|
+
default={sortBy === "price-low"}
|
|
335
|
+
>
|
|
336
|
+
💰 Price: Low to High
|
|
337
|
+
</option>
|
|
338
|
+
<option
|
|
339
|
+
value="price-high"
|
|
340
|
+
description="Most expensive first"
|
|
341
|
+
default={sortBy === "price-high"}
|
|
342
|
+
>
|
|
343
|
+
💎 Price: High to Low
|
|
344
|
+
</option>
|
|
345
|
+
<option
|
|
346
|
+
value="name"
|
|
347
|
+
description="A-Z alphabetically"
|
|
348
|
+
default={sortBy === "name"}
|
|
349
|
+
>
|
|
350
|
+
🔤 Name: A-Z
|
|
351
|
+
</option>
|
|
352
|
+
<option
|
|
353
|
+
value="rating"
|
|
354
|
+
description="Best rated first"
|
|
355
|
+
default={sortBy === "rating"}
|
|
356
|
+
>
|
|
357
|
+
⭐ Rating: Best First
|
|
358
|
+
</option>
|
|
359
|
+
</string-select>
|
|
360
|
+
</action-row>
|
|
361
|
+
|
|
362
|
+
<separator></separator>
|
|
363
|
+
|
|
110
364
|
<text-display
|
|
111
|
-
>📦 Product {currentIndex + 1} of {
|
|
112
|
-
|
|
365
|
+
>📦 Product {currentIndex + 1} of {getFilteredProducts().length} | 🛒 Cart:
|
|
366
|
+
{cart.length}
|
|
367
|
+
items {selectedCategory !== "all"
|
|
368
|
+
? `| 🏷️ ${selectedCategory}`
|
|
369
|
+
: ""}</text-display
|
|
113
370
|
>
|
|
114
371
|
|
|
115
372
|
<action-row>
|
|
116
373
|
<button style="Secondary" onclick={prevProduct}>◀️ Prev</button>
|
|
117
374
|
<button style="Secondary" onclick={nextProduct}>Next ▶️</button>
|
|
118
|
-
<button style="Success" onclick={
|
|
375
|
+
<button style="Success" onclick={openQuantityModal}
|
|
376
|
+
>🛒 Add to Cart</button
|
|
377
|
+
>
|
|
119
378
|
<button style="Primary" onclick={showDetails}>📋 Details</button>
|
|
120
379
|
<button style="Primary" onclick={showCart}
|
|
121
380
|
>🛒 View Cart ({cart.length})</button
|
|
122
381
|
>
|
|
123
382
|
</action-row>
|
|
124
383
|
|
|
384
|
+
<action-row>
|
|
385
|
+
<button style="Secondary" onclick={openReviewModal}
|
|
386
|
+
>⭐ Write Review</button
|
|
387
|
+
>
|
|
388
|
+
<button style="Secondary" onclick={showReviews}
|
|
389
|
+
>📝 View Reviews ({reviews.length})</button
|
|
390
|
+
>
|
|
391
|
+
</action-row>
|
|
392
|
+
|
|
125
393
|
<separator></separator>
|
|
126
394
|
<text-display>⏱️ Session: {formatTime(elapsedTime)}</text-display>
|
|
127
395
|
</components>
|
|
@@ -164,31 +432,75 @@
|
|
|
164
432
|
<section>
|
|
165
433
|
<components>
|
|
166
434
|
<text-display>## 📋 Product Details</text-display>
|
|
167
|
-
<text-display>**{
|
|
435
|
+
<text-display>**{getCurrentProduct()?.name}**</text-display>
|
|
168
436
|
</components>
|
|
169
|
-
<thumbnail media={
|
|
437
|
+
<thumbnail media={getCurrentProduct()?.image}></thumbnail>
|
|
170
438
|
</section>
|
|
171
439
|
|
|
172
440
|
<separator></separator>
|
|
173
441
|
|
|
174
442
|
<text-display
|
|
175
443
|
>### Description
|
|
176
|
-
{
|
|
444
|
+
{getCurrentProduct()?.description}</text-display
|
|
177
445
|
>
|
|
178
446
|
|
|
179
447
|
<separator></separator>
|
|
180
448
|
|
|
181
449
|
<text-display
|
|
182
|
-
>### Specifications • **Category:** {
|
|
183
|
-
• **Rating:** ⭐ {
|
|
184
|
-
|
|
185
|
-
]?.stock} available • **Price:** 💰 **\${products[currentIndex]
|
|
450
|
+
>### Specifications • **Category:** {getCurrentProduct()?.category}
|
|
451
|
+
• **Rating:** ⭐ {getCurrentProduct()?.rating}/5 • **Stock:** {getCurrentProduct()
|
|
452
|
+
?.stock} available • **Price:** 💰 **\${getCurrentProduct()
|
|
186
453
|
?.price}**</text-display
|
|
187
454
|
>
|
|
188
455
|
|
|
189
456
|
<action-row>
|
|
190
457
|
<button style="Secondary" onclick={showBrowse}>◀️ Back</button>
|
|
191
|
-
<button style="Success" onclick={
|
|
458
|
+
<button style="Success" onclick={openQuantityModal}
|
|
459
|
+
>🛒 Add to Cart</button
|
|
460
|
+
>
|
|
461
|
+
<button style="Secondary" onclick={openReviewModal}
|
|
462
|
+
>⭐ Write Review</button
|
|
463
|
+
>
|
|
464
|
+
</action-row>
|
|
465
|
+
|
|
466
|
+
<separator></separator>
|
|
467
|
+
<text-display>⏱️ Session: {formatTime(elapsedTime)}</text-display>
|
|
468
|
+
</components>
|
|
469
|
+
</container>
|
|
470
|
+
{:else if view === "reviews"}
|
|
471
|
+
<container accent-color="EB459E">
|
|
472
|
+
<components>
|
|
473
|
+
<text-display>## 📝 Product Reviews</text-display>
|
|
474
|
+
|
|
475
|
+
{#if reviews.length === 0}
|
|
476
|
+
<text-display
|
|
477
|
+
>*No reviews yet. Be the first to write one!*</text-display
|
|
478
|
+
>
|
|
479
|
+
{:else}
|
|
480
|
+
{#each reviews as r, i}
|
|
481
|
+
<text-display
|
|
482
|
+
>**{r.productName}** - {"⭐".repeat(r.rating)}{"☆".repeat(
|
|
483
|
+
5 - r.rating
|
|
484
|
+
)} ({r.rating}/5)
|
|
485
|
+
{r.review ? `> ${r.review}` : ""}
|
|
486
|
+
*{r.date}*</text-display
|
|
487
|
+
>
|
|
488
|
+
{#if i < reviews.length - 1}
|
|
489
|
+
<separator></separator>
|
|
490
|
+
{/if}
|
|
491
|
+
{/each}
|
|
492
|
+
{/if}
|
|
493
|
+
|
|
494
|
+
<separator></separator>
|
|
495
|
+
<text-display>**Total Reviews:** {reviews.length}</text-display>
|
|
496
|
+
|
|
497
|
+
<action-row>
|
|
498
|
+
<button style="Secondary" onclick={showBrowse}
|
|
499
|
+
>◀️ Back to Browse</button
|
|
500
|
+
>
|
|
501
|
+
<button style="Primary" onclick={openReviewModal}
|
|
502
|
+
>⭐ Write Review</button
|
|
503
|
+
>
|
|
192
504
|
</action-row>
|
|
193
505
|
|
|
194
506
|
<separator></separator>
|
|
@@ -197,3 +509,47 @@
|
|
|
197
509
|
</container>
|
|
198
510
|
{/if}
|
|
199
511
|
</components>
|
|
512
|
+
|
|
513
|
+
<!-- Review Modal: Now using new Label component structure with String Select -->
|
|
514
|
+
<components
|
|
515
|
+
type="modal"
|
|
516
|
+
id="review-modal"
|
|
517
|
+
title="Write a Review for {editingProduct?.name || 'Product'}"
|
|
518
|
+
>
|
|
519
|
+
<field label="Rating" description="How would you rate this product?">
|
|
520
|
+
<string-select id="rating" placeholder="Select a rating">
|
|
521
|
+
<option value="5" emoji="⭐">5 Stars - Excellent!</option>
|
|
522
|
+
<option value="4" emoji="⭐">4 Stars - Great</option>
|
|
523
|
+
<option value="3" emoji="⭐">3 Stars - Good</option>
|
|
524
|
+
<option value="2" emoji="⭐">2 Stars - Fair</option>
|
|
525
|
+
<option value="1" emoji="⭐">1 Star - Poor</option>
|
|
526
|
+
</string-select>
|
|
527
|
+
</field>
|
|
528
|
+
<field
|
|
529
|
+
label="Your Review"
|
|
530
|
+
description="Share your experience with this product"
|
|
531
|
+
>
|
|
532
|
+
<text-input
|
|
533
|
+
id="review"
|
|
534
|
+
placeholder="Write your detailed review here..."
|
|
535
|
+
style="Paragraph"
|
|
536
|
+
></text-input>
|
|
537
|
+
</field>
|
|
538
|
+
</components>
|
|
539
|
+
|
|
540
|
+
<!-- Quantity Modal: Opens when user clicks "Add to Cart" -->
|
|
541
|
+
<components
|
|
542
|
+
type="modal"
|
|
543
|
+
id="quantity-modal"
|
|
544
|
+
title="Add {editingProduct?.name || 'Product'} to Cart"
|
|
545
|
+
>
|
|
546
|
+
<field label="Quantity" description="How many would you like to add?">
|
|
547
|
+
<text-input
|
|
548
|
+
id="quantity"
|
|
549
|
+
placeholder="Enter quantity (1-10)"
|
|
550
|
+
style="Short"
|
|
551
|
+
value="1"
|
|
552
|
+
required
|
|
553
|
+
></text-input>
|
|
554
|
+
</field>
|
|
555
|
+
</components>
|