@liiift-studio/sales-portal 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,7 +72,7 @@ export default function SalesPortal() {
72
72
  <SalesPortalPage
73
73
  texts={{
74
74
  title: 'Sales Portal',
75
- dashboardTitle: 'Monthly Sales',
75
+ dashboardTitle: ' Sales',
76
76
  dashboardSubtitle: 'by designer'
77
77
  }}
78
78
  />
package/SETUP.md CHANGED
@@ -39,7 +39,7 @@ export default function SalesPortal() {
39
39
  title: 'Sales Portal',
40
40
  description: 'Track your typeface sales performance and revenue.',
41
41
  subtitle: 'All sales are reviewed internally before payouts.',
42
- dashboardTitle: 'Monthly Sales',
42
+ dashboardTitle: 'Sales',
43
43
  dashboardSubtitle: 'by designer'
44
44
  }}
45
45
  />
@@ -17,8 +17,8 @@ import { isTestSale } from '../clients';
17
17
  function findMatches({ invoice, line = {}, sanitySales = [] }) {
18
18
  // Find matching order
19
19
  const associatedOrder =
20
- (invoice?.id && sanitySales.find(order => order.orderStatus.invoiceId === invoice.id)) ||
21
- (invoice?.payment_intent && sanitySales.find(order => order.orderStatus.paymentIntentId === invoice.payment_intent)) ||
20
+ (invoice?.id && sanitySales.find(order => order?.orderStatus?.invoiceId === invoice.id)) ||
21
+ (invoice?.payment_intent && sanitySales.find(order => order?.orderStatus?.paymentIntentId === invoice.payment_intent)) ||
22
22
  (invoice?.customer?.email && sanitySales.find(order => {
23
23
  const orderDate = new Date(order._createdAt);
24
24
  const invoiceDate = new Date(invoice.created * 1000);
@@ -514,7 +514,7 @@ export function DateRangeSalesTable({ designer, admin, loading, updateLoadingSta
514
514
  ? '2px solid var(--green, green)'
515
515
  : '2px solid var(--red, red)',
516
516
  color: reconciliationData.isReconciled
517
- ? 'var(--green, green)'
517
+ ? 'var(--black, #1a1a1a)'
518
518
  : 'var(--red, red)',
519
519
  backgroundColor: 'rgba(255, 255, 255, 0.9)',
520
520
  filter: 'invert(0) !important',
@@ -203,6 +203,11 @@ export default function Sales(props) {
203
203
  },
204
204
  'Sales data'
205
205
  );
206
+
207
+ if (!response.ok) {
208
+ throw new Error(`HTTP ${response.status}`);
209
+ }
210
+
206
211
  const data = await response.json();
207
212
 
208
213
  if (data.success) {
@@ -257,6 +262,10 @@ export default function Sales(props) {
257
262
  'Previous sales data'
258
263
  );
259
264
 
265
+ if (!response.ok) {
266
+ throw new Error(`HTTP ${response.status}`);
267
+ }
268
+
260
269
  const data = await response.json();
261
270
 
262
271
  if (data.success) {
@@ -314,8 +323,27 @@ export default function Sales(props) {
314
323
  const processedLicenseTypeData = processLicenseTypeData(sales);
315
324
  setLicenseTypeData(processedLicenseTypeData);
316
325
 
317
- // Set total sales (sum of all sales minus tax and shipping)
318
- const totalRevenue = (processedData.salesMax - processedData.taxData.at(-1) - processedData.shippingData.at(-1)) || 0;
326
+ // Set total sales from unique invoice amounts (accounts for discounts correctly)
327
+ const seenInvoices = new Set();
328
+ let totalRevenue = 0;
329
+ sales.forEach(s => {
330
+ if (s.shippingProvision) return;
331
+ const sid = s.id;
332
+ if (sid && !seenInvoices.has(sid)) {
333
+ seenInvoices.add(sid);
334
+ totalRevenue += (s.invoiceTotal || s.total || 0) / 100;
335
+ }
336
+ });
337
+ // Subtract refunds
338
+ const seenRefunds = new Set();
339
+ sales.forEach(s => {
340
+ s.refunds?.forEach(r => {
341
+ if (r.id && !seenRefunds.has(r.id)) {
342
+ seenRefunds.add(r.id);
343
+ totalRevenue -= (r.total || 0) / 100;
344
+ }
345
+ });
346
+ });
319
347
  setTotal(totalRevenue);
320
348
 
321
349
  // Calculate revenue change percentage compared to previous period
@@ -567,10 +595,14 @@ export default function Sales(props) {
567
595
  data-disabled={loading}
568
596
  >
569
597
  <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
570
- <IconButton
598
+ <IconButton
571
599
  onClick={() => {
572
600
  const newDate = new Date(date);
573
- newDate.setUTCMonth(date.getUTCMonth() - 1);
601
+ if (viewMode === 'year') {
602
+ newDate.setUTCFullYear(date.getUTCFullYear() - 1);
603
+ } else {
604
+ newDate.setUTCMonth(date.getUTCMonth() - 1);
605
+ }
574
606
  setDate(newDate);
575
607
  }}
576
608
  size="small"
@@ -579,19 +611,23 @@ export default function Sales(props) {
579
611
  <ChevronLeftIcon />
580
612
  </IconButton>
581
613
  <DatePicker
582
- label='Month/Year (UTC)'
583
- views={['month', 'year']}
584
- format="MMM YYYY"
614
+ label={viewMode === 'year' ? 'Year (UTC)' : 'Month/Year (UTC)'}
615
+ views={viewMode === 'year' ? ['year'] : ['month', 'year']}
616
+ format={viewMode === 'year' ? 'YYYY' : 'MMM YYYY'}
585
617
  formatDensity="dense"
586
618
  slotProps={{ textField: { variant: "filled" } }}
587
619
  sx={{ "& *": { borderRadius: "4px" } }}
588
620
  value={dayjs.utc(date)}
589
621
  onChange={(newValue) => setDate(newValue.toDate())}
590
622
  />
591
- <IconButton
623
+ <IconButton
592
624
  onClick={() => {
593
625
  const newDate = new Date(date);
594
- newDate.setUTCMonth(date.getUTCMonth() + 1);
626
+ if (viewMode === 'year') {
627
+ newDate.setUTCFullYear(date.getUTCFullYear() + 1);
628
+ } else {
629
+ newDate.setUTCMonth(date.getUTCMonth() + 1);
630
+ }
595
631
  setDate(newDate);
596
632
  }}
597
633
  size="small"
@@ -783,25 +819,9 @@ export default function Sales(props) {
783
819
  <Box sx={{ display: 'flex', flexWrap: 'wrap', width: '100%' }}>
784
820
  {HeaderSection}
785
821
 
786
- {/* Year Overview */}
787
- {viewMode === 'year' && date && (
788
- <Box sx={{ width: '100%', px: { xs: 2, md: 0 } }}>
789
- <YearOverview
790
- designer={designer}
791
- year={date.getUTCFullYear()}
792
- onMonthClick={(monthIndex) => {
793
- const newDate = new Date(Date.UTC(date.getUTCFullYear(), monthIndex, 1));
794
- setDate(newDate);
795
- setViewMode('month');
796
- }}
797
- />
798
- </Box>
799
- )}
800
-
801
- {/* Monthly Sales Data Display */}
802
- {viewMode === 'month' && !!sales.length && (
822
+ {/* Sales Data Display */}
823
+ {!!sales.length && (
803
824
  <>
804
-
805
825
  <Box
806
826
  data-disabled={loading}
807
827
  className={`sales-data-section ${styles.salesSection}`}
@@ -813,18 +833,32 @@ export default function Sales(props) {
813
833
  }}
814
834
  >
815
835
 
816
- {/* Chart Section */}
817
- <Box className="sales-chart-wrapper">
818
- <SalesChart
819
- sales={sales}
820
- chartState={chartState}
821
- seriesData={seriesData}
822
- displayLosses={displayLosses}
823
- setDisplayLosses={setDisplayLosses}
824
- date={date}
825
- loading={loading}
826
- />
827
- </Box>
836
+ {/* Chart Section — Year or Month */}
837
+ {viewMode === 'year' && date ? (
838
+ <Box className="year-overview-wrapper">
839
+ <YearOverview
840
+ designer={designer}
841
+ year={date.getUTCFullYear()}
842
+ onMonthClick={(monthIndex) => {
843
+ const newDate = new Date(Date.UTC(date.getUTCFullYear(), monthIndex, 1));
844
+ setDate(newDate);
845
+ setViewMode('month');
846
+ }}
847
+ />
848
+ </Box>
849
+ ) : (
850
+ <Box className="sales-chart-wrapper">
851
+ <SalesChart
852
+ sales={sales}
853
+ chartState={chartState}
854
+ seriesData={seriesData}
855
+ displayLosses={displayLosses}
856
+ setDisplayLosses={setDisplayLosses}
857
+ date={date}
858
+ loading={loading}
859
+ />
860
+ </Box>
861
+ )}
828
862
 
829
863
  {/* Summary Dashboard */}
830
864
  <Box className="summary-cards-wrapper">
@@ -876,7 +910,7 @@ export default function Sales(props) {
876
910
  {/* Sales Table Section */}
877
911
  {designer && (
878
912
  <Box className="sales-table-wrapper">
879
- <SalesTable
913
+ <SalesTable
880
914
  sales={sales}
881
915
  designer={designer}
882
916
  admin={admin}
@@ -889,12 +923,12 @@ export default function Sales(props) {
889
923
  </Box>
890
924
 
891
925
  {/* Date Range Sales Table Section */}
892
- <DateRangeSalesTable
893
- designer={designer}
894
- admin={admin}
895
- loading={loadingStates.dateRangeSalesData}
896
- updateLoadingState={updateLoadingState}
897
- />
926
+ <DateRangeSalesTable
927
+ designer={designer}
928
+ admin={admin}
929
+ loading={loadingStates.dateRangeSalesData}
930
+ updateLoadingState={updateLoadingState}
931
+ />
898
932
  </>
899
933
  )}
900
934
 
@@ -60,7 +60,7 @@ export default function SalesPortalPage({
60
60
 
61
61
  // Default page title text
62
62
  const {
63
- dashboardTitle = 'Monthly Sales',
63
+ dashboardTitle = 'Sales',
64
64
  dashboardSubtitle = 'by designer'
65
65
  } = texts;
66
66
 
@@ -173,7 +173,7 @@ export function SalesTable({ sales = [], designer = {}, admin = false, loading =
173
173
  ? '2px solid var(--green, green)'
174
174
  : '2px solid var(--red, red)',
175
175
  color: reconciliationData.isReconciled
176
- ? 'var(--green, green)'
176
+ ? 'var(--black, #1a1a1a)'
177
177
  : 'var(--red, red)',
178
178
  }}>
179
179
  {reconciliationData.isLoading ? (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/sales-portal",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Centralized sales portal package for Liiift Studio projects",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -47,7 +47,7 @@
47
47
  align-items: center;
48
48
  white-space: nowrap;
49
49
  :global(.MuiTypography-root){
50
- font-family: 'Tomato Grotesk', sans-serif;
50
+ font-family: inherit;
51
51
  }
52
52
  }
53
53
 
@@ -27,7 +27,7 @@ let size = createTheme({
27
27
  let typography = createTheme({
28
28
 
29
29
  typography: {
30
- fontFamily: '"Ordinary", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
30
+ fontFamily: 'inherit, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
31
31
  h1: {
32
32
  maxWidth: '95%',
33
33
  fontStyle: 'normal',