@liiift-studio/sales-portal 3.1.4 → 3.1.5
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/components/Insights.js +5 -8
- package/components/YearOverview.js +53 -35
- package/package.json +1 -1
package/components/Insights.js
CHANGED
|
@@ -67,6 +67,7 @@ export default function Insights({
|
|
|
67
67
|
let grossRevenue, netRevenue, orderCount, averageOrderValue, discountTotal, discountRate, refundTotal, refundRate;
|
|
68
68
|
let topLocation = 'Unknown', topLocationPercentage = 0, locationChange = null;
|
|
69
69
|
let hasPreviousData = false;
|
|
70
|
+
let previousRevenue = 0, previousOrderCount = 0, previousAvgOrderValue = 0, previousRefundTotal = 0;
|
|
70
71
|
let revenueChange = null, orderCountChange = null, avgOrderValueChange = null, refundChange = null;
|
|
71
72
|
|
|
72
73
|
if (isYearView) {
|
|
@@ -113,11 +114,6 @@ export default function Insights({
|
|
|
113
114
|
locationChange = locData.locationChange ?? null;
|
|
114
115
|
|
|
115
116
|
// Calculate previous period metrics (if available)
|
|
116
|
-
let previousRevenue = 0;
|
|
117
|
-
let previousOrderCount = 0;
|
|
118
|
-
let previousAvgOrderValue = 0;
|
|
119
|
-
let previousRefundTotal = 0;
|
|
120
|
-
|
|
121
117
|
if (previousSales && previousSales.length > 0) {
|
|
122
118
|
hasPreviousData = true;
|
|
123
119
|
previousOrderCount = previousSales.length;
|
|
@@ -167,21 +163,21 @@ export default function Insights({
|
|
|
167
163
|
title: 'Gross Sales',
|
|
168
164
|
value: formatCurrency(grossRevenue),
|
|
169
165
|
change: revenueChange,
|
|
170
|
-
tooltip: isYearView ? `${year} year total` : hasPreviousData ?
|
|
166
|
+
tooltip: isYearView ? `${year} year total` : hasPreviousData ? `Previous: ${formatCurrency(previousRevenue)}` : 'No prior data available',
|
|
171
167
|
bgcolor: '#000000',
|
|
172
168
|
},
|
|
173
169
|
{
|
|
174
170
|
title: 'Orders',
|
|
175
171
|
value: orderCount,
|
|
176
172
|
change: orderCountChange,
|
|
177
|
-
tooltip: isYearView ? `${year} year total` : hasPreviousData ?
|
|
173
|
+
tooltip: isYearView ? `${year} year total` : hasPreviousData ? `Previous: ${previousOrderCount}` : 'No prior data available',
|
|
178
174
|
bgcolor: '#000000',
|
|
179
175
|
},
|
|
180
176
|
{
|
|
181
177
|
title: 'Avg. Order Value',
|
|
182
178
|
value: formatCurrency(averageOrderValue),
|
|
183
179
|
change: avgOrderValueChange,
|
|
184
|
-
tooltip: isYearView ? `${year} year average` : hasPreviousData ?
|
|
180
|
+
tooltip: isYearView ? `${year} year average` : hasPreviousData ? `Previous: ${formatCurrency(previousAvgOrderValue)}` : 'No prior data available',
|
|
185
181
|
bgcolor: '#000000',
|
|
186
182
|
},
|
|
187
183
|
{
|
|
@@ -253,6 +249,7 @@ export default function Insights({
|
|
|
253
249
|
sx={{
|
|
254
250
|
display: 'flex',
|
|
255
251
|
flexWrap: 'wrap',
|
|
252
|
+
width: '100%',
|
|
256
253
|
gap: isMobile ? 1.5 : 2,
|
|
257
254
|
mt: 2,
|
|
258
255
|
position: 'relative',
|
|
@@ -112,16 +112,6 @@ export default function YearOverview({ data, loading = false, error = '', year,
|
|
|
112
112
|
|
|
113
113
|
return (
|
|
114
114
|
<Box sx={{ width: '100%' }}>
|
|
115
|
-
{/* Year total */}
|
|
116
|
-
<Typography variant="h6" sx={{ mb: 2, opacity: 0.7 }}>
|
|
117
|
-
{year} Total: {symbol}{(data.yearTotal / 100).toLocaleString('en-US', { minimumFractionDigits: 2 })}
|
|
118
|
-
{data.yearShipping > 0 && (
|
|
119
|
-
<span style={{ opacity: 0.5, fontSize: '0.8em' }}>
|
|
120
|
-
{' '}(+ {symbol}{(data.yearShipping / 100).toLocaleString('en-US', { minimumFractionDigits: 2 })} shipping)
|
|
121
|
-
</span>
|
|
122
|
-
)}
|
|
123
|
-
</Typography>
|
|
124
|
-
|
|
125
115
|
{/* Stacked bar chart */}
|
|
126
116
|
{series.length > 0 ? (
|
|
127
117
|
<Box sx={{ width: '100%', height: { xs: 200, sm: 300, md: 350 } }}>
|
|
@@ -176,35 +166,63 @@ export default function YearOverview({ data, loading = false, error = '', year,
|
|
|
176
166
|
)}
|
|
177
167
|
|
|
178
168
|
{/* Monthly breakdown table */}
|
|
179
|
-
<Box sx={{
|
|
169
|
+
<Box sx={{
|
|
170
|
+
mt: 4,
|
|
171
|
+
mb: 6,
|
|
172
|
+
opacity: 0.8,
|
|
173
|
+
display: 'grid',
|
|
174
|
+
gridTemplateColumns: 'auto auto auto',
|
|
175
|
+
columnGap: { xs: 3, sm: 4 },
|
|
176
|
+
width: 'fit-content',
|
|
177
|
+
}}>
|
|
180
178
|
{data.months.map((m, i) => (
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
>
|
|
195
|
-
<Typography variant="body2">
|
|
179
|
+
<React.Fragment key={i}>
|
|
180
|
+
<Typography
|
|
181
|
+
variant="body2"
|
|
182
|
+
onClick={() => onMonthClick && onMonthClick(i)}
|
|
183
|
+
sx={{
|
|
184
|
+
py: { xs: 1.5, sm: 0.75 },
|
|
185
|
+
pl: { xs: 1.5, sm: 1 },
|
|
186
|
+
cursor: 'pointer',
|
|
187
|
+
borderBottom: '1px solid rgba(0,0,0,0.06)',
|
|
188
|
+
'&:hover': { bgcolor: 'rgba(0,0,0,0.03)' },
|
|
189
|
+
borderRadius: '4px 0 0 4px',
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
196
192
|
{MONTH_LABELS[i]} {year}
|
|
197
193
|
{m.error && <span style={{ color: 'var(--red, red)', marginLeft: 8 }}>error</span>}
|
|
198
194
|
</Typography>
|
|
199
|
-
<
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
195
|
+
<Typography
|
|
196
|
+
variant="body2"
|
|
197
|
+
onClick={() => onMonthClick && onMonthClick(i)}
|
|
198
|
+
sx={{
|
|
199
|
+
py: { xs: 1.5, sm: 0.75 },
|
|
200
|
+
opacity: 0.5,
|
|
201
|
+
textAlign: 'right',
|
|
202
|
+
cursor: 'pointer',
|
|
203
|
+
borderBottom: '1px solid rgba(0,0,0,0.06)',
|
|
204
|
+
'&:hover': { bgcolor: 'rgba(0,0,0,0.03)' },
|
|
205
|
+
}}
|
|
206
|
+
>
|
|
207
|
+
{m.salesCount} sale{m.salesCount !== 1 ? 's' : ''}
|
|
208
|
+
</Typography>
|
|
209
|
+
<Typography
|
|
210
|
+
variant="body2"
|
|
211
|
+
onClick={() => onMonthClick && onMonthClick(i)}
|
|
212
|
+
sx={{
|
|
213
|
+
py: { xs: 1.5, sm: 0.75 },
|
|
214
|
+
pr: { xs: 1.5, sm: 1 },
|
|
215
|
+
fontWeight: 'bold',
|
|
216
|
+
textAlign: 'right',
|
|
217
|
+
cursor: 'pointer',
|
|
218
|
+
borderBottom: '1px solid rgba(0,0,0,0.06)',
|
|
219
|
+
'&:hover': { bgcolor: 'rgba(0,0,0,0.03)' },
|
|
220
|
+
borderRadius: '0 4px 4px 0',
|
|
221
|
+
}}
|
|
222
|
+
>
|
|
223
|
+
{symbol}{(m.total / 100).toLocaleString('en-US', { minimumFractionDigits: 2 })}
|
|
224
|
+
</Typography>
|
|
225
|
+
</React.Fragment>
|
|
208
226
|
))}
|
|
209
227
|
</Box>
|
|
210
228
|
</Box>
|