@apptimate/ui 6.1.0 → 6.2.0

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": "@apptimate/ui",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "main": "src/index.tsx",
5
5
  "types": "src/index.tsx",
6
6
  "dependencies": {
@@ -22,7 +22,7 @@ export interface ItemFormData {
22
22
  category_id: string; category_name: string;
23
23
  brand_id: string; brand_name: string;
24
24
  description: string;
25
- sale_price: string; cost_price: string;
25
+ sale_price: string; cost_price: string; min_sales_price: string;
26
26
  has_variants: boolean; tracking_type: string; valuation_method: string;
27
27
  // Page 2: Stock & Config
28
28
  uom_id: string; uom_name: string;
@@ -41,7 +41,7 @@ export interface ItemFormData {
41
41
 
42
42
  export interface VariantRow {
43
43
  id?: number; sku: string; variant_name: string; barcode: string;
44
- sale_price: string; cost_price: string; attribute_values: Record<string, string>;
44
+ sale_price: string; cost_price: string; min_sales_price: string; attribute_values: Record<string, string>;
45
45
  }
46
46
 
47
47
  export const emptyFormData: ItemFormData = {
@@ -49,7 +49,7 @@ export const emptyFormData: ItemFormData = {
49
49
  category_id: "", category_name: "",
50
50
  brand_id: "", brand_name: "",
51
51
  description: "",
52
- sale_price: "0", cost_price: "0",
52
+ sale_price: "0", cost_price: "0", min_sales_price: "0",
53
53
  has_variants: false, tracking_type: "none", valuation_method: "fifo",
54
54
  uom_id: "", uom_name: "", purchase_uom_id: "", sales_uom_id: "",
55
55
  purchase_uom_conversion: "1", sales_uom_conversion: "1",
@@ -473,7 +473,7 @@ export default function ItemFormWizard({
473
473
  const addVariantRow = () => {
474
474
  setFormData((p) => ({
475
475
  ...p,
476
- variants: [...p.variants, { sku: "", variant_name: "", barcode: "", sale_price: "", cost_price: "", attribute_values: {} }],
476
+ variants: [...p.variants, { sku: "", variant_name: "", barcode: "", sale_price: "", cost_price: "", min_sales_price: "", attribute_values: {} }],
477
477
  }));
478
478
  };
479
479
 
@@ -524,6 +524,7 @@ export default function ItemFormWizard({
524
524
  barcode: "",
525
525
  sale_price: formData.sale_price || "0",
526
526
  cost_price: formData.cost_price || "0",
527
+ min_sales_price: formData.min_sales_price || "0",
527
528
  attribute_values: combo,
528
529
  };
529
530
  });
@@ -543,13 +544,16 @@ export default function ItemFormWizard({
543
544
  {!formData.has_variants ? (
544
545
  <div className="p-4 bg-gray-50/50 rounded-xl border border-gray-100">
545
546
  <p className="text-[11px] font-bold text-gray-400 uppercase tracking-wider mb-3">Item Pricing</p>
546
- <div className="grid grid-cols-2 gap-4">
547
+ <div className="grid grid-cols-3 gap-4">
547
548
  {formData.is_available_sell && (
548
549
  <Input label="Sale Price" type="number" placeholder="0.00" value={formData.sale_price} onChange={(e) => update("sale_price", e.target.value)} />
549
550
  )}
550
551
  {formData.is_available_purchase && (
551
552
  <Input label="Cost Price" type="number" placeholder="0.00" value={formData.cost_price} onChange={(e) => update("cost_price", e.target.value)} />
552
553
  )}
554
+ {formData.is_available_sell && (
555
+ <Input label="Min Sales Price" type="number" placeholder="0.00" value={formData.min_sales_price} onChange={(e) => update("min_sales_price", e.target.value)} />
556
+ )}
553
557
  </div>
554
558
  </div>
555
559
  ) : (
@@ -577,14 +581,24 @@ export default function ItemFormWizard({
577
581
  </th>
578
582
  <th className="px-4 py-3 font-medium align-top">Barcode</th>
579
583
  {formData.is_available_sell && (
580
- <th className="px-4 py-3 font-medium w-[140px] align-top">
581
- <div className="flex flex-col items-start gap-1">
582
- <span>Sale Price</span>
583
- {formData.variants.length > 1 && (
584
- <button type="button" onClick={() => copyToAll("sale_price")} className="text-primary-600 hover:text-primary-700 font-semibold normal-case text-[11px]">Copy to All</button>
585
- )}
586
- </div>
587
- </th>
584
+ <>
585
+ <th className="px-4 py-3 font-medium w-[140px] align-top">
586
+ <div className="flex flex-col items-start gap-1">
587
+ <span>Sale Price</span>
588
+ {formData.variants.length > 1 && (
589
+ <button type="button" onClick={() => copyToAll("sale_price")} className="text-primary-600 hover:text-primary-700 font-semibold normal-case text-[11px]">Copy to All</button>
590
+ )}
591
+ </div>
592
+ </th>
593
+ <th className="px-4 py-3 font-medium w-[140px] align-top">
594
+ <div className="flex flex-col items-start gap-1">
595
+ <span>Min Sale Price</span>
596
+ {formData.variants.length > 1 && (
597
+ <button type="button" onClick={() => copyToAll("min_sales_price")} className="text-primary-600 hover:text-primary-700 font-semibold normal-case text-[11px]">Copy to All</button>
598
+ )}
599
+ </div>
600
+ </th>
601
+ </>
588
602
  )}
589
603
  {formData.is_available_purchase && (
590
604
  <th className="px-4 py-3 font-medium w-[140px] align-top">
@@ -612,9 +626,14 @@ export default function ItemFormWizard({
612
626
  <Input placeholder="Leave empty to auto-generate" value={v.barcode} onChange={(e) => updateVariant(idx, "barcode", e.target.value)} />
613
627
  </td>
614
628
  {formData.is_available_sell && (
615
- <td className="p-2 min-w-[120px] align-top">
616
- <Input type="number" placeholder="0.00" value={v.sale_price} onChange={(e) => updateVariant(idx, "sale_price", e.target.value)} />
617
- </td>
629
+ <>
630
+ <td className="p-2 min-w-[120px] align-top">
631
+ <Input type="number" placeholder="0.00" value={v.sale_price} onChange={(e) => updateVariant(idx, "sale_price", e.target.value)} />
632
+ </td>
633
+ <td className="p-2 min-w-[120px] align-top">
634
+ <Input type="number" placeholder="0.00" value={v.min_sales_price} onChange={(e) => updateVariant(idx, "min_sales_price", e.target.value)} />
635
+ </td>
636
+ </>
618
637
  )}
619
638
  {formData.is_available_purchase && (
620
639
  <td className="p-2 min-w-[120px] align-top">
@@ -213,6 +213,7 @@ export function replaceTokens(text: string, entityData: any): string {
213
213
  if ((val === undefined || val === null) && path.trim() === "inventory_item_variants.sku") val = data.variants?.[0]?.sku || data.sku || data.code;
214
214
  if ((val === undefined || val === null) && path.trim() === "inventory_item_variants.barcode") val = data.variants?.[0]?.barcode || data.barcode || data.sku || data.code;
215
215
  if ((val === undefined || val === null) && (path.trim() === "inventory_item_variants.sale_price" || path.trim() === "sale_price" || path.trim() === "price")) val = data.sale_price || data.price || data.variants?.[0]?.sale_price || 0;
216
+ if ((val === undefined || val === null) && (path.trim() === "inventory_item_variants.min_sales_price" || path.trim() === "min_sales_price")) val = data.min_sales_price || data.variants?.[0]?.min_sales_price || 0;
216
217
  if ((val === undefined || val === null) && (path.trim() === "inventory_item_variants.cost_price" || path.trim() === "cost_price")) val = data.cost_price || data.variants?.[0]?.cost_price || 0;
217
218
  if ((val === undefined || val === null) && path.trim() === "inventory_batches.batch_number") val = data.batch_number || data.batch?.batch_number || "";
218
219
  if ((val === undefined || val === null) && path.trim() === "inventory_batches.selling_price") val = data.price || data.selling_price || data.batch?.selling_price || data.sale_price || 0;
@@ -347,6 +348,15 @@ function LineRenderer({ line, defaultFont, defaultColor, defaultFontSize, entity
347
348
  wordBreak: "break-word" as const,
348
349
  };
349
350
 
351
+ const ec = typeof line.extra_config === 'string' ? JSON.parse(line.extra_config || '{}') : (line.extra_config || {});
352
+
353
+ if (ec.truncate_single_line) {
354
+ baseStyle.whiteSpace = "nowrap";
355
+ baseStyle.overflow = "hidden";
356
+ baseStyle.textOverflow = "ellipsis";
357
+ baseStyle.display = "block";
358
+ }
359
+
350
360
  if (line.border_style && line.border_style !== "none") {
351
361
  const bw = `${line.border_width || 1}px`;
352
362
  const bc = line.border_color || "#000000";
@@ -360,7 +370,11 @@ function LineRenderer({ line, defaultFont, defaultColor, defaultFontSize, entity
360
370
 
361
371
  // Text
362
372
  if (line.line_type === "text") {
363
- return <div style={baseStyle}>{replaceTokens(line.static_text || "", entityData).trim().split("\n").map((t, i) => <div key={i}>{t || <br />}</div>)}</div>;
373
+ const content = replaceTokens(line.static_text || "", entityData).trim();
374
+ if (ec.truncate_single_line) {
375
+ return <div style={baseStyle}>{content}</div>;
376
+ }
377
+ return <div style={baseStyle}>{content.split("\n").map((t, i) => <div key={i}>{t || <br />}</div>)}</div>;
364
378
  }
365
379
 
366
380
  // Token (legacy)
@@ -405,11 +419,14 @@ function LineRenderer({ line, defaultFont, defaultColor, defaultFontSize, entity
405
419
  const childCols = line.extra_config?.columns || line.columns || [];
406
420
  return (
407
421
  <div style={{ display: "flex", marginTop: `${line.spacing_before || 0}px`, marginBottom: `${line.spacing_after || 0}px`, backgroundColor: line.background_color || "transparent", padding: line.padding || "0", borderRadius: line.border_radius || "0" }}>
408
- {childCols.map((col: any, i: number) => (
409
- <div key={i} style={{ width: `${col.width_percent || 50}%`, fontFamily: `${col.font_family || line.font_family || defaultFont}, sans-serif`, fontSize: `${col.font_size ? Number(col.font_size) : (line.font_size ? Number(line.font_size) : defaultFontSize)}px`, lineHeight: 1.15, fontWeight: col.font_weight === "bold" ? 700 : col.font_weight === "light" ? 300 : (line.font_weight === "bold" ? 700 : line.font_weight === "light" ? 300 : (isA4 === false ? 600 : 400)), color: col.font_color || line.font_color || defaultColor, textAlign: (col.text_align || col.align || "left") as any, fontStyle: col.is_italic ? "italic" : "normal", textDecoration: col.is_underline ? "underline" : "none", textTransform: (col.text_transform || "none") as any, backgroundColor: col.background_color || "transparent", padding: col.padding || "0", whiteSpace: "pre-wrap" }}>
410
- {replaceTokens(col.content || col.static_text || "", entityData).split("\n").map((t, ii) => <div key={ii}>{t || <br />}</div>)}
411
- </div>
412
- ))}
422
+ {childCols.map((col: any, i: number) => {
423
+ const isTruncated = col.truncate_single_line || ec.truncate_single_line;
424
+ return (
425
+ <div key={i} style={{ width: `${col.width_percent || 50}%`, fontFamily: `${col.font_family || line.font_family || defaultFont}, sans-serif`, fontSize: `${col.font_size ? Number(col.font_size) : (line.font_size ? Number(line.font_size) : defaultFontSize)}px`, lineHeight: 1.15, fontWeight: col.font_weight === "bold" ? 700 : col.font_weight === "light" ? 300 : (line.font_weight === "bold" ? 700 : line.font_weight === "light" ? 300 : (isA4 === false ? 600 : 400)), color: col.font_color || line.font_color || defaultColor, textAlign: (col.text_align || col.align || "left") as any, fontStyle: col.is_italic ? "italic" : "normal", textDecoration: col.is_underline ? "underline" : "none", textTransform: (col.text_transform || "none") as any, backgroundColor: col.background_color || "transparent", padding: col.padding || "0", whiteSpace: isTruncated ? "nowrap" : "pre-wrap", overflow: isTruncated ? "hidden" : "visible", textOverflow: isTruncated ? "ellipsis" : "clip", display: "block" }}>
426
+ {isTruncated ? replaceTokens(col.content || col.static_text || "", entityData) : replaceTokens(col.content || col.static_text || "", entityData).split("\n").map((t, ii) => <div key={ii}>{t || <br />}</div>)}
427
+ </div>
428
+ );
429
+ })}
413
430
  </div>
414
431
  );
415
432
  }
@@ -560,6 +577,10 @@ function LineRenderer({ line, defaultFont, defaultColor, defaultFontSize, entity
560
577
  let num = 0;
561
578
  if (path === 'inventory_item_variants.cost_price' || path === 'cost_price') {
562
579
  num = Number(entityData.cost_price || entityData.variants?.[0]?.cost_price || 0);
580
+ } else if (path === 'inventory_item_variants.min_sales_price' || path === 'min_sales_price') {
581
+ num = Number(entityData.min_sales_price || entityData.variants?.[0]?.min_sales_price || 0);
582
+ } else if (path === 'inventory_item_variants.sale_price' || path === 'sale_price' || path === 'price') {
583
+ num = Number(entityData.sale_price || entityData.price || entityData.variants?.[0]?.sale_price || 0);
563
584
  } else {
564
585
  let val = entityData;
565
586
  for (const k of path.split(".")) {