structure and home page and detail company

This commit is contained in:
hamid zarghami
2025-02-09 14:36:13 +03:30
commit 0341b836f7
111 changed files with 10674 additions and 0 deletions
Vendored
BIN
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
node_modules
+50
View File
@@ -0,0 +1,50 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
export default tseslint.config({
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
- Optionally add `...tseslint.configs.stylisticTypeChecked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
```js
// eslint.config.js
import react from 'eslint-plugin-react'
export default tseslint.config({
// Set the react version
settings: { react: { version: '18.3' } },
plugins: {
// Add the react plugin
react,
},
rules: {
// other rules...
// Enable its recommended rules
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
},
})
```
+28
View File
@@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+5340
View File
File diff suppressed because it is too large Load Diff
+50
View File
@@ -0,0 +1,50 @@
{
"name": "lms",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@headlessui/react": "^2.2.0",
"@material-tailwind/react": "^2.1.10",
"@tanstack/react-query": "^5.64.1",
"axios": "^1.7.9",
"formik": "^2.4.6",
"i18next": "^24.2.1",
"iconsax-react": "^0.0.8",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-dropzone": "^14.3.5",
"react-i18next": "^15.4.0",
"react-loading": "^2.0.3",
"react-multi-date-picker": "^4.5.2",
"react-router-dom": "^7.1.3",
"react-toastify": "^11.0.3",
"swiper": "^11.2.1",
"tailwind-merge": "^2.6.0",
"yup": "^1.6.1",
"zustand": "^5.0.3"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@types/swiper": "^5.4.3",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"eslint": "^9.17.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",
"postcss": "^8.5.1",
"tailwindcss": "^3.4.17",
"typescript": "~5.6.2",
"typescript-eslint": "^8.18.2",
"vite": "^6.0.5"
}
}
+7
View File
@@ -0,0 +1,7 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
View File
Binary file not shown.
+34
View File
@@ -0,0 +1,34 @@
import MainRouter from './router/Main'
import i18next from 'i18next'
import { I18nextProvider } from 'react-i18next'
import { BrowserRouter } from 'react-router-dom'
import 'swiper/swiper-bundle.css';
import FaJson from './langs/fa.json'
i18next.init({
interpolation: { escapeValue: false },
lng: 'fa',
resources: {
fa: {
global: FaJson
}
}
})
function App() {
return (
<>
<BrowserRouter>
<I18nextProvider i18n={i18next}>
<MainRouter />
</I18nextProvider>
</BrowserRouter>
</>
)
}
export default App
Binary file not shown.
Binary file not shown.
Binary file not shown.
+18
View File
@@ -0,0 +1,18 @@
@font-face {
font-family: "irancell";
font-style: thin;
font-weight: 200;
src: url("./irancell-extralight.ttf");
}
@font-face {
font-family: "irancell";
font-style: normal;
font-weight: 300;
src: url("./irancell-light.ttf");
}
@font-face {
font-family: "irancell";
font-style: thin;
font-weight: 600;
src: url("./irancell-bold.ttf");
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.97508 5.44165L2.91675 10.5L7.97508 15.5583" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.0834 10.5H3.05835" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 KiB

+12
View File
@@ -0,0 +1,12 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.16675 1.66675V4.16675" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.8333 1.66675V4.16675" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.41675 7.57495H17.5834" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 7.08341V14.1667C18 16.6667 16.75 18.3334 13.8333 18.3334H7.16667C4.25 18.3334 3 16.6667 3 14.1667V7.08341C3 4.58341 4.25 2.91675 7.16667 2.91675H13.8333C16.75 2.91675 18 4.58341 18 7.08341Z" stroke="#292D32" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.5788 11.4167H13.5863" stroke="#292D32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.5788 13.9167H13.5863" stroke="#292D32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.4963 11.4167H10.5038" stroke="#292D32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.4963 13.9167H10.5038" stroke="#292D32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.41185 11.4167H7.41933" stroke="#292D32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.41185 13.9167H7.41933" stroke="#292D32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+4
View 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="M5 15L15 5" stroke="#8C90A3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15 15L5 5" stroke="#8C90A3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 319 B

+6
View File
@@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 7.33301V11.333L7.33333 9.99967" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.00033 11.3333L4.66699 10" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.6663 6.66634V9.99967C14.6663 13.333 13.333 14.6663 9.99967 14.6663H5.99967C2.66634 14.6663 1.33301 13.333 1.33301 9.99967V5.99967C1.33301 2.66634 2.66634 1.33301 5.99967 1.33301H9.33301" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.6663 6.66634H11.9997C9.99967 6.66634 9.33301 5.99967 9.33301 3.99967V1.33301L14.6663 6.66634Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 825 B

+4
View 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="M12.9833 9.99993C12.9833 11.6499 11.6499 12.9833 9.99993 12.9833C8.34993 12.9833 7.0166 11.6499 7.0166 9.99993C7.0166 8.34993 8.34993 7.0166 9.99993 7.0166C11.6499 7.0166 12.9833 8.34993 12.9833 9.99993Z" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.99987 16.8918C12.9415 16.8918 15.6832 15.1584 17.5915 12.1584C18.3415 10.9834 18.3415 9.00843 17.5915 7.83343C15.6832 4.83343 12.9415 3.1001 9.99987 3.1001C7.0582 3.1001 4.31654 4.83343 2.4082 7.83343C1.6582 9.00843 1.6582 10.9834 2.4082 12.1584C4.31654 15.1584 7.0582 16.8918 9.99987 16.8918Z" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

+13
View File
@@ -0,0 +1,13 @@
<svg width="39" height="54" viewBox="0 0 39 54" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31.1559 34.8412C31.1559 34.8412 24.6651 41.094 19.2416 46.3804V53.5924C18.6935 54.0179 38.4905 34.8845 38.4905 34.8845L19.2416 14.6909V22.3573L31.1559 34.8412Z" fill="#1D1D1B"/>
<path d="M19.1479 39.0818L0 18.643L0.237992 18.405L19.3931 38.8583L19.1479 39.0818Z" fill="#1D1D1B"/>
<path d="M19.1407 37.1635L1.75977 18.6719L1.99776 18.4412L19.3787 36.9399L19.1407 37.1635Z" fill="#1D1D1B"/>
<path d="M19.1334 35.2523L3.52661 18.708L3.7574 18.47L19.3786 35.0215L19.1334 35.2523Z" fill="#1D1D1B"/>
<path d="M19.1262 33.3337L5.28638 18.7366L5.50995 18.5059L19.3714 33.1029L19.1262 33.3337Z" fill="#1D1D1B"/>
<path d="M19.1191 31.4155L7.05347 18.7729L7.26982 18.5349L19.3571 31.1848L19.1191 31.4155Z" fill="#1D1D1B"/>
<path d="M0.375071 18.7368L0.144287 18.4988L19.1407 0L19.3715 0.237996L0.375071 18.7368Z" fill="#1D1D1B"/>
<path d="M2.15645 18.7442L1.91846 18.5134L19.1263 1.73828L19.3571 1.97628L2.15645 18.7442Z" fill="#1D1D1B"/>
<path d="M3.93049 18.7587L3.69971 18.5279L19.1117 3.48364L19.3425 3.71443L3.93049 18.7587Z" fill="#1D1D1B"/>
<path d="M5.71187 18.7658L5.47388 18.5422L19.0973 5.22168L19.3281 5.45968L5.71187 18.7658Z" fill="#1D1D1B"/>
<path d="M7.49326 18.7799L7.24805 18.5564L19.0901 6.95947L19.3137 7.19747L7.49326 18.7799Z" fill="#1D1D1B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+35
View File
@@ -0,0 +1,35 @@
<svg width="348" height="122" viewBox="0 0 348 122" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M70.6828 79.0438C70.6828 79.0438 55.9573 93.2295 43.6532 105.223V121.584C42.4097 122.55 87.3227 79.142 87.3227 79.142L43.6532 33.3291V50.7216L70.6828 79.0438Z" fill="#1D1D1B"/>
<path d="M43.4404 88.6643L0 42.2951L0.539928 41.7551L43.9967 88.157L43.4404 88.6643Z" fill="#1D1D1B"/>
<path d="M43.4241 84.3123L3.99231 42.3607L4.53225 41.8372L43.9641 83.805L43.4241 84.3123Z" fill="#1D1D1B"/>
<path d="M43.4077 79.9764L8.00092 42.4425L8.52449 41.9026L43.964 79.4528L43.4077 79.9764Z" fill="#1D1D1B"/>
<path d="M43.3914 75.6236L11.9932 42.5075L12.5004 41.9839L43.9477 75.1001L43.3914 75.6236Z" fill="#1D1D1B"/>
<path d="M43.3751 71.2716L16.0019 42.5895L16.4927 42.0496L43.915 70.7481L43.3751 71.2716Z" fill="#1D1D1B"/>
<path d="M0.850815 42.5078L0.32724 41.9679L43.4241 0L43.9477 0.539938L0.850815 42.5078Z" fill="#1D1D1B"/>
<path d="M4.89217 42.5244L4.35223 42.0008L43.3914 3.94336L43.9149 4.4833L4.89217 42.5244Z" fill="#1D1D1B"/>
<path d="M8.91716 42.5573L8.39359 42.0337L43.3586 7.90308L43.8822 8.42665L8.91716 42.5573Z" fill="#1D1D1B"/>
<path d="M12.9586 42.5738L12.4186 42.0666L43.326 11.8464L43.8495 12.3864L12.9586 42.5738Z" fill="#1D1D1B"/>
<path d="M16.9999 42.606L16.4436 42.0988L43.3096 15.7891L43.8168 16.329L16.9999 42.606Z" fill="#1D1D1B"/>
<path d="M109.958 77.9629H107.055V86.0417H105.865V77.9629H102.963V76.9954H109.958V77.9629Z" fill="#1D1D1B"/>
<path d="M120.494 86.1672C120.081 86.1803 119.67 86.1071 119.287 85.9521C118.903 85.7972 118.557 85.564 118.269 85.2674C117.675 84.6095 117.366 83.7435 117.408 82.8583V82.6551C117.395 82.0364 117.528 81.4233 117.795 80.8652C118.025 80.3626 118.394 79.9363 118.859 79.6364C119.3 79.343 119.819 79.188 120.349 79.1913C120.732 79.1717 121.115 79.2389 121.468 79.388C121.822 79.5371 122.137 79.7643 122.391 80.0524C122.918 80.766 123.175 81.6438 123.116 82.5293V83.0034H118.559C118.55 83.5917 118.756 84.163 119.14 84.6095C119.316 84.8094 119.533 84.9684 119.777 85.0753C120.021 85.1823 120.286 85.2346 120.552 85.2287C120.914 85.2389 121.271 85.152 121.587 84.9772C121.872 84.8062 122.121 84.5829 122.323 84.3192L123.029 84.8611C122.76 85.284 122.384 85.6279 121.939 85.8574C121.493 86.087 120.995 86.1939 120.494 86.1672ZM120.378 80.1298C120.158 80.127 119.939 80.1713 119.737 80.2598C119.535 80.3482 119.355 80.4787 119.207 80.6426C118.873 81.0423 118.67 81.5357 118.627 82.0552H121.994V81.9778C121.996 81.4824 121.828 81.0011 121.52 80.6136C121.377 80.4531 121.201 80.3264 121.004 80.2427C120.806 80.159 120.592 80.1205 120.378 80.1298Z" fill="#1D1D1B"/>
<path d="M134.339 85.2291C134.731 85.2343 135.111 85.1006 135.413 84.8517C135.561 84.7354 135.682 84.5889 135.769 84.4221C135.856 84.2553 135.906 84.0719 135.917 83.8842H137.01C136.983 84.2787 136.846 84.6577 136.613 84.9775C136.364 85.3298 136.031 85.6155 135.646 85.8096C135.078 86.0974 134.432 86.1945 133.805 86.0864C133.177 85.9784 132.601 85.6709 132.163 85.2097C131.589 84.5048 131.299 83.6112 131.35 82.7038V82.5103C131.337 81.9072 131.459 81.3088 131.708 80.7591C131.933 80.2805 132.289 79.8749 132.733 79.5884C133.216 79.3022 133.769 79.158 134.33 79.1723C135.02 79.146 135.693 79.3889 136.207 79.8496C136.455 80.072 136.654 80.3432 136.792 80.6462C136.93 80.9492 137.004 81.2775 137.01 81.6105H135.917C135.9 81.2016 135.722 80.8158 135.423 80.5365C135.278 80.3966 135.107 80.287 134.919 80.2139C134.732 80.1408 134.531 80.1058 134.33 80.1108C134.07 80.0989 133.811 80.1499 133.576 80.2594C133.34 80.3689 133.134 80.5337 132.975 80.7397C132.614 81.2782 132.444 81.9219 132.492 82.5684V82.7909C132.45 83.4233 132.616 84.0519 132.966 84.5808C133.125 84.7926 133.333 84.9623 133.572 85.0753C133.812 85.1883 134.075 85.2411 134.339 85.2291Z" fill="#1D1D1B"/>
<path d="M146.646 80.1301C146.877 79.8231 147.177 79.5752 147.521 79.407C147.866 79.2387 148.246 79.1549 148.63 79.1626C150.052 79.1626 150.768 79.9656 150.787 81.562V86.0127H149.636V81.562C149.662 81.1742 149.541 80.7909 149.297 80.4881C149.169 80.3666 149.017 80.273 148.85 80.2131C148.684 80.1531 148.507 80.1282 148.33 80.1398C147.985 80.1352 147.648 80.2365 147.362 80.43C147.081 80.6321 146.855 80.9016 146.705 81.2137V86.0514H145.534V76.4922H146.685L146.646 80.1301Z" fill="#1D1D1B"/>
<path d="M160.782 79.3076V80.159C161.017 79.8497 161.322 79.6006 161.672 79.4324C162.021 79.2642 162.406 79.1816 162.795 79.1915C164.217 79.1915 164.933 79.9945 164.952 81.5909V86.0416H163.839V81.5909C163.865 81.2031 163.745 80.8198 163.501 80.517C163.372 80.3955 163.22 80.3019 163.053 80.242C162.887 80.1821 162.71 80.1571 162.533 80.1687C162.189 80.1641 161.851 80.2654 161.566 80.4589C161.284 80.661 161.058 80.9305 160.908 81.2426V86.0803H159.698V79.3076H160.782Z" fill="#1D1D1B"/>
<path d="M173.592 82.6166C173.581 82.0011 173.713 81.3914 173.979 80.8363C174.219 80.323 174.602 79.8897 175.082 79.5882C175.558 79.3022 176.104 79.1547 176.659 79.1625C177.079 79.1483 177.496 79.2278 177.881 79.3952C178.266 79.5625 178.609 79.8136 178.885 80.13C179.476 80.8386 179.779 81.7432 179.736 82.6649V82.7424C179.745 83.3503 179.616 83.9524 179.359 84.5033C179.128 85.0037 178.754 85.4246 178.285 85.7127C177.804 86.0148 177.246 86.1696 176.679 86.1577C176.259 86.1712 175.842 86.0914 175.457 85.9241C175.072 85.7568 174.729 85.5061 174.453 85.1902C173.867 84.487 173.566 83.5891 173.611 82.6746L173.592 82.6166ZM174.743 82.752C174.711 83.3905 174.895 84.0213 175.266 84.542C175.425 84.7605 175.635 84.9372 175.878 85.0568C176.121 85.1765 176.389 85.2355 176.659 85.2289C176.928 85.2364 177.195 85.1777 177.437 85.058C177.678 84.9382 177.886 84.761 178.043 84.542C178.423 83.9712 178.607 83.2916 178.565 82.6069C178.595 81.9658 178.411 81.3329 178.043 80.8073C177.881 80.5892 177.669 80.413 177.425 80.2935C177.181 80.174 176.912 80.1146 176.64 80.1204C176.373 80.1158 176.11 80.1748 175.871 80.2926C175.632 80.4104 175.425 80.5835 175.266 80.7976C174.888 81.3761 174.711 82.063 174.763 82.752H174.743Z" fill="#1D1D1B"/>
<path d="M189.614 86.0417H188.463V76.4922H189.614V86.0417Z" fill="#1D1D1B"/>
<path d="M198.342 82.6166C198.335 82.0015 198.467 81.3929 198.729 80.8363C198.964 80.3255 199.339 79.8925 199.812 79.5882C200.291 79.3014 200.841 79.1539 201.399 79.1625C201.817 79.1484 202.233 79.228 202.616 79.3954C203 79.5628 203.341 79.8138 203.615 80.13C204.21 80.8363 204.514 81.7425 204.466 82.6649V82.7423C204.475 83.3503 204.346 83.9524 204.089 84.5032C203.858 85.0037 203.484 85.4246 203.015 85.7127C202.534 86.0148 201.976 86.1695 201.409 86.1577C200.991 86.1711 200.575 86.0912 200.192 85.9239C199.808 85.7565 199.467 85.5059 199.193 85.1902C198.599 84.4908 198.294 83.591 198.342 82.6746V82.6166ZM199.503 82.752C199.472 83.3888 199.653 84.0178 200.015 84.5419C200.18 84.7548 200.391 84.9272 200.633 85.0457C200.874 85.1643 201.14 85.226 201.409 85.226C201.678 85.226 201.943 85.1643 202.185 85.0457C202.426 84.9272 202.637 84.7548 202.802 84.5419C203.175 83.9683 203.355 83.2901 203.315 82.6069C203.344 81.9657 203.161 81.3329 202.792 80.8073C202.63 80.5946 202.42 80.4223 202.18 80.3037C201.94 80.1851 201.676 80.1234 201.409 80.1234C201.141 80.1234 200.877 80.1851 200.637 80.3037C200.397 80.4223 200.188 80.5946 200.025 80.8073C199.642 81.3807 199.459 82.064 199.503 82.752Z" fill="#1D1D1B"/>
<path d="M212.825 82.6168C212.779 81.7266 213.034 80.8468 213.551 80.1206C213.773 79.8161 214.064 79.5694 214.401 79.401C214.737 79.2326 215.11 79.1476 215.486 79.153C215.855 79.1324 216.223 79.2007 216.56 79.3522C216.897 79.5038 217.192 79.7341 217.421 80.0238L217.479 79.2691H218.524V85.8386C218.546 86.2171 218.488 86.596 218.355 86.9509C218.222 87.3058 218.016 87.6289 217.75 87.8995C217.185 88.4099 216.441 88.6775 215.68 88.6445C215.185 88.6473 214.697 88.5416 214.248 88.3349C213.822 88.1583 213.456 87.8627 213.193 87.4834L213.783 86.7965C213.993 87.0787 214.266 87.3079 214.58 87.4658C214.894 87.6237 215.241 87.706 215.593 87.706C215.83 87.7204 216.069 87.6867 216.293 87.6069C216.517 87.5271 216.723 87.4029 216.899 87.2416C217.064 87.0627 217.191 86.8521 217.273 86.6226C217.354 86.3931 217.388 86.1494 217.373 85.9064V85.3936C217.133 85.6668 216.834 85.8812 216.499 86.0205C216.163 86.1597 215.8 86.22 215.438 86.1966C215.06 86.2079 214.686 86.1254 214.348 85.9566C214.011 85.7877 213.72 85.5378 213.503 85.2291C212.99 84.4593 212.751 83.5389 212.825 82.6168ZM213.986 82.7522C213.952 83.3826 214.114 84.008 214.451 84.5421C214.597 84.7504 214.794 84.9188 215.022 85.0321C215.25 85.1453 215.503 85.1997 215.757 85.1904C216.089 85.2059 216.418 85.1222 216.703 84.9499C216.987 84.7775 217.214 84.5244 217.353 84.2229V81.1558C217.211 80.8581 216.985 80.6082 216.704 80.4365C216.422 80.2647 216.097 80.1785 215.767 80.1883C215.512 80.1772 215.259 80.2308 215.03 80.3442C214.802 80.4576 214.606 80.6269 214.461 80.8365C214.108 81.4108 213.943 82.08 213.986 82.7522Z" fill="#1D1D1B"/>
<path d="M229.631 84.3581L231.199 79.3076H232.437L229.728 87.0479C229.312 88.1702 228.645 88.7217 227.735 88.7217H227.513L227.087 88.6346V87.6671H227.396C227.716 87.6863 228.034 87.6051 228.306 87.4349C228.571 87.2102 228.76 86.9096 228.848 86.5738L229.099 85.8868L226.7 79.2302H227.958L229.631 84.3581Z" fill="#1D1D1B"/>
<path d="M257.835 83.1681C257.786 84.0146 257.416 84.8106 256.8 85.3934C256.135 85.941 255.289 86.2205 254.429 86.1771C253.942 86.1942 253.458 86.0993 253.013 85.8997C252.569 85.7001 252.176 85.4011 251.865 85.0257C251.185 84.1491 250.841 83.0573 250.898 81.949V81.0782C250.88 80.3127 251.032 79.5528 251.343 78.8529C251.616 78.2485 252.06 77.7372 252.62 77.3822C253.203 77.0313 253.874 76.8534 254.555 76.8695C255.375 76.8389 256.177 77.1187 256.8 77.6532C257.388 78.2484 257.733 79.042 257.767 79.8785H256.567C256.527 79.2942 256.288 78.7412 255.89 78.3111C255.489 77.9634 254.968 77.7863 254.439 77.8176C254.093 77.8012 253.749 77.8703 253.437 78.0188C253.125 78.1674 252.854 78.3907 252.649 78.6691C252.173 79.3785 251.945 80.2256 252.001 81.0782V81.949C251.958 82.7767 252.17 83.5976 252.61 84.3001C252.801 84.5785 253.058 84.8043 253.36 84.9566C253.661 85.1088 253.995 85.1825 254.332 85.1709C254.88 85.2062 255.422 85.045 255.861 84.7161C256.273 84.2923 256.523 83.7377 256.567 83.1487L257.835 83.1681Z" fill="#1D1D1B"/>
<path d="M266.165 82.6166C266.158 82.0015 266.291 81.3929 266.552 80.8363C266.787 80.3255 267.163 79.8925 267.636 79.5882C268.115 79.3014 268.664 79.1539 269.223 79.1625C269.641 79.1484 270.057 79.228 270.44 79.3954C270.823 79.5628 271.164 79.8138 271.438 80.13C272.034 80.8363 272.338 81.7425 272.29 82.6649V82.7423C272.299 83.3503 272.17 83.9524 271.912 84.5032C271.682 85.0037 271.308 85.4246 270.838 85.7127C270.358 86.0148 269.8 86.1695 269.232 86.1577C268.814 86.1704 268.399 86.0902 268.016 85.9229C267.633 85.7556 267.291 85.5053 267.017 85.1902C266.427 84.4884 266.123 83.5903 266.165 82.6746V82.6166ZM267.326 82.752C267.296 83.3888 267.476 84.0178 267.839 84.5419C268.004 84.7548 268.215 84.9272 268.456 85.0457C268.698 85.1643 268.963 85.226 269.232 85.226C269.501 85.226 269.767 85.1643 270.008 85.0457C270.25 84.9272 270.461 84.7548 270.626 84.5419C271.003 83.97 271.183 83.2905 271.138 82.6069C271.168 81.9657 270.984 81.3329 270.616 80.8073C270.453 80.5946 270.244 80.4223 270.004 80.3037C269.764 80.1851 269.5 80.1234 269.232 80.1234C268.965 80.1234 268.701 80.1851 268.461 80.3037C268.221 80.4223 268.011 80.5946 267.849 80.8073C267.466 81.3807 267.283 82.064 267.326 82.752Z" fill="#1D1D1B"/>
<path d="M282.004 79.3076V80.0623C282.248 79.7751 282.554 79.5473 282.9 79.3964C283.245 79.2455 283.62 79.1754 283.997 79.1915C284.392 79.152 284.789 79.2348 285.135 79.4286C285.481 79.6224 285.759 79.918 285.932 80.2751C286.157 79.9438 286.459 79.6717 286.812 79.4818C287.204 79.2797 287.64 79.1799 288.08 79.1915C289.55 79.1915 290.305 79.9655 290.324 81.5329V86.0416H289.183V81.6007C289.209 81.2114 289.092 80.8259 288.854 80.517C288.701 80.3872 288.524 80.2894 288.333 80.2295C288.142 80.1697 287.94 80.149 287.741 80.1687C287.352 80.157 286.972 80.2916 286.677 80.5461C286.389 80.8127 286.216 81.1801 286.193 81.5716V86.0416H285.032V81.6297C285.062 81.4334 285.047 81.2328 284.986 81.0437C284.925 80.8547 284.821 80.6824 284.682 80.5405C284.543 80.3987 284.373 80.2913 284.185 80.2267C283.997 80.1622 283.797 80.1423 283.6 80.1687C283.272 80.142 282.945 80.2217 282.666 80.396C282.387 80.5703 282.172 80.8299 282.052 81.1362V86.0513H280.901V79.2786L282.004 79.3076Z" fill="#1D1D1B"/>
<path d="M304.953 82.7519C304.996 83.6312 304.751 84.5004 304.257 85.2287C304.04 85.5381 303.75 85.7885 303.412 85.9574C303.074 86.1263 302.699 86.2084 302.322 86.1963C301.961 86.2164 301.601 86.1566 301.267 86.0211C300.932 85.8856 300.632 85.6778 300.387 85.4126V88.6538H299.274V79.3365H300.338L300.396 80.0912C300.628 79.8045 300.924 79.5763 301.26 79.4251C301.596 79.2738 301.963 79.2037 302.331 79.2204C302.704 79.2017 303.075 79.2749 303.413 79.4335C303.75 79.5921 304.043 79.8312 304.267 80.1299C304.779 80.8758 305.027 81.7713 304.973 82.6745L304.953 82.7519ZM303.812 82.6164C303.848 81.9818 303.674 81.353 303.318 80.8265C303.171 80.6142 302.972 80.4427 302.74 80.3277C302.508 80.2127 302.251 80.158 301.993 80.1686C301.666 80.1601 301.343 80.2471 301.065 80.419C300.787 80.5908 300.564 80.84 300.425 81.1361V84.3483C300.566 84.6452 300.79 84.8947 301.07 85.0665C301.35 85.2383 301.674 85.3249 302.003 85.3158C302.259 85.3222 302.513 85.2655 302.742 85.1508C302.972 85.0361 303.17 84.8669 303.318 84.6579C303.704 84.0508 303.878 83.3328 303.812 82.6164Z" fill="#1D1D1B"/>
<path d="M317.754 86.0418C317.661 85.8136 317.605 85.5718 317.589 85.3258C317.346 85.5964 317.047 85.8115 316.713 85.9568C316.379 86.102 316.018 86.1739 315.654 86.1676C315.06 86.1889 314.479 85.985 314.029 85.5967C313.822 85.4232 313.657 85.2051 313.547 84.9588C313.436 84.7125 313.383 84.4444 313.39 84.1745C313.379 83.8578 313.445 83.5431 313.583 83.258C313.721 82.9729 313.928 82.7261 314.184 82.5394C314.856 82.1143 315.644 81.9112 316.438 81.9588H317.551V81.4364C317.56 81.2592 317.534 81.0819 317.472 80.9155C317.41 80.749 317.315 80.597 317.193 80.4688C316.899 80.2134 316.517 80.0847 316.128 80.1108C315.759 80.101 315.396 80.2095 315.093 80.4205C314.97 80.5056 314.869 80.618 314.796 80.7489C314.724 80.8799 314.683 81.0258 314.677 81.1751H313.526C313.532 80.8213 313.658 80.4801 313.884 80.2076C314.138 79.8809 314.471 79.6245 314.851 79.4626C315.275 79.2774 315.734 79.185 316.196 79.1917C316.854 79.1526 317.502 79.3595 318.015 79.7722C318.238 79.9731 318.415 80.2197 318.533 80.4953C318.652 80.7708 318.709 81.0687 318.702 81.3686V84.426C318.679 84.9277 318.762 85.4286 318.944 85.8967V86.0031L317.754 86.0418ZM315.819 85.1613C316.179 85.1587 316.533 85.062 316.844 84.8808C317.146 84.716 317.389 84.4627 317.541 84.1551V82.7716H316.641C315.229 82.7716 314.532 83.1876 314.532 84.01C314.523 84.1698 314.551 84.3295 314.613 84.477C314.675 84.6245 314.77 84.756 314.89 84.8614C315.153 85.0701 315.483 85.1766 315.819 85.1613Z" fill="#1D1D1B"/>
<path d="M328.813 79.3076V80.159C329.047 79.8497 329.352 79.6006 329.702 79.4324C330.052 79.2642 330.437 79.1816 330.825 79.1915C332.247 79.1915 332.963 79.9945 332.973 81.5909V86.0416H331.831V81.5909C331.857 81.2031 331.736 80.8198 331.493 80.517C331.359 80.3892 331.198 80.2919 331.023 80.2318C330.848 80.1717 330.661 80.1501 330.477 80.1687C330.132 80.1641 329.795 80.2654 329.509 80.4589C329.228 80.661 329.002 80.9305 328.851 81.2426V86.0803H327.7V79.3076H328.813Z" fill="#1D1D1B"/>
<path d="M344.148 84.3581L345.706 79.3076H346.944L344.235 87.0479C343.819 88.1702 343.151 88.7217 342.242 88.7217H342.019L341.594 88.6346V87.6671H341.903C342.223 87.6876 342.542 87.6063 342.813 87.4349C343.077 87.2102 343.267 86.9096 343.355 86.5738L343.606 85.8868L341.207 79.2302H342.465L344.148 84.3581Z" fill="#1D1D1B"/>
<path d="M103.204 67.4167V32.5857H113.025C115.836 32.5338 118.61 33.2282 121.065 34.5982C123.387 35.9299 125.262 37.9196 126.454 40.3163C127.764 42.9578 128.427 45.8727 128.389 48.8209V51.0462C128.446 54.0366 127.782 56.9967 126.454 59.6765C125.258 62.0776 123.366 64.0632 121.026 65.3753C118.507 66.7347 115.684 67.4306 112.822 67.3974L103.204 67.4167ZM107.8 36.3591V63.6627H112.638C116.179 63.6627 118.936 62.5694 120.901 60.3635C122.865 58.1575 123.851 55.0324 123.851 50.9591V48.9273C123.851 44.9669 122.923 41.8901 121.065 39.6971C119.217 37.5104 116.576 36.3978 113.17 36.3591H107.8Z" fill="#1D1D1B"/>
<path d="M238.146 67.417H233.579L216.009 40.5971V67.417H211.393V32.5859H216.009L233.579 59.5606V32.6053H238.146V67.417Z" fill="#1D1D1B"/>
<path d="M329.258 51.2399L325.001 55.6615V67.417H320.415V32.5859H325.001V49.8079L340.481 32.5859H346.025L332.335 47.96L347.109 67.3976H341.603L329.258 51.2399Z" fill="#1D1D1B"/>
<path d="M170.264 38.2747L180.897 67.4166H185.599L172.276 32.605H168.261L154.967 67.4166H159.698L170.264 38.2747Z" fill="#1D1D1B"/>
<path d="M279.246 38.2747L289.879 67.4166H294.582L281.259 32.605H277.243L263.95 67.4166H268.681L279.246 38.2747Z" fill="#1D1D1B"/>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg width="40" height="40" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="31" height="31" rx="7.5" stroke="black"/>
<path d="M17.6663 15.1001H14.333" stroke="black" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 13.4734V16.8067" stroke="black" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.2129 9.33325H12.7862C11.3662 9.33325 10.2129 10.4933 10.2129 11.9066V21.2999C10.2129 22.4999 11.0729 23.0066 12.1262 22.4266L15.3796 20.6199C15.7262 20.4266 16.2862 20.4266 16.6262 20.6199L19.8796 22.4266C20.9329 23.0133 21.7929 22.5066 21.7929 21.2999V11.9066C21.7862 10.4933 20.6329 9.33325 19.2129 9.33325Z" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 800 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="20" height="24" viewBox="0 0 20 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.647 0.479492C16.8265 0.521346 17.009 0.558249 17.188 0.605502C18.888 1.05554 20.0825 2.66307 19.9932 4.37501C19.8959 6.23726 18.5754 7.73093 16.7699 7.9816C15.5651 8.14901 14.5061 7.80158 13.6261 6.95236C13.4812 6.81285 13.3928 6.82095 13.2381 6.91771C11.3048 8.12591 9.36827 9.32931 7.42844 10.5279C7.30464 10.6044 7.27594 10.6688 7.32393 10.8096C7.59332 11.5806 7.59332 12.4207 7.32393 13.1917C7.27638 13.3325 7.30554 13.3964 7.42934 13.4729C9.38113 14.6793 11.3304 15.8905 13.2771 17.1065C13.4247 17.1988 13.4933 17.1682 13.6081 17.0615C14.7084 16.035 15.9931 15.706 17.4208 16.1835C18.8561 16.6637 19.7062 17.7073 19.9524 19.201C20.2866 21.2298 18.8239 23.1874 16.7946 23.4763C16.7439 23.484 16.6888 23.4763 16.6484 23.5214H15.841C15.4431 23.4282 15.0426 23.3499 14.6662 23.1748C13.8364 22.7926 13.1743 22.1195 12.8041 21.2821C12.4339 20.4447 12.3812 19.5004 12.6558 18.6268C12.7151 18.4386 12.6935 18.3491 12.5146 18.2388C10.5795 17.0486 8.64954 15.85 6.72467 14.643C6.5901 14.5593 6.52282 14.5665 6.40665 14.6759C5.32699 15.6948 4.05804 16.0377 2.64331 15.589C1.22859 15.1403 0.365577 14.1385 0.0704313 12.6813C-0.354794 10.5806 1.19764 8.48339 3.31928 8.25837C4.52498 8.13056 5.55844 8.49689 6.42414 9.33846C6.52596 9.43747 6.5901 9.44062 6.70673 9.36771C8.65911 8.14901 10.614 6.93391 12.5715 5.72242C12.6967 5.64456 12.72 5.582 12.6742 5.43754C11.9471 3.15496 13.3596 0.890825 15.7356 0.515945C15.7713 0.506836 15.8061 0.494639 15.8397 0.479492L16.647 0.479492ZM16.2321 22.1411C16.7034 22.1421 17.1645 22.0029 17.557 21.7412C17.9496 21.4795 18.256 21.1069 18.4377 20.6706C18.6193 20.2342 18.6681 19.7537 18.5777 19.2896C18.4874 18.8255 18.262 18.3986 17.9301 18.0629C17.5981 17.7273 17.1745 17.4978 16.7126 17.4035C16.2508 17.3092 15.7714 17.3543 15.3351 17.5331C14.8988 17.7119 14.5251 18.0164 14.2611 18.4082C13.9972 18.8 13.8549 19.2614 13.8521 19.7343C13.8501 20.0493 13.9102 20.3617 14.0288 20.6533C14.1475 20.945 14.3224 21.2103 14.5436 21.434C14.7647 21.6576 15.0278 21.8352 15.3175 21.9566C15.6073 22.0779 15.9181 22.1406 16.2321 22.1411ZM16.2299 1.8593C15.7587 1.86064 15.2984 2.002 14.9072 2.26554C14.516 2.52908 14.2113 2.90299 14.0317 3.34008C13.8521 3.77716 13.8056 4.25784 13.898 4.72144C13.9904 5.18504 14.2176 5.61078 14.551 5.94493C14.8844 6.27908 15.3089 6.50666 15.7711 6.59895C16.2332 6.69124 16.7123 6.6441 17.1477 6.46348C17.5832 6.28287 17.9556 5.97687 18.2179 5.58411C18.4802 5.19135 18.6207 4.72943 18.6216 4.25665C18.6232 3.94118 18.5624 3.62853 18.4427 3.3368C18.3231 3.04506 18.1469 2.78004 17.9245 2.55708C17.7021 2.33411 17.4377 2.15763 17.1469 2.03786C16.856 1.91808 16.5443 1.85739 16.2299 1.8593ZM3.76783 9.60938C3.2971 9.60992 2.83707 9.75031 2.44578 10.0129C2.05448 10.2754 1.74944 10.6483 1.56914 11.0846C1.38884 11.5209 1.34136 12.0009 1.43267 12.4643C1.52399 12.9276 1.75002 13.3534 2.08225 13.688C2.41448 14.0226 2.83803 14.2509 3.29947 14.3443C3.76091 14.4376 4.23957 14.3918 4.67508 14.2125C5.11058 14.0333 5.48342 13.7286 5.74656 13.337C6.0097 12.9454 6.15136 12.4844 6.15366 12.0121C6.15598 11.6967 6.09591 11.384 5.97695 11.092C5.85798 10.8 5.68247 10.5346 5.46058 10.3111C5.23869 10.0877 4.97483 9.91059 4.68427 9.79013C4.3937 9.66968 4.08221 9.60824 3.76783 9.60938Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
View File
Binary file not shown.
+28
View File
@@ -0,0 +1,28 @@
import { ButtonHTMLAttributes, FC, memo, ReactNode } from 'react'
import ReactLoading from 'react-loading'
import { XOR } from '../helpers/types'
import { clx } from '../helpers/utils'
type Props = {
className?: string;
isLoading?: boolean,
} & ButtonHTMLAttributes<HTMLButtonElement> &
XOR<{ children: ReactNode }, { label: string, icon?: string, isLoading?: boolean }>;
const Button: FC<Props> = memo((props: Props) => {
const buttonClass = clx(
'flex rounded-xl items-center justify-center text-center h-10 text-sm bg-primary text-white w-full',
props.disabled && 'cursor-not-allowed opacity-30',
props.className
);
return (
<button {...props} className={`${buttonClass} ${props.className}`} >
<img src={props.icon} alt="" />
{
props.isLoading ?
<ReactLoading type='spin' color={'white'} width={20} height={20} />
:
props.label || props.children
}
</button>
)
})
export default Button
+43
View File
@@ -0,0 +1,43 @@
import { clx } from '../helpers/utils'
import Button from './Button'
import book from '../assets/images/books.jpg'
import { FC } from 'react'
import { Link } from 'react-router-dom'
import { Pages } from '../config/Pages'
type Props = {
image: string;
title: string;
time: string;
teacher: string;
price: string;
}
const CourseCard: FC<Props> = (props: Props) => {
const cardClass = clx(
'w-full bg-white rounded-lg overflow-hidden'
);
return (
<div
{...props} className={`${cardClass}`}
>
<img className='h-[92px] w-full' src={book} alt="" />
<div className='mt-1 p-2 text-xs space-y-3'>
<div className='flex justify-between items-center '>
<p>{props.title}</p>
<p>{props.price} تومان</p>
</div>
<div className='text-description font-extralight space-y-2'>
<p>{props.time} ساعت آموزش</p>
<p>مدرس : {props.teacher}</p>
</div>
<Link
to={Pages.courses.detail + '123'}>
<Button
label='ثبت نام' className='mt-4 h-7 text-[10px] rounded-[8px]' />
</Link>
</div>
</div>
)
}
export default CourseCard
+74
View File
@@ -0,0 +1,74 @@
import { useState, useEffect, FC } from 'react';
import DatePicker from 'react-multi-date-picker';
import persian from 'react-date-object/calendars/persian';
import persian_fa from 'react-date-object/locales/persian_fa';
import DateObject from 'react-date-object';
import CalenderIcon from '../assets/images/calendar.svg'
type Props = {
onChange: (date: string) => void;
defaulValue?: string;
error_text?: string;
placeholder: string;
reset?: boolean;
isDateTime?: boolean;
className?: string;
label: string
};
const DatePickerComponent: FC<Props> = (props: Props) => {
const [value, setValue] = useState<DateObject | null>(null);
useEffect(() => {
if (props.reset) {
setValue(null);
}
}, [props.reset]);
useEffect(() => {
if (value) {
const formattedDate = `${value.year}/${value.month.number}/${value.day}`;
props.onChange(formattedDate);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value]);
useEffect(() => {
if (props.defaulValue && !value) {
const defaultDate = new DateObject({
date: props.defaulValue,
calendar: persian,
locale: persian_fa,
});
setValue(defaultDate);
}
}, [props.defaulValue, value]);
return (
<div className="w-full">
<div className='text-sm'>
{props.label}
</div>
<div className='relative mt-1.5'>
<DatePicker
placeholder={props.placeholder}
value={value}
onChange={(date) => setValue(date as DateObject)}
calendar={persian}
locale={persian_fa}
calendarPosition="bottom-right"
className={props.className}
/>
{props.error_text && props.error_text !== '' && (
<div className="text-xs text-right text-red-600 mt-2 mr-2 font-medium">
{props.error_text}
</div>
)}
<img src={CalenderIcon} className='absolute top-0 bottom-0 my-auto left-2' />
</div>
</div>
);
};
export default DatePickerComponent;
+57
View File
@@ -0,0 +1,57 @@
import { FC, Fragment, ReactNode, useEffect } from 'react'
import HeaderModal from './HeaderModal'
interface Props {
open: boolean,
close: () => void,
children: ReactNode,
isHeader?: boolean,
title_header?: string,
width?: number
}
const DefaulModal: FC<Props> = (props: Props) => {
useEffect(() => {
if (props.open) {
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = 'auto'
}
}, [props.open])
return (
<Fragment>
{
props.open && (
<Fragment>
<div style={{ maxWidth: props.width }} className='xl:justify-center xl:items-center items-end flex overflow-x-hidden overflow-y-auto fixed inset-0 z-[60] h-auto top-0 bottom-0 m-auto outline-none focus:outline-none xl:max-w-xl mx-auto'>
<div className='relative xl:h-full h-[80%] bottom-0 left-0 flex xl:items-center sm:h-auto w-full xl:my-6 xl:p-2'>
<div className='border-0 h-auto p-5 lg:min-w-full overflow-y-auto rounded-3xl rounded-b-none xl:rounded-b-3xl relative flex flex-col w-full modalGlass2 outline-none focus:outline-none'>
{
props.isHeader && props.title_header &&
<div className='pb-6 border-b border-white border-opacity-20'>
<div className='h-[5px] w-[200px] mx-auto bg-[#D1D3D7] rounded-full mb-4 xl:hidden'></div>
<HeaderModal close={props.close} label={props.title_header} />
</div>
}
{props.children}
</div>
</div>
</div>
<div onClick={props.close} className='fixed size-full top-0 bottom-0 right-0 modalGlass inset-0 z-50 '></div>
</Fragment>
)
}
</Fragment>
)
}
export default DefaulModal
+15
View File
@@ -0,0 +1,15 @@
import { FC } from 'react'
type Props = {
errorText: string
}
const Error: FC<Props> = (props: Props) => {
return (
<div className='mt-1.5 font-normal text-red-500 text-[11px]'>
{props.errorText}
</div>
)
}
export default Error
+21
View File
@@ -0,0 +1,21 @@
import { FC } from 'react'
import XIcon from '../assets/images/close-circle.svg'
type Props = {
label: string,
close: () => void,
}
const HeaderModal: FC<Props> = (props: Props) => {
return (
<div className='flex justify-between items-center'>
<div className='text-sm'>{props.label}</div>
<div className='size-7 rounded-full bg-white bg-opacity-35 flex justify-center items-center'>
<img src={XIcon} alt='close' className='w-4 h-4' onClick={props.close} />
</div>
</div>
)
}
export default HeaderModal
+54
View File
@@ -0,0 +1,54 @@
import { FC, InputHTMLAttributes, useState } from 'react'
import { clx } from '../helpers/utils';
import EyeIcon from '../assets/images/eye.svg'
import { SearchNormal } from 'iconsax-react';
type Variant = "floating_outlined" | "primary" | "search";
type Props = {
label?: string;
className?: string;
variant?: Variant;
error_text?: string;
onEnter?: () => void;
unit?: string;
seprator?: boolean;
isNotRequired?: boolean;
} & InputHTMLAttributes<HTMLInputElement>
const Input: FC<Props> = (props: Props) => {
const [showPassword, setShowPassword] = useState<boolean>(false)
const inputClass = clx(
'w-full h-10 text-black block px-4 text-xs rounded-xl border border-border',
props.readOnly && 'bg-gray-100 border-0 text-description',
props.variant === 'search' && 'bg-[#EEF0F7] border-0 ps-10',
props.className
);
return (
<div className='w-full'>
<label className='text-sm'>
{props.label}
</label>
<div className='w-full relative mt-1'>
<input {...props} type={props.type === 'password' && showPassword ? 'text' : props.type === 'password' ? 'password' : undefined} className={inputClass} />
{
props.type === 'password' &&
<img onClick={() => setShowPassword((oldValue) => !oldValue)} src={EyeIcon} className='w-5 absolute top-0 bottom-0 cursor-pointer my-auto left-3' />
}
{
props.variant === 'search' &&
<SearchNormal size={20} color='#8C90A3' className='absolute top-0 w-5 bottom-0 my-auto right-3' />
}
</div>
</div>
)
}
export default Input
+24
View File
@@ -0,0 +1,24 @@
import { clx } from '../helpers/utils'
import { FC } from 'react'
type Props = {
image: string;
title: string;
}
const LevelingCard: FC<Props> = (props: Props) => {
const cardClass = clx(
'rounded-[12px] border-2 border-white bg-white bg-opacity-30 size-[88px] flex items-center justify-center'
);
return (
<div
{...props} className={`${cardClass}`}>
<div className='flex flex-col justify-center items-center gap-2'>
<img className='w-8' src={props.image} alt="" />
<p className='text-xs'>{props.title}</p>
</div>
</div>
)
}
export default LevelingCard
+20
View File
@@ -0,0 +1,20 @@
import { FC } from 'react'
type Props = {
isActive: boolean,
value: 'year' | '3month' | 'month',
onChange: (value: 'year' | '3month' | 'month') => void
}
const Radio: FC<Props> = (props: Props) => {
return (
<div onClick={() => props.onChange(props.value)} className='size-4 cursor-pointer rounded-full bg-[#EAEDF5] flex justify-center items-center'>
{
props.isActive &&
<div className='size-2 bg-black rounded-full'></div>
}
</div>
)
}
export default Radio
+31
View File
@@ -0,0 +1,31 @@
import { FC } from 'react'
import Radio from './Radio'
type Props = {
items: {
label: string
value: 'year' | '3month' | 'month'
}[]
selected: string
onChange: (value: 'year' | '3month' | 'month') => void
}
const RadioGroup: FC<Props> = (props: Props) => {
return (
<div className='space-y-4 text-xs'>
{
props.items.map((item, index) => (
<div key={index} className='flex gap-2 items-center'>
<Radio value={item.value} onChange={props.onChange} isActive={item.value === props.selected} />
<div className='mt-0.5'>
{item.label}
</div>
</div>
))
}
</div>
)
}
export default RadioGroup
+62
View File
@@ -0,0 +1,62 @@
import { FC, SelectHTMLAttributes } from 'react'
import { clx } from '../helpers/utils'
import { ArrowDown2 } from 'iconsax-react'
import Error from './Error'
export type ItemsSelectType = {
value: string,
label: string,
}
type Props = {
className?: string,
items: ItemsSelectType[],
error_text?: string,
placeholder?: string,
label?: string,
} & SelectHTMLAttributes<HTMLSelectElement>
const Select: FC<Props> = (props: Props) => {
return (
<div className='w-full relative'>
{
props.label &&
<label className='text-sm'>
{props.label}
</label>
}
<select {...props} className={clx(
'w-full text-black block border appearance-none border-border px-2.5 h-10 text-sm rounded-2.5 bg-gray',
props.className,
props.label && 'mt-1'
)}>
{
props.placeholder &&
<option value="" disabled selected>{props.placeholder}</option>
}
{
props.items.map((item) => {
return (
<option key={item.value} value={item.value}>
{item.label}
</option>
)
})
}
</select>
<ArrowDown2 size={16} color='black' className={clx(
'absolute z-0 top-3 left-2',
props.label && 'top-10'
)} />
{
props.error_text && props.error_text !== '' ?
<Error
errorText={props.error_text}
/>
: null
}
</div>
)
}
export default Select
+58
View File
@@ -0,0 +1,58 @@
import { FC, InputHTMLAttributes, useState } from 'react'
import { clx } from '../helpers/utils';
import EyeIcon from '../assets/images/eye.svg'
import { SearchNormal } from 'iconsax-react';
export type ItemsSelectType = {
value: string,
label: string,
}
type Variant = "floating_outlined" | "primary" | "search";
type Props = {
label?: string;
className?: string;
variant?: Variant;
error_text?: string;
onEnter?: () => void;
unit?: string;
seprator?: boolean;
isNotRequired?: boolean;
} & InputHTMLAttributes<HTMLInputElement>
const Input: FC<Props> = (props: Props) => {
const [showPassword, setShowPassword] = useState<boolean>(false)
const inputClass = clx(
'w-full h-10 text-black block px-4 text-xs rounded-xl border border-border',
props.readOnly && 'bg-gray-100 border-0 text-description',
props.variant === 'search' && 'bg-[#EEF0F7] border-0 ps-10',
props.className
);
return (
<div className='w-full'>
<label className='text-sm'>
{props.label}
</label>
<div className='w-full relative mt-1'>
<input {...props} type={props.type === 'password' && showPassword ? 'text' : props.type === 'password' ? 'password' : undefined} className={inputClass} />
{
props.type === 'password' &&
<img onClick={() => setShowPassword((oldValue) => !oldValue)} src={EyeIcon} className='w-5 absolute top-0 bottom-0 cursor-pointer my-auto left-3' />
}
{
props.variant === 'search' &&
<SearchNormal size={20} color='#8C90A3' className='absolute top-0 w-5 bottom-0 my-auto right-3' />
}
</div>
</div>
)
}
export default Input
+15
View File
@@ -0,0 +1,15 @@
import { FC } from 'react'
type Props = {
color: string
}
const StatusCircle: FC<Props> = (props: Props) => {
return (
<div style={{ background: props.color }} className='size-1.5 rounded-full'>
</div>
)
}
export default StatusCircle
+22
View File
@@ -0,0 +1,22 @@
import { FC } from 'react'
import { clx } from '../helpers/utils'
type Props = {
variant: 'success' | 'error' | 'warning',
text: string,
}
const StatusWithText: FC<Props> = (props: Props) => {
return (
<div className={clx(
'w-fit py-1 px-2 text-xs h-fit rounded-full',
props.variant === 'success' && 'bg-green-100 text-success',
props.variant === 'error' && 'bg-red-100 text-red-400',
props.variant === 'warning' && 'bg-yellow-100 text-yellow-400',
)}>
{props.text}
</div>
)
}
export default StatusWithText
+50
View File
@@ -0,0 +1,50 @@
import { Fragment } from 'react'
import { Switch } from '@headlessui/react'
import ReactLoading from 'react-loading'
interface Props {
active: boolean,
onChange: (value: boolean) => void,
isLoading?: boolean,
label?: string,
}
const SwitchComponent = (props: Props) => {
const handleChange = (value: boolean) => {
props.onChange(value)
}
if (props.isLoading) {
return (
<ReactLoading type='spin' color='black' width={20} height={20} className='ml-4' />
)
}
return (
<div className='dltr w-fit flex gap-2 items-center'>
<Switch checked={props.active} onChange={handleChange} as={Fragment}>
{({ checked }) => (
<button
className={`${checked ? 'bg-primary' : 'bg-gray-200'
} relative inline-flex h-6 w-11 items-center rounded-full`}
>
<span className="sr-only">Enable notifications</span>
<span
className={`${checked ? 'translate-x-6' : 'translate-x-1'
} inline-block h-4 w-4 transform rounded-full bg-white transition`}
/>
</button>
)}
</Switch>
{
props.label &&
<div className='text-sm'>
: {props.label}
</div>
}
</div>
)
}
export default SwitchComponent
+54
View File
@@ -0,0 +1,54 @@
import { FC, ReactNode } from 'react'
import { clx } from '../helpers/utils'
import { Swiper, SwiperSlide } from 'swiper/react'
type Item = {
icon: ReactNode,
label: string,
value: string,
}
type Props = {
items: Item[],
onChange: (value: string) => void,
active: string,
isFullWidth?: boolean
}
const Tabs: FC<Props> = (props: Props) => {
const SWIPER_SLIDE_STYLE = {
overflow: 'visible !important',
display: 'flex',
}
return (
<Swiper
slidesPerView='auto'
spaceBetween={30}
className={clx(
'px-10 max-w-full w-fit items-center text-description mx-auto backdrop-blur-md border-2 border-white gap-10 flex h-[80px] rounded-[32px] bg-white bg-opacity-45',
props.isFullWidth && 'w-full center'
)}>
{
props.items.map((item: Item, index: number) => {
return (
<SwiperSlide style={SWIPER_SLIDE_STYLE} onClick={() => props.onChange(item.value)} key={item.value} className={clx(
'flex flex-col max-w-fit mt-[15px] items-center gap-2 cursor-pointer',
index === 0 && 'pr-[30px]',
index === props.items.length - 1 && 'pl-[10px]',
props.active === item.value && 'text-black'
)}>
{item.icon}
<div className='text-xs'>
{item.label}
</div>
</SwiperSlide>
)
})
}
</Swiper>
)
}
export default Tabs
+22
View File
@@ -0,0 +1,22 @@
import { FC, ReactNode } from 'react'
interface Props {
text: string,
children?: ReactNode,
dir?: string,
}
const Td: FC<Props> = (props: Props) => {
return (
<td className='td' style={{ direction: props.dir === "ltr" ? "ltr" : "rtl" }}>
{
props.text ?
props.text
:
props.children
}
</td>
)
}
export default Td
+25
View File
@@ -0,0 +1,25 @@
import { FC, InputHTMLAttributes } from 'react'
type Props = {
label: string,
} & InputHTMLAttributes<HTMLTextAreaElement>
const Textarea: FC<Props> = (props: Props) => {
return (
<div className='w-full relative'>
<label className='text-sm'>
{props.label}
</label>
<textarea
className='border border-border leading-7 w-full rounded-xl px-4 py-3 min-h-[100px] mt-1 text-xs'
{...props}
>
</textarea>
</div>
)
}
export default Textarea
+18
View File
@@ -0,0 +1,18 @@
import { FC } from 'react'
type Props = {
title: string,
}
const TitleLine: FC<Props> = (props: Props) => {
return (
<div className='w-full text-sm flex items-center gap-4'>
<div className='whitespace-nowrap'>{props.title}</div>
<svg style={{ width: '100%', height: 1 }} xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
<line x1="0" y1="0" x2="100%" y2="0" stroke="#aaa" strokeWidth="1" strokeDasharray="5 3" />
</svg>
</div>
)
}
export default TitleLine
+33
View File
@@ -0,0 +1,33 @@
import { ArrowLeft } from 'iconsax-react'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'
type Props = {
title: string,
link: string
}
const TitleSeeAll: FC<Props> = (props: Props) => {
const { t } = useTranslation('global')
return (
<div className='flex justify-between'>
<div>
{props.title}
</div>
<Link to={props.link} className='flex items-center gap-2'>
<div className='text-sm'>
{t('home.see_all')}
</div>
<ArrowLeft
size={16}
color='black'
/>
</Link>
</div>
)
}
export default TitleSeeAll
+44
View File
@@ -0,0 +1,44 @@
import { FC, useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useDropzone } from 'react-dropzone'
type Props = {
label: string,
}
const UploadBox: FC<Props> = (props: Props) => {
const { t } = useTranslation('global')
const [file, setFile] = useState<File>()
const onDrop = useCallback((acceptedFiles: File[]) => {
setFile(acceptedFiles[0])
}, [])
const { getRootProps, getInputProps } = useDropzone({ onDrop })
return (
<div>
<div className='text-sm'>{props.label}</div>
<div className='w-full h-10 mt-1 border border-border rounded-xl items-center px-2 flex gap-2.5'>
<button {...getRootProps()} className=' w-fit h-7 rounded-lg text-xs px-6 bg-secondary'>
<input {...getInputProps()} />
{t('select_file')}
</button>
<div className='text-description text-xs'>
{
file ?
file.name
:
t('no_select_file')
}
</div>
</div>
</div>
)
}
export default UploadBox
+45
View File
@@ -0,0 +1,45 @@
export const Pages = {
auth: {
login: "/auth/login",
register: "/auth/register",
forgotPassword: "/auth/forgot",
},
dashboard: "/dashboard",
services: {
mine: "/services",
other: "/other-service",
detail: "/services/detail/",
},
transactions: "/transactions",
receipts: {
index: "/receipts",
detail: "/receipts/",
},
ticket: {
list: "/tickets",
create: "/tickets/create",
detail: "/tickets/messages/",
},
announcement: {
list: "/announcement",
detail: "/announcement/",
},
company: {
detail: "/company/detail/",
},
grade: {
records: "/grade-records",
},
criticisms: "/criticisms",
learning: "/learning",
setting: "/setting",
wallet: "/wallet",
profile: "/profile",
certificate: "/certificate",
admitCard: "/admitCard",
myCourses: "/my-courses",
payment: "/payment",
reportCard: {
list: "/report-card/list",
},
};
+28
View File
@@ -0,0 +1,28 @@
import axios from "axios";
import { Pages } from "./Pages";
const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
});
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
if (error.response.status === 401) {
localStorage.removeItem(import.meta.env.VITE_TOKEN_NAME);
window.location.href = Pages.auth.login;
}
return Promise.reject(error);
}
);
axiosInstance.interceptors.request.use(async function (config) {
const token = localStorage.getItem(import.meta.env.VITE_TOKEN_NAME)
? localStorage.getItem(import.meta.env.VITE_TOKEN_NAME)
: "";
config.headers.Authorization = "Bearer " + token;
return config;
});
export default axiosInstance;
+10
View File
@@ -0,0 +1,10 @@
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
export type XOR<T, U> = T | U extends object
? (Without<T, U> & U) | (Without<U, T> & T)
: T | U;
export type ItemsSelectType = {
value: any,
label: string,
}
+11
View File
@@ -0,0 +1,11 @@
import { twMerge } from "tailwind-merge";
/**
* Concatenates truthy classes into a space-separated string.
*
* @param classes - The classes to concatenate.
* @returns The concatenated classes.
*/
export const clx = (...classes: (string | boolean | undefined)[]): string => {
return twMerge(classes.filter(Boolean).join(" "));
};
+24
View File
@@ -0,0 +1,24 @@
import { useEffect, useRef } from 'react';
export const useOutsideClick = (callback: () => void) => {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
callback();
}
};
document.addEventListener('mouseup', handleClickOutside);
document.addEventListener('touchend', handleClickOutside);
return () => {
document.removeEventListener('mouseup', handleClickOutside);
document.removeEventListener('touchend', handleClickOutside);
};
}, [callback]);
return ref;
};
+146
View File
@@ -0,0 +1,146 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
html {
direction: rtl;
}
body {
font-family: "irancell" !important;
margin: 0;
padding: 0;
width: 100%;
direction: rtl;
overflow-x: hidden !important;
background: #eceef6;
font-weight: 300;
overflow: hidden;
}
html,
body,
#root {
min-height: 100% !important;
height: 100%;
}
* {
outline: none;
}
input::placeholder,
textarea::placeholder {
color: #888888;
}
@layer components {
.rowTwoInput {
@apply flex flex-col sm:flex-row sm:gap-5 gap-5;
}
.td {
@apply px-6 py-4 whitespace-nowrap text-xs;
}
.thead {
@apply h-[69px] bg-white text-sm text-[#8C90A3] rounded-3xl overflow-hidden;
}
.tr {
@apply w-full h-[74px] bg-white border-t border-[#EAEDF5] hover:bg-secondary;
}
}
tbody tr {
background-color: white;
}
.thead tr td:first-child {
border-top-right-radius: 23px;
/* border-bottom-right-radius: 23px; */
}
.thead tr td:last-child {
border-top-left-radius: 23px;
/* border-bottom-left-radius: 23px; */
}
.rmdp-input {
min-height: 40px;
border-radius: 12px !important;
border: 1px solid #d0d0d0 !important;
font-size: 12px !important;
width: 100% !important;
margin: 0px;
padding-right: 16px !important;
}
.rmdp-container {
width: 100%;
}
.dltr {
direction: ltr;
}
.drtl {
direction: rtl;
}
.otp div {
width: 100%;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.borderDashed {
background-image: url("data:image/svg+xml,%3Csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='none' rx='24' ry='24' stroke='%238C90A3' stroke-width='2' stroke-dasharray='6%2C 14' stroke-dashoffset='25' stroke-linecap='square'/%3E%3C/svg%3E");
border-radius: 24px; /* تطابق با rx و ry */
overflow: hidden; /* برای رفع مشکلات برش */
}
.modalGlass {
background: rgba(0, 0, 0, 0.436);
background-blend-mode: multiply;
backdrop-filter: blur(7px);
}
.modalGlass2 {
background: rgba(255, 255, 255, 0.502);
background-blend-mode: multiply;
/* backdrop-filter: blur(5px); */
}
.modalGlass3 {
background: rgba(62, 61, 61, 0.2);
background-blend-mode: multiply;
backdrop-filter: blur(44px);
}
.rc-rate-star {
margin-right: 3px !important; /* فاصله بین ستاره‌ها */
}
.hide-scroll-bar::-webkit-scrollbar {
display: none;
}
.hide-scroll-bar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.swiper-wrapper {
width: fit-content;
}
.center .swiper-wrapper {
display: flex;
justify-content: center;
}
.overflowX {
overflow-x: scroll;
scrollbar-width: none;
}
.overflowX::-webkit-scrollbar {
display: none;
}
+321
View File
@@ -0,0 +1,321 @@
{
"description": "توضیحات",
"courses": "دوره ها",
"register": "ثبت نام",
"teacher": "مدرس",
"semester_courses": "دوره های ترمی",
"new_courses": "دوره های جدید",
"grade-records": {
"all-records": "کارنامه ها",
"search": "جستجو در کارنامه ها",
"title": "عنوان دوره",
"status": "وضعیت",
"record": "کارنامه"
},
"payment": {
"fullPay": "پرداخت کامل",
"prePay": "پیش پرداخت"
},
"admit_card": {
"allCards": "کارت ورود به جلسه",
"search": "جستجو در کارت ها"
},
"certificate": {
"allSertificates": "گواهینامه ها",
"certificate": "گواهینامه",
"search": "جستجو در گواهینامه ها"
},
"myCourses": {
"myCourses": "دوره های من",
"search": "جستجو در دوره ها"
},
"auth": {
"welcome": "خوش آمدید",
"enter_information": "برای ثبت نام اطلاعات خود را وارد کنید.",
"user_info": "اطلاعات کاربر",
"name": "نام",
"enter_name": "نام خود را وارد کنید",
"family": "نام خانوادگی",
"enter_family": "نام خانوادگی خود را وارد کنید",
"select_date": "انتخاب تاریخ",
"dateofbirth": "تاریخ تولد",
"national_code": "کد ملی",
"national_code_enter": "کد ملی خود را وارد کنید",
"email": "ایمیل",
"enter_email": "ایمیل خود را وارد کنید",
"phonen_number": "شماره تماس",
"enter_phone_number": "شماره تماس خود را وارد کنید",
"password": "رمز عبور",
"enter_password": "رمز عبور انتخابی خود را وارد کنید",
"next": "ادامه",
"before_registered": "آیا قبلا ثبت نام کردید؟",
"login": "ورود",
"enter_info_login": "لطفا اطلاعات خود را وارد کنید.",
"mobile_or_email": " شماره موبایل یا ایمیل",
"enter_mobile_or_email": " شماره موبایل یا ایمیل خود را وارد کنید",
"have_account": "آیا حساب کاربری ندارید؟",
"register": "ثبت نام",
"enter_otp": "کد تایید را وارد کنید",
"verfify_code_text_1": "کد تایید برای ",
"verfify_code_text_2": "ارسال شد.",
"change_number": "تغییر شماره",
"verify_code": "کد تایید ",
"counter_otp": "مانده تا دریافت مجدد کد",
"login_with_password": "ورود با رمز عبور",
"enter_password_step3": "رمز عبور را وارد کنید",
"enter_your_password": "رمز عبور خود را وارد کنید.",
"forgot_password": "فراموشی رمز عبور",
"login_with_otp": "ورود با رمز عبور یکبار مصرف"
},
"errors": {
"required": "این فیلد اجباری می باشد"
},
"sidebar": {
"menu": "منو",
"mainpage": "صفحه اصلی",
"myservice": "سرویس های من",
"mycourses": "دوره های من",
"other_service": "سایر سرویس ها",
"other_courses": "سایر دوره ها",
"receipt_list": "لیست صورتحساب",
"Language_learners_list": "لیست زبان آموزان",
"user_list": "لیست کاربران",
"transactions": "تراکنش ها",
"other": "سایر",
"ticket": "تیکت ها",
"announcement": "اطلاعیه ها",
"criticisms": "انتقادات",
"learning": "آموزش",
"setting": "تنظیمات",
"logout": "خروج"
},
"header": {
"search": "جستجو"
},
"home": {
"last_announcement": "آخرین اطلاعیه ها",
"see_all": "دیدن همه",
"companies": "شرکت ها"
},
"all": "همه",
"service": {
"my_service": "سرویس های من",
"search": "جستجو در سرویس ها",
"active_menu": "منو فعال",
"selected_danak": "انتخاب داناک",
"buy": "خرید",
"other_services": "سایر سرویس ها",
"other_courses": "سایر دوره ها",
"year": "سالانه",
"month3": "۳ ماهه",
"month": "ماهیانه",
"company_develop": "شرکت توسعه دهنده",
"company_danak": "شرکت داناک",
"user_count": "تعداد کاربران",
"year_make": "سال ساخت",
"lang_app": "زبان نرم افزار",
"persian": "فارسی",
"score": "امتیاز",
"compelete_description": "توضیحات تکمیلی",
"submit_comment": "ثبت نظر",
"hint_comment": "شما پس از خرید این سرویس میتوانید نظر خود را به ثبت برسانید.",
"fullName": "نام و نام خانوادگی",
"enter_your_name": "نام خود را واردکنید",
"title": "عنوان",
"enter_title_comment": "عنوان نظر خود را وارد کنید",
"your_comment": "نظر شما",
"write_your_comment": "نظر خود را راجع به این سرویس بنویسید",
"users_comments": "نظرات کاربران"
},
"footer": {
"home": "صفحه اصلی",
"companies": "شرکت ها",
"my_company": "شرکت من",
"other_courses": "سایر دوره ها",
"my_services": "سرویس های من",
"announcements": "اطلاعیه ها",
"tickets": "تیکت ها"
},
"ticket": {
"tickets": "تیکت ها",
"new_ticket": "تیکت جدید",
"answered": "پاسخ داده شده",
"checking": "در حال بررسی",
"wait_answered": "در انتظار پاسخ",
"closed": "بسته شده",
"number": "شماره",
"title": "عنوان",
"team": "تیم",
"date": "تاریخ",
"status": "وضعیت",
"priority": "الویت",
"detail": "جزییات",
"submit_your_message": "ثبت پیام شما",
"name": "نام",
"email": "ایمیل",
"subject": "موضوع",
"enter_your_subject": "موضوع درخواست خودرا وارد کنید",
"select_your_service": "سرویس مربوطه",
"not_once": "هیچکدام",
"your_description": "توضیحات شما",
"attachment": "فایل های ضمیمه",
"send": "ارسال",
"title_hint": "جهت پاسخگویی بهتر به مسائل زیر توجه بفرمایید :",
"ticket_number": "تیکت شماره",
"user_name": "نام کاربر",
"expert": "کارشناس: ",
"answer": "پاسخ",
"send_answer": "ارسال پاسخ",
"close_ticket": "بستن تیکت",
"info_ticket": "اطلاعات تیکت",
"update_sms": "به روزرسانی این تیکت از طریق پیامک اطلاع داده میشود"
},
"receip": {
"receip_list": "لیست صورتحساب ها",
"user_list": "لیست کاربران",
"Language_learners_list": "لیست زبان آموزان",
"dont_pay": "پرداخت نشده",
"dont_confrim": "تایید نشده",
"paid": "پرداخت شده",
"canceled": "لغو شده",
"returned": "پس داده شده",
"its_time": "سررسیده شده",
"archive": "بایگانی شده",
"detail_account": "جزییات صورتحساب ",
"personal_information": "اطلاعات شخصی",
"type_person": "نوع شخص : ",
"company": "حقوقی",
"representativeـname": "نام نماینده :",
"company_name": "نام شرکت/ سازمان :",
"registration_number": "شماره ثبت :",
"tel_company": "تلفن شرکت :",
"national_code": "کد ملی نماینده :",
"postal_code": "کد پستی :",
"state": "استان :",
"city": "شهر :",
"address": "آدرس :",
"detail_receip": "مشخصات صورتحساب",
"not_paid": "پرداخت نشده",
"description": "توضیحات",
"price": "مبلغ",
"receip_information": "اطلاعات صورتحساب",
"date_factor": "تاریخ صدور فاکتور",
"due_date": "تاریخ سررسید فاکتور",
"factor_number": "شماره فاکتور",
"status_factor": "وضعیت فاکتور"
},
"edit": "ویرایش",
"transaction": {
"transaction": "تراکنش ها",
"totao_deposit": "مجموع واریز ها",
"total_withdrawal": "مجموع برداشت ها",
"balance": "موجودی کیف پول",
"transaction2": "تراکنش",
"type_deposit": "نوع واریز",
"status": "وضعیت",
"success_paid": "پرداخت موفق",
"print": "پرینت",
"download": "دانلود",
"type_getway": "نوع درگاه",
"doer": "انجام دهنده",
"tracking_number": "شماره پیگیری",
"description_transaction": "شرح تراکنش",
"amount": "مبلغ",
"date": "تاریخ تراکنش",
"order_number": "شماره سفارش"
},
"announcement": {
"announcements": "اطلاعیه ها",
"search": "جستجو در اطلاعیه ها",
"new": "جدید",
"important": "مهم",
"text_announcement": "متن اطلاعیه"
},
"back": "بازگشت",
"criticisms": {
"criticisms": "انتقادات",
"submit_your_criticisms": "ثبت انتقاد شما",
"description_criticisms": "با انتقاد خود راهگشای ما باشید.",
"subject": "موضوع",
"enter_your_subject": "موضوع انتقاد خودرا وارد کنید",
"description": "توضیحات",
"enter_description": "توضیحات شما"
},
"select_file": "انتخاب فایل",
"no_select_file": "هیچ فایلی انتخاب نشده است",
"attachment": "فایل های ضمیمه",
"learning": {
"learning": "آموزش"
},
"setting": {
"setting": "تنظیمات",
"notification": "پیام و ایمیل",
"password": "رمز عبور",
"setting_email_message": "تنظیمات ارسال پیام و ایمیل",
"notification_announcement": "اعلانات و اطلاعیه ها",
"email": "ایمیل",
"sms": "پیامک",
"change_password": "تغییر رمز عبور",
"current_password": "رمز عبور فعلی",
"new_password": "رمز عبور جدید",
"reapeat_password": "تکرار رمز عبور",
"save_change": "ذخیره تغییرات",
"password_is": "رمز عبور می‌بایست:",
"min-8": "حداقل 8 کاراکتر باشد",
"mix_charachter_number": "ترکیبی از حروف کوچک و بزرگ باشد",
"exsist_number": "شامل اعداد باشد",
"exsist_special_character": "شامل کاراکتر های خاص (نماد ها) باشد"
},
"wallet": {
"increese_wallet": "افزایش موجودی حساب اعتباری",
"online_pay": "پرداخت آنلاین",
"card_to_card": "کارت به کارت",
"select_price": "انتخاب مبلغ",
"optional_price": "مبلغ دلخواه‌ (به ریال وارد کنید)",
"enter_your_price": "مبلغ دلخواه خودرا وارد کنید",
"select_payment": "انتخاب درگاه پرداخت",
"pay": "پرداخت",
"amount": "مبلغ",
"enter_amount_card": "مبلغ کارت به کارت شده را وارد کنید",
"upload_deposit_slip": "آپلود فیش واریزی",
"submit_slip": "ثبت فیش",
"sheba": "شبا",
"pay_sheba": "پرداخت شبا",
"enter_sheba_deposit": "مبلغ شبا شده شده را وارد کنید"
},
"notif": {
"natification": "اعلان ها",
"add_notification": "درج اطلاعیه",
"title": "عنوان",
"group": "گروه"
},
"profile": {
"account_user": "حساب کاربری",
"image_profile": "تصویر حساب کاربری",
"image_profile_desc": "تصویر خود را در حساب کاربری قرار دهید.",
"upload_image": "آپلود تصویر",
"format_image": "فرمت های jpg, png,jpeg.حداکثر ۱مگابایت",
"info_account": "اطلاعات حساب",
"auth_after_change_info": "در صورت تغییر اطلاعات حساب باید مجددا احراز هویت انجام شود.",
"username": "نام کاربری",
"phone_call": "شماره تماس",
"personal_information": "اطلاعات شخصی",
"enter_carefully_your_information": "اطلاعت شخصی خود را با دقت وارد کنید.",
"date_of_birth": "تاریخ تولد",
"national_code": "کد ملی",
"address": "آدرس",
"address_live": "آدرس محل سکونت خود را با دقت وارد کنید",
"province": "استان",
"city": "شهر",
"postal_code": "کد پستی"
},
"email": "ایمیل",
"reportCard": {
"reportCard": "کارنامه",
"search": "جستجو در کارنامه ها",
"title_course": "عنوان دوره"
},
"status": "وضعیت",
"download": "دانلود"
}
+10
View File
@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
BIN
View File
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '../../components/Input'
import Select from '../../components/Select'
import Td from '../../components/Td'
import StatusCircle from '../../components/StatusCircle'
import Button from '../../components/Button'
import { DocumentDownload } from 'iconsax-react'
const ReportCardList: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-4'>
<div>
{t('reportCard.reportCard')}
</div>
<div className='mt-2 flex gap-4 items-center'>
<Input
variant='search'
placeholder={t('reportCard.search')}
className='bg-white'
/>
<div className='w-[150px]'>
<Select
items={[
{ value: '1', label: t('all') },
]}
className='bg-white'
/>
</div>
</div>
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
<table className='w-full text-sm '>
<thead className='thead'>
<tr>
<Td text={t('reportCard.title_course')} />
<Td text={t('status')} />
<Td text={t('reportCard.reportCard')} />
</tr>
</thead>
<tbody>
<tr className='tr'>
<Td text={t('reportCard.title_course')} />
<Td text=''>
<div className='flex gap-1.5 items-center'>
<StatusCircle color='red' />
<div className='text-[11px]'>ثبت نشده</div>
</div>
</Td>
<Td text={''}>
<Button
className='h-7 rounded-lg w-fit px-6'
>
<div className='flex gap-2 items-center'>
<DocumentDownload size={16} color='white' />
<div className='text-xs mt-0.5'>{t('download')}</div>
</div>
</Button>
</Td>
</tr>
<tr className='tr'>
<Td text={t('reportCard.title_course')} />
<Td text=''>
<div className='flex gap-1.5 items-center'>
<StatusCircle color='red' />
<div className='text-[11px]'>ثبت نشده</div>
</div>
</Td>
<Td text={''}>
<Button
disabled
className='h-7 rounded-lg w-fit px-6'
>
<div className='flex gap-2 items-center'>
<DocumentDownload size={16} color='white' />
<div className='text-xs mt-0.5'>{t('download')}</div>
</div>
</Button>
</Td>
</tr>
</tbody>
</table>
</div>
</div>
)
}
export default ReportCardList
+100
View File
@@ -0,0 +1,100 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Td from '../../components/Td'
import Select from '../../components/Select'
import Input from '../../components/Input'
import Button from '../../components/Button'
import { DocumentDownload } from 'iconsax-react'
const AdmitCard: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-4 min-h-[500px]'>
<div className='flex justify-between items-center'>
<div>
{t('admit_card.allCards')}
</div>
</div>
<div>
<div className='flex justify-between items-end gap-2 xl:items-end'>
<div className='mt-6 xl:mt-0 max-w-[420px] xl:w-[300px] w-full'>
<Input
variant='search'
placeholder={t('admit_card.search')}
className='bg-white w-full'
/>
</div>
<div className='flex items-center gap-4'>
<div className='w-[100px] xl:w-[200px]'>
<Select
items={[
{
label: 'همه',
value: 'active'
}
]}
className='bg-white border'
/>
</div>
</div>
</div>
</div>
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
<table className='w-full text-sm'>
<thead className='thead'>
<tr>
<Td text={t('grade-records.title')} />
<Td text={t('grade-records.status')} />
<Td text=''>
<div className='max-w-[200px] flex justify-center'>
{t('admit_card.allCards')}
</div>
</Td>
</tr>
</thead>
<tbody>
<tr className='tr'>
<Td text={t('grade-records.title')} />
<Td text=''>
<div className='flex gap-1 !items-center'>
<div className='w-2 h-2 rounded-full bg-green-600'></div>
<div>{t('grade-records.status')}</div>
</div>
</Td>
<Td text=''>
<Button
disabled
className='h-7 rounded-lg w-fit px-6'
>
<div className='flex gap-2 items-center'>
<DocumentDownload size={16} color='white' />
<div className='text-xs mt-0.5'>{t('download')}</div>
</div>
</Button>
</Td>
</tr>
<tr className='tr'>
<Td text={t('grade-records.title')} />
<Td text=''>
<div className='flex gap-1 !items-center'>
<div className='w-2 h-2 rounded-full bg-yellow-600'></div>
<div>{t('grade-records.status')}</div>
</div>
</Td>
<Td text=''>
<Button
className='h-7 rounded-lg w-fit px-6'
>
<div className='flex gap-2 items-center'>
<DocumentDownload size={16} color='white' />
<div className='text-xs mt-0.5'>{t('download')}</div>
</div>
</Button>
</Td>
</tr>
</tbody>
</table>
</div>
</div>
)
}
export default AdmitCard
+100
View File
@@ -0,0 +1,100 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Td from '../../components/Td'
import Select from '../../components/Select'
import Input from '../../components/Input'
import Button from '../../components/Button'
import { DocumentDownload } from 'iconsax-react'
const Records: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-4 min-h-[500px]'>
<div className='flex justify-between items-center'>
<div>
{t('certificate.allSertificates')}
</div>
</div>
<div>
<div className='flex justify-between items-end gap-2 xl:items-end'>
<div className='mt-6 xl:mt-0 max-w-[420px] xl:w-[300px] w-full'>
<Input
variant='search'
placeholder={t('certificate.search')}
className='bg-white w-full'
/>
</div>
<div className='flex items-center gap-4'>
<div className='w-[100px] xl:w-[200px]'>
<Select
items={[
{
label: 'همه',
value: 'active'
}
]}
className='bg-white border'
/>
</div>
</div>
</div>
</div>
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
<table className='w-full text-sm'>
<thead className='thead'>
<tr>
<Td text={t('grade-records.title')} />
<Td text={t('grade-records.status')} />
<Td text=''>
<div className='max-w-[200px] flex justify-center'>
{t('certificate.certificate')}
</div>
</Td>
</tr>
</thead>
<tbody>
<tr className='tr'>
<Td text={t('grade-records.title')} />
<Td text=''>
<div className='flex gap-1 !items-center'>
<div className='w-2 h-2 rounded-full bg-green-600'></div>
<div>{t('grade-records.status')}</div>
</div>
</Td>
<Td text=''>
<Button
className='h-7 rounded-lg w-fit px-6'
>
<div className='flex gap-2 items-center'>
<DocumentDownload size={16} color='white' />
<div className='text-xs mt-0.5'>{t('download')}</div>
</div>
</Button>
</Td>
</tr>
<tr className='tr'>
<Td text={t('grade-records.title')} />
<Td text=''>
<div className='flex gap-1 !items-center'>
<div className='w-2 h-2 rounded-full bg-yellow-600'></div>
<div>{t('grade-records.status')}</div>
</div>
</Td>
<Td text=''>
<Button
disabled
className='h-7 rounded-lg w-fit px-6'
>
<div className='flex gap-2 items-center'>
<DocumentDownload size={16} color='white' />
<div className='text-xs mt-0.5'>{t('download')}</div>
</div>
</Button>
</Td>
</tr>
</tbody>
</table>
</div>
</div>
)
}
export default Records
+149
View File
@@ -0,0 +1,149 @@
import { Calendar, CallCalling, Location, Map1, MessageMinus, Profile, Setting2, Sms } from 'iconsax-react'
import { FC } from 'react'
const CompanyDetail: FC = () => {
return (
<div className='mt-4 pb-20'>
<img
src='https://picsum.photos/200/300'
className='w-full h-[220px] rounded-xl'
/>
<div className='p-6 bg-white rounded-xl -mt-4 relative'>
<div className='flex gap-3'>
<img
src='https://picsum.photos/200/300'
className='size-16 rounded-xl object-cover'
/>
<div className='flex-1 items-center flex justify-between '>
<div className='text-sm'>
شرکت شماره ۱
</div>
<div className='flex items-center gap-1.5 text-xs'>
<Map1
size={20}
color='black'
/>
<div>
آدرس در نقشه
</div>
</div>
</div>
</div>
<div className='mt-5 text-[10px]'>
<div className='flex justify-between'>
<div className='flex gap-1 items-center'>
<Profile
size={18}
color='black'
/>
<div>
مدیرعامل: مهرداد مظفری
</div>
</div>
<div className='flex gap-1 items-center'>
<Setting2
size={18}
color='black'
/>
<div>
صنعت : نفت و گاز
</div>
</div>
</div>
<div className='flex justify-between mt-4'>
<div className='flex gap-1 items-center'>
<CallCalling
size={18}
color='black'
/>
<div>
شماره تماس : ۰۸۶۳۲۳۴۵۶۷۸
</div>
</div>
<div className='flex gap-1 items-center'>
<MessageMinus
size={18}
color='black'
/>
<div>
شناسه ملی : ۱۲۳۴
</div>
</div>
</div>
<div className='flex justify-between mt-4'>
<div className='flex gap-1 items-center'>
<Sms
size={18}
color='black'
/>
<div>
ایمیل : info@example.com
</div>
</div>
<div className='flex gap-1 items-center'>
<Calendar
size={18}
color='black'
/>
<div>
تاریخ تاسیس : ۱۳۸۹
</div>
</div>
</div>
<div className='flex gap-2 mt-4'>
<Location
size={18}
color='black'
/>
<div>
آدرس : لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
</div>
</div>
</div>
</div>
<div className='mt-6 flex justify-center gap-2 items-center'>
<div className='flex h-8 bg-white px-6 items-center rounded-full text-xs border-2 border-white pt-[1px]'>
محصولات
</div>
<div className='flex h-8 bg-white px-6 items-center rounded-full text-xs border-2 border-white bg-opacity-45 pt-[1px]'>
خدمات
</div>
<div className='flex h-8 bg-white px-6 items-center rounded-full text-xs border-2 border-white bg-opacity-45 pt-[1px]'>
گالری
</div>
</div>
<div className='flex gap-2 flex-wrap mt-6'>
<div className='flex-1 gap-2.5 bg-white min-w-[28%] h-20 flex items-center p-2 rounded-xl'>
<div className='size-12 rounded-xl bg-red-100'></div>
<div className='text-xs'>
<div>
شیر صنعتی
</div>
</div>
</div>
<div className='flex-1 gap-2.5 bg-white min-w-[28%] h-20 flex items-center p-2 rounded-xl'>
<div className='size-12 rounded-xl bg-red-100'></div>
<div className='text-xs'>
<div>
شیر صنعتی
</div>
</div>
</div>
<div className='flex-1 gap-2.5 bg-white min-w-[28%] h-20 flex items-center p-2 rounded-xl'>
<div className='size-12 rounded-xl bg-red-100'></div>
<div className='text-xs'>
<div>
شیر صنعتی
</div>
</div>
</div>
</div>
</div>
)
}
export default CompanyDetail
+134
View File
@@ -0,0 +1,134 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '../../components/Input'
import Textarea from '../../components/Textarea'
import UploadBox from '../../components/UploadBox'
import Button from '../../components/Button'
import Select from '../../components/Select'
import { TickSquare } from 'iconsax-react'
const AddCriticisms: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-4 text-sm'>
<div>
{t('notif.add_notification')}
</div>
<div className='flex gap-6 xl:mt-8 mt-5'>
<div className='flex-1 bg-white py-8 xl:px-10 px-5 rounded-3xl'>
<div>
{t('criticisms.submit_your_criticisms')}
</div>
<div className='mt-3 text-xs text-description'>
{t('criticisms.description_criticisms')}
</div>
<div className='mt-10'>
<Input
label={t('notif.title')}
placeholder={t('criticisms.enter_your_subject')}
/>
</div>
<div className='mt-7'>
<Select
items={[
{ value: '1', label: 'گروه' },
{ value: '2', label: 'فعال' },
{ value: '3', label: 'غیر فعال' },
]}
/>
</div>
<div className='mt-7'>
<Select
items={[
{ value: '1', label: 'انتخاب زمان انتشار' },
{ value: '2', label: 'فعال' },
{ value: '3', label: 'غیر فعال' },
]}
/>
</div>
<div className='mt-7'>
<Textarea
label={t('notif.group')}
placeholder={t('criticisms.enter_description')}
/>
</div>
<div className='mt-7'>
<UploadBox
label={t('attachment')}
/>
</div>
<div className='mt-20 flex justify-end'>
<Button
label={t('ticket.send')}
className='max-w-[100px]'
/>
</div>
</div>
<div className='bg-white w-sidebar hidden xl:block py-10 px-5 h-fit rounded-3xl'>
<div className='text-sm'>
{t('ticket.title_hint')}
</div>
<div className='mt-6'>
<div className='flex items-start gap-2 border-b pb-5'>
<div>
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
</div>
</div>
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
<div>
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
</div>
</div>
<div className='flex items-start gap-2 mt-6'>
<div>
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default AddCriticisms
+85
View File
@@ -0,0 +1,85 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Td from '../../components/Td'
import Select from '../../components/Select'
import Input from '../../components/Input'
import Button from '../../components/Button'
import downloadIcon from '../../assets/images/download.svg'
const Records: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-4 min-h-[500px]'>
<div className='flex justify-between items-center'>
<div>
{t('grade-records.all-records')}
</div>
</div>
<div>
<div className='flex justify-between items-end gap-2 xl:items-end'>
<div className='mt-6 xl:mt-0 max-w-[420px] xl:w-[300px] w-full'>
<Input
variant='search'
placeholder={t('grade-records.search')}
className='bg-white w-full'
/>
</div>
<div className='flex items-center gap-4'>
<div className='w-[100px] xl:w-[200px]'>
<Select
items={[
{
label: 'همه',
value: 'active'
}
]}
className='bg-white border'
/>
</div>
</div>
</div>
</div>
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
<table className='w-full text-sm'>
<thead className='thead'>
<tr>
<Td text={t('grade-records.title')} />
<Td text={t('grade-records.status')} />
<Td text=''>
<div className='max-w-[200px] flex justify-center'>
{t('grade-records.record')}
</div>
</Td>
</tr>
</thead>
<tbody>
<tr className='tr'>
<Td text={t('grade-records.title')} />
<Td text=''>
<div className='flex gap-1 !items-center'>
<div className='w-2 h-2 rounded-full bg-green-600'></div>
<div>{t('grade-records.status')}</div>
</div>
</Td>
<Td text=''>
<Button className='text-xs px-2 rounded-lg gap-2 max-w-[200px]' label='دانلود' icon={downloadIcon} />
</Td>
</tr>
<tr className='tr'>
<Td text={t('grade-records.title')} />
<Td text=''>
<div className='flex gap-1 !items-center'>
<div className='w-2 h-2 rounded-full bg-yellow-600'></div>
<div>{t('grade-records.status')}</div>
</div>
</Td>
<Td text=''>
<Button className='text-xs px-2 rounded-lg gap-2 max-w-[200px]' label='دانلود' icon={downloadIcon} />
</Td>
</tr>
</tbody>
</table>
</div>
</div>
)
}
export default Records
+171
View File
@@ -0,0 +1,171 @@
import { Building, InfoCircle } from 'iconsax-react'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { clx } from '../../helpers/utils'
import StatusWithText from '../../components/StatusWithText'
import TitleSeeAll from '../../components/TitleSeeAll'
const Home: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-4'>
<div className='w-full h-10 flex gap-2 items-center px-4 rounded-xl bg-[#FCE7CE] text-[#FF7B00]'>
<InfoCircle
size={24}
color='#FF7B00'
/>
<div className='text-xs'>
درخواست شما برای ثبت شرکت، هنوز تایید نشده است
</div>
</div>
<div className='mt-6'>
<TitleSeeAll
title={t('home.last_announcement')}
link='/'
/>
</div>
<div className='mt-4'>
<div className={clx(
'p-4 rounded-2xl',
'bg-[#F8F9FB]'
)}>
<div className='flex justify-between'>
<div className='leading-6 text-xs xl:text-[12.5px] max-w-[70%]'>
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
</div>
<div className='flex gap-1'>
<StatusWithText
variant='error'
text={t('announcement.important')}
/>
<StatusWithText
variant='success'
text={t('announcement.new')}
/>
</div>
</div>
<div className='m-2 flex justify-between items-center text-[11px]'>
<div className='max-w-[70%] truncate text-description'>
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
</div>
<div className='text-description'>
۱۴۰۳/۰۹/۲۴
</div>
</div>
</div>
</div>
<div className='mt-6'>
<TitleSeeAll
title={t('home.companies')}
link='/'
/>
</div>
<div className='flex gap-2 mt-4 overflowX'>
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
<Building
size={20}
color='black'
/>
<div className='text-sm'>
همه
</div>
</div>
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
<Building
size={20}
color='black'
/>
<div className='text-sm'>
همه
</div>
</div>
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
<Building
size={20}
color='black'
/>
<div className='text-sm'>
همه
</div>
</div>
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
<Building
size={20}
color='black'
/>
<div className='text-sm'>
همه
</div>
</div>
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
<Building
size={20}
color='black'
/>
<div className='text-sm'>
همه
</div>
</div>
</div>
<div className='mt-6 flex gap-4 flex-wrap'>
<div className='flex-1 gap-2.5 bg-white min-w-[40%] h-20 flex items-center p-2 rounded-xl'>
<div className='size-[64px] rounded-xl bg-red-100'></div>
<div className='text-xs'>
<div>
شرکت
</div>
<div className='text-description mt-2'>
شهرک شماره ۱
</div>
</div>
</div>
<div className='flex-1 gap-2.5 bg-white min-w-[40%] h-20 flex items-center p-2 rounded-xl'>
<div className='size-[64px] rounded-xl bg-red-100'></div>
<div className='text-xs'>
<div>
هنرمند
</div>
<div className='text-description mt-2'>
درامز
</div>
</div>
</div>
<div className='flex-1 gap-2.5 bg-white min-w-[40%] h-20 flex items-center p-2 rounded-xl'>
<div className='size-[64px] rounded-xl bg-red-100'></div>
<div className='text-xs'>
<div>
هنرمند
</div>
<div className='text-description mt-2'>
درامز
</div>
</div>
</div>
<div className='flex-1 gap-2.5 bg-white min-w-[40%] h-20 flex items-center p-2 rounded-xl'>
<div className='size-[64px] rounded-xl bg-red-100'></div>
<div className='text-xs'>
<div>
هنرمند
</div>
<div className='text-description mt-2'>
درامز
</div>
</div>
</div>
</div>
</div>
)
}
export default Home
+85
View File
@@ -0,0 +1,85 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Select from '../../components/Select'
import Input from '../../components/Input'
import LevelingCart from '../../components/LevelingCard'
import germanyFly from '../../assets/images/germany.png'
import CourseCard from '../../components/CourseCard'
import book from '../../assets/images/books.jpg'
const AdmitCard: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-4 min-h-[500px]'>
<div className='flex justify-between items-center'>
<div>
{t('myCourses.myCourses')}
</div>
</div>
<div>
<div className='flex justify-between items-end gap-2 xl:items-end'>
<div className='mt-6 xl:mt-0 max-w-[420px] xl:w-[300px] w-full'>
<Input
variant='search'
placeholder={t('myCourses.search')}
className='bg-white w-full'
/>
</div>
<div className='flex items-center gap-4'>
<div className='w-[100px] xl:w-[200px]'>
<Select
items={[
{
label: 'همه',
value: 'active'
}
]}
className='bg-white border'
/>
</div>
</div>
</div>
</div>
<div className='flex overflow-auto gap-4 mt-8 hide-scroll-bar'>
<LevelingCart
image={germanyFly}
title='سطح A1'
/>
</div>
<div className='mt-6 pb-[120px]'>
<h2>{t('myCourses.myCourses')}</h2>
<div className='flex flex-wrap gap-2 mt-2'>
<div className='min-w-[40%] flex-1 lg:min-w-[175px]'>
<CourseCard
image={book}
teacher='حمید ضرغامی'
time='20'
price='1.000.000'
title='دوره 504 واژه'
/>
</div>
<div className='min-w-[40%] flex-1 lg:min-w-[175px]'>
<CourseCard
image={book}
teacher='حمید ضرغامی'
time='20'
price='1.000.000'
title='دوره 504 واژه'
/>
</div>
<div className='min-w-[40%] flex-1 lg:min-w-[175px]'>
<CourseCard
image={book}
teacher='حمید ضرغامی'
time='20'
price='1.000.000'
title='دوره 504 واژه'
/>
</div>
<div className='min-w-[40%] flex-1 lg:min-w-[175px]'>
</div>
</div>
</div>
</div>
)
}
export default AdmitCard
+113
View File
@@ -0,0 +1,113 @@
import { Notification } from 'iconsax-react'
import { FC, useState } from 'react'
import XIcon from '../../assets/images/close-circle.svg'
import { useTranslation } from 'react-i18next'
import { clx } from '../../helpers/utils'
import StatusCircle from '../../components/StatusCircle'
import { useOutsideClick } from '../../hooks/useOutSideClick'
const Notifications: FC = () => {
const { t } = useTranslation('global')
const ref = useOutsideClick(() => setShowModal(false))
const [activeTab, setActiveTab] = useState<'all' | 'read' | 'unread'>('all')
const [showModal, setShowModal] = useState<boolean>(false)
return (
<div ref={ref}>
<Notification onClick={() => setShowModal(!showModal)} size={18} color='black' />
{
showModal && (
<div className='xl:h-[calc(100%-110px)] h-[70%] p-6 z-50 xl:w-[300px] w-full xl:left-7 left-0 xl:top-[90px] top-0 overflow-y-auto xl:rounded-3xl rounded-b-3xl rounded-t-none fixed modalGlass3 '>
<div className='flex justify-between items-center'>
<div>
{t('notif.natification')}
</div>
<div className='size-7 rounded-full bg-white bg-opacity-35 flex justify-center items-center'>
<img src={XIcon} alt='close' className='w-4 h-4' onClick={() => setShowModal(false)} />
</div>
</div>
<div className='mt-6 flex h-8 border border-white border-opacity-35 text-xs rounded-lg overflow-hidden'>
<div onClick={() => setActiveTab('all')} className={clx(
'flex-1 border-l cursor-pointer text-white border-white border-opacity-70 flex justify-center items-center',
activeTab === 'all' ? 'bg-white bg-opacity-70 text-black' : ''
)}>
همه
</div>
<div onClick={() => setActiveTab('unread')} className={clx(
'flex-1 border-l cursor-pointer text-white border-white border-opacity-70 bg-transparent flex justify-center items-center',
activeTab === 'unread' ? 'bg-white bg-opacity-70 text-black' : ''
)}>
خوانده نشده
</div>
<div onClick={() => setActiveTab('read')} className={clx(
'flex-1 cursor-pointer text-white border-white bg-transparent flex justify-center items-center',
activeTab === 'read' ? 'bg-white bg-opacity-70 text-black' : ''
)}>
خوانده شده
</div>
</div>
<div className='mt-6 flex flex-col gap-4 text-xs'>
<div className='bg-white h-fit gap-4 items-start rounded-xl bg-opacity-60 p-3 flex'>
<div className='mt-1'>
<StatusCircle color='#00BA4B' />
</div>
<div className=''>
<div className='truncate'>
لورم ایپسوم متن ساختگی با تولید سادگی
</div>
<div className='mt-2 flex gap-2 text-[10px] text-description'>
<div>۲ ساعت پیش</div>
<div className='flex gap-1 items-center'>
<StatusCircle color='#8C90A3' />
<div>دیزاین</div>
</div>
</div>
</div>
</div>
<div className='bg-white h-fit gap-4 items-start rounded-xl bg-opacity-60 p-3 flex'>
<div className='mt-1'>
<StatusCircle color='#00BA4B' />
</div>
<div className=''>
<div className='truncate'>
لورم ایپسوم متن ساختگی با تولید سادگی
</div>
<div className='mt-2 flex gap-2 text-[10px] text-description'>
<div>۲ ساعت پیش</div>
<div className='flex gap-1 items-center'>
<StatusCircle color='#8C90A3' />
<div>دیزاین</div>
</div>
</div>
</div>
</div>
<div className='bg-white h-fit gap-4 items-start rounded-xl bg-opacity-60 p-3 flex'>
<div className='mt-1'>
<StatusCircle color='#00BA4B' />
</div>
<div className=''>
<div className='truncate'>
لورم ایپسوم متن ساختگی با تولید سادگی
</div>
<div className='mt-2 flex gap-2 text-[10px] text-description'>
<div>۲ ساعت پیش</div>
<div className='flex gap-1 items-center'>
<StatusCircle color='#8C90A3' />
<div>دیزاین</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
</div>
)
}
export default Notifications
+102
View File
@@ -0,0 +1,102 @@
import { useState } from 'react'
import book from '../../assets/images/books.jpg'
import { useTranslation } from 'react-i18next'
import Tabs from '../../components/Tabs'
import { MoneyTick, MoneyTime } from 'iconsax-react'
import Input from '../../components/Input'
import Button from '../../components/Button'
import RadioGroup from '../../components/RadioGroup'
import bank from '../../assets/images/bank.png'
const PaymentDetails = () => {
const [activeTab, setActiveTab] = useState<string>('unpaid')
const { t } = useTranslation('global')
return (
<div className='pb-[120px]'>
<div className="rounded-[32px] bg-white py-6 flex items-center gap-4 px-6">
<img src={book} className='w-[95px] h-[95px] rounded-2xl' alt="" />
<div className='space-y-2'>
<h2 className='text-[14px]'>دوره مقدماتی</h2>
<div className='space-y-[2px]'>
<p className='text-[10px] text-description'>1 شنبه - 2 شنبه، ساعت 2 تا 4 عصر</p>
<div className='flex items-center gap-1 text-[13px]'>
<p className='text-description'>پیش پرداخت :</p>
<p>500,000 تومان</p>
</div>
<div className='flex items-center gap-1 text-[13px]'>
<p className='text-description'>کل مبلغ دوره :</p>
<p>500,000 تومان</p>
</div>
</div>
</div>
</div>
<div className='mt-6 w-full'>
<Tabs
isFullWidth
active={activeTab}
items={[
{
icon: <MoneyTime color={activeTab === 'unpaid' ? 'black' : '#8C90A3'} size={22} />,
label: t('payment.prePay'),
value: 'unpaid'
},
{
icon: <MoneyTick color={activeTab === 'not_confimed' ? 'black' : '#8C90A3'} size={22} />,
label: t('payment.fullPay'),
value: 'not_confimed'
}
]}
onChange={setActiveTab}
/>
</div>
<div className="rounded-[32px] bg-white py-6 mt-6 gap-4 px-6">
<h2 className='text-[14px]'>{t('payment.fullPay')}</h2>
<div className='flex items-end mt-4 gap-2 justify-between'>
<Input placeholder='کد تخفیف' />
<Button className='bg-white text-xs text-black border border-black max-w-[80px]' label='اعمال' />
</div>
<div className='flex items-center text-xs gap-3 my-6'>
<p className='text-description'>مبلغ قابل پرداخت</p>
<div className='border-b border-dashed border-description flex-1'></div>
<p>1,000,000 تومان</p>
</div>
<div className='flex items-center gap-2'>
<RadioGroup
items={[
{
label: t('استفاده از موجودی کیف پول'),
value: '3month'
},
{
label: t('پرداخت مستقیم'),
value: 'year'
}
]}
onChange={() => ''}
selected={''}
/>
</div>
<div className='text-xs gap-3 my-6'>
<h2 className='text-[14px]'>روش پرداخت</h2>
<div className='grid grid-cols-2 gap-2 mt-4'>
<div className='border border-black rounded-2xl flex items-center justify-center'>
<div className='text-center flex flex-col items-center space-y-1 py-4'>
<img src={bank} className='h-[50px] w-[50px]' alt="" />
<p>بانک پارسیان</p>
<p className='text-description text-[10px]'>تمام کارت های عضو شتاب</p>
</div>
</div>
<div className='border border-black rounded-2xl flex items-center justify-center'>
<div className='text-center flex flex-col items-center space-y-1 py-4'>
<img src={bank} className='h-[50px] w-[50px]' alt="" />
<p>بانک پارسیان</p>
<p className='text-description text-[10px]'>تمام کارت های عضو شتاب</p>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default PaymentDetails
+174
View File
@@ -0,0 +1,174 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import AvatarImage from '../../assets/images/avatar_image.png'
import Button from '../../components/Button'
import { DocumentUpload, Edit } from 'iconsax-react'
import Input from '../../components/Input'
import DatePickerComponent from '../../components/DatePicker'
import Select from '../../components/Select'
import Textarea from '../../components/Textarea'
import { useGetProfile } from './hooks/useProfileData'
import Username from './components/Username'
const Profile: FC = () => {
const { t } = useTranslation('global')
const getProfile = useGetProfile()
return (
<div className='mt-4 '>
<div>
{t('profile.account_user')}
</div>
<div className='bg-white rounded-3xl xl:p-6 p-4 mt-8 text-sm'>
<div className='xl:mt-10 mt-4 flex xl:flex-row flex-col xl:gap-0 gap-7 xl:items-center border-b pb-7'>
<div className='flex-1'>
<div>
{t('profile.image_profile')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.image_profile_desc')}
</div>
</div>
<div className='flex-1 flex xl:gap-6 gap-4'>
<img src={AvatarImage} className='xl:size-20 size-14 rounded-full object-cover' />
<div>
<Button
className='xl:w-[160px] w-fit px-4 xl:;px-0 h-8 xl:h-10 text-xs xl:text-sm'
>
<div className='flex items-center gap-3'>
<DocumentUpload color='white' size={18} />
<div>{t('profile.upload_image')}</div>
</div>
</Button>
<p className='mt-3 text-description'>
{t('profile.format_image')}
</p>
</div>
</div>
</div>
<div className='mt-8 flex xl:flex-row flex-col xl:gap-0 gap-7 border-b pb-7'>
<div className='flex-1'>
<div>
{t('profile.info_account')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.auth_after_change_info')}
</div>
</div>
<div className='flex-1 '>
<Username
username={getProfile.data?.data?.user?.userName}
/>
<div className='flex items-end xl:gap-6 gap-3 xl:mt-7 mt-4'>
<Input
label={t('email')}
value={getProfile.data?.data?.user?.email}
readOnly
/>
<div className='flex relative -top-3 gap-1 text-[#0047FF] text-xs items-center'>
<Edit className='xl:size-[18px] size-4' color='#0047FF' />
<div>
{t('edit')}
</div>
</div>
</div>
<div className='flex items-end xl:gap-6 gap-3 xl:mt-7 mt-4'>
<Input
label={t('profile.phone_call')}
value={getProfile.data?.data?.user?.phone}
readOnly
/>
<div className='flex relative -top-3 gap-1 text-[#0047FF] text-xs items-center'>
<Edit className='xl:size-[18px] size-4' color='#0047FF' />
<div>
{t('edit')}
</div>
</div>
</div>
</div>
</div>
<div className='mt-8 flex xl:flex-row flex-col xl:gap-0 gap-7 border-b pb-7'>
<div className='flex-1'>
<div>
{t('profile.personal_information')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.enter_carefully_your_information')}
</div>
</div>
<div className='flex-1'>
<DatePickerComponent
label={t('profile.date_of_birth')}
onChange={() => { }}
placeholder=''
defaulValue='1400-01-01'
/>
<div className='xl:mt-7 mt-4'>
<Input
label={t('profile.national_code')}
value={'۰۰۱۲۳۴۵۳۴۴'}
/>
</div>
</div>
</div>
<div className='mt-8 flex xl:flex-row flex-col xl:gap-0 gap-7'>
<div className='flex-1'>
<div>
{t('profile.address')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.address_live')}
</div>
</div>
<div className='flex-1'>
<div className='rowTwoInput'>
<Select
label={t('profile.province')}
items={[
{
label: 'تهران',
value: 'tehran'
}
]}
className='bg-white border'
/>
<Select
label={t('profile.city')}
items={[
{
label: 'تهران',
value: 'tehran'
}
]}
/>
</div>
<div className='xl:mt-7 mt-4'>
<Input
label={t('profile.postal_code')}
value={'۱۲۳۴۵۶۷۸'}
/>
</div>
<div className='xl:mt-7 mt-4'>
<Textarea
label={t('profile.address')}
value={'للورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک استورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است'}
/>
</div>
</div>
</div>
<div className='h-20 xl:hidden'></div>
</div>
</div>
)
}
export default Profile
+132
View File
@@ -0,0 +1,132 @@
import { FC, useEffect, useState } from 'react'
import Input from '../../../components/Input'
import { Edit } from 'iconsax-react'
import { useTranslation } from 'react-i18next'
import Button from '../../../components/Button'
import { useCheckUserName, useUpdateProfile } from '../hooks/useProfileData'
import { CheckUserNameType, UpdateProfileType } from '../types/ProfileTypes'
import { toast } from 'react-toastify'
import { ErrorType } from '../../../helpers/types'
type Props = {
username: string | null,
}
const Username: FC<Props> = (props: Props) => {
const { t } = useTranslation('global')
const [username, setUsername] = useState<string>(props.username ? props.username : '')
const [canSave, setCanSave] = useState<boolean>(false)
const [canEdit, setCanEdit] = useState<boolean>(false)
const [status, setStatus] = useState<'success' | 'error' | ''>('')
const checkUsername = useCheckUserName()
const updateProfile = useUpdateProfile()
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
setCanSave(false)
if (value.indexOf(' ') === -1) {
if (value.length <= 3) {
setUsername(value)
return
}
setStatus('')
const params: CheckUserNameType = {
value: e.target.value,
type: 'userName'
}
setUsername(e.target.value)
checkUsername.mutate(params, {
onSuccess() {
setStatus('success')
setCanSave(true)
},
onError() {
setStatus('error')
}
})
}
}
const handleSave = () => {
const params: UpdateProfileType = {
userName: username
}
updateProfile.mutate(params, {
onSuccess: () => {
toast.success(t('profile.username_save_success'))
setCanEdit(false)
setCanSave(false)
setStatus('')
},
onError(error: ErrorType) {
toast.error(error.response?.data?.error?.message?.[0])
},
})
}
useEffect(() => {
if (username !== props.username) {
setUsername(props.username ? props.username : '')
}
}, [props.username])
return (
<>
<div className='flex items-end xl:gap-6 gap-3'>
<div className='relative w-full'>
<Input
label={t('profile.username')}
value={username}
readOnly={!canEdit}
onChange={handleChange}
minLength={3}
maxLength={20}
/>
{
checkUsername.isPending &&
<div className='absolute left-4 top-8'>
<div className='loader'></div>
</div>
}
</div>
<div className='flex relative gap-1 text-[#0047FF] text-xs items-center'>
{
!canEdit ?
<div onClick={() => setCanEdit(true)} className='flex gap-1 cursor-pointer relative -top-3'>
<Edit className='xl:size-[18px] size-4' color='#0047FF' />
<div>
{t('edit')}
</div>
</div>
:
<Button
label={t('save')}
onClick={handleSave}
className='px-4'
disabled={!canSave}
isLoading={updateProfile.isPending}
/>
}
</div>
</div>
{
status === 'success' ?
<div className='text-xs text-green-500 mt-2'>
{t('profile.username_available')}
</div>
:
status === 'error' ?
<div className='text-xs text-red-500 mt-2'>
{t('profile.username_not_available')}
</div>
: null
}
</>
)
}
export default Username
@@ -0,0 +1,22 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import * as api from '../service/ProfileService'
import { CheckUserNameType, UpdateProfileType } from '../types/ProfileTypes';
export const useGetProfile = () => {
return useQuery({
queryKey: ["profile"],
queryFn: () => api.getProfile(),
});
};
export const useCheckUserName = () => {
return useMutation({
mutationFn: (variables: CheckUserNameType) => api.checkUserName(variables),
});
};
export const useUpdateProfile = () => {
return useMutation({
mutationFn: (variables: UpdateProfileType) => api.updateProfile(variables),
});
};
@@ -0,0 +1,17 @@
import axios from "../../../config/axios";
import { CheckUserNameType, UpdateProfileType } from "../types/ProfileTypes";
export const getProfile = async () => {
const { data } = await axios.get(`/users/me`);
return data;
};
export const checkUserName = async (params: CheckUserNameType) => {
const { data } = await axios.post(`/users/check-validity`, params);
return data;
};
export const updateProfile = async (params: UpdateProfileType) => {
const { data } = await axios.patch(`/users/update-profile`, params);
return data;
};
+11
View File
@@ -0,0 +1,11 @@
export type CheckUserNameType = {
type: "userName";
value: string;
};
export type UpdateProfileType = {
firstName?: string;
lastName?: string;
birthDate?: string;
userName?: string;
};
+258
View File
@@ -0,0 +1,258 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Edit } from 'iconsax-react'
import StatusCircle from '../../components/StatusCircle'
import SamanImage from '../../assets/images/saman.png'
const ReceiptsDetail: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-4 pb-20'>
<div>
{t('receip.detail_account')}
۱۲۳۱۲
</div>
<div className='flex xl:flex-row flex-col mt-8 gap-6'>
<div className='flex-1'>
<div className='w-full bg-white rounded-3xl p-8'>
<div className='flex justify-between items-center'>
<div>
{t('receip.personal_information')}
</div>
<div className='flex gap-1 items-center text-xs text-[#0047FF]'>
<Edit size={16} color='#0047FF' />
<div>
{t('edit')}
</div>
</div>
</div>
<div className='mt-8 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.type_person')}
</div>
<div>
{t('receip.company')}
</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.representativeـname')}
</div>
<div>
مهرداد مظفری
</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.company_name')}
</div>
<div>
داناک
</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.registration_number')}
</div>
<div>
۱۲۳۴
</div>
</div>
</div>
<div className='mt-8 border-t pt-7 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.tel_company')}
</div>
<div>
۰۰۱۲۳۴۵۶۷۸
</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.national_code')}
</div>
<div>
۰۰۱۲۳۴۵۶۷۸
</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.postal_code')}
</div>
<div>
۳۸۲۱۳۵۸۸۹۲
</div>
</div>
</div>
<div className='mt-6 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.state')}
</div>
<div>
مرکزی
</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.state')}
</div>
<div>
مرکزی
</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.city')}
</div>
<div>
اراک
</div>
</div>
<div className='flex gap-2'>
<div className='text-description'>
{t('receip.address')}
</div>
<div>
اراک -خیابان شریعتی-خیابان جنت
</div>
</div>
</div>
</div>
<div className='w-full bg-white rounded-3xl mt-8 p-8'>
<div className='flex justify-between items-center'>
<div>
{t('receip.detail_receip')}
</div>
<div className='flex gap-2 text-xs items-center'>
<StatusCircle
color='#FF7B00'
/>
<div>
{t('receip.not_paid')}
</div>
</div>
</div>
<div className='mt-7 text-sm'>
<div className='flex justify-between items-center text-description h-14'>
<div>
{t('receip.description')}
</div>
<div>
{t('receip.price')}
</div>
</div>
<div className='flex justify-between items-center text-xs border-t border-[#EAEDF5] h-14'>
<div>
اکانت یکساله با دسترسی
</div>
<div>
۱۰,۰۰۰,۰۰۰ ریال
</div>
</div>
<div className='flex justify-between items-center text-xs border-t border-[#EAEDF5] h-14'>
<div>
۱۰٪ ارزش افزوده
</div>
<div>
۱۰,۰۰۰,۰۰۰ ریال
</div>
</div>
<div className='flex justify-between items-center text-xs border-t border-[#EAEDF5] h-14'>
<div>
جمع کل
</div>
<div>
۱۰,۰۰۰,۰۰۰ ریال
</div>
</div>
</div>
</div>
</div>
<div className='bg-white xl:w-sidebar text-sm h-fit rounded-3xl p-8'>
<div className='flex justify-between items-center'>
<div>
{t('receip.receip_information')}
</div>
<div className='flex gap-2 text-xs items-center'>
<StatusCircle
color='#FF7B00'
/>
<div>پرداخت نشده</div>
</div>
</div>
<div className='mt-6 text-xs font-extralight'>
<div className='flex justify-between h-12 border-b items-center'>
<div className='text-description'>
{t('receip.date_factor')}
</div>
<div>
۱۴۰۲/۱۰/۰۴
</div>
</div>
<div className='flex justify-between h-12 border-b items-center'>
<div className='text-description'>
{t('receip.due_date')}
</div>
<div>
۱۴۰۲/۱۰/۰۴
</div>
</div>
<div className='flex justify-between h-12 border-b items-center'>
<div className='text-description'>
{t('receip.factor_number')}
</div>
<div>
۱۲۳۱
</div>
</div>
<div className='flex justify-between h-12 border-b items-center'>
<div className='text-description'>
{t('receip.status_factor')}
</div>
<div>
تایید شده
</div>
</div>
</div>
<div className='mt-5 flex justify-center gap-2'>
<div className='border size-[72px] flex-col text-[10px] justify-center items-center rounded-xl border-border flex'>
<img src={SamanImage} alt='saman' className='w-8 h-8' />
<div className='mt-2'>
بانک سامان
</div>
</div>
<div className='border size-[72px] flex-col text-[10px] justify-center items-center rounded-xl border-border flex'>
<img src={SamanImage} alt='saman' className='w-8 h-8' />
<div className='mt-2'>
بانک سامان
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default ReceiptsDetail
+112
View File
@@ -0,0 +1,112 @@
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Tabs from '../../components/Tabs'
import { Calendar, CloseCircle, Eye, FolderOpen, MoneyChange, WalletCheck, WalletRemove, WalletSearch } from 'iconsax-react'
import Td from '../../components/Td'
import { Link } from 'react-router-dom'
import { Pages } from '../../config/Pages'
const ReceiptsList: FC = () => {
const { t } = useTranslation('global')
const [activeTab, setActiveTab] = useState<string>('unpaid')
return (
<div className='flex justify-center'>
<div className='mt-4 w-full max-w-[1200px]'>
<div>
{t('receip.Language_learners_list')}
</div>
<div className='mt-14'>
<Tabs
active={activeTab}
items={[
{
icon: <WalletRemove color={activeTab === 'unpaid' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.dont_pay'),
value: 'unpaid'
},
{
icon: <WalletSearch color={activeTab === 'not_confimed' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.dont_confrim'),
value: 'not_confimed'
},
{
icon: <WalletCheck color={activeTab === 'paid' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.paid'),
value: 'paid'
},
{
icon: <CloseCircle color={activeTab === 'canceled' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.canceled'),
value: 'canceled'
},
{
icon: <MoneyChange color={activeTab === 'returned' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.returned'),
value: 'returned'
},
{
icon: <Calendar color={activeTab === 'time' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.its_time'),
value: 'time'
},
{
icon: <FolderOpen color={activeTab === 'archive' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.archive'),
value: 'archive'
},
]}
onChange={setActiveTab}
/>
</div>
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
<table className='w-full text-sm '>
<thead className='thead'>
<tr>
<Td text={t('ticket.number')} />
<Td text={t('ticket.title')} />
<Td text={t('ticket.team')} />
<Td text={t('ticket.date')} />
<Td text={t('ticket.status')} />
<Td text={t('ticket.priority')} />
<Td text={t('ticket.detail')} />
<Td text={''} />
</tr>
</thead>
<tbody>
<tr className='tr'>
<Td text={t('ticket.number')} />
<Td text={t('ticket.title')} />
<Td text={t('ticket.team')} />
<Td text={t('ticket.date')} />
<Td text={t('ticket.status')} />
<Td text={t('ticket.priority')} />
<Td text={t('ticket.detail')} />
<Td text={''}>
<Link to={Pages.receipts.detail + '1'}>
<Eye size={20} color='black' />
</Link>
</Td>
</tr>
<tr className='tr'>
<Td text={t('ticket.number')} />
<Td text={t('ticket.title')} />
<Td text={t('ticket.team')} />
<Td text={t('ticket.date')} />
<Td text={t('ticket.status')} />
<Td text={t('ticket.priority')} />
<Td text={t('ticket.detail')} />
<Td text={''} />
</tr>
</tbody>
</table>
</div>
</div>
</div>
)
}
export default ReceiptsList
+243
View File
@@ -0,0 +1,243 @@
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '../../components/Input'
import Select from '../../components/Select'
import Textarea from '../../components/Textarea'
import UploadBox from '../../components/UploadBox'
import Button from '../../components/Button'
import { TickSquare } from 'iconsax-react'
import { useCreateTicket, useGetCategoriesTicket, useMultiUpload } from './hooks/useTicketData'
import { useGetProfile } from '../profile/hooks/useProfileData'
import PageLoading from '../../components/PageLoading'
import { useFormik } from 'formik'
import { CreateTicketType } from './types/TicketTypes'
import * as Yup from 'yup'
import { toast } from 'react-toastify'
import { ErrorType } from '../../helpers/types'
import { useNavigate } from 'react-router-dom'
import { Pages } from '../../config/Pages'
const CreateTicket: FC = () => {
const { t } = useTranslation('global')
const navigate = useNavigate()
const [files, setFiles] = useState<File[]>([])
const getProfile = useGetProfile()
const getCategories = useGetCategoriesTicket()
const multiUpload = useMultiUpload()
const createTicket = useCreateTicket()
const formik = useFormik<CreateTicketType>({
initialValues: {
subject: '',
message: '',
categoryId: '',
priority: 'LOW',
danakServiceId: '',
title: '',
attachmentUrls: []
},
validationSchema: Yup.object({
subject: Yup.string().required(t('errors.required')),
message: Yup.string().required(t('errors.required')),
categoryId: Yup.string().required(t('errors.required')),
priority: Yup.string().required(t('errors.required')),
}),
onSubmit: async (values) => {
if (files.length > 0) {
const images = new FormData()
files.forEach(file => {
images.append('files', file)
})
await multiUpload.mutateAsync(images, {
onSuccess: async (data) => {
values.attachmentUrls = await data.data.map((item: { url: string }) => item?.url)
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
} else {
delete values.attachmentUrls
}
createTicket.mutate(values, {
onSuccess: (data) => {
toast.success(t('success'))
formik.resetForm()
navigate(Pages.ticket.detail + data.data?.ticketId)
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
}
})
console.log(formik.errors);
return (
<div className='mt-4'>
<div>
{t('ticket.new_ticket')}
</div>
{
getProfile.isPending || getCategories.isPending ?
<PageLoading />
:
<div className='flex gap-6 xl:mt-8 mt-4'>
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
<div>
{t('ticket.submit_your_message')}
</div>
<div className='mt-6 rowTwoInput'>
<Input
label={t('ticket.name')}
readOnly
placeholder={getProfile?.data?.data?.user?.firstName + ' ' + getProfile?.data?.data?.user?.lastName}
/>
<Input
label={t('ticket.email')}
readOnly
placeholder={getProfile?.data?.data?.user?.email}
/>
</div>
<div className='mt-6 rowTwoInput'>
<Input
label={t('ticket.subject')}
placeholder={t('ticket.enter_your_subject')}
name='subject'
value={formik.values.subject}
onChange={formik.handleChange}
error_text={formik.touched.subject && formik.errors.subject ? formik.errors.subject : ''}
/>
<Select
label={t('ticket.priority')}
items={[
{
label: t('ticket.low'),
value: 'LOW'
},
{
label: t('ticket.medium'),
value: 'MEDIUM'
},
{
label: t('ticket.high'),
value: 'HIGH'
}
]}
placeholder={t('select')}
className='border'
name='priority'
value={formik.values.priority}
onChange={formik.handleChange}
error_text={formik.touched.priority && formik.errors.priority ? formik.errors.priority : ''}
/>
</div>
<div className='mt-6 rowTwoInput'>
<Select
label={t('ticket.select_your_service')}
items={[
{
label: '',
value: ''
}
]}
placeholder={t('ticket.not_once')}
className='border'
name='danakServiceId'
/>
<Select
label={t('ticket.category')}
items={getCategories.data?.data?.ticketCategories?.map((item: { id: string, title: string }) => ({
label: item.title,
value: item.id
})
)}
placeholder={t('select')}
className='border'
name='categoryId'
value={formik.values.categoryId}
onChange={formik.handleChange}
error_text={formik.touched.categoryId && formik.errors.categoryId ? formik.errors.categoryId : ''}
/>
</div>
<div className='mt-6'>
<Textarea
label={t('description')}
placeholder={t('ticket.your_description')}
name='message'
value={formik.values.message}
onChange={formik.handleChange}
error_text={formik.touched.message && formik.errors.message ? formik.errors.message : ''}
/>
</div>
<div className='mt-6'>
<UploadBox
label={t('ticket.attachment')}
isMultiple
onChange={setFiles}
/>
</div>
<div className='mt-8 flex justify-end'>
<Button
label={t('ticket.send')}
className='max-w-[100px]'
onClick={() => formik.handleSubmit()}
isLoading={createTicket.isPending || multiUpload.isPending}
/>
</div>
</div>
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
<div className='text-sm'>
{t('ticket.title_hint')}
</div>
<div className='mt-6'>
<div className='flex items-start gap-2 border-b pb-5'>
<div>
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
</div>
</div>
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
<div>
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
</div>
</div>
<div className='flex items-start gap-2 mt-6'>
<div>
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
</div>
</div>
</div>
</div>
</div>
}
</div>
)
}
export default CreateTicket
+309
View File
@@ -0,0 +1,309 @@
import { FC, Fragment, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '../../components/Input'
import Textarea from '../../components/Textarea'
import UploadBox from '../../components/UploadBox'
import Button from '../../components/Button'
import { CloseCircle, InfoCircle, Paperclip2 } from 'iconsax-react'
import { useNavigate, useParams } from 'react-router-dom'
import { useAddMessageTicket, useCloseTicket, useGetMessages, useMultiUpload } from './hooks/useTicketData'
import PageLoading from '../../components/PageLoading'
import moment from 'moment-jalaali'
import { useFormik } from 'formik'
import { AddMessageTicketType } from './types/TicketTypes'
import * as Yup from 'yup'
import { toast } from 'react-toastify'
import { ErrorType } from '../../helpers/types'
import { clx } from '../../helpers/utils'
import { Pages } from '../../config/Pages'
const TicketDetail: FC = () => {
const { t } = useTranslation('global')
const navigate = useNavigate()
const { id } = useParams()
const getMessages = useGetMessages(id ? id : '')
const [files, setFiles] = useState<File[]>([])
const addMessage = useAddMessageTicket(id ? id : '')
const multiUpload = useMultiUpload()
const [isResetFiles, setIsResetFiles] = useState<boolean>(false)
const closeTicket = useCloseTicket()
const formik = useFormik<AddMessageTicketType>({
initialValues: {
content: '',
attachmentUrls: [],
},
validationSchema: Yup.object().shape({
content: Yup.string().required(t('errors.required')),
}),
onSubmit: async (values) => {
if (files.length > 0) {
const images = new FormData()
files.forEach(file => {
images.append('files', file)
})
await multiUpload.mutateAsync(images, {
onSuccess: async (data) => {
values.attachmentUrls = await data.data.map((item: { url: string }) => item?.url)
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
} else {
delete values.attachmentUrls
}
addMessage.mutate(values, {
onSuccess: () => {
toast.success(t('success'))
formik.resetForm()
setFiles([])
setIsResetFiles(true)
setTimeout(() => {
setIsResetFiles(false)
}, 1000);
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
},
})
const hadleCloseTicket = () => {
closeTicket.mutate(id ? id : '', {
onSuccess: () => {
toast.success(t('success'))
navigate(Pages.ticket.list)
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
}
return (
<div className='mt-4'>
<div className='flex gap-1'>
<div>{t('ticket.ticket_number')}</div>
<div>12312</div>
</div>
{
getMessages.isPending ?
<PageLoading />
:
<div className='flex xl:flex-row flex-col-reverse gap-6 xl:mt-8 mt-6'>
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
<div className='gap-6 rowTwoInput'>
<Input
label={t('ticket.select_your_service')}
value={'دی منو'}
readOnly
/>
<Input
label={t('ticket.user_name')}
value={getMessages.data?.data?.ticket?.user?.firstName + ' ' + getMessages.data?.data?.ticket?.user?.lastName}
readOnly
/>
</div>
<div className='mt-8 xl:text-sm text-xs'>
{getMessages.data?.data?.ticket?.subject}
</div>
{
getMessages.data?.data?.messages.map((item: any) => {
if (item?.author?.role?.name === 'user') {
return (
<div key={item.id} className='mt-6 xl:text-sm text-xs bg-[#F6F7FA] p-6 rounded-3xl rounded-tr-none xl:max-w-[70%] max-w-[90%]'>
<div className='leading-7'>
{item.content}
</div>
<div className='flex dltr end mt-6 text-xs text-description'>
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
</div>
<div className='flex gap-2 flex-wrap text-sm'>
{
item.attachments && item?.attachments?.map((item: any, index: number) => {
return (
<div className='text-[#0047FF] mt-2 flex gap-1 items-center' key={item.id}>
<Paperclip2 size={20} color='#0047FF' />
<a href={item.attachmentUrl} target='_blank' rel='noreferrer'>
{t('attach') + ' ' + Number(index + 1)}
</a>
</div>
)
})
}
</div>
</div>
)
} else {
return (
<div className='w-full flex justify-end'>
<div className='mt-6 xl:text-sm text-xs bg-[#EBEDF5] p-6 rounded-3xl rounded-tl-none xl:max-w-[70%] max-w-[90%]'>
<div className='flex gap-1'>
<div className='font-bold'>
{t('ticket.expert')}
</div>
<div>{item.author?.firstName + ' ' + item?.author?.lastName}</div>
</div>
<div className='leading-7 mt-4'>
{item.content}
</div>
<div className='flex justify-end mt-6 text-xs text-description'>
10:07 | 1403/09/30
</div>
</div>
<div className='flex gap-2 flex-wrap text-sm'>
{
item.attachments && item?.attachments?.map((item: any, index: number) => {
return (
<div className='text-[#0047FF] mt-2 flex gap-1 items-center' key={item.id}>
<Paperclip2 size={20} color='#0047FF' />
<a href={item.attachmentUrl} target='_blank' rel='noreferrer'>
{t('attach') + ' ' + Number(index + 1)}
</a>
</div>
)
})
}
</div>
</div>
)
}
})
}
{
getMessages.data?.data?.ticket?.status !== 'CLOSED' &&
<Fragment>
<div className='mt-9'>
<Textarea
label={t('ticket.answer')}
placeholder={t('ticket.your_description')}
value={formik.values.content}
onChange={formik.handleChange}
name='content'
error_text={formik.touched.content && formik.errors.content ? formik.errors.content : ''}
/>
</div>
<div className='mt-7'>
<UploadBox
label={t('ticket.attachment')}
onChange={setFiles}
isMultiple
isReset={isResetFiles}
/>
</div>
<div className='flex justify-end mt-6'>
<Button
label={t('ticket.send')}
className='max-w-[100px]'
onClick={() => formik.handleSubmit()}
isLoading={addMessage.isPending || multiUpload.isPending}
/>
</div>
</Fragment>
}
<div className='h-8'></div>
</div>
<div className={`h-fit lg:sticky left-0 lg:top-0 xl:w-sidebar w-full`}>
{
getMessages.data?.data?.ticket?.status !== 'CLOSED' &&
<div className='bg-white xl:p-6 p-4 rounded-3xl flex gap-4'>
<Button
label={t('ticket.send_answer')}
className='text-xs xl:h-10 h-8'
/>
<Button
className='bg-[#D52903] xl:h-10 h-8'
onClick={hadleCloseTicket}
isLoading={closeTicket.isPending}
>
<div className='flex gap-2 text-xs items-center'>
<CloseCircle
color='white'
size={20}
/>
<div>
{t('ticket.close_ticket')}
</div>
</div>
</Button>
</div>
}
<div className={clx(
'bg-white p-6 rounded-3xl mt-8',
getMessages.data?.data?.ticket?.status === 'CLOSED' && 'mt-0'
)}>
<div className='flex justify-between'>
<div className='text-sm'>
{t('ticket.info_ticket')}
</div>
<div className={clx(
'h-7 px-3 rounded-xl flex items-center text-xs bg-green-100 text-green-400',
getMessages.data?.data?.ticket?.status === 'PENDING' && 'bg-orange-100 text-orange-700',
getMessages.data?.data?.ticket?.status === 'CLOSED' && 'bg-red-100 text-red-400'
)}>
{t(`ticket.${getMessages.data?.data?.ticket?.status}`)}
</div>
</div>
<div className='mt-14 text-xs w-full'>
<div className='flex justify-between text-description'>
<div>{t('ticket.ticket_number')}</div>
<div>
{getMessages.data?.data?.ticket?.numericId}
</div>
</div>
<div className='flex mt-6 justify-between text-description'>
<div>{t('ticket.asignto')}</div>
<div>
{getMessages.data?.data?.ticket?.assignedTo ? getMessages.data?.data?.ticket?.assignedTo?.firstName + ' ' + getMessages.data?.data?.ticket?.assignedTo?.lastName : ''}
</div>
</div>
<div className='flex mt-6 justify-between text-description'>
<div>{t('date')}</div>
<div className='dltr'>
{moment(getMessages.data?.data?.ticket?.createdAt).format('jYYYY/jMM/jDD HH:mm')}
</div>
</div>
<div className='flex mt-6 justify-between text-description'>
<div>{t('ticket.category')}</div>
<div>
{getMessages.data?.data?.ticket?.category?.title}
</div>
</div>
<div className='flex mt-6 justify-between text-description'>
<div>{t('ticket.priority')}</div>
<div>
{getMessages.data?.data?.ticket?.priority === 'low' ? t('ticket.low') : getMessages.data?.data?.ticket?.priority === 'medium' ? t('ticket.medium') : t('ticket.high')}
</div>
</div>
</div>
<div className='mt-10 border-t text-xs pt-5 border-border items-center flex gap-2'>
<InfoCircle size={20} color='black' />
<div>
{t('ticket.update_sms')}
</div>
</div>
</div>
</div>
</div>
}
</div>
)
}
export default TicketDetail
+112
View File
@@ -0,0 +1,112 @@
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '../../components/Button'
import { Add, Eye, MessageRemove, MessageTick, MessageTime } from 'iconsax-react'
import Tabs from '../../components/Tabs'
import Td from '../../components/Td'
import { Link } from 'react-router-dom'
import { Pages } from '../../config/Pages'
import { useGetTickets } from './hooks/useTicketData'
import moment from 'moment-jalaali'
const TicketList: FC = () => {
const { t } = useTranslation('global')
const [activeTab, setActiveTab] = useState<'ANSWERED' | 'PENDING' | 'CLOSED'>('ANSWERED')
const getTickets = useGetTickets(activeTab)
return (
<div className='mt-4'>
<div className='flex justify-between items-center'>
<div>
{t('ticket.tickets')}
</div>
<Link to={Pages.ticket.create}>
<Button
className='w-[172px]'
>
<div className='flex gap-2 items-center'>
<Add size={20} color='white' />
<div>
{t('ticket.new_ticket')}
</div>
</div>
</Button>
</Link>
</div>
<div className='mt-14'>
<Tabs
active={activeTab}
items={[
{
icon: <MessageTick color={activeTab === 'ANSWERED' ? 'black' : '#8C90A3'} size={22} />,
label: t('ticket.answered'),
value: 'ANSWERED'
},
{
icon: <MessageTime color={activeTab === 'PENDING' ? 'black' : '#8C90A3'} size={22} />,
label: t('ticket.checking'),
value: 'PENDING'
},
// {
// icon: <Message2 color={activeTab === 'wating' ? 'black' : '#8C90A3'} size={22} />,
// label: t('ticket.wait_answered'),
// value: 'wating'
// },
{
icon: <MessageRemove color={activeTab === 'CLOSED' ? 'black' : '#8C90A3'} size={22} />,
label: t('ticket.closed'),
value: 'CLOSED'
},
]}
onChange={(value) => setActiveTab(value as 'ANSWERED' | 'PENDING' | 'CLOSED')}
/>
</div>
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
<table className='w-full text-sm '>
<thead className='thead'>
<tr>
<Td text={t('ticket.number')} />
<Td text={t('ticket.title')} />
<Td text={t('ticket.team')} />
<Td text={t('ticket.date')} />
<Td text={t('ticket.status')} />
<Td text={t('ticket.priority')} />
<Td text={t('ticket.detail')} />
<Td text={''} />
</tr>
</thead>
<tbody>
{
getTickets.data?.data?.tickets?.map((item: any) => (
<tr key={item.id} className='tr'>
<Td text={item.numericId} />
<Td text={item.subject} />
<Td text={item?.category?.title} />
<Td text={''}>
<div className='dltr text-right'>
{moment(item.updatedAt).format('jYYYY-jMM-jDD HH:mm')}
</div>
</Td>
<Td text={t(`ticket.${item.status}`)} />
<Td text={t(`ticket.${item.priority}`)} />
<Td text={''}>
<Link to={Pages.ticket.detail + item.id}>
<Eye size={20} color='#8C90A3' />
</Link>
</Td>
<Td text={''} />
</tr>
))
}
</tbody>
</table>
</div>
</div>
)
}
export default TicketList
+62
View File
@@ -0,0 +1,62 @@
import * as api from "../service/TicketService";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { AddMessageTicketType, CreateTicketType } from "../types/TicketTypes";
export const useGetTickets = (status: string) => {
return useQuery({
queryKey: ["tickets", status],
queryFn: () => api.getTickets(status),
});
};
export const useCreateTicket = () => {
return useMutation({
mutationFn: (variables: CreateTicketType) => api.createTicket(variables),
});
};
export const useGetCategoriesTicket = () => {
return useQuery({
queryKey: ["category-tickets"],
queryFn: () => api.getCategoriesTicket(),
});
};
export const useSingleUpload = () => {
return useMutation({
mutationFn: (variables: FormData) => api.uploadSingle(variables),
});
};
export const useMultiUpload = () => {
return useMutation({
mutationFn: (variables: FormData) => api.uploadMultiple(variables),
});
};
export const useGetMessages = (id: string) => {
return useQuery({
queryKey: ["ticket-messages", id],
queryFn: () => api.getMessages(id),
enabled: !!id,
});
};
export const useAddMessageTicket = (id: string) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (variables: AddMessageTicketType) =>
api.addMessageTicket(id, variables),
onSuccess: () => {
queryClient.refetchQueries({
queryKey: ["ticket-messages", id],
});
},
});
};
export const useCloseTicket = () => {
return useMutation({
mutationFn: (id: string) => api.closeTicket(id),
});
};
+45
View File
@@ -0,0 +1,45 @@
import axios from "../../../config/axios";
import { AddMessageTicketType, CreateTicketType } from "../types/TicketTypes";
export const getTickets = async (status: string) => {
const { data } = await axios.get(`/tickets?status=${status}`);
return data;
};
export const createTicket = async (params: CreateTicketType) => {
const { data } = await axios.post(`/tickets`, params);
return data;
};
export const getCategoriesTicket = async () => {
const { data } = await axios.get(`/tickets/categories`);
return data;
};
export const uploadSingle = async (params: FormData) => {
const { data } = await axios.post(`/uploader/single-file`, params);
return data;
};
export const uploadMultiple = async (params: FormData) => {
const { data } = await axios.post(`/uploader/multi-file`, params);
return data;
};
export const getMessages = async (id: string) => {
const { data } = await axios.get(`/tickets/${id}/messages`);
return data;
};
export const addMessageTicket = async (
id: string,
params: AddMessageTicketType
) => {
const { data } = await axios.post(`/tickets/${id}/messages`, params);
return data;
};
export const closeTicket = async (id: string) => {
const { data } = await axios.post(`/tickets/${id}/close`);
return data;
};
+14
View File
@@ -0,0 +1,14 @@
export type CreateTicketType = {
title: string;
subject: string;
priority: "LOW" | "MEDIUM" | "HIGH";
danakServiceId?: string;
categoryId: string;
message: string;
attachmentUrls?: string[];
};
export type AddMessageTicketType = {
content: string;
attachmentUrls?: string[];
};
BIN
View File
Binary file not shown.
+123
View File
@@ -0,0 +1,123 @@
import { ArrowRight, MoneyRecive, MoneySend } from 'iconsax-react'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Td from '../../components/Td'
import Detail from './components/Detail'
const TransactionList: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-4'>
<div>
{t('transaction.transaction')}
</div>
<div className='mt-6 flex xl:gap-6 gap-1 justify-center'>
<div className='bg-white flex-1 rounded-2xl xl:p-6 py-2 px-4 xl:max-w-[253px]'>
<div className='flex xl:flex-row flex-col gap-2 items-center'>
<div className='size-8 bg-[#EEF0F7] rounded-full flex justify-center items-center'>
<MoneySend size={18} color='black' />
</div>
<div className='xl:text-xs text-[10px] text-[#101828]'>
{t('transaction.totao_deposit')}
</div>
</div>
<div className='mt-2 flex xl:flex-row flex-col gap-2 items-center'>
<div className='text-[#101828] xl:text-xs text-[10px]'>
۱۴۰,۰۰۰,۰۰۰ ریال
</div>
<div className='flex gap gap-0.5 px-2 py-1 bg-green-100 rounded-lg text-[10px] text-success'>
<ArrowRight size={12} color='#00BA4B' className='-rotate-45' />
<div>۳۰٪</div>
</div>
</div>
</div>
<div className='bg-white flex-1 rounded-2xl xl:p-6 py-2 px-4 xl:max-w-[253px]'>
<div className='flex xl:flex-row flex-col gap-2 items-center'>
<div className='size-8 bg-[#EEF0F7] rounded-full flex justify-center items-center'>
<MoneyRecive size={18} color='black' />
</div>
<div className='xl:text-xs text-[10px] text-[#101828]'>
{t('transaction.total_withdrawal')}
</div>
</div>
<div className='mt-2 flex xl:flex-row flex-col gap-2 items-center'>
<div className='text-[#101828] xl:text-xs text-[10px]'>
۱۴۰,۰۰۰,۰۰۰ ریال
</div>
<div className='flex gap gap-0.5 px-2 py-1 bg-green-100 rounded-lg text-[10px] text-success'>
<ArrowRight size={12} color='#00BA4B' className='-rotate-45' />
<div>۳۰٪</div>
</div>
</div>
</div>
<div className='bg-white flex-1 rounded-2xl xl:p-6 py-2 px-4 xl:max-w-[253px]'>
<div className='flex xl:flex-row flex-col gap-2 items-center'>
<div className='size-8 bg-[#EEF0F7] rounded-full flex xl:flex-row flex-col justify-center items-center'>
<MoneyRecive size={18} color='black' />
</div>
<div className='xl:text-xs text-[10px] text-[#101828]'>
{t('transaction.balance')}
</div>
</div>
<div className='mt-2 flex xl:flex-row flex-col gap-2 items-center'>
<div className='text-[#101828] xl:text-xs text-[10px]'>
۱۴۰,۰۰۰,۰۰۰ ریال
</div>
<div className='flex gap gap-0.5 px-2 py-1 text-red-400 rounded-lg text-[10px] bg-red-100'>
<ArrowRight size={12} color='red' className='rotate-45' />
<div>۳۰٪</div>
</div>
</div>
</div>
</div>
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
<table className='w-full text-sm '>
<thead className='thead'>
<tr>
<Td text={t('ticket.number')} />
<Td text={t('ticket.title')} />
<Td text={t('ticket.team')} />
<Td text={t('ticket.date')} />
<Td text={t('ticket.status')} />
<Td text={t('ticket.priority')} />
<Td text={t('ticket.detail')} />
<Td text={''} />
</tr>
</thead>
<tbody>
<tr className='tr'>
<Td text={t('ticket.number')} />
<Td text={t('ticket.title')} />
<Td text={t('ticket.team')} />
<Td text={t('ticket.date')} />
<Td text={t('ticket.status')} />
<Td text={t('ticket.priority')} />
<Td text={t('ticket.detail')} />
<Td text={''}>
<Detail
/>
</Td>
</tr>
<tr className='tr'>
<Td text={t('ticket.number')} />
<Td text={t('ticket.title')} />
<Td text={t('ticket.team')} />
<Td text={t('ticket.date')} />
<Td text={t('ticket.status')} />
<Td text={t('ticket.priority')} />
<Td text={t('ticket.detail')} />
<Td text={''} />
</tr>
</tbody>
</table>
</div>
</div>
)
}
export default TransactionList
+177
View File
@@ -0,0 +1,177 @@
import { Calendar, CardPos, Document, DocumentDownload, DollarCircle, Eye, NoteText, Printer, Profile, Receipt, Status, Wallet1 } from 'iconsax-react'
import { FC, useState } from 'react'
import DefaulModal from '../../../components/DefaulModal'
import { useTranslation } from 'react-i18next'
import StatusCircle from '../../../components/StatusCircle'
import Button from '../../../components/Button'
const Detail: FC = () => {
const { t } = useTranslation('global')
const [showModal, setShowModal] = useState<boolean>(false)
return (
<div>
<Eye onClick={() => setShowModal(true)} className='cursor-pointer' size={20} color='black' />
<DefaulModal
open={showModal}
close={() => setShowModal(false)}
isHeader
title_header={`${t('transaction.transaction2')} ۱۲۳۴۵۵`}
>
<div className='my-10'>
<div className='flex lg:flex-row flex-col '>
<div className='flex-1 px-6 h-10 flex justify-between items-center'>
<div className='flex gap-2 text-description'>
<CardPos size={14} color='#8C90A3' />
<div>
{t('transaction.type_deposit')}
</div>
</div>
<div>
واریز شتابی
</div>
</div>
<div className='flex-1 mt-4 px-6 h-10 flex justify-between items-center'>
<div className='flex gap-2 text-description'>
<Status size={14} color='#8C90A3' />
<div>
{t('transaction.status')}
</div>
</div>
<div className='flex gap-2 items-center'>
<StatusCircle
color='#00BA4B'
/>
<div>{t('transaction.success_paid')}</div>
</div>
</div>
</div>
<div className='flex lg:flex-row flex-col'>
<div className='flex-1 mt-4 px-6 h-10 flex justify-between items-center'>
<div className='flex gap-2 text-description'>
<Wallet1 size={14} color='#8C90A3' />
<div>
{t('transaction.type_getway')}
</div>
</div>
<div>
آنلاین بانک سامان
</div>
</div>
<div className='flex-1 mt-4 px-6 h-10 flex justify-between items-center'>
<div className='flex gap-2 text-description'>
<DollarCircle size={14} color='#8C90A3' />
<div>
{t('transaction.amount')}
</div>
</div>
<div className='flex gap-2 items-center'>
۱۰,۰۰۰,۰۰۰ ریال
</div>
</div>
</div>
<div className='flex lg:flex-row flex-col '>
<div className='flex-1 mt-4 px-6 h-10 flex justify-between items-center'>
<div className='flex gap-2 text-description'>
<Profile size={14} color='#8C90A3' />
<div>
{t('transaction.doer')}
</div>
</div>
<div>
مهرداد مظفری
</div>
</div>
<div className='flex-1 mt-4 px-6 h-10 flex justify-between items-center'>
<div className='flex gap-2 text-description'>
<Calendar size={14} color='#8C90A3' />
<div>
{t('transaction.date')}
</div>
</div>
<div className='flex gap-2 items-center'>
۱۴۰۳/۰۹/۳۰
</div>
</div>
</div>
<div className='flex lg:flex-row flex-col '>
<div className='flex-1 mt-4 px-6 h-10 flex justify-between items-center'>
<div className='flex gap-2 text-description'>
<Document size={14} color='#8C90A3' />
<div>
{t('transaction.tracking_number')}
</div>
</div>
<div>
bbd704c-۱۲۳۴۵۶
</div>
</div>
<div className='flex-1 mt-4 px-6 h-10 flex justify-between items-center'>
<div className='flex gap-2 text-description'>
<Receipt size={14} color='#8C90A3' />
<div>
{t('transaction.order_number')}
</div>
</div>
<div className='flex gap-2 items-center'>
bbd704c-۱۲۳۴۵۶
</div>
</div>
</div>
<div className='flex lg:flex-row flex-col gap-8'>
<div className='flex-1 mt-4 px-6 h-10 flex justify-between items-center'>
<div className='flex gap-2 text-description'>
<NoteText size={14} color='#8C90A3' />
<div>
{t('transaction.description_transaction')}
</div>
</div>
<div>
-
</div>
</div>
</div>
</div>
<div className='border-t pt-8 flex justify-end border-white border-opacity-40'>
<div className='flex gap-5'>
<Button
className='bg-white bg-opacity-35 w-[170px] text-description'
>
<div className='flex gap-2 items-center justify-center'>
<Printer
size={20}
color='#8C90A3'
/>
<div>
{t('transaction.print')}
</div>
</div>
</Button>
<Button
className='bg-black w-[170px] '
>
<div className='flex gap-2 items-center justify-center'>
<DocumentDownload
size={20}
color='white'
/>
<div>
{t('transaction.download')}
</div>
</div>
</Button>
</div>
</div>
</DefaulModal>
</div>
)
}
export default Detail
+93
View File
@@ -0,0 +1,93 @@
import { Cards, Money3, MoneyRecive, TickSquare } from 'iconsax-react'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Tabs from '../../components/Tabs'
import Online from './components/Online'
import CardtoCard from './components/CardtoCard'
import Sheba from './components/Sheba'
const Wallet: FC = () => {
const { t } = useTranslation('global')
const [activeTab, setActiveTab] = useState<string>('online')
return (
<div className='mt-4 pb-14'>
<div>
{t('wallet.increese_wallet')}
</div>
<div className='mt-8 flex gap-6'>
<div className='flex-1'>
<Tabs
isFullWidth
active={activeTab}
items={[
{
label: t('wallet.online_pay'),
value: 'online',
icon: <MoneyRecive color={activeTab === 'online' ? 'black' : '#8C90A3'} size={22} />
},
{
icon: <Cards color={activeTab === 'card' ? 'black' : '#8C90A3'} size={22} />,
label: t('wallet.card_to_card'),
value: 'card'
},
{
icon: <Money3 color={activeTab === 'sheba' ? 'black' : '#8C90A3'} size={22} />,
label: t('wallet.sheba'),
value: 'sheba'
},
]}
onChange={setActiveTab}
/>
{
activeTab === 'online' ?
<Online />
: activeTab === 'card' ?
<CardtoCard />
: <Sheba />
}
</div>
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
<div className='text-sm'>
{t('ticket.title_hint')}
</div>
<div className='mt-6'>
<div className='flex items-start gap-2 border-b pb-5'>
<div>
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
</div>
</div>
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
<div>
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
</div>
</div>
<div className='flex items-start gap-2 mt-6'>
<div>
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default Wallet
@@ -0,0 +1,41 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '../../../components/Input'
import UploadBox from '../../../components/UploadBox'
import Button from '../../../components/Button'
const CardtoCard: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-8 bg-white p-8 rounded-3xl'>
<div>
{t('wallet.card_to_card')}
</div>
<div className='mt-8'>
<Input
label={t('wallet.amount')}
placeholder={t('wallet.enter_amount_card')}
/>
</div>
<div className='mt-8'>
<UploadBox
label={t('wallet.upload_deposit_slip')}
/>
</div>
<div className='mt-10 flex justify-end'>
<Button
label={t('wallet.submit_slip')}
className='w-[180px]'
/>
</div>
</div>
)
}
export default CardtoCard
+111
View File
@@ -0,0 +1,111 @@
import { FC, useState } from 'react'
import Button from '../../../components/Button'
import { useTranslation } from 'react-i18next'
import { clx } from '../../../helpers/utils'
import SamanImage from '../../../assets/images/saman.png'
import Input from '../../../components/Input'
const Online: FC = () => {
const { t } = useTranslation('global')
const [priceSelect, setPriceSelect] = useState<number>(1)
const [bankSelected, setBankSelected] = useState<number>(1)
return (
<div className='mt-8 bg-white p-8 rounded-3xl'>
<div>
{t('wallet.online_pay')}
</div>
<div className='mt-8 text-sm'>
{t('wallet.select_price')}
</div>
<div className='mt-4 flex gap-4 flex-wrap'>
<div onClick={() => setPriceSelect(1)} className={clx(
'bg-[#EBEDF5] border border-[#EBEDF5] cursor-pointer flex items-center text-xs h-10 px-4 rounded-lg',
priceSelect === 1 && 'border-black'
)}>
۱۰,۰۰۰,۰۰۰ تومان
</div>
<div onClick={() => setPriceSelect(2)} className={clx(
'bg-[#EBEDF5] border border-[#EBEDF5] cursor-pointer flex items-center text-xs h-10 px-2 rounded-lg',
priceSelect === 2 && 'border-black'
)}>
۱۰,۰۰۰,۰۰۰ تومان
</div>
<div onClick={() => setPriceSelect(3)} className={clx(
'bg-[#EBEDF5] border border-[#EBEDF5] cursor-pointer flex items-center text-xs h-10 px-2 rounded-lg',
priceSelect === 3 && 'border-black'
)}>
۱۰,۰۰۰,۰۰۰ تومان
</div>
<div onClick={() => setPriceSelect(4)} className={clx(
'bg-[#EBEDF5] border border-[#EBEDF5] cursor-pointer flex items-center text-xs h-10 px-2 rounded-lg',
priceSelect === 4 && 'border-black'
)}>
۱۰,۰۰۰,۰۰۰ تومان
</div>
<div onClick={() => setPriceSelect(5)} className={clx(
'bg-[#EBEDF5] border border-[#EBEDF5] cursor-pointer flex items-center text-xs h-10 px-2 rounded-lg',
priceSelect === 5 && 'border-black'
)}>
۱۰,۰۰۰,۰۰۰ تومان
</div>
</div>
<div className='mt-9'>
<Input
label={t('wallet.optional_price')}
placeholder={t('wallet.enter_your_price')}
/>
</div>
<div className='mt-8 text-sm'>
<div>
{t('wallet.select_payment')}
</div>
<div className='mt-6 flex gap-4'>
<div onClick={() => setBankSelected(1)} className={clx(
'xl:h-16 rounded-xl cursor-pointer flex xl:flex-row flex-col gap-4 items-center text-center py-3 xl:py-0 xl:text-right px-4 border border-border xl:w-[240px]',
bankSelected === 1 && 'border-black'
)}>
<img src={SamanImage} className='w-10' />
<div>
<div>
بانک پارسیان
</div>
<div className='text-xs mt-1 text-description'>
تمام کارت های عضو شتاب
</div>
</div>
</div>
<div onClick={() => setBankSelected(2)} className={clx(
'xl:h-16 rounded-xl cursor-pointer flex xl:flex-row flex-col gap-4 items-center text-center py-3 xl:py-0 xl:text-right px-4 border border-border xl:w-[240px]',
bankSelected === 2 && 'border-black'
)}>
<img src={SamanImage} className='w-10' />
<div>
<div>
بانک پارسیان
</div>
<div className='text-xs mt-1 text-description'>
تمام کارت های عضو شتاب
</div>
</div>
</div>
</div>
<div className='mt-10 flex justify-end'>
<Button
label={t('wallet.pay')}
className='w-[180px]'
/>
</div>
</div>
</div>
)
}
export default Online
+41
View File
@@ -0,0 +1,41 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '../../../components/Input'
import UploadBox from '../../../components/UploadBox'
import Button from '../../../components/Button'
const Sheba: FC = () => {
const { t } = useTranslation('global')
return (
<div className='mt-8 bg-white p-8 rounded-3xl'>
<div>
{t('wallet.pay_sheba')}
</div>
<div className='mt-8'>
<Input
label={t('wallet.amount')}
placeholder={t('wallet.enter_sheba_deposit')}
/>
</div>
<div className='mt-8'>
<UploadBox
label={t('wallet.upload_deposit_slip')}
/>
</div>
<div className='mt-10 flex justify-end'>
<Button
label={t('wallet.submit_slip')}
className='w-[180px]'
/>
</div>
</div>
)
}
export default Sheba
+52
View File
@@ -0,0 +1,52 @@
import Header from "../shared/Header"
import SideBar from "../shared/SideBar"
import '../assets/fonts/irancell/style.css'
import { Route, Routes } from 'react-router-dom'
import { Pages } from '../config/Pages'
import Home from '../pages/home/Home'
import ReceiptsList from '../pages/receipts/List'
import AddCriticisms from '../pages/criticisms/Add'
import Footer from "../shared/Footer"
import GradeRecordes from "../pages/grade/records"
import Certificate from "../pages/certificate/List"
import AdmitCard from "../pages/admitCard/List"
import MyCourses from "../pages/my-courses/List"
import Payment from "../pages/payment/Details"
import Wallet from "../pages/wallet/Wallet"
import ReceiptsDetail from "../pages/receipts/Detail"
import ReportCardList from "../pages/ReportCard/List"
import TransactionList from "../pages/transaction/List"
import Profile from "../pages/profile/Profile"
import CompanyDetail from "../pages/company/Detail"
const MainRouter = () => {
return (
<div className='p-4 overflow-hidden'>
<SideBar />
<Header />
<div className='flex-1 xl:ms-[269px] mt-[68px] xl:mt-[81px]'>
<div className={`hide-scroll-bar overflow-auto w-[${window.innerWidth}] max-h-[calc(100vh-113px)]`}>
<div>
<Routes>
<Route path={Pages.dashboard} element={<Home />} />
<Route path={Pages.receipts.index} element={<ReceiptsList />} />
<Route path={Pages.receipts.detail + ':id'} element={<ReceiptsDetail />} />
<Route path={Pages.company.detail + ':id'} element={<CompanyDetail />} />
<Route path={Pages.criticisms} element={<AddCriticisms />} />
<Route path={Pages.grade.records} element={<GradeRecordes />} />
<Route path={Pages.transactions} element={<TransactionList />} />
<Route path={Pages.certificate} element={<Certificate />} />
<Route path={Pages.admitCard} element={<AdmitCard />} />
<Route path={Pages.myCourses} element={<MyCourses />} />
<Route path={Pages.payment} element={<Payment />} />
<Route path={Pages.wallet} element={<Wallet />} />
<Route path={Pages.reportCard.list} element={<ReportCardList />} />
<Route path={Pages.profile} element={<Profile />} />
</Routes>
</div>
</div>
</div>
<Footer />
</div>
)
}
export default MainRouter
+80
View File
@@ -0,0 +1,80 @@
import { Buildings, Buildings2, Home2, Messages3, NotificationStatus } from 'iconsax-react'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Pages } from '../config/Pages'
import { Link } from 'react-router-dom'
const Footer: FC = () => {
const { t } = useTranslation('global')
return (
<div className='xl:hidden'>
<div className='h-[60px] '></div>
<div className='fixed bottom-2 z-10 right-3 left-3 bg-white h-[60px] rounded-2xl flex justify-between items-center px-3'>
<Link to={Pages.dashboard}>
<div className='text-description w-[70px] flex flex-col items-center gap-1.5'>
<Home2
className='size-5'
color='#8C90A3'
/>
<div className='text-[10px]'>
{t('footer.home')}
</div>
</div>
</Link>
<Link to={Pages.services.other}>
<div className='text-description w-[70px] flex flex-col items-center gap-1.5'>
<Buildings2
className='size-5'
color='#8C90A3'
/>
<div className='text-[10px]'>
{t('footer.companies')}
</div>
</div>
</Link>
<Link to={Pages.services.mine}>
<div className='text-description w-[70px] flex flex-col items-center gap-1.5'>
<div className='bg-white p-1 rounded-full -mt-7'>
<div className='bg-black flex justify-center items-center size-10 rounded-full'>
<Buildings
className='size-5'
color='white'
/>
</div>
</div>
<div className='text-[10px] '>
{t('footer.my_company')}
</div>
</div>
</Link>
<Link to={Pages.announcement.list}>
<div className='text-description w-[70px] flex flex-col items-center gap-1.5'>
<NotificationStatus
className='size-5'
color='#8C90A3'
/>
<div className='text-[10px]'>
{t('footer.announcements')}
</div>
</div>
</Link>
<Link to={Pages.ticket.list}>
<div className='text-description w-[70px] flex flex-col items-center gap-1.5'>
<Messages3
className='size-5'
color='#8C90A3'
/>
<div className='text-[10px]'>
{t('footer.tickets')}
</div>
</div>
</Link>
</div>
</div>
)
}
export default Footer

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