indas-ui 0.1.2 → 0.1.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/cjs/components/chat/ai-messages.js +3 -2
- package/dist/cjs/components/chat/chat.js +2 -1
- package/dist/cjs/components/dashboard/charts/Area.js +6 -4
- package/dist/cjs/components/dashboard/charts/Bar.js +6 -4
- package/dist/cjs/components/dashboard/charts/FlowMap.js +2 -1
- package/dist/cjs/components/dashboard/charts/Funnel.js +2 -1
- package/dist/cjs/components/dashboard/charts/GeoMap.js +2 -1
- package/dist/cjs/components/dashboard/charts/Line.js +5 -3
- package/dist/cjs/components/dashboard/charts/Pareto.js +2 -1
- package/dist/cjs/components/dashboard/charts/Sankey.js +4 -3
- package/dist/cjs/components/dashboard/charts/Sparkline.js +2 -1
- package/dist/cjs/components/dashboard/charts/Treemap.js +2 -1
- package/dist/cjs/components/dashboard/charts/index.js +7 -1
- package/dist/cjs/components/dashboard/kpi/KpiBreakdown.js +3 -2
- package/dist/cjs/components/dashboard/kpi/KpiComparison.js +2 -1
- package/dist/cjs/components/dashboard/kpi/KpiQuickStats.js +2 -1
- package/dist/cjs/components/dashboard/kpi/KpiRating.js +3 -2
- package/dist/cjs/components/datagrid/DataGrid.js +2 -2
- package/dist/cjs/components/datagrid/cells/CellRenderer.js +4 -4
- package/dist/cjs/components/datagrid/cells/SelectionCell.js +10 -10
- package/dist/cjs/components/datagrid/columns/ColumnContextMenu.js +2 -1
- package/dist/cjs/components/datagrid/controls/PaginationControls.js +3 -3
- package/dist/cjs/components/datagrid/controls/menu/view/CardView.js +8 -7
- package/dist/cjs/components/datagrid/controls/menu/view/ChartView.js +2 -1
- package/dist/cjs/components/datagrid/rows/DraggableRow.js +6 -6
- package/dist/cjs/components/datagrid/search/InlineSearchCell.js +4 -4
- package/dist/cjs/components/datagrid/utils/grid-helpers.js +6 -5
- package/dist/cjs/components/draft/AutoSaveIndicator.js +9 -9
- package/dist/cjs/components/draft/DraftModal.js +6 -6
- package/dist/cjs/components/forms/date-picker/DatePicker.js +7 -13
- package/dist/cjs/components/forms/date-picker/DateTimePicker.js +2 -2
- package/dist/cjs/components/layout/footer.js +2 -1
- package/dist/cjs/components/layout/side-panel.js +4 -1
- package/dist/cjs/components/ui/display/stats-card.js +2 -1
- package/dist/cjs/lib/number-format.js +60 -0
- package/dist/cjs/lib/utils.js +4 -3
- package/dist/cjs/styles/index.css +5 -5
- package/dist/components/chat/ai-messages.js +3 -2
- package/dist/components/chat/chat.js +2 -1
- package/dist/components/dashboard/charts/Area.d.ts +5 -1
- package/dist/components/dashboard/charts/Area.js +6 -4
- package/dist/components/dashboard/charts/Bar.d.ts +5 -1
- package/dist/components/dashboard/charts/Bar.js +6 -4
- package/dist/components/dashboard/charts/FlowMap.js +2 -1
- package/dist/components/dashboard/charts/Funnel.js +2 -1
- package/dist/components/dashboard/charts/GeoMap.js +2 -1
- package/dist/components/dashboard/charts/Line.d.ts +5 -1
- package/dist/components/dashboard/charts/Line.js +5 -3
- package/dist/components/dashboard/charts/Pareto.js +2 -1
- package/dist/components/dashboard/charts/Sankey.js +4 -3
- package/dist/components/dashboard/charts/Sparkline.js +2 -1
- package/dist/components/dashboard/charts/Treemap.js +2 -1
- package/dist/components/dashboard/charts/index.d.ts +1 -0
- package/dist/components/dashboard/charts/index.js +2 -0
- package/dist/components/dashboard/kpi/KpiBreakdown.js +3 -2
- package/dist/components/dashboard/kpi/KpiComparison.js +2 -1
- package/dist/components/dashboard/kpi/KpiQuickStats.js +2 -1
- package/dist/components/dashboard/kpi/KpiRating.js +3 -2
- package/dist/components/datagrid/DataGrid.js +2 -2
- package/dist/components/datagrid/cells/CellRenderer.js +4 -4
- package/dist/components/datagrid/cells/SelectionCell.js +10 -10
- package/dist/components/datagrid/columns/ColumnContextMenu.js +2 -1
- package/dist/components/datagrid/controls/PaginationControls.js +3 -3
- package/dist/components/datagrid/controls/menu/view/CardView.js +8 -7
- package/dist/components/datagrid/controls/menu/view/ChartView.js +2 -1
- package/dist/components/datagrid/rows/DraggableRow.js +6 -6
- package/dist/components/datagrid/search/InlineSearchCell.js +4 -4
- package/dist/components/datagrid/utils/grid-helpers.d.ts +1 -1
- package/dist/components/datagrid/utils/grid-helpers.js +3 -2
- package/dist/components/draft/AutoSaveIndicator.js +9 -9
- package/dist/components/draft/DraftModal.js +6 -6
- package/dist/components/forms/date-picker/DatePicker.js +7 -13
- package/dist/components/forms/date-picker/DateTimePicker.js +2 -2
- package/dist/components/layout/footer.js +2 -1
- package/dist/components/layout/side-panel.js +4 -1
- package/dist/components/ui/display/stats-card.js +2 -1
- package/dist/lib/number-format.d.ts +18 -0
- package/dist/lib/number-format.js +54 -0
- package/dist/lib/utils.js +4 -3
- package/dist/styles/index.css +5 -5
- package/package.json +4 -4
|
@@ -10,6 +10,7 @@ const ui_1 = require("../../../../ui/index.js");
|
|
|
10
10
|
const SelectionCell_1 = require("../../../cells/SelectionCell.js");
|
|
11
11
|
const skeleton_1 = require("../../../../ui/feedback/skeleton.js");
|
|
12
12
|
const LanguageContext_1 = require("../../../../../contexts/LanguageContext.js");
|
|
13
|
+
const number_format_1 = require("../../../../../lib/number-format.js");
|
|
13
14
|
const sizeConfig = {
|
|
14
15
|
compact: {
|
|
15
16
|
grid: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
|
|
@@ -131,7 +132,7 @@ function CardView({ data, columns, onRowClick, selectedRows, onRowSelect, isLoad
|
|
|
131
132
|
return value ? ((0, jsx_runtime_1.jsx)(ui_1.Badge, { variant: "default", className: "h-4.5 text-[0.6rem] px-1.5 py-0 bg-[rgb(var(--color-success))]/10 text-[rgb(var(--color-success))] border-0 font-medium", children: t('Yes') })) : ((0, jsx_runtime_1.jsx)(ui_1.Badge, { variant: "secondary", className: "h-4.5 text-[0.6rem] px-1.5 py-0 bg-[rgb(var(--bg-subtle))] text-[rgb(var(--fg-muted))] border-0 font-medium", children: t('No') }));
|
|
132
133
|
}
|
|
133
134
|
if (typeof value === 'number')
|
|
134
|
-
return
|
|
135
|
+
return (0, number_format_1.formatNumber)(value);
|
|
135
136
|
return value?.toString() || '—';
|
|
136
137
|
}, [t]);
|
|
137
138
|
const getFieldLabel = (0, react_1.useCallback)((column) => {
|
|
@@ -154,14 +155,14 @@ function CardView({ data, columns, onRowClick, selectedRows, onRowSelect, isLoad
|
|
|
154
155
|
return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, className: "outline-none", tabIndex: 0, onKeyDown: handleKeyDown, children: [(0, jsx_runtime_1.jsx)("div", { className: cfg.pad, children: (0, jsx_runtime_1.jsx)("div", { className: `grid ${cfg.grid} ${cfg.gap}`, children: (0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { initial: false, children: data.map((item, index) => {
|
|
155
156
|
const isFocused = focusedIndex === index;
|
|
156
157
|
const itemSelected = isSelected(item);
|
|
157
|
-
return ((0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { ref: (el) => { cardRefs.current[index] = el; }, initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -6 }, transition: { duration: 0.12, delay: Math.min(index * 0.015, 0.15) }, className: "group/card outline-none", tabIndex: -1, onClick: () => setFocusedIndex(index), onDoubleClick: () => handleCardClick(item), children: (0, jsx_runtime_1.jsxs)("div", { className: `
|
|
158
|
-
relative rounded-lg border bg-[rgb(var(--bg-surface))] overflow-hidden h-full
|
|
159
|
-
transition-all duration-150
|
|
158
|
+
return ((0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { ref: (el) => { cardRefs.current[index] = el; }, initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -6 }, transition: { duration: 0.12, delay: Math.min(index * 0.015, 0.15) }, className: "group/card outline-none", tabIndex: -1, onClick: () => setFocusedIndex(index), onDoubleClick: () => handleCardClick(item), children: (0, jsx_runtime_1.jsxs)("div", { className: `
|
|
159
|
+
relative rounded-lg border bg-[rgb(var(--bg-surface))] overflow-hidden h-full
|
|
160
|
+
transition-all duration-150
|
|
160
161
|
${itemSelected
|
|
161
162
|
? 'border-[rgb(var(--color-primary))] shadow-sm'
|
|
162
|
-
: 'border-[rgb(var(--bd-default))] hover:border-[rgb(var(--color-primary))]/40 hover:shadow-sm'}
|
|
163
|
-
${isFocused ? 'ring-2 ring-[rgb(var(--color-primary))]/30 ring-offset-1' : ''}
|
|
164
|
-
${getCardClassName?.(item) ?? ''}
|
|
163
|
+
: 'border-[rgb(var(--bd-default))] hover:border-[rgb(var(--color-primary))]/40 hover:shadow-sm'}
|
|
164
|
+
${isFocused ? 'ring-2 ring-[rgb(var(--color-primary))]/30 ring-offset-1' : ''}
|
|
165
|
+
${getCardClassName?.(item) ?? ''}
|
|
165
166
|
`, children: [(0, jsx_runtime_1.jsx)("div", { className: `absolute left-0 top-0 bottom-0 w-[2px] transition-colors duration-150 ${itemSelected ? 'bg-[rgb(var(--color-primary))]' : 'bg-transparent group-hover/card:bg-[rgb(var(--color-primary))]/30'}` }), (0, jsx_runtime_1.jsxs)("div", { className: "pl-3 pr-2.5 py-2.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [onRowSelect && ((0, jsx_runtime_1.jsx)("div", { className: "flex-shrink-0", onClick: (e) => e.stopPropagation(), children: (0, jsx_runtime_1.jsx)(SelectionCell_1.SelectionCheckbox, { checked: itemSelected, onChange: (checked) => handleCheckboxChange(item, checked), circular: circularCheckboxes, mode: "checkbox" }) })), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0 flex items-baseline gap-1.5", children: [(0, jsx_runtime_1.jsx)("p", { className: `${cfg.titleText} font-semibold text-[rgb(var(--fg-default))] truncate leading-tight`, children: displayColumns.length > 0 && getDisplayValue(item, displayColumns[0].accessorKey) }), displayColumns.length > 1 && ((0, jsx_runtime_1.jsx)("p", { className: `${cfg.subtitleText} text-[rgb(var(--color-primary))] font-medium truncate flex-shrink-0`, children: getDisplayValue(item, displayColumns[1].accessorKey) }))] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-shrink-0 opacity-0 group-hover/card:opacity-100 transition-opacity duration-150", onClick: (e) => e.stopPropagation(), children: actionsColumn && actionsColumn.cell?.({
|
|
166
167
|
row: { original: item },
|
|
167
168
|
getValue: () => null,
|
|
@@ -48,6 +48,7 @@ const ui_1 = require("../../../../ui/index.js");
|
|
|
48
48
|
const ui_2 = require("../../../../ui/index.js");
|
|
49
49
|
const components_1 = require("../../../../index.js");
|
|
50
50
|
const ui_3 = require("../../../../ui/index.js");
|
|
51
|
+
const number_format_1 = require("../../../../../lib/number-format.js");
|
|
51
52
|
const CHART_COLORS = [
|
|
52
53
|
'#3b82f6', // Blue
|
|
53
54
|
'#ef4444', // Red
|
|
@@ -381,7 +382,7 @@ function DataVisualization({ data, columns, title = "Data Insights" }) {
|
|
|
381
382
|
if (data.length === 0) {
|
|
382
383
|
return ((0, jsx_runtime_1.jsx)(ui_1.Card, { className: "p-8", children: (0, jsx_runtime_1.jsxs)("div", { className: "text-center text-gray-500", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.BarChart3, { className: "h-12 w-12 mx-auto mb-4 opacity-50" }), (0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-medium mb-2", children: "No Data Available" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm", children: "Add some data to view visualizations" })] }) }));
|
|
383
384
|
}
|
|
384
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-6 p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-bold tracking-tight", children: title }), (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-gray-600 mt-1", children: ["Visual analysis of ", data.length, " records"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center space-x-2", children: [(0, jsx_runtime_1.jsxs)(ui_2.Button, { variant: selectedChart === 'bar' ? 'primary' : 'outline', size: "sm", onClick: () => setSelectedChart('bar'), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.BarChart3, { className: "h-4 w-4 mr-2" }), "Bar"] }), (0, jsx_runtime_1.jsxs)(ui_2.Button, { variant: selectedChart === 'line' ? 'primary' : 'outline', size: "sm", onClick: () => setSelectedChart('line'), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.LineChart, { className: "h-4 w-4 mr-2" }), "Line"] }), (0, jsx_runtime_1.jsxs)(ui_2.Button, { variant: selectedChart === 'area' ? 'primary' : 'outline', size: "sm", onClick: () => setSelectedChart('area'), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Activity, { className: "h-4 w-4 mr-2" }), "Area"] }), (0, jsx_runtime_1.jsxs)(ui_2.Button, { variant: selectedChart === 'pie' ? 'primary' : 'outline', size: "sm", onClick: () => setSelectedChart('pie'), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.PieChart, { className: "h-4 w-4 mr-2" }), "Pie"] })] })] }), summaryStats && ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [(0, jsx_runtime_1.jsx)(ui_1.Card, { children: (0, jsx_runtime_1.jsxs)(ui_1.CardContent, { className: "p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-blue-600", children: summaryStats.sum
|
|
385
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-6 p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-bold tracking-tight", children: title }), (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-gray-600 mt-1", children: ["Visual analysis of ", data.length, " records"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center space-x-2", children: [(0, jsx_runtime_1.jsxs)(ui_2.Button, { variant: selectedChart === 'bar' ? 'primary' : 'outline', size: "sm", onClick: () => setSelectedChart('bar'), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.BarChart3, { className: "h-4 w-4 mr-2" }), "Bar"] }), (0, jsx_runtime_1.jsxs)(ui_2.Button, { variant: selectedChart === 'line' ? 'primary' : 'outline', size: "sm", onClick: () => setSelectedChart('line'), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.LineChart, { className: "h-4 w-4 mr-2" }), "Line"] }), (0, jsx_runtime_1.jsxs)(ui_2.Button, { variant: selectedChart === 'area' ? 'primary' : 'outline', size: "sm", onClick: () => setSelectedChart('area'), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Activity, { className: "h-4 w-4 mr-2" }), "Area"] }), (0, jsx_runtime_1.jsxs)(ui_2.Button, { variant: selectedChart === 'pie' ? 'primary' : 'outline', size: "sm", onClick: () => setSelectedChart('pie'), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.PieChart, { className: "h-4 w-4 mr-2" }), "Pie"] })] })] }), summaryStats && ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [(0, jsx_runtime_1.jsx)(ui_1.Card, { children: (0, jsx_runtime_1.jsxs)(ui_1.CardContent, { className: "p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-blue-600", children: (0, number_format_1.formatNumber)(summaryStats.sum) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-gray-600", children: "Total" })] }) }), (0, jsx_runtime_1.jsx)(ui_1.Card, { children: (0, jsx_runtime_1.jsxs)(ui_1.CardContent, { className: "p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-green-600", children: summaryStats.avg.toFixed(1) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-gray-600", children: "Average" })] }) }), (0, jsx_runtime_1.jsx)(ui_1.Card, { children: (0, jsx_runtime_1.jsxs)(ui_1.CardContent, { className: "p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-orange-600", children: (0, number_format_1.formatNumber)(summaryStats.min) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-gray-600", children: "Minimum" })] }) }), (0, jsx_runtime_1.jsx)(ui_1.Card, { children: (0, jsx_runtime_1.jsxs)(ui_1.CardContent, { className: "p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-purple-600", children: (0, number_format_1.formatNumber)(summaryStats.max) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-gray-600", children: "Maximum" })] }) })] })), (0, jsx_runtime_1.jsxs)(ui_1.Card, { children: [(0, jsx_runtime_1.jsx)(ui_1.CardHeader, { children: (0, jsx_runtime_1.jsx)(ui_1.CardTitle, { className: "text-lg", children: "Chart Configuration" }) }), (0, jsx_runtime_1.jsx)(ui_1.CardContent, { children: (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("label", { className: "text-sm font-medium mb-2 block", children: ["X-Axis (", selectedChart === 'pie' ? 'Category' : 'Categories', ")"] }), (0, jsx_runtime_1.jsx)(components_1.Dropdown, { options: categoricalColumns.map((col) => ({
|
|
385
386
|
value: col.key,
|
|
386
387
|
label: col.label,
|
|
387
388
|
})), value: xAxisColumn, onValueChange: (value) => setXAxisColumn(typeof value === 'string' ? value : value[0] || ''), placeholder: "Select column" })] }), selectedChart !== 'pie' && ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: "text-sm font-medium mb-2 block", children: "Y-Axis (Values)" }), (0, jsx_runtime_1.jsx)(components_1.Dropdown, { options: numericColumns.map((col) => ({
|
|
@@ -20,15 +20,15 @@ navigationState = {}, columnSearches = {}, isColumnSearchActive = {}, onColumnSe
|
|
|
20
20
|
transition,
|
|
21
21
|
opacity: isDragging ? 0.5 : 1,
|
|
22
22
|
};
|
|
23
|
-
return ((0, jsx_runtime_1.jsxs)("tr", { ref: setNodeRef, style: style, className: `
|
|
24
|
-
cursor-pointer group
|
|
25
|
-
transition-colors duration-150 ease-out
|
|
26
|
-
${isDragging ? 'shadow-lg z-50' : ''}
|
|
23
|
+
return ((0, jsx_runtime_1.jsxs)("tr", { ref: setNodeRef, style: style, className: `
|
|
24
|
+
cursor-pointer group
|
|
25
|
+
transition-colors duration-150 ease-out
|
|
26
|
+
${isDragging ? 'shadow-lg z-50' : ''}
|
|
27
27
|
${isHighlighted
|
|
28
28
|
? 'border-2 border-[rgb(var(--color-primary))] bg-gradient-to-r from-[rgb(var(--color-primary))]/10 to-[rgb(var(--color-primary))]/20 shadow-lg relative'
|
|
29
29
|
: isSelected
|
|
30
30
|
? 'bg-[color-mix(in_srgb,rgb(var(--color-primary))_8%,rgb(var(--bg-surface)))] shadow-sm hover:bg-[color-mix(in_srgb,rgb(var(--color-primary))_15%,rgb(var(--bg-surface)))]'
|
|
31
|
-
: 'hover:bg-[color-mix(in_srgb,rgb(var(--color-primary))_5%,rgb(var(--bg-surface)))]'}
|
|
32
|
-
${className ?? ''}
|
|
31
|
+
: 'hover:bg-[color-mix(in_srgb,rgb(var(--color-primary))_5%,rgb(var(--bg-surface)))]'}
|
|
32
|
+
${className ?? ''}
|
|
33
33
|
`, onClick: onRowClick, onDoubleClick: onRowDoubleClick, onContextMenu: onRowContextMenu, children: [showDragHandle && ((0, jsx_runtime_1.jsx)("td", { className: "text-center align-middle cursor-grab active:cursor-grabbing", style: { width: '1.875rem', minWidth: '1.875rem', maxWidth: '1.875rem' }, ...attributes, ...listeners, children: (0, jsx_runtime_1.jsx)("div", { className: "w-full h-full flex items-center justify-center", children: (0, jsx_runtime_1.jsx)(lucide_react_1.GripVertical, { className: "h-4 w-4 text-gray-400" }) }) })), row.getVisibleCells().map((cell, cellIndex) => ((0, jsx_runtime_1.jsx)(CellRenderer_1.CellRenderer, { cell: cell, cellIndex: cellIndex, row: row, enableBacchaSearch: enableBacchaSearch, mainColumnsArray: mainColumnsArray, frozenColumnsState: frozenColumnsState, navigationState: navigationState, columnSearches: columnSearches, isColumnSearchActive: isColumnSearchActive, onColumnSearch: onColumnSearch, onClearColumnSearch: onClearColumnSearch, SearchHighlighter: SearchHighlighter, InlineSearchCell: InlineSearchCell, isSelected: isSelected }, cell.id)))] }));
|
|
34
34
|
}
|
|
@@ -89,10 +89,10 @@ function InlineSearchCell({ value, columnId, rowId, onSearch, onClearSearch, isS
|
|
|
89
89
|
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: parts.map((part, index) => part.toLowerCase() === highlight.toLowerCase() ? ((0, jsx_runtime_1.jsx)("mark", { className: "bg-yellow-200 text-yellow-900 px-0.5 rounded", children: part }, index)) : ((0, jsx_runtime_1.jsx)("span", { children: part }, index))) }));
|
|
90
90
|
};
|
|
91
91
|
const displayValue = value?.toString() || '';
|
|
92
|
-
return ((0, jsx_runtime_1.jsxs)("div", { ref: cellRef, tabIndex: 0, className: `bacchasearch relative group cursor-pointer outline-none inline-flex items-center w-full ${className}`, onClick: handleCellClick, children: [(0, jsx_runtime_1.jsx)("span", { className: `
|
|
93
|
-
text-xs flex-1 rounded transition-colors
|
|
94
|
-
${isSearchActive ? 'bg-[rgb(var(--color-primary)/0.1)]' : 'hover:bg-[rgb(var(--color-primary)/0.05)]'}
|
|
95
|
-
${isTyping ? 'ring-1 ring-[rgb(var(--color-primary)/0.3)] bg-[rgb(var(--color-primary)/0.15)]' : ''}
|
|
92
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: cellRef, tabIndex: 0, className: `bacchasearch relative group cursor-pointer outline-none inline-flex items-center w-full ${className}`, onClick: handleCellClick, children: [(0, jsx_runtime_1.jsx)("span", { className: `
|
|
93
|
+
text-xs flex-1 rounded transition-colors
|
|
94
|
+
${isSearchActive ? 'bg-[rgb(var(--color-primary)/0.1)]' : 'hover:bg-[rgb(var(--color-primary)/0.05)]'}
|
|
95
|
+
${isTyping ? 'ring-1 ring-[rgb(var(--color-primary)/0.3)] bg-[rgb(var(--color-primary)/0.15)]' : ''}
|
|
96
96
|
`, children: isSearchActive && searchTerm ?
|
|
97
97
|
renderHighlightedText(displayValue, searchTerm) :
|
|
98
98
|
displayValue }), (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { className: `h-3 w-3 text-[rgb(var(--fg-muted))] ml-0.5 flex-shrink-0 transition-opacity ${isTyping ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'}` }), (isTyping || isSearchActive) && ((0, jsx_runtime_1.jsx)("div", { className: "absolute -top-0.5 -right-0.5", children: (0, jsx_runtime_1.jsx)("div", { className: `h-1.5 w-1.5 rounded-full ${isTyping ? 'bg-[rgb(var(--color-success))]' : 'bg-[rgb(var(--color-primary))]'}` }) })), isTyping && localSearchTerm && ((0, jsx_runtime_1.jsxs)("div", { className: "absolute -bottom-5 left-0 bg-[rgb(var(--bg-surface))] text-[rgb(var(--fg-default))] text-xs px-1.5 py-0.5 rounded shadow-lg border border-[rgb(var(--bd-default))] z-10 whitespace-nowrap", children: ["\"", localSearchTerm, "\""] }))] }));
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
"use strict";
|
|
3
|
-
/**
|
|
4
|
-
* Grid utility functions and helpers
|
|
5
|
-
*/
|
|
6
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
4
|
exports.calculatePaginationInfo = calculatePaginationInfo;
|
|
8
5
|
exports.generatePageNumbers = generatePageNumbers;
|
|
@@ -10,6 +7,10 @@ exports.debounce = debounce;
|
|
|
10
7
|
exports.getRowId = getRowId;
|
|
11
8
|
exports.formatNumber = formatNumber;
|
|
12
9
|
exports.matchesSearch = matchesSearch;
|
|
10
|
+
const number_format_1 = require("../../../lib/number-format.js");
|
|
11
|
+
/**
|
|
12
|
+
* Grid utility functions and helpers
|
|
13
|
+
*/
|
|
13
14
|
/**
|
|
14
15
|
* Calculate pagination info
|
|
15
16
|
*/
|
|
@@ -83,10 +84,10 @@ function getRowId(row, index, getRowIdFn) {
|
|
|
83
84
|
return String(index);
|
|
84
85
|
}
|
|
85
86
|
/**
|
|
86
|
-
* Format large numbers with
|
|
87
|
+
* Format large numbers with grouping, in the library's number locale (Indian by default)
|
|
87
88
|
*/
|
|
88
89
|
function formatNumber(num) {
|
|
89
|
-
return
|
|
90
|
+
return (0, number_format_1.formatNumber)(num);
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
92
93
|
* Check if value matches search term
|
|
@@ -57,14 +57,14 @@ function AutoSaveIndicator({ enabled, lastSaved, isDirty, isSaving, onOpenDraftM
|
|
|
57
57
|
};
|
|
58
58
|
};
|
|
59
59
|
const status = getStatus();
|
|
60
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs text-[rgb(var(--fg-muted))]", children: "Auto-save:" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => onToggleAutoSave?.(!enabled), className: `
|
|
61
|
-
relative inline-flex h-5 w-9 items-center rounded-full transition-colors
|
|
62
|
-
${enabled ? 'bg-[rgb(var(--color-primary))]' : 'bg-[rgb(var(--bg-muted))]'}
|
|
63
|
-
`, title: enabled ? 'Disable auto-save' : 'Enable auto-save', children: (0, jsx_runtime_1.jsx)("span", { className: `
|
|
64
|
-
inline-block h-3.5 w-3.5 transform rounded-full bg-white transition-transform
|
|
65
|
-
${enabled ? 'translate-x-5' : 'translate-x-1'}
|
|
66
|
-
` }) }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium text-[rgb(var(--fg-default))]", children: enabled ? 'ON' : 'OFF' })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-4 w-px bg-[rgb(var(--bd-default))]" }), (0, jsx_runtime_1.jsxs)("button", { onClick: onOpenDraftManager, className: `
|
|
67
|
-
flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors
|
|
68
|
-
${status.bg} ${status.color} hover:opacity-80
|
|
60
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs text-[rgb(var(--fg-muted))]", children: "Auto-save:" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => onToggleAutoSave?.(!enabled), className: `
|
|
61
|
+
relative inline-flex h-5 w-9 items-center rounded-full transition-colors
|
|
62
|
+
${enabled ? 'bg-[rgb(var(--color-primary))]' : 'bg-[rgb(var(--bg-muted))]'}
|
|
63
|
+
`, title: enabled ? 'Disable auto-save' : 'Enable auto-save', children: (0, jsx_runtime_1.jsx)("span", { className: `
|
|
64
|
+
inline-block h-3.5 w-3.5 transform rounded-full bg-white transition-transform
|
|
65
|
+
${enabled ? 'translate-x-5' : 'translate-x-1'}
|
|
66
|
+
` }) }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium text-[rgb(var(--fg-default))]", children: enabled ? 'ON' : 'OFF' })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-4 w-px bg-[rgb(var(--bd-default))]" }), (0, jsx_runtime_1.jsxs)("button", { onClick: onOpenDraftManager, className: `
|
|
67
|
+
flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors
|
|
68
|
+
${status.bg} ${status.color} hover:opacity-80
|
|
69
69
|
`, title: "Click to manage drafts", children: [status.icon, (0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium", children: status.text })] })] }));
|
|
70
70
|
}
|
|
@@ -192,10 +192,10 @@ function DraftModal({ open, onClose, drafts, onLoad, onDelete, isLoading = false
|
|
|
192
192
|
// Early return after all hooks to comply with Rules of Hooks
|
|
193
193
|
if (!open)
|
|
194
194
|
return null;
|
|
195
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-[rgb(var(--bg-surface))] rounded-lg shadow-2xl w-full max-w-4xl max-h-[85vh] flex flex-col", children: [(0, jsx_runtime_1.jsxs)("div", { className: "px-6 py-3 border-b border-[rgb(var(--bd-default))] flex items-center gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 flex-shrink-0", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.FileText, { className: "w-5 h-5 text-[rgb(var(--color-primary))]" }), (0, jsx_runtime_1.jsx)("h2", { className: "text-base font-semibold text-[rgb(var(--fg-default))]", children: "Saved Drafts" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-xs text-[rgb(var(--fg-muted))]", children: ["(", drafts.length, ")"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "relative flex-1", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-[rgb(var(--fg-muted))]" }), (0, jsx_runtime_1.jsx)("input", { type: "text", placeholder: "Search...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "\n w-full pl-9 pr-3 py-1.5 rounded-lg text-sm\n bg-[rgb(var(--bg-default))]\n border border-[rgb(var(--bd-default))]\n text-[rgb(var(--fg-default))]\n placeholder:text-[rgb(var(--fg-muted))]\n focus:outline-none focus:ring-2 focus:ring-[rgb(var(--color-primary))]\n " })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-shrink-0 w-36", children: (0, jsx_runtime_1.jsx)(dropdown_1.Dropdown, { options: SORT_OPTIONS, value: sortBy, onValueChange: (val) => setSortBy(val), size: "sm", autoWidth: true }) }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "p-1.5 rounded-lg hover:bg-[rgb(var(--bg-muted))] text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] transition-colors flex-shrink-0", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "w-5 h-5" }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto px-6 py-4", children: isLoading ? ((0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center py-16", children: (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-12 h-12 rounded-full border-4 border-[rgb(var(--color-primary)_/_0.2)] border-t-[rgb(var(--color-primary))] animate-spin mx-auto mb-3" }), (0, jsx_runtime_1.jsx)("p", { className: "text-[rgb(var(--fg-muted))]", children: "Loading drafts..." })] }) })) : filteredAndSortedDrafts.length === 0 ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.FileText, { className: "w-16 h-16 text-[rgb(var(--fg-muted))] mb-4 opacity-50" }), (0, jsx_runtime_1.jsx)("p", { className: "text-lg font-medium text-[rgb(var(--fg-default))] mb-1", children: searchQuery ? 'No matching drafts' : 'No drafts saved yet' }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-[rgb(var(--fg-muted))]", children: searchQuery ? 'Try a different search term' : 'Start by clicking "Save Draft" button' })] })) : ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: paginatedDrafts.map((draft) => {
|
|
195
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-[rgb(var(--bg-surface))] rounded-lg shadow-2xl w-full max-w-4xl max-h-[85vh] flex flex-col", children: [(0, jsx_runtime_1.jsxs)("div", { className: "px-6 py-3 border-b border-[rgb(var(--bd-default))] flex items-center gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 flex-shrink-0", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.FileText, { className: "w-5 h-5 text-[rgb(var(--color-primary))]" }), (0, jsx_runtime_1.jsx)("h2", { className: "text-base font-semibold text-[rgb(var(--fg-default))]", children: "Saved Drafts" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-xs text-[rgb(var(--fg-muted))]", children: ["(", drafts.length, ")"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "relative flex-1", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-[rgb(var(--fg-muted))]" }), (0, jsx_runtime_1.jsx)("input", { type: "text", placeholder: "Search...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "\r\n w-full pl-9 pr-3 py-1.5 rounded-lg text-sm\r\n bg-[rgb(var(--bg-default))]\r\n border border-[rgb(var(--bd-default))]\r\n text-[rgb(var(--fg-default))]\r\n placeholder:text-[rgb(var(--fg-muted))]\r\n focus:outline-none focus:ring-2 focus:ring-[rgb(var(--color-primary))]\r\n " })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-shrink-0 w-36", children: (0, jsx_runtime_1.jsx)(dropdown_1.Dropdown, { options: SORT_OPTIONS, value: sortBy, onValueChange: (val) => setSortBy(val), size: "sm", autoWidth: true }) }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "p-1.5 rounded-lg hover:bg-[rgb(var(--bg-muted))] text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] transition-colors flex-shrink-0", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "w-5 h-5" }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto px-6 py-4", children: isLoading ? ((0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center py-16", children: (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-12 h-12 rounded-full border-4 border-[rgb(var(--color-primary)_/_0.2)] border-t-[rgb(var(--color-primary))] animate-spin mx-auto mb-3" }), (0, jsx_runtime_1.jsx)("p", { className: "text-[rgb(var(--fg-muted))]", children: "Loading drafts..." })] }) })) : filteredAndSortedDrafts.length === 0 ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.FileText, { className: "w-16 h-16 text-[rgb(var(--fg-muted))] mb-4 opacity-50" }), (0, jsx_runtime_1.jsx)("p", { className: "text-lg font-medium text-[rgb(var(--fg-default))] mb-1", children: searchQuery ? 'No matching drafts' : 'No drafts saved yet' }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-[rgb(var(--fg-muted))]", children: searchQuery ? 'Try a different search term' : 'Start by clicking "Save Draft" button' })] })) : ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: paginatedDrafts.map((draft) => {
|
|
196
196
|
const progress = calculateProgress(draft);
|
|
197
197
|
const preview = draft.previewData || {};
|
|
198
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: "\n group relative p-3 rounded-lg\n bg-[rgb(var(--bg-default))]\n border border-[rgb(var(--bd-default))]\n hover:border-[rgb(var(--color-primary))]\n hover:shadow-lg\n transition-all duration-200\n ", children: (0, jsx_runtime_1.jsx)("div", { className: "flex items-start gap-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsx)("h3", { className: "font-semibold text-[rgb(var(--fg-default))] truncate text-base", children: draft.draftName || 'Untitled Draft' }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mt-1 text-xs text-[rgb(var(--fg-muted))]", children: [(0, jsx_runtime_1.jsx)("span", { children: formatRelativeTime(draft.lastSaved) }), (0, jsx_runtime_1.jsxs)("span", { className: "hidden group-hover:inline", children: ["\u2022 ", formatFullDate(draft.lastSaved)] }), draft.isAutoSave && ((0, jsx_runtime_1.jsx)("span", { className: "text-[10px] px-2 py-0.5 rounded-full bg-[rgb(var(--color-primary)_/_0.1)] text-[rgb(var(--color-primary))] font-medium", children: "AUTO-SAVE" }))] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => handleLoad(draft.draftId), className: "\n px-4 py-2 rounded-lg text-sm font-medium\n bg-[rgb(var(--color-primary))]\n text-white\n hover:opacity-90\n transition-opacity\n flex items-center gap-2\n ", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Download, { className: "w-4 h-4" }), "Load"] }), (0, jsx_runtime_1.jsx)("button", { onClick: () => handleDelete(draft.draftId), disabled: deletingId === draft.draftId, className: "\n p-2 rounded-lg\n text-[rgb(var(--fg-muted))]\n hover:text-red-600\n hover:bg-red-50\n transition-colors\n disabled:opacity-50\n ", title: "Delete draft", children: deletingId === draft.draftId ? ((0, jsx_runtime_1.jsx)("div", { className: "w-4 h-4 rounded-full border-2 border-red-200 border-t-red-600 animate-spin" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { className: "w-4 h-4" })) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 gap-x-6 gap-y-1 mt-2 text-sm", children: [preview.jobName && ((0, jsx_runtime_1.jsx)("div", { className: "text-[rgb(var(--fg-muted))] truncate", children: preview.jobName }, "job")), preview.clientName && ((0, jsx_runtime_1.jsx)("div", { className: "text-[rgb(var(--fg-muted))] truncate", children: preview.clientName }, "client")), preview.category && ((0, jsx_runtime_1.jsx)("div", { className: "text-[rgb(var(--fg-muted))] truncate", children: preview.category }, "category")), preview.totalCost !== undefined && preview.totalCost > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "font-medium text-[rgb(var(--color-primary))]", children: [currencySymbol, preview.totalCost.toLocaleString('en-IN')] }, "cost"))] }), progress > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between text-xs text-[rgb(var(--fg-muted))] mb-1", children: [(0, jsx_runtime_1.jsx)("span", { children: "Progress" }), (0, jsx_runtime_1.jsxs)("span", { className: "font-medium", children: [progress, "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-1.5 bg-[rgb(var(--bg-muted))] rounded-full overflow-hidden", children: (0, jsx_runtime_1.jsx)("div", { className: "h-full bg-[rgb(var(--color-primary))] transition-all duration-500 rounded-full", style: { width: `${progress}%` } }) })] }))] }) }) }, draft.draftId));
|
|
198
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "\r\n group relative p-3 rounded-lg\r\n bg-[rgb(var(--bg-default))]\r\n border border-[rgb(var(--bd-default))]\r\n hover:border-[rgb(var(--color-primary))]\r\n hover:shadow-lg\r\n transition-all duration-200\r\n ", children: (0, jsx_runtime_1.jsx)("div", { className: "flex items-start gap-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsx)("h3", { className: "font-semibold text-[rgb(var(--fg-default))] truncate text-base", children: draft.draftName || 'Untitled Draft' }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mt-1 text-xs text-[rgb(var(--fg-muted))]", children: [(0, jsx_runtime_1.jsx)("span", { children: formatRelativeTime(draft.lastSaved) }), (0, jsx_runtime_1.jsxs)("span", { className: "hidden group-hover:inline", children: ["\u2022 ", formatFullDate(draft.lastSaved)] }), draft.isAutoSave && ((0, jsx_runtime_1.jsx)("span", { className: "text-[10px] px-2 py-0.5 rounded-full bg-[rgb(var(--color-primary)_/_0.1)] text-[rgb(var(--color-primary))] font-medium", children: "AUTO-SAVE" }))] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => handleLoad(draft.draftId), className: "\r\n px-4 py-2 rounded-lg text-sm font-medium\r\n bg-[rgb(var(--color-primary))]\r\n text-white\r\n hover:opacity-90\r\n transition-opacity\r\n flex items-center gap-2\r\n ", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Download, { className: "w-4 h-4" }), "Load"] }), (0, jsx_runtime_1.jsx)("button", { onClick: () => handleDelete(draft.draftId), disabled: deletingId === draft.draftId, className: "\r\n p-2 rounded-lg\r\n text-[rgb(var(--fg-muted))]\r\n hover:text-red-600\r\n hover:bg-red-50\r\n transition-colors\r\n disabled:opacity-50\r\n ", title: "Delete draft", children: deletingId === draft.draftId ? ((0, jsx_runtime_1.jsx)("div", { className: "w-4 h-4 rounded-full border-2 border-red-200 border-t-red-600 animate-spin" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { className: "w-4 h-4" })) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 gap-x-6 gap-y-1 mt-2 text-sm", children: [preview.jobName && ((0, jsx_runtime_1.jsx)("div", { className: "text-[rgb(var(--fg-muted))] truncate", children: preview.jobName }, "job")), preview.clientName && ((0, jsx_runtime_1.jsx)("div", { className: "text-[rgb(var(--fg-muted))] truncate", children: preview.clientName }, "client")), preview.category && ((0, jsx_runtime_1.jsx)("div", { className: "text-[rgb(var(--fg-muted))] truncate", children: preview.category }, "category")), preview.totalCost !== undefined && preview.totalCost > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "font-medium text-[rgb(var(--color-primary))]", children: [currencySymbol, preview.totalCost.toLocaleString('en-IN')] }, "cost"))] }), progress > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between text-xs text-[rgb(var(--fg-muted))] mb-1", children: [(0, jsx_runtime_1.jsx)("span", { children: "Progress" }), (0, jsx_runtime_1.jsxs)("span", { className: "font-medium", children: [progress, "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-1.5 bg-[rgb(var(--bg-muted))] rounded-full overflow-hidden", children: (0, jsx_runtime_1.jsx)("div", { className: "h-full bg-[rgb(var(--color-primary))] transition-all duration-500 rounded-full", style: { width: `${progress}%` } }) })] }))] }) }) }, draft.draftId));
|
|
199
199
|
}) })) }), (0, jsx_runtime_1.jsxs)("div", { className: "px-6 py-4 border-t border-[rgb(var(--bd-default))] flex justify-between items-center bg-[rgb(var(--bg-muted)_/_0.3)]", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-sm text-[rgb(var(--fg-muted))]", children: filteredAndSortedDrafts.length > 0 ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Showing ", ((currentPage - 1) * PAGE_SIZE) + 1, "-", Math.min(currentPage * PAGE_SIZE, filteredAndSortedDrafts.length), " of ", filteredAndSortedDrafts.length, filteredAndSortedDrafts.length !== drafts.length && ` (filtered from ${drafts.length})`] })) : (`0 of ${drafts.length} drafts`) }), totalPages > 1 && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setCurrentPage(p => Math.max(1, p - 1)), disabled: currentPage === 1, className: "p-1.5 rounded text-[rgb(var(--fg-muted))] hover:bg-[rgb(var(--bg-default))] disabled:opacity-30 disabled:cursor-not-allowed transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronLeft, { className: "w-4 h-4" }) }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-1", children: Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
|
200
200
|
let pageNum;
|
|
201
201
|
if (totalPages <= 5) {
|
|
@@ -210,11 +210,11 @@ function DraftModal({ open, onClose, drafts, onLoad, onDelete, isLoading = false
|
|
|
210
210
|
else {
|
|
211
211
|
pageNum = currentPage - 2 + i;
|
|
212
212
|
}
|
|
213
|
-
return ((0, jsx_runtime_1.jsx)("button", { onClick: () => setCurrentPage(pageNum), className: `
|
|
214
|
-
w-8 h-8 rounded text-sm font-medium transition-colors
|
|
213
|
+
return ((0, jsx_runtime_1.jsx)("button", { onClick: () => setCurrentPage(pageNum), className: `
|
|
214
|
+
w-8 h-8 rounded text-sm font-medium transition-colors
|
|
215
215
|
${currentPage === pageNum
|
|
216
216
|
? 'bg-[rgb(var(--color-primary))] text-white'
|
|
217
|
-
: 'text-[rgb(var(--fg-muted))] hover:bg-[rgb(var(--bg-default))]'}
|
|
217
|
+
: 'text-[rgb(var(--fg-muted))] hover:bg-[rgb(var(--bg-default))]'}
|
|
218
218
|
`, children: pageNum }, pageNum));
|
|
219
|
-
}) }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setCurrentPage(p => Math.min(totalPages, p + 1)), disabled: currentPage === totalPages, className: "p-1.5 rounded text-[rgb(var(--fg-muted))] hover:bg-[rgb(var(--bg-default))] disabled:opacity-30 disabled:cursor-not-allowed transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "w-4 h-4" }) })] })), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "\n px-5 py-2 rounded-lg text-sm font-medium\n bg-[rgb(var(--bg-default))]\n text-[rgb(var(--fg-default))]\n border border-[rgb(var(--bd-default))]\n hover:bg-[rgb(var(--bg-muted))]\n transition-colors\n ", children: "Close" })] })] }) }));
|
|
219
|
+
}) }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setCurrentPage(p => Math.min(totalPages, p + 1)), disabled: currentPage === totalPages, className: "p-1.5 rounded text-[rgb(var(--fg-muted))] hover:bg-[rgb(var(--bg-default))] disabled:opacity-30 disabled:cursor-not-allowed transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "w-4 h-4" }) })] })), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "\r\n px-5 py-2 rounded-lg text-sm font-medium\r\n bg-[rgb(var(--bg-default))]\r\n text-[rgb(var(--fg-default))]\r\n border border-[rgb(var(--bd-default))]\r\n hover:bg-[rgb(var(--bg-muted))]\r\n transition-colors\r\n ", children: "Close" })] })] }) }));
|
|
220
220
|
}
|
|
@@ -129,20 +129,14 @@ function DatePicker({ id, 'aria-label': ariaLabel, value, onChange, mode = 'sing
|
|
|
129
129
|
const normalizeDateToMidnight = (date) => {
|
|
130
130
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
|
|
131
131
|
};
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return `${day} ${month}, ${year}`;
|
|
137
|
-
};
|
|
132
|
+
// "1 Sep 2026": built by hand rather than with toLocaleDateString, whose en-GB and en-IN
|
|
133
|
+
// locales spell September "Sept". parseDate reads it back with or without a comma.
|
|
134
|
+
const formatDateNoYear = (date) => `${date.getDate()} ${shared_utils_1.MONTHS[date.getMonth()].slice(0, 3)}`;
|
|
135
|
+
const formatDate = (date) => `${formatDateNoYear(date)} ${date.getFullYear()}`;
|
|
138
136
|
const formatDateShort = (date) => {
|
|
139
137
|
if (!date)
|
|
140
138
|
return '--';
|
|
141
|
-
return date
|
|
142
|
-
day: '2-digit',
|
|
143
|
-
month: 'short',
|
|
144
|
-
year: 'numeric'
|
|
145
|
-
});
|
|
139
|
+
return formatDate(date);
|
|
146
140
|
};
|
|
147
141
|
const formatDisplayValue = () => {
|
|
148
142
|
if (!value)
|
|
@@ -700,9 +694,9 @@ function DatePicker({ id, 'aria-label': ariaLabel, value, onChange, mode = 'sing
|
|
|
700
694
|
}
|
|
701
695
|
// Default popover mode
|
|
702
696
|
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)('relative w-full', className), ref: containerRef, children: [label && ((0, jsx_runtime_1.jsx)("label", { className: "block text-sm font-medium text-[rgb(var(--fg-muted))] mb-2", children: label })), (0, jsx_runtime_1.jsxs)(Popover.Root, { open: !disabled && isOpen, onOpenChange: (open) => !disabled && setIsOpen(open), children: [(0, jsx_runtime_1.jsx)(Popover.Trigger, { asChild: true, disabled: disabled, children: iconOnly ? ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: (0, utils_1.cn)('relative flex items-center gap-1 text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] transition-colors p-0.5 flex-shrink-0', (selectedRange.from || selectedRange.to) && 'text-[rgb(var(--color-primary))]', className), title: selectedRange.from || selectedRange.to
|
|
703
|
-
? `Date filter: ${selectedRange.from ? new Date(selectedRange.from)
|
|
697
|
+
? `Date filter: ${selectedRange.from ? formatDate(new Date(selectedRange.from)) : ''}${selectedRange.to ? ` – ${formatDate(new Date(selectedRange.to))}` : ''}`
|
|
704
698
|
: 'Select date range', children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CalendarDays, { className: "h-3.5 w-3.5 flex-shrink-0" }), (0, jsx_runtime_1.jsx)("span", { className: "hidden sm:inline text-xs", children: selectedRange.from || selectedRange.to
|
|
705
|
-
? `${selectedRange.from ? new Date(selectedRange.from)
|
|
699
|
+
? `${selectedRange.from ? formatDateNoYear(new Date(selectedRange.from)) : ''}${selectedRange.to ? ` – ${formatDateNoYear(new Date(selectedRange.to))}` : ''}`
|
|
706
700
|
: 'Date' }), (selectedRange.from || selectedRange.to) && ((0, jsx_runtime_1.jsx)("span", { className: "absolute -top-0.5 -right-0.5 h-1.5 w-1.5 rounded-full bg-[rgb(var(--color-primary))] ring-1 ring-[rgb(var(--bg-surface))]" }))] })) : ((0, jsx_runtime_1.jsx)("div", { className: "relative", ref: inputWrapperRef, onClick: (e) => e.preventDefault(), children: (0, jsx_runtime_1.jsx)(ui_2.Input, { id: id, "aria-label": ariaLabel, value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleInputKeyDown, placeholder: getPlaceholder(), disabled: disabled, error: error, className: (0, utils_1.cn)('pl-2 pr-9 text-xs w-full', error && 'border-red-300'), onFocus: () => !disabled && setIsOpen(true), rightIcon: lucide_react_1.Calendar, onRightIconClick: () => !disabled && setIsOpen(!isOpen) }) })) }), (0, jsx_runtime_1.jsx)(Popover.Portal, { container: typeof document !== 'undefined' ? document.body : undefined, children: (0, jsx_runtime_1.jsxs)(Popover.Content, { ref: calendarRef, className: (0, utils_1.cn)("z-[300] bg-[rgb(var(--bg-surface))] rounded-xl shadow-2xl border border-[rgb(var(--bd-default))] backdrop-blur-sm", "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95", "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95", "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2", mode === 'range' ? 'w-[95vw] sm:w-auto sm:min-w-[580px] p-2' : 'w-[95vw] sm:w-auto sm:min-w-[280px] p-3', 'max-w-[calc(100vw-2rem)]'), side: "bottom", align: popoverAlign ?? (mode === 'range' ? 'end' : 'start'), sideOffset: 8, avoidCollisions: true, collisionPadding: 16, sticky: "always", onOpenAutoFocus: (e) => e.preventDefault(), onKeyDown: handleCalendarKeyDown, tabIndex: -1, onInteractOutside: (e) => {
|
|
707
701
|
// Prevent closing when interacting with nested Radix portals (like Dropdowns)
|
|
708
702
|
const target = e.target;
|
|
@@ -76,7 +76,7 @@ function DateTimePicker({ value, onChange, placeholder = 'Select date & time...'
|
|
|
76
76
|
return returnFormat === 'string' ? date.toISOString() : date;
|
|
77
77
|
};
|
|
78
78
|
const formatDisplayValue = (date) => {
|
|
79
|
-
const day = date.getDate()
|
|
79
|
+
const day = date.getDate();
|
|
80
80
|
const month = shared_utils_1.MONTHS[date.getMonth()].slice(0, 3);
|
|
81
81
|
const year = date.getFullYear();
|
|
82
82
|
let timeStr = '';
|
|
@@ -90,7 +90,7 @@ function DateTimePicker({ value, onChange, placeholder = 'Select date & time...'
|
|
|
90
90
|
else {
|
|
91
91
|
timeStr = `${hrs.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}`;
|
|
92
92
|
}
|
|
93
|
-
return `${day} ${month}
|
|
93
|
+
return `${day} ${month} ${year} ${timeStr}`;
|
|
94
94
|
};
|
|
95
95
|
const handleDateClick = (date) => {
|
|
96
96
|
setSelectedDate(date);
|
|
@@ -6,6 +6,7 @@ exports.CompactFooter = CompactFooter;
|
|
|
6
6
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
7
|
const utils_1 = require("../../lib/utils.js");
|
|
8
8
|
const useTranslation_1 = require("../../hooks/useTranslation.js");
|
|
9
|
+
const number_format_1 = require("../../lib/number-format.js");
|
|
9
10
|
function Footer({ kpis, actions, iconButtons, leftContent, className, sticky = false, compact = false, padding, variant = 'page', gradient = false }) {
|
|
10
11
|
if (!kpis && !actions && !leftContent && !iconButtons) {
|
|
11
12
|
return null;
|
|
@@ -45,7 +46,7 @@ function Footer({ kpis, actions, iconButtons, leftContent, className, sticky = f
|
|
|
45
46
|
padding === 'normal' ? "py-3 sm:py-4" :
|
|
46
47
|
compact ? "py-1" :
|
|
47
48
|
isModal ? "py-1.5 sm:py-2" : "py-3 sm:py-4"), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center gap-2 sm:gap-4 w-full sm:w-auto", children: [leftContent, kpis && kpis.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap items-center gap-3 sm:gap-6 md:gap-8", children: kpis.map((kpi) => ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)("flex items-center gap-1.5 sm:gap-2 text-xs sm:text-sm", kpi.onClick && "cursor-pointer hover:text-[rgb(var(--color-primary-hover))] transition-colors"), onClick: kpi.onClick, children: [(0, jsx_runtime_1.jsxs)("span", { className: "text-[rgb(var(--fg-muted))] whitespace-nowrap", children: [kpi.label, ":"] }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium text-[rgb(var(--fg-default))]", children: typeof kpi.value === 'number'
|
|
48
|
-
? kpi.value
|
|
49
|
+
? (0, number_format_1.formatNumber)(kpi.value)
|
|
49
50
|
: kpi.value }), kpi.trend && ((0, jsx_runtime_1.jsxs)("span", { className: (0, utils_1.cn)("text-xs font-medium", kpi.trend.direction === 'up'
|
|
50
51
|
? "text-[rgb(var(--color-success))]"
|
|
51
52
|
: "text-[rgb(var(--color-error))]"), children: [kpi.trend.direction === 'up' ? '↑' : '↓', Math.abs(kpi.trend.value), "%"] }))] }, kpi.id))) }))] }), iconButtons && iconButtons.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap items-center gap-1.5 sm:gap-2", children: iconButtons.map((button) => {
|
|
@@ -110,7 +110,10 @@ function SidePanel({ title, icon: Icon, categories, activeItemId, onItemSelect,
|
|
|
110
110
|
return (0, jsx_runtime_1.jsx)(ItemIcon, { className: "w-4 h-4 shrink-0 opacity-50" });
|
|
111
111
|
})(), (0, jsx_runtime_1.jsx)("span", { className: "truncate flex-1", children: item.label }), item.badge !== undefined && item.badge !== 0 && ((0, jsx_runtime_1.jsx)("span", { className: (0, utils_1.cn)("text-xs px-1.5 py-0.5 rounded-full shrink-0 tabular-nums", activeItemId === item.id
|
|
112
112
|
? "bg-[rgb(var(--color-primary))] text-white"
|
|
113
|
-
: "bg-[rgb(var(--bg-subtle))] text-[rgb(var(--fg-muted))]"), children: item.badge }))] }), itemContextMenu && (
|
|
113
|
+
: "bg-[rgb(var(--bg-subtle))] text-[rgb(var(--fg-muted))]"), children: item.badge }))] }), itemContextMenu && (
|
|
114
|
+
// Named for screen readers, and shown on keyboard focus as well as hover:
|
|
115
|
+
// it was an unlabelled button nobody tabbing could see.
|
|
116
|
+
(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: (e) => handleMenuToggle(e, item.id), "aria-label": `More actions for ${item.label}`, "aria-haspopup": "menu", "aria-expanded": menuOpen === item.id, className: "absolute right-1.5 top-1/2 -translate-y-1/2 w-6 h-6 rounded-md text-[rgb(var(--fg-muted))]/50 flex items-center justify-center hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-all opacity-0 group-hover:opacity-100 focus-visible:opacity-100", children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreVertical, { className: "w-3.5 h-3.5" }) }))] }, item.id))) }))] }, category.id))))] })) }), footer && !isCollapsed && ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)("shrink-0 border-t border-[rgb(var(--bd-default))] transition-opacity duration-200", isAnimating ? "opacity-0" : "opacity-100"), children: footer }))] }), menuOpen !== null && itemContextMenu && ((0, jsx_runtime_1.jsx)("div", { ref: menuRef, className: "fixed bg-[rgb(var(--bg-surface))] border border-[rgb(var(--bd-default))] rounded-lg min-w-[170px] shadow-lg overflow-hidden z-[9999]", style: {
|
|
114
117
|
left: `${menuPosition.left}px`,
|
|
115
118
|
top: `${menuPosition.top}px`,
|
|
116
119
|
transform: 'translateY(-50%)'
|
|
@@ -7,6 +7,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
7
7
|
const lucide_react_1 = require("lucide-react");
|
|
8
8
|
const utils_1 = require("../../../lib/utils.js");
|
|
9
9
|
const card_1 = require("./card.js");
|
|
10
|
+
const number_format_1 = require("../../../lib/number-format.js");
|
|
10
11
|
const iconVariants = {
|
|
11
12
|
default: "bg-primary text-white",
|
|
12
13
|
accent: "bg-accent text-white",
|
|
@@ -16,7 +17,7 @@ const iconVariants = {
|
|
|
16
17
|
function StatsCard({ title, value, icon: Icon, trend, description, className, variant = 'default' }) {
|
|
17
18
|
const TrendIcon = trend?.direction === 'up' ? lucide_react_1.TrendingUp : lucide_react_1.TrendingDown;
|
|
18
19
|
const trendColor = trend?.direction === 'up' ? 'text-success' : 'text-error';
|
|
19
|
-
return ((0, jsx_runtime_1.jsx)(card_1.Card, { className: (0, utils_1.cn)("p-6 min-h-[140px] w-full", className), children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-4", children: [Icon && ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)("flex-shrink-0 p-3 rounded-lg", iconVariants[variant]), children: (0, jsx_runtime_1.jsx)(Icon, { className: "h-6 w-6" }) })), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-baseline flex-wrap gap-2", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-2xl font-semibold text-fg-default truncate", children: typeof value === 'number' ?
|
|
20
|
+
return ((0, jsx_runtime_1.jsx)(card_1.Card, { className: (0, utils_1.cn)("p-6 min-h-[140px] w-full", className), children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-4", children: [Icon && ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)("flex-shrink-0 p-3 rounded-lg", iconVariants[variant]), children: (0, jsx_runtime_1.jsx)(Icon, { className: "h-6 w-6" }) })), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-baseline flex-wrap gap-2", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-2xl font-semibold text-fg-default truncate", children: typeof value === 'number' ? (0, number_format_1.formatNumber)(value) : value }), trend && ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)("flex items-center text-sm font-semibold", trendColor), children: [(0, jsx_runtime_1.jsx)(TrendIcon, { className: "h-4 w-4 flex-shrink-0" }), (0, jsx_runtime_1.jsxs)("span", { className: "ml-1", children: [Math.abs(trend.value), "%", trend.label && ` ${trend.label}`] })] }))] }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-fg-muted mt-2 line-clamp-2", children: title }), description && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-fg-subtle mt-1 line-clamp-1", children: description }))] })] }) }));
|
|
20
21
|
}
|
|
21
22
|
function StatsGrid({ children, columns = 4, className }) {
|
|
22
23
|
const gridCols = {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* How every number in the library is written. Indian grouping by default (20,00,000, not
|
|
5
|
+
* 2,000,000): the products built on this library keep Indian books, and a bare `toLocaleString()`
|
|
6
|
+
* follows whatever locale the browser happens to be set to, so the same figure read differently on
|
|
7
|
+
* two machines.
|
|
8
|
+
*
|
|
9
|
+
* A product that needs another convention calls `setNumberLocale()` once at start-up.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.setNumberLocale = setNumberLocale;
|
|
13
|
+
exports.getNumberLocale = getNumberLocale;
|
|
14
|
+
exports.formatNumber = formatNumber;
|
|
15
|
+
exports.formatCompactNumber = formatCompactNumber;
|
|
16
|
+
let numberLocale = 'en-IN';
|
|
17
|
+
function setNumberLocale(locale) {
|
|
18
|
+
numberLocale = locale;
|
|
19
|
+
}
|
|
20
|
+
function getNumberLocale() {
|
|
21
|
+
return numberLocale;
|
|
22
|
+
}
|
|
23
|
+
/** A full number with grouping, up to two decimals. Empty for null, undefined or NaN. */
|
|
24
|
+
function formatNumber(value, maximumFractionDigits = 2) {
|
|
25
|
+
if (value == null || value === '')
|
|
26
|
+
return '';
|
|
27
|
+
const n = typeof value === 'number' ? value : Number(value);
|
|
28
|
+
if (!Number.isFinite(n))
|
|
29
|
+
return typeof value === 'string' ? value : '';
|
|
30
|
+
return n.toLocaleString(numberLocale, { maximumFractionDigits });
|
|
31
|
+
}
|
|
32
|
+
const trim = (n) => n.toFixed(n >= 100 ? 0 : n >= 10 ? 1 : 2).replace(/\.?0+$/, '');
|
|
33
|
+
/**
|
|
34
|
+
* A short number for chart axes, where a full figure runs into its neighbour: 12,500 · 20 L ·
|
|
35
|
+
* 1.4 Cr in Indian grouping; 12.5K · 2M · 1.4B otherwise. Below a lakh (or a thousand) it is the
|
|
36
|
+
* full number.
|
|
37
|
+
*/
|
|
38
|
+
function formatCompactNumber(value) {
|
|
39
|
+
if (value == null || value === '')
|
|
40
|
+
return '';
|
|
41
|
+
const n = typeof value === 'number' ? value : Number(value);
|
|
42
|
+
if (!Number.isFinite(n))
|
|
43
|
+
return String(value);
|
|
44
|
+
const sign = n < 0 ? '-' : '';
|
|
45
|
+
const a = Math.abs(n);
|
|
46
|
+
if (numberLocale === 'en-IN') {
|
|
47
|
+
if (a >= 1e7)
|
|
48
|
+
return `${sign}${trim(a / 1e7)} Cr`;
|
|
49
|
+
if (a >= 1e5)
|
|
50
|
+
return `${sign}${trim(a / 1e5)} L`;
|
|
51
|
+
return formatNumber(n, 0);
|
|
52
|
+
}
|
|
53
|
+
if (a >= 1e9)
|
|
54
|
+
return `${sign}${trim(a / 1e9)}B`;
|
|
55
|
+
if (a >= 1e6)
|
|
56
|
+
return `${sign}${trim(a / 1e6)}M`;
|
|
57
|
+
if (a >= 1e3)
|
|
58
|
+
return `${sign}${trim(a / 1e3)}K`;
|
|
59
|
+
return formatNumber(n, 0);
|
|
60
|
+
}
|
package/dist/cjs/lib/utils.js
CHANGED
|
@@ -11,11 +11,12 @@ exports.getModuleRoutePath = getModuleRoutePath;
|
|
|
11
11
|
exports.naturalCompare = naturalCompare;
|
|
12
12
|
const clsx_1 = require("clsx");
|
|
13
13
|
const tailwind_merge_1 = require("tailwind-merge");
|
|
14
|
+
const number_format_1 = require("./number-format.js");
|
|
14
15
|
function cn(...inputs) {
|
|
15
16
|
return (0, tailwind_merge_1.twMerge)((0, clsx_1.clsx)(inputs));
|
|
16
17
|
}
|
|
17
|
-
function formatCurrency(amount, currency = '
|
|
18
|
-
return new Intl.NumberFormat(
|
|
18
|
+
function formatCurrency(amount, currency = 'INR') {
|
|
19
|
+
return new Intl.NumberFormat((0, number_format_1.getNumberLocale)(), {
|
|
19
20
|
style: 'currency',
|
|
20
21
|
currency,
|
|
21
22
|
}).format(amount);
|
|
@@ -28,7 +29,7 @@ function formatDate(date) {
|
|
|
28
29
|
}).format(new Date(date));
|
|
29
30
|
}
|
|
30
31
|
function formatNumber(num, decimals = 0) {
|
|
31
|
-
return new Intl.NumberFormat(
|
|
32
|
+
return new Intl.NumberFormat((0, number_format_1.getNumberLocale)(), {
|
|
32
33
|
minimumFractionDigits: decimals,
|
|
33
34
|
maximumFractionDigits: decimals,
|
|
34
35
|
}).format(num);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
@tailwind base;
|
|
2
|
-
@tailwind components;
|
|
3
|
-
@tailwind utilities;
|
|
4
|
-
|
|
5
|
-
@import './tokens.css';
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
@import './tokens.css';
|
|
@@ -10,6 +10,7 @@ import { cn } from '../../lib/utils.js';
|
|
|
10
10
|
import { User, Copy, Check, Download, Share2, Pencil, Bot, RotateCcw, ThumbsUp, ThumbsDown } from 'lucide-react';
|
|
11
11
|
import ReactMarkdown from 'react-markdown';
|
|
12
12
|
import remarkGfm from 'remark-gfm';
|
|
13
|
+
import { formatNumber } from '../../lib/number-format.js';
|
|
13
14
|
// Individual Message Bubble Component
|
|
14
15
|
function Message({ message, onSelectItem, onEditMessage, onRegenerateAi, onFeedback, botAvatar, renderBotContent }) {
|
|
15
16
|
const isUser = message.role === 'user';
|
|
@@ -158,7 +159,7 @@ function Message({ message, onSelectItem, onEditMessage, onRegenerateAi, onFeedb
|
|
|
158
159
|
return full;
|
|
159
160
|
});
|
|
160
161
|
return processed;
|
|
161
|
-
})() }) })) })) })) }), !isEditing && (_jsxs("div", { className: cn('flex items-center gap-2 mt-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200', isUser ? 'justify-end' : 'justify-start'), children: [formattedTimestamp && (_jsx("span", { className: "text-[0.7rem] text-[rgb(var(--fg-muted))]/70 select-none", children: formattedTimestamp })), isUser && onEditMessage && (_jsx("button", { onClick: handleRegenerate, className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Regenerate response", title: "Regenerate", children: _jsx(RotateCcw, { className: "w-3.5 h-3.5" }) })), isUser && onEditMessage && (_jsx("button", { onClick: handleStartEdit, className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Edit message", title: "Edit", children: _jsx(Pencil, { className: "w-3.5 h-3.5" }) })), _jsx("button", { onClick: handleCopy, className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Copy message", title: "Copy", children: copied ? (_jsx(Check, { className: "w-3.5 h-3.5" })) : (_jsx(Copy, { className: "w-3.5 h-3.5" })) }), !isUser && onRegenerateAi && (_jsx("button", { onClick: handleRegenerateAi, className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Regenerate response", title: "Regenerate", children: _jsx(RotateCcw, { className: "w-3.5 h-3.5" }) })), !isUser && (_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => handleFeedback('up'), className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Good response", title: "Good response", children: _jsx(ThumbsUp, { className: "w-3.5 h-3.5", fill: feedback === 'up' ? 'currentColor' : 'none' }) }), _jsx("button", { onClick: () => handleFeedback('down'), className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Bad response", title: "Bad response", children: _jsx(ThumbsDown, { className: "w-3.5 h-3.5", fill: feedback === 'down' ? 'currentColor' : 'none' }) })] })), !isUser && message.tokens && process.env.NEXT_PUBLIC_SHOW_TOKENS === 'true' && (_jsxs("span", { className: "text-[0.65rem] text-[rgb(var(--fg-muted))]/70 font-mono select-none ml-1", children: ["P: ", message.tokens.prompt
|
|
162
|
+
})() }) })) })) })) }), !isEditing && (_jsxs("div", { className: cn('flex items-center gap-2 mt-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200', isUser ? 'justify-end' : 'justify-start'), children: [formattedTimestamp && (_jsx("span", { className: "text-[0.7rem] text-[rgb(var(--fg-muted))]/70 select-none", children: formattedTimestamp })), isUser && onEditMessage && (_jsx("button", { onClick: handleRegenerate, className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Regenerate response", title: "Regenerate", children: _jsx(RotateCcw, { className: "w-3.5 h-3.5" }) })), isUser && onEditMessage && (_jsx("button", { onClick: handleStartEdit, className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Edit message", title: "Edit", children: _jsx(Pencil, { className: "w-3.5 h-3.5" }) })), _jsx("button", { onClick: handleCopy, className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Copy message", title: "Copy", children: copied ? (_jsx(Check, { className: "w-3.5 h-3.5" })) : (_jsx(Copy, { className: "w-3.5 h-3.5" })) }), !isUser && onRegenerateAi && (_jsx("button", { onClick: handleRegenerateAi, className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Regenerate response", title: "Regenerate", children: _jsx(RotateCcw, { className: "w-3.5 h-3.5" }) })), !isUser && (_jsxs(_Fragment, { children: [_jsx("button", { onClick: () => handleFeedback('up'), className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Good response", title: "Good response", children: _jsx(ThumbsUp, { className: "w-3.5 h-3.5", fill: feedback === 'up' ? 'currentColor' : 'none' }) }), _jsx("button", { onClick: () => handleFeedback('down'), className: "w-7 h-7 rounded-md flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-colors", "aria-label": "Bad response", title: "Bad response", children: _jsx(ThumbsDown, { className: "w-3.5 h-3.5", fill: feedback === 'down' ? 'currentColor' : 'none' }) })] })), !isUser && message.tokens && process.env.NEXT_PUBLIC_SHOW_TOKENS === 'true' && (_jsxs("span", { className: "text-[0.65rem] text-[rgb(var(--fg-muted))]/70 font-mono select-none ml-1", children: ["P: ", formatNumber(message.tokens.prompt), " R: ", formatNumber(message.tokens.completion), " T: ", formatNumber(message.tokens.total)] }))] }))] })] }));
|
|
162
163
|
}
|
|
163
164
|
// Synthia thinking phrases — the fun ones
|
|
164
165
|
const SYNTHIA_THINKING = [
|
|
@@ -260,6 +261,6 @@ export function Messages({ messages, isLoading = false, onExport, onShare, onSel
|
|
|
260
261
|
}, { prompt: 0, completion: 0, total: 0 });
|
|
261
262
|
if (totals.total === 0)
|
|
262
263
|
return _jsx("div", {});
|
|
263
|
-
return (_jsxs("span", { className: "text-[0.65rem] text-[rgb(var(--fg-muted))] font-mono", children: ["P:", totals.prompt
|
|
264
|
+
return (_jsxs("span", { className: "text-[0.65rem] text-[rgb(var(--fg-muted))] font-mono", children: ["P:", formatNumber(totals.prompt), " \u00B7 C:", formatNumber(totals.completion), " \u00B7 T:", formatNumber(totals.total), " \u00B7 ", messages.filter(m => m.tokens).length, " calls"] }));
|
|
264
265
|
})() : _jsx("div", {}), _jsxs("div", { className: "flex gap-2", children: [onExport && (_jsxs("button", { onClick: onExport, className: "flex items-center gap-2 px-3 py-1.5 text-sm text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] rounded-lg transition-colors", children: [_jsx(Download, { className: "w-4 h-4" }), "Export"] })), onShare && (_jsxs("button", { onClick: onShare, className: "flex items-center gap-2 px-3 py-1.5 text-sm text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] rounded-lg transition-colors", children: [_jsx(Share2, { className: "w-4 h-4" }), "Share"] }))] })] })), _jsx("div", { className: "flex-1 overflow-y-auto", children: _jsxs("div", { className: "max-w-5xl mx-auto px-3 sm:px-4 py-6 space-y-6 animate-[fadeInUp_0.28s_ease-out]", children: [messages.map((message) => (_jsx(Message, { message: message, onSelectItem: onSelectItem, onEditMessage: onEditMessage, onRegenerateAi: onRegenerateAi, onFeedback: onFeedback, botAvatar: botAvatar, renderBotContent: renderBotContent }, message.id))), isLoading && _jsx(TypingIndicator, { lastUserMessage: messages.filter(m => m.role === 'user').at(-1)?.content }), _jsx("div", { ref: bottomRef })] }, fadeKey) })] }));
|
|
265
266
|
}
|
|
@@ -18,6 +18,7 @@ import { MultimodalInput } from './multimodal-input.js';
|
|
|
18
18
|
import { ChatPromptNavigator } from './chat-prompt-navigator.js';
|
|
19
19
|
import { DatePicker } from '../forms/date-picker/index.js';
|
|
20
20
|
import { getLocalDateString } from '../../lib/utils.js';
|
|
21
|
+
import { formatNumber } from '../../lib/number-format.js';
|
|
21
22
|
function buildSendPayload(ctx, message, autoTitle) {
|
|
22
23
|
if (ctx.mode === 'new') {
|
|
23
24
|
return { message, conversationId: undefined, newChat: true, title: autoTitle };
|
|
@@ -1019,7 +1020,7 @@ export function Chat({ mode = 'fullpage', externalMuted, title = 'Chat', sidebar
|
|
|
1019
1020
|
}, { prompt: 0, completion: 0, total: 0 });
|
|
1020
1021
|
if (totals.total === 0)
|
|
1021
1022
|
return null;
|
|
1022
|
-
return (_jsxs("span", { className: "text-[0.65rem] text-[rgb(var(--fg-muted))] font-mono", children: ["Prompt: ", totals.prompt
|
|
1023
|
+
return (_jsxs("span", { className: "text-[0.65rem] text-[rgb(var(--fg-muted))] font-mono", children: ["Prompt: ", formatNumber(totals.prompt), " \u00B7 Response: ", formatNumber(totals.completion), " \u00B7 Total: ", formatNumber(totals.total), " tokens \u00B7 ", messages.filter(m => m.tokens).length, " calls"] }));
|
|
1023
1024
|
})(), _jsxs("div", { className: "absolute right-3 flex items-center gap-0.5", children: [headerActions, _jsx("button", { onClick: () => setIsMuted(!isMuted), className: "w-7 h-7 flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] rounded-md transition-colors", title: isMuted ? "Unmute sounds" : "Mute sounds", children: isMuted ? _jsx(VolumeX, { className: "w-3.5 h-3.5" }) : _jsx(Volume2, { className: "w-3.5 h-3.5" }) }), messages.length > 0 && (_jsxs(_Fragment, { children: [_jsx("button", { onClick: handleExport, className: "w-7 h-7 flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] rounded-md transition-colors", title: t('Export'), children: _jsx(Download, { className: "w-3.5 h-3.5" }) }), _jsx("button", { onClick: handleShare, className: "w-7 h-7 flex items-center justify-center text-[rgb(var(--fg-muted))] hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] rounded-md transition-colors", title: t('Share'), children: _jsx(Share2, { className: "w-3.5 h-3.5" }) })] })), fullpagePath && mode === 'modal' && (_jsx("button", { onClick: () => {
|
|
1024
1025
|
const target = currentConversationId
|
|
1025
1026
|
? `${fullpagePath}?c=${currentConversationId}`
|
|
@@ -20,9 +20,13 @@ export interface AreaChartProps {
|
|
|
20
20
|
showLegend?: boolean;
|
|
21
21
|
showGrid?: boolean;
|
|
22
22
|
smooth?: boolean;
|
|
23
|
+
/** How a value reads in the tooltip. Default: the full number, Indian grouping (20,00,000). */
|
|
24
|
+
valueFormatter?: (value: number) => string;
|
|
25
|
+
/** How a value reads on the value axis. Default: compact (12,500 · 20 L · 1.4 Cr). */
|
|
26
|
+
axisFormatter?: (value: number) => string;
|
|
23
27
|
className?: string;
|
|
24
28
|
}
|
|
25
29
|
/**
|
|
26
30
|
* AreaChart - ECharts Area Chart component
|
|
27
31
|
*/
|
|
28
|
-
export declare function AreaChart({ data, xKey, series, height, showLegend, showGrid, smooth, className }: AreaChartProps): import("react").JSX.Element;
|
|
32
|
+
export declare function AreaChart({ data, xKey, series, height, showLegend, showGrid, smooth, valueFormatter, axisFormatter, className }: AreaChartProps): import("react").JSX.Element;
|