chore: Remove Admin v1 (#7214)

* chore: Remove v1 admin

* update eslint
This commit is contained in:
Oli Juhl
2024-05-03 10:56:38 +02:00
committed by GitHub
parent 46ded00c08
commit c41e90fa36
965 changed files with 153 additions and 154707 deletions
-100
View File
@@ -82,8 +82,6 @@ module.exports = {
project: [ project: [
"./packages/medusa/tsconfig.json", "./packages/medusa/tsconfig.json",
"./packages/admin-ui/tsconfig.json",
"./packages/admin-ui/tsconfig.spec.json",
"./packages/admin-next/dashboard/tsconfig.json", "./packages/admin-next/dashboard/tsconfig.json",
"./packages/inventory/tsconfig.spec.json", "./packages/inventory/tsconfig.spec.json",
@@ -214,48 +212,6 @@ module.exports = {
project: "./packages/design-system/icons/tsconfig.json", project: "./packages/design-system/icons/tsconfig.json",
}, },
}, },
{
files: ["packages/admin-ui/ui/**/*.ts", "packages/admin-ui/ui/**/*.tsx"],
plugins: ["unused-imports"],
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-ui/ui/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",
"no-unused-expressions": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn",
{
vars: "all",
varsIgnorePattern: "^_",
args: "after-used",
argsIgnorePattern: "^_",
},
],
},
},
{ {
files: [ files: [
"packages/admin-next/dashboard/**/*.ts", "packages/admin-next/dashboard/**/*.ts",
@@ -305,61 +261,5 @@ module.exports = {
], ],
}, },
}, },
{
files: ["packages/admin-ui/lib/**/*.ts"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./packages/admin-ui/tsconfig.json",
},
},
{
files: ["packages/admin/**/*.ts"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./packages/admin/tsconfig.json",
},
},
{
files: [
"packages/medusa-payment-stripe/src/admin/**/*.ts",
"packages/medusa-payment-stripe/src/admin/**/*.tsx",
],
plugins: ["unused-imports"],
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/medusa-payment-stripe/tsconfig.admin.json",
},
env: {
browser: true,
},
rules: {
"prettier/prettier": "error",
"react/prop-types": "off",
"new-cap": "off",
"require-jsdoc": "off",
"valid-jsdoc": "off",
"no-unused-expressions": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn",
{
vars: "all",
varsIgnorePattern: "^_",
args: "after-used",
argsIgnorePattern: "^_",
},
],
},
},
], ],
} }
-5
View File
@@ -1,5 +0,0 @@
/dist
/build
.vercel
/ui/preview
/ui/src/extensions
-6
View File
@@ -1,6 +0,0 @@
.DS_store
.turbo
/src
/build
.vercel
/ui/preview
-686
View File
@@ -1,686 +0,0 @@
# @medusajs/admin-ui
## 2.1.14
### Patch Changes
- [#7128](https://github.com/medusajs/medusa/pull/7128) [`40686ba980`](https://github.com/medusajs/medusa/commit/40686ba98077c1c509744c0bd44fea4d4786abaa) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(medusa,admin-ui): Remove forced `backendUrl` from develop command. Allow specifying an alternative host value
- [#6952](https://github.com/medusajs/medusa/pull/6952) [`0bf0bf819b`](https://github.com/medusajs/medusa/commit/0bf0bf819b04e0c96aabf1e959d4a592970e64ec) Thanks [@shahednasser](https://github.com/shahednasser)! - fix(admin-ui): Resolve `node-gyp` error during installation with npm
- Updated dependencies [[`0c0b425de7`](https://github.com/medusajs/medusa/commit/0c0b425de7b154b80b712ab17b16215cf62d1e83), [`c3260a2c5a`](https://github.com/medusajs/medusa/commit/c3260a2c5add86ada641db91e834d9f9de62ed14), [`eadc5e8a79`](https://github.com/medusajs/medusa/commit/eadc5e8a794ec07f7b523808aa0fec2ac394c984), [`5e30b8cce6`](https://github.com/medusajs/medusa/commit/5e30b8cce63524d939a5068050798b0d8fcb9a53)]:
- medusa-react@9.0.17
- @medusajs/ui@3.0.0
## 2.1.13
### Patch Changes
- [#6824](https://github.com/medusajs/medusa/pull/6824) [`ef58826dd2`](https://github.com/medusajs/medusa/commit/ef58826dd240ef4093a08fbafb6788f1f7e4087f) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui, admin): Add missing regions relation
- Updated dependencies [[`247ca3c3fa`](https://github.com/medusajs/medusa/commit/247ca3c3fadd69a1e90415041643727b53458e41), [`26531c5a38`](https://github.com/medusajs/medusa/commit/26531c5a38bf09ab3e77a1444cefd65a073ae713)]:
- @medusajs/ui@2.4.3
- @medusajs/ui-preset@1.1.3
## 2.1.12
### Patch Changes
- [#6306](https://github.com/medusajs/medusa/pull/6306) [`58baead34`](https://github.com/medusajs/medusa/commit/58baead34e32abff3c587dbc1c6b11b527be8381) Thanks [@erikengervall](https://github.com/erikengervall)! - fix(admin-ui) Refactor `use-copy-promotion` to map product IDs
- Updated dependencies [[`8cbf6c60f`](https://github.com/medusajs/medusa/commit/8cbf6c60fec7fe8ddf59dcf420b9339f84b8636c), [`73fd92a1a`](https://github.com/medusajs/medusa/commit/73fd92a1afdec88c0d5f4aeed16349f2bd62cfa2)]:
- @medusajs/ui-preset@1.1.1
- @medusajs/ui@2.4.0
## 2.1.11
### Patch Changes
- [#5822](https://github.com/medusajs/medusa/pull/5822) [`7ecea3448`](https://github.com/medusajs/medusa/commit/7ecea34487972bc05aae46918d5caebce607594b) Thanks [@hexrw](https://github.com/hexrw)! - feat(admin-ui): Add Czech translation
- [#6001](https://github.com/medusajs/medusa/pull/6001) [`46d610bc5`](https://github.com/medusajs/medusa/commit/46d610bc555797df2ae81eb89b18faf1411b33b8) Thanks [@abusaidm](https://github.com/abusaidm)! - Add missing country in admin region and set Libya to formal name
- Updated dependencies [[`7d650771d`](https://github.com/medusajs/medusa/commit/7d650771d1c1d3e5d77ff95c12e4970743b64303), [`925feea04`](https://github.com/medusajs/medusa/commit/925feea04a8222285175c33577548e50516069a7)]:
- medusa-react@9.0.13
## 2.1.10
### Patch Changes
- [#5769](https://github.com/medusajs/medusa/pull/5769) [`f20d484b2`](https://github.com/medusajs/medusa/commit/f20d484b2381d07aac3e86d02f424f16b532c302) Thanks [@iweurman](https://github.com/iweurman)! - feat(admin): Add Korean language support
- [#5701](https://github.com/medusajs/medusa/pull/5701) [`6975eacb3`](https://github.com/medusajs/medusa/commit/6975eacb338874b976c14aae030c74362d57410c) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(medusa, medusa-cli): Improve add line item + cluster starting with medusa cli
- Updated dependencies [[`4e9d95454`](https://github.com/medusajs/medusa/commit/4e9d954549916ea260583a6b5e36f0c6c02d4d22), [`591ba2388`](https://github.com/medusajs/medusa/commit/591ba2388d9768f70ca2faf48d353e5a65e4e123)]:
- medusa-react@9.0.12
- @medusajs/ui@2.2.4
## 2.1.9
### Patch Changes
- [#5710](https://github.com/medusajs/medusa/pull/5710) [`07934cdaa`](https://github.com/medusajs/medusa/commit/07934cdaac2f3b49a935e9701ffa7d5cf1381847) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Ensure that the prices step is disabled until at least one product is selected in PriceList forms
- [#5737](https://github.com/medusajs/medusa/pull/5737) [`d5526736e`](https://github.com/medusajs/medusa/commit/d5526736ecffab19dd4633ed64e08e097e433abe) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Fix height of Bulk Editor cells when product has few variants"
- [#5707](https://github.com/medusajs/medusa/pull/5707) [`f4c489fb8`](https://github.com/medusajs/medusa/commit/f4c489fb85aaee605b60c527817f62cfdb2f3140) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Fix an issue where if product.categories was null the details page would crash
- [#5676](https://github.com/medusajs/medusa/pull/5676) [`02ea9ac3a`](https://github.com/medusajs/medusa/commit/02ea9ac3ac87f1091c6f21c689ccfc55080b17cc) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): delete edit variant prices in favor of bulk editor
- [#5726](https://github.com/medusajs/medusa/pull/5726) [`5f6751a61`](https://github.com/medusajs/medusa/commit/5f6751a61f11b901e4916d16b62607cda6d42032) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Makes it possible to scroll overflow on the Y-axis in the /products/:id bulk editor
- [#5727](https://github.com/medusajs/medusa/pull/5727) [`3bcf08c0c`](https://github.com/medusajs/medusa/commit/3bcf08c0cb3a00799d61c127a2f3c192790c982f) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): delete inventory items when removing product
- Updated dependencies [[`b25b29fe7`](https://github.com/medusajs/medusa/commit/b25b29fe7ba3bc9fca8da7a6b10461437a3e4d2d)]:
- @medusajs/ui@2.2.3
## 2.1.8
### Patch Changes
- [#5479](https://github.com/medusajs/medusa/pull/5479) [`3f016d871`](https://github.com/medusajs/medusa/commit/3f016d871a091a0a72c2eaec2f4ff74e0ad5b426) Thanks [@VariableVic](https://github.com/VariableVic)! - fix: sort supportedLanguages array
- [#5641](https://github.com/medusajs/medusa/pull/5641) [`c08240df3`](https://github.com/medusajs/medusa/commit/c08240df3f2fe0c4c5c35af471f68a2187f8e582) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Resolves an issue where decimal numbers would be stripped when the locale uses commas as the decimal separator
- Updated dependencies [[`a67a8e7e9`](https://github.com/medusajs/medusa/commit/a67a8e7e90f35636c8d99858fc5b19358df4c174)]:
- @medusajs/ui@2.2.2
## 2.1.7
### Patch Changes
- [#5566](https://github.com/medusajs/medusa/pull/5566) [`d7e188722`](https://github.com/medusajs/medusa/commit/d7e1887221467929175e7c69b3064dc7e6c967d8) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Fixes an issue where using the bulk editor with comma as the decimal separator would convert the input to NaN
- [#5596](https://github.com/medusajs/medusa/pull/5596) [`e3f1da92d`](https://github.com/medusajs/medusa/commit/e3f1da92db1a5bb07ae8d79a90f0d1d04f9bfee3) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui,ui): Align @types/react versions
- [#5530](https://github.com/medusajs/medusa/pull/5530) [`8e48be58e`](https://github.com/medusajs/medusa/commit/8e48be58efe9bfffd0f3f5af098ef2a4cefc4b2d) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Fixes an issue where regions were not scrollable in tax settings view, also adds a IntersectionObserver to ensure that we load all regions as the user scrolls. The current implementation was capped at loading 20 regions.
- [#5535](https://github.com/medusajs/medusa/pull/5535) [`f7e982988`](https://github.com/medusajs/medusa/commit/f7e98298815b0ffb4ce1b6d2d2e1f26ca5e01049) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Fix issue were Price List domain would fetch all products on initial load"
- [#5548](https://github.com/medusajs/medusa/pull/5548) [`b4e8adfcf`](https://github.com/medusajs/medusa/commit/b4e8adfcf9c71f97a2a0b4ad8f0632e213fe7123) Thanks [@rick-lam](https://github.com/rick-lam)! - fix(admin-ui): Admin UI: Invalid Request Header
- Updated dependencies [[`91615f9c4`](https://github.com/medusajs/medusa/commit/91615f9c459a2d8cb842561c5edb335680d30298), [`e3f1da92d`](https://github.com/medusajs/medusa/commit/e3f1da92db1a5bb07ae8d79a90f0d1d04f9bfee3)]:
- medusa-react@9.0.11
- @medusajs/ui@2.2.1
## 2.1.6
### Patch Changes
- [#5456](https://github.com/medusajs/medusa/pull/5456) [`ea2ee343f`](https://github.com/medusajs/medusa/commit/ea2ee343f0f2bee347d0e2a3ece2eb3252564c67) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): PriceList bulk editor fixes
- [#5467](https://github.com/medusajs/medusa/pull/5467) [`f08a295ad`](https://github.com/medusajs/medusa/commit/f08a295ad4e2886b9df5c4c4471c0f4e004fc1d9) Thanks [@shahednasser](https://github.com/shahednasser)! - fix(admin-ui): fix analytics config not being saved in invite form
- [#5288](https://github.com/medusajs/medusa/pull/5288) [`2057d3b24`](https://github.com/medusajs/medusa/commit/2057d3b24733680571ff977130b3e23bcc12d733) Thanks [@pepijn-vanvlaanderen](https://github.com/pepijn-vanvlaanderen)! - Fixes crash on the batch job activity card when using a custom batch job.
- [#5442](https://github.com/medusajs/medusa/pull/5442) [`ca05436fc`](https://github.com/medusajs/medusa/commit/ca05436fc1d03c2f6512b2b9399eadafb3d5d721) Thanks [@huuduc2312](https://github.com/huuduc2312)! - feat(admin-ui): add Vietnamese translation
- [#5428](https://github.com/medusajs/medusa/pull/5428) [`ebba93e03`](https://github.com/medusajs/medusa/commit/ebba93e03d223fb3a79276090f7cfad612d70243) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(medusa): Update status type in API payload definition
- [#5356](https://github.com/medusajs/medusa/pull/5356) [`397da6c2b`](https://github.com/medusajs/medusa/commit/397da6c2ba39532ebf09b91d61ca2b7f1c4b8126) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): TIP in shipping option creation
- [#5480](https://github.com/medusajs/medusa/pull/5480) [`a780b92b8`](https://github.com/medusajs/medusa/commit/a780b92b8d590baa0e86682d1154f9e5b0869ea1) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(medusa): admin get product should return prices when expected
- [#5484](https://github.com/medusajs/medusa/pull/5484) [`b69f18257`](https://github.com/medusajs/medusa/commit/b69f1825711a9d62da4e2ab4f1900eff473ec8e2) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Make Webpack config more fault tolerant. Removes loader that would throw if dependency was missing source maps
- [#5315](https://github.com/medusajs/medusa/pull/5315) [`4ce8279d2`](https://github.com/medusajs/medusa/commit/4ce8279d2527fa691c3ca043b914ebb85cb4ac55) Thanks [@ram-you](https://github.com/ram-you)! - Enhanced 'activity-drawer' visibility with z-index adjustment
- Updated dependencies [[`a780b92b8`](https://github.com/medusajs/medusa/commit/a780b92b8d590baa0e86682d1154f9e5b0869ea1)]:
- medusa-react@9.0.10
## 2.1.5
### Patch Changes
- [#5339](https://github.com/medusajs/medusa/pull/5339) [`e04927a3c`](https://github.com/medusajs/medusa/commit/e04927a3cb0b8159b1f10a377beee705da6eef31) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): minor bugs throughout Price List domain plus improved form validation
- [#5377](https://github.com/medusajs/medusa/pull/5377) [`d30f4477d`](https://github.com/medusajs/medusa/commit/d30f4477d908e2665ab951d93d559e5db8c4211e) Thanks [@anamarijapapic](https://github.com/anamarijapapic)! - feat(admin-ui): Add Croatian translation
- [#5358](https://github.com/medusajs/medusa/pull/5358) [`3376f4ccc`](https://github.com/medusajs/medusa/commit/3376f4ccc1acb0a8896c7f10c45a980aec8188b1) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Alias design system packages
- [#5395](https://github.com/medusajs/medusa/pull/5395) [`3aba6269e`](https://github.com/medusajs/medusa/commit/3aba6269ed5efb6353d193e71d241ae55a40e721) Thanks [@samiralibabic](https://github.com/samiralibabic)! - feat(admin-ui): add Bosnian translation
- [#5394](https://github.com/medusajs/medusa/pull/5394) [`a0963f0ed`](https://github.com/medusajs/medusa/commit/a0963f0edf909f6cfea17bd7a0c7899707808057) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Remove t() on product.status update
- Updated dependencies [[`5a5c96e21`](https://github.com/medusajs/medusa/commit/5a5c96e2118e50a558d9d6dc340e505454d4c593)]:
- medusa-react@9.0.8
## 2.1.4
### Patch Changes
- [#5233](https://github.com/medusajs/medusa/pull/5233) [`0f34e0f38`](https://github.com/medusajs/medusa/commit/0f34e0f381833a4790ee590a9cbf93b7660634f3) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - feat(admin-ui, medusa, medusa-react, medusa-js): Price List UI revamp
- Updated dependencies [[`0f34e0f38`](https://github.com/medusajs/medusa/commit/0f34e0f381833a4790ee590a9cbf93b7660634f3)]:
- medusa-react@9.0.7
## 2.1.3
### Patch Changes
- [#5176](https://github.com/medusajs/medusa/pull/5176) [`25dad081b`](https://github.com/medusajs/medusa/commit/25dad081b7f99b5398d0d64eba50ebcb08e1138f) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Fix portuguese transalations
- [#5140](https://github.com/medusajs/medusa/pull/5140) [`f88e3865a`](https://github.com/medusajs/medusa/commit/f88e3865a07eafe476c8d7295d806674de478c19) Thanks [@PeterAbdalla](https://github.com/PeterAbdalla)! - feat(admin-ui): Add Arabic translations
- [#5173](https://github.com/medusajs/medusa/pull/5173) [`adb6fb3f7`](https://github.com/medusajs/medusa/commit/adb6fb3f7d2bce941395516cd61bcfae2908a74f) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Allow nullish values in update variant
- Updated dependencies [[`a9972d7d6`](https://github.com/medusajs/medusa/commit/a9972d7d6fa5c9698318a55173440f635ebf0a11)]:
- medusa-react@9.0.6
## 2.1.2
### Patch Changes
- [#5163](https://github.com/medusajs/medusa/pull/5163) [`78b50ac0c`](https://github.com/medusajs/medusa/commit/78b50ac0c433a104370ee7d2415ac2609761b802) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Add hook to customer group table
- [#5101](https://github.com/medusajs/medusa/pull/5101) [`15a1c55de`](https://github.com/medusajs/medusa/commit/15a1c55def5e8b51d78e35dad804c040a557b676) Thanks [@chemicalkosek](https://github.com/chemicalkosek)! - feat(admin-ui): Add Polish translation
- [#5162](https://github.com/medusajs/medusa/pull/5162) [`ef2a12e45`](https://github.com/medusajs/medusa/commit/ef2a12e451b06838a5cb3cb3ee06e405d409be38) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Undefined vars in admin
- [#5154](https://github.com/medusajs/medusa/pull/5154) [`54531e38b`](https://github.com/medusajs/medusa/commit/54531e38bc331662cfcb1bdcbb2cdb0e647439b3) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Patch admin path issue
- [#5155](https://github.com/medusajs/medusa/pull/5155) [`8b189d2b9`](https://github.com/medusajs/medusa/commit/8b189d2b90bd585a7cc0b43f2e40e679818c6b4b) Thanks [@adrien2p](https://github.com/adrien2p)! - feat(admin, admin-ui): Add missing attributes translations
## 2.1.1
### Patch Changes
- [#5113](https://github.com/medusajs/medusa/pull/5113) [`4dd11c886`](https://github.com/medusajs/medusa/commit/4dd11c88675c304b46806bbe28ef3574b02f0476) Thanks [@fPolic](https://github.com/fPolic)! - fix(admin-ui): modal pointer events
- [#5111](https://github.com/medusajs/medusa/pull/5111) [`8772a0722`](https://github.com/medusajs/medusa/commit/8772a0722ecfb6bceed0cfccfce66d0e7cdba331) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Wrap invite route in AnalyticsProvider
- [#5103](https://github.com/medusajs/medusa/pull/5103) [`4710520c3`](https://github.com/medusajs/medusa/commit/4710520c35886c1df7dd934e7950b6309e1f78df) Thanks [@fPolic](https://github.com/fPolic)! - fix(admin-ui): import modal template
## 2.1.0
### Minor Changes
- [#4962](https://github.com/medusajs/medusa/pull/4962) [`afd4e72cd`](https://github.com/medusajs/medusa/commit/afd4e72cdf69781924bae642f2e225ef9572e9a5) Thanks [@gempain](https://github.com/gempain)! - feat(admin-ui): Multi-language support
### Patch Changes
- [#5064](https://github.com/medusajs/medusa/pull/5064) [`c722440c3`](https://github.com/medusajs/medusa/commit/c722440c30d51719c83a6f4c419690b2078cb79c) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Load translations from path
- [#5057](https://github.com/medusajs/medusa/pull/5057) [`cb67760f7`](https://github.com/medusajs/medusa/commit/cb67760f715436f82d9271464228ffdfb3c9cba7) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin,admin-ui): Copy `/public` folder to admin build folder during build, in order for the build to include translation files. Also bumps the version of `dotenv` to match the version used in other Medusa dependencies.
- [#5028](https://github.com/medusajs/medusa/pull/5028) [`d4432dd18`](https://github.com/medusajs/medusa/commit/d4432dd1838f28ee2f3637114ff97ff63e22b0de) Thanks [@olivermrbl](https://github.com/olivermrbl)! - feat(admin-ui): Add display name + pull supported languages from config
- [#5066](https://github.com/medusajs/medusa/pull/5066) [`53ffd614b`](https://github.com/medusajs/medusa/commit/53ffd614b599eb14d3f3d339b5a90335c32e9e77) Thanks [@dahorarodrigo](https://github.com/dahorarodrigo)! - feat(admin-ui): pt-BR translations and en version text fixes
- [#4939](https://github.com/medusajs/medusa/pull/4939) [`87e3a7d06`](https://github.com/medusajs/medusa/commit/87e3a7d06ae76b6bee92b1bb97d0c8f8da07d4e9) Thanks [@jporsay](https://github.com/jporsay)! - correctly skipping analytics when user opted out
- [#5080](https://github.com/medusajs/medusa/pull/5080) [`a7459029f`](https://github.com/medusajs/medusa/commit/a7459029fe9549b3845ee9e01d0d88ea5e85635f) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore(admin-ui): Add pt-BR to supported languages
- Updated dependencies [[`7d3572302`](https://github.com/medusajs/medusa/commit/7d35723023ed5bcfaf06ff2480e97508527e8665)]:
- medusa-react@9.0.5
## 2.0.2
### Patch Changes
- [#4888](https://github.com/medusajs/medusa/pull/4888) [`50798d118`](https://github.com/medusajs/medusa/commit/50798d118986ccd18e1504129773ca40e63abd68) Thanks [@fPolic](https://github.com/fPolic)! - fix(admin-ui): bulk editor cell separators
- [#4886](https://github.com/medusajs/medusa/pull/4886) [`b7de679e9`](https://github.com/medusajs/medusa/commit/b7de679e9b665368074d406ac5d9b3fbb0187a68) Thanks [@fPolic](https://github.com/fPolic)! - feat(admin-ui): batch job completed notification
- [#4834](https://github.com/medusajs/medusa/pull/4834) [`470379e63`](https://github.com/medusajs/medusa/commit/470379e631ca17ee486a28c50ec828344c61d727) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(medusa, admin-ui): Allow soft-deleted return reason restoration
## 2.0.1
### Patch Changes
- [#4837](https://github.com/medusajs/medusa/pull/4837) [`57e199ef5`](https://github.com/medusajs/medusa/commit/57e199ef522e45c45ba5d79287a4c258e6f3914c) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Fix sign out in admin panel not redirecting correctly
- [#4843](https://github.com/medusajs/medusa/pull/4843) [`f4bf9ee16`](https://github.com/medusajs/medusa/commit/f4bf9ee16915a88548c1666b182df478e4f5ed74) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Fix variant creation when no stock locations provided
- [#4840](https://github.com/medusajs/medusa/pull/4840) [`0c7c2eeea`](https://github.com/medusajs/medusa/commit/0c7c2eeeabad684c31a18d6f5579f30820b29efb) Thanks [@fPolic](https://github.com/fPolic)! - feat(admin-ui): refresh products when import is complete
## 2.0.0
### Major Changes
- [#4761](https://github.com/medusajs/medusa/pull/4761) [`f1a05f472`](https://github.com/medusajs/medusa/commit/f1a05f4725dcc45150f014769562bd3dfbc0f1f8) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - feat(admin, admin-ui, medusa, medusa-js, medusa-react, stripe-plugin): Support admin extensions
### Patch Changes
- [#4568](https://github.com/medusajs/medusa/pull/4568) [`4a448b68f`](https://github.com/medusajs/medusa/commit/4a448b68fde90beb956375018ac833f6478726e6) Thanks [@fPolic](https://github.com/fPolic)! - feat(admin-ui): bulk advanced selections + copy/paste
- Updated dependencies [[`9469063f6`](https://github.com/medusajs/medusa/commit/9469063f643180002ede7a8e94c6de53d2770d04), [`f1a05f472`](https://github.com/medusajs/medusa/commit/f1a05f4725dcc45150f014769562bd3dfbc0f1f8)]:
- medusa-react@9.0.4
## 1.9.4
### Patch Changes
- [#4670](https://github.com/medusajs/medusa/pull/4670) [`bb534e045`](https://github.com/medusajs/medusa/commit/bb534e0453d3d27e4d5afee120d969f927e8f004) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Customer Groups in Pricing + TIP
## 1.9.3
### Patch Changes
- [#4594](https://github.com/medusajs/medusa/pull/4594) [`aab890174`](https://github.com/medusajs/medusa/commit/aab890174dbe2c73d57e8a6f15e74f88b6ffa532) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Add missing SO relation
- [#4536](https://github.com/medusajs/medusa/pull/4536) [`f561601bf`](https://github.com/medusajs/medusa/commit/f561601bf638be73ff0d0e55e28bd2192de8db32) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin): Exclude prices from PriceLists overview
## 1.9.2
### Patch Changes
- [#4541](https://github.com/medusajs/medusa/pull/4541) [`aae4b3ad3`](https://github.com/medusajs/medusa/commit/aae4b3ad371e0b7e8e0007a6ef21ee66eda665f1) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(admin-ui): always show list of currencies in currency overview
- [#4530](https://github.com/medusajs/medusa/pull/4530) [`55db91415`](https://github.com/medusajs/medusa/commit/55db914151fc70cf0abe8a71dd8e17067cce3908) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): number input setting
- [#4552](https://github.com/medusajs/medusa/pull/4552) [`221f62dc4`](https://github.com/medusajs/medusa/commit/221f62dc4cf4a30505bf3e15034c735663659ade) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Omit PriceList prices from variant update
- [#4516](https://github.com/medusajs/medusa/pull/4516) [`bfc0ea569`](https://github.com/medusajs/medusa/commit/bfc0ea5695b8999a63df9ba58409e3e139473afa) Thanks [@fPolic](https://github.com/fPolic)! - feat(admin-ui): variant prices bulk editor
- Updated dependencies [[`3e6cee284`](https://github.com/medusajs/medusa/commit/3e6cee28469b5ce30687de5e5096266244f6b3a9), [`e3a856521`](https://github.com/medusajs/medusa/commit/e3a85652132c110e4dd81ae1120414121fbab0fd)]:
- medusa-react@9.0.3
## 1.9.1
### Patch Changes
- [#4444](https://github.com/medusajs/medusa/pull/4444) [`78a1f3943`](https://github.com/medusajs/medusa/commit/78a1f3943b1631170721169c19b5b47a4832330f) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore(admin-ui): Remove new badges from inventory + categories
- [#4389](https://github.com/medusajs/medusa/pull/4389) [`9dcdc0041`](https://github.com/medusajs/medusa/commit/9dcdc0041a2b08cc0723343dd8d9127d9977b086) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(medusa, utils): fix the way selects are consumed alongside the relations
- Updated dependencies [[`fe25c8a91`](https://github.com/medusajs/medusa/commit/fe25c8a91f4462c9ac5a15594fa71155df48c1eb), [`417debfe7`](https://github.com/medusajs/medusa/commit/417debfe7db6edd6722929fe0f905a63548a0c9e)]:
- medusa-react@9.0.2
## 1.9.0
### Minor Changes
- [#4115](https://github.com/medusajs/medusa/pull/4115) [`79cca2ab8`](https://github.com/medusajs/medusa/commit/79cca2ab809b77606e65706c19f9fce37b174365) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(admin-ui): Add filtering capabilities for reservations view
### Patch Changes
- [#4231](https://github.com/medusajs/medusa/pull/4231) [`1bd2e7a5b`](https://github.com/medusajs/medusa/commit/1bd2e7a5be4aeb15ac15f5f0e70dc37a6dfaa86f) Thanks [@fPolic](https://github.com/fPolic)! - fix(admin-ui): prevent Timeline flickering while loading
- [#4316](https://github.com/medusajs/medusa/pull/4316) [`0bfdc33a0`](https://github.com/medusajs/medusa/commit/0bfdc33a0ba7f9518b68db762ede5960db878b6d) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): minor fixes for reservation management
- [#4133](https://github.com/medusajs/medusa/pull/4133) [`869628bf4`](https://github.com/medusajs/medusa/commit/869628bf4c50f93375bd32946cd0605de3d981cd) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(admin-ui): rename allocation to reservation
- [#4267](https://github.com/medusajs/medusa/pull/4267) [`f8643361c`](https://github.com/medusajs/medusa/commit/f8643361cdcb549c61e645d4ee275f20883e00d9) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(medusa, admin-ui): remove reservations feature flag
- [#4276](https://github.com/medusajs/medusa/pull/4276) [`afd1b67f1`](https://github.com/medusajs/medusa/commit/afd1b67f1c7de8cf07fd9fcbdde599a37914e9b5) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Use caret range
- Updated dependencies [[`79cca2ab8`](https://github.com/medusajs/medusa/commit/79cca2ab809b77606e65706c19f9fce37b174365)]:
- medusa-react@9.0.1
## 1.8.7
### Patch Changes
- [#4071](https://github.com/medusajs/medusa/pull/4071) [`0476f5251`](https://github.com/medusajs/medusa/commit/0476f52519237c622b37d29de0718f9774b6add7) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(admin-ui): update edit allocation modal design
- [#4184](https://github.com/medusajs/medusa/pull/4184) [`0f87d3d64`](https://github.com/medusajs/medusa/commit/0f87d3d642b56bf19de8136e1f5bfedf364c5193) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore(medusa,admin-ui): Add reservations FF
- [#4173](https://github.com/medusajs/medusa/pull/4173) [`9a46c11f2`](https://github.com/medusajs/medusa/commit/9a46c11f2acab1eba10c2cad6854c0a7f4a0ee13) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Ensures that the default invite link uses the **BASE** in constructing the link if it exists
- [#4163](https://github.com/medusajs/medusa/pull/4163) [`f47b11293`](https://github.com/medusajs/medusa/commit/f47b11293e273d9a5a5238e5114c9a3a530ecf1b) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Delete variant
- [#4051](https://github.com/medusajs/medusa/pull/4051) [`294576949`](https://github.com/medusajs/medusa/commit/294576949792f8c4dc2a8c1a03fdc9db73a8a00e) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(admin-ui): fix allow_backorder updates
- [#4180](https://github.com/medusajs/medusa/pull/4180) [`5988fa8f4`](https://github.com/medusajs/medusa/commit/5988fa8f4ca1c5538e4b48cfe9b600bf43037998) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Omit purchasable variant prop on duplicate
- [#4081](https://github.com/medusajs/medusa/pull/4081) [`4f3c8f5d7`](https://github.com/medusajs/medusa/commit/4f3c8f5d70b5ae4a11e9d4a2fea4a8410b2daf47) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(medusa,client-types,medusa-js,admin-ui,medusa-react): add reservation table and creation
- [#4178](https://github.com/medusajs/medusa/pull/4178) [`063d9b6d5`](https://github.com/medusajs/medusa/commit/063d9b6d5535e548356e8b47656371a8fbc54936) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): On duplicate product ensure order of variant options matches product options
- Updated dependencies [[`4f3c8f5d7`](https://github.com/medusajs/medusa/commit/4f3c8f5d70b5ae4a11e9d4a2fea4a8410b2daf47)]:
- medusa-react@9.0.0
## 1.8.6
### Patch Changes
- Updated dependencies []:
- medusa-react@8.0.0
## 1.8.5
### Patch Changes
- [#4049](https://github.com/medusajs/medusa/pull/4049) [`aa74ebb18`](https://github.com/medusajs/medusa/commit/aa74ebb181617f7373e61f3e18cf0f614912985a) Thanks [@riqwan](https://github.com/riqwan)! - fix(admin-ui): ensure that sales channel names are always an array
- [#4041](https://github.com/medusajs/medusa/pull/4041) [`353fc94e4`](https://github.com/medusajs/medusa/commit/353fc94e4f4da45262214446281e8951f23709e7) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): toggle manage inventory
- [#4050](https://github.com/medusajs/medusa/pull/4050) [`282953759`](https://github.com/medusajs/medusa/commit/282953759280eeed27615df0342ab00e43b79793) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Make download path respect **BASE** in UploadModal.
- Updated dependencies []:
- medusa-react@7.0.1
## 1.8.4
### Patch Changes
- [#3955](https://github.com/medusajs/medusa/pull/3955) [`4c5c4fd9b`](https://github.com/medusajs/medusa/commit/4c5c4fd9b3941ee27e6c8166377fd8e8f1b7ad51) Thanks [@pKorsholm](https://github.com/pKorsholm)! - Feat(admin-ui): add metadata controls for stock locations
- [#3950](https://github.com/medusajs/medusa/pull/3950) [`0c30d05cf`](https://github.com/medusajs/medusa/commit/0c30d05cf3805515086dc589a0332b59b6df045f) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): allocation status for cancelled and archived orders
- [#3999](https://github.com/medusajs/medusa/pull/3999) [`a7adb8fbf`](https://github.com/medusajs/medusa/commit/a7adb8fbf4f6a0708e590848ec6c315ff7eb05cf) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Pins version of `react-router-dom` as upgrade to 6.11.0 broke navigation in nested routes
- [#3989](https://github.com/medusajs/medusa/pull/3989) [`596566a51`](https://github.com/medusajs/medusa/commit/596566a51061ba868ffd2306658f01030e52316f) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): fixes an issue where navigating to tax settings would append the basename multiple times. Removes `react-helmet` for `react-helmet-async` to get rid off error caused by unsafe sideeffects"
- Updated dependencies []:
- medusa-react@7.0.0
## 1.8.3
### Patch Changes
- [#3753](https://github.com/medusajs/medusa/pull/3753) [`ba45a316a`](https://github.com/medusajs/medusa/commit/ba45a316a7638cc003599900d0088e5a5aeb51c8) Thanks [@StephixOne](https://github.com/StephixOne)! - feat(admin): Implement allocations on draft orders
- [#3895](https://github.com/medusajs/medusa/pull/3895) [`88a224e42`](https://github.com/medusajs/medusa/commit/88a224e42a490fd7fae29677c463740b89ad9b27) Thanks [@fPolic](https://github.com/fPolic)! - fix(admin): display correct items in the timeline after OE
- [`d302db239`](https://github.com/medusajs/medusa/commit/d302db23975227527e5632110d67c2ac299317b3) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin): add guard for null variant in inventory overview
- [#3886](https://github.com/medusajs/medusa/pull/3886) [`5fdccf5e3`](https://github.com/medusajs/medusa/commit/5fdccf5e3256b5e515c3d9a8ba9eaf60b2ce7ad3) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Check for undefined categories
- [#3897](https://github.com/medusajs/medusa/pull/3897) [`0be681657`](https://github.com/medusajs/medusa/commit/0be6816578f5cac6f22ddea267fc4c066727b0ae) Thanks [@olivermrbl](https://github.com/olivermrbl)! - feat(admin-ui): Add Sales Channel filter
- [#3883](https://github.com/medusajs/medusa/pull/3883) [`966ddd2f1`](https://github.com/medusajs/medusa/commit/966ddd2f1648d5d3c1c68094f488f307e3186d92) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(inventory): Minor fixes to upserting inventory items
- Updated dependencies []:
- medusa-react@6.0.0
## 1.8.2
### Patch Changes
- Updated dependencies []:
- medusa-react@5.0.2
## 1.8.1
### Patch Changes
- [#3750](https://github.com/medusajs/medusa/pull/3750) [`6f48cc57b`](https://github.com/medusajs/medusa/commit/6f48cc57bc0b06925a707278a6d820f0e2e0fe81) Thanks [@fPolic](https://github.com/fPolic)! - fix(admin-ui): correct notification text on fulfillment cancel
- [#3755](https://github.com/medusajs/medusa/pull/3755) [`72b76bbd3`](https://github.com/medusajs/medusa/commit/72b76bbd3c36e06c1536e115dea9ac6de659d482) Thanks [@fPolic](https://github.com/fPolic)! - fix(admin-ui): display SC update notification
- [#3768](https://github.com/medusajs/medusa/pull/3768) [`d533caa4c`](https://github.com/medusajs/medusa/commit/d533caa4c2986c1c9e320cce97423b0cdc213b6c) Thanks [@riqwan](https://github.com/riqwan)! - feat(medusa, admin-ui): add description field to product categories
- Updated dependencies [[`c24c2a973`](https://github.com/medusajs/medusa/commit/c24c2a9732bb0d1aff481b723e421b7fcb49a1f0), [`30ee10fdd`](https://github.com/medusajs/medusa/commit/30ee10fddd7d8e2f7d742101aad10d2c688ac98e)]:
- medusa-react@5.0.1
## 1.8.0
### Patch Changes
- [#3403](https://github.com/medusajs/medusa/pull/3403) [`57d7728dd`](https://github.com/medusajs/medusa/commit/57d7728dd9d00df712e1a872899b8397955dfe46) Thanks [@StephixOne](https://github.com/StephixOne)! - add location support in fulfillment modal
- [#3461](https://github.com/medusajs/medusa/pull/3461) [`478903b55`](https://github.com/medusajs/medusa/commit/478903b55a6f7c276fa2285ee35bf8a2b51a72fb) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Admin UI fixes / enhancements
- Inventory and order UI fixes and tweaks
- focus-border clipping
- Fix use of `expand` parameter on order page
- Fix location address editing form state
- ensure that the allocation indicator is correctly displayed
- Fix inventory table pagination on location filter change
- Try and ensure allocation table checkmarks align better
- Add gap in table actions
- remove allocate button if no more allcoations can be made
- update edit-allocation sidebar
- create/update/delete inventory items according to inventory items on the variant
- Fix minor bugs related to the edit-allocation modal
- move tailwind to direct dependency
- display error messages for batch jobs
- draft order shipping details
- Implements redesigned public facing pages of admin UI.
- Add location names to fulfilment rows and timeline events
- Encode location id in URL on location table
- [#3666](https://github.com/medusajs/medusa/pull/3666) [`ca3b32d53`](https://github.com/medusajs/medusa/commit/ca3b32d53c6854716bcbfb641ed381fa0f019a58) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Make copy and UI on manage locations modal better when no locations
- [#3676](https://github.com/medusajs/medusa/pull/3676) [`788ddc0f4`](https://github.com/medusajs/medusa/commit/788ddc0f43696df607f07133af15a04b29d5447d) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui, medusa-react, medusa): Minor fixes to GC domain in admin UI. Also fixes GC update payload type in medusa-react and medusa.
- [#3512](https://github.com/medusajs/medusa/pull/3512) [`a8423b8ac`](https://github.com/medusajs/medusa/commit/a8423b8acc8723dd3f5475d23bd9cb8be9c630c0) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(medusa, admin-ui): Fix edit order variant stock
- [#3644](https://github.com/medusajs/medusa/pull/3644) [`4342ac884`](https://github.com/medusajs/medusa/commit/4342ac884bc3fe473576ef10d291f3547e0ffc62) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - feat(admin-ui): Adds metadata forms to all applicable domains in the UI.
fix(medusa): Fixes an issue where metadata was not being set for order addresses using `setMetadata`.
- [#3670](https://github.com/medusajs/medusa/pull/3670) [`a5ad6c054`](https://github.com/medusajs/medusa/commit/a5ad6c05428e1bb090bbc5a51345a00821781c06) Thanks [@pKorsholm](https://github.com/pKorsholm)! - Fix(admin-ui,medusa): Ensure stock locations are created with a name
- [#3657](https://github.com/medusajs/medusa/pull/3657) [`7e1d8ef59`](https://github.com/medusajs/medusa/commit/7e1d8ef599fe4588d439b449373debf1221e625d) Thanks [@StephixOne](https://github.com/StephixOne)! - feat(admin-ui): Add new feature badge for categories and inventory
- [#3685](https://github.com/medusajs/medusa/pull/3685) [`8ddb3952c`](https://github.com/medusajs/medusa/commit/8ddb3952c045e6c05c8d0f6922f0d4ba30cf3bd4) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Fix RC package versions
- [#3607](https://github.com/medusajs/medusa/pull/3607) [`d1a6aa5a9`](https://github.com/medusajs/medusa/commit/d1a6aa5a90c147a34448e5398a1f205649598c09) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): fix bug with create fulfillment and a couple of other minor tweaks
- [#3706](https://github.com/medusajs/medusa/pull/3706) [`dae8da109`](https://github.com/medusajs/medusa/commit/dae8da1099bbe5fd3c59adc7fd2c28b41c0f71bc) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(admin): Duplicate product without prices
- [#3660](https://github.com/medusajs/medusa/pull/3660) [`eed784d7d`](https://github.com/medusajs/medusa/commit/eed784d7d0b58aeddc9f6f5ea56fe80c608b22f5) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui, medusa): resolve bugs for orders with variants without inventory items
- [#3438](https://github.com/medusajs/medusa/pull/3438) [`f027bc26f`](https://github.com/medusajs/medusa/commit/f027bc26fca94931df61c1cc0c7450de08020975) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin): Show correct reserved/available values when editing stock on variant
- [#3418](https://github.com/medusajs/medusa/pull/3418) [`8a7421db5`](https://github.com/medusajs/medusa/commit/8a7421db5bd235dd3cf88bf73d2438af8769f7ba) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - feat(admin,admin-ui): Updates the default behaviour of the plugin, and makes building for external deployment easier
- [#3532](https://github.com/medusajs/medusa/pull/3532) [`bfef22b33`](https://github.com/medusajs/medusa/commit/bfef22b33e0aa4aa542d3f22fb32ee261f5a19ea) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Revamps gift card manage page
- [#3608](https://github.com/medusajs/medusa/pull/3608) [`345005573`](https://github.com/medusajs/medusa/commit/345005573a337d63394b63d33d6740a4171d1479) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Update order edit variants table to fit longer content
- [#3585](https://github.com/medusajs/medusa/pull/3585) [`0d0090338`](https://github.com/medusajs/medusa/commit/0d00903385df980e355c54cee9bcd4e8ede21635) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): minor fixes for inventory items
- [#3592](https://github.com/medusajs/medusa/pull/3592) [`455c56c4b`](https://github.com/medusajs/medusa/commit/455c56c4b3846c583f39ba56352d645128b0c967) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Hide inventory quantity field in variant stock form if SL module enabled
- [#3395](https://github.com/medusajs/medusa/pull/3395) [`15f47baf5`](https://github.com/medusajs/medusa/commit/15f47baf56e6722b7821cfaa2fb468e582dfa2c1) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui, medusa): Minor ui fixes relating to stock locations
- [#3645](https://github.com/medusajs/medusa/pull/3645) [`e6b5859af`](https://github.com/medusajs/medusa/commit/e6b5859af213185408e1087073c1cba10f7b33c2) Thanks [@StephixOne](https://github.com/StephixOne)! - feat(admin-ui): Make number input increment/decrement buttons not tabbable-to
- [#3522](https://github.com/medusajs/medusa/pull/3522) [`55c5fba0d`](https://github.com/medusajs/medusa/commit/55c5fba0d3dbd015c3ffd74d645a8057892d0f52) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(medusa,admin): location_ids in swap and claim creation
- [#3334](https://github.com/medusajs/medusa/pull/3334) [`40de54b01`](https://github.com/medusajs/medusa/commit/40de54b0101bdfd37f577d18c10ec9f1ab1ce8fe) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - feat(medusa,admin,admin-ui): Add new plugin to serve the admin dashboard from the server. Adds a new plugin injection step `setup`, code placed in the `setup` folder of a plugin will be run before any code from a plugin is injected into the Medusa server.
- [#3416](https://github.com/medusajs/medusa/pull/3416) [`478d1af8d`](https://github.com/medusajs/medusa/commit/478d1af8d0df0af16baf4f130e19b0be34f5f295) Thanks [@riqwan](https://github.com/riqwan)! - feat(medusa, admin-ui, medusa-react): Improvements to product categories
- Adds name as required in category create form
- Adds name and handle as required in category edit form
- Updates message on create/update forms
- Adds category indicators for is_internal and is_active fields in the tree list
- Fixes bug where tree is not reset when update fails
- allow appending all category descendants with a param in list endpoint
- fix rank order changing on category update
- invalidate products query on category delete
- adds category ui for tree/list, edit, create, delete
- add product category queries and mutations
- category list API can return all descendant
- added breadcrumbs for categories on create/edit modal
- add empty state for product categories
- increase tree depth + scope categories on store + allow categories relation in products API
- categories can be ranked based on position
- seed command can create product categories
- hide categories in products behind feature flag
- fixes bug for mpath incorrectly updated for nested categories
- [#3434](https://github.com/medusajs/medusa/pull/3434) [`f43f03bad`](https://github.com/medusajs/medusa/commit/f43f03badbfd1d3598f3c6bc6c81e6fa2e9c2a09) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin): Fix fulfilment creation
- [#3655](https://github.com/medusajs/medusa/pull/3655) [`45fd0fb63`](https://github.com/medusajs/medusa/commit/45fd0fb639f05db6115be6823d42b85672c0891d) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Always show categories in product page
- [#3531](https://github.com/medusajs/medusa/pull/3531) [`4e9d257d3`](https://github.com/medusajs/medusa/commit/4e9d257d3bf76703ef5be8ca054cc9f0f7339def) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Remove dependency on @medusajs/medusa from Inventory and Stock-Location Modules
- [#3577](https://github.com/medusajs/medusa/pull/3577) [`95c9fbfdd`](https://github.com/medusajs/medusa/commit/95c9fbfdd5f290df0f22115f27b82f0812f8bd67) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Fix effect check in inventory table and overflow UI
- [#3448](https://github.com/medusajs/medusa/pull/3448) [`d4af87311`](https://github.com/medusajs/medusa/commit/d4af873113e4e3bb75ff1f5646870eb77b7a2b40) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin): Show all locations in allocation creation modal
- [#3541](https://github.com/medusajs/medusa/pull/3541) [`feaf8d2e1`](https://github.com/medusajs/medusa/commit/feaf8d2e19715585d154464d003759c3a1f4f322) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(medusa, admin-ui): refine create-fulfillment flow
- [#3403](https://github.com/medusajs/medusa/pull/3403) [`57d7728dd`](https://github.com/medusajs/medusa/commit/57d7728dd9d00df712e1a872899b8397955dfe46) Thanks [@StephixOne](https://github.com/StephixOne)! - Add order allocation to admin ui
- [#3451](https://github.com/medusajs/medusa/pull/3451) [`55a1f232a`](https://github.com/medusajs/medusa/commit/55a1f232a3746a22adb1fcd1844b2659077a59f9) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(medusa,admin-ui): support location_id in
- [#3401](https://github.com/medusajs/medusa/pull/3401) [`47d344076`](https://github.com/medusajs/medusa/commit/47d3440766efa26aba6638a05edec520a4e62828) Thanks [@riqwan](https://github.com/riqwan)! - feat(admin-ui, medusa-react): allow products to be categorized in product create/edit page
- [#3625](https://github.com/medusajs/medusa/pull/3625) [`7428ffa30`](https://github.com/medusajs/medusa/commit/7428ffa300a1e5781ef18b86edf97b711df0b585) Thanks [@StephixOne](https://github.com/StephixOne)! - Fix team table filter dropdown transparency
- Updated dependencies [[`d06ab9299`](https://github.com/medusajs/medusa/commit/d06ab929946c9e3842dcf4299b2c7e4c85fcc116), [`788ddc0f4`](https://github.com/medusajs/medusa/commit/788ddc0f43696df607f07133af15a04b29d5447d), [`fe9eea4c1`](https://github.com/medusajs/medusa/commit/fe9eea4c18b7e04ba91660716c92b11a49840a3c), [`7f87c4f2c`](https://github.com/medusajs/medusa/commit/7f87c4f2c8abb876213a595005e67d770be9cbe4), [`8ddb3952c`](https://github.com/medusajs/medusa/commit/8ddb3952c045e6c05c8d0f6922f0d4ba30cf3bd4), [`9ba09ba4d`](https://github.com/medusajs/medusa/commit/9ba09ba4d753f132537f0447097fe9f54922c074), [`12d304307`](https://github.com/medusajs/medusa/commit/12d304307af87ea9287a41869eb33ef09f273d85), [`80b95a230`](https://github.com/medusajs/medusa/commit/80b95a230056d9ed15f7302f248094f879516faf), [`54dcc1871`](https://github.com/medusajs/medusa/commit/54dcc1871c8f28bea962dbb9df6e79b038d56449), [`478d1af8d`](https://github.com/medusajs/medusa/commit/478d1af8d0df0af16baf4f130e19b0be34f5f295), [`6748877c6`](https://github.com/medusajs/medusa/commit/6748877c694c1433f666c6987f20af76b201b495), [`ef5ef9f5a`](https://github.com/medusajs/medusa/commit/ef5ef9f5a26febf0b64d9981606c1e59999ca76e), [`2d2727f75`](https://github.com/medusajs/medusa/commit/2d2727f753dd9386160d7e677b927c4915e1fce7), [`47d344076`](https://github.com/medusajs/medusa/commit/47d3440766efa26aba6638a05edec520a4e62828)]:
- medusa-react@5.0.0
## 0.0.1-rc.8
### Patch Changes
- Updated dependencies []:
- medusa-react@5.0.0-rc.8
## 0.0.1-rc.7
### Patch Changes
- [#3706](https://github.com/medusajs/medusa/pull/3706) [`dae8da109`](https://github.com/medusajs/medusa/commit/dae8da1099bbe5fd3c59adc7fd2c28b41c0f71bc) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(admin): Duplicate product without prices
- Updated dependencies []:
- medusa-react@5.0.0-rc.7
## 0.0.1-rc.6
### Patch Changes
- [#3666](https://github.com/medusajs/medusa/pull/3666) [`ca3b32d53`](https://github.com/medusajs/medusa/commit/ca3b32d53c6854716bcbfb641ed381fa0f019a58) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Make copy and UI on manage locations modal better when no locations
- [#3676](https://github.com/medusajs/medusa/pull/3676) [`788ddc0f4`](https://github.com/medusajs/medusa/commit/788ddc0f43696df607f07133af15a04b29d5447d) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui, medusa-react, medusa): Minor fixes to GC domain in admin UI. Also fixes GC update payload type in medusa-react and medusa.
- [#3670](https://github.com/medusajs/medusa/pull/3670) [`a5ad6c054`](https://github.com/medusajs/medusa/commit/a5ad6c05428e1bb090bbc5a51345a00821781c06) Thanks [@pKorsholm](https://github.com/pKorsholm)! - Fix(admin-ui,medusa): Ensure stock locations are created with a name
- [#3660](https://github.com/medusajs/medusa/pull/3660) [`eed784d7d`](https://github.com/medusajs/medusa/commit/eed784d7d0b58aeddc9f6f5ea56fe80c608b22f5) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui, medusa): resolve bugs for orders with variants without inventory items
- Updated dependencies [[`d06ab9299`](https://github.com/medusajs/medusa/commit/d06ab929946c9e3842dcf4299b2c7e4c85fcc116), [`788ddc0f4`](https://github.com/medusajs/medusa/commit/788ddc0f43696df607f07133af15a04b29d5447d), [`7f87c4f2c`](https://github.com/medusajs/medusa/commit/7f87c4f2c8abb876213a595005e67d770be9cbe4)]:
- medusa-react@5.0.0-rc.6
## 0.0.1-rc.5
### Patch Changes
- [#3644](https://github.com/medusajs/medusa/pull/3644) [`4342ac884`](https://github.com/medusajs/medusa/commit/4342ac884bc3fe473576ef10d291f3547e0ffc62) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - feat(admin-ui): Adds metadata forms to all applicable domains in the UI.
fix(medusa): Fixes an issue where metadata was not being set for order addresses using `setMetadata`.
- [#3657](https://github.com/medusajs/medusa/pull/3657) [`7e1d8ef59`](https://github.com/medusajs/medusa/commit/7e1d8ef599fe4588d439b449373debf1221e625d) Thanks [@StephixOne](https://github.com/StephixOne)! - feat(admin-ui): Add new feature badge for categories and inventory
- [#3645](https://github.com/medusajs/medusa/pull/3645) [`e6b5859af`](https://github.com/medusajs/medusa/commit/e6b5859af213185408e1087073c1cba10f7b33c2) Thanks [@StephixOne](https://github.com/StephixOne)! - feat(admin-ui): Make number input increment/decrement buttons not tabbable-to
- [#3655](https://github.com/medusajs/medusa/pull/3655) [`45fd0fb63`](https://github.com/medusajs/medusa/commit/45fd0fb639f05db6115be6823d42b85672c0891d) Thanks [@olivermrbl](https://github.com/olivermrbl)! - fix(admin-ui): Always show categories in product page
- Updated dependencies []:
- medusa-react@5.0.0-rc.5
## 0.0.1-rc.4
### Patch Changes
- [#3585](https://github.com/medusajs/medusa/pull/3585) [`0d0090338`](https://github.com/medusajs/medusa/commit/0d00903385df980e355c54cee9bcd4e8ede21635) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): minor fixes for inventory items
- [#3625](https://github.com/medusajs/medusa/pull/3625) [`7428ffa30`](https://github.com/medusajs/medusa/commit/7428ffa300a1e5781ef18b86edf97b711df0b585) Thanks [@StephixOne](https://github.com/StephixOne)! - Fix team table filter dropdown transparency
- Updated dependencies []:
- medusa-react@5.0.0-rc.4
## 0.0.1-rc.3
### Patch Changes
- Updated dependencies []:
- medusa-react@5.0.0-rc.3
## 0.0.1-rc.2
### Patch Changes
- chore: Fix RC package versions
- Updated dependencies []:
- medusa-react@5.0.0-rc.2
## 0.0.1-rc.1
### Patch Changes
- [#3607](https://github.com/medusajs/medusa/pull/3607) [`d1a6aa5a9`](https://github.com/medusajs/medusa/commit/d1a6aa5a90c147a34448e5398a1f205649598c09) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui): fix bug with create fulfillment and a couple of other minor tweaks
- [#3608](https://github.com/medusajs/medusa/pull/3608) [`345005573`](https://github.com/medusajs/medusa/commit/345005573a337d63394b63d33d6740a4171d1479) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Update order edit variants table to fit longer content
- [#3592](https://github.com/medusajs/medusa/pull/3592) [`455c56c4b`](https://github.com/medusajs/medusa/commit/455c56c4b3846c583f39ba56352d645128b0c967) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Hide inventory quantity field in variant stock form if SL module enabled
- [#3577](https://github.com/medusajs/medusa/pull/3577) [`95c9fbfdd`](https://github.com/medusajs/medusa/commit/95c9fbfdd5f290df0f22115f27b82f0812f8bd67) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Fix effect check in inventory table and overflow UI
- [#3541](https://github.com/medusajs/medusa/pull/3541) [`feaf8d2e1`](https://github.com/medusajs/medusa/commit/feaf8d2e19715585d154464d003759c3a1f4f322) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(medusa, admin-ui): refine create-fulfillment flow
- Updated dependencies []:
- medusa-react@5.0.0-rc.1
## 0.0.1-rc.0
### Patch Changes
- [#3403](https://github.com/medusajs/medusa/pull/3403) [`57d7728dd`](https://github.com/medusajs/medusa/commit/57d7728dd9d00df712e1a872899b8397955dfe46) Thanks [@StephixOne](https://github.com/StephixOne)! - add location support in fulfillment modal
- [#3461](https://github.com/medusajs/medusa/pull/3461) [`478903b55`](https://github.com/medusajs/medusa/commit/478903b55a6f7c276fa2285ee35bf8a2b51a72fb) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin-ui): Admin UI fixes / enhancements
- Inventory and order UI fixes and tweaks
- focus-border clipping
- Fix use of `expand` parameter on order page
- Fix location address editing form state
- ensure that the allocation indicator is correctly displayed
- Fix inventory table pagination on location filter change
- Try and ensure allocation table checkmarks align better
- Add gap in table actions
- remove allocate button if no more allcoations can be made
- update edit-allocation sidebar
- create/update/delete inventory items according to inventory items on the variant
- Fix minor bugs related to the edit-allocation modal
- move tailwind to direct dependency
- display error messages for batch jobs
- draft order shipping details
- Implements redesigned public facing pages of admin UI.
- Add location names to fulfilment rows and timeline events
- Encode location id in URL on location table
- [#3512](https://github.com/medusajs/medusa/pull/3512) [`a8423b8ac`](https://github.com/medusajs/medusa/commit/a8423b8acc8723dd3f5475d23bd9cb8be9c630c0) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(medusa, admin-ui): Fix edit order variant stock
- [#3438](https://github.com/medusajs/medusa/pull/3438) [`f027bc26f`](https://github.com/medusajs/medusa/commit/f027bc26fca94931df61c1cc0c7450de08020975) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin): Show correct reserved/available values when editing stock on variant
- [#3418](https://github.com/medusajs/medusa/pull/3418) [`8a7421db5`](https://github.com/medusajs/medusa/commit/8a7421db5bd235dd3cf88bf73d2438af8769f7ba) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - feat(admin,admin-ui): Updates the default behaviour of the plugin, and makes building for external deployment easier
- [#3532](https://github.com/medusajs/medusa/pull/3532) [`bfef22b33`](https://github.com/medusajs/medusa/commit/bfef22b33e0aa4aa542d3f22fb32ee261f5a19ea) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - fix(admin-ui): Revamps gift card manage page
- [#3395](https://github.com/medusajs/medusa/pull/3395) [`15f47baf5`](https://github.com/medusajs/medusa/commit/15f47baf56e6722b7821cfaa2fb468e582dfa2c1) Thanks [@pKorsholm](https://github.com/pKorsholm)! - fix(admin-ui, medusa): Minor ui fixes relating to stock locations
- [#3522](https://github.com/medusajs/medusa/pull/3522) [`55c5fba0d`](https://github.com/medusajs/medusa/commit/55c5fba0d3dbd015c3ffd74d645a8057892d0f52) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(medusa,admin): location_ids in swap and claim creation
- [#3334](https://github.com/medusajs/medusa/pull/3334) [`40de54b01`](https://github.com/medusajs/medusa/commit/40de54b0101bdfd37f577d18c10ec9f1ab1ce8fe) Thanks [@kasperkristensen](https://github.com/kasperkristensen)! - feat(medusa,admin,admin-ui): Add new plugin to serve the admin dashboard from the server. Adds a new plugin injection step `setup`, code placed in the `setup` folder of a plugin will be run before any code from a plugin is injected into the Medusa server.
- [#3416](https://github.com/medusajs/medusa/pull/3416) [`478d1af8d`](https://github.com/medusajs/medusa/commit/478d1af8d0df0af16baf4f130e19b0be34f5f295) Thanks [@riqwan](https://github.com/riqwan)! - feat(medusa, admin-ui, medusa-react): Improvements to product categories
- Adds name as required in category create form
- Adds name and handle as required in category edit form
- Updates message on create/update forms
- Adds category indicators for is_internal and is_active fields in the tree list
- Fixes bug where tree is not reset when update fails
- allow appending all category descendants with a param in list endpoint
- fix rank order changing on category update
- invalidate products query on category delete
- adds category ui for tree/list, edit, create, delete
- add product category queries and mutations
- category list API can return all descendant
- added breadcrumbs for categories on create/edit modal
- add empty state for product categories
- increase tree depth + scope categories on store + allow categories relation in products API
- categories can be ranked based on position
- seed command can create product categories
- hide categories in products behind feature flag
- fixes bug for mpath incorrectly updated for nested categories
- [#3434](https://github.com/medusajs/medusa/pull/3434) [`f43f03bad`](https://github.com/medusajs/medusa/commit/f43f03badbfd1d3598f3c6bc6c81e6fa2e9c2a09) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin): Fix fulfilment creation
- [#3531](https://github.com/medusajs/medusa/pull/3531) [`4e9d257d3`](https://github.com/medusajs/medusa/commit/4e9d257d3bf76703ef5be8ca054cc9f0f7339def) Thanks [@carlos-r-l-rodrigues](https://github.com/carlos-r-l-rodrigues)! - Remove dependency on @medusajs/medusa from Inventory and Stock-Location Modules
- [#3448](https://github.com/medusajs/medusa/pull/3448) [`d4af87311`](https://github.com/medusajs/medusa/commit/d4af873113e4e3bb75ff1f5646870eb77b7a2b40) Thanks [@StephixOne](https://github.com/StephixOne)! - fix(admin): Show all locations in allocation creation modal
- [#3403](https://github.com/medusajs/medusa/pull/3403) [`57d7728dd`](https://github.com/medusajs/medusa/commit/57d7728dd9d00df712e1a872899b8397955dfe46) Thanks [@StephixOne](https://github.com/StephixOne)! - Add order allocation to admin ui
- [#3451](https://github.com/medusajs/medusa/pull/3451) [`55a1f232a`](https://github.com/medusajs/medusa/commit/55a1f232a3746a22adb1fcd1844b2659077a59f9) Thanks [@pKorsholm](https://github.com/pKorsholm)! - feat(medusa,admin-ui): support location_id in
- [#3401](https://github.com/medusajs/medusa/pull/3401) [`47d344076`](https://github.com/medusajs/medusa/commit/47d3440766efa26aba6638a05edec520a4e62828) Thanks [@riqwan](https://github.com/riqwan)! - feat(admin-ui, medusa-react): allow products to be categorized in product create/edit page
- Updated dependencies [[`fe9eea4c1`](https://github.com/medusajs/medusa/commit/fe9eea4c18b7e04ba91660716c92b11a49840a3c), [`9ba09ba4d`](https://github.com/medusajs/medusa/commit/9ba09ba4d753f132537f0447097fe9f54922c074), [`12d304307`](https://github.com/medusajs/medusa/commit/12d304307af87ea9287a41869eb33ef09f273d85), [`80b95a230`](https://github.com/medusajs/medusa/commit/80b95a230056d9ed15f7302f248094f879516faf), [`54dcc1871`](https://github.com/medusajs/medusa/commit/54dcc1871c8f28bea962dbb9df6e79b038d56449), [`478d1af8d`](https://github.com/medusajs/medusa/commit/478d1af8d0df0af16baf4f130e19b0be34f5f295), [`6748877c6`](https://github.com/medusajs/medusa/commit/6748877c694c1433f666c6987f20af76b201b495), [`ef5ef9f5a`](https://github.com/medusajs/medusa/commit/ef5ef9f5a26febf0b64d9981606c1e59999ca76e), [`2d2727f75`](https://github.com/medusajs/medusa/commit/2d2727f753dd9386160d7e677b927c4915e1fce7), [`47d344076`](https://github.com/medusajs/medusa/commit/47d3440766efa26aba6638a05edec520a4e62828)]:
- medusa-react@5.0.0-rc.0
-38
View File
@@ -1,38 +0,0 @@
<p align="center">
<a href="https://www.medusajs.com">
<img alt="Medusa" src="https://user-images.githubusercontent.com/7554214/153162406-bf8fd16f-aa98-4604-b87b-e13ab4baf604.png" width="100" />
</a>
</p>
<h1 align="center">
@medusajs/admin-ui
</h1>
<h4 align="center">
<a href="https://docs.medusajs.com">Documentation</a> |
<a href="https://demo.medusajs.com/">Medusa Admin Demo</a> |
<a href="https://www.medusajs.com">Website</a>
</h4>
<p align="center">
An open source composable commerce engine built for developers.
</p>
<p align="center">
<a href="https://github.com/medusajs/medusa/blob/master/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Medusa is released under the MIT license." />
</a>
<a href="https://circleci.com/gh/medusajs/medusa">
<img src="https://circleci.com/gh/medusajs/medusa.svg?style=shield" alt="Current CircleCI build status." />
</a>
<a href="https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md">
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="PRs welcome!" />
</a>
<a href="https://www.producthunt.com/posts/medusa"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Day-%23DA552E" alt="Product Hunt"></a>
<a href="https://discord.gg/xpCwq3Kfn8">
<img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
</a>
<a href="https://twitter.com/intent/follow?screen_name=medusajs">
<img src="https://img.shields.io/twitter/follow/medusajs.svg?label=Follow%20@medusajs" alt="Follow @medusajs" />
</a>
</p>
## The Medusa Admin App. Included with the [`@medusajs/admin`](https://www.npmjs.com/package/@medusajs/admin) plugin. You shouldn't install this package separately.
-13
View File
@@ -1,13 +0,0 @@
module.exports = {
globals: {
"ts-jest": {
tsconfig: "tsconfig.spec.json",
isolatedModules: false,
},
},
transform: {
"^.+\\.[jt]s?$": "ts-jest",
},
testEnvironment: `node`,
moduleFileExtensions: [`js`, `ts`],
}
-139
View File
@@ -1,139 +0,0 @@
{
"name": "@medusajs/admin-ui",
"author": "Kasper Kristensen <kasper@medusajs.com>",
"license": "MIT",
"version": "2.1.14",
"repository": {
"type": "git",
"url": "https://github.com/medusajs/medusa.git",
"directory": "packages/admin-ui"
},
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./ui": "./ui",
"./package.json": "./package.json"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist",
"ui"
],
"scripts": {
"test": "jest --runInBand --forceExit -- ./src/**/__tests__/**/*.ts",
"create:dev:entry": "node ./scripts/create-dev-entry.js",
"dev": "yarn create:dev:entry && webpack serve --mode=development --config ./webpack.config.dev.ts --progress profile",
"analyze:bundle": "ANALYZE_BUNDLE=true webpack --config ./webpack.config.dev.ts",
"analyze:deps": "ANALYZE_DEPS=true webpack serve --config ./webpack.config.dev.ts --progress profile",
"build": "tsup",
"sync:i18n": "i18next 'ui/src/**/*.{ts,tsx,js,jsx}' -c 'ui/i18next-parser.config.js'"
},
"dependencies": {
"@babel/parser": "7.22.5",
"@babel/traverse": "7.22.5",
"@hookform/error-message": "^2.0.1",
"@hookform/resolvers": "^3.3.1",
"@medusajs/icons": "^1.1.0",
"@medusajs/ui": "^3.0.0",
"@medusajs/ui-preset": "^1.1.3",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@radix-ui/react-accordion": "^1.0.1",
"@radix-ui/react-avatar": "^1.0.1",
"@radix-ui/react-collapsible": "^1.0.1",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.2",
"@radix-ui/react-popover": "^1.0.3",
"@radix-ui/react-portal": "^1.0.2",
"@radix-ui/react-radio-group": "^1.1.1",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-switch": "^1.0.1",
"@radix-ui/react-tooltip": "^1.0.3",
"@segment/analytics-next": "^1.51.1",
"@stdlib/number-float64-base-normalize": "0.0.8",
"@svgr/webpack": "^8.0.1",
"@swc/core": "^1.3.61",
"@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "4.22.0",
"@tanstack/react-table": "^8.7.9",
"autoprefixer": "^10.4.13",
"chokidar": "^3.5.3",
"clsx": "^1.2.1",
"copy-to-clipboard": "^3.3.1",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.8.1",
"emoji-picker-react": "^4.4.3",
"framer-motion": "^9.1.6",
"html-webpack-plugin": "^5.5.1",
"i18next": "^22.5.0",
"i18next-browser-languagedetector": "^7.0.1",
"i18next-http-backend": "^2.2.1",
"md5": "^2.3.0",
"medusa-react": "^9.0.17",
"mini-css-extract-plugin": "^2.7.6",
"moment": "^2.29.4",
"path-browserify": "^1.0.1",
"pluralize": "^8.0.0",
"postcss": "^8.4.21",
"postcss-loader": "^7.3.2",
"postcss-preset-env": "^8.4.1",
"prism-react-renderer": "^2.0.4",
"process": "^0.11.10",
"query-string": "^8.1.0",
"react": "^18.2.0",
"react-collapsible": "^2.8.3",
"react-country-flag": "^3.0.2",
"react-currency-input-field": "^3.6.8",
"react-datepicker": "^4.8.0",
"react-dev-utils": "^12.0.1",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-helmet-async": "^1.3.0",
"react-highlight-words": "^0.18.0",
"react-hook-form": "7.38.0",
"react-hot-toast": "2.4.0",
"react-hotkeys-hook": "^3.4.7",
"react-i18next": "^12.3.1",
"react-json-tree": "^0.17.0",
"react-jwt": "^1.1.4",
"react-nestable": "^2.0.0",
"react-refresh": "^0.14.0",
"react-router-dom": "6.8.0",
"react-select": "^5.5.4",
"react-table": "^7.7.0",
"style-loader": "^3.3.3",
"swc-loader": "^0.2.3",
"swc-minify-webpack-plugin": "^2.1.1",
"tailwindcss": "3.2.2",
"tailwindcss-radix": "^2.7.0",
"ts-dedent": "^2.2.0",
"type-fest": "^3.6.0",
"webpack": "^5.84.1",
"webpack-dev-server": "4.15.0",
"webpackbar": "^5.0.2",
"zod": "^3.22.2"
},
"devDependencies": {
"@babel/types": "7.22.5",
"@medusajs/medusa": "^1.20.5",
"@medusajs/types": "^1.11.16",
"@types/pluralize": "^0.0.29",
"@types/react": "^18.2.0",
"@types/react-datepicker": "^4.10.0",
"@types/react-dom": "^18.2.0",
"@types/react-table": "^7.7.9",
"duplicate-dependencies-webpack-plugin": "^1.0.2",
"i18next-parser": "^8.0.0",
"jest": "25.5.4",
"ts-jest": "25.5.1",
"ts-node": "^10.9.1",
"tsup": "6.7.0",
"typescript": "^4.9.3",
"webpack-bundle-analyzer": "^4.9.0",
"webpack-cli": "^5.1.1"
},
"packageManager": "yarn@3.2.1"
}
@@ -1,23 +0,0 @@
const fse = require("fs-extra")
const path = require("path")
function createDevEntryFile() {
const devEntryContent = `
const extensions = []
export default extensions
`
const devEntryPath = path.resolve(
__dirname,
"..",
"ui",
"src",
"extensions",
"_main-entry.ts"
)
fse.outputFileSync(devEntryPath, devEntryContent)
}
createDevEntryFile()
-16
View File
@@ -1,16 +0,0 @@
export { forbiddenRoutes } from "../../ui/src/constants/forbidden-routes"
export { injectionZones } from "../../ui/src/constants/injection-zones"
export type {
Extension,
ForbiddenRoute,
InjectionZone,
RouteConfig,
RouteExtension,
RouteProps,
SettingConfig,
SettingExtension,
SettingProps,
WidgetConfig,
WidgetExtension,
WidgetProps,
} from "../../ui/src/types/extensions"
-2
View File
@@ -1,2 +0,0 @@
export * from "./client"
export * from "./node"
@@ -1,60 +0,0 @@
import path from "node:path"
import webpack, { WebpackError } from "webpack"
import { BuildArgs } from "../types"
import { logger } from "../utils"
import { createCacheDir } from "../utils/create-cache-dir"
import { getCustomWebpackConfig } from "../webpack"
/**
* Builds the admin UI.
*/
export async function build({
appDir,
buildDir,
plugins,
options,
reporting = "fancy",
}: BuildArgs) {
if (!process.env.PLUGIN_ADMIN_UI_SKIP_CACHE) {
await createCacheDir({ appDir, plugins })
}
const cacheDir = path.resolve(appDir, ".cache")
const entry = path.resolve(cacheDir, "admin", "src", "main.tsx")
const dest = path.resolve(appDir, buildDir)
const env = "production"
const config = await getCustomWebpackConfig(appDir, {
entry,
dest,
cacheDir,
env,
options,
reporting,
})
const compiler = webpack(config)
return new Promise((resolve, reject) => {
compiler.run((err: WebpackError, stats) => {
if (err) {
if (err.details) {
logger.error(err.details)
}
reject(err)
}
const info = stats.toJson()
if (stats.hasErrors()) {
logger.error(JSON.stringify(info.errors))
}
return resolve({
stats,
warnings: info.warnings,
})
})
})
}
@@ -1,18 +0,0 @@
import fse from "fs-extra"
import path from "node:path"
type CleanArgs = {
appDir: string
outDir: string
}
/**
* Cleans the build directory and cache directory.
*/
export async function clean({ appDir, outDir }: CleanArgs) {
const cacheDir = path.resolve(appDir, ".cache", "admin")
const buildDir = path.resolve(appDir, outDir)
await fse.remove(buildDir)
await fse.remove(cacheDir)
}
@@ -1,112 +0,0 @@
import path from "node:path"
import openBrowser from "react-dev-utils/openBrowser"
import webpack from "webpack"
import WebpackDevDerver, {
Configuration as DevServerConfiguration,
} from "webpack-dev-server"
import { DevelopArgs } from "../types"
import { logger, watchLocalAdminFolder } from "../utils"
import { createCacheDir } from "../utils/create-cache-dir"
import { getCustomWebpackConfig } from "../webpack"
/**
* Starts a development server for the admin UI.
*/
export async function develop({
appDir,
buildDir,
plugins,
options = {
path: "/",
backend: "http://localhost:9000",
develop: {
open: true,
host: "localhost",
port: 7001,
logLevel: "error",
stats: "normal",
allowedHosts: "auto",
},
},
}: DevelopArgs) {
const { cacheDir } = await createCacheDir({
appDir,
plugins,
})
const entry = path.resolve(cacheDir, "admin", "src", "main.tsx")
const dest = path.resolve(appDir, buildDir)
const env = "development"
const config = await getCustomWebpackConfig(appDir, {
entry,
dest,
cacheDir,
env,
options,
})
const compiler = webpack({
...config,
infrastructureLogging: { level: options.develop.logLevel },
stats: options.develop.stats === "normal" ? "errors-only" : undefined,
})
const devServerArgs: DevServerConfiguration = {
port: options.develop.port,
host: options.develop.host,
client: {
logging: "none",
overlay: {
errors: true,
warnings: false,
},
webSocketURL: options.develop.webSocketURL
? options.develop.webSocketURL
: `ws://${options.develop.host}:${options.develop.port}/ws`,
},
open: false,
onListening: options.develop.open
? function (devServer) {
if (!devServer) {
logger.warn("Failed to open browser.")
}
openBrowser(
`http://${options.develop.host}:${options.develop.port}${
options.path ? options.path : ""
}`
)
}
: undefined,
devMiddleware: {
publicPath: options.path,
stats: options.develop.stats === "normal" ? false : undefined,
},
historyApiFallback: {
index: options.path,
disableDotRule: true,
},
hot: true,
allowedHosts: options.develop.allowedHosts
? options.develop.allowedHosts
: "auto",
}
const server = new WebpackDevDerver(devServerArgs, compiler)
const runServer = async () => {
logger.info(
`Started development server on http://${options.develop.host}:${
options.develop.port
}${options.path ? options.path : ""}`
)
await server.start()
}
await runServer()
await watchLocalAdminFolder(appDir, cacheDir, plugins)
}
@@ -1,5 +0,0 @@
import { build } from "./build"
import { clean } from "./clean"
import { develop } from "./develop"
export { clean, build, develop }
-16
View File
@@ -1,16 +0,0 @@
export const ALIASED_PACKAGES = [
"react",
"react-dom",
"react-router-dom",
"react-dnd",
"react-dnd-html5-backend",
"react-select",
"react-helmet-async",
"@tanstack/react-query",
"@tanstack/react-table",
"@emotion/react",
"medusa-react",
"@medusajs/ui",
"@medusajs/icons",
"@medusajs/ui-preset",
] as const
-11
View File
@@ -1,11 +0,0 @@
export { build, clean, develop } from "./actions"
export { ALIASED_PACKAGES } from "./constants"
export type { AdminOptions, DevelopArgs } from "./types"
export {
findAllValidRoutes,
findAllValidSettings,
findAllValidWidgets,
logger,
normalizePath,
} from "./utils"
export { withCustomWebpackConfig } from "./webpack"
-105
View File
@@ -1,105 +0,0 @@
import type { Configuration } from "webpack"
type WebSocketURL = {
hostname?: string | undefined
password?: string | undefined
pathname?: string | undefined
port?: string | number | undefined
protocol?: string | undefined
username?: string | undefined
}
export type DevelopOptions = {
/**
* Determines whether the development server should open the admin dashboard
* in the browser.
*
* @default true
*/
open?: boolean
/**
* The host the development server should run on.
*
* @default "localhost"
*/
host?: string
/**
* The port the development server should run on.
* @default 7001
* */
port?: number
/**
* Determines the log level of the development server.
* @default "error"
*/
logLevel?: "error" | "none" | "warn" | "info" | "log" | "verbose"
/**
* Determines the verbosity of the development server.
* @default "normal"
*/
stats?: "normal" | "debug"
/**
* The development server allowed hosts.
* @default "auto"
*/
allowedHosts?: "auto" | "all" | string[]
/**
* Specifying URL to web socket server
*/
webSocketURL?: string | WebSocketURL | undefined
}
export type AdminOptions = {
/**
* The URL of your Medusa instance.
*
* This option will only be used if `serve` is `false`.
*/
backend?: string
/**
* The path to the admin dashboard. The path must be in the format of `/<path>`.
* The chosen path cannot be one of the reserved paths: "admin", "store".
* @default "/app"
*/
path?: string
/**
* The directory to output the build to. By default the plugin will build
* the dashboard to the `build` directory in the root folder.
* @default undefined
*/
outDir?: string
/**
* Options for the development server.
*/
develop?: DevelopOptions
}
type BuildReporting = "minimal" | "fancy"
export type WebpackConfigArgs = {
entry: string
dest: string
cacheDir: string
env: "development" | "production"
options?: AdminOptions
template?: string
publicFolder?: string
reporting?: BuildReporting
}
export type CustomWebpackConfigArgs = WebpackConfigArgs & {
devServer?: Configuration["devServer"]
}
type BaseArgs = {
appDir: string
buildDir: string
plugins?: string[]
options?: AdminOptions
}
export type BuildArgs = BaseArgs & {
reporting?: BuildReporting
}
export type DevelopArgs = BaseArgs
@@ -1,58 +0,0 @@
import path from "path"
import { normalizePath } from "../normalize-path"
describe("normalize path", () => {
beforeEach(() => {
jest.clearAllMocks()
})
describe("normalizePath", () => {
it("should normalize a file path", async () => {
const testPath = path.join("/", "custom", "page.tsx")
const result = normalizePath(testPath)
expect(result).toEqual("/custom/page.tsx")
})
it("should normalize a file path with brackets", async () => {
const testPath = path.join("/", "custom", "[id]", "page.tsx")
const result = normalizePath(testPath)
expect(result).toEqual("/custom/[id]/page.tsx")
})
})
describe("test windows platform", () => {
const originalPlatform = process.platform
beforeAll(() => {
Object.defineProperty(process, "platform", {
value: "win32",
})
})
afterAll(() => {
Object.defineProperty(process, "platform", {
value: originalPlatform,
})
})
it("should normalize a file path on Windows", async () => {
const testPath = path.win32.join("/", "custom", "page.tsx")
const result = normalizePath(testPath)
expect(result).toEqual("/custom/page.tsx")
})
it("should normalize a file path with brackets on Windows", async () => {
const testPath = path.win32.join("/", "custom", "[id]", "page.tsx")
const result = normalizePath(testPath)
expect(result).toEqual("/custom/[id]/page.tsx")
})
})
})
@@ -1,60 +0,0 @@
import path from "path"
import { createPath } from "../validate-extensions"
describe("validate extensions", () => {
beforeEach(function () {
jest.clearAllMocks()
})
describe("createPath", () => {
it("should return a URL path", async () => {
const testPath = path.join("/", "custom", "page.tsx")
const result = createPath(testPath)
expect(result).toEqual("/custom")
})
it("should return a URL path with a parameter", async () => {
const testPath = path.join("/", "custom", "[id]", "page.tsx")
const result = createPath(testPath)
expect(result).toEqual("/custom/:id")
})
})
describe("test windows platform", () => {
const originalPlatform = process.platform
beforeAll(() => {
Object.defineProperty(process, "platform", {
value: "win32",
})
})
afterAll(() => {
Object.defineProperty(process, "platform", {
value: originalPlatform,
})
})
describe("createPath", () => {
it("should return a URL path on Windows", async () => {
const testPath = path.win32.join("/", "custom", "page.tsx")
const result = createPath(testPath)
expect(result).toEqual("/custom")
})
it("should return a URL path with a parameter on Windows", async () => {
const testPath = path.win32.join("/", "custom", "[id]", "page.tsx")
const result = createPath(testPath)
expect(result).toEqual("/custom/:id")
})
})
})
})
@@ -1,22 +0,0 @@
import fse from "fs-extra"
/**
* Filter function to exclude test files and folders, as well as webpack configurations from being copied to the cache folder.
*/
export function copyFilter(src: string) {
if (fse.lstatSync(src).isDirectory() && src.includes("__test__")) {
return false
}
if (fse.lstatSync(src).isFile()) {
if (
src.includes(".test") ||
src.includes(".spec") ||
src.includes("webpack.config")
) {
return false
}
}
return true
}
@@ -1,50 +0,0 @@
import fse from "fs-extra"
import path from "node:path"
import { copyFilter } from "./copy-filter"
import { createEntry } from "./create-entry"
import { logger } from "./logger"
async function copyAdmin(dest: string) {
if (process.env.PLUGIN_ADMIN_UI_SKIP_CACHE) {
return true
}
const adminDir = path.resolve(__dirname, "..", "ui")
const destDir = path.resolve(dest, "admin")
try {
await fse.copy(adminDir, destDir, {
filter: copyFilter,
})
} catch (err) {
logger.panic(
`Could not copy the admin UI to ${destDir}. See the error below for details:`,
{
error: err,
}
)
}
}
type CreateCacheDirArgs = {
appDir: string
plugins?: string[]
}
async function createCacheDir({ appDir, plugins }: CreateCacheDirArgs) {
const cacheDir = path.resolve(appDir, ".cache")
await copyAdmin(cacheDir)
await createEntry({
appDir,
dest: cacheDir,
plugins,
})
return {
cacheDir,
}
}
export { createCacheDir }
@@ -1,325 +0,0 @@
import fse from "fs-extra"
import path from "node:path"
import dedent from "ts-dedent"
import { copyFilter } from "./copy-filter"
import { logger } from "./logger"
import { normalizePath } from "./normalize-path"
import {
findAllValidRoutes,
findAllValidSettings,
findAllValidWidgets,
} from "./validate-extensions"
const FILE_EXT_REGEX = /\.[^/.]+$/
async function copyLocalExtensions(src: string, dest: string) {
if (process.env.PLUGIN_ADMIN_UI_SKIP_CACHE) {
return true
}
try {
await fse.copy(src, dest, {
filter: copyFilter,
})
} catch (err) {
logger.error(
`Could not copy local extensions to cache folder. See the error below for details:`,
{
error: err,
}
)
return false
}
return true
}
/**
* Creates an entry file for any local extensions, if they exist.
*/
async function createLocalExtensionsEntry(appDir: string, dest: string) {
const localAdminDir = path.resolve(appDir, "src", "admin")
const localAdminDirExists = await fse.pathExists(localAdminDir)
if (!localAdminDirExists) {
return false
}
const copied = await copyLocalExtensions(
localAdminDir,
path.resolve(dest, "admin", "src", "extensions")
)
if (!copied) {
logger.error(
"Could not copy local extensions to cache folder. See above error for details. The error must be fixed before any local extensions can be injected."
)
return false
}
const [localWidgets, localRoutes, localSettings] = await Promise.all([
findAllValidWidgets(
path.resolve(dest, "admin", "src", "extensions", "widgets")
),
findAllValidRoutes(
path.resolve(dest, "admin", "src", "extensions", "routes")
),
findAllValidSettings(
path.resolve(dest, "admin", "src", "extensions", "settings")
),
])
const widgetsArray = localWidgets.map((file, index) => {
const relativePath = normalizePath(
path
.relative(path.resolve(dest, "admin", "src", "extensions"), file)
.replace(FILE_EXT_REGEX, "")
)
return {
importStatement: `import Widget${index}, { config as widgetConfig${index} } from "./${relativePath}"`,
extension: `{ Component: Widget${index}, config: { ...widgetConfig${index}, type: "widget" } }`,
}
})
const routesArray = localRoutes.map((route, index) => {
const relativePath = normalizePath(
path
.relative(path.resolve(dest, "admin", "src", "extensions"), route.file)
.replace(FILE_EXT_REGEX, "")
)
const importStatement = route.hasConfig
? `import Page${index}, { config as routeConfig${index} } from "./${relativePath}"`
: `import Page${index} from "./${relativePath}"`
const extension = route.hasConfig
? `{ Component: Page${index}, config: { ...routeConfig${index}, type: "route", path: "${route.path}" } }`
: `{ Component: Page${index}, config: { path: "${route.path}", type: "route" } }`
return {
importStatement,
extension,
}
})
const settingsArray = localSettings.map((setting, index) => {
const relativePath = normalizePath(
path
.relative(
path.resolve(dest, "admin", "src", "extensions"),
setting.file
)
.replace(FILE_EXT_REGEX, "")
)
return {
importStatement: `import Setting${index}, { config as settingConfig${index} } from "./${relativePath}"`,
extension: `{ Component: Setting${index}, config: { ...settingConfig${index}, path: "${setting.path}", type: "setting" } }`,
}
})
const extensionsArray = [...widgetsArray, ...routesArray, ...settingsArray]
const extensionsEntry = dedent`
${extensionsArray.map((extension) => extension.importStatement).join("\n")}
const LocalEntry = {
identifier: "local",
extensions: [
${extensionsArray.map((extension) => extension.extension).join(",\n")}
],
}
export default LocalEntry
`
try {
await fse.outputFile(
path.resolve(dest, "admin", "src", "extensions", "_local-entry.ts"),
extensionsEntry
)
} catch (err) {
logger.panic(
`Failed to write the entry file for the local extensions. See the error below for details:`,
{
error: err,
}
)
}
return true
}
function findPluginsWithExtensions(plugins: string[]) {
const pluginsWithExtensions: string[] = []
for (const plugin of plugins) {
try {
const pluginDir = path.dirname(
require.resolve(`${plugin}/package.json`, {
paths: [process.cwd()],
})
)
const entrypoint = path.resolve(
pluginDir,
"dist",
"admin",
"_virtual_entry.js"
)
if (fse.existsSync(entrypoint)) {
pluginsWithExtensions.push(entrypoint)
}
} catch (_err) {
logger.warn(
`There was an error while attempting to load extensions from the plugin: ${plugin}. Are you sure it is installed?`
)
// no plugin found - noop
}
}
return pluginsWithExtensions
}
async function writeTailwindContentFile(dest: string, plugins: string[]) {
const tailwindContent = dedent`
const path = require("path")
const devPath = path.join(__dirname, "..", "..", "src/admin/**/*.{js,jsx,ts,tsx}")
module.exports = {
content: [
devPath,
${plugins
.map((plugin) => {
const tailwindContentPath = normalizePath(
path.relative(
path.resolve(dest, "admin"),
path.dirname(path.join(plugin, "..", ".."))
)
)
return `"${tailwindContentPath}/dist/admin/**/*.{js,jsx,ts,tsx}"`
})
.join(",\n")}
],
}
`
try {
await fse.outputFile(
path.resolve(dest, "admin", "tailwind.content.js"),
tailwindContent
)
} catch (err) {
logger.warn(
`Failed to write the Tailwind content file to ${dest}. The admin UI will remain functional, but CSS classes applied to extensions from plugins might not have the correct styles`
)
}
}
async function createMainExtensionsEntry(
dest: string,
plugins: string[],
hasLocalExtensions: boolean
) {
if (!plugins.length && !hasLocalExtensions) {
// We still want to generate the entry file, even if there are no extensions
// to load, so that the admin UI can be built without errors
const emptyEntry = dedent`
const extensions = []
export default extensions
`
try {
await fse.outputFile(
path.resolve(dest, "admin", "src", "extensions", "_main-entry.ts"),
emptyEntry
)
} catch (err) {
logger.panic(
`Failed to write the entry file for the main extensions. See the error below for details:`,
{
error: err,
}
)
}
return
}
const pluginsArray = plugins.map((plugin) => {
const relativePath = normalizePath(
path
.relative(path.resolve(dest, "admin", "src", "extensions"), plugin)
.replace(FILE_EXT_REGEX, "")
)
return relativePath
})
const extensionsArray = [
...pluginsArray.map((plugin, index) => {
const importStatement = `import Plugin${index} from "${plugin}"`
return {
importStatement,
extension: `Plugin${index}`,
}
}),
...(hasLocalExtensions
? [
{
importStatement: `import LocalEntry from "./_local-entry"`,
extension: `LocalEntry`,
},
]
: []),
]
const extensionsEntry = dedent`
${extensionsArray
.map((extension) => extension.importStatement)
.join("\n")}
const extensions = [
${extensionsArray.map((extension) => extension.extension).join(",\n")}
]
export default extensions
`
try {
await fse.outputFile(
path.resolve(dest, "admin", "src", "extensions", "_main-entry.ts"),
extensionsEntry
)
} catch (err) {
logger.panic(
`Failed to write the extensions entry file. See the error below for details:`,
{
error: err,
}
)
}
}
type CreateEntryArgs = {
appDir: string
dest: string
plugins?: string[]
}
export async function createEntry({ appDir, dest, plugins }: CreateEntryArgs) {
const hasLocalExtensions = await createLocalExtensionsEntry(appDir, dest)
const adminPlugins = findPluginsWithExtensions(plugins)
await createMainExtensionsEntry(dest, adminPlugins, hasLocalExtensions)
await writeTailwindContentFile(dest, adminPlugins)
}
@@ -1,61 +0,0 @@
import dotenv from "dotenv"
import fse from "fs-extra"
import path from "node:path"
const MEDUSA_ADMIN = /^MEDUSA_ADMIN_/i
let ENV_FILE_NAME = ""
switch (process.env.NODE_ENV) {
case "production":
ENV_FILE_NAME = ".env.production"
break
case "staging":
ENV_FILE_NAME = ".env.staging"
break
case "test":
ENV_FILE_NAME = ".env.test"
break
case "development":
default:
ENV_FILE_NAME = ".env"
break
}
if (fse.existsSync(ENV_FILE_NAME)) {
dotenv.config({ path: path.resolve(process.cwd(), ENV_FILE_NAME) })
} else if (ENV_FILE_NAME !== ".env") {
// Fall back to .env if the specified file does not exist
dotenv.config({ path: path.resolve(process.cwd(), ".env") })
}
type GetClientEnvArgs = {
path?: string
env?: string
backend?: string
}
export const getClientEnv = (args: GetClientEnvArgs) => {
const raw = Object.keys(process.env)
.filter((key) => MEDUSA_ADMIN.test(key))
.reduce(
(acc, current) => {
acc[current] = process.env[current]
return acc
},
{
ADMIN_PATH: args.path || "/",
NODE_ENV: args.env || "development",
MEDUSA_BACKEND_URL: args.backend || process.env.MEDUSA_BACKEND_URL,
}
)
const stringified = {
"process.env": Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key])
return env
}, {}),
}
return stringified
}
-27
View File
@@ -1,27 +0,0 @@
import { createCacheDir } from "./create-cache-dir"
import { getClientEnv } from "./get-client-env"
import { logger } from "./logger"
import { normalizePath } from "./normalize-path"
import {
findAllValidRoutes,
findAllValidSettings,
findAllValidWidgets,
validateRoute,
validateSetting,
validateWidget,
} from "./validate-extensions"
import { watchLocalAdminFolder } from "./watch-local-admin-folder"
export {
logger,
normalizePath,
getClientEnv,
createCacheDir,
validateWidget,
validateRoute,
validateSetting,
findAllValidWidgets,
findAllValidRoutes,
findAllValidSettings,
watchLocalAdminFolder,
}
@@ -1,74 +0,0 @@
import colors from "picocolors"
import readline from "readline"
const prefix = "[@medusajs/admin]"
type LogType = "error" | "warn" | "info"
interface LogOptions {
clearScreen?: boolean
}
interface LogErrorOptions extends LogOptions {
error?: Error | null
}
interface Logger {
info(msg: string, options?: LogOptions): void
warn(msg: string, options?: LogOptions): void
error(msg: string, options?: LogErrorOptions): void
panic(msg: string, options?: LogErrorOptions): void
}
function clearScreen() {
const repeatCount = process.stdout.rows - 2
const blank = repeatCount > 0 ? "\n".repeat(repeatCount) : ""
console.log(blank)
readline.cursorTo(process.stdout, 0, 0)
readline.clearScreenDown(process.stdout)
}
const canClearScreen = process.stdout.isTTY && !process.env.CI
const clear = canClearScreen
? clearScreen
: () => {
// noop
}
function createLogger(): Logger {
const output = (type: LogType, msg: string, options?: LogErrorOptions) => {
const method = type === "info" ? "log" : type
const format = () => {
const tag =
type === "info"
? colors.cyan(colors.bold(prefix))
: type === "warn"
? colors.yellow(colors.bold(prefix))
: colors.red(colors.bold(prefix))
return `${colors.dim(new Date().toLocaleTimeString())} ${tag} ${msg}`
}
if (options?.clearScreen) {
clear()
}
console[method](format())
if (options?.error) {
console.error(options.error)
}
}
return {
info: (msg, options) => output("info", msg, options),
warn: (msg, options) => output("warn", msg, options),
error: (msg, options) => output("error", msg, options),
panic: (msg, options) => {
output("error", msg, options)
output("error", "Exiting process", {})
process.exit(1)
},
}
}
export const logger = createLogger()
@@ -1,6 +0,0 @@
export function normalizePath(path: string): string {
const isWindows = process.platform === "win32"
const separator = isWindows ? "\\" : "/"
const regex = new RegExp(`\\${separator}`, "g")
return path.replace(regex, "/")
}
@@ -1,28 +0,0 @@
import { CustomWebpackConfigArgs } from "../types"
import { logger } from "./logger"
function validateArgs(args: CustomWebpackConfigArgs) {
const { options } = args
if (options.path) {
if (!options.path.startsWith("/")) {
logger.panic(
"'path' in the options of `@medusajs/admin` must start with a '/'"
)
}
if (options.path !== "/" && options.path.endsWith("/")) {
logger.panic(
"'path' in the options of `@medusajs/admin` cannot end with a '/'"
)
}
if (typeof options.path !== "string") {
logger.panic(
"'path' in the options of `@medusajs/admin` must be a string"
)
}
}
}
export { validateArgs }
@@ -1,685 +0,0 @@
import { parse, ParseResult, ParserOptions } from "@babel/parser"
import traverse, { NodePath } from "@babel/traverse"
import type {
ExportDefaultDeclaration,
ExportNamedDeclaration,
ObjectExpression,
ObjectMethod,
ObjectProperty,
SpreadElement,
} from "@babel/types"
import fse from "fs-extra"
import path from "path"
import { forbiddenRoutes, InjectionZone, injectionZones } from "../../client"
import { logger } from "./logger"
import { normalizePath } from "./normalize-path"
function isValidInjectionZone(zone: any): zone is InjectionZone {
return injectionZones.includes(zone)
}
/**
* Validates that the widget config export is valid.
* In order to be valid it must have a `zone` property that is either a `InjectionZone` or a `InjectionZone` array.
*/
function validateWidgetConfigExport(
properties: (ObjectMethod | ObjectProperty | SpreadElement)[]
): boolean {
const zoneProperty = properties.find(
(p) =>
p.type === "ObjectProperty" &&
p.key.type === "Identifier" &&
p.key.name === "zone"
) as ObjectProperty | undefined
if (!zoneProperty) {
return false
}
let zoneIsValid = false
if (zoneProperty.value.type === "StringLiteral") {
zoneIsValid = isValidInjectionZone(zoneProperty.value.value)
} else if (zoneProperty.value.type === "ArrayExpression") {
zoneIsValid = zoneProperty.value.elements.every(
(zone) =>
zone.type === "StringLiteral" && isValidInjectionZone(zone.value)
)
}
return zoneIsValid
}
function validateRouteConfigExport(
properties: (ObjectMethod | ObjectProperty | SpreadElement)[]
): boolean {
const linkProperty = properties.find(
(p) =>
p.type === "ObjectProperty" &&
p.key.type === "Identifier" &&
p.key.name === "link"
) as ObjectProperty | undefined
// Link property is optional for routes
if (!linkProperty) {
return true
}
const linkValue = linkProperty.value as ObjectExpression
let labelIsValid = false
// Check that the linkProperty is an object and has a `label` property that is a string
if (
linkValue.properties.some(
(p) =>
p.type === "ObjectProperty" &&
p.key.type === "Identifier" &&
p.key.name === "label" &&
p.value.type === "StringLiteral"
)
) {
labelIsValid = true
}
return labelIsValid
}
function validateSettingConfigExport(
properties: (ObjectMethod | ObjectProperty | SpreadElement)[]
): boolean {
const cardProperty = properties.find(
(p) =>
p.type === "ObjectProperty" &&
p.key.type === "Identifier" &&
p.key.name === "card"
) as ObjectProperty | undefined
// Link property is required for settings
if (!cardProperty) {
return false
}
const cardValue = cardProperty.value as ObjectExpression
let hasLabel = false
let hasDescription = false
if (
cardValue.properties.some(
(p) =>
p.type === "ObjectProperty" &&
p.key.type === "Identifier" &&
p.key.name === "label" &&
p.value.type === "StringLiteral"
)
) {
hasLabel = true
}
if (
cardValue.properties.some(
(p) =>
p.type === "ObjectProperty" &&
p.key.type === "Identifier" &&
p.key.name === "description" &&
p.value.type === "StringLiteral"
)
) {
hasDescription = true
}
return hasLabel && hasDescription
}
function validateConfigExport(
path: NodePath<ExportNamedDeclaration>,
type: "widget" | "route" | "setting"
) {
let hasValidConfigExport = false
const declaration = path.node.declaration
if (declaration && declaration.type === "VariableDeclaration") {
const configDeclaration = declaration.declarations.find(
(d) =>
d.type === "VariableDeclarator" &&
d.id.type === "Identifier" &&
d.id.name === "config"
)
if (
configDeclaration &&
configDeclaration.init.type === "ObjectExpression"
) {
const properties = configDeclaration.init.properties
if (type === "widget") {
hasValidConfigExport = validateWidgetConfigExport(properties)
}
if (type === "route") {
hasValidConfigExport = validateRouteConfigExport(properties)
}
if (type === "setting") {
hasValidConfigExport = validateSettingConfigExport(properties)
}
} else {
hasValidConfigExport = false
}
}
return hasValidConfigExport
}
/**
* Validates that the default export of a file is a valid React component.
* This is determined by checking if the default export is a function declaration
* with a return statement that returns a JSX element or fragment.
*/
function validateDefaultExport(
path: NodePath<ExportDefaultDeclaration>,
ast: ParseResult<any>
) {
let hasComponentExport = false
const declaration = path.node.declaration
if (
declaration &&
(declaration.type === "Identifier" ||
declaration.type === "FunctionDeclaration")
) {
const exportName =
declaration.type === "Identifier"
? declaration.name
: declaration.id && declaration.id.name
if (exportName) {
try {
traverse(ast, {
VariableDeclarator({ node, scope }) {
let isDefaultExport = false
if (node.id.type === "Identifier" && node.id.name === exportName) {
isDefaultExport = true
}
if (!isDefaultExport) {
return
}
traverse(
node,
{
ReturnStatement(path) {
if (
path.node.argument?.type === "JSXElement" ||
path.node.argument?.type === "JSXFragment"
) {
hasComponentExport = true
}
},
},
scope
)
},
})
} catch (e) {
logger.error(
`There was an error while validating the default export of ${path}. The following error must be resolved before continuing:`,
{
error: e,
}
)
return false
}
}
}
return hasComponentExport
}
/**
* Validates that a widget file has a valid default export and a valid config export.
*
*/
async function validateWidget(file: string) {
const content = await fse.readFile(file, "utf-8")
const parserOptions: ParserOptions = {
sourceType: "module",
plugins: ["jsx"],
}
if (file.endsWith(".ts") || file.endsWith(".tsx")) {
parserOptions.plugins.push("typescript")
}
let ast: ParseResult<any>
try {
ast = parse(content, parserOptions)
} catch (e) {
logger.error(
`An error occurred while parsing the Widget "${file}", and the Widget cannot be injected. The following error must be resolved before continuing:`,
{
error: e,
}
)
return false
}
let hasConfigExport = false
let hasComponentExport = false
try {
traverse(ast, {
ExportDefaultDeclaration: (path) => {
hasComponentExport = validateDefaultExport(path, ast)
},
ExportNamedDeclaration: (path) => {
hasConfigExport = validateConfigExport(path, "widget")
},
})
} catch (e) {
logger.error(
`An error occurred while validating the Widget "${file}". The following error must be resolved before continuing:`,
{
error: e,
}
)
return false
}
if (hasConfigExport && !hasComponentExport) {
if (!hasComponentExport) {
logger.error(
`The default export in the Widget "${file}" is invalid and the widget will not be injected. Please make sure that the default export is a valid React component.`
)
}
}
if (!hasConfigExport && hasComponentExport) {
logger.error(
`The Widget config export in "${file}" is invalid and the Widget cannot be injected. Please ensure that the config is valid.`
)
}
return hasConfigExport && hasComponentExport
}
/**
* This function takes a file path and converts it to a URL path.
* It converts the file path to a URL path by replacing any
* square brackets with colons, and then removing the "page.[jt]s" suffix.
*/
function createPath(filePath: string): string {
const normalizedPath = normalizePath(filePath)
const regex = /\[(.*?)\]/g
const strippedPath = normalizedPath.replace(regex, ":$1")
const url = strippedPath.replace(/\/page\.[jt]sx?$/i, "")
return url
}
function isForbiddenRoute(path: any): boolean {
return forbiddenRoutes.includes(path)
}
function validatePath(
path: string,
origin: string
): {
valid: boolean
error: string
} {
if (isForbiddenRoute(path)) {
return {
error: `A route from ${origin} is using a forbidden path: ${path}.`,
valid: false,
}
}
const specialChars = ["/", ":", "-"]
for (let i = 0; i < path.length; i++) {
const currentChar = path[i]
if (
!specialChars.includes(currentChar) &&
!/^[a-z0-9]$/i.test(currentChar)
) {
return {
error: `A route from ${origin} is using an invalid path: ${path}. Only alphanumeric characters, "/", ":", and "-" are allowed.`,
valid: false,
}
}
if (currentChar === ":" && (i === 0 || path[i - 1] !== "/")) {
return {
error: `A route from ${origin} is using an invalid path: ${path}. All dynamic segments must be preceded by a "/".`,
valid: false,
}
}
}
return {
valid: true,
error: "",
}
}
/**
* Validates that a file is a valid route.
* This is determined by checking if the file exports a valid React component
* as the default export, and a optional route config as a named export.
* If the file is not a valid route, `null` is returned.
* If the file is a valid route, a `ValidRouteResult` is returned.
*/
async function validateRoute(
file: string,
basePath: string
): Promise<{
path: string
hasConfig: boolean
file: string
} | null> {
const cleanPath = createPath(file.replace(basePath, ""))
const { valid, error } = validatePath(cleanPath, file)
if (!valid) {
logger.error(
`The path ${cleanPath} for the UI Route "${file}" is invalid and the route cannot be injected. The following error must be fixed before the route can be injected: ${error}`
)
return null
}
const content = await fse.readFile(file, "utf-8")
let hasComponentExport = false
let hasConfigExport = false
const parserOptions: ParserOptions = {
sourceType: "module",
plugins: ["jsx"],
}
if (file.endsWith(".ts") || file.endsWith(".tsx")) {
parserOptions.plugins.push("typescript")
}
let ast: ParseResult<any>
try {
ast = parse(content, parserOptions)
} catch (e) {
logger.error(
`An error occurred while parsing the UI Route "${file}", and the UI Route cannot be injected. The following error must be resolved before continuing:`,
{
error: e,
}
)
return null
}
try {
traverse(ast, {
ExportDefaultDeclaration: (path) => {
hasComponentExport = validateDefaultExport(path, ast)
},
ExportNamedDeclaration: (path) => {
hasConfigExport = validateConfigExport(path, "route")
},
})
} catch (e) {
logger.error(
`An error occurred while validating the UI Route "${file}", and the UI Route cannot be injected. The following error must be resolved before continuing:`,
{
error: e,
}
)
return null
}
if (!hasComponentExport) {
logger.error(
`The default export in the UI Route "${file}" is invalid and the route cannot be injected. Please make sure that the default export is a valid React component.`
)
return null
}
return {
path: cleanPath,
hasConfig: hasConfigExport,
file,
}
}
async function validateSetting(file: string, basePath: string) {
const cleanPath = createPath(file.replace(basePath, ""))
const { valid, error } = validatePath(cleanPath, file)
if (!valid) {
logger.error(
`The path ${cleanPath} for the Setting "${file}" is invalid and the setting cannot be injected. The following error must be fixed before the Setting can be injected: ${error}`
)
return null
}
const content = await fse.readFile(file, "utf-8")
let hasComponentExport = false
let hasConfigExport = false
const parserOptions: ParserOptions = {
sourceType: "module",
plugins: ["jsx"],
}
if (file.endsWith(".ts") || file.endsWith(".tsx")) {
parserOptions.plugins.push("typescript")
}
let ast: ParseResult<any>
try {
ast = parse(content, parserOptions)
} catch (e) {
logger.error(
`
An error occured while parsing the Setting "${file}". The following error must be resolved before continuing:
`,
{
error: e,
}
)
return null
}
try {
traverse(ast, {
ExportDefaultDeclaration: (path) => {
hasComponentExport = validateDefaultExport(path, ast)
},
ExportNamedDeclaration: (path) => {
hasConfigExport = validateConfigExport(path, "setting")
},
})
} catch (e) {
logger.error(
`
An error occured while validating the Setting "${file}". The following error must be resolved before continuing:`,
{
error: e,
}
)
return null
}
if (!hasComponentExport) {
logger.error(
`The default export in the Setting "${file}" is invalid and the page will not be injected. Please make sure that the default export is a valid React component.`
)
return null
}
if (!hasConfigExport) {
logger.error(
`The named export "config" in the Setting "${file}" is invalid or missing and the settings page will not be injected. Please make sure that the file exports a valid config.`
)
return null
}
return {
path: cleanPath,
file,
}
}
async function findAllValidSettings(dir: string) {
const settingsFiles: string[] = []
const dirExists = await fse.pathExists(dir)
if (!dirExists) {
return []
}
const paths = await fse.readdir(dir)
let hasSubDirs = false
// We only check the first level of directories for settings files
for (const pa of paths) {
const filePath = path.join(dir, pa)
const fileStat = await fse.stat(filePath)
if (fileStat.isDirectory()) {
const files = await fse.readdir(filePath)
for (const file of files) {
const filePath = path.join(dir, pa, file)
const fileStat = await fse.stat(filePath)
if (fileStat.isFile() && /^(.*\/)?page\.[jt]sx?$/i.test(file)) {
settingsFiles.push(filePath)
break
} else if (fileStat.isDirectory()) {
hasSubDirs = true
}
}
}
}
if (hasSubDirs) {
logger.warn(
`The directory ${dir} contains subdirectories. Settings do not support nested routes, only UI Routes support nested paths.`
)
}
const validSettingsFiles = await Promise.all(
settingsFiles.map(async (file) => validateSetting(file, dir))
)
return validSettingsFiles.filter((file) => file !== null)
}
/**
* Scans a directory for valid widgets.
* A valid widget is a file that exports a valid widget config and a valid React component.
*/
async function findAllValidWidgets(dir: string) {
const jsxAndTsxFiles: string[] = []
const dirExists = await fse.pathExists(dir)
if (!dirExists) {
return []
}
async function traverseDirectory(currentPath: string) {
const files = await fse.readdir(currentPath)
for (const file of files) {
const filePath = path.join(currentPath, file)
const fileStat = await fse.stat(filePath)
if (fileStat.isDirectory()) {
await traverseDirectory(filePath)
} else if (fileStat.isFile() && /\.(js|jsx|ts|tsx)$/i.test(file)) {
jsxAndTsxFiles.push(filePath)
}
}
}
await traverseDirectory(dir)
const promises = jsxAndTsxFiles.map((file) => {
const isValid = validateWidget(file)
return isValid ? file : null
})
const validFiles = await Promise.all(promises)
return validFiles.filter((file) => file !== null)
}
/**
* Scans a directory for valid routes.
* A valid route is a file that exports a optional route config and a valid React component.
*/
async function findAllValidRoutes(dir: string) {
const pageFiles: string[] = []
const dirExists = await fse.pathExists(dir)
if (!dirExists) {
return []
}
async function traverseDirectory(currentPath: string) {
const files = await fse.readdir(currentPath)
for (const file of files) {
const filePath = path.join(currentPath, file)
const fileStat = await fse.stat(filePath)
if (fileStat.isDirectory()) {
await traverseDirectory(filePath)
} else if (fileStat.isFile() && /^(.*\/)?page\.[jt]sx?$/i.test(file)) {
pageFiles.push(filePath)
}
}
}
await traverseDirectory(dir)
const promises = pageFiles.map(async (file) => {
return validateRoute(file, dir)
})
const validFiles = await Promise.all(promises)
return validFiles.filter((file) => file !== null)
}
export {
createPath,
validateWidget,
validateRoute,
validateSetting,
findAllValidSettings,
findAllValidWidgets,
findAllValidRoutes,
}
@@ -1,57 +0,0 @@
import chokidar from "chokidar"
import fse from "fs-extra"
import path from "node:path"
import { createEntry } from "./create-entry"
import { logger } from "./logger"
/**
* Watches the local admin directory for changes and updates the extensions cache directory accordingly.
*/
export async function watchLocalAdminFolder(
appDir: string,
cacheDir: string,
plugins: string[]
) {
const adminDir = path.resolve(appDir, "src", "admin")
const watcher = chokidar.watch(adminDir, {
ignored: /(^|[/\\])\../,
ignoreInitial: true,
})
watcher.on("all", async (event, file) => {
if (event === "unlinkDir" || event === "unlink") {
removeUnlinkedFile(file, appDir, cacheDir)
}
await createEntry({
appDir,
dest: cacheDir,
plugins,
})
logger.info("Extensions cache directory was re-initialized")
})
process
.on("SIGINT", async () => {
await watcher.close()
})
.on("SIGTERM", async () => {
await watcher.close()
})
}
function removeUnlinkedFile(file: string, appDir: string, cacheDir: string) {
const srcDir = path.resolve(appDir, "src", "admin")
const relativePath = path.relative(srcDir, file)
const destDir = path.resolve(cacheDir, "admin", "src", "extensions")
const fileToDelete = path.resolve(destDir, relativePath)
try {
fse.removeSync(fileToDelete)
} catch (error) {
logger.error(`An error occurred while removing ${fileToDelete}: ${error}`)
}
}
@@ -1,52 +0,0 @@
import fse from "fs-extra"
import path from "node:path"
import webpack from "webpack"
import { CustomWebpackConfigArgs } from "../types"
import { logger } from "../utils"
import { validateArgs } from "../utils/validate-args"
import { getWebpackConfig } from "./get-webpack-config"
import { withCustomWebpackConfig } from "./with-custom-webpack-config"
export async function getCustomWebpackConfig(
appDir: string,
args: CustomWebpackConfigArgs
) {
validateArgs(args)
let config = getWebpackConfig(args)
const adminConfigPath = path.join(appDir, "src", "admin", "webpack.config.js")
const pathExists = await fse.pathExists(adminConfigPath)
if (pathExists) {
let webpackAdminConfig: ReturnType<typeof withCustomWebpackConfig>
try {
webpackAdminConfig = require(adminConfigPath)
} catch (e) {
logger.panic(
`An error occured while trying to load your custom Webpack config. See the error below for details:`,
{
error: e,
}
)
}
if (typeof webpackAdminConfig === "function") {
if (args.devServer) {
config.devServer = args.devServer
}
config = webpackAdminConfig(config, webpack)
if (!config) {
logger.panic(
"Nothing was returned from your custom webpack configuration"
)
}
}
}
return config
}
@@ -1,191 +0,0 @@
import ReactRefreshPlugin from "@pmmmwh/react-refresh-webpack-plugin"
import CopyPlugin from "copy-webpack-plugin"
import HtmlWebpackPlugin from "html-webpack-plugin"
import MiniCssExtractPlugin from "mini-css-extract-plugin"
import path from "node:path"
import { SwcMinifyWebpackPlugin } from "swc-minify-webpack-plugin"
import type { Configuration } from "webpack"
import webpack from "webpack"
import WebpackBar from "webpackbar"
import { WebpackConfigArgs } from "../types"
import { getClientEnv } from "../utils"
import { webpackAliases } from "./webpack-aliases"
function formatPublicPath(path?: string) {
if (!path) {
return "/app/"
}
if (path === "/") {
return path
}
return path.endsWith("/") ? path : `${path}/`
}
export function getWebpackConfig({
entry,
dest,
cacheDir,
env,
options,
template,
publicFolder,
reporting = "fancy",
}: WebpackConfigArgs): Configuration {
const isProd = env === "production"
const envVars = getClientEnv({
env,
backend: options?.backend,
path: options?.path,
})
const publicPath = formatPublicPath(options?.path)
const webpackPlugins = isProd
? [
new MiniCssExtractPlugin({
filename: "[name].[chunkhash].css",
chunkFilename: "[name].[chunkhash].css",
}),
new WebpackBar({
basic: reporting === "minimal",
fancy: reporting === "fancy",
}),
]
: [new MiniCssExtractPlugin(), new ReactRefreshPlugin()]
return {
mode: env,
devtool: isProd ? false : "inline-source-map",
entry: [entry],
output: {
path: dest,
filename: isProd ? "[name].[contenthash:8].js" : "[name].bundle.js",
chunkFilename: isProd
? "[name].[contenthash:8].chunk.js"
: "[name].chunk.js",
},
optimization: {
minimize: true,
minimizer: [new SwcMinifyWebpackPlugin()],
moduleIds: "deterministic",
runtimeChunk: true,
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
include: [cacheDir],
use: {
loader: "swc-loader",
options: {
jsc: {
parser: {
syntax: "typescript", // Use TypeScript syntax for parsing
jsx: true, // Enable JSX parsing
},
transform: {
react: {
runtime: "automatic",
development: !isProd,
refresh: !isProd,
},
},
},
},
},
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
include: [cacheDir],
use: {
loader: "swc-loader",
options: {
jsc: {
parser: {
syntax: "ecmascript", // Use Ecmascript syntax for parsing
jsx: true, // Enable JSX parsing
},
transform: {
react: {
runtime: "automatic",
},
},
},
},
},
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
},
{
test: /\.svg$/,
oneOf: [
{
type: "asset/resource",
resourceQuery: /url/,
},
{
type: "asset/inline",
resourceQuery: /base64/,
},
{
issuer: /\.[jt]sx?$/,
use: ["@svgr/webpack"],
},
],
generator: {
filename: `images/${isProd ? "[name]-[hash][ext]" : "[name][ext]"}`,
},
},
{
test: /\.(eot|otf|ttf|woff|woff2)$/,
type: "asset/resource",
},
{
test: /\.m?jsx?$/,
resolve: {
fullySpecified: false,
},
},
],
},
resolve: {
alias: webpackAliases,
symlinks: false,
extensions: [".js", ".jsx", ".ts", ".tsx"],
mainFields: ["browser", "module", "main"],
modules: ["node_modules", path.resolve(__dirname, "..", "node_modules")],
fallback: {
readline: false,
path: false,
},
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: template || path.resolve(__dirname, "..", "ui", "index.html"),
publicPath: publicPath,
}),
new webpack.DefinePlugin(envVars),
new CopyPlugin({
patterns: [
{
from: publicFolder || path.resolve(__dirname, "..", "ui", "public"),
to: path.resolve(dest, "public"),
},
],
}),
...webpackPlugins,
].filter(Boolean),
stats: isProd ? "errors-only" : "errors-warnings",
}
}
@@ -1,5 +0,0 @@
import { getCustomWebpackConfig } from "./get-custom-webpack-config"
import { getWebpackConfig } from "./get-webpack-config"
import { withCustomWebpackConfig } from "./with-custom-webpack-config"
export { getCustomWebpackConfig, getWebpackConfig, withCustomWebpackConfig }
@@ -1,9 +0,0 @@
import { ALIASED_PACKAGES } from "../constants"
/**
* Ensure that the admin-ui uses the same version of these packages as the project.
*/
export const webpackAliases = ALIASED_PACKAGES.reduce((acc, pkg) => {
acc[`${pkg}$`] = require.resolve(pkg)
return acc
}, {})
@@ -1,16 +0,0 @@
import webpack, { type Configuration } from "webpack"
/**
* Helper function to create a custom webpack config that can be used to
* extend the default webpack config used to build the admin UI.
*/
export function withCustomWebpackConfig(
callback: (
config: Configuration,
webpackInstance: typeof webpack
) => Configuration
) {
return (config: Configuration, webpackInstance: typeof webpack) => {
return callback(config, webpackInstance)
}
}
-24
View File
@@ -1,24 +0,0 @@
{
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"lib": ["es2019"],
"module": "commonjs",
"moduleResolution": "node",
"noEmit": false,
"resolveJsonModule": true,
"esModuleInterop": true,
"outDir": "dist",
"rootDir": ".",
"skipLibCheck": true
},
"include": [
"src",
"tsup.config.ts",
"webpack.config.dev.ts",
"src/node/webpack/get-webpack-config.ts"
],
"exclude": ["node_modules", "ui", "./src/**/__tests__"]
}
-5
View File
@@ -1,5 +0,0 @@
{
"extends": "./tsconfig.json",
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
-11
View File
@@ -1,11 +0,0 @@
import { defineConfig } from "tsup"
export default defineConfig({
entry: ["src/index.ts"],
dts: true,
skipNodeModulesBundle: true,
sourcemap: true,
minify: true,
clean: true,
format: ["cjs", "esm"],
})
@@ -1,106 +0,0 @@
module.exports = {
contextSeparator: "_",
// Key separator used in your translation keys
createOldCatalogs: false,
// Save the \_old files
defaultNamespace: "translation",
// Default namespace used in your i18next config
defaultValue: "",
// Default value to give to keys with no value
// You may also specify a function accepting the locale, namespace, key, and value as arguments
indentation: 2,
// Indentation of the catalog files
keepRemoved: false,
// Keep keys from the catalog that are no longer in code
keySeparator: false,
// Key separator used in your translation keys
// If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
// see below for more details
lexers: {
hbs: ["HandlebarsLexer"],
handlebars: ["HandlebarsLexer"],
htm: ["HTMLLexer"],
html: ["HTMLLexer"],
mjs: ["JavascriptLexer"],
js: ["JavascriptLexer"], // if you're writing jsx inside .js files, change this to JsxLexer
ts: ["JavascriptLexer"],
jsx: ["JsxLexer"],
tsx: ["JsxLexer"],
default: ["JavascriptLexer"],
},
lineEnding: "auto",
// Control the line ending. See options at https://github.com/ryanve/eol
locales: ["en"],
// An array of the locales in your applications
namespaceSeparator: false,
// Namespace separator used in your translation keys
// If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
output: "ui/public/locales/$LOCALE/$NAMESPACE.json",
// Supports $LOCALE and $NAMESPACE injection
// Supports JSON (.json) and YAML (.yml) file formats
// Where to write the locale files relative to process.cwd()
pluralSeparator: "_",
// Plural separator used in your translation keys
// If you want to use plain english keys, separators such as `_` might conflict. You might want to set `pluralSeparator` to a different string that does not occur in your keys.
input: undefined,
// An array of globs that describe where to look for source files
// relative to the location of the configuration file
sort: false,
// Whether or not to sort the catalog. Can also be a [compareFunction](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#parameters)
verbose: true,
// Display info about the parsing including some stats
failOnWarnings: true,
// Exit with an exit code of 1 on warnings
failOnUpdate: false,
// Exit with an exit code of 1 when translations are updated (for CI purpose)
customValueTemplate: null,
// If you wish to customize the value output the value as an object, you can set your own format.
// ${defaultValue} is the default value you set in your translation function.
// Any other custom property will be automatically extracted.
//
// Example:
// {
// message: "${defaultValue}",
// description: "${maxLength}", // t('my-key', {maxLength: 150})
// }
resetDefaultValueLocale: null,
// The locale to compare with default values to determine whether a default value has been changed.
// If this is set and a default value differs from a translation in the specified locale, all entries
// for that key across locales are reset to the default value, and existing translations are moved to
// the `_old` file.
i18nextOptions: null,
// If you wish to customize options in internally used i18next instance, you can define an object with any
// configuration property supported by i18next (https://www.i18next.com/overview/configuration-options).
// { compatibilityJSON: 'v3' } can be used to generate v3 compatible plurals.
yamlOptions: null,
// If you wish to customize options for yaml output, you can define an object here.
// Configuration options are here (https://github.com/nodeca/js-yaml#dump-object---options-).
// Example:
// {
// lineWidth: -1,
// }
}
-12
View File
@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Medusa</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
-11
View File
@@ -1,11 +0,0 @@
const path = require("path")
module.exports = {
plugins: [
require("tailwindcss/nesting"),
require("tailwindcss")({
config: path.join(__dirname, "tailwind.config.js"),
}),
require("autoprefixer"),
],
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-10
View File
@@ -1,10 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M26.4471 5.86972L19.6283 1.96144C17.3973 0.679521 14.6635 0.679521 12.4325 1.96144L5.58223 5.86972C3.38261 7.15164 2 9.52788 2 12.0604V19.9083C2 22.4721 3.38261 24.8171 5.58223 26.099L12.401 30.0386C14.6321 31.3205 17.3659 31.3205 19.5969 30.0386L26.4157 26.099C28.6468 24.8171 29.9979 22.4721 29.9979 19.9083V12.0604C30.0608 9.52788 28.6782 7.15164 26.4471 5.86972ZM16.0147 22.9724C12.1496 22.9724 9.00734 19.8458 9.00734 16C9.00734 12.1542 12.1496 9.02762 16.0147 9.02762C19.8797 9.02762 23.0534 12.1542 23.0534 16C23.0534 19.8458 19.9111 22.9724 16.0147 22.9724Z" fill="url(#paint0_linear_362_938)"/>
<defs>
<linearGradient id="paint0_linear_362_938" x1="2" y1="31" x2="35.7561" y2="21.5406" gradientUnits="userSpaceOnUse">
<stop stop-color="#000"/>
<stop offset="1" stop-color="#000"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 984 B

-61
View File
@@ -1,61 +0,0 @@
import { lazy, Suspense } from "react"
import {
createBrowserRouter,
createRoutesFromElements,
Route,
RouterProvider,
} from "react-router-dom"
import Spinner from "./components/atoms/spinner"
import { AnalyticsProvider } from "./providers/analytics-provider"
import { WRITE_KEY } from "./constants/analytics"
const NotFound = lazy(() => import("./pages/404"))
const Dashboard = lazy(() => import("./pages/a"))
const IndexPage = lazy(() => import("./pages/index"))
const InvitePage = lazy(() => import("./pages/invite"))
const LoginPage = lazy(() => import("./pages/login"))
const ResetPasswordPage = lazy(() => import("./pages/reset-password"))
const router = createBrowserRouter(
createRoutesFromElements(
<>
<Route path="/" element={<IndexPage />} />
<Route
path="a/*"
element={
<AnalyticsProvider writeKey={WRITE_KEY}>
<Dashboard />
</AnalyticsProvider>
}
/>
<Route
path="invite"
element={
<AnalyticsProvider writeKey={WRITE_KEY}>
<InvitePage />
</AnalyticsProvider>
}
/>
<Route path="login" element={<LoginPage />} />
<Route path="reset-password" element={<ResetPasswordPage />} />
<Route path="*" element={<NotFound />} />
</>
),
{
basename: process.env.ADMIN_PATH,
}
)
const Loading = () => (
<div className="bg-grey-5 text-grey-90 flex h-screen w-full items-center justify-center">
<Spinner variant="secondary" />
</div>
)
const App = () => (
<Suspense fallback={<Loading />}>
<RouterProvider router={router} />
</Suspense>
)
export default App
@@ -1,109 +0,0 @@
.emoji-picker-react {
padding: 16px !important;
border: none !important;
}
.emoji-picker-react .emoji-group {
padding: 0 !important;
font-size: 12px !important;
font-weight: 400 !important;
}
.emoji-picker-react .emoji-group:before {
font-family: "Inter" !important;
text-transform: none !important;
font-size: 12px !important;
font-weight: 600 !important;
}
.emoji-picker-react .native {
font-size: 24px !important;
}
.emoji-picker-react .emoji {
color: #f3f4f6 !important;
}
.emoji-picker-react input.emoji-search {
background-color: #f9fafb !important;
border-radius: 4px !important;
border-color: #e5e7eb !important;
margin: 0 !important;
width: 100% !important;
font-size: 12px !important;
font-family: "Inter" !important;
color: #111827 !important;
caret-color: #7c3aed !important;
}
.emoji-picker-react input.emoji-search::placeholder {
font-size: 12px !important;
font-family: "Inter" !important;
color: #9ca3af !important;
}
.emoji-picker-react .emoji-categories button.icn-smileys_people {
background-image: url("../svg/happy.svg") !important;
}
.emoji-picker-react .emoji-categories button.icn-animals_nature {
background-image: url("../svg/sprout.svg") !important;
}
.emoji-picker-react .emoji-categories button.icn-food_drink {
background-image: url("../svg/carrot.svg") !important;
}
.emoji-picker-react .emoji-categories button.icn-travel_places {
background-image: url("../svg/plane.svg") !important;
}
.emoji-picker-react .emoji-categories button.icn-activities {
background-image: url("../svg/controller.svg") !important;
}
.emoji-picker-react .emoji-categories button.icn-objects {
background-image: url("../svg/lightbulb.svg") !important;
}
.emoji-picker-react .emoji-categories button.icn-symbols {
background-image: url("../svg/heart.svg") !important;
}
.emoji-picker-react .emoji-categories button.icn-flags {
background-image: url("../svg/flag.svg") !important;
}
.emoji-picker-react .emoji-categories button {
width: 32px !important;
height: 32px !important;
border-radius: 4px !important;
}
.emoji-picker-react .emoji-categories button.active {
background-color: white !important;
}
.emoji-picker-react .emoji-categories {
background-color: #f3f4f6 !important;
padding: 4px !important;
border-radius: 4px !important;
margin-bottom: 8px !important;
}
.emoji-picker-react
.active-category-indicator-wrapper
.active-category-indicator {
display: none !important;
}
.emoji-scroll-wrapper {
overflow-x: hidden !important;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.emoji-scroll-wrapper::-webkit-scrollbar {
/* chrome */
display: none;
}
@@ -1,465 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: "Inter";
src: url("../../fonts/Inter-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter";
src: url("../../fonts/Inter-Medium.ttf") format("truetype");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter";
src: url("../../fonts/Inter-SemiBold.ttf") format("truetype");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Roboto Mono";
src: url("../../fonts/RobotoMono-Bold.ttf") format("truetype");
font-weight: bold;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Roboto Mono";
src: url("../../fonts/RobotoMono-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@layer base {
html {
@apply overflow-hidden;
}
}
@layer components {
.inter-5xlarge-regular {
@apply text-5xlarge leading-4xlarge font-sans font-normal;
}
.inter-5xlarge-semibold {
@apply text-5xlarge leading-4xlarge font-sans font-semibold;
}
.inter-4xlarge-regular {
@apply text-4xlarge leading-3xlarge font-sans font-normal;
}
.inter-4xlarge-semibold {
@apply text-4xlarge leading-3xlarge font-sans font-semibold;
}
.inter-3xlarge-regular {
@apply text-3xlarge leading-2xlarge font-sans font-normal;
}
.inter-3xlarge-semibold {
@apply text-3xlarge leading-2xlarge font-sans font-semibold;
}
.inter-2xlarge-regular {
@apply text-2xlarge leading-xlarge font-sans font-normal;
}
.inter-2xlarge-semibold {
@apply text-2xlarge leading-xlarge font-sans font-semibold;
}
.inter-xlarge-regular {
@apply text-xlarge leading-large font-sans font-normal;
}
.inter-xlarge-semibold {
@apply text-xlarge leading-large font-sans font-semibold;
}
.inter-large-regular {
@apply text-large leading-base font-sans font-normal;
}
.inter-large-semibold {
@apply text-large leading-base font-sans font-semibold;
}
.inter-base-regular {
@apply leading-base font-sans text-base font-normal;
}
.inter-base-semibold {
@apply leading-base font-sans text-base font-semibold;
}
.inter-small-regular {
@apply text-small leading-small font-sans font-normal;
}
.inter-small-semibold {
@apply text-small leading-small font-sans font-semibold;
}
.inter-xsmall-regular {
@apply text-xsmall leading-xsmall font-sans font-normal;
}
.inter-xsmall-semibold {
@apply text-xsmall leading-xsmall font-sans font-semibold;
}
.mono-5xlarge-regular {
@apply text-5xlarge leading-4xlarge font-mono font-normal;
}
.mono-5xlarge-semibold {
@apply text-5xlarge leading-4xlarge font-mono font-bold;
}
.mono-4xlarge-regular {
@apply text-4xlarge leading-3xlarge font-mono font-normal;
}
.mono-4xlarge-semibold {
@apply text-4xlarge leading-3xlarge font-mono font-bold;
}
.mono-3xlarge-regular {
@apply text-3xlarge leading-2xlarge font-mono font-normal;
}
.mono-3xlarge-semibold {
@apply text-3xlarge leading-2xlarge font-mono font-bold;
}
.mono-2xlarge-regular {
@apply text-2xlarge leading-xlarge font-mono font-normal;
}
.mono-2xlarge-semibold {
@apply text-2xlarge leading-xlarge font-mono font-bold;
}
.mono-xlarge-regular {
@apply text-xlarge leading-large font-mono font-normal;
}
.mono-xlarge-semibold {
@apply text-xlarge leading-large font-mono font-bold;
}
.mono-large-regular {
@apply text-large leading-base font-mono font-normal;
}
.mono-large-semibold {
@apply text-large leading-base font-mono font-bold;
}
.mono-base-regular {
@apply leading-base font-mono text-base font-normal;
}
.mono-base-semibold {
@apply leading-base font-mono text-base font-bold;
}
.mono-small-regular {
@apply text-small leading-small font-mono font-normal;
}
.mono-small-semibold {
@apply text-small leading-small font-mono font-bold;
}
.mono-xsmall-regular {
@apply text-xsmall leading-xsmall font-mono font-normal;
}
.mono-xsmall-semibold {
@apply text-xsmall leading-xsmall font-mono font-bold;
}
.radio-outer-ring > span.indicator[data-state="checked"] {
@apply rounded-circle shadow-violet-60 shadow-[0_0_0_2px];
}
.bold-active-item + span {
@apply inter-base-semibold;
}
}
@layer components {
.react-select-container {
@apply -mx-3 mb-1 h-6 cursor-text border-0 p-0;
.react-select__control {
@apply border-0 bg-inherit shadow-none;
}
.react-select__control,
.react-select__control--is-focused,
.react-select__control--menu-is-open {
@apply m-0 h-6 p-0 !important;
}
.react-select__value-container--is-multi,
.react-select__value-container--has-value {
@apply m-0 h-6 p-0 pl-3 !important;
}
.react-select__menu,
.react-select__menu-list {
@apply z-[110] mt-0 rounded-t-none !important;
}
.react-select__value-container {
@apply pl-3 pr-0;
}
.react-select__indicators {
@apply flex h-full items-center p-0 pr-3;
.react-select__indicator {
@apply p-0;
}
}
.react-select__input {
@apply mt-0 w-full min-w-[120px] pt-0 !important;
}
.react-select__option,
.react-select__option--is-focused,
.react-select__option--is-selected {
@apply bg-grey-0 hover:bg-grey-5 !important;
}
.react-select__multi-value,
.react-select__input-container {
@apply my-0 py-0;
}
}
}
@layer components {
.badge {
@apply rounded-rounded inter-small-semibold w-min py-0.5 px-2;
}
.badge-disabled {
@apply bg-grey-50 text-grey-50 bg-opacity-10;
}
.badge-primary {
@apply bg-violet-60 text-violet-60 bg-opacity-10;
}
.badge-danger {
@apply bg-rose-50 bg-opacity-10 text-rose-50;
}
.badge-success {
@apply bg-teal-50 bg-opacity-10 text-teal-50;
}
.badge-warning {
@apply bg-yellow-40 text-yellow-60 bg-opacity-20;
}
.badge-ghost {
@apply text-grey-90 border-grey-20 whitespace-nowrap border;
}
.badge-default {
@apply inter-small-regular bg-grey-10 text-grey-90 whitespace-nowrap;
}
.btn {
@apply rounded-rounded focus:shadow-cta flex items-center justify-center focus:outline-none;
}
.btn-large {
@apply inter-base-semibold px-large py-small;
}
.btn-medium {
@apply inter-base-semibold px-base py-xsmall;
}
.btn-small {
@apply inter-small-semibold px-small py-[6px];
}
.btn-primary {
@apply bg-violet-60 text-grey-0 active:bg-violet-70 disabled:bg-grey-20 disabled:text-grey-40 hover:bg-violet-50;
}
.btn-secondary {
@apply bg-grey-0 text-grey-90 border-grey-20 hover:bg-grey-5 active:bg-grey-5 active:text-violet-60 focus:border-violet-60 disabled:bg-grey-0 disabled:text-grey-30 border;
}
.btn-danger {
@apply bg-grey-0 border-grey-20 hover:bg-grey-10 active:bg-grey-20 disabled:bg-grey-0 disabled:text-grey-30 border text-rose-50;
}
.btn-nuclear {
@apply text-grey-0 hover:bg-rose-40 active:bg-rose-60 disabled:bg-grey-20 disabled:text-grey-40 bg-rose-50;
}
.btn-ghost {
@apply text-grey-90 hover:bg-grey-5 active:bg-grey-5 active:text-violet-60 focus:border-violet-60 disabled:text-grey-30 bg-transparent disabled:bg-transparent;
}
.btn-primary-large {
@apply btn btn-large btn-primary;
}
.btn-primary-medium {
@apply btn btn-medium;
}
.btn-primary-small {
@apply btn btn-small;
}
.btn-secondary-large {
@apply btn btn-large btn-seconday;
}
.btn-secondary-medium {
@apply btn btn-medium btn-seconday;
}
.btn-secondary-small {
@apply btn btn-small btn-seconday;
}
.btn-ghost-large {
@apply btn btn-large btn-ghost;
}
.btn-ghost-medium {
@apply btn btn-medium btn-ghost;
}
.btn-ghost-small {
@apply btn btn-small btn-ghost;
}
}
@layer components {
.date-picker {
@apply border-0 pt-6 outline-none !important;
.react-datepicker__month-container {
.react-datepicker__header {
@apply border-0 bg-inherit;
}
}
.react-datepicker__day-names {
@apply inter-base-semibold pt-4;
.react-datepicker__day-name {
@apply m-0 w-[40px];
}
}
.react-datepicker__month {
@apply m-0;
}
.react-datepicker__day {
@apply inter-base-regular;
}
.react-datepicker__day--today {
@apply text-grey-90 inter-base-semibold bg-grey-10 rounded !important;
}
.react-datepicker__day--outside-month,
.past {
@apply text-grey-40 !important;
}
.date {
@apply text-grey-90 relative m-[0px] h-[38px] w-[38px] pt-3 align-middle leading-none;
:hover {
@apply cursor-pointer;
}
}
.chosen,
.react-datepicker__day--keyboard-selected {
@apply bg-violet-60 text-grey-0 inter-base-semibold leading-none !important;
}
}
.time-list::-webkit-scrollbar {
/* chrome */
display: none;
}
.time-list {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
}
@layer utilities {
/* Hide scrollbar for Chrome, Safari and Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.vice-city {
@apply from-vice-start to-vice-stop bg-gradient-to-tr;
}
.hidden-actions[data-state="open"] {
opacity: 1;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
::-webkit-scrollbar {
width: 4px;
height: 4px;
}
::-webkit-scrollbar-track {
@apply bg-transparent;
}
::-webkit-scrollbar-thumb {
@apply rounded-rounded bg-grey-30;
}
::-webkit-scrollbar-thumb:hover {
@apply bg-grey-40;
}
/* TODO: Fix this as it breaks builds when using preset */
/* .accordion-margin-transition {
@apply transition-[margin] duration-300 ease-[cubic-bezier(0.87,0,0.13,1)];
} */
.col-tree:last-child .bottom-half-dash {
@apply border-none;
}
}
.scrolling-touch {
-webkit-overflow-scrolling: touch;
}
.scrolling-auto {
-webkit-overflow-scrolling: auto;
}
/* Classes to remove number spinners from inputs of type number */
/* Chrome, Safari, Edge, Opera */
.remove-number-spinner::-webkit-outer-spin-button,
.remove-number-spinner::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
.remove-number-spinner {
-moz-appearance: textfield;
}
@@ -1,5 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.4292 12.3984L10.556 10.5251M2.47656 17.5004C2.47656 17.5004 9.99245 14.8351 12.1703 12.6573C12.4888 12.3393 12.7416 11.9617 12.9141 11.546C13.0867 11.1303 13.1757 10.6847 13.176 10.2346C13.1764 9.78457 13.0881 9.33883 12.9162 8.92288C12.7443 8.50693 12.4921 8.12891 12.1741 7.81041C11.8561 7.49191 11.4785 7.23917 11.0628 7.0666C10.6471 6.89404 10.2015 6.80504 9.75147 6.80469C9.3014 6.80434 8.85566 6.89263 8.43971 7.06454C8.02376 7.23645 7.64575 7.48861 7.32724 7.80661C5.14177 9.99208 2.47656 17.5004 2.47656 17.5004ZM7.32724 11.6369L5.76619 10.0835L7.32724 11.6369Z" stroke="#9CA3AF" stroke-width="1.52298" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.5003 7.82962C17.5003 7.82962 16.4876 6.30664 14.8351 6.30664C13.5863 6.30664 12.1699 7.82962 12.1699 7.82962C12.1699 7.82962 13.1827 9.3526 14.8351 9.3526C16.4876 9.3526 17.5003 7.82962 17.5003 7.82962Z" stroke="#9CA3AF" stroke-width="1.52298" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.1695 2.49805C12.1695 2.49805 10.6465 3.51083 10.6465 5.16326C10.6465 6.81569 12.1695 7.82847 12.1695 7.82847C12.1695 7.82847 13.6924 6.42733 13.6924 5.16326C13.6924 3.51083 12.1695 2.49805 12.1695 2.49805Z" stroke="#9CA3AF" stroke-width="1.52298" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@@ -1,7 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.47266 8.89453H8.47266" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.97266 7.39453V10.3945" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.4512 9.58398H12.4599" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.1055 7.71289H14.1142" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.99 4.5H6.01C5.26771 4.50018 4.55184 4.78865 4.00078 5.30965C3.44971 5.83065 3.10258 6.54717 3.0265 7.32071C3.022 7.36157 3.019 7.40007 3.01375 7.44014C2.953 7.96971 2.5 11.9297 2.5 13.1429C2.5 13.768 2.73705 14.3676 3.15901 14.8096C3.58097 15.2517 4.15326 15.5 4.75 15.5C5.5 15.5 5.875 15.1071 6.25 14.7143L7.3105 13.6033C7.59174 13.3086 7.97321 13.1429 8.371 13.1429H11.629C12.0268 13.1429 12.4083 13.3086 12.6895 13.6033L13.75 14.7143C14.125 15.1071 14.5 15.5 15.25 15.5C15.8467 15.5 16.419 15.2517 16.841 14.8096C17.2629 14.3676 17.5 13.768 17.5 13.1429C17.5 11.9289 17.047 7.96971 16.9863 7.44014C16.981 7.40086 16.978 7.36157 16.9735 7.3215C16.8976 6.54782 16.5505 5.8311 15.9995 5.30994C15.4484 4.78878 14.7324 4.5002 13.99 4.5V4.5Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@@ -1,4 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.16602 12.25C4.16602 12.25 4.91602 11.5 7.16602 11.5C9.41602 11.5 10.916 13 13.166 13C15.416 13 16.166 12.25 16.166 12.25V3.25C16.166 3.25 15.416 4 13.166 4C10.916 4 9.41602 2.5 7.16602 2.5C4.91602 2.5 4.16602 3.25 4.16602 3.25V12.25Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.16602 17.5V12.25" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 550 B

@@ -1,6 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 17.5C14.1421 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 10 17.5Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.66602 11.666C6.66602 11.666 7.91602 13.3327 9.99935 13.3327C12.0827 13.3327 13.3327 11.666 13.3327 11.666" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 8H8.00875" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 8H12.0088" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 759 B

@@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.316 4.52475C15.9399 4.14732 15.493 3.84785 15.0008 3.64351C14.5087 3.43917 13.9811 3.33398 13.4483 3.33398C12.9154 3.33398 12.3878 3.43917 11.8957 3.64351C11.4036 3.84785 10.9567 4.14732 10.5806 4.52475L10.0033 5.10955L9.42596 4.52475C9.04984 4.14732 8.6029 3.84785 8.11079 3.64351C7.61868 3.43917 7.09108 3.33398 6.55823 3.33398C6.02538 3.33398 5.49778 3.43917 5.00567 3.64351C4.51356 3.84785 4.06663 4.14732 3.6905 4.52475C2.10107 6.11419 2.0036 8.79823 4.00539 10.8375L10.0033 16.8354L16.0011 10.8375C18.0029 8.79823 17.9054 6.11419 16.316 4.52475Z" stroke="#9CA3AF" stroke-width="1.49947" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 758 B

@@ -1,5 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.875 14.25H12.125" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.58398 17.084H11.4173" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.1887 11.416C12.3162 10.7218 12.6492 10.1835 13.1875 9.64518C13.5295 9.33033 13.801 8.94664 13.9842 8.51934C14.1673 8.09205 14.2579 7.63083 14.25 7.16602C14.25 6.03885 13.8022 4.95784 13.0052 4.16081C12.2082 3.36378 11.1272 2.91602 10 2.91602C8.87283 2.91602 7.79183 3.36378 6.9948 4.16081C6.19777 4.95784 5.75 6.03885 5.75 7.16602C5.75 7.87435 5.91292 8.7456 6.8125 9.64518C7.32537 10.1141 7.67525 10.7345 7.81125 11.416" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 857 B

@@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.9856 15.1639L12.6495 9.07732L15.2474 6.47938C16.3608 5.36598 16.732 3.88144 16.3608 3.13918C15.6186 2.76804 14.134 3.13918 13.0206 4.25258L10.4227 6.85052L4.33608 5.51443C3.96495 5.44021 3.66804 5.58866 3.51959 5.88557L3.29691 6.2567C3.14845 6.62783 3.22268 6.99897 3.51959 7.22165L7.45361 9.81959L5.96907 12.0464H3.74227L3 12.7887L5.2268 14.2732L6.71134 16.5L7.45361 15.7577V13.5309L9.68041 12.0464L12.2784 15.9804C12.501 16.2773 12.8722 16.3515 13.2433 16.2031L13.6144 16.0546C13.9113 15.832 14.0598 15.5351 13.9856 15.1639V15.1639Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 737 B

@@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.9696 18.0303C17.2625 18.3232 17.7374 18.3232 18.0302 18.0303C18.3231 17.7374 18.3231 17.2626 18.0302 16.9697L16.9696 18.0303ZM14.4052 13.3447C14.1124 13.0518 13.6375 13.0518 13.3446 13.3447C13.0517 13.6376 13.0517 14.1124 13.3446 14.4053L14.4052 13.3447ZM15.0833 9.16667C15.0833 12.4344 12.4344 15.0833 9.16667 15.0833V16.5833C13.2628 16.5833 16.5833 13.2628 16.5833 9.16667H15.0833ZM9.16667 15.0833C5.89898 15.0833 3.25 12.4344 3.25 9.16667H1.75C1.75 13.2628 5.07055 16.5833 9.16667 16.5833V15.0833ZM3.25 9.16667C3.25 5.89898 5.89898 3.25 9.16667 3.25V1.75C5.07055 1.75 1.75 5.07055 1.75 9.16667H3.25ZM9.16667 3.25C12.4344 3.25 15.0833 5.89898 15.0833 9.16667H16.5833C16.5833 5.07055 13.2628 1.75 9.16667 1.75V3.25ZM18.0302 16.9697L14.4052 13.3447L13.3446 14.4053L16.9696 18.0303L18.0302 16.9697Z" fill="#9CA3AF"/>
</svg>

Before

Width:  |  Height:  |  Size: 932 B

@@ -1,6 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.83398 16.666H14.1673" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.33398 16.6673C12.9173 14.584 9.00065 11.334 10.834 8.33398" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.91732 7.83349C8.83398 8.50016 9.41732 9.66683 9.83398 10.9168C8.16732 11.2502 6.91732 11.2502 5.83398 10.6668C4.83398 10.1668 3.91732 9.08349 3.33398 7.16683C5.66732 6.75016 7.00065 7.16683 7.91732 7.83349V7.83349Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.7498 5.00065C11.1145 5.99362 10.7949 7.15577 10.8332 8.33398C12.4165 8.25065 13.5832 7.83398 14.4165 7.16732C15.2498 6.33398 15.7498 5.25065 15.8332 3.33398C13.5832 3.41732 12.4998 4.16732 11.7498 5.00065Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 997 B

@@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 2.5L12.3175 7.195L17.5 7.9525L13.75 11.605L14.635 16.765L10 14.3275L5.365 16.765L6.25 11.605L2.5 7.9525L7.6825 7.195L10 2.5Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 327 B

@@ -1,62 +0,0 @@
import * as RadixAvatar from "@radix-ui/react-avatar"
import clsx from "clsx"
import React from "react"
import Spinner from "../spinner"
type AvatarProps = {
user?: {
img?: string
first_name?: string
last_name?: string
email?: string
}
font?: string
color?: string
isLoading?: boolean
}
const Avatar: React.FC<AvatarProps> = ({
user,
font = "inter-small-semibold",
color = "bg-grey-80",
isLoading = false,
}) => {
let username: string
if (user?.first_name && user?.last_name) {
username = user.first_name + " " + user.last_name
} else if (user?.email) {
username = user.email
} else {
username = "Medusa user"
}
return (
<RadixAvatar.Root
className={clsx(
"rounded-circle h-full w-full select-none items-center justify-center overflow-hidden",
color
)}
>
<RadixAvatar.Image
src={user?.img}
alt={username}
className="rounded-circle h-full w-full object-cover"
/>
<RadixAvatar.Fallback
className={clsx(
"text-grey-0 rounded-circle flex h-full w-full items-center justify-center bg-inherit",
font
)}
>
{isLoading ? (
<Spinner size="small" variant="primary" />
) : (
username.slice(0, 1).toUpperCase()
)}
</RadixAvatar.Fallback>
</RadixAvatar.Root>
)
}
export default Avatar
@@ -1,32 +0,0 @@
import clsx from "clsx"
import { useNavigate } from "react-router-dom"
import { useTranslation } from "react-i18next"
import ArrowLeftIcon from "../../fundamentals/icons/arrow-left-icon"
type Props = {
path?: string
label?: string
className?: string
}
const BackButton = ({ path, label, className }: Props) => {
const { t } = useTranslation()
const navigate = useNavigate()
return (
<button
onClick={() => {
path ? navigate(path) : navigate(-1)
}}
className={clsx("px-small py-xsmall", className)}
>
<div className="gap-x-xsmall text-grey-50 inter-grey-40 inter-small-semibold flex items-center">
<ArrowLeftIcon size={20} />
<span className="ml-1">
{label || t("back-button-go-back", "Go back")}
</span>
</div>
</button>
)
}
export default BackButton
@@ -1,32 +0,0 @@
import clsx from "clsx"
import React, { ReactNode, useImperativeHandle } from "react"
export type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement> & {
label: ReactNode
}
const Checkbox = React.forwardRef(
({ label, value, className, id, ...rest }: CheckboxProps, ref) => {
const checkboxRef = React.useRef<HTMLInputElement>(null)
useImperativeHandle(ref, () => checkboxRef.current)
return (
<label
className={clsx("flex cursor-pointer items-center", className)}
htmlFor={id}
>
<input
type="checkbox"
ref={checkboxRef}
className="form-checkbox rounded-base text-violet-60 mr-small border-grey-30 h-[20px] w-[20px] focus:ring-0"
value={value}
id={id}
{...rest}
/>
{label}
</label>
)
}
)
export default Checkbox
@@ -1,59 +0,0 @@
import clsx from "clsx"
import React, { useEffect } from "react"
import useClipboard from "../../../hooks/use-clipboard"
import useNotification from "../../../hooks/use-notification"
import Button from "../../fundamentals/button"
import ClipboardCopyIcon from "../../fundamentals/icons/clipboard-copy-icon"
type CopyToClipboardProps = {
value: string
displayValue?: string
successDuration?: number
showValue?: boolean
iconSize?: number
onCopy?: () => void
}
const CopyToClipboard: React.FC<CopyToClipboardProps> = ({
value,
displayValue,
successDuration = 3000,
showValue = true,
iconSize = 20,
onCopy = () => {},
}) => {
const [isCopied, handleCopy] = useClipboard(value, {
onCopied: onCopy,
successDuration: successDuration,
})
const notification = useNotification()
useEffect(() => {
if (isCopied) {
notification("Success", "Copied!", "success")
}
}, [isCopied, notification])
return (
<div className="inter-small-regular text-grey-50 gap-x-xsmall flex items-center">
<Button
variant="ghost"
size="small"
type="button"
className={clsx("text-grey-50 p-0", {
["text-violet-60"]: isCopied,
})}
onClick={handleCopy}
>
<ClipboardCopyIcon size={iconSize} />
</Button>
{showValue && (
<span className="w-full truncate">
{displayValue ? displayValue : value}
</span>
)}
</div>
)
}
export default CopyToClipboard
@@ -1,44 +0,0 @@
import { ReactDatePickerCustomHeaderProps } from "react-datepicker"
import NativeSelect from "../../molecules/native-select"
import { getYearRange, monthNames } from "./utils"
const CustomHeader = ({
date,
changeYear,
changeMonth,
}: ReactDatePickerCustomHeaderProps) => {
const month = date.getMonth()
const monthName = monthNames[month]
const year = date.getFullYear()
return (
<div className="flex w-full items-center gap-4">
<div className="flex flex-1 items-center justify-end gap-3">
<NativeSelect
defaultValue={monthName}
onValueChange={(v) => changeMonth(monthNames.indexOf(v))}
>
{monthNames.map((month) => (
<NativeSelect.Item key={month} value={month}>
{month}
</NativeSelect.Item>
))}
</NativeSelect>
</div>
<div className="flex flex-1 items-center justify-start gap-3">
<NativeSelect
defaultValue={year.toString()}
onValueChange={(v) => changeYear(parseInt(v, 10))}
>
{getYearRange().map((year) => (
<NativeSelect.Item key={year} value={year.toString()}>
{year.toString()}
</NativeSelect.Item>
))}
</NativeSelect>
</div>
</div>
)
}
export default CustomHeader
@@ -1,155 +0,0 @@
import "react-datepicker/dist/react-datepicker.css"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import React, { useEffect, useState } from "react"
import ArrowDownIcon from "../../fundamentals/icons/arrow-down-icon"
import Button from "../../fundamentals/button"
import CustomHeader from "./custom-header"
import { DateTimePickerProps } from "./types"
import InputContainer from "../../fundamentals/input-container"
import InputHeader from "../../fundamentals/input-header"
import ReactDatePicker from "react-datepicker"
import clsx from "clsx"
import moment from "moment"
const getDateClassname = (
d: Date,
tempDate: Date | null,
greyPastDates: boolean = true
): string => {
const classes: string[] = ["date"]
if (
tempDate &&
moment(d).format("YY,MM,DD") === moment(tempDate).format("YY,MM,DD")
) {
classes.push("chosen")
} else if (
greyPastDates &&
moment(d).format("YY,MM,DD") < moment(new Date()).format("YY,MM,DD")
) {
classes.push("past")
}
return classes.join(" ")
}
const DatePicker: React.FC<DateTimePickerProps> = ({
date,
onSubmitDate,
label = "start date",
required = false,
tooltipContent,
tooltip,
}) => {
const [tempDate, setTempDate] = useState<Date | null>(date || null)
const [isOpen, setIsOpen] = useState(false)
useEffect(() => setTempDate(date), [isOpen])
const submitDate = () => {
if (!tempDate || !date) {
onSubmitDate(null)
setIsOpen(false)
return
}
// update only date, month and year
const newDate = new Date(date.getTime())
newDate.setUTCDate(tempDate.getUTCDate())
newDate.setUTCMonth(tempDate.getUTCMonth())
newDate.setUTCFullYear(tempDate.getUTCFullYear())
onSubmitDate(newDate)
setIsOpen(false)
}
return (
<div className="w-full">
<PopoverPrimitive.Root open={isOpen} onOpenChange={setIsOpen}>
<PopoverPrimitive.Trigger asChild>
<button
className={clsx("rounded-rounded w-full border ", {
"shadow-input border-violet-60": isOpen,
"border-grey-20": !isOpen,
})}
type="button"
>
<InputContainer className="shadown-none border-0 focus-within:shadow-none">
<div className="text-grey-50 flex w-full justify-between pr-0.5">
{label && (
<InputHeader
{...{
label,
required,
tooltipContent,
tooltip,
}}
/>
)}
<ArrowDownIcon size={16} />
</div>
<label className="w-full text-left">
{date
? moment(date).format("ddd, DD MMM YYYY")
: "---, -- -- ----"}
</label>
</InputContainer>
</button>
</PopoverPrimitive.Trigger>
<PopoverPrimitive.Content
side="top"
sideOffset={8}
className="rounded-rounded border-grey-20 bg-grey-0 shadow-dropdown w-full border px-8"
>
<CalendarComponent
date={tempDate}
onChange={(date) => setTempDate(date)}
/>
<div className="mb-8 mt-5 flex w-full">
<Button
variant="ghost"
size="medium"
onClick={() => setIsOpen(false)}
className="border-grey-20 mr-2 flex w-1/3 justify-center border"
>
Cancel
</Button>
<Button
size="medium"
variant="primary"
onClick={() => submitDate()}
className="flex w-2/3 justify-center"
>{`Set ${label}`}</Button>
</div>
</PopoverPrimitive.Content>
</PopoverPrimitive.Root>
</div>
)
}
type CalendarComponentProps = {
date: Date | null
onChange: (
date: Date | null,
event: React.SyntheticEvent<any, Event> | undefined
) => void
greyPastDates?: boolean
}
export const CalendarComponent = ({
date,
onChange,
greyPastDates = true,
}: CalendarComponentProps) => (
<ReactDatePicker
selected={date}
inline
onChange={onChange}
calendarClassName="date-picker"
dayClassName={(d) => getDateClassname(d, date, greyPastDates)}
renderCustomHeader={({ ...props }) => <CustomHeader {...props} />}
/>
)
export default DatePicker
@@ -1,103 +0,0 @@
import * as PopoverPrimitive from "@radix-ui/react-popover"
import clsx from "clsx"
import { isNil } from "lodash"
import moment from "moment"
import React, { useEffect, useState } from "react"
import ArrowDownIcon from "../../fundamentals/icons/arrow-down-icon"
import ClockIcon from "../../fundamentals/icons/clock-icon"
import InputContainer from "../../fundamentals/input-container"
import InputHeader from "../../fundamentals/input-header"
import NumberScroller from "../number-scroller"
import { DateTimePickerProps } from "./types"
const TimePicker: React.FC<DateTimePickerProps> = ({
date,
onSubmitDate,
label = "start date",
required = false,
tooltipContent,
tooltip,
}) => {
const [selectedMinute, setSelectedMinute] = useState(
new Date(date)?.getUTCMinutes()
)
const [selectedHour, setSelectedHour] = useState(
new Date(date)?.getUTCHours()
)
useEffect(() => {
setSelectedMinute(new Date(date)?.getUTCMinutes())
setSelectedHour(new Date(date)?.getUTCHours())
}, [date])
useEffect(() => {
if (date && !isNil(selectedHour) && !isNil(selectedMinute)) {
const newDate = new Date(new Date(date).getTime())
newDate.setUTCHours(selectedHour)
newDate.setUTCMinutes(selectedMinute)
onSubmitDate(newDate)
}
}, [selectedMinute, selectedHour])
const [isOpen, setIsOpen] = useState(false)
const minuteNumbers = [...Array(60).keys()]
const hourNumbers = [...Array(24).keys()]
return (
<div className="w-full">
<PopoverPrimitive.Root open={isOpen} onOpenChange={setIsOpen}>
<PopoverPrimitive.Trigger asChild>
<button
className={clsx("rounded-rounded w-full border ", {
"shadow-input border-violet-60": isOpen,
"border-grey-20": !isOpen,
})}
type="button"
>
<InputContainer className="shadown-none border-0 focus-within:shadow-none">
<div className="text-grey-50 flex w-full justify-between pr-0.5">
<InputHeader
{...{
label,
required,
tooltipContent,
tooltip,
}}
/>
<ArrowDownIcon size={16} />
</div>
<div className="text-grey-40 flex w-full items-center text-left">
<ClockIcon size={16} />
<span className="mx-1">UTC</span>
<span className="text-grey-90">
{date ? moment.utc(date).format("HH:mm") : "--:--"}
</span>
</div>
</InputContainer>
</button>
</PopoverPrimitive.Trigger>
<PopoverPrimitive.Content
side="top"
sideOffset={8}
className="rounded-rounded scrollbar-hide border-grey-20 bg-grey-0 shadow-dropdown flex w-full border px-6 pt-6 pb-4"
>
<NumberScroller
numbers={hourNumbers}
selected={selectedHour}
onSelect={(n) => setSelectedHour(n as number)}
className="pr-4"
/>
<NumberScroller
numbers={minuteNumbers}
selected={selectedMinute}
onSelect={(n) => setSelectedMinute(n as number)}
/>
<div className="to-grey-0 h-xlarge absolute bottom-4 left-0 right-0 z-10 bg-gradient-to-b from-transparent" />
</PopoverPrimitive.Content>
</PopoverPrimitive.Root>
</div>
)
}
export default TimePicker
@@ -1,6 +0,0 @@
import { InputHeaderProps } from "../../fundamentals/input-header"
export type DateTimePickerProps = {
date: Date | null
onSubmitDate: (newDate: Date | null) => void
} & InputHeaderProps
@@ -1,25 +0,0 @@
export const range = (start, end) => {
const range: number[] = []
for (let i = start; i <= end; i++) {
range.push(i)
}
return range
}
export const getYearRange = (step = 20) =>
range(new Date().getFullYear() - step, new Date().getFullYear() + step)
export const monthNames = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
]
@@ -1,47 +0,0 @@
import clsx from "clsx"
import React, { useEffect, useState } from "react"
type FadeProps = {
isVisible: boolean
isFullScreen?: boolean
start?: string
transitionClass?: string
end?: string
classname?: string
children?: React.ReactNode
}
const Fade: React.FC<FadeProps> = ({
isVisible,
start,
end,
classname,
children,
isFullScreen = false,
}) => {
const [show, setShow] = useState(false)
useEffect(() => {
if (show && !isVisible) {
setTimeout(() => setShow(false), 100)
} else {
setShow(isVisible)
}
})
const classes = {
[start || "scale-[0.98] opacity-0"]: !isVisible,
[end || "scale-100 opacity-100"]: isVisible,
"absolute inset-0": show && isFullScreen,
}
return (
<div
className={clsx("z-50 transition-all duration-100", classes, classname)}
>
{show ? children : null}
</div>
)
}
export default Fade
@@ -1,105 +0,0 @@
import clsx from "clsx"
import React, { useRef, useState } from "react"
type FileUploadFieldProps = {
onFileChosen: (files: File[]) => void
filetypes: string[]
errorMessage?: string
placeholder?: React.ReactElement | string
className?: string
multiple?: boolean
text?: React.ReactElement | string
}
const defaultText = (
<span>
Drop your images here, or{" "}
<span className="text-violet-60">click to browse</span>
</span>
)
const FileUploadField: React.FC<FileUploadFieldProps> = ({
onFileChosen,
filetypes,
errorMessage,
className,
text = defaultText,
placeholder = "",
multiple = false,
}) => {
const inputRef = useRef<HTMLInputElement>(null)
const [fileUploadError, setFileUploadError] = useState(false)
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const fileList = e.target.files
if (fileList) {
onFileChosen(Array.from(fileList))
}
}
const handleFileDrop = (e: React.DragEvent<HTMLDivElement>) => {
setFileUploadError(false)
e.preventDefault()
const files: File[] = []
if (e.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < e.dataTransfer.items.length; i++) {
// If dropped items aren't files, reject them
if (e.dataTransfer.items[i].kind === "file") {
const file = e.dataTransfer.items[i].getAsFile()
if (file && filetypes.indexOf(file.type) > -1) {
files.push(file)
}
}
}
} else {
// Use DataTransfer interface to access the file(s)
for (let i = 0; i < e.dataTransfer.files.length; i++) {
if (filetypes.indexOf(e.dataTransfer.files[i].type) > -1) {
files.push(e.dataTransfer.files[i])
}
}
}
if (files.length === 1) {
onFileChosen(files)
} else {
setFileUploadError(true)
}
}
return (
<div
onClick={() => inputRef?.current?.click()}
onDrop={handleFileDrop}
onDragOver={(e) => e.preventDefault()}
className={clsx(
"inter-base-regular text-grey-50 rounded-rounded border-grey-20 hover:border-violet-60 hover:text-grey-40 flex h-full w-full cursor-pointer select-none flex-col items-center justify-center border-2 border-dashed transition-colors",
className
)}
>
<div className="flex flex-col items-center">
<p>{text}</p>
{placeholder}
</div>
{fileUploadError && (
<span className="text-rose-60">
{errorMessage || "Please upload an image file"}
</span>
)}
<input
ref={inputRef}
accept={filetypes.join(", ")}
multiple={multiple}
type="file"
onChange={handleFileUpload}
className="hidden"
/>
</div>
)
}
export default FileUploadField
@@ -1,25 +0,0 @@
import clsx from "clsx"
import TaxesIcon from "../../fundamentals/icons/taxes-icon"
import Tooltip from "../tooltip"
type Props = {
includesTax?: boolean
}
const IncludesTaxTooltip = ({ includesTax }: Props) => {
return (
<Tooltip content={includesTax ? "Tax incl. price" : "Tax excl. price"}>
<div className="w-large h-large rounded-rounded border-grey-20 flex items-center justify-center border">
<TaxesIcon
size={16}
className={clsx({
"text-grey-50": includesTax,
"text-grey-30": !includesTax,
})}
/>
</div>
</Tooltip>
)
}
export default IncludesTaxTooltip
@@ -1,72 +0,0 @@
import { ErrorMessage } from "@hookform/error-message"
import clsx from "clsx"
import { MultipleFieldErrors } from "react-hook-form"
import Tooltip from "../tooltip"
type InputErrorProps = {
errors?: { [x: string]: unknown }
name?: string
className?: string
}
const InputError = ({ errors, name, className }: InputErrorProps) => {
if (!errors || !name) {
return null
}
return (
<ErrorMessage
name={name}
errors={errors}
render={({ message, messages }) => {
return (
<div
className={clsx("inter-small-regular mt-2 text-rose-50", className)}
>
{messages ? (
<MultipleMessages messages={messages} />
) : (
<p>{message}</p>
)}
</div>
)
}}
/>
)
}
const MultipleMessages = ({ messages }: { messages: MultipleFieldErrors }) => {
const errors = Object.entries(messages).map(([_, message]) => message)
const displayedError = errors[0]
const remainderErrors = errors.slice(1)
return (
<div className="flex cursor-default items-center gap-x-1">
<p>{displayedError}</p>
{remainderErrors?.length > 0 && (
<Tooltip
content={
<div className="inter-small-regular text-rose-50">
{remainderErrors.map((e, i) => {
return (
<p key={i}>
{Array.from(Array(i + 1)).map((_) => "*")}
{e}
</p>
)
})}
</div>
}
>
<p>
+{remainderErrors.length}{" "}
{remainderErrors.length > 1 ? "errors" : "error"}
</p>
</Tooltip>
)}
</div>
)
}
export default InputError
@@ -1,32 +0,0 @@
import * as React from "react"
import Spinner from "../spinner"
type LoadingContainerProps = {
isLoading: boolean
placeholder?: React.ReactElement
children: React.ReactElement | React.ReactElement[]
}
const LoadingContainer = ({
isLoading,
children,
placeholder,
...props
}: LoadingContainerProps) => {
placeholder = placeholder || <Spinner size="large" variant="secondary" />
if (isLoading) {
return (
<div
className="pt-2xlarge flex min-h-[756px] w-full items-center justify-center"
{...props}
>
{placeholder}
</div>
)
}
return children as React.ReactElement
}
export default LoadingContainer
@@ -1,62 +0,0 @@
import React from "react"
import type { Toast } from "react-hot-toast"
import { toast as globalToast } from "react-hot-toast"
import AlertIcon from "../../fundamentals/icons/alert-icon"
import CheckCircleIcon from "../../fundamentals/icons/check-circle-icon"
import CrossIcon from "../../fundamentals/icons/cross-icon"
import InfoIcon from "../../fundamentals/icons/info-icon"
import XCircleIcon from "../../fundamentals/icons/x-circle-icon"
import ToasterContainer from "../toaster-container"
export type NotificationTypes = "success" | "warning" | "error" | "info"
type NotificationProps = {
toast: Toast
type: NotificationTypes
title: string
message: string
}
const Notification: React.FC<NotificationProps> = ({
toast,
type,
title,
message,
}) => {
const onDismiss = () => {
globalToast.dismiss(toast.id)
}
return (
<ToasterContainer visible={toast.visible} className="w-[380px]">
<div>{getIcon(type)}</div>
<div className="ml-small mr-base gap-y-2xsmall flex flex-grow flex-col text-white">
<span className="inter-small-semibold">{title}</span>
<span className="inter-small-regular text-grey-20">{message}</span>
</div>
<div>
<button onClick={onDismiss}>
<CrossIcon size={ICON_SIZE} className="text-grey-40" />
</button>
<span className="sr-only">Close</span>
</div>
</ToasterContainer>
)
}
const ICON_SIZE = 20
function getIcon(type: NotificationTypes) {
switch (type) {
case "success":
return <CheckCircleIcon size={ICON_SIZE} className="text-emerald-40" />
case "warning":
return <AlertIcon size={ICON_SIZE} className="text-orange-40" />
case "error":
return <XCircleIcon size={ICON_SIZE} className="text-rose-40" />
default:
return <InfoIcon size={ICON_SIZE} className="text-grey-40" />
}
}
export default Notification
@@ -1,51 +0,0 @@
import clsx from "clsx"
import React from "react"
type NumberScrollerProps = {
numbers: number[]
selected: number
onSelect: (value: number) => void
} & React.HTMLAttributes<HTMLDivElement>
const NumberScroller: React.FC<NumberScrollerProps> = ({
numbers,
selected,
onSelect,
className,
...props
}) => {
return (
<div
{...props}
className={clsx(
"time-list flex h-[305px] flex-col overflow-y-auto",
className
)}
>
{numbers.map((n, i) => {
return (
<div
key={i}
className={clsx(
"inter-base-regular hover:bg-grey-20 h-[40px] w-[40px] rounded last:mb-4",
{
"bg-violet-60 text-grey-0 inter-base-semibold hover:bg-violet-50":
n === selected,
}
)}
>
<button
onClick={() => onSelect(n)}
className="h-full w-full py-2"
type="button"
>
{n.toLocaleString("en-US", { minimumIntegerDigits: 2 })}
</button>
</div>
)
})}
</div>
)
}
export default NumberScroller
@@ -1,48 +0,0 @@
type OSShortcutProps = {
winModifiers: string | string[]
macModifiers: string | string[]
keys: string[] | string
}
const OSShortcut = ({ winModifiers, macModifiers, keys }: OSShortcutProps) => {
const isMac =
typeof window !== "undefined" &&
navigator?.platform?.toUpperCase().indexOf("MAC") >= 0
? true
: false
let modifiers: string
if (isMac) {
if (Array.isArray(macModifiers)) {
modifiers = macModifiers.join("")
} else {
modifiers = macModifiers
}
} else {
if (Array.isArray(winModifiers)) {
modifiers = winModifiers.join(" + ")
} else {
modifiers = winModifiers
}
}
let input: string
if (Array.isArray(keys)) {
input = keys.join(" + ")
} else {
input = keys
}
return (
<div className="text-grey-40 flex items-center">
<p className="inter-base-semibold m-0">
<span className="inter-base-semibold">{modifiers} </span>
{input}
</p>
</div>
)
}
export default OSShortcut
@@ -1,20 +0,0 @@
import React from "react"
type PageDescriptionProps = {
title?: string
subtitle?: string
}
const PageDescription: React.FC<PageDescriptionProps> = ({
title,
subtitle,
}) => {
return (
<div className="mb-xlarge">
<h1 className="inter-2xlarge-semibold mb-xsmall">{title}</h1>
<h2 className="inter-base-regular text-grey-50">{subtitle}</h2>
</div>
)
}
export default PageDescription
@@ -1,49 +0,0 @@
import React, { useEffect } from "react"
import type { Toast } from "react-hot-toast"
import CrossIcon from "../../fundamentals/icons/cross-icon"
import XCircleIcon from "../../fundamentals/icons/x-circle-icon"
import ToasterContainer from "../toaster-container"
type SavingStateProps = {
toast: Toast
title?: string
message?: string
onDismiss: () => void
}
const ErrorState: React.FC<SavingStateProps> = ({
toast,
title = "Error",
message = "An error occured while trying to save your changes. Please try again.",
onDismiss,
}) => {
useEffect(() => {
const life = setTimeout(() => {
onDismiss()
}, 2000)
return () => {
clearTimeout(life)
}
}, [toast])
return (
<ToasterContainer visible={toast.visible} className="w-[448px]">
<div>
<XCircleIcon size={20} className="text-rose-40" />
</div>
<div className="ml-small mr-base gap-y-2xsmall flex flex-grow flex-col">
<span className="inter-small-semibold">{title}</span>
<span className="inter-small-regular text-grey-50">{message}</span>
</div>
<div>
<button onClick={onDismiss}>
<CrossIcon size={20} className="text-grey-40" />
</button>
<span className="sr-only">Close</span>
</div>
</ToasterContainer>
)
}
export default ErrorState
@@ -1,94 +0,0 @@
import React, { ReactNode } from "react"
import type { Toast } from "react-hot-toast"
import { toast as globalToast } from "react-hot-toast"
import RefreshIcon from "../../fundamentals/icons/refresh-icon"
import ToasterContainer from "../toaster-container"
import ErrorState from "./error-state"
import SavingState from "./saving-state"
import SuccessState from "./success-state"
type SaveNotificationProps = {
toast: Toast
icon?: ReactNode
title?: string
message?: string
onSave: () => Promise<void>
reset: () => void
}
const SaveNotification: React.FC<SaveNotificationProps> = ({
toast,
icon,
title = "Unsaved changes",
message = "You have unsaved changes. Do you want to save and publish or discard them?",
onSave,
reset,
}) => {
const onDismiss = () => {
reset()
globalToast.dismiss(toast.id)
}
const handleSave = () => {
globalToast.custom((t) => <SavingState toast={t} />, {
id: toast.id,
})
onSave()
.then(() => {
globalToast.custom(
(t) => <SuccessState toast={t} onDismiss={onDismiss} />,
{
id: toast.id,
}
)
})
.catch((_err) => {
globalToast.custom(
(t) => <ErrorState toast={t} onDismiss={onDismiss} />,
{
id: toast.id,
}
)
})
}
return (
<ToasterContainer visible={toast.visible} className="pl-base w-[448px] p-0">
<div className="py-base">{getIcon(icon)}</div>
<div className="ml-small mr-base gap-y-2xsmall py-base flex flex-grow flex-col">
<span className="inter-small-semibold">{title}</span>
<span className="inter-small-regular text-grey-50">{message}</span>
</div>
<div className="inter-small-semibold border-grey-20 flex h-full flex-col border-l">
<button
onClick={handleSave}
className="inter-small-semibold border-grey-20 px-base text-violet-60 flex h-1/2 items-center justify-center border-b"
>
Publish
</button>
<button
className="inter-small-semibold px-base flex h-1/2 items-center justify-center"
onClick={onDismiss}
>
Discard
</button>
</div>
</ToasterContainer>
)
}
const ICON_SIZE = 20
function getIcon(icon?: any) {
if (icon) {
return React.cloneElement(icon, {
size: ICON_SIZE,
className: "text-grey-90",
})
} else {
return <RefreshIcon size={20} className="text-grey-90" />
}
}
export default SaveNotification
@@ -1,30 +0,0 @@
import React from "react"
import type { Toast } from "react-hot-toast"
import Spinner from "../spinner"
import ToasterContainer from "../toaster-container"
type SavingStateProps = {
toast: Toast
title?: string
message?: string
}
const SavingState: React.FC<SavingStateProps> = ({
toast,
title = "Saving changes",
message = "Hang on, this may take a few moments.",
}) => {
return (
<ToasterContainer visible={toast.visible} className="w-[448px]">
<div>
<Spinner variant="secondary" size="large" />
</div>
<div className="ml-small mr-base gap-y-2xsmall flex flex-grow flex-col">
<span className="inter-small-semibold">{title}</span>
<span className="inter-small-regular text-grey-50">{message}</span>
</div>
</ToasterContainer>
)
}
export default SavingState
@@ -1,49 +0,0 @@
import React, { useEffect } from "react"
import type { Toast } from "react-hot-toast"
import CheckCircleIcon from "../../fundamentals/icons/check-circle-icon"
import CrossIcon from "../../fundamentals/icons/cross-icon"
import ToasterContainer from "../toaster-container"
type SavingStateProps = {
toast: Toast
title?: string
message?: string
onDismiss: () => void
}
const SuccessState: React.FC<SavingStateProps> = ({
toast,
title = "Success",
message = "Your changes have been saved.",
onDismiss,
}) => {
useEffect(() => {
const life = setTimeout(() => {
onDismiss()
}, 2000)
return () => {
clearTimeout(life)
}
}, [toast])
return (
<ToasterContainer visible={toast.visible} className="w-[448px]">
<div>
<CheckCircleIcon size={20} className="text-emerald-40" />
</div>
<div className="ml-small mr-base gap-y-2xsmall flex flex-grow flex-col">
<span className="inter-small-semibold">{title}</span>
<span className="inter-small-regular text-grey-50">{message}</span>
</div>
<div>
<button onClick={onDismiss}>
<CrossIcon size={20} className="text-grey-40" />
</button>
<span className="sr-only">Close</span>
</div>
</ToasterContainer>
)
}
export default SuccessState
@@ -1,58 +0,0 @@
import React from "react"
import { Link } from "react-router-dom"
import ChevronRightIcon from "../../fundamentals/icons/chevron-right-icon"
type SettingsCardProps = {
icon: React.ReactNode
heading: string
description: string
to?: string
externalLink?: string
disabled?: boolean
}
const SettingsCard: React.FC<SettingsCardProps> = ({
icon,
heading,
description,
to = null,
externalLink = null,
disabled = false,
}) => {
if (disabled) {
to = null
}
return (
<Link to={to ?? ""} className="flex flex-1 items-center">
<button
className="bg-grey-0 rounded-rounded p-base border-grey-20 group flex h-full flex-1 items-center border"
disabled={disabled}
onClick={() => {
if (externalLink) {
window.location.href = externalLink
}
}}
>
<div className="h-2xlarge w-2xlarge bg-grey-0 rounded-rounded border-grey-20 text-grey-60 group-disabled:bg-grey-10 group-disabled:text-grey-40 flex items-center justify-center border">
<div className="bg-grey-10 h-xlarge w-xlarge flex items-center justify-center overflow-hidden rounded-md">
{icon}
</div>
</div>
<div className="mx-large flex-1 text-left">
<h3 className="inter-large-semibold text-grey-90 group-disabled:text-grey-40 m-0">
{heading}
</h3>
<p className="inter-base-regular text-grey-50 group-disabled:text-grey-40 m-0">
{description}
</p>
</div>
<div className="text-grey-40 group-disabled:text-grey-30">
<ChevronRightIcon />
</div>
</button>
</Link>
)
}
export default SettingsCard
@@ -1,25 +0,0 @@
import clsx from "clsx"
import { PropsWithChildren } from "react"
import { useSkeleton } from "../../../providers/skeleton-provider"
type Props = PropsWithChildren<{
isLoading?: boolean
}>
const Skeleton = ({ children, isLoading }: Props) => {
const { isLoading: providerState = false } = useSkeleton()
const state = isLoading || providerState
return (
<div
className={clsx("h-fit w-fit", {
"bg-grey-10 rounded-rounded animate-pulse [&>*]:opacity-0": state,
})}
>
{children}
</div>
)
}
export default Skeleton
@@ -1,9 +0,0 @@
/**
* Spacer that ensures a consistent bottom margin on all pages
* Temporary fix to BodyCard and main layout not having a consistent bottom margin
*/
const Spacer = () => {
return <div className="h-xlarge w-full" />
}
export default Spacer

Some files were not shown because too many files have changed in this diff Show More