commit 30bd9fc01cb8659a4560cde2026878ec6a74afbf Author: mho3ein220 Date: Sat Aug 8 19:05:08 2026 +0330 start diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c6c8b36 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..6bb4480 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,99 @@ +{ + "env": { + "browser": true, + "es2021": true, + "jest": true + }, + "extends": [ + "plugin:react/recommended", + "standard" + ], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "parser": "@babel/eslint-parser", + "plugins": [ + "react", + "react-hooks" + ], + "rules": { + "react/react-in-jsx-scope": "off", + "indent": [ + "error", + 2, + { + "SwitchCase": 1 + } + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single", + { + "avoidEscape": true, + "allowTemplateLiterals": true + } + ], + "semi": [ + "error", + "always" + ], + "no-console": [ + "error", + { + "allow": [ + "warn", + "error" + ] + } + ], + "jsx-quotes": [ + "error", + "prefer-single" + ], + "space-before-function-paren": [ + "error", + { + "anonymous": "always", + "named": "never", + "asyncArrow": "always" + } + ], + "curly": [ + "error", + "multi-or-nest", + "consistent" + ], + "object-curly-spacing": [ + "error", + "never" + ], + "multiline-ternary": "off", + "operator-linebreak": [ + "error", + "after" + ], + "no-unused-vars": [ + "error", + { + "ignoreRestSiblings": true + } + ], + "lines-between-class-members": [ + "error", + "always", + { + "exceptAfterSingleLine": true + } + ], + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn" + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0bbbc75 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +dist/ +build/ + +.env* + +# Local Claude Code config — settings.local.json holds per-machine permissions. +.claude/ diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..7a40566 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,13 @@ +{ + "trailingComma": "none", + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": true, + "bracketSpacing": false, + "bracketSameLine": false, + "arrowParens": "avoid", + "endOfLine": "lf", + "printWidth": 100 +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2823801 --- /dev/null +++ b/package.json @@ -0,0 +1,77 @@ +{ + "name": "bi_panel", + "version": "0.1.0", + "private": true, + "dependencies": { + "@heroicons/react": "^1.0.5", + "@testing-library/jest-dom": "^5.16.1", + "@testing-library/react": "^12.1.2", + "@testing-library/user-event": "^13.5.0", + "axios": "^0.24.0", + "chart.js": "^3.7.1", + "chartjs-adapter-date-fns": "^2.0.0", + "classnames": "^2.3.1", + "date-fns": "^2.28.0", + "date-fns-jalali": "^2.28.0-0", + "formik": "^2.2.9", + "moment-jalaali": "^0.9.4", + "prop-types": "^15.8.0", + "react": "^17.0.2", + "react-chartjs-2": "^4.0.1", + "react-dom": "^17.0.2", + "react-query": "^3.34.7", + "react-redux": "^7.2.6", + "react-router-dom": "^5.3.0", + "react-scripts": "^5.0.0", + "react-toast-notifications": "^2.5.1", + "redux": "^4.1.2", + "redux-thunk": "^2.4.1", + "web-vitals": "^2.1.2" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@babel/eslint-parser": "^7.16.5", + "@tailwindcss/forms": "^0.4.0", + "@wojtekmaj/enzyme-adapter-react-17": "^0.6.6", + "autoprefixer": "^10.4.0", + "enzyme": "^3.11.0", + "enzyme-to-json": "^3.6.2", + "eslint": "^8.5.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0", + "faker": "^5.5.3", + "postcss": "^8.4.5", + "react-test-renderer": "^17.0.2", + "redux-devtools-extension": "^2.13.9", + "tailwindcss": "^3.0.8", + "tailwindcss-rtl": "^0.8.0" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/public/assets/css/fontiran.css b/public/assets/css/fontiran.css new file mode 100644 index 0000000..28247ce --- /dev/null +++ b/public/assets/css/fontiran.css @@ -0,0 +1,78 @@ +/** +* +* Name: IRAN Sans-Serif Font +* Version: 5.0 +* Author: Moslem Ebrahimi (moslemebrahimi.com) +* Created on: Dec 25, 2012 +* Updated on: Sep 01, 2017 +* Website: http://fontiran.com +* Copyright: Commercial/Proprietary Software +-------------------------------------------------------------------------------------- +فونت های ایران سن سریف یک نرم افزار مالکیتی محسوب می شود. جهت آگاهی از قوانین استفاده از این فونت ها لطفا به وب سایت (فونت ایران دات کام) مراجعه نمایید +-------------------------------------------------------------------------------------- +IRAN Sans-serif fonts are considered a proprietary software. To gain information about the laws regarding the use of these fonts, please visit www.fontiran.com +-------------------------------------------------------------------------------------- +This set of fonts are used in this project under the license: (GY3QV) +-------------------------------------------------------------------------------------- +* +**/ +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: 900; + src: url('../fonts/eot/IRANSansWeb(FaNum)_Black.eot'); + src: url('../fonts/eot/IRANSansWeb(FaNum)_Black.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ + url('../fonts/woff2/IRANSansWeb(FaNum)_Black.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ + url('../fonts/woff/IRANSansWeb(FaNum)_Black.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url('../fonts/ttf/IRANSansWeb(FaNum)_Black.ttf') format('truetype'); +} +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: bold; + src: url('../fonts/eot/IRANSansWeb(FaNum)_Bold.eot'); + src: url('../fonts/eot/IRANSansWeb(FaNum)_Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ + url('../fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ + url('../fonts/woff/IRANSansWeb(FaNum)_Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url('../fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf') format('truetype'); +} +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: 500; + src: url('../fonts/eot/IRANSansWeb(FaNum)_Medium.eot'); + src: url('../fonts/eot/IRANSansWeb(FaNum)_Medium.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ + url('../fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ + url('../fonts/woff/IRANSansWeb(FaNum)_Medium.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url('../fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf') format('truetype'); +} +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: 300; + src: url('../fonts/eot/IRANSansWeb(FaNum)_Light.eot'); + src: url('../fonts/eot/IRANSansWeb(FaNum)_Light.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ + url('../fonts/woff2/IRANSansWeb(FaNum)_Light.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ + url('../fonts/woff/IRANSansWeb(FaNum)_Light.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url('../fonts/ttf/IRANSansWeb(FaNum)_Light.ttf') format('truetype'); +} +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: 200; + src: url('../fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot'); + src: url('../fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ + url('../fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ + url('../fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url('../fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf') format('truetype'); +} +@font-face { + font-family: IRANSans; + font-style: normal; + font-weight: normal; + src: url('../fonts/eot/IRANSansWeb(FaNum).eot'); + src: url('../fonts/eot/IRANSansWeb(FaNum).eot?#iefix') format('embedded-opentype'), /* IE6-8 */ + url('../fonts/woff2/IRANSansWeb(FaNum).woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ + url('../fonts/woff/IRANSansWeb(FaNum).woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ + url('../fonts/ttf/IRANSansWeb(FaNum).ttf') format('truetype'); +} diff --git a/public/assets/css/style.css b/public/assets/css/style.css new file mode 100644 index 0000000..3468644 --- /dev/null +++ b/public/assets/css/style.css @@ -0,0 +1,66 @@ +@import url(./fontiran.css); + +body { + font-family: IRANSans !important; + font-weight: 300; + direction: rtl; + margin: 0; +} + +h1, h2, h3, h4, h5, h6, text, strong, input, textarea { + font-family: IRANSans !important; +} + +div, span, a, label, button { + font-family: IRANSans !important; +} + +.preloader-container { + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + flex-direction: column; +} + +.meter { + height: 10px; + width: 172px; + background: #f3efe6; + direction: ltr; + border-radius: 5px; + margin: 12px 0; +} + +.progress { + height: 100%; + display: block; + border-radius: 5px; + animation: progressBar 1.2s ease-in-out infinite; + animation-fill-mode: both; +} + +@keyframes progressBar { + 0% { + width: 0; + background-color: transparent; + } + 49.99% { + width: 100%; + background-color: #00B1DA; + } + 50.01% { + width: 0; + background-color: transparent; + } + 100% { + width: 100%; + background-color: #FB8928 + } +} + +.leading-0 { + line-height: 0; +} diff --git a/public/assets/css/style.min.css b/public/assets/css/style.min.css new file mode 100644 index 0000000..ab36aa3 --- /dev/null +++ b/public/assets/css/style.min.css @@ -0,0 +1 @@ +@import url(./fontiran.css);@keyframes progressBar{0%,50.01%{width:0;background-color:transparent}49.99%{width:100%;background-color:#00b1da}to{width:100%;background-color:#fb8928}}body{font-weight:300;direction:rtl;margin:0}a,body,button,div,h1,h2,h3,h4,h5,h6,input,label,span,strong,text,textarea{font-family:IRANSans!important}.preloader-container{width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;z-index:9999;flex-direction:column}.meter{height:10px;width:172px;background:#f3efe6;direction:ltr;border-radius:5px;margin:12px 0}.progress{height:100%;display:block;border-radius:5px;animation:progressBar 1.2s ease-in-out infinite;animation-fill-mode:both}.leading-0{line-height:0} \ No newline at end of file diff --git a/public/assets/fonts/eot/IRANSansWeb(FaNum).eot b/public/assets/fonts/eot/IRANSansWeb(FaNum).eot new file mode 100644 index 0000000..5a6fdd0 Binary files /dev/null and b/public/assets/fonts/eot/IRANSansWeb(FaNum).eot differ diff --git a/public/assets/fonts/eot/IRANSansWeb(FaNum)_Black.eot b/public/assets/fonts/eot/IRANSansWeb(FaNum)_Black.eot new file mode 100644 index 0000000..a012bc6 Binary files /dev/null and b/public/assets/fonts/eot/IRANSansWeb(FaNum)_Black.eot differ diff --git a/public/assets/fonts/eot/IRANSansWeb(FaNum)_Bold.eot b/public/assets/fonts/eot/IRANSansWeb(FaNum)_Bold.eot new file mode 100644 index 0000000..82525e5 Binary files /dev/null and b/public/assets/fonts/eot/IRANSansWeb(FaNum)_Bold.eot differ diff --git a/public/assets/fonts/eot/IRANSansWeb(FaNum)_Light.eot b/public/assets/fonts/eot/IRANSansWeb(FaNum)_Light.eot new file mode 100644 index 0000000..f899008 Binary files /dev/null and b/public/assets/fonts/eot/IRANSansWeb(FaNum)_Light.eot differ diff --git a/public/assets/fonts/eot/IRANSansWeb(FaNum)_Medium.eot b/public/assets/fonts/eot/IRANSansWeb(FaNum)_Medium.eot new file mode 100644 index 0000000..117c2c8 Binary files /dev/null and b/public/assets/fonts/eot/IRANSansWeb(FaNum)_Medium.eot differ diff --git a/public/assets/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot b/public/assets/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot new file mode 100644 index 0000000..9cd9b66 Binary files /dev/null and b/public/assets/fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot differ diff --git a/public/assets/fonts/ttf/IRANSansWeb(FaNum).ttf b/public/assets/fonts/ttf/IRANSansWeb(FaNum).ttf new file mode 100644 index 0000000..cd74265 Binary files /dev/null and b/public/assets/fonts/ttf/IRANSansWeb(FaNum).ttf differ diff --git a/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Black.ttf b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Black.ttf new file mode 100644 index 0000000..0f84dbf Binary files /dev/null and b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Black.ttf differ diff --git a/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf new file mode 100644 index 0000000..6fa2412 Binary files /dev/null and b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf differ diff --git a/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Light.ttf b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Light.ttf new file mode 100644 index 0000000..d9000c9 Binary files /dev/null and b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Light.ttf differ diff --git a/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf new file mode 100644 index 0000000..e20001c Binary files /dev/null and b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf differ diff --git a/public/assets/fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf new file mode 100644 index 0000000..0072606 Binary files /dev/null and b/public/assets/fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf differ diff --git a/public/assets/fonts/woff/IRANSansWeb(FaNum).woff b/public/assets/fonts/woff/IRANSansWeb(FaNum).woff new file mode 100644 index 0000000..e34dfc4 Binary files /dev/null and b/public/assets/fonts/woff/IRANSansWeb(FaNum).woff differ diff --git a/public/assets/fonts/woff/IRANSansWeb(FaNum)_Black.woff b/public/assets/fonts/woff/IRANSansWeb(FaNum)_Black.woff new file mode 100644 index 0000000..bbbca77 Binary files /dev/null and b/public/assets/fonts/woff/IRANSansWeb(FaNum)_Black.woff differ diff --git a/public/assets/fonts/woff/IRANSansWeb(FaNum)_Bold.woff b/public/assets/fonts/woff/IRANSansWeb(FaNum)_Bold.woff new file mode 100644 index 0000000..05d933e Binary files /dev/null and b/public/assets/fonts/woff/IRANSansWeb(FaNum)_Bold.woff differ diff --git a/public/assets/fonts/woff/IRANSansWeb(FaNum)_Light.woff b/public/assets/fonts/woff/IRANSansWeb(FaNum)_Light.woff new file mode 100644 index 0000000..fe80d47 Binary files /dev/null and b/public/assets/fonts/woff/IRANSansWeb(FaNum)_Light.woff differ diff --git a/public/assets/fonts/woff/IRANSansWeb(FaNum)_Medium.woff b/public/assets/fonts/woff/IRANSansWeb(FaNum)_Medium.woff new file mode 100644 index 0000000..f13a870 Binary files /dev/null and b/public/assets/fonts/woff/IRANSansWeb(FaNum)_Medium.woff differ diff --git a/public/assets/fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff b/public/assets/fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff new file mode 100644 index 0000000..9c653a4 Binary files /dev/null and b/public/assets/fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff differ diff --git a/public/assets/fonts/woff2/IRANSansWeb(FaNum).woff2 b/public/assets/fonts/woff2/IRANSansWeb(FaNum).woff2 new file mode 100644 index 0000000..30a377d Binary files /dev/null and b/public/assets/fonts/woff2/IRANSansWeb(FaNum).woff2 differ diff --git a/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Black.woff2 b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Black.woff2 new file mode 100644 index 0000000..0b15441 Binary files /dev/null and b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Black.woff2 differ diff --git a/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2 b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2 new file mode 100644 index 0000000..f2353b2 Binary files /dev/null and b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2 differ diff --git a/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Light.woff2 b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Light.woff2 new file mode 100644 index 0000000..562d663 Binary files /dev/null and b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Light.woff2 differ diff --git a/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2 b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2 new file mode 100644 index 0000000..a69f4d0 Binary files /dev/null and b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2 differ diff --git a/public/assets/fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2 b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2 new file mode 100644 index 0000000..1114f71 Binary files /dev/null and b/public/assets/fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2 differ diff --git a/public/browserconfig.xml b/public/browserconfig.xml new file mode 100644 index 0000000..e636aa5 --- /dev/null +++ b/public/browserconfig.xml @@ -0,0 +1,12 @@ + + + + + + + + + #ffffff + + + diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..820a129 --- /dev/null +++ b/public/index.html @@ -0,0 +1,157 @@ + + + + + + + + + پنل هوش تجاری فلش کارت های گروه مد مشاور + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ logo +
+ +
+
+
+ + + diff --git a/public/lib/.gitkeep b/public/lib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..9ca882b --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,83 @@ +{ + "short_name": "Medmoshaver FlashCard BI Panel", + "name": "Medmoshaver FlashCard BI Panel", + "icons": [ + { + "src": "https://medmoshaver.com/icons/pwa/icon-36.png", + "sizes": "36x36", + "type": "image/png", + "density": "0.75", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-48.png", + "sizes": "48x48", + "type": "image/png", + "density": "1.0", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-72.png", + "sizes": "72x72", + "type": "image/png", + "density": "1.5", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-96.png", + "sizes": "96x96", + "type": "image/png", + "density": "2.0", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-128.png", + "sizes": "128x128", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-144.png", + "sizes": "144x144", + "type": "image/png", + "density": "3.0", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-152.png", + "sizes": "152x152", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "density": "4.0", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-256.png", + "sizes": "256x256", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-384.png", + "sizes": "384x384", + "type": "image/png", + "density": "8.0", + "purpose": "maskable any" + }, + { + "src": "https://medmoshaver.com/icons/pwa/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable any" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#fafafa", + "background_color": "#fafafa" +} diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..e69de29 diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..b925908 --- /dev/null +++ b/src/App.js @@ -0,0 +1,26 @@ +import React from 'react'; +import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'; +import moment from 'moment-jalaali'; +import HomePage from './components/home'; +import DefaultPage from './components/default'; +import LoginPage from './components/login'; +import OrdersPage from './components/orders'; +import StudentsCountPage from './components/students-count'; + +function App() { + return ( + + + + + + + + + + ); +} + +moment.loadPersian({usePersianDigits: false, dialect: 'persian-modern'}); + +export default App; diff --git a/src/App.min.css b/src/App.min.css new file mode 100644 index 0000000..e69de29 diff --git a/src/api/auth.js b/src/api/auth.js new file mode 100644 index 0000000..aa1986b --- /dev/null +++ b/src/api/auth.js @@ -0,0 +1,42 @@ +import {request} from './index'; + +// local storage keys +export const ACCESS_TOKEN_KEY = 'access_token'; +export const REFRESH_TOKEN_KEY = 'refresh_token'; + +export async function getToken() { + const accessToken = localStorage.getItem(ACCESS_TOKEN_KEY); + if (accessToken) return accessToken; + return await getFreshToken(); +} + +export async function getFreshToken() { + const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY); + if (!refreshToken) throw Error('Refresh token not found.'); + const data = await fetchFreshToken(refreshToken); + localStorage.setItem(ACCESS_TOKEN_KEY, data.access); + return data.access; +} + +export async function fetchToken({mobile, password}) { + return await request({ + method: 'POST', + url: '/auth/login', + data: {mobile, password} + }); +} + +export async function fetchFreshToken(token) { + return await request({ + method: 'POST', + url: '/auth/refresh', + data: {refresh: token} + }); +} + +export function logoutUser(queryClient, history = null) { + localStorage.removeItem(ACCESS_TOKEN_KEY); + localStorage.removeItem(REFRESH_TOKEN_KEY); + queryClient.invalidateQueries('user'); + if (history) history.push('/login'); +} diff --git a/src/api/index.js b/src/api/index.js new file mode 100644 index 0000000..eeba929 --- /dev/null +++ b/src/api/index.js @@ -0,0 +1,28 @@ +import axios from 'axios'; +import {getFreshToken, getToken} from './auth'; + +const apiClient = axios.create({ + baseURL: process.env.REACT_APP_API_BASE_URL, + headers: {} +}); + +export async function request(options) { + const {auth, ...requestOptions} = options; + if (auth) { + const accessToken = await getToken(); + requestOptions.headers = {...requestOptions.headers, Authorization: `Bearer ${accessToken}`}; + } + + try { + return (await apiClient(requestOptions)).data; + } catch (error) { + if (!auth || error.response?.status !== 401) throw error.response; + const accessToken = await getFreshToken(); + requestOptions.headers.Authorization = `Bearer ${accessToken}`; + try { + return (await apiClient(requestOptions)).data; + } catch (error) { + throw error.response; + } + } +} diff --git a/src/api/order.js b/src/api/order.js new file mode 100644 index 0000000..581a1ce --- /dev/null +++ b/src/api/order.js @@ -0,0 +1,5 @@ +import {request} from './index'; + +export async function fetchOrderData() { + return await request({url: '/decks/ordered-decks/data', auth: true}); +} diff --git a/src/api/student.js b/src/api/student.js new file mode 100644 index 0000000..1c14992 --- /dev/null +++ b/src/api/student.js @@ -0,0 +1,5 @@ +import {request} from './index'; + +export async function fetchStudentsCountData() { + return await request({url: '/students/students-count-data', auth: true}); +} diff --git a/src/api/user.js b/src/api/user.js new file mode 100644 index 0000000..ef5a1ee --- /dev/null +++ b/src/api/user.js @@ -0,0 +1,5 @@ +import {request} from './index'; + +export async function fetchUser() { + return await request({url: '/users/user', auth: true}); +} diff --git a/src/components/default/index.js b/src/components/default/index.js new file mode 100644 index 0000000..450725f --- /dev/null +++ b/src/components/default/index.js @@ -0,0 +1,9 @@ +import MainLayout from '../layouts/main'; + +function DefaultPage() { + return ( + + ); +} + +export default DefaultPage; diff --git a/src/components/home/home.js b/src/components/home/home.js new file mode 100644 index 0000000..7080b08 --- /dev/null +++ b/src/components/home/home.js @@ -0,0 +1,12 @@ +import Segment from '../shared/segment'; + +function Home() { + return ( + +

پنل هوش تجاری فلش کارت های گروه مد مشاور

+

به پنل هوش تجاری فلش کارت های گروه مدمشاور خوش آمدید.

+
+ ); +} + +export default Home; diff --git a/src/components/home/index.js b/src/components/home/index.js new file mode 100644 index 0000000..a1872b1 --- /dev/null +++ b/src/components/home/index.js @@ -0,0 +1,16 @@ +import MainLayout from '../layouts/main'; +import Home from './home'; + +function HomePage() { + return ( + + + + ); +} + +export default HomePage; diff --git a/src/components/layouts/main/auth-header.js b/src/components/layouts/main/auth-header.js new file mode 100644 index 0000000..d42148f --- /dev/null +++ b/src/components/layouts/main/auth-header.js @@ -0,0 +1,53 @@ +import {useHistory} from 'react-router-dom'; +import {useQueryClient} from 'react-query'; +import PropTypes from 'prop-types'; +import NavLink from '../../shared/buttons/nav-link'; +import ButtonGroup from '../../shared/buttons/button-group'; +import Button from '../../shared/buttons/button'; +import {useUser} from '../../../hooks/user'; +import {logoutUser} from '../../../api/auth'; + +function AuthHeader(props) { + const history = useHistory(); + const {status, data: user} = useUser(); + const queryClient = useQueryClient(); + const nextURL = `${history.location.pathname}${history.location.search.replace('&', ';')}`; + + if (status === 'error') { + return ( + + + + ); + } + + return ( + + + + + ); +} + +AuthHeader.propTypes = { + className: PropTypes.string +}; + +export default AuthHeader; diff --git a/src/components/layouts/main/footer.js b/src/components/layouts/main/footer.js new file mode 100644 index 0000000..98a68b7 --- /dev/null +++ b/src/components/layouts/main/footer.js @@ -0,0 +1,7 @@ +function Footer() { + return ( +