@liiift-studio/sales-portal 3.1.4 → 3.1.6
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
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',
|
package/components/Sales.js
CHANGED
|
@@ -706,56 +706,53 @@ export default function Sales(props) {
|
|
|
706
706
|
const portalIndex = allPortals.indexOf(currentPortal) + 1;
|
|
707
707
|
|
|
708
708
|
style.textContent = `
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
page-break-inside: avoid;
|
|
757
|
-
}
|
|
758
|
-
}
|
|
709
|
+
@media print {
|
|
710
|
+
@page { size: letter portrait; margin: 0.5in; }
|
|
711
|
+
body { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
|
|
712
|
+
|
|
713
|
+
/* Hide site chrome and other portals */
|
|
714
|
+
header, nav, footer, #navBar, #footer, #titleContainer,
|
|
715
|
+
.exportSection, .date-range-sales-table-wrapper,
|
|
716
|
+
.salesPortal:not(:nth-child(${portalIndex})) { display: none !important; }
|
|
717
|
+
|
|
718
|
+
/* Hide interactive controls */
|
|
719
|
+
.MuiIconButton-root, .MuiInput-root, .MuiDateCalendar-root,
|
|
720
|
+
.print-button, .MuiFilledInput-root, .MuiFormControl-root { display: none !important; }
|
|
721
|
+
|
|
722
|
+
/* Portal layout */
|
|
723
|
+
.salesPortal, .salesPortalWrap { background: white !important; padding: 0 !important; width: 100% !important; }
|
|
724
|
+
|
|
725
|
+
/* Full width sections */
|
|
726
|
+
.sales-header-section, .sales-data-section, .insights-wrapper, .sales-table-wrapper,
|
|
727
|
+
.sales-chart-wrapper, .year-overview-wrapper, .typeface-list-wrapper,
|
|
728
|
+
.license-type-list-wrapper, .top-performers-wrapper { width: 100% !important; padding: 0 !important; margin: 10px 0 !important; }
|
|
729
|
+
|
|
730
|
+
/* Page break control */
|
|
731
|
+
.insights-wrapper, .top-performers-wrapper, .typeface-list-wrapper,
|
|
732
|
+
.license-type-list-wrapper, .sales-table-wrapper { page-break-inside: avoid; break-inside: avoid; }
|
|
733
|
+
|
|
734
|
+
/* Prevent cutting elements across pages */
|
|
735
|
+
.MuiPaper-root, tr, .designers-section > div,
|
|
736
|
+
.top-performers-section > div, .product-section > div { page-break-inside: avoid; break-inside: avoid; }
|
|
737
|
+
|
|
738
|
+
/* Insight cards: keep dark bg with white text */
|
|
739
|
+
.insights-wrapper .MuiPaper-root { background-color: #000 !important; color: white !important; border: none !important; box-shadow: none !important; }
|
|
740
|
+
.insights-wrapper .MuiTypography-root { color: inherit !important; }
|
|
741
|
+
|
|
742
|
+
/* Other text: black on white */
|
|
743
|
+
.sales-header-section .MuiTypography-root, .typeface-list-wrapper .MuiTypography-root,
|
|
744
|
+
.license-type-list-wrapper .MuiTypography-root, .sales-table-wrapper .MuiTypography-root { color: black !important; }
|
|
745
|
+
|
|
746
|
+
/* Section header bars: keep dark bg */
|
|
747
|
+
.designers-section .MuiTypography-h5, .top-performers-section .MuiTypography-h5,
|
|
748
|
+
.product-section .MuiTypography-h5 { color: white !important; }
|
|
749
|
+
|
|
750
|
+
/* Charts: ensure visible */
|
|
751
|
+
.sales-chart-wrapper, .year-overview-wrapper { max-height: 400px !important; overflow: hidden !important; page-break-inside: avoid; }
|
|
752
|
+
|
|
753
|
+
/* Compact tables */
|
|
754
|
+
.MuiTableCell-root { padding: 4px 8px !important; font-size: 10px !important; }
|
|
755
|
+
}
|
|
759
756
|
`;
|
|
760
757
|
document.head.appendChild(style);
|
|
761
758
|
|
|
@@ -196,7 +196,7 @@ export default function TopPerformers({
|
|
|
196
196
|
|
|
197
197
|
{/* Individual typefaces for this designer */}
|
|
198
198
|
{typefaceData
|
|
199
|
-
?.filter(typeface => typeface.author
|
|
199
|
+
?.filter(typeface => typeface.author?._id === designer._id)
|
|
200
200
|
.map((typeface, j) => (
|
|
201
201
|
<Typography
|
|
202
202
|
key={`designer-typeface-${i}-${j}`}
|
|
@@ -209,7 +209,7 @@ export default function TopPerformers({
|
|
|
209
209
|
}}>
|
|
210
210
|
{typeface.title}
|
|
211
211
|
<span className={styles.earningContainer}>
|
|
212
|
-
<strong> {(typeface.total / (total + chartState
|
|
212
|
+
<strong> {(typeface.total / (total + (chartState?.refundTotal || 0))).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</strong>%
|
|
213
213
|
</span>
|
|
214
214
|
<span style={{ opacity: "0.5", textTransform: "none" }}> ({typeface.orders} Orders for a total of </span>
|
|
215
215
|
<span style={{ opacity: "0.5" }}> USD</span>$<strong>{(typeface.total / 100).toLocaleString('en-US', { minimumFractionDigits: 2 })}</strong>
|
|
@@ -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>
|
package/package.json
CHANGED
|
@@ -446,6 +446,7 @@ export function processDesignersData(typefaceData) {
|
|
|
446
446
|
let designersData = [];
|
|
447
447
|
|
|
448
448
|
typefaceData?.forEach(typeface => {
|
|
449
|
+
if (!typeface.author?._id) return;
|
|
449
450
|
let designer = designersData.find(author => author._id === typeface.author._id);
|
|
450
451
|
if (designer) {
|
|
451
452
|
designer.total += typeface.total;
|