@vendure/dashboard 3.4.3-master-202509250229 → 3.5.0-minor-202509261210

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.
Files changed (75) hide show
  1. package/dist/plugin/api/api-extensions.js +11 -14
  2. package/dist/plugin/api/metrics.resolver.d.ts +2 -2
  3. package/dist/plugin/api/metrics.resolver.js +2 -2
  4. package/dist/plugin/config/metrics-strategies.d.ts +9 -9
  5. package/dist/plugin/config/metrics-strategies.js +6 -6
  6. package/dist/plugin/constants.d.ts +2 -0
  7. package/dist/plugin/constants.js +3 -1
  8. package/dist/plugin/dashboard.plugin.js +13 -0
  9. package/dist/plugin/service/metrics.service.d.ts +3 -3
  10. package/dist/plugin/service/metrics.service.js +37 -53
  11. package/dist/plugin/types.d.ts +9 -12
  12. package/dist/plugin/types.js +7 -11
  13. package/dist/vite/utils/compiler.js +2 -0
  14. package/dist/vite/vite-plugin-vendure-dashboard.js +2 -2
  15. package/package.json +4 -4
  16. package/src/app/routes/_authenticated/_collections/collections.tsx +7 -2
  17. package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +15 -2
  18. package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +14 -2
  19. package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +10 -0
  20. package/src/app/routes/_authenticated/_products/components/product-option-group-badge.tsx +19 -0
  21. package/src/app/routes/_authenticated/_products/components/product-options-table.tsx +111 -0
  22. package/src/app/routes/_authenticated/_products/product-option-groups.graphql.ts +103 -0
  23. package/src/app/routes/_authenticated/_products/products.graphql.ts +13 -1
  24. package/src/app/routes/_authenticated/_products/products.tsx +27 -3
  25. package/src/app/routes/_authenticated/_products/products_.$id.tsx +26 -9
  26. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$id.tsx +181 -0
  27. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +208 -0
  28. package/src/app/routes/_authenticated/_zones/components/zone-countries-sheet.tsx +4 -1
  29. package/src/app/routes/_authenticated/index.tsx +41 -24
  30. package/src/lib/components/data-display/json.tsx +16 -1
  31. package/src/lib/components/data-input/index.ts +3 -0
  32. package/src/lib/components/data-input/slug-input.tsx +296 -0
  33. package/src/lib/components/data-table/add-filter-menu.tsx +13 -6
  34. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +38 -1
  35. package/src/lib/components/data-table/data-table-context.tsx +91 -0
  36. package/src/lib/components/data-table/data-table-filter-badge.tsx +9 -5
  37. package/src/lib/components/data-table/data-table-view-options.tsx +17 -8
  38. package/src/lib/components/data-table/data-table.tsx +146 -94
  39. package/src/lib/components/data-table/global-views-bar.tsx +97 -0
  40. package/src/lib/components/data-table/global-views-sheet.tsx +11 -0
  41. package/src/lib/components/data-table/manage-global-views-button.tsx +26 -0
  42. package/src/lib/components/data-table/my-views-button.tsx +47 -0
  43. package/src/lib/components/data-table/refresh-button.tsx +12 -3
  44. package/src/lib/components/data-table/save-view-button.tsx +45 -0
  45. package/src/lib/components/data-table/save-view-dialog.tsx +113 -0
  46. package/src/lib/components/data-table/use-generated-columns.tsx +3 -1
  47. package/src/lib/components/data-table/user-views-sheet.tsx +11 -0
  48. package/src/lib/components/data-table/views-sheet.tsx +297 -0
  49. package/src/lib/components/date-range-picker.tsx +184 -0
  50. package/src/lib/components/shared/paginated-list-data-table.tsx +59 -32
  51. package/src/lib/components/ui/button.tsx +1 -1
  52. package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +29 -2
  53. package/src/lib/framework/dashboard-widget/metrics-widget/index.tsx +10 -7
  54. package/src/lib/framework/dashboard-widget/metrics-widget/metrics-widget.graphql.ts +9 -3
  55. package/src/lib/framework/dashboard-widget/orders-summary/index.tsx +19 -75
  56. package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +33 -0
  57. package/src/lib/framework/document-introspection/add-custom-fields.spec.ts +319 -9
  58. package/src/lib/framework/document-introspection/add-custom-fields.ts +60 -31
  59. package/src/lib/framework/document-introspection/get-document-structure.spec.ts +1 -159
  60. package/src/lib/framework/document-introspection/include-only-selected-list-fields.spec.ts +1840 -0
  61. package/src/lib/framework/document-introspection/include-only-selected-list-fields.ts +940 -0
  62. package/src/lib/framework/document-introspection/testing-utils.ts +161 -0
  63. package/src/lib/framework/extension-api/display-component-extensions.tsx +2 -0
  64. package/src/lib/framework/extension-api/types/data-table.ts +62 -4
  65. package/src/lib/framework/extension-api/types/navigation.ts +16 -0
  66. package/src/lib/framework/form-engine/utils.ts +34 -0
  67. package/src/lib/framework/page/list-page.tsx +289 -4
  68. package/src/lib/framework/page/use-extended-router.tsx +59 -17
  69. package/src/lib/graphql/api.ts +4 -2
  70. package/src/lib/graphql/graphql-env.d.ts +13 -10
  71. package/src/lib/hooks/use-extended-list-query.ts +5 -0
  72. package/src/lib/hooks/use-saved-views.ts +230 -0
  73. package/src/lib/index.ts +15 -0
  74. package/src/lib/types/saved-views.ts +39 -0
  75. package/src/lib/utils/saved-views-utils.ts +40 -0
@@ -6,33 +6,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.adminApiExtensions = void 0;
7
7
  const graphql_tag_1 = __importDefault(require("graphql-tag"));
8
8
  exports.adminApiExtensions = (0, graphql_tag_1.default) `
9
- type MetricSummary {
10
- interval: MetricInterval!
11
- type: MetricType!
9
+ type DashboardMetricSummary {
10
+ type: DashboardMetricType!
12
11
  title: String!
13
- entries: [MetricSummaryEntry!]!
12
+ entries: [DashboardMetricSummaryEntry!]!
14
13
  }
15
- enum MetricInterval {
16
- Daily
17
- }
18
- enum MetricType {
14
+ enum DashboardMetricType {
19
15
  OrderCount
20
16
  OrderTotal
21
17
  AverageOrderValue
22
18
  }
23
- type MetricSummaryEntry {
19
+ type DashboardMetricSummaryEntry {
24
20
  label: String!
25
21
  value: Float!
26
22
  }
27
- input MetricSummaryInput {
28
- interval: MetricInterval!
29
- types: [MetricType!]!
23
+ input DashboardMetricSummaryInput {
24
+ types: [DashboardMetricType!]!
30
25
  refresh: Boolean
26
+ startDate: DateTime!
27
+ endDate: DateTime!
31
28
  }
32
29
  extend type Query {
33
30
  """
34
- Get metrics for the given interval and metric types.
31
+ Get metrics for the given date range and metric types.
35
32
  """
36
- metricSummary(input: MetricSummaryInput): [MetricSummary!]!
33
+ dashboardMetricSummary(input: DashboardMetricSummaryInput): [DashboardMetricSummary!]!
37
34
  }
38
35
  `;
@@ -1,8 +1,8 @@
1
1
  import { RequestContext } from '@vendure/core';
2
2
  import { MetricsService } from '../service/metrics.service.js';
3
- import { MetricSummary, MetricSummaryInput } from '../types.js';
3
+ import { DashboardMetricSummary, DashboardMetricSummaryInput } from '../types.js';
4
4
  export declare class MetricsResolver {
5
5
  private service;
6
6
  constructor(service: MetricsService);
7
- metricSummary(ctx: RequestContext, input: MetricSummaryInput): Promise<MetricSummary[]>;
7
+ dashboardMetricSummary(ctx: RequestContext, input: DashboardMetricSummaryInput): Promise<DashboardMetricSummary[]>;
8
8
  }
@@ -20,7 +20,7 @@ let MetricsResolver = class MetricsResolver {
20
20
  constructor(service) {
21
21
  this.service = service;
22
22
  }
23
- async metricSummary(ctx, input) {
23
+ async dashboardMetricSummary(ctx, input) {
24
24
  return this.service.getMetrics(ctx, input);
25
25
  }
26
26
  };
@@ -33,7 +33,7 @@ __decorate([
33
33
  __metadata("design:type", Function),
34
34
  __metadata("design:paramtypes", [core_1.RequestContext, Object]),
35
35
  __metadata("design:returntype", Promise)
36
- ], MetricsResolver.prototype, "metricSummary", null);
36
+ ], MetricsResolver.prototype, "dashboardMetricSummary", null);
37
37
  exports.MetricsResolver = MetricsResolver = __decorate([
38
38
  (0, graphql_1.Resolver)(),
39
39
  __metadata("design:paramtypes", [metrics_service_js_1.MetricsService])
@@ -1,6 +1,6 @@
1
1
  import { RequestContext } from '@vendure/core';
2
2
  import { MetricData } from '../service/metrics.service.js';
3
- import { MetricInterval, MetricSummaryEntry, MetricType } from '../types.js';
3
+ import { DashboardMetricSummaryEntry, DashboardMetricType } from '../types.js';
4
4
  /**
5
5
  * Calculate your metric data based on the given input.
6
6
  * Be careful with heavy queries and calculations,
@@ -8,32 +8,32 @@ import { MetricInterval, MetricSummaryEntry, MetricType } from '../types.js';
8
8
  *
9
9
  */
10
10
  export interface MetricCalculation {
11
- type: MetricType;
11
+ type: DashboardMetricType;
12
12
  getTitle(ctx: RequestContext): string;
13
- calculateEntry(ctx: RequestContext, interval: MetricInterval, data: MetricData): MetricSummaryEntry;
13
+ calculateEntry(ctx: RequestContext, data: MetricData): DashboardMetricSummaryEntry;
14
14
  }
15
15
  export declare function getMonthName(monthNr: number): string;
16
16
  /**
17
17
  * Calculates the average order value per month/week
18
18
  */
19
19
  export declare class AverageOrderValueMetric implements MetricCalculation {
20
- readonly type = MetricType.AverageOrderValue;
20
+ readonly type = DashboardMetricType.AverageOrderValue;
21
21
  getTitle(ctx: RequestContext): string;
22
- calculateEntry(ctx: RequestContext, interval: MetricInterval, data: MetricData): MetricSummaryEntry;
22
+ calculateEntry(ctx: RequestContext, data: MetricData): DashboardMetricSummaryEntry;
23
23
  }
24
24
  /**
25
25
  * Calculates number of orders
26
26
  */
27
27
  export declare class OrderCountMetric implements MetricCalculation {
28
- readonly type = MetricType.OrderCount;
28
+ readonly type = DashboardMetricType.OrderCount;
29
29
  getTitle(ctx: RequestContext): string;
30
- calculateEntry(ctx: RequestContext, interval: MetricInterval, data: MetricData): MetricSummaryEntry;
30
+ calculateEntry(ctx: RequestContext, data: MetricData): DashboardMetricSummaryEntry;
31
31
  }
32
32
  /**
33
33
  * Calculates order total
34
34
  */
35
35
  export declare class OrderTotalMetric implements MetricCalculation {
36
- readonly type = MetricType.OrderTotal;
36
+ readonly type = DashboardMetricType.OrderTotal;
37
37
  getTitle(ctx: RequestContext): string;
38
- calculateEntry(ctx: RequestContext, interval: MetricInterval, data: MetricData): MetricSummaryEntry;
38
+ calculateEntry(ctx: RequestContext, data: MetricData): DashboardMetricSummaryEntry;
39
39
  }
@@ -12,12 +12,12 @@ function getMonthName(monthNr) {
12
12
  */
13
13
  class AverageOrderValueMetric {
14
14
  constructor() {
15
- this.type = types_js_1.MetricType.AverageOrderValue;
15
+ this.type = types_js_1.DashboardMetricType.AverageOrderValue;
16
16
  }
17
17
  getTitle(ctx) {
18
18
  return 'average-order-value';
19
19
  }
20
- calculateEntry(ctx, interval, data) {
20
+ calculateEntry(ctx, data) {
21
21
  const label = data.date.toISOString();
22
22
  if (!data.orders.length) {
23
23
  return {
@@ -39,12 +39,12 @@ exports.AverageOrderValueMetric = AverageOrderValueMetric;
39
39
  */
40
40
  class OrderCountMetric {
41
41
  constructor() {
42
- this.type = types_js_1.MetricType.OrderCount;
42
+ this.type = types_js_1.DashboardMetricType.OrderCount;
43
43
  }
44
44
  getTitle(ctx) {
45
45
  return 'order-count';
46
46
  }
47
- calculateEntry(ctx, interval, data) {
47
+ calculateEntry(ctx, data) {
48
48
  const label = data.date.toISOString();
49
49
  return {
50
50
  label,
@@ -58,12 +58,12 @@ exports.OrderCountMetric = OrderCountMetric;
58
58
  */
59
59
  class OrderTotalMetric {
60
60
  constructor() {
61
- this.type = types_js_1.MetricType.OrderTotal;
61
+ this.type = types_js_1.DashboardMetricType.OrderTotal;
62
62
  }
63
63
  getTitle(ctx) {
64
64
  return 'order-totals';
65
65
  }
66
- calculateEntry(ctx, interval, data) {
66
+ calculateEntry(ctx, data) {
67
67
  const label = data.date.toISOString();
68
68
  return {
69
69
  label,
@@ -1,6 +1,8 @@
1
+ import { RwPermissionDefinition } from '@vendure/core';
1
2
  export declare const DEFAULT_APP_PATH: string;
2
3
  export declare const loggerCtx = "DashboardPlugin";
3
4
  export declare const defaultLanguage = "en";
4
5
  export declare const defaultLocale: undefined;
5
6
  export declare const defaultAvailableLanguages: string[];
6
7
  export declare const defaultAvailableLocales: string[];
8
+ export declare const manageDashboardGlobalViews: RwPermissionDefinition;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.defaultAvailableLocales = exports.defaultAvailableLanguages = exports.defaultLocale = exports.defaultLanguage = exports.loggerCtx = exports.DEFAULT_APP_PATH = void 0;
3
+ exports.manageDashboardGlobalViews = exports.defaultAvailableLocales = exports.defaultAvailableLanguages = exports.defaultLocale = exports.defaultLanguage = exports.loggerCtx = exports.DEFAULT_APP_PATH = void 0;
4
+ const core_1 = require("@vendure/core");
4
5
  const path_1 = require("path");
5
6
  exports.DEFAULT_APP_PATH = (0, path_1.join)(__dirname, 'dist');
6
7
  exports.loggerCtx = 'DashboardPlugin';
@@ -8,3 +9,4 @@ exports.defaultLanguage = 'en';
8
9
  exports.defaultLocale = undefined;
9
10
  exports.defaultAvailableLanguages = ['en', 'de', 'es', 'cs', 'zh_Hans', 'pt_BR', 'pt_PT', 'zh_Hant'];
10
11
  exports.defaultAvailableLocales = ['en-US', 'de-DE', 'es-ES', 'zh-CN', 'zh-TW', 'pt-BR', 'pt-PT'];
12
+ exports.manageDashboardGlobalViews = new core_1.RwPermissionDefinition('DashboardGlobalViews');
@@ -154,11 +154,24 @@ exports.DashboardPlugin = DashboardPlugin = DashboardPlugin_1 = __decorate([
154
154
  },
155
155
  providers: [metrics_service_js_1.MetricsService],
156
156
  configuration: config => {
157
+ config.authOptions.customPermissions.push(constants_js_1.manageDashboardGlobalViews);
157
158
  config.settingsStoreFields['vendure.dashboard'] = [
158
159
  {
159
160
  name: 'userSettings',
160
161
  scope: core_1.SettingsStoreScopes.user,
161
162
  },
163
+ {
164
+ name: 'globalSavedViews',
165
+ scope: core_1.SettingsStoreScopes.global,
166
+ requiresPermission: {
167
+ read: constants_js_1.manageDashboardGlobalViews.Read,
168
+ write: constants_js_1.manageDashboardGlobalViews.Write,
169
+ },
170
+ },
171
+ {
172
+ name: 'userSavedViews',
173
+ scope: core_1.SettingsStoreScopes.user,
174
+ },
162
175
  ];
163
176
  return config;
164
177
  },
@@ -1,6 +1,6 @@
1
1
  import { CacheService, Order, RequestContext, TransactionalConnection } from '@vendure/core';
2
2
  import { MetricCalculation } from '../config/metrics-strategies.js';
3
- import { MetricInterval, MetricSummary, MetricSummaryInput } from '../types.js';
3
+ import { DashboardMetricSummary, DashboardMetricSummaryInput } from '../types.js';
4
4
  export type MetricData = {
5
5
  date: Date;
6
6
  orders: Order[];
@@ -10,6 +10,6 @@ export declare class MetricsService {
10
10
  private cacheService;
11
11
  metricCalculations: MetricCalculation[];
12
12
  constructor(connection: TransactionalConnection, cacheService: CacheService);
13
- getMetrics(ctx: RequestContext, { interval, types, refresh }: MetricSummaryInput): Promise<MetricSummary[]>;
14
- loadData(ctx: RequestContext, interval: MetricInterval, endDate: Date): Promise<Map<number, MetricData>>;
13
+ getMetrics(ctx: RequestContext, { types, refresh, startDate, endDate }: DashboardMetricSummaryInput): Promise<DashboardMetricSummary[]>;
14
+ loadData(ctx: RequestContext, startDate: Date, endDate: Date): Promise<Map<string, MetricData>>;
15
15
  }
@@ -11,13 +11,11 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.MetricsService = void 0;
13
13
  const common_1 = require("@nestjs/common");
14
- const shared_utils_1 = require("@vendure/common/lib/shared-utils");
15
14
  const core_1 = require("@vendure/core");
16
15
  const crypto_1 = require("crypto");
17
16
  const date_fns_1 = require("date-fns");
18
17
  const metrics_strategies_js_1 = require("../config/metrics-strategies.js");
19
18
  const constants_js_1 = require("../constants.js");
20
- const types_js_1 = require("../types.js");
21
19
  let MetricsService = class MetricsService {
22
20
  constructor(connection, cacheService) {
23
21
  this.connection = connection;
@@ -28,15 +26,15 @@ let MetricsService = class MetricsService {
28
26
  new metrics_strategies_js_1.OrderTotalMetric(),
29
27
  ];
30
28
  }
31
- async getMetrics(ctx, { interval, types, refresh }) {
32
- // Set 23:59:59.999 as endDate
33
- const endDate = (0, date_fns_1.endOfDay)(new Date());
29
+ async getMetrics(ctx, { types, refresh, startDate, endDate }) {
30
+ const calculatedStartDate = (0, date_fns_1.startOfDay)(new Date(startDate));
31
+ const calculatedEndDate = (0, date_fns_1.endOfDay)(new Date(endDate));
34
32
  // Check if we have cached result
35
33
  const hash = (0, crypto_1.createHash)('sha1')
36
34
  .update(JSON.stringify({
37
- endDate,
35
+ startDate: calculatedStartDate,
36
+ endDate: calculatedEndDate,
38
37
  types: types.sort(),
39
- interval,
40
38
  channel: ctx.channel.token,
41
39
  }))
42
40
  .digest('base64');
@@ -47,22 +45,21 @@ let MetricsService = class MetricsService {
47
45
  return cachedMetricList;
48
46
  }
49
47
  // No cache, calculating new metrics
50
- core_1.Logger.verbose(`No cache hit, calculating ${interval} metrics until ${endDate.toISOString()} for channel ${ctx.channel.token} for all orders`, constants_js_1.loggerCtx);
51
- const data = await this.loadData(ctx, interval, endDate);
48
+ core_1.Logger.verbose(`No cache hit, calculating metrics from ${calculatedStartDate.toISOString()} to ${calculatedEndDate.toISOString()} for channel ${ctx.channel.token} for all orders`, constants_js_1.loggerCtx);
49
+ const data = await this.loadData(ctx, calculatedStartDate, calculatedEndDate);
52
50
  const metrics = [];
53
51
  for (const type of types) {
54
52
  const metric = this.metricCalculations.find(m => m.type === type);
55
53
  if (!metric) {
56
54
  continue;
57
55
  }
58
- // Calculate entry (month or week)
56
+ // Calculate entries for each day
59
57
  const entries = [];
60
- data.forEach(dataPerTick => {
61
- entries.push(metric.calculateEntry(ctx, interval, dataPerTick));
58
+ data.forEach(dataPerDay => {
59
+ entries.push(metric.calculateEntry(ctx, dataPerDay));
62
60
  });
63
61
  // Create metric with calculated entries
64
62
  metrics.push({
65
- interval,
66
63
  title: metric.getTitle(ctx),
67
64
  type: metric.type,
68
65
  entries,
@@ -71,26 +68,11 @@ let MetricsService = class MetricsService {
71
68
  await this.cacheService.set(cacheKey, metrics, { ttl: 1000 * 60 * 60 * 2 }); // 2 hours
72
69
  return metrics;
73
70
  }
74
- async loadData(ctx, interval, endDate) {
75
- let nrOfEntries;
76
- let backInTimeAmount;
71
+ async loadData(ctx, startDate, endDate) {
77
72
  const orderRepo = this.connection.getRepository(ctx, core_1.Order);
78
- // What function to use to get the current Tick of a date (i.e. the week or month number)
79
- let getTickNrFn;
80
- let maxTick;
81
- switch (interval) {
82
- case types_js_1.MetricInterval.Daily: {
83
- nrOfEntries = 30;
84
- backInTimeAmount = { days: nrOfEntries };
85
- getTickNrFn = date_fns_1.getDayOfYear;
86
- maxTick = 365;
87
- break;
88
- }
89
- default:
90
- (0, shared_utils_1.assertNever)(interval);
91
- }
92
- const startDate = (0, date_fns_1.startOfDay)((0, date_fns_1.sub)(endDate, backInTimeAmount));
93
- const startTick = getTickNrFn(startDate);
73
+ // Calculate number of days between start and end
74
+ const diffTime = Math.abs(endDate.getTime() - startDate.getTime());
75
+ const nrOfDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1;
94
76
  // Get orders in a loop until we have all
95
77
  let skip = 0;
96
78
  const take = 1000;
@@ -103,38 +85,40 @@ let MetricsService = class MetricsService {
103
85
  .where('orderChannel.id=:channelId', { channelId: ctx.channelId })
104
86
  .andWhere('order.orderPlacedAt >= :startDate', {
105
87
  startDate: startDate.toISOString(),
88
+ })
89
+ .andWhere('order.orderPlacedAt <= :endDate', {
90
+ endDate: endDate.toISOString(),
106
91
  })
107
92
  .skip(skip)
108
93
  .take(take);
109
94
  const [items, nrOfOrders] = await query.getManyAndCount();
110
95
  orders.push(...items);
111
- core_1.Logger.verbose(`Fetched orders ${skip}-${skip + take} for channel ${ctx.channel.token} for ${interval} metrics`, constants_js_1.loggerCtx);
96
+ core_1.Logger.verbose(`Fetched orders ${skip}-${skip + take} for channel ${ctx.channel.token} for date range metrics`, constants_js_1.loggerCtx);
112
97
  skip += items.length;
113
98
  if (orders.length >= nrOfOrders) {
114
99
  hasMoreOrders = false;
115
100
  }
116
101
  }
117
- core_1.Logger.verbose(`Finished fetching all ${orders.length} orders for channel ${ctx.channel.token} for ${interval} metrics`, constants_js_1.loggerCtx);
118
- const dataPerInterval = new Map();
119
- const ticks = [];
120
- for (let i = 1; i <= nrOfEntries; i++) {
121
- if (startTick + i >= maxTick) {
122
- // make sure we don't go over month 12 or week 52
123
- ticks.push(startTick + i - maxTick);
124
- }
125
- else {
126
- ticks.push(startTick + i);
127
- }
128
- }
129
- ticks.forEach(tick => {
130
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
131
- const ordersInCurrentTick = orders.filter(order => getTickNrFn(order.orderPlacedAt) === tick);
132
- dataPerInterval.set(tick, {
133
- orders: ordersInCurrentTick,
134
- date: (0, date_fns_1.setDayOfYear)(endDate, tick),
102
+ core_1.Logger.verbose(`Finished fetching all ${orders.length} orders for channel ${ctx.channel.token} for date range metrics`, constants_js_1.loggerCtx);
103
+ const dataPerDay = new Map();
104
+ // Create a map entry for each day in the range
105
+ for (let i = 0; i < nrOfDays; i++) {
106
+ const currentDate = new Date(startDate);
107
+ currentDate.setDate(startDate.getDate() + i);
108
+ const dateKey = currentDate.toISOString().split('T')[0]; // YYYY-MM-DD format
109
+ // Filter orders for this specific day
110
+ const ordersForDay = orders.filter(order => {
111
+ if (!order.orderPlacedAt)
112
+ return false;
113
+ const orderDate = new Date(order.orderPlacedAt).toISOString().split('T')[0];
114
+ return orderDate === dateKey;
135
115
  });
136
- });
137
- return dataPerInterval;
116
+ dataPerDay.set(dateKey, {
117
+ orders: ordersForDay,
118
+ date: currentDate,
119
+ });
120
+ }
121
+ return dataPerDay;
138
122
  }
139
123
  };
140
124
  exports.MetricsService = MetricsService;
@@ -1,23 +1,20 @@
1
- export type MetricSummary = {
2
- interval: MetricInterval;
3
- type: MetricType;
1
+ export type DashboardMetricSummary = {
2
+ type: DashboardMetricType;
4
3
  title: string;
5
- entries: MetricSummaryEntry[];
4
+ entries: DashboardMetricSummaryEntry[];
6
5
  };
7
- export declare enum MetricType {
6
+ export declare enum DashboardMetricType {
8
7
  OrderCount = "OrderCount",
9
8
  OrderTotal = "OrderTotal",
10
9
  AverageOrderValue = "AverageOrderValue"
11
10
  }
12
- export declare enum MetricInterval {
13
- Daily = "Daily"
14
- }
15
- export type MetricSummaryEntry = {
11
+ export type DashboardMetricSummaryEntry = {
16
12
  label: string;
17
13
  value: number;
18
14
  };
19
- export interface MetricSummaryInput {
20
- interval: MetricInterval;
21
- types: MetricType[];
15
+ export interface DashboardMetricSummaryInput {
16
+ types: DashboardMetricType[];
22
17
  refresh?: boolean;
18
+ startDate: string;
19
+ endDate: string;
23
20
  }
@@ -1,13 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MetricInterval = exports.MetricType = void 0;
4
- var MetricType;
5
- (function (MetricType) {
6
- MetricType["OrderCount"] = "OrderCount";
7
- MetricType["OrderTotal"] = "OrderTotal";
8
- MetricType["AverageOrderValue"] = "AverageOrderValue";
9
- })(MetricType || (exports.MetricType = MetricType = {}));
10
- var MetricInterval;
11
- (function (MetricInterval) {
12
- MetricInterval["Daily"] = "Daily";
13
- })(MetricInterval || (exports.MetricInterval = MetricInterval = {}));
3
+ exports.DashboardMetricType = void 0;
4
+ var DashboardMetricType;
5
+ (function (DashboardMetricType) {
6
+ DashboardMetricType["OrderCount"] = "OrderCount";
7
+ DashboardMetricType["OrderTotal"] = "OrderTotal";
8
+ DashboardMetricType["AverageOrderValue"] = "AverageOrderValue";
9
+ })(DashboardMetricType || (exports.DashboardMetricType = DashboardMetricType = {}));
@@ -20,6 +20,8 @@ export async function compile(options) {
20
20
  const { vendureConfigPath, outputPath, pathAdapter, logger = noopLogger, pluginPackageScanner } = options;
21
21
  const getCompiledConfigPath = (_a = pathAdapter === null || pathAdapter === void 0 ? void 0 : pathAdapter.getCompiledConfigPath) !== null && _a !== void 0 ? _a : defaultPathAdapter.getCompiledConfigPath;
22
22
  const transformTsConfigPathMappings = (_b = pathAdapter === null || pathAdapter === void 0 ? void 0 : pathAdapter.transformTsConfigPathMappings) !== null && _b !== void 0 ? _b : defaultPathAdapter.transformTsConfigPathMappings;
23
+ // 0. Clear the outputPath
24
+ fs.removeSync(outputPath);
23
25
  // 1. Compile TypeScript files
24
26
  const compileStart = Date.now();
25
27
  await compileTypeScript({
@@ -1,5 +1,5 @@
1
1
  import tailwindcss from '@tailwindcss/vite';
2
- import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
2
+ import { tanstackRouter } from '@tanstack/router-plugin/vite';
3
3
  import react from '@vitejs/plugin-react';
4
4
  import path from 'path';
5
5
  import { adminApiSchemaPlugin } from './vite-plugin-admin-api-schema.js';
@@ -30,7 +30,7 @@ export function vendureDashboardPlugin(options) {
30
30
  ...(options.disableTansStackRouterPlugin
31
31
  ? []
32
32
  : [
33
- TanStackRouterVite({
33
+ tanstackRouter({
34
34
  autoCodeSplitting: true,
35
35
  routeFileIgnorePattern: '.graphql.ts|components|hooks|utils',
36
36
  routesDirectory: path.join(packageRoot, 'src/app/routes'),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vendure/dashboard",
3
3
  "private": false,
4
- "version": "3.4.3-master-202509250229",
4
+ "version": "3.5.0-minor-202509261210",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -104,8 +104,8 @@
104
104
  "@types/react": "^19.0.10",
105
105
  "@types/react-dom": "^19.0.4",
106
106
  "@uidotdev/usehooks": "^2.4.1",
107
- "@vendure/common": "^3.4.3-master-202509250229",
108
- "@vendure/core": "^3.4.3-master-202509250229",
107
+ "@vendure/common": "^3.5.0-minor-202509261210",
108
+ "@vendure/core": "^3.5.0-minor-202509261210",
109
109
  "@vitejs/plugin-react": "^4.3.4",
110
110
  "acorn": "^8.11.3",
111
111
  "acorn-walk": "^8.3.2",
@@ -156,5 +156,5 @@
156
156
  "lightningcss-linux-arm64-musl": "^1.29.3",
157
157
  "lightningcss-linux-x64-musl": "^1.29.1"
158
158
  },
159
- "gitHead": "7555f68cec1c4a90ca20bbc2b79798b027dc7c4f"
159
+ "gitHead": "aa1c6971ebbd4954da1ce77fba1d6022051953db"
160
160
  }
@@ -96,13 +96,18 @@ function CollectionListPage() {
96
96
  }}
97
97
  customizeColumns={{
98
98
  name: {
99
+ meta: {
100
+ // This column needs the following fields to always be available
101
+ // in order to correctly render.
102
+ dependencies: ['children', 'breadcrumbs'],
103
+ },
99
104
  header: 'Collection Name',
100
105
  cell: ({ row }) => {
101
106
  const isExpanded = row.getIsExpanded();
102
107
  const hasChildren = !!row.original.children?.length;
103
108
  return (
104
109
  <div
105
- style={{ marginLeft: (row.original.breadcrumbs.length - 2) * 20 + 'px' }}
110
+ style={{ marginLeft: (row.original.breadcrumbs?.length - 2) * 20 + 'px' }}
106
111
  className="flex gap-2 items-center"
107
112
  >
108
113
  <Button
@@ -143,7 +148,7 @@ function CollectionListPage() {
143
148
  collectionId={row.original.id}
144
149
  collectionName={row.original.name}
145
150
  >
146
- <Trans>{row.original.productVariants.totalItems} variants</Trans>
151
+ <Trans>{row.original.productVariants?.totalItems} variants</Trans>
147
152
  </CollectionContentsSheet>
148
153
  );
149
154
  },
@@ -1,3 +1,4 @@
1
+ import { SlugInput } from '@/vdb/components/data-input/index.js';
1
2
  import { RichTextInput } from '@/vdb/components/data-input/rich-text-input.js';
2
3
  import { EntityAssets } from '@/vdb/components/shared/entity-assets.js';
3
4
  import { ErrorPage } from '@/vdb/components/shared/error-page.js';
@@ -89,7 +90,11 @@ function CollectionDetailPage() {
89
90
  },
90
91
  params: { id: params.id },
91
92
  onSuccess: async data => {
92
- toast(i18n.t(creatingNewEntity ? 'Successfully created collection' : 'Successfully updated collection'));
93
+ toast(
94
+ i18n.t(
95
+ creatingNewEntity ? 'Successfully created collection' : 'Successfully updated collection',
96
+ ),
97
+ );
93
98
  resetForm();
94
99
  if (creatingNewEntity) {
95
100
  await navigate({ to: `../$id`, params: { id: data.id } });
@@ -147,7 +152,15 @@ function CollectionDetailPage() {
147
152
  control={form.control}
148
153
  name="slug"
149
154
  label={<Trans>Slug</Trans>}
150
- render={({ field }) => <Input {...field} />}
155
+ render={({ field }) => (
156
+ <SlugInput
157
+ fieldName="slug"
158
+ watchFieldName="name"
159
+ entityName="Collection"
160
+ entityId={entity?.id}
161
+ {...field}
162
+ />
163
+ )}
151
164
  />
152
165
  </DetailFormGrid>
153
166
  <TranslatableFormFieldWrapper
@@ -1,3 +1,4 @@
1
+ import { SlugInput } from '@/vdb/components/data-input/index.js';
1
2
  import { ErrorPage } from '@/vdb/components/shared/error-page.js';
2
3
  import { FormFieldWrapper } from '@/vdb/components/shared/form-field-wrapper.js';
3
4
  import { PermissionGuard } from '@/vdb/components/shared/permission-guard.js';
@@ -32,7 +33,10 @@ export const Route = createFileRoute('/_authenticated/_facets/facets_/$id')({
32
33
  pageId,
33
34
  queryDocument: facetDetailDocument,
34
35
  breadcrumb(isNew, entity) {
35
- return [{ path: '/facets', label: <Trans>Facets</Trans> }, isNew ? <Trans>New facet</Trans> : entity?.name];
36
+ return [
37
+ { path: '/facets', label: <Trans>Facets</Trans> },
38
+ isNew ? <Trans>New facet</Trans> : entity?.name,
39
+ ];
36
40
  },
37
41
  }),
38
42
  errorComponent: ({ error }) => <ErrorPage message={error.message} />,
@@ -124,7 +128,15 @@ function FacetDetailPage() {
124
128
  control={form.control}
125
129
  name="code"
126
130
  label={<Trans>Code</Trans>}
127
- render={({ field }) => <Input {...field} />}
131
+ render={({ field }) => (
132
+ <SlugInput
133
+ fieldName="code"
134
+ watchFieldName="name"
135
+ entityName="Facet"
136
+ entityId={entity?.id}
137
+ {...field}
138
+ />
139
+ )}
128
140
  />
129
141
  </DetailFormGrid>
130
142
  </PageBlock>
@@ -60,6 +60,16 @@ export const productVariantDetailDocument = graphql(
60
60
  name
61
61
  }
62
62
  }
63
+ options {
64
+ id
65
+ name
66
+ code
67
+ group {
68
+ id
69
+ name
70
+ code
71
+ }
72
+ }
63
73
  translations {
64
74
  id
65
75
  languageCode
@@ -0,0 +1,19 @@
1
+ import { Badge } from '@/vdb/components/ui/badge.js';
2
+ import { Link } from '@tanstack/react-router';
3
+ import { Edit2 } from 'lucide-react';
4
+
5
+ interface ProductOptionGroupBadgeProps {
6
+ id: string;
7
+ name: string;
8
+ }
9
+
10
+ export function ProductOptionGroupBadge({ id, name }: ProductOptionGroupBadgeProps) {
11
+ return (
12
+ <Badge variant="secondary" className="text-xs">
13
+ <span>{name}</span>
14
+ <Link to={`option-groups/${id}`} className="ml-1.5 inline-flex">
15
+ <Edit2 className="h-3 w-3" />
16
+ </Link>
17
+ </Badge>
18
+ );
19
+ }