@agilo/medusa-analytics-plugin 1.0.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.
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.adminOrdersListQuerySchema = void 0;
4
+ exports.GET = GET;
5
+ const utils_1 = require("@medusajs/framework/utils");
6
+ const zod_1 = require("zod");
7
+ const date_fns_1 = require("date-fns");
8
+ const orders_1 = require("../../../../utils/orders");
9
+ const luxon_1 = require("luxon");
10
+ exports.adminOrdersListQuerySchema = zod_1.z.discriminatedUnion('preset', [
11
+ zod_1.z.object({
12
+ preset: zod_1.z.literal('custom'),
13
+ date_from: zod_1.z.string(),
14
+ date_to: zod_1.z.string(),
15
+ }),
16
+ zod_1.z.object({
17
+ preset: zod_1.z.literal('this-month'),
18
+ }),
19
+ zod_1.z.object({
20
+ preset: zod_1.z.literal('last-month'),
21
+ }),
22
+ zod_1.z.object({
23
+ preset: zod_1.z.literal('last-3-months'),
24
+ }),
25
+ ]);
26
+ const DEFAULT_CURRENCY = 'EUR';
27
+ function getPercentChange(current, previous) {
28
+ if (previous === 0)
29
+ return current === 0 ? 0 : 100;
30
+ return Number((((current - previous) / previous) * 100).toFixed(2));
31
+ }
32
+ async function GET(req, res) {
33
+ const result = exports.adminOrdersListQuerySchema.safeParse(req.query);
34
+ if (!result.success) {
35
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, result.error.errors.map((err) => err.message).join(', '));
36
+ }
37
+ const validatedQuery = result.data;
38
+ const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
39
+ const storeModuleService = req.scope.resolve(utils_1.Modules.STORE);
40
+ const cacheModuleService = req.scope.resolve(utils_1.Modules.CACHE);
41
+ const fetchOrders = async (dateRange) => {
42
+ const { data: orders } = await query.graph({
43
+ entity: 'order',
44
+ fields: [
45
+ 'id',
46
+ 'total',
47
+ 'created_at',
48
+ 'status',
49
+ 'currency_code',
50
+ 'region.name',
51
+ ],
52
+ pagination: {
53
+ order: {
54
+ created_at: 'asc',
55
+ },
56
+ },
57
+ filters: {
58
+ created_at: {
59
+ $gte: dateRange.from + 'T00:00:00Z',
60
+ $lte: dateRange.to + 'T23:59:59.999Z',
61
+ },
62
+ status: { $nin: ['draft'] },
63
+ },
64
+ });
65
+ return orders;
66
+ };
67
+ const stores = await storeModuleService.listStores({}, { relations: ['supported_currencies'] });
68
+ const store = stores?.[0];
69
+ const currencyCode = store?.supported_currencies
70
+ ?.find((c) => c.is_default)
71
+ ?.currency_code?.toUpperCase() || DEFAULT_CURRENCY;
72
+ const cacheKey = `exchange_rates_${currencyCode}`;
73
+ let exchangeRates = await cacheModuleService.get(cacheKey);
74
+ if (!exchangeRates) {
75
+ const response = await fetch(`https://api.frankfurter.dev/v1/latest?base=${currencyCode}`);
76
+ exchangeRates = await response.json();
77
+ const now = luxon_1.DateTime.now().setZone('Europe/Berlin');
78
+ let expireAt = now.set({ hour: 16, minute: 0, second: 0, millisecond: 0 });
79
+ if (now >= expireAt) {
80
+ expireAt = expireAt.plus({ days: 1 });
81
+ }
82
+ const ttl = Math.floor(expireAt.diff(now, 'seconds').seconds);
83
+ await cacheModuleService.set(cacheKey, exchangeRates, ttl);
84
+ }
85
+ const calculateDateRange = orders_1.calculateDateRangeMethod[validatedQuery.preset];
86
+ if (!calculateDateRange) {
87
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, 'Invalid preset value');
88
+ }
89
+ const { current, previous, days } = calculateDateRange(validatedQuery);
90
+ const currentFrom = (0, date_fns_1.format)(current.start, 'yyyy-MM-dd');
91
+ const currentTo = (0, date_fns_1.format)(current.end, 'yyyy-MM-dd');
92
+ const previousFrom = (0, date_fns_1.format)(previous.start, 'yyyy-MM-dd');
93
+ const previousTo = (0, date_fns_1.format)(previous.end, 'yyyy-MM-dd');
94
+ const orders = await fetchOrders({
95
+ from: currentFrom,
96
+ to: currentTo,
97
+ });
98
+ const prevRangeOrders = await fetchOrders({
99
+ from: previousFrom,
100
+ to: previousTo,
101
+ });
102
+ let groupBy = 'day';
103
+ if (days > 120) {
104
+ groupBy = 'month';
105
+ }
106
+ else if (days > 30) {
107
+ groupBy = 'week';
108
+ }
109
+ const keyRange = (0, orders_1.getAllDateGroupingKeys)(groupBy, currentFrom, currentTo);
110
+ let regions = {};
111
+ let totalSales = 0;
112
+ let statuses = {};
113
+ const groupedByKey = {};
114
+ for (const order of orders) {
115
+ const exchangeRate = order.currency_code.toUpperCase() !== currencyCode
116
+ ? exchangeRates?.rates[order.currency_code.toUpperCase()]
117
+ : 1;
118
+ const orderTotal = new utils_1.BigNumber(order.total).numeric / exchangeRate;
119
+ const key = (0, orders_1.getDateGroupingKey)(new Date(order.created_at), groupBy, currentFrom, currentTo);
120
+ if (!groupedByKey[key]) {
121
+ groupedByKey[key] = { orderCount: 0, sales: 0 };
122
+ }
123
+ groupedByKey[key].orderCount += 1;
124
+ groupedByKey[key].sales += orderTotal;
125
+ totalSales += orderTotal;
126
+ if (order.region?.name) {
127
+ regions[order.region.name] =
128
+ (regions[order.region.name] ?? 0) + orderTotal;
129
+ }
130
+ if (order.status) {
131
+ statuses[order.status] = (statuses[order.status] ?? 0) + 1;
132
+ }
133
+ }
134
+ let prevTotalSales = 0;
135
+ for (const order of prevRangeOrders) {
136
+ const exchangeRate = order.currency_code.toUpperCase() !== currencyCode
137
+ ? exchangeRates?.rates[order.currency_code.toUpperCase()]
138
+ : 1;
139
+ const orderTotal = new utils_1.BigNumber(order.total).numeric / exchangeRate;
140
+ prevTotalSales += orderTotal;
141
+ }
142
+ const prevTotalOrders = prevRangeOrders.length;
143
+ const percentOrders = getPercentChange(orders.length, prevTotalOrders);
144
+ const percentSales = getPercentChange(totalSales, prevTotalSales);
145
+ const salesArray = keyRange.map((date) => ({
146
+ name: date,
147
+ sales: groupedByKey[date]?.sales ?? 0,
148
+ }));
149
+ const orderCountArray = keyRange.map((date) => ({
150
+ name: date,
151
+ count: groupedByKey[date]?.orderCount ?? 0,
152
+ }));
153
+ const regionsArray = Object.entries(regions)
154
+ .map(([region, amount]) => ({
155
+ name: region,
156
+ sales: Number(amount.toFixed(2)),
157
+ }))
158
+ .sort((a, b) => b.sales - a.sales)
159
+ .slice(0, 5);
160
+ const statusesArray = Object.entries(statuses).map(([status, count]) => ({
161
+ name: status,
162
+ count,
163
+ }));
164
+ const orderData = {
165
+ total_orders: orders.length,
166
+ prev_orders_percent: percentOrders,
167
+ regions: regionsArray,
168
+ total_sales: totalSales,
169
+ prev_sales_percent: percentSales,
170
+ statuses: statusesArray,
171
+ order_sales: salesArray,
172
+ order_count: orderCountArray,
173
+ currency_code: currencyCode,
174
+ };
175
+ res.json(orderData);
176
+ }
177
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2FnaWxvLWFuYWx5dGljcy9vcmRlcnMvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBdUNBLGtCQXVNQztBQTdPRCxxREFLbUM7QUFDbkMsNkJBQXdCO0FBQ3hCLHVDQUFrQztBQUNsQyxxREFJa0M7QUFDbEMsaUNBQWlDO0FBRXBCLFFBQUEsMEJBQTBCLEdBQUcsT0FBQyxDQUFDLGtCQUFrQixDQUFDLFFBQVEsRUFBRTtJQUN2RSxPQUFDLENBQUMsTUFBTSxDQUFDO1FBQ1AsTUFBTSxFQUFFLE9BQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBQzNCLFNBQVMsRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFO1FBQ3JCLE9BQU8sRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFO0tBQ3BCLENBQUM7SUFDRixPQUFDLENBQUMsTUFBTSxDQUFDO1FBQ1AsTUFBTSxFQUFFLE9BQUMsQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDO0tBQ2hDLENBQUM7SUFDRixPQUFDLENBQUMsTUFBTSxDQUFDO1FBQ1AsTUFBTSxFQUFFLE9BQUMsQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDO0tBQ2hDLENBQUM7SUFDRixPQUFDLENBQUMsTUFBTSxDQUFDO1FBQ1AsTUFBTSxFQUFFLE9BQUMsQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDO0tBQ25DLENBQUM7Q0FDSCxDQUFDLENBQUM7QUFDSCxNQUFNLGdCQUFnQixHQUFHLEtBQUssQ0FBQztBQUUvQixTQUFTLGdCQUFnQixDQUFDLE9BQWUsRUFBRSxRQUFnQjtJQUN6RCxJQUFJLFFBQVEsS0FBSyxDQUFDO1FBQUUsT0FBTyxPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztJQUNuRCxPQUFPLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FB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
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.adminProductAnalyticsQuerySchema = void 0;
4
+ exports.GET = GET;
5
+ const utils_1 = require("@medusajs/framework/utils");
6
+ const zod_1 = require("zod");
7
+ const DEFAULT_THRESHOLD = 5;
8
+ exports.adminProductAnalyticsQuerySchema = zod_1.z.object({
9
+ date_from: zod_1.z.string(),
10
+ date_to: zod_1.z.string(),
11
+ });
12
+ async function GET(req, res) {
13
+ const result = exports.adminProductAnalyticsQuerySchema.safeParse(req.query);
14
+ if (!result.success) {
15
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, result.error.errors.map((err) => err.message).join(', '));
16
+ }
17
+ const validatedQuery = result.data;
18
+ const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
19
+ const productService = req.scope.resolve(utils_1.Modules.PRODUCT);
20
+ const inventoryService = req.scope.resolve(utils_1.Modules.INVENTORY);
21
+ const config = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
22
+ const pluginConfig = config.plugins.find((p) => typeof p === 'string'
23
+ ? p === '@agilo/medusa-analytics-plugin'
24
+ : p.resolve === '@agilo/medusa-analytics-plugin');
25
+ const threshold = typeof pluginConfig === 'string'
26
+ ? DEFAULT_THRESHOLD
27
+ : pluginConfig?.options?.stock_threshold || DEFAULT_THRESHOLD;
28
+ const { data: orders } = await query.graph({
29
+ entity: 'order',
30
+ fields: [
31
+ 'id',
32
+ 'items.quantity',
33
+ 'items.variant.id',
34
+ 'items.variant.title',
35
+ 'items.product.title',
36
+ 'items.*',
37
+ ],
38
+ pagination: {
39
+ order: {
40
+ created_at: 'asc',
41
+ },
42
+ },
43
+ filters: {
44
+ created_at: {
45
+ $gte: validatedQuery.date_from + 'T00:00:00Z',
46
+ $lte: validatedQuery.date_to + 'T23:59:59.999Z',
47
+ },
48
+ status: { $nin: ['draft'] },
49
+ },
50
+ });
51
+ let variantQuantitySold = {};
52
+ orders.forEach((o) => {
53
+ o.items?.forEach((i) => {
54
+ if (i?.variant?.id) {
55
+ if (!variantQuantitySold[i?.variant?.id]) {
56
+ variantQuantitySold[i?.variant.id] = {
57
+ title: i.product?.title + ' ' + i.variant.title,
58
+ quantity: 0,
59
+ };
60
+ }
61
+ variantQuantitySold[i.variant.id].quantity += i.quantity;
62
+ }
63
+ });
64
+ });
65
+ const sortedVariantQuantitySold = Object.values(variantQuantitySold)
66
+ .map(({ title, quantity }) => ({ title, quantity }))
67
+ .sort((a, b) => b.quantity - a.quantity);
68
+ const inventoryLevel = await inventoryService.listInventoryLevels({
69
+ stocked_quantity: { $lte: threshold },
70
+ }, { select: ['id', 'inventory_item_id', 'stocked_quantity'] });
71
+ const inventoryItems = await inventoryService.listInventoryItems({
72
+ id: inventoryLevel.map((i) => i.inventory_item_id),
73
+ }, { select: ['id', 'sku'] });
74
+ const productVariants = await productService.listProductVariants({
75
+ sku: inventoryItems
76
+ .map((i) => i.sku)
77
+ .filter((i) => i !== undefined && i !== null),
78
+ }, { select: ['id', 'title', 'sku', 'product_id'] });
79
+ const quantityByItemId = {};
80
+ inventoryLevel.forEach((level) => {
81
+ quantityByItemId[level?.inventory_item_id] = level.stocked_quantity;
82
+ });
83
+ const quantityBySku = {};
84
+ inventoryItems.forEach((item) => {
85
+ if (item.sku) {
86
+ quantityBySku[item.sku] = quantityByItemId[item.id];
87
+ }
88
+ });
89
+ const lowStockVariants = [];
90
+ productVariants.forEach((variant) => {
91
+ if (variant.sku) {
92
+ lowStockVariants.push({
93
+ sku: variant.sku,
94
+ inventoryQuantity: quantityBySku[variant.sku],
95
+ variantName: variant.title,
96
+ productId: variant.product_id || '',
97
+ variantId: variant.id,
98
+ });
99
+ }
100
+ });
101
+ res.json({
102
+ lowStockVariants,
103
+ variantQuantitySold: sortedVariantQuantitySold.slice(0, 10),
104
+ });
105
+ }
106
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2FnaWxvLWFuYWx5dGljcy9wcm9kdWN0cy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFlQSxrQkErSEM7QUE3SUQscURBSW1DO0FBQ25DLDZCQUF3QjtBQUV4QixNQUFNLGlCQUFpQixHQUFHLENBQUMsQ0FBQztBQUVmLFFBQUEsZ0NBQWdDLEdBQUcsT0FBQyxDQUFDLE1BQU0sQ0FBQztJQUN2RCxTQUFTLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRTtJQUNyQixPQUFPLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRTtDQUNwQixDQUFDLENBQUM7QUFFSSxLQUFLLFVBQVUsR0FBRyxDQUFDLEdBQWtCLEVBQUUsR0FBbUI7SUFDL0QsTUFBTSxNQUFNLEdBQUcsd0NBQWdDLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNyRSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ3BCLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FDekQsQ0FBQztJQUNKLENBQUM7SUFDRCxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBQ25DLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ2pFLE1BQU0sY0FBYyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUMxRCxN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
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateDateRangeMethod = void 0;
4
+ exports.getWeekRangeKeyForDate = getWeekRangeKeyForDate;
5
+ exports.getAllWeekRangeKeys = getAllWeekRangeKeys;
6
+ exports.getDateGroupingKey = getDateGroupingKey;
7
+ exports.getAllDateGroupingKeys = getAllDateGroupingKeys;
8
+ const date_fns_1 = require("date-fns");
9
+ /**
10
+ * Preset functions to calculate current and previous date ranges.
11
+ * @param query Query object containing optional date_from, date_to, and preset name.
12
+ * @returns Object with current date range, previous date range, and number of days in current range.
13
+ */
14
+ exports.calculateDateRangeMethod = {
15
+ custom: (query) => {
16
+ if (!query.date_from || !query.date_to) {
17
+ throw new Error('No date range provided');
18
+ }
19
+ const start = (0, date_fns_1.parseISO)(query.date_from);
20
+ const end = (0, date_fns_1.parseISO)(query.date_to);
21
+ const days = (0, date_fns_1.differenceInCalendarDays)(end, start) + 1;
22
+ const prevEnd = new Date(start);
23
+ prevEnd.setDate(start.getDate() - 1);
24
+ const prevStart = new Date(prevEnd);
25
+ prevStart.setDate(prevEnd.getDate() - (days - 1));
26
+ return {
27
+ current: {
28
+ start: new Date(query.date_from),
29
+ end: new Date(query.date_to),
30
+ },
31
+ previous: { start: prevStart, end: prevEnd },
32
+ days,
33
+ };
34
+ },
35
+ 'this-month': () => {
36
+ const now = new Date();
37
+ const start = (0, date_fns_1.startOfMonth)(now);
38
+ const end = (0, date_fns_1.endOfMonth)(now);
39
+ const prevStart = (0, date_fns_1.startOfMonth)((0, date_fns_1.subMonths)(now, 1));
40
+ const prevEnd = (0, date_fns_1.endOfMonth)((0, date_fns_1.subMonths)(now, 1));
41
+ const days = (0, date_fns_1.differenceInCalendarDays)(end, start) + 1;
42
+ return {
43
+ current: { start, end },
44
+ previous: { start: prevStart, end: prevEnd },
45
+ days,
46
+ };
47
+ },
48
+ 'last-month': () => {
49
+ const last = (0, date_fns_1.subMonths)(new Date(), 1);
50
+ const start = (0, date_fns_1.startOfMonth)(last);
51
+ const end = (0, date_fns_1.endOfMonth)(last);
52
+ const prevStart = (0, date_fns_1.startOfMonth)((0, date_fns_1.subMonths)(last, 1));
53
+ const prevEnd = (0, date_fns_1.endOfMonth)((0, date_fns_1.subMonths)(last, 1));
54
+ const days = (0, date_fns_1.differenceInCalendarDays)(end, start) + 1;
55
+ return {
56
+ current: { start, end },
57
+ previous: { start: prevStart, end: prevEnd },
58
+ days,
59
+ };
60
+ },
61
+ 'last-3-months': () => {
62
+ const now = new Date();
63
+ const start = (0, date_fns_1.startOfMonth)((0, date_fns_1.subMonths)(now, 3));
64
+ const end = (0, date_fns_1.endOfMonth)((0, date_fns_1.subMonths)(now, 1));
65
+ const prevStart = (0, date_fns_1.startOfMonth)((0, date_fns_1.subMonths)(now, 6));
66
+ const prevEnd = (0, date_fns_1.endOfMonth)((0, date_fns_1.subMonths)(now, 4));
67
+ const days = (0, date_fns_1.differenceInCalendarDays)(end, start) + 1;
68
+ return {
69
+ current: { start, end },
70
+ previous: { start: prevStart, end: prevEnd },
71
+ days,
72
+ };
73
+ },
74
+ };
75
+ /**
76
+ * Returns a formatted key representing the week range in which a given date falls,
77
+ * based on a provided overall date range.
78
+ *
79
+ * @param date The date to check.
80
+ * @param dateFrom The start date of the overall range (in ISO string format).
81
+ * @param dateTo The end date of the overall range (in ISO string format).
82
+ * @returns A string key in the format 'dd.MM-dd.MM' or fallback 'yyyy-MM-dd' if no range is found.
83
+ */
84
+ function getWeekRangeKeyForDate(date, dateFrom, dateTo) {
85
+ const start = (0, date_fns_1.startOfDay)((0, date_fns_1.parseISO)(dateFrom));
86
+ const end = (0, date_fns_1.endOfDay)((0, date_fns_1.parseISO)(dateTo));
87
+ const targetDate = (0, date_fns_1.startOfDay)(date);
88
+ let current = start;
89
+ while (current <= end) {
90
+ const weekStart = current;
91
+ const weekEnd = (0, date_fns_1.isAfter)((0, date_fns_1.addDays)(current, 6), end)
92
+ ? end
93
+ : (0, date_fns_1.addDays)(current, 6);
94
+ if (targetDate >= weekStart && targetDate <= weekEnd) {
95
+ const startMonth = weekStart.getMonth();
96
+ const endMonth = weekEnd.getMonth();
97
+ if (startMonth === endMonth) {
98
+ const startDay = weekStart.getDate();
99
+ const endDay = weekEnd.getDate();
100
+ if (startDay === endDay) {
101
+ return `${(0, date_fns_1.format)(weekStart, 'd.M')}`;
102
+ }
103
+ return `${(0, date_fns_1.format)(weekStart, 'd.')}-${(0, date_fns_1.format)(weekEnd, 'd.M')}`;
104
+ }
105
+ else {
106
+ return `${(0, date_fns_1.format)(weekStart, 'd.M')}-${(0, date_fns_1.format)(weekEnd, 'd.M')}`;
107
+ }
108
+ }
109
+ current = (0, date_fns_1.addDays)(weekEnd, 1);
110
+ }
111
+ return (0, date_fns_1.format)(targetDate, 'yyyy-MM-dd');
112
+ }
113
+ /**
114
+ * Generates a list of week range keys (formatted as 'dd.MM-dd.MM') between the given start and end dates.
115
+ *
116
+ * @param start The start date of the overall range.
117
+ * @param end The end date of the overall range.
118
+ * @returns An array of strings representing week ranges.
119
+ */
120
+ function getAllWeekRangeKeys(start, end) {
121
+ const weeks = [];
122
+ let current = start;
123
+ while (current <= end) {
124
+ const weekStart = current;
125
+ const weekEnd = (0, date_fns_1.isAfter)((0, date_fns_1.addDays)(current, 6), end)
126
+ ? end
127
+ : (0, date_fns_1.addDays)(current, 6);
128
+ const startMonth = weekStart.getMonth();
129
+ const endMonth = weekEnd.getMonth();
130
+ if (startMonth === endMonth) {
131
+ const startDay = weekStart.getDate();
132
+ const endDay = weekEnd.getDate();
133
+ if (startDay === endDay) {
134
+ weeks.push(`${(0, date_fns_1.format)(weekStart, 'd.M')}`);
135
+ }
136
+ else {
137
+ weeks.push(`${(0, date_fns_1.format)(weekStart, 'd.')}-${(0, date_fns_1.format)(weekEnd, 'd.M')}`);
138
+ }
139
+ }
140
+ else {
141
+ weeks.push(`${(0, date_fns_1.format)(weekStart, 'd.M')}-${(0, date_fns_1.format)(weekEnd, 'd.M')}`);
142
+ }
143
+ current = (0, date_fns_1.addDays)(weekEnd, 1);
144
+ }
145
+ return weeks;
146
+ }
147
+ /**
148
+ * Generates a grouping key for a given date depending on the selected grouping mode (day, week, or month).
149
+ *
150
+ * @param date The date for which to generate the key.
151
+ * @param groupBy Grouping mode: 'day', 'week', or 'month'.
152
+ * @param dateFrom Optional start date of the range (required for 'week' grouping).
153
+ * @param dateTo Optional end date of the range (required for 'week' grouping).
154
+ * @returns A string key used for grouping data.
155
+ */
156
+ function getDateGroupingKey(date, groupBy, dateFrom, dateTo) {
157
+ if (groupBy === 'month') {
158
+ return (0, date_fns_1.format)((0, date_fns_1.startOfMonth)(new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()))), 'yyyy-MM');
159
+ }
160
+ if (groupBy === 'week' && dateFrom && dateTo) {
161
+ return getWeekRangeKeyForDate(new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())), dateFrom, dateTo);
162
+ }
163
+ return (0, date_fns_1.format)(new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())), 'yyyy-MM-dd');
164
+ }
165
+ /**
166
+ * Generates a list of grouping keys for all periods between two dates, depending on the selected grouping mode.
167
+ *
168
+ * @param groupBy Grouping mode: 'day', 'week', or 'month'.
169
+ * @param dateFrom Start date of the range (in ISO string format).
170
+ * @param dateTo End date of the range (in ISO string format).
171
+ * @returns An array of strings representing keys for each grouped period.
172
+ */
173
+ function getAllDateGroupingKeys(groupBy, dateFrom, dateTo) {
174
+ const start = (0, date_fns_1.parseISO)(dateFrom);
175
+ const end = (0, date_fns_1.parseISO)(dateTo);
176
+ if (groupBy === 'day') {
177
+ return (0, date_fns_1.eachDayOfInterval)({ start, end }).map((d) => (0, date_fns_1.format)(d, 'yyyy-MM-dd'));
178
+ }
179
+ if (groupBy === 'month') {
180
+ return (0, date_fns_1.eachMonthOfInterval)({ start, end }).map((d) => (0, date_fns_1.format)(d, 'yyyy-MM'));
181
+ }
182
+ return getAllWeekRangeKeys(start, end);
183
+ }
184
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JkZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL29yZGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFrSEEsd0RBc0NDO0FBU0Qsa0RBNkJDO0FBV0QsZ0RBK0JDO0FBVUQsd0RBbUJDO0FBclFELHVDQWFrQjtBQUlsQjs7OztHQUlHO0FBQ1UsUUFBQSx3QkFBd0IsR0FPakM7SUFDRixNQUFNLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtRQUNoQixJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN2QyxNQUFNLElBQUksS0FBSyxDQUFDLHdCQUF3QixDQUFDLENBQUM7UUFDNUMsQ0FBQztRQUVELE1BQU0sS0FBSyxHQUFHLElBQUEsbUJBQVEsRUFBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDeEMsTUFBTSxHQUFHLEdBQUcsSUFBQSxtQkFBUSxFQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNwQyxNQUFNLElBQUksR0FBRyxJQUFBLG1DQUF3QixFQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDdEQsTUFBTSxPQUFPLEdBQUcsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDaEMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDckMsTUFBTSxTQUFTLEdBQUcsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDcEMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLEdBQUcsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUVsRCxPQUFPO1lBQ0wsT0FBTyxFQUFFO2dCQUNQLEtBQUssRUFBRSxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFD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
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ <p align="center">
2
+ <a href="https://www.medusajs.com">
3
+ <img alt="Medusa logo" src="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
4
+ </a>
5
+ </p>
6
+ <h1 align="center">
7
+ Medusa Analytics Plugin
8
+ </h1>
9
+
10
+ <p align="center">
11
+ Get actionable insights into your store's performance and make data-driven decisions right from the Medusa Admin dashboard.
12
+ </p>
13
+
14
+ ## Overview
15
+
16
+ The Medusa Analytics Plugin is a lightweight analytics extension for the Medusa Admin dashboard. It provides store admins with a clear view of sales and product performance using focused KPIs, charts, and tables, all accessible directly within the Medusa Admin panel.
17
+
18
+ ✅ Compatible with Medusa v2
19
+
20
+ ## Features
21
+
22
+ - **Date Range Picker** with presets: This Month, Last Month, Last 3 Months, Custom Range (applies to all analytics)
23
+ - **Tabbed Interface**: Switch between Orders and Products analytics
24
+ - **Charts & KPIs**:
25
+ - **Orders Tab**:
26
+ - Total Orders (KPI)
27
+ - Total Sales (KPI)
28
+ - Orders Over Time (Line Chart)
29
+ - Sales Over Time (Line Chart)
30
+ - Top Regions by Sales (Bar Chart)
31
+ - Order Status Breakdown (Pie Chart)
32
+ - **Products Tab**:
33
+ - Top-Selling Products (Bar Chart)
34
+ - Out-of-Stock Variants (Table)
35
+ - Low Stock Variants (Table)
36
+
37
+ ## Getting Started
38
+
39
+ 1. **Install the plugin** in your Medusa project:
40
+ ```bash
41
+ yarn add @agilo/medusa-analytics-plugin
42
+ ```
43
+ 2. **Add the plugin** to your Medusa backend configuration. In `medusa-config.ts`, add the following to the `plugins` array:
44
+
45
+ ```js
46
+ plugins: [
47
+ {
48
+ resolve: '@agilo/medusa-analytics-plugin',
49
+ options: {},
50
+ },
51
+ // ...other plugins
52
+ ],
53
+ ```
54
+
55
+ 3. **Install dependencies:**
56
+ ```bash
57
+ yarn
58
+ ```
59
+ 4. **Start your Medusa server:**
60
+ ```bash
61
+ yarn dev
62
+ ```
63
+ 5. **Access the Analytics page** from the Medusa Admin dashboard.
64
+
65
+ ## Contributing
66
+
67
+ We welcome contributions and feedback.
68
+ To get involved, [open an issue](https://github.com/Agilo/medusa-analytics-plugin/issues) or [submit a pull request](https://github.com/Agilo/medusa-analytics-plugin/pulls) on [GitHub →](https://github.com/Agilo/medusa-analytics-plugin)