feat(medusa,dashboard,admin-sdk): Run admin dashboard from Medusa instance (#7330)
This commit is contained in:
1 parent
ec5415ea1a
commit
490586f566
82 files changed
+3943
-785
No files matched your search
@@ -7,14 +7,7 @@
|
|||||||
"access": "public",
|
"access": "public",
|
||||||
"baseBranch": "develop",
|
"baseBranch": "develop",
|
||||||
"updateInternalDependencies": "patch",
|
"updateInternalDependencies": "patch",
|
||||||
"ignore": [
|
"ignore": ["integration-tests-api", "integration-tests-modules"],
|
||||||
"integration-tests-api",
|
|
||||||
"integration-tests-modules",
|
|
||||||
"@medusajs/dashboard",
|
|
||||||
"@medusajs/admin-shared",
|
|
||||||
"@medusajs/admin-bundler",
|
|
||||||
"@medusajs/vite-plugin-extension"
|
|
||||||
],
|
|
||||||
"snapshot": {
|
"snapshot": {
|
||||||
"useCalculatedVersion": true
|
"useCalculatedVersion": true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ module.exports = {
|
|||||||
"./packages/medusa/tsconfig.json",
|
"./packages/medusa/tsconfig.json",
|
||||||
|
|
||||||
"./packages/admin-next/dashboard/tsconfig.json",
|
"./packages/admin-next/dashboard/tsconfig.json",
|
||||||
|
"./packages/admin-next/admin-sdk/tsconfig.json",
|
||||||
|
"./packages/admin-next/admin-vite-plugin/tsconfig.json",
|
||||||
|
|
||||||
"./packages/inventory/tsconfig.spec.json",
|
"./packages/inventory/tsconfig.spec.json",
|
||||||
"./packages/stock-location/tsconfig.spec.json",
|
"./packages/stock-location/tsconfig.spec.json",
|
||||||
@@ -267,5 +269,54 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"packages/admin-next/app/**/*.ts",
|
||||||
|
"packages/admin-next/app/**/*.tsx",
|
||||||
|
],
|
||||||
|
plugins: ["unused-imports", "react-refresh"],
|
||||||
|
extends: [
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:react/jsx-runtime",
|
||||||
|
"plugin:react-hooks/recommended",
|
||||||
|
],
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
|
||||||
|
sourceType: "module", // Allows for the use of imports
|
||||||
|
project: "./packages/admin-next/app/tsconfig.json",
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
__BASE__: "readonly",
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
"react/prop-types": "off",
|
||||||
|
"new-cap": "off",
|
||||||
|
"require-jsdoc": "off",
|
||||||
|
"valid-jsdoc": "off",
|
||||||
|
"react-refresh/only-export-components": [
|
||||||
|
"warn",
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
"no-unused-expressions": "off",
|
||||||
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
"unused-imports/no-unused-vars": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
vars: "all",
|
||||||
|
varsIgnorePattern: "^_",
|
||||||
|
args: "after-used",
|
||||||
|
argsIgnorePattern: "^_",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-server
|
uses: ./.github/actions/setup-server
|
||||||
with:
|
with:
|
||||||
cache-extension: "cli-test"
|
cache-extension: "cli-test"
|
||||||
node-version: "16.14"
|
node-version: 18
|
||||||
|
|
||||||
- name: Install Medusa cli
|
- name: Install Medusa cli
|
||||||
run: npm i -g @medusajs/medusa-cli@preview
|
run: npm i -g @medusajs/medusa-cli@preview
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ process.env.LOG_LEVEL = "error"
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [],
|
plugins: [],
|
||||||
|
admin: {
|
||||||
|
disable: true,
|
||||||
|
},
|
||||||
projectConfig: {
|
projectConfig: {
|
||||||
redis_url: redisUrl,
|
redis_url: redisUrl,
|
||||||
database_url: DB_URL,
|
database_url: DB_URL,
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ const customFulfillmentProvider = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
admin: {
|
||||||
|
disable: true,
|
||||||
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
projectConfig: {
|
projectConfig: {
|
||||||
database_url: DB_URL,
|
database_url: DB_URL,
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# cli
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
function start() {
|
|
||||||
return import("../dist/cli/index.mjs");
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@medusajs/admin-bundler",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"build": "rimraf dist && tsup"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"medusa-admin": "./bin/medusa-admin.js"
|
|
||||||
},
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"types": "dist/index.d.ts",
|
|
||||||
"module": "dist/index.mjs",
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"devDependencies": {
|
|
||||||
"rimraf": "5.0.1",
|
|
||||||
"tsup": "^8.0.1",
|
|
||||||
"typescript": "^5.3.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@medusajs/ui-preset": "^1.1.3",
|
|
||||||
"@medusajs/vite-plugin-extension": "*",
|
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
|
||||||
"autoprefixer": "^10.4.16",
|
|
||||||
"commander": "^11.1.0",
|
|
||||||
"deepmerge": "^4.3.1",
|
|
||||||
"glob": "^7.1.6",
|
|
||||||
"postcss": "^8.4.32",
|
|
||||||
"tailwindcss": "^3.3.6",
|
|
||||||
"vite": "5.0.10"
|
|
||||||
},
|
|
||||||
"packageManager": "yarn@3.2.1"
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { resolve } from "path"
|
|
||||||
import { build as command } from "vite"
|
|
||||||
|
|
||||||
import { createViteConfig } from "./create-vite-config"
|
|
||||||
|
|
||||||
type BuildArgs = {
|
|
||||||
root?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function build({ root }: BuildArgs) {
|
|
||||||
const config = await createViteConfig({
|
|
||||||
build: {
|
|
||||||
outDir: resolve(process.cwd(), "build"),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!config) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await command(config)
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { readFileSync } from "fs"
|
|
||||||
import glob from "glob"
|
|
||||||
import { relative, resolve } from "path"
|
|
||||||
import { build as command } from "vite"
|
|
||||||
|
|
||||||
type BundleArgs = {
|
|
||||||
root?: string | undefined
|
|
||||||
watch?: boolean | undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function bundle({ watch, root }: BundleArgs) {
|
|
||||||
const resolvedRoot = root
|
|
||||||
? resolve(process.cwd(), root)
|
|
||||||
: resolve(process.cwd(), "src", "admin")
|
|
||||||
|
|
||||||
const files = glob.sync(`${resolvedRoot}/**/*.{ts,tsx,js,jsx}`)
|
|
||||||
|
|
||||||
const input: Record<string, string> = {}
|
|
||||||
for (const file of files) {
|
|
||||||
const relativePath = relative(resolvedRoot, file)
|
|
||||||
input[relativePath] = file
|
|
||||||
}
|
|
||||||
|
|
||||||
const packageJson = JSON.parse(
|
|
||||||
readFileSync(resolve(process.cwd(), "package.json"), "utf-8")
|
|
||||||
)
|
|
||||||
const external = [
|
|
||||||
...Object.keys(packageJson.dependencies),
|
|
||||||
"@medusajs/ui",
|
|
||||||
"@medusajs/ui-preset",
|
|
||||||
"react",
|
|
||||||
"react-dom",
|
|
||||||
"react-router-dom",
|
|
||||||
"react-hook-form",
|
|
||||||
]
|
|
||||||
|
|
||||||
await command({
|
|
||||||
build: {
|
|
||||||
watch: watch ? {} : undefined,
|
|
||||||
rollupOptions: {
|
|
||||||
input: input,
|
|
||||||
external: external,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
import inject from "@medusajs/vite-plugin-extension"
|
|
||||||
import react from "@vitejs/plugin-react"
|
|
||||||
import deepmerge from "deepmerge"
|
|
||||||
import { createRequire } from "module"
|
|
||||||
import path from "path"
|
|
||||||
import { type Config } from "tailwindcss"
|
|
||||||
import { ContentConfig } from "tailwindcss/types/config"
|
|
||||||
import { InlineConfig, Logger, createLogger, mergeConfig } from "vite"
|
|
||||||
|
|
||||||
const require = createRequire(import.meta.url)
|
|
||||||
|
|
||||||
export async function createViteConfig(
|
|
||||||
inline: InlineConfig
|
|
||||||
): Promise<InlineConfig | null> {
|
|
||||||
const root = process.cwd()
|
|
||||||
const logger = createCustomLogger()
|
|
||||||
|
|
||||||
let dashboardRoot: string | null = null
|
|
||||||
|
|
||||||
try {
|
|
||||||
dashboardRoot = path.dirname(require.resolve("@medusajs/dashboard"))
|
|
||||||
} catch (err) {
|
|
||||||
dashboardRoot = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dashboardRoot) {
|
|
||||||
logger.error(
|
|
||||||
"Unable to find @medusajs/dashboard. Please install it in your project, or specify the root directory."
|
|
||||||
)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const { plugins, userConfig } = (await loadConfig(root, logger)) ?? {}
|
|
||||||
|
|
||||||
let viteConfig: InlineConfig = mergeConfig(inline, {
|
|
||||||
plugins: [
|
|
||||||
react(),
|
|
||||||
inject({
|
|
||||||
sources: plugins,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
configFile: false,
|
|
||||||
root: dashboardRoot,
|
|
||||||
css: {
|
|
||||||
postcss: {
|
|
||||||
plugins: [
|
|
||||||
require("tailwindcss")({
|
|
||||||
config: createTwConfig(process.cwd(), dashboardRoot),
|
|
||||||
}),
|
|
||||||
require("autoprefixer"),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies InlineConfig)
|
|
||||||
|
|
||||||
if (userConfig) {
|
|
||||||
viteConfig = await userConfig(viteConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
return viteConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeTailwindConfigs(config1: Config, config2: Config): Config {
|
|
||||||
const content1 = config1.content
|
|
||||||
const content2 = config2.content
|
|
||||||
|
|
||||||
let mergedContent: ContentConfig
|
|
||||||
|
|
||||||
if (Array.isArray(content1) && Array.isArray(content2)) {
|
|
||||||
mergedContent = [...content1, ...content2]
|
|
||||||
} else if (!Array.isArray(content1) && !Array.isArray(content2)) {
|
|
||||||
mergedContent = {
|
|
||||||
files: [...content1.files, ...content2.files],
|
|
||||||
relative: content1.relative || content2.relative,
|
|
||||||
extract: { ...content1.extract, ...content2.extract },
|
|
||||||
transform: { ...content1.transform, ...content2.transform },
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error("Cannot merge content fields of different types")
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergedConfig = deepmerge(config1, config2)
|
|
||||||
mergedConfig.content = mergedContent
|
|
||||||
|
|
||||||
console.log(config1.presets, config2.presets)
|
|
||||||
|
|
||||||
// Ensure presets only contain unique values
|
|
||||||
mergedConfig.presets = config1.presets || []
|
|
||||||
|
|
||||||
return mergedConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTwConfig(root: string, dashboardRoot: string) {
|
|
||||||
const uiRoot = path.join(
|
|
||||||
path.dirname(require.resolve("@medusajs/ui")),
|
|
||||||
"**/*.{js,jsx,ts,tsx}"
|
|
||||||
)
|
|
||||||
|
|
||||||
const baseConfig: Config = {
|
|
||||||
presets: [require("@medusajs/ui-preset")],
|
|
||||||
content: [
|
|
||||||
`${root}/src/admin/**/*.{js,jsx,ts,tsx}`,
|
|
||||||
`${dashboardRoot}/src/**/*.{js,jsx,ts,tsx}`,
|
|
||||||
uiRoot,
|
|
||||||
],
|
|
||||||
darkMode: "class",
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
let userConfig: Config | null = null
|
|
||||||
const extensions = ["js", "cjs", "mjs", "ts", "cts", "mts"]
|
|
||||||
|
|
||||||
for (const ext of extensions) {
|
|
||||||
try {
|
|
||||||
userConfig = require(path.join(root, `tailwind.config.${ext}`))
|
|
||||||
break
|
|
||||||
} catch (err) {
|
|
||||||
console.log("Failed to load tailwind config with extension", ext, err)
|
|
||||||
userConfig = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userConfig) {
|
|
||||||
return baseConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
return mergeTailwindConfigs(baseConfig, userConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
function createCustomLogger() {
|
|
||||||
const logger = createLogger("info", {
|
|
||||||
prefix: "medusa-admin",
|
|
||||||
})
|
|
||||||
const loggerInfo = logger.info
|
|
||||||
|
|
||||||
logger.info = (msg, opts) => {
|
|
||||||
if (
|
|
||||||
msg.includes("hmr invalidate") &&
|
|
||||||
msg.includes(
|
|
||||||
"Could not Fast Refresh. Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loggerInfo(msg, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
return logger
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PluginOptions extends Record<string, unknown> {
|
|
||||||
enableUI?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type Plugin =
|
|
||||||
| string
|
|
||||||
| {
|
|
||||||
resolve: string
|
|
||||||
options?: PluginOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MedusaConfig extends Record<string, unknown> {
|
|
||||||
plugins?: Plugin[]
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadConfig(root: string, logger: Logger) {
|
|
||||||
const configPath = path.resolve(root, "medusa-config.js")
|
|
||||||
|
|
||||||
const config: MedusaConfig = await import(configPath)
|
|
||||||
.then((c) => c)
|
|
||||||
.catch((e) => {
|
|
||||||
if (e.code === "ERR_MODULE_NOT_FOUND") {
|
|
||||||
logger.warn(
|
|
||||||
"Root 'medusa-config.js' file not found; extensions won't load. If running Admin UI as a standalone app, use the 'standalone' option.",
|
|
||||||
{
|
|
||||||
timestamp: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
logger.error(
|
|
||||||
`An error occured while attempting to load '${configPath}':\n${e}`,
|
|
||||||
{
|
|
||||||
timestamp: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!config) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.plugins?.length) {
|
|
||||||
logger.info(
|
|
||||||
"No plugins in 'medusa-config.js', no extensions will load. To enable Admin UI extensions, add them to the 'plugins' array in 'medusa-config.js'.",
|
|
||||||
{
|
|
||||||
timestamp: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const uiPlugins = config.plugins
|
|
||||||
.filter((p) => typeof p !== "string" && p.options?.enableUI)
|
|
||||||
.map((p: Plugin) => {
|
|
||||||
return typeof p === "string" ? p : p.resolve
|
|
||||||
})
|
|
||||||
|
|
||||||
const extensionSources = uiPlugins.map((p) => {
|
|
||||||
return path.resolve(require.resolve(p), "dist", "admin")
|
|
||||||
})
|
|
||||||
|
|
||||||
const rootSource = path.resolve(process.cwd(), "src", "admin")
|
|
||||||
|
|
||||||
extensionSources.push(rootSource)
|
|
||||||
|
|
||||||
const adminPlugin = config.plugins.find((p) =>
|
|
||||||
typeof p === "string"
|
|
||||||
? p === "@medusajs/admin"
|
|
||||||
: p.resolve === "@medusajs/admin"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!adminPlugin) {
|
|
||||||
logger.info(
|
|
||||||
"No @medusajs/admin in 'medusa-config.js', no extensions will load. To enable Admin UI extensions, add it to the 'plugins' array in 'medusa-config.js'.",
|
|
||||||
{
|
|
||||||
timestamp: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const adminPluginOptions =
|
|
||||||
typeof adminPlugin !== "string" && !!adminPlugin.options
|
|
||||||
? adminPlugin.options
|
|
||||||
: {}
|
|
||||||
|
|
||||||
const viteConfig = adminPluginOptions.withFinal as
|
|
||||||
| ((config: InlineConfig) => InlineConfig)
|
|
||||||
| ((config: InlineConfig) => Promise<InlineConfig>)
|
|
||||||
| undefined
|
|
||||||
|
|
||||||
return {
|
|
||||||
plugins: extensionSources,
|
|
||||||
userConfig: viteConfig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { createServer } from "vite"
|
|
||||||
// @ts-ignore
|
|
||||||
import { createViteConfig } from "./create-vite-config"
|
|
||||||
|
|
||||||
type DevArgs = {
|
|
||||||
port?: number | undefined
|
|
||||||
host?: string | boolean | undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function dev({ port = 5173, host }: DevArgs) {
|
|
||||||
const config = await createViteConfig({
|
|
||||||
server: {
|
|
||||||
port,
|
|
||||||
host,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!config) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const server = await createServer(config)
|
|
||||||
|
|
||||||
await server.listen()
|
|
||||||
|
|
||||||
server.printUrls()
|
|
||||||
server.bindCLIShortcuts({ print: true })
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { Command } from "commander"
|
|
||||||
|
|
||||||
import { build } from "../api/build"
|
|
||||||
import { bundle } from "../api/bundle"
|
|
||||||
import { dev } from "../api/dev"
|
|
||||||
|
|
||||||
export async function createCli() {
|
|
||||||
const program = new Command()
|
|
||||||
|
|
||||||
program.name("medusa-admin")
|
|
||||||
|
|
||||||
program
|
|
||||||
.command("dev")
|
|
||||||
.description("Starts the development server")
|
|
||||||
.action(dev)
|
|
||||||
|
|
||||||
program
|
|
||||||
.command("build")
|
|
||||||
.description("Builds the admin dashboard")
|
|
||||||
.action(build)
|
|
||||||
|
|
||||||
program
|
|
||||||
.command("bundle")
|
|
||||||
.description("Bundles the admin dashboard")
|
|
||||||
.action(bundle)
|
|
||||||
|
|
||||||
return program
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { createCli } from "./create-cli"
|
|
||||||
|
|
||||||
createCli()
|
|
||||||
.then(async (cli) => cli.parseAsync(process.argv))
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
process.exit(1)
|
|
||||||
})
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export { build } from "./api/build.js"
|
|
||||||
export { bundle } from "./api/bundle.js"
|
|
||||||
export { dev } from "./api/dev.js"
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# `@medusajs/admin-sdk`
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name": "@medusajs/admin-sdk",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Admin SDK for Medusa.",
|
||||||
|
"author": "Kasper Kristensen <kasper@medusajs.com>",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup && copyfiles -f ./src/index.html ./src/entry.tsx ./src/index.css ./dist"
|
||||||
|
},
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"package.json"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@medusajs/types": "^1.11.16",
|
||||||
|
"@types/compression": "^1.7.5",
|
||||||
|
"@types/connect-history-api-fallback": "^1.5.4",
|
||||||
|
"copyfiles": "^2.4.1",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"tsup": "^8.0.1",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@medusajs/admin-vite-plugin": "0.0.1",
|
||||||
|
"@medusajs/dashboard": "0.0.1",
|
||||||
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
"autoprefixer": "^10.4.16",
|
||||||
|
"commander": "^11.1.0",
|
||||||
|
"compression": "^1.7.4",
|
||||||
|
"connect-history-api-fallback": "^2.0.0",
|
||||||
|
"deepmerge": "^4.3.1",
|
||||||
|
"glob": "^7.1.6",
|
||||||
|
"postcss": "^8.4.32",
|
||||||
|
"tailwindcss": "^3.3.6",
|
||||||
|
"vite": "^5.2.11",
|
||||||
|
"vite-plugin-node-polyfills": "^0.21.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"react-dom": "^18.0.0"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@3.2.1"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import App from "@medusajs/dashboard"
|
||||||
|
import React from "react"
|
||||||
|
import { createRoot } from "react-dom/client"
|
||||||
|
|
||||||
|
import "./index.css"
|
||||||
|
|
||||||
|
const container = document.getElementById("root")
|
||||||
|
const root = createRoot(container!)
|
||||||
|
root.render(<App />)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@import "@medusajs/dashboard/css";
|
||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||||
|
<link rel="icon" href="data:," data-placeholder-favicon />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="./entry.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export { build } from "./lib/build"
|
||||||
|
export { develop } from "./lib/develop"
|
||||||
|
export { serve } from "./lib/serve"
|
||||||
|
|
||||||
|
export * from "./types"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { BundlerOptions } from "../types"
|
||||||
|
import { getViteConfig } from "./config"
|
||||||
|
|
||||||
|
export async function build(options: BundlerOptions) {
|
||||||
|
const vite = await import("vite")
|
||||||
|
|
||||||
|
const viteConfig = await getViteConfig(options)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await vite.build(
|
||||||
|
vite.mergeConfig(viteConfig, { mode: "production", logLevel: "silent" })
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw new Error("Failed to build admin panel")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import path from "path"
|
||||||
|
import { Config } from "tailwindcss"
|
||||||
|
import type { InlineConfig } from "vite"
|
||||||
|
import { nodePolyfills } from "vite-plugin-node-polyfills"
|
||||||
|
|
||||||
|
import { BundlerOptions } from "../types"
|
||||||
|
|
||||||
|
export async function getViteConfig(
|
||||||
|
options: BundlerOptions
|
||||||
|
): Promise<InlineConfig> {
|
||||||
|
const { searchForWorkspaceRoot } = await import("vite")
|
||||||
|
const { default: react } = await import("@vitejs/plugin-react")
|
||||||
|
const { default: inject } = await import("@medusajs/admin-vite-plugin")
|
||||||
|
|
||||||
|
const getPort = await import("get-port")
|
||||||
|
const hmrPort = await getPort.default()
|
||||||
|
|
||||||
|
const root = path.resolve(__dirname, "./")
|
||||||
|
|
||||||
|
return {
|
||||||
|
root: path.resolve(__dirname, "./"),
|
||||||
|
base: options.path,
|
||||||
|
build: {
|
||||||
|
emptyOutDir: true,
|
||||||
|
outDir: path.resolve(process.cwd(), options.outDir),
|
||||||
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
include: ["@medusajs/dashboard", "react-dom/client"],
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
__BASE__: JSON.stringify(options.path),
|
||||||
|
/**
|
||||||
|
* TODO: Accept backend url from config to support hosting the admin elsewhere.
|
||||||
|
* The empty string should be the default value, as that ensures that requests
|
||||||
|
* are made to the server that serves the admin dashboard.
|
||||||
|
*/
|
||||||
|
__BACKEND_URL__: JSON.stringify(""),
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
open: true,
|
||||||
|
fs: {
|
||||||
|
allow: [
|
||||||
|
searchForWorkspaceRoot(process.cwd()),
|
||||||
|
path.resolve(__dirname, "../../medusa"),
|
||||||
|
path.resolve(__dirname, "../../app"),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hmr: {
|
||||||
|
port: hmrPort,
|
||||||
|
},
|
||||||
|
middlewareMode: true,
|
||||||
|
},
|
||||||
|
css: {
|
||||||
|
postcss: {
|
||||||
|
plugins: [
|
||||||
|
require("tailwindcss")({
|
||||||
|
config: createTailwindConfig(root),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* TODO: Remove polyfills, they are currently only required for the
|
||||||
|
* `axios` dependency in the dashboard. Once we have the new SDK,
|
||||||
|
* we should remove this, and leave it up to the user to include
|
||||||
|
* polyfills if they need them.
|
||||||
|
*/
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
inject(),
|
||||||
|
nodePolyfills({
|
||||||
|
include: ["crypto", "util", "stream"],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTailwindConfig(entry: string) {
|
||||||
|
const root = path.join(entry, "**/*.{js,ts,jsx,tsx}")
|
||||||
|
const html = path.join(entry, "index.html")
|
||||||
|
|
||||||
|
let dashboard = ""
|
||||||
|
|
||||||
|
try {
|
||||||
|
dashboard = path.join(
|
||||||
|
path.dirname(require.resolve("@medusajs/dashboard")),
|
||||||
|
"**/*.{js,ts,jsx,tsx}"
|
||||||
|
)
|
||||||
|
} catch (_e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
let ui: string = ""
|
||||||
|
|
||||||
|
try {
|
||||||
|
ui = path.join(
|
||||||
|
path.dirname(require.resolve("@medusajs/ui")),
|
||||||
|
"**/*.{js,ts,jsx,tsx}"
|
||||||
|
)
|
||||||
|
} catch (_e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
presets: [require("@medusajs/ui-preset")],
|
||||||
|
content: [html, root, dashboard, ui],
|
||||||
|
darkMode: "class",
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import express from "express"
|
||||||
|
|
||||||
|
import { BundlerOptions } from "../types"
|
||||||
|
import { getViteConfig } from "./config"
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
export async function develop(options: BundlerOptions) {
|
||||||
|
const vite = await import("vite")
|
||||||
|
|
||||||
|
try {
|
||||||
|
const viteConfig = await getViteConfig(options)
|
||||||
|
const server = await vite.createServer(
|
||||||
|
vite.mergeConfig(viteConfig, { logLevel: "info", mode: "development" })
|
||||||
|
)
|
||||||
|
|
||||||
|
router.use(server.middlewares)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw new Error("Could not start development server")
|
||||||
|
}
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Request, Response, Router, static as static_ } from "express"
|
||||||
|
import fs from "fs"
|
||||||
|
import { ServerResponse } from "http"
|
||||||
|
import path from "path"
|
||||||
|
|
||||||
|
type ServeOptions = {
|
||||||
|
outDir: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
export async function serve(options: ServeOptions) {
|
||||||
|
const htmlPath = path.resolve(options.outDir, "index.html")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The admin UI should always be built at this point, but in the
|
||||||
|
* rare case that another plugin terminated a previous startup, the admin
|
||||||
|
* may not have been built correctly. Here we check if the admin UI
|
||||||
|
* build files exist, and if not, we throw an error, providing the
|
||||||
|
* user with instructions on how to fix their build.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const indexExists = fs.existsSync(htmlPath)
|
||||||
|
|
||||||
|
if (!indexExists) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find the admin UI build files. Please run "medusa-admin build" or enable "autoRebuild" in the plugin options to build the admin UI.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = fs.readFileSync(htmlPath, "utf-8")
|
||||||
|
|
||||||
|
const sendHtml = (_req: Request, res: Response) => {
|
||||||
|
res.setHeader("Cache-Control", "no-cache")
|
||||||
|
res.setHeader("Vary", "Origin, Cache-Control")
|
||||||
|
res.send(html)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setStaticHeaders = (res: ServerResponse) => {
|
||||||
|
res.setHeader("Cache-Control", "max-age=31536000, immutable")
|
||||||
|
res.setHeader("Vary", "Origin, Cache-Control")
|
||||||
|
}
|
||||||
|
|
||||||
|
router.get("/", sendHtml)
|
||||||
|
router.use(
|
||||||
|
static_(options.outDir, {
|
||||||
|
setHeaders: setStaticHeaders,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
router.get(`/*`, sendHtml)
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { AdminOptions } from "@medusajs/types"
|
||||||
|
|
||||||
|
export type BundlerOptions = Required<Pick<AdminOptions, "outDir" | "path">> &
|
||||||
|
Pick<AdminOptions, "vite">
|
||||||
+4
-1
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"jsx": "react",
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"module": "ES2020",
|
"module": "ES2020",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
@@ -14,5 +16,6 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"],
|
||||||
|
"exclude": ["tsup.config.ts", "node_modules", "dist"]
|
||||||
}
|
}
|
||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
import { defineConfig } from "tsup"
|
import { defineConfig } from "tsup"
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
entry: ["./src/index.ts", "./src/cli/index.ts"],
|
entry: ["src/index.ts"],
|
||||||
format: ["esm"],
|
format: ["cjs"],
|
||||||
dts: true,
|
dts: true,
|
||||||
|
clean: true,
|
||||||
})
|
})
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "@medusajs/admin-shared",
|
"name": "@medusajs/admin-shared",
|
||||||
"version": "0.0.0",
|
"description": "Shared code for Medusa admin packages.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"author": "Kasper Kristensen <kasper@medusajs.com>",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"package.json"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc"
|
"build": "tsc"
|
||||||
},
|
},
|
||||||
|
|||||||
File renamed without changes.
+6
-5
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@medusajs/vite-plugin-extension",
|
"name": "@medusajs/admin-vite-plugin",
|
||||||
"version": "0.0.0",
|
"version": "0.0.1",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"module": "dist/index.mjs",
|
"module": "dist/index.mjs",
|
||||||
@@ -11,19 +11,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist",
|
||||||
|
"package.json"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsup"
|
"build": "tsup"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/types": "7.22.5",
|
"@babel/types": "7.22.5",
|
||||||
"@medusajs/admin-shared": "*",
|
"@medusajs/admin-shared": "0.0.1",
|
||||||
"@types/babel__traverse": "7.20.5",
|
"@types/babel__traverse": "7.20.5",
|
||||||
"@types/node": "^20.10.4",
|
"@types/node": "^20.10.4",
|
||||||
"tsup": "8.0.1",
|
"tsup": "8.0.1",
|
||||||
"typescript": "5.3.3",
|
"typescript": "5.3.3",
|
||||||
"vite": "5.0.10"
|
"vite": "^5.2.11"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vite": "^5.0.0"
|
"vite": "^5.0.0"
|
||||||
+19
-19
@@ -18,7 +18,7 @@ import { InjectionZone, injectionZones } from "@medusajs/admin-shared"
|
|||||||
|
|
||||||
const traverse = (_traverse as any).default as typeof _traverse
|
const traverse = (_traverse as any).default as typeof _traverse
|
||||||
|
|
||||||
const VIRTUAL_PREFIX = "/@virtual/medusajs-vite-plugin-extension/"
|
const VIRTUAL_PREFIX = "/@virtual/medusajs-admin-vite-plugin/"
|
||||||
const IMPORT_PREFIX = "medusa-admin:"
|
const IMPORT_PREFIX = "medusa-admin:"
|
||||||
|
|
||||||
const WIDGET_MODULE = `${IMPORT_PREFIX}widgets/`
|
const WIDGET_MODULE = `${IMPORT_PREFIX}widgets/`
|
||||||
@@ -321,7 +321,7 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
async function generateWidgetEntrypoint(zone: InjectionZone) {
|
async function generateWidgetEntrypoint(zone: InjectionZone) {
|
||||||
const files = (
|
const files = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Array.from(_sources).map((source) =>
|
Array.from(_sources).map(async (source) =>
|
||||||
traverseDirectory(`${source}/widgets`)
|
traverseDirectory(`${source}/widgets`)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -463,7 +463,7 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
async function generateRouteEntrypoint(get: "page" | "link") {
|
async function generateRouteEntrypoint(get: "page" | "link") {
|
||||||
const files = (
|
const files = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Array.from(_sources).map((source) =>
|
Array.from(_sources).map(async (source) =>
|
||||||
traverseDirectory(`${source}/routes`, "page", { min: 1 })
|
traverseDirectory(`${source}/routes`, "page", { min: 1 })
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -496,11 +496,11 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
|
|
||||||
const exportString = `export default {
|
const exportString = `export default {
|
||||||
${get}s: [${validatedRoutes
|
${get}s: [${validatedRoutes
|
||||||
.map((file, index) =>
|
.map((file, index) => {
|
||||||
get === "page"
|
return get === "page"
|
||||||
? `{ path: "${createPath(file)}", file: "${file}" }`
|
? `{ path: "${createPath(file)}", file: "${file}" }`
|
||||||
: `{ path: "${createPath(file)}", ...routeConfig${index}.link }`
|
: `{ path: "${createPath(file)}", ...routeConfig${index}.link }`
|
||||||
)
|
})
|
||||||
.join(", ")}],
|
.join(", ")}],
|
||||||
}`
|
}`
|
||||||
|
|
||||||
@@ -610,7 +610,7 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
async function generateSettingEntrypoint(get: "page" | "card") {
|
async function generateSettingEntrypoint(get: "page" | "card") {
|
||||||
const files = (
|
const files = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Array.from(_sources).map((source) =>
|
Array.from(_sources).map(async (source) =>
|
||||||
traverseDirectory(`${source}/settings`, "page", { min: 1, max: 1 })
|
traverseDirectory(`${source}/settings`, "page", { min: 1, max: 1 })
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -643,11 +643,11 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
|
|
||||||
const exportString = `export default {
|
const exportString = `export default {
|
||||||
${get}s: [${validatedSettings
|
${get}s: [${validatedSettings
|
||||||
.map((file, index) =>
|
.map((file, index) => {
|
||||||
get === "page"
|
return get === "page"
|
||||||
? `{ path: "${createPath(file)}", file: "${file}" }`
|
? `{ path: "${createPath(file)}", file: "${file}" }`
|
||||||
: `{ path: "${createPath(file)}", ...settingConfig${index}.card }`
|
: `{ path: "${createPath(file)}", ...settingConfig${index}.card }`
|
||||||
)
|
})
|
||||||
.join(", ")}],
|
.join(", ")}],
|
||||||
}`
|
}`
|
||||||
|
|
||||||
@@ -701,7 +701,7 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
const module = server.moduleGraph.getModuleById(moduleId)
|
const module = server.moduleGraph.getModuleById(moduleId)
|
||||||
|
|
||||||
if (module) {
|
if (module) {
|
||||||
server.reloadModule(module)
|
await server.reloadModule(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -719,7 +719,7 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
const module = server.moduleGraph.getModuleById(fullModuleId)
|
const module = server.moduleGraph.getModuleById(fullModuleId)
|
||||||
|
|
||||||
if (module) {
|
if (module) {
|
||||||
server.reloadModule(module)
|
await server.reloadModule(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -737,7 +737,7 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
const module = server.moduleGraph.getModuleById(fullModuleId)
|
const module = server.moduleGraph.getModuleById(fullModuleId)
|
||||||
|
|
||||||
if (module) {
|
if (module) {
|
||||||
server.reloadModule(module)
|
await server.reloadModule(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -754,7 +754,7 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
|
|
||||||
if (module) {
|
if (module) {
|
||||||
_extensionGraph.delete(file)
|
_extensionGraph.delete(file)
|
||||||
server.reloadModule(module)
|
await server.reloadModule(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -775,7 +775,7 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "@medusajs/vite-plugin-extension",
|
name: "@medusajs/admin-vite-plugin",
|
||||||
configureServer(s) {
|
configureServer(s) {
|
||||||
server = s
|
server = s
|
||||||
logger = s.config.logger
|
logger = s.config.logger
|
||||||
@@ -835,8 +835,8 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
watcher.on("unlink", (file) => {
|
watcher.on("unlink", async (file) => {
|
||||||
handleExtensionUnlink(file)
|
await handleExtensionUnlink(file)
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -876,9 +876,9 @@ export default function inject(args?: InjectArgs): PluginOption {
|
|||||||
|
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
closeBundle() {
|
async closeBundle() {
|
||||||
if (watcher) {
|
if (watcher) {
|
||||||
watcher.close()
|
await watcher.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
File renamed without changes.
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import { defineConfig } from "tsup";
|
import { defineConfig } from "tsup"
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
entry: ["./src/index.ts"],
|
entry: ["./src/index.ts"],
|
||||||
format: ["cjs", "esm"],
|
format: ["cjs", "esm"],
|
||||||
dts: true,
|
dts: true,
|
||||||
});
|
})
|
||||||
@@ -1,19 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "@medusajs/dashboard",
|
"name": "@medusajs/dashboard",
|
||||||
"private": true,
|
"version": "0.0.1",
|
||||||
"version": "0.0.0",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"generate:static": "node ./scripts/generate-countries.js && prettier --write ./src/lib/countries.ts && node ./scripts/generate-currencies.js && prettier --write ./src/lib/currencies.ts",
|
"generate:static": "node ./scripts/generate-countries.js && prettier --write ./src/lib/countries.ts && node ./scripts/generate-currencies.js && prettier --write ./src/lib/currencies.ts",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "tsup && node ./scripts/generate-types.js",
|
||||||
|
"build:preview": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
|
||||||
},
|
},
|
||||||
"main": "index.html",
|
"main": "dist/app.js",
|
||||||
|
"module": "dist/app.mjs",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/app.mjs",
|
||||||
|
"require": "./dist/app.js",
|
||||||
|
"types": "./dist/index.d.ts"
|
||||||
|
},
|
||||||
|
"./css": {
|
||||||
|
"import": "./dist/app.css",
|
||||||
|
"require": "./dist/app.css"
|
||||||
|
}
|
||||||
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"index.html",
|
"dist",
|
||||||
"public",
|
|
||||||
"src",
|
|
||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -21,8 +32,8 @@
|
|||||||
"@dnd-kit/core": "^6.1.0",
|
"@dnd-kit/core": "^6.1.0",
|
||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
"@hookform/resolvers": "3.3.2",
|
"@hookform/resolvers": "3.3.2",
|
||||||
"@medusajs/icons": "workspace:^",
|
"@medusajs/icons": "1.2.1",
|
||||||
"@medusajs/ui": "workspace:^",
|
"@medusajs/ui": "3.0.0",
|
||||||
"@radix-ui/react-collapsible": "1.0.3",
|
"@radix-ui/react-collapsible": "1.0.3",
|
||||||
"@radix-ui/react-hover-card": "^1.0.7",
|
"@radix-ui/react-hover-card": "^1.0.7",
|
||||||
"@tanstack/react-query": "^5.28.14",
|
"@tanstack/react-query": "^5.28.14",
|
||||||
@@ -47,13 +58,12 @@
|
|||||||
"react-jwt": "^1.2.0",
|
"react-jwt": "^1.2.0",
|
||||||
"react-resizable-panels": "^2.0.16",
|
"react-resizable-panels": "^2.0.16",
|
||||||
"react-router-dom": "6.20.1",
|
"react-router-dom": "6.20.1",
|
||||||
"zod": "3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@medusajs/medusa": "workspace:^",
|
"@medusajs/admin-vite-plugin": "0.0.1",
|
||||||
"@medusajs/types": "workspace:^",
|
"@medusajs/types": "1.11.16",
|
||||||
"@medusajs/ui-preset": "workspace:^",
|
"@medusajs/ui-preset": "1.1.3",
|
||||||
"@medusajs/vite-plugin-extension": "workspace:^",
|
|
||||||
"@types/node": "^20.11.15",
|
"@types/node": "^20.11.15",
|
||||||
"@types/react": "^18.2.79",
|
"@types/react": "^18.2.79",
|
||||||
"@types/react-dom": "^18.2.25",
|
"@types/react-dom": "^18.2.25",
|
||||||
@@ -62,8 +72,9 @@
|
|||||||
"postcss": "^8.4.33",
|
"postcss": "^8.4.33",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
|
"tsup": "^8.0.2",
|
||||||
"typescript": "5.2.2",
|
"typescript": "5.2.2",
|
||||||
"vite": "5.0.10"
|
"vite": "^5.2.11"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@3.2.1"
|
"packageManager": "yarn@3.2.1"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* We can't use the `tsc` command to generate types for the project because it
|
||||||
|
* will generate types for each file in the project, which isn't needed. We only
|
||||||
|
* need a single file that exports the App component.
|
||||||
|
*/
|
||||||
|
async function generateTypes() {
|
||||||
|
const fs = require("fs")
|
||||||
|
const path = require("path")
|
||||||
|
|
||||||
|
const distDir = path.resolve(__dirname, "../dist")
|
||||||
|
const filePath = path.join(distDir, "index.d.ts")
|
||||||
|
|
||||||
|
const fileContent = `
|
||||||
|
import * as react_jsx_runtime from "react/jsx-runtime"
|
||||||
|
|
||||||
|
declare const App: () => react_jsx_runtime.JSX.Element
|
||||||
|
|
||||||
|
export default App
|
||||||
|
`
|
||||||
|
|
||||||
|
// Ensure the dist directory exists
|
||||||
|
if (!fs.existsSync(distDir)) {
|
||||||
|
fs.mkdirSync(distDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the content to the index.d.ts file
|
||||||
|
fs.writeFileSync(filePath, fileContent.trim(), "utf8")
|
||||||
|
|
||||||
|
console.log(`File created at ${filePath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
try {
|
||||||
|
await generateTypes()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
})()
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
import { Toaster } from "@medusajs/ui"
|
import { Toaster } from "@medusajs/ui"
|
||||||
import { QueryClientProvider } from "@tanstack/react-query"
|
import { QueryClientProvider } from "@tanstack/react-query"
|
||||||
|
|
||||||
|
import { I18n } from "./components/utilities/i18n"
|
||||||
import { queryClient } from "./lib/medusa"
|
import { queryClient } from "./lib/medusa"
|
||||||
import { RouterProvider } from "./providers/router-provider"
|
import { RouterProvider } from "./providers/router-provider"
|
||||||
import { ThemeProvider } from "./providers/theme-provider"
|
import { ThemeProvider } from "./providers/theme-provider"
|
||||||
|
|
||||||
|
import "./index.css"
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
|
<I18n />
|
||||||
<RouterProvider />
|
<RouterProvider />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
@@ -64,9 +64,11 @@ export const FileUpload = ({
|
|||||||
|
|
||||||
const fileList = Array.from(files)
|
const fileList = Array.from(files)
|
||||||
const fileObj = fileList.map((file) => {
|
const fileObj = fileList.map((file) => {
|
||||||
|
const id = Math.random().toString(36).substring(7)
|
||||||
|
|
||||||
const previewUrl = URL.createObjectURL(file)
|
const previewUrl = URL.createObjectURL(file)
|
||||||
return {
|
return {
|
||||||
id: crypto.randomUUID(),
|
id: id,
|
||||||
url: previewUrl,
|
url: previewUrl,
|
||||||
file,
|
file,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,12 @@ import { Avatar, Text } from "@medusajs/ui"
|
|||||||
import * as Collapsible from "@radix-ui/react-collapsible"
|
import * as Collapsible from "@radix-ui/react-collapsible"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
|
import { ComponentType } from "react"
|
||||||
import { useStore } from "../../../hooks/api/store"
|
import { useStore } from "../../../hooks/api/store"
|
||||||
import { Skeleton } from "../../common/skeleton"
|
import { Skeleton } from "../../common/skeleton"
|
||||||
import { NavItem, NavItemProps } from "../../layout/nav-item"
|
import { NavItem, NavItemProps } from "../../layout/nav-item"
|
||||||
import { Shell } from "../../layout/shell"
|
import { Shell } from "../../layout/shell"
|
||||||
|
|
||||||
import extensions from "medusa-admin:routes/links"
|
|
||||||
|
|
||||||
export const MainLayout = () => {
|
export const MainLayout = () => {
|
||||||
return (
|
return (
|
||||||
<Shell>
|
<Shell>
|
||||||
@@ -167,6 +166,10 @@ const CoreRouteSection = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extensions = {
|
||||||
|
links: null as { path: string; label: string; icon?: ComponentType }[] | null,
|
||||||
|
}
|
||||||
|
|
||||||
const ExtensionRouteSection = () => {
|
const ExtensionRouteSection = () => {
|
||||||
if (!extensions.links || extensions.links.length === 0) {
|
if (!extensions.links || extensions.links.length === 0) {
|
||||||
return null
|
return null
|
||||||
|
|||||||
+4
-3
@@ -1,7 +1,7 @@
|
|||||||
import { DatePicker } from "@medusajs/ui"
|
import { DatePicker } from "@medusajs/ui"
|
||||||
import { ComponentPropsWithoutRef } from "react"
|
import { ComponentPropsWithoutRef } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { languages } from "../../../i18n/config"
|
import { languages } from "../../../i18n/languages"
|
||||||
|
|
||||||
type LocalizedDatePickerProps = Omit<
|
type LocalizedDatePickerProps = Omit<
|
||||||
ComponentPropsWithoutRef<typeof DatePicker>,
|
ComponentPropsWithoutRef<typeof DatePicker>,
|
||||||
@@ -14,8 +14,9 @@ export const LocalizedDatePicker = ({
|
|||||||
}: LocalizedDatePickerProps) => {
|
}: LocalizedDatePickerProps) => {
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
|
|
||||||
const locale = languages.find((lang) => lang.code === i18n.language)
|
const locale = languages.find(
|
||||||
?.date_locale
|
(lang) => lang.code === i18n.language
|
||||||
|
)?.date_locale
|
||||||
|
|
||||||
const translations = {
|
const translations = {
|
||||||
cancel: t("actions.cancel"),
|
cancel: t("actions.cancel"),
|
||||||
|
|||||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,23 @@
|
|||||||
|
import i18n from "i18next"
|
||||||
|
import LanguageDetector from "i18next-browser-languagedetector"
|
||||||
|
import { initReactI18next } from "react-i18next"
|
||||||
|
|
||||||
|
import { defaultI18nOptions } from "../../../i18n/config"
|
||||||
|
|
||||||
|
export const I18n = () => {
|
||||||
|
if (i18n.isInitialized) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
i18n
|
||||||
|
.use(
|
||||||
|
new LanguageDetector(null, {
|
||||||
|
lookupCookie: "lng",
|
||||||
|
lookupLocalStorage: "lng",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.use(initReactI18next)
|
||||||
|
.init(defaultI18nOptions)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./i18n"
|
||||||
@@ -2,7 +2,7 @@ import { format, formatDistance, sub } from "date-fns"
|
|||||||
import { enUS } from "date-fns/locale"
|
import { enUS } from "date-fns/locale"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { languages } from "../i18n/config"
|
import { languages } from "../i18n/languages"
|
||||||
|
|
||||||
export const useDate = () => {
|
export const useDate = () => {
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
|
|||||||
@@ -1,34 +1,19 @@
|
|||||||
import { enUS } from "date-fns/locale"
|
import { InitOptions } from "i18next"
|
||||||
import i18n from "i18next"
|
|
||||||
import LanguageDetector from "i18next-browser-languagedetector"
|
|
||||||
import Backend, { type HttpBackendOptions } from "i18next-http-backend"
|
|
||||||
import { initReactI18next } from "react-i18next"
|
|
||||||
|
|
||||||
import { Language } from "./types"
|
import translations from "./translations"
|
||||||
|
|
||||||
void i18n
|
export const defaultI18nOptions: InitOptions = {
|
||||||
.use(Backend)
|
|
||||||
.use(LanguageDetector)
|
|
||||||
.use(initReactI18next)
|
|
||||||
.init<HttpBackendOptions>({
|
|
||||||
fallbackLng: "en-US",
|
|
||||||
load: "languageOnly",
|
|
||||||
debug: process.env.NODE_ENV === "development",
|
debug: process.env.NODE_ENV === "development",
|
||||||
|
detection: {
|
||||||
|
caches: ["cookie", "localStorage", "header"],
|
||||||
|
lookupCookie: "lng",
|
||||||
|
lookupLocalStorage: "lng",
|
||||||
|
order: ["cookie", "localStorage", "header"],
|
||||||
|
},
|
||||||
|
fallbackLng: "en",
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
},
|
},
|
||||||
backend: {
|
resources: translations,
|
||||||
loadPath: "/locales/{{lng}}/{{ns}}.json",
|
supportedLngs: Object.keys(translations),
|
||||||
},
|
}
|
||||||
})
|
|
||||||
|
|
||||||
export const languages: Language[] = [
|
|
||||||
{
|
|
||||||
code: "en-US",
|
|
||||||
display_name: "English (US)",
|
|
||||||
ltr: true,
|
|
||||||
date_locale: enUS,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export default i18n
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { enUS } from "date-fns/locale"
|
||||||
|
import { Language } from "./types"
|
||||||
|
|
||||||
|
export const languages: Language[] = [
|
||||||
|
{
|
||||||
|
code: "en-US",
|
||||||
|
display_name: "English (US)",
|
||||||
|
ltr: true,
|
||||||
|
date_locale: enUS,
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"$schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"cancel": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"save": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"create": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"extensions": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"cancel",
|
||||||
|
"save",
|
||||||
|
"create",
|
||||||
|
"createItem",
|
||||||
|
"delete",
|
||||||
|
"deleteItem",
|
||||||
|
"edit",
|
||||||
|
"editItem",
|
||||||
|
"extensions",
|
||||||
|
"details"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"products": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"variants": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"availableInSalesChannels": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain", "variants", "availableInSalesChannels"]
|
||||||
|
},
|
||||||
|
"categories": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"collections": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"inventory": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"customers": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"customerGroups": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"orders": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"draftOrders": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"discounts": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"promotions": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"taxRegions": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"taxRates": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"campaigns": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"giftCards": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"pricing": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain"]
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"admin": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"member": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"developer": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["admin", "member", "developer"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["domain", "role", "roles"]
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"scheduled": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"expired": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"subtitle": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"handle": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"category": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"categories": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"collection": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"discountable": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sales_channels": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"title",
|
||||||
|
"description",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"password",
|
||||||
|
"subtitle",
|
||||||
|
"handle",
|
||||||
|
"type",
|
||||||
|
"category",
|
||||||
|
"categories",
|
||||||
|
"collection",
|
||||||
|
"discountable",
|
||||||
|
"tags",
|
||||||
|
"sales_channels"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"general",
|
||||||
|
"products",
|
||||||
|
"categories",
|
||||||
|
"collections",
|
||||||
|
"inventory",
|
||||||
|
"customers",
|
||||||
|
"customerGroups",
|
||||||
|
"orders",
|
||||||
|
"draftOrders",
|
||||||
|
"discounts",
|
||||||
|
"giftCards",
|
||||||
|
"pricing",
|
||||||
|
"users",
|
||||||
|
"fields"
|
||||||
|
]
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,7 @@
|
|||||||
|
import en from "./en.json"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
en: {
|
||||||
|
translation: en,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { Locale } from "date-fns"
|
import type { Locale } from "date-fns"
|
||||||
import en from "../../public/locales/en-US/translation.json"
|
import enUS from "./translations/en.json"
|
||||||
|
|
||||||
const resources = {
|
const resources = {
|
||||||
translation: en,
|
translation: enUS,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type Resources = typeof resources
|
export type Resources = typeof resources
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { stringify } from "qs"
|
import { stringify } from "qs"
|
||||||
|
|
||||||
const baseUrl =
|
const baseUrl = __BACKEND_URL__ ?? "http://localhost:9000"
|
||||||
import.meta.env.VITE_MEDUSA_ADMIN_BACKEND_URL || "http://localhost:9000"
|
|
||||||
|
|
||||||
const commonHeaders: HeadersInit = {
|
const commonHeaders: HeadersInit = {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import Medusa from "@medusajs/medusa-js"
|
import Medusa from "@medusajs/medusa-js"
|
||||||
import { QueryClient } from "@tanstack/react-query"
|
import { QueryClient } from "@tanstack/react-query"
|
||||||
|
|
||||||
export const MEDUSA_BACKEND_URL =
|
export const MEDUSA_BACKEND_URL = __BACKEND_URL__ ?? "http://localhost:9000"
|
||||||
import.meta.env.VITE_MEDUSA_ADMIN_BACKEND_URL || "http://localhost:9000"
|
|
||||||
|
|
||||||
export const queryClient = new QueryClient({
|
export const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import ReactDOM from "react-dom/client"
|
import ReactDOM from "react-dom/client"
|
||||||
import App from "./app.js"
|
import App from "./app.js"
|
||||||
import "./i18n/config.js"
|
|
||||||
import "./index.css"
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import { RouteObject } from "react-router-dom"
|
import { RouteObject } from "react-router-dom"
|
||||||
|
|
||||||
import routes from "medusa-admin:routes/pages"
|
// import routes from "medusa-admin:routes/pages"
|
||||||
|
|
||||||
|
export const RouteExtensions: RouteObject[] = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UI Route extensions.
|
* UI Route extensions.
|
||||||
*/
|
*/
|
||||||
export const RouteExtensions: RouteObject[] = routes.pages.map((ext) => {
|
// export const RouteExtensions: RouteObject[] = routes.pages.map((ext) => {
|
||||||
return {
|
// return {
|
||||||
path: ext.path,
|
// path: ext.path,
|
||||||
async lazy() {
|
// async lazy() {
|
||||||
const { default: Component } = await import(/* @vite-ignore */ ext.file)
|
// const { default: Component } = await import(/* @vite-ignore */ ext.file)
|
||||||
return { Component }
|
// return { Component }
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
@@ -15,9 +15,9 @@ import {
|
|||||||
import { Outlet, RouteObject } from "react-router-dom"
|
import { Outlet, RouteObject } from "react-router-dom"
|
||||||
|
|
||||||
import { ProtectedRoute } from "../../components/authentication/protected-route"
|
import { ProtectedRoute } from "../../components/authentication/protected-route"
|
||||||
import { ErrorBoundary } from "../../components/error/error-boundary"
|
|
||||||
import { MainLayout } from "../../components/layout/main-layout"
|
import { MainLayout } from "../../components/layout/main-layout"
|
||||||
import { SettingsLayout } from "../../components/layout/settings-layout"
|
import { SettingsLayout } from "../../components/layout/settings-layout"
|
||||||
|
import { ErrorBoundary } from "../../components/utilities/error-boundary"
|
||||||
import { InventoryItemRes, PriceListRes } from "../../types/api-responses"
|
import { InventoryItemRes, PriceListRes } from "../../types/api-responses"
|
||||||
|
|
||||||
import { RouteExtensions } from "./route-extensions"
|
import { RouteExtensions } from "./route-extensions"
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import {
|
|||||||
|
|
||||||
import { RouteMap } from "./route-map"
|
import { RouteMap } from "./route-map"
|
||||||
|
|
||||||
const router = createBrowserRouter(RouteMap)
|
const router = createBrowserRouter(RouteMap, {
|
||||||
|
basename: __BASE__ || "/",
|
||||||
|
})
|
||||||
|
|
||||||
export const RouterProvider = () => {
|
export const RouterProvider = () => {
|
||||||
return <Provider router={router} />
|
return <Provider router={router} />
|
||||||
|
|||||||
+14
-10
@@ -1,15 +1,19 @@
|
|||||||
import settings from "medusa-admin:settings/pages"
|
|
||||||
import { RouteObject } from "react-router-dom"
|
import { RouteObject } from "react-router-dom"
|
||||||
|
|
||||||
|
// import settings from "medusa-admin:settings/pages"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UI Settings extensions.
|
* UI Settings extensions.
|
||||||
*/
|
*/
|
||||||
export const SettingsExtensions: RouteObject[] = settings.pages.map((ext) => {
|
|
||||||
return {
|
export const SettingsExtensions: RouteObject[] = []
|
||||||
path: `/settings${ext.path}`,
|
|
||||||
async lazy() {
|
// export const SettingsExtensions: RouteObject[] = settings.pages.map((ext) => {
|
||||||
const { default: Component } = await import(/* @vite-ignore */ ext.file)
|
// return {
|
||||||
return { Component }
|
// path: `/settings${ext.path}`,
|
||||||
},
|
// async lazy() {
|
||||||
}
|
// const { default: Component } = await import(/* @vite-ignore */ ext.file)
|
||||||
})
|
// return { Component }
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// })
|
||||||
@@ -1,28 +1,30 @@
|
|||||||
import { Container, Heading } from "@medusajs/ui";
|
import { Container, Heading } from "@medusajs/ui"
|
||||||
|
|
||||||
import after from "medusa-admin:widgets/product_category/details/after";
|
// import after from "medusa-admin:widgets/product_category/details/after"
|
||||||
import before from "medusa-admin:widgets/product_category/details/before";
|
// import before from "medusa-admin:widgets/product_category/details/before"
|
||||||
|
|
||||||
export const CategoryDetails = () => {
|
export const CategoryDetails = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
{before.widgets.map((w, i) => {
|
{/* {before.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})} */}
|
||||||
|
|
||||||
<Container>
|
<Container>
|
||||||
<Heading>Category</Heading>
|
<Heading>Category</Heading>
|
||||||
</Container>
|
</Container>
|
||||||
{after.widgets.map((w, i) => {
|
|
||||||
|
{/* {after.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})} */}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
@@ -1,28 +1,30 @@
|
|||||||
import { Container, Heading } from "@medusajs/ui";
|
import { Container, Heading } from "@medusajs/ui"
|
||||||
|
|
||||||
import after from "medusa-admin:widgets/product_category/list/after";
|
// import after from "medusa-admin:widgets/product_category/list/after"
|
||||||
import before from "medusa-admin:widgets/product_category/list/before";
|
// import before from "medusa-admin:widgets/product_category/list/before"
|
||||||
|
|
||||||
export const CategoriesList = () => {
|
export const CategoriesList = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
{before.widgets.map((w, i) => {
|
{/* {before.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})} */}
|
||||||
|
|
||||||
<Container>
|
<Container>
|
||||||
<Heading>Categories</Heading>
|
<Heading>Categories</Heading>
|
||||||
</Container>
|
</Container>
|
||||||
{after.widgets.map((w, i) => {
|
|
||||||
|
{/* {after.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})} */}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
+9
-6
@@ -10,8 +10,8 @@ import { DiscountConditionsSection } from "./components/discount-conditions-sect
|
|||||||
import { RedemptionsSection } from "./components/discount-redemptions-section"
|
import { RedemptionsSection } from "./components/discount-redemptions-section"
|
||||||
import { discountLoader, expand } from "./loader"
|
import { discountLoader, expand } from "./loader"
|
||||||
|
|
||||||
import after from "medusa-admin:widgets/discount/details/after"
|
// import after from "medusa-admin:widgets/discount/details/after"
|
||||||
import before from "medusa-admin:widgets/discount/details/before"
|
// import before from "medusa-admin:widgets/discount/details/before"
|
||||||
|
|
||||||
export const DiscountDetail = () => {
|
export const DiscountDetail = () => {
|
||||||
const initialData = useLoaderData() as Awaited<
|
const initialData = useLoaderData() as Awaited<
|
||||||
@@ -33,13 +33,14 @@ export const DiscountDetail = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
{before.widgets.map((w, i) => {
|
{/* {before.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})} */}
|
||||||
|
|
||||||
<div className="flex flex-col gap-x-4 xl:flex-row xl:items-start">
|
<div className="flex flex-col gap-x-4 xl:flex-row xl:items-start">
|
||||||
<div className="flex w-full flex-col gap-y-2">
|
<div className="flex w-full flex-col gap-y-2">
|
||||||
<DiscountGeneralSection discount={discount} />
|
<DiscountGeneralSection discount={discount} />
|
||||||
@@ -49,13 +50,15 @@ export const DiscountDetail = () => {
|
|||||||
<RedemptionsSection redemptions={discount.usage_count} />
|
<RedemptionsSection redemptions={discount.usage_count} />
|
||||||
<DetailsSection discount={discount} />
|
<DetailsSection discount={discount} />
|
||||||
</div>
|
</div>
|
||||||
{after.widgets.map((w, i) => {
|
|
||||||
|
{/* {after.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})} */}
|
||||||
|
|
||||||
<JsonViewSection data={discount} />
|
<JsonViewSection data={discount} />
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden w-full max-w-[400px] flex-col gap-y-2 xl:flex">
|
<div className="hidden w-full max-w-[400px] flex-col gap-y-2 xl:flex">
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import after from "medusa-admin:widgets/discount/list/after"
|
// import after from "medusa-admin:widgets/discount/list/after"
|
||||||
import before from "medusa-admin:widgets/discount/list/before"
|
// import before from "medusa-admin:widgets/discount/list/before"
|
||||||
|
|
||||||
import { DiscountListTable } from "./components/discount-list-table"
|
import { DiscountListTable } from "./components/discount-list-table"
|
||||||
|
|
||||||
export const DiscountsList = () => {
|
export const DiscountsList = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
{before.widgets.map((w, i) => (
|
{/* {before.widgets.map((w, i) => (
|
||||||
<w.Component key={i} />
|
<w.Component key={i} />
|
||||||
))}
|
))} */}
|
||||||
|
|
||||||
<DiscountListTable />
|
<DiscountListTable />
|
||||||
{after.widgets.map((w, i) => (
|
|
||||||
|
{/* {after.widgets.map((w, i) => (
|
||||||
<w.Component key={i} />
|
<w.Component key={i} />
|
||||||
))}
|
))} */}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+16
-15
@@ -9,12 +9,13 @@ import { ProductSalesChannelSection } from "./components/product-sales-channel-s
|
|||||||
import { ProductVariantSection } from "./components/product-variant-section"
|
import { ProductVariantSection } from "./components/product-variant-section"
|
||||||
import { productLoader } from "./loader"
|
import { productLoader } from "./loader"
|
||||||
|
|
||||||
import after from "medusa-admin:widgets/product/details/after"
|
// import after from "medusa-admin:widgets/product/details/after"
|
||||||
import before from "medusa-admin:widgets/product/details/before"
|
// import before from "medusa-admin:widgets/product/details/before"
|
||||||
import sideAfter from "medusa-admin:widgets/product/details/side/after"
|
// import sideAfter from "medusa-admin:widgets/product/details/side/after"
|
||||||
import sideBefore from "medusa-admin:widgets/product/details/side/before"
|
// import sideBefore from "medusa-admin:widgets/product/details/side/before"
|
||||||
import { ProductOrganizationSection } from "./components/product-organization-section"
|
|
||||||
import { useProduct } from "../../../hooks/api/products"
|
import { useProduct } from "../../../hooks/api/products"
|
||||||
|
import { ProductOrganizationSection } from "./components/product-organization-section"
|
||||||
|
|
||||||
// TODO: Use product domain translations only
|
// TODO: Use product domain translations only
|
||||||
export const ProductDetail = () => {
|
export const ProductDetail = () => {
|
||||||
@@ -37,50 +38,50 @@ export const ProductDetail = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
{before.widgets.map((w, i) => {
|
{/* {before.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})} */}
|
||||||
|
|
||||||
<div className="flex flex-col gap-x-4 lg:flex-row lg:items-start">
|
<div className="flex flex-col gap-x-4 lg:flex-row lg:items-start">
|
||||||
<div className="w-full flex flex-col gap-y-2">
|
<div className="flex w-full flex-col gap-y-2">
|
||||||
<ProductGeneralSection product={product} />
|
<ProductGeneralSection product={product} />
|
||||||
<ProductMediaSection product={product} />
|
<ProductMediaSection product={product} />
|
||||||
<ProductOptionSection product={product} />
|
<ProductOptionSection product={product} />
|
||||||
<ProductVariantSection product={product} />
|
<ProductVariantSection product={product} />
|
||||||
{after.widgets.map((w, i) => {
|
{/* {after.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})} */}
|
||||||
|
|
||||||
<div className="hidden lg:block">
|
<div className="hidden lg:block">
|
||||||
<JsonViewSection data={product} root="product" />
|
<JsonViewSection data={product} root="product" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full lg:max-w-[400px] max-w-[100%] mt-2 lg:mt-0 flex flex-col gap-y-2">
|
<div className="mt-2 flex w-full max-w-[100%] flex-col gap-y-2 lg:mt-0 lg:max-w-[400px]">
|
||||||
{sideBefore.widgets.map((w, i) => {
|
{/* {sideBefore.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})} */}
|
||||||
<ProductSalesChannelSection product={product} />
|
<ProductSalesChannelSection product={product} />
|
||||||
<ProductOrganizationSection product={product} />
|
<ProductOrganizationSection product={product} />
|
||||||
<ProductAttributeSection product={product} />
|
<ProductAttributeSection product={product} />
|
||||||
{sideAfter.widgets.map((w, i) => {
|
{/* {sideAfter.widgets.map((w, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
<w.Component />
|
<w.Component />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})} */}
|
||||||
|
|
||||||
<div className="lg:hidden">
|
<div className="lg:hidden">
|
||||||
<JsonViewSection data={product} root="product" />
|
<JsonViewSection data={product} root="product" />
|
||||||
|
|||||||
@@ -1,18 +1,9 @@
|
|||||||
import after from "medusa-admin:widgets/product/list/after"
|
|
||||||
import before from "medusa-admin:widgets/product/list/before"
|
|
||||||
|
|
||||||
import { ProductListTable } from "./components/product-list-table"
|
import { ProductListTable } from "./components/product-list-table"
|
||||||
|
|
||||||
export const ProductList = () => {
|
export const ProductList = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
{before.widgets.map((w, i) => (
|
|
||||||
<w.Component key={i} />
|
|
||||||
))}
|
|
||||||
<ProductListTable />
|
<ProductListTable />
|
||||||
{after.widgets.map((w, i) => (
|
|
||||||
<w.Component key={i} />
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+7
-5
@@ -9,15 +9,15 @@ import { useTranslation } from "react-i18next"
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { Link } from "react-router-dom"
|
import { Link } from "react-router-dom"
|
||||||
|
import {
|
||||||
|
FileType,
|
||||||
|
FileUpload,
|
||||||
|
} from "../../../../../components/common/file-upload"
|
||||||
import { Form } from "../../../../../components/common/form"
|
import { Form } from "../../../../../components/common/form"
|
||||||
import {
|
import {
|
||||||
RouteFocusModal,
|
RouteFocusModal,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/route-modal"
|
} from "../../../../../components/route-modal"
|
||||||
import {
|
|
||||||
FileType,
|
|
||||||
FileUpload,
|
|
||||||
} from "../../../../../components/common/file-upload"
|
|
||||||
import { useUpdateProduct } from "../../../../../hooks/api/products"
|
import { useUpdateProduct } from "../../../../../hooks/api/products"
|
||||||
|
|
||||||
type ProductMediaViewProps = {
|
type ProductMediaViewProps = {
|
||||||
@@ -400,8 +400,10 @@ const getDefaultValues = (images: Image[] | null, thumbnail: string | null) => {
|
|||||||
})) || []
|
})) || []
|
||||||
|
|
||||||
if (thumbnail && !media.some((mediaItem) => mediaItem.url === thumbnail)) {
|
if (thumbnail && !media.some((mediaItem) => mediaItem.url === thumbnail)) {
|
||||||
|
const id = Math.random().toString(36).substring(7)
|
||||||
|
|
||||||
media.unshift({
|
media.unshift({
|
||||||
id: crypto.randomUUID(),
|
id: id,
|
||||||
url: thumbnail,
|
url: thumbnail,
|
||||||
isThumbnail: true,
|
isThumbnail: true,
|
||||||
file: null,
|
file: null,
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ import { UserDTO } from "@medusajs/types"
|
|||||||
import { Button, Container, Heading, StatusBadge, Text } from "@medusajs/ui"
|
import { Button, Container, Heading, StatusBadge, Text } from "@medusajs/ui"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { Link } from "react-router-dom"
|
import { Link } from "react-router-dom"
|
||||||
import { languages } from "../../../../../i18n/config"
|
import { languages } from "../../../../../i18n/languages"
|
||||||
|
|
||||||
type ProfileGeneralSectionProps = {
|
type ProfileGeneralSectionProps = {
|
||||||
user: UserDTO
|
user: UserDTO
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ import {
|
|||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/route-modal"
|
} from "../../../../../components/route-modal"
|
||||||
import { useUpdateUser } from "../../../../../hooks/api/users"
|
import { useUpdateUser } from "../../../../../hooks/api/users"
|
||||||
import { languages } from "../../../../../i18n/config"
|
import { languages } from "../../../../../i18n/languages"
|
||||||
|
|
||||||
type EditProfileProps = {
|
type EditProfileProps = {
|
||||||
user: Partial<Omit<UserDTO, "password_hash">>
|
user: Partial<Omit<UserDTO, "password_hash">>
|
||||||
|
|||||||
-19
@@ -7,9 +7,6 @@ import { PromotionConditionsSection } from "./components/promotion-conditions-se
|
|||||||
import { PromotionGeneralSection } from "./components/promotion-general-section"
|
import { PromotionGeneralSection } from "./components/promotion-general-section"
|
||||||
import { promotionLoader } from "./loader"
|
import { promotionLoader } from "./loader"
|
||||||
|
|
||||||
import after from "medusa-admin:widgets/promotion/details/after"
|
|
||||||
import before from "medusa-admin:widgets/promotion/details/before"
|
|
||||||
|
|
||||||
export const PromotionDetail = () => {
|
export const PromotionDetail = () => {
|
||||||
const initialData = useLoaderData() as Awaited<
|
const initialData = useLoaderData() as Awaited<
|
||||||
ReturnType<typeof promotionLoader>
|
ReturnType<typeof promotionLoader>
|
||||||
@@ -27,14 +24,6 @@ export const PromotionDetail = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
{before.widgets.map((w, i) => {
|
|
||||||
return (
|
|
||||||
<div key={i}>
|
|
||||||
<w.Component />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-x-4 xl:flex-row xl:items-start">
|
<div className="flex flex-col gap-x-4 xl:flex-row xl:items-start">
|
||||||
<div className="flex w-full flex-col gap-y-2">
|
<div className="flex w-full flex-col gap-y-2">
|
||||||
<PromotionGeneralSection promotion={promotion} />
|
<PromotionGeneralSection promotion={promotion} />
|
||||||
@@ -57,14 +46,6 @@ export const PromotionDetail = () => {
|
|||||||
<CampaignSection campaign={promotion.campaign!} />
|
<CampaignSection campaign={promotion.campaign!} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{after.widgets.map((w, i) => {
|
|
||||||
return (
|
|
||||||
<div key={i}>
|
|
||||||
<w.Component />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
|
|
||||||
<JsonViewSection data={promotion as any} />
|
<JsonViewSection data={promotion as any} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
-11
@@ -1,20 +1,9 @@
|
|||||||
import after from "medusa-admin:widgets/promotion/list/after"
|
|
||||||
import before from "medusa-admin:widgets/promotion/list/before"
|
|
||||||
|
|
||||||
import { PromotionListTable } from "./components/promotion-list-table"
|
import { PromotionListTable } from "./components/promotion-list-table"
|
||||||
|
|
||||||
export const PromotionsList = () => {
|
export const PromotionsList = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-2">
|
||||||
{before.widgets.map((w, i) => (
|
|
||||||
<w.Component key={i} />
|
|
||||||
))}
|
|
||||||
|
|
||||||
<PromotionListTable />
|
<PromotionListTable />
|
||||||
|
|
||||||
{after.widgets.map((w, i) => (
|
|
||||||
<w.Component key={i} />
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -8,3 +8,6 @@ interface ImportMetaEnv {
|
|||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
readonly env: ImportMetaEnv
|
readonly env: ImportMetaEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare const __BACKEND_URL__: string | undefined
|
||||||
|
declare const __BASE__: string
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"composite": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { defineConfig } from "tsup"
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
entry: ["./src/app.tsx"],
|
||||||
|
format: ["cjs", "esm"],
|
||||||
|
external: [
|
||||||
|
"medusa-admin:settings/pages",
|
||||||
|
"medusa-admin:routes/pages",
|
||||||
|
"medusa-admin:widgets/promotion/list/after",
|
||||||
|
"medusa-admin:widgets/promotion/list/before",
|
||||||
|
"medusa-admin:widgets/promotion/details/after",
|
||||||
|
"medusa-admin:widgets/promotion/details/before",
|
||||||
|
"medusa-admin:widgets/product/list/after",
|
||||||
|
"medusa-admin:widgets/product/list/before",
|
||||||
|
"medusa-admin:widgets/product/details/after",
|
||||||
|
"medusa-admin:widgets/product/details/before",
|
||||||
|
"medusa-admin:widgets/product/details/side/after",
|
||||||
|
"medusa-admin:widgets/product/details/side/before",
|
||||||
|
"medusa-admin:routes/links",
|
||||||
|
],
|
||||||
|
tsconfig: "tsconfig.build.json",
|
||||||
|
clean: true,
|
||||||
|
})
|
||||||
@@ -1,22 +1,18 @@
|
|||||||
import inject from "@medusajs/vite-plugin-extension"
|
import inject from "@medusajs/admin-vite-plugin"
|
||||||
import react from "@vitejs/plugin-react"
|
import react from "@vitejs/plugin-react"
|
||||||
import { defineConfig } from "vite"
|
import { defineConfig } from "vite"
|
||||||
|
|
||||||
|
const BASE = "/"
|
||||||
|
const BACKEND_URL = "http://localhost:9000"
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react(), inject()],
|
plugins: [react(), inject()],
|
||||||
|
define: {
|
||||||
|
__BASE__: JSON.stringify(BASE),
|
||||||
|
__BACKEND_URL__: JSON.stringify(BACKEND_URL),
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
open: true,
|
open: true,
|
||||||
},
|
},
|
||||||
build: {
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
manualChunks: {
|
|
||||||
react: ["react"],
|
|
||||||
"react-dom": ["react-dom"],
|
|
||||||
"react-router-dom": ["react-router-dom"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
@@ -117,7 +117,7 @@ function buildLocalCommands(cli, isLocalProject) {
|
|||||||
.option(`v2`, {
|
.option(`v2`, {
|
||||||
type: `boolean`,
|
type: `boolean`,
|
||||||
describe: `Install Medusa with the V2 feature flag enabled. WARNING: Medusa V2 is still in development and shouldn't be used in production.`,
|
describe: `Install Medusa with the V2 feature flag enabled. WARNING: Medusa V2 is still in development and shouldn't be used in production.`,
|
||||||
default: false
|
default: false,
|
||||||
}),
|
}),
|
||||||
desc: `Create a new Medusa project.`,
|
desc: `Create a new Medusa project.`,
|
||||||
handler: handlerP(newStarter),
|
handler: handlerP(newStarter),
|
||||||
@@ -238,6 +238,19 @@ function buildLocalCommands(cli, isLocalProject) {
|
|||||||
})
|
})
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
.command({
|
||||||
|
command: `build`,
|
||||||
|
desc: `Build your project.`,
|
||||||
|
builder: (_) => _,
|
||||||
|
handler: handlerP(
|
||||||
|
getCommandHandler(`build`, (args, cmd) => {
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || `development`
|
||||||
|
cmd(args)
|
||||||
|
|
||||||
|
return new Promise((resolve) => {})
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
.command({
|
.command({
|
||||||
command: `start-cluster`,
|
command: `start-cluster`,
|
||||||
desc: `Start development server in cluster mode (beta).`,
|
desc: `Start development server in cluster mode (beta).`,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"rimraf": "^5.0.1",
|
"rimraf": "^5.0.1",
|
||||||
"typeorm": "^0.3.16",
|
"typeorm": "^0.3.16",
|
||||||
"typescript": "^5.1.6",
|
"typescript": "^5.1.6",
|
||||||
|
"vite": "^5.2.11",
|
||||||
"winston": "^3.8.2"
|
"winston": "^3.8.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -3,8 +3,42 @@ import {
|
|||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
} from "../modules-sdk"
|
} from "../modules-sdk"
|
||||||
|
|
||||||
import { RedisOptions } from "ioredis"
|
import type { RedisOptions } from "ioredis"
|
||||||
import { LoggerOptions } from "typeorm"
|
import type { LoggerOptions } from "typeorm"
|
||||||
|
import type { InlineConfig } from "vite"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @interface
|
||||||
|
*
|
||||||
|
* Admin dashboard configurations.
|
||||||
|
*/
|
||||||
|
export type AdminOptions = {
|
||||||
|
/**
|
||||||
|
* Whether to disable the admin dashboard. If set to `true`, the admin dashboard is disabled,
|
||||||
|
* in both development and production environments. The default value is `false`.
|
||||||
|
*/
|
||||||
|
disable?: boolean
|
||||||
|
/**
|
||||||
|
* The path to the admin dashboard. The default value is `/app`.
|
||||||
|
*
|
||||||
|
* The value cannot be one of the reserved paths:
|
||||||
|
* - `/admin`
|
||||||
|
* - `/store`
|
||||||
|
* - `/auth`
|
||||||
|
* - `/`
|
||||||
|
*/
|
||||||
|
path?: `/${string}`
|
||||||
|
/**
|
||||||
|
* The directory where the admin build is output. This is where the build process will place the generated files.
|
||||||
|
* The default value is `./build`.
|
||||||
|
*/
|
||||||
|
outDir?: string
|
||||||
|
/**
|
||||||
|
* Configure the Vite configuration for the admin dashboard. This function receives the default Vite configuration
|
||||||
|
* and returns the modified configuration. The default value is `undefined`.
|
||||||
|
*/
|
||||||
|
vite?: (config: InlineConfig) => InlineConfig
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
@@ -632,6 +666,11 @@ export type ConfigModule = {
|
|||||||
*/
|
*/
|
||||||
projectConfig: ProjectConfigOptions
|
projectConfig: ProjectConfigOptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin dashboard configurations.
|
||||||
|
*/
|
||||||
|
admin?: AdminOptions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On your Medusa backend, you can use [Plugins](https://docs.medusajs.com/development/plugins/overview) to add custom features or integrate third-party services.
|
* On your Medusa backend, you can use [Plugins](https://docs.medusajs.com/development/plugins/overview) to add custom features or integrate third-party services.
|
||||||
* For example, installing a plugin to use Stripe as a payment processor.
|
* For example, installing a plugin to use Stripe as a payment processor.
|
||||||
|
|||||||
@@ -23,14 +23,12 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@medusajs/types": "^1.11.16",
|
"@medusajs/types": "^1.11.16",
|
||||||
"@swc/core": "^1.4.8",
|
|
||||||
"@swc/jest": "^0.2.36",
|
"@swc/jest": "^0.2.36",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/ioredis": "^4.28.10",
|
"@types/ioredis": "^4.28.10",
|
||||||
"@types/jsonwebtoken": "^8.5.9",
|
"@types/jsonwebtoken": "^8.5.9",
|
||||||
"@types/lodash": "^4.14.191",
|
"@types/lodash": "^4.14.191",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/papaparse": "^5.3.7",
|
|
||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
"medusa-interfaces": "^1.3.9",
|
"medusa-interfaces": "^1.3.9",
|
||||||
@@ -51,6 +49,7 @@
|
|||||||
"medusa-interfaces": "^1.3.7"
|
"medusa-interfaces": "^1.3.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@medusajs/admin-sdk": "0.0.1",
|
||||||
"@medusajs/core-flows": "^0.0.9",
|
"@medusajs/core-flows": "^0.0.9",
|
||||||
"@medusajs/link-modules": "^0.2.11",
|
"@medusajs/link-modules": "^0.2.11",
|
||||||
"@medusajs/medusa-cli": "^1.3.22",
|
"@medusajs/medusa-cli": "^1.3.22",
|
||||||
@@ -58,6 +57,7 @@
|
|||||||
"@medusajs/orchestration": "^0.5.7",
|
"@medusajs/orchestration": "^0.5.7",
|
||||||
"@medusajs/utils": "^1.11.9",
|
"@medusajs/utils": "^1.11.9",
|
||||||
"@medusajs/workflows-sdk": "^0.1.6",
|
"@medusajs/workflows-sdk": "^0.1.6",
|
||||||
|
"@swc/core": "^1.4.8",
|
||||||
"awilix": "^8.0.0",
|
"awilix": "^8.0.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"boxen": "^5.0.1",
|
"boxen": "^5.0.1",
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
import { ConfigModule } from "@medusajs/types"
|
||||||
|
import { transformFile } from "@swc/core"
|
||||||
|
import { getConfigFile } from "medusa-core-utils"
|
||||||
|
import fs from "node:fs/promises"
|
||||||
|
import path from "path"
|
||||||
|
|
||||||
|
type BuildArgs = {
|
||||||
|
directory: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileConfig = {
|
||||||
|
inputDir: string
|
||||||
|
outputDir: string
|
||||||
|
targetExtension?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const INPUT_DIR = "./src"
|
||||||
|
const OUTPUT_DIR = "./dist"
|
||||||
|
|
||||||
|
const COMPILE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"]
|
||||||
|
const IGNORE_EXTENSIONS = [".md"]
|
||||||
|
|
||||||
|
async function clean(path: string) {
|
||||||
|
await fs.rm(path, { recursive: true }).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findFiles(dir: string): Promise<string[]> {
|
||||||
|
try {
|
||||||
|
const files = await fs.readdir(dir, { withFileTypes: true })
|
||||||
|
const paths = await Promise.all(
|
||||||
|
files.map(async (file) => {
|
||||||
|
const res = path.join(dir, file.name)
|
||||||
|
return file.isDirectory() ? findFiles(res) : res
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return paths.flat()
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Failed to read directory ${dir}`)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOutputPath = (file: string, config: FileConfig) => {
|
||||||
|
const { inputDir, outputDir, targetExtension } = config
|
||||||
|
|
||||||
|
const inputDirName = path.basename(inputDir)
|
||||||
|
const outputDirName = path.basename(outputDir)
|
||||||
|
|
||||||
|
const relativePath = file.replace(inputDirName, outputDirName)
|
||||||
|
let outputPath = relativePath
|
||||||
|
|
||||||
|
if (targetExtension) {
|
||||||
|
const currentExtension = path.extname(outputPath)
|
||||||
|
outputPath = outputPath.replace(currentExtension, targetExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputPath
|
||||||
|
}
|
||||||
|
|
||||||
|
const writeToOut = async (
|
||||||
|
file: string,
|
||||||
|
content: string,
|
||||||
|
config: FileConfig
|
||||||
|
) => {
|
||||||
|
const outputPath = getOutputPath(file, config)
|
||||||
|
|
||||||
|
await fs.mkdir(outputPath.replace(/\/[^/]+$/, ""), { recursive: true })
|
||||||
|
await fs.writeFile(outputPath, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyToOut(file: string, config: FileConfig) {
|
||||||
|
const outputPath = getOutputPath(file, config)
|
||||||
|
const dirNameRegex = new RegExp("\\" + path.sep + "([^\\" + path.sep + "]+)$")
|
||||||
|
|
||||||
|
await fs.mkdir(outputPath.replace(dirNameRegex, ""), { recursive: true })
|
||||||
|
await fs.copyFile(file, outputPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const medusaTransform = async (file: string) => {
|
||||||
|
if (COMPILE_EXTENSIONS.some((ext) => file.endsWith(ext))) {
|
||||||
|
const outputPath = getOutputPath(file, {
|
||||||
|
inputDir: INPUT_DIR,
|
||||||
|
outputDir: OUTPUT_DIR,
|
||||||
|
})
|
||||||
|
const output = await transformFile(file, {
|
||||||
|
sourceFileName: path.relative(path.dirname(outputPath), file),
|
||||||
|
sourceMaps: "inline",
|
||||||
|
module: {
|
||||||
|
type: "commonjs",
|
||||||
|
},
|
||||||
|
jsc: {
|
||||||
|
parser: {
|
||||||
|
syntax: "typescript",
|
||||||
|
decorators: true,
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
decoratorMetadata: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await writeToOut(file, output.code, {
|
||||||
|
inputDir: INPUT_DIR,
|
||||||
|
outputDir: OUTPUT_DIR,
|
||||||
|
targetExtension: ".js",
|
||||||
|
})
|
||||||
|
} else if (!IGNORE_EXTENSIONS.some((ext) => file.endsWith(ext))) {
|
||||||
|
// Copy non-ts files
|
||||||
|
await copyToOut(file, { inputDir: INPUT_DIR, outputDir: OUTPUT_DIR })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function ({ directory }: BuildArgs) {
|
||||||
|
const started = Date.now()
|
||||||
|
|
||||||
|
const { configModule, error } = getConfigFile<ConfigModule>(
|
||||||
|
directory,
|
||||||
|
"medusa-config"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log(`Failed to load medusa-config.js`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = path.join(directory, INPUT_DIR)
|
||||||
|
const dist = path.join(directory, OUTPUT_DIR)
|
||||||
|
|
||||||
|
await clean(dist)
|
||||||
|
|
||||||
|
const files = await findFiles(input)
|
||||||
|
|
||||||
|
await Promise.all(files.map(medusaTransform))
|
||||||
|
|
||||||
|
const adminOptions = {
|
||||||
|
disable: false,
|
||||||
|
path: "/app" as const,
|
||||||
|
outDir: "./build",
|
||||||
|
...configModule.admin,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!adminOptions.disable) {
|
||||||
|
try {
|
||||||
|
const { build: buildProductionBuild } = await import(
|
||||||
|
"@medusajs/admin-sdk"
|
||||||
|
)
|
||||||
|
|
||||||
|
await buildProductionBuild(adminOptions)
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Failed to build admin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const time = Date.now() - started
|
||||||
|
|
||||||
|
console.log(`Build completed in ${time}ms`)
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { AdminOptions, ConfigModule } from "@medusajs/types"
|
||||||
|
import { Express } from "express"
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
app: Express
|
||||||
|
configModule: ConfigModule
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntializedOptions = Required<
|
||||||
|
Pick<AdminOptions, "path" | "disable" | "outDir">
|
||||||
|
> &
|
||||||
|
AdminOptions
|
||||||
|
|
||||||
|
export default async function adminLoader({ app, configModule }: Options) {
|
||||||
|
const { admin } = configModule
|
||||||
|
|
||||||
|
const adminOptions: IntializedOptions = {
|
||||||
|
disable: false,
|
||||||
|
path: "/app",
|
||||||
|
outDir: "./build",
|
||||||
|
...admin,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (admin?.disable) {
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.COMMAND_INITIATED_BY === "develop") {
|
||||||
|
return initDevelopmentServer(app, adminOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
return serveProductionBuild(app, adminOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initDevelopmentServer(app: Express, options: IntializedOptions) {
|
||||||
|
const { develop } = await import("@medusajs/admin-sdk")
|
||||||
|
|
||||||
|
const adminMiddleware = await develop(options)
|
||||||
|
app.use(options.path, adminMiddleware)
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
async function serveProductionBuild(app: Express, options: IntializedOptions) {
|
||||||
|
const { serve } = await import("@medusajs/admin-sdk")
|
||||||
|
|
||||||
|
const adminRoute = await serve(options)
|
||||||
|
|
||||||
|
app.use(options.path, adminRoute)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
@@ -77,6 +77,7 @@ export default (rootDirectory: string): ConfigModule => {
|
|||||||
...configModule?.projectConfig,
|
...configModule?.projectConfig,
|
||||||
worker_mode,
|
worker_mode,
|
||||||
},
|
},
|
||||||
|
admin: configModule?.admin ?? {},
|
||||||
modules: configModule.modules ?? {},
|
modules: configModule.modules ?? {},
|
||||||
featureFlags: configModule?.featureFlags ?? {},
|
featureFlags: configModule?.featureFlags ?? {},
|
||||||
plugins: configModule?.plugins ?? [],
|
plugins: configModule?.plugins ?? [],
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { createMedusaContainer } from "medusa-core-utils"
|
|||||||
import requestIp from "request-ip"
|
import requestIp from "request-ip"
|
||||||
import { v4 } from "uuid"
|
import { v4 } from "uuid"
|
||||||
import { MedusaContainer } from "../types/global"
|
import { MedusaContainer } from "../types/global"
|
||||||
|
import adminLoader from "./admin"
|
||||||
import apiLoader from "./api"
|
import apiLoader from "./api"
|
||||||
import loadConfig from "./config"
|
import loadConfig from "./config"
|
||||||
import expressLoader from "./express"
|
import expressLoader from "./express"
|
||||||
@@ -54,6 +55,8 @@ async function loadEntrypoints(
|
|||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await adminLoader({ app: expressApp, configModule })
|
||||||
|
|
||||||
// subscribersLoader({ container })
|
// subscribersLoader({ container })
|
||||||
|
|
||||||
await apiLoader({
|
await apiLoader({
|
||||||
|
|||||||
Reference in new issue
Block a user