├── .gitignore
├── README.md
├── docs
└── store-analytics-logo.PNG
├── v1
├── .babelrc.js
├── .gitignore
├── .npmignore
├── .yarnrc.yml
├── LICENSE
├── README.md
├── datasource.js
├── docs
│ ├── medusa-store-analytics-1.PNG
│ ├── medusa-store-analytics-2.PNG
│ └── medusa-store-analytics-3.PNG
├── index.js
├── medusa-config.js
├── package copy.json
├── package.json
├── src
│ ├── admin
│ │ └── routes
│ │ │ └── analytics
│ │ │ └── page.tsx
│ ├── api
│ │ ├── README.md
│ │ └── admin
│ │ │ ├── customers-analytics
│ │ │ └── [kind]
│ │ │ │ └── route.ts
│ │ │ ├── marketing-analytics
│ │ │ └── [kind]
│ │ │ │ └── route.ts
│ │ │ ├── orders-analytics
│ │ │ └── [kind]
│ │ │ │ └── route.ts
│ │ │ ├── products-analytics
│ │ │ └── [kind]
│ │ │ │ └── route.ts
│ │ │ ├── reports-analytics
│ │ │ └── [kind]
│ │ │ │ └── route.ts
│ │ │ └── sales-analytics
│ │ │ └── [kind]
│ │ │ └── route.ts
│ ├── index.ts
│ ├── jobs
│ │ └── README.md
│ ├── loaders
│ │ └── README.md
│ ├── migrations
│ │ └── README.md
│ ├── models
│ │ └── README.md
│ ├── services
│ │ ├── customersAnalytics.ts
│ │ ├── marketingAnalytics.ts
│ │ ├── ordersAnalytics.ts
│ │ ├── pdf-templates
│ │ │ ├── common.ts
│ │ │ ├── customers-template.ts
│ │ │ ├── hr.ts
│ │ │ ├── orders-template.ts
│ │ │ ├── products-template.ts
│ │ │ └── sales-template.ts
│ │ ├── productsAnalytics.ts
│ │ ├── reportsAnalytics.ts
│ │ ├── salesAnalytics.ts
│ │ └── utils
│ │ │ ├── currency.ts
│ │ │ ├── dateTransformations.ts
│ │ │ └── types.ts
│ ├── subscribers
│ │ └── README.md
│ └── ui-components
│ │ ├── common
│ │ ├── chart-components.tsx
│ │ ├── icon-comparison.tsx
│ │ ├── overview-components.tsx
│ │ ├── percentage-comparison.tsx
│ │ ├── popularity-table.tsx
│ │ └── utils
│ │ │ └── chartUtils.ts
│ │ ├── customers
│ │ ├── cumulative-history
│ │ │ ├── cumulative-customers-card.tsx
│ │ │ └── cumulative-customers-chart.tsx
│ │ ├── customers-by-new-chart.tsx
│ │ ├── customers-number-overview.tsx
│ │ ├── customers-overview-card.tsx
│ │ ├── repeat-customer-rate
│ │ │ ├── customers-repeat-customer-rate-number.tsx
│ │ │ ├── customers-repeat-customer-rate.tsx
│ │ │ ├── order-frequency-distribution-chart.tsx
│ │ │ └── order-frequency-distribution.tsx
│ │ ├── retention-customer-rate
│ │ │ ├── customers-retention-customer-rate-number.tsx
│ │ │ └── customers-retention-customer-rate.tsx
│ │ └── types.ts
│ │ ├── index.ts
│ │ ├── marketing
│ │ ├── discounts-top-by-count.tsx
│ │ └── discounts-top-table.tsx
│ │ ├── orders
│ │ ├── orders-by-new-chart.tsx
│ │ ├── orders-number-overview.tsx
│ │ ├── orders-overview-card.tsx
│ │ ├── orders-payment-provider-card.tsx
│ │ ├── orders-payment-provider-chart.tsx
│ │ └── types.ts
│ │ ├── products
│ │ ├── out_of_the_stock_variants
│ │ │ ├── helpers.tsx
│ │ │ ├── out-of-the-stock-variants-all.tsx
│ │ │ ├── out-of-the-stock-variants-by-count.tsx
│ │ │ └── out-of-the-stock-variants-table.tsx
│ │ ├── products-sold-count.tsx
│ │ ├── returned_variants
│ │ │ ├── returned-variants-by-count.tsx
│ │ │ └── returned-variants-table.tsx
│ │ ├── variants-top-by-count.tsx
│ │ └── variants-top-table.tsx
│ │ ├── sales
│ │ ├── refunds
│ │ │ ├── refunds-numbers.tsx
│ │ │ └── refunds-overview-card.tsx
│ │ ├── regions-popularity-card.tsx
│ │ ├── sales-channel-popularity-card.tsx
│ │ ├── sales-number-overview.tsx
│ │ ├── sales-overview-card.tsx
│ │ ├── sales-total-chart.tsx
│ │ └── types.ts
│ │ ├── tabs
│ │ ├── customers.tsx
│ │ ├── orders.tsx
│ │ ├── overview.tsx
│ │ ├── pro.tsx
│ │ ├── products.tsx
│ │ └── sales.tsx
│ │ └── utils
│ │ ├── helpers.ts
│ │ └── types.ts
├── tsconfig.admin.json
├── tsconfig.json
├── tsconfig.server.json
└── tsconfig.spec.json
└── v2
├── README.md
├── docs
├── medusa-store-analytics-1.PNG
└── medusa-store-analytics-2.PNG
├── package.json
├── src
├── admin
│ └── routes
│ │ └── analytics
│ │ └── page.tsx
├── api
│ ├── README.md
│ └── admin
│ │ ├── customers-analytics
│ │ └── [kind]
│ │ │ └── route.ts
│ │ ├── helpers-analytics
│ │ └── pro-check
│ │ │ └── route.ts
│ │ ├── marketing-analytics
│ │ └── [kind]
│ │ │ └── route.ts
│ │ ├── orders-analytics
│ │ └── [kind]
│ │ │ └── route.ts
│ │ ├── products-analytics
│ │ └── [kind]
│ │ │ └── route.ts
│ │ ├── reports-analytics
│ │ └── [kind]
│ │ │ └── route.ts
│ │ └── sales-analytics
│ │ └── [kind]
│ │ └── route.ts
├── index.ts
├── jobs
│ └── README.md
├── loaders
│ └── README.md
├── migrations
│ └── README.md
├── models
│ └── README.md
├── modules
│ └── store-analytics
│ │ ├── index.ts
│ │ ├── service.ts
│ │ ├── services
│ │ ├── customersAnalytics.ts
│ │ ├── index.ts
│ │ ├── marketingAnalytics.ts
│ │ ├── ordersAnalytics.ts
│ │ ├── pdf-templates
│ │ │ ├── common.ts
│ │ │ ├── customers-template.ts
│ │ │ ├── hr.ts
│ │ │ ├── orders-template.ts
│ │ │ ├── products-template.ts
│ │ │ └── sales-template.ts
│ │ ├── productsAnalytics.ts
│ │ ├── reportsAnalytics.ts
│ │ ├── salesAnalytics.ts
│ │ └── utils
│ │ │ ├── currency.ts
│ │ │ └── types.ts
│ │ └── utils
│ │ ├── currency.ts
│ │ ├── dateTransformations.ts
│ │ └── types.ts
├── subscribers
│ └── README.md
└── ui-components
│ ├── common
│ ├── chart-components.tsx
│ ├── icon-comparison.tsx
│ ├── overview-components.tsx
│ ├── percentage-comparison.tsx
│ ├── popularity-table.tsx
│ └── utils
│ │ └── chartUtils.ts
│ ├── customers
│ ├── cumulative-history
│ │ ├── cumulative-customers-card.tsx
│ │ └── cumulative-customers-chart.tsx
│ ├── customers-by-new-chart.tsx
│ ├── customers-number-overview.tsx
│ ├── customers-overview-card.tsx
│ ├── repeat-customer-rate
│ │ ├── customers-repeat-customer-rate-number.tsx
│ │ ├── customers-repeat-customer-rate.tsx
│ │ ├── order-frequency-distribution-chart.tsx
│ │ └── order-frequency-distribution.tsx
│ ├── retention-customer-rate
│ │ ├── customers-retention-customer-rate-number.tsx
│ │ └── customers-retention-customer-rate.tsx
│ └── types.ts
│ ├── index.ts
│ ├── marketing
│ ├── discounts-top-by-count.tsx
│ └── discounts-top-table.tsx
│ ├── orders
│ ├── orders-by-new-chart.tsx
│ ├── orders-number-overview.tsx
│ ├── orders-overview-card.tsx
│ ├── orders-payment-provider-card.tsx
│ ├── orders-payment-provider-chart.tsx
│ └── types.ts
│ ├── products
│ ├── out_of_the_stock_variants
│ │ ├── helpers.tsx
│ │ ├── out-of-the-stock-variants-all.tsx
│ │ ├── out-of-the-stock-variants-by-count.tsx
│ │ └── out-of-the-stock-variants-table.tsx
│ ├── products-sold-count.tsx
│ ├── returned_variants
│ │ ├── returned-variants-by-count.tsx
│ │ └── returned-variants-table.tsx
│ ├── variants-top-by-count.tsx
│ └── variants-top-table.tsx
│ ├── sales
│ ├── refunds
│ │ ├── refunds-numbers.tsx
│ │ └── refunds-overview-card.tsx
│ ├── regions-popularity-card.tsx
│ ├── sales-channel-popularity-card.tsx
│ ├── sales-number-overview.tsx
│ ├── sales-overview-card.tsx
│ ├── sales-total-chart.tsx
│ └── types.ts
│ ├── tabs
│ ├── customers.tsx
│ ├── orders.tsx
│ ├── overview.tsx
│ ├── pro.tsx
│ ├── products.tsx
│ └── sales.tsx
│ └── utils
│ ├── helpers.ts
│ └── types.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | .env
3 | .DS_Store
4 | /uploads
5 | /node_modules
6 | /v2/node_modules
7 | /v2/.medusa
8 | yarn-error.log
9 |
10 | .idea
11 |
12 | coverage
13 |
14 | !src/**
15 |
16 | ./tsconfig.tsbuildinfo
17 | package-lock.json
18 | yarn.lock
19 | medusa-db.sql
20 | build
21 | .cache
22 |
23 | .yarn/*
24 | !.yarn/patches
25 | !.yarn/plugins
26 | !.yarn/releases
27 | !.yarn/sdks
28 | !.yarn/versions
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Medusa store-analytics
9 |
10 |
11 | Medusa "store-analytics" is a plugin which shows analytics data of your store, including orders, sales and other useful information.
12 |
13 | ### Why?
14 |
15 | Knowledge about your store is crucial to take proper action to increase the sales. Analytics data can show various things like what is a most popular region, sales channel or on which day people are buying the most. Every such data may help to find problem and possible solutions.
16 |
17 | ### Supported Medusa versions
18 |
19 | Choose README dependent on your Medusa version.
20 |
21 |
22 | Medusa V1
23 |
24 |
25 | [README](https://github.com/RSC-Labs/medusa-store-analytics/blob/main/v1/README.md)
26 |
27 |
28 | Medusa V2
29 |
30 |
31 | [README](https://github.com/RSC-Labs/medusa-store-analytics/blob/main/v2/README.md)
32 |
33 | ## Supported statistics
34 |
35 | ### General
36 |
37 | | Name | Status |
38 | | --- | --- |
39 | | 4 ranges of dates | :white_check_mark: |
40 | | Comparison across date ranges | :white_check_mark: |
41 | | Filtering by orders' status | :white_check_mark: |
42 |
43 | ### Orders
44 |
45 | | Name | Status |
46 | | --- | --- |
47 | | Orders by time | :white_check_mark: |
48 | | Orders chart | :white_check_mark: |
49 | | Regions popularity | :white_check_mark: |
50 | | Sales channel popularity | :white_check_mark: |
51 | | Orders frequency distribution | :white_check_mark: |
52 | | Payment provider popularity | :white_check_mark: |
53 |
54 | ### Sales
55 |
56 | | Name | Status |
57 | | --- | --- |
58 | | Sales by time | :white_check_mark: |
59 | | Sales by currency code | :white_check_mark: |
60 | | Sales chart | :white_check_mark: |
61 | | Refunds | :white_check_mark: |
62 |
63 | ### Customers
64 |
65 |
66 | | Name | Status |
67 | | --- | --- |
68 | | New customers by time | :white_check_mark: |
69 | | Repeat customer rate | :white_check_mark: |
70 | | Customers chart | :white_check_mark: |
71 | | Cumulative customers by time | :white_check_mark: |
72 |
73 | ### Products
74 |
75 | | Name | Status |
76 | | --- | --- |
77 | | Top variants | :white_check_mark: |
78 | | Top returned variants | :white_check_mark: |
79 | | Products sold count | :white_check_mark: |
80 | | Out of the stock variants | :white_check_mark: |
81 |
82 | ### Marketing
83 |
84 | | Name | Status |
85 | | --- | --- |
86 | | Top discounts | :white_check_mark: |
87 |
88 | ---
89 |
90 | © 2024 RSC https://rsoftcon.com/
91 |
--------------------------------------------------------------------------------
/docs/store-analytics-logo.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RSC-Labs/medusa-store-analytics/4d04707866d80087d9b48f57a99a0efed85e0e1e/docs/store-analytics-logo.PNG
--------------------------------------------------------------------------------
/v1/.babelrc.js:
--------------------------------------------------------------------------------
1 | let ignore = [`**/dist`]
2 |
3 | // Jest needs to compile this code, but generally we don't want this copied
4 | // to output folders
5 | if (process.env.NODE_ENV !== `test`) {
6 | ignore.push(`**/__tests__`)
7 | }
8 |
9 | module.exports = {
10 | presets: [["babel-preset-medusa-package"], ["@babel/preset-typescript"]],
11 | ignore,
12 | }
13 |
--------------------------------------------------------------------------------
/v1/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | .env
3 | .DS_Store
4 | /uploads
5 | /node_modules
6 | yarn-error.log
7 |
8 | .idea
9 |
10 | coverage
11 |
12 | !src/**
13 |
14 | ./tsconfig.tsbuildinfo
15 | package-lock.json
16 | yarn.lock
17 | medusa-db.sql
18 | build
19 | .cache
20 |
21 | .yarn/*
22 | !.yarn/patches
23 | !.yarn/plugins
24 | !.yarn/releases
25 | !.yarn/sdks
26 | !.yarn/versions
27 |
--------------------------------------------------------------------------------
/v1/.npmignore:
--------------------------------------------------------------------------------
1 | /lib
2 | node_modules
3 | .DS_store
4 | .env*
5 | /*.js
6 | !index.js
7 | yarn.lock
8 | src
9 | .gitignore
10 | .eslintrc
11 | .babelrc
12 | .prettierrc
13 | build
14 | .cache
15 | .yarn
16 | uploads
17 |
18 | # These are files that are included in a
19 | # Medusa project and can be removed from a
20 | # plugin project
21 | medusa-config.js
22 | Dockerfile
23 | medusa-db.sql
24 | develop.sh
--------------------------------------------------------------------------------
/v1/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
--------------------------------------------------------------------------------
/v1/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 RSC-Labs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/v1/datasource.js:
--------------------------------------------------------------------------------
1 | const { DataSource } = require("typeorm")
2 |
3 | const AppDataSource = new DataSource({
4 | type: "postgres",
5 | port: 5432,
6 | username: "postgres",
7 | password: "postgres",
8 | database: "medusa-z0g5",
9 | entities: [
10 | ],
11 | migrations: [
12 | "dist/migrations/*.js",
13 | ],
14 | autoLoadEntities: true
15 | })
16 |
17 | module.exports = {
18 | datasource: AppDataSource,
19 | }
--------------------------------------------------------------------------------
/v1/docs/medusa-store-analytics-1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RSC-Labs/medusa-store-analytics/4d04707866d80087d9b48f57a99a0efed85e0e1e/v1/docs/medusa-store-analytics-1.PNG
--------------------------------------------------------------------------------
/v1/docs/medusa-store-analytics-2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RSC-Labs/medusa-store-analytics/4d04707866d80087d9b48f57a99a0efed85e0e1e/v1/docs/medusa-store-analytics-2.PNG
--------------------------------------------------------------------------------
/v1/docs/medusa-store-analytics-3.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RSC-Labs/medusa-store-analytics/4d04707866d80087d9b48f57a99a0efed85e0e1e/v1/docs/medusa-store-analytics-3.PNG
--------------------------------------------------------------------------------
/v1/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 | const { GracefulShutdownServer } = require("medusa-core-utils")
3 |
4 | const loaders = require("@medusajs/medusa/dist/loaders/index").default
5 |
6 | ;(async() => {
7 | async function start() {
8 | const app = express()
9 | const directory = process.cwd()
10 |
11 | try {
12 | const { container } = await loaders({
13 | directory,
14 | expressApp: app
15 | })
16 | const configModule = container.resolve("configModule")
17 | const port = process.env.PORT ?? configModule.projectConfig.port ?? 9000
18 |
19 | const server = GracefulShutdownServer.create(
20 | app.listen(port, (err) => {
21 | if (err) {
22 | return
23 | }
24 | console.log(`Server is ready on port: ${port}`)
25 | })
26 | )
27 |
28 | // Handle graceful shutdown
29 | const gracefulShutDown = () => {
30 | server
31 | .shutdown()
32 | .then(() => {
33 | console.info("Gracefully stopping the server.")
34 | process.exit(0)
35 | })
36 | .catch((e) => {
37 | console.error("Error received when shutting down the server.", e)
38 | process.exit(1)
39 | })
40 | }
41 | process.on("SIGTERM", gracefulShutDown)
42 | process.on("SIGINT", gracefulShutDown)
43 | } catch (err) {
44 | console.error("Error starting server", err)
45 | process.exit(1)
46 | }
47 | }
48 |
49 | await start()
50 | })()
51 |
--------------------------------------------------------------------------------
/v1/medusa-config.js:
--------------------------------------------------------------------------------
1 | const dotenv = require("dotenv");
2 |
3 | let ENV_FILE_NAME = "";
4 | switch (process.env.NODE_ENV) {
5 | case "production":
6 | ENV_FILE_NAME = ".env.production";
7 | break;
8 | case "staging":
9 | ENV_FILE_NAME = ".env.staging";
10 | break;
11 | case "test":
12 | ENV_FILE_NAME = ".env.test";
13 | break;
14 | case "development":
15 | default:
16 | ENV_FILE_NAME = ".env";
17 | break;
18 | }
19 |
20 | try {
21 | dotenv.config({ path: process.cwd() + "/" + ENV_FILE_NAME });
22 | } catch (e) {}
23 |
24 | // CORS when consuming Medusa from admin
25 | const ADMIN_CORS =
26 | process.env.ADMIN_CORS || "http://localhost:7000,http://localhost:7001";
27 |
28 | // CORS to avoid issues when consuming Medusa from a client
29 | const STORE_CORS = process.env.STORE_CORS || "http://localhost:8000";
30 |
31 | const DATABASE_URL =
32 | process.env.DATABASE_URL || "postgres://localhost/medusa-starter-default";
33 |
34 | const REDIS_URL = process.env.REDIS_URL || "redis://localhost:6379";
35 |
36 | const plugins = [
37 |
38 | ];
39 |
40 | const modules = {
41 |
42 | };
43 |
44 | /** @type {import('@medusajs/medusa').ConfigModule["projectConfig"]} */
45 | const projectConfig = {
46 | jwtSecret: process.env.JWT_SECRET,
47 | cookieSecret: process.env.COOKIE_SECRET,
48 | store_cors: STORE_CORS,
49 | database_url: DATABASE_URL,
50 | admin_cors: ADMIN_CORS,
51 | // Uncomment the following lines to enable REDIS
52 | // redis_url: REDIS_URL
53 | };
54 |
55 | /** @type {import('@medusajs/medusa').ConfigModule} */
56 | module.exports = {
57 | projectConfig,
58 | plugins,
59 | modules,
60 | };
61 |
--------------------------------------------------------------------------------
/v1/package copy.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@rsc-labs/medusa-store-analytics",
3 | "version": "0.14.2",
4 | "description": "Get analytics data about your store",
5 | "author": "RSC Labs (https://rsoftcon.com)",
6 | "main": "dist/index.js",
7 | "types": "dist/index.d.ts",
8 | "license": "MIT",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/RSC-Labs/medusa-store-analytics"
12 | },
13 | "keywords": [
14 | "medusa-plugin",
15 | "medusa-plugin-analytics",
16 | "analytics",
17 | "statistics",
18 | "store"
19 | ],
20 | "scripts": {
21 | "clean": "cross-env ./node_modules/.bin/rimraf dist",
22 | "build": "cross-env npm run clean && npm run build:server && npm run build:admin",
23 | "build:server": "cross-env npm run clean && tsc -p tsconfig.json",
24 | "build:admin": "cross-env medusa-admin build",
25 | "prepare": "cross-env NODE_ENV=production npm run build:server && medusa-admin bundle"
26 | },
27 | "dependencies": {
28 | "@medusajs/admin": "7.1.17",
29 | "@medusajs/ui": "^3.0.0",
30 | "@medusajs/icons": "1.2.2",
31 | "@medusajs/utils": "1.11.11",
32 | "@tanstack/react-query": "4.22",
33 | "medusa-interfaces": "^1.3.7",
34 | "medusa-react": "9.0.18",
35 | "@mui/material": "^5.15.3",
36 | "typeorm": "^0.3.16",
37 | "react-hook-form": "^7.49.2",
38 | "@emotion/react": "^11.11.3",
39 | "@emotion/styled": "11.13.0",
40 | "recharts": "^2.10.3",
41 | "pdfkit": "^0.15.0"
42 | },
43 | "devDependencies": {
44 | "@babel/cli": "^7.14.3",
45 | "@babel/core": "^7.14.3",
46 | "@medusajs/medusa": "^1.20.7",
47 | "@types/express": "^4.17.13",
48 | "babel-preset-medusa-package": "^1.1.19",
49 | "cross-env": "^7.0.3",
50 | "eslint": "^6.8.0",
51 | "rimraf": "^3.0.2",
52 | "typescript": "^4.5.2"
53 | },
54 | "peerDependencies": {
55 | "@medusajs/medusa": "^1.20.7",
56 | "react": "^18.2.0",
57 | "react-router-dom": "^6.13.0"
58 | },
59 | "jest": {
60 | "globals": {
61 | "ts-jest": {
62 | "tsconfig": "tsconfig.spec.json"
63 | }
64 | },
65 | "moduleFileExtensions": [
66 | "js",
67 | "json",
68 | "ts"
69 | ],
70 | "testPathIgnorePatterns": [
71 | "/node_modules/",
72 | "/node_modules/"
73 | ],
74 | "rootDir": "src",
75 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|js)$",
76 | "transform": {
77 | ".ts": "ts-jest"
78 | },
79 | "collectCoverageFrom": [
80 | "**/*.(t|j)s"
81 | ],
82 | "coverageDirectory": "./coverage",
83 | "testEnvironment": "node"
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/v1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@rsc-labs/medusa-store-analytics",
3 | "version": "0.15.0",
4 | "description": "Get analytics data about your store",
5 | "author": "RSC Labs (https://rsoftcon.com)",
6 | "main": "dist/index.js",
7 | "types": "dist/index.d.ts",
8 | "license": "MIT",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/RSC-Labs/medusa-store-analytics"
12 | },
13 | "keywords": [
14 | "medusa-plugin",
15 | "medusa-plugin-analytics",
16 | "analytics",
17 | "statistics",
18 | "store"
19 | ],
20 | "scripts": {
21 | "clean": "cross-env ./node_modules/.bin/rimraf dist",
22 | "build": "cross-env npm run clean && npm run build:server && npm run build:admin",
23 | "build:server": "cross-env npm run clean && tsc -p tsconfig.json",
24 | "build:admin": "cross-env medusa-admin build",
25 | "prepare": "cross-env NODE_ENV=production npm run build:server && medusa-admin bundle"
26 | },
27 | "dependencies": {
28 | "@medusajs/admin": "7.1.17",
29 | "@medusajs/ui": "^3.0.0",
30 | "@medusajs/icons": "1.2.2",
31 | "@medusajs/utils": "1.11.11",
32 | "@tanstack/react-query": "4.22",
33 | "medusa-interfaces": "^1.3.7",
34 | "medusa-react": "9.0.18",
35 | "@mui/material": "^5.15.3",
36 | "typeorm": "^0.3.16",
37 | "react-hook-form": "^7.49.2",
38 | "@emotion/react": "^11.11.3",
39 | "@emotion/styled": "11.13.0",
40 | "recharts": "^2.10.3",
41 | "pdfkit": "^0.15.0"
42 | },
43 | "devDependencies": {
44 | "@babel/cli": "^7.14.3",
45 | "@babel/core": "^7.14.3",
46 | "@medusajs/medusa": "^1.20.7",
47 | "@types/express": "^4.17.13",
48 | "babel-preset-medusa-package": "^1.1.19",
49 | "cross-env": "^7.0.3",
50 | "eslint": "^6.8.0",
51 | "rimraf": "^3.0.2",
52 | "typescript": "^4.5.2"
53 | },
54 | "peerDependencies": {
55 | "@medusajs/medusa": "^1.20.7",
56 | "react": "^18.2.0",
57 | "react-router-dom": "^6.13.0"
58 | },
59 | "jest": {
60 | "globals": {
61 | "ts-jest": {
62 | "tsconfig": "tsconfig.spec.json"
63 | }
64 | },
65 | "moduleFileExtensions": [
66 | "js",
67 | "json",
68 | "ts"
69 | ],
70 | "testPathIgnorePatterns": [
71 | "/node_modules/",
72 | "/node_modules/"
73 | ],
74 | "rootDir": "src",
75 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|js)$",
76 | "transform": {
77 | ".ts": "ts-jest"
78 | },
79 | "collectCoverageFrom": [
80 | "**/*.(t|j)s"
81 | ],
82 | "coverageDirectory": "./coverage",
83 | "testEnvironment": "node"
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/v1/src/api/admin/marketing-analytics/[kind]/route.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import type {
14 | MedusaRequest,
15 | MedusaResponse,
16 | } from "@medusajs/medusa"
17 | import { OrderStatus } from "@medusajs/medusa";
18 | import { MedusaError, MedusaErrorTypes } from "@medusajs/utils"
19 | import MarketingAnalyticsService from "../../../../services/marketingAnalytics";
20 |
21 | export const GET = async (
22 | req: MedusaRequest,
23 | res: MedusaResponse
24 | ) => {
25 |
26 | const kind = req.params.kind;
27 | const dateRangeFrom = req.query.dateRangeFrom;
28 | const dateRangeTo = req.query.dateRangeTo;
29 | const orderStatusesFromQuery: string[] = req.query.orderStatuses as string[];
30 |
31 | const orderStatuses: OrderStatus[] = orderStatusesFromQuery !== undefined ?
32 | orderStatusesFromQuery.map(status => OrderStatus[status.toUpperCase()]).filter(orderStatus => orderStatus !== undefined): [];
33 |
34 |
35 | let result: any;
36 | const marketingAnalyticsService: MarketingAnalyticsService = req.scope.resolve('marketingAnalyticsService');
37 |
38 | try {
39 | switch (kind) {
40 | case 'discounts-by-count':
41 | result = await marketingAnalyticsService.getTopDiscountsByCount(
42 | orderStatuses,
43 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
44 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
45 | );
46 | break;
47 | }
48 | res.status(200).json({
49 | analytics: result
50 | });
51 | } catch (error) {
52 | throw new MedusaError(
53 | MedusaErrorTypes.DB_ERROR,
54 | error.message
55 | )
56 | }
57 | }
--------------------------------------------------------------------------------
/v1/src/api/admin/orders-analytics/[kind]/route.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import type {
14 | MedusaRequest,
15 | MedusaResponse,
16 | } from "@medusajs/medusa"
17 | import { OrderStatus } from "@medusajs/medusa";
18 | import { MedusaError, MedusaErrorTypes } from "@medusajs/utils"
19 | import OrdersAnalyticsService from "../../../../services/ordersAnalytics";
20 |
21 | export const GET = async (
22 | req: MedusaRequest,
23 | res: MedusaResponse
24 | ) => {
25 |
26 | const kind = req.params.kind;
27 | const dateRangeFrom = req.query.dateRangeFrom;
28 | const dateRangeTo = req.query.dateRangeTo;
29 | const dateRangeFromCompareTo = req.query.dateRangeFromCompareTo;
30 | const dateRangeToCompareTo = req.query.dateRangeToCompareTo;
31 | const orderStatusesFromQuery: string[] = req.query.orderStatuses as string[];
32 |
33 | const orderStatuses: OrderStatus[] = orderStatusesFromQuery !== undefined ?
34 | orderStatusesFromQuery.map(status => OrderStatus[status.toUpperCase()]).filter(orderStatus => orderStatus !== undefined): [];
35 |
36 |
37 | let result;
38 | const ordersAnalyticsService: OrdersAnalyticsService = req.scope.resolve('ordersAnalyticsService');
39 |
40 | try {
41 | switch (kind) {
42 | case 'history':
43 | result = await ordersAnalyticsService.getOrdersHistory(
44 | orderStatuses,
45 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
46 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
47 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
48 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
49 | );
50 | break;
51 | case 'count':
52 | result = await ordersAnalyticsService.getOrdersCount(
53 | orderStatuses,
54 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
55 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
56 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
57 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
58 | );
59 | break;
60 | case 'payment-provider':
61 | result = await ordersAnalyticsService.getPaymentProviderPopularity(
62 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
63 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
64 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
65 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
66 | );
67 | break;
68 | }
69 | res.status(200).json({
70 | analytics: result
71 | });
72 | } catch (error) {
73 | throw new MedusaError(
74 | MedusaErrorTypes.DB_ERROR,
75 | error.message
76 | )
77 | }
78 | }
--------------------------------------------------------------------------------
/v1/src/api/admin/products-analytics/[kind]/route.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import type {
14 | MedusaRequest,
15 | MedusaResponse,
16 | } from "@medusajs/medusa"
17 | import { OrderStatus } from "@medusajs/medusa";
18 | import { MedusaError, MedusaErrorTypes } from "@medusajs/utils"
19 | import ProductsAnalyticsService from "../../../../services/productsAnalytics";
20 |
21 | export const GET = async (
22 | req: MedusaRequest,
23 | res: MedusaResponse
24 | ) => {
25 |
26 | const kind = req.params.kind;
27 | const dateRangeFrom = req.query.dateRangeFrom;
28 | const dateRangeTo = req.query.dateRangeTo;
29 | const dateRangeFromCompareTo = req.query.dateRangeFromCompareTo;
30 | const dateRangeToCompareTo = req.query.dateRangeToCompareTo;
31 | const orderStatusesFromQuery: string[] = req.query.orderStatuses as string[];
32 |
33 | const orderStatuses: OrderStatus[] = orderStatusesFromQuery !== undefined ?
34 | orderStatusesFromQuery.map(status => OrderStatus[status.toUpperCase()]).filter(orderStatus => orderStatus !== undefined): [];
35 |
36 | let result: any;
37 | const productsAnalyticsService: ProductsAnalyticsService = req.scope.resolve('productsAnalyticsService');
38 |
39 | try {
40 | switch (kind) {
41 | case 'popularity-by-count':
42 | result = await productsAnalyticsService.getTopVariantsByCount(
43 | orderStatuses,
44 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
45 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
46 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
47 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
48 | );
49 | break;
50 | case 'returned-by-count':
51 | result = await productsAnalyticsService.getTopReturnedVariantsByCount(
52 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
53 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
54 | );
55 | break;
56 | case 'sold-count':
57 | result = await productsAnalyticsService.getProductsSoldCount(
58 | orderStatuses,
59 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
60 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
61 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
62 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
63 | );
64 | break;
65 | case 'out-of-the-stock-variants':
66 | const limit = req.query.limit as string;
67 | result = await productsAnalyticsService.getOutOfTheStockVariants(limit ? parseInt(limit) : undefined);
68 | break;
69 | }
70 | res.status(200).json({
71 | analytics: result
72 | });
73 | } catch (error) {
74 | throw new MedusaError(
75 | MedusaErrorTypes.DB_ERROR,
76 | error.message
77 | )
78 | }
79 | }
--------------------------------------------------------------------------------
/v1/src/api/admin/reports-analytics/[kind]/route.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import type {
14 | MedusaRequest,
15 | MedusaResponse,
16 | } from "@medusajs/medusa"
17 | import { OrderStatus } from "@medusajs/medusa";
18 | import { MedusaError, MedusaErrorTypes } from "@medusajs/utils"
19 | import ReportsAnalyticsService from "../../../../services/reportsAnalytics";
20 |
21 | export const POST = async (
22 | req: MedusaRequest,
23 | res: MedusaResponse
24 | ) => {
25 |
26 | const kind = req.params.kind;
27 | const body: any = req.body as any;
28 | const dateRangeFrom = body.dateRangeFrom;
29 | const dateRangeTo = body.dateRangeTo;
30 | const dateRangeFromCompareTo = body.dateRangeFromCompareTo;
31 | const dateRangeToCompareTo = body.dateRangeToCompareTo;
32 | const orderStatusesFromQuery: string[] = body.orderStatuses as string[];
33 |
34 | const orderStatuses: OrderStatus[] = orderStatusesFromQuery !== undefined ?
35 | orderStatusesFromQuery.map(status => OrderStatus[status.toUpperCase()]).filter(orderStatus => orderStatus !== undefined): [];
36 |
37 | let result: Buffer;
38 | const reportsAnalyticsService: ReportsAnalyticsService = req.scope.resolve('reportsAnalyticsService');
39 |
40 | try {
41 | switch (kind) {
42 | case 'general':
43 | result = await reportsAnalyticsService.generateReport(
44 | orderStatuses,
45 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
46 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
47 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
48 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
49 | );
50 | break;
51 | }
52 | res.status(201).json({
53 | buffer: result
54 | });
55 | } catch (error) {
56 | throw new MedusaError(
57 | MedusaErrorTypes.DB_ERROR,
58 | error.message
59 | )
60 | }
61 | }
--------------------------------------------------------------------------------
/v1/src/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export * from './ui-components'
--------------------------------------------------------------------------------
/v1/src/jobs/README.md:
--------------------------------------------------------------------------------
1 | # Custom scheduled jobs
2 |
3 | You may define custom scheduled jobs (cron jobs) by creating files in the `/jobs` directory.
4 |
5 | ```ts
6 | import {
7 | ProductService,
8 | ScheduledJobArgs,
9 | ScheduledJobConfig,
10 | } from "@medusajs/medusa";
11 |
12 | export default async function myCustomJob({ container }: ScheduledJobArgs) {
13 | const productService: ProductService = container.resolve("productService");
14 |
15 | const products = await productService.listAndCount();
16 |
17 | // Do something with the products
18 | }
19 |
20 | export const config: ScheduledJobConfig = {
21 | name: "daily-product-report",
22 | schedule: "0 0 * * *", // Every day at midnight
23 | };
24 | ```
25 |
26 | A scheduled job is defined in two parts a `handler` and a `config`. The `handler` is a function which is invoked when the job is scheduled. The `config` is an object which defines the name of the job, the schedule, and an optional data object.
27 |
28 | The `handler` is a function which takes one parameter, an `object` of type `ScheduledJobArgs` with the following properties:
29 |
30 | - `container` - a `MedusaContainer` instance which can be used to resolve services.
31 | - `data` - an `object` containing data passed to the job when it was scheduled. This object is passed in the `config` object.
32 | - `pluginOptions` - an `object` containing plugin options, if the job is defined in a plugin.
33 |
--------------------------------------------------------------------------------
/v1/src/loaders/README.md:
--------------------------------------------------------------------------------
1 | # Custom loader
2 |
3 | The loader allows you have access to the Medusa service container. This allows you to access the database and the services registered on the container.
4 | you can register custom registrations in the container or run custom code on startup.
5 |
6 | ```ts
7 | // src/loaders/my-loader.ts
8 |
9 | import { AwilixContainer } from 'awilix'
10 |
11 | /**
12 | *
13 | * @param container The container in which the registrations are made
14 | * @param config The options of the plugin or the entire config object
15 | */
16 | export default (container: AwilixContainer, config: Record): void | Promise => {
17 | /* Implement your own loader. */
18 | }
19 | ```
--------------------------------------------------------------------------------
/v1/src/migrations/README.md:
--------------------------------------------------------------------------------
1 | # Custom migrations
2 |
3 | You may define custom models (entities) that will be registered on the global container by creating files in the `src/models` directory that export an instance of `BaseEntity`.
4 | In that case you also need to provide a migration in order to create the table in the database.
5 |
6 | ## Example
7 |
8 | ### 1. Create the migration
9 |
10 | See [How to Create Migrations](https://docs.medusajs.com/advanced/backend/migrations/) in the documentation.
11 |
12 | ```ts
13 | // src/migration/my-migration.ts
14 |
15 | import { MigrationInterface, QueryRunner } from "typeorm"
16 |
17 | export class MyMigration1617703530229 implements MigrationInterface {
18 | name = "myMigration1617703530229"
19 |
20 | public async up(queryRunner: QueryRunner): Promise {
21 | // write you migration here
22 | }
23 |
24 | public async down(queryRunner: QueryRunner): Promise {
25 | // write you migration here
26 | }
27 | }
28 |
29 | ```
--------------------------------------------------------------------------------
/v1/src/models/README.md:
--------------------------------------------------------------------------------
1 | # Custom models
2 |
3 | You may define custom models (entities) that will be registered on the global container by creating files in the `src/models` directory that export an instance of `BaseEntity`.
4 |
5 | ## Example
6 |
7 | ### 1. Create the Entity
8 |
9 | ```ts
10 | // src/models/post.ts
11 |
12 | import { BeforeInsert, Column, Entity, PrimaryColumn } from "typeorm";
13 | import { generateEntityId } from "@medusajs/utils";
14 | import { BaseEntity } from "@medusajs/medusa";
15 |
16 | @Entity()
17 | export class Post extends BaseEntity {
18 | @Column({type: 'varchar'})
19 | title: string | null;
20 |
21 | @BeforeInsert()
22 | private beforeInsert(): void {
23 | this.id = generateEntityId(this.id, "post")
24 | }
25 | }
26 | ```
27 |
28 | ### 2. Create the Migration
29 |
30 | You also need to create a Migration to create the new table in the database. See [How to Create Migrations](https://docs.medusajs.com/advanced/backend/migrations/) in the documentation.
31 |
32 | ### 3. Create a Repository
33 | Entities data can be easily accessed and modified using [TypeORM Repositories](https://typeorm.io/working-with-repository). To create a repository, create a file in `src/repositories`. For example, here’s a repository `PostRepository` for the `Post` entity:
34 |
35 | ```ts
36 | // src/repositories/post.ts
37 |
38 | import { EntityRepository, Repository } from "typeorm"
39 |
40 | import { Post } from "../models/post"
41 |
42 | @EntityRepository(Post)
43 | export class PostRepository extends Repository { }
44 | ```
45 |
46 | See more about defining and accesing your custom [Entities](https://docs.medusajs.com/advanced/backend/entities/overview) in the documentation.
--------------------------------------------------------------------------------
/v1/src/services/pdf-templates/common.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { OrderStatus } from "@medusajs/medusa"
14 |
15 |
16 | const Y_REACHED_TO_ADD_NEW_PAGE = '650';
17 |
18 | function addPageIfReachingEnd(doc) {
19 | if (doc.y > Y_REACHED_TO_ADD_NEW_PAGE) {
20 | doc.addPage();
21 | }
22 | }
23 |
24 | export function moveDown(doc) {
25 | addPageIfReachingEnd(doc);
26 | doc.moveDown();
27 | }
28 |
29 | export function generateReportHeader(doc, orderStatuses: OrderStatus[], from?: Date, to?: Date, dateRangeFromCompareTo?: Date, dateRangeToCompareTo?: Date) : void {
30 | doc
31 | .fillColor("#444444")
32 | .fontSize(20)
33 |
34 | doc
35 | .text('Store analytics report', { align: "center"})
36 |
37 | doc
38 | .fontSize(16)
39 | .moveDown()
40 |
41 | doc
42 | .text('for', { align: "center"})
43 | .moveDown();
44 |
45 | if (from) {
46 | doc
47 | .text(`${from.toDateString()} - ${to ? to.toDateString() : new Date(Date.now()).toDateString()}`, { align: "center"})
48 | .moveDown();
49 | } else {
50 | doc
51 | .text(`All time - ${to ? to.toDateString() : new Date(Date.now()).toDateString()}`, { align: "center"})
52 | .moveDown();
53 | }
54 | doc
55 | .fontSize(10)
56 | .text(`Filtered by order statuses:`, { align: "center"})
57 |
58 | orderStatuses.map(orderStatus => doc
59 | .text(orderStatus, { align: "center"})
60 | );
61 |
62 | doc
63 | .addPage()
64 | }
--------------------------------------------------------------------------------
/v1/src/services/pdf-templates/customers-template.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { CustomersHistoryResult } from "../customersAnalytics";
14 | import { moveDown } from "./common";
15 | import { generateHr } from "./hr";
16 |
17 | export default class PdfCustomersTemplate {
18 | private static generateTableRow(
19 | doc,
20 | date,
21 | customers
22 | ) {
23 | const startY = doc.y;
24 | doc
25 | .fontSize(10)
26 | .text(date, 70, startY, { align: "left" })
27 | .text(customers, 320, startY, { width: 90, align: "right" })
28 | }
29 |
30 | static generateTable(doc, customersHistoryResult: CustomersHistoryResult) : void {
31 |
32 | doc.font("Helvetica-Bold");
33 | this.generateTableRow(
34 | doc,
35 | "Date",
36 | "New customers count",
37 | );
38 | moveDown(doc)
39 | generateHr(doc);
40 | moveDown(doc)
41 | doc.font("Helvetica");
42 |
43 | let totalCurrent = 0;
44 | for (const currentCustomersHistoryResult of customersHistoryResult.current) {
45 | totalCurrent += Number(currentCustomersHistoryResult.customerCount);
46 | this.generateTableRow(
47 | doc,
48 | new Date(currentCustomersHistoryResult.date).toLocaleDateString(),
49 | currentCustomersHistoryResult.customerCount
50 | );
51 |
52 | moveDown(doc)
53 | generateHr(doc);
54 | moveDown(doc)
55 | }
56 |
57 | this.generateTableRow(
58 | doc,
59 | "Total",
60 | totalCurrent
61 | );
62 | moveDown(doc)
63 | }
64 |
65 | static generateHeader(doc) : void {
66 | doc
67 | .fontSize(18)
68 | moveDown(doc)
69 |
70 | doc
71 | .text('New customers', 70, doc.y, { align: "left"})
72 |
73 | doc
74 | .fontSize(12)
75 | moveDown(doc)
76 | }
77 | }
--------------------------------------------------------------------------------
/v1/src/services/pdf-templates/hr.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export function generateHr(doc) {
14 | doc
15 | .strokeColor("#aaaaaa")
16 | .lineWidth(1)
17 | .moveTo(70, doc.y)
18 | .lineTo(550, doc.y)
19 | .stroke();
20 | }
21 |
--------------------------------------------------------------------------------
/v1/src/services/pdf-templates/orders-template.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { generateHr } from "./hr";
14 | import { OrdersHistoryResult } from "../utils/types";
15 | import { moveDown } from "./common";
16 |
17 | export default class PdfOrdersTemplate {
18 | private static generateTableRow(
19 | doc,
20 | date,
21 | orders
22 | ) {
23 | const startY = doc.y;
24 | doc
25 | .fontSize(10)
26 | .text(date, 70, startY, { align: "left" })
27 | .text(orders, 320, startY, { width: 90, align: "right" })
28 | }
29 |
30 | static generateTable(doc, ordersHistoryResult: OrdersHistoryResult) : void {
31 |
32 | doc.font("Helvetica-Bold");
33 | this.generateTableRow(
34 | doc,
35 | "Date",
36 | "Orders count",
37 | );
38 | moveDown(doc)
39 | generateHr(doc);
40 | moveDown(doc)
41 | doc.font("Helvetica");
42 |
43 | let totalCurrent = 0;
44 | for (const currentOrdersHistoryResult of ordersHistoryResult.current) {
45 | totalCurrent += Number(currentOrdersHistoryResult.orderCount);
46 | this.generateTableRow(
47 | doc,
48 | new Date(currentOrdersHistoryResult.date).toLocaleDateString(),
49 | currentOrdersHistoryResult.orderCount
50 | );
51 |
52 | moveDown(doc)
53 | generateHr(doc);
54 | moveDown(doc)
55 | }
56 |
57 | this.generateTableRow(
58 | doc,
59 | "Total",
60 | totalCurrent
61 | );
62 | moveDown(doc)
63 | }
64 |
65 | static generateHeader(doc) : void {
66 | doc
67 | .fontSize(18)
68 | moveDown(doc)
69 |
70 | doc
71 | .text('Orders', 70, doc.y, { align: "left"})
72 |
73 | doc
74 | .fontSize(12)
75 | moveDown(doc)
76 | }
77 | }
--------------------------------------------------------------------------------
/v1/src/services/pdf-templates/products-template.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { VariantsCountPopularityResult } from "../productsAnalytics";
14 | import { moveDown } from "./common";
15 | import { generateHr } from "./hr";
16 |
17 | export default class PdfProductsTemplate {
18 | private static generateTableRow(
19 | doc,
20 | product,
21 | variant,
22 | sum,
23 | ) {
24 | const startY = doc.y;
25 | doc
26 | .fontSize(10)
27 | .text(product, 70, startY, { align: "left" })
28 | .text(variant, 200, startY, { align: "left" })
29 | .text(sum, 320, startY, { width: 90, align: "right" })
30 | }
31 |
32 | static generateTable(doc, variantsCountPopularityResult: VariantsCountPopularityResult) : void {
33 |
34 | doc.font("Helvetica-Bold");
35 | this.generateTableRow(
36 | doc,
37 | "Product",
38 | "Variant",
39 | "Sum",
40 | );
41 | moveDown(doc)
42 | generateHr(doc);
43 | moveDown(doc)
44 | doc.font("Helvetica");
45 |
46 | let totalCurrent = 0;
47 | for (const currentResults of variantsCountPopularityResult.current) {
48 | totalCurrent += Number(currentResults.sum);
49 | this.generateTableRow(
50 | doc,
51 | currentResults.productTitle,
52 | currentResults.variantTitle,
53 | currentResults.sum
54 | );
55 |
56 | moveDown(doc)
57 | generateHr(doc);
58 | moveDown(doc)
59 | }
60 |
61 | this.generateTableRow(
62 | doc,
63 | "Total",
64 | "",
65 | totalCurrent
66 | );
67 | moveDown(doc)
68 | }
69 |
70 | static generateHeader(doc) : void {
71 | doc
72 | .fontSize(18)
73 | moveDown(doc)
74 |
75 | doc
76 | .text('Top products', 70, doc.y, { align: "left"})
77 |
78 | doc
79 | .fontSize(12)
80 | moveDown(doc)
81 | }
82 | }
--------------------------------------------------------------------------------
/v1/src/services/pdf-templates/sales-template.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Region } from "@medusajs/medusa";
14 | import { SalesHistoryResult } from "../salesAnalytics";
15 | import { generateHr } from "./hr";
16 | import { amountToDisplay } from "../utils/currency";
17 | import { moveDown } from "./common";
18 |
19 | export default class PdfSalesTemplate {
20 | private static generateTableRow(
21 | doc,
22 | date,
23 | currencyCode,
24 | sales
25 | ) {
26 | const startY = doc.y;
27 | doc
28 | .fontSize(10)
29 | .text(date, 70, startY, { align: "left" })
30 | .text(currencyCode, 200, startY, { align: "left" })
31 | .text(sales, 320, startY, { width: 90, align: "right" })
32 | }
33 |
34 | static generateTableTitle(doc, region: Region) {
35 | doc
36 | .fontSize(14)
37 | moveDown(doc)
38 |
39 | doc
40 | .text(`${region.name}`, 70, doc.y, { align: "left"})
41 |
42 | doc
43 | .fontSize(12)
44 | moveDown(doc)
45 | }
46 |
47 | static generateTable(doc, salesHistoryResult: SalesHistoryResult) : void {
48 |
49 | doc.font("Helvetica-Bold");
50 | this.generateTableRow(
51 | doc,
52 | "Date",
53 | "Currency code",
54 | "Sales",
55 | );
56 | moveDown(doc)
57 | generateHr(doc);
58 | moveDown(doc)
59 | doc.font("Helvetica");
60 |
61 | let totalCurrent = 0;
62 | for (const currentSalesResult of salesHistoryResult.current) {
63 | totalCurrent += Number(currentSalesResult.total);
64 | this.generateTableRow(
65 | doc,
66 | currentSalesResult.date.toLocaleDateString(),
67 | salesHistoryResult.currencyCode.toUpperCase(),
68 | amountToDisplay(Number(currentSalesResult.total), salesHistoryResult.currencyCode)
69 | );
70 |
71 | moveDown(doc)
72 | generateHr(doc);
73 | moveDown(doc)
74 | }
75 |
76 | this.generateTableRow(
77 | doc,
78 | "Total",
79 | "",
80 | amountToDisplay(totalCurrent, salesHistoryResult.currencyCode)
81 | );
82 | moveDown(doc)
83 | }
84 |
85 | static generateHeader(doc) : void {
86 | doc
87 | .fontSize(18)
88 | moveDown(doc)
89 |
90 |
91 | doc
92 | .text('Sales by region', 70, doc.y, { align: "left"})
93 |
94 | doc
95 | .fontSize(12)
96 | moveDown(doc)
97 | }
98 | }
--------------------------------------------------------------------------------
/v1/src/services/utils/currency.ts:
--------------------------------------------------------------------------------
1 | import { defaultCurrencies } from "@medusajs/utils";
2 |
3 | const DEFAULT_DECIMAL_DIGITS = 2;
4 |
5 | export function getDecimalDigits(currencyCode: string): number {
6 | try {
7 | const decimalDigits = defaultCurrencies[currencyCode.toUpperCase()] !== undefined ? defaultCurrencies[currencyCode.toUpperCase()].decimal_digits : undefined;
8 | if (decimalDigits !== undefined) {
9 | return decimalDigits;
10 | }
11 | } catch {
12 | return DEFAULT_DECIMAL_DIGITS;
13 | }
14 | return DEFAULT_DECIMAL_DIGITS;
15 | }
16 |
17 | export function amountToDisplay(amount: number, currencyCode: string) : string {
18 | const decimalDigits = getDecimalDigits(currencyCode);
19 | return `${(amount / Math.pow(10, decimalDigits)).toFixed(decimalDigits)} ${currencyCode.toUpperCase()}`;
20 | }
--------------------------------------------------------------------------------
/v1/src/services/utils/dateTransformations.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export enum DateResolutionType {
14 | Day = 'day',
15 | Month = 'month'
16 | }
17 |
18 | export function calculateResolution(date?: Date) : DateResolutionType | undefined {
19 | if (!date) return undefined;
20 |
21 | const weekAgoTruncated = new Date(new Date(Date.now() - 604800000).setHours(0,0,0,0));
22 | if (date.getTime() >= weekAgoTruncated.getTime()) {
23 | return DateResolutionType.Day;
24 | }
25 |
26 | const monthAgoTruncated = new Date(new Date(new Date().setMonth(new Date().getMonth() - 1)).setHours(0,0,0,0));
27 | if (date.getTime() >= monthAgoTruncated.getTime()) {
28 | return DateResolutionType.Day;
29 | }
30 |
31 | const yearAgoTruncated = new Date(new Date(new Date().setFullYear(new Date().getFullYear() - 1)).setHours(0,0,0,0));
32 | if (date.getTime() > yearAgoTruncated.getTime()) {
33 | return DateResolutionType.Month;
34 | }
35 | return DateResolutionType.Month
36 | }
37 |
38 | export function getTruncateFunction(dateResolution: DateResolutionType) : (date: Date) => Date {
39 | if (dateResolution == DateResolutionType.Day) {
40 | return (date: Date) => new Date(new Date(date).setHours(0,0,0,0));
41 | } else {
42 | return (date: Date) => new Date(new Date(new Date(date).setDate(0)).setHours(0,0,0,0))
43 | }
44 | }
--------------------------------------------------------------------------------
/v1/src/services/utils/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | type OrdersHistory = {
14 | orderCount: string,
15 | date: string
16 | }
17 |
18 | export type OrdersHistoryResult = {
19 | dateRangeFrom?: number
20 | dateRangeTo?: number,
21 | dateRangeFromCompareTo?: number,
22 | dateRangeToCompareTo?: number,
23 | current: OrdersHistory[];
24 | previous: OrdersHistory[];
25 | }
--------------------------------------------------------------------------------
/v1/src/subscribers/README.md:
--------------------------------------------------------------------------------
1 | # Custom subscribers
2 |
3 | You may define custom eventhandlers, `subscribers` by creating files in the `/subscribers` directory.
4 |
5 | ```ts
6 | import MyCustomService from "../services/my-custom";
7 | import {
8 | OrderService,
9 | SubscriberArgs,
10 | SubscriberConfig,
11 | } from "@medusajs/medusa";
12 |
13 | type OrderPlacedEvent = {
14 | id: string;
15 | no_notification: boolean;
16 | };
17 |
18 | export default async function orderPlacedHandler({
19 | data,
20 | eventName,
21 | container,
22 | }: SubscriberArgs) {
23 | const orderService: OrderService = container.resolve(OrderService);
24 |
25 | const order = await orderService.retrieve(data.id, {
26 | relations: ["items", "items.variant", "items.variant.product"],
27 | });
28 |
29 | // Do something with the order
30 | }
31 |
32 | export const config: SubscriberConfig = {
33 | event: OrderService.Events.PLACED,
34 | };
35 | ```
36 |
37 | A subscriber is defined in two parts a `handler` and a `config`. The `handler` is a function which is invoked when an event is emitted. The `config` is an object which defines which event(s) the subscriber should subscribe to.
38 |
39 | The `handler` is a function which takes one parameter, an `object` of type `SubscriberArgs` with the following properties:
40 |
41 | - `data` - an `object` of type `T` containing information about the event.
42 | - `eventName` - a `string` containing the name of the event.
43 | - `container` - a `MedusaContainer` instance which can be used to resolve services.
44 | - `pluginOptions` - an `object` containing plugin options, if the subscriber is defined in a plugin.
45 |
--------------------------------------------------------------------------------
/v1/src/ui-components/common/icon-comparison.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { ArrowUpMini, ArrowDownMini, MinusMini } from "@medusajs/icons"
14 |
15 | export const IconComparison = ({current, previous, switchArrow} : {current: number, previous?: number, switchArrow?: boolean}) => {
16 | if (current == previous) {
17 | return
18 | }
19 | if (current > previous) {
20 | return
21 | }
22 | if (current < previous) {
23 | return
24 | }
25 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/common/percentage-comparison.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Tooltip } from "@medusajs/ui";
14 | import { calculatePercentage } from "../utils/helpers";
15 |
16 | export const PercentageComparison = ({current, label, previous, headingLevel = "h2" } : {current: string, label: string, previous: string, headingLevel?: any}) => {
17 | const percentage: number | undefined = calculatePercentage(parseInt(current), parseInt(previous));
18 | return (
19 |
20 |
21 |
22 | {percentage !== undefined ? `${percentage}%` : `N/A`}
23 |
24 |
25 |
26 | )
27 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/common/utils/chartUtils.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export enum ChartResolutionType {
14 | DayMonth,
15 | Month
16 | }
17 |
18 | export function calculateResolution(fromDate?: Date, toDate?: Date) : ChartResolutionType {
19 | if (!fromDate) return undefined;
20 |
21 | const calculateToDate = toDate ? new Date(toDate) : new Date(Date.now());
22 | const diffTime = calculateToDate.getTime() - fromDate.getTime();
23 |
24 | const weekTime = 604800000;
25 | const monthTime = weekTime * 4;
26 | const twoMonthsTime = monthTime * 2;
27 | if (diffTime <= twoMonthsTime) {
28 | return ChartResolutionType.DayMonth;
29 | }
30 |
31 | const yearTime = monthTime * 12;
32 | if (diffTime < yearTime) {
33 | return ChartResolutionType.Month;
34 | }
35 | return ChartResolutionType.Month
36 | }
37 |
38 | export const compareDatesBasedOnResolutionType = (date1: Date, date2: Date, resolutionType: ChartResolutionType): boolean => {
39 | switch (resolutionType) {
40 | case ChartResolutionType.DayMonth:
41 | return new Date(new Date(date1).setHours(0,0,0,0)).getTime() == new Date(new Date(date2).setHours(0,0,0,0)).getTime();
42 | case ChartResolutionType.Month:
43 | return new Date(new Date(new Date(date1).setDate(0)).setHours(0,0,0,0)).getTime() == new Date(new Date(new Date(date2).setDate(0)).setHours(0,0,0,0)).getTime();
44 | default:
45 | return new Date(new Date(date1).setHours(0,0,0,0)).getTime() == new Date(new Date(date2).setHours(0,0,0,0)).getTime();
46 | }
47 | }
48 |
49 | export const getChartDateName = (date: Date, resolutionType: ChartResolutionType, startDate: Date, endDate: Date): string => {
50 |
51 | switch (resolutionType) {
52 | case ChartResolutionType.DayMonth:
53 | if (compareDatesBasedOnResolutionType(date, startDate, resolutionType) || compareDatesBasedOnResolutionType(date, endDate, resolutionType)) {
54 | return `${date.getDate().toString()} ${getShortMonthName(date)}`;
55 | }
56 | return date.getDate().toString();
57 | case ChartResolutionType.Month:
58 | if (compareDatesBasedOnResolutionType(date, startDate, resolutionType) || compareDatesBasedOnResolutionType(date, endDate, resolutionType)) {
59 | return `${getShortMonthName(date)} ${date.getFullYear().toString()}`;
60 | }
61 | return getShortMonthName(date);
62 | default:
63 | return date.getFullYear().toString()
64 | }
65 | }
66 |
67 | export const getChartTooltipDate = (date: Date, resolutionType: ChartResolutionType): string => {
68 | switch (resolutionType) {
69 | case ChartResolutionType.DayMonth:
70 | return `${date.getDate().toString()}-${getShortMonthName(date)}`;
71 | case ChartResolutionType.Month:
72 | return `${getShortMonthName(date)}-${date.getFullYear()}`;
73 | default:
74 | return date.getFullYear().toString()
75 | }
76 | }
77 |
78 | export const getLegendName = (current: boolean): string => {
79 | return current ? `Current` : `Preceding`;
80 | }
81 |
82 | const getShortMonthName = (date: Date) => {
83 | let days = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
84 | return days[date.getMonth()];
85 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/customers/cumulative-history/cumulative-customers-card.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Users } from "@medusajs/icons";
15 | import { Grid } from "@mui/material";
16 | import type { DateRange } from "../../utils/types";
17 | import { CumulativeCustomersChart } from "./cumulative-customers-chart";
18 |
19 | export const CumulativeCustomersCard = ({dateRange, dateRangeCompareTo, compareEnabled} :
20 | {dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Cumulative customers
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/customers/customers-number-overview.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Alert } from "@medusajs/ui";
14 | import { CircularProgress, Grid } from "@mui/material";
15 | import { useAdminCustomQuery } from "medusa-react"
16 | import type { DateRange } from "../utils/types";
17 | import { PercentageComparison } from "../common/percentage-comparison";
18 | import { IconComparison } from "../common/icon-comparison";
19 |
20 | type AdminCustomersStatisticsQuery = {
21 | dateRangeFrom: number
22 | dateRangeTo: number,
23 | dateRangeFromCompareTo?: number,
24 | dateRangeToCompareTo?: number,
25 | }
26 |
27 | export type CustomersCountResponse = {
28 | analytics: {
29 | dateRangeFrom: number
30 | dateRangeTo: number,
31 | dateRangeFromCompareTo?: number,
32 | dateRangeToCompareTo?: number,
33 | current: string,
34 | previous: string
35 | }
36 | }
37 |
38 | export const CustomersNumber = ({dateRange, dateRangeCompareTo, compareEnabled} : {dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled?: boolean}) => {
39 | const { data, isLoading, isError, error } = useAdminCustomQuery<
40 | AdminCustomersStatisticsQuery,
41 | CustomersCountResponse
42 | >(
43 | `/customers-analytics/count`,
44 | [dateRange, dateRangeCompareTo],
45 | {
46 | dateRangeFrom: dateRange ? dateRange.from.getTime() : undefined,
47 | dateRangeTo: dateRange ? dateRange.to.getTime() : undefined,
48 | dateRangeFromCompareTo: dateRangeCompareTo ? dateRangeCompareTo.from.getTime() : undefined,
49 | dateRangeToCompareTo: dateRangeCompareTo ? dateRangeCompareTo.to.getTime() : undefined
50 | }
51 | )
52 |
53 | if (isLoading) {
54 | return
55 | }
56 |
57 | if (isError) {
58 | const trueError = error as any;
59 | const errorText = `Error when loading data. It shouldn't have happened - please raise an issue. For developer: ${trueError?.response?.data?.message}`
60 | return {errorText}
61 | }
62 |
63 | if (data.analytics == undefined) {
64 | return Cannot get customers
65 | }
66 | return (
67 |
68 |
69 |
70 | {data.analytics.current}
71 |
72 |
73 | {compareEnabled && dateRangeCompareTo &&
74 |
75 |
76 |
77 |
78 |
79 | {data.analytics.previous !== undefined &&
80 |
81 | }
82 |
83 |
84 | }
85 |
86 | );
87 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/customers/customers-overview-card.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Users } from "@medusajs/icons";
15 | import { Grid } from "@mui/material";
16 | import type { DateRange } from "../utils/types";
17 | import { CustomersNumber } from "./customers-number-overview";
18 | import { CustomersByNewChart } from "./customers-by-new-chart";
19 |
20 | export const CustomersOverviewCard = ({dateRange, dateRangeCompareTo, compareEnabled} :
21 | {dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | New customers
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | )
44 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/customers/repeat-customer-rate/customers-repeat-customer-rate-number.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { PercentageComparison } from "../../common/percentage-comparison";
16 | import { IconComparison } from "../../common/icon-comparison";
17 | import { CustomersRepeatCustomerRateResponse } from "../types"
18 |
19 | export const RepeatCustomerRateNummber = ({repeatCustomerRateResponse, compareEnabled} : {repeatCustomerRateResponse: CustomersRepeatCustomerRateResponse, compareEnabled?: boolean}) => {
20 | const currentPercentage: number | undefined =
21 | repeatCustomerRateResponse.analytics.current !== undefined && repeatCustomerRateResponse.analytics.current.returnCustomerRate !== undefined ?
22 | parseInt(repeatCustomerRateResponse.analytics.current.returnCustomerRate) : undefined;
23 | const previousPercentage: number | undefined =
24 | repeatCustomerRateResponse.analytics.previous !== undefined && repeatCustomerRateResponse.analytics.previous.returnCustomerRate !== undefined ?
25 | parseInt(repeatCustomerRateResponse.analytics.previous.returnCustomerRate) : undefined;
26 |
27 | return (
28 |
29 |
30 | {currentPercentage !== undefined ?
31 |
32 | {`${currentPercentage}%`}
33 | :
34 |
35 | {`No orders or customers`}
36 |
37 | }
38 |
39 | {compareEnabled && repeatCustomerRateResponse.analytics.dateRangeFromCompareTo && currentPercentage !== undefined &&
40 |
41 |
42 |
43 |
44 |
45 | {previousPercentage !== undefined &&
46 |
47 | }
48 |
49 |
50 | }
51 |
52 | );
53 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/customers/repeat-customer-rate/order-frequency-distribution-chart.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { calculateResolution, getLegendName } from "../../common/utils/chartUtils";
14 | import { CustomersRepeatCustomerRateResponse, Distributions } from "../types"
15 | import { Legend, Pie, PieChart, Tooltip } from "recharts";
16 |
17 | const ONE_TIME_LABEL_NAME = 'One-time purchase';
18 | const REPEAT_LABEL_NAME = 'Repeat purchase';
19 |
20 | function convertToChartData(distributions: Distributions) {
21 | if (distributions) {
22 | if (distributions.orderOneTimeFrequency || distributions.orderRepeatFrequency) {
23 | const oneTimeValue = distributions.orderOneTimeFrequency ? parseInt(distributions.orderOneTimeFrequency) : 0;
24 | const repeatValue = distributions.orderRepeatFrequency ? parseInt(distributions.orderRepeatFrequency) : 0;
25 | return [
26 | {
27 | name: ONE_TIME_LABEL_NAME,
28 | value: oneTimeValue,
29 | displayValue: ONE_TIME_LABEL_NAME
30 | },
31 | {
32 | name: REPEAT_LABEL_NAME,
33 | value: repeatValue,
34 | displayValue: REPEAT_LABEL_NAME
35 | }
36 | ]
37 | }
38 | }
39 | return undefined;
40 | }
41 |
42 | export const OrderFrequencyDistributionPieChart = ({repeatCustomerRateResponse, compareEnabled} : {repeatCustomerRateResponse: CustomersRepeatCustomerRateResponse, compareEnabled?: boolean}) => {
43 |
44 | const currentData = convertToChartData(repeatCustomerRateResponse.analytics.current);
45 | const previousData = convertToChartData(repeatCustomerRateResponse.analytics.previous);
46 |
47 | const renderLabel = function(entry) {
48 | return entry.displayValue;
49 | }
50 |
51 | return (
52 |
53 |
54 | {compareEnabled && repeatCustomerRateResponse.analytics.dateRangeFromCompareTo && currentData !== undefined &&
55 |
56 | }
57 | {(compareEnabled && repeatCustomerRateResponse.analytics.dateRangeFromCompareTo) && }
67 | `${value}%`}/>
68 |
69 | );
70 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/customers/repeat-customer-rate/order-frequency-distribution.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { CustomersRepeatCustomerRateResponse } from "../types"
16 | import { OrderFrequencyDistributionPieChart } from "./order-frequency-distribution-chart";
17 |
18 | export const OrderFrequencyDistribution = ({repeatCustomerRateResponse, compareEnabled} : {repeatCustomerRateResponse: CustomersRepeatCustomerRateResponse, compareEnabled?: boolean}) => {
19 | return (
20 |
21 |
22 |
23 | How orders were distributed?
24 |
25 |
26 |
27 |
28 |
29 |
30 | )
31 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/customers/retention-customer-rate/customers-retention-customer-rate-number.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { PercentageComparison } from "../../common/percentage-comparison";
16 | import { IconComparison } from "../../common/icon-comparison";
17 | import { CustomersRetentionCustomerRateResponse } from "../types"
18 |
19 | export const RetentionCustomerRateNumber = ({retentionCustomerRateResponse, compareEnabled} : {retentionCustomerRateResponse: CustomersRetentionCustomerRateResponse, compareEnabled?: boolean}) => {
20 | const currentPercentage: number | undefined =
21 | retentionCustomerRateResponse.analytics.current !== undefined ?
22 | parseInt(retentionCustomerRateResponse.analytics.current) : undefined;
23 | const previousPercentage: number | undefined =
24 | retentionCustomerRateResponse.analytics.previous !== undefined ?
25 | parseInt(retentionCustomerRateResponse.analytics.previous) : undefined;
26 |
27 | return (
28 |
29 |
30 | {currentPercentage !== undefined ?
31 |
32 | {`${currentPercentage}%`}
33 | :
34 |
35 | {`No orders or customers`}
36 |
37 | }
38 |
39 | {compareEnabled && retentionCustomerRateResponse.analytics.dateRangeFromCompareTo && currentPercentage !== undefined &&
40 |
41 |
42 |
43 |
44 |
45 | {previousPercentage !== undefined &&
46 |
47 | }
48 |
49 |
50 | }
51 |
52 | );
53 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/customers/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export type Distributions = {
14 | returnCustomerRate: string,
15 | orderOneTimeFrequency?: string,
16 | orderRepeatFrequency?: string
17 | }
18 |
19 | export type CustomersRepeatCustomerRateResponse = {
20 | analytics: {
21 | dateRangeFrom: number
22 | dateRangeTo: number,
23 | dateRangeFromCompareTo?: number,
24 | dateRangeToCompareTo?: number,
25 | current: Distributions,
26 | previous: Distributions
27 | }
28 | }
29 |
30 | export type CustomersRetentionCustomerRateResponse = {
31 | analytics: {
32 | dateRangeFrom: number
33 | dateRangeTo: number,
34 | dateRangeFromCompareTo?: number,
35 | dateRangeToCompareTo?: number,
36 | current?: string,
37 | previous?: string
38 | }
39 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export { ComparedDate, SwitchComparison, DropdownOrderStatus, SelectDateLasts, GenerateReportButton } from './common/overview-components';
14 |
15 | export { OrdersOverviewCard } from './orders/orders-overview-card'
16 | export { OrdersPaymentProviderCard } from './orders/orders-payment-provider-card'
17 |
18 | export { SalesOverviewCard } from './sales/sales-overview-card'
19 | export { SalesChannelPopularityCard } from './sales/sales-channel-popularity-card'
20 | export { RefundsOverviewCard } from './sales/refunds/refunds-overview-card'
21 | export { RegionsPopularityCard } from './sales/regions-popularity-card'
22 |
23 | export { CustomersOverviewCard } from './customers/customers-overview-card'
24 | export { CustomersRepeatCustomerRate } from './customers/repeat-customer-rate/customers-repeat-customer-rate';
25 | export { CustomersRetentionCustomerRate } from './customers/retention-customer-rate/customers-retention-customer-rate';
26 | export { CumulativeCustomersCard } from './customers/cumulative-history/cumulative-customers-card';
27 |
28 | export { VariantsTopByCountCard } from './products/variants-top-by-count';
29 | export { ReturnedVariantsByCountCard } from './products/returned_variants/returned-variants-by-count';
30 | export { ProductsSoldCountCard } from './products/products-sold-count';
31 | export { OutOfTheStockVariantsCard } from './products/out_of_the_stock_variants/out-of-the-stock-variants-by-count';
32 |
33 | export { DiscountsTopCard } from './marketing/discounts-top-by-count';
34 |
35 | export { DateLasts, OrderStatus } from './utils/types'
36 | export type { DateRange } from './utils/types'
37 | export { convertDateLastsToComparedDateRange, convertDateLastsToDateRange, amountToDisplay, calculatePercentage } from './utils/helpers'
--------------------------------------------------------------------------------
/v1/src/ui-components/marketing/discounts-top-table.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Text } from "@medusajs/ui";
14 | import { Divider, Grid } from "@mui/material";
15 |
16 | export type DiscountsTopTableRow = {
17 | sum: string,
18 | discountCode: string
19 | }
20 |
21 | export const DiscountsTopTable = ({tableRows} : {tableRows: DiscountsTopTableRow[]}) => {
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Discount
32 |
33 |
34 |
35 |
36 | Count
37 |
38 |
39 |
40 |
41 | {tableRows.length > 0 ? tableRows.map(tableRow => (
42 |
43 |
44 |
45 |
46 | {tableRow.discountCode}
47 |
48 |
49 |
50 |
51 | {tableRow.sum}
52 |
53 |
54 |
55 |
56 | )) :
57 |
58 |
59 |
60 |
61 | None
62 |
63 |
64 |
65 |
66 | None
67 |
68 |
69 |
70 |
71 | }
72 |
73 | )
74 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/orders/orders-overview-card.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { ShoppingCart } from "@medusajs/icons";
15 | import { Grid } from "@mui/material";
16 | import { OrdersByNewChart } from "./orders-by-new-chart";
17 | import type { DateRange } from "../utils/types";
18 | import { OrdersNumber } from "./orders-number-overview";
19 | import { OrderStatus } from "../utils/types";
20 |
21 | export const OrdersOverviewCard = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
22 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
23 | return (
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Orders
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | )
45 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/orders/orders-payment-provider-chart.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { calculateResolution, getLegendName } from "../common/utils/chartUtils";
14 | import { Legend, Pie, PieChart, Tooltip } from "recharts";
15 | import { OrdersPaymentProvider, OrdersPaymentProviderResponse } from "./types";
16 | import { Text, Container } from "@medusajs/ui";
17 |
18 | function convertToChartData(ordersPaymentProviders: OrdersPaymentProvider[]) {
19 | if (ordersPaymentProviders.length) {
20 | return ordersPaymentProviders.map(ordersPaymentProvider => {
21 | return {
22 | name: ordersPaymentProvider.paymentProviderId,
23 | value: parseFloat(ordersPaymentProvider.percentage),
24 | displayValue: ordersPaymentProvider.paymentProviderId,
25 | orderCount: ordersPaymentProvider.orderCount,
26 | }
27 | })
28 | }
29 | return undefined;
30 | }
31 | const ChartCustomTooltip = ({ active, payload, label }) => {
32 | if (active && payload && payload.length) {
33 | return (
34 |
35 | {`${payload[0].payload.value}%`}
36 | {`Provider: ${payload[0].payload.name}`}
37 | {`Order count: ${payload[0].payload.orderCount}`}
38 |
39 | )
40 | }
41 | return null;
42 | };
43 |
44 | export const OrdersPaymentProviderPieChart = ({ordersPaymentProviderResponse, compareEnabled} : {ordersPaymentProviderResponse: OrdersPaymentProviderResponse, compareEnabled?: boolean}) => {
45 |
46 | const currentData = convertToChartData(ordersPaymentProviderResponse.analytics.current);
47 | const previousData = convertToChartData(ordersPaymentProviderResponse.analytics.previous);
48 |
49 | const renderLabel = function(entry) {
50 | return entry.displayValue;
51 | }
52 |
53 | return (
54 |
55 |
56 | {compareEnabled && ordersPaymentProviderResponse.analytics.dateRangeFromCompareTo &&
57 |
58 | }
59 | {(compareEnabled && ordersPaymentProviderResponse.analytics.dateRangeFromCompareTo) && }
69 | } />
70 |
71 | );
72 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/orders/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 |
14 | export type OrdersPaymentProvider = {
15 | orderCount: string,
16 | percentage: string,
17 | paymentProviderId: string
18 | }
19 |
20 | export type OrdersPaymentProviderPopularityResult = {
21 | dateRangeFrom?: number
22 | dateRangeTo?: number,
23 | dateRangeFromCompareTo?: number,
24 | dateRangeToCompareTo?: number,
25 | current: OrdersPaymentProvider[]
26 | previous: OrdersPaymentProvider[]
27 | }
28 |
29 | export type OrdersPaymentProviderResponse = {
30 | analytics: OrdersPaymentProviderPopularityResult
31 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/products/out_of_the_stock_variants/helpers.tsx:
--------------------------------------------------------------------------------
1 | export type AdminOutOfTheStockVariantsStatisticsQuery = {}
2 |
3 | export type OutOfTheStockVariantsCount = {
4 | productId: string,
5 | variantId: string,
6 | productTitle: string,
7 | variantTitle: string,
8 | thumbnail: string,
9 | }
10 |
11 | export type OutOfTheStockVariantsCountResult = {
12 | dateRangeFrom?: number
13 | dateRangeTo?: number,
14 | dateRangeFromCompareTo?: number,
15 | dateRangeToCompareTo?: number,
16 | current: OutOfTheStockVariantsCount[],
17 | }
18 |
19 | export type OutOfTheStockVariantsCountResponse = {
20 | analytics: OutOfTheStockVariantsCountResult
21 | }
22 | export type OutOfTheStockVariantsTableRow = {
23 | variantId: string,
24 | productId: string,
25 | productTitle: string,
26 | variantTitle: string,
27 | thumbnail: string,
28 | }
29 |
30 | export function transformToVariantTopTable(result: OutOfTheStockVariantsCountResult): OutOfTheStockVariantsTableRow[] {
31 | const currentMap = new Map();
32 |
33 | result.current.forEach(currentItem => {
34 | currentMap.set(currentItem.variantId, {
35 | variantId: currentItem.variantId,
36 | productId: currentItem.productId,
37 | productTitle: currentItem.productTitle,
38 | variantTitle: currentItem.variantTitle,
39 | thumbnail: currentItem.thumbnail,
40 | });
41 | });
42 |
43 | return Array.from(currentMap.values());
44 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/products/out_of_the_stock_variants/out-of-the-stock-variants-by-count.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Alert, Tooltip, Badge } from "@medusajs/ui";
14 | import { ArrowRightOnRectangle, InformationCircle } from "@medusajs/icons";
15 | import { CircularProgress, Grid } from "@mui/material";
16 | import { useAdminCustomQuery } from "medusa-react"
17 | import { OutOfTheStockVariantsTable } from "./out-of-the-stock-variants-table";
18 | import { OutOfTheStockVariantsModal } from "./out-of-the-stock-variants-all";
19 | import { AdminOutOfTheStockVariantsStatisticsQuery, OutOfTheStockVariantsCountResponse, transformToVariantTopTable } from "./helpers";
20 |
21 | const OutOfTheStockVariants = () => {
22 | const { data, isError, isLoading, error } = useAdminCustomQuery<
23 | AdminOutOfTheStockVariantsStatisticsQuery,
24 | OutOfTheStockVariantsCountResponse
25 | >(
26 | `/products-analytics/out-of-the-stock-variants`,
27 | [],
28 | {
29 | limit: 5
30 | }
31 | )
32 |
33 | if (isLoading) {
34 | return
35 | }
36 |
37 | if (isError) {
38 | const trueError = error as any;
39 | const errorText = `Error when loading data. It shouldn't have happened - please raise an issue. For developer: ${trueError?.response?.data?.message}`
40 | return {errorText}
41 | }
42 |
43 | if (data.analytics == undefined) {
44 | return Cannot get variants
45 | }
46 |
47 | return
48 | }
49 |
50 | export const OutOfTheStockVariantsCard = () => {
51 | return (
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Out of the stock variants
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | Last 5 variants
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | )
87 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/products/out_of_the_stock_variants/out-of-the-stock-variants-table.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Text } from "@medusajs/ui";
14 | import { Box, Divider, Grid } from "@mui/material";
15 | import { Link } from "react-router-dom"
16 | import { OutOfTheStockVariantsTableRow } from "./helpers";
17 |
18 | export const OutOfTheStockVariantsTable = ({tableRows} : {tableRows: OutOfTheStockVariantsTableRow[]}) => {
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Variant
29 |
30 |
31 |
32 |
33 | {tableRows.length > 0 ? tableRows.map(tableRow => (
34 |
35 |
36 |
37 |
38 |
39 | {tableRow.thumbnail &&
40 |
49 | }
50 |
51 | {tableRow.productTitle} - {tableRow.variantTitle}
52 |
53 |
54 |
55 |
56 |
57 |
58 | )) :
59 |
60 |
61 |
62 |
63 | None
64 |
65 |
66 |
67 |
68 | }
69 |
70 | )
71 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/products/returned_variants/returned-variants-table.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Text } from "@medusajs/ui";
14 | import { Box, Divider, Grid } from "@mui/material";
15 | import { Link } from "react-router-dom"
16 |
17 | export type VariantsTopTableRow = {
18 | sum: string,
19 | productId: string,
20 | productTitle: string,
21 | variantTitle: string,
22 | thumbnail: string,
23 | }
24 |
25 | export const ReturnedVariantsTable = ({tableRows} : {tableRows: VariantsTopTableRow[]}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Variant
36 |
37 |
38 |
39 |
40 | Count
41 |
42 |
43 |
44 |
45 | {tableRows.length > 0 ? tableRows.map(tableRow => (
46 |
47 |
48 |
49 |
50 |
51 | {tableRow.thumbnail &&
52 |
61 | }
62 |
63 | {tableRow.productTitle} - {tableRow.variantTitle}
64 |
65 |
66 |
67 |
68 |
69 |
70 | {tableRow.sum}
71 |
72 |
73 |
74 |
75 | )) :
76 |
77 |
78 |
79 |
80 | None
81 |
82 |
83 |
84 |
85 | None
86 |
87 |
88 |
89 |
90 | }
91 |
92 | )
93 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/products/variants-top-table.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Text } from "@medusajs/ui";
14 | import { Box, Divider, Grid } from "@mui/material";
15 | import { Link } from "react-router-dom"
16 |
17 | export type VariantsTopTableRow = {
18 | sum: string,
19 | productId: string,
20 | productTitle: string,
21 | variantTitle: string,
22 | thumbnail: string,
23 | }
24 |
25 | export const VariantsTopTable = ({tableRows} : {tableRows: VariantsTopTableRow[]}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Variant
36 |
37 |
38 |
39 |
40 | Count
41 |
42 |
43 |
44 |
45 | {tableRows.length > 0 ? tableRows.map(tableRow => (
46 |
47 |
48 |
49 |
50 |
51 | {tableRow.thumbnail &&
52 |
61 | }
62 |
63 | {tableRow.productTitle} - {tableRow.variantTitle}
64 |
65 |
66 |
67 |
68 |
69 |
70 | {tableRow.sum}
71 |
72 |
73 |
74 |
75 | )) :
76 |
77 |
78 |
79 |
80 | None
81 |
82 |
83 |
84 |
85 | None
86 |
87 |
88 |
89 |
90 | }
91 |
92 | )
93 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/sales/refunds/refunds-numbers.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { PercentageComparison } from "../../common/percentage-comparison";
16 | import { IconComparison } from "../../common/icon-comparison";
17 | import { RefundsResponse } from "../types";
18 | import { amountToDisplay } from "../../utils/helpers";
19 |
20 | export const RefundsNumber = ({refundsResponse, compareEnabled} : {refundsResponse: RefundsResponse, compareEnabled?: boolean}) => {
21 | const overallCurrentSum: number = parseInt(refundsResponse.analytics.current);
22 | const overallPreviousSum: number | undefined = refundsResponse.analytics.previous !== undefined ?
23 | parseInt(refundsResponse.analytics.previous) : undefined;
24 |
25 | return (
26 |
27 |
28 |
29 | {amountToDisplay(overallCurrentSum, refundsResponse.analytics.currencyDecimalDigits)} {refundsResponse.analytics.currencyCode.toUpperCase()}
30 |
31 |
32 | {compareEnabled && refundsResponse.analytics.dateRangeFromCompareTo &&
33 |
34 |
35 |
36 |
37 |
38 | {overallPreviousSum !== undefined &&
39 |
40 | }
41 |
42 |
43 | }
44 |
45 | );
46 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/sales/sales-number-overview.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { PercentageComparison } from "../common/percentage-comparison";
16 | import { IconComparison } from "../common/icon-comparison";
17 | import { SalesHistoryResponse } from "./types";
18 | import { amountToDisplay } from "../utils/helpers";
19 |
20 | export const SalesNumber = ({salesHistoryResponse, compareEnabled} : {salesHistoryResponse: SalesHistoryResponse, compareEnabled?: boolean}) => {
21 | const overallCurrentSum: number = salesHistoryResponse.analytics.current.reduce((sum, order) => sum + parseInt(order.total), 0);
22 | const overallPreviousSum: number | undefined = salesHistoryResponse.analytics.previous.length > 0 ?
23 | salesHistoryResponse.analytics.previous.reduce((sum, order) => sum + parseInt(order.total), 0) :
24 | undefined;
25 |
26 | return (
27 |
28 |
29 |
30 | {amountToDisplay(overallCurrentSum, salesHistoryResponse.analytics.currencyDecimalDigits)} {salesHistoryResponse.analytics.currencyCode.toUpperCase()}
31 |
32 |
33 | {compareEnabled && salesHistoryResponse.analytics.dateRangeFromCompareTo &&
34 |
35 |
36 |
37 |
38 |
39 | {overallPreviousSum !== undefined &&
40 |
41 | }
42 |
43 |
44 | }
45 |
46 | );
47 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/sales/sales-total-chart.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { ChartCurrentPrevious } from "../common/chart-components";
15 | import { SalesHistoryResponse } from "./types";
16 | import { amountToDisplay } from "../utils/helpers";
17 |
18 | export const SalesByNewChart = ({salesHistoryResponse, compareEnabled} : {salesHistoryResponse: SalesHistoryResponse, compareEnabled?: boolean}) => {
19 | const rawChartData = {
20 | current: salesHistoryResponse.analytics.current.map(currentData => {
21 | return {
22 | date: new Date(currentData.date),
23 | value: amountToDisplay(parseInt(currentData.total), salesHistoryResponse.analytics.currencyDecimalDigits)
24 | };
25 | }),
26 | previous: salesHistoryResponse.analytics.previous.map(previousData => {
27 | return {
28 | date: new Date(previousData.date),
29 | value: amountToDisplay(parseInt(previousData.total), salesHistoryResponse.analytics.currencyDecimalDigits)
30 | };
31 | }),
32 | };
33 | return (
34 | <>
35 | Sales by time
36 |
44 | >
45 | )
46 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/sales/types.ts:
--------------------------------------------------------------------------------
1 | export type SalesHistory = {
2 | total: string,
3 | date: string
4 | }
5 |
6 | export type SalesHistoryResponse = {
7 | analytics: {
8 | dateRangeFrom?: number
9 | dateRangeTo?: number,
10 | dateRangeFromCompareTo?: number,
11 | dateRangeToCompareTo?: number,
12 | currencyCode: string,
13 | currencyDecimalDigits: number,
14 | current: SalesHistory[];
15 | previous: SalesHistory[];
16 | }
17 | }
18 |
19 | export type RefundsResponse = {
20 | analytics: {
21 | dateRangeFrom?: number
22 | dateRangeTo?: number,
23 | dateRangeFromCompareTo?: number,
24 | dateRangeToCompareTo?: number,
25 | currencyCode: string
26 | currencyDecimalDigits: number,
27 | current: string;
28 | previous: string;
29 | }
30 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/tabs/customers.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Container } from "@medusajs/ui"
14 | import {
15 | CustomersOverviewCard,
16 | CustomersRepeatCustomerRate,
17 | CumulativeCustomersCard,
18 | CustomersRetentionCustomerRate,
19 | OrderStatus,
20 | } from '..';
21 | import type { DateRange } from '..';
22 | import { Grid } from "@mui/material";
23 |
24 | const CustomersTab = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
25 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default CustomersTab
--------------------------------------------------------------------------------
/v1/src/ui-components/tabs/orders.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Container } from "@medusajs/ui"
14 | import {
15 | OrdersOverviewCard,
16 | OrderStatus,
17 | } from '..';
18 | import type { DateRange } from '..';
19 | import { Grid } from "@mui/material";
20 | import { OrdersPaymentProviderCard } from "../orders/orders-payment-provider-card";
21 |
22 | const OrdersTab = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
23 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | )
38 | }
39 |
40 | export default OrdersTab
--------------------------------------------------------------------------------
/v1/src/ui-components/tabs/products.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Container } from "@medusajs/ui"
14 | import {
15 | ProductsSoldCountCard,
16 | VariantsTopByCountCard,
17 | ReturnedVariantsByCountCard,
18 | OrderStatus,
19 | } from '..';
20 | import type { DateRange } from '..';
21 | import { Grid } from "@mui/material";
22 | import { OutOfTheStockVariantsCard } from "../products/out_of_the_stock_variants/out-of-the-stock-variants-by-count";
23 |
24 | const ProductsTab = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
25 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default ProductsTab
--------------------------------------------------------------------------------
/v1/src/ui-components/tabs/sales.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Container } from "@medusajs/ui"
14 | import {
15 | DiscountsTopCard,
16 | SalesChannelPopularityCard,
17 | OrderStatus,
18 | SalesOverviewCard,
19 | RefundsOverviewCard
20 | } from '..';
21 | import type { DateRange } from '..';
22 | import { Grid } from "@mui/material";
23 |
24 | const SalesTab = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
25 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default SalesTab
--------------------------------------------------------------------------------
/v1/src/ui-components/utils/helpers.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { DateLasts } from "./types";
14 | import type { DateRange } from "./types"
15 |
16 | export function amountToDisplay(amount: number, decimalDigits: number) : string {
17 | return (amount / Math.pow(10, decimalDigits)).toFixed(decimalDigits);
18 | }
19 |
20 | export function calculatePercentage(current: number, previous: number) : number | undefined {
21 | if (current == previous) {
22 | return 0;
23 | }
24 | if (current == 0) {
25 | return 100;
26 | }
27 |
28 | if (previous == 0) {
29 | return undefined;
30 | }
31 |
32 | const percentage: number = Number((((current) - previous) / previous).toFixed(2)) * 100;
33 | if (percentage > 0) {
34 | return Math.round(percentage * 100) / 100;
35 | }
36 | return Math.round((percentage - percentage - percentage) * 100) / 100;
37 | }
38 |
39 | export function convertDateLastsToDateRange(dateLasts: DateLasts): DateRange | undefined {
40 | let result: DateRange | undefined;
41 | switch (dateLasts) {
42 | case DateLasts.LastMonth:
43 | result = {
44 | // 86400000 - alignment for taking last 29 days, as the current day is 30
45 | from: new Date(new Date(new Date().setDate(new Date().getDate() - 29)).setHours(0,0,0,0)),
46 | to: new Date(Date.now())
47 | }
48 | break;
49 | case DateLasts.LastWeek:
50 | result = {
51 | // 86400000 - alignment for taking last 6 days, as the current day is 7th
52 | from: new Date(new Date(new Date(Date.now() - 604800000 + 86400000)).setHours(0,0,0,0)),
53 | to: new Date(Date.now())
54 | }
55 | break;
56 | case DateLasts.LastYear:
57 | const lastYearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
58 | result = {
59 | // + 1 - alignment for taking last 11 months, as the current month is 12th
60 | from: new Date(new Date(new Date().setDate(new Date().getDate() - 364)).setHours(0,0,0,0)),
61 | to: new Date(Date.now())
62 | }
63 | break;
64 | }
65 | return result;
66 | }
67 |
68 | export function convertDateLastsToComparedDateRange(dateLasts: DateLasts): DateRange | undefined {
69 | let result: DateRange | undefined;
70 | switch (dateLasts) {
71 | case DateLasts.LastMonth:
72 | result = {
73 | from: new Date(new Date(new Date().setDate(new Date().getDate() - 59)).setHours(0,0,0,0)),
74 | to: new Date(new Date(new Date().setDate(new Date().getDate() - 29)).setHours(0,0,0,0)),
75 | }
76 | break;
77 | case DateLasts.LastWeek:
78 | result = {
79 | from: new Date(new Date(Date.now() - 604800000 * 2 + 86400000).setHours(0,0,0,0)),
80 | to: new Date(new Date(Date.now() - 604800000 + 86400000).setHours(0,0,0,0)),
81 | }
82 | break;
83 | case DateLasts.LastYear:
84 | result = {
85 | from: new Date(new Date(new Date().setDate(new Date().getDate() - 729)).setHours(0,0,0,0)),
86 | to: new Date(new Date(new Date().setDate(new Date().getDate() - 364)).setHours(0,0,0,0)),
87 | }
88 | break;
89 | }
90 | return result;
91 | }
--------------------------------------------------------------------------------
/v1/src/ui-components/utils/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export enum OrderStatus {
14 | /**
15 | * The order is pending.
16 | */
17 | PENDING = "pending",
18 | /**
19 | * The order is completed, meaning that
20 | * the items have been fulfilled and the payment
21 | * has been captured.
22 | */
23 | COMPLETED = "completed",
24 | /**
25 | * The order is archived.
26 | */
27 | ARCHIVED = "archived",
28 | /**
29 | * The order is canceled.
30 | */
31 | CANCELED = "canceled",
32 | /**
33 | * The order requires action.
34 | */
35 | REQUIRES_ACTION = "requires_action"
36 | }
37 |
38 | export enum DateLasts {
39 | All = "All time",
40 | LastMonth = "Last 30 days",
41 | LastWeek = "Last 7 days",
42 | LastYear = "Last 365 days"
43 | }
44 | export type DateRange = {
45 | from: Date,
46 | to: Date
47 | }
--------------------------------------------------------------------------------
/v1/tsconfig.admin.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "esnext"
5 | },
6 | "include": ["src/admin"],
7 | "exclude": ["**/*.spec.js"]
8 | }
--------------------------------------------------------------------------------
/v1/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2019",
4 | "module": "commonjs",
5 | "allowJs": true,
6 | "checkJs": false,
7 | "jsx": "react-jsx",
8 | "declaration": true,
9 | "outDir": "./dist",
10 | "rootDir": "./src",
11 | "experimentalDecorators": true,
12 | "emitDecoratorMetadata": true,
13 | "noEmit": false,
14 | "strict": false,
15 | "moduleResolution": "node",
16 | "esModuleInterop": true,
17 | "resolveJsonModule": true,
18 | "skipLibCheck": true,
19 | "forceConsistentCasingInFileNames": true
20 | },
21 | "include": ["src/"],
22 | "exclude": [
23 | "dist",
24 | "build",
25 | ".cache",
26 | "tests",
27 | "**/*.spec.js",
28 | "**/*.spec.ts",
29 | "node_modules",
30 | ".eslintrc.js"
31 | ]
32 | }
--------------------------------------------------------------------------------
/v1/tsconfig.server.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | /* Emit a single file with source maps instead of having a separate file. */
5 | "inlineSourceMap": true
6 | },
7 | "exclude": ["src/admin", "**/*.spec.js"]
8 | }
--------------------------------------------------------------------------------
/v1/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": ["src"],
4 | "exclude": ["dist", "node_modules"]
5 | }
6 |
--------------------------------------------------------------------------------
/v2/docs/medusa-store-analytics-1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RSC-Labs/medusa-store-analytics/4d04707866d80087d9b48f57a99a0efed85e0e1e/v2/docs/medusa-store-analytics-1.PNG
--------------------------------------------------------------------------------
/v2/docs/medusa-store-analytics-2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RSC-Labs/medusa-store-analytics/4d04707866d80087d9b48f57a99a0efed85e0e1e/v2/docs/medusa-store-analytics-2.PNG
--------------------------------------------------------------------------------
/v2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@rsc-labs/medusa-store-analytics-v2",
3 | "version": "0.1.4",
4 | "description": "Get analytics data about your store",
5 | "author": "RSC Labs (https://rsoftcon.com)",
6 | "license": "MIT",
7 | "files": [
8 | ".medusa/server"
9 | ],
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/RSC-Labs/medusa-store-analytics"
13 | },
14 | "keywords": [
15 | "medusa-plugin",
16 | "medusa-v2",
17 | "medusa-plugin-analytics",
18 | "analytics",
19 | "statistics",
20 | "store"
21 | ],
22 | "scripts": {
23 | "build": "medusa plugin:build",
24 | "dev": "medusa plugin:develop",
25 | "prepublishOnly": "medusa plugin:build"
26 | },
27 | "dependencies": {
28 | "recharts": "^2.10.3",
29 | "pdfkit": "^0.15.0",
30 | "@mui/material": "^5.15.3",
31 | "react-hook-form": "^7.49.2",
32 | "@emotion/react": "^11.11.3",
33 | "@emotion/styled": "11.13.0"
34 | },
35 | "devDependencies": {
36 | "@medusajs/admin-sdk": "^2.7.1",
37 | "@medusajs/cli": "^2.7.1",
38 | "@medusajs/framework": "^2.7.1",
39 | "@medusajs/medusa": "^2.7.1",
40 | "@medusajs/test-utils": "^2.7.1",
41 | "@medusajs/ui": "4.0.3",
42 | "@medusajs/icons": "^2.7.1",
43 | "@mikro-orm/cli": "6.4.3",
44 | "@mikro-orm/core": "6.4.3",
45 | "@mikro-orm/knex": "6.4.3",
46 | "@mikro-orm/migrations": "6.4.3",
47 | "@mikro-orm/postgresql": "6.4.3",
48 | "@swc/core": "1.5.7",
49 | "@types/node": "^20.0.0",
50 | "@types/react": "^18.3.2",
51 | "@types/react-dom": "^18.2.25",
52 | "awilix": "^8.0.1",
53 | "pg": "^8.13.0",
54 | "prop-types": "^15.8.1",
55 | "react": "^18.2.0",
56 | "react-dom": "^18.2.0",
57 | "ts-node": "^10.9.2",
58 | "typescript": "^5.6.2",
59 | "vite": "^5.2.11",
60 | "yalc": "^1.0.0-pre.53"
61 | },
62 | "peerDependencies": {
63 | "@medusajs/admin-sdk": "^2.7.1",
64 | "@medusajs/cli": "^2.7.1",
65 | "@medusajs/framework": "^2.7.1",
66 | "@medusajs/test-utils": "^2.7.1",
67 | "@medusajs/medusa": "^2.7.1",
68 | "@medusajs/ui": "4.0.3",
69 | "@medusajs/icons": "^2.7.1",
70 | "@mikro-orm/cli": "6.4.3",
71 | "@mikro-orm/core": "6.4.3",
72 | "@mikro-orm/knex": "6.4.3",
73 | "@mikro-orm/migrations": "6.4.3",
74 | "@mikro-orm/postgresql": "6.4.3",
75 | "awilix": "^8.0.1",
76 | "pg": "^8.13.0"
77 | },
78 | "engines": {
79 | "node": ">=20"
80 | },
81 | "exports": {
82 | "./package.json": "./package.json",
83 | "./workflows": "./.medusa/server/src/workflows/index.js",
84 | "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
85 | "./modules/*": "./.medusa/server/src/modules/*/index.js",
86 | "./providers/*": "./.medusa/server/src/providers/*/index.js",
87 | "./*": "./.medusa/server/src/*.js",
88 | "./admin": {
89 | "import": "./.medusa/server/src/admin/index.mjs",
90 | "require": "./.medusa/server/src/admin/index.js",
91 | "default": "./.medusa/server/src/admin/index.js"
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/v2/src/api/admin/helpers-analytics/pro-check/route.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import type {
14 | MedusaRequest,
15 | MedusaResponse,
16 | } from "@medusajs/framework/http"
17 | import { MedusaError, MedusaErrorTypes } from "@medusajs/utils"
18 | import { STORE_ANALYTICS_MODULE } from "../../../../modules/store-analytics";
19 | import StoreAnalyticsModuleService from "../../../../modules/store-analytics/service";
20 |
21 | export const GET = async (
22 | req: MedusaRequest,
23 | res: MedusaResponse
24 | ) => {
25 |
26 | const storeAnalyticsModuleService: StoreAnalyticsModuleService = req.scope.resolve(STORE_ANALYTICS_MODULE)
27 | try {
28 | res.status(200).json({
29 | hideProTab: storeAnalyticsModuleService.getHideProSetting()
30 | });
31 | } catch (error) {
32 | throw new MedusaError(
33 | MedusaErrorTypes.DB_ERROR,
34 | error.message
35 | )
36 | }
37 | }
--------------------------------------------------------------------------------
/v2/src/api/admin/marketing-analytics/[kind]/route.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import type {
14 | MedusaRequest,
15 | MedusaResponse,
16 | } from "@medusajs/framework/http"
17 | import { MedusaError, MedusaErrorTypes, OrderStatus } from "@medusajs/utils"
18 | import { STORE_ANALYTICS_MODULE } from "../../../../modules/store-analytics";
19 | import StoreAnalyticsModuleService from "../../../../modules/store-analytics/service";
20 |
21 | export const GET = async (
22 | req: MedusaRequest,
23 | res: MedusaResponse
24 | ) => {
25 |
26 | const kind = req.params.kind;
27 | const dateRangeFrom = req.query.dateRangeFrom;
28 | const dateRangeTo = req.query.dateRangeTo;
29 | const orderStatusesFromQuery: string[] = req.query.orderStatuses as string[];
30 |
31 | const orderStatuses: OrderStatus[] = orderStatusesFromQuery !== undefined ?
32 | orderStatusesFromQuery.map(status => OrderStatus[status.toUpperCase()]).filter(orderStatus => orderStatus !== undefined): [];
33 |
34 | let result;
35 | const storeAnalyticsModuleService: StoreAnalyticsModuleService = req.scope.resolve(STORE_ANALYTICS_MODULE)
36 |
37 | try {
38 | switch (kind) {
39 | case 'discounts-by-count':
40 | result = await storeAnalyticsModuleService.getMarketingTopDiscounts(
41 | orderStatuses,
42 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
43 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
44 | );
45 | break;
46 | }
47 | res.status(200).json({
48 | analytics: result
49 | });
50 | } catch (error) {
51 | throw new MedusaError(
52 | MedusaErrorTypes.DB_ERROR,
53 | error.message
54 | )
55 | }
56 | }
--------------------------------------------------------------------------------
/v2/src/api/admin/orders-analytics/[kind]/route.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import type {
14 | MedusaRequest,
15 | MedusaResponse,
16 | } from "@medusajs/framework/http"
17 | import { MedusaError, MedusaErrorTypes, OrderStatus } from "@medusajs/utils"
18 | import { STORE_ANALYTICS_MODULE } from "../../../../modules/store-analytics";
19 | import StoreAnalyticsModuleService from "../../../../modules/store-analytics/service";
20 |
21 | export const GET = async (
22 | req: MedusaRequest,
23 | res: MedusaResponse
24 | ) => {
25 |
26 | const kind = req.params.kind;
27 | const dateRangeFrom = req.query.dateRangeFrom;
28 | const dateRangeTo = req.query.dateRangeTo;
29 | const dateRangeFromCompareTo = req.query.dateRangeFromCompareTo;
30 | const dateRangeToCompareTo = req.query.dateRangeToCompareTo;
31 | const orderStatusesFromQuery: string[] = req.query.orderStatuses as string[];
32 |
33 | const orderStatuses: OrderStatus[] = orderStatusesFromQuery !== undefined ?
34 | orderStatusesFromQuery.map(status => OrderStatus[status.toUpperCase()]).filter(orderStatus => orderStatus !== undefined): [];
35 |
36 | let result;
37 | const storeAnalyticsModuleService: StoreAnalyticsModuleService = req.scope.resolve(STORE_ANALYTICS_MODULE)
38 |
39 | try {
40 | switch (kind) {
41 | case 'history':
42 | result = await storeAnalyticsModuleService.getOrdersHistory(
43 | orderStatuses,
44 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
45 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
46 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
47 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
48 | );
49 | break;
50 | case 'count':
51 | result = await storeAnalyticsModuleService.getOrdersCount(
52 | orderStatuses,
53 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
54 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
55 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
56 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
57 | );
58 | break;
59 | case 'payment-provider':
60 | result = await storeAnalyticsModuleService.getOrdersPaymentProviderPopularity(
61 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
62 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
63 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
64 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
65 | );
66 | break;
67 | }
68 | res.status(200).json({
69 | analytics: result
70 | });
71 | } catch (error) {
72 | throw new MedusaError(
73 | MedusaErrorTypes.DB_ERROR,
74 | error.message
75 | )
76 | }
77 | }
--------------------------------------------------------------------------------
/v2/src/api/admin/products-analytics/[kind]/route.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import type {
14 | MedusaRequest,
15 | MedusaResponse,
16 | } from "@medusajs/framework/http"
17 | import { MedusaError, MedusaErrorTypes, OrderStatus } from "@medusajs/utils"
18 | import { STORE_ANALYTICS_MODULE } from "../../../../modules/store-analytics";
19 | import StoreAnalyticsModuleService from "../../../../modules/store-analytics/service";
20 |
21 | export const GET = async (
22 | req: MedusaRequest,
23 | res: MedusaResponse
24 | ) => {
25 |
26 | const kind = req.params.kind;
27 | const dateRangeFrom = req.query.dateRangeFrom;
28 | const dateRangeTo = req.query.dateRangeTo;
29 | const dateRangeFromCompareTo = req.query.dateRangeFromCompareTo;
30 | const dateRangeToCompareTo = req.query.dateRangeToCompareTo;
31 | const orderStatusesFromQuery: string[] = req.query.orderStatuses as string[];
32 |
33 | const orderStatuses: OrderStatus[] = orderStatusesFromQuery !== undefined ?
34 | orderStatusesFromQuery.map(status => OrderStatus[status.toUpperCase()]).filter(orderStatus => orderStatus !== undefined): [];
35 |
36 | let result;
37 | const storeAnalyticsModuleService: StoreAnalyticsModuleService = req.scope.resolve(STORE_ANALYTICS_MODULE)
38 |
39 | try {
40 | switch (kind) {
41 | case 'popularity-by-count':
42 | result = await storeAnalyticsModuleService.getProductsTopVariantsByCount(
43 | orderStatuses,
44 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
45 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
46 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
47 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
48 | );
49 | break;
50 | case 'returned-by-count':
51 | result = await storeAnalyticsModuleService.getProductsTopReturnedVariantsByCount(
52 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
53 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
54 | );
55 | break;
56 | case 'sold-count':
57 | result = await storeAnalyticsModuleService.getProductsSoldCount(
58 | orderStatuses,
59 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
60 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
61 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
62 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
63 | );
64 | break;
65 | case 'out-of-the-stock-variants':
66 | const limit = req.query.limit as string;
67 | result = await storeAnalyticsModuleService.getProductsOutOfTheStockVariants(limit ? parseInt(limit) : undefined);
68 | break;
69 | }
70 | res.status(200).json({
71 | analytics: result
72 | });
73 | } catch (error) {
74 | throw new MedusaError(
75 | MedusaErrorTypes.DB_ERROR,
76 | error.message
77 | )
78 | }
79 | }
--------------------------------------------------------------------------------
/v2/src/api/admin/reports-analytics/[kind]/route.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 |
14 | import { MedusaError, MedusaErrorTypes, Modules, OrderStatus } from "@medusajs/utils"
15 | import { MedusaRequest, MedusaResponse } from "@medusajs/framework";
16 | import StoreAnalyticsModuleService from "../../../../modules/store-analytics/service";
17 | import { STORE_ANALYTICS_MODULE } from "../../../../modules/store-analytics";
18 |
19 | export const POST = async (
20 | req: MedusaRequest,
21 | res: MedusaResponse
22 | ) => {
23 |
24 | const rawRequest = req as unknown as any;
25 |
26 | const kind = req.params.kind;
27 | const body: any = rawRequest.body as any;
28 | const dateRangeFrom = body.dateRangeFrom;
29 | const dateRangeTo = body.dateRangeTo;
30 | const dateRangeFromCompareTo = body.dateRangeFromCompareTo;
31 | const dateRangeToCompareTo = body.dateRangeToCompareTo;
32 | const orderStatusesFromQuery: string[] = body.orderStatuses as string[];
33 |
34 | const orderStatuses: OrderStatus[] = orderStatusesFromQuery !== undefined ?
35 | orderStatusesFromQuery.map(status => OrderStatus[status.toUpperCase()]).filter(orderStatus => orderStatus !== undefined): [];
36 |
37 | let result: Buffer | undefined;
38 | const storeAnalyticsModuleService: StoreAnalyticsModuleService = req.scope.resolve(STORE_ANALYTICS_MODULE)
39 |
40 | try {
41 | switch (kind) {
42 | case 'general':
43 | const regionModuleService = req.scope.resolve(
44 | Modules.REGION
45 | )
46 | const regions = await regionModuleService.listRegions();
47 | result = await storeAnalyticsModuleService.generateReport(
48 | regions,
49 | orderStatuses,
50 | dateRangeFrom ? new Date(Number(dateRangeFrom)) : undefined,
51 | dateRangeTo ? new Date(Number(dateRangeTo)) : undefined,
52 | dateRangeFromCompareTo ? new Date(Number(dateRangeFromCompareTo)) : undefined,
53 | dateRangeToCompareTo ? new Date(Number(dateRangeToCompareTo)) : undefined,
54 | );
55 | break;
56 | }
57 | res.status(201).json({
58 | buffer: result
59 | });
60 | } catch (error) {
61 | throw new MedusaError(
62 | MedusaErrorTypes.DB_ERROR,
63 | error.message
64 | )
65 | }
66 | }
--------------------------------------------------------------------------------
/v2/src/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export * from './ui-components'
--------------------------------------------------------------------------------
/v2/src/jobs/README.md:
--------------------------------------------------------------------------------
1 | # Custom scheduled jobs
2 |
3 | You may define custom scheduled jobs (cron jobs) by creating files in the `/jobs` directory.
4 |
5 | ```ts
6 | import {
7 | ProductService,
8 | ScheduledJobArgs,
9 | ScheduledJobConfig,
10 | } from "@medusajs/medusa";
11 |
12 | export default async function myCustomJob({ container }: ScheduledJobArgs) {
13 | const productService: ProductService = container.resolve("productService");
14 |
15 | const products = await productService.listAndCount();
16 |
17 | // Do something with the products
18 | }
19 |
20 | export const config: ScheduledJobConfig = {
21 | name: "daily-product-report",
22 | schedule: "0 0 * * *", // Every day at midnight
23 | };
24 | ```
25 |
26 | A scheduled job is defined in two parts a `handler` and a `config`. The `handler` is a function which is invoked when the job is scheduled. The `config` is an object which defines the name of the job, the schedule, and an optional data object.
27 |
28 | The `handler` is a function which takes one parameter, an `object` of type `ScheduledJobArgs` with the following properties:
29 |
30 | - `container` - a `MedusaContainer` instance which can be used to resolve services.
31 | - `data` - an `object` containing data passed to the job when it was scheduled. This object is passed in the `config` object.
32 | - `pluginOptions` - an `object` containing plugin options, if the job is defined in a plugin.
33 |
--------------------------------------------------------------------------------
/v2/src/loaders/README.md:
--------------------------------------------------------------------------------
1 | # Custom loader
2 |
3 | The loader allows you have access to the Medusa service container. This allows you to access the database and the services registered on the container.
4 | you can register custom registrations in the container or run custom code on startup.
5 |
6 | ```ts
7 | // src/loaders/my-loader.ts
8 |
9 | import { AwilixContainer } from 'awilix'
10 |
11 | /**
12 | *
13 | * @param container The container in which the registrations are made
14 | * @param config The options of the plugin or the entire config object
15 | */
16 | export default (container: AwilixContainer, config: Record): void | Promise => {
17 | /* Implement your own loader. */
18 | }
19 | ```
--------------------------------------------------------------------------------
/v2/src/migrations/README.md:
--------------------------------------------------------------------------------
1 | # Custom migrations
2 |
3 | You may define custom models (entities) that will be registered on the global container by creating files in the `src/models` directory that export an instance of `BaseEntity`.
4 | In that case you also need to provide a migration in order to create the table in the database.
5 |
6 | ## Example
7 |
8 | ### 1. Create the migration
9 |
10 | See [How to Create Migrations](https://docs.medusajs.com/advanced/backend/migrations/) in the documentation.
11 |
12 | ```ts
13 | // src/migration/my-migration.ts
14 |
15 | import { MigrationInterface, QueryRunner } from "typeorm"
16 |
17 | export class MyMigration1617703530229 implements MigrationInterface {
18 | name = "myMigration1617703530229"
19 |
20 | public async up(queryRunner: QueryRunner): Promise {
21 | // write you migration here
22 | }
23 |
24 | public async down(queryRunner: QueryRunner): Promise {
25 | // write you migration here
26 | }
27 | }
28 |
29 | ```
--------------------------------------------------------------------------------
/v2/src/models/README.md:
--------------------------------------------------------------------------------
1 | # Custom models
2 |
3 | You may define custom models (entities) that will be registered on the global container by creating files in the `src/models` directory that export an instance of `BaseEntity`.
4 |
5 | ## Example
6 |
7 | ### 1. Create the Entity
8 |
9 | ```ts
10 | // src/models/post.ts
11 |
12 | import { BeforeInsert, Column, Entity, PrimaryColumn } from "typeorm";
13 | import { generateEntityId } from "@medusajs/utils";
14 | import { BaseEntity } from "@medusajs/medusa";
15 |
16 | @Entity()
17 | export class Post extends BaseEntity {
18 | @Column({type: 'varchar'})
19 | title: string | null;
20 |
21 | @BeforeInsert()
22 | private beforeInsert(): void {
23 | this.id = generateEntityId(this.id, "post")
24 | }
25 | }
26 | ```
27 |
28 | ### 2. Create the Migration
29 |
30 | You also need to create a Migration to create the new table in the database. See [How to Create Migrations](https://docs.medusajs.com/advanced/backend/migrations/) in the documentation.
31 |
32 | ### 3. Create a Repository
33 | Entities data can be easily accessed and modified using [TypeORM Repositories](https://typeorm.io/working-with-repository). To create a repository, create a file in `src/repositories`. For example, here’s a repository `PostRepository` for the `Post` entity:
34 |
35 | ```ts
36 | // src/repositories/post.ts
37 |
38 | import { EntityRepository, Repository } from "typeorm"
39 |
40 | import { Post } from "../models/post"
41 |
42 | @EntityRepository(Post)
43 | export class PostRepository extends Repository { }
44 | ```
45 |
46 | See more about defining and accesing your custom [Entities](https://docs.medusajs.com/advanced/backend/entities/overview) in the documentation.
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/index.ts:
--------------------------------------------------------------------------------
1 | import StoreAnalyticsModuleService from "./service"
2 | import { Module } from "@medusajs/framework/utils"
3 |
4 | export const STORE_ANALYTICS_MODULE = "storeAnalyticsModuleService"
5 |
6 | export default Module(STORE_ANALYTICS_MODULE, {
7 | service: StoreAnalyticsModuleService,
8 | })
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./customersAnalytics"
2 | export * from "./marketingAnalytics"
3 | export * from "./ordersAnalytics"
4 | export * from "./productsAnalytics"
5 | export * from "./reportsAnalytics"
6 | export * from "./salesAnalytics"
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/pdf-templates/common.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { OrderStatus } from "@medusajs/framework/utils";
14 |
15 |
16 | const Y_REACHED_TO_ADD_NEW_PAGE = '650';
17 |
18 | function addPageIfReachingEnd(doc) {
19 | if (doc.y > Y_REACHED_TO_ADD_NEW_PAGE) {
20 | doc.addPage();
21 | }
22 | }
23 |
24 | export function moveDown(doc) {
25 | addPageIfReachingEnd(doc);
26 | doc.moveDown();
27 | }
28 |
29 | export function generateReportHeader(doc, orderStatuses: OrderStatus[], from?: Date, to?: Date, dateRangeFromCompareTo?: Date, dateRangeToCompareTo?: Date) : void {
30 | doc
31 | .fillColor("#444444")
32 | .fontSize(20)
33 |
34 | doc
35 | .text('Store analytics report', { align: "center"})
36 |
37 | doc
38 | .fontSize(16)
39 | .moveDown()
40 |
41 | doc
42 | .text('for', { align: "center"})
43 | .moveDown();
44 |
45 | if (from) {
46 | doc
47 | .text(`${from.toDateString()} - ${to ? to.toDateString() : new Date(Date.now()).toDateString()}`, { align: "center"})
48 | .moveDown();
49 | } else {
50 | doc
51 | .text(`All time - ${to ? to.toDateString() : new Date(Date.now()).toDateString()}`, { align: "center"})
52 | .moveDown();
53 | }
54 | doc
55 | .fontSize(10)
56 | .text(`Filtered by order statuses:`, { align: "center"})
57 |
58 | orderStatuses.map(orderStatus => doc
59 | .text(orderStatus, { align: "center"})
60 | );
61 |
62 | doc
63 | .addPage()
64 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/pdf-templates/customers-template.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { CustomersHistoryResult } from "../customersAnalytics";
14 | import { moveDown } from "./common";
15 | import { generateHr } from "./hr";
16 |
17 | export default class PdfCustomersTemplate {
18 | private static generateTableRow(
19 | doc,
20 | date,
21 | customers
22 | ) {
23 | const startY = doc.y;
24 | doc
25 | .fontSize(10)
26 | .text(date, 70, startY, { align: "left" })
27 | .text(customers, 320, startY, { width: 90, align: "right" })
28 | }
29 |
30 | static generateTable(doc, customersHistoryResult: CustomersHistoryResult) : void {
31 |
32 | doc.font("Helvetica-Bold");
33 | this.generateTableRow(
34 | doc,
35 | "Date",
36 | "New customers count",
37 | );
38 | moveDown(doc)
39 | generateHr(doc);
40 | moveDown(doc)
41 | doc.font("Helvetica");
42 |
43 | let totalCurrent = 0;
44 | for (const currentCustomersHistoryResult of customersHistoryResult.current) {
45 | totalCurrent += Number(currentCustomersHistoryResult.customerCount);
46 | this.generateTableRow(
47 | doc,
48 | new Date(currentCustomersHistoryResult.date).toLocaleDateString(),
49 | currentCustomersHistoryResult.customerCount
50 | );
51 |
52 | moveDown(doc)
53 | generateHr(doc);
54 | moveDown(doc)
55 | }
56 |
57 | this.generateTableRow(
58 | doc,
59 | "Total",
60 | totalCurrent
61 | );
62 | moveDown(doc)
63 | }
64 |
65 | static generateHeader(doc) : void {
66 | doc
67 | .fontSize(18)
68 | moveDown(doc)
69 |
70 | doc
71 | .text('New customers', 70, doc.y, { align: "left"})
72 |
73 | doc
74 | .fontSize(12)
75 | moveDown(doc)
76 | }
77 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/pdf-templates/hr.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export function generateHr(doc) {
14 | doc
15 | .strokeColor("#aaaaaa")
16 | .lineWidth(1)
17 | .moveTo(70, doc.y)
18 | .lineTo(550, doc.y)
19 | .stroke();
20 | }
21 |
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/pdf-templates/orders-template.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { generateHr } from "./hr";
14 | import { OrdersHistoryResult } from "../utils/types";
15 | import { moveDown } from "./common";
16 |
17 | export default class PdfOrdersTemplate {
18 | private static generateTableRow(
19 | doc,
20 | date,
21 | orders
22 | ) {
23 | const startY = doc.y;
24 | doc
25 | .fontSize(10)
26 | .text(date, 70, startY, { align: "left" })
27 | .text(orders, 320, startY, { width: 90, align: "right" })
28 | }
29 |
30 | static generateTable(doc, ordersHistoryResult: OrdersHistoryResult) : void {
31 |
32 | doc.font("Helvetica-Bold");
33 | this.generateTableRow(
34 | doc,
35 | "Date",
36 | "Orders count",
37 | );
38 | moveDown(doc)
39 | generateHr(doc);
40 | moveDown(doc)
41 | doc.font("Helvetica");
42 |
43 | let totalCurrent = 0;
44 | for (const currentOrdersHistoryResult of ordersHistoryResult.current) {
45 | totalCurrent += Number(currentOrdersHistoryResult.orderCount);
46 | this.generateTableRow(
47 | doc,
48 | new Date(currentOrdersHistoryResult.date).toLocaleDateString(),
49 | currentOrdersHistoryResult.orderCount
50 | );
51 |
52 | moveDown(doc)
53 | generateHr(doc);
54 | moveDown(doc)
55 | }
56 |
57 | this.generateTableRow(
58 | doc,
59 | "Total",
60 | totalCurrent
61 | );
62 | moveDown(doc)
63 | }
64 |
65 | static generateHeader(doc) : void {
66 | doc
67 | .fontSize(18)
68 | moveDown(doc)
69 |
70 | doc
71 | .text('Orders', 70, doc.y, { align: "left"})
72 |
73 | doc
74 | .fontSize(12)
75 | moveDown(doc)
76 | }
77 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/pdf-templates/products-template.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { VariantsCountPopularityResult } from "./../productsAnalytics";
14 | import { moveDown } from "./common";
15 | import { generateHr } from "./hr";
16 |
17 | export default class PdfProductsTemplate {
18 | private static generateTableRow(
19 | doc,
20 | product,
21 | variant,
22 | sum,
23 | ) {
24 | const startY = doc.y;
25 | doc
26 | .fontSize(10)
27 | .text(product, 70, startY, { align: "left" })
28 | .text(variant, 200, startY, { align: "left" })
29 | .text(sum, 320, startY, { width: 90, align: "right" })
30 | }
31 |
32 | static generateTable(doc, variantsCountPopularityResult: VariantsCountPopularityResult) : void {
33 |
34 | doc.font("Helvetica-Bold");
35 | this.generateTableRow(
36 | doc,
37 | "Product",
38 | "Variant",
39 | "Sum",
40 | );
41 | moveDown(doc)
42 | generateHr(doc);
43 | moveDown(doc)
44 | doc.font("Helvetica");
45 |
46 | let totalCurrent = 0;
47 | for (const currentResults of variantsCountPopularityResult.current) {
48 | totalCurrent += Number(currentResults.sum);
49 | this.generateTableRow(
50 | doc,
51 | currentResults.productTitle,
52 | currentResults.variantTitle,
53 | currentResults.sum
54 | );
55 |
56 | moveDown(doc)
57 | generateHr(doc);
58 | moveDown(doc)
59 | }
60 |
61 | this.generateTableRow(
62 | doc,
63 | "Total",
64 | "",
65 | totalCurrent
66 | );
67 | moveDown(doc)
68 | }
69 |
70 | static generateHeader(doc) : void {
71 | doc
72 | .fontSize(18)
73 | moveDown(doc)
74 |
75 | doc
76 | .text('Top products', 70, doc.y, { align: "left"})
77 |
78 | doc
79 | .fontSize(12)
80 | moveDown(doc)
81 | }
82 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/pdf-templates/sales-template.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { SalesHistoryResult } from "./../salesAnalytics";
14 | import { generateHr } from "./hr";
15 | import { amountToDisplay } from "../utils/currency";
16 | import { moveDown } from "./common";
17 | import { RegionDTO } from "@medusajs/framework/types";
18 |
19 | export default class PdfSalesTemplate {
20 | private static generateTableRow(
21 | doc,
22 | date,
23 | currencyCode,
24 | sales
25 | ) {
26 | const startY = doc.y;
27 | doc
28 | .fontSize(10)
29 | .text(date, 70, startY, { align: "left" })
30 | .text(currencyCode, 200, startY, { align: "left" })
31 | .text(sales, 320, startY, { width: 90, align: "right" })
32 | }
33 |
34 | static generateTableTitle(doc, region: RegionDTO) {
35 | doc
36 | .fontSize(14)
37 | moveDown(doc)
38 |
39 | doc
40 | .text(`${region.name}`, 70, doc.y, { align: "left"})
41 |
42 | doc
43 | .fontSize(12)
44 | moveDown(doc)
45 | }
46 |
47 | static generateTable(doc, salesHistoryResult: SalesHistoryResult) : void {
48 |
49 | doc.font("Helvetica-Bold");
50 | this.generateTableRow(
51 | doc,
52 | "Date",
53 | "Currency code",
54 | "Sales",
55 | );
56 | moveDown(doc)
57 | generateHr(doc);
58 | moveDown(doc)
59 | doc.font("Helvetica");
60 |
61 | let totalCurrent = 0;
62 | for (const currentSalesResult of salesHistoryResult.current) {
63 | totalCurrent += Number(currentSalesResult.total);
64 | this.generateTableRow(
65 | doc,
66 | currentSalesResult.date.toLocaleDateString(),
67 | salesHistoryResult.currencyCode.toUpperCase(),
68 | amountToDisplay(Number(currentSalesResult.total), salesHistoryResult.currencyCode)
69 | );
70 |
71 | moveDown(doc)
72 | generateHr(doc);
73 | moveDown(doc)
74 | }
75 |
76 | this.generateTableRow(
77 | doc,
78 | "Total",
79 | "",
80 | amountToDisplay(totalCurrent, salesHistoryResult.currencyCode)
81 | );
82 | moveDown(doc)
83 | }
84 |
85 | static generateHeader(doc) : void {
86 | doc
87 | .fontSize(18)
88 | moveDown(doc)
89 |
90 |
91 | doc
92 | .text('Sales by region', 70, doc.y, { align: "left"})
93 |
94 | doc
95 | .fontSize(12)
96 | moveDown(doc)
97 | }
98 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/reportsAnalytics.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { OrderStatus } from "@medusajs/framework/utils";
14 | import { RegionDTO } from "@medusajs/framework/types";
15 | import { PgConnectionType } from "../utils/types"
16 |
17 | import PDFDocument from 'pdfkit';
18 | import PdfSalesTemplate from "./pdf-templates/sales-template"
19 | import PdfOrdersTemplate from "./pdf-templates/orders-template";
20 | import PdfCustomersTemplate from "./pdf-templates/customers-template";
21 | import { generateReportHeader } from "./pdf-templates/common";
22 | import PdfProductsTemplate from "./pdf-templates/products-template";
23 | import { OrdersHistoryResult } from "./ordersAnalytics";
24 | import { SalesHistoryResult } from "./salesAnalytics";
25 | import { CustomersHistoryResult } from "./customersAnalytics";
26 | import { VariantsCountPopularityResult } from "./productsAnalytics";
27 |
28 | type InjectedDependencies = {
29 | __pg_connection__: PgConnectionType,
30 | }
31 |
32 | type ReportInput = {
33 | ordersHistory: OrdersHistoryResult,
34 | salesHistories: SalesHistoryResult[],
35 | customersHistory: CustomersHistoryResult,
36 | variantsCountPopularityResult: VariantsCountPopularityResult,
37 | regions: RegionDTO[]
38 | }
39 |
40 | export class ReportsAnalyticsService {
41 |
42 | protected pgConnection: PgConnectionType;
43 |
44 | constructor({ __pg_connection__ }: InjectedDependencies) {
45 | this.pgConnection = __pg_connection__
46 | }
47 | async generateReport(orderStatuses: OrderStatus[], input: ReportInput, from?: Date, to?: Date, dateRangeFromCompareTo?: Date, dateRangeToCompareTo?: Date) {
48 | var doc = new PDFDocument();
49 |
50 | const buffers = []
51 | doc.on("data", buffers.push.bind(buffers))
52 |
53 | generateReportHeader(doc, orderStatuses, from, to, dateRangeFromCompareTo, dateRangeToCompareTo);
54 |
55 | // Orders
56 | PdfOrdersTemplate.generateHeader(doc);
57 | PdfOrdersTemplate.generateTable(doc, input.ordersHistory);
58 |
59 | // Sales
60 | PdfSalesTemplate.generateHeader(doc);
61 | for (const region of input.regions) {
62 | const salesHistory = input.salesHistories.find(salesHistory => salesHistory.currencyCode == region.currency_code);
63 | if (salesHistory) {
64 | PdfSalesTemplate.generateTableTitle(doc, region);
65 | PdfSalesTemplate.generateTable(doc, salesHistory);
66 | }
67 | }
68 |
69 | // Customers
70 | doc.addPage();
71 | PdfCustomersTemplate.generateHeader(doc);
72 | PdfCustomersTemplate.generateTable(doc, input.customersHistory);
73 |
74 | // Products
75 | doc.addPage();
76 | PdfProductsTemplate.generateHeader(doc);
77 | PdfProductsTemplate.generateTable(doc, input.variantsCountPopularityResult);
78 |
79 | doc.end();
80 |
81 | const bufferPromise = new Promise(resolve => {
82 | doc.on("end", () => {
83 | const pdfData = Buffer.concat(buffers)
84 | resolve(pdfData)
85 | })
86 | })
87 |
88 | return await bufferPromise;
89 | }
90 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/utils/currency.ts:
--------------------------------------------------------------------------------
1 | import { defaultCurrencies } from "@medusajs/utils";
2 |
3 | const DEFAULT_DECIMAL_DIGITS = 2;
4 |
5 | export function getDecimalDigits(currencyCode: string): number {
6 | try {
7 | const decimalDigits = defaultCurrencies[currencyCode.toUpperCase()] !== undefined ? defaultCurrencies[currencyCode.toUpperCase()].decimal_digits : undefined;
8 | if (decimalDigits !== undefined) {
9 | return decimalDigits;
10 | }
11 | } catch {
12 | return DEFAULT_DECIMAL_DIGITS;
13 | }
14 | return DEFAULT_DECIMAL_DIGITS;
15 | }
16 |
17 | export function amountToDisplay(amount: number, currencyCode: string) : string {
18 | const decimalDigits = getDecimalDigits(currencyCode);
19 | return `${(amount / Math.pow(10, decimalDigits)).toFixed(decimalDigits)} ${currencyCode.toUpperCase()}`;
20 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/services/utils/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | type OrdersHistory = {
14 | orderCount: string,
15 | date: string
16 | }
17 |
18 | export type OrdersHistoryResult = {
19 | dateRangeFrom?: number
20 | dateRangeTo?: number,
21 | dateRangeFromCompareTo?: number,
22 | dateRangeToCompareTo?: number,
23 | current: OrdersHistory[];
24 | previous: OrdersHistory[];
25 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/utils/currency.ts:
--------------------------------------------------------------------------------
1 | import { defaultCurrencies } from "@medusajs/utils";
2 |
3 | const DEFAULT_DECIMAL_DIGITS = 2;
4 |
5 | export function getDecimalDigits(currencyCode: string): number {
6 | try {
7 | const decimalDigits = defaultCurrencies[currencyCode.toUpperCase()] !== undefined ? defaultCurrencies[currencyCode.toUpperCase()].decimal_digits : undefined;
8 | if (decimalDigits !== undefined) {
9 | return decimalDigits;
10 | }
11 | } catch {
12 | return DEFAULT_DECIMAL_DIGITS;
13 | }
14 | return DEFAULT_DECIMAL_DIGITS;
15 | }
16 |
17 | export function amountToDisplay(amount: number, currencyCode: string) : string {
18 | const decimalDigits = getDecimalDigits(currencyCode);
19 | return `${(amount / Math.pow(10, decimalDigits)).toFixed(decimalDigits)} ${currencyCode.toUpperCase()}`;
20 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/utils/dateTransformations.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export enum DateResolutionType {
14 | Day = 'day',
15 | Month = 'month'
16 | }
17 |
18 | export function calculateResolution(date?: Date, toDate?: Date) : DateResolutionType {
19 | if (!date) return DateResolutionType.Month;
20 |
21 | const weekAgoTruncated = new Date(new Date(Date.now() - 604800000).setHours(0,0,0,0));
22 | const monthAgoTruncated = new Date(new Date(new Date().setMonth(new Date().getMonth() - 1)).setHours(0,0,0,0));
23 | const yearAgoTruncated = new Date(new Date(new Date().setFullYear(new Date().getFullYear() - 1)).setHours(0,0,0,0));
24 |
25 | if (toDate) {
26 | const diffTime = toDate.getTime() - date.getTime();
27 |
28 | const weekTime = 604800000;
29 | const monthTime = weekTime * 4;
30 | if (diffTime <= monthTime) {
31 | return DateResolutionType.Day;
32 | }
33 | const yearTime = monthTime * 12;
34 |
35 | if (diffTime < yearTime) {
36 | return DateResolutionType.Month;
37 | }
38 | }
39 |
40 | if (date.getTime() >= weekAgoTruncated.getTime()) {
41 | return DateResolutionType.Day;
42 | }
43 |
44 | if (date.getTime() >= monthAgoTruncated.getTime()) {
45 | return DateResolutionType.Day;
46 | }
47 |
48 | if (date.getTime() > yearAgoTruncated.getTime()) {
49 | return DateResolutionType.Month;
50 | }
51 | return DateResolutionType.Month
52 | }
53 |
54 | export function getTruncateFunction(dateResolution: DateResolutionType) : (date: Date) => Date {
55 | if (dateResolution == DateResolutionType.Day) {
56 | return (date: Date) => new Date(new Date(date).setHours(0,0,0,0));
57 | } else {
58 | return (date: Date) => new Date(new Date(new Date(date).setDate(0)).setHours(0,0,0,0))
59 | }
60 | }
--------------------------------------------------------------------------------
/v2/src/modules/store-analytics/utils/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { ModulesSdkUtils } from "@medusajs/framework/utils"
14 |
15 | type OrdersHistory = {
16 | orderCount: string,
17 | date: string
18 | }
19 |
20 | export type OrdersHistoryResult = {
21 | dateRangeFrom?: number
22 | dateRangeTo?: number,
23 | dateRangeFromCompareTo?: number,
24 | dateRangeToCompareTo?: number,
25 | current: OrdersHistory[];
26 | previous: OrdersHistory[];
27 | }
28 |
29 | export type PgConnectionType = ReturnType;
--------------------------------------------------------------------------------
/v2/src/subscribers/README.md:
--------------------------------------------------------------------------------
1 | # Custom subscribers
2 |
3 | You may define custom eventhandlers, `subscribers` by creating files in the `/subscribers` directory.
4 |
5 | ```ts
6 | import MyCustomService from "../services/my-custom";
7 | import {
8 | OrderService,
9 | SubscriberArgs,
10 | SubscriberConfig,
11 | } from "@medusajs/medusa";
12 |
13 | type OrderPlacedEvent = {
14 | id: string;
15 | no_notification: boolean;
16 | };
17 |
18 | export default async function orderPlacedHandler({
19 | data,
20 | eventName,
21 | container,
22 | }: SubscriberArgs) {
23 | const orderService: OrderService = container.resolve(OrderService);
24 |
25 | const order = await orderService.retrieve(data.id, {
26 | relations: ["items", "items.variant", "items.variant.product"],
27 | });
28 |
29 | // Do something with the order
30 | }
31 |
32 | export const config: SubscriberConfig = {
33 | event: OrderService.Events.PLACED,
34 | };
35 | ```
36 |
37 | A subscriber is defined in two parts a `handler` and a `config`. The `handler` is a function which is invoked when an event is emitted. The `config` is an object which defines which event(s) the subscriber should subscribe to.
38 |
39 | The `handler` is a function which takes one parameter, an `object` of type `SubscriberArgs` with the following properties:
40 |
41 | - `data` - an `object` of type `T` containing information about the event.
42 | - `eventName` - a `string` containing the name of the event.
43 | - `container` - a `MedusaContainer` instance which can be used to resolve services.
44 | - `pluginOptions` - an `object` containing plugin options, if the subscriber is defined in a plugin.
45 |
--------------------------------------------------------------------------------
/v2/src/ui-components/common/icon-comparison.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { ArrowUpMini, ArrowDownMini, MinusMini } from "@medusajs/icons"
14 |
15 | export const IconComparison = ({current, previous, switchArrow} : {current: number, previous?: number, switchArrow?: boolean}) => {
16 | if (current == previous) {
17 | return
18 | }
19 | if (previous && (current > previous)) {
20 | return
21 | }
22 | if (previous && (current < previous)) {
23 | return
24 | }
25 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/common/percentage-comparison.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Tooltip, TooltipProvider } from "@medusajs/ui";
14 | import { calculatePercentage } from "../utils/helpers";
15 |
16 | export const PercentageComparison = ({current, label, previous, headingLevel = "h2" } : {current: string, label: string, previous: string, headingLevel?: any}) => {
17 | const percentage: number | undefined = calculatePercentage(parseInt(current), parseInt(previous));
18 | return (
19 |
20 |
21 |
22 |
23 | {percentage !== undefined ? `${percentage}%` : `N/A`}
24 |
25 |
26 |
27 |
28 | )
29 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/customers/cumulative-history/cumulative-customers-card.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Users } from "@medusajs/icons";
15 | import { Grid } from "@mui/material";
16 | import type { DateRange } from "../../utils/types";
17 | import { CumulativeCustomersChart } from "./cumulative-customers-chart";
18 |
19 | export const CumulativeCustomersCard = ({dateRange, dateRangeCompareTo, compareEnabled} :
20 | {dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Cumulative customers
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/customers/customers-overview-card.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Users } from "@medusajs/icons";
15 | import { Grid } from "@mui/material";
16 | import type { DateRange } from "../utils/types";
17 | import { CustomersNumber } from "./customers-number-overview";
18 | import { CustomersByNewChart } from "./customers-by-new-chart";
19 |
20 | export const CustomersOverviewCard = ({dateRange, dateRangeCompareTo, compareEnabled} :
21 | {dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | New customers
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | )
44 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/customers/repeat-customer-rate/customers-repeat-customer-rate-number.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { PercentageComparison } from "../../common/percentage-comparison";
16 | import { IconComparison } from "../../common/icon-comparison";
17 | import { CustomersRepeatCustomerRateResponse } from "../types"
18 |
19 | export const RepeatCustomerRateNummber = ({repeatCustomerRateResponse, compareEnabled} : {repeatCustomerRateResponse: CustomersRepeatCustomerRateResponse, compareEnabled?: boolean}) => {
20 | const currentPercentage: number | undefined =
21 | repeatCustomerRateResponse.analytics.current !== undefined && repeatCustomerRateResponse.analytics.current.returnCustomerRate !== undefined ?
22 | parseInt(repeatCustomerRateResponse.analytics.current.returnCustomerRate) : undefined;
23 | const previousPercentage: number | undefined =
24 | repeatCustomerRateResponse.analytics.previous !== undefined && repeatCustomerRateResponse.analytics.previous.returnCustomerRate !== undefined ?
25 | parseInt(repeatCustomerRateResponse.analytics.previous.returnCustomerRate) : undefined;
26 |
27 | return (
28 |
29 |
30 | {currentPercentage !== undefined ?
31 |
32 | {`${currentPercentage}%`}
33 | :
34 |
35 | {`No orders or customers`}
36 |
37 | }
38 |
39 | {compareEnabled && repeatCustomerRateResponse.analytics.dateRangeFromCompareTo && currentPercentage !== undefined &&
40 |
41 |
42 |
43 |
44 |
45 | {previousPercentage !== undefined &&
46 |
47 | }
48 |
49 |
50 | }
51 |
52 | );
53 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/customers/repeat-customer-rate/order-frequency-distribution-chart.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { getLegendName } from "../../common/utils/chartUtils";
14 | import { CustomersRepeatCustomerRateResponse, Distributions } from "../types"
15 | import { Legend, Pie, PieChart, Tooltip } from "recharts";
16 |
17 | const ONE_TIME_LABEL_NAME = 'One-time purchase';
18 | const REPEAT_LABEL_NAME = 'Repeat purchase';
19 |
20 | function convertToChartData(distributions: Distributions) {
21 | if (distributions) {
22 | if (distributions.orderOneTimeFrequency || distributions.orderRepeatFrequency) {
23 | const oneTimeValue = distributions.orderOneTimeFrequency ? parseInt(distributions.orderOneTimeFrequency) : 0;
24 | const repeatValue = distributions.orderRepeatFrequency ? parseInt(distributions.orderRepeatFrequency) : 0;
25 | return [
26 | {
27 | name: ONE_TIME_LABEL_NAME,
28 | value: oneTimeValue,
29 | displayValue: ONE_TIME_LABEL_NAME
30 | },
31 | {
32 | name: REPEAT_LABEL_NAME,
33 | value: repeatValue,
34 | displayValue: REPEAT_LABEL_NAME
35 | }
36 | ]
37 | }
38 | }
39 | return undefined;
40 | }
41 |
42 | export const OrderFrequencyDistributionPieChart = ({repeatCustomerRateResponse, compareEnabled} : {repeatCustomerRateResponse: CustomersRepeatCustomerRateResponse, compareEnabled?: boolean}) => {
43 |
44 | const currentData = convertToChartData(repeatCustomerRateResponse.analytics.current);
45 | const previousData = convertToChartData(repeatCustomerRateResponse.analytics.previous);
46 |
47 | const renderLabel = function(entry) {
48 | return entry.displayValue;
49 | }
50 |
51 | return (
52 |
53 |
54 | {compareEnabled && repeatCustomerRateResponse.analytics.dateRangeFromCompareTo && currentData !== undefined &&
55 |
56 | }
57 | {(compareEnabled && repeatCustomerRateResponse.analytics.dateRangeFromCompareTo) && }
67 | `${value}%`}/>
68 |
69 | );
70 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/customers/repeat-customer-rate/order-frequency-distribution.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { CustomersRepeatCustomerRateResponse } from "../types"
16 | import { OrderFrequencyDistributionPieChart } from "./order-frequency-distribution-chart";
17 |
18 | export const OrderFrequencyDistribution = ({repeatCustomerRateResponse, compareEnabled} : {repeatCustomerRateResponse: CustomersRepeatCustomerRateResponse, compareEnabled?: boolean}) => {
19 | return (
20 |
21 |
22 |
23 | How orders were distributed?
24 |
25 |
26 |
27 |
28 |
29 |
30 | )
31 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/customers/retention-customer-rate/customers-retention-customer-rate-number.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { PercentageComparison } from "../../common/percentage-comparison";
16 | import { IconComparison } from "../../common/icon-comparison";
17 | import { CustomersRetentionCustomerRateResponse } from "../types"
18 |
19 | export const RetentionCustomerRateNumber = ({retentionCustomerRateResponse, compareEnabled} : {retentionCustomerRateResponse: CustomersRetentionCustomerRateResponse, compareEnabled?: boolean}) => {
20 | const currentPercentage: number | undefined =
21 | retentionCustomerRateResponse.analytics.current !== undefined ?
22 | parseInt(retentionCustomerRateResponse.analytics.current) : undefined;
23 | const previousPercentage: number | undefined =
24 | retentionCustomerRateResponse.analytics.previous !== undefined ?
25 | parseInt(retentionCustomerRateResponse.analytics.previous) : undefined;
26 |
27 | return (
28 |
29 |
30 | {currentPercentage !== undefined ?
31 |
32 | {`${currentPercentage}%`}
33 | :
34 |
35 | {`No orders or customers`}
36 |
37 | }
38 |
39 | {compareEnabled && retentionCustomerRateResponse.analytics.dateRangeFromCompareTo && currentPercentage !== undefined &&
40 |
41 |
42 |
43 |
44 |
45 | {previousPercentage !== undefined &&
46 |
47 | }
48 |
49 |
50 | }
51 |
52 | );
53 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/customers/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export type Distributions = {
14 | returnCustomerRate: string,
15 | orderOneTimeFrequency?: string,
16 | orderRepeatFrequency?: string
17 | }
18 |
19 | export type CustomersRepeatCustomerRateResponse = {
20 | analytics: {
21 | dateRangeFrom: number
22 | dateRangeTo: number,
23 | dateRangeFromCompareTo?: number,
24 | dateRangeToCompareTo?: number,
25 | current: Distributions,
26 | previous: Distributions
27 | }
28 | }
29 |
30 | export type CustomersRetentionCustomerRateResponse = {
31 | analytics: {
32 | dateRangeFrom: number
33 | dateRangeTo: number,
34 | dateRangeFromCompareTo?: number,
35 | dateRangeToCompareTo?: number,
36 | current?: string,
37 | previous?: string
38 | }
39 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export { ComparedDate, SwitchComparison, DropdownOrderStatus, SelectDateLasts } from './common/overview-components';
14 |
15 | export { OrdersOverviewCard } from './orders/orders-overview-card'
16 | export { OrdersPaymentProviderCard } from './orders/orders-payment-provider-card'
17 |
18 | export { SalesOverviewCard } from './sales/sales-overview-card'
19 | export { SalesChannelPopularityCard } from './sales/sales-channel-popularity-card'
20 | export { RefundsOverviewCard } from './sales/refunds/refunds-overview-card'
21 | export { RegionsPopularityCard } from './sales/regions-popularity-card'
22 |
23 | export { CustomersOverviewCard } from './customers/customers-overview-card'
24 | export { CustomersRepeatCustomerRate } from './customers/repeat-customer-rate/customers-repeat-customer-rate';
25 | export { CustomersRetentionCustomerRate } from './customers/retention-customer-rate/customers-retention-customer-rate';
26 | export { CumulativeCustomersCard } from './customers/cumulative-history/cumulative-customers-card';
27 |
28 | export { VariantsTopByCountCard } from './products/variants-top-by-count';
29 | export { ReturnedVariantsByCountCard } from './products/returned_variants/returned-variants-by-count';
30 | export { ProductsSoldCountCard } from './products/products-sold-count';
31 | export { OutOfTheStockVariantsCard } from './products/out_of_the_stock_variants/out-of-the-stock-variants-by-count';
32 |
33 | export { DiscountsTopCard } from './marketing/discounts-top-by-count';
34 |
35 | export { DateLasts, OrderStatus } from './utils/types'
36 | export type { DateRange } from './utils/types'
37 | export { convertDateLastsToComparedDateRange, convertDateLastsToDateRange, amountToDisplay, calculatePercentage } from './utils/helpers'
--------------------------------------------------------------------------------
/v2/src/ui-components/marketing/discounts-top-table.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Text } from "@medusajs/ui";
14 | import { Divider, Grid } from "@mui/material";
15 |
16 | export type DiscountsTopTableRow = {
17 | sum: string,
18 | discountCode: string
19 | }
20 |
21 | export const DiscountsTopTable = ({tableRows} : {tableRows: DiscountsTopTableRow[]}) => {
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Discount
32 |
33 |
34 |
35 |
36 | Count
37 |
38 |
39 |
40 |
41 | {tableRows.length > 0 ? tableRows.map(tableRow => (
42 |
43 |
44 |
45 |
46 | {tableRow.discountCode}
47 |
48 |
49 |
50 |
51 | {tableRow.sum}
52 |
53 |
54 |
55 |
56 | )) :
57 |
58 |
59 |
60 |
61 | None
62 |
63 |
64 |
65 |
66 | None
67 |
68 |
69 |
70 |
71 | }
72 |
73 | )
74 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/orders/orders-overview-card.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { ShoppingCart } from "@medusajs/icons";
15 | import { Grid } from "@mui/material";
16 | import { OrdersByNewChart } from "./orders-by-new-chart";
17 | import type { DateRange } from "../utils/types";
18 | import { OrdersNumber } from "./orders-number-overview";
19 | import { OrderStatus } from "../utils/types";
20 |
21 | export const OrdersOverviewCard = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
22 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
23 | return (
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Orders
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | )
45 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/orders/orders-payment-provider-chart.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { calculateResolution, getLegendName } from "../common/utils/chartUtils";
14 | import { Legend, Pie, PieChart, Tooltip } from "recharts";
15 | import { OrdersPaymentProvider, OrdersPaymentProviderResponse } from "./types";
16 | import { Text, Container } from "@medusajs/ui";
17 |
18 | function convertToChartData(ordersPaymentProviders: OrdersPaymentProvider[]) {
19 | if (ordersPaymentProviders.length) {
20 | return ordersPaymentProviders.map(ordersPaymentProvider => {
21 | return {
22 | name: ordersPaymentProvider.paymentProviderId,
23 | value: parseFloat(ordersPaymentProvider.percentage),
24 | displayValue: ordersPaymentProvider.paymentProviderId,
25 | orderCount: ordersPaymentProvider.orderCount,
26 | }
27 | })
28 | }
29 | return undefined;
30 | }
31 | const ChartCustomTooltip = ({ active, payload, label }) => {
32 | if (active && payload && payload.length) {
33 | return (
34 |
35 | {`${payload[0].payload.value}%`}
36 | {`Provider: ${payload[0].payload.name}`}
37 | {`Order count: ${payload[0].payload.orderCount}`}
38 |
39 | )
40 | }
41 | return null;
42 | };
43 |
44 | export const OrdersPaymentProviderPieChart = ({ordersPaymentProviderResponse, compareEnabled} : {ordersPaymentProviderResponse: OrdersPaymentProviderResponse, compareEnabled?: boolean}) => {
45 |
46 | const currentData = convertToChartData(ordersPaymentProviderResponse.analytics.current);
47 | const previousData = convertToChartData(ordersPaymentProviderResponse.analytics.previous);
48 |
49 | const renderLabel = function(entry) {
50 | return entry.displayValue;
51 | }
52 |
53 | return (
54 |
55 |
56 | {compareEnabled && ordersPaymentProviderResponse.analytics.dateRangeFromCompareTo &&
57 |
58 | }
59 | {(compareEnabled && ordersPaymentProviderResponse.analytics.dateRangeFromCompareTo) && }
69 | } />
70 |
71 | );
72 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/orders/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 |
14 | export type OrdersPaymentProvider = {
15 | orderCount: string,
16 | percentage: string,
17 | paymentProviderId: string
18 | }
19 |
20 | export type OrdersPaymentProviderPopularityResult = {
21 | dateRangeFrom?: number
22 | dateRangeTo?: number,
23 | dateRangeFromCompareTo?: number,
24 | dateRangeToCompareTo?: number,
25 | current: OrdersPaymentProvider[]
26 | previous: OrdersPaymentProvider[]
27 | }
28 |
29 | export type OrdersPaymentProviderResponse = {
30 | analytics: OrdersPaymentProviderPopularityResult
31 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/products/out_of_the_stock_variants/helpers.tsx:
--------------------------------------------------------------------------------
1 | export type AdminOutOfTheStockVariantsStatisticsQuery = {}
2 |
3 | export type OutOfTheStockVariantsCount = {
4 | productId: string,
5 | variantId: string,
6 | productTitle: string,
7 | variantTitle: string,
8 | thumbnail: string,
9 | }
10 |
11 | export type OutOfTheStockVariantsCountResult = {
12 | dateRangeFrom?: number
13 | dateRangeTo?: number,
14 | dateRangeFromCompareTo?: number,
15 | dateRangeToCompareTo?: number,
16 | current: OutOfTheStockVariantsCount[],
17 | }
18 |
19 | export type OutOfTheStockVariantsCountResponse = {
20 | analytics: OutOfTheStockVariantsCountResult
21 | }
22 | export type OutOfTheStockVariantsTableRow = {
23 | variantId: string,
24 | productId: string,
25 | productTitle: string,
26 | variantTitle: string,
27 | thumbnail: string,
28 | }
29 |
30 | export function transformToVariantTopTable(result: OutOfTheStockVariantsCountResult): OutOfTheStockVariantsTableRow[] {
31 | const currentMap = new Map();
32 |
33 | result.current.forEach(currentItem => {
34 | currentMap.set(currentItem.variantId, {
35 | variantId: currentItem.variantId,
36 | productId: currentItem.productId,
37 | productTitle: currentItem.productTitle,
38 | variantTitle: currentItem.variantTitle,
39 | thumbnail: currentItem.thumbnail,
40 | });
41 | });
42 |
43 | return Array.from(currentMap.values());
44 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/products/out_of_the_stock_variants/out-of-the-stock-variants-table.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Text } from "@medusajs/ui";
14 | import { Box, Divider, Grid } from "@mui/material";
15 | import { Link } from "react-router-dom"
16 | import { OutOfTheStockVariantsTableRow } from "./helpers";
17 |
18 | export const OutOfTheStockVariantsTable = ({tableRows} : {tableRows: OutOfTheStockVariantsTableRow[]}) => {
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Variant
29 |
30 |
31 |
32 |
33 | {tableRows.length > 0 ? tableRows.map(tableRow => (
34 |
35 |
36 |
37 |
38 |
39 | {tableRow.thumbnail &&
40 |
49 | }
50 |
51 | {tableRow.productTitle} - {tableRow.variantTitle}
52 |
53 |
54 |
55 |
56 |
57 |
58 | )) :
59 |
60 |
61 |
62 |
63 | None
64 |
65 |
66 |
67 |
68 | }
69 |
70 | )
71 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/products/returned_variants/returned-variants-table.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Text } from "@medusajs/ui";
14 | import { Box, Divider, Grid } from "@mui/material";
15 | import { Link } from "react-router-dom"
16 |
17 | export type VariantsTopTableRow = {
18 | sum: string,
19 | productId: string,
20 | productTitle: string,
21 | variantTitle: string,
22 | thumbnail: string,
23 | }
24 |
25 | export const ReturnedVariantsTable = ({tableRows} : {tableRows: VariantsTopTableRow[]}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Variant
36 |
37 |
38 |
39 |
40 | Count
41 |
42 |
43 |
44 |
45 | {tableRows.length > 0 ? tableRows.map(tableRow => (
46 |
47 |
48 |
49 |
50 |
51 | {tableRow.thumbnail &&
52 |
61 | }
62 |
63 | {tableRow.productTitle} - {tableRow.variantTitle}
64 |
65 |
66 |
67 |
68 |
69 |
70 | {tableRow.sum}
71 |
72 |
73 |
74 |
75 | )) :
76 |
77 |
78 |
79 |
80 | None
81 |
82 |
83 |
84 |
85 | None
86 |
87 |
88 |
89 |
90 | }
91 |
92 | )
93 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/products/variants-top-table.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading, Text } from "@medusajs/ui";
14 | import { Box, Divider, Grid } from "@mui/material";
15 | import { Link } from "react-router-dom"
16 |
17 | export type VariantsTopTableRow = {
18 | sum: string,
19 | productId: string,
20 | productTitle: string,
21 | variantTitle: string,
22 | thumbnail: string,
23 | }
24 |
25 | export const VariantsTopTable = ({tableRows} : {tableRows: VariantsTopTableRow[]}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Variant
36 |
37 |
38 |
39 |
40 | Count
41 |
42 |
43 |
44 |
45 | {tableRows.length > 0 ? tableRows.map(tableRow => (
46 |
47 |
48 |
49 |
50 |
51 | {tableRow.thumbnail &&
52 |
61 | }
62 |
63 | {tableRow.productTitle} - {tableRow.variantTitle}
64 |
65 |
66 |
67 |
68 |
69 |
70 | {tableRow.sum}
71 |
72 |
73 |
74 |
75 | )) :
76 |
77 |
78 |
79 |
80 | None
81 |
82 |
83 |
84 |
85 | None
86 |
87 |
88 |
89 |
90 | }
91 |
92 | )
93 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/sales/refunds/refunds-numbers.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { PercentageComparison } from "../../common/percentage-comparison";
16 | import { IconComparison } from "../../common/icon-comparison";
17 | import { RefundsResponse } from "../types";
18 | import { amountToDisplay } from "../../utils/helpers";
19 |
20 | export const RefundsNumber = ({refundsResponse, compareEnabled} : {refundsResponse: RefundsResponse, compareEnabled?: boolean}) => {
21 | const overallCurrentSum: number = parseInt(refundsResponse.analytics.current);
22 | const overallPreviousSum: number | undefined = refundsResponse.analytics.previous !== undefined ?
23 | parseInt(refundsResponse.analytics.previous) : undefined;
24 |
25 | return (
26 |
27 |
28 |
29 | {overallCurrentSum.toFixed(refundsResponse.analytics.currencyDecimalDigits)} {refundsResponse.analytics.currencyCode.toUpperCase()}
30 |
31 |
32 | {compareEnabled && refundsResponse.analytics.dateRangeFromCompareTo &&
33 |
34 |
35 |
36 |
37 |
38 | {overallPreviousSum !== undefined &&
39 |
40 | }
41 |
42 |
43 | }
44 |
45 | );
46 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/sales/sales-number-overview.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { Grid } from "@mui/material";
15 | import { PercentageComparison } from "../common/percentage-comparison";
16 | import { IconComparison } from "../common/icon-comparison";
17 | import { SalesHistoryResponse } from "./types";
18 | import { amountToDisplay } from "../utils/helpers";
19 |
20 | export const SalesNumber = ({salesHistoryResponse, compareEnabled} : {salesHistoryResponse: SalesHistoryResponse, compareEnabled?: boolean}) => {
21 | const overallCurrentSum: number = salesHistoryResponse.analytics.current.reduce((sum, order) => sum + parseInt(order.total), 0);
22 | const overallPreviousSum: number | undefined = salesHistoryResponse.analytics.previous.length > 0 ?
23 | salesHistoryResponse.analytics.previous.reduce((sum, order) => sum + parseInt(order.total), 0) :
24 | undefined;
25 |
26 | return (
27 |
28 |
29 |
30 | {amountToDisplay(overallCurrentSum, salesHistoryResponse.analytics.currencyDecimalDigits)} {salesHistoryResponse.analytics.currencyCode.toUpperCase()}
31 |
32 |
33 | {compareEnabled && salesHistoryResponse.analytics.dateRangeFromCompareTo &&
34 |
35 |
36 |
37 |
38 |
39 | {overallPreviousSum !== undefined &&
40 |
41 | }
42 |
43 |
44 | }
45 |
46 | );
47 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/sales/sales-total-chart.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Heading } from "@medusajs/ui";
14 | import { ChartCurrentPrevious } from "../common/chart-components";
15 | import { SalesHistoryResponse } from "./types";
16 | import { amountToDisplay } from "../utils/helpers";
17 |
18 | export const SalesByNewChart = ({dateRangeFrom, dateRangeTo, salesHistoryResponse, compareEnabled} :
19 | {dateRangeFrom: Date, dateRangeTo: Date, salesHistoryResponse: SalesHistoryResponse, compareEnabled?: boolean}) => {
20 | const rawChartData = {
21 | current: salesHistoryResponse.analytics.current.map(currentData => {
22 | return {
23 | date: new Date(currentData.date),
24 | value: amountToDisplay(parseInt(currentData.total), salesHistoryResponse.analytics.currencyDecimalDigits)
25 | };
26 | }),
27 | previous: salesHistoryResponse.analytics.previous.map(previousData => {
28 | return {
29 | date: new Date(previousData.date),
30 | value: amountToDisplay(parseInt(previousData.total), salesHistoryResponse.analytics.currencyDecimalDigits)
31 | };
32 | }),
33 | };
34 | return (
35 | <>
36 | Sales by time
37 |
45 | >
46 | )
47 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/sales/types.ts:
--------------------------------------------------------------------------------
1 | export type SalesHistory = {
2 | total: string,
3 | date: string
4 | }
5 |
6 | export type SalesHistoryResponse = {
7 | analytics: {
8 | dateRangeFrom?: number
9 | dateRangeTo?: number,
10 | dateRangeFromCompareTo?: number,
11 | dateRangeToCompareTo?: number,
12 | currencyCode: string,
13 | currencyDecimalDigits: number,
14 | current: SalesHistory[];
15 | previous: SalesHistory[];
16 | }
17 | }
18 |
19 | export type RefundsResponse = {
20 | analytics: {
21 | dateRangeFrom?: number
22 | dateRangeTo?: number,
23 | dateRangeFromCompareTo?: number,
24 | dateRangeToCompareTo?: number,
25 | currencyCode: string
26 | currencyDecimalDigits: number,
27 | current: string;
28 | previous: string;
29 | }
30 | }
--------------------------------------------------------------------------------
/v2/src/ui-components/tabs/customers.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Container } from "@medusajs/ui"
14 | import {
15 | CustomersOverviewCard,
16 | CustomersRepeatCustomerRate,
17 | CumulativeCustomersCard,
18 | CustomersRetentionCustomerRate,
19 | OrderStatus,
20 | } from '..';
21 | import type { DateRange } from '..';
22 | import { Grid } from "@mui/material";
23 |
24 | const CustomersTab = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
25 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default CustomersTab
--------------------------------------------------------------------------------
/v2/src/ui-components/tabs/orders.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Container } from "@medusajs/ui"
14 | import {
15 | OrdersOverviewCard,
16 | OrderStatus,
17 | } from '..';
18 | import type { DateRange } from '..';
19 | import { Grid } from "@mui/material";
20 | import { OrdersPaymentProviderCard } from "../orders/orders-payment-provider-card";
21 |
22 | const OrdersTab = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
23 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | )
38 | }
39 |
40 | export default OrdersTab
--------------------------------------------------------------------------------
/v2/src/ui-components/tabs/products.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Container } from "@medusajs/ui"
14 | import {
15 | ProductsSoldCountCard,
16 | VariantsTopByCountCard,
17 | ReturnedVariantsByCountCard,
18 | OutOfTheStockVariantsCard,
19 | OrderStatus,
20 | } from '..';
21 | import type { DateRange } from '..';
22 | import { Grid } from "@mui/material";
23 |
24 | const ProductsTab = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
25 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default ProductsTab
--------------------------------------------------------------------------------
/v2/src/ui-components/tabs/sales.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | import { Container } from "@medusajs/ui"
14 | import {
15 | DiscountsTopCard,
16 | SalesChannelPopularityCard,
17 | OrderStatus,
18 | SalesOverviewCard,
19 | RefundsOverviewCard
20 | } from '..';
21 | import type { DateRange } from '..';
22 | import { Grid } from "@mui/material";
23 |
24 | const SalesTab = ({orderStatuses, dateRange, dateRangeCompareTo, compareEnabled} :
25 | {orderStatuses: OrderStatus[], dateRange?: DateRange, dateRangeCompareTo?: DateRange, compareEnabled: boolean}) => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default SalesTab
--------------------------------------------------------------------------------
/v2/src/ui-components/utils/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 RSC-Labs, https://rsoftcon.com/
3 | *
4 | * MIT License
5 | *
6 | * Unless required by applicable law or agreed to in writing, software
7 | * distributed under the License is distributed on an "AS IS" BASIS,
8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | * See the License for the specific language governing permissions and
10 | * limitations under the License.
11 | */
12 |
13 | export enum OrderStatus {
14 | /**
15 | * The order is pending.
16 | */
17 | PENDING = "pending",
18 | /**
19 | * The order is completed, meaning that
20 | * the items have been fulfilled and the payment
21 | * has been captured.
22 | */
23 | COMPLETED = "completed",
24 | /**
25 | * The order is archived.
26 | */
27 | ARCHIVED = "archived",
28 | /**
29 | * The order is canceled.
30 | */
31 | CANCELED = "canceled",
32 | /**
33 | * The order requires action.
34 | */
35 | REQUIRES_ACTION = "requires_action"
36 | }
37 |
38 | export enum DateLasts {
39 | All = "All time",
40 | LastMonth = "Last 30 days",
41 | LastWeek = "Last 7 days",
42 | LastYear = "Last 365 days"
43 | }
44 | export type DateRange = {
45 | from: Date,
46 | to: Date
47 | }
--------------------------------------------------------------------------------
/v2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2021",
4 | "esModuleInterop": true,
5 | "module": "Node16",
6 | "moduleResolution": "Node16",
7 | "emitDecoratorMetadata": true,
8 | "experimentalDecorators": true,
9 | "skipLibCheck": true,
10 | "skipDefaultLibCheck": true,
11 | "declaration": false,
12 | "sourceMap": false,
13 | "inlineSourceMap": true,
14 | "outDir": "./.medusa/server",
15 | "rootDir": "./",
16 | "baseUrl": ".",
17 | "jsx": "react-jsx",
18 | "forceConsistentCasingInFileNames": true,
19 | "resolveJsonModule": true,
20 | "checkJs": false,
21 | "strictNullChecks": true
22 | },
23 | "ts-node": {
24 | "swc": true
25 | },
26 | "include": [
27 | "**/*",
28 | ".medusa/types/*"
29 | ],
30 | "exclude": [
31 | "node_modules",
32 | ".medusa/server",
33 | ".medusa/admin",
34 | "src/admin",
35 | ".cache"
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------