Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35e5feb22c | |||
| 19169e43de | |||
| 40ae2a0fba | |||
| 7009bcc660 | |||
| 6331cce7ac | |||
| bda60b7550 | |||
| 77bf29cb3f |
@@ -1,12 +0,0 @@
|
|||||||
node_modules
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
.git
|
|
||||||
.github
|
|
||||||
*.md
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
.vscode
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.log
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
VITE_BASE_URL = 'https://api.shinan.ir'
|
VITE_BASE_URL = 'https://api.fajrtabloshop.com'
|
||||||
# VITE_BASE_URL = 'http://192.168.99.235:4000'
|
# VITE_BASE_URL = 'http://192.168.99.235:4000'
|
||||||
|
|
||||||
# VITE_BASE_URL = 'https://api.shinan.ir'
|
# VITE_BASE_URL = 'https://api.shinan.ir'
|
||||||
VITE_TOKEN_NAME = 'shinan_admin_token'
|
VITE_TOKEN_NAME = 'sh_admin_token'
|
||||||
VITE_REFRESH_TOKEN_NAME = 'shinan_admin_refresh_token'
|
VITE_REFRESH_TOKEN_NAME = 'sh_admin_refresh_token'
|
||||||
@@ -3,7 +3,7 @@ name: deploy to danak
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- shinan
|
- tablofajr
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_deploy:
|
build_and_deploy:
|
||||||
@@ -11,8 +11,10 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
DANAK_SERVER: "https://captain.dev.danakcorp.com"
|
DANAK_SERVER: "https://captain.dev.danakcorp.com"
|
||||||
APP_TOKEN: 0fbba2ce0112b82dbdb6e8d2d5f6b07db97e3074d2bb7cca6b81f8287e680bb9
|
APP_TOKEN: 0c540850b77568a873eaf92399a54cc042fa1054a0336b7909ae8f42effe5971
|
||||||
APP_NAME: shinan-admin
|
APP_NAME: shop-admin
|
||||||
|
# APP_TOKEN: 111ac60c637716478be8ab8b888fd20baba808a10b0fc0c18bf788766883ec9e
|
||||||
|
# APP_NAME: e-commerce-front
|
||||||
GITHUB_TOKEN: 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
|
GITHUB_TOKEN: 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
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
FROM node:20-alpine AS builder
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
|
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.de.velop.ir/alpine|g' /etc/apk/repositories \
|
||||||
|
&& npm config set registry https://package-mirror.liara.ir/repository/npm/
|
||||||
|
|
||||||
|
ENV NPM_CONFIG_REGISTRY=https://package-mirror.liara.ir/repository/npm/
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
@@ -10,6 +15,8 @@ RUN npm run build
|
|||||||
|
|
||||||
FROM nginx:stable-alpine AS production
|
FROM nginx:stable-alpine AS production
|
||||||
|
|
||||||
|
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.de.velop.ir/alpine|g' /etc/apk/repositories
|
||||||
|
|
||||||
COPY --from=builder /build/dist /usr/share/nginx/html
|
COPY --from=builder /build/dist /usr/share/nginx/html
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>پنل مدیریت | شینان</title>
|
<title>پنل مدیریت | فجر تابلو مرکزی</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Generated
+22
-2
@@ -103,6 +103,7 @@
|
|||||||
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.2.0",
|
"@ampproject/remapping": "^2.2.0",
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
@@ -2497,6 +2498,7 @@
|
|||||||
"integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==",
|
"integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.12.0"
|
"undici-types": "~7.12.0"
|
||||||
}
|
}
|
||||||
@@ -2506,6 +2508,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz",
|
||||||
"integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==",
|
"integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@@ -2516,6 +2519,7 @@
|
|||||||
"integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
|
"integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.0.0"
|
"@types/react": "^19.0.0"
|
||||||
}
|
}
|
||||||
@@ -2572,6 +2576,7 @@
|
|||||||
"integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==",
|
"integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.41.0",
|
"@typescript-eslint/scope-manager": "8.41.0",
|
||||||
"@typescript-eslint/types": "8.41.0",
|
"@typescript-eslint/types": "8.41.0",
|
||||||
@@ -2824,6 +2829,7 @@
|
|||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -3008,6 +3014,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001737",
|
"caniuse-lite": "^1.0.30001737",
|
||||||
"electron-to-chromium": "^1.5.211",
|
"electron-to-chromium": "^1.5.211",
|
||||||
@@ -3554,6 +3561,7 @@
|
|||||||
"integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==",
|
"integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -4166,6 +4174,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.27.6"
|
"@babel/runtime": "^7.27.6"
|
||||||
},
|
},
|
||||||
@@ -4989,6 +4998,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -5089,6 +5099,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
|
||||||
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
|
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-diff": "^1.3.0",
|
"fast-diff": "^1.3.0",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
@@ -5141,6 +5152,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
||||||
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
|
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -5156,6 +5168,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
|
||||||
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
|
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.26.0"
|
"scheduler": "^0.26.0"
|
||||||
},
|
},
|
||||||
@@ -5226,7 +5239,8 @@
|
|||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/react-multi-date-picker": {
|
"node_modules/react-multi-date-picker": {
|
||||||
"version": "4.5.2",
|
"version": "4.5.2",
|
||||||
@@ -5272,6 +5286,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/use-sync-external-store": "^0.0.6",
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
"use-sync-external-store": "^1.4.0"
|
"use-sync-external-store": "^1.4.0"
|
||||||
@@ -5461,7 +5476,8 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/redux-thunk": {
|
"node_modules/redux-thunk": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
@@ -5778,6 +5794,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -5864,6 +5881,7 @@
|
|||||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -6023,6 +6041,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz",
|
||||||
"integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==",
|
"integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -6114,6 +6133,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 549 KiB |
+1063
-21
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 550 KiB After Width: | Height: | Size: 65 KiB |
+17
-4
@@ -1,19 +1,32 @@
|
|||||||
import { type FC, type ReactNode } from 'react'
|
import { type FC, type ReactNode } from 'react'
|
||||||
|
import { clx } from '../helpers/utils'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
text: string,
|
text: string,
|
||||||
children?: ReactNode,
|
children?: ReactNode,
|
||||||
dir?: string,
|
dir?: string,
|
||||||
|
align?: 'start' | 'center' | 'end',
|
||||||
|
className?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Td: FC<Props> = (props: Props) => {
|
const Td: FC<Props> = (props: Props) => {
|
||||||
|
const alignClass = props.align === 'end'
|
||||||
|
? 'text-end'
|
||||||
|
: props.align === 'center'
|
||||||
|
? 'text-center'
|
||||||
|
: props.align === 'start'
|
||||||
|
? 'text-start'
|
||||||
|
: ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<td className='td' style={{ direction: props.dir === "ltr" ? "ltr" : "rtl" }}>
|
<td
|
||||||
|
className={clx('td', alignClass, props.className)}
|
||||||
|
style={{ direction: props.dir === 'ltr' ? 'ltr' : 'rtl' }}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
props.text ?
|
|
||||||
props.text
|
props.text
|
||||||
:
|
? props.text
|
||||||
props.children
|
: props.children
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ textarea::placeholder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--color-primary: #a62186;
|
--color-primary: #da2129;
|
||||||
--color-secondary: #f4f5f9;
|
--color-secondary: #f4f5f9;
|
||||||
--color-border: #d0d0d0;
|
--color-border: #d0d0d0;
|
||||||
--color-description: #888888;
|
--color-description: #888888;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ type Props = {
|
|||||||
error_text?: string
|
error_text?: string
|
||||||
categories: CategoryTreeNode[]
|
categories: CategoryTreeNode[]
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
compact?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const CategorySelect: FC<Props> = ({
|
const CategorySelect: FC<Props> = ({
|
||||||
@@ -22,7 +23,8 @@ const CategorySelect: FC<Props> = ({
|
|||||||
onChange,
|
onChange,
|
||||||
error_text,
|
error_text,
|
||||||
categories,
|
categories,
|
||||||
disabled = false
|
disabled = false,
|
||||||
|
compact = false
|
||||||
}) => {
|
}) => {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
@@ -163,7 +165,10 @@ const CategorySelect: FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="w-full relative" ref={dropdownRef}>
|
<div className="w-full relative" ref={dropdownRef}>
|
||||||
{label && (
|
{label && (
|
||||||
<label className="text-sm text-gray-700 mb-2 block font-medium">
|
<label className={clx(
|
||||||
|
'text-sm block',
|
||||||
|
compact ? 'text-black' : 'text-gray-700 mb-2 font-medium'
|
||||||
|
)}>
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
@@ -171,17 +176,27 @@ const CategorySelect: FC<Props> = ({
|
|||||||
{/* دکمه انتخاب */}
|
{/* دکمه انتخاب */}
|
||||||
<div
|
<div
|
||||||
className={clx(
|
className={clx(
|
||||||
'w-full border border-gray-300 rounded-xl bg-white cursor-pointer transition-all duration-200',
|
'w-full border bg-white cursor-pointer transition-all duration-200',
|
||||||
'hover:border-blue-400 hover:shadow-sm',
|
compact
|
||||||
isOpen && 'border-blue-500 ring-2 ring-blue-100',
|
? 'h-10 rounded-[10px] border-border hover:border-border'
|
||||||
|
: 'border-gray-300 rounded-xl hover:border-blue-400 hover:shadow-sm',
|
||||||
|
isOpen && (compact ? 'border-primary' : 'border-blue-500 ring-2 ring-blue-100'),
|
||||||
disabled && 'opacity-50 cursor-not-allowed bg-gray-50',
|
disabled && 'opacity-50 cursor-not-allowed bg-gray-50',
|
||||||
label && 'mt-1'
|
label && 'mt-1'
|
||||||
)}
|
)}
|
||||||
onClick={() => !disabled && setIsOpen(!isOpen)}
|
onClick={() => !disabled && setIsOpen(!isOpen)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between p-4">
|
<div className={clx(
|
||||||
|
'flex items-center justify-between',
|
||||||
|
compact ? 'h-full px-[10px]' : 'p-4'
|
||||||
|
)}>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
{selectedCategory ? (
|
{selectedCategory ? (
|
||||||
|
compact ? (
|
||||||
|
<span className="text-sm text-black truncate block">
|
||||||
|
{selectedCategory.title_fa}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
{selectedCategory.children.length > 0 ? (
|
{selectedCategory.children.length > 0 ? (
|
||||||
@@ -199,11 +214,16 @@ const CategorySelect: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
compact ? (
|
||||||
|
<span className="text-sm text-gray-400">{placeholder}</span>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Folder2 size={20} className="text-gray-400" />
|
<Folder2 size={20} className="text-gray-400" />
|
||||||
<span className="text-gray-400">{placeholder}</span>
|
<span className="text-gray-400">{placeholder}</span>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export type PaymentMethodType = {
|
|||||||
|
|
||||||
export type PriceDetailsType = {
|
export type PriceDetailsType = {
|
||||||
shipping_cost: number;
|
shipping_cost: number;
|
||||||
|
shipping_cost_on_delivery?: number;
|
||||||
process_cost: number;
|
process_cost: number;
|
||||||
total_retail_price: number;
|
total_retail_price: number;
|
||||||
total_payable_price: number;
|
total_payable_price: number;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export interface PaymentMethod {
|
|||||||
|
|
||||||
export interface PriceDetails {
|
export interface PriceDetails {
|
||||||
shipping_cost: number;
|
shipping_cost: number;
|
||||||
|
shipping_cost_on_delivery?: number;
|
||||||
process_cost: number;
|
process_cost: number;
|
||||||
total_retail_price: number;
|
total_retail_price: number;
|
||||||
total_payable_price: number;
|
total_payable_price: number;
|
||||||
|
|||||||
+117
-31
@@ -2,6 +2,9 @@ import { type FC, useState } from 'react'
|
|||||||
import { useDeleteProduct, useDraftProduct, useGetProducts } from './hooks/useProductData';
|
import { useDeleteProduct, useDraftProduct, useGetProducts } from './hooks/useProductData';
|
||||||
import { type ProductListType } from './types/Types';
|
import { type ProductListType } from './types/Types';
|
||||||
import { ProductStatus } from './enum/ProductEnum';
|
import { ProductStatus } from './enum/ProductEnum';
|
||||||
|
import CategorySelect from '../category/components/CategorySelect';
|
||||||
|
import { useGetCategories } from '../category/hooks/useCategoryData';
|
||||||
|
import Select from '@/components/Select';
|
||||||
import PageLoading from '../../components/PageLoading';
|
import PageLoading from '../../components/PageLoading';
|
||||||
import Error from '../../components/Error';
|
import Error from '../../components/Error';
|
||||||
import PaginationUi from '../../components/PaginationUi';
|
import PaginationUi from '../../components/PaginationUi';
|
||||||
@@ -10,7 +13,6 @@ import Td from '../../components/Td';
|
|||||||
import TrashWithConfrim from '../../components/TrashWithConfrim';
|
import TrashWithConfrim from '../../components/TrashWithConfrim';
|
||||||
import ModalConfrim from '../../components/ModalConfrim';
|
import ModalConfrim from '../../components/ModalConfrim';
|
||||||
import PageTitle from '../../components/PageTitle';
|
import PageTitle from '../../components/PageTitle';
|
||||||
import { Edit } from 'iconsax-react';
|
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Pages } from '@/config/Pages';
|
import { Pages } from '@/config/Pages';
|
||||||
@@ -22,9 +24,16 @@ const List: FC = () => {
|
|||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [categoryId, setCategoryId] = useState('');
|
||||||
|
const [status, setStatus] = useState<ProductStatus | ''>('');
|
||||||
const [isDraftConfirmOpen, setIsDraftConfirmOpen] = useState(false);
|
const [isDraftConfirmOpen, setIsDraftConfirmOpen] = useState(false);
|
||||||
const [selectedProductId, setSelectedProductId] = useState<string | null>(null);
|
const [selectedProductId, setSelectedProductId] = useState<string | null>(null);
|
||||||
const { data: productsData, isLoading, error, refetch } = useGetProducts(currentPage);
|
const { data: categoriesData } = useGetCategories();
|
||||||
|
const categories = categoriesData?.results?.data || [];
|
||||||
|
const { data: productsData, isLoading, error, refetch } = useGetProducts(currentPage, 10, {
|
||||||
|
...(categoryId && { categoryId }),
|
||||||
|
...(status && { status }),
|
||||||
|
});
|
||||||
const deleteProductMutation = useDeleteProduct();
|
const deleteProductMutation = useDeleteProduct();
|
||||||
const draftProductMutation = useDraftProduct();
|
const draftProductMutation = useDraftProduct();
|
||||||
|
|
||||||
@@ -68,18 +77,70 @@ const List: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const hasActiveFilters = !!categoryId || !!status;
|
||||||
|
|
||||||
|
const handleCategoryChange = (value: string) => {
|
||||||
|
setCategoryId(value);
|
||||||
|
setCurrentPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStatusChange = (value: string) => {
|
||||||
|
setStatus(value as ProductStatus | '');
|
||||||
|
setCurrentPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearFilters = () => {
|
||||||
|
setCategoryId('');
|
||||||
|
setStatus('');
|
||||||
|
setCurrentPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageTitle />
|
<PageTitle />
|
||||||
<div className='flex justify-end mt-5'>
|
<div className='flex flex-wrap items-end justify-between gap-4 mt-5'>
|
||||||
|
<div className='flex flex-wrap items-end gap-4'>
|
||||||
|
<div className='w-72'>
|
||||||
|
<CategorySelect
|
||||||
|
label='دستهبندی'
|
||||||
|
placeholder='همه دستهبندیها'
|
||||||
|
value={categoryId}
|
||||||
|
onChange={handleCategoryChange}
|
||||||
|
categories={categories}
|
||||||
|
compact
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='w-48'>
|
||||||
|
<Select
|
||||||
|
label='وضعیت'
|
||||||
|
value={status}
|
||||||
|
onChange={(e) => handleStatusChange(e.target.value)}
|
||||||
|
items={[
|
||||||
|
{ value: '', label: 'همه وضعیتها' },
|
||||||
|
{ value: ProductStatus.Approved, label: 'تایید شده' },
|
||||||
|
{ value: ProductStatus.Pending, label: 'در انتظار' },
|
||||||
|
{ value: ProductStatus.Rejected, label: 'رد شده' },
|
||||||
|
{ value: ProductStatus.Draft, label: 'پیشنویس' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{hasActiveFilters && (
|
||||||
|
<Button
|
||||||
|
label='پاک کردن فیلترها'
|
||||||
|
variant='outline'
|
||||||
|
onClick={clearFilters}
|
||||||
|
className='w-fit h-10'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<Button
|
<Button
|
||||||
label='افزودن محصول'
|
label='افزودن محصول'
|
||||||
onClick={() => navigate(`${Pages.products.create}`)}
|
onClick={() => navigate(`${Pages.products.create}`)}
|
||||||
className='w-fit'
|
className='w-fit'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='relative overflow-x-auto rounded-3xl mt-5 w-full'>
|
<div className='relative overflow-x-auto rounded-3xl mt-5 w-full border border-[#EAEDF5]'>
|
||||||
<table className='w-full text-sm'>
|
<table className='w-full text-sm min-w-[960px]'>
|
||||||
<thead className='thead'>
|
<thead className='thead'>
|
||||||
<tr>
|
<tr>
|
||||||
<Td text={'تصویر'} />
|
<Td text={'تصویر'} />
|
||||||
@@ -88,15 +149,15 @@ const List: FC = () => {
|
|||||||
<Td text={'دستهبندی'} />
|
<Td text={'دستهبندی'} />
|
||||||
<Td text='تنوع' />
|
<Td text='تنوع' />
|
||||||
<Td text={'وضعیت'} />
|
<Td text={'وضعیت'} />
|
||||||
<Td text={'قیمت'} />
|
<Td text={'قیمت'} align='end' />
|
||||||
<Td text={'موجودی'} />
|
<Td text={'موجودی'} align='end' />
|
||||||
<Td text={'عملیات'} />
|
<Td text={'عملیات'} align='center' className='min-w-[120px]' />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{products.length === 0 ? (
|
{products.length === 0 ? (
|
||||||
<tr className='tr'>
|
<tr className='tr'>
|
||||||
<td colSpan={9} className="text-center py-8 text-gray-500">
|
<td colSpan={9} className="text-center py-8 text-gray-500 text-sm">
|
||||||
هیچ محصولی یافت نشد
|
هیچ محصولی یافت نشد
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -104,7 +165,7 @@ const List: FC = () => {
|
|||||||
products.map((product: ProductListType) => (
|
products.map((product: ProductListType) => (
|
||||||
<tr key={product._id} className='tr'>
|
<tr key={product._id} className='tr'>
|
||||||
<Td text="">
|
<Td text="">
|
||||||
<div className="w-12 h-12 rounded-lg overflow-hidden">
|
<div className="w-11 h-11 rounded-lg overflow-hidden bg-gray-50 shrink-0">
|
||||||
<img
|
<img
|
||||||
src={product.imagesUrl?.cover || '/placeholder-image.png'}
|
src={product.imagesUrl?.cover || '/placeholder-image.png'}
|
||||||
alt={product.title_fa}
|
alt={product.title_fa}
|
||||||
@@ -113,25 +174,35 @@ const List: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
<Td text="">
|
<Td text="">
|
||||||
<div>
|
<div className="max-w-[220px]">
|
||||||
<div className="font-medium text-gray-900">
|
<div className="font-medium text-gray-900 truncate" title={product.title_fa}>
|
||||||
{product.title_fa}
|
{product.title_fa}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-gray-500 text-xs">
|
<div className="text-gray-400 text-xs truncate mt-0.5" title={product.title_en}>
|
||||||
{product.title_en}
|
{product.title_en}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
<Td text={product.brand_title_fa} />
|
<Td text="">
|
||||||
<Td text={product.category_title_fa} />
|
<span className="text-gray-700 max-w-[120px] truncate inline-block" title={product.brand_title_fa}>
|
||||||
|
{product.brand_title_fa}
|
||||||
|
</span>
|
||||||
|
</Td>
|
||||||
|
<Td text="">
|
||||||
|
<span className="text-gray-700 max-w-[120px] truncate inline-block" title={product.category_title_fa}>
|
||||||
|
{product.category_title_fa}
|
||||||
|
</span>
|
||||||
|
</Td>
|
||||||
<Td text=''>
|
<Td text=''>
|
||||||
<Link
|
<Link to={`${Pages.products.variants}${product._id}`}>
|
||||||
to={`${Pages.products.variants}${product._id}`}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
className='h-8 text-xs'
|
variant='outline'
|
||||||
|
size='sm'
|
||||||
|
className='w-fit px-2.5 whitespace-nowrap'
|
||||||
>
|
>
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
مدیریت تنوع
|
مدیریت تنوع
|
||||||
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</Td>
|
</Td>
|
||||||
@@ -141,26 +212,41 @@ const List: FC = () => {
|
|||||||
text={getStatusText(product.status)}
|
text={getStatusText(product.status)}
|
||||||
/>
|
/>
|
||||||
</Td>
|
</Td>
|
||||||
<Td text={`${product.default_variant?.price?.selling_price?.toLocaleString('fa-IR') || 0} تومان`} />
|
<Td text="" align='end'>
|
||||||
<Td text={String(product.default_variant?.stock || 0)} />
|
<span className="text-gray-800 font-medium whitespace-nowrap">
|
||||||
<Td text="">
|
{(product.default_variant?.price?.selling_price ?? 0).toLocaleString('fa-IR')}
|
||||||
<div className="flex items-center gap-2">
|
<span className="text-gray-400 font-normal ms-1">تومان</span>
|
||||||
{
|
</span>
|
||||||
product.status !== ProductStatus.Draft && (
|
</Td>
|
||||||
|
<Td text="" align='end'>
|
||||||
|
<span className="text-gray-700 tabular-nums">
|
||||||
|
{product.default_variant?.stock ?? 0}
|
||||||
|
</span>
|
||||||
|
</Td>
|
||||||
|
<Td text="" align='center' className='min-w-[120px]'>
|
||||||
|
<div className="flex items-center justify-center gap-1.5">
|
||||||
|
{product.status !== ProductStatus.Draft && (
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
label='پیش نویس'
|
size='sm'
|
||||||
className='text-xs h-8 px-2'
|
className='w-fit px-2.5 shrink-0'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedProductId(String(product._id));
|
setSelectedProductId(String(product._id));
|
||||||
setIsDraftConfirmOpen(true);
|
setIsDraftConfirmOpen(true);
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
)
|
پیشنویس
|
||||||
}
|
</Button>
|
||||||
|
)}
|
||||||
{product.status === ProductStatus.Draft && (
|
{product.status === ProductStatus.Draft && (
|
||||||
<Link to={`${Pages.products.update}${product._id}`}>
|
<Link to={`${Pages.products.update}${product._id}`}>
|
||||||
<Edit color='#8C90A3' size={16} className="cursor-pointer hover:text-blue-500" />
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
size='sm'
|
||||||
|
className='w-fit px-2.5 shrink-0'
|
||||||
|
>
|
||||||
|
ویرایش
|
||||||
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
type UpdateProductRequestType
|
type UpdateProductRequestType
|
||||||
} from "../types/Types";
|
} from "../types/Types";
|
||||||
import { useSharedStore } from '@/shared/store/sharedStore';
|
import { useSharedStore } from '@/shared/store/sharedStore';
|
||||||
|
import { type GetProductsFilters } from '../service/ProductService';
|
||||||
|
|
||||||
export const useCreateProductDetail = () => {
|
export const useCreateProductDetail = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
@@ -29,11 +30,15 @@ export const useSaveProduct = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetProducts = (page: number = 1, limit: number = 10) => {
|
export const useGetProducts = (
|
||||||
|
page: number = 1,
|
||||||
|
limit: number = 10,
|
||||||
|
filters?: Pick<GetProductsFilters, 'categoryId' | 'status'>
|
||||||
|
) => {
|
||||||
const { search } = useSharedStore();
|
const { search } = useSharedStore();
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['products', page, limit, search],
|
queryKey: ['products', page, limit, search, filters?.categoryId, filters?.status],
|
||||||
queryFn: () => api.getProducts(page, limit, search),
|
queryFn: () => api.getProducts(page, limit, { search, ...filters }),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -94,7 +99,7 @@ export const useUpdateProduct = () => {
|
|||||||
export const useSearchProducts = (search: string, enabled: boolean = true) => {
|
export const useSearchProducts = (search: string, enabled: boolean = true) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['products-search', search],
|
queryKey: ['products-search', search],
|
||||||
queryFn: () => api.getProducts(1, 50, search),
|
queryFn: () => api.getProducts(1, 50, { search }),
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import axios from "../../../config/axios";
|
import axios from "../../../config/axios";
|
||||||
|
import { ProductStatus } from "../enum/ProductEnum";
|
||||||
import {
|
import {
|
||||||
type CreateProductDetailRequestType,
|
type CreateProductDetailRequestType,
|
||||||
type CreateProductDetailResponseType,
|
type CreateProductDetailResponseType,
|
||||||
@@ -24,6 +25,12 @@ import {
|
|||||||
type UpdateProductResponseType
|
type UpdateProductResponseType
|
||||||
} from "../types/Types";
|
} from "../types/Types";
|
||||||
|
|
||||||
|
export type GetProductsFilters = {
|
||||||
|
search?: string;
|
||||||
|
categoryId?: string;
|
||||||
|
status?: ProductStatus;
|
||||||
|
};
|
||||||
|
|
||||||
export const createProductDetail = async (
|
export const createProductDetail = async (
|
||||||
variables: CreateProductDetailRequestType
|
variables: CreateProductDetailRequestType
|
||||||
): Promise<CreateProductDetailResponseType> => {
|
): Promise<CreateProductDetailResponseType> => {
|
||||||
@@ -45,10 +52,20 @@ export const saveProduct = async (
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getProducts = async (page: number = 1, limit: number = 10, search?: string): Promise<GetProductsResponseType> => {
|
export const getProducts = async (
|
||||||
|
page: number = 1,
|
||||||
|
limit: number = 10,
|
||||||
|
filters?: GetProductsFilters
|
||||||
|
): Promise<GetProductsResponseType> => {
|
||||||
const params: Record<string, string | number> = { page, limit };
|
const params: Record<string, string | number> = { page, limit };
|
||||||
if (search && search.trim()) {
|
if (filters?.search?.trim()) {
|
||||||
params.q = search.trim();
|
params.q = filters.search.trim();
|
||||||
|
}
|
||||||
|
if (filters?.categoryId) {
|
||||||
|
params.categoryId = filters.categoryId;
|
||||||
|
}
|
||||||
|
if (filters?.status) {
|
||||||
|
params.status = filters.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await axios.get(`/admin/products/native`, {
|
const { data } = await axios.get(`/admin/products/native`, {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { TickCircle, Add, Trash } from 'iconsax-react'
|
|||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { extractErrorMessage } from '../../helpers/utils'
|
import { extractErrorMessage } from '../../helpers/utils'
|
||||||
import { Pages } from '../../config/Pages'
|
import { Pages } from '../../config/Pages'
|
||||||
|
import SwitchComponent from '../../components/Switch'
|
||||||
|
|
||||||
const CreateShipment: FC = () => {
|
const CreateShipment: FC = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@@ -25,6 +26,7 @@ const CreateShipment: FC = () => {
|
|||||||
costs: [],
|
costs: [],
|
||||||
deliveryTime: 0,
|
deliveryTime: 0,
|
||||||
deliveryType: 'Standard',
|
deliveryType: 'Standard',
|
||||||
|
payDeliveryFeeOnDelivery: false,
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
name: Yup.string().required('عنوان روش ارسال الزامی است'),
|
name: Yup.string().required('عنوان روش ارسال الزامی است'),
|
||||||
@@ -134,6 +136,19 @@ const CreateShipment: FC = () => {
|
|||||||
{...formik.getFieldProps('description')}
|
{...formik.getFieldProps('description')}
|
||||||
error_text={formik.touched.description && formik.errors.description ? formik.errors.description : ''}
|
error_text={formik.touched.description && formik.errors.description ? formik.errors.description : ''}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className='flex items-center justify-between border border-gray-200 rounded-lg p-4'>
|
||||||
|
<div>
|
||||||
|
<div className='font-medium text-gray-900'>پرداخت هزینه ارسال در زمان تحویل</div>
|
||||||
|
<div className='text-xs text-gray-500 mt-1'>
|
||||||
|
در صورت فعال بودن، هزینه ارسال در زمان تحویل پرداخت میشود و در پرداخت آنلاین لحاظ نمیگردد.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SwitchComponent
|
||||||
|
active={formik.values.payDeliveryFeeOnDelivery ?? false}
|
||||||
|
onChange={(value) => formik.setFieldValue('payDeliveryFeeOnDelivery', value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ const ShippingList: FC = () => {
|
|||||||
<Td text={'توضیحات'} />
|
<Td text={'توضیحات'} />
|
||||||
<Td text={'نوع ارسال'} />
|
<Td text={'نوع ارسال'} />
|
||||||
<Td text={'زمان ارسال (روز)'} />
|
<Td text={'زمان ارسال (روز)'} />
|
||||||
|
<Td text={'پرداخت در محل'} />
|
||||||
<Td text={'هزینهها'} />
|
<Td text={'هزینهها'} />
|
||||||
<Td text={'عملیات'} />
|
<Td text={'عملیات'} />
|
||||||
</tr>
|
</tr>
|
||||||
@@ -106,6 +107,7 @@ const ShippingList: FC = () => {
|
|||||||
<Td text={shipment.description} />
|
<Td text={shipment.description} />
|
||||||
<Td text={getDeliveryTypeText(shipment.deliveryType)} />
|
<Td text={getDeliveryTypeText(shipment.deliveryType)} />
|
||||||
<Td text={shipment.deliveryTime.toString()} />
|
<Td text={shipment.deliveryTime.toString()} />
|
||||||
|
<Td text={shipment.payDeliveryFeeOnDelivery ? 'بله' : 'خیر'} />
|
||||||
<Td text="">
|
<Td text="">
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
{shipment.costs.length > 0 ? (
|
{shipment.costs.length > 0 ? (
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { TickCircle, Add, Trash } from 'iconsax-react'
|
|||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { extractErrorMessage } from '../../helpers/utils'
|
import { extractErrorMessage } from '../../helpers/utils'
|
||||||
import { Pages } from '../../config/Pages'
|
import { Pages } from '../../config/Pages'
|
||||||
|
import SwitchComponent from '../../components/Switch'
|
||||||
|
|
||||||
const UpdateShipment: FC = () => {
|
const UpdateShipment: FC = () => {
|
||||||
const { id } = useParams<{ id: string }>()
|
const { id } = useParams<{ id: string }>()
|
||||||
@@ -27,6 +28,7 @@ const UpdateShipment: FC = () => {
|
|||||||
costs: shipmentDetail?.costs || [],
|
costs: shipmentDetail?.costs || [],
|
||||||
deliveryTime: shipmentDetail?.deliveryTime || 0,
|
deliveryTime: shipmentDetail?.deliveryTime || 0,
|
||||||
deliveryType: shipmentDetail?.deliveryType || 'Standard',
|
deliveryType: shipmentDetail?.deliveryType || 'Standard',
|
||||||
|
payDeliveryFeeOnDelivery: shipmentDetail?.payDeliveryFeeOnDelivery ?? false,
|
||||||
},
|
},
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
@@ -170,6 +172,19 @@ const UpdateShipment: FC = () => {
|
|||||||
{...formik.getFieldProps('description')}
|
{...formik.getFieldProps('description')}
|
||||||
error_text={formik.touched.description && formik.errors.description ? formik.errors.description : ''}
|
error_text={formik.touched.description && formik.errors.description ? formik.errors.description : ''}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className='flex items-center justify-between border border-gray-200 rounded-lg p-4'>
|
||||||
|
<div>
|
||||||
|
<div className='font-medium text-gray-900'>پرداخت هزینه ارسال در زمان تحویل</div>
|
||||||
|
<div className='text-xs text-gray-500 mt-1'>
|
||||||
|
در صورت فعال بودن، هزینه ارسال در زمان تحویل پرداخت میشود و در پرداخت آنلاین لحاظ نمیگردد.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SwitchComponent
|
||||||
|
active={formik.values.payDeliveryFeeOnDelivery ?? false}
|
||||||
|
onChange={(value) => formik.setFieldValue('payDeliveryFeeOnDelivery', value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export interface Shipper {
|
|||||||
costs: Cost[];
|
costs: Cost[];
|
||||||
deliveryType: "Standard" | "SameDay" | "Express";
|
deliveryType: "Standard" | "SameDay" | "Express";
|
||||||
deliveryTime: number;
|
deliveryTime: number;
|
||||||
|
payDeliveryFeeOnDelivery: boolean;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
@@ -35,6 +36,7 @@ export interface CreateShipper {
|
|||||||
description: string;
|
description: string;
|
||||||
costs: Cost[];
|
costs: Cost[];
|
||||||
deliveryTime: number;
|
deliveryTime: number;
|
||||||
|
payDeliveryFeeOnDelivery?: boolean;
|
||||||
deliveryType?: "Standard" | "SameDay" | "Express";
|
deliveryType?: "Standard" | "SameDay" | "Express";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +64,7 @@ export interface ShipmentMethod {
|
|||||||
costs: Cost[];
|
costs: Cost[];
|
||||||
deliveryType: string;
|
deliveryType: string;
|
||||||
deliveryTime: number;
|
deliveryTime: number;
|
||||||
|
payDeliveryFeeOnDelivery: boolean;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user