@deneb-ui/create-template 2.0.107 → 2.0.108

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deneb-ui/create-template",
3
- "version": "2.0.107",
3
+ "version": "2.0.108",
4
4
  "description": "Scaffold production-ready DENEB UI storefront templates for Next.js in seconds.",
5
5
  "bin": {
6
6
  "create-deneb-template": "bin/index.js",
@@ -162,6 +162,24 @@ Edit `src/data/site-data.json` under `template.structure.theme`:
162
162
  ```
163
163
  3. Add the page content to `src/data/site-data.json`.
164
164
 
165
+ ### One-page section navigation
166
+
167
+ Use route links such as `/about` only when that route is exported. For a
168
+ one-page design, link to a stable section ID instead:
169
+
170
+ ```tsx
171
+ <a href="#about">About</a>
172
+ <section id="about" data-section-id="about" data-design-section="about">
173
+ {/* About content */}
174
+ </section>
175
+ ```
176
+
177
+ Every visible navigation item must resolve to exactly one visible section in
178
+ the exported Home document. Keep labels such as Home, About, Services, Gallery,
179
+ and Contact as neutral template UI copy rather than AI-generated marketing
180
+ text. Test direct `/#about` loading and sticky-header scrolling before
181
+ packaging.
182
+
165
183
  ---
166
184
 
167
185
  ## Packaging for Fivora
@@ -347,6 +347,21 @@
347
347
  "type": "text",
348
348
  "label": "Feature"
349
349
  }
350
+ },
351
+ {
352
+ "key": "price",
353
+ "type": "number",
354
+ "label": "Price"
355
+ },
356
+ {
357
+ "key": "priceLabel",
358
+ "type": "text",
359
+ "label": "Price Label (e.g. Starting from, per hour)"
360
+ },
361
+ {
362
+ "key": "showPrice",
363
+ "type": "boolean",
364
+ "label": "Show Price on Storefront"
350
365
  }
351
366
  ]
352
367
  },
@@ -442,8 +457,7 @@
442
457
  {
443
458
  "key": "price",
444
459
  "type": "number",
445
- "label": "Price",
446
- "required": true
460
+ "label": "Price"
447
461
  },
448
462
  {
449
463
  "key": "compareAtPrice",
@@ -469,6 +483,31 @@
469
483
  "key": "badge",
470
484
  "type": "text",
471
485
  "label": "Badge"
486
+ },
487
+ {
488
+ "key": "showPrice",
489
+ "type": "boolean",
490
+ "label": "Show Price on Storefront"
491
+ },
492
+ {
493
+ "key": "isPriceRange",
494
+ "type": "boolean",
495
+ "label": "Enable Price Range"
496
+ },
497
+ {
498
+ "key": "minPrice",
499
+ "type": "number",
500
+ "label": "Minimum Price"
501
+ },
502
+ {
503
+ "key": "maxPrice",
504
+ "type": "number",
505
+ "label": "Maximum Price"
506
+ },
507
+ {
508
+ "key": "priceRange",
509
+ "type": "text",
510
+ "label": "Price Range Display"
472
511
  }
473
512
  ]
474
513
  }
@@ -18,7 +18,7 @@
18
18
  "next": "15.5.23",
19
19
  "react": "19.2.8",
20
20
  "react-dom": "19.2.8",
21
- "@deneb-ui/ui": "^2.0.107"
21
+ "@deneb-ui/ui": "^2.0.108"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@eslint/eslintrc": "3.3.6",
@@ -28,7 +28,7 @@
28
28
  "eslint": "9.39.5",
29
29
  "eslint-config-next": "15.5.23",
30
30
  "typescript": "5.8.3",
31
- "@deneb-ui/cli": "^2.0.107",
31
+ "@deneb-ui/cli": "^2.0.108",
32
32
  "tailwindcss": "^3.4.17",
33
33
  "postcss": "8.5.26",
34
34
  "autoprefixer": "^10.4.20"
@@ -255,6 +255,28 @@ export default function HomePage() {
255
255
  const badge = contentText(product.badge);
256
256
  const category = contentText(product.category);
257
257
 
258
+ const itemShowPrice =
259
+ (product as any)?.showPrice !== undefined
260
+ ? Boolean((product as any).showPrice)
261
+ : ((product as any)?.customData?.showPrice !== undefined
262
+ ? Boolean((product as any).customData.showPrice)
263
+ : true);
264
+ const isRange = Boolean(
265
+ (product as any)?.isPriceRange ||
266
+ ((product as any)?.minPrice !== undefined && (product as any)?.maxPrice !== undefined) ||
267
+ (product as any)?.priceRange
268
+ );
269
+ let displayPrice = `${currency} ${price}`;
270
+ if (isRange) {
271
+ if ((product as any).priceRange) {
272
+ displayPrice = String((product as any).priceRange);
273
+ } else {
274
+ const min = (product as any).minPrice !== undefined ? (product as any).minPrice : price;
275
+ const max = (product as any).maxPrice !== undefined ? (product as any).maxPrice : price;
276
+ displayPrice = `${currency} ${min} – ${currency} ${max}`;
277
+ }
278
+ }
279
+
258
280
  return (
259
281
  <EditableCard
260
282
  key={productId ?? index}
@@ -311,25 +333,45 @@ export default function HomePage() {
311
333
  defaultValue={contentText(product.description)}
312
334
  />
313
335
 
314
- <div style={{ display: 'flex', alignItems: 'baseline', gap: '0.5rem', marginTop: 'auto', paddingTop: '0.75rem' }}>
315
- <EditableText
316
- as="span"
317
- weight="bold"
318
- size="xl"
319
- color="primary"
320
- data-preview-field-path={`products[${index}].price`}
321
- defaultValue={`${currency} ${price}`}
322
- />
323
- {compareAt > 0 && (
336
+ {itemShowPrice ? (
337
+ <div style={{ display: 'flex', alignItems: 'baseline', gap: '0.5rem', marginTop: 'auto', paddingTop: '0.75rem', flexWrap: 'wrap' }}>
324
338
  <EditableText
325
339
  as="span"
326
- color="muted"
327
- style={{ textDecoration: 'line-through', fontSize: '0.875rem' }}
328
- data-preview-field-path={`products[${index}].compareAtPrice`}
329
- defaultValue={`${currency} ${compareAt}`}
340
+ weight="bold"
341
+ size="xl"
342
+ color="primary"
343
+ data-preview-field-path={`products[${index}].price`}
344
+ defaultValue={displayPrice}
330
345
  />
331
- )}
332
- </div>
346
+ {compareAt > 0 && !isRange && (
347
+ <EditableText
348
+ as="span"
349
+ color="muted"
350
+ style={{ textDecoration: 'line-through', fontSize: '0.875rem' }}
351
+ data-preview-field-path={`products[${index}].compareAtPrice`}
352
+ defaultValue={`${currency} ${compareAt}`}
353
+ />
354
+ )}
355
+ </div>
356
+ ) : (
357
+ <div style={{ marginTop: 'auto', paddingTop: '0.75rem' }}>
358
+ <span
359
+ style={{
360
+ display: 'inline-flex',
361
+ alignItems: 'center',
362
+ padding: '3px 8px',
363
+ borderRadius: '6px',
364
+ fontSize: '0.75rem',
365
+ fontWeight: 600,
366
+ backgroundColor: 'rgba(255, 255, 255, 0.08)',
367
+ color: 'var(--muted-text, #94a3b8)',
368
+ border: '1px solid rgba(255, 255, 255, 0.12)',
369
+ }}
370
+ >
371
+ Price on Request
372
+ </span>
373
+ </div>
374
+ )}
333
375
 
334
376
  <div style={{ marginTop: '0.75rem' }}>
335
377
  <a
@@ -93,6 +93,72 @@ export default function ServicesPage() {
93
93
  ))}
94
94
  </ul>
95
95
  )}
96
+ {(() => {
97
+ const itemShowPrice =
98
+ (service as any)?.showPrice !== undefined
99
+ ? Boolean((service as any).showPrice)
100
+ : ((service as any)?.customData?.showPrice !== undefined
101
+ ? Boolean((service as any).customData.showPrice)
102
+ : true);
103
+ const price = (service as any).price;
104
+ const priceLabel = contentText((service as any).priceLabel);
105
+ const hasNumericPrice = price !== undefined && price !== null && String(price).trim() !== '';
106
+ const formattedPrice = hasNumericPrice
107
+ ? typeof price === 'number'
108
+ ? `LKR ${price.toLocaleString()}`
109
+ : String(price).includes('LKR')
110
+ ? String(price)
111
+ : `LKR ${price}`
112
+ : '';
113
+
114
+ if (itemShowPrice && (hasNumericPrice || priceLabel)) {
115
+ return (
116
+ <div style={{ marginTop: 'auto', paddingTop: '0.75rem', display: 'flex', alignItems: 'baseline', gap: '0.375rem', flexWrap: 'wrap' }}>
117
+ {hasNumericPrice && (
118
+ <EditableText
119
+ as="span"
120
+ weight="bold"
121
+ size="lg"
122
+ color="primary"
123
+ data-preview-field-path={`services[${index}].price`}
124
+ defaultValue={formattedPrice}
125
+ />
126
+ )}
127
+ {priceLabel && (
128
+ <EditableText
129
+ as="span"
130
+ color="muted"
131
+ data-preview-field-path={`services[${index}].priceLabel`}
132
+ defaultValue={priceLabel}
133
+ style={{ fontSize: '0.8125rem' }}
134
+ />
135
+ )}
136
+ </div>
137
+ );
138
+ }
139
+ if (!itemShowPrice) {
140
+ return (
141
+ <div style={{ marginTop: 'auto', paddingTop: '0.75rem' }}>
142
+ <span
143
+ style={{
144
+ display: 'inline-flex',
145
+ alignItems: 'center',
146
+ padding: '3px 8px',
147
+ borderRadius: '6px',
148
+ fontSize: '0.75rem',
149
+ fontWeight: 600,
150
+ backgroundColor: 'rgba(255, 255, 255, 0.08)',
151
+ color: 'var(--muted-text, #94a3b8)',
152
+ border: '1px solid rgba(255, 255, 255, 0.12)',
153
+ }}
154
+ >
155
+ Quote on Request
156
+ </span>
157
+ </div>
158
+ );
159
+ }
160
+ return null;
161
+ })()}
96
162
  </EditableCard>
97
163
  );
98
164
  })}