feat(admin,admin-ui,medusa): Add Medusa Admin plugin (#3334)
7
.changeset/sixty-jeans-fold.md
Normal file
@@ -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.
|
||||||
@@ -7,6 +7,8 @@ jest*
|
|||||||
packages/*
|
packages/*
|
||||||
# List of packages to Lint
|
# List of packages to Lint
|
||||||
!packages/medusa
|
!packages/medusa
|
||||||
|
!packages/admin-ui
|
||||||
|
!packages/admin
|
||||||
!packages/medusa-payment-stripe
|
!packages/medusa-payment-stripe
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
56
.eslintrc.js
@@ -83,7 +83,8 @@ module.exports = {
|
|||||||
project: [
|
project: [
|
||||||
"./packages/medusa/tsconfig.json",
|
"./packages/medusa/tsconfig.json",
|
||||||
"./packages/medusa-payment-stripe/tsconfig.spec.json",
|
"./packages/medusa-payment-stripe/tsconfig.spec.json",
|
||||||
]
|
"./packages/admin-ui/tsconfig.json",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
"valid-jsdoc": "off",
|
"valid-jsdoc": "off",
|
||||||
@@ -111,5 +112,58 @@ module.exports = {
|
|||||||
"@typescript-eslint/no-var-requires": "off",
|
"@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",
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
10
.prettierrc
@@ -4,5 +4,13 @@
|
|||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"arrowParens": "always"
|
"arrowParens": "always",
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "./packages/admin-ui/**/*.{js,jsx,ts,tsx}",
|
||||||
|
"options": {
|
||||||
|
"plugins": ["prettier-plugin-tailwindcss"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"@babel/core": "^7.12.10",
|
"@babel/core": "^7.12.10",
|
||||||
"@babel/node": "^7.12.10",
|
"@babel/node": "^7.12.10",
|
||||||
"babel-preset-medusa-package": "*",
|
"babel-preset-medusa-package": "*",
|
||||||
"jest": "^26.6.3"
|
"jest": "^26.6.3",
|
||||||
|
"jest-environment-node": "26.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"@babel/core": "^7.12.10",
|
"@babel/core": "^7.12.10",
|
||||||
"@babel/node": "^7.12.10",
|
"@babel/node": "^7.12.10",
|
||||||
"babel-preset-medusa-package": "*",
|
"babel-preset-medusa-package": "*",
|
||||||
"jest": "^26.6.3"
|
"jest": "^26.6.3",
|
||||||
|
"jest-environment-node": "26.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"@babel/core": "^7.12.10",
|
"@babel/core": "^7.12.10",
|
||||||
"@babel/node": "^7.12.10",
|
"@babel/node": "^7.12.10",
|
||||||
"babel-preset-medusa-package": "*",
|
"babel-preset-medusa-package": "*",
|
||||||
"jest": "^26.6.3"
|
"jest": "^26.6.3",
|
||||||
|
"jest-environment-node": "26.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
"@medusajs/medusa-oas-cli": "*",
|
"@medusajs/medusa-oas-cli": "*",
|
||||||
"@readme/openapi-parser": "^2.4.0",
|
"@readme/openapi-parser": "^2.4.0",
|
||||||
"@redocly/cli": "1.0.0-beta.123",
|
"@redocly/cli": "1.0.0-beta.123",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.36.2",
|
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
||||||
"@typescript-eslint/parser": "^5.36.2",
|
"@typescript-eslint/parser": "^5.53.0",
|
||||||
"axios": "^0.21.4",
|
"axios": "^0.21.4",
|
||||||
"axios-mock-adapter": "^1.19.0",
|
"axios-mock-adapter": "^1.19.0",
|
||||||
"babel-jest": "^26.6.3",
|
"babel-jest": "^26.6.3",
|
||||||
@@ -36,6 +36,8 @@
|
|||||||
"eslint-plugin-markdown": "^3.0.0",
|
"eslint-plugin-markdown": "^3.0.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-react": "^7.31.11",
|
"eslint-plugin-react": "^7.31.11",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-unused-imports": "^2.0.0",
|
||||||
"execa": "^5.1.1",
|
"execa": "^5.1.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"get-port": "^5.1.1",
|
"get-port": "^5.1.1",
|
||||||
@@ -47,6 +49,7 @@
|
|||||||
"microbundle": "^0.13.3",
|
"microbundle": "^0.13.3",
|
||||||
"pg-god": "^1.0.12",
|
"pg-god": "^1.0.12",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.2.3",
|
||||||
"resolve-cwd": "^3.0.0",
|
"resolve-cwd": "^3.0.0",
|
||||||
"ts-jest": "^26.5.6",
|
"ts-jest": "^26.5.6",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
|
|||||||
4
packages/admin-ui/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/dist
|
||||||
|
/build
|
||||||
|
.vercel
|
||||||
|
/ui/preview
|
||||||
6
packages/admin-ui/.npmignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.DS_store
|
||||||
|
.turbo
|
||||||
|
/src
|
||||||
|
/build
|
||||||
|
.vercel
|
||||||
|
/ui/preview
|
||||||
38
packages/admin-ui/README.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://www.medusajs.com">
|
||||||
|
<img alt="Medusa" src="https://user-images.githubusercontent.com/7554214/153162406-bf8fd16f-aa98-4604-b87b-e13ab4baf604.png" width="100" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<h1 align="center">
|
||||||
|
@medusajs/admin-ui
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h4 align="center">
|
||||||
|
<a href="https://docs.medusajs.com">Documentation</a> |
|
||||||
|
<a href="https://demo.medusajs.com/">Medusa Admin Demo</a> |
|
||||||
|
<a href="https://www.medusajs.com">Website</a>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
An open source composable commerce engine built for developers.
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/medusajs/medusa/blob/master/LICENSE">
|
||||||
|
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Medusa is released under the MIT license." />
|
||||||
|
</a>
|
||||||
|
<a href="https://circleci.com/gh/medusajs/medusa">
|
||||||
|
<img src="https://circleci.com/gh/medusajs/medusa.svg?style=shield" alt="Current CircleCI build status." />
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md">
|
||||||
|
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="PRs welcome!" />
|
||||||
|
</a>
|
||||||
|
<a href="https://www.producthunt.com/posts/medusa"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Day-%23DA552E" alt="Product Hunt"></a>
|
||||||
|
<a href="https://discord.gg/xpCwq3Kfn8">
|
||||||
|
<img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
|
||||||
|
</a>
|
||||||
|
<a href="https://twitter.com/intent/follow?screen_name=medusajs">
|
||||||
|
<img src="https://img.shields.io/twitter/follow/medusajs.svg?label=Follow%20@medusajs" alt="Follow @medusajs" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## The Medusa Admin App. Included with the [`@medusajs/admin`](https://www.npmjs.com/package/@medusajs/admin) plugin. You shouldn't install this package separately.
|
||||||
90
packages/admin-ui/package.json
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"name": "@medusajs/admin-ui",
|
||||||
|
"author": "Kasper Kristensen <kasper@medusajs.com>",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
28
packages/admin-ui/src/index.ts
Normal file
@@ -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 }
|
||||||
15
packages/admin-ui/src/types/build.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { DeepPartial } from "./misc"
|
||||||
|
|
||||||
|
type GlobalsConfig = {
|
||||||
|
base?: string
|
||||||
|
backend?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuildConfig = {
|
||||||
|
outDir?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AdminBuildConfig = {
|
||||||
|
globals?: DeepPartial<GlobalsConfig>
|
||||||
|
build?: DeepPartial<BuildConfig>
|
||||||
|
}
|
||||||
5
packages/admin-ui/src/types/config.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { AdminBuildConfig } from "./build"
|
||||||
|
|
||||||
|
export type AdminUIConfig = {
|
||||||
|
build?: AdminBuildConfig
|
||||||
|
}
|
||||||
2
packages/admin-ui/src/types/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./build"
|
||||||
|
export * from "./misc"
|
||||||
9
packages/admin-ui/src/types/misc.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export type DeepPartial<T> = {
|
||||||
|
[P in keyof T]?: T[P] extends (infer U)[]
|
||||||
|
? DeepPartial<U>[]
|
||||||
|
: T[P] extends ReadonlyArray<infer V>
|
||||||
|
? ReadonlyArray<DeepPartial<V>>
|
||||||
|
: DeepPartial<T[P]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Base<T extends string> = `/${T}/`
|
||||||
5
packages/admin-ui/src/utils/format-base.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Base } from "../types"
|
||||||
|
|
||||||
|
export const formatBase = <T extends string>(base: T): Base<T> => {
|
||||||
|
return `/${base}/`
|
||||||
|
}
|
||||||
76
packages/admin-ui/src/utils/get-custom-vite-config.ts
Normal file
@@ -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",
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/admin-ui/src/utils/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./format-base"
|
||||||
|
export * from "./get-custom-vite-config"
|
||||||
18
packages/admin-ui/tsconfig.json
Normal file
@@ -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"]
|
||||||
|
}
|
||||||
13
packages/admin-ui/ui/index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Medusa</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
9
packages/admin-ui/ui/postcss.config.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const path = require("path")
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
"tailwindcss/nesting": {},
|
||||||
|
tailwindcss: { config: path.join(__dirname, "tailwind.config.js") },
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
10
packages/admin-ui/ui/public/logo.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M26.4471 5.86972L19.6283 1.96144C17.3973 0.679521 14.6635 0.679521 12.4325 1.96144L5.58223 5.86972C3.38261 7.15164 2 9.52788 2 12.0604V19.9083C2 22.4721 3.38261 24.8171 5.58223 26.099L12.401 30.0386C14.6321 31.3205 17.3659 31.3205 19.5969 30.0386L26.4157 26.099C28.6468 24.8171 29.9979 22.4721 29.9979 19.9083V12.0604C30.0608 9.52788 28.6782 7.15164 26.4471 5.86972ZM16.0147 22.9724C12.1496 22.9724 9.00734 19.8458 9.00734 16C9.00734 12.1542 12.1496 9.02762 16.0147 9.02762C19.8797 9.02762 23.0534 12.1542 23.0534 16C23.0534 19.8458 19.9111 22.9724 16.0147 22.9724Z" fill="url(#paint0_linear_362_938)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_362_938" x1="2" y1="31" x2="35.7561" y2="21.5406" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#7C53FF"/>
|
||||||
|
<stop offset="1" stop-color="#F796FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 990 B |
45
packages/admin-ui/ui/src/App.tsx
Normal file
@@ -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(
|
||||||
|
<>
|
||||||
|
<Route path="/" element={<IndexPage />} />
|
||||||
|
<Route path="a/*" element={<Dashboard />} />
|
||||||
|
<Route path="invite" element={<InvitePage />} />
|
||||||
|
<Route path="login" element={<LoginPage />} />
|
||||||
|
<Route path="reset-password" element={<ResetPasswordPage />} />
|
||||||
|
<Route path="*" element={<NotFound />} />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
{
|
||||||
|
basename: __BASE__,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const Loading = () => (
|
||||||
|
<div className="bg-grey-5 text-grey-90 flex h-screen w-full items-center justify-center">
|
||||||
|
<Spinner variant="secondary" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<Suspense fallback={<Loading />}>
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default App
|
||||||
107
packages/admin-ui/ui/src/assets/styles/emoji-picker.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
456
packages/admin-ui/ui/src/assets/styles/global.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
5
packages/admin-ui/ui/src/assets/svg/carrot.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12.4292 12.3984L10.556 10.5251M2.47656 17.5004C2.47656 17.5004 9.99245 14.8351 12.1703 12.6573C12.4888 12.3393 12.7416 11.9617 12.9141 11.546C13.0867 11.1303 13.1757 10.6847 13.176 10.2346C13.1764 9.78457 13.0881 9.33883 12.9162 8.92288C12.7443 8.50693 12.4921 8.12891 12.1741 7.81041C11.8561 7.49191 11.4785 7.23917 11.0628 7.0666C10.6471 6.89404 10.2015 6.80504 9.75147 6.80469C9.3014 6.80434 8.85566 6.89263 8.43971 7.06454C8.02376 7.23645 7.64575 7.48861 7.32724 7.80661C5.14177 9.99208 2.47656 17.5004 2.47656 17.5004ZM7.32724 11.6369L5.76619 10.0835L7.32724 11.6369Z" stroke="#9CA3AF" stroke-width="1.52298" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M17.5003 7.82962C17.5003 7.82962 16.4876 6.30664 14.8351 6.30664C13.5863 6.30664 12.1699 7.82962 12.1699 7.82962C12.1699 7.82962 13.1827 9.3526 14.8351 9.3526C16.4876 9.3526 17.5003 7.82962 17.5003 7.82962Z" stroke="#9CA3AF" stroke-width="1.52298" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12.1695 2.49805C12.1695 2.49805 10.6465 3.51083 10.6465 5.16326C10.6465 6.81569 12.1695 7.82847 12.1695 7.82847C12.1695 7.82847 13.6924 6.42733 13.6924 5.16326C13.6924 3.51083 12.1695 2.49805 12.1695 2.49805Z" stroke="#9CA3AF" stroke-width="1.52298" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
7
packages/admin-ui/ui/src/assets/svg/controller.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5.47266 8.89453H8.47266" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M6.97266 7.39453V10.3945" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12.4512 9.58398H12.4599" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M14.1055 7.71289H14.1142" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M13.99 4.5H6.01C5.26771 4.50018 4.55184 4.78865 4.00078 5.30965C3.44971 5.83065 3.10258 6.54717 3.0265 7.32071C3.022 7.36157 3.019 7.40007 3.01375 7.44014C2.953 7.96971 2.5 11.9297 2.5 13.1429C2.5 13.768 2.73705 14.3676 3.15901 14.8096C3.58097 15.2517 4.15326 15.5 4.75 15.5C5.5 15.5 5.875 15.1071 6.25 14.7143L7.3105 13.6033C7.59174 13.3086 7.97321 13.1429 8.371 13.1429H11.629C12.0268 13.1429 12.4083 13.3086 12.6895 13.6033L13.75 14.7143C14.125 15.1071 14.5 15.5 15.25 15.5C15.8467 15.5 16.419 15.2517 16.841 14.8096C17.2629 14.3676 17.5 13.768 17.5 13.1429C17.5 11.9289 17.047 7.96971 16.9863 7.44014C16.981 7.40086 16.978 7.36157 16.9735 7.3215C16.8976 6.54782 16.5505 5.8311 15.9995 5.30994C15.4484 4.78878 14.7324 4.5002 13.99 4.5V4.5Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
4
packages/admin-ui/ui/src/assets/svg/flag.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4.16602 12.25C4.16602 12.25 4.91602 11.5 7.16602 11.5C9.41602 11.5 10.916 13 13.166 13C15.416 13 16.166 12.25 16.166 12.25V3.25C16.166 3.25 15.416 4 13.166 4C10.916 4 9.41602 2.5 7.16602 2.5C4.91602 2.5 4.16602 3.25 4.16602 3.25V12.25Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M4.16602 17.5V12.25" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 550 B |
6
packages/admin-ui/ui/src/assets/svg/happy.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10 17.5C14.1421 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 10 17.5Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M6.66602 11.666C6.66602 11.666 7.91602 13.3327 9.99935 13.3327C12.0827 13.3327 13.3327 11.666 13.3327 11.666" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M8 8H8.00875" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12 8H12.0088" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 759 B |
3
packages/admin-ui/ui/src/assets/svg/heart.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M16.316 4.52475C15.9399 4.14732 15.493 3.84785 15.0008 3.64351C14.5087 3.43917 13.9811 3.33398 13.4483 3.33398C12.9154 3.33398 12.3878 3.43917 11.8957 3.64351C11.4036 3.84785 10.9567 4.14732 10.5806 4.52475L10.0033 5.10955L9.42596 4.52475C9.04984 4.14732 8.6029 3.84785 8.11079 3.64351C7.61868 3.43917 7.09108 3.33398 6.55823 3.33398C6.02538 3.33398 5.49778 3.43917 5.00567 3.64351C4.51356 3.84785 4.06663 4.14732 3.6905 4.52475C2.10107 6.11419 2.0036 8.79823 4.00539 10.8375L10.0033 16.8354L16.0011 10.8375C18.0029 8.79823 17.9054 6.11419 16.316 4.52475Z" stroke="#9CA3AF" stroke-width="1.49947" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 758 B |
5
packages/admin-ui/ui/src/assets/svg/lightbulb.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M7.875 14.25H12.125" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M8.58398 17.084H11.4173" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12.1887 11.416C12.3162 10.7218 12.6492 10.1835 13.1875 9.64518C13.5295 9.33033 13.801 8.94664 13.9842 8.51934C14.1673 8.09205 14.2579 7.63083 14.25 7.16602C14.25 6.03885 13.8022 4.95784 13.0052 4.16081C12.2082 3.36378 11.1272 2.91602 10 2.91602C8.87283 2.91602 7.79183 3.36378 6.9948 4.16081C6.19777 4.95784 5.75 6.03885 5.75 7.16602C5.75 7.87435 5.91292 8.7456 6.8125 9.64518C7.32537 10.1141 7.67525 10.7345 7.81125 11.416" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 857 B |
3
packages/admin-ui/ui/src/assets/svg/plane.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M13.9856 15.1639L12.6495 9.07732L15.2474 6.47938C16.3608 5.36598 16.732 3.88144 16.3608 3.13918C15.6186 2.76804 14.134 3.13918 13.0206 4.25258L10.4227 6.85052L4.33608 5.51443C3.96495 5.44021 3.66804 5.58866 3.51959 5.88557L3.29691 6.2567C3.14845 6.62783 3.22268 6.99897 3.51959 7.22165L7.45361 9.81959L5.96907 12.0464H3.74227L3 12.7887L5.2268 14.2732L6.71134 16.5L7.45361 15.7577V13.5309L9.68041 12.0464L12.2784 15.9804C12.501 16.2773 12.8722 16.3515 13.2433 16.2031L13.6144 16.0546C13.9113 15.832 14.0598 15.5351 13.9856 15.1639V15.1639Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 737 B |
3
packages/admin-ui/ui/src/assets/svg/search.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M16.9696 18.0303C17.2625 18.3232 17.7374 18.3232 18.0302 18.0303C18.3231 17.7374 18.3231 17.2626 18.0302 16.9697L16.9696 18.0303ZM14.4052 13.3447C14.1124 13.0518 13.6375 13.0518 13.3446 13.3447C13.0517 13.6376 13.0517 14.1124 13.3446 14.4053L14.4052 13.3447ZM15.0833 9.16667C15.0833 12.4344 12.4344 15.0833 9.16667 15.0833V16.5833C13.2628 16.5833 16.5833 13.2628 16.5833 9.16667H15.0833ZM9.16667 15.0833C5.89898 15.0833 3.25 12.4344 3.25 9.16667H1.75C1.75 13.2628 5.07055 16.5833 9.16667 16.5833V15.0833ZM3.25 9.16667C3.25 5.89898 5.89898 3.25 9.16667 3.25V1.75C5.07055 1.75 1.75 5.07055 1.75 9.16667H3.25ZM9.16667 3.25C12.4344 3.25 15.0833 5.89898 15.0833 9.16667H16.5833C16.5833 5.07055 13.2628 1.75 9.16667 1.75V3.25ZM18.0302 16.9697L14.4052 13.3447L13.3446 14.4053L16.9696 18.0303L18.0302 16.9697Z" fill="#9CA3AF"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 932 B |
6
packages/admin-ui/ui/src/assets/svg/sprout.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5.83398 16.666H14.1673" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M8.33398 16.6673C12.9173 14.584 9.00065 11.334 10.834 8.33398" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M7.91732 7.83349C8.83398 8.50016 9.41732 9.66683 9.83398 10.9168C8.16732 11.2502 6.91732 11.2502 5.83398 10.6668C4.83398 10.1668 3.91732 9.08349 3.33398 7.16683C5.66732 6.75016 7.00065 7.16683 7.91732 7.83349V7.83349Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M11.7498 5.00065C11.1145 5.99362 10.7949 7.15577 10.8332 8.33398C12.4165 8.25065 13.5832 7.83398 14.4165 7.16732C15.2498 6.33398 15.7498 5.25065 15.8332 3.33398C13.5832 3.41732 12.4998 4.16732 11.7498 5.00065Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 997 B |
3
packages/admin-ui/ui/src/assets/svg/star.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10 2.5L12.3175 7.195L17.5 7.9525L13.75 11.605L14.635 16.765L10 14.3275L5.365 16.765L6.25 11.605L2.5 7.9525L7.6825 7.195L10 2.5Z" stroke="#9CA3AF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 327 B |
62
packages/admin-ui/ui/src/components/atoms/avatar/index.tsx
Normal file
@@ -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<AvatarProps> = ({
|
||||||
|
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 (
|
||||||
|
<RadixAvatar.Root
|
||||||
|
className={clsx(
|
||||||
|
"w-full h-full items-center justify-center overflow-hidden select-none rounded-circle",
|
||||||
|
color
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<RadixAvatar.Image
|
||||||
|
src={user?.img}
|
||||||
|
alt={username}
|
||||||
|
className="object-cover w-full h-full rounded-circle"
|
||||||
|
/>
|
||||||
|
<RadixAvatar.Fallback
|
||||||
|
className={clsx(
|
||||||
|
"w-full h-full flex items-center justify-center bg-inherit text-grey-0 rounded-circle",
|
||||||
|
font
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<Spinner size="small" variant="primary" />
|
||||||
|
) : (
|
||||||
|
username.slice(0, 1).toUpperCase()
|
||||||
|
)}
|
||||||
|
</RadixAvatar.Fallback>
|
||||||
|
</RadixAvatar.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Avatar
|
||||||
@@ -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 (
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
path ? navigate(path) : navigate(-1)
|
||||||
|
}}
|
||||||
|
className={clsx("px-small py-xsmall", className)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-x-xsmall text-grey-50 inter-grey-40 inter-small-semibold">
|
||||||
|
<ArrowLeftIcon size={20} />
|
||||||
|
<span className="ml-1">{label}</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BackButton
|
||||||
32
packages/admin-ui/ui/src/components/atoms/checkbox/index.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import clsx from "clsx"
|
||||||
|
import React, { ReactNode, useImperativeHandle } from "react"
|
||||||
|
|
||||||
|
export type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
||||||
|
label: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const Checkbox = React.forwardRef(
|
||||||
|
({ label, value, className, id, ...rest }: CheckboxProps, ref) => {
|
||||||
|
const checkboxRef = React.useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => checkboxRef.current)
|
||||||
|
return (
|
||||||
|
<label
|
||||||
|
className={clsx("flex items-center cursor-pointer", className)}
|
||||||
|
htmlFor={id}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
ref={checkboxRef}
|
||||||
|
className="form-checkbox w-[20px] h-[20px] rounded-base text-violet-60 focus:ring-0 mr-small border-grey-30"
|
||||||
|
value={value}
|
||||||
|
id={id}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Checkbox
|
||||||
@@ -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<CopyToClipboardProps> = ({
|
||||||
|
value,
|
||||||
|
displayValue,
|
||||||
|
successDuration = 3000,
|
||||||
|
showValue = true,
|
||||||
|
iconSize = 20,
|
||||||
|
onCopy = () => {},
|
||||||
|
}) => {
|
||||||
|
const [isCopied, handleCopy] = useClipboard(value, {
|
||||||
|
onCopied: onCopy,
|
||||||
|
successDuration: successDuration,
|
||||||
|
})
|
||||||
|
const notification = useNotification()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isCopied) {
|
||||||
|
notification("Success", "Copied!", "success")
|
||||||
|
}
|
||||||
|
}, [isCopied])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center inter-small-regular text-grey-50 gap-x-xsmall">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="small"
|
||||||
|
className={clsx("p-0 text-grey-50", {
|
||||||
|
["text-violet-60"]: isCopied,
|
||||||
|
})}
|
||||||
|
onClick={handleCopy}
|
||||||
|
>
|
||||||
|
<ClipboardCopyIcon size={iconSize} />
|
||||||
|
</Button>
|
||||||
|
{showValue && (
|
||||||
|
<span className="w-full truncate">
|
||||||
|
{displayValue ? displayValue : value}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CopyToClipboard
|
||||||
@@ -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 (
|
||||||
|
<div className="flex w-full gap-4 items-center">
|
||||||
|
<div className="flex flex-1 items-center justify-end gap-3">
|
||||||
|
<NativeSelect
|
||||||
|
defaultValue={monthName}
|
||||||
|
onValueChange={(v) => changeMonth(monthNames.indexOf(v))}
|
||||||
|
>
|
||||||
|
{monthNames.map((month) => (
|
||||||
|
<NativeSelect.Item key={month} value={month}>
|
||||||
|
{month}
|
||||||
|
</NativeSelect.Item>
|
||||||
|
))}
|
||||||
|
</NativeSelect>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-1 items-center justify-start gap-3">
|
||||||
|
<NativeSelect
|
||||||
|
defaultValue={year.toString()}
|
||||||
|
onValueChange={(v) => changeYear(parseInt(v, 10))}
|
||||||
|
>
|
||||||
|
{getYearRange().map((year) => (
|
||||||
|
<NativeSelect.Item key={year} value={year.toString()}>
|
||||||
|
{year.toString()}
|
||||||
|
</NativeSelect.Item>
|
||||||
|
))}
|
||||||
|
</NativeSelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomHeader
|
||||||
@@ -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<DateTimePickerProps> = ({
|
||||||
|
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 (
|
||||||
|
<div className="w-full">
|
||||||
|
<PopoverPrimitive.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
|
<PopoverPrimitive.Trigger asChild>
|
||||||
|
<button
|
||||||
|
className={clsx("w-full rounded-rounded border ", {
|
||||||
|
"shadow-input border-violet-60": isOpen,
|
||||||
|
"border-grey-20": !isOpen,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<InputContainer className="border-0 shadown-none focus-within:shadow-none">
|
||||||
|
<div className="w-full flex text-grey-50 pr-0.5 justify-between">
|
||||||
|
<InputHeader
|
||||||
|
{...{
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
tooltipContent,
|
||||||
|
tooltip,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ArrowDownIcon size={16} />
|
||||||
|
</div>
|
||||||
|
<label className="w-full text-left">
|
||||||
|
{moment(date).format("ddd, DD MMM YYYY")}
|
||||||
|
</label>
|
||||||
|
</InputContainer>
|
||||||
|
</button>
|
||||||
|
</PopoverPrimitive.Trigger>
|
||||||
|
<PopoverPrimitive.Content
|
||||||
|
side="top"
|
||||||
|
sideOffset={8}
|
||||||
|
className="rounded-rounded px-8 border border-grey-20 bg-grey-0 w-full shadow-dropdown"
|
||||||
|
>
|
||||||
|
<CalendarComponent date={tempDate} onChange={setTempDate} />
|
||||||
|
<div className="flex w-full mb-8 mt-5">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="medium"
|
||||||
|
onClick={() => setIsOpen(false)}
|
||||||
|
className="mr-2 w-1/3 flex justify-center border border-grey-20"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="medium"
|
||||||
|
variant="primary"
|
||||||
|
onClick={() => submitDate()}
|
||||||
|
className="w-2/3 flex justify-center"
|
||||||
|
>{`Set ${label}`}</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverPrimitive.Content>
|
||||||
|
</PopoverPrimitive.Root>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CalendarComponent = ({ date, onChange }) => (
|
||||||
|
<ReactDatePicker
|
||||||
|
selected={date}
|
||||||
|
inline
|
||||||
|
onChange={onChange}
|
||||||
|
calendarClassName="date-picker"
|
||||||
|
dayClassName={(d) => getDateClassname(d, date)}
|
||||||
|
renderCustomHeader={({ ...props }) => <CustomHeader {...props} />}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default DatePicker
|
||||||
@@ -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<DateTimePickerProps> = ({
|
||||||
|
date,
|
||||||
|
onSubmitDate,
|
||||||
|
label = "start date",
|
||||||
|
required = false,
|
||||||
|
tooltipContent,
|
||||||
|
tooltip,
|
||||||
|
}) => {
|
||||||
|
const [selectedMinute, setSelectedMinute] = useState(
|
||||||
|
new Date(date)?.getUTCMinutes()
|
||||||
|
)
|
||||||
|
const [selectedHour, setSelectedHour] = useState(
|
||||||
|
new Date(date)?.getUTCHours()
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedMinute(new Date(date)?.getUTCMinutes())
|
||||||
|
setSelectedHour(new Date(date)?.getUTCHours())
|
||||||
|
}, [date])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (date && !isNil(selectedHour) && !isNil(selectedMinute)) {
|
||||||
|
const newDate = new Date(new Date(date).getTime())
|
||||||
|
newDate.setUTCHours(selectedHour)
|
||||||
|
newDate.setUTCMinutes(selectedMinute)
|
||||||
|
onSubmitDate(newDate)
|
||||||
|
}
|
||||||
|
}, [selectedMinute, selectedHour])
|
||||||
|
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
||||||
|
const minuteNumbers = [...Array(60).keys()]
|
||||||
|
const hourNumbers = [...Array(24).keys()]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<PopoverPrimitive.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
|
<PopoverPrimitive.Trigger asChild>
|
||||||
|
<button
|
||||||
|
className={clsx("w-full rounded-rounded border ", {
|
||||||
|
"shadow-input border-violet-60": isOpen,
|
||||||
|
"border-grey-20": !isOpen,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<InputContainer className="border-0 shadown-none focus-within:shadow-none">
|
||||||
|
<div className="w-full flex text-grey-50 pr-0.5 justify-between">
|
||||||
|
<InputHeader
|
||||||
|
{...{
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
tooltipContent,
|
||||||
|
tooltip,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ArrowDownIcon size={16} />
|
||||||
|
</div>
|
||||||
|
<div className="w-full items-center flex text-left text-grey-40">
|
||||||
|
<ClockIcon size={16} />
|
||||||
|
<span className="mx-1">UTC</span>
|
||||||
|
<span className="text-grey-90">
|
||||||
|
{moment.utc(date).format("HH:mm")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</InputContainer>
|
||||||
|
</button>
|
||||||
|
</PopoverPrimitive.Trigger>
|
||||||
|
<PopoverPrimitive.Content
|
||||||
|
side="top"
|
||||||
|
sideOffset={8}
|
||||||
|
className="rounded-rounded scrollbar-hide border px-6 pt-6 pb-4 border-grey-20 bg-grey-0 w-full flex shadow-dropdown"
|
||||||
|
>
|
||||||
|
<NumberScroller
|
||||||
|
numbers={hourNumbers}
|
||||||
|
selected={selectedHour}
|
||||||
|
onSelect={(n) => setSelectedHour(n)}
|
||||||
|
className="pr-4"
|
||||||
|
/>
|
||||||
|
<NumberScroller
|
||||||
|
numbers={minuteNumbers}
|
||||||
|
selected={selectedMinute}
|
||||||
|
onSelect={(n) => setSelectedMinute(n)}
|
||||||
|
/>
|
||||||
|
<div className="absolute bottom-4 left-0 right-0 bg-gradient-to-b from-transparent to-grey-0 h-xlarge z-10" />
|
||||||
|
</PopoverPrimitive.Content>
|
||||||
|
</PopoverPrimitive.Root>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TimePicker
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { InputHeaderProps } from "../../fundamentals/input-header"
|
||||||
|
|
||||||
|
export type DateTimePickerProps = {
|
||||||
|
date: Date
|
||||||
|
onSubmitDate: (newDate: Date) => void
|
||||||
|
} & InputHeaderProps
|
||||||
@@ -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",
|
||||||
|
]
|
||||||
@@ -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<FadeProps> = ({
|
||||||
|
isVisible,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
classname,
|
||||||
|
children,
|
||||||
|
isFullScreen = false,
|
||||||
|
}) => {
|
||||||
|
const [show, setShow] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show && !isVisible) {
|
||||||
|
setTimeout(() => setShow(false), 100)
|
||||||
|
} else {
|
||||||
|
setShow(isVisible)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const classes = {
|
||||||
|
[start || "scale-[0.98] opacity-0"]: !isVisible,
|
||||||
|
[end || "scale-100 opacity-100"]: isVisible,
|
||||||
|
"absolute inset-0": show && isFullScreen,
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx("transition-all duration-100 z-50", classes, classname)}
|
||||||
|
>
|
||||||
|
{show ? children : null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Fade
|
||||||
@@ -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 = (
|
||||||
|
<span>
|
||||||
|
Drop your images here, or{" "}
|
||||||
|
<span className="text-violet-60">click to browse</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
|
||||||
|
const FileUploadField: React.FC<FileUploadFieldProps> = ({
|
||||||
|
onFileChosen,
|
||||||
|
filetypes,
|
||||||
|
errorMessage,
|
||||||
|
className,
|
||||||
|
text = defaultText,
|
||||||
|
placeholder = "",
|
||||||
|
multiple = false,
|
||||||
|
}) => {
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
const [fileUploadError, setFileUploadError] = useState(false)
|
||||||
|
|
||||||
|
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const fileList = e.target.files
|
||||||
|
|
||||||
|
if (fileList) {
|
||||||
|
onFileChosen(Array.from(fileList))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFileDrop = (e: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
setFileUploadError(false)
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const files: File[] = []
|
||||||
|
|
||||||
|
if (e.dataTransfer.items) {
|
||||||
|
// Use DataTransferItemList interface to access the file(s)
|
||||||
|
for (let i = 0; i < e.dataTransfer.items.length; i++) {
|
||||||
|
// If dropped items aren't files, reject them
|
||||||
|
if (e.dataTransfer.items[i].kind === "file") {
|
||||||
|
const file = e.dataTransfer.items[i].getAsFile()
|
||||||
|
if (file && filetypes.indexOf(file.type) > -1) {
|
||||||
|
files.push(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use DataTransfer interface to access the file(s)
|
||||||
|
for (let i = 0; i < e.dataTransfer.files.length; i++) {
|
||||||
|
if (filetypes.indexOf(e.dataTransfer.files[i].type) > -1) {
|
||||||
|
files.push(e.dataTransfer.files[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (files.length === 1) {
|
||||||
|
onFileChosen(files)
|
||||||
|
} else {
|
||||||
|
setFileUploadError(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={() => inputRef?.current?.click()}
|
||||||
|
onDrop={handleFileDrop}
|
||||||
|
onDragOver={(e) => e.preventDefault()}
|
||||||
|
className={clsx(
|
||||||
|
"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
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<p>{text}</p>
|
||||||
|
{placeholder}
|
||||||
|
</div>
|
||||||
|
{fileUploadError && (
|
||||||
|
<span className="text-rose-60">
|
||||||
|
{errorMessage || "Please upload an image file"}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
accept={filetypes.join(", ")}
|
||||||
|
multiple={multiple}
|
||||||
|
type="file"
|
||||||
|
onChange={handleFileUpload}
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileUploadField
|
||||||
@@ -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 (
|
||||||
|
<Tooltip content={includesTax ? "Tax incl. price" : "Tax excl. price"}>
|
||||||
|
<div className="w-large h-large rounded-rounded border border-grey-20 flex items-center justify-center">
|
||||||
|
<TaxesIcon
|
||||||
|
size={16}
|
||||||
|
className={clsx({
|
||||||
|
"text-grey-50": includesTax,
|
||||||
|
"text-grey-30": !includesTax,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IncludesTaxTooltip
|
||||||
@@ -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 (
|
||||||
|
<ErrorMessage
|
||||||
|
name={name}
|
||||||
|
errors={errors}
|
||||||
|
render={({ message, messages }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx("text-rose-50 inter-small-regular mt-2", className)}
|
||||||
|
>
|
||||||
|
{messages ? (
|
||||||
|
<MultipleMessages messages={messages} />
|
||||||
|
) : (
|
||||||
|
<p>{message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const MultipleMessages = ({ messages }: { messages: MultipleFieldErrors }) => {
|
||||||
|
const errors = Object.entries(messages).map(([_, message]) => message)
|
||||||
|
|
||||||
|
const displayedError = errors[0]
|
||||||
|
const remainderErrors = errors.slice(1)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-x-1 cursor-default">
|
||||||
|
<p>{displayedError}</p>
|
||||||
|
{remainderErrors?.length > 0 && (
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<div className="text-rose-50 inter-small-regular">
|
||||||
|
{remainderErrors.map((e, i) => {
|
||||||
|
return (
|
||||||
|
<p key={i}>
|
||||||
|
{Array.from(Array(i + 1)).map((_) => "*")}
|
||||||
|
{e}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
+{remainderErrors.length}{" "}
|
||||||
|
{remainderErrors.length > 1 ? "errors" : "error"}
|
||||||
|
</p>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InputError
|
||||||
@@ -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 || <Spinner size="large" variant="secondary" />
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="pt-2xlarge flex min-h-[756px] w-full items-center justify-center"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{placeholder}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return children as React.ReactElement
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoadingContainer
|
||||||
@@ -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<NotificationProps> = ({
|
||||||
|
toast,
|
||||||
|
type,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
}) => {
|
||||||
|
const onDismiss = () => {
|
||||||
|
globalToast.dismiss(toast.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToasterContainer visible={toast.visible} className="w-[380px]">
|
||||||
|
<div>{getIcon(type)}</div>
|
||||||
|
<div className="flex flex-col ml-small mr-base gap-y-2xsmall flex-grow text-white">
|
||||||
|
<span className="inter-small-semibold">{title}</span>
|
||||||
|
<span className="inter-small-regular text-grey-20">{message}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onClick={onDismiss}>
|
||||||
|
<CrossIcon size={ICON_SIZE} className="text-grey-40" />
|
||||||
|
</button>
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</div>
|
||||||
|
</ToasterContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ICON_SIZE = 20
|
||||||
|
|
||||||
|
function getIcon(type: NotificationTypes) {
|
||||||
|
switch (type) {
|
||||||
|
case "success":
|
||||||
|
return <CheckCircleIcon size={ICON_SIZE} className="text-emerald-40" />
|
||||||
|
case "warning":
|
||||||
|
return <AlertIcon size={ICON_SIZE} className="text-orange-40" />
|
||||||
|
case "error":
|
||||||
|
return <XCircleIcon size={ICON_SIZE} className="text-rose-40" />
|
||||||
|
default:
|
||||||
|
return <InfoIcon size={ICON_SIZE} className="text-grey-40" />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Notification
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import clsx from "clsx"
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
type NumberScrollerProps = {
|
||||||
|
numbers: number[]
|
||||||
|
selected: number
|
||||||
|
onSelect: (value: number) => void
|
||||||
|
} & React.HTMLAttributes<HTMLDivElement>
|
||||||
|
|
||||||
|
const NumberScroller: React.FC<NumberScrollerProps> = ({
|
||||||
|
numbers,
|
||||||
|
selected,
|
||||||
|
onSelect,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
className={clsx(
|
||||||
|
"flex flex-col time-list h-[305px] overflow-y-auto",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{numbers.map((n, i) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={clsx(
|
||||||
|
"w-[40px] h-[40px] last:mb-4 rounded inter-base-regular hover:bg-grey-20",
|
||||||
|
{
|
||||||
|
"bg-violet-60 hover:bg-violet-50 text-grey-0 inter-base-semibold":
|
||||||
|
n === selected,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<button onClick={() => onSelect(n)} className="w-full h-full py-2">
|
||||||
|
{n.toLocaleString("en-US", { minimumIntegerDigits: 2 })}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NumberScroller
|
||||||
@@ -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 (
|
||||||
|
<div className="flex items-center text-grey-40">
|
||||||
|
<p className="m-0 inter-base-semibold">
|
||||||
|
<span className="inter-base-semibold">{modifiers} </span>
|
||||||
|
{input}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OSShortcut
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import React from "react"
|
||||||
|
|
||||||
|
type PageDescriptionProps = {
|
||||||
|
title?: string
|
||||||
|
subtitle?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const PageDescription: React.FC<PageDescriptionProps> = ({
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="mb-xlarge">
|
||||||
|
<h1 className="inter-2xlarge-semibold mb-xsmall">{title}</h1>
|
||||||
|
<h2 className="inter-base-regular text-grey-50">{subtitle}</h2>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PageDescription
|
||||||
@@ -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<SavingStateProps> = ({
|
||||||
|
toast,
|
||||||
|
title = "Error",
|
||||||
|
message = "An error occured while trying to save your changes. Please try again.",
|
||||||
|
onDismiss,
|
||||||
|
}) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const life = setTimeout(() => {
|
||||||
|
onDismiss()
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(life)
|
||||||
|
}
|
||||||
|
}, [toast])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToasterContainer visible={toast.visible} className="w-[448px]">
|
||||||
|
<div>
|
||||||
|
<XCircleIcon size={20} className="text-rose-40" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col ml-small mr-base gap-y-2xsmall flex-grow">
|
||||||
|
<span className="inter-small-semibold">{title}</span>
|
||||||
|
<span className="inter-small-regular text-grey-50">{message}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onClick={onDismiss}>
|
||||||
|
<CrossIcon size={20} className="text-grey-40" />
|
||||||
|
</button>
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</div>
|
||||||
|
</ToasterContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ErrorState
|
||||||
@@ -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<void>
|
||||||
|
reset: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const SaveNotification: React.FC<SaveNotificationProps> = ({
|
||||||
|
toast,
|
||||||
|
icon,
|
||||||
|
title = "Unsaved changes",
|
||||||
|
message = "You have unsaved changes. Do you want to save and publish or discard them?",
|
||||||
|
onSave,
|
||||||
|
reset,
|
||||||
|
}) => {
|
||||||
|
const onDismiss = () => {
|
||||||
|
reset()
|
||||||
|
globalToast.dismiss(toast.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
globalToast.custom((t) => <SavingState toast={t} />, {
|
||||||
|
id: toast.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
onSave()
|
||||||
|
.then(() => {
|
||||||
|
globalToast.custom(
|
||||||
|
(t) => <SuccessState toast={t} onDismiss={onDismiss} />,
|
||||||
|
{
|
||||||
|
id: toast.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.catch((_err) => {
|
||||||
|
globalToast.custom(
|
||||||
|
(t) => <ErrorState toast={t} onDismiss={onDismiss} />,
|
||||||
|
{
|
||||||
|
id: toast.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToasterContainer visible={toast.visible} className="p-0 pl-base w-[448px]">
|
||||||
|
<div className="py-base">{getIcon(icon)}</div>
|
||||||
|
<div className="flex flex-col ml-small mr-base gap-y-2xsmall flex-grow py-base">
|
||||||
|
<span className="inter-small-semibold">{title}</span>
|
||||||
|
<span className="inter-small-regular text-grey-50">{message}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col inter-small-semibold border-l border-grey-20 h-full">
|
||||||
|
<button
|
||||||
|
onClick={handleSave}
|
||||||
|
className="inter-small-semibold flex items-center justify-center h-1/2 border-b border-grey-20 px-base text-violet-60"
|
||||||
|
>
|
||||||
|
Publish
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="inter-small-semibold flex items-center justify-center h-1/2 px-base"
|
||||||
|
onClick={onDismiss}
|
||||||
|
>
|
||||||
|
Discard
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ToasterContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ICON_SIZE = 20
|
||||||
|
|
||||||
|
function getIcon(icon?: any) {
|
||||||
|
if (icon) {
|
||||||
|
return React.cloneElement(icon, {
|
||||||
|
size: ICON_SIZE,
|
||||||
|
className: "text-grey-90",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return <RefreshIcon size={20} className="text-grey-90" />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SaveNotification
|
||||||
@@ -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<SavingStateProps> = ({
|
||||||
|
toast,
|
||||||
|
title = "Saving changes",
|
||||||
|
message = "Hang on, this may take a few moments.",
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ToasterContainer visible={toast.visible} className="w-[448px]">
|
||||||
|
<div>
|
||||||
|
<Spinner variant="secondary" size="large" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col ml-small mr-base gap-y-2xsmall flex-grow">
|
||||||
|
<span className="inter-small-semibold">{title}</span>
|
||||||
|
<span className="inter-small-regular text-grey-50">{message}</span>
|
||||||
|
</div>
|
||||||
|
</ToasterContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SavingState
|
||||||
@@ -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<SavingStateProps> = ({
|
||||||
|
toast,
|
||||||
|
title = "Success",
|
||||||
|
message = "Your changes have been saved.",
|
||||||
|
onDismiss,
|
||||||
|
}) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const life = setTimeout(() => {
|
||||||
|
onDismiss()
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(life)
|
||||||
|
}
|
||||||
|
}, [toast])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToasterContainer visible={toast.visible} className="w-[448px]">
|
||||||
|
<div>
|
||||||
|
<CheckCircleIcon size={20} className="text-emerald-40" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col ml-small mr-base gap-y-2xsmall flex-grow">
|
||||||
|
<span className="inter-small-semibold">{title}</span>
|
||||||
|
<span className="inter-small-regular text-grey-50">{message}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onClick={onDismiss}>
|
||||||
|
<CrossIcon size={20} className="text-grey-40" />
|
||||||
|
</button>
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</div>
|
||||||
|
</ToasterContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SuccessState
|
||||||
@@ -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<SettingsCardProps> = ({
|
||||||
|
icon,
|
||||||
|
heading,
|
||||||
|
description,
|
||||||
|
to = null,
|
||||||
|
externalLink = null,
|
||||||
|
disabled = false,
|
||||||
|
}) => {
|
||||||
|
if (disabled) {
|
||||||
|
to = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link to={to ?? ""} className="flex flex-1 items-center">
|
||||||
|
<button
|
||||||
|
className="bg-grey-0 rounded-rounded p-large border-grey-20 group flex h-full flex-1 items-center border"
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => {
|
||||||
|
if (externalLink) {
|
||||||
|
window.location.href = externalLink
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="h-2xlarge w-2xlarge bg-violet-20 rounded-circle text-violet-60 group-disabled:bg-grey-10 group-disabled:text-grey-40 flex items-center justify-center">
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
<div className="mx-large flex-1 text-left">
|
||||||
|
<h3 className="inter-large-semibold text-grey-90 group-disabled:text-grey-40 m-0">
|
||||||
|
{heading}
|
||||||
|
</h3>
|
||||||
|
<p className="inter-base-regular text-grey-50 group-disabled:text-grey-40 m-0">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-grey-40 group-disabled:text-grey-30">
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingsCard
|
||||||
25
packages/admin-ui/ui/src/components/atoms/skeleton/index.tsx
Normal file
@@ -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 (
|
||||||
|
<div
|
||||||
|
className={clsx("h-fit w-fit", {
|
||||||
|
"bg-grey-10 rounded-rounded animate-pulse [&>*]:opacity-0": state,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Skeleton
|
||||||
35
packages/admin-ui/ui/src/components/atoms/spinner.tsx
Normal file
@@ -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<SpinnerProps> = ({
|
||||||
|
size = "large",
|
||||||
|
variant = "primary",
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"flex items-center justify-center",
|
||||||
|
{ "h-[24px] w-[24px]": size === "large" },
|
||||||
|
{ "h-[20px] w-[20px]": size === "medium" },
|
||||||
|
{ "h-[16px] w-[16px]": size === "small" }
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center relative w-full h-full">
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"animate-ring border-2 h-4/5 w-4/5 rounded-circle border-transparent",
|
||||||
|
{ "border-t-grey-0": variant === "primary" },
|
||||||
|
{ "border-t-violet-60": variant === "secondary" }
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Spinner
|
||||||
26
packages/admin-ui/ui/src/components/atoms/switch/index.tsx
Normal file
@@ -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 (
|
||||||
|
<RadixSwitch.Root
|
||||||
|
{...props}
|
||||||
|
disabled={props.disabled}
|
||||||
|
className={clsx(
|
||||||
|
"w-8 h-[18px] rounded-full transition-bg bg-gray-300 radix-state-checked:bg-violet-60"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<RadixSwitch.Thumb
|
||||||
|
className={clsx(
|
||||||
|
"w-2 h-2 bg-white rounded-full block transition-transform translate-x-[5px] radix-state-checked:translate-x-[19px]"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</RadixSwitch.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Switch
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import React from "react"
|
||||||
|
import clsx from "clsx"
|
||||||
|
|
||||||
|
type TextInputProps = React.InputHTMLAttributes<HTMLInputElement>
|
||||||
|
|
||||||
|
const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
|
||||||
|
({ className, ...props }, ref) => (
|
||||||
|
<input
|
||||||
|
ref={ref}
|
||||||
|
className={clsx(
|
||||||
|
"placeholder:inter-base-regular placeholder-grey-40 focus:outline-none",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
export default TextInput
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Thumbnail as default } from "./thumbnail"
|
||||||
@@ -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 (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"bg-grey-5 flex items-center justify-center overflow-hidden rounded-rounded",
|
||||||
|
{
|
||||||
|
"w-[30px] h-10": size === "small",
|
||||||
|
"w-9 h-12": size === "medium",
|
||||||
|
"w-[170px] h-[226px]": size === "large",
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{src ? (
|
||||||
|
<img src={src} className="object-cover object-center flex-1" />
|
||||||
|
) : (
|
||||||
|
<ImagePlaceholderIcon />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import clsx from "clsx"
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
type ToasterContainerProps = {
|
||||||
|
visible: boolean
|
||||||
|
} & React.HTMLAttributes<HTMLDivElement>
|
||||||
|
|
||||||
|
const ToasterContainer: React.FC<ToasterContainerProps> = ({
|
||||||
|
children,
|
||||||
|
visible,
|
||||||
|
className,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"flex items-start bg-grey-90 p-base border rounded-rounded shadow-toaster mb-xsmall last:mb-0",
|
||||||
|
{
|
||||||
|
"animate-enter": visible,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"animate-leave": !visible,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToasterContainer
|
||||||
58
packages/admin-ui/ui/src/components/atoms/tooltip/index.tsx
Normal file
@@ -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<HTMLButtonElement>["onClick"]
|
||||||
|
}
|
||||||
|
|
||||||
|
const Tooltip = ({
|
||||||
|
children,
|
||||||
|
content,
|
||||||
|
open,
|
||||||
|
defaultOpen,
|
||||||
|
onOpenChange,
|
||||||
|
delayDuration,
|
||||||
|
className,
|
||||||
|
side,
|
||||||
|
onClick,
|
||||||
|
...props
|
||||||
|
}: TooltipProps) => {
|
||||||
|
return (
|
||||||
|
<RadixTooltip.Provider delayDuration={100}>
|
||||||
|
<RadixTooltip.Root
|
||||||
|
open={open}
|
||||||
|
defaultOpen={defaultOpen}
|
||||||
|
onOpenChange={onOpenChange}
|
||||||
|
delayDuration={delayDuration}
|
||||||
|
>
|
||||||
|
<RadixTooltip.Trigger onClick={onClick} asChild={true}>
|
||||||
|
<span>{children}</span>
|
||||||
|
</RadixTooltip.Trigger>
|
||||||
|
<RadixTooltip.Content
|
||||||
|
side={side ?? "bottom"}
|
||||||
|
sideOffset={8}
|
||||||
|
align="center"
|
||||||
|
className={clsx(
|
||||||
|
"inter-small-semibold text-grey-50",
|
||||||
|
"bg-grey-0 py-2 px-3 shadow-dropdown rounded-rounded",
|
||||||
|
"border border-solid border-grey-20",
|
||||||
|
"max-w-[220px]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</RadixTooltip.Content>
|
||||||
|
</RadixTooltip.Root>
|
||||||
|
</RadixTooltip.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tooltip
|
||||||
@@ -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<HTMLButtonElement, Props>(
|
||||||
|
({ onDelete, deleting = false, children, className }: Props, ref) => {
|
||||||
|
const [armed, setArmed] = useState(false)
|
||||||
|
const innerRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
|
useImperativeHandle<HTMLButtonElement | null, HTMLButtonElement | null>(
|
||||||
|
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 (
|
||||||
|
<button
|
||||||
|
className={clsx(
|
||||||
|
"transition-all rounded-lg border flex items-center justify-center",
|
||||||
|
{
|
||||||
|
"bg-rose-50 border-rose-50 px-3 py-1.5": armed,
|
||||||
|
"bg-transparent border-gray-20 p-1.5": !armed,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"!bg-grey-40 !border-grey-40 !p-1.5": deleting,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
disabled={deleting}
|
||||||
|
onClick={handleTwoStepDelete}
|
||||||
|
ref={innerRef}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={clsx("text-rose-50 inter-small-semibold", {
|
||||||
|
hidden: armed || deleting,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children || <TrashIcon className="text-grey-50" size={20} />}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
className={clsx("text-white inter-small-semibold", {
|
||||||
|
hidden: !armed || deleting,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
content="Are you sure?"
|
||||||
|
side="top"
|
||||||
|
sideOffset={16}
|
||||||
|
open={armed}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={clsx("flex items-center justify-center", {
|
||||||
|
hidden: !deleting,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Spinner size="medium" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default TwoStepDelete
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import clsx from "clsx"
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
type BadgeProps = {
|
||||||
|
variant:
|
||||||
|
| "primary"
|
||||||
|
| "danger"
|
||||||
|
| "success"
|
||||||
|
| "warning"
|
||||||
|
| "ghost"
|
||||||
|
| "default"
|
||||||
|
| "disabled"
|
||||||
|
} & React.HTMLAttributes<HTMLDivElement>
|
||||||
|
|
||||||
|
const Badge: React.FC<BadgeProps> = ({
|
||||||
|
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 (
|
||||||
|
<div
|
||||||
|
className={clsx("badge", variantClassname, className)}
|
||||||
|
onClick={onClick}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Badge
|
||||||
@@ -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<HTMLButtonElement>
|
||||||
|
|
||||||
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
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 (
|
||||||
|
<button
|
||||||
|
{...attributes}
|
||||||
|
className={clsx(
|
||||||
|
"btn",
|
||||||
|
variantClassname,
|
||||||
|
sizeClassname,
|
||||||
|
attributes.className
|
||||||
|
)}
|
||||||
|
disabled={attributes.disabled || loading}
|
||||||
|
ref={ref}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<Spinner size={size} variant={"secondary"} />
|
||||||
|
) : (
|
||||||
|
Children.map(children, (child, i) => {
|
||||||
|
return (
|
||||||
|
<span key={i} className="mr-xsmall last:mr-0">
|
||||||
|
{child}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Button
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15.4444 17H4.55556C3.7 17 3 16.35 3 15.5556V5.44444C3 4.65 3.7 4 4.55556 4H15.4444C16.3 4 17 4.65 17 5.44444V15.5556C17 16.35 16.3 17 15.4444 17Z" stroke="#6B7280" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M13 2.43994V3.43994" stroke="#6B7280" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M7 2.43994V3.43994" stroke="#6B7280" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10 10.7632C11.1046 10.7632 12 9.86775 12 8.76318C12 7.65861 11.1046 6.76318 10 6.76318C8.89543 6.76318 8 7.65861 8 8.76318C8 9.86775 8.89543 10.7632 10 10.7632Z" stroke="#6B7280" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M14 14.2979C12.88 13.6312 11.52 13.2979 10 13.2979C8.48 13.2979 7.12 13.6979 6 14.2979" stroke="#6B7280" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1013 B |
@@ -0,0 +1,57 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../icons/types/icon-type"
|
||||||
|
|
||||||
|
const DetailsIcon: React.FC<IconProps> = ({
|
||||||
|
size = "16",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M15.4444 17H4.55556C3.7 17 3 16.35 3 15.5556V5.44444C3 4.65 3.7 4 4.55556 4H15.4444C16.3 4 17 4.65 17 5.44444V15.5556C17 16.35 16.3 17 15.4444 17Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M13 2.43994V3.43994"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M7 2.43994V3.43994"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M10 10.7632C11.1046 10.7632 12 9.86775 12 8.76318C12 7.65861 11.1046 6.76318 10 6.76318C8.89543 6.76318 8 7.65861 8 8.76318C8 9.86775 8.89543 10.7632 10 10.7632Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M14 14.2979C12.88 13.6312 11.52 13.2979 10 13.2979C8.48 13.2979 7.12 13.6979 6 14.2979"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailsIcon
|
||||||
@@ -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<FeatureToggleProps> = ({
|
||||||
|
featureFlag,
|
||||||
|
showOnlyWhenDisabled = false,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const { isFeatureEnabled } = useFeatureFlag()
|
||||||
|
|
||||||
|
const showContent = isFeatureEnabled(featureFlag) === !showOnlyWhenDisabled
|
||||||
|
return showContent ? <>{children}</> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FeatureToggle
|
||||||
@@ -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<HTMLDivElement>
|
||||||
|
|
||||||
|
const IconBadge: React.FC<IconBadgeProps> = ({
|
||||||
|
children,
|
||||||
|
variant,
|
||||||
|
className,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
variant={variant ?? "default"}
|
||||||
|
className={clsx(
|
||||||
|
"flex items-center justify-center aspect-square w-[40px] h-[40px] border-2 border-white outline outline-1 outline-grey-20",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IconBadge
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const AlertIcon: React.FC<IconProps> = ({
|
||||||
|
size = "20",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10 17.5C14.1421 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 10 17.5Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M10 6.66669V10"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M10 13.3333H10.0088"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AlertIcon
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "./types/icon-type"
|
||||||
|
|
||||||
|
const ArrowDownIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24px",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8 3.33331V12.6666"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M12.667 8L8.00033 12.6667L3.33366 8"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArrowDownIcon
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const ArrowLeftIcon: React.FC<IconProps> = ({
|
||||||
|
size = "20",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.75 10H16.875"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M8.125 5L3.125 10L8.125 15"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArrowLeftIcon
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const ArrowRightIcon: React.FC<IconProps> = ({
|
||||||
|
size = "16",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.33301 8H12.6663"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M8 3.33331L12.6667 7.99998L8 12.6666"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArrowRightIcon
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const ArrowTopRightIcon: React.FC<IconProps> = ({
|
||||||
|
size = "20",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M15.0129 4.98792L4.93799 15.0628M15.0129 4.98792L7.93994 4.93713M15.0129 4.98792L15.0636 12.0608"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArrowTopRightIcon
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "./types/icon-type"
|
||||||
|
|
||||||
|
const ArrowUpIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24px",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8 12.6667V3.33335"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3.33301 8L7.99967 3.33333L12.6663 8"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArrowUpIcon
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const BackIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.33301 2.91669V7.91669H8.33301"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3.41396 11.6784C3.57168 13.1642 4.14444 13.9943 5.18922 15.0653C6.23401 16.1363 7.6093 16.8262 9.0944 17.0244C10.5795 17.2226 12.0883 16.9175 13.3787 16.1581C14.6691 15.3988 15.6663 14.2291 16.2103 12.8369C16.7542 11.4446 16.8134 9.91052 16.3783 8.48073C15.9432 7.05094 15.039 5.80836 13.8109 4.95238C12.5828 4.0964 11.1019 3.67666 9.60596 3.76051C8.10998 3.84436 6.68561 4.42693 5.56142 5.41475L3.33301 7.41474"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BackIcon
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const BackspaceIcon: React.FC<IconProps> = ({
|
||||||
|
size = "16",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M7.5 6.66675L10.1667 9.33341"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M10.1667 6.66675L7.5 9.33341"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4.56137 11.8559L2.55004 9.49392H2.55004C1.81665 8.63293 1.81665 7.36691 2.55004 6.50592L4.56137 4.14392V4.14392C4.9991 3.62949 5.64058 3.33312 6.31604 3.33325H11.6954V3.33325C12.9682 3.33325 14 4.36509 14 5.63792C14 5.63792 14 5.63792 14 5.63792V10.3619V10.3619C14 11.6348 12.9682 12.6666 11.6954 12.6666H6.31604V12.6666C5.64058 12.6667 4.99911 12.3704 4.56137 11.8559V11.8559Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BackspaceIcon
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const BellIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M14 20C13.7968 20.3042 13.505 20.5566 13.154 20.7321C12.803 20.9076 12.4051 21 12 21C11.5949 21 11.197 20.9076 10.846 20.7321C10.495 20.5566 10.2032 20.3042 10 20"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M17.3333 8.2C17.3333 6.82087 16.7714 5.49823 15.7712 4.52304C14.771 3.54786 13.4145 3 12 3C10.5855 3 9.22896 3.54786 8.22876 4.52304C7.22857 5.49823 6.66667 6.82087 6.66667 8.2C6.66667 14.2667 4 16 4 16H20C20 16 17.3333 14.2667 17.3333 8.2Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BellIcon
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
type IBellNotiIconProps = IconProps & {
|
||||||
|
accentColor?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const BellNotiIcon: React.FC<IBellNotiIconProps> = ({
|
||||||
|
size = "24px",
|
||||||
|
color = "currentColor",
|
||||||
|
accentColor = "#F43F5E",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M14 20C13.7968 20.3042 13.505 20.5566 13.154 20.7321C12.803 20.9076 12.4051 21 12 21C11.5949 21 11.197 20.9076 10.846 20.7321C10.495 20.5566 10.2032 20.3042 10 20"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M18.4735 11.7793C18.0077 11.9228 17.5129 12 17.0001 12C16.9947 12 16.9894 12 16.9841 12C17.2526 13.1526 17.6265 14.0517 18.0122 14.7412C18.1157 14.9262 18.2197 15.0955 18.3223 15.25H5.67782C5.78041 15.0955 5.88443 14.9262 5.98793 14.7412C6.72391 13.4256 7.41673 11.347 7.41673 8.2C7.41673 7.026 7.89488 5.89613 8.7524 5.06004C9.61057 4.22333 10.7784 3.75 12.0001 3.75C12.372 3.75 12.7391 3.79389 13.0938 3.87856C13.4571 3.42462 13.8978 3.03537 14.3959 2.73085C13.6453 2.41632 12.831 2.25 12.0001 2.25C10.3927 2.25 8.84747 2.87238 7.70525 3.98604C6.56238 5.10034 5.91673 6.61575 5.91673 8.2C5.91673 8.46632 5.9114 8.7235 5.90126 8.97181L3.58406 15.3759C3.31067 15.5581 3.18742 15.8975 3.28099 16.2132C3.37538 16.5316 3.66795 16.75 4.00006 16.75H20.0001C20.3322 16.75 20.6247 16.5316 20.7191 16.2132C20.8127 15.8975 20.6895 15.5582 20.4161 15.376M3.59055 15.3713L3.59037 15.3718L3.58947 15.3724L3.58787 15.3734L3.58537 15.3751L3.58406 15.3759L3.58945 15.3721C3.5898 15.3718 3.59017 15.3716 3.59055 15.3713ZM3.59055 15.3713L5.90099 8.97837C5.79921 11.4485 5.22124 13.0393 4.67886 14.0088C4.37901 14.5448 4.08562 14.8989 3.87883 15.1117C3.77524 15.2183 3.69289 15.29 3.6417 15.3316C3.61711 15.3516 3.5997 15.3646 3.59055 15.3713ZM20.4108 15.3725L20.4123 15.3734L20.4148 15.3751L20.4157 15.3757C20.4146 15.3749 20.413 15.3737 20.4107 15.3721C20.4019 15.3657 20.384 15.3524 20.3584 15.3316C20.3072 15.29 20.2249 15.2183 20.1213 15.1117C19.9145 14.8989 19.6211 14.5448 19.3213 14.0088C19.0206 13.4713 18.7089 12.7428 18.4735 11.7793M5.90099 8.97837L7.70525 3.98604L5.90126 8.97181C5.90117 8.974 5.90108 8.97619 5.90099 8.97837Z"
|
||||||
|
fill={color}
|
||||||
|
/>
|
||||||
|
<circle cx="17" cy="7" r="3" fill={accentColor} />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BellNotiIcon
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const BellOffIcon: React.FC<IconProps> = ({
|
||||||
|
size = "20",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M11.2971 16.7566C11.1653 16.9839 10.976 17.1726 10.7483 17.3037C10.5206 17.4349 10.2624 17.5039 9.99964 17.5039C9.73686 17.5039 9.47869 17.4349 9.25097 17.3037C9.02326 17.1726 8.83401 16.9839 8.70215 16.7566"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.49999"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M14.9723 10.7567C14.6386 9.53526 14.4795 8.27274 14.4998 7.00671"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.49999"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5.69498 5.7016C5.56468 6.1243 5.49894 6.56426 5.49998 7.00659C5.49998 12.2566 3.25 13.7565 3.25 13.7565H13.7499"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.49999"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M14.4994 7.00665C14.5006 6.19145 14.2804 5.39124 13.8622 4.69148C13.444 3.99173 12.8435 3.41871 12.125 3.03365C11.4065 2.64859 10.5969 2.46595 9.78261 2.50523C8.96836 2.54451 8.18007 2.80424 7.50195 3.25667"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.49999"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M2.5 2.50665L17.4999 17.5065"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.49999"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BellOffIcon
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const BuildingsIcon: React.FC<IconProps> = ({
|
||||||
|
size = "20",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox={`0 0 20 20`}
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M17.0141 16.5422V8.79964C17.0141 8.5943 16.9325 8.39736 16.7873 8.25216C16.6421 8.10696 16.4452 8.02539 16.2398 8.02539H13.1428"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3.07739 16.5422V4.15414C3.07739 3.94879 3.15897 3.75186 3.30417 3.60666C3.44937 3.46146 3.6463 3.37988 3.85165 3.37988H12.3684C12.5738 3.37988 12.7707 3.46146 12.9159 3.60666C13.0611 3.75186 13.1427 3.94879 13.1427 4.15414V16.5422"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5.98071 6.6521H10.2391"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5.98071 9.96094H10.2391"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5.98087 16.5422H10.2393V13.2089H5.98087V16.5422Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M17.7885 16.5422H2.30347"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BuildingsIcon
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const CancelIcon: React.FC<IconProps> = ({
|
||||||
|
size = "16",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10 17.5C14.1421 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 10 17.5Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5 5L15 15"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CancelIcon
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const CartIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5.70291 11.7848L4.18457 5.34766H16.6736C17.3175 5.34766 17.7893 5.89045 17.633 6.45189L16.2997 11.242C16.0969 11.9695 15.4084 12.5043 14.5776 12.579L7.83552 13.1848C6.83054 13.2745 5.91162 12.6713 5.70291 11.7848V11.7848Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4.18418 5.34722L3.54123 2.68213H1.83594"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M14.2117 15.7988C13.7978 15.7988 13.4618 16.1349 13.4659 16.5488C13.4659 16.9628 13.8019 17.2988 14.2158 17.2988C14.6298 17.2988 14.9658 16.9628 14.9658 16.5488C14.9638 16.1349 14.6277 15.7988 14.2117 15.7988Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M7.20104 15.7988C6.78655 15.7988 6.45003 16.1344 6.45414 16.5478C6.45003 16.9632 6.7886 17.2988 7.20309 17.2988C7.61758 17.2988 7.9541 16.9632 7.9541 16.5499C7.9541 16.1344 7.61758 15.7988 7.20104 15.7988Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default CartIcon
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const CashIcon: React.FC<IconProps> = ({
|
||||||
|
size = "20",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.75 7.5H2.5"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3.75 12.5H2.5"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3.33366 10H1.66699"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4.69727 4.6967C5.74616 3.64781 7.08253 2.9335 8.53739 2.64411C9.99225 2.35472 11.5002 2.50325 12.8707 3.07091C14.2411 3.63856 15.4125 4.59986 16.2366 5.83323C17.0607 7.0666 17.5006 8.51664 17.5006 10C17.5006 11.4834 17.0607 12.9334 16.2366 14.1668C15.4125 15.4001 14.2411 16.3614 12.8707 16.9291C11.5002 17.4968 9.99225 17.6453 8.53739 17.3559C7.08253 17.0665 5.74616 16.3522 4.69727 15.3033"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M10 6.25V7.00737"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M8.125 8.52196C8.17767 8.07456 8.40336 7.66555 8.75379 7.38246C9.10421 7.09936 9.55152 6.96468 10 7.00722"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M10 13.7501V12.9927"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M11.875 11.4778C11.8223 11.9252 11.5966 12.3343 11.2462 12.6174C10.8958 12.9004 10.4485 13.0351 10 12.9926"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M11.8273 8.19148C11.6899 7.8243 11.4381 7.51099 11.1091 7.29776C10.7801 7.08453 10.3913 6.98266 10 7.00718"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M8.17188 11.8083C8.30928 12.1755 8.56105 12.4888 8.89004 12.702C9.21904 12.9152 9.60785 13.0171 9.99913 12.9926"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M8.125 8.52216C8.125 8.83316 8.23276 9.13455 8.42993 9.37506C8.6271 9.61556 8.90151 9.78033 9.20647 9.84132L10.7935 10.1587C11.0985 10.2197 11.3729 10.3845 11.5701 10.625C11.7672 10.8655 11.875 11.1669 11.875 11.4779"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CashIcon
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import React from "react"
|
||||||
|
|
||||||
|
import IconProps from "./types/icon-type"
|
||||||
|
|
||||||
|
const ChannelsIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24px",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M17.6627 7.05C18.7811 7.05 19.6877 6.14338 19.6877 5.025C19.6877 3.90662 18.7811 3 17.6627 3C16.5443 3 15.6377 3.90662 15.6377 5.025C15.6377 6.14338 16.5443 7.05 17.6627 7.05Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M17.6627 14.025C18.7811 14.025 19.6877 13.1184 19.6877 12C19.6877 10.8816 18.7811 9.97498 17.6627 9.97498C16.5443 9.97498 15.6377 10.8816 15.6377 12C15.6377 13.1184 16.5443 14.025 17.6627 14.025Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5.5123 14.025C6.63068 14.025 7.5373 13.1184 7.5373 12C7.5373 10.8816 6.63068 9.97498 5.5123 9.97498C4.39393 9.97498 3.4873 10.8816 3.4873 12C3.4873 13.1184 4.39393 14.025 5.5123 14.025Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M17.6627 21C18.7811 21 19.6877 20.0933 19.6877 18.975C19.6877 17.8566 18.7811 16.95 17.6627 16.95C16.5443 16.95 15.6377 17.8566 15.6377 18.975C15.6377 20.0933 16.5443 21 17.6627 21Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M15.6369 5.02502H13.3869C12.3924 5.02502 11.5869 5.83052 11.5869 6.82502V17.175C11.5869 18.1695 12.3924 18.975 13.3869 18.975H15.6369"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M15.6361 12H7.53613"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChannelsIcon
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const CheckCircleFillIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M18 10C18 14.4184 14.4184 18 10 18C5.5816 18 2 14.4184 2 10C2 5.5816 5.5816 2 10 2C14.4184 2 18 5.5816 18 10ZM13.9053 8.28033C14.1982 7.98744 14.1982 7.51256 13.9053 7.21967C13.6124 6.92678 13.1376 6.92678 12.8447 7.21967L8.875 11.1893L7.15533 9.46967C6.86244 9.17678 6.38756 9.17678 6.09467 9.46967C5.80178 9.76256 5.80178 10.2374 6.09467 10.5303L8.34467 12.7803C8.63756 13.0732 9.11244 13.0732 9.40533 12.7803L13.9053 8.28033Z"
|
||||||
|
fill={color}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckCircleFillIcon
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const CheckCircleIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10 17.5C14.1422 17.5 17.5 14.1422 17.5 10C17.5 5.85775 14.1422 2.5 10 2.5C5.85775 2.5 2.5 5.85775 2.5 10C2.5 14.1422 5.85775 17.5 10 17.5Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M7.5 9.99998L9.16667 11.6666L12.5 8.33331"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckCircleIcon
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "./types/icon-type"
|
||||||
|
|
||||||
|
const CheckIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24px",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M13.3334 4L6.00008 11.3333L2.66675 8"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckIcon
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "./types/icon-type"
|
||||||
|
|
||||||
|
const ChevronDownIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24px",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4 6L8 10L12 6"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChevronDownIcon
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const ChevronLeftIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M9 12L5 8L9 4"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChevronLeftIcon
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const ChevronRightIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6 12L10 8L6 4"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChevronRightIcon
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "./types/icon-type"
|
||||||
|
|
||||||
|
const ChevronUpIcon: React.FC<IconProps> = ({
|
||||||
|
size = "24px",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5 12.5L10 7.5L15 12.5"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChevronUpIcon
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import React from "react"
|
||||||
|
import IconProps from "../types/icon-type"
|
||||||
|
|
||||||
|
const ClipboardCopyIcon: React.FC<IconProps> = ({
|
||||||
|
size = "20",
|
||||||
|
color = "currentColor",
|
||||||
|
...attributes
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...attributes}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12.917 4.16669H14.3753C14.7621 4.16669 15.133 4.32277 15.4065 4.6006C15.68 4.87843 15.8337 5.25526 15.8337 5.64817V8.33335M7.08366 4.16669H5.62533C5.23855 4.16669 4.86762 4.32277 4.59413 4.6006C4.32064 4.87843 4.16699 5.25526 4.16699 5.64817V16.0185C4.16699 16.4115 4.32064 16.7883 4.59413 17.0661C4.86762 17.3439 5.23855 17.5 5.62533 17.5H14.3753C14.7621 17.5 15.133 17.3439 15.4065 17.0661C15.68 16.7883 15.8337 16.4115 15.8337 16.0185V15"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M11.875 2.5H8.125C7.77982 2.5 7.5 2.8731 7.5 3.33333V5C7.5 5.46024 7.77982 5.83333 8.125 5.83333H11.875C12.2202 5.83333 12.5 5.46024 12.5 5V3.33333C12.5 2.8731 12.2202 2.5 11.875 2.5Z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M17.5 11.6667H10"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M12.5 9.16669L10 11.6667L12.5 14.1667"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ClipboardCopyIcon
|
||||||