From 40de54b0101bdfd37f577d18c10ec9f1ab1ce8fe Mon Sep 17 00:00:00 2001 From: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com> Date: Fri, 3 Mar 2023 10:09:16 +0100 Subject: [PATCH] feat(admin,admin-ui,medusa): Add Medusa Admin plugin (#3334) --- .changeset/sixty-jeans-fold.md | 7 + .eslintignore | 2 + .eslintrc.js | 56 +- .prettierrc | 10 +- integration-tests/api/package.json | 3 +- integration-tests/plugins/package.json | 3 +- integration-tests/repositories/package.json | 3 +- package.json | 7 +- packages/admin-ui/.gitignore | 4 + packages/admin-ui/.npmignore | 6 + packages/admin-ui/README.md | 38 + packages/admin-ui/package.json | 90 + packages/admin-ui/src/index.ts | 28 + packages/admin-ui/src/types/build.ts | 15 + packages/admin-ui/src/types/config.ts | 5 + packages/admin-ui/src/types/index.ts | 2 + packages/admin-ui/src/types/misc.ts | 9 + packages/admin-ui/src/utils/format-base.ts | 5 + .../src/utils/get-custom-vite-config.ts | 76 + packages/admin-ui/src/utils/index.ts | 2 + packages/admin-ui/tsconfig.json | 18 + packages/admin-ui/ui/index.html | 13 + packages/admin-ui/ui/postcss.config.js | 9 + packages/admin-ui/ui/public/logo.svg | 10 + packages/admin-ui/ui/src/App.tsx | 45 + .../ui/src/assets/styles/emoji-picker.css | 107 + .../admin-ui/ui/src/assets/styles/global.css | 456 ++ .../admin-ui/ui/src/assets/svg/carrot.svg | 5 + .../admin-ui/ui/src/assets/svg/controller.svg | 7 + packages/admin-ui/ui/src/assets/svg/flag.svg | 4 + packages/admin-ui/ui/src/assets/svg/happy.svg | 6 + packages/admin-ui/ui/src/assets/svg/heart.svg | 3 + .../admin-ui/ui/src/assets/svg/lightbulb.svg | 5 + packages/admin-ui/ui/src/assets/svg/plane.svg | 3 + .../admin-ui/ui/src/assets/svg/search.svg | 3 + .../admin-ui/ui/src/assets/svg/sprout.svg | 6 + packages/admin-ui/ui/src/assets/svg/star.svg | 3 + .../ui/src/components/atoms/avatar/index.tsx | 62 + .../components/atoms/back-button/index.tsx | 29 + .../src/components/atoms/checkbox/index.tsx | 32 + .../atoms/copy-to-clipboard/index.tsx | 58 + .../atoms/date-picker/custom-header.tsx | 44 + .../atoms/date-picker/date-picker.tsx | 115 + .../atoms/date-picker/time-picker.tsx | 102 + .../components/atoms/date-picker/types.tsx | 6 + .../src/components/atoms/date-picker/utils.ts | 25 + .../components/atoms/fade-wrapper/index.tsx | 47 + .../atoms/file-upload-field/index.tsx | 105 + .../atoms/includes-tax-tooltip/index.tsx | 26 + .../components/atoms/input-error/index.tsx | 73 + .../atoms/loading-container/index.tsx | 32 + .../components/atoms/notification/index.tsx | 62 + .../atoms/number-scroller/index.tsx | 47 + .../components/atoms/os-shortcut/index.tsx | 50 + .../atoms/page-description/index.tsx | 20 + .../atoms/save-notification/error-state.tsx | 49 + .../atoms/save-notification/index.tsx | 94 + .../atoms/save-notification/saving-state.tsx | 30 + .../atoms/save-notification/success-state.tsx | 49 + .../components/atoms/settings-card/index.tsx | 56 + .../src/components/atoms/skeleton/index.tsx | 25 + .../ui/src/components/atoms/spinner.tsx | 35 + .../ui/src/components/atoms/switch/index.tsx | 26 + .../src/components/atoms/text-input/index.tsx | 19 + .../src/components/atoms/thumbnail/index.ts | 1 + .../components/atoms/thumbnail/thumbnail.tsx | 30 + .../atoms/toaster-container/index.tsx | 33 + .../ui/src/components/atoms/tooltip/index.tsx | 58 + .../atoms/two-step-delete/index.tsx | 110 + .../components/declarative-toaster/index.tsx | 23 + .../components/fundamentals/badge/index.tsx | 43 + .../components/fundamentals/button/index.tsx | 71 + .../fundamentals/details-icon/contact.svg | 7 + .../fundamentals/details-icon/index.tsx | 57 + .../fundamentals/feature-toggle.tsx | 21 + .../fundamentals/icon-badge/index.tsx | 36 + .../fundamentals/icons/alert-icon/index.tsx | 43 + .../fundamentals/icons/arrow-down-icon.tsx | 36 + .../icons/arrow-left-icon/index.tsx | 35 + .../icons/arrow-right-icon/index.tsx | 36 + .../icons/arrow-top-right-icon/index.tsx | 29 + .../fundamentals/icons/arrow-up-icon.tsx | 36 + .../fundamentals/icons/back-icon/index.tsx | 36 + .../icons/backspace-icon/index.tsx | 42 + .../fundamentals/icons/bell-icon/index.tsx | 36 + .../icons/bell-noti-icon/index.tsx | 41 + .../icons/bell-off-icon/index.tsx | 57 + .../icons/buildings-icon/index.tsx | 64 + .../fundamentals/icons/cancel-icon/index.tsx | 36 + .../fundamentals/icons/cart-icon/index.tsx | 49 + .../fundamentals/icons/cash-icon/index.tsx | 99 + .../fundamentals/icons/channels-icon.tsx | 65 + .../icons/check-circle-fill-icon/index.tsx | 28 + .../icons/check-circle-icon/index.tsx | 36 + .../fundamentals/icons/check-icon.tsx | 29 + .../fundamentals/icons/chevron-down.tsx | 29 + .../icons/chevron-left-icon/index.tsx | 29 + .../icons/chevron-right-icon/index.tsx | 29 + .../fundamentals/icons/chevron-up.tsx | 29 + .../icons/clipboard-copy-icon/index.tsx | 50 + .../fundamentals/icons/clock-icon/index.tsx | 36 + .../fundamentals/icons/coins-icon/index.tsx | 50 + .../icons/corner-down-right-icon/index.tsx | 36 + .../fundamentals/icons/cross-icon/index.tsx | 36 + .../icons/crosshair-icon/index.tsx | 57 + .../icons/customer-icon/index.tsx | 36 + .../fundamentals/icons/details-icon.tsx | 57 + .../fundamentals/icons/discord-icon.tsx | 26 + .../icons/dollar-sign-icon/index.tsx | 36 + .../fundamentals/icons/down-left/index.tsx | 34 + .../fundamentals/icons/download-icon.tsx | 42 + .../fundamentals/icons/duplicate-icon.tsx | 36 + .../fundamentals/icons/edit-icon.tsx | 36 + .../fundamentals/icons/edit-icon/index.tsx | 36 + .../fundamentals/icons/export-icon/index.tsx | 43 + .../fundamentals/icons/eye-icon/index.tsx | 36 + .../fundamentals/icons/eye-off-icon/index.tsx | 36 + .../icons/fast-delivery-icon/index.tsx | 64 + .../fundamentals/icons/file-icon.tsx | 55 + .../fundamentals/icons/gear-icon/index.tsx | 36 + .../fundamentals/icons/gift-icon/index.tsx | 57 + .../fundamentals/icons/grip-icon.tsx | 64 + .../fundamentals/icons/happy-icon/index.tsx | 50 + .../fundamentals/icons/help-circle.tsx | 43 + .../icons/image-placeholder-icon/index.tsx | 43 + .../fundamentals/icons/info-icon/index.tsx | 43 + .../fundamentals/icons/key-icon.tsx | 30 + .../icons/list-arrow-icon/index.tsx | 57 + .../fundamentals/icons/list-icon.tsx | 64 + .../fundamentals/icons/lock-icon/index.tsx | 36 + .../fundamentals/icons/log-out-icon/index.tsx | 43 + .../icons/long-arrow-right-icon/index.tsx | 38 + .../fundamentals/icons/mail-icon/index.tsx | 36 + .../fundamentals/icons/map-pin-icon/index.tsx | 36 + .../fundamentals/icons/medusa-icon/index.tsx | 23 + .../fundamentals/icons/medusa-vice/index.tsx | 39 + .../fundamentals/icons/minus-icon/index.tsx | 29 + .../icons/more-horizontal-icon.tsx | 43 + .../fundamentals/icons/package-icon/index.tsx | 50 + .../fundamentals/icons/percent-icon/index.tsx | 43 + .../fundamentals/icons/plus-icon/index.tsx | 36 + .../fundamentals/icons/pointer-icon/index.tsx | 35 + .../fundamentals/icons/publish-icon/index.tsx | 57 + .../fundamentals/icons/refresh-icon.tsx | 50 + .../components/fundamentals/icons/refund.tsx | 57 + .../icons/sad-face-icon/index.tsx | 51 + .../fundamentals/icons/sale-icon/index.tsx | 50 + .../fundamentals/icons/search-icon/index.tsx | 27 + .../fundamentals/icons/send-icon/index.tsx | 33 + .../icons/sided-mouth-face/index.tsx | 51 + .../fundamentals/icons/sorting-icon/index.tsx | 54 + .../icons/sparkles-icon/index.tsx | 29 + .../fundamentals/icons/stop-icon.tsx | 36 + .../fundamentals/icons/tag-icon/index.tsx | 36 + .../fundamentals/icons/taxes-icon.tsx | 54 + .../fundamentals/icons/tile-icon.tsx | 50 + .../fundamentals/icons/trash-icon.tsx | 50 + .../icons/triangle-right-icon/index.tsx | 28 + .../fundamentals/icons/truck-icon/index.tsx | 43 + .../fundamentals/icons/types/icon-type.ts | 8 + .../fundamentals/icons/u-turn-icon.tsx | 29 + .../icons/unpublish-icon/index.tsx | 57 + .../fundamentals/icons/upload-icon/index.tsx | 43 + .../fundamentals/icons/users-icon/index.tsx | 50 + .../fundamentals/icons/warning-circle.tsx | 42 + .../fundamentals/icons/x-circle-icon.tsx | 43 + .../fundamentals/image-placeholder.tsx | 11 + .../fundamentals/input-container.tsx | 37 + .../components/fundamentals/input-header.tsx | 38 + .../fundamentals/status-indicator/index.tsx | 34 + .../src/components/molecules/actionables.tsx | 115 + .../molecules/activity-card/index.tsx | 88 + .../amount-and-currency-input.tsx | 134 + .../molecules/amount-input/amount-input.tsx | 92 + .../molecules/amount-input/index.ts | 4 + .../molecules/availability-duration/index.tsx | 86 + .../molecules/banner-card/index.tsx | 73 + .../molecules/batch-job-file-card/index.tsx | 42 + .../components/molecules/breadcrumb/index.tsx | 42 + .../molecules/collapsible-tree/index.tsx | 187 + .../components/molecules/connected-form.tsx | 20 + .../molecules/customer-avatar-item/index.tsx | 34 + .../customers-groups-summary/index.tsx | 33 + .../molecules/emoji-picker/index.tsx | 41 + .../molecules/filter-dropdown/container.tsx | 89 + .../molecules/filter-dropdown/item.tsx | 551 ++ .../molecules/filter-dropdown/save-field.tsx | 45 + .../components/molecules/filter-tab/index.tsx | 62 + .../molecules/form-error-toaster/index.tsx | 41 + .../molecules/form-toaster/index.tsx | 177 + .../molecules/generating-input/index.tsx | 93 + .../components/molecules/grid-input/index.tsx | 15 + .../molecules/hot-key-action/index.tsx | 23 + .../molecules/icon-tooltip/index.tsx | 36 + .../indeterminate-checkbox/index.tsx | 82 + .../molecules/input-signin/index.tsx | 99 + .../src/components/molecules/input/index.tsx | 155 + .../components/molecules/json-view/index.tsx | 94 + .../molecules/modal/focus-modal.tsx | 114 + .../src/components/molecules/modal/index.tsx | 161 + .../molecules/modal/layered-modal.tsx | 163 + .../components/molecules/modal/side-modal.tsx | 60 + .../molecules/modal/stepped-modal.tsx | 227 + .../molecules/native-select/index.tsx | 68 + .../components/molecules/note-input/index.tsx | 76 + .../molecules/notification-bell/index.tsx | 21 + .../molecules/numbered-item/index.tsx | 52 + .../molecules/order-status/index.tsx | 113 + .../molecules/rma-select-shipping/index.tsx | 54 + .../sales-channels-display/index.tsx | 70 + .../molecules/sales-channels-list/index.tsx | 46 + .../sales-channels-summary/index.tsx | 39 + .../src/components/molecules/search-bar.tsx | 49 + .../components/molecules/section/index.tsx | 32 + .../src/components/molecules/select/index.tsx | 235 + .../next-select/components/containers.tsx | 190 + .../select/next-select/components/control.tsx | 149 + .../select/next-select/components/index.ts | 39 + .../select/next-select/components/input.tsx | 36 + .../select/next-select/components/menu.tsx | 343 ++ .../next-select/components/multi-value.tsx | 113 + .../next-select/components/placeholder.tsx | 33 + .../components/select-primitives.ts | 3 + .../next-select/components/single-value.tsx | 42 + .../next-select/createable-select/index.tsx | 40 + .../molecules/select/next-select/index.tsx | 4 + .../select/next-select/module-augmentation.ts | 74 + .../select/next-select/select/index.tsx | 62 + .../select/next-select/use-select-props.tsx | 85 + .../molecules/select/next-select/utils.tsx | 83 + .../molecules/select/select-components.tsx | 314 ++ .../molecules/shipping-option/index.tsx | 70 + .../molecules/sidebar-company-logo/index.tsx | 20 + .../molecules/sidebar-menu-item/index.tsx | 72 + .../molecules/sidebar-team-member/index.tsx | 28 + .../molecules/status-selector/index.tsx | 55 + .../molecules/switchable-item/index.tsx | 55 + .../molecules/table-fileds-filter/index.tsx | 200 + .../molecules/table-pagination/index.ts | 1 + .../table-pagination/table-pagination.tsx | 77 + .../molecules/table-toaster/index.tsx | 27 + .../molecules/table/filtering-option.tsx | 96 + .../src/components/molecules/table/index.tsx | 250 + .../molecules/table/table-search.tsx | 47 + .../molecules/tag-grid.tsx/index.tsx | 38 + .../components/molecules/tag-input/index.tsx | 231 + .../components/molecules/textarea/index.tsx | 114 + .../timeline-events/claim-event/index.tsx | 269 + .../timeline-events/event-actionables.tsx | 20 + .../timeline-events/event-container.tsx | 105 + .../timeline-events/event-item-container.tsx | 40 + .../molecules/timeline-events/event-type.ts | 5 + .../molecules/timeline-events/exchange.tsx | 338 ++ .../timeline-events/items-fulfilled.tsx | 32 + .../timeline-events/items-shipped.tsx | 32 + .../molecules/timeline-events/note.tsx | 73 + .../timeline-events/notification/index.tsx | 66 + .../notification/resend-modal.tsx | 99 + .../timeline-events/order-canceled.tsx | 20 + .../timeline-events/order-edit/canceled.tsx | 26 + .../timeline-events/order-edit/confirmed.tsx | 40 + .../timeline-events/order-edit/created.tsx | 292 ++ .../timeline-events/order-edit/declined.tsx | 46 + .../timeline-events/order-edit/index.tsx | 27 + .../order-edit/payment-required.tsx | 80 + .../order-edit/refund-required.tsx | 73 + .../timeline-events/order-edit/requested.tsx | 62 + .../timeline-events/order-placed.tsx | 29 + .../molecules/timeline-events/refund.tsx | 43 + .../molecules/timeline-events/return.tsx | 151 + .../components/molecules/user-menu/index.tsx | 75 + .../components/organisms/accordion/index.tsx | 113 + .../organisms/activity-drawer/index.tsx | 72 + .../add-denomination-modal/index.tsx | 266 + .../use-values-field-array.ts | 72 + .../organisms/analytics-config-form/index.tsx | 81 + .../organisms/analytics-preferences/index.tsx | 139 + .../batch-jobs-activity-list/index.tsx | 247 + .../batch-jobs-activity-list/utils.ts | 48 + .../ui/src/components/organisms/body-card.tsx | 115 + .../organisms/confirmation-prompt.tsx | 71 + .../organisms/currency-input/index.tsx | 296 ++ .../organisms/custom-table-header/index.tsx | 37 + .../components/organisms/delete-prompt.tsx | 78 + .../organisms/details-collapsible/index.tsx | 50 + .../edit-denominations-modal/index.tsx | 179 + .../organisms/edit-user-modal/index.tsx | 123 + .../organisms/error-boundary/index.tsx | 163 + .../organisms/export-modal/index.tsx | 62 + .../organisms/file-upload-modal/index.tsx | 32 + .../organisms/gift-card-banner/index.tsx | 96 + .../organisms/help-dialog/index.tsx | 86 + .../organisms/invite-modal/index.tsx | 112 + .../components/organisms/login-card/index.tsx | 82 + .../organisms/medusa-price-input/index.tsx | 51 + .../components/organisms/metadata/index.tsx | 148 + .../organisms/price-input/index.tsx | 65 + .../organisms/product-variant-tree/index.tsx | 85 + .../organisms/radio-group/index.tsx | 174 + .../components/organisms/raw-json/index.tsx | 38 + .../organisms/reset-token-card/index.tsx | 115 + .../rma-return-product-table/index.tsx | 114 + .../rma-select-product-table/index.tsx | 303 ++ .../components/organisms/section/index.tsx | 52 + .../components/organisms/sidebar/index.tsx | 110 + .../organisms/table-container/index.tsx | 50 + .../organisms/table-container/pagination.tsx | 64 + .../organisms/table-container/types.ts | 12 + .../components/organisms/timeline/index.tsx | 212 + .../src/components/organisms/topbar/index.tsx | 62 + .../organisms/upload-modal/index.tsx | 302 ++ .../ui/src/components/private-route/index.tsx | 31 + .../admin-ui/ui/src/components/seo/index.tsx | 75 + .../templates/add-products-modal/index.tsx | 109 + .../product-table-config.tsx | 88 + .../templates/add-products-modal/utils.ts | 32 + .../src/components/templates/address-form.tsx | 180 + .../templates/collection-modal/index.tsx | 191 + .../add-product-table.tsx | 233 + .../collection-product-table/index.tsx | 193 + .../collection-product-table/types.ts | 11 + .../use-collection-product-columns.tsx | 48 + .../use-sorting-options.tsx | 88 + .../use-view-product-columns.tsx | 59 + .../collection-product-table/utils.tsx | 25 + .../view-products-table.tsx | 194 + .../templates/collections-table/index.tsx | 191 + .../use-collection-actions.tsx | 44 + .../use-collection-column.tsx | 52 + .../templates/customer-group-table/config.tsx | 104 + .../customer-groups-table.tsx | 279 ++ .../customers-list-table.tsx | 231 + .../edit-customers-table.tsx | 253 + .../templates/customer-orders-table/index.tsx | 154 + .../use-customer-orders-columns.tsx | 149 + .../templates/customer-table/index.tsx | 206 + .../customer-table/use-customer-columns.tsx | 48 + .../customer-table/use-customer-filters.tsx | 213 + .../discount-filter-dropdown/index.tsx | 140 + .../templates/discount-table/index.tsx | 234 + .../discount-table/use-copy-promotion.tsx | 74 + .../discount-table/use-promotion-columns.tsx | 151 + .../discount-table/use-promotion-filters.tsx | 519 ++ .../use-promotion-row-actions.tsx | 86 + .../templates/draft-order-table/index.tsx | 166 + .../use-draft-order-column.tsx | 82 + .../use-draft-order-filters.ts | 213 + .../gift-card-filter-dropdown/index.tsx | 179 + .../templates/gift-card-table/index.tsx | 197 + .../gift-card-table/use-gift-card-column.tsx | 103 + .../gift-card-table/use-gift-card-filters.ts | 591 +++ .../templates/image-table/index.tsx | 163 + .../ui/src/components/templates/layout.tsx | 30 + .../src/components/templates/login-layout.tsx | 29 + .../templates/order-filter-dropdown/index.tsx | 226 + .../templates/order-table/index.tsx | 241 + .../order-table/use-order-column.tsx | 132 + .../order-table/use-order-filters.ts | 617 +++ .../price-list-table/price-list-filters.tsx | 133 + .../price-list-table/price-list-table.tsx | 202 + .../price-list-table/use-copy-price-list.tsx | 56 + .../use-price-list-actions.tsx | 79 + .../use-price-list-columns.tsx | 71 + .../use-price-list-filters.tsx | 495 ++ .../templates/price-list-table/utils.tsx | 43 + .../templates/price-overrides/index.tsx | 222 + .../price-overrides/price-amount.tsx | 75 + .../templates/product-table/index.tsx | 269 + .../templates/product-table/overview.tsx | 107 + .../product-table/use-copy-product.ts | 177 + .../product-table/use-filter-tabs.tsx | 487 ++ .../product-table/use-product-actions.tsx | 88 + .../product-table/use-product-column.tsx | 142 + .../product-table/use-product-filters.ts | 604 +++ .../templates/search-modal/index.tsx | 163 + .../search-modal/keyboard-shortcuts.tsx | 46 + .../search-modal/results/customer-results.tsx | 69 + .../search-modal/results/discount-results.tsx | 64 + .../search-modal/results/order-results.tsx | 63 + .../search-modal/results/product-results.tsx | 69 + .../search-modal/section-collapsible.tsx | 28 + .../use-keyboard-navigation-list.tsx | 117 + .../templates/selectable-table/index.tsx | 211 + .../templates/settings-overview.tsx | 18 + .../templates/transfer-orders-modal/index.tsx | 234 + .../components/templates/two-split-pane.tsx | 54 + .../src/components/templates/user-table.tsx | 329 ++ .../admin-ui/ui/src/constants/analytics.ts | 1 + .../ui/src/constants/medusa-backend-url.ts | 2 + .../admin-ui/ui/src/constants/query-client.ts | 11 + .../src/domain/collections/details/index.tsx | 210 + .../ui/src/domain/collections/index.tsx | 12 + .../ui/src/domain/customers/details/edit.tsx | 146 + .../ui/src/domain/customers/details/index.tsx | 132 + .../groups/context/customer-group-context.tsx | 81 + .../customers/groups/customer-group-modal.tsx | 114 + .../src/domain/customers/groups/details.tsx | 248 + .../ui/src/domain/customers/groups/index.tsx | 58 + .../ui/src/domain/customers/header.tsx | 28 + .../ui/src/domain/customers/index.tsx | 33 + .../add-condition/conditions-provider.tsx | 183 + .../conditions/add-condition/index.tsx | 30 + .../collections/add-collections.tsx | 80 + .../collections-conditions-table.tsx | 85 + .../customer-groups/add-customer-groups.tsx | 80 + .../customer-groups-conditions-table.tsx | 85 + .../product-types/add-types.tsx | 82 + .../product-types/type-conditions-table.tsx | 85 + .../products/add-products.tsx | 78 + .../products/product-conditions-table.tsx | 84 + .../add-condition-resources/tags/add-tags.tsx | 78 + .../tags/tags-conditions-table.tsx | 83 + .../edit-condition/add-conditions-screens.tsx | 33 + .../condition-table-actions.tsx | 75 + .../edit-condition/edit-condition-modal.tsx | 74 + .../edit-condition-provider.tsx | 124 + .../discounts/details/conditions/index.tsx | 80 + .../conditions/use-discount-conditions.tsx | 105 + .../configurations/edit-configurations.tsx | 242 + .../details/configurations/index.tsx | 60 + .../use-discount-configurations.tsx | 139 + .../details/general/edit-general.tsx | 236 + .../discounts/details/general/index.tsx | 176 + .../ui/src/domain/discounts/details/index.tsx | 76 + .../ui/src/domain/discounts/index.tsx | 54 + .../discount-form/add-conditions-modal.tsx | 118 + .../add-condition-footer.tsx | 69 + .../add-condition-tables/collections.tsx | 91 + .../add-condition-tables/customer-groups.tsx | 88 + .../add-condition-tables/products.tsx | 84 + .../add-condition-tables/tags.tsx | 75 + .../add-condition-tables/types.tsx | 78 + .../details-condition-tables/collections.tsx | 95 + .../customer-groups.tsx | 89 + .../details-condition-footer.tsx | 62 + .../details-condition-tables/products.tsx | 85 + .../details-condition-tables/tags.tsx | 77 + .../details-condition-tables/types.tsx | 79 + .../edit-condition-tables/collections.tsx | 86 + .../edit-condition-tables/customer-groups.tsx | 82 + .../edit-condition-footer.tsx | 63 + .../edit-condition-tables/products.tsx | 78 + .../edit-condition-tables/tags.tsx | 77 + .../edit-condition-tables/types.tsx | 72 + .../condition-tables/shared/collection.tsx | 70 + .../condition-tables/shared/common.tsx | 4 + .../shared/condition-operator.tsx | 36 + .../condition-tables/shared/groups.tsx | 67 + .../condition-tables/shared/products.tsx | 118 + .../condition-tables/shared/tags.tsx | 58 + .../condition-tables/shared/types.tsx | 58 + .../discount-form/edit-conditions-modal.tsx | 47 + .../form/discount-form-context.tsx | 208 + .../new/discount-form/form/mappers.ts | 150 + .../discount-form/form/use-form-actions.tsx | 44 + .../discounts/new/discount-form/index.tsx | 158 + .../sections/conditions/condition-item.tsx | 241 + .../sections/conditions/index.tsx | 84 + .../discount-form/sections/configuration.tsx | 235 + .../sections/discount-allocation.tsx | 42 + .../discount-form/sections/discount-type.tsx | 55 + .../new/discount-form/sections/general.tsx | 186 + .../use-condition-modal-items.tsx | 118 + .../ui/src/domain/discounts/new/index.tsx | 14 + .../admin-ui/ui/src/domain/discounts/types.ts | 81 + .../ui/src/domain/discounts/utils/index.tsx | 22 + .../src/domain/gift-cards/custom-giftcard.tsx | 164 + .../details/edit-gift-card-modal.tsx | 111 + .../src/domain/gift-cards/details/index.tsx | 171 + .../details/update-balance-modal.tsx | 116 + .../ui/src/domain/gift-cards/index.tsx | 16 + .../gift-cards/manage/denomination-table.tsx | 119 + .../manage/form/gift-card-form-context.tsx | 92 + .../domain/gift-cards/manage/form/mappers.ts | 59 + .../ui/src/domain/gift-cards/manage/index.tsx | 105 + .../manage/sections/denominations.tsx | 126 + .../gift-cards/manage/sections/images.tsx | 105 + .../manage/sections/information.tsx | 227 + .../domain/gift-cards/manage/utils/types.ts | 12 + .../admin-ui/ui/src/domain/gift-cards/new.tsx | 294 ++ .../ui/src/domain/gift-cards/overview.tsx | 144 + .../ui/src/domain/inventory/header.tsx | 29 + .../ui/src/domain/inventory/index.tsx | 14 + .../src/domain/inventory/inventory/index.tsx | 19 + .../components/address-form/index.tsx | 150 + .../components/edit-sales-channels/index.tsx | 76 + .../components/general-form/index.tsx | 37 + .../components/location-card/index.tsx | 105 + .../components/sales-channels-form/index.tsx | 75 + .../sales-channels-section/index.tsx | 26 + .../domain/inventory/locations/edit/index.tsx | 114 + .../src/domain/inventory/locations/index.tsx | 59 + .../domain/inventory/locations/new/index.tsx | 183 + .../admin-ui/ui/src/domain/oauth/index.tsx | 27 + .../__tests__/claim-type-form.test.tsx | 59 + .../components/claim-type-form/index.tsx | 46 + .../__tests__/items-to-receive-form.test.tsx | 59 + .../components/items-to-receive-form/index.ts | 2 + .../items-to-receive-form.tsx | 71 + .../items-to-receive-table.tsx | 62 + .../use-items-to-receive-columns.tsx | 164 + .../__tests__/items-to-return-form.test.tsx | 119 + .../add-return-reason-screen.tsx | 132 + .../add-return-reason/index.tsx | 90 + .../components/items-to-return-form/index.tsx | 88 + .../items-to-return-table.tsx | 86 + .../use-return-item-columns.tsx | 171 + .../__tests__/items-to-send-form.test.tsx | 80 + .../add-additional-items-screen.tsx | 193 + .../add-additional-items-table.tsx | 85 + .../add-additional-items-screen/index.ts | 1 + .../use-add-additional-items-columns.tsx | 194 + .../additional-items-table.tsx | 71 + .../components/items-to-send-form/index.tsx | 96 + .../use-additional-items-columns.tsx | 156 + .../__tests__/refund-amount-form.test.tsx | 59 + .../components/refund-amount-form/index.tsx | 114 + .../__tests__/claim-summary.test.tsx | 72 + .../rma-summaries/claim-summary.tsx | 177 + .../orders/components/rma-summaries/index.ts | 3 + .../rma-summaries/receive-return-summary.tsx | 120 + .../rma-summaries/summary-line-item.tsx | 58 + .../rma-summaries/summary-shipping-line.tsx | 41 + .../__tests__/send-notification-form.test.tsx | 52 + .../send-notification-form/index.tsx | 46 + .../__tests__/shipping-address-form.test.tsx | 53 + .../shipping-address-form/index.tsx | 59 + .../shipping-address-form-modal.tsx | 114 + .../__tests__/shipping-form.test.tsx | 148 + .../orders/components/shipping-form/index.tsx | 210 + .../components/table-quantity-selector.tsx | 91 + .../domain/orders/details/address-modal.tsx | 156 + .../details/claim/register-claim-menu.tsx | 294 ++ .../details/create-fulfillment/index.tsx | 201 + .../details/create-fulfillment/item-table.tsx | 161 + .../src/domain/orders/details/email-modal.tsx | 95 + .../ui/src/domain/orders/details/index.tsx | 677 +++ .../orders/details/mark-shipped/index.tsx | 234 + .../domain/orders/details/order-line/edit.tsx | 299 ++ .../orders/details/order-line/index.tsx | 65 + .../orders/details/receive-return/index.tsx | 182 + .../domain/orders/details/refund/index.tsx | 214 + .../domain/orders/details/returns/index.tsx | 315 ++ .../orders/details/rma-sub-modals/address.tsx | 198 + .../details/rma-sub-modals/products.tsx | 292 ++ .../details/rma-sub-modals/return-reasons.tsx | 179 + .../src/domain/orders/details/swap/create.tsx | 414 ++ .../orders/details/templates/address.tsx | 35 + .../details/templates/display-total.tsx | 61 + .../details/templates/fulfillment-status.tsx | 22 + .../orders/details/templates/fulfillment.tsx | 129 + .../domain/orders/details/templates/index.ts | 9 + .../orders/details/templates/order-status.tsx | 18 + .../details/templates/payment-actionables.tsx | 73 + .../details/templates/payment-details.tsx | 55 + .../details/templates/payment-status.tsx | 18 + .../details/templates/tracking-link.tsx | 19 + .../orders/details/utils/create-filtering.ts | 64 + .../details/utils/get-default-values.ts | 261 + .../details/utils/order-returnable-fields.ts | 4 + .../utils/use-admin-expand-paramter.ts | 35 + .../domain/orders/draft-orders/details.tsx | 510 ++ .../src/domain/orders/draft-orders/index.tsx | 69 + .../ui/src/domain/orders/edit/context.tsx | 54 + .../ui/src/domain/orders/edit/modal.tsx | 443 ++ .../ui/src/domain/orders/edit/utils/user.ts | 19 + .../src/domain/orders/edit/variants-table.tsx | 303 ++ .../admin-ui/ui/src/domain/orders/index.tsx | 116 + .../orders/new/components/billing-details.tsx | 84 + .../new/components/custom-item-sub-modal.tsx | 85 + .../domain/orders/new/components/items.tsx | 328 ++ .../orders/new/components/select-region.tsx | 62 + .../orders/new/components/select-shipping.tsx | 158 + .../new/components/shipping-details.tsx | 253 + .../domain/orders/new/components/summary.tsx | 373 ++ .../ui/src/domain/orders/new/form/index.tsx | 211 + .../ui/src/domain/orders/new/new-order.tsx | 135 + .../admin-ui/ui/src/domain/orders/utils.ts | 30 + .../src/domain/pricing/batch-job/import.tsx | 194 + .../ui/src/domain/pricing/details/index.tsx | 42 + .../pricing/details/sections/header.tsx | 129 + .../edit-prices-overrides/index.tsx | 134 + .../edit-prices-overrides/mappers.tsx | 21 + .../product-variant-leaf.tsx | 50 + .../sections/prices-details/edit-prices.tsx | 94 + .../details/sections/prices-details/index.tsx | 51 + .../prices-details/prices-table/index.tsx | 139 + .../prices-table/use-columns.tsx | 56 + .../details/sections/prices-details/utils.ts | 23 + .../ui/src/domain/pricing/details/utils.ts | 11 + .../admin-ui/ui/src/domain/pricing/index.tsx | 45 + .../admin-ui/ui/src/domain/pricing/new.tsx | 14 + .../pricing-form/form-header/index.tsx | 166 + .../pricing/pricing-form/form/mappers.ts | 109 + .../form/pricing-form-context.tsx | 168 + .../src/domain/pricing/pricing-form/index.tsx | 43 + .../pricing-form/sections/configuration.tsx | 201 + .../pricing/pricing-form/sections/general.tsx | 55 + .../pricing/pricing-form/sections/prices.tsx | 49 + .../pricing-form/sections/product-prices.tsx | 197 + .../pricing/pricing-form/sections/type.tsx | 51 + .../pricing/pricing-form/types/index.ts | 104 + .../ui/src/domain/pricing/pricing-table.tsx | 67 + .../src/domain/products/batch-job/import.tsx | 200 + .../components/customs-form/index.tsx | 76 + .../components/dimensions-form/index.tsx | 75 + .../components/discountable-form/index.tsx | 35 + .../components/general-form/index.tsx | 103 + .../products/components/media-form/index.tsx | 235 + .../components/organize-form/index.tsx | 87 + .../organize-form/use-organize-data.tsx | 35 + .../products/components/prices-form/index.tsx | 173 + .../components/prices-form/nested-price.tsx | 123 + .../prices-form/price-form-input.tsx | 83 + .../sales-channels-modal/add-screen.tsx | 132 + .../sales-channels-modal/available-screen.tsx | 88 + .../components/sales-channels-modal/index.tsx | 56 + .../components/sales-channels-modal/table.tsx | 227 + .../use-sales-channels-modal.tsx | 24 + .../components/thumbnail-form/index.tsx | 116 + .../create-flow-variant-form/index.tsx | 111 + .../edit-flow-variant-form/index.tsx | 123 + .../variant-general-form/index.tsx | 51 + .../variant-prices-form/index.tsx | 22 + .../variant-select-options-form/hooks.tsx | 74 + .../variant-select-options-form/index.tsx | 78 + .../variant-form/variant-stock-form/index.tsx | 103 + .../edit/hooks/use-edit-product-actions.tsx | 165 + .../ui/src/domain/products/edit/index.tsx | 70 + .../sections/attributes/attribute-modal.tsx | 139 + .../edit/sections/attributes/index.tsx | 68 + .../edit/sections/general/channels-modal.tsx | 32 + .../edit/sections/general/general-modal.tsx | 149 + .../products/edit/sections/general/index.tsx | 169 + .../products/edit/sections/media/index.tsx | 50 + .../edit/sections/media/media-modal.tsx | 132 + .../products/edit/sections/raw/index.tsx | 20 + .../edit/sections/thumbnail/index.tsx | 82 + .../sections/thumbnail/thumbnail-modal.tsx | 135 + .../sections/variants/add-variant-modal.tsx | 151 + .../sections/variants/edit-variant-modal.tsx | 154 + .../edit-variant-screen.tsx | 146 + .../variants/edit-variants-modal/index.tsx | 189 + .../use-edit-variants-modal.tsx | 21 + .../edit-variants-modal/variant-card.tsx | 170 + .../products/edit/sections/variants/index.tsx | 180 + .../edit/sections/variants/options-modal.tsx | 243 + .../sections/variants/options-provider.tsx | 47 + .../products/edit/sections/variants/table.tsx | 156 + .../src/domain/products/filter-dropdown.tsx | 242 + .../admin-ui/ui/src/domain/products/index.tsx | 14 + .../products/new/add-sales-channels.tsx | 117 + .../products/new/add-variants/index.tsx | 474 ++ .../new/add-variants/new-variant/index.tsx | 375 ++ .../ui/src/domain/products/new/index.tsx | 405 ++ .../ui/src/domain/products/overview/index.tsx | 207 + .../src/domain/publishable-api-keys/index.tsx | 13 + .../modals/add-sales-channels.tsx | 147 + .../publishable-api-keys/modals/details.tsx | 103 + .../modals/manage-sales-channels.tsx | 405 ++ .../publishable-api-keys/pages/index.tsx | 264 + .../tables/publishable-api-keys-table.tsx | 306 ++ .../tables/sales-channels-table.tsx | 240 + .../sales-channels/form/add-sales-channel.tsx | 203 + .../form/edit-sales-channel.tsx | 102 + .../ui/src/domain/sales-channels/index.tsx | 13 + .../domain/sales-channels/pages/details.tsx | 489 ++ .../domain/sales-channels/tables/config.tsx | 56 + .../sales-channels/tables/placeholder.tsx | 28 + .../domain/sales-channels/tables/product.tsx | 530 ++ .../components/currency-tax-setting.tsx | 93 + .../default-currency-selector.tsx | 96 + .../default-store-currency/index.tsx | 26 + .../add-currencies-screen.tsx | 146 + .../current-currencies-screen.tsx | 175 + .../edit-currencies-modal.tsx | 47 + .../components/store-currencies/index.tsx | 32 + .../components/store-currencies/table.tsx | 123 + .../use-currency-table-columns.tsx | 58 + .../src/domain/settings/currencies/index.tsx | 122 + .../ui/src/domain/settings/details.tsx | 138 + .../admin-ui/ui/src/domain/settings/index.tsx | 115 + .../edit-user-information-modal.tsx | 109 + .../edit-user-information/index.tsx | 65 + .../settings/personal-information/index.tsx | 40 + .../usage-insights/index.tsx | 55 + .../usage-insights/usage-insights-modal.tsx | 88 + .../region-form/region-details-form.tsx | 136 + .../region-form/region-providers-form.tsx | 89 + .../components/region-form/use-store-data.tsx | 58 + .../shipping-option-card/edit-modal.tsx | 139 + .../components/shipping-option-card/index.tsx | 113 + .../components/shipping-option-form/index.tsx | 279 ++ .../use-shipping-option-form-data.tsx | 102 + .../general-section/edit-region.modal.tsx | 158 + .../regions/edit/general-section/index.tsx | 210 + .../domain/settings/regions/edit/index.tsx | 50 + .../create-return-shipping-option.modal.tsx | 105 + .../edit/return-shipping-options/index.tsx | 52 + .../create-shipping-option-modal.tsx | 105 + .../regions/edit/shipping-options/index.tsx | 48 + .../ui/src/domain/settings/regions/index.tsx | 25 + .../src/domain/settings/regions/new/index.tsx | 169 + .../regions/region-overview/index.tsx | 97 + .../regions/region-overview/region-card.tsx | 48 + .../return-reasons/create-reason-modal.tsx | 133 + .../domain/settings/return-reasons/detail.tsx | 143 + .../domain/settings/return-reasons/index.tsx | 95 + .../ui/src/domain/settings/taxes/details.tsx | 158 + .../src/domain/settings/taxes/edit-form.tsx | 353 ++ .../settings/taxes/edit-tax-rate-details.tsx | 76 + .../ui/src/domain/settings/taxes/edit.tsx | 52 + .../ui/src/domain/settings/taxes/index.tsx | 85 + .../ui/src/domain/settings/taxes/new.tsx | 270 + .../settings/taxes/product-selector.tsx | 77 + .../settings/taxes/product-type-selector.tsx | 42 + .../src/domain/settings/taxes/region-form.tsx | 167 + .../settings/taxes/selectable-table.tsx | 193 + .../taxes/shipping-option-selector.tsx | 44 + .../domain/settings/taxes/tax-rate-row.tsx | 78 + .../domain/settings/taxes/tax-rule-item.tsx | 44 + .../settings/taxes/tax-rule-selector.tsx | 148 + .../settings/taxes/use-tax-rate-columns.tsx | 48 + .../ui/src/domain/settings/users/index.tsx | 85 + .../admin-ui/ui/src/fonts/Inter-Regular.ttf | Bin 0 -> 401788 bytes .../admin-ui/ui/src/fonts/Inter-SemiBold.ttf | Bin 0 -> 407256 bytes .../admin-ui/ui/src/fonts/RobotoMono-Bold.ttf | Bin 0 -> 87620 bytes .../ui/src/fonts/RobotoMono-Regular.ttf | Bin 0 -> 87520 bytes .../ui/src/hooks/use-build-timeline.tsx | 592 +++ .../admin-ui/ui/src/hooks/use-clipboard.js | 38 + .../ui/src/hooks/use-computed-height.ts | 19 + .../admin-ui/ui/src/hooks/use-debounce.ts | 22 + .../ui/src/hooks/use-detect-change.tsx | 50 + .../ui/src/hooks/use-highlight-search.tsx | 33 + .../ui/src/hooks/use-imperative-dialog.tsx | 139 + packages/admin-ui/ui/src/hooks/use-is-me.tsx | 12 + .../ui/src/hooks/use-notification.tsx | 21 + .../ui/src/hooks/use-observe-width.ts | 30 + .../ui/src/hooks/use-on-click-outside.tsx | 28 + .../ui/src/hooks/use-outside-click.ts | 19 + .../ui/src/hooks/use-query-filters.ts | 228 + packages/admin-ui/ui/src/hooks/use-scroll.ts | 20 + .../ui/src/hooks/use-selection-column.tsx | 39 + .../ui/src/hooks/use-set-search-params.tsx | 24 + .../admin-ui/ui/src/hooks/use-toggle-state.ts | 46 + .../ui/src/hooks/use-window-dimensions.ts | 25 + packages/admin-ui/ui/src/index.css | 3 + packages/admin-ui/ui/src/main.tsx | 13 + packages/admin-ui/ui/src/medusa-app.tsx | 17 + packages/admin-ui/ui/src/pages/404.tsx | 13 + packages/admin-ui/ui/src/pages/a.tsx | 66 + packages/admin-ui/ui/src/pages/index.tsx | 20 + packages/admin-ui/ui/src/pages/invite.tsx | 229 + packages/admin-ui/ui/src/pages/login.tsx | 38 + .../admin-ui/ui/src/pages/reset-password.tsx | 158 + .../ui/src/providers/analytics-provider.tsx | 212 + .../src/providers/feature-flag-provider.tsx | 73 + .../ui/src/providers/medusa-provider.tsx | 17 + .../ui/src/providers/polling-provider.tsx | 99 + .../admin-ui/ui/src/providers/providers.tsx | 23 + .../ui/src/providers/skeleton-provider.tsx | 32 + .../admin-ui/ui/src/services/analytics.ts | 112 + packages/admin-ui/ui/src/services/api.js | 729 +++ packages/admin-ui/ui/src/services/request.js | 15 + .../ui/src/types/react-table-config.d.ts | 133 + packages/admin-ui/ui/src/types/shared.ts | 55 + packages/admin-ui/ui/src/types/utils.ts | 3 + .../admin-ui/ui/src/utils/bytes-converter.ts | 18 + packages/admin-ui/ui/src/utils/callAll.ts | 2 + packages/admin-ui/ui/src/utils/color.ts | 12 + .../ui/src/utils/consolidate-images.ts | 15 + .../src/utils/convert-empty-string-to-null.js | 9 + packages/admin-ui/ui/src/utils/countries.ts | 638 +++ packages/admin-ui/ui/src/utils/currencies.ts | 1087 ++++ packages/admin-ui/ui/src/utils/date-utils.ts | 5 + .../ui/src/utils/decide-badge-color.js | 40 + packages/admin-ui/ui/src/utils/equals-set.ts | 11 + .../admin-ui/ui/src/utils/error-messages.ts | 10 + .../ui/src/utils/extract-customer-name.ts | 69 + .../admin-ui/ui/src/utils/extract-options.ts | 26 + packages/admin-ui/ui/src/utils/filters.js | 40 + .../admin-ui/ui/src/utils/focus-by-name.ts | 3 + .../admin-ui/ui/src/utils/form-helpers.ts | 22 + .../admin-ui/ui/src/utils/form-validator.ts | 67 + .../admin-ui/ui/src/utils/format-address.js | 19 + .../src/utils/fulfillment-providers.mapper.ts | 26 + .../ui/src/utils/generate-promotion-code.ts | 12 + .../admin-ui/ui/src/utils/get-combinations.js | 26 + .../admin-ui/ui/src/utils/get-error-status.ts | 9 + .../ui/src/utils/get-relative-time.ts | 38 + .../ui/src/utils/handle-form-error.tsx | 65 + packages/admin-ui/ui/src/utils/images.ts | 33 + .../admin-ui/ui/src/utils/is-line-item.ts | 37 + .../ui/src/utils/is-nullish-object.ts | 14 + .../ui/src/utils/map-address-to-form.ts | 23 + packages/admin-ui/ui/src/utils/nested-form.ts | 83 + .../ui/src/utils/payment-providers-mapper.ts | 73 + packages/admin-ui/ui/src/utils/prices.ts | 153 + .../ui/src/utils/product-status-variant.ts | 13 + .../admin-ui/ui/src/utils/remove-nullish.ts | 2 + .../utils/sales-channel-compare-operator.ts | 15 + packages/admin-ui/ui/src/utils/time.js | 58 + packages/admin-ui/ui/src/utils/totals.js | 15 + packages/admin-ui/ui/src/utils/trim-values.ts | 11 + .../admin-ui/ui/src/utils/validate-email.ts | 7 + .../admin-ui/ui/src/utils/xorObjFields.ts | 4 + packages/admin-ui/ui/src/vite-env.d.ts | 3 + packages/admin-ui/ui/tailwind.config.js | 365 ++ .../admin-ui/ui/test/fixtures/fixtures.json | 1274 +++++ packages/admin-ui/ui/test/fixtures/index.ts | 27 + .../admin-ui/ui/test/mocks/medusa-react.tsx | 49 + packages/admin-ui/ui/test/setup.ts | 19 + .../ui/test/utils/render-with-providers.tsx | 27 + packages/admin-ui/ui/tsconfig.json | 21 + packages/admin-ui/vite.config.dev.ts | 26 + packages/admin/.gitignore | 29 + packages/admin/.npmignore | 2 + packages/admin/README.md | 108 + packages/admin/bin/medusa-admin.js | 2 + packages/admin/package.json | 45 + packages/admin/src/api/index.ts | 78 + packages/admin/src/commands/build.ts | 48 + packages/admin/src/commands/create-cli.ts | 15 + packages/admin/src/commands/index.ts | 8 + packages/admin/src/setup/index.ts | 84 + packages/admin/src/types/index.ts | 36 + packages/admin/src/utils/index.ts | 3 + packages/admin/src/utils/load-config.ts | 32 + packages/admin/src/utils/reporter.ts | 19 + packages/admin/src/utils/validate-path.ts | 15 + packages/admin/tsconfig.json | 19 + packages/medusa-payment-paypal/package.json | 1 + packages/medusa-react/jest.config.js | 1 - packages/medusa-react/jest.setup.js | 26 +- packages/medusa-react/package.json | 8 +- .../test/hooks/admin/auth/mutations.test.ts | 4 +- .../test/hooks/admin/auth/queries.test.ts | 4 +- .../hooks/admin/batch-jobs/mutations.test.ts | 2 +- .../hooks/admin/batch-jobs/queries.test.ts | 2 +- .../test/hooks/admin/claims/mutations.test.ts | 10 +- .../hooks/admin/collections/mutations.test.ts | 18 +- .../hooks/admin/collections/queries.test.ts | 4 +- .../hooks/admin/currencies/mutation.test.ts | 2 +- .../hooks/admin/currencies/queries.test.ts | 2 +- .../admin/customer-groups/mutations.test.ts | 4 +- .../admin/customer-groups/queries.test.ts | 4 +- .../hooks/admin/customers/mutations.test.ts | 4 +- .../hooks/admin/customers/queries.test.ts | 4 +- .../hooks/admin/discounts/mutations.test.ts | 2 +- .../hooks/admin/discounts/queries.test.ts | 2 +- .../admin/draft-orders/mutations.test.ts | 2 +- .../hooks/admin/draft-orders/queries.test.ts | 4 +- .../hooks/admin/gift-cards/mutations.test.ts | 6 +- .../hooks/admin/gift-cards/queries.test.ts | 4 +- .../admin/inventory-items/mutations.test.ts | 10 +- .../admin/inventory-items/queries.test.ts | 5 +- .../hooks/admin/invites/mutations.test.ts | 2 +- .../test/hooks/admin/invites/queries.test.ts | 4 +- .../test/hooks/admin/notes/mutations.test.ts | 6 +- .../test/hooks/admin/notes/queries.test.ts | 4 +- .../admin/notifications/mutations.test.ts | 4 +- .../hooks/admin/notifications/queries.test.ts | 2 +- .../hooks/admin/order-edits/mutations.test.ts | 8 +- .../hooks/admin/order-edits/queries.test.ts | 2 +- .../test/hooks/admin/orders/mutations.test.ts | 18 +- .../test/hooks/admin/orders/queries.test.ts | 4 +- .../payment-collections/mutations.test.ts | 4 +- .../admin/payment-collections/queries.test.ts | 4 +- .../hooks/admin/payments/mutations.test.ts | 4 +- .../test/hooks/admin/payments/queries.test.ts | 4 +- .../hooks/admin/price-lists/mutations.test.ts | 46 +- .../hooks/admin/price-lists/queries.test.ts | 4 +- .../product-categories/mutations.test.ts | 4 +- .../admin/product-categories/queries.test.ts | 7 +- .../hooks/admin/products/mutations.test.ts | 8 +- .../test/hooks/admin/products/queries.test.ts | 5 +- .../publishable-api-keys/mutations.test.ts | 10 +- .../publishable-api-keys/queries.test.ts | 4 +- .../hooks/admin/regions/mutations.test.ts | 8 +- .../test/hooks/admin/regions/queries.test.ts | 6 +- .../admin/reservations/mutations.test.ts | 9 +- .../hooks/admin/reservations/queries.test.ts | 4 +- .../admin/return-reasons/mutations.test.ts | 6 +- .../admin/return-reasons/queries.test.ts | 4 +- .../hooks/admin/returns/mutations.test.ts | 2 +- .../test/hooks/admin/returns/queries.test.ts | 2 +- .../admin/sales-channels/mutations.test.ts | 10 +- .../admin/sales-channels/queries.test.ts | 4 +- .../admin/shipping-options/mutations.test.ts | 6 +- .../admin/shipping-options/queries.test.ts | 4 +- .../admin/shipping-profiles/mutations.test.ts | 6 +- .../admin/shipping-profiles/queries.test.ts | 4 +- .../admin/stock-location/mutations.test.ts | 2 +- .../admin/stock-location/queries.test.ts | 2 +- .../test/hooks/admin/store/mutations.test.ts | 6 +- .../test/hooks/admin/store/queries.test.ts | 4 +- .../test/hooks/admin/swaps/mutations.test.ts | 4 +- .../test/hooks/admin/swaps/queries.test.ts | 4 +- .../hooks/admin/uploads/mutations.test.ts | 4 +- .../test/hooks/admin/users/mutations.test.ts | 6 +- .../test/hooks/admin/users/queries.test.ts | 4 +- .../hooks/admin/variants/mutations.test.ts | 6 +- .../test/hooks/admin/variants/queries.test.ts | 4 +- .../test/hooks/store/carts/mutations.test.ts | 14 +- .../test/hooks/store/carts/queries.test.ts | 4 +- .../hooks/store/collections/queries.test.ts | 4 +- .../hooks/store/customers/mutations.test.ts | 4 +- .../hooks/store/customers/queries.test.ts | 6 +- .../hooks/store/gift_cards/queries.test.ts | 4 +- .../hooks/store/line-items/mutations.test.ts | 6 +- .../hooks/store/order-edits/mutations.test.ts | 2 +- .../hooks/store/order-edits/queries.test.ts | 4 +- .../test/hooks/store/orders/mutations.test.ts | 4 +- .../test/hooks/store/orders/queries.test.ts | 6 +- .../payment-collections/mutations.test.ts | 6 +- .../store/payment-collections/queries.test.ts | 4 +- .../hooks/store/product-tags/queries.test.ts | 2 +- .../test/hooks/store/products/queries.test.ts | 6 +- .../test/hooks/store/regions/queries.test.ts | 4 +- .../store/return-reasons/queries.test.ts | 4 +- .../hooks/store/returns/mutations.test.ts | 4 +- .../store/shipping-options/queries.test.ts | 6 +- .../test/hooks/store/swaps/mutations.test.ts | 4 +- .../test/hooks/store/swaps/queries.test.ts | 4 +- packages/medusa-react/tsup.config.ts | 2 + packages/medusa-source-shopify/package.json | 2 +- packages/medusa/src/loaders/plugins.ts | 30 +- yarn.lock | 4438 ++++++++++++++++- 928 files changed, 85430 insertions(+), 373 deletions(-) create mode 100644 .changeset/sixty-jeans-fold.md create mode 100644 packages/admin-ui/.gitignore create mode 100644 packages/admin-ui/.npmignore create mode 100644 packages/admin-ui/README.md create mode 100644 packages/admin-ui/package.json create mode 100644 packages/admin-ui/src/index.ts create mode 100644 packages/admin-ui/src/types/build.ts create mode 100644 packages/admin-ui/src/types/config.ts create mode 100644 packages/admin-ui/src/types/index.ts create mode 100644 packages/admin-ui/src/types/misc.ts create mode 100644 packages/admin-ui/src/utils/format-base.ts create mode 100644 packages/admin-ui/src/utils/get-custom-vite-config.ts create mode 100644 packages/admin-ui/src/utils/index.ts create mode 100644 packages/admin-ui/tsconfig.json create mode 100644 packages/admin-ui/ui/index.html create mode 100644 packages/admin-ui/ui/postcss.config.js create mode 100644 packages/admin-ui/ui/public/logo.svg create mode 100644 packages/admin-ui/ui/src/App.tsx create mode 100644 packages/admin-ui/ui/src/assets/styles/emoji-picker.css create mode 100644 packages/admin-ui/ui/src/assets/styles/global.css create mode 100644 packages/admin-ui/ui/src/assets/svg/carrot.svg create mode 100644 packages/admin-ui/ui/src/assets/svg/controller.svg create mode 100644 packages/admin-ui/ui/src/assets/svg/flag.svg create mode 100644 packages/admin-ui/ui/src/assets/svg/happy.svg create mode 100644 packages/admin-ui/ui/src/assets/svg/heart.svg create mode 100644 packages/admin-ui/ui/src/assets/svg/lightbulb.svg create mode 100644 packages/admin-ui/ui/src/assets/svg/plane.svg create mode 100644 packages/admin-ui/ui/src/assets/svg/search.svg create mode 100644 packages/admin-ui/ui/src/assets/svg/sprout.svg create mode 100644 packages/admin-ui/ui/src/assets/svg/star.svg create mode 100644 packages/admin-ui/ui/src/components/atoms/avatar/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/back-button/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/checkbox/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/copy-to-clipboard/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/date-picker/custom-header.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/date-picker/date-picker.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/date-picker/time-picker.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/date-picker/types.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/date-picker/utils.ts create mode 100644 packages/admin-ui/ui/src/components/atoms/fade-wrapper/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/file-upload-field/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/includes-tax-tooltip/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/input-error/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/loading-container/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/notification/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/number-scroller/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/os-shortcut/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/page-description/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/save-notification/error-state.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/save-notification/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/save-notification/saving-state.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/save-notification/success-state.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/settings-card/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/skeleton/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/spinner.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/switch/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/text-input/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/thumbnail/index.ts create mode 100644 packages/admin-ui/ui/src/components/atoms/thumbnail/thumbnail.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/toaster-container/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/tooltip/index.tsx create mode 100644 packages/admin-ui/ui/src/components/atoms/two-step-delete/index.tsx create mode 100644 packages/admin-ui/ui/src/components/declarative-toaster/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/badge/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/button/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/details-icon/contact.svg create mode 100644 packages/admin-ui/ui/src/components/fundamentals/details-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/feature-toggle.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icon-badge/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/alert-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/arrow-down-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/arrow-left-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/arrow-right-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/arrow-top-right-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/arrow-up-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/back-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/backspace-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/bell-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/bell-noti-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/bell-off-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/buildings-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/cancel-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/cart-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/cash-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/channels-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/check-circle-fill-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/check-circle-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/check-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/chevron-down.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/chevron-left-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/chevron-right-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/chevron-up.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/clock-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/coins-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/corner-down-right-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/cross-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/crosshair-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/customer-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/details-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/discord-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/dollar-sign-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/down-left/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/download-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/duplicate-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/edit-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/edit-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/export-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/eye-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/eye-off-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/fast-delivery-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/file-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/gear-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/gift-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/grip-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/happy-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/help-circle.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/image-placeholder-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/info-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/key-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/list-arrow-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/list-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/lock-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/log-out-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/long-arrow-right-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/mail-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/map-pin-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/medusa-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/medusa-vice/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/minus-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/more-horizontal-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/package-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/percent-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/plus-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/pointer-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/publish-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/refresh-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/refund.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/sad-face-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/sale-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/search-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/send-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/sided-mouth-face/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/sorting-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/sparkles-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/stop-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/tag-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/taxes-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/tile-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/trash-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/triangle-right-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/truck-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/types/icon-type.ts create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/u-turn-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/unpublish-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/upload-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/users-icon/index.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/warning-circle.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/icons/x-circle-icon.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/image-placeholder.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/input-container.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/input-header.tsx create mode 100644 packages/admin-ui/ui/src/components/fundamentals/status-indicator/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/actionables.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/activity-card/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/amount-input/amount-and-currency-input.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/amount-input/amount-input.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/amount-input/index.ts create mode 100644 packages/admin-ui/ui/src/components/molecules/availability-duration/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/banner-card/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/batch-job-file-card/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/breadcrumb/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/collapsible-tree/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/connected-form.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/customer-avatar-item/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/customers-groups-summary/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/emoji-picker/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/filter-dropdown/container.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/filter-dropdown/item.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/filter-dropdown/save-field.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/filter-tab/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/form-error-toaster/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/form-toaster/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/generating-input/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/grid-input/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/hot-key-action/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/icon-tooltip/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/indeterminate-checkbox/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/input-signin/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/input/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/json-view/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/modal/focus-modal.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/modal/layered-modal.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/modal/side-modal.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/modal/stepped-modal.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/native-select/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/note-input/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/notification-bell/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/numbered-item/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/order-status/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/rma-select-shipping/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/sales-channels-display/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/sales-channels-list/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/sales-channels-summary/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/search-bar.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/section/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/components/containers.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/components/control.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/components/index.ts create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/components/input.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/components/menu.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/components/multi-value.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/components/placeholder.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/components/select-primitives.ts create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/components/single-value.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/createable-select/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/module-augmentation.ts create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/select/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/use-select-props.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/next-select/utils.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/select/select-components.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/shipping-option/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/sidebar-company-logo/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/sidebar-menu-item/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/sidebar-team-member/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/status-selector/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/switchable-item/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/table-fileds-filter/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/table-pagination/index.ts create mode 100644 packages/admin-ui/ui/src/components/molecules/table-pagination/table-pagination.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/table-toaster/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/table/filtering-option.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/table/table-search.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/tag-grid.tsx/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/tag-input/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/textarea/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/claim-event/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/event-actionables.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/event-container.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/event-item-container.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/event-type.ts create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/exchange.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/items-fulfilled.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/items-shipped.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/note.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/notification/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/notification/resend-modal.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-canceled.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-edit/canceled.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-edit/confirmed.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-edit/created.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-edit/declined.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-edit/index.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-edit/payment-required.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-edit/refund-required.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-edit/requested.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/order-placed.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/refund.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/timeline-events/return.tsx create mode 100644 packages/admin-ui/ui/src/components/molecules/user-menu/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/accordion/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/activity-drawer/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/add-denomination-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/add-denomination-modal/use-values-field-array.ts create mode 100644 packages/admin-ui/ui/src/components/organisms/analytics-config-form/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/analytics-preferences/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/batch-jobs-activity-list/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/batch-jobs-activity-list/utils.ts create mode 100644 packages/admin-ui/ui/src/components/organisms/body-card.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/confirmation-prompt.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/currency-input/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/custom-table-header/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/delete-prompt.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/details-collapsible/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/edit-denominations-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/edit-user-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/error-boundary/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/export-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/file-upload-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/gift-card-banner/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/help-dialog/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/invite-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/login-card/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/medusa-price-input/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/metadata/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/price-input/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/product-variant-tree/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/radio-group/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/raw-json/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/reset-token-card/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/rma-return-product-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/rma-select-product-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/section/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/sidebar/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/table-container/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/table-container/pagination.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/table-container/types.ts create mode 100644 packages/admin-ui/ui/src/components/organisms/timeline/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/topbar/index.tsx create mode 100644 packages/admin-ui/ui/src/components/organisms/upload-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/private-route/index.tsx create mode 100644 packages/admin-ui/ui/src/components/seo/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/add-products-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/add-products-modal/product-table-config.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/add-products-modal/utils.ts create mode 100644 packages/admin-ui/ui/src/components/templates/address-form.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collection-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collection-product-table/add-product-table.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collection-product-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collection-product-table/types.ts create mode 100644 packages/admin-ui/ui/src/components/templates/collection-product-table/use-collection-product-columns.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collection-product-table/use-sorting-options.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collection-product-table/use-view-product-columns.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collection-product-table/utils.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collection-product-table/view-products-table.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collections-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collections-table/use-collection-actions.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/collections-table/use-collection-column.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/customer-group-table/config.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/customer-group-table/customer-groups-table.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/customer-group-table/customers-list-table.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/customer-group-table/edit-customers-table.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/customer-orders-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/customer-orders-table/use-customer-orders-columns.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/customer-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/customer-table/use-customer-columns.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/customer-table/use-customer-filters.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/discount-filter-dropdown/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/discount-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/discount-table/use-copy-promotion.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/discount-table/use-promotion-columns.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/discount-table/use-promotion-filters.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/discount-table/use-promotion-row-actions.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/draft-order-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/draft-order-table/use-draft-order-column.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/draft-order-table/use-draft-order-filters.ts create mode 100644 packages/admin-ui/ui/src/components/templates/gift-card-filter-dropdown/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/gift-card-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/gift-card-table/use-gift-card-column.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/gift-card-table/use-gift-card-filters.ts create mode 100644 packages/admin-ui/ui/src/components/templates/image-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/layout.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/login-layout.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/order-filter-dropdown/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/order-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/order-table/use-order-column.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/order-table/use-order-filters.ts create mode 100644 packages/admin-ui/ui/src/components/templates/price-list-table/price-list-filters.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/price-list-table/price-list-table.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/price-list-table/use-copy-price-list.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/price-list-table/use-price-list-actions.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/price-list-table/use-price-list-columns.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/price-list-table/use-price-list-filters.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/price-list-table/utils.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/price-overrides/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/price-overrides/price-amount.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/product-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/product-table/overview.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/product-table/use-copy-product.ts create mode 100644 packages/admin-ui/ui/src/components/templates/product-table/use-filter-tabs.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/product-table/use-product-actions.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/product-table/use-product-column.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/product-table/use-product-filters.ts create mode 100644 packages/admin-ui/ui/src/components/templates/search-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/search-modal/keyboard-shortcuts.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/search-modal/results/customer-results.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/search-modal/results/discount-results.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/search-modal/results/order-results.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/search-modal/results/product-results.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/search-modal/section-collapsible.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/search-modal/use-keyboard-navigation-list.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/selectable-table/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/settings-overview.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/transfer-orders-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/two-split-pane.tsx create mode 100644 packages/admin-ui/ui/src/components/templates/user-table.tsx create mode 100644 packages/admin-ui/ui/src/constants/analytics.ts create mode 100644 packages/admin-ui/ui/src/constants/medusa-backend-url.ts create mode 100644 packages/admin-ui/ui/src/constants/query-client.ts create mode 100644 packages/admin-ui/ui/src/domain/collections/details/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/collections/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/customers/details/edit.tsx create mode 100644 packages/admin-ui/ui/src/domain/customers/details/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/customers/groups/context/customer-group-context.tsx create mode 100644 packages/admin-ui/ui/src/domain/customers/groups/customer-group-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/customers/groups/details.tsx create mode 100644 packages/admin-ui/ui/src/domain/customers/groups/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/customers/header.tsx create mode 100644 packages/admin-ui/ui/src/domain/customers/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/add-condition/conditions-provider.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/add-condition/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/collections/add-collections.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/collections/collections-conditions-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/customer-groups/add-customer-groups.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/customer-groups/customer-groups-conditions-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/product-types/add-types.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/product-types/type-conditions-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/products/add-products.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/products/product-conditions-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/tags/add-tags.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-condition-resources/tags/tags-conditions-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/add-conditions-screens.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/condition-table-actions.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/edit-condition-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/edit-condition/edit-condition-provider.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/conditions/use-discount-conditions.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/configurations/edit-configurations.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/configurations/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/configurations/use-discount-configurations.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/general/edit-general.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/general/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/details/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/add-conditions-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/add-condition-tables/add-condition-footer.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/add-condition-tables/collections.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/add-condition-tables/customer-groups.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/add-condition-tables/products.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/add-condition-tables/tags.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/add-condition-tables/types.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/details-condition-tables/collections.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/details-condition-tables/customer-groups.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/details-condition-tables/details-condition-footer.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/details-condition-tables/products.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/details-condition-tables/tags.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/details-condition-tables/types.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/edit-condition-tables/collections.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/edit-condition-tables/customer-groups.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/edit-condition-tables/edit-condition-footer.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/edit-condition-tables/products.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/edit-condition-tables/tags.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/edit-condition-tables/types.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/shared/collection.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/shared/common.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/shared/condition-operator.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/shared/groups.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/shared/products.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/shared/tags.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/condition-tables/shared/types.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/edit-conditions-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/form/discount-form-context.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/form/mappers.ts create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/form/use-form-actions.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/sections/conditions/condition-item.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/sections/conditions/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/sections/configuration.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/sections/discount-allocation.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/sections/discount-type.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/sections/general.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/discount-form/use-condition-modal-items.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/new/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/discounts/types.ts create mode 100644 packages/admin-ui/ui/src/domain/discounts/utils/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/custom-giftcard.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/details/edit-gift-card-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/details/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/details/update-balance-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/manage/denomination-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/manage/form/gift-card-form-context.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/manage/form/mappers.ts create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/manage/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/manage/sections/denominations.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/manage/sections/images.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/manage/sections/information.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/manage/utils/types.ts create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/new.tsx create mode 100644 packages/admin-ui/ui/src/domain/gift-cards/overview.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/header.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/inventory/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/locations/components/address-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/locations/components/edit-sales-channels/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/locations/components/general-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/locations/components/location-card/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/locations/components/sales-channels-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/locations/components/sales-channels-section/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/locations/edit/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/locations/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/inventory/locations/new/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/oauth/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/claim-type-form/__tests__/claim-type-form.test.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/claim-type-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-receive-form/__tests__/items-to-receive-form.test.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-receive-form/index.ts create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-receive-form/items-to-receive-form.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-receive-form/items-to-receive-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-receive-form/use-items-to-receive-columns.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-return-form/__tests__/items-to-return-form.test.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-return-form/add-return-reason/add-return-reason-screen.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-return-form/add-return-reason/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-return-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-return-form/items-to-return-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-return-form/use-return-item-columns.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-send-form/__tests__/items-to-send-form.test.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-send-form/add-additional-items-screen/add-additional-items-screen.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-send-form/add-additional-items-screen/add-additional-items-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-send-form/add-additional-items-screen/index.ts create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-send-form/add-additional-items-screen/use-add-additional-items-columns.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-send-form/additional-items-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-send-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/items-to-send-form/use-additional-items-columns.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/refund-amount-form/__tests__/refund-amount-form.test.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/refund-amount-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/rma-summaries/__tests__/claim-summary.test.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/rma-summaries/claim-summary.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/rma-summaries/index.ts create mode 100644 packages/admin-ui/ui/src/domain/orders/components/rma-summaries/receive-return-summary.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/rma-summaries/summary-line-item.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/rma-summaries/summary-shipping-line.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/send-notification-form/__tests__/send-notification-form.test.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/send-notification-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/shipping-address-form/__tests__/shipping-address-form.test.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/shipping-address-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/shipping-address-form/shipping-address-form-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/shipping-form/__tests__/shipping-form.test.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/shipping-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/components/table-quantity-selector.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/address-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/claim/register-claim-menu.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/create-fulfillment/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/create-fulfillment/item-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/email-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/mark-shipped/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/order-line/edit.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/order-line/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/receive-return/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/refund/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/returns/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/rma-sub-modals/address.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/rma-sub-modals/products.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/rma-sub-modals/return-reasons.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/swap/create.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/address.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/display-total.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/fulfillment-status.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/fulfillment.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/index.ts create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/order-status.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/payment-actionables.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/payment-details.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/payment-status.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/templates/tracking-link.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/details/utils/create-filtering.ts create mode 100644 packages/admin-ui/ui/src/domain/orders/details/utils/get-default-values.ts create mode 100644 packages/admin-ui/ui/src/domain/orders/details/utils/order-returnable-fields.ts create mode 100644 packages/admin-ui/ui/src/domain/orders/details/utils/use-admin-expand-paramter.ts create mode 100644 packages/admin-ui/ui/src/domain/orders/draft-orders/details.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/draft-orders/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/edit/context.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/edit/modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/edit/utils/user.ts create mode 100644 packages/admin-ui/ui/src/domain/orders/edit/variants-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/new/components/billing-details.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/new/components/custom-item-sub-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/new/components/items.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/new/components/select-region.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/new/components/select-shipping.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/new/components/shipping-details.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/new/components/summary.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/new/form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/new/new-order.tsx create mode 100644 packages/admin-ui/ui/src/domain/orders/utils.ts create mode 100644 packages/admin-ui/ui/src/domain/pricing/batch-job/import.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/sections/header.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/sections/prices-details/edit-prices-overrides/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/sections/prices-details/edit-prices-overrides/mappers.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/sections/prices-details/edit-prices-overrides/product-variant-leaf.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/sections/prices-details/edit-prices.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/sections/prices-details/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/sections/prices-details/prices-table/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/sections/prices-details/prices-table/use-columns.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/sections/prices-details/utils.ts create mode 100644 packages/admin-ui/ui/src/domain/pricing/details/utils.ts create mode 100644 packages/admin-ui/ui/src/domain/pricing/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/new.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/form-header/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/form/mappers.ts create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/form/pricing-form-context.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/sections/configuration.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/sections/general.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/sections/prices.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/sections/product-prices.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/sections/type.tsx create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-form/types/index.ts create mode 100644 packages/admin-ui/ui/src/domain/pricing/pricing-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/batch-job/import.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/customs-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/dimensions-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/discountable-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/general-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/media-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/organize-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/organize-form/use-organize-data.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/prices-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/prices-form/nested-price.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/prices-form/price-form-input.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/sales-channels-modal/add-screen.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/sales-channels-modal/available-screen.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/sales-channels-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/sales-channels-modal/table.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/sales-channels-modal/use-sales-channels-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/thumbnail-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/variant-form/create-flow-variant-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/variant-form/edit-flow-variant-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/variant-form/variant-general-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/variant-form/variant-prices-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/variant-form/variant-select-options-form/hooks.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/variant-form/variant-select-options-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/components/variant-form/variant-stock-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/hooks/use-edit-product-actions.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/attributes/attribute-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/attributes/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/general/channels-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/general/general-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/general/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/media/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/media/media-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/raw/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/thumbnail/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/thumbnail/thumbnail-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/add-variant-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/edit-variant-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/edit-variants-modal/edit-variant-screen.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/edit-variants-modal/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/edit-variants-modal/use-edit-variants-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/edit-variants-modal/variant-card.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/options-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/options-provider.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/edit/sections/variants/table.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/filter-dropdown.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/new/add-sales-channels.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/new/add-variants/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/new/add-variants/new-variant/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/new/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/products/overview/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/publishable-api-keys/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/publishable-api-keys/modals/add-sales-channels.tsx create mode 100644 packages/admin-ui/ui/src/domain/publishable-api-keys/modals/details.tsx create mode 100644 packages/admin-ui/ui/src/domain/publishable-api-keys/modals/manage-sales-channels.tsx create mode 100644 packages/admin-ui/ui/src/domain/publishable-api-keys/pages/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/publishable-api-keys/tables/publishable-api-keys-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/publishable-api-keys/tables/sales-channels-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/sales-channels/form/add-sales-channel.tsx create mode 100644 packages/admin-ui/ui/src/domain/sales-channels/form/edit-sales-channel.tsx create mode 100644 packages/admin-ui/ui/src/domain/sales-channels/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/sales-channels/pages/details.tsx create mode 100644 packages/admin-ui/ui/src/domain/sales-channels/tables/config.tsx create mode 100644 packages/admin-ui/ui/src/domain/sales-channels/tables/placeholder.tsx create mode 100644 packages/admin-ui/ui/src/domain/sales-channels/tables/product.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/components/currency-tax-setting.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/components/default-store-currency/default-currency-selector.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/components/default-store-currency/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/components/store-currencies/add-currencies-screen.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/components/store-currencies/current-currencies-screen.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/components/store-currencies/edit-currencies-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/components/store-currencies/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/components/store-currencies/table.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/components/store-currencies/use-currency-table-columns.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/currencies/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/details.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/personal-information/edit-user-information/edit-user-information-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/personal-information/edit-user-information/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/personal-information/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/personal-information/usage-insights/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/personal-information/usage-insights/usage-insights-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/components/region-form/region-details-form.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/components/region-form/region-providers-form.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/components/region-form/use-store-data.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/components/shipping-option-card/edit-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/components/shipping-option-card/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/components/shipping-option-form/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/components/shipping-option-form/use-shipping-option-form-data.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/edit/general-section/edit-region.modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/edit/general-section/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/edit/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/edit/return-shipping-options/create-return-shipping-option.modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/edit/return-shipping-options/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/edit/shipping-options/create-shipping-option-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/edit/shipping-options/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/new/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/region-overview/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/regions/region-overview/region-card.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/return-reasons/create-reason-modal.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/return-reasons/detail.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/return-reasons/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/details.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/edit-form.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/edit-tax-rate-details.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/edit.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/index.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/new.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/product-selector.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/product-type-selector.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/region-form.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/selectable-table.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/shipping-option-selector.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/tax-rate-row.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/tax-rule-item.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/tax-rule-selector.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/taxes/use-tax-rate-columns.tsx create mode 100644 packages/admin-ui/ui/src/domain/settings/users/index.tsx create mode 100644 packages/admin-ui/ui/src/fonts/Inter-Regular.ttf create mode 100644 packages/admin-ui/ui/src/fonts/Inter-SemiBold.ttf create mode 100644 packages/admin-ui/ui/src/fonts/RobotoMono-Bold.ttf create mode 100644 packages/admin-ui/ui/src/fonts/RobotoMono-Regular.ttf create mode 100644 packages/admin-ui/ui/src/hooks/use-build-timeline.tsx create mode 100644 packages/admin-ui/ui/src/hooks/use-clipboard.js create mode 100644 packages/admin-ui/ui/src/hooks/use-computed-height.ts create mode 100644 packages/admin-ui/ui/src/hooks/use-debounce.ts create mode 100644 packages/admin-ui/ui/src/hooks/use-detect-change.tsx create mode 100644 packages/admin-ui/ui/src/hooks/use-highlight-search.tsx create mode 100644 packages/admin-ui/ui/src/hooks/use-imperative-dialog.tsx create mode 100644 packages/admin-ui/ui/src/hooks/use-is-me.tsx create mode 100644 packages/admin-ui/ui/src/hooks/use-notification.tsx create mode 100644 packages/admin-ui/ui/src/hooks/use-observe-width.ts create mode 100644 packages/admin-ui/ui/src/hooks/use-on-click-outside.tsx create mode 100644 packages/admin-ui/ui/src/hooks/use-outside-click.ts create mode 100644 packages/admin-ui/ui/src/hooks/use-query-filters.ts create mode 100644 packages/admin-ui/ui/src/hooks/use-scroll.ts create mode 100644 packages/admin-ui/ui/src/hooks/use-selection-column.tsx create mode 100644 packages/admin-ui/ui/src/hooks/use-set-search-params.tsx create mode 100644 packages/admin-ui/ui/src/hooks/use-toggle-state.ts create mode 100644 packages/admin-ui/ui/src/hooks/use-window-dimensions.ts create mode 100644 packages/admin-ui/ui/src/index.css create mode 100644 packages/admin-ui/ui/src/main.tsx create mode 100644 packages/admin-ui/ui/src/medusa-app.tsx create mode 100644 packages/admin-ui/ui/src/pages/404.tsx create mode 100644 packages/admin-ui/ui/src/pages/a.tsx create mode 100644 packages/admin-ui/ui/src/pages/index.tsx create mode 100644 packages/admin-ui/ui/src/pages/invite.tsx create mode 100644 packages/admin-ui/ui/src/pages/login.tsx create mode 100644 packages/admin-ui/ui/src/pages/reset-password.tsx create mode 100644 packages/admin-ui/ui/src/providers/analytics-provider.tsx create mode 100644 packages/admin-ui/ui/src/providers/feature-flag-provider.tsx create mode 100644 packages/admin-ui/ui/src/providers/medusa-provider.tsx create mode 100644 packages/admin-ui/ui/src/providers/polling-provider.tsx create mode 100644 packages/admin-ui/ui/src/providers/providers.tsx create mode 100644 packages/admin-ui/ui/src/providers/skeleton-provider.tsx create mode 100644 packages/admin-ui/ui/src/services/analytics.ts create mode 100644 packages/admin-ui/ui/src/services/api.js create mode 100644 packages/admin-ui/ui/src/services/request.js create mode 100644 packages/admin-ui/ui/src/types/react-table-config.d.ts create mode 100644 packages/admin-ui/ui/src/types/shared.ts create mode 100644 packages/admin-ui/ui/src/types/utils.ts create mode 100644 packages/admin-ui/ui/src/utils/bytes-converter.ts create mode 100644 packages/admin-ui/ui/src/utils/callAll.ts create mode 100644 packages/admin-ui/ui/src/utils/color.ts create mode 100644 packages/admin-ui/ui/src/utils/consolidate-images.ts create mode 100644 packages/admin-ui/ui/src/utils/convert-empty-string-to-null.js create mode 100644 packages/admin-ui/ui/src/utils/countries.ts create mode 100644 packages/admin-ui/ui/src/utils/currencies.ts create mode 100644 packages/admin-ui/ui/src/utils/date-utils.ts create mode 100644 packages/admin-ui/ui/src/utils/decide-badge-color.js create mode 100644 packages/admin-ui/ui/src/utils/equals-set.ts create mode 100644 packages/admin-ui/ui/src/utils/error-messages.ts create mode 100644 packages/admin-ui/ui/src/utils/extract-customer-name.ts create mode 100644 packages/admin-ui/ui/src/utils/extract-options.ts create mode 100644 packages/admin-ui/ui/src/utils/filters.js create mode 100644 packages/admin-ui/ui/src/utils/focus-by-name.ts create mode 100644 packages/admin-ui/ui/src/utils/form-helpers.ts create mode 100644 packages/admin-ui/ui/src/utils/form-validator.ts create mode 100644 packages/admin-ui/ui/src/utils/format-address.js create mode 100644 packages/admin-ui/ui/src/utils/fulfillment-providers.mapper.ts create mode 100644 packages/admin-ui/ui/src/utils/generate-promotion-code.ts create mode 100644 packages/admin-ui/ui/src/utils/get-combinations.js create mode 100644 packages/admin-ui/ui/src/utils/get-error-status.ts create mode 100644 packages/admin-ui/ui/src/utils/get-relative-time.ts create mode 100644 packages/admin-ui/ui/src/utils/handle-form-error.tsx create mode 100644 packages/admin-ui/ui/src/utils/images.ts create mode 100644 packages/admin-ui/ui/src/utils/is-line-item.ts create mode 100644 packages/admin-ui/ui/src/utils/is-nullish-object.ts create mode 100644 packages/admin-ui/ui/src/utils/map-address-to-form.ts create mode 100644 packages/admin-ui/ui/src/utils/nested-form.ts create mode 100644 packages/admin-ui/ui/src/utils/payment-providers-mapper.ts create mode 100644 packages/admin-ui/ui/src/utils/prices.ts create mode 100644 packages/admin-ui/ui/src/utils/product-status-variant.ts create mode 100644 packages/admin-ui/ui/src/utils/remove-nullish.ts create mode 100644 packages/admin-ui/ui/src/utils/sales-channel-compare-operator.ts create mode 100644 packages/admin-ui/ui/src/utils/time.js create mode 100644 packages/admin-ui/ui/src/utils/totals.js create mode 100644 packages/admin-ui/ui/src/utils/trim-values.ts create mode 100644 packages/admin-ui/ui/src/utils/validate-email.ts create mode 100644 packages/admin-ui/ui/src/utils/xorObjFields.ts create mode 100644 packages/admin-ui/ui/src/vite-env.d.ts create mode 100644 packages/admin-ui/ui/tailwind.config.js create mode 100644 packages/admin-ui/ui/test/fixtures/fixtures.json create mode 100644 packages/admin-ui/ui/test/fixtures/index.ts create mode 100644 packages/admin-ui/ui/test/mocks/medusa-react.tsx create mode 100644 packages/admin-ui/ui/test/setup.ts create mode 100644 packages/admin-ui/ui/test/utils/render-with-providers.tsx create mode 100644 packages/admin-ui/ui/tsconfig.json create mode 100644 packages/admin-ui/vite.config.dev.ts create mode 100644 packages/admin/.gitignore create mode 100644 packages/admin/.npmignore create mode 100644 packages/admin/README.md create mode 100755 packages/admin/bin/medusa-admin.js create mode 100644 packages/admin/package.json create mode 100644 packages/admin/src/api/index.ts create mode 100644 packages/admin/src/commands/build.ts create mode 100644 packages/admin/src/commands/create-cli.ts create mode 100644 packages/admin/src/commands/index.ts create mode 100644 packages/admin/src/setup/index.ts create mode 100644 packages/admin/src/types/index.ts create mode 100644 packages/admin/src/utils/index.ts create mode 100644 packages/admin/src/utils/load-config.ts create mode 100644 packages/admin/src/utils/reporter.ts create mode 100644 packages/admin/src/utils/validate-path.ts create mode 100644 packages/admin/tsconfig.json diff --git a/.changeset/sixty-jeans-fold.md b/.changeset/sixty-jeans-fold.md new file mode 100644 index 0000000000000..fad382fbe9eb9 --- /dev/null +++ b/.changeset/sixty-jeans-fold.md @@ -0,0 +1,7 @@ +--- +"@medusajs/admin-ui": patch +"@medusajs/medusa": patch +"@medusajs/admin": patch +--- + +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. diff --git a/.eslintignore b/.eslintignore index 201dccdc07700..7c26663d157ed 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,6 +7,8 @@ jest* packages/* # List of packages to Lint !packages/medusa +!packages/admin-ui +!packages/admin !packages/medusa-payment-stripe diff --git a/.eslintrc.js b/.eslintrc.js index 9893804579d03..49f7bc6f009c9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -83,7 +83,8 @@ module.exports = { project: [ "./packages/medusa/tsconfig.json", "./packages/medusa-payment-stripe/tsconfig.spec.json", - ] + "./packages/admin-ui/tsconfig.json", + ], }, rules: { "valid-jsdoc": "off", @@ -111,5 +112,58 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", }, }, + { + 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", + }, + 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: ["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", + }, + }, ], } diff --git a/.prettierrc b/.prettierrc index aa4d695476ae2..3718cf898655b 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,5 +4,13 @@ "singleQuote": false, "tabWidth": 2, "trailingComma": "es5", - "arrowParens": "always" + "arrowParens": "always", + "overrides": [ + { + "files": "./packages/admin-ui/**/*.{js,jsx,ts,tsx}", + "options": { + "plugins": ["prettier-plugin-tailwindcss"] + } + } + ] } diff --git a/integration-tests/api/package.json b/integration-tests/api/package.json index f2f39f27f467a..569e097737397 100644 --- a/integration-tests/api/package.json +++ b/integration-tests/api/package.json @@ -19,6 +19,7 @@ "@babel/core": "^7.12.10", "@babel/node": "^7.12.10", "babel-preset-medusa-package": "*", - "jest": "^26.6.3" + "jest": "^26.6.3", + "jest-environment-node": "26.6.2" } } diff --git a/integration-tests/plugins/package.json b/integration-tests/plugins/package.json index 500f22163172d..320e2a4369da6 100644 --- a/integration-tests/plugins/package.json +++ b/integration-tests/plugins/package.json @@ -21,6 +21,7 @@ "@babel/core": "^7.12.10", "@babel/node": "^7.12.10", "babel-preset-medusa-package": "*", - "jest": "^26.6.3" + "jest": "^26.6.3", + "jest-environment-node": "26.6.2" } } diff --git a/integration-tests/repositories/package.json b/integration-tests/repositories/package.json index 846ba465e8460..c279b788a8408 100644 --- a/integration-tests/repositories/package.json +++ b/integration-tests/repositories/package.json @@ -17,6 +17,7 @@ "@babel/core": "^7.12.10", "@babel/node": "^7.12.10", "babel-preset-medusa-package": "*", - "jest": "^26.6.3" + "jest": "^26.6.3", + "jest-environment-node": "26.6.2" } } diff --git a/package.json b/package.json index fb1b0d07161fe..a3be3300441cd 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,8 @@ "@medusajs/medusa-oas-cli": "*", "@readme/openapi-parser": "^2.4.0", "@redocly/cli": "1.0.0-beta.123", - "@typescript-eslint/eslint-plugin": "^5.36.2", - "@typescript-eslint/parser": "^5.36.2", + "@typescript-eslint/eslint-plugin": "^5.53.0", + "@typescript-eslint/parser": "^5.53.0", "axios": "^0.21.4", "axios-mock-adapter": "^1.19.0", "babel-jest": "^26.6.3", @@ -36,6 +36,8 @@ "eslint-plugin-markdown": "^3.0.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.31.11", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-unused-imports": "^2.0.0", "execa": "^5.1.1", "express": "^4.17.1", "get-port": "^5.1.1", @@ -47,6 +49,7 @@ "microbundle": "^0.13.3", "pg-god": "^1.0.12", "prettier": "^2.7.1", + "prettier-plugin-tailwindcss": "^0.2.3", "resolve-cwd": "^3.0.0", "ts-jest": "^26.5.6", "ts-node": "^10.9.1", diff --git a/packages/admin-ui/.gitignore b/packages/admin-ui/.gitignore new file mode 100644 index 0000000000000..cc4ef712b4658 --- /dev/null +++ b/packages/admin-ui/.gitignore @@ -0,0 +1,4 @@ +/dist +/build +.vercel +/ui/preview \ No newline at end of file diff --git a/packages/admin-ui/.npmignore b/packages/admin-ui/.npmignore new file mode 100644 index 0000000000000..2d1db95136d3e --- /dev/null +++ b/packages/admin-ui/.npmignore @@ -0,0 +1,6 @@ +.DS_store +.turbo +/src +/build +.vercel +/ui/preview \ No newline at end of file diff --git a/packages/admin-ui/README.md b/packages/admin-ui/README.md new file mode 100644 index 0000000000000..71226fb350f69 --- /dev/null +++ b/packages/admin-ui/README.md @@ -0,0 +1,38 @@ +

+ + Medusa + +

+

+ @medusajs/admin-ui +

+ +

+ Documentation | + Medusa Admin Demo | + Website +

+ +

+An open source composable commerce engine built for developers. +

+

+ + Medusa is released under the MIT license. + + + Current CircleCI build status. + + + PRs welcome! + + Product Hunt + + Discord Chat + + + Follow @medusajs + +

+ +## The Medusa Admin App. Included with the [`@medusajs/admin`](https://www.npmjs.com/package/@medusajs/admin) plugin. You shouldn't install this package separately. diff --git a/packages/admin-ui/package.json b/packages/admin-ui/package.json new file mode 100644 index 0000000000000..86f6f1deac94f --- /dev/null +++ b/packages/admin-ui/package.json @@ -0,0 +1,90 @@ +{ + "name": "@medusajs/admin-ui", + "author": "Kasper Kristensen ", + "license": "MIT", + "version": "0.0.0", + "repository": { + "type": "git", + "url": "https://github.com/medusajs/medusa.git", + "directory": "packages/admin-ui" + }, + "exports": { + ".": "./dist/index.js", + "./ui": "./ui", + "./package.json": "./package.json" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist", + "ui" + ], + "scripts": { + "dev": "vite -c vite.config.dev.ts --port 7001", + "build": "tsc --build", + "test:ui": "vitest --config vite.config.dev.ts", + "test:ui:once": "vitest --config vite.config.dev.ts --run", + "test": "echo \"Tests disabled temporarily\"" + }, + "dependencies": { + "@hookform/error-message": "^2.0.1", + "@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.2", + "@radix-ui/react-dropdown-menu": "^2.0.2", + "@radix-ui/react-popover": "^1.0.3", + "@radix-ui/react-radio-group": "^1.1.1", + "@radix-ui/react-select": "^1.2.0", + "@radix-ui/react-switch": "^1.0.1", + "@radix-ui/react-tooltip": "^1.0.3", + "@segment/analytics-next": "^1.51.1", + "@tanstack/react-query": "4.22.0", + "@tanstack/react-table": "^8.7.9", + "@vitejs/plugin-react": "^3.1.0", + "clsx": "^1.2.1", + "confetti-js": "^0.0.18", + "copy-to-clipboard": "^3.3.1", + "emoji-picker-react": "^4.4.3", + "framer-motion": "^9.1.6", + "medusa-react": "*", + "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-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "^18.2.0", + "react-helmet": "^6.0.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-json-tree": "^0.17.0", + "react-jwt": "^1.1.4", + "react-router-dom": "^6.8.0", + "react-select": "^5.5.4", + "react-table": "^7.7.0", + "type-fest": "^3.6.0", + "vite": "^4.1.4" + }, + "devDependencies": { + "@medusajs/medusa": "*", + "@tailwindcss/forms": "^0.5.3", + "@tailwindcss/line-clamp": "^0.4.2", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.4.3", + "@types/react": "^18.0.27", + "@types/react-dom": "^18.0.10", + "@types/react-table": "^7.7.9", + "autoprefixer": "^10.4.13", + "postcss": "^8.4.21", + "tailwindcss": "3.2.2", + "tailwindcss-radix": "^2.7.0", + "typescript": "^4.9.3", + "vitest": "^0.28.5" + }, + "packageManager": "yarn@3.2.1" +} diff --git a/packages/admin-ui/src/index.ts b/packages/admin-ui/src/index.ts new file mode 100644 index 0000000000000..39f937d08947d --- /dev/null +++ b/packages/admin-ui/src/index.ts @@ -0,0 +1,28 @@ +import fse from "fs-extra" +import { resolve } from "path" +import vite from "vite" +import { AdminBuildConfig } from "./types" +import { getCustomViteConfig } from "./utils" + +async function build(options?: AdminBuildConfig) { + const config = getCustomViteConfig(options) + + await vite.build(config).catch((_err) => { + process.exit(1) + }) + + await fse.writeJSON( + resolve(config.build.outDir, "build-manifest.json"), + options + ) +} + +async function watch() { + throw new Error("Not implemented") +} + +async function clean() { + throw new Error("Not implemented") +} + +export { build, watch, clean } diff --git a/packages/admin-ui/src/types/build.ts b/packages/admin-ui/src/types/build.ts new file mode 100644 index 0000000000000..0449a178dba65 --- /dev/null +++ b/packages/admin-ui/src/types/build.ts @@ -0,0 +1,15 @@ +import { DeepPartial } from "./misc" + +type GlobalsConfig = { + base?: string + backend?: string +} + +type BuildConfig = { + outDir?: string +} + +export type AdminBuildConfig = { + globals?: DeepPartial + build?: DeepPartial +} diff --git a/packages/admin-ui/src/types/config.ts b/packages/admin-ui/src/types/config.ts new file mode 100644 index 0000000000000..8bfd6137c95d2 --- /dev/null +++ b/packages/admin-ui/src/types/config.ts @@ -0,0 +1,5 @@ +import { AdminBuildConfig } from "./build" + +export type AdminUIConfig = { + build?: AdminBuildConfig +} diff --git a/packages/admin-ui/src/types/index.ts b/packages/admin-ui/src/types/index.ts new file mode 100644 index 0000000000000..348f635b62ca9 --- /dev/null +++ b/packages/admin-ui/src/types/index.ts @@ -0,0 +1,2 @@ +export * from "./build" +export * from "./misc" diff --git a/packages/admin-ui/src/types/misc.ts b/packages/admin-ui/src/types/misc.ts new file mode 100644 index 0000000000000..3ab124072a3ea --- /dev/null +++ b/packages/admin-ui/src/types/misc.ts @@ -0,0 +1,9 @@ +export type DeepPartial = { + [P in keyof T]?: T[P] extends (infer U)[] + ? DeepPartial[] + : T[P] extends ReadonlyArray + ? ReadonlyArray> + : DeepPartial +} + +export type Base = `/${T}/` diff --git a/packages/admin-ui/src/utils/format-base.ts b/packages/admin-ui/src/utils/format-base.ts new file mode 100644 index 0000000000000..cbf2e0f14a421 --- /dev/null +++ b/packages/admin-ui/src/utils/format-base.ts @@ -0,0 +1,5 @@ +import { Base } from "../types" + +export const formatBase = (base: T): Base => { + return `/${base}/` +} diff --git a/packages/admin-ui/src/utils/get-custom-vite-config.ts b/packages/admin-ui/src/utils/get-custom-vite-config.ts new file mode 100644 index 0000000000000..7bc4af95543ad --- /dev/null +++ b/packages/admin-ui/src/utils/get-custom-vite-config.ts @@ -0,0 +1,76 @@ +import react from "@vitejs/plugin-react" +import { resolve } from "path" +import { BuildOptions, InlineConfig } from "vite" +import { AdminBuildConfig } from "../types" +import { formatBase } from "./format-base" + +export const getCustomViteConfig = (config: AdminBuildConfig): InlineConfig => { + const { globals = {}, build = {} } = config + + const uiPath = resolve(__dirname, "..", "..", "ui") + + const globalReplacements = () => { + const base = globals.base || "app" + + let backend = "/" + + if (globals.backend) { + try { + // Test if the backend is a valid URL + new URL(globals.backend) + backend = globals.backend + } catch (_e) { + throw new Error( + `The provided backend URL is not valid: ${globals.backend}. Please provide a valid URL (e.g. https://my-medusa-server.com).` + ) + } + } + + return { + __BASE__: JSON.stringify(`/${base}`), + __MEDUSA_BACKEND_URL__: JSON.stringify(backend), + } + } + + const buildConfig = (): BuildOptions => { + const { outDir } = build + + let destDir: string + + if (!outDir) { + /** + * Default build directory is at the root of the `@medusajs/admin-ui` package. + */ + destDir = resolve(__dirname, "..", "..", "build") + } else { + /** + * If a custom build directory is specified, it is resolved relative to the + * current working directory. + */ + destDir = resolve(process.cwd(), outDir) + } + + return { + outDir: destDir, + emptyOutDir: true, + } + } + + return { + plugins: [react()], + root: uiPath, + mode: "production", + base: formatBase(globals.base), + define: globalReplacements(), + build: buildConfig(), + resolve: { + alias: { + "@tanstack/react-query": resolve( + require.resolve("@tanstack/react-query") + ), + }, + }, + clearScreen: false, + logLevel: "error", + } +} diff --git a/packages/admin-ui/src/utils/index.ts b/packages/admin-ui/src/utils/index.ts new file mode 100644 index 0000000000000..06ac389b4cd8f --- /dev/null +++ b/packages/admin-ui/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from "./format-base" +export * from "./get-custom-vite-config" diff --git a/packages/admin-ui/tsconfig.json b/packages/admin-ui/tsconfig.json new file mode 100644 index 0000000000000..ff0e60262a0f4 --- /dev/null +++ b/packages/admin-ui/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "module": "commonjs", + "moduleResolution": "node", + "noEmit": false, + "resolveJsonModule": true, + "esModuleInterop": true, + "outDir": "dist", + "rootDir": "src", + "skipLibCheck": true + }, + "include": ["src"], + "exclude": ["**/node_modules", "ui"] +} diff --git a/packages/admin-ui/ui/index.html b/packages/admin-ui/ui/index.html new file mode 100644 index 0000000000000..3732020477520 --- /dev/null +++ b/packages/admin-ui/ui/index.html @@ -0,0 +1,13 @@ + + + + + + + Medusa + + +
+ + + diff --git a/packages/admin-ui/ui/postcss.config.js b/packages/admin-ui/ui/postcss.config.js new file mode 100644 index 0000000000000..8a0deb6f9864b --- /dev/null +++ b/packages/admin-ui/ui/postcss.config.js @@ -0,0 +1,9 @@ +const path = require("path") + +module.exports = { + plugins: { + "tailwindcss/nesting": {}, + tailwindcss: { config: path.join(__dirname, "tailwind.config.js") }, + autoprefixer: {}, + }, +} diff --git a/packages/admin-ui/ui/public/logo.svg b/packages/admin-ui/ui/public/logo.svg new file mode 100644 index 0000000000000..87c22a658734a --- /dev/null +++ b/packages/admin-ui/ui/public/logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/packages/admin-ui/ui/src/App.tsx b/packages/admin-ui/ui/src/App.tsx new file mode 100644 index 0000000000000..06c7fb458599b --- /dev/null +++ b/packages/admin-ui/ui/src/App.tsx @@ -0,0 +1,45 @@ +import { lazy, Suspense } from "react" +import { + createBrowserRouter, + createRoutesFromElements, + Route, + RouterProvider, +} from "react-router-dom" +import Spinner from "./components/atoms/spinner" + +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( + <> + } /> + } /> + } /> + } /> + } /> + } /> + + ), + { + basename: __BASE__, + } +) + +const Loading = () => ( +
+ +
+) + +const App = () => ( + }> + + +) + +export default App diff --git a/packages/admin-ui/ui/src/assets/styles/emoji-picker.css b/packages/admin-ui/ui/src/assets/styles/emoji-picker.css new file mode 100644 index 0000000000000..59eb2c83ecbcd --- /dev/null +++ b/packages/admin-ui/ui/src/assets/styles/emoji-picker.css @@ -0,0 +1,107 @@ +.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; +} \ No newline at end of file diff --git a/packages/admin-ui/ui/src/assets/styles/global.css b/packages/admin-ui/ui/src/assets/styles/global.css new file mode 100644 index 0000000000000..c5c679f4b797c --- /dev/null +++ b/packages/admin-ui/ui/src/assets/styles/global.css @@ -0,0 +1,456 @@ +@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-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 font-sans text-5xlarge leading-4xlarge font-normal; + } + .inter-5xlarge-semibold { + @apply font-sans text-5xlarge leading-4xlarge font-semibold; + } + + .inter-4xlarge-regular { + @apply font-sans text-4xlarge leading-3xlarge font-normal; + } + .inter-4xlarge-semibold { + @apply font-sans text-4xlarge leading-3xlarge font-semibold; + } + + .inter-3xlarge-regular { + @apply font-sans text-3xlarge leading-2xlarge font-normal; + } + .inter-3xlarge-semibold { + @apply font-sans text-3xlarge leading-2xlarge font-semibold; + } + + .inter-2xlarge-regular { + @apply font-sans text-2xlarge leading-xlarge font-normal; + } + .inter-2xlarge-semibold { + @apply font-sans text-2xlarge leading-xlarge font-semibold; + } + + .inter-xlarge-regular { + @apply font-sans text-xlarge leading-large font-normal; + } + .inter-xlarge-semibold { + @apply font-sans text-xlarge leading-large font-semibold; + } + + .inter-large-regular { + @apply font-sans text-large leading-base font-normal; + } + .inter-large-semibold { + @apply font-sans text-large leading-base font-semibold; + } + + .inter-base-regular { + @apply font-sans text-base leading-base font-normal; + } + .inter-base-semibold { + @apply font-sans text-base leading-base font-semibold; + } + + .inter-small-regular { + @apply font-sans text-small leading-small font-normal; + } + .inter-small-semibold { + @apply font-sans text-small leading-small font-semibold; + } + + .inter-xsmall-regular { + @apply font-sans text-xsmall leading-xsmall font-normal; + } + .inter-xsmall-semibold { + @apply font-sans text-xsmall leading-xsmall font-semibold; + } + + .mono-5xlarge-regular { + @apply font-mono text-5xlarge leading-4xlarge font-normal; + } + .mono-5xlarge-semibold { + @apply font-mono text-5xlarge leading-4xlarge font-bold; + } + + .mono-4xlarge-regular { + @apply font-mono text-4xlarge leading-3xlarge font-normal; + } + .mono-4xlarge-semibold { + @apply font-mono text-4xlarge leading-3xlarge font-bold; + } + + .mono-3xlarge-regular { + @apply font-mono text-3xlarge leading-2xlarge font-normal; + } + .mono-3xlarge-semibold { + @apply font-mono text-3xlarge leading-2xlarge font-bold; + } + + .mono-2xlarge-regular { + @apply font-mono text-2xlarge leading-xlarge font-normal; + } + .mono-2xlarge-semibold { + @apply font-mono text-2xlarge leading-xlarge font-bold; + } + + .mono-xlarge-regular { + @apply font-mono text-xlarge leading-large font-normal; + } + .mono-xlarge-semibold { + @apply font-mono text-xlarge leading-large font-bold; + } + + .mono-large-regular { + @apply font-mono text-large leading-base font-normal; + } + .mono-large-semibold { + @apply font-mono text-large leading-base font-bold; + } + + .mono-base-regular { + @apply font-mono text-base leading-base font-normal; + } + .mono-base-semibold { + @apply font-mono text-base leading-base font-bold; + } + + .mono-small-regular { + @apply font-mono text-small leading-small font-normal; + } + .mono-small-semibold { + @apply font-mono text-small leading-small font-bold; + } + + .mono-xsmall-regular { + @apply font-mono text-xsmall leading-xsmall font-normal; + } + .mono-xsmall-semibold { + @apply font-mono text-xsmall leading-xsmall 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 p-0 -mx-3 border-0 mb-1 cursor-text h-6; + + .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 h-6 p-0 m-0 !important; + } + + .react-select__value-container--is-multi, + .react-select__value-container--has-value { + @apply h-6 pl-3 p-0 m-0 !important; + } + + .react-select__menu, + .react-select__menu-list { + @apply rounded-t-none mt-0 z-[110] !important; + } + + .react-select__value-container { + @apply pl-3 pr-0; + } + + .react-select__indicators { + @apply p-0 h-full items-center flex pr-3; + + .react-select__indicator { + @apply p-0; + } + } + + .react-select__input { + @apply w-full mt-0 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 w-min py-0.5 px-2 rounded-rounded inter-small-semibold; + } + + .badge-disabled { + @apply bg-grey-50 bg-opacity-10 text-grey-50; + } + + .badge-primary { + @apply bg-violet-60 bg-opacity-10 text-violet-60; + } + + .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 bg-opacity-20 text-yellow-60; + } + + .badge-ghost { + @apply text-grey-90 border border-grey-20 whitespace-nowrap; + } + + .badge-default { + @apply inter-small-regular bg-grey-10 text-grey-90 whitespace-nowrap; + } + + .btn { + @apply flex items-center justify-center rounded-rounded focus:outline-none focus:shadow-cta; + } + + .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 hover:bg-violet-50 active:bg-violet-70 disabled:bg-grey-20 disabled:text-grey-40; + } + + .btn-secondary { + @apply bg-grey-0 text-grey-90 border 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; + } + + .btn-danger { + @apply bg-grey-0 text-rose-50 border border-grey-20 hover:bg-grey-10 active:bg-grey-20 disabled:bg-grey-0 disabled:text-grey-30; + } + + .btn-nuclear { + @apply bg-rose-50 text-grey-0 hover:bg-rose-40 active:bg-rose-60 disabled:bg-grey-20 disabled:text-grey-40; + } + + .btn-ghost { + @apply bg-transparent text-grey-90 hover:bg-grey-5 active:bg-grey-5 active:text-violet-60 focus:border-violet-60 disabled:bg-transparent disabled:text-grey-30; + } + + .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 outline-none pt-6 !important; + + .react-datepicker__month-container { + .react-datepicker__header { + @apply bg-inherit border-0; + } + } + + .react-datepicker__day-names { + @apply inter-base-semibold pt-4; + + .react-datepicker__day-name { + @apply w-[40px] m-0; + } + } + + .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 m-[0px] w-[38px] h-[38px] align-middle relative leading-none pt-3; + :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 bg-gradient-to-tr from-vice-start to-vice-stop; + } + + .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; + } + + .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; +} diff --git a/packages/admin-ui/ui/src/assets/svg/carrot.svg b/packages/admin-ui/ui/src/assets/svg/carrot.svg new file mode 100644 index 0000000000000..1209385b7dbc5 --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/carrot.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/admin-ui/ui/src/assets/svg/controller.svg b/packages/admin-ui/ui/src/assets/svg/controller.svg new file mode 100644 index 0000000000000..ec4d9190be43c --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/controller.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/admin-ui/ui/src/assets/svg/flag.svg b/packages/admin-ui/ui/src/assets/svg/flag.svg new file mode 100644 index 0000000000000..9b204a3c5dd94 --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/flag.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/admin-ui/ui/src/assets/svg/happy.svg b/packages/admin-ui/ui/src/assets/svg/happy.svg new file mode 100644 index 0000000000000..760ed1bbd4bf7 --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/happy.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/admin-ui/ui/src/assets/svg/heart.svg b/packages/admin-ui/ui/src/assets/svg/heart.svg new file mode 100644 index 0000000000000..fd2ffbe89e1c9 --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/admin-ui/ui/src/assets/svg/lightbulb.svg b/packages/admin-ui/ui/src/assets/svg/lightbulb.svg new file mode 100644 index 0000000000000..2ee0d862823af --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/lightbulb.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/admin-ui/ui/src/assets/svg/plane.svg b/packages/admin-ui/ui/src/assets/svg/plane.svg new file mode 100644 index 0000000000000..3e321b9e09a43 --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/plane.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/admin-ui/ui/src/assets/svg/search.svg b/packages/admin-ui/ui/src/assets/svg/search.svg new file mode 100644 index 0000000000000..69cc6547ce031 --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/admin-ui/ui/src/assets/svg/sprout.svg b/packages/admin-ui/ui/src/assets/svg/sprout.svg new file mode 100644 index 0000000000000..0742a10337a67 --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/sprout.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/admin-ui/ui/src/assets/svg/star.svg b/packages/admin-ui/ui/src/assets/svg/star.svg new file mode 100644 index 0000000000000..2d3ff1bd3e1d8 --- /dev/null +++ b/packages/admin-ui/ui/src/assets/svg/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/admin-ui/ui/src/components/atoms/avatar/index.tsx b/packages/admin-ui/ui/src/components/atoms/avatar/index.tsx new file mode 100644 index 0000000000000..9912beabe9e5a --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/avatar/index.tsx @@ -0,0 +1,62 @@ +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 = ({ + user, + font = "inter-small-semibold", + color = "bg-violet-60", + 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 ( + + + + {isLoading ? ( + + ) : ( + username.slice(0, 1).toUpperCase() + )} + + + ) +} + +export default Avatar diff --git a/packages/admin-ui/ui/src/components/atoms/back-button/index.tsx b/packages/admin-ui/ui/src/components/atoms/back-button/index.tsx new file mode 100644 index 0000000000000..bdeba612363f2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/back-button/index.tsx @@ -0,0 +1,29 @@ +import clsx from "clsx" +import React from "react" +import { useNavigate } from "react-router-dom" +import ArrowLeftIcon from "../../fundamentals/icons/arrow-left-icon" + +type Props = { + path?: string + label?: string + className?: string +} + +const BackButton = ({ path, label = "Go back", className }: Props) => { + const navigate = useNavigate() + return ( + + ) +} + +export default BackButton diff --git a/packages/admin-ui/ui/src/components/atoms/checkbox/index.tsx b/packages/admin-ui/ui/src/components/atoms/checkbox/index.tsx new file mode 100644 index 0000000000000..2e4fa8dca7ed8 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/checkbox/index.tsx @@ -0,0 +1,32 @@ +import clsx from "clsx" +import React, { ReactNode, useImperativeHandle } from "react" + +export type CheckboxProps = React.InputHTMLAttributes & { + label: ReactNode +} + +const Checkbox = React.forwardRef( + ({ label, value, className, id, ...rest }: CheckboxProps, ref) => { + const checkboxRef = React.useRef(null) + + useImperativeHandle(ref, () => checkboxRef.current) + return ( + + ) + } +) + +export default Checkbox diff --git a/packages/admin-ui/ui/src/components/atoms/copy-to-clipboard/index.tsx b/packages/admin-ui/ui/src/components/atoms/copy-to-clipboard/index.tsx new file mode 100644 index 0000000000000..7b46e054d2d71 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/copy-to-clipboard/index.tsx @@ -0,0 +1,58 @@ +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 = ({ + 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]) + + return ( +
+ + {showValue && ( + + {displayValue ? displayValue : value} + + )} +
+ ) +} + +export default CopyToClipboard diff --git a/packages/admin-ui/ui/src/components/atoms/date-picker/custom-header.tsx b/packages/admin-ui/ui/src/components/atoms/date-picker/custom-header.tsx new file mode 100644 index 0000000000000..bfb99e0f595af --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/date-picker/custom-header.tsx @@ -0,0 +1,44 @@ +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 ( +
+
+ changeMonth(monthNames.indexOf(v))} + > + {monthNames.map((month) => ( + + {month} + + ))} + +
+
+ changeYear(parseInt(v, 10))} + > + {getYearRange().map((year) => ( + + {year.toString()} + + ))} + +
+
+ ) +} + +export default CustomHeader diff --git a/packages/admin-ui/ui/src/components/atoms/date-picker/date-picker.tsx b/packages/admin-ui/ui/src/components/atoms/date-picker/date-picker.tsx new file mode 100644 index 0000000000000..165b263018429 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/date-picker/date-picker.tsx @@ -0,0 +1,115 @@ +import * as PopoverPrimitive from "@radix-ui/react-popover" +import clsx from "clsx" +import moment from "moment" +import React, { useEffect, useState } from "react" +import ReactDatePicker from "react-datepicker" +import "react-datepicker/dist/react-datepicker.css" +import Button from "../../fundamentals/button" +import ArrowDownIcon from "../../fundamentals/icons/arrow-down-icon" +import InputContainer from "../../fundamentals/input-container" +import InputHeader from "../../fundamentals/input-header" +import CustomHeader from "./custom-header" +import { DateTimePickerProps } from "./types" + +const getDateClassname = (d, tempDate) => { + return moment(d).format("YY,MM,DD") === moment(tempDate).format("YY,MM,DD") + ? "date chosen" + : `date ${ + moment(d).format("YY,MM,DD") < moment(new Date()).format("YY,MM,DD") + ? "past" + : "" + }` +} + +const DatePicker: React.FC = ({ + date, + onSubmitDate, + label = "start date", + required = false, + tooltipContent, + tooltip, +}) => { + const [tempDate, setTempDate] = useState(date) + const [isOpen, setIsOpen] = useState(false) + + useEffect(() => setTempDate(date), [isOpen]) + + const submitDate = () => { + // 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 ( +
+ + + + + + +
+ + +
+
+
+
+ ) +} + +export const CalendarComponent = ({ date, onChange }) => ( + getDateClassname(d, date)} + renderCustomHeader={({ ...props }) => } + /> +) + +export default DatePicker diff --git a/packages/admin-ui/ui/src/components/atoms/date-picker/time-picker.tsx b/packages/admin-ui/ui/src/components/atoms/date-picker/time-picker.tsx new file mode 100644 index 0000000000000..f0f62f943b333 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/date-picker/time-picker.tsx @@ -0,0 +1,102 @@ +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 = ({ + 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 ( +
+ + + + + + setSelectedHour(n)} + className="pr-4" + /> + setSelectedMinute(n)} + /> +
+ + +
+ ) +} + +export default TimePicker diff --git a/packages/admin-ui/ui/src/components/atoms/date-picker/types.tsx b/packages/admin-ui/ui/src/components/atoms/date-picker/types.tsx new file mode 100644 index 0000000000000..88b4d662d039f --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/date-picker/types.tsx @@ -0,0 +1,6 @@ +import { InputHeaderProps } from "../../fundamentals/input-header" + +export type DateTimePickerProps = { + date: Date + onSubmitDate: (newDate: Date) => void +} & InputHeaderProps diff --git a/packages/admin-ui/ui/src/components/atoms/date-picker/utils.ts b/packages/admin-ui/ui/src/components/atoms/date-picker/utils.ts new file mode 100644 index 0000000000000..d46746616b741 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/date-picker/utils.ts @@ -0,0 +1,25 @@ +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", +] diff --git a/packages/admin-ui/ui/src/components/atoms/fade-wrapper/index.tsx b/packages/admin-ui/ui/src/components/atoms/fade-wrapper/index.tsx new file mode 100644 index 0000000000000..2594591b8e668 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/fade-wrapper/index.tsx @@ -0,0 +1,47 @@ +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 = ({ + 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 ( +
+ {show ? children : null} +
+ ) +} + +export default Fade diff --git a/packages/admin-ui/ui/src/components/atoms/file-upload-field/index.tsx b/packages/admin-ui/ui/src/components/atoms/file-upload-field/index.tsx new file mode 100644 index 0000000000000..265700c5cd63f --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/file-upload-field/index.tsx @@ -0,0 +1,105 @@ +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 = ( + + Drop your images here, or{" "} + click to browse + +) + +const FileUploadField: React.FC = ({ + onFileChosen, + filetypes, + errorMessage, + className, + text = defaultText, + placeholder = "", + multiple = false, +}) => { + const inputRef = useRef(null) + const [fileUploadError, setFileUploadError] = useState(false) + + const handleFileUpload = (e: React.ChangeEvent) => { + const fileList = e.target.files + + if (fileList) { + onFileChosen(Array.from(fileList)) + } + } + + const handleFileDrop = (e: React.DragEvent) => { + 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 ( +
inputRef?.current?.click()} + onDrop={handleFileDrop} + onDragOver={(e) => e.preventDefault()} + className={clsx( + "flex flex-col select-none inter-base-regular text-grey-50 cursor-pointer items-center justify-center w-full h-full rounded-rounded border-2 border-dashed border-grey-20 transition-colors hover:border-violet-60 hover:text-grey-40", + className + )} + > +
+

{text}

+ {placeholder} +
+ {fileUploadError && ( + + {errorMessage || "Please upload an image file"} + + )} + +
+ ) +} + +export default FileUploadField diff --git a/packages/admin-ui/ui/src/components/atoms/includes-tax-tooltip/index.tsx b/packages/admin-ui/ui/src/components/atoms/includes-tax-tooltip/index.tsx new file mode 100644 index 0000000000000..9f25abb610637 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/includes-tax-tooltip/index.tsx @@ -0,0 +1,26 @@ +import clsx from "clsx" +import React from "react" +import TaxesIcon from "../../fundamentals/icons/taxes-icon" +import Tooltip from "../tooltip" + +type Props = { + includesTax?: boolean +} + +const IncludesTaxTooltip = ({ includesTax }: Props) => { + return ( + +
+ +
+
+ ) +} + +export default IncludesTaxTooltip diff --git a/packages/admin-ui/ui/src/components/atoms/input-error/index.tsx b/packages/admin-ui/ui/src/components/atoms/input-error/index.tsx new file mode 100644 index 0000000000000..23ad952658e9d --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/input-error/index.tsx @@ -0,0 +1,73 @@ +import { ErrorMessage } from "@hookform/error-message" +import clsx from "clsx" +import React from "react" +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 ( + { + return ( +
+ {messages ? ( + + ) : ( +

{message}

+ )} +
+ ) + }} + /> + ) +} + +const MultipleMessages = ({ messages }: { messages: MultipleFieldErrors }) => { + const errors = Object.entries(messages).map(([_, message]) => message) + + const displayedError = errors[0] + const remainderErrors = errors.slice(1) + + return ( +
+

{displayedError}

+ {remainderErrors?.length > 0 && ( + + {remainderErrors.map((e, i) => { + return ( +

+ {Array.from(Array(i + 1)).map((_) => "*")} + {e} +

+ ) + })} +
+ } + > +

+ +{remainderErrors.length}{" "} + {remainderErrors.length > 1 ? "errors" : "error"} +

+ + )} +
+ ) +} + +export default InputError diff --git a/packages/admin-ui/ui/src/components/atoms/loading-container/index.tsx b/packages/admin-ui/ui/src/components/atoms/loading-container/index.tsx new file mode 100644 index 0000000000000..6b5778f6eeade --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/loading-container/index.tsx @@ -0,0 +1,32 @@ +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 || + + if (isLoading) { + return ( +
+ {placeholder} +
+ ) + } + + return children as React.ReactElement +} + +export default LoadingContainer diff --git a/packages/admin-ui/ui/src/components/atoms/notification/index.tsx b/packages/admin-ui/ui/src/components/atoms/notification/index.tsx new file mode 100644 index 0000000000000..3a5f6f7f376f0 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/notification/index.tsx @@ -0,0 +1,62 @@ +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 = ({ + toast, + type, + title, + message, +}) => { + const onDismiss = () => { + globalToast.dismiss(toast.id) + } + + return ( + +
{getIcon(type)}
+
+ {title} + {message} +
+
+ + Close +
+
+ ) +} + +const ICON_SIZE = 20 + +function getIcon(type: NotificationTypes) { + switch (type) { + case "success": + return + case "warning": + return + case "error": + return + default: + return + } +} + +export default Notification diff --git a/packages/admin-ui/ui/src/components/atoms/number-scroller/index.tsx b/packages/admin-ui/ui/src/components/atoms/number-scroller/index.tsx new file mode 100644 index 0000000000000..514b901d7cbb5 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/number-scroller/index.tsx @@ -0,0 +1,47 @@ +import clsx from "clsx" +import React from "react" + +type NumberScrollerProps = { + numbers: number[] + selected: number + onSelect: (value: number) => void +} & React.HTMLAttributes + +const NumberScroller: React.FC = ({ + numbers, + selected, + onSelect, + className, + ...props +}) => { + return ( +
+ {numbers.map((n, i) => { + return ( +
+ +
+ ) + })} +
+ ) +} + +export default NumberScroller diff --git a/packages/admin-ui/ui/src/components/atoms/os-shortcut/index.tsx b/packages/admin-ui/ui/src/components/atoms/os-shortcut/index.tsx new file mode 100644 index 0000000000000..74f419573236b --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/os-shortcut/index.tsx @@ -0,0 +1,50 @@ +import React from "react" + +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 ( +
+

+ {modifiers} + {input} +

+
+ ) +} + +export default OSShortcut diff --git a/packages/admin-ui/ui/src/components/atoms/page-description/index.tsx b/packages/admin-ui/ui/src/components/atoms/page-description/index.tsx new file mode 100644 index 0000000000000..899bc836dea2a --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/page-description/index.tsx @@ -0,0 +1,20 @@ +import React from "react" + +type PageDescriptionProps = { + title?: string + subtitle?: string +} + +const PageDescription: React.FC = ({ + title, + subtitle, +}) => { + return ( +
+

{title}

+

{subtitle}

+
+ ) +} + +export default PageDescription diff --git a/packages/admin-ui/ui/src/components/atoms/save-notification/error-state.tsx b/packages/admin-ui/ui/src/components/atoms/save-notification/error-state.tsx new file mode 100644 index 0000000000000..e84d1d47bc129 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/save-notification/error-state.tsx @@ -0,0 +1,49 @@ +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 = ({ + 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 ( + +
+ +
+
+ {title} + {message} +
+
+ + Close +
+
+ ) +} + +export default ErrorState diff --git a/packages/admin-ui/ui/src/components/atoms/save-notification/index.tsx b/packages/admin-ui/ui/src/components/atoms/save-notification/index.tsx new file mode 100644 index 0000000000000..d86745f93fff2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/save-notification/index.tsx @@ -0,0 +1,94 @@ +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 + reset: () => void +} + +const SaveNotification: React.FC = ({ + 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) => , { + id: toast.id, + }) + + onSave() + .then(() => { + globalToast.custom( + (t) => , + { + id: toast.id, + } + ) + }) + .catch((_err) => { + globalToast.custom( + (t) => , + { + id: toast.id, + } + ) + }) + } + + return ( + +
{getIcon(icon)}
+
+ {title} + {message} +
+
+ + +
+
+ ) +} + +const ICON_SIZE = 20 + +function getIcon(icon?: any) { + if (icon) { + return React.cloneElement(icon, { + size: ICON_SIZE, + className: "text-grey-90", + }) + } else { + return + } +} + +export default SaveNotification diff --git a/packages/admin-ui/ui/src/components/atoms/save-notification/saving-state.tsx b/packages/admin-ui/ui/src/components/atoms/save-notification/saving-state.tsx new file mode 100644 index 0000000000000..feb481e02db80 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/save-notification/saving-state.tsx @@ -0,0 +1,30 @@ +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 = ({ + toast, + title = "Saving changes", + message = "Hang on, this may take a few moments.", +}) => { + return ( + +
+ +
+
+ {title} + {message} +
+
+ ) +} + +export default SavingState diff --git a/packages/admin-ui/ui/src/components/atoms/save-notification/success-state.tsx b/packages/admin-ui/ui/src/components/atoms/save-notification/success-state.tsx new file mode 100644 index 0000000000000..007eadbff99f7 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/save-notification/success-state.tsx @@ -0,0 +1,49 @@ +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 = ({ + toast, + title = "Success", + message = "Your changes have been saved.", + onDismiss, +}) => { + useEffect(() => { + const life = setTimeout(() => { + onDismiss() + }, 2000) + + return () => { + clearTimeout(life) + } + }, [toast]) + + return ( + +
+ +
+
+ {title} + {message} +
+
+ + Close +
+
+ ) +} + +export default SuccessState diff --git a/packages/admin-ui/ui/src/components/atoms/settings-card/index.tsx b/packages/admin-ui/ui/src/components/atoms/settings-card/index.tsx new file mode 100644 index 0000000000000..f9085766b26f7 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/settings-card/index.tsx @@ -0,0 +1,56 @@ +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 = ({ + icon, + heading, + description, + to = null, + externalLink = null, + disabled = false, +}) => { + if (disabled) { + to = null + } + + return ( + + + + ) +} + +export default SettingsCard diff --git a/packages/admin-ui/ui/src/components/atoms/skeleton/index.tsx b/packages/admin-ui/ui/src/components/atoms/skeleton/index.tsx new file mode 100644 index 0000000000000..993939e378eb7 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/skeleton/index.tsx @@ -0,0 +1,25 @@ +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 ( +
*]:opacity-0": state, + })} + > + {children} +
+ ) +} + +export default Skeleton diff --git a/packages/admin-ui/ui/src/components/atoms/spinner.tsx b/packages/admin-ui/ui/src/components/atoms/spinner.tsx new file mode 100644 index 0000000000000..f00aaeb8b3a57 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/spinner.tsx @@ -0,0 +1,35 @@ +import clsx from "clsx" +import React from "react" + +type SpinnerProps = { + size?: "large" | "medium" | "small" + variant?: "primary" | "secondary" +} + +const Spinner: React.FC = ({ + size = "large", + variant = "primary", +}) => { + return ( +
+
+
+
+
+ ) +} + +export default Spinner diff --git a/packages/admin-ui/ui/src/components/atoms/switch/index.tsx b/packages/admin-ui/ui/src/components/atoms/switch/index.tsx new file mode 100644 index 0000000000000..6db8450990784 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/switch/index.tsx @@ -0,0 +1,26 @@ +import * as RadixSwitch from "@radix-ui/react-switch" +import clsx from "clsx" +import React from "react" + +/** + * A controlled switch component atom. + */ +function Switch(props: RadixSwitch.SwitchProps) { + return ( + + + + ) +} + +export default Switch diff --git a/packages/admin-ui/ui/src/components/atoms/text-input/index.tsx b/packages/admin-ui/ui/src/components/atoms/text-input/index.tsx new file mode 100644 index 0000000000000..15a6a585f4c20 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/text-input/index.tsx @@ -0,0 +1,19 @@ +import React from "react" +import clsx from "clsx" + +type TextInputProps = React.InputHTMLAttributes + +const TextInput = React.forwardRef( + ({ className, ...props }, ref) => ( + + ) +) + +export default TextInput diff --git a/packages/admin-ui/ui/src/components/atoms/thumbnail/index.ts b/packages/admin-ui/ui/src/components/atoms/thumbnail/index.ts new file mode 100644 index 0000000000000..e42d9e98f6c59 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/thumbnail/index.ts @@ -0,0 +1 @@ +export { Thumbnail as default } from "./thumbnail" diff --git a/packages/admin-ui/ui/src/components/atoms/thumbnail/thumbnail.tsx b/packages/admin-ui/ui/src/components/atoms/thumbnail/thumbnail.tsx new file mode 100644 index 0000000000000..2f463c8ad7a6b --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/thumbnail/thumbnail.tsx @@ -0,0 +1,30 @@ +import clsx from "clsx" +import ImagePlaceholderIcon from "../../fundamentals/icons/image-placeholder-icon" + +type Props = { + src?: string | null + className?: string + size?: "small" | "medium" | "large" +} + +export const Thumbnail = ({ src, className, size = "small" }: Props) => { + return ( +
+ {src ? ( + + ) : ( + + )} +
+ ) +} diff --git a/packages/admin-ui/ui/src/components/atoms/toaster-container/index.tsx b/packages/admin-ui/ui/src/components/atoms/toaster-container/index.tsx new file mode 100644 index 0000000000000..e165b9c8e7b7d --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/toaster-container/index.tsx @@ -0,0 +1,33 @@ +import clsx from "clsx" +import React from "react" + +type ToasterContainerProps = { + visible: boolean +} & React.HTMLAttributes + +const ToasterContainer: React.FC = ({ + children, + visible, + className, + ...rest +}) => { + return ( +
+ {children} +
+ ) +} + +export default ToasterContainer diff --git a/packages/admin-ui/ui/src/components/atoms/tooltip/index.tsx b/packages/admin-ui/ui/src/components/atoms/tooltip/index.tsx new file mode 100644 index 0000000000000..9cf4d667ae66a --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/tooltip/index.tsx @@ -0,0 +1,58 @@ +import * as RadixTooltip from "@radix-ui/react-tooltip" +import clsx from "clsx" +import React from "react" + +export type TooltipProps = RadixTooltip.TooltipContentProps & + Pick< + RadixTooltip.TooltipProps, + "open" | "defaultOpen" | "onOpenChange" | "delayDuration" + > & { + content: React.ReactNode + side?: "bottom" | "left" | "top" | "right" + onClick?: React.ButtonHTMLAttributes["onClick"] + } + +const Tooltip = ({ + children, + content, + open, + defaultOpen, + onOpenChange, + delayDuration, + className, + side, + onClick, + ...props +}: TooltipProps) => { + return ( + + + + {children} + + + {content} + + + + ) +} + +export default Tooltip diff --git a/packages/admin-ui/ui/src/components/atoms/two-step-delete/index.tsx b/packages/admin-ui/ui/src/components/atoms/two-step-delete/index.tsx new file mode 100644 index 0000000000000..5c7f85a6e19b8 --- /dev/null +++ b/packages/admin-ui/ui/src/components/atoms/two-step-delete/index.tsx @@ -0,0 +1,110 @@ +import clsx from "clsx" +import React, { + forwardRef, + useCallback, + useEffect, + useImperativeHandle, + useRef, + useState, +} from "react" +import TrashIcon from "../../fundamentals/icons/trash-icon" +import Spinner from "../spinner" +import Tooltip from "../tooltip" + +type Props = { + onDelete: () => void + deleting?: boolean + className?: string + children?: React.ReactNode +} + +const TwoStepDelete = forwardRef( + ({ onDelete, deleting = false, children, className }: Props, ref) => { + const [armed, setArmed] = useState(false) + const innerRef = useRef(null) + + useImperativeHandle( + ref, + () => innerRef.current + ) + + const handleTwoStepDelete = () => { + if (!armed) { + setArmed(true) + return + } + + onDelete() + setArmed(false) + } + + const disarmOnClickOutside = useCallback( + (e: MouseEvent) => { + if (innerRef.current && !innerRef.current.contains(e.target as Node)) { + if (armed) { + setArmed(false) + } + } + }, + [armed] + ) + + useEffect(() => { + document.addEventListener("mousedown", disarmOnClickOutside) + + return () => { + document.removeEventListener("mousedown", disarmOnClickOutside) + } + }, [disarmOnClickOutside]) + + return ( + + ) + } +) + +export default TwoStepDelete diff --git a/packages/admin-ui/ui/src/components/declarative-toaster/index.tsx b/packages/admin-ui/ui/src/components/declarative-toaster/index.tsx new file mode 100644 index 0000000000000..34ddfaf17e2e1 --- /dev/null +++ b/packages/admin-ui/ui/src/components/declarative-toaster/index.tsx @@ -0,0 +1,23 @@ +import * as React from "react" +import { toast, ToastOptions } from "react-hot-toast" + +export type ToasterProps = { + visible: boolean + children: React.ReactElement +} & ToastOptions + +const Toaster = ({ visible, children, ...options }: ToasterProps) => { + React.useEffect(() => { + if (visible) { + toast.custom((t) => React.cloneElement(children, { toast: t }), { + ...options, + }) + } else { + toast.dismiss(options.id) + } + }, [visible, children]) + + return null +} + +export default Toaster diff --git a/packages/admin-ui/ui/src/components/fundamentals/badge/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/badge/index.tsx new file mode 100644 index 0000000000000..fd445a1d75270 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/badge/index.tsx @@ -0,0 +1,43 @@ +import clsx from "clsx" +import React from "react" + +type BadgeProps = { + variant: + | "primary" + | "danger" + | "success" + | "warning" + | "ghost" + | "default" + | "disabled" +} & React.HTMLAttributes + +const Badge: React.FC = ({ + children, + variant, + onClick, + className, + ...props +}) => { + const variantClassname = clsx({ + ["badge-primary"]: variant === "primary", + ["badge-danger"]: variant === "danger", + ["badge-success"]: variant === "success", + ["badge-warning"]: variant === "warning", + ["badge-ghost"]: variant === "ghost", + ["badge-default"]: variant === "default", + ["badge-disabled"]: variant === "disabled", + }) + + return ( +
+ {children} +
+ ) +} + +export default Badge diff --git a/packages/admin-ui/ui/src/components/fundamentals/button/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/button/index.tsx new file mode 100644 index 0000000000000..c04a651724dbc --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/button/index.tsx @@ -0,0 +1,71 @@ +import clsx from "clsx" +import React, { Children } from "react" +import Spinner from "../../atoms/spinner" + +export type ButtonProps = { + variant: "primary" | "secondary" | "ghost" | "danger" | "nuclear" + size?: "small" | "medium" | "large" + loading?: boolean +} & React.ButtonHTMLAttributes + +const Button = React.forwardRef( + ( + { + variant = "primary", + size = "large", + loading = false, + children, + ...attributes + }, + ref + ) => { + const handleClick = (e) => { + if (!loading && attributes.onClick) { + attributes.onClick(e) + } + } + + const variantClassname = clsx({ + ["btn-primary"]: variant === "primary", + ["btn-secondary"]: variant === "secondary", + ["btn-ghost"]: variant === "ghost", + ["btn-danger"]: variant === "danger", + ["btn-nuclear"]: variant === "nuclear", + }) + + const sizeClassname = clsx({ + ["btn-large"]: size === "large", + ["btn-medium"]: size === "medium", + ["btn-small"]: size === "small", + }) + + return ( + + ) + } +) + +export default Button diff --git a/packages/admin-ui/ui/src/components/fundamentals/details-icon/contact.svg b/packages/admin-ui/ui/src/components/fundamentals/details-icon/contact.svg new file mode 100644 index 0000000000000..d4fff1c3123b9 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/details-icon/contact.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/admin-ui/ui/src/components/fundamentals/details-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/details-icon/index.tsx new file mode 100644 index 0000000000000..1e71482b85e3f --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/details-icon/index.tsx @@ -0,0 +1,57 @@ +import React from "react" +import IconProps from "../icons/types/icon-type" + +const DetailsIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default DetailsIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/feature-toggle.tsx b/packages/admin-ui/ui/src/components/fundamentals/feature-toggle.tsx new file mode 100644 index 0000000000000..6f97f6a2fded6 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/feature-toggle.tsx @@ -0,0 +1,21 @@ +import React from "react" +import { useFeatureFlag } from "../../providers/feature-flag-provider" + +export type FeatureToggleProps = { + featureFlag: string + showOnlyWhenDisabled?: boolean + children?: React.ReactNode +} + +const FeatureToggle: React.FC = ({ + featureFlag, + showOnlyWhenDisabled = false, + children, +}) => { + const { isFeatureEnabled } = useFeatureFlag() + + const showContent = isFeatureEnabled(featureFlag) === !showOnlyWhenDisabled + return showContent ? <>{children} : null +} + +export default FeatureToggle diff --git a/packages/admin-ui/ui/src/components/fundamentals/icon-badge/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icon-badge/index.tsx new file mode 100644 index 0000000000000..5f30a61d09229 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icon-badge/index.tsx @@ -0,0 +1,36 @@ +import clsx from "clsx" +import React from "react" +import Badge from "../badge" + +type IconBadgeProps = { + variant?: + | "primary" + | "danger" + | "success" + | "warning" + | "ghost" + | "default" + | "disabled" +} & React.HTMLAttributes + +const IconBadge: React.FC = ({ + children, + variant, + className, + ...rest +}) => { + return ( + + {children} + + ) +} + +export default IconBadge diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/alert-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/alert-icon/index.tsx new file mode 100644 index 0000000000000..1fa57b92364bb --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/alert-icon/index.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const AlertIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default AlertIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-down-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-down-icon.tsx new file mode 100644 index 0000000000000..4590e3eea5119 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-down-icon.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const ArrowDownIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default ArrowDownIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-left-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-left-icon/index.tsx new file mode 100644 index 0000000000000..18bb2a165a95e --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-left-icon/index.tsx @@ -0,0 +1,35 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ArrowLeftIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default ArrowLeftIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-right-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-right-icon/index.tsx new file mode 100644 index 0000000000000..a13f0961158c3 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-right-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ArrowRightIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default ArrowRightIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-top-right-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-top-right-icon/index.tsx new file mode 100644 index 0000000000000..3ced3262e8ba8 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-top-right-icon/index.tsx @@ -0,0 +1,29 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ArrowTopRightIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default ArrowTopRightIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-up-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-up-icon.tsx new file mode 100644 index 0000000000000..bee38736925a2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/arrow-up-icon.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const ArrowUpIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default ArrowUpIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/back-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/back-icon/index.tsx new file mode 100644 index 0000000000000..9892c086c22ba --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/back-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const BackIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default BackIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/backspace-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/backspace-icon/index.tsx new file mode 100644 index 0000000000000..8b8701810f2e3 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/backspace-icon/index.tsx @@ -0,0 +1,42 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const BackspaceIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default BackspaceIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/bell-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/bell-icon/index.tsx new file mode 100644 index 0000000000000..39d316838c64d --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/bell-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const BellIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default BellIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/bell-noti-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/bell-noti-icon/index.tsx new file mode 100644 index 0000000000000..70af40f17f432 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/bell-noti-icon/index.tsx @@ -0,0 +1,41 @@ +import React from "react" +import IconProps from "../types/icon-type" + +type IBellNotiIconProps = IconProps & { + accentColor?: string +} + +const BellNotiIcon: React.FC = ({ + size = "24px", + color = "currentColor", + accentColor = "#F43F5E", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default BellNotiIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/bell-off-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/bell-off-icon/index.tsx new file mode 100644 index 0000000000000..17a30b5a291ec --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/bell-off-icon/index.tsx @@ -0,0 +1,57 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const BellOffIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default BellOffIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/buildings-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/buildings-icon/index.tsx new file mode 100644 index 0000000000000..53f32fe12d32f --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/buildings-icon/index.tsx @@ -0,0 +1,64 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const BuildingsIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + + ) +} + +export default BuildingsIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/cancel-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/cancel-icon/index.tsx new file mode 100644 index 0000000000000..bfd880882b96b --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/cancel-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CancelIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default CancelIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/cart-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/cart-icon/index.tsx new file mode 100644 index 0000000000000..3e8b98f95b5e4 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/cart-icon/index.tsx @@ -0,0 +1,49 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CartIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} +export default CartIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/cash-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/cash-icon/index.tsx new file mode 100644 index 0000000000000..ffe380bf6d16e --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/cash-icon/index.tsx @@ -0,0 +1,99 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CashIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + + + + + + + ) +} + +export default CashIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/channels-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/channels-icon.tsx new file mode 100644 index 0000000000000..37fc735db40a1 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/channels-icon.tsx @@ -0,0 +1,65 @@ +import React from "react" + +import IconProps from "./types/icon-type" + +const ChannelsIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + + ) +} + +export default ChannelsIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/check-circle-fill-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/check-circle-fill-icon/index.tsx new file mode 100644 index 0000000000000..61d2268599354 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/check-circle-fill-icon/index.tsx @@ -0,0 +1,28 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CheckCircleFillIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default CheckCircleFillIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/check-circle-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/check-circle-icon/index.tsx new file mode 100644 index 0000000000000..05fd4dcf3cc2c --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/check-circle-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CheckCircleIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default CheckCircleIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/check-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/check-icon.tsx new file mode 100644 index 0000000000000..7e7ccb3009583 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/check-icon.tsx @@ -0,0 +1,29 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const CheckIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default CheckIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-down.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-down.tsx new file mode 100644 index 0000000000000..5af31d7ddea63 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-down.tsx @@ -0,0 +1,29 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const ChevronDownIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default ChevronDownIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-left-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-left-icon/index.tsx new file mode 100644 index 0000000000000..469065c6818a2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-left-icon/index.tsx @@ -0,0 +1,29 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ChevronLeftIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default ChevronLeftIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-right-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-right-icon/index.tsx new file mode 100644 index 0000000000000..fad3f02718f56 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-right-icon/index.tsx @@ -0,0 +1,29 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ChevronRightIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default ChevronRightIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-up.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-up.tsx new file mode 100644 index 0000000000000..750baa77a1d51 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/chevron-up.tsx @@ -0,0 +1,29 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const ChevronUpIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default ChevronUpIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx new file mode 100644 index 0000000000000..c2ad4311f7f0c --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/clipboard-copy-icon/index.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ClipboardCopyIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default ClipboardCopyIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/clock-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/clock-icon/index.tsx new file mode 100644 index 0000000000000..14996231bd987 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/clock-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ClockIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default ClockIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/coins-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/coins-icon/index.tsx new file mode 100644 index 0000000000000..02225c60bda79 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/coins-icon/index.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CoinsIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default CoinsIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/corner-down-right-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/corner-down-right-icon/index.tsx new file mode 100644 index 0000000000000..7ad7aecc7ab42 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/corner-down-right-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CornerDownRightIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default CornerDownRightIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/cross-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/cross-icon/index.tsx new file mode 100644 index 0000000000000..aaeda63ad569a --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/cross-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CrossIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default CrossIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/crosshair-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/crosshair-icon/index.tsx new file mode 100644 index 0000000000000..77c14e9ccb2b2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/crosshair-icon/index.tsx @@ -0,0 +1,57 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CrosshairIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default CrosshairIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/customer-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/customer-icon/index.tsx new file mode 100644 index 0000000000000..6c1cdd49df425 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/customer-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const CustomerIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default CustomerIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/details-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/details-icon.tsx new file mode 100644 index 0000000000000..d0905c0798921 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/details-icon.tsx @@ -0,0 +1,57 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const DetailsIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default DetailsIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/discord-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/discord-icon.tsx new file mode 100644 index 0000000000000..ee6fa5d3c99a0 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/discord-icon.tsx @@ -0,0 +1,26 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const DiscordIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default DiscordIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/dollar-sign-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/dollar-sign-icon/index.tsx new file mode 100644 index 0000000000000..7174e441bbaf1 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/dollar-sign-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const DollarSignIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default DollarSignIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/down-left/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/down-left/index.tsx new file mode 100644 index 0000000000000..657b3944c9616 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/down-left/index.tsx @@ -0,0 +1,34 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const DownLeftIcon: React.FC = ({ + size = "16", + color = "#9CA3AF", +}) => { + return ( + + + + + ) +} + +export default DownLeftIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/download-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/download-icon.tsx new file mode 100644 index 0000000000000..9a76b151112f0 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/download-icon.tsx @@ -0,0 +1,42 @@ +import React, { FC } from "react" +import IconProps from "./types/icon-type" + +const DownloadIcon: FC = (props) => { + const { fill, size, ...attributes } = props + const line = fill || "#111827" + + return ( + + + + + + ) +} + +export default DownloadIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/duplicate-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/duplicate-icon.tsx new file mode 100644 index 0000000000000..d18ef78cdac74 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/duplicate-icon.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const DuplicateIcon: React.FC = ({ + size = "20px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default DuplicateIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/edit-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/edit-icon.tsx new file mode 100644 index 0000000000000..55a5ff1047e02 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/edit-icon.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const EditIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default EditIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/edit-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/edit-icon/index.tsx new file mode 100644 index 0000000000000..bad0f3bbc8a7b --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/edit-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const EditIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default EditIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/export-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/export-icon/index.tsx new file mode 100644 index 0000000000000..ee097067dcaf0 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/export-icon/index.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ExportIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default ExportIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/eye-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/eye-icon/index.tsx new file mode 100644 index 0000000000000..4c9707aa18f8e --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/eye-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const EyeIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default EyeIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/eye-off-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/eye-off-icon/index.tsx new file mode 100644 index 0000000000000..7f5e19f367439 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/eye-off-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const EyeOffIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default EyeOffIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/fast-delivery-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/fast-delivery-icon/index.tsx new file mode 100644 index 0000000000000..91a5386fb5e0a --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/fast-delivery-icon/index.tsx @@ -0,0 +1,64 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const FastDeliveryIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + + ) +} + +export default FastDeliveryIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/file-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/file-icon.tsx new file mode 100644 index 0000000000000..e3e8f43b80506 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/file-icon.tsx @@ -0,0 +1,55 @@ +import React, { FC } from "react" +import IconProps from "./types/icon-type" + +const FileIcon: FC = (props) => { + const { fill, size, ...attributes } = props + const line = fill || "#2DD4BF" + return ( + + + + + + + + ) +} + +export default FileIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/gear-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/gear-icon/index.tsx new file mode 100644 index 0000000000000..b44072f9f8c01 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/gear-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const GearIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default GearIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/gift-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/gift-icon/index.tsx new file mode 100644 index 0000000000000..dcbdcb16b7ab1 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/gift-icon/index.tsx @@ -0,0 +1,57 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const GiftIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default GiftIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/grip-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/grip-icon.tsx new file mode 100644 index 0000000000000..59ec878fae05a --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/grip-icon.tsx @@ -0,0 +1,64 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const GripIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + + ) +} + +export default GripIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/happy-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/happy-icon/index.tsx new file mode 100644 index 0000000000000..37b8ac558fd59 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/happy-icon/index.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const HappyIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default HappyIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/help-circle.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/help-circle.tsx new file mode 100644 index 0000000000000..3d8246e2e4e3a --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/help-circle.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const HelpCircleIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default HelpCircleIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/image-placeholder-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/image-placeholder-icon/index.tsx new file mode 100644 index 0000000000000..c63780bf6254f --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/image-placeholder-icon/index.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ImagePlaceholderIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default ImagePlaceholderIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/info-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/info-icon/index.tsx new file mode 100644 index 0000000000000..723aca577e7b7 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/info-icon/index.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const InfoIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default InfoIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/key-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/key-icon.tsx new file mode 100644 index 0000000000000..c70ad4833708f --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/key-icon.tsx @@ -0,0 +1,30 @@ +import React from "react" + +import IconProps from "./types/icon-type" + +const KeyIcon: React.FC = ({ + size = "32px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default KeyIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/list-arrow-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/list-arrow-icon/index.tsx new file mode 100644 index 0000000000000..03a2f2950a30f --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/list-arrow-icon/index.tsx @@ -0,0 +1,57 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const ListArrowIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default ListArrowIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/list-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/list-icon.tsx new file mode 100644 index 0000000000000..f9eb384900ada --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/list-icon.tsx @@ -0,0 +1,64 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const ListIcon: React.FC = ({ + size = "20px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + + ) +} + +export default ListIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/lock-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/lock-icon/index.tsx new file mode 100644 index 0000000000000..16f15eaea175a --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/lock-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const LockIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default LockIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/log-out-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/log-out-icon/index.tsx new file mode 100644 index 0000000000000..0f7038ac1d1e9 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/log-out-icon/index.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const SignOutIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default SignOutIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/long-arrow-right-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/long-arrow-right-icon/index.tsx new file mode 100644 index 0000000000000..4d486d3ed0ac7 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/long-arrow-right-icon/index.tsx @@ -0,0 +1,38 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const LongArrowRightIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + const width = +size * 2 + + return ( + + + + + ) +} + +export default LongArrowRightIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/mail-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/mail-icon/index.tsx new file mode 100644 index 0000000000000..8f87867fa81e9 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/mail-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const MailIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default MailIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/map-pin-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/map-pin-icon/index.tsx new file mode 100644 index 0000000000000..33edfff80593d --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/map-pin-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const MapPinIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default MapPinIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/medusa-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/medusa-icon/index.tsx new file mode 100644 index 0000000000000..aede6fea446a2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/medusa-icon/index.tsx @@ -0,0 +1,23 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const MedusaIcon: React.FC = ({ size = "48", ...attributes }) => { + const width = +size * 0.9375 // width relative to height (from size prop) + return ( + + + + ) +} + +export default MedusaIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/medusa-vice/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/medusa-vice/index.tsx new file mode 100644 index 0000000000000..37a198f1fe72d --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/medusa-vice/index.tsx @@ -0,0 +1,39 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const MedusaVice: React.FC = ({ + size = "96", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + + + ) +} + +export default MedusaVice diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/minus-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/minus-icon/index.tsx new file mode 100644 index 0000000000000..6ec583f91b36f --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/minus-icon/index.tsx @@ -0,0 +1,29 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const MinusIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default MinusIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/more-horizontal-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/more-horizontal-icon.tsx new file mode 100644 index 0000000000000..eeee2d1ac7bc9 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/more-horizontal-icon.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const MoreHorizontalIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default MoreHorizontalIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/package-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/package-icon/index.tsx new file mode 100644 index 0000000000000..dcbfbe5a3b0fe --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/package-icon/index.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const PackageIcon: React.FC = ({ + size = "16", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default PackageIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/percent-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/percent-icon/index.tsx new file mode 100644 index 0000000000000..3fab474b91233 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/percent-icon/index.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const PercentIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default PercentIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/plus-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/plus-icon/index.tsx new file mode 100644 index 0000000000000..f62578a31d430 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/plus-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const PlusIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default PlusIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/pointer-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/pointer-icon/index.tsx new file mode 100644 index 0000000000000..2eb7885f416da --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/pointer-icon/index.tsx @@ -0,0 +1,35 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const PointerIcon: React.FC = ({ + size = "16", + color = "#9CA3AF", + ...attributes +}) => { + return ( + + + + + ) +} + +export default PointerIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/publish-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/publish-icon/index.tsx new file mode 100644 index 0000000000000..39b9537bb6d03 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/publish-icon/index.tsx @@ -0,0 +1,57 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const PublishIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default PublishIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/refresh-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/refresh-icon.tsx new file mode 100644 index 0000000000000..a6d2e052d2655 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/refresh-icon.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const RefreshIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default RefreshIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/refund.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/refund.tsx new file mode 100644 index 0000000000000..bc9127e63ce3d --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/refund.tsx @@ -0,0 +1,57 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const RefundIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default RefundIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/sad-face-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/sad-face-icon/index.tsx new file mode 100644 index 0000000000000..62053bfe0a501 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/sad-face-icon/index.tsx @@ -0,0 +1,51 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const SadFaceIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default SadFaceIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/sale-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/sale-icon/index.tsx new file mode 100644 index 0000000000000..9eebba0b27b44 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/sale-icon/index.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const SaleIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default SaleIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/search-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/search-icon/index.tsx new file mode 100644 index 0000000000000..1c88da425d723 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/search-icon/index.tsx @@ -0,0 +1,27 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const SearchIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + Search + + + ) +} + +export default SearchIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/send-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/send-icon/index.tsx new file mode 100644 index 0000000000000..50a48367123b7 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/send-icon/index.tsx @@ -0,0 +1,33 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const SendIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default SendIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/sided-mouth-face/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/sided-mouth-face/index.tsx new file mode 100644 index 0000000000000..2448795b60dd2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/sided-mouth-face/index.tsx @@ -0,0 +1,51 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const SidedMouthFaceIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default SidedMouthFaceIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/sorting-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/sorting-icon/index.tsx new file mode 100644 index 0000000000000..ba6912b2fb232 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/sorting-icon/index.tsx @@ -0,0 +1,54 @@ +import clsx from "clsx" +import React from "react" +import IconProps from "../types/icon-type" + +type SortingIconProps = { + ascendingColor?: string + descendingColor?: string + isSorted?: "asc" | "desc" | false +} & IconProps + +const SortingIcon: React.FC = ({ + size = "24", + color = "currentColor", + ascendingColor, + descendingColor, + isSorted = false, + ...attributes +}) => { + return ( + + + + + ) +} + +export default SortingIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/sparkles-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/sparkles-icon/index.tsx new file mode 100644 index 0000000000000..5b33d75e1f989 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/sparkles-icon/index.tsx @@ -0,0 +1,29 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const SparklesIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default SparklesIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/stop-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/stop-icon.tsx new file mode 100644 index 0000000000000..90f790a8c9684 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/stop-icon.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const StopIcon: React.FC = ({ + size = "20px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default StopIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/tag-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/tag-icon/index.tsx new file mode 100644 index 0000000000000..7cbfd29a5b645 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/tag-icon/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const TagIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + ) +} + +export default TagIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/taxes-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/taxes-icon.tsx new file mode 100644 index 0000000000000..13bd2a3dc4adb --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/taxes-icon.tsx @@ -0,0 +1,54 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const TaxesIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + + ) +} + +export default TaxesIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/tile-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/tile-icon.tsx new file mode 100644 index 0000000000000..2fd60da800c3b --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/tile-icon.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const TileIcon: React.FC = ({ + size = "20px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default TileIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/trash-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/trash-icon.tsx new file mode 100644 index 0000000000000..ac951b00ed31f --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/trash-icon.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const TrashIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default TrashIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/triangle-right-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/triangle-right-icon/index.tsx new file mode 100644 index 0000000000000..a77d54b4f7bd6 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/triangle-right-icon/index.tsx @@ -0,0 +1,28 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const TriangleRightIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default TriangleRightIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/truck-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/truck-icon/index.tsx new file mode 100644 index 0000000000000..880efb9139928 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/truck-icon/index.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const TruckIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default TruckIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/types/icon-type.ts b/packages/admin-ui/ui/src/components/fundamentals/icons/types/icon-type.ts new file mode 100644 index 0000000000000..782681a462220 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/types/icon-type.ts @@ -0,0 +1,8 @@ +import React from "react" + +type IconProps = { + color?: string + size?: string | number +} & React.SVGAttributes + +export default IconProps diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/u-turn-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/u-turn-icon.tsx new file mode 100644 index 0000000000000..57a9ce98b0857 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/u-turn-icon.tsx @@ -0,0 +1,29 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const UTurnIcon: React.FC = ({ + size = "20px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + ) +} + +export default UTurnIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/unpublish-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/unpublish-icon/index.tsx new file mode 100644 index 0000000000000..c33da151df7cb --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/unpublish-icon/index.tsx @@ -0,0 +1,57 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const UnpublishIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + + ) +} + +export default UnpublishIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/upload-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/upload-icon/index.tsx new file mode 100644 index 0000000000000..5557b92cd6de4 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/upload-icon/index.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const UploadIcon: React.FC = ({ + size = "20", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default UploadIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/users-icon/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/users-icon/index.tsx new file mode 100644 index 0000000000000..9665bb52b73de --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/users-icon/index.tsx @@ -0,0 +1,50 @@ +import React from "react" +import IconProps from "../types/icon-type" + +const UsersIcon: React.FC = ({ + size = "24", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + + ) +} + +export default UsersIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/warning-circle.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/warning-circle.tsx new file mode 100644 index 0000000000000..d97d275068a90 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/warning-circle.tsx @@ -0,0 +1,42 @@ +import React, { FC } from "react" +import IconProps from "./types/icon-type" + +const WarningCircle: FC = (props) => { + const { fill, size, ...attributes } = props + const line = fill || "#111827" + + return ( + + + + + + ) +} + +export default WarningCircle diff --git a/packages/admin-ui/ui/src/components/fundamentals/icons/x-circle-icon.tsx b/packages/admin-ui/ui/src/components/fundamentals/icons/x-circle-icon.tsx new file mode 100644 index 0000000000000..318327a76a383 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/icons/x-circle-icon.tsx @@ -0,0 +1,43 @@ +import React from "react" +import IconProps from "./types/icon-type" + +const XCircleIcon: React.FC = ({ + size = "24px", + color = "currentColor", + ...attributes +}) => { + return ( + + + + + + ) +} + +export default XCircleIcon diff --git a/packages/admin-ui/ui/src/components/fundamentals/image-placeholder.tsx b/packages/admin-ui/ui/src/components/fundamentals/image-placeholder.tsx new file mode 100644 index 0000000000000..a6d7b7b2dc429 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/image-placeholder.tsx @@ -0,0 +1,11 @@ +import ImagePlaceholderIcon from "./icons/image-placeholder-icon" + +const ImagePlaceholder = () => { + return ( +
+ +
+ ) +} + +export default ImagePlaceholder diff --git a/packages/admin-ui/ui/src/components/fundamentals/input-container.tsx b/packages/admin-ui/ui/src/components/fundamentals/input-container.tsx new file mode 100644 index 0000000000000..81464a3a3cdc1 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/input-container.tsx @@ -0,0 +1,37 @@ +import clsx from "clsx" +import React, { MouseEventHandler } from "react" + +type InputContainerProps = React.HTMLAttributes & { + className?: string + onClick?: MouseEventHandler + onFocusLost?: () => void +} + +const InputContainer: React.FC = ({ + onClick, + onFocusLost, + children, + className, + ...props +}) => { + return ( +
{ + if (onFocusLost && !e.currentTarget.contains(e.relatedTarget)) { + onFocusLost() + } + }} + className={clsx([ + `bg-grey-5 inter-base-regular w-full p-3 flex h-18 flex-col cursor-text border border-grey-20 focus-within:shadow-input focus-within:border-violet-60 rounded-rounded`, + className, + ])} + > + {children} +
+ ) +} + +export default InputContainer diff --git a/packages/admin-ui/ui/src/components/fundamentals/input-header.tsx b/packages/admin-ui/ui/src/components/fundamentals/input-header.tsx new file mode 100644 index 0000000000000..c2bc79296fc78 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/input-header.tsx @@ -0,0 +1,38 @@ +import clsx from "clsx" +import React from "react" +import IconTooltip from "../molecules/icon-tooltip" + +export type InputHeaderProps = { + label?: string + required?: boolean + tooltipContent?: string + tooltip?: React.ReactNode + className?: string +} + +const InputHeader: React.FC = ({ + label, + required = false, + tooltipContent, + tooltip, + className, +}) => { + return ( +
+ + {required &&
*
} + {tooltip || tooltipContent ? ( +
+ {tooltip || } +
+ ) : null} +
+ ) +} + +export default InputHeader diff --git a/packages/admin-ui/ui/src/components/fundamentals/status-indicator/index.tsx b/packages/admin-ui/ui/src/components/fundamentals/status-indicator/index.tsx new file mode 100644 index 0000000000000..4a2993cd01157 --- /dev/null +++ b/packages/admin-ui/ui/src/components/fundamentals/status-indicator/index.tsx @@ -0,0 +1,34 @@ +import clsx from "clsx" +import React from "react" + +type StatusIndicatorProps = { + title?: string + variant: "primary" | "danger" | "warning" | "success" | "active" | "default" +} & React.HTMLAttributes + +const StatusIndicator: React.FC = ({ + title, + variant = "success", + className, + ...props +}) => { + const dotClass = clsx({ + "bg-teal-50": variant === "success", + "bg-rose-50": variant === "danger", + "bg-yellow-50": variant === "warning", + "bg-violet-60": variant === "primary", + "bg-emerald-40": variant === "active", + "bg-grey-40": variant === "default", + }) + return ( +
+
+ {title && {title}} +
+ ) +} + +export default StatusIndicator diff --git a/packages/admin-ui/ui/src/components/molecules/actionables.tsx b/packages/admin-ui/ui/src/components/molecules/actionables.tsx new file mode 100644 index 0000000000000..9a883ba65cf12 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/actionables.tsx @@ -0,0 +1,115 @@ +import * as DropdownMenu from "@radix-ui/react-dropdown-menu" +import clsx from "clsx" +import React from "react" +import Button from "../fundamentals/button" +import MoreHorizontalIcon from "../fundamentals/icons/more-horizontal-icon" + +export type ActionType = { + label: string + onClick: (e: React.MouseEvent) => void + variant?: "normal" | "danger" + disabled?: boolean + icon?: React.ReactNode +} + +type ActionablesProps = { + actions?: ActionType[] + customTrigger?: React.ReactNode + forceDropdown?: boolean +} + +const Actionables: React.FC = ({ + actions, + customTrigger, + forceDropdown = false, +}) => { + if (actions && (forceDropdown || actions.length > 1)) { + return ( +
+ + + {!customTrigger ? ( + + ) : ( + customTrigger + )} + + + + {actions.map((action, i) => { + return ( + + { + + } + + ) + })} + + +
+ ) + } + + if (customTrigger) { + const triggers = Array.isArray(customTrigger) + ? customTrigger + : [customTrigger] + return ( +
+ {triggers.map((trigger, i) => ( +
{trigger}
+ ))} +
+ ) + } + + const [action] = actions ?? [] + if (action) { + return ( +
+ +
+ ) + } + + return null +} + +export default Actionables diff --git a/packages/admin-ui/ui/src/components/molecules/activity-card/index.tsx b/packages/admin-ui/ui/src/components/molecules/activity-card/index.tsx new file mode 100644 index 0000000000000..adab095cfabd7 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/activity-card/index.tsx @@ -0,0 +1,88 @@ +import React, { MutableRefObject, ReactNode } from "react" +import StatusIndicator from "../../fundamentals/status-indicator" +import Tooltip from "../../atoms/tooltip" +import clsx from "clsx" + +export type ActivityCardProps = { + key?: string + title: string + icon?: ReactNode + relativeTimeElapsed?: string + date?: string | Date + shouldShowStatus?: boolean + children?: ReactNode[] +} + +export const ActivityCard: React.FC = ( + props: ActivityCardProps +) => { + const { + key, + title, + icon, + relativeTimeElapsed, + shouldShowStatus, + children + } = props + + const date = !!props.date && new Date(props.date).toLocaleDateString( + "en-us", + { + hour12: true, + day: "2-digit", + month: "short", + hour: "numeric", + minute: "numeric", + second: "numeric", + }) + const formattedDate = !!date && date.replace(",", " at") + + const getTimeElement = () => { + return ( +
+ { + !!relativeTimeElapsed && ( + {relativeTimeElapsed} + ) + } + { + shouldShowStatus && + + } +
+ ) + } + + return ( +
+
+
+
+
+ { + !!icon && icon + } + {title} +
+ + { + ((!!relativeTimeElapsed || shouldShowStatus)) && ( + formattedDate ? ( + + {getTimeElement()} + + ) : ( + getTimeElement() + ) + ) + } +
+ +
+ {children} +
+
+
+
+ ) +} diff --git a/packages/admin-ui/ui/src/components/molecules/amount-input/amount-and-currency-input.tsx b/packages/admin-ui/ui/src/components/molecules/amount-input/amount-and-currency-input.tsx new file mode 100644 index 0000000000000..8926d613632e5 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/amount-input/amount-and-currency-input.tsx @@ -0,0 +1,134 @@ +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" +import AmountField from "react-currency-input-field" +import { Option } from "../../../types/shared" +import { currencies } from "../../../utils/currencies" +import InputError from "../../atoms/input-error" +import InputHeader from "../../fundamentals/input-header" +import { NextSelect } from "../select/next-select" + +type Value = { + amount: number | null | undefined + currency: Option | null | undefined +} + +type Props = { + label?: string + errors?: Record + name?: string + onChange: (value: Value) => void + onBlur?: React.FocusEventHandler + value: Value +} + +const AmountAndCurrencyInput = ({ + label, + errors, + name, + value, + onChange, + onBlur, +}: Props) => { + const { symbol_native, decimal_digits } = useMemo(() => { + let symbol_native = "$" + let decimal_digits = 2 + + if (value.currency) { + const currency = currencies[value.currency.value.toUpperCase()] + + symbol_native = currency.symbol_native + decimal_digits = currency.decimal_digits + } + + return { symbol_native, decimal_digits } + }, [value]) + + const step = useMemo(() => 10 ** -decimal_digits, [decimal_digits]) + + const getFormattedValue = useCallback( + (value: number) => { + return `${value / 10 ** decimal_digits}` + }, + [decimal_digits] + ) + + const [formattedValue, setFormattedValue] = useState( + value.amount !== undefined && value.amount !== null + ? getFormattedValue(value.amount) + : undefined + ) + + const inputRef = useRef(null) + + /** + * Update the amount when the decimal digits change + */ + useEffect(() => { + inputRef.current?.dispatchEvent(new Event("blur")) + }, [decimal_digits]) + + const onCurrencyChange = (currency?: Option | null) => { + onChange({ amount: value.amount, currency: currency }) + } + + /** + * On amount change, update the amount and formatted value. + * The amount passed to the onChange function is the DB persisted value, + * the formatted value is the value that is displayed in the input. + */ + const onAmountChange = (amount?: string, floatValue?: number | null) => { + let numericalValue: number | null | undefined = 0 + + if (floatValue) { + numericalValue = Math.round(floatValue * 10 ** decimal_digits) + } + + onChange({ amount: numericalValue, currency: value.currency }) + setFormattedValue(amount) + } + + return ( +
+ {label && } +
+
+ ({ + value: c.code, + label: c.code, + }))} + isMulti={false} + onBlur={onBlur} + value={value.currency} + onChange={onCurrencyChange} + /> +
+ + onAmountChange(value, values?.float) + } + allowNegativeValue={false} + placeholder="-" + decimalScale={decimal_digits} + className="bg-transparent outline-none outline-0 w-full remove-number-spinner leading-base text-grey-90 font-normal caret-violet-60 placeholder-grey-40 text-right" + /> +
+

{symbol_native}

+
+
+
+
+ +
+ ) +} + +export default AmountAndCurrencyInput diff --git a/packages/admin-ui/ui/src/components/molecules/amount-input/amount-input.tsx b/packages/admin-ui/ui/src/components/molecules/amount-input/amount-input.tsx new file mode 100644 index 0000000000000..b746e272e5dfe --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/amount-input/amount-input.tsx @@ -0,0 +1,92 @@ +import clsx from "clsx" +import React, { useEffect, useState } from "react" +import AmountField from "react-currency-input-field" +import { currencies } from "../../../utils/currencies" +import InputError from "../../atoms/input-error" +import InputHeader from "../../fundamentals/input-header" + +type Props = { + currencyCode: string + value?: number | null + onChange: (amount?: number | null) => void + errors?: { [x: string]: unknown } + name?: string + label?: string +} + +const AmountInput = ({ + name, + label, + currencyCode, + errors, + value, + onChange, +}: Props) => { + const { symbol_native, decimal_digits } = currencies[ + currencyCode.toUpperCase() + ] + + const getFormattedValue = (value: number) => { + return `${value / 10 ** decimal_digits}` + } + + const [formattedValue, setFormattedValue] = useState( + value !== undefined && value !== null ? getFormattedValue(value) : undefined + ) + + useEffect(() => { + if (value) { + setFormattedValue(getFormattedValue(value)) + } + }, [value, decimal_digits]) + + const onAmountChange = (value?: string, floatValue?: number | null) => { + if (floatValue) { + const numericalValue = Math.round(floatValue * 10 ** decimal_digits) + onChange(numericalValue) + } else { + onChange(0) + } + setFormattedValue(value) + } + + const step = 10 ** -decimal_digits + + return ( +
+ {label && } +
+
+

{currencyCode.toUpperCase()}

+
+ +
+ + onAmountChange(value, values?.float) + } + allowNegativeValue={false} + placeholder="-" + decimalScale={decimal_digits} + className="bg-transparent outline-none outline-0 w-full remove-number-spinner leading-base text-grey-90 font-normal caret-violet-60 placeholder-grey-40 text-right" + /> +
+

{symbol_native}

+
+
+
+ +
+ ) +} + +export default AmountInput diff --git a/packages/admin-ui/ui/src/components/molecules/amount-input/index.ts b/packages/admin-ui/ui/src/components/molecules/amount-input/index.ts new file mode 100644 index 0000000000000..107e86ddd8f7a --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/amount-input/index.ts @@ -0,0 +1,4 @@ +import AmountAndCurrencyInput from "./amount-and-currency-input" +import AmountInput from "./amount-input" + +export { AmountAndCurrencyInput, AmountInput } diff --git a/packages/admin-ui/ui/src/components/molecules/availability-duration/index.tsx b/packages/admin-ui/ui/src/components/molecules/availability-duration/index.tsx new file mode 100644 index 0000000000000..bf62a87080706 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/availability-duration/index.tsx @@ -0,0 +1,86 @@ +import { parse } from "iso8601-duration" +import React, { useEffect, useState } from "react" +import InputField from "../input" + +type AvailabilityDurationProps = { + onChange: React.Dispatch> + value: string | undefined +} + +const getValue = (e: React.ChangeEvent) => + parseFloat(e.target.value) + +const AvailabilityDuration: React.FC = ({ + value, + onChange, +}) => { + const duration = value ? parse(value) : {} + const [durationYears, setDurationYears] = useState(duration.years || 0) + const [durationMonths, setDurationMonths] = useState(duration.months || 0) + const [durationDays, setDurationDays] = useState(duration.days || 0) + const [durationHours, setDurationHours] = useState(duration.hours || 0) + const [durationMinutes, setDurationMinutes] = useState(duration.minutes || 0) + + useEffect(() => { + const isoString = `P${durationYears || 0}Y${durationMonths || 0}M${ + durationDays || 0 + }DT${durationHours || 0}H${durationMinutes || 0}M` + + onChange(isoString) + }, [ + durationYears, + durationMonths, + durationDays, + durationHours, + durationMinutes, + ]) + + return ( +
+
+ setDurationYears(getValue(e))} + min={0} + /> + setDurationMonths(getValue(e))} + min={0} + /> + setDurationDays(getValue(e))} + min={0} + /> + setDurationHours(getValue(e))} + min={0} + /> + setDurationMinutes(getValue(e))} + min={0} + /> +
+
+ ) +} + +export default AvailabilityDuration diff --git a/packages/admin-ui/ui/src/components/molecules/banner-card/index.tsx b/packages/admin-ui/ui/src/components/molecules/banner-card/index.tsx new file mode 100644 index 0000000000000..8bcf7c316ccca --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/banner-card/index.tsx @@ -0,0 +1,73 @@ +import React from "react" +import Actionables, { ActionType } from "../../molecules/actionables" + +type BannerCardProps = { + actions?: ActionType[] + title: string + thumbnail: string | null +} & React.RefAttributes + +type BannerCardDescriptionProps = { + cta?: { + label: string + onClick: (e: React.MouseEvent) => void + } +} + +const BannerCard: React.FC & { + Description: React.FC + Footer: React.FC +} = ({ title, thumbnail, actions, children }) => { + return ( +
+
+ {thumbnail && ( +
+ Thumbnail +
+ )} +
+
+

{title}

+ +
+ {children} +
+
+
+ ) +} + +const Description: React.FC = ({ + cta, + children, +}) => { + return ( +
+

+ {children} +

+ {cta && ( + + )} +
+ ) +} + +const Footer: React.FC = ({ children }) => { + return
{children}
+} + +BannerCard.Description = Description +BannerCard.Footer = Footer + +export default BannerCard diff --git a/packages/admin-ui/ui/src/components/molecules/batch-job-file-card/index.tsx b/packages/admin-ui/ui/src/components/molecules/batch-job-file-card/index.tsx new file mode 100644 index 0000000000000..850341bba7f9b --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/batch-job-file-card/index.tsx @@ -0,0 +1,42 @@ +import React, { ReactNode } from "react" + +type Props = { + fileName: string + fileSize?: string + icon?: ReactNode + onClick?: () => void +} + +const BatchJobFileCard = ({ fileName, fileSize, icon, onClick }: Props) => { + const preparedOnClick = (onClick ?? (() => void 0)) + + return ( +
+
+ {!!icon && icon} +
+ +
+
+ {fileName} +
+ + {!!fileSize && ( +
+ {fileSize} +
+ )} +
+
+ ) +} + +export default BatchJobFileCard diff --git a/packages/admin-ui/ui/src/components/molecules/breadcrumb/index.tsx b/packages/admin-ui/ui/src/components/molecules/breadcrumb/index.tsx new file mode 100644 index 0000000000000..7acb5dd2683bf --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/breadcrumb/index.tsx @@ -0,0 +1,42 @@ +import clsx from "clsx" +import React from "react" +import { useNavigate } from "react-router-dom" +import ChevronRightIcon from "../../fundamentals/icons/chevron-right-icon" + +type BreadcrumbProps = React.HtmlHTMLAttributes & { + previousRoute?: string + previousBreadcrumb?: string + currentPage: string +} + +const Breadcrumb: React.FC = ({ + previousRoute = "/a/settings", + previousBreadcrumb = "Settings", + currentPage, + className, + ...props +}) => { + const navigate = useNavigate() + return ( +
+ navigate(previousRoute)} + > + {previousBreadcrumb} + + + + + {currentPage} +
+ ) +} + +export default Breadcrumb diff --git a/packages/admin-ui/ui/src/components/molecules/collapsible-tree/index.tsx b/packages/admin-ui/ui/src/components/molecules/collapsible-tree/index.tsx new file mode 100644 index 0000000000000..13a48de7c528c --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/collapsible-tree/index.tsx @@ -0,0 +1,187 @@ +import React from "react" +import clsx from "clsx" + +import MoreHorizontalIcon from "../../fundamentals/icons/more-horizontal-icon" +import MinusIcon from "../../fundamentals/icons/minus-icon" +import PlusIcon from "../../fundamentals/icons/plus-icon" +import Button from "../../fundamentals/button" +import Actionables, { ActionType } from "../actionables" + +/* ---------------------------- CollapsibleTree ------------------------------------ */ + +type CollapsibleTreeType = React.FC & { + Parent: React.FC + Leaf: React.FC + Content: React.FC +} + +type TCollapsibleTreeContext = { + open: boolean + handleOpen: () => void + handleClose: () => void + toggle: () => void +} + +const CollapsibleTreeContext = React.createContext( + null +) + +export const CollapsibleTree: CollapsibleTreeType = ({ children }) => { + const [open, setOpen] = React.useState(false) + + return ( + setOpen(true), + handleClose: () => setOpen(false), + toggle: () => setOpen((open) => !open), + }} + > + {children} + + ) +} + +CollapsibleTreeContext.displayName = "CollapsibleTreeContext" + +const useCollapsibleTree = () => { + const context = React.useContext(CollapsibleTreeContext) + if (!context) { + throw new Error("useCollapsibleTree must be a child of CollapsibleTree") + } + return context +} + +/* ---------------------------- CollapsibleTreeContent ------------------------------------ */ + +type CollapsibleTreeContentProps = React.HTMLAttributes + +const CollapsibleTreeContent: React.FC = ({ + children, + className, + ...props +}) => { + const { open } = useCollapsibleTree() + return ( +
+ {children} +
+ ) +} + +CollapsibleTree.Content = CollapsibleTreeContent + +/* ---------------------------- CollapsibleTreeParent ------------------------------------ */ + +type CollapsibleTreeParentProps = { + actions?: ActionType[] + className?: string +} + +const CollapsibleTreeParent: React.FC = ({ + actions, + className, + children, +}) => { + const { open, toggle } = useCollapsibleTree() + return ( +
+ +
+
{children}
+
+ {actions && ( + + )} +
+ +
+
+ +
+ ) +} + +CollapsibleTree.Parent = CollapsibleTreeParent + +/* ---------------------------- CollapsibleTreeLeaf ------------------------------------ */ + +type CollapsibleTreeLeafProps = { + actions?: ActionType[] + className?: string +} + +const CollapsibleTreeLeaf: React.FC = ({ + className, + children, + actions, +}) => { + return ( +
+
+
+
+
+
+ + {children} + {actions && ( + + )} + +
+ ) +} + +CollapsibleTree.Leaf = CollapsibleTreeLeaf + +const Container: React.FC<{ className?: string }> = ({ + children, + className, +}) => { + return ( +
+ {children} +
+ ) +} + +const Trigger = () => { + return ( + + ) +} diff --git a/packages/admin-ui/ui/src/components/molecules/connected-form.tsx b/packages/admin-ui/ui/src/components/molecules/connected-form.tsx new file mode 100644 index 0000000000000..7242ee5314f3e --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/connected-form.tsx @@ -0,0 +1,20 @@ +import type { ReactElement } from "react" +import type { FieldValues, UseFormReturn } from "react-hook-form" +import { useFormContext } from "react-hook-form" + +interface ConnectedFormProps { + children(children: UseFormReturn): ReactElement +} + +/** + * Utility component for nested forms. + */ +const ConnectedForm = ({ + children, +}: ConnectedFormProps) => { + const methods = useFormContext() + + return children({ ...methods }) +} + +export default ConnectedForm diff --git a/packages/admin-ui/ui/src/components/molecules/customer-avatar-item/index.tsx b/packages/admin-ui/ui/src/components/molecules/customer-avatar-item/index.tsx new file mode 100644 index 0000000000000..76334c90aeada --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/customer-avatar-item/index.tsx @@ -0,0 +1,34 @@ +import React from "react" +import Avatar from "../../atoms/avatar" + +type CustomerAvatarItemProps = { + color?: string + customer: { + first_name?: string + last_name?: string + email: string + } +} + +const CustomerAvatarItem: React.FC = ({ + color = "bg-violet-60", + customer, +}: CustomerAvatarItemProps) => { + const identifier = + customer.first_name || customer.last_name + ? `${customer.first_name} ${customer.last_name}` + : customer.email + ? customer.email + : "-" + + return ( +
+
+ +
+ {identifier} +
+ ) +} + +export default CustomerAvatarItem diff --git a/packages/admin-ui/ui/src/components/molecules/customers-groups-summary/index.tsx b/packages/admin-ui/ui/src/components/molecules/customers-groups-summary/index.tsx new file mode 100644 index 0000000000000..f797450e75ef2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/customers-groups-summary/index.tsx @@ -0,0 +1,33 @@ +import { sortBy } from "lodash" + +import { CustomerGroup } from "@medusajs/medusa" + +/** + * Customers Associated Groups props + */ +interface P { + groups: CustomerGroup[] +} + +/* + * Render a summary of groups to which the customer belongs + */ +function CustomersGroupsSummary(props: P) { + const groups = sortBy(props.groups, "name") + if (!groups.length) { + return null + } + + const left = groups.length - 1 + const leadName = groups[0].name + const allGroups = groups.map((g) => g.name).join(", ") + + return ( +
+ {leadName} + {!!left && + {left} more} +
+ ) +} + +export default CustomersGroupsSummary diff --git a/packages/admin-ui/ui/src/components/molecules/emoji-picker/index.tsx b/packages/admin-ui/ui/src/components/molecules/emoji-picker/index.tsx new file mode 100644 index 0000000000000..69287233d81f0 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/emoji-picker/index.tsx @@ -0,0 +1,41 @@ +import * as DropdownMenu from "@radix-ui/react-dropdown-menu" +import Picker, { EmojiStyle, SkinTones } from "emoji-picker-react" +import React from "react" +import Button from "../../fundamentals/button" +import HappyIcon from "../../fundamentals/icons/happy-icon" + +type indexProps = { + onEmojiClick: (emoji: string) => void +} + +const EmojiPicker: React.FC = ({ onEmojiClick }) => { + return ( + + + + + + + onEmojiClick(emojiData.emoji)} + defaultSkinTone={SkinTones.NEUTRAL} + emojiStyle={EmojiStyle.NATIVE} + skinTonesDisabled + searchPlaceHolder={"Search Emoji..."} + /> + + + ) +} + +export default EmojiPicker diff --git a/packages/admin-ui/ui/src/components/molecules/filter-dropdown/container.tsx b/packages/admin-ui/ui/src/components/molecules/filter-dropdown/container.tsx new file mode 100644 index 0000000000000..1ef51aa675d94 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/filter-dropdown/container.tsx @@ -0,0 +1,89 @@ +import * as RadixPopover from "@radix-ui/react-popover" +import React, { + PropsWithChildren, + ReactNode, + useEffect, + useRef, + useState, +} from "react" +import { useWindowDimensions } from "../../../hooks/use-window-dimensions" +import Button from "../../fundamentals/button" + +type FilterDropdownContainerProps = { + submitFilters: () => void + clearFilters: () => void + triggerElement: ReactNode +} + +const FilterDropdownContainer = ({ + submitFilters, + clearFilters, + triggerElement, + children, +}: PropsWithChildren) => { + const { height } = useWindowDimensions() + const ref = useRef(null) + const [isOpen, setIsOpen] = useState(false) + const [heightStyle, setHeightStyle] = useState({ + maxHeight: height, + }) + + useEffect(() => { + setHeightStyle({ + maxHeight: height - (ref?.current?.getBoundingClientRect().y ?? 0) - 50, + }) + }, [ref]) + + const onSubmit = () => { + setIsOpen(false) + submitFilters() + } + + const onClear = () => { + setIsOpen(false) + clearFilters() + } + + return ( + + + {triggerElement} + + +
+ + +
+ {React.Children.map(children, (child) => { + return ( +
+ {child} +
+ ) + })} +
+
+ ) +} + +export default FilterDropdownContainer diff --git a/packages/admin-ui/ui/src/components/molecules/filter-dropdown/item.tsx b/packages/admin-ui/ui/src/components/molecules/filter-dropdown/item.tsx new file mode 100644 index 0000000000000..ea5cbb073a33d --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/filter-dropdown/item.tsx @@ -0,0 +1,551 @@ +import * as RadixCollapsible from "@radix-ui/react-collapsible" +import * as RadixPopover from "@radix-ui/react-popover" +import clsx from "clsx" +import moment from "moment" +import { useEffect, useMemo, useState } from "react" +import { DateFilters } from "../../../utils/filters" +import { addHours, atMidnight, dateToUnixTimestamp } from "../../../utils/time" +import { CalendarComponent } from "../../atoms/date-picker/date-picker" +import Spinner from "../../atoms/spinner" +import ArrowRightIcon from "../../fundamentals/icons/arrow-right-icon" +import CheckIcon from "../../fundamentals/icons/check-icon" +import ChevronUpIcon from "../../fundamentals/icons/chevron-up" +import InputField from "../input" + +const DAY_IN_SECONDS = 86400 + +const FilterDropdownItem = ({ + filterTitle, + options, + filters, + open, + setFilter, + isLoading, + hasMore, + hasPrev, + onShowNext, + onShowPrev, +}) => { + const prefilled = useMemo(() => { + try { + const toReturn = filters.reduce((acc, f) => { + acc[f] = true + return acc + }, {}) + return toReturn + } catch (e) { + return {} + } + }, [filters]) + + const [checked, setChecked] = useState(prefilled) + + const handlePrev = () => { + if (onShowPrev) { + onShowPrev() + } + } + + const handleNext = () => { + if (onShowNext) { + onShowNext() + } + } + + useEffect(() => { + if (!open) { + setChecked({}) + } + }, [open]) + + const onCheck = (filter) => { + const checkedState = checked + + if (!checkedState[filter]) { + checkedState[filter] = true + } else { + checkedState[filter] = false + } + + const newFilter = Object.entries(checkedState).reduce( + (acc, [key, value]) => { + if (value === true) { + acc.push(key) + } + return acc + }, + [] + ) + + setChecked(checkedState) + + setFilter({ open: open, filter: newFilter }) + } + + return ( +
+ setFilter({ filter: filters, open })} + > + +
+
+ + {open && } + +
+ + {filterTitle} +
+ {open && ( + + + + )} +
+ + {hasPrev && ( +
+ +
+ )} + {isLoading ? ( +
+ +
+ ) : filterTitle === "Date" ? ( + + ) : ( + options.map((el, i) => { + let value: string + let label: string + + if (typeof el === "string") { + value = el + label = el + } else { + value = el.value + label = el.label + } + + return ( +
onCheck(value)} + > +
+ + {checked[value] === true && } + +
+ + {label} +
+ ) + }) + )} + {hasMore && ( +
+ +
+ )} +
+
+
+ ) +} + +export default FilterDropdownItem + +const parseDateFilter = (filter) => { + if (!filter) { + return {} + } + const dateEntries = Object.entries(filter) + + /** + * From a query object we need to figure out which date filter that is + * being used of the following: + * + * InTheLast: { gt: "x|[days|months]" } + * OlderThan: { lt: "x|[days|months]" } + * Between: { lt: [ts], gt: [ts] } + * After: { gt: [ts] } + * Before: { lt: [ts] }, + * EqualTo: { lt: [midnight], gt: [morning] } + * + */ + + const flags = { + sawGt: false, + sawLt: false, + sawGtRelative: false, + sawLtRelative: false, + } + + for (const [key, value] of dateEntries) { + switch (key) { + case "gt": { + flags.sawGt = true + flags.sawGtRelative = value.includes("|") + break + } + case "lt": { + flags.sawLt = true + flags.sawLtRelative = value.includes("|") + break + } + default: { + break + } + } + } + + if (flags.sawGt && flags.sawGtRelative) { + const [amount, daysMonths] = filter.gt.split("|") + return { + filterType: DateFilters.InTheLast, + daysMonthsValue: daysMonths, + relativeAmount: amount, + value: null, + } + } + + if (flags.sawLt && flags.sawLtRelative) { + const [amount, daysMonths] = filter.lt.split("|") + return { + filterType: DateFilters.OlderThan, + daysMonthsValue: daysMonths, + relativeAmount: amount, + value: null, + } + } + + if (flags.sawLt && flags.sawGt) { + const startDate = filter.gt + const endDate = filter.lt + + if (endDate - startDate === DAY_IN_SECONDS) { + return { + filterType: DateFilters.EqualTo, + value: moment.unix(startDate).toDate(), + } + } + } + + if (flags.sawLt) { + const endDate = filter.lt + return { + filterType: DateFilters.Before, + value: moment.unix(endDate).toDate(), + } + } + + if (flags.sawGt) { + const startDate = filter.gt + return { + filterType: DateFilters.After, + value: moment.unix(startDate).toDate(), + } + } + + return {} +} + +const DateFilter = ({ + options, + open, + setFilter, + existingDate, + existingFilter, +}) => { + const initialVals = useMemo(() => { + const parsed = parseDateFilter(existingDate) + return { + filterType: options[0], + value: null, + relativeAmount: undefined, + daysMonthsValue: "days", + ...parsed, + } + }, [existingDate]) + + const [currentFilter, setCurrentFilter] = useState(initialVals.filterType) + const [relativeAmount, setRelativeAmount] = useState( + initialVals.relativeAmount + ) + const [daysMonthsValue, setDaysMonthsValue] = useState( + initialVals.daysMonthsValue + ) + const [startDate, setStartDate] = useState(initialVals.value) + + useEffect(() => { + switch (currentFilter) { + case DateFilters.InTheLast: + case DateFilters.OlderThan: + setFilter({ + open: true, + filter: handleDateFormat(relativeAmount), + }) + break + case DateFilters.EqualTo: + setFilter({ + open: true, + filter: handleDateFormat(startDate), + }) + break + default: + setFilter({ + open: true, + filter: handleDateFormat(startDate), + }) + } + }, [currentFilter, relativeAmount, daysMonthsValue, startDate]) + + const handleDateFormat = (value: string | null) => { + switch (currentFilter) { + case DateFilters.InTheLast: { + // Relative date + return { gt: `${value}|${daysMonthsValue}` } + } + + case DateFilters.OlderThan: { + // Relative date: + return { lt: `${value}|${daysMonthsValue}` } + } + + case DateFilters.EqualTo: { + const momentToSet = atMidnight(value) + if (momentToSet) { + const day = dateToUnixTimestamp(momentToSet.toDate()) + const nextDay = dateToUnixTimestamp( + addHours(momentToSet, 24).toDate() + ) + return { gt: day, lt: nextDay } + } else { + return {} + } + } + + case DateFilters.After: { + const momentToSet = atMidnight(value) + if (momentToSet) { + return { gt: dateToUnixTimestamp(momentToSet.toDate()) } + } else { + return {} + } + } + + case DateFilters.Before: { + const momentToSet = atMidnight(value) + if (momentToSet) { + return { lt: dateToUnixTimestamp(momentToSet.toDate()) } + } else { + return {} + } + } + + default: { + return {} + } + } + } + + const handleFilterContent = () => { + switch (currentFilter) { + case DateFilters.InTheLast: + case DateFilters.OlderThan: + return ( +
+ { + setRelativeAmount(e.target.value) + }} + /> + + + + + +
+ } + > + + +
+ ) + case DateFilters.EqualTo: + case DateFilters.After: + case DateFilters.Before: + return ( +
+ + + + + +
+ } + > + { + setStartDate(date) + }} + /> + +
+ ) + } + } + return ( +
+ + + + + +
+ } + > + setCurrentFilter(filter)} + selectedItem={currentFilter} + /> + + {currentFilter &&
{handleFilterContent()}
} +
+ ) +} + +const PopoverOptions = ({ options, onClick, selectedItem }) => { + return ( + <> + {options.map((item) => ( +
{ + e.stopPropagation() + onClick(item) + }} + className={clsx( + "px-3 py-1.5 my-1 flex items-center rounded hover:bg-grey-5 cursor-pointer", + { + "inter-small-semibold": item === selectedItem, + "inter-small-regular": item !== selectedItem, + } + )} + > +
+ {item === selectedItem && ( +
+ )} +
+ {item} +
+ ))} + + ) +} + +const RightPopover = ({ trigger, children }) => ( + + + {trigger} + + + {children} + + +) diff --git a/packages/admin-ui/ui/src/components/molecules/filter-dropdown/save-field.tsx b/packages/admin-ui/ui/src/components/molecules/filter-dropdown/save-field.tsx new file mode 100644 index 0000000000000..694a82ed8f445 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/filter-dropdown/save-field.tsx @@ -0,0 +1,45 @@ +import React from "react" +import Button from "../../fundamentals/button" +import InputField from "../input" +import { trim } from "lodash" + +type SaveFilterItemProps = { + saveFilter: () => void + name: string + setName: (name: string) => void +} + +const SaveFilterItem: React.FC = ({ + saveFilter, + setName, + name, +}) => { + const onSave = () => { + const trimmedName = trim(name) + if (trimmedName !== "") { + saveFilter() + setName("") + } + } + + return ( +
+ setName(e.target.value)} + value={name} + /> + +
+ ) +} + +export default SaveFilterItem diff --git a/packages/admin-ui/ui/src/components/molecules/filter-tab/index.tsx b/packages/admin-ui/ui/src/components/molecules/filter-tab/index.tsx new file mode 100644 index 0000000000000..c808654bfa571 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/filter-tab/index.tsx @@ -0,0 +1,62 @@ +import React from "react" +import clsx from "clsx" + +import CrossIcon from "../../fundamentals/icons/cross-icon" + +type FilterTabProps = { + label?: string + isActive?: boolean + onClick?: React.MouseEventHandler + removable?: boolean + onRemove?: () => void +} + +export const FilterTab: React.FC = ({ + label, + isActive, + onClick, + removable, + onRemove, +}) => { + const handleClick = (e) => { + if (typeof onClick !== "undefined") { + onClick(e) + } + } + + const handleRemove = () => { + if (typeof onRemove !== "undefined") { + onRemove() + } + } + + const handleKeyPress = (e) => { + if (removable && onRemove) { + if (e.key === "Backspace") { + onRemove() + } + } + } + + return ( + + ) +} + +export default FilterTab diff --git a/packages/admin-ui/ui/src/components/molecules/form-error-toaster/index.tsx b/packages/admin-ui/ui/src/components/molecules/form-error-toaster/index.tsx new file mode 100644 index 0000000000000..98dcb974de693 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/form-error-toaster/index.tsx @@ -0,0 +1,41 @@ +import React from "react" +import { toast as Controller, Toast } from "react-hot-toast" +import ToasterContainer from "../../atoms/toaster-container" +import CrossIcon from "../../fundamentals/icons/cross-icon" +import XCircleIcon from "../../fundamentals/icons/x-circle-icon" + +type FormErrorToasterProps = { + toast: Toast + message: string | React.ReactNode + title: string +} + +const FormErrorToaster: React.FC = ({ + toast, + message, + title, +}) => { + const onDismiss = () => { + Controller.dismiss(toast.id) + } + + return ( + +
+ +
+
+ {title} + {message} +
+
+ + Close +
+
+ ) +} + +export default FormErrorToaster diff --git a/packages/admin-ui/ui/src/components/molecules/form-toaster/index.tsx b/packages/admin-ui/ui/src/components/molecules/form-toaster/index.tsx new file mode 100644 index 0000000000000..ef6c3d8c4a2a8 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/form-toaster/index.tsx @@ -0,0 +1,177 @@ +import * as Dropdown from "@radix-ui/react-dropdown-menu" +import clsx from "clsx" +import React, { Children, HTMLAttributes, useMemo } from "react" +import { Toast } from "react-hot-toast" +import Spinner from "../../atoms/spinner" +import ChevronDownIcon from "../../fundamentals/icons/chevron-down" +import RefreshIcon from "../../fundamentals/icons/refresh-icon" + +type FormToasterContainerProps = { + toast?: Toast + isLoading?: boolean + loadingMessage?: string + unsavedChangesMessage?: string + icon?: React.ReactNode +} + +type MultiActionButtonProps = { + actions: { + onClick: () => void | Promise + label: string + icon?: any + }[] + className?: string +} + +const FormToasterContainer: React.FC & { + Actions: React.FC + DiscardButton: React.FC> + ActionButton: React.FC> + MultiActionButton: React.FC +} = ({ + children, + toast, + isLoading = false, + loadingMessage = "Hang on, this may take a few moments...", + unsavedChangesMessage = "You have unsaved changes", + icon = , +}) => { + const content = useMemo(() => { + if (isLoading) { + return ( +
+ + + + {loadingMessage} +
+ ) + } else { + return ( + <> +
+ {icon} + {unsavedChangesMessage} +
+ {children} + + ) + } + }, [isLoading, children]) + + return ( +
+
+ {content} +
+
+ ) +} + +const Actions: React.FC = ({ children }) => { + return ( +
+ {Children.map(children, (child) => { + return ( +
+ {child} +
+ ) + })} +
+ ) +} + +const DiscardButton: React.FC> = ({ + children, + className, + ...props +}) => { + return ( + + ) +} + +const ActionButton: React.FC> = ({ + children, + className, + ...props +}) => { + return ( + + ) +} + +const MultiActionButton: React.FC = ({ + children, + className, + actions, +}) => { + return ( + + + {children} + + + + + {actions.map((action, i) => { + return ( + + + + ) + })} + + + ) +} + +FormToasterContainer.Actions = Actions +FormToasterContainer.DiscardButton = DiscardButton +FormToasterContainer.ActionButton = ActionButton +FormToasterContainer.MultiActionButton = MultiActionButton + +export default FormToasterContainer diff --git a/packages/admin-ui/ui/src/components/molecules/generating-input/index.tsx b/packages/admin-ui/ui/src/components/molecules/generating-input/index.tsx new file mode 100644 index 0000000000000..bb731165f7b59 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/generating-input/index.tsx @@ -0,0 +1,93 @@ +import React, { + InputHTMLAttributes, + useImperativeHandle, + useRef, + useState, +} from "react" +import { generatePromotionCode } from "../../../utils/generate-promotion-code" +import RefreshIcon from "../../fundamentals/icons/refresh-icon" +import InputContainer from "../../fundamentals/input-container" +import InputHeader from "../../fundamentals/input-header" +import { InputProps } from "../input" + +const GeneratingInput = React.forwardRef( + ( + { + placeholder, + label, + name, + required, + deletable, + onDelete, + onChange, + onFocus, + tooltipContent, + tooltip, + props, + className, + value: valueProp, + ...fieldProps + }: Omit, + ref + ) => { + const [value, setValue] = useState< + InputHTMLAttributes["value"] + >(valueProp || "") + const inputRef = useRef(null) + + useImperativeHandle(ref, () => inputRef.current) + + const generateCode = () => { + setValue(generatePromotionCode()) + } + + const handleChange = (e: React.ChangeEvent) => { + setValue(e.target.value) + if (onChange) { + onChange(e) + } + } + + return ( + !fieldProps.disabled && inputRef?.current?.focus()} + {...props} + > +
+ + {!value && ( + + )} +
+
+ + {value && ( + + )} +
+
+ ) + } +) + +export default GeneratingInput diff --git a/packages/admin-ui/ui/src/components/molecules/grid-input/index.tsx b/packages/admin-ui/ui/src/components/molecules/grid-input/index.tsx new file mode 100644 index 0000000000000..3042034fe7366 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/grid-input/index.tsx @@ -0,0 +1,15 @@ +import React from "react" + +type GridInputProps = React.InputHTMLAttributes + +const GridInput: React.FC = ({ value, ...props }) => { + return ( + + ) +} + +export default GridInput diff --git a/packages/admin-ui/ui/src/components/molecules/hot-key-action/index.tsx b/packages/admin-ui/ui/src/components/molecules/hot-key-action/index.tsx new file mode 100644 index 0000000000000..ed29a463b4532 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/hot-key-action/index.tsx @@ -0,0 +1,23 @@ +import React from "react" +import { useHotkeys } from "react-hotkeys-hook" + +type HotKeyActionProps = { + label: string + hotKey: string + icon: React.ReactNode + onAction: (keyboardEvent: KeyboardEvent, hotkeysEvent: any) => void | boolean +} + +const HotKeyAction = ({ label, hotKey, icon, onAction }: HotKeyActionProps) => { + useHotkeys(hotKey, onAction, {}) + return ( +
+ {label} +
+ {icon} +
+
+ ) +} + +export default HotKeyAction diff --git a/packages/admin-ui/ui/src/components/molecules/icon-tooltip/index.tsx b/packages/admin-ui/ui/src/components/molecules/icon-tooltip/index.tsx new file mode 100644 index 0000000000000..dfccbb24b71e2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/icon-tooltip/index.tsx @@ -0,0 +1,36 @@ +import React from "react" +import Tooltip, { TooltipProps } from "../../atoms/tooltip" +import AlertIcon from "../../fundamentals/icons/alert-icon" +import InfoIcon from "../../fundamentals/icons/info-icon" +import IconProps from "../../fundamentals/icons/types/icon-type" +import XCircleIcon from "../../fundamentals/icons/x-circle-icon" + +type IconTooltipProps = TooltipProps & { + type?: "info" | "warning" | "error" +} & Pick + +const IconTooltip: React.FC = ({ + type = "info", + size = 16, + content, + ...props +}) => { + const icon = (type: IconTooltipProps["type"]) => { + switch (type) { + case "warning": + return + case "error": + return + default: + return + } + } + + return ( + + {icon(type)} + + ) +} + +export default IconTooltip diff --git a/packages/admin-ui/ui/src/components/molecules/indeterminate-checkbox/index.tsx b/packages/admin-ui/ui/src/components/molecules/indeterminate-checkbox/index.tsx new file mode 100644 index 0000000000000..5fef801d78f30 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/indeterminate-checkbox/index.tsx @@ -0,0 +1,82 @@ +import clsx from "clsx" +import React, { useImperativeHandle } from "react" + +import CheckIcon from "../../fundamentals/icons/check-icon" + +type IndeterminateCheckboxProps = { + type?: "checkbox" | "radio" + onChange?: (e: React.ChangeEvent) => void + checked?: boolean + title?: string + indeterminate?: boolean + className?: React.HTMLAttributes["className"] + name?: string + disabled?: boolean // NOTE: only visual, still have to filter disabled ids out +} + +const IndeterminateCheckbox = React.forwardRef< + HTMLInputElement, + IndeterminateCheckboxProps +>(({ indeterminate = false, className, checked, ...rest }, ref) => { + const type = rest.type || "checkbox" + const innerRef = React.useRef(null) + + useImperativeHandle( + ref, + () => innerRef.current + ) + + React.useEffect(() => { + if (innerRef.current) { + innerRef.current.indeterminate = indeterminate + } + }, [innerRef, indeterminate]) + + const handleClick = () => { + if (!rest.disabled && innerRef.current) { + innerRef.current.click() + } + } + + if (type === "radio") { + return ( +
+ +
+ ) + } + + return ( +
+
+ + {checked && } + +
+ +
+ ) +}) + +export default IndeterminateCheckbox diff --git a/packages/admin-ui/ui/src/components/molecules/input-signin/index.tsx b/packages/admin-ui/ui/src/components/molecules/input-signin/index.tsx new file mode 100644 index 0000000000000..df64198fdef8b --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/input-signin/index.tsx @@ -0,0 +1,99 @@ +import clsx from "clsx" +import React, { + ChangeEventHandler, + FocusEventHandler, + useEffect, + useImperativeHandle, + useRef, + useState, +} from "react" +import EyeIcon from "../../fundamentals/icons/eye-icon" +import EyeOffIcon from "../../fundamentals/icons/eye-off-icon" +import LockIcon from "../../fundamentals/icons/lock-icon" + +type InputProps = React.InputHTMLAttributes & { + key?: string + onChange?: ChangeEventHandler + onFocus?: FocusEventHandler + props?: React.HTMLAttributes +} + +const SigninInput = React.forwardRef( + ( + { + placeholder, + name, + key, + required, + onChange, + onFocus, + className, + type, + ...props + }: InputProps, + ref + ) => { + const inputRef = useRef(null) + const [showPassword, setShowPassword] = useState(false) + const [inputType, setInputType] = useState(type) + + useEffect(() => { + if (type === "password" && showPassword) { + setInputType("text") + } + + if (type === "password" && !showPassword) { + setInputType("password") + } + }, [type, showPassword]) + + useImperativeHandle(ref, () => inputRef.current) + + return ( +
+ {props.readOnly && ( + + )} + + {type === "password" && ( + + )} +
+ ) + } +) + +export default SigninInput diff --git a/packages/admin-ui/ui/src/components/molecules/input/index.tsx b/packages/admin-ui/ui/src/components/molecules/input/index.tsx new file mode 100644 index 0000000000000..396dd17fb8513 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/input/index.tsx @@ -0,0 +1,155 @@ +import clsx from "clsx" +import React, { + ChangeEventHandler, + FocusEventHandler, + MouseEventHandler, + useImperativeHandle, + useRef, +} from "react" +import InputError from "../../atoms/input-error" +import MinusIcon from "../../fundamentals/icons/minus-icon" +import PlusIcon from "../../fundamentals/icons/plus-icon" +import InputHeader, { InputHeaderProps } from "../../fundamentals/input-header" + +export type InputProps = Omit, "prefix"> & + InputHeaderProps & { + small?: boolean + label?: string + deletable?: boolean + onDelete?: MouseEventHandler + onChange?: ChangeEventHandler + onFocus?: FocusEventHandler + errors?: { [x: string]: unknown } + prefix?: React.ReactNode + props?: React.HTMLAttributes + } + +const InputField = React.forwardRef( + ( + { + small, + placeholder, + label, + name, + required, + deletable, + onDelete, + onChange, + onFocus, + tooltipContent, + tooltip, + prefix, + errors, + props, + className, + ...fieldProps + }: InputProps, + ref + ) => { + const inputRef = useRef(null) + + useImperativeHandle( + ref, + () => inputRef.current + ) + + const onNumberIncrement = () => { + inputRef.current?.stepUp() + if (onChange) { + inputRef.current?.dispatchEvent( + new InputEvent("change", { + view: window, + bubbles: true, + cancelable: false, + }) + ) + } + } + + const onNumberDecrement = () => { + inputRef.current?.stepDown() + if (onChange) { + inputRef.current?.dispatchEvent( + new InputEvent("change", { + view: window, + bubbles: true, + cancelable: false, + }) + ) + } + } + + return ( +
+ {label && ( + + )} +
+ {prefix ? ( + {prefix} + ) : null} + + + {deletable && ( + + )} + + {fieldProps.type === "number" && ( +
+ + +
+ )} +
+ +
+ ) + } +) + +export default InputField diff --git a/packages/admin-ui/ui/src/components/molecules/json-view/index.tsx b/packages/admin-ui/ui/src/components/molecules/json-view/index.tsx new file mode 100644 index 0000000000000..37a7db3a06975 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/json-view/index.tsx @@ -0,0 +1,94 @@ +import * as Collapsible from "@radix-ui/react-collapsible" +import clsx from "clsx" +import { useMemo, useState } from "react" +import { JSONTree } from "react-json-tree" +import useClipboard from "../../../hooks/use-clipboard" +import Button from "../../fundamentals/button" +import ChevronDownIcon from "../../fundamentals/icons/chevron-down" +import ClipboardCopyIcon from "../../fundamentals/icons/clipboard-copy-icon" + +type JSONViewProps = { + data: object +} + +const JSONView = ({ data }: JSONViewProps) => { + const [expanded, setExpanded] = useState(false) + const [isCopied, handleCopy] = useClipboard( + JSON.stringify(data, undefined, 2), + { + successDuration: 5500, + onCopied: () => {}, + } + ) + + const length = useMemo(() => { + return Object.keys(data).length + }, [data]) + + return ( +
+ + +
+
+

+ {expanded ? "{" : length > 0 ? "{ ... }" : "{}"} +

+ + ({length} {length === 1 ? "item" : "items"}) + +
+ +
+
+ +
+ false} + /> +
+
+ {expanded &&

{`}`}

} +
+ {isCopied && ( + Copied! + )} + +
+
+
+
+
+ ) +} + +export default JSONView diff --git a/packages/admin-ui/ui/src/components/molecules/modal/focus-modal.tsx b/packages/admin-ui/ui/src/components/molecules/modal/focus-modal.tsx new file mode 100644 index 0000000000000..bc8c02b07f42f --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/modal/focus-modal.tsx @@ -0,0 +1,114 @@ +import clsx from "clsx" + +import Button from "../../fundamentals/button" +import CrossIcon from "../../fundamentals/icons/cross-icon" +import { ReactFCWithChildren } from "../../../types/utils" + +type FocusModalElementProps = { + className?: string + children?: React.ReactNode +} + +type IFocusModal = ReactFCWithChildren & { + Header: ReactFCWithChildren + Main: ReactFCWithChildren + BasicFocusModal: ReactFCWithChildren +} + +type BasicFocusModalProps = { + handleClose: (e) => void + onSubmit: (e) => void + cancelText?: string + submitText?: string + children?: React.ReactNode +} + +const FocusModal: IFocusModal = ({ className, children }) => ( +
+ {children} +
+) + +FocusModal.Header = ({ children, className }) => ( +
+ {children} +
+) + +FocusModal.Main = ({ children, className }) => ( +
+ {children} +
+) + +FocusModal.BasicFocusModal = ({ + handleClose, + onSubmit, + children, + cancelText = "Cancel", + submitText = "Save changes", +}) => { + return ( + + + {children} + + ) +} + +const BasicFocusModalHeader: React.FC = ({ + handleClose, + onSubmit, + cancelText, + submitText, +}) => { + return ( + +
+ +
+ + +
+
+
+ ) +} + +export default FocusModal diff --git a/packages/admin-ui/ui/src/components/molecules/modal/index.tsx b/packages/admin-ui/ui/src/components/molecules/modal/index.tsx new file mode 100644 index 0000000000000..43a741ff0aab1 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/modal/index.tsx @@ -0,0 +1,161 @@ +import * as Dialog from "@radix-ui/react-dialog" +import * as Portal from "@radix-ui/react-portal" +import clsx from "clsx" +import React from "react" +import { useWindowDimensions } from "../../../hooks/use-window-dimensions" +import CrossIcon from "../../fundamentals/icons/cross-icon" + +type ModalState = { + portalRef: any + isLargeModal?: boolean +} + +export const ModalContext = React.createContext({ + portalRef: undefined, + isLargeModal: true, +}) + +export type ModalProps = { + isLargeModal?: boolean + handleClose: () => void + open?: boolean + children?: React.ReactNode +} + +type ModalChildProps = { + className?: string + style?: React.CSSProperties + children?: React.ReactNode +} + +type ModalHeaderProps = { + handleClose: () => void + children?: React.ReactNode +} + +type ModalType = React.FC & { + Body: React.FC + Header: React.FC + Footer: React.FC + Content: React.FC +} + +const Overlay: React.FC = ({ children }) => { + return ( + + {children} + + ) +} + +const Content: React.FC = ({ children }) => { + const { height } = useWindowDimensions() + const style = { + maxHeight: height - 64, + } + return ( + + {children} + + ) +} + +const Modal: ModalType = ({ + open = true, + handleClose, + isLargeModal = true, + children, +}) => { + const portalRef = React.useRef(null) + return ( + + + + + {children} + + + + + ) +} + +Modal.Body = ({ children, className, style }) => { + const { isLargeModal } = React.useContext(ModalContext) + + return ( +
e.stopPropagation()} + > + {children} +
+ ) +} + +Modal.Content = ({ children, className }) => { + const { isLargeModal } = React.useContext(ModalContext) + + const { height } = useWindowDimensions() + const style = { + maxHeight: height - 90 - 141, + } + return ( +
+ {children} +
+ ) +} + +Modal.Header = ({ handleClose = undefined, children }) => { + return ( +
e.stopPropagation()} + > +
+ {handleClose && ( + + )} +
+ {children} +
+ ) +} + +Modal.Footer = ({ children, className }) => { + const { isLargeModal } = React.useContext(ModalContext) + + return ( +
e.stopPropagation()} + className={clsx( + "px-7 bottom-0 pb-5 flex w-full", + { + "border-t border-grey-20 pt-4": isLargeModal, + }, + className + )} + > + {children} +
+ ) +} + +export default Modal diff --git a/packages/admin-ui/ui/src/components/molecules/modal/layered-modal.tsx b/packages/admin-ui/ui/src/components/molecules/modal/layered-modal.tsx new file mode 100644 index 0000000000000..bdb84c0ce921b --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/modal/layered-modal.tsx @@ -0,0 +1,163 @@ +import clsx from "clsx" +import React, { ReactNode, useContext, useReducer } from "react" +import Button from "../../fundamentals/button" +import ArrowLeftIcon from "../../fundamentals/icons/arrow-left-icon" +import Modal, { ModalProps } from "../../molecules/modal" + +enum LayeredModalActions { + PUSH, + POP, + RESET, +} + +export type LayeredModalScreen = { + title: string + subtitle?: string + onBack: () => void + onConfirm?: () => void + view: ReactNode +} + +export type ILayeredModalContext = { + screens: LayeredModalScreen[] + push: (screen: LayeredModalScreen) => void + pop: () => void + reset: () => void +} + +const defaultContext: ILayeredModalContext = { + screens: [], + push: (screen) => {}, + pop: () => {}, + reset: () => {}, +} + +export const LayeredModalContext = React.createContext(defaultContext) + +const reducer = (state, action) => { + switch (action.type) { + case LayeredModalActions.PUSH: { + return { ...state, screens: [...state.screens, action.payload] } + } + case LayeredModalActions.POP: { + return { ...state, screens: state.screens.slice(0, -1) } + } + case LayeredModalActions.RESET: { + return { ...state, screens: [] } + } + } +} + +type LayeredModalProps = { + context: ILayeredModalContext +} & ModalProps + +export const LayeredModalProvider = ({ children }) => { + const [state, dispatch] = useReducer(reducer, defaultContext) + + return ( + { + dispatch({ type: LayeredModalActions.PUSH, payload: screen }) + }, + + pop: () => { + dispatch({ type: LayeredModalActions.POP }) + }, + + reset: () => { + dispatch({ type: LayeredModalActions.RESET }) + }, + }} + > + {children} + + ) +} + +const LayeredModal: React.FC = ({ + context, + children, + handleClose, + open, + isLargeModal = true, +}) => { + const emptyScreensAndClose = () => { + context.reset() + handleClose() + } + + const screen = context.screens[context.screens.length - 1] + return ( + + + {screen ? ( + <> + +
+ +
+

{screen.title}

+ {screen.subtitle && ( + + ({screen.subtitle}) + + )} +
+
+
+ {screen.view} + + ) : ( + <> + )} +
+
+
+ {children} +
+
+
+ ) +} + +export const useLayeredModal = () => { + const context = useContext(LayeredModalContext) + if (context === null) { + throw new Error( + "useLayeredModal must be used within a LayeredModalProvider" + ) + } + return context +} + +export default LayeredModal diff --git a/packages/admin-ui/ui/src/components/molecules/modal/side-modal.tsx b/packages/admin-ui/ui/src/components/molecules/modal/side-modal.tsx new file mode 100644 index 0000000000000..14f49334034cf --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/modal/side-modal.tsx @@ -0,0 +1,60 @@ +import React, { PropsWithChildren } from "react" +import { AnimatePresence, motion } from "framer-motion" + +const MODAL_WIDTH = 560 + +type SideModalProps = PropsWithChildren<{ + close: () => void + isVisible: boolean +}> + +/** + * Side modal displayed as right drawer on open. + */ +function SideModal(props: SideModalProps) { + const { isVisible, children, close } = props + return ( + + {isVisible && ( + <> + + + {children} + + + )} + + ) +} + +export default SideModal diff --git a/packages/admin-ui/ui/src/components/molecules/modal/stepped-modal.tsx b/packages/admin-ui/ui/src/components/molecules/modal/stepped-modal.tsx new file mode 100644 index 0000000000000..6c5bfef9f7f0d --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/modal/stepped-modal.tsx @@ -0,0 +1,227 @@ +import clsx from "clsx" +import React, { ReactNode, useReducer } from "react" +import Button from "../../fundamentals/button" +import Modal, { ModalProps } from "../../molecules/modal" +import LayeredModal, { ILayeredModalContext } from "./layered-modal" + +enum SteppedActions { + ENABLENEXTPAGE, + DISABLENEXTPAGE, + GOTONEXTPAGE, + GOTOPREVIOUSPAGE, + SETPAGE, + SUBMIT, + RESET, +} + +type ISteppedContext = { + currentStep: number + nextStepEnabled: boolean + enableNextPage: () => void + disableNextPage: () => void + goToNextPage: () => void + goToPreviousPage: () => void + submit: () => void + reset: () => void + setPage: (page: number) => void +} + +const defaultContext: ISteppedContext = { + currentStep: 0, + nextStepEnabled: true, + enableNextPage: () => {}, + disableNextPage: () => {}, + goToNextPage: () => {}, + goToPreviousPage: () => {}, + submit: () => {}, + reset: () => {}, + setPage: (page) => {}, +} + +export const SteppedContext = React.createContext(defaultContext) + +const reducer = (state, action) => { + switch (action.type) { + case SteppedActions.ENABLENEXTPAGE: { + return { ...state, nextStepEnabled: true } + } + case SteppedActions.DISABLENEXTPAGE: { + return { ...state, nextStepEnabled: false } + } + case SteppedActions.GOTONEXTPAGE: { + return { ...state, currentStep: state.currentStep + 1 } + } + case SteppedActions.GOTOPREVIOUSPAGE: { + return { ...state, currentStep: Math.max(0, state.currentStep - 1) } + } + case SteppedActions.SETPAGE: { + return { + ...state, + currentStep: action.payload > 0 ? action.payload : state.currentStep, + } + } + case SteppedActions.SUBMIT: { + return { ...state } + } + case SteppedActions.RESET: { + return { ...state, currentStep: 0, nextStepEnabled: true } + } + } +} + +type SteppedProps = { + context: ISteppedContext + title: string + onSubmit: () => void + lastScreenIsSummary?: boolean + steps: ReactNode[] + layeredContext?: ILayeredModalContext +} & ModalProps + +export const SteppedProvider = ({ children }) => { + const [state, dispatch] = useReducer(reducer, defaultContext) + + return ( + { + dispatch({ type: SteppedActions.ENABLENEXTPAGE }) + }, + disableNextPage: () => { + dispatch({ type: SteppedActions.DISABLENEXTPAGE }) + }, + goToNextPage: () => { + dispatch({ type: SteppedActions.GOTONEXTPAGE }) + }, + goToPreviousPage: () => { + dispatch({ type: SteppedActions.GOTOPREVIOUSPAGE }) + }, + submit: () => { + dispatch({ type: SteppedActions.SUBMIT }) + }, + setPage: (page: number) => { + dispatch({ type: SteppedActions.SETPAGE, payload: page }) + }, + reset: () => { + dispatch({ type: SteppedActions.RESET }) + }, + }} + > + {children} + + ) +} + +const SteppedModal: React.FC = ({ + context, + steps, + layeredContext, + title, + onSubmit, + lastScreenIsSummary = false, + handleClose, + isLargeModal = true, +}) => { + const resetAndClose = () => { + context.reset() + handleClose() + } + + const resetAndSubmit = () => { + onSubmit() + } + return ( + + + +
+

{title}

+ {!lastScreenIsSummary || + (lastScreenIsSummary && + context.currentStep !== steps.length - 1 && ( +
+ {`Step ${ + context.currentStep + 1 + } of ${steps.length}`} + {steps.map((_, i) => ( + context.currentStep, + "bg-violet-60": context.currentStep >= i, + }, + { + "outline-4 outline outline-violet-20": + context.currentStep === i, + } + )} + /> + ))} +
+ ))} +
+
+ {steps[context.currentStep]} +
+ +
+ + +
+
+
+ ) +} + +const ModalElement = ({ + layeredContext, + handleClose, + isLargeModal = true, + children, +}) => + layeredContext ? ( + + {children} + + ) : ( + + {children} + + ) + +export default SteppedModal diff --git a/packages/admin-ui/ui/src/components/molecules/native-select/index.tsx b/packages/admin-ui/ui/src/components/molecules/native-select/index.tsx new file mode 100644 index 0000000000000..d44ad179e6499 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/native-select/index.tsx @@ -0,0 +1,68 @@ +import * as RadixSelect from "@radix-ui/react-select" +import clsx from "clsx" +import React from "react" +import CheckIcon from "../../fundamentals/icons/check-icon" +import ChevronDownIcon from "../../fundamentals/icons/chevron-down" +import ChevronUpIcon from "../../fundamentals/icons/chevron-up" + +type NativeSelectType = React.FC & { + Item: React.FC +} + +type NativeSelectProps = { + triggerProps?: RadixSelect.SelectTriggerProps +} & RadixSelect.SelectProps + +const ICON_SIZE = 16 + +const NativeSelect: NativeSelectType = ({ + children, + triggerProps, + ...props +}) => { + return ( + + + + + + + + + + + + {children} + + + + + + ) +} + +type ItemProps = RadixSelect.SelectItemProps + +const Item: React.FC = ({ children, ...props }) => ( + + + + + {children} + +) + +NativeSelect.Item = Item + +export default NativeSelect diff --git a/packages/admin-ui/ui/src/components/molecules/note-input/index.tsx b/packages/admin-ui/ui/src/components/molecules/note-input/index.tsx new file mode 100644 index 0000000000000..ad88395dfc18d --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/note-input/index.tsx @@ -0,0 +1,76 @@ +import React, { useCallback, useRef, useState } from "react" +import SendIcon from "../../fundamentals/icons/send-icon" +import EmojiPicker from "../emoji-picker" + +type NoteInputProps = { + onSubmit: (note: string | undefined) => void +} + +const NoteInput: React.FC = ({ onSubmit }) => { + const [note, setNote] = useState(undefined) + const inputRef = useRef(null) + + const handleAddEmoji = (emoji: string) => { + setNote(`${note ? note : ""}${emoji}`) + } + + const handleSubmit = () => { + if (onSubmit && note) { + onSubmit(note) + setNote("") + } + } + + const onKeyDownHandler = useCallback( + (event) => { + switch (event.key) { + case "Enter": + event.preventDefault() + event.stopPropagation() + handleSubmit() + inputRef.current?.blur() + break + case "Esc": + case "Escape": + inputRef.current?.blur() + break + default: + break + } + }, + [note, setNote, onSubmit] + ) + + return ( +
+
inputRef.current?.focus()} + > +
+ + setNote(e.target.value)} + className="flex-grow bg-transparent inter-base-regular placeholder:text-grey-40 focus:outline-none" + ref={inputRef} + id="note-input" + autoComplete="off" + onKeyDown={onKeyDownHandler} + /> +
+ +
+
+ ) +} + +export default NoteInput diff --git a/packages/admin-ui/ui/src/components/molecules/notification-bell/index.tsx b/packages/admin-ui/ui/src/components/molecules/notification-bell/index.tsx new file mode 100644 index 0000000000000..e466da7880945 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/notification-bell/index.tsx @@ -0,0 +1,21 @@ +import React from "react" +import Button, { ButtonProps } from "../../fundamentals/button" +import BellIcon from "../../fundamentals/icons/bell-icon" +import BellNotiIcon from "../../fundamentals/icons/bell-noti-icon" + +type NotificationBellProps = ButtonProps & { + hasNotifications?: boolean +} + +const NotificationBell: React.FC = ({ + hasNotifications = false, + ...attributes +}) => { + return ( + + ) +} + +export default NotificationBell diff --git a/packages/admin-ui/ui/src/components/molecules/numbered-item/index.tsx b/packages/admin-ui/ui/src/components/molecules/numbered-item/index.tsx new file mode 100644 index 0000000000000..c04f2b8f5aba4 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/numbered-item/index.tsx @@ -0,0 +1,52 @@ +import React from "react" +import Badge from "../../../components/fundamentals/badge" +import Actionables, { + ActionType, +} from "../../../components/molecules/actionables" + +type NumberedItemProps = { + actions?: ActionType[] + index: number + title: string + description?: React.ReactNode | string +} + +const NumberedItem: React.FC = ({ + actions, + index, + title, + description, +}) => { + return ( +
+
+
+ + §{index} + +
+
+
{title}
+ {description && + (typeof description === "string" ? ( +
+ {description} +
+ ) : ( + description + ))} +
+
+ {actions && ( +
+ +
+ )} +
+ ) +} + +export default NumberedItem diff --git a/packages/admin-ui/ui/src/components/molecules/order-status/index.tsx b/packages/admin-ui/ui/src/components/molecules/order-status/index.tsx new file mode 100644 index 0000000000000..9a8c0838d723a --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/order-status/index.tsx @@ -0,0 +1,113 @@ +import React from "react" +import StatusIndicator from "../../fundamentals/status-indicator" + +type PaymentStatusProps = { + paymentStatus: string +} + +type FulfillmentStatusProps = { + fulfillmentStatus: string +} + +type OrderStatusProps = { + orderStatus: string +} + +type ReturnStatusProps = { + returnStatus: string +} + +type RefundStatusProps = { + refundStatus: string +} + +const PaymentStatus: React.FC = ({ paymentStatus }) => { + switch (paymentStatus) { + case "captured": + return + case "awaiting": + return + case "not_paid": + return + case "canceled": + return + case "requires_action": + return + default: + return null + } +} + +const OrderStatus: React.FC = ({ orderStatus }) => { + switch (orderStatus) { + case "completed": + return + case "pending": + return + case "canceled": + return + case "requires_action": + return + default: + return null + } +} + +const FulfillmentStatus: React.FC = ({ + fulfillmentStatus, +}) => { + switch (fulfillmentStatus) { + case "shipped": + return + case "fulfilled": + return + case "canceled": + return + case "partially_fulfilled": + return + case "not_fulfilled": + return + case "requires_action": + return + default: + return null + } +} + +const ReturnStatus: React.FC = ({ returnStatus }) => { + switch (returnStatus) { + case "received": + return + case "requested": + return + case "canceled": + return + case "requires_action": + return + default: + return null + } +} + +const RefundStatus: React.FC = ({ refundStatus }) => { + switch (refundStatus) { + case "na": + return + case "not_refunded": + return + case "refunded": + return + case "canceled": + return + default: + return null + } +} + +export { + PaymentStatus, + OrderStatus, + FulfillmentStatus, + ReturnStatus, + RefundStatus, +} diff --git a/packages/admin-ui/ui/src/components/molecules/rma-select-shipping/index.tsx b/packages/admin-ui/ui/src/components/molecules/rma-select-shipping/index.tsx new file mode 100644 index 0000000000000..5f5912eed9fcb --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/rma-select-shipping/index.tsx @@ -0,0 +1,54 @@ +import React from "react" +import Button from "../../fundamentals/button" +import TrashIcon from "../../fundamentals/icons/trash-icon" +import { AmountInput } from "../amount-input" + +type RMAShippingPriceProps = { + inclTax: boolean + useCustomShippingPrice: boolean + shippingPrice: number | undefined + currencyCode: string + updateShippingPrice: (price: number | undefined) => void + setUseCustomShippingPrice: (useCustomShippingPrice: boolean) => void +} + +const RMAShippingPrice: React.FC = ({ + useCustomShippingPrice, + inclTax, + shippingPrice, + currencyCode, + updateShippingPrice, + setUseCustomShippingPrice, +}) => { + return useCustomShippingPrice ? ( +
+ updateShippingPrice(amount ?? 0)} + value={shippingPrice} + /> + +
+ ) : ( +
+ +
+ ) +} + +export default RMAShippingPrice diff --git a/packages/admin-ui/ui/src/components/molecules/sales-channels-display/index.tsx b/packages/admin-ui/ui/src/components/molecules/sales-channels-display/index.tsx new file mode 100644 index 0000000000000..7aa4d6d88c1fe --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/sales-channels-display/index.tsx @@ -0,0 +1,70 @@ +import { SalesChannel } from "@medusajs/medusa" +import { useAdminSalesChannels } from "medusa-react" +import React from "react" +import Tooltip from "../../atoms/tooltip" +import Badge from "../../fundamentals/badge" + +type Props = { + channels?: SalesChannel[] +} + +const SalesChannelsDisplay = ({ channels = [] }: Props) => { + const { count } = useAdminSalesChannels() + const remainder = Math.max(channels.length - 3, 0) + + return ( +
+ {channels.length > 0 && ( +
+
+ {channels.slice(0, 3).map((sc) => ( + + ))} +
+ {remainder > 0 && ( + + {channels.slice(3).map((sc) => { + return {sc.name} + })} +
+ } + > + +
+ + {remainder} more +
+
+ + )} +
+ )} +

+ Available in{" "} + + {channels.length ? channels.length : 0} + {" "} + out of{" "} + {count || 0}{" "} + Sales Channels +

+
+ ) +} + +type SalesChannelBadgeProps = { + channel: SalesChannel +} + +const SalesChannelBadge: React.FC = ({ channel }) => { + return ( + +
+ {channel.name} +
+
+ ) +} + +export default SalesChannelsDisplay diff --git a/packages/admin-ui/ui/src/components/molecules/sales-channels-list/index.tsx b/packages/admin-ui/ui/src/components/molecules/sales-channels-list/index.tsx new file mode 100644 index 0000000000000..b576c0eba0f21 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/sales-channels-list/index.tsx @@ -0,0 +1,46 @@ +import { SalesChannel } from "@medusajs/medusa" +import Tooltip from "../../atoms/tooltip" +import IconBadge from "../../fundamentals/icon-badge" +import ChannelsIcon from "../../fundamentals/icons/channels-icon" + +type Props = { + salesChannels: SalesChannel[] + showMax?: number +} + +const SalesChannelsList = ({ salesChannels, showMax = 3 }: Props) => { + const truncateSalesChannels = salesChannels.length > showMax + return ( +
+ + + + {salesChannels + .slice(0, showMax) + .map((salesChannel, index, slicedArray) => ( + + {salesChannel.name} + {index < slicedArray.length - 1 && ", "} + + ))} + {truncateSalesChannels && ( + + {salesChannels.slice(showMax).map((channel) => ( +
{channel.name}
+ ))} + + } + side="top" + > + + + {salesChannels.length - showMax} more + +
+ )} +
+ ) +} + +export default SalesChannelsList diff --git a/packages/admin-ui/ui/src/components/molecules/sales-channels-summary/index.tsx b/packages/admin-ui/ui/src/components/molecules/sales-channels-summary/index.tsx new file mode 100644 index 0000000000000..b8110ef5c47b8 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/sales-channels-summary/index.tsx @@ -0,0 +1,39 @@ +import { sortBy } from "lodash" + +import { SalesChannel } from "@medusajs/medusa" + +/** + * Customers Associated Groups props + */ +interface P { + channels: SalesChannel[] + showCount: number +} + +/* + * Render a summary of groups to which the customer belongs + */ +function SalesChannelsSummary(props: P) { + const channels = sortBy(props.channels, "name") + if (!channels.length) { + return null + } + + const allGroups = channels.map((g) => g.name).join(", ") + + const leadName = channels + .slice(0, props.showCount) + .map((g) => g.name) + .join(", ") + + const left = channels.length - props.showCount + + return ( +
+ {leadName} + {left > 0 && + {left} more} +
+ ) +} + +export default SalesChannelsSummary diff --git a/packages/admin-ui/ui/src/components/molecules/search-bar.tsx b/packages/admin-ui/ui/src/components/molecules/search-bar.tsx new file mode 100644 index 0000000000000..6d2ddb86adfd4 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/search-bar.tsx @@ -0,0 +1,49 @@ +import React, { useEffect, useState } from "react" +import { useHotkeys } from "react-hotkeys-hook" +import { useLocation } from "react-router-dom" +import OSShortcut from "../atoms/os-shortcut" +import SearchIcon from "../fundamentals/icons/search-icon" +import SearchModal from "../templates/search-modal" + +const SearchBar: React.FC = () => { + const [showSearchModal, setShowSearchModal] = useState(false) + const location = useLocation() + + const toggleSearch = (e) => { + e.preventDefault() + e.stopPropagation() + setShowSearchModal((show) => !show) + } + + const closeModal = () => { + setShowSearchModal(false) + } + + useHotkeys("cmd+k", toggleSearch, {}, []) + useHotkeys("ctrl+k", toggleSearch, {}, []) + useHotkeys("/", toggleSearch, {}, []) + + useEffect(() => { + closeModal() + }, [location]) + + return ( + <> + + {showSearchModal && } + + ) +} + +export default SearchBar diff --git a/packages/admin-ui/ui/src/components/molecules/section/index.tsx b/packages/admin-ui/ui/src/components/molecules/section/index.tsx new file mode 100644 index 0000000000000..1cc1796b794b5 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/section/index.tsx @@ -0,0 +1,32 @@ +import React from "react" +import IconTooltip from "../icon-tooltip" + +type SectionProps = { + title: string + description: string + tooltip?: string +} + +const Section: React.FC = ({ + title, + description, + tooltip, + children, +}) => { + return ( +
+
+

{title}

+ {tooltip && ( +
+ +
+ )} +
+

{description}

+ {children} +
+ ) +} + +export default Section diff --git a/packages/admin-ui/ui/src/components/molecules/select/index.tsx b/packages/admin-ui/ui/src/components/molecules/select/index.tsx new file mode 100644 index 0000000000000..85cea7fbefef6 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/select/index.tsx @@ -0,0 +1,235 @@ +import clsx from "clsx" +import React, { + useContext, + useEffect, + useImperativeHandle, + useRef, + useState, +} from "react" +import Primitive from "react-select" +import AsyncPrimitive from "react-select/async" +import AsyncCreatablePrimitive from "react-select/async-creatable" +import CreatablePrimitive from "react-select/creatable" +import InputHeader, { InputHeaderProps } from "../../fundamentals/input-header" +import { ModalContext } from "../modal" +import { SelectComponents } from "./select-components" + +export type SelectOption = { + value: T + label: string + disabled?: boolean +} + +type MultiSelectProps = InputHeaderProps & { + // component props + label: string + required?: boolean + name?: string + className?: string + fullWidth?: boolean + // Multiselect props + placeholder?: string + isMultiSelect?: boolean + labelledBy?: string + options: { label: string; value: string | null; disabled?: boolean }[] + value: + | { label: string; value: string }[] + | { label: string; value: string } + | null + filterOptions?: (q: string) => any[] + hasSelectAll?: boolean + isLoading?: boolean + shouldToggleOnHover?: boolean + onChange: (values: any[] | any) => void + disabled?: boolean + enableSearch?: boolean + isCreatable?: boolean + clearSelected?: boolean + onCreateOption?: (value: string) => { value: string; label: string } +} + +const SSelect = React.forwardRef( + ( + { + label, + name, + fullWidth = false, + required, + value, + onChange, + className, + isMultiSelect, + hasSelectAll, + tooltipContent, + tooltip, + enableSearch = true, + clearSelected = false, + isCreatable, + filterOptions, + placeholder = "Search...", + options, + onCreateOption, + }: MultiSelectProps, + ref + ) => { + const { portalRef } = useContext(ModalContext) + + const [isFocussed, setIsFocussed] = useState(false) + const [scrollBlocked, setScrollBlocked] = useState(true) + + useEffect(() => { + window.addEventListener("resize", () => { + setIsFocussed(false) + selectRef?.current?.blur() + }) + }, []) + + const selectRef = useRef(null) + + useImperativeHandle(ref, () => selectRef.current) + + const containerRef = useRef(null) + + const onClickOption = (val, ...args) => { + if ( + val?.length && + val?.find((option) => option.value === "all") && + hasSelectAll && + isMultiSelect + ) { + onChange(options) + } else { + onChange(val) + if (!isMultiSelect) { + selectRef?.current?.blur() + setIsFocussed(false) + } + } + } + + const handleOnCreateOption = (val) => { + if (onCreateOption) { + onCreateOption(val) + setIsFocussed(false) + selectRef?.current?.blur() + } + } + + useEffect(() => { + const delayDebounceFn = setTimeout(() => { + if (isFocussed) { + setScrollBlocked(false) + } + }, 50) + + return () => clearTimeout(delayDebounceFn) + }, [isFocussed]) + + return ( +
+
+
+ +
+ + { + { + setIsFocussed(true) + }} + onMenuClose={() => { + setScrollBlocked(true) + setIsFocussed(false) + }} + closeMenuOnScroll={(e) => { + if ( + !scrollBlocked && + e.target?.contains(containerRef.current) && + e.target !== document + ) { + return true + } + }} + closeMenuOnSelect={!isMultiSelect} + blurInputOnSelect={!isMultiSelect} + styles={{ menuPortal: (base) => ({ ...base, zIndex: 60 }) }} + hideSelectedOptions={false} + menuPortalTarget={portalRef?.current?.lastChild || document.body} + menuPlacement="auto" + backspaceRemovesValue={false} + classNamePrefix="react-select" + placeholder={placeholder} + className="react-select-container" + onCreateOption={handleOnCreateOption} + components={SelectComponents} + /> + } + {isFocussed && enableSearch &&
} +
+
+ ) + } +) + +const GetSelect = React.forwardRef( + ( + { isCreatable, searchBackend, onCreateOption, handleClose, ...props }, + ref + ) => { + if (isCreatable) { + return searchBackend ? ( + + ) : ( + + ) + } else if (searchBackend) { + return ( + + ) + } + return + } +) + +export default SSelect diff --git a/packages/admin-ui/ui/src/components/molecules/select/next-select/components/containers.tsx b/packages/admin-ui/ui/src/components/molecules/select/next-select/components/containers.tsx new file mode 100644 index 0000000000000..1422475d5b9f2 --- /dev/null +++ b/packages/admin-ui/ui/src/components/molecules/select/next-select/components/containers.tsx @@ -0,0 +1,190 @@ +import clsx from "clsx" +import React, { ComponentPropsWithRef, forwardRef } from "react" +import { + ContainerProps, + GroupBase, + IndicatorsContainerProps, + ValueContainerProps, +} from "react-select" +import InputError from "../../../../atoms/input-error" + +type AdjacentContainerProps = { + label?: string + htmlFor?: string + helperText?: string + required?: boolean + name?: string + errors?: Record + children?: React.ReactNode +} & ComponentPropsWithRef<"div"> + +export const AdjacentContainer = forwardRef< + HTMLDivElement, + AdjacentContainerProps +>( + ( + { + label, + helperText, + required, + errors, + name, + children, + }: AdjacentContainerProps, + ref + ) => { + return ( +
+ {label && ( + + )} + {children} + {name && errors ? ( + + ) : helperText ? ( +

{helperText}

+ ) : null} +
+ ) + } +) + +export const SelectContainer = < + Option, + IsMulti extends boolean, + Group extends GroupBase