diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..a4cba6c
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,81 @@
+image: node:latest
+
+cache:
+ paths:
+ - node_modules
+
+stages:
+ - lint
+ - test
+ - test:build
+ - deploy
+
+merge:test:lint:
+ stage: lint
+ script:
+ - npm install
+ - cp example.env.local .env.local
+ - cp example.env.local .env.test
+ - npm run lint
+ only:
+ - merge_requests
+
+merge:test:jest:
+ stage: test
+ script:
+ - npm install
+ - cp example.env.local .env.local
+ - cp example.env.local .env.test
+ - npm run test
+ only:
+ - merge_requests
+
+#merge:test:build:
+# stage: test:build
+# script:
+# - npm install
+# - cp example.env.local .env.local
+# - cp example.env.local .env.test
+# - npm run build
+# only:
+# - merge_requests
+
+test:lint:
+ stage: lint
+ script:
+ - npm install
+ - cp example.env.local .env.local
+ - cp example.env.local .env.test
+ - npm run lint
+ only:
+ - develop
+ - main
+
+test:jest:
+ stage: test
+ script:
+ - npm install
+ - cp example.env.local .env.local
+ - cp example.env.local .env.test
+ - npm run test
+ only:
+ - develop
+ - main
+
+#test:build:
+# stage: test:build
+# script:
+# - npm install
+# - cp example.env.local .env.local
+# - cp example.env.local .env.test
+# - npm run build
+# only:
+# - develop
+# - main
+
+deploy:
+ stage: deploy
+ script:
+ - echo "Run deploy project"
+ only:
+ - main
\ No newline at end of file
diff --git a/cypress.config.js b/cypress.config.js
new file mode 100644
index 0000000..3b67130
--- /dev/null
+++ b/cypress.config.js
@@ -0,0 +1,16 @@
+const { defineConfig } = require("cypress");
+
+module.exports = defineConfig({
+ e2e: {
+ setupNodeEvents(on, config) {
+ // implement node event listeners here
+ },
+ },
+
+ component: {
+ devServer: {
+ framework: "next",
+ bundler: "webpack",
+ },
+ },
+});
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
new file mode 100644
index 0000000..66ea16e
--- /dev/null
+++ b/cypress/support/commands.js
@@ -0,0 +1,25 @@
+// ***********************************************
+// This example commands.js shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+// Cypress.Commands.add('login', (email, password) => { ... })
+//
+//
+// -- This is a child command --
+// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
\ No newline at end of file
diff --git a/cypress/support/component-index.html b/cypress/support/component-index.html
new file mode 100644
index 0000000..3e16e9b
--- /dev/null
+++ b/cypress/support/component-index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Components App
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cypress/support/component.js b/cypress/support/component.js
new file mode 100644
index 0000000..8f9154b
--- /dev/null
+++ b/cypress/support/component.js
@@ -0,0 +1,27 @@
+// ***********************************************************
+// This example support/component.js is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
+
+import { mount } from 'cypress/react18'
+
+Cypress.Commands.add('mount', mount)
+
+// Example use:
+// cy.mount()
\ No newline at end of file
diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js
new file mode 100644
index 0000000..0e7290a
--- /dev/null
+++ b/cypress/support/e2e.js
@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/e2e.js is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
\ No newline at end of file
diff --git a/example.env.local b/example.env.local
index b9a7e4c..a2d8ab0 100644
--- a/example.env.local
+++ b/example.env.local
@@ -1,5 +1,5 @@
NEXT_PUBLIC_API_NAME = "Loan Facilities Dashboard"
-NEXT_PUBLIC_API_VERSION = "1.8.10"
+NEXT_PUBLIC_API_VERSION = "1.10.2"
NEXT_PUBLIC_DEFAULT_LANGUAGE = "fa"
NEXT_PUBLIC_DEFAULT_DIRECTION = "rtl"
@@ -7,6 +7,7 @@ NEXT_PUBLIC_PRIMARY_MAIN = "#084070"
NEXT_PUBLIC_SECONDARY_MAIN = "#FF4E00"
NEXT_PUBLIC_BASE_URL = "https://loan.witel.ir"
+NEXT_PUBLIC_POWERED_BY_URL = "https://witel.ir"
#["NEXT_PUBLIC_HAS_WIDGET" , "NEXT_PUBLIC_HAS_NOTIFICATION"]
NEXT_PUBLIC_HAS_VALUE=["NEXT_PUBLIC_HAS_NOTIFICATION"]
diff --git a/jest.config.mjs b/jest.config.mjs
new file mode 100644
index 0000000..5bda02f
--- /dev/null
+++ b/jest.config.mjs
@@ -0,0 +1,17 @@
+import nextJest from 'next/jest.js'
+
+const createJestConfig = nextJest({
+ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
+ dir: './',
+})
+
+// Add any custom config to be passed to Jest
+/** @type {import('jest').Config} */
+const config = {
+ // Add more setup options before each test is run
+ setupFilesAfterEnv: ['/jest.setup.js'],
+ testEnvironment: 'jest-environment-jsdom',
+}
+
+// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
+export default createJestConfig(config)
\ No newline at end of file
diff --git a/jest.setup.js b/jest.setup.js
new file mode 100644
index 0000000..06299aa
--- /dev/null
+++ b/jest.setup.js
@@ -0,0 +1,22 @@
+import '@testing-library/jest-dom'
+import {server} from "./mocks/server";
+import mockRouter from 'next-router-mock'
+
+jest.mock('next/router', () => jest.requireActual('next-router-mock'))
+
+beforeAll(() => {
+ server.listen()
+})
+beforeEach(() => {
+ localStorage.clear();
+ mockRouter.query = {}
+
+})
+
+afterEach(() => {
+ server.resetHandlers()
+})
+
+afterAll(() => {
+ server.close()
+})
\ No newline at end of file
diff --git a/mocks/AppWithProvider.jsx b/mocks/AppWithProvider.jsx
new file mode 100644
index 0000000..404c839
--- /dev/null
+++ b/mocks/AppWithProvider.jsx
@@ -0,0 +1,13 @@
+import App from "@/pages/_app";
+import fa from "&/locales/fa/app.json"
+
+const translations = {fa};
+const MockAppWithProviders = ({children, locale, title, isBot}) => {
+ const pageProps = {
+ title: title || '', isBot: isBot || true, locale: locale || 'fa', messages: translations[locale || 'fa']
+ }
+
+ return ( (<>{children}>)} pageProps={pageProps}/>)
+}
+
+export default MockAppWithProviders
\ No newline at end of file
diff --git a/mocks/handler.js b/mocks/handler.js
new file mode 100644
index 0000000..a4e5e8f
--- /dev/null
+++ b/mocks/handler.js
@@ -0,0 +1,5 @@
+import {userHandler} from "./handlers/user";
+
+export const handler = [
+ ...userHandler
+];
diff --git a/mocks/handlers/user.js b/mocks/handlers/user.js
new file mode 100644
index 0000000..10677de
--- /dev/null
+++ b/mocks/handlers/user.js
@@ -0,0 +1,19 @@
+import {rest} from "msw";
+import {GET_USER_ROUTE} from "@/core/data/apiRoutes";
+
+export const userHandler = [
+ rest.get(GET_USER_ROUTE, (req, res, ctx) => {
+ return res(ctx.json({
+ data: {
+ id: 10,
+ full_name: "Witel Company",
+ position: "Software Engineer",
+ permissions: [
+ "manage_users",
+ "manage_roles",
+ "manage_boss"
+ ],
+ }
+ }))
+ }),
+]
diff --git a/mocks/server.js b/mocks/server.js
new file mode 100644
index 0000000..0bf9e08
--- /dev/null
+++ b/mocks/server.js
@@ -0,0 +1,4 @@
+import {setupServer} from "msw/node";
+import {handler} from "./handler";
+
+export const server = setupServer(...handler)
\ No newline at end of file
diff --git a/package.json b/package.json
index 229374d..4583a4c 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,11 @@
"lint": "next lint",
"update": "run-script-os",
"update:win32": "echo 'ok'",
- "update:linux": "rm -rf .next/ && npm i && npm run build && sudo systemctl restart loan_facilities_develop_expert.service"
+ "update:linux": "rm -rf .next/ && npm i && npm run build && sudo systemctl restart loan_facilities_develop_expert.service",
+ "test": "jest",
+ "test:watch": "jest --watchAll",
+ "cypress:open": "cypress open",
+ "cypress:run": "cypress run"
},
"dependencies": {
"@emotion/react": "^11.10.6",
@@ -51,7 +55,17 @@
},
"devDependencies": {
"@faker-js/faker": "^7.6.0",
- "eslint-config-next": "^13.3.0",
+ "@testing-library/jest-dom": "^6.1.4",
+ "@testing-library/react": "^14.0.0",
+ "@testing-library/user-event": "^14.5.1",
+ "cypress": "^13.4.0",
+ "eslint-config-next": "^13.5.6",
+ "eslint-plugin-jest-dom": "^5.1.0",
+ "eslint-plugin-testing-library": "^6.1.0",
+ "jest": "^29.7.0",
+ "jest-environment-jsdom": "^29.7.0",
+ "msw": "^1.3.1",
+ "next-router-mock": "^0.9.10",
"run-script-os": "^1.1.6"
}
}
diff --git a/public/fontiran.scss b/public/fontiran.scss
index 86d585d..5ee0036 100644
--- a/public/fontiran.scss
+++ b/public/fontiran.scss
@@ -167,3 +167,10 @@ This set of fonts are used in this project under the license: (.....)
url('./fonts/woff/Parastoo-Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/Parastoo-Bold.ttf') format('truetype');
}
+
+@font-face {
+ font-family: Bnazanin;
+ font-style: normal;
+ font-weight: normal;
+ src: url('./fonts/ttf/BNazanin.ttf') format('truetype');
+}
diff --git a/public/fonts/ttf/BNazanin.ttf b/public/fonts/ttf/BNazanin.ttf
new file mode 100644
index 0000000..4e59251
Binary files /dev/null and b/public/fonts/ttf/BNazanin.ttf differ
diff --git a/public/images/logo.png b/public/images/logo.png
new file mode 100644
index 0000000..075e3fe
Binary files /dev/null and b/public/images/logo.png differ
diff --git a/public/images/logo.svg b/public/images/logo.svg
deleted file mode 100644
index eebb9fa..0000000
--- a/public/images/logo.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-
diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json
index da7e0ee..b36f081 100644
--- a/public/locales/fa/app.json
+++ b/public/locales/fa/app.json
@@ -2,6 +2,7 @@
"app_name": "سامانه جامع تسهیلات",
"app_short_name": "سامانه تسهیلات",
"dashboard": "داشبورد",
+ "powered_by_witel": "توسعه یافته توسط وایتل",
"first_page": "خوش آمدید",
"login": "ورود",
"login_expert": "ورود کارشناس",
@@ -15,6 +16,11 @@
"between": "میان",
"online_message": "شما به اینترنت وصل هستید",
"offline_message": "اتصال شما به اینترنت قطع شده است",
+ "routing_to": "در حال انتقال به صفحه",
+ "data_not_found": "اطلاعاتی یافت نشد!",
+ "btn_print": "چاپ",
+ "print_loading": "درحال پردازش اطلاعات ...",
+ "button_back_to_previous": "بازگشت به صفحه قبل",
"header": {
"open_profile": "پروفایل",
"edit_profile": " پروفایل",
@@ -161,7 +167,10 @@
"updated_at": "تاریخ بروزرسانی",
"navgan_id": "کد ناوگان",
"vehicle_type": "نوع ماشین",
- "state_name": "وضعیت درخواست"
+ "state_name": "وضعیت درخواست",
+ "confirm": "ارجاع به کارشناس ماشین آلات",
+ "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
+ "upload_file_format": "فرمت قابل قبول : png,jpg,pdf"
},
"CommercialChief": {
"name": "نام",
@@ -183,7 +192,10 @@
"updated_at": "تاریخ بروزرسانی",
"navgan_id": "کد ناوگان",
"vehicle_type": "نوع ماشین",
- "state_name": "وضعیت درخواست"
+ "state_name": "وضعیت درخواست",
+ "confirm": "تایید نهایی و ارجاع به بانک",
+ "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
+ "upload_file_format": "فرمت قابل قبول : png,jpg,pdf"
},
"PassengerBoss": {
"name": "نام",
@@ -194,7 +206,14 @@
"updated_at": "تاریخ بروزرسانی",
"navgan_id": "کد ناوگان",
"vehicle_type": "نوع ماشین",
- "state_name": "وضعیت درخواست"
+ "state_name": "وضعیت درخواست",
+ "confirm": "ارجاع به معاون حمل و نقل",
+ "upload_file": "صورت جلسه کارگروه استانی را بارگذاری کنید",
+ "upload_file_required": "وارد کردن صورت جلسه کارگروه استانی الزامیست",
+ "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
+ "upload_file_format": "فرمت قابل قبول : png,jpg,pdf",
+ "print_final_credit_amount": "چاپ صورت جلسه تعیین مبلغ نهایی",
+ "max_amount": "حداکثر مبلغ تصویب شده {value} میلیون ریال می باشد"
},
"RefahiProvinceManager": {
"name": "نام",
@@ -262,7 +281,10 @@
"updated_at": "تاریخ بروزرسانی",
"navgan_id": "کد ناوگان",
"vehicle_type": "نوع ماشین",
- "state_name": "وضعیت درخواست"
+ "state_name": "وضعیت درخواست",
+ "confirm": "ارجاع به مدیر کل استانی",
+ "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
+ "upload_file_format": "فرمت قابل قبول : png,jpg,pdf"
},
"InspectorExpert": {
"name": "نام",
@@ -284,7 +306,14 @@
"updated_at": "تاریخ بروزرسانی",
"navgan_id": "کد ناوگان",
"vehicle_type": "نوع ماشین",
- "state_name": "وضعیت درخواست"
+ "state_name": "وضعیت درخواست",
+ "confirm": "ارجاع به کارگروه استانی",
+ "upload_file": "گزارش کارشناسی را بارگذاری کنید",
+ "upload_file_required": "وارد کردن گزارش کارشناسی الزامیست",
+ "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد",
+ "upload_file_format": "فرمت قابل قبول : png,jpg,pdf",
+ "print_report": "چاپ گزارش کارشناسی ماشین آلات",
+ "max_amount": "حداکثر مبلغ پیشنهادی {value} میلیون ریال می باشد"
},
"UserManagement": {
"name": "نام",
@@ -306,23 +335,24 @@
"updated_at": "تاریخ بروزرسانی"
},
"ConfirmDialog": {
- "confirm": "تایید",
+ "confirm": "ارجاع",
"context": "آیا از تایید این آیتم اطمینان دارید؟",
"button-cancel": "بستن",
- "button-confirm": "تایید",
+ "button-confirm": "ارجاع",
"description_error": "وارد کردن توضیحات الزامی است!",
"optional": " (اختیاری)",
- "unit": " (ریال)",
+ "unit": " (میلیون ریال)",
"description": "توضیحات خود را وارد نمائید",
"choose_file": "انتخاب فایل",
"approved_amount": "مبلغ تصویب شده",
"approved_amount_error": "وارد کردن مبلغ تصویب شده الزامیست",
"approved_amount_positive": "مبلغ تصویب شده باید مثبت باشد",
"approved_amount_number": "مبلغ تصویب شده باید عدد باشد",
- "toman": "تومان",
+ "toman": "میلیون تومان",
"proposed_amount_error": "وارد کردن مقدار پیشنهادی الزامیست",
"proposed_amount": "مقدار پیشنهادی",
- "proposed_amount_positive": "مقدار پیشنهادی باید مثبت باشد"
+ "proposed_amount_positive": "مقدار پیشنهادی باید مثبت باشد",
+ "proposed_amount_number": "مقدار پیشنهادی باید عدد باشد"
},
"ReviseDialog": {
"revise": " نیاز به ویرایش",
@@ -361,7 +391,8 @@
"permission_min_error": "حداقل باید یک دسترسی انتخاب شود",
"type_id": "نوع کاربر",
"navgan_id": "کد ناوگان",
- "button-update": "ویرایش"
+ "button-update": "ویرایش",
+ "loading_permissions_list": "درحال دریافت لیست دسترسی ها"
},
"UploadSystem": {
"upload_file": "فایل خود را بارگذاری کنید",
@@ -468,7 +499,8 @@
"type_id": "نوع کاربر",
"navgan_id": "کد ناوگان",
"button-cancel": "انصراف",
- "button-add": "ثبت"
+ "button-add": "ثبت",
+ "loading_permissions_list": "درحال دریافت لیست دسترسی ها"
},
"reports": {
"loan_progress": "درصد پیشرفت تسویه وام",
diff --git a/public/print.scss b/public/print.scss
new file mode 100644
index 0000000..990a647
--- /dev/null
+++ b/public/print.scss
@@ -0,0 +1,9 @@
+@media print {
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ @page {
+ size: A4;
+ }
+}
\ No newline at end of file
diff --git a/src/components/dashboard/commercial-chief/TableTollbar.jsx b/src/components/dashboard/commercial-chief/TableTollbar.jsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/components/dashboard/development-assistant/TableTollbar.jsx b/src/components/dashboard/development-assistant/TableTollbar.jsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/components/dashboard/inspector-expert/TableTollbar.jsx b/src/components/dashboard/inspector-expert/TableTollbar.jsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/components/dashboard/machinary-office/Buttons/printReport.jsx b/src/components/dashboard/machinary-office/Buttons/printReport.jsx
new file mode 100644
index 0000000..47aab16
--- /dev/null
+++ b/src/components/dashboard/machinary-office/Buttons/printReport.jsx
@@ -0,0 +1,48 @@
+import PrintIcon from "@mui/icons-material/Print";
+import {Button, CircularProgress} from "@mui/material";
+import {useTranslations} from "next-intl";
+import {useRouter} from "next/router";
+import {useState} from "react";
+import useRequest from "@/lib/app/hooks/useRequest";
+import {GET_MACHINARY_OFFICE} from "@/core/data/apiRoutes";
+import usePrint from "@/lib/app/hooks/usePrint";
+
+const PrintReport = () => {
+ const t = useTranslations();
+ const router = useRouter()
+ const [loading, setLoading] = useState(false)
+ const requestServer = useRequest({auth: true, pending: false, success: {notification: {show: false}}})
+ const {setPrintPage, setPrintTitle} = usePrint()
+ const clickHandler = () => {
+ setLoading(true)
+ const params = new URLSearchParams();
+ params.set("start", '0');
+ params.set("filters", '[]');
+ params.set("sorting", '[]');
+ requestServer(`${GET_MACHINARY_OFFICE}?${params}`, 'get').then((response) => {
+ const _data = response.data.data
+ setPrintPage(_data.length)
+ setPrintTitle(t('MachinaryOffice.print_report'))
+ sessionStorage.setItem('report-print', JSON.stringify(_data))
+ router.push('/dashboard/machinery-expert/prints/report')
+ }).catch(() => {
+ setLoading(false)
+ })
+ }
+
+ return (
+ : }
+ onClick={clickHandler}
+ >
+ {t("MachinaryOffice.print_report")}
+
+ )
+}
+
+export default PrintReport
\ No newline at end of file
diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx
index 261c083..16b7ca3 100644
--- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx
+++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx
@@ -1,16 +1,19 @@
import UploadSystem from "@/core/components/UploadSystem";
import useNotification from "@/lib/app/hooks/useNotification";
import useRequest from "@/lib/app/hooks/useRequest";
-import {Button, DialogActions, DialogContent, Stack, TextField, Typography} from "@mui/material"
+import {Button, DialogActions, DialogContent, FormHelperText, Stack, TextField, Typography} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {CONFIRM_MACHINARY_OFFICE} from "@/core/data/apiRoutes";
-import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import {useState} from "react";
import PriceField from "@/core/components/PriceField";
import * as Yup from "yup";
-const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
+const vehicle_amount = {
+ "اتوبوس": 7000,
+ "مینی بوس": 2000,
+}
+const ConfirmContent = ({mutate, setOpenConfirmDialog, rowId, vehicle_type}) => {
const t = useTranslations();
const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null);
@@ -18,14 +21,24 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const [showAddIcon, setShowAddIcon] = useState(true);
const requestServer = useRequest({auth: true})
const {update_notification} = useNotification()
-
const validationSchema = Yup.object().shape({
proposed_amount: Yup.mixed().test(
"is-number",
`${t("ConfirmDialog.proposed_amount_number")}`,
(value) => !isNaN(value)
- ).test("positive", `${t("ConfirmDialog.proposed_amount_positive")}`, (value) => value >= 0)
- .required(t("ConfirmDialog.proposed_amount_error"))
+ ).test("max-amount", `${t("MachinaryOffice.max_amount", {value: parseInt(vehicle_amount[vehicle_type]).toLocaleString('en')})}`,
+ (value) => value <= vehicle_amount[vehicle_type])
+ .test("positive", `${t("ConfirmDialog.proposed_amount_positive")}`, (value) => value >= 0)
+ .required(t("ConfirmDialog.proposed_amount_error")),
+ confirm_img: Yup.mixed()
+ .required(t("MachinaryOffice.upload_file_required"))
+ .test('fileSize', `${t("MachinaryOffice.upload_file_unit")}`, (value) => {
+ return value.size <= 2 * 1024 * 1024;
+ })
+ .test('fileType', `${t("MachinaryOffice.upload_file_format")}`, (value) => {
+ const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf'];
+ return allowedTypes.includes(value.type);
+ })
});
const formik = useFormik({
@@ -37,9 +50,8 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
formData.append("proposed_amount", values.proposed_amount);
- if (values.description != "") formData.append("description", values.description);
+ if (values.description != "") formData.append("expert_description", values.description);
if (values.confirm_img != null) formData.append("attachment", values.confirm_img);
-
requestServer(`${CONFIRM_MACHINARY_OFFICE}/${rowId}`, 'post', {
data: formData,
}).then((response) => {
@@ -60,19 +72,14 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
- const maxFileSize = 2 * 1024 * 1024;
- if (uploadedFile.size > maxFileSize) {
- UploadFileNotification(t);
- event.target.value = "";
- return;
- }
-
const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType);
setfileName(fileName);
- formik.setFieldValue("confirm_img", uploadedFile);
+ formik.setFieldValue("confirm_img", uploadedFile).then(() => {
+ formik.setFieldTouched("confirm_img", true);
+ })
setShowAddIcon(false);
}
};
@@ -129,7 +136,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
/>
- {t("UploadSystem.upload_file")}{t("UploadSystem.optional")}
+ {t("MachinaryOffice.upload_file")}
{
fileName={fileName}
imageAlt={t("app_name")}
imageSize={[250, 150]}
+ onBlur={formik.handleBlur("confirm_img")}
+ error={
+ formik.touched.confirm_img &&
+ Boolean(formik.errors.confirm_img)
+ }
setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon}
/>
+
+ {formik.touched.confirm_img && formik.errors.confirm_img}
+
diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx
index 95a6367..3affc7b 100644
--- a/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx
+++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx
@@ -1,10 +1,10 @@
import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material";
import {useTranslations} from "next-intl";
-import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import {useState} from "react";
import ConfirmContent from "./ConfirmContent";
+import CallMadeIcon from '@mui/icons-material/CallMade';
-const Confirm = ({rowId, mutate}) => {
+const Confirm = ({rowId, mutate, vehicle_type}) => {
const t = useTranslations();
const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
return (
@@ -16,13 +16,14 @@ const Confirm = ({rowId, mutate}) => {
setOpenConfirmDialog(true)
}}
>
-
+
>
)
diff --git a/src/components/dashboard/machinary-office/Form/RejectForm/RejectContent.jsx b/src/components/dashboard/machinary-office/Form/RejectForm/RejectContent.jsx
index 340a757..479b213 100644
--- a/src/components/dashboard/machinary-office/Form/RejectForm/RejectContent.jsx
+++ b/src/components/dashboard/machinary-office/Form/RejectForm/RejectContent.jsx
@@ -6,7 +6,7 @@ import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {useState} from "react";
import * as Yup from "yup";
-import {REJECT_INSPECTOR_EXPERT} from "@/core/data/apiRoutes";
+import {REJECT_MACHINARY_OFFICE} from "@/core/data/apiRoutes";
const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
const [selectedImage, setSelectedImage] = useState("");
@@ -29,10 +29,10 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
validationSchema,
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
- formData.append("description", values.description);
+ formData.append("expert_description", values.description);
if (values.reject_img != null) formData.append("attachment", values.reject_img);
- requestServer(`${REJECT_INSPECTOR_EXPERT}/${rowId}`, 'post', {
+ requestServer(`${REJECT_MACHINARY_OFFICE}/${rowId}`, 'post', {
data: formData,
}).then((response) => {
setOpenRejectDialog(false)
diff --git a/src/components/dashboard/machinary-office/Prints/report/Content.jsx b/src/components/dashboard/machinary-office/Prints/report/Content.jsx
new file mode 100644
index 0000000..8098ecb
--- /dev/null
+++ b/src/components/dashboard/machinary-office/Prints/report/Content.jsx
@@ -0,0 +1,96 @@
+import PrintablePage from "@/core/components/PrintablePage";
+import {Box, Stack, Typography} from "@mui/material";
+
+const Content = ({data}) => {
+ return (
+ <>
+
+
+ {'گزارش کارشناسی ماشین آلات'}
+
+
+ {'با توجه به نامه شماره'}
+ 35482
+ {'مورخ'}
+ 1402/08/20
+ {'معاون محترم برنامه ریزی سازمان راهداری و حمل و نقل جاده ای در خصوص تسهیلات بند (الف) تبصره (18) قانون بودجه سال'}
+ 1402
+ {'موضوع بازسازی ناوگان اتوبوس و مینی بوس و نیز تصویب آن درشورای برنامه ریزی و توسعه استان'}
+ تهران
+ {'و نامه شماره'}
+ 35482
+ {'مورخ'}
+ 1402/08/20
+ {'اداره کل راهداری و حمل و نقل جاده ای'}
+ تهران
+ {'در خصوص استفاده از ظرفیت انجمن های صنفی رانندگان در تاریخ'}
+ 1402/08/20
+ {'از ناوگان اتوبوس/مینی بوس به شماره پلاک انتظامی'}
+ 25پ4582
+ {'و شماره هوشمند ناوگان'}
+ 5875698725
+ {'به مالکیت آقای'}
+ امیرحسین محمودی
+ {'با کد ملی'}
+ 0311318897
+ {'و شماره تماس'}
+ 09120630655
+ {'ساکن شهرستان'}
+ تهران
+ {'در جلسه مطرح شد که نتیجه آن به شرح ذیل مورد تایید می باشد.'}
+
+
+ {'مبلغ پیشنهادی به عدد .............................................. ریال و به حروف ...................................................................... ریال می باشد.'}
+ {'توضیحات : ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................'}
+
+ {'نام و نام خانوادگی کارشناس:'}
+ {'تاریخ و امضاء'}
+
+
+
+ >
+ )
+}
+
+export default Content
\ No newline at end of file
diff --git a/src/components/dashboard/machinary-office/Prints/report/index.jsx b/src/components/dashboard/machinary-office/Prints/report/index.jsx
new file mode 100644
index 0000000..54dca24
--- /dev/null
+++ b/src/components/dashboard/machinary-office/Prints/report/index.jsx
@@ -0,0 +1,60 @@
+import {useEffect, useState} from "react";
+import CenterLayout from "@/layouts/CenterLayout";
+import {useTranslations} from "next-intl";
+import {Stack, Typography} from "@mui/material";
+import Content from "@/components/dashboard/machinary-office/Prints/report/Content";
+
+const ReportComponent = () => {
+ const t = useTranslations()
+ const [data, setData] = useState(null)
+ const [loading, setLoading] = useState(true)
+ const [isNotData, setIsNotData] = useState(true)
+
+ useEffect(() => {
+ if (sessionStorage.getItem('report-print') === null) {
+ setLoading(false)
+ setIsNotData(true)
+ sessionStorage.setItem('report-print', 'seen')
+ } else if (sessionStorage.getItem('report-print') === 'seen') {
+ setLoading(false)
+ setData(null)
+ setIsNotData(true)
+ } else if (data) {
+ setLoading(false)
+ sessionStorage.setItem('report-print', 'seen')
+ setIsNotData(false)
+ } else {
+ setLoading(true)
+ setData(JSON.parse(sessionStorage.getItem('report-print')))
+ setIsNotData(true)
+ }
+ }, [data]);
+
+ return (
+
+ {loading ? (
+
+
+
+ {t('print_loading')}
+
+
+
+ ) : !data ? (
+
+
+ {t('data_not_found')}
+
+
+ ) : !data.length ? (
+
+
+ {t('data_not_found')}
+
+
+ ) : data.map(item => )}
+
+ )
+}
+
+export default ReportComponent
\ No newline at end of file
diff --git a/src/components/dashboard/machinary-office/TableRowActions.jsx b/src/components/dashboard/machinary-office/TableRowActions.jsx
index 69f9d6f..f50938e 100644
--- a/src/components/dashboard/machinary-office/TableRowActions.jsx
+++ b/src/components/dashboard/machinary-office/TableRowActions.jsx
@@ -3,11 +3,11 @@ import Confirm from "./Form/ConfirmForm";
import Reject from "./Form/RejectForm";
const TableRow = ({row, mutate}) => {
-
return (
-
-
- );
-}
-
-export default TableToolbar;
diff --git a/src/components/dashboard/machinary-office/TableToolbar.jsx b/src/components/dashboard/machinary-office/TableToolbar.jsx
new file mode 100644
index 0000000..9704f95
--- /dev/null
+++ b/src/components/dashboard/machinary-office/TableToolbar.jsx
@@ -0,0 +1,12 @@
+import {Stack} from "@mui/material";
+import PrintReport from "@/components/dashboard/machinary-office/Buttons/printReport";
+
+const TableToolbar = () => {
+ return (
+
+
+
+ )
+}
+
+export default TableToolbar
\ No newline at end of file
diff --git a/src/components/dashboard/machinary-office/index.jsx b/src/components/dashboard/machinary-office/index.jsx
index 820519e..853c065 100644
--- a/src/components/dashboard/machinary-office/index.jsx
+++ b/src/components/dashboard/machinary-office/index.jsx
@@ -6,6 +6,7 @@ import TableRowActions from "./TableRowActions";
import moment from "jalali-moment";
import DataTable from "@/core/components/DataTable";
import MuiDatePicker from "@/core/components/MuiDatePicker";
+import TableToolbar from "@/components/dashboard/machinary-office/TableToolbar";
function DashboardMachinaryOfficeComponent() {
const t = useTranslations();
@@ -160,7 +161,8 @@ function DashboardMachinaryOfficeComponent() {
tableUrl={GET_MACHINARY_OFFICE}
columns={columns}
selectableRow={false}
- enableCustomToolbar={false}
+ enableCustomToolbar={true}
+ CustomToolbar={TableToolbar}
enableLastUpdate={true}
sorting={[{
id: 'id', desc: false
diff --git a/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx
index 5de9eb3..daa4df9 100644
--- a/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx
+++ b/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx
@@ -1,12 +1,12 @@
import UploadSystem from "@/core/components/UploadSystem";
import useNotification from "@/lib/app/hooks/useNotification";
import useRequest from "@/lib/app/hooks/useRequest";
-import {Button, DialogActions, DialogContent, Stack, TextField, Typography} from "@mui/material"
+import {Button, DialogActions, DialogContent, FormHelperText, Stack, TextField, Typography} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {CONFIRM_NAVGAN_PROVINCE_MANAGER} from "@/core/data/apiRoutes";
-import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import {useState} from "react";
+import * as Yup from "yup";
const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const t = useTranslations();
@@ -17,14 +17,27 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const requestServer = useRequest({auth: true})
const {update_notification} = useNotification()
+ const validationSchema = Yup.object().shape({
+ confirm_img: Yup.mixed().notRequired(t("ConfirmDialog.approved_amount_error"))
+ .test('fileSize', `${t("NavganProvinceManager.upload_file_unit")}`, (value) => {
+ if (!value) return true
+ return value.size <= 2 * 1024 * 1024;
+ })
+ .test('fileType', `${t("NavganProvinceManager.upload_file_format")}`, (value) => {
+ if (!value) return true
+ const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types
+ return allowedTypes.includes(value.type);
+ })
+ })
+
const formik = useFormik({
initialValues: {
description: "",
confirm_img: null
- },
+ }, validationSchema,
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
- if (values.description != "") formData.append("description", values.description);
+ if (values.description != "") formData.append("expert_description", values.description);
if (values.confirm_img != null) formData.append("attachment", values.confirm_img);
requestServer(`${CONFIRM_NAVGAN_PROVINCE_MANAGER}/${rowId}`, 'post', {
@@ -47,23 +60,17 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
- const maxFileSize = 2 * 1024 * 1024;
- if (uploadedFile.size > maxFileSize) {
- UploadFileNotification(t);
- event.target.value = "";
- return;
- }
-
const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType);
setfileName(fileName);
- formik.setFieldValue("confirm_img", uploadedFile);
+ formik.setFieldValue("confirm_img", uploadedFile).then(() => {
+ formik.setFieldTouched("confirm_img", true);
+ })
setShowAddIcon(false);
}
};
-
return (
<>
@@ -96,7 +103,17 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
imageSize={[250, 150]}
setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon}
+ onBlur={() => formik.handleBlur("confirm_img")}
+ error={
+ formik.touched.confirm_img &&
+ Boolean(formik.errors.confirm_img)
+ }
/>
+
+ {formik.touched.confirm_img && formik.errors.confirm_img}
+
@@ -106,7 +123,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
{t("ConfirmDialog.button-cancel")}
diff --git a/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/index.jsx b/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/index.jsx
index 95a6367..2d983db 100644
--- a/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/index.jsx
+++ b/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/index.jsx
@@ -1,9 +1,8 @@
import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material";
import {useTranslations} from "next-intl";
-import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import {useState} from "react";
import ConfirmContent from "./ConfirmContent";
-
+import CallMadeIcon from '@mui/icons-material/CallMade';
const Confirm = ({rowId, mutate}) => {
const t = useTranslations();
const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
@@ -16,12 +15,12 @@ const Confirm = ({rowId, mutate}) => {
setOpenConfirmDialog(true)
}}
>
-
+
>
diff --git a/src/components/dashboard/navgan-province-manager/Form/RejectForm/RejectContent.jsx b/src/components/dashboard/navgan-province-manager/Form/RejectForm/RejectContent.jsx
index 2502898..0b26c8b 100644
--- a/src/components/dashboard/navgan-province-manager/Form/RejectForm/RejectContent.jsx
+++ b/src/components/dashboard/navgan-province-manager/Form/RejectForm/RejectContent.jsx
@@ -29,7 +29,7 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
validationSchema,
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
- formData.append("description", values.description);
+ formData.append("expert_description", values.description);
if (values.reject_img != null) formData.append("attachment", values.reject_img);
requestServer(`${REJECT_NAVGAN_PROVINCE_MANAGER}/${rowId}`, 'post', {
diff --git a/src/components/dashboard/navgan-province-manager/TableTollbar.jsx b/src/components/dashboard/navgan-province-manager/TableTollbar.jsx
deleted file mode 100644
index e267691..0000000
--- a/src/components/dashboard/navgan-province-manager/TableTollbar.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {Stack, Tooltip} from "@mui/material";
-import {useTranslations} from "next-intl";
-
-function TableToolbar() {
- const t = useTranslations();
- return (
-
-
-
- );
-}
-
-export default TableToolbar;
diff --git a/src/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount.jsx b/src/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount.jsx
new file mode 100644
index 0000000..58a97af
--- /dev/null
+++ b/src/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount.jsx
@@ -0,0 +1,48 @@
+import PrintIcon from "@mui/icons-material/Print";
+import {Button, CircularProgress} from "@mui/material";
+import {useTranslations} from "next-intl";
+import {useRouter} from "next/router";
+import {useState} from "react";
+import useRequest from "@/lib/app/hooks/useRequest";
+import {GET_PASSENGER_BOSS} from "@/core/data/apiRoutes";
+import usePrint from "@/lib/app/hooks/usePrint";
+
+const PrintFinalCreditAmount = () => {
+ const t = useTranslations();
+ const router = useRouter()
+ const [loading, setLoading] = useState(false)
+ const requestServer = useRequest({auth: true, pending: false, success: {notification: {show: false}}})
+ const {setPrintPage, setPrintTitle} = usePrint()
+ const clickHandler = () => {
+ setLoading(true)
+ const params = new URLSearchParams();
+ params.set("start", '0');
+ params.set("filters", '[]');
+ params.set("sorting", '[]');
+ requestServer(`${GET_PASSENGER_BOSS}?${params}`, 'get').then((response) => {
+ const _data = response.data.data
+ setPrintPage(_data.length)
+ setPrintTitle(t('PassengerBoss.print_final_credit_amount'))
+ sessionStorage.setItem('final-credit-amount-print', JSON.stringify(_data))
+ router.push('/dashboard/passenger-boss/prints/final-credit-amount')
+ }).catch(() => {
+ setLoading(false)
+ })
+ }
+
+ return (
+ : }
+ onClick={clickHandler}
+ >
+ {t("PassengerBoss.print_final_credit_amount")}
+
+ )
+}
+
+export default PrintFinalCreditAmount
\ No newline at end of file
diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx
index 66ccd27..f0a8c12 100644
--- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx
+++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx
@@ -1,16 +1,19 @@
import UploadSystem from "@/core/components/UploadSystem";
import useNotification from "@/lib/app/hooks/useNotification";
import useRequest from "@/lib/app/hooks/useRequest";
-import {Button, DialogActions, DialogContent, Stack, TextField, Typography} from "@mui/material"
+import {Button, DialogActions, DialogContent, FormHelperText, Stack, TextField, Typography} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {CONFIRM_PASSENGER_BOSS} from "@/core/data/apiRoutes";
-import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import {useState} from "react";
import PriceField from "@/core/components/PriceField";
import * as Yup from "yup";
-const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
+const vehicle_amount = {
+ "اتوبوس": 7000,
+ "مینی بوس": 2000,
+}
+const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog, vehicle_type}) => {
const t = useTranslations();
const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null);
@@ -24,8 +27,19 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
"is-number",
`${t("ConfirmDialog.approved_amount_number")}`,
(value) => !isNaN(value)
- ).test("positive", `${t("ConfirmDialog.approved_amount_positive")}`, (value) => value >= 0)
- .required(t("ConfirmDialog.approved_amount_error"))
+ ).test("max-amount", `${t("PassengerBoss.max_amount", {value: parseInt(vehicle_amount[vehicle_type]).toLocaleString('en')})}`,
+ (value) => value <= vehicle_amount[vehicle_type])
+ .test("positive", `${t("ConfirmDialog.approved_amount_positive")}`, (value) => value >= 0)
+ .required(t("ConfirmDialog.approved_amount_error")),
+ confirm_img: Yup.mixed()
+ .required(t("PassengerBoss.upload_file_required"))
+ .test('fileSize', `${t("PassengerBoss.upload_file_unit")}`, (value) => {
+ return value.size <= 2 * 1024 * 1024;
+ })
+ .test('fileType', `${t("PassengerBoss.upload_file_format")}`, (value) => {
+ const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types
+ return allowedTypes.includes(value.type);
+ })
});
const formik = useFormik({
@@ -37,8 +51,8 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
formData.append("approved_amount", values.approved_amount);
- if (values.description != "") formData.append("description", values.description);
- if (values.confirm_img != null) formData.append("attachment", values.confirm_img);
+ if (values.description != "") formData.append("expert_description", values.description);
+ formData.append("attachment", values.confirm_img);
requestServer(`${CONFIRM_PASSENGER_BOSS}/${rowId}`, 'post', {
data: formData,
@@ -60,19 +74,14 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
- const maxFileSize = 2 * 1024 * 1024;
- if (uploadedFile.size > maxFileSize) {
- UploadFileNotification(t);
- event.target.value = "";
- return;
- }
-
const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType);
setfileName(fileName);
- formik.setFieldValue("confirm_img", uploadedFile);
+ formik.setFieldValue("confirm_img", uploadedFile).then(() => {
+ formik.setFieldTouched("confirm_img", true);
+ })
setShowAddIcon(false);
}
};
@@ -83,7 +92,6 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
formik.setFieldValue("approved_amount", formik.values.approved_amount)
}
};
-
return (
<>
@@ -129,7 +137,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
/>
- {t("UploadSystem.upload_file")}{t("UploadSystem.optional")}
+ {t("PassengerBoss.upload_file")}
{
imageSize={[250, 150]}
setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon}
+ onBlur={() => formik.handleBlur("confirm_img")}
+ error={
+ formik.touched.confirm_img &&
+ Boolean(formik.errors.confirm_img)
+ }
/>
+
+ {formik.touched.confirm_img && formik.errors.confirm_img}
+
diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx
index 95a6367..004d119 100644
--- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx
+++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx
@@ -1,10 +1,10 @@
import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material";
import {useTranslations} from "next-intl";
-import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import {useState} from "react";
import ConfirmContent from "./ConfirmContent";
+import CallMadeIcon from '@mui/icons-material/CallMade';
-const Confirm = ({rowId, mutate}) => {
+const Confirm = ({rowId, mutate, vehicle_type}) => {
const t = useTranslations();
const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
return (
@@ -16,13 +16,14 @@ const Confirm = ({rowId, mutate}) => {
setOpenConfirmDialog(true)
}}
>
-
+
>
)
diff --git a/src/components/dashboard/passenger-boss/Form/RejectForm/RejectContent.jsx b/src/components/dashboard/passenger-boss/Form/RejectForm/RejectContent.jsx
index 6b6b385..93fe8c3 100644
--- a/src/components/dashboard/passenger-boss/Form/RejectForm/RejectContent.jsx
+++ b/src/components/dashboard/passenger-boss/Form/RejectForm/RejectContent.jsx
@@ -29,7 +29,7 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
validationSchema,
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
- formData.append("description", values.description);
+ formData.append("expert_description", values.description);
if (values.reject_img != null) formData.append("attachment", values.reject_img);
requestServer(`${REJECT_PASSENGER_BOSS}/${rowId}`, 'post', {
diff --git a/src/components/dashboard/passenger-boss/Prints/final-credit-amount/Content.jsx b/src/components/dashboard/passenger-boss/Prints/final-credit-amount/Content.jsx
new file mode 100644
index 0000000..e9844ee
--- /dev/null
+++ b/src/components/dashboard/passenger-boss/Prints/final-credit-amount/Content.jsx
@@ -0,0 +1,123 @@
+import PrintablePage from "@/core/components/PrintablePage";
+import {Box, Grid, Stack, Typography} from "@mui/material";
+
+const Content = ({data}) => {
+ return (
+ <>
+
+
+ {'فرم صورتجلسه تعیین مبلغ نهایی اعتبار'}
+
+
+ {'با توجه به نامه شماره'}
+ 35482
+ {'مورخ'}
+ 1402/08/20
+ {'معاون محترم برنامه ریزی سازمان راهداری و حمل و نقل جاده ای در خصوص تسهیلات بند (الف) تبصره (18) قانون بودجه سال'}
+ 1402
+ {'موضوع بازسازی ناوگان اتوبوس و مینی بوس و نیز تصویب آن درشورای برنامه ریزی و توسعه استان'}
+ تهران
+ {'و نامه شماره'}
+ 35482
+ {'مورخ'}
+ 1402/08/20
+ {'اداره کل راهداری و حمل و نقل جاده ای'}
+ تهران
+ {'در خصوص استفاده از ظرفیت انجمن های صنفی رانندگان در تاریخ'}
+ 1402/08/20
+ {'از ناوگان اتوبوس/مینی بوس به شماره پلاک انتظامی'}
+ 25پ4582
+ {'و شماره هوشمند ناوگان'}
+ 5875698725
+ {'به مالکیت آقای'}
+ امیرحسین محمودی
+ {'با کد ملی'}
+ 0311318897
+ {'و شماره تماس'}
+ 09120630655
+ {'ساکن شهرستان'}
+ تهران
+ {'در جلسه مطرح شد که نتیجه آن به شرح ذیل مورد تایید می باشد.'}
+
+
+ {'با توجه به گزارش واصله از اداره ماشین آلات (نامه در پیوست موجود می باشد) جهت بازسازی ناوگان مذکور مبلغ .............................................. ریال معادل .............................................. تومان مورد تایید می باشد.'}
+
+
+
+
+ رئیس انجمن
+
+
+
+
+ رئیس اداره مسافر
+
+
+
+
+
+
+
+
+
+
+ امضاء
+
+
+
+
+ امضاء
+
+
+
+
+ امضاء
+
+
+
+
+ امضاء
+
+
+
+
+ >
+ )
+}
+
+export default Content
\ No newline at end of file
diff --git a/src/components/dashboard/passenger-boss/Prints/final-credit-amount/index.jsx b/src/components/dashboard/passenger-boss/Prints/final-credit-amount/index.jsx
new file mode 100644
index 0000000..9fd6a78
--- /dev/null
+++ b/src/components/dashboard/passenger-boss/Prints/final-credit-amount/index.jsx
@@ -0,0 +1,60 @@
+import {useEffect, useState} from "react";
+import CenterLayout from "@/layouts/CenterLayout";
+import {useTranslations} from "next-intl";
+import {Stack, Typography} from "@mui/material";
+import Content from "@/components/dashboard/passenger-boss/Prints/final-credit-amount/Content";
+
+const FinalCreditAmountComponent = () => {
+ const t = useTranslations()
+ const [data, setData] = useState(null)
+ const [loading, setLoading] = useState(true)
+ const [isNotData, setIsNotData] = useState(true)
+
+ useEffect(() => {
+ if (sessionStorage.getItem('final-credit-amount-print') === null) {
+ setLoading(false)
+ setIsNotData(true)
+ sessionStorage.setItem('final-credit-amount-print', 'seen')
+ } else if (sessionStorage.getItem('final-credit-amount-print') === 'seen') {
+ setLoading(false)
+ setData(null)
+ setIsNotData(true)
+ } else if (data) {
+ setLoading(false)
+ sessionStorage.setItem('final-credit-amount-print', 'seen')
+ setIsNotData(false)
+ } else {
+ setLoading(true)
+ setData(JSON.parse(sessionStorage.getItem('final-credit-amount-print')))
+ setIsNotData(true)
+ }
+ }, [data]);
+
+ return (
+
+ {loading ? (
+
+
+
+ {t('print_loading')}
+
+
+
+ ) : !data ? (
+
+
+ {t('data_not_found')}
+
+
+ ) : !data.length ? (
+
+
+ {t('data_not_found')}
+
+
+ ) : data.map(item => )}
+
+ )
+}
+
+export default FinalCreditAmountComponent
\ No newline at end of file
diff --git a/src/components/dashboard/passenger-boss/TableRowActions.jsx b/src/components/dashboard/passenger-boss/TableRowActions.jsx
index d10e248..cef474d 100644
--- a/src/components/dashboard/passenger-boss/TableRowActions.jsx
+++ b/src/components/dashboard/passenger-boss/TableRowActions.jsx
@@ -8,6 +8,7 @@ const TableRow = ({row, mutate}) => {
-
-
- );
-}
-
-export default TableToolbar;
diff --git a/src/components/dashboard/passenger-boss/TableToolbar.jsx b/src/components/dashboard/passenger-boss/TableToolbar.jsx
new file mode 100644
index 0000000..ed224f4
--- /dev/null
+++ b/src/components/dashboard/passenger-boss/TableToolbar.jsx
@@ -0,0 +1,12 @@
+import {Stack} from "@mui/material";
+import PrintFinalCreditAmount from "@/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount";
+
+const TableToolbar = () => {
+ return (
+
+
+
+ )
+}
+
+export default TableToolbar
\ No newline at end of file
diff --git a/src/components/dashboard/passenger-boss/index.jsx b/src/components/dashboard/passenger-boss/index.jsx
index c338f6e..1a13b3b 100644
--- a/src/components/dashboard/passenger-boss/index.jsx
+++ b/src/components/dashboard/passenger-boss/index.jsx
@@ -6,6 +6,7 @@ import TableRowActions from "./TableRowActions";
import moment from "jalali-moment";
import DataTable from "@/core/components/DataTable";
import MuiDatePicker from "@/core/components/MuiDatePicker";
+import TableToolbar from "@/components/dashboard/passenger-boss/TableToolbar";
function DashboardPassengerOfficeComponent() {
const t = useTranslations();
@@ -161,7 +162,8 @@ function DashboardPassengerOfficeComponent() {
tableUrl={GET_PASSENGER_BOSS}
columns={columns}
selectableRow={false}
- enableCustomToolbar={false}
+ enableCustomToolbar={true}
+ CustomToolbar={TableToolbar}
enableLastUpdate={true}
sorting={[{
id: 'id', desc: false
diff --git a/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx
index 46a735f..d86c4f2 100644
--- a/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx
+++ b/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx
@@ -1,12 +1,12 @@
import UploadSystem from "@/core/components/UploadSystem";
import useNotification from "@/lib/app/hooks/useNotification";
import useRequest from "@/lib/app/hooks/useRequest";
-import {Button, DialogActions, DialogContent, Stack, TextField, Typography} from "@mui/material"
+import {Button, DialogActions, DialogContent, FormHelperText, Stack, TextField, Typography} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {CONFIRM_PASSENGER_OFFICE} from "@/core/data/apiRoutes";
-import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import {useState} from "react";
+import * as Yup from "yup";
const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const t = useTranslations();
@@ -17,11 +17,24 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const requestServer = useRequest({auth: true})
const {update_notification} = useNotification()
+ const validationSchema = Yup.object().shape({
+ confirm_img: Yup.mixed().notRequired(t("ConfirmDialog.approved_amount_error"))
+ .test('fileSize', `${t("PassengerOffice.upload_file_unit")}`, (value) => {
+ if (!value) return true
+ return value.size <= 2 * 1024 * 1024;
+ })
+ .test('fileType', `${t("PassengerOffice.upload_file_format")}`, (value) => {
+ if (!value) return true
+ const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types
+ return allowedTypes.includes(value.type);
+ })
+ })
+
const formik = useFormik({
initialValues: {
description: "",
confirm_img: null
- },
+ }, validationSchema,
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
if (values.description != "") formData.append("description", values.description);
@@ -47,19 +60,14 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
- const maxFileSize = 2 * 1024 * 1024;
- if (uploadedFile.size > maxFileSize) {
- UploadFileNotification(t);
- event.target.value = "";
- return;
- }
-
const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType);
setfileName(fileName);
- formik.setFieldValue("confirm_img", uploadedFile);
+ formik.setFieldValue("confirm_img", uploadedFile).then(() => {
+ formik.setFieldTouched("confirm_img", true);
+ })
setShowAddIcon(false);
}
};
@@ -96,7 +104,17 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
imageSize={[250, 150]}
setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon}
+ onBlur={() => formik.handleBlur("confirm_img")}
+ error={
+ formik.touched.confirm_img &&
+ Boolean(formik.errors.confirm_img)
+ }
/>
+
+ {formik.touched.confirm_img && formik.errors.confirm_img}
+
@@ -106,7 +124,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
{t("ConfirmDialog.button-cancel")}
diff --git a/src/components/dashboard/passenger-office/Form/ConfirmForm/index.jsx b/src/components/dashboard/passenger-office/Form/ConfirmForm/index.jsx
index 95a6367..fa47fcf 100644
--- a/src/components/dashboard/passenger-office/Form/ConfirmForm/index.jsx
+++ b/src/components/dashboard/passenger-office/Form/ConfirmForm/index.jsx
@@ -1,9 +1,8 @@
import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material";
import {useTranslations} from "next-intl";
-import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import {useState} from "react";
import ConfirmContent from "./ConfirmContent";
-
+import CallMadeIcon from '@mui/icons-material/CallMade';
const Confirm = ({rowId, mutate}) => {
const t = useTranslations();
const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
@@ -16,12 +15,12 @@ const Confirm = ({rowId, mutate}) => {
setOpenConfirmDialog(true)
}}
>
-
+
>
diff --git a/src/components/dashboard/passenger-office/Form/RejectForm/RejectContent.jsx b/src/components/dashboard/passenger-office/Form/RejectForm/RejectContent.jsx
index fd7ea2c..2a8a82d 100644
--- a/src/components/dashboard/passenger-office/Form/RejectForm/RejectContent.jsx
+++ b/src/components/dashboard/passenger-office/Form/RejectForm/RejectContent.jsx
@@ -29,7 +29,7 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
validationSchema,
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
- formData.append("description", values.description);
+ formData.append("expert_description", values.description);
if (values.reject_img != null) formData.append("attachment", values.reject_img);
requestServer(`${REJECT_PASSENGER_OFFICE}/${rowId}`, 'post', {
diff --git a/src/components/dashboard/passenger-office/TableTollbar.jsx b/src/components/dashboard/passenger-office/TableTollbar.jsx
deleted file mode 100644
index e267691..0000000
--- a/src/components/dashboard/passenger-office/TableTollbar.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {Stack, Tooltip} from "@mui/material";
-import {useTranslations} from "next-intl";
-
-function TableToolbar() {
- const t = useTranslations();
- return (
-
-
-
- );
-}
-
-export default TableToolbar;
diff --git a/src/components/dashboard/province-head-expert/TableTollbar.jsx b/src/components/dashboard/province-head-expert/TableTollbar.jsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/components/dashboard/refahi-province-manager/TableTollbar.jsx b/src/components/dashboard/refahi-province-manager/TableTollbar.jsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx
index 45b14ba..e276e5d 100644
--- a/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx
+++ b/src/components/dashboard/role-management/Form/CreateForm/CreateContent.jsx
@@ -1,6 +1,6 @@
import {
Button,
- Checkbox,
+ Checkbox, CircularProgress,
DialogActions,
DialogContent,
FormControl,
@@ -9,7 +9,7 @@ import {
FormLabel,
Grid,
Stack,
- TextField
+ TextField, Typography
} from "@mui/material";
import {useTranslations} from "next-intl";
import useRequest from "@/lib/app/hooks/useRequest";
@@ -21,7 +21,7 @@ import usePermissions from "@/lib/app/hooks/usePermissions";
const CreateContent = ({mutate, setOpenConfirmDialog}) => {
const t = useTranslations();
const requestServer = useRequest({auth: true})
- const {permissions_list} = usePermissions()
+ const {permissions_list, isLoading} = usePermissions()
const validationSchema = Yup.object().shape({
name: Yup.string().required(t("AddDialog.name_error")),
name_fa: Yup.string().required(t("AddDialog.name_fa_error")),
@@ -93,29 +93,40 @@ const CreateContent = ({mutate, setOpenConfirmDialog}) => {
sx={{mt: 2}}
>
{formik.touched.permissions && formik.errors.permissions}
-
- {permissions_list ? (
- permissions_list.map((permission) => (
-
- {
- if (e.target.checked) {
- formik.setFieldValue("permissions", [...formik.values.permissions, permission.id])
- } else {
- formik.setFieldValue("permissions", formik.values.permissions.filter((id) => id !== permission.id))
- }
- }}
+ {isLoading ?
+
+
+ {t("AddDialog.loading_permissions_list")}
+
+ : (
+
+ <>
+ {permissions_list.map((permission, index) => (
+
+ {
+ if (e.target.checked) {
+ formik.setFieldValue("permissions", [...formik.values.permissions, permission.id])
+ } else {
+ formik.setFieldValue("permissions", formik.values.permissions.filter((id) => id !== permission.id))
+ }
+ }}
+ />
+ }
+ label={permission.name_fa}
/>
- }
- label={permission.name_fa}
- />
-
- ))
- ) : null}
-
+
+ ))}
+ >
+
+ )
+ }
diff --git a/src/components/dashboard/role-management/Form/DeleteForm.jsx b/src/components/dashboard/role-management/Form/DeleteForm.jsx
index 78319bd..5f2d169 100644
--- a/src/components/dashboard/role-management/Form/DeleteForm.jsx
+++ b/src/components/dashboard/role-management/Form/DeleteForm.jsx
@@ -24,6 +24,7 @@ const DeleteForm = ({rowId, mutate}) => {
const handleSubmit = () => {
setIsSubmitting(true)
requestServer(`${DELETE_ROLE_MANAGEMENT}/${rowId}`, 'delete').then((response) => {
+ setOpenConfirmDialog(false)
mutate()
update_notification()
}).catch(() => {
diff --git a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx
index 8e13b12..037ed99 100644
--- a/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx
+++ b/src/components/dashboard/role-management/Form/UpdateForm/UpdateContent.jsx
@@ -4,7 +4,7 @@ import usePermissions from "@/lib/app/hooks/usePermissions";
import useRequest from "@/lib/app/hooks/useRequest";
import {
Button,
- Checkbox,
+ Checkbox, CircularProgress,
DialogActions,
DialogContent,
FormControl,
@@ -13,7 +13,7 @@ import {
FormLabel,
Grid,
Stack,
- TextField
+ TextField, Typography
} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
@@ -23,7 +23,7 @@ const UpdateContent = ({row, mutate, setOpenConfirmDialog}) => {
const t = useTranslations();
const requestServer = useRequest({auth: true})
const {update_notification} = useNotification()
- const {permissions_list} = usePermissions()
+ const {permissions_list, isLoading} = usePermissions()
const validationSchema = Yup.object().shape({
name: Yup.string().required(t("UpdateDialog.name_error")),
@@ -93,29 +93,40 @@ const UpdateContent = ({row, mutate, setOpenConfirmDialog}) => {
sx={{mt: 2}}
>
{formik.touched.permissions && formik.errors.permissions}
-
- {permissions_list ? (
- permissions_list.map((permission) => (
-
- {
- if (e.target.checked) {
- formik.setFieldValue("permissions", [...formik.values.permissions, permission.id])
- } else {
- formik.setFieldValue("permissions", formik.values.permissions.filter((id) => id !== permission.id))
- }
- }}
+ {isLoading ?
+
+
+ {t("UpdateDialog.loading_permissions_list")}
+
+ : (
+
+ <>
+ {permissions_list.map((permission) => (
+
+ {
+ if (e.target.checked) {
+ formik.setFieldValue("permissions", [...formik.values.permissions, permission.id])
+ } else {
+ formik.setFieldValue("permissions", formik.values.permissions.filter((id) => id !== permission.id))
+ }
+ }}
+ />
+ }
+ label={permission.name_fa}
/>
- }
- label={permission.name_fa}
- />
-
- ))
- ) : null}
-
+
+ ))}
+ >
+
+ )
+ }
diff --git a/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx
index dc4d093..0dcec2a 100644
--- a/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx
+++ b/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx
@@ -1,12 +1,12 @@
import UploadSystem from "@/core/components/UploadSystem";
import useNotification from "@/lib/app/hooks/useNotification";
import useRequest from "@/lib/app/hooks/useRequest";
-import {Button, DialogActions, DialogContent, Stack, TextField, Typography} from "@mui/material"
+import {Button, DialogActions, DialogContent, FormHelperText, Stack, TextField, Typography} from "@mui/material"
import {useFormik} from "formik";
import {useTranslations} from "next-intl";
import {CONFIRM_TRANSPORTATION_ASSISTANCE} from "@/core/data/apiRoutes";
-import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import {useState} from "react";
+import * as Yup from "yup";
const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const t = useTranslations();
@@ -17,14 +17,27 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const requestServer = useRequest({auth: true})
const {update_notification} = useNotification()
+ const validationSchema = Yup.object().shape({
+ confirm_img: Yup.mixed().notRequired(t("ConfirmDialog.approved_amount_error"))
+ .test('fileSize', `${t("TransportationAssistance.upload_file_unit")}`, (value) => {
+ if (!value) return true
+ return value.size <= 2 * 1024 * 1024;
+ })
+ .test('fileType', `${t("TransportationAssistance.upload_file_format")}`, (value) => {
+ if (!value) return true
+ const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types
+ return allowedTypes.includes(value.type);
+ })
+ })
+
const formik = useFormik({
initialValues: {
description: "",
confirm_img: null
- },
+ }, validationSchema,
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
- if (values.description != "") formData.append("description", values.description);
+ if (values.description != "") formData.append("expert_description", values.description);
if (values.confirm_img != null) formData.append("attachment", values.confirm_img);
requestServer(`${CONFIRM_TRANSPORTATION_ASSISTANCE}/${rowId}`, 'post', {
@@ -47,19 +60,14 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0];
if (uploadedFile) {
- const maxFileSize = 2 * 1024 * 1024;
- if (uploadedFile.size > maxFileSize) {
- UploadFileNotification(t);
- event.target.value = "";
- return;
- }
-
const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType);
setfileName(fileName);
- formik.setFieldValue("confirm_img", uploadedFile);
+ formik.setFieldValue("confirm_img", uploadedFile).then(() => {
+ formik.setFieldTouched("confirm_img", true);
+ })
setShowAddIcon(false);
}
};
@@ -96,7 +104,17 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
imageSize={[250, 150]}
setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon}
+ onBlur={() => formik.handleBlur("confirm_img")}
+ error={
+ formik.touched.confirm_img &&
+ Boolean(formik.errors.confirm_img)
+ }
/>
+
+ {formik.touched.confirm_img && formik.errors.confirm_img}
+
@@ -106,7 +124,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => {
{t("ConfirmDialog.button-cancel")}
diff --git a/src/components/dashboard/transportation-assistance/Form/ConfirmForm/index.jsx b/src/components/dashboard/transportation-assistance/Form/ConfirmForm/index.jsx
index 95a6367..2ef7d25 100644
--- a/src/components/dashboard/transportation-assistance/Form/ConfirmForm/index.jsx
+++ b/src/components/dashboard/transportation-assistance/Form/ConfirmForm/index.jsx
@@ -1,9 +1,8 @@
import {Dialog, DialogTitle, IconButton, Tooltip} from "@mui/material";
import {useTranslations} from "next-intl";
-import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import {useState} from "react";
import ConfirmContent from "./ConfirmContent";
-
+import CallMadeIcon from '@mui/icons-material/CallMade';
const Confirm = ({rowId, mutate}) => {
const t = useTranslations();
const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
@@ -16,12 +15,12 @@ const Confirm = ({rowId, mutate}) => {
setOpenConfirmDialog(true)
}}
>
-
+
>
diff --git a/src/components/dashboard/transportation-assistance/Form/RejectForm/RejectContent.jsx b/src/components/dashboard/transportation-assistance/Form/RejectForm/RejectContent.jsx
index 3641f71..b692df4 100644
--- a/src/components/dashboard/transportation-assistance/Form/RejectForm/RejectContent.jsx
+++ b/src/components/dashboard/transportation-assistance/Form/RejectForm/RejectContent.jsx
@@ -29,7 +29,7 @@ const RejectContent = ({rowId, mutate, setOpenRejectDialog}) => {
validationSchema,
onSubmit: (values, {setSubmitting}) => {
const formData = new FormData();
- formData.append("description", values.description);
+ formData.append("expert_description", values.description);
if (values.reject_img != null) formData.append("attachment", values.reject_img);
requestServer(`${REJECT_TRANSPORTATION_ASSISTANCE}/${rowId}`, 'post', {
diff --git a/src/components/dashboard/transportation-assistance/TableTollbar.jsx b/src/components/dashboard/transportation-assistance/TableTollbar.jsx
deleted file mode 100644
index e267691..0000000
--- a/src/components/dashboard/transportation-assistance/TableTollbar.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import {Stack, Tooltip} from "@mui/material";
-import {useTranslations} from "next-intl";
-
-function TableToolbar() {
- const t = useTranslations();
- return (
-
-
-
- );
-}
-
-export default TableToolbar;
diff --git a/src/components/first/__tests__/index.test.js b/src/components/first/__tests__/index.test.js
new file mode 100644
index 0000000..e6aff67
--- /dev/null
+++ b/src/components/first/__tests__/index.test.js
@@ -0,0 +1,61 @@
+import {render, screen, waitFor} from "@testing-library/react";
+import FirstComponent from "@/components/first";
+import MockAppWithProviders from "../../../../mocks/AppWithProvider";
+import {server} from "../../../../mocks/server";
+import {rest} from "msw";
+import {GET_USER_ROUTE} from "@/core/data/apiRoutes";
+
+describe("First Component From First Page", () => {
+ describe("Rendering", () => {
+ it("App Name Text Rendered", () => {
+ render();
+ const appNameElement = screen.queryByText(/سامانه جامع تسهیلات/i);
+ expect(appNameElement).toBeInTheDocument()
+ });
+ it("App version Text Rendered", () => {
+ render();
+ const versionControler = screen.queryByText(process.env.NEXT_PUBLIC_API_VERSION, {exact: false});
+ expect(versionControler).toBeInTheDocument()
+ });
+ it("Powered By Rendered With Currect URL", () => {
+ render();
+ const linkElement = screen.queryByText('توسعه یافته توسط وایتل');
+ expect(linkElement).toBeInTheDocument()
+ expect(linkElement).toHaveAttribute('href', process.env.NEXT_PUBLIC_POWERED_BY_URL);
+ });
+ });
+ describe("Behavioral", () => {
+ it("Show Login Button And Do Not Show Dashboard Button When User Is Not Authenticated", async () => {
+ render();
+ await waitFor(() => {
+ const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i)
+ const authenticationButtonDashboard = screen.queryByText(/داشبورد/i)
+ expect(authenticationButtonLogin).toBeInTheDocument()
+ expect(authenticationButtonDashboard).not.toBeInTheDocument()
+ })
+ });
+ it("Show Dashboard Button And Do Not Show Login Button When User Is Authenticated", async () => {
+ localStorage.setItem("_token", 'token');
+ render();
+ await waitFor(() => {
+ const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i)
+ const authenticationButtonDashboard = screen.queryByText(/داشبورد/i)
+ expect(authenticationButtonLogin).not.toBeInTheDocument()
+ expect(authenticationButtonDashboard).toBeInTheDocument()
+ })
+ });
+ it("Show Login Button And Do Not Show Dashboard Button When User Authentication Is Expired", async () => {
+ localStorage.setItem("_token", 'token');
+ server.use(rest.get(GET_USER_ROUTE, (req, res, ctx) => {
+ return res(ctx.status(401))
+ }))
+ render();
+ await waitFor(() => {
+ const authenticationButtonLogin = screen.queryByText(/ورود کارشناس/i)
+ const authenticationButtonDashboard = screen.queryByText(/داشبورد/i)
+ expect(authenticationButtonLogin).toBeInTheDocument()
+ expect(authenticationButtonDashboard).not.toBeInTheDocument()
+ })
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/components/first/index.jsx b/src/components/first/index.jsx
index cd5b5a5..2d144f1 100644
--- a/src/components/first/index.jsx
+++ b/src/components/first/index.jsx
@@ -1,11 +1,10 @@
-import {NextLinkComposed} from "@/core/components/LinkRouting";
+import LinkRouting, {NextLinkComposed} from "@/core/components/LinkRouting";
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import useUser from "@/lib/app/hooks/useUser";
import {Button, Stack, Typography} from "@mui/material";
import {useTranslations} from "next-intl";
import SvgDashboard from "@/core/components/svgs/SvgDashboard";
-import process from "next/dist/build/webpack/loaders/resolve-url-loader/lib/postcss";
const FirstComponent = () => {
const t = useTranslations();
@@ -18,28 +17,16 @@ const FirstComponent = () => {
{t("app_name")}
- {isAuth ? (
-
- ) : (
-
- )}
+
{
color: 'primary.main',
fontFamily: 'Arial',
fontWeight: 'bold'
- }}>v{process.env.NEXT_PUBLIC_API_VERSION}
+ }}
+ >
+ v{process.env.NEXT_PUBLIC_API_VERSION}
+
+
+
+
+ {t("powered_by_witel")}
+
);
};
-export default FirstComponent;
+export default FirstComponent;
\ No newline at end of file
diff --git a/src/components/layouts/Dashboard/Content/index.jsx b/src/components/layouts/Dashboard/Content/index.jsx
new file mode 100644
index 0000000..e3f39db
--- /dev/null
+++ b/src/components/layouts/Dashboard/Content/index.jsx
@@ -0,0 +1,65 @@
+import LoadingHardPage from "@/core/components/LoadingHardPage";
+import RolePermissionMiddleware from "@/middlewares/RolePermission";
+import BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs";
+import FullPageLayout from "@/layouts/FullPageLayout";
+import {useEffect, useMemo, useState} from "react";
+import sidebarMenu from "@/core/data/sidebarMenu";
+import {useRouter} from "next/router";
+import {useTranslations} from "next-intl";
+
+const Content = (props) => {
+ const t = useTranslations()
+ const router = useRouter()
+ const [routing, setRouting] = useState(false)
+ const [routingItem, setRoutingItem] = useState({})
+ const pageList = useMemo(() => {
+ const list = []
+ for (const menu of sidebarMenu) {
+ for (const item of menu) {
+ if (item.type === 'menu') {
+ for (const subItem of item.subItem) {
+ list.push(subItem)
+ }
+ } else {
+ list.push(item)
+ }
+ }
+ }
+
+ return list
+ }, [])
+
+ useEffect(() => {
+ const handlerStartRoute = (url) => {
+ setRoutingItem(pageList.find(page => page.route === url))
+ setRouting(true)
+ }
+ const handlerCompleteRoute = () => {
+ setRouting(false)
+ }
+
+ router.events.on('routeChangeStart', handlerStartRoute)
+ router.events.on('routeChangeComplete', handlerCompleteRoute)
+
+ return () => {
+ router.events.off('routeChangeStart', handlerStartRoute)
+ router.events.off('routeChangeComplete', handlerCompleteRoute)
+ }
+ }, [router]);
+
+ return (
+
+
+
+
+ {props.children}
+
+
+
+ )
+}
+
+export default Content
\ No newline at end of file
diff --git a/src/components/layouts/Dashboard/Header/index.jsx b/src/components/layouts/Dashboard/Header/index.jsx
index 8760960..8050806 100644
--- a/src/components/layouts/Dashboard/Header/index.jsx
+++ b/src/components/layouts/Dashboard/Header/index.jsx
@@ -1,5 +1,5 @@
import MenuIcon from "@mui/icons-material/Menu";
-import {AppBar, Box, Container, CssBaseline, IconButton, Stack, Toolbar, useTheme} from "@mui/material";
+import {AppBar, Box, Container, IconButton, Stack, Toolbar, useTheme} from "@mui/material";
import ProfileMenu from "./ProfileMenu";
function Header({drawerWidth, handleDrawerToggle}) {
@@ -7,7 +7,6 @@ function Header({drawerWidth, handleDrawerToggle}) {
return (
<>
-
itemsArr.map((item) =>
- action.key == item.key
- ? {...item, showSubItem: !item.showSubItem}
- : item
+ action.key === item.key ? {...item, showSubItem: !item.showSubItem} : item
)
);
case "SELECTED":
return state.map((itemsArr) =>
- itemsArr.map((item) =>
- action.route == item.route
- ? {...item, selected: true}
- : {...item, selected: false}
- )
+ itemsArr.map((item) => {
+ return item.type === "page"
+ ? {...item, selected: action.route === item.route, routing: false}
+ : item.subItem && Array.isArray(item.subItem)
+ ? {
+ ...item,
+ subItem: item.subItem.map((subitem) => ({
+ ...subitem,
+ selected: subitem.route === action.route,
+ routing: false
+ })),
+ showSubItem: item.subItem.some(
+ (subitem) => subitem.selected
+ ),
+ }
+ : item;
+ })
+ );
+ case "SELECTING":
+ return state.map((itemsArr) =>
+ itemsArr.map((item) => {
+ return item.type === "page"
+ ? {...item, selected: action.route === item.route, routing: action.route === item.route}
+ : item.subItem && Array.isArray(item.subItem)
+ ? {
+ ...item,
+ subItem: item.subItem.map((subitem) => ({
+ ...subitem,
+ selected: subitem.route === action.route,
+ routing: subitem.route === action.route
+ })),
+ showSubItem: item.subItem.some(
+ (subitem) => subitem.selected
+ ),
+ }
+ : item;
+ })
);
default:
throw new Error();
@@ -32,26 +62,48 @@ export default function SidebarList({handleDrawerToggle}) {
const [itemMenu, dispatch] = useReducer(reducer, sidebarMenu);
const {user} = useUser();
const router = useRouter();
+ const [selectedKey, setSelectedKey] = useState(null);
useEffect(() => {
dispatch({type: "SELECTED", route: router.pathname});
- }, [router.pathname]);
+ setSelectedKey(router.pathname);
+ const handlerStartRoute = (url) => {
+ dispatch({type: "SELECTING", route: url});
+ }
- const filteredItemMenu = itemMenu[0].filter(
- (item) => user.permissions.includes(item.permission) || item.permission === "all"
- );
+ router.events.on('routeChangeStart', handlerStartRoute)
+
+ return () => {
+ router.events.off('routeChangeStart', handlerStartRoute)
+ }
+ }, [router]);
+
+
+ useEffect(() => {
+ selectedKey && document.querySelector(`[data-route="${selectedKey}"]`)?.scrollIntoView({
+ behavior: "smooth",
+ block: "center"
+ });
+ }, [selectedKey]);
return (
-
- {filteredItemMenu.map((item, index) => (
-
+
+ {itemMenu.map((itemArr, index) => (
+
+ {itemArr.map((item) =>
+
+ {(user?.permissions?.includes(item.permission) || item.permission === "all") &&
+ }
+
+ )}
+
+
))}
- {filteredItemMenu.length > 0 && }
+
);
}
diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx
index ebeb14c..81fd444 100644
--- a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx
+++ b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx
@@ -1,7 +1,16 @@
import {NextLinkComposed} from "@/core/components/LinkRouting";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
-import {Badge, IconButton, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography,} from "@mui/material";
+import {
+ Badge,
+ CircularProgress,
+ IconButton,
+ ListItem,
+ ListItemButton,
+ ListItemIcon,
+ ListItemText,
+ Typography,
+} from "@mui/material";
import {useTranslations} from "next-intl";
import {Fragment} from "react";
import SidebarListSubItem from "./SidebarListSubItem";
@@ -9,23 +18,27 @@ import useNotification from "@/lib/app/hooks/useNotification";
const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
const t = useTranslations();
- const {notification_count} = useNotification()
+ const {notification_count} = useNotification();
+ const hasSubItems = item.type === "menu" && item.subItem && item.subItem.length > 0;
+ const renderBadge = () => {
+ return !hasSubItems ? (
+
+
+
+ ) : null;
+ };
return (
-
-
-
-
- }>
+ <>
+
{
},
})}
onClick={() => {
- if (item.type == "menu") {
+ if (hasSubItems) {
dispatch({type: "COLLAPSE_MENU", key: item.key});
}
- handleDrawerToggle();
}}
sx={{
minHeight: 48,
@@ -50,10 +62,13 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
minWidth: 0,
justifyContent: "center",
color: "primary.main",
+ width: 40,
+ height: 24,
pr: 2,
}}
>
- {item.icon}
+ {item.routing ?
+ : item.icon}
{
}
/>
-
- {item.type == "menu" &&
- (item.showSubItem ? : )}
+ {hasSubItems && (item.showSubItem ? : )}
{item.subItem && (
@@ -77,7 +90,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
handleDrawerToggle={handleDrawerToggle}
/>
)}
-
+ >
);
};
diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx
index 3407081..3ef8731 100644
--- a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx
+++ b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx
@@ -1,18 +1,44 @@
import {NextLinkComposed} from "@/core/components/LinkRouting";
-import InboxIcon from "@mui/icons-material/MoveToInbox";
-import {Collapse, List, ListItemButton, ListItemIcon, ListItemText,} from "@mui/material";
+import {
+ Badge,
+ CircularProgress,
+ Collapse,
+ IconButton,
+ List,
+ ListItem,
+ ListItemButton,
+ ListItemIcon,
+ ListItemText,
+ Typography,
+} from "@mui/material";
import {useTranslations} from "next-intl";
-import {Fragment} from "react";
+import useNotification from "@/lib/app/hooks/useNotification";
const SidebarListSubItem = ({item, handleDrawerToggle}) => {
const t = useTranslations();
+ const {notification_count} = useNotification();
+ const renderBadge = (subitem) => (
+
+
+
+ );
return (
-
-
- {item.subItem.map((subitem, index) => (
-
+
+
+
+ {item.subItem.map((subitem) => (
+
{
sx={{
minHeight: 48,
}}
- onClick={(event) => {
- if (item.type == "menu") {
- dispatch({type: "COLLAPSE_MENU", key: item.key});
- }
- handleDrawerToggle();
- }}
>
-
+ {subitem.routing ?
+ : subitem.icon}
-
+
+ {t(subitem.secondary)}
+
+ ) : null
+ }/>
-
+
))}
+
);
};
diff --git a/src/components/layouts/Dashboard/Sidebar/index.jsx b/src/components/layouts/Dashboard/Sidebar/index.jsx
index 2498dfd..27f2d0d 100644
--- a/src/components/layouts/Dashboard/Sidebar/index.jsx
+++ b/src/components/layouts/Dashboard/Sidebar/index.jsx
@@ -1,13 +1,31 @@
-import {Box, Drawer} from "@mui/material";
+import {Box, Drawer, useMediaQuery} from "@mui/material";
import SidebarDrawer from "./SidebarDrawer";
+import {useTheme} from "@mui/material/styles";
const Sidebar = (props) => {
+ const theme = useTheme();
+ const isUpSm = useMediaQuery((theme.breakpoints.up('sm')))
return (
+ > {isUpSm ? (
+
+
+
+ ) : (
{
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: props.drawerWidth,
+ overflow: "hidden",
},
}}
>
-
-
-
+ )}
);
};
diff --git a/src/components/layouts/Dashboard/index.jsx b/src/components/layouts/Dashboard/index.jsx
index f54a894..ef3f3bd 100644
--- a/src/components/layouts/Dashboard/index.jsx
+++ b/src/components/layouts/Dashboard/index.jsx
@@ -1,10 +1,9 @@
import {useState} from "react";
-import Header from "./header";
-import Sidebar from "./sidebar";
import {Toolbar} from "@mui/material";
-import BreadCrumbs from "./breadcrumbs";
import FullPageLayout from "@/layouts/FullPageLayout";
-import RolePermissionMiddleware from "@/middlewares/RolePermission";
+import Header from "@/components/layouts/Dashboard/Header";
+import Sidebar from "@/components/layouts/Dashboard/Sidebar";
+import Content from "@/components/layouts/Dashboard/Content";
const drawerWidth = 240;
@@ -17,6 +16,7 @@ const DashboardLayouts = (props) => {
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
+
return (
{
sx={{flexGrow: 1, width: {sm: `calc(100% - ${drawerWidth}px)`}}}
>
-
-
-
- {props.children}
-
-
+
);
diff --git a/src/components/layouts/Print/Header/ProfileData.jsx b/src/components/layouts/Print/Header/ProfileData.jsx
new file mode 100644
index 0000000..548378b
--- /dev/null
+++ b/src/components/layouts/Print/Header/ProfileData.jsx
@@ -0,0 +1,18 @@
+import {Avatar, Stack, Typography} from "@mui/material";
+import useUser from "@/lib/app/hooks/useUser";
+
+export default function ProfileData() {
+ const {user} = useUser();
+ return (
+
+
+
+ {user.username}
+
+
+ );
+}
diff --git a/src/components/layouts/Print/Header/ProfileMenu.jsx b/src/components/layouts/Print/Header/ProfileMenu.jsx
new file mode 100644
index 0000000..415d501
--- /dev/null
+++ b/src/components/layouts/Print/Header/ProfileMenu.jsx
@@ -0,0 +1,62 @@
+import {Avatar, IconButton, Menu, Tooltip} from "@mui/material";
+import {useState} from "react";
+import ProfileData from "./ProfileData";
+import ProfileOptions from "./ProfileOptions";
+import {useTranslations} from "next-intl";
+import useUser from "@/lib/app/hooks/useUser";
+
+function ProfileMenu() {
+ const t = useTranslations();
+ const [anchorElUser, setAnchorElUser] = useState(null);
+ const {user} = useUser();
+
+ const handleOpenUserMenu = (event) => {
+ setAnchorElUser(event.currentTarget);
+ };
+ const handleCloseUserMenu = () => {
+ setAnchorElUser(null);
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
+}
+
+export default ProfileMenu;
diff --git a/src/components/layouts/Print/Header/ProfileOptionLogOut.jsx b/src/components/layouts/Print/Header/ProfileOptionLogOut.jsx
new file mode 100644
index 0000000..2a6cf85
--- /dev/null
+++ b/src/components/layouts/Print/Header/ProfileOptionLogOut.jsx
@@ -0,0 +1,49 @@
+import {Box, Button, MenuItem, Typography} from "@mui/material";
+import MeetingRoomIcon from "@mui/icons-material/MeetingRoom";
+import useUser from "@/lib/app/hooks/useUser";
+import {useTranslations} from "next-intl";
+
+export default function ProfileOptionLogOut({handleCloseUserMenu}) {
+ const t = useTranslations();
+ const {clearToken} = useUser();
+ const handleClickLogOut = () => {
+ handleCloseUserMenu();
+ clearToken();
+ };
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/src/components/layouts/Print/Header/ProfileOptions.jsx b/src/components/layouts/Print/Header/ProfileOptions.jsx
new file mode 100644
index 0000000..6e7bdba
--- /dev/null
+++ b/src/components/layouts/Print/Header/ProfileOptions.jsx
@@ -0,0 +1,49 @@
+import {Box, MenuItem, Typography} from "@mui/material";
+import {NextLinkComposed} from "@/core/components/LinkRouting";
+import {useTranslations} from "next-intl";
+import headerProfileItems from "@/core/data/headerProfileItems";
+import ProfileOptionLogOut from "./ProfileOptionLogOut";
+
+export default function ProfileOptions({handleCloseUserMenu}) {
+ const t = useTranslations();
+
+ return (
+ <>
+ {headerProfileItems.map((profile_item) => (
+
+ ))}
+
+ >
+ );
+}
diff --git a/src/components/layouts/Print/Header/index.jsx b/src/components/layouts/Print/Header/index.jsx
new file mode 100644
index 0000000..7c0d990
--- /dev/null
+++ b/src/components/layouts/Print/Header/index.jsx
@@ -0,0 +1,72 @@
+import {AppBar, Button, Container, Stack, Toolbar, Typography, useTheme} from "@mui/material";
+import {useTranslations} from "next-intl";
+import {useRouter} from "next/router";
+import {useState} from "react";
+import usePrint from "@/lib/app/hooks/usePrint";
+
+function Header({drawerWidth, handleDrawerToggle}) {
+ const theme = useTheme();
+ const t = useTranslations()
+ const router = useRouter()
+ const {printDetails} = usePrint()
+ const [disabled, setDisabled] = useState(false)
+
+ return (
+
+
+
+
+
+
+
+ {printDetails.title}
+ {'تعداد صفحه:'} {printDetails.count}
+
+
+
+
+
+
+
+ );
+}
+
+export default Header;
\ No newline at end of file
diff --git a/src/components/layouts/Print/index.jsx b/src/components/layouts/Print/index.jsx
new file mode 100644
index 0000000..afa6585
--- /dev/null
+++ b/src/components/layouts/Print/index.jsx
@@ -0,0 +1,14 @@
+import Header from "@/components/layouts/Print/Header";
+import {Toolbar} from "@mui/material";
+
+const Print = (props) => {
+ return (
+ <>
+
+
+ {props.children}
+ >
+ )
+}
+
+export default Print
\ No newline at end of file
diff --git a/src/components/login-expert/index.jsx b/src/components/login-expert/index.jsx
index 221c530..23185df 100644
--- a/src/components/login-expert/index.jsx
+++ b/src/components/login-expert/index.jsx
@@ -1,5 +1,4 @@
import LinkRouting from "@/core/components/LinkRouting";
-import PasswordField from "@/core/components/PasswordField";
import StyledForm from "@/core/components/StyledForm";
import {GET_USER_TOKEN} from "@/core/data/apiRoutes";
import CenterLayout from "@/layouts/CenterLayout";
@@ -14,6 +13,7 @@ import * as Yup from "yup";
import useDirection from "@/lib/app/hooks/useDirection";
import SvgLogin from "@/core/components/svgs/SvgLogin";
import useRequest from "@/lib/app/hooks/useRequest";
+import PasswordField from "@/core/components/PasswordField";
const LoginComponent = () => {
const t = useTranslations();
diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx
index 70cee09..ea3a117 100644
--- a/src/core/components/DataTable.jsx
+++ b/src/core/components/DataTable.jsx
@@ -14,7 +14,6 @@ function DataTable(props) {
const [columnFilters, setColumnFilters] = useState([]);
const [sorting, setSorting] = useState(props.sorting || []);
const [pagination, setPagination] = useState({pageIndex: 0, pageSize: 10});
- const [rowCount, setRowCount] = useState(0);
const [columnFilterFns, setColumnFilterFns] = useState(() => {
let output = {};
const list = props.columns.map((item) => item.enableColumnFilter ? {[item.id]: item.filterFn} : {[item.id]: ""});
@@ -34,8 +33,11 @@ function DataTable(props) {
const tableLocalization = useMemo(() => languageList.find((item) => item.key == languageApp).tableLocalization, [languageApp, languageList]);
const fetchUrl = useMemo(() => {
- const url = new URL(props.tableUrl);
- url.searchParams.set("start", `${pagination.pageIndex * pagination.pageSize}`);
+ const params = new URLSearchParams();
+ params.set(
+ "start",
+ `${pagination.pageIndex * pagination.pageSize}`
+ );
const filters = columnFilters.map((filter) => {
let datatype;
for (const i in props.columns) {
@@ -47,10 +49,10 @@ function DataTable(props) {
...filter, fn: columnFilterFns[filter.id], datatype: datatype,
};
});
- url.searchParams.set("size", pagination.pageSize);
- url.searchParams.set("filters", JSON.stringify(filters ?? []));
- url.searchParams.set("sorting", JSON.stringify(sorting ?? []));
- return url;
+ params.set("size", pagination.pageSize);
+ params.set("filters", JSON.stringify(filters ?? []));
+ params.set("sorting", JSON.stringify(sorting ?? []));
+ return `${props.tableUrl}?${params}`;
}, [props.tableUrl, columnFilters, columnFilterFns, pagination, sorting, props.columns,]);
const {data, isValidating, mutate} = useSWR(fetchUrl, (...args) =>
@@ -60,7 +62,7 @@ function DataTable(props) {
}).then((response) => response.data).catch(() => {
})
, {
- revalidateIfStale: false,
+ revalidateIfStale: true,
revalidateOnFocus: false,
revalidateOnReconnect: true,
keepPreviousData: true
diff --git a/src/core/components/LoadingHardPage.jsx b/src/core/components/LoadingHardPage.jsx
index c3b24d6..59e4a02 100644
--- a/src/core/components/LoadingHardPage.jsx
+++ b/src/core/components/LoadingHardPage.jsx
@@ -1,4 +1,4 @@
-import {Backdrop, Box, styled} from "@mui/material";
+import {Backdrop, Box, Stack, styled, Typography} from "@mui/material";
import SvgLoading from "@/core/components/svgs/SvgLoading";
const LoadingImage = styled(Box)({
@@ -9,7 +9,7 @@ const LoadingImage = styled(Box)({
},
"50%": {
// opacity: 1,
- transform: "scale(2)",
+ transform: "scale(.5)",
},
"100%": {
// opacity: 0,
@@ -19,20 +19,29 @@ const LoadingImage = styled(Box)({
animation: "load 2s infinite",
});
-const LoadingHardPage = ({children, loading}) => {
+const LoadingHardPage = ({children, loading, sx = {}, icon = null, width = 200, height = 200, label = ''}) => {
return (
<>
theme.zIndex.drawer + 1}}
+ sx={{bgcolor: "#fff", zIndex: (theme) => theme.zIndex.drawer + 1, ...sx}}
open={loading}
>
-
-
-
+
+
+ {icon ? (
+
+ {icon}
+
+ ) : (
+
+ )}
+
+ {label}
+
{children}
>
diff --git a/src/core/components/LtrTextField.jsx b/src/core/components/LtrTextField.jsx
new file mode 100644
index 0000000..aa9efcb
--- /dev/null
+++ b/src/core/components/LtrTextField.jsx
@@ -0,0 +1,11 @@
+import {styled, TextField} from "@mui/material";
+
+const LtrTextField = styled(TextField)`
+ .MuiInputBase-input {
+ /* @noflip */
+ direction: ltr;
+ text-align: left;
+ }
+`;
+
+export default LtrTextField
\ No newline at end of file
diff --git a/src/core/components/NetworkComponent.jsx b/src/core/components/NetworkComponent.jsx
index 8ebbb68..9610b1c 100644
--- a/src/core/components/NetworkComponent.jsx
+++ b/src/core/components/NetworkComponent.jsx
@@ -6,13 +6,13 @@ import WifiOffIcon from '@mui/icons-material/WifiOff';
import {useTranslations} from "next-intl";
const NetworkComponent = () => {
- const toastId = useRef(null);
+ const networkToastId = useRef(null);
const network = useNetwork()
const t = useTranslations()
useEffect(() => {
if (network.online) {
- toast.update(toastId.current, {
+ toast.update(networkToastId.current, {
type: toast.TYPE.SUCCESS,
render: t('online_message'),
autoClose: 2000,
@@ -22,8 +22,9 @@ const NetworkComponent = () => {
});
return
}
- toast.dismiss()
- toastId.current = toast.warn(t('offline_message'), {
+ networkToastId.current = toast.warn(t('offline_message'), {
+ containerId: 'connection',
+ draggable: false,
autoClose: false, closeButton: false, closeOnClick: false, icon:
})
}, [network.online]);
diff --git a/src/core/components/PasswordField.jsx b/src/core/components/PasswordField/index.jsx
similarity index 100%
rename from src/core/components/PasswordField.jsx
rename to src/core/components/PasswordField/index.jsx
diff --git a/src/core/components/PriceField.jsx b/src/core/components/PriceField.jsx
index e62e4e1..1df1ba2 100644
--- a/src/core/components/PriceField.jsx
+++ b/src/core/components/PriceField.jsx
@@ -1,12 +1,13 @@
-import {Stack, TextField, Typography} from "@mui/material";
+import {Stack, Typography} from "@mui/material";
import {useTranslations} from "next-intl";
+import LtrTextField from "@/core/components/LtrTextField";
const PriceField = (props) => {
const t = useTranslations();
return (
-
diff --git a/src/core/components/PrintablePage.jsx b/src/core/components/PrintablePage.jsx
new file mode 100644
index 0000000..70a264b
--- /dev/null
+++ b/src/core/components/PrintablePage.jsx
@@ -0,0 +1,82 @@
+import {Box, Container, Divider, Grid, Paper, Stack, Typography} from '@mui/material';
+
+const PrintablePage = ({children, header, footer}) => (
+ <>
+
+
+
+
+
+
+
+
+
+ باسمه تعالی
+ جمهوری اسلامی ایران
+ وزارت راه و
+ شهرسازی
+ سازمان
+ راهداری و حمل و نقل جاده
+ ای
+
+
+
+ شماره:
+ تاریخ:
+ پیوست:
+
+
+
+
+ {children}
+
+
+
+
+
+ آدرس: تهران-بلوار
+ کشاورز-خیابان فلسطین جنوبی-خیابان
+ دمشق-پلاک17
+ تلفن: 88-88804379
+ تلفن گویا: 88804400
+ کدپستی: 1416753941
+
+
+ صندوق پستی: 3773-14155
+ پست الکترونیک: info@rmto.ir
+ سایت الکترونیک: www.rmto.ir
+
+
+
+
+
+ >
+);
+
+export default PrintablePage;
\ No newline at end of file
diff --git a/src/core/components/UploadSystem.jsx b/src/core/components/UploadSystem.jsx
index b40fb46..ea573d7 100644
--- a/src/core/components/UploadSystem.jsx
+++ b/src/core/components/UploadSystem.jsx
@@ -10,7 +10,6 @@ const UploadSystem = ({
handleUploadChange,
fieldname,
setFieldValue,
- imageAlt,
imageSize,
fileType,
fileName,
@@ -19,7 +18,6 @@ const UploadSystem = ({
}) => {
const t = useTranslations();
const fileInputRef = useRef(null);
-
const handleClick = () => {
fileInputRef.current.click();
};
@@ -69,13 +67,14 @@ const UploadSystem = ({
variant="subtitle2"
sx={{
fontWeight: 600,
- fontSize: "1rem",
+ fontSize: "0.8rem",
color: "#a19d9d",
mt: 1,
}}
textAlign="center"
>
- {t("UploadSystem.upload_file")}
+ {t("UploadSystem.upload_file_format")}
+ {t("UploadSystem.upload_file_unit")}
>
diff --git a/src/core/components/notifications/ErrorNotification.jsx b/src/core/components/notifications/ErrorNotification.jsx
index 4ff1ab0..776c904 100644
--- a/src/core/components/notifications/ErrorNotification.jsx
+++ b/src/core/components/notifications/ErrorNotification.jsx
@@ -2,8 +2,8 @@ import DangerousIcon from "@mui/icons-material/Dangerous";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
-const ErrorNotification = (t, status, message) => {
- toast(
+const ErrorNotification = (pushToastList, notificationType, t, status, message) => {
+ const toastId = toast(
() => (
<>
{
>
),
{
+ containerId: 'validation',
autoClose: false,
closeOnClick: false,
draggable: false,
}
);
+ pushToastList(notificationType, toastId);
};
-export default ErrorNotification;
+export default ErrorNotification;
\ No newline at end of file
diff --git a/src/core/components/notifications/PendingNotification.jsx b/src/core/components/notifications/PendingNotification.jsx
index 846b847..cc28a58 100644
--- a/src/core/components/notifications/PendingNotification.jsx
+++ b/src/core/components/notifications/PendingNotification.jsx
@@ -1,12 +1,14 @@
import {toast} from "react-toastify";
-const PendingNotification = (t) => {
- toast(t("notifications.pending"), {
+const PendingNotification = (pushToastList, notificationType, t) => {
+ const toastId = toast(t("notifications.pending"), {
+ containerId: 'validation',
autoClose: false,
closeButton: false,
closeOnClick: false,
draggable: false,
});
+ pushToastList(notificationType, toastId);
};
-export default PendingNotification;
+export default PendingNotification;
\ No newline at end of file
diff --git a/src/core/components/notifications/SuccessNotification.jsx b/src/core/components/notifications/SuccessNotification.jsx
index 998deaa..1207000 100644
--- a/src/core/components/notifications/SuccessNotification.jsx
+++ b/src/core/components/notifications/SuccessNotification.jsx
@@ -2,8 +2,8 @@ import BeenhereIcon from "@mui/icons-material/Beenhere";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
-const SuccessNotification = (t, status) => {
- toast(
+const SuccessNotification = (pushToastList, notificationType, t, status) => {
+ const toastId = toast(
() => (
<>
{
>
),
{
+ containerId: 'validation',
autoClose: 3000,
hideProgressBar: true,
pauseOnHover: true,
@@ -37,6 +38,7 @@ const SuccessNotification = (t, status) => {
draggable: true,
}
);
+ pushToastList(notificationType, toastId);
};
-export default SuccessNotification;
+export default SuccessNotification;
\ No newline at end of file
diff --git a/src/core/components/notifications/UploadFileNotification.jsx b/src/core/components/notifications/UploadFileNotification.jsx
index 6556944..1c20ba3 100644
--- a/src/core/components/notifications/UploadFileNotification.jsx
+++ b/src/core/components/notifications/UploadFileNotification.jsx
@@ -26,6 +26,8 @@ const UploadFileNotification = (t) => {
>
),
{
+ containerId: 'validation',
+ toastId: 'upload',
autoClose: 3000,
hideProgressBar: true,
pauseOnHover: true,
@@ -35,4 +37,4 @@ const UploadFileNotification = (t) => {
);
};
-export default UploadFileNotification;
+export default UploadFileNotification;
\ No newline at end of file
diff --git a/src/core/components/notifications/WarningNotification.jsx b/src/core/components/notifications/WarningNotification.jsx
index 8493180..bc2a1fb 100644
--- a/src/core/components/notifications/WarningNotification.jsx
+++ b/src/core/components/notifications/WarningNotification.jsx
@@ -2,8 +2,8 @@ import ReportIcon from "@mui/icons-material/Report";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
-const WarningNotification = (t, status) => {
- toast(
+const WarningNotification = (pushToastList, notificationType, t, status) => {
+ const toastId = toast(
() => (
<>
{
>
),
{
+ containerId: 'validation',
autoClose: false,
closeOnClick: false,
draggable: false,
}
);
+ pushToastList(notificationType, toastId);
};
-export default WarningNotification;
+export default WarningNotification;
\ No newline at end of file
diff --git a/src/core/components/notifications/index.jsx b/src/core/components/notifications/index.jsx
index bc8eb66..34e44f3 100644
--- a/src/core/components/notifications/index.jsx
+++ b/src/core/components/notifications/index.jsx
@@ -1,54 +1,27 @@
-import {toast} from "react-toastify";
-import ErrorNotification from "./ErrorNotification";
-import WarningNotification from "./WarningNotification";
-import SuccessNotification from "./SuccessNotification";
+import pendingNotification from "@/core/components/notifications/PendingNotification";
+import WarningNotification from "@/core/components/notifications/WarningNotification";
+import ErrorNotification from "@/core/components/notifications/ErrorNotification";
+import SuccessNotification from "@/core/components/notifications/SuccessNotification";
-const Notifications = async (t, response) => {
- const {status, data} = response != undefined ? response : ""
- toast.dismiss();
- switch (status) {
- case 200:
- SuccessNotification(t, status);
+const Notifications = (pushToastList, notificationType, t, status, message) => {
+ switch (notificationType) {
+ case "pending":
+ pendingNotification(pushToastList, notificationType, t);
break;
- case 400:
- ErrorNotification(t, status);
+ case "warning":
+ WarningNotification(pushToastList, notificationType, t, status);
break;
- case 401:
- ErrorNotification(t, status);
+ case "error":
+ if (message) {
+ ErrorNotification(pushToastList, notificationType, t, status, message)
+ } else {
+ ErrorNotification(pushToastList, notificationType, t, status)
+ }
break;
- case 403:
- ErrorNotification(t, status);
- break;
- case 422:
- ErrorNotification(t, status, data.message);
- break;
- case 500:
- WarningNotification(t, status);
- break;
- case 503:
- WarningNotification(t, status);
- break;
- case 504:
- WarningNotification(t, status);
- break;
- default:
- toast(t("notifications.pending"), {
- autoClose: false,
- closeOnClick: false,
- draggable: false,
- });
+ case "success":
+ SuccessNotification(pushToastList, notificationType, t, status);
break;
}
};
export default Notifications;
-
-/*
-usage document
-
-** for pending use ( Notifications( t, undefined) ) this before your request.
-** for success use ( Notifications( t, response) ) this inside .then() of your request.
-** for Error and Warning use ( Notifications( t, error.response) ) this inside .catche() of your request.
-
-end usage document
-*/
diff --git a/src/core/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx
index 5d0386c..09b81da 100644
--- a/src/core/data/sidebarMenu.jsx
+++ b/src/core/data/sidebarMenu.jsx
@@ -21,7 +21,7 @@ const sidebarMenu = [
key: "sidebar.dashboard",
type: "page",
route: "/dashboard",
- icon: ,
+ icon: ,
selected: false,
permission: "all",
},
@@ -40,7 +40,7 @@ const sidebarMenu = [
name: "passenger_office_chief",
type: "page",
route: "/dashboard/passenger-office-chief",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_passenger_office_navgan",
},
@@ -49,7 +49,7 @@ const sidebarMenu = [
name: "machinery_expert",
type: "page",
route: "/dashboard/machinery-expert",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_machinery_navgan",
},
@@ -59,7 +59,7 @@ const sidebarMenu = [
name: "province_working_group",
type: "page",
route: "/dashboard/passenger-boss",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_province_working_group_navgan",
},
@@ -69,7 +69,7 @@ const sidebarMenu = [
name: "transportation_assistant",
type: "page",
route: "/dashboard/transportation-assistant",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_transportation_navgan",
},
@@ -79,7 +79,7 @@ const sidebarMenu = [
name: "province_manager_navgan",
type: "page",
route: "/dashboard/navgan-province-manager",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_province_affairs_navgan",
},
@@ -88,7 +88,7 @@ const sidebarMenu = [
name: "province_head_expert",
type: "page",
route: "/dashboard/province-head-expert",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_province_headquarter_refahi",
},
@@ -97,7 +97,7 @@ const sidebarMenu = [
name: "inspector_expert",
type: "page",
route: "/dashboard/inspector-expert",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_inspector_refahi",
},
@@ -106,7 +106,7 @@ const sidebarMenu = [
name: "commercial_chief",
type: "page",
route: "/dashboard/commercial-chief",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_commercial_refahi",
},
@@ -115,7 +115,7 @@ const sidebarMenu = [
name: "development_assistant",
type: "page",
route: "/dashboard/development-assistant",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_development_refahi",
},
@@ -125,7 +125,7 @@ const sidebarMenu = [
name: "province_manager_refahi",
type: "page",
route: "/dashboard/refahi-province-manager",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_province_affairs_refahi",
},
@@ -135,7 +135,7 @@ const sidebarMenu = [
name: "refahi_loan_management",
type: "page",
route: "/dashboard/refahi-loan-management",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_refahi_loan",
},
@@ -145,7 +145,7 @@ const sidebarMenu = [
name: "navgan_loan_management",
type: "page",
route: "/dashboard/navgan-loan-management",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_navgan_loan",
},
@@ -154,7 +154,7 @@ const sidebarMenu = [
name: "expert_management",
type: "page",
route: "/dashboard/expert-management",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_experts",
},
@@ -163,7 +163,7 @@ const sidebarMenu = [
name: "user_management",
type: "page",
route: "/dashboard/user-management",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_users",
},
@@ -172,7 +172,7 @@ const sidebarMenu = [
name: "role_management",
type: "page",
route: "/dashboard/role-management",
- icon: ,
+ icon: ,
selected: false,
permission: "manage_roles",
},
diff --git a/src/core/utils/errorHandler.js b/src/core/utils/errorHandler.js
index 8dbd364..e380e84 100644
--- a/src/core/utils/errorHandler.js
+++ b/src/core/utils/errorHandler.js
@@ -1,45 +1,48 @@
-import ErrorNotification from "@/core/components/notifications/ErrorNotification";
-import WarningNotification from "@/core/components/notifications/WarningNotification";
-import {toast} from "react-toastify";
+import Notifications from "@/core/components/notifications";
-export const errorSetting = (t, notification) => {
- if (notification) toast.dismiss();
+export const errorSetting = (dismissToastList, t, notification) => {
+ if (notification) {
+ dismissToastList(["pending", "warning", "error", "success"])
+ }
}
-
-export const errorRequest = (t, notification) => {
- if (notification) toast.dismiss();
-}
-
-export const errorResponse = (response, clearToken, t, notification) => {
- if (notification) toast.dismiss();
- if (isServerError(response.status)) {
- errorServer(response, t, notification)
- } else if (isClientError(response.status)) {
- errorClient(response, clearToken, t, notification)
+export const errorRequest = (dismissToastList, t, notification) => {
+ if (notification) {
+ dismissToastList(["pending", "warning", "error", "success"])
}
}
-const errorServer = (response, t, notification) => {
- if (notification) WarningNotification(t, response.status);
+export const errorResponse = (pushToastList, dismissToastList, response, clearToken, t, notification) => {
+ if (notification) {
+ dismissToastList(["pending", "warning", "error", "success"])
+ }
+ if (isServerError(response.status)) {
+ errorServer(pushToastList, response, t, notification)
+ } else if (isClientError(response.status)) {
+ errorClient(pushToastList, response, clearToken, t, notification)
+ }
}
-const errorClient = (response, clearToken, t, notification) => {
+
+const errorServer = (pushToastList, response, t, notification) => {
+ if (notification) Notifications(pushToastList, "warning", t, response.status);
+}
+const errorClient = (pushToastList, response, clearToken, t, notification) => {
switch (response.status) {
case 401:
clearToken()
- if (notification) ErrorNotification(t, response.status)
+ if (notification) Notifications(pushToastList, "error", t, response.status);
break;
case 422:
if ('type' in response.data) {
- errorLogic(response, t, notification)
+ errorLogic(pushToastList, response, t, notification)
break;
}
- errorValidation(response, t, notification)
+ errorValidation(pushToastList, response, t, notification)
break;
case 429:
- if (notification) ErrorNotification(t, response.status)
+ if (notification) Notifications(pushToastList, "error", t, response.status);
break
default:
- if (notification) ErrorNotification(t, response.status)
+ if (notification) Notifications(pushToastList, "error", t, response.status);
break
}
}
@@ -47,16 +50,16 @@ const errorClient = (response, clearToken, t, notification) => {
const isServerError = status => status >= 500 && status <= 599;
const isClientError = status => status >= 400 && status <= 499;
-const errorLogic = (response, t, notification) => {
- if (notification) ErrorNotification(t, response.status, response.data.message)
+const errorLogic = (pushToastList, response, t, notification) => {
+ if (notification) Notifications(pushToastList, "error", t, response.status, response.data.message);
}
-const errorValidation = (response, t, notification) => {
+const errorValidation = (pushToastList, response, t, notification) => {
if (notification) {
const errorsMap = Object.keys(response.data.errors)
const errorsArray = response.data.errors
errorsMap.map((item, index) => {
- ErrorNotification(t, response.status, errorsArray[item][0]);
+ Notifications(pushToastList, "error", t, response.status, errorsArray[item][0]);
})
}
}
\ No newline at end of file
diff --git a/src/core/utils/succesHandler.js b/src/core/utils/succesHandler.js
index a61ac85..c852f86 100644
--- a/src/core/utils/succesHandler.js
+++ b/src/core/utils/succesHandler.js
@@ -1,9 +1,8 @@
-import SuccessNotification from "@/core/components/notifications/SuccessNotification";
-import {toast} from "react-toastify";
+import Notifications from "@/core/components/notifications";
-export const successRequest = (response, t, options) => {
+export const successRequest = (pushToastList, dismissToastList, response, t, options) => {
if (options.notification && options.success.notification.show) {
- toast.dismiss();
- SuccessNotification(t, response.status)
+ dismissToastList(["pending", "warning", "error", "success"])
+ Notifications(pushToastList, "success", t, response.status);
}
}
\ No newline at end of file
diff --git a/src/layouts/AppLayout.jsx b/src/layouts/AppLayout.jsx
index 4fc5e1d..5c01881 100644
--- a/src/layouts/AppLayout.jsx
+++ b/src/layouts/AppLayout.jsx
@@ -47,7 +47,16 @@ function AppLayout({children, isBot}) {
color={theme.palette.secondary.dark}
options={{showSpinner: false}}
/>
-
+
+
{children}
>);
diff --git a/src/layouts/PrintLayout.jsx b/src/layouts/PrintLayout.jsx
new file mode 100644
index 0000000..301a161
--- /dev/null
+++ b/src/layouts/PrintLayout.jsx
@@ -0,0 +1,12 @@
+import WithAuthMiddleware from "@/middlewares/WithAuth";
+import Print from "@/components/layouts/Print";
+
+const PrintLayout = (props) => {
+ return (
+
+
+
+ )
+}
+
+export default PrintLayout
\ No newline at end of file
diff --git a/src/layouts/index.jsx b/src/layouts/index.jsx
index 1506661..a69d886 100644
--- a/src/layouts/index.jsx
+++ b/src/layouts/index.jsx
@@ -1,14 +1,16 @@
import DashboardLayout from "@/layouts/DashboardLayout";
import {Fragment} from "react";
+import PrintLayout from "@/layouts/PrintLayout";
const layoutList = {
- DashboardLayout
+ DashboardLayout,
+ PrintLayout
}
const Layout = ({layout = {}, children}) => {
const Component = layoutList[layout?.name] || Fragment
- const props = layout.props || {}
+ const props = layout?.props || {}
return (
diff --git a/src/lib/app/contexts/language.jsx b/src/lib/app/contexts/language.jsx
index a31ffd9..243d7e8 100644
--- a/src/lib/app/contexts/language.jsx
+++ b/src/lib/app/contexts/language.jsx
@@ -1,8 +1,8 @@
import {FA_DATATABLE_LOCALIZATION} from "&/locales/fa/datatable";
-import {FA_CHART_LOCALIZATION} from "&/locales/fa/chart";
import {useRouter} from "next/router";
import {createContext, useEffect, useState} from "react";
import useUser from "../hooks/useUser";
+import {FA_CHART_LOCALIZATION} from "&/locales/fa/chart";
export const LanguageContext = createContext();
@@ -20,7 +20,7 @@ export const LanguageProvider = ({children}) => {
];
const {user, userChangedLanguage, changeLanguageState} = useUser();
const [languageIsReady, setLanguageIsReady] = useState(false);
- const [languageApp, setLanguageApp] = useState();
+ const [languageApp, setLanguageApp] = useState(process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE);
const [directionApp, setDirectionApp] = useState(
process.env.NEXT_PUBLIC_DEFAULT_DIRECTION
);
@@ -28,9 +28,7 @@ export const LanguageProvider = ({children}) => {
useEffect(() => {
const lang = localStorage.getItem("_language");
- if (!lang && !languageApp) {
- setLanguageApp(process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE);
- } else if (lang) {
+ if (lang) {
setLanguageApp(lang);
}
}, []);
diff --git a/src/lib/app/contexts/print.jsx b/src/lib/app/contexts/print.jsx
new file mode 100644
index 0000000..20fd1dc
--- /dev/null
+++ b/src/lib/app/contexts/print.jsx
@@ -0,0 +1,24 @@
+import {createContext, useReducer} from "react";
+
+export const PrintContext = createContext();
+
+const reducer = (state, action) => {
+ switch (action.type) {
+ case "SET_PRINT_PAGE":
+ return {
+ ...state, count: action.count
+ };
+ case "SET_PRINT_TITLE":
+ return {
+ ...state, title: action.title
+ };
+ }
+};
+export const PrintProvider = ({children}) => {
+ const [printDetails, printDispatch] = useReducer(reducer, {title: '', count: 0});
+ return (
+
+ {children}
+
+ );
+};
\ No newline at end of file
diff --git a/src/lib/app/contexts/toast.jsx b/src/lib/app/contexts/toast.jsx
new file mode 100644
index 0000000..b9d4cbc
--- /dev/null
+++ b/src/lib/app/contexts/toast.jsx
@@ -0,0 +1,37 @@
+import {createContext, useReducer} from "react";
+import {toast} from "react-toastify";
+
+export const ToastContext = createContext()
+
+const reducer = (state, action) => {
+ switch (action.type) {
+ case "PUSH":
+ return {
+ ...state,
+ [action.toast_type]: [...state[action.toast_type], action.toast_id]
+ };
+ case "DISMISS":
+ action.toast_type.map((item) => {
+ state[item].map((id) => {
+ toast.dismiss(id);
+ })
+ state[item] = []
+ });
+ return state;
+ }
+};
+
+export const ToastProvider = ({children}) => {
+ const [state, dispatch] = useReducer(reducer, {
+ pending: [],
+ error: [],
+ warning: [],
+ success: []
+ });
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/lib/app/contexts/user.jsx b/src/lib/app/contexts/user.jsx
index 10611b8..7f5c97c 100644
--- a/src/lib/app/contexts/user.jsx
+++ b/src/lib/app/contexts/user.jsx
@@ -1,7 +1,6 @@
import {GET_USER_ROUTE} from "@/core/data/apiRoutes";
import axios from "axios";
import {createContext, useCallback, useEffect, useReducer} from "react";
-import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler";
const initialUser = {
isAuth: false,
@@ -78,13 +77,7 @@ export const UserProvider = ({children}) => {
if (typeof callback === "function") callback(data);
})
.catch(error => {
- if (error.response) {
- errorResponse(error.response, clearToken, null, false)
- } else if (error.request) {
- errorRequest(null, false)
- } else {
- errorSetting(null, false)
- }
+ if (error.response.status === 401) clearToken()
})
},
[state.token]
diff --git a/src/lib/app/hooks/usePermissions.jsx b/src/lib/app/hooks/usePermissions.jsx
index 0e6aa07..2e6fff5 100644
--- a/src/lib/app/hooks/usePermissions.jsx
+++ b/src/lib/app/hooks/usePermissions.jsx
@@ -11,15 +11,16 @@ const usePermissions = () => {
})
};
- const {data} = useSWR(GET_PERMISSIONS_LIST, fetcher, {
+ const {data, isLoading} = useSWR(GET_PERMISSIONS_LIST, fetcher, {
revalidateIfStale: false,
revalidateOnFocus: false,
- revalidateOnReconnect: false
+ revalidateOnReconnect: false,
+ keepPreviousData : true
})
const permissions_list = data
//swr config
// render data
- return {permissions_list}
+ return {permissions_list, isLoading}
}
export default usePermissions;
\ No newline at end of file
diff --git a/src/lib/app/hooks/usePrint.jsx b/src/lib/app/hooks/usePrint.jsx
new file mode 100644
index 0000000..35a1017
--- /dev/null
+++ b/src/lib/app/hooks/usePrint.jsx
@@ -0,0 +1,18 @@
+import {useContext} from "react";
+import {PrintContext} from "@/lib/app/contexts/print";
+
+const usePrint = () => {
+ const {printDetails, printDispatch} = useContext(PrintContext);
+
+ const setPrintPage = (count) => {
+ printDispatch({type: 'SET_PRINT_PAGE', count})
+ }
+
+ const setPrintTitle = (title) => {
+ printDispatch({type: 'SET_PRINT_TITLE', title})
+ }
+
+ return {printDetails, setPrintPage, setPrintTitle}
+};
+
+export default usePrint;
\ No newline at end of file
diff --git a/src/lib/app/hooks/useRequest.jsx b/src/lib/app/hooks/useRequest.jsx
index d361e08..4c3be1d 100644
--- a/src/lib/app/hooks/useRequest.jsx
+++ b/src/lib/app/hooks/useRequest.jsx
@@ -1,10 +1,11 @@
import axios from "axios";
import {successRequest} from "@/core/utils/succesHandler";
-import PendingNotification from "@/core/components/notifications/PendingNotification";
import {useTranslations} from "next-intl";
import useUser from "@/lib/app/hooks/useUser";
import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler";
import useNetwork from "@/lib/app/hooks/useNetwork";
+import Notifications from "@/core/components/notifications";
+import useToast from "@/lib/app/hooks/useToast";
const defaultOptions = {
auth: false, data: {}, requestOptions: {
@@ -23,6 +24,7 @@ const useRequest = (initOptions) => {
const network = useNetwork()
const t = useTranslations()
const {token, clearToken} = useUser()
+ const {pushToastList, dismissToastList} = useToast();
let _options = {...defaultOptions, ...initOptions}
function requestServer(url = '', method = 'get', options) {
@@ -33,27 +35,30 @@ const useRequest = (initOptions) => {
headers: {..._options.requestOptions.headers, authorization: `Bearer ${token}`}
}
}
-
return new Promise((resolve, reject) => {
if (!network.online) {
reject()
return
}
- if (_options.notification && _options.failed.notification.show && _options.pending) PendingNotification(t)
+ if (_options.notification && _options.failed.notification.show && _options.pending) {
+ dismissToastList(["pending", "warning", "error", "success"]);
+ Notifications(pushToastList, "pending", t);
+ }
+
axios({
url: url, method: method, data: _options.data, ..._options.requestOptions
})
.then(response => {
- successRequest(response, t, _options)
+ successRequest(pushToastList, dismissToastList, response, t, _options)
resolve(response)
})
.catch(error => {
if (error.response) {
- errorResponse(error.response, clearToken, t, _options.notification && _options.failed.notification.show)
+ errorResponse(pushToastList, dismissToastList, error.response, clearToken, t, _options.notification && _options.failed.notification.show)
} else if (error.request) {
- errorRequest(t, _options.notification && _options.failed.notification.show)
+ errorRequest(dismissToastList, t, _options.notification && _options.failed.notification.show)
} else {
- errorSetting(t, _options.notification && _options.failed.notification.show)
+ errorSetting(dismissToastList, t, _options.notification && _options.failed.notification.show)
}
reject(error)
})
diff --git a/src/lib/app/hooks/useToast.jsx b/src/lib/app/hooks/useToast.jsx
new file mode 100644
index 0000000..5e1d5b4
--- /dev/null
+++ b/src/lib/app/hooks/useToast.jsx
@@ -0,0 +1,21 @@
+import {useContext} from "react";
+import {ToastContext} from "@/lib/app/contexts/toast";
+
+const useToast = () => {
+ const {dispatch} = useContext(ToastContext);
+
+ const pushToastList = (toast_type, toast_id) => {
+ dispatch({type: "PUSH", toast_type, toast_id});
+ };
+
+ const dismissToastList = (toast_type) => {
+ dispatch({type: "DISMISS", toast_type});
+ };
+
+ return {
+ pushToastList,
+ dismissToastList
+ }
+};
+
+export default useToast;
\ No newline at end of file
diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx
index 342f2a4..9824687 100644
--- a/src/pages/_app.jsx
+++ b/src/pages/_app.jsx
@@ -8,6 +8,8 @@ import "moment/locale/fa";
import {NextIntlProvider} from "next-intl";
import TitlePage from "@/core/components/TitlePage";
import Layout from "@/layouts";
+import {ToastProvider} from "@/lib/app/contexts/toast";
+import {PrintProvider} from "@/lib/app/contexts/print";
const App = ({Component, pageProps}) => {
@@ -15,15 +17,19 @@ const App = ({Component, pageProps}) => {
<>
-
+
{pageProps.messages ? : ''}
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/dashboard/change-password/index.jsx b/src/pages/dashboard/change-password/index.jsx
index d23721d..ef75fb7 100644
--- a/src/pages/dashboard/change-password/index.jsx
+++ b/src/pages/dashboard/change-password/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.change_password",
isBot,
+ locale,
layout: {name: 'DashboardLayout'}
},
};
diff --git a/src/pages/dashboard/commercial-chief/index.jsx b/src/pages/dashboard/commercial-chief/index.jsx
index e5dc96c..1ec5fbf 100644
--- a/src/pages/dashboard/commercial-chief/index.jsx
+++ b/src/pages/dashboard/commercial-chief/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.commercial_chief_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_commercial_refahi"]}}
},
};
diff --git a/src/pages/dashboard/development-assistant/index.jsx b/src/pages/dashboard/development-assistant/index.jsx
index 6f1a0ff..346a340 100644
--- a/src/pages/dashboard/development-assistant/index.jsx
+++ b/src/pages/dashboard/development-assistant/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.development_assistant_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_development_refahi"]}}
},
};
diff --git a/src/pages/dashboard/edit-profile/index.jsx b/src/pages/dashboard/edit-profile/index.jsx
index dede28e..032a607 100644
--- a/src/pages/dashboard/edit-profile/index.jsx
+++ b/src/pages/dashboard/edit-profile/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.edit_profile",
isBot,
+ locale,
layout: {name: 'DashboardLayout'}
},
};
diff --git a/src/pages/dashboard/expert-management/index.jsx b/src/pages/dashboard/expert-management/index.jsx
index 2db2389..5f1c29a 100644
--- a/src/pages/dashboard/expert-management/index.jsx
+++ b/src/pages/dashboard/expert-management/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.expert_management",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_experts"]}}
},
};
diff --git a/src/pages/dashboard/index.jsx b/src/pages/dashboard/index.jsx
index f3cf481..0e309c3 100644
--- a/src/pages/dashboard/index.jsx
+++ b/src/pages/dashboard/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.dashboard_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout'}
},
};
diff --git a/src/pages/dashboard/inspector-expert/index.jsx b/src/pages/dashboard/inspector-expert/index.jsx
index f953f30..c2d14f8 100644
--- a/src/pages/dashboard/inspector-expert/index.jsx
+++ b/src/pages/dashboard/inspector-expert/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.inspector_expert_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_inspector_refahi"]}}
},
};
diff --git a/src/pages/dashboard/machinery-expert/index.jsx b/src/pages/dashboard/machinery-expert/index.jsx
index 5b2f945..df23f2e 100644
--- a/src/pages/dashboard/machinery-expert/index.jsx
+++ b/src/pages/dashboard/machinery-expert/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.machinary_office_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_machinery_navgan"]}}
},
};
diff --git a/src/pages/dashboard/machinery-expert/prints/report.jsx b/src/pages/dashboard/machinery-expert/prints/report.jsx
new file mode 100644
index 0000000..526851b
--- /dev/null
+++ b/src/pages/dashboard/machinery-expert/prints/report.jsx
@@ -0,0 +1,21 @@
+import {parse} from "next-useragent";
+import ReportComponent from "@/components/dashboard/machinary-office/Prints/report";
+
+export default function Report() {
+ return (
+
+ )
+}
+
+export async function getServerSideProps({req, locale}) {
+ const {isBot} = parse(req.headers["user-agent"]);
+ return {
+ props: {
+ messages: (await import(`&/locales/${locale}/app.json`)).default,
+ title: "Dashboard.machinary_office_page",
+ isBot,
+ locale,
+ layout: {name: 'PrintLayout'}
+ },
+ };
+}
\ No newline at end of file
diff --git a/src/pages/dashboard/navgan-loan-management/index.jsx b/src/pages/dashboard/navgan-loan-management/index.jsx
index 61922ad..792ea3e 100644
--- a/src/pages/dashboard/navgan-loan-management/index.jsx
+++ b/src/pages/dashboard/navgan-loan-management/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.loan_management_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_navgan_loan"]}}
},
};
diff --git a/src/pages/dashboard/navgan-province-manager/index.jsx b/src/pages/dashboard/navgan-province-manager/index.jsx
index b0c0be6..39102cb 100644
--- a/src/pages/dashboard/navgan-province-manager/index.jsx
+++ b/src/pages/dashboard/navgan-province-manager/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.province_manager_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_province_affairs_navgan"]}}
},
};
diff --git a/src/pages/dashboard/passenger-boss/index.jsx b/src/pages/dashboard/passenger-boss/index.jsx
index a549f74..c38b95f 100644
--- a/src/pages/dashboard/passenger-boss/index.jsx
+++ b/src/pages/dashboard/passenger-boss/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.passenger_boss_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_province_working_group_navgan"]}}
},
};
diff --git a/src/pages/dashboard/passenger-boss/prints/final-credit-amount.jsx b/src/pages/dashboard/passenger-boss/prints/final-credit-amount.jsx
new file mode 100644
index 0000000..0bd763b
--- /dev/null
+++ b/src/pages/dashboard/passenger-boss/prints/final-credit-amount.jsx
@@ -0,0 +1,21 @@
+import {parse} from "next-useragent";
+import FinalCreditAmountComponent from "@/components/dashboard/passenger-boss/Prints/final-credit-amount";
+
+export default function FinalCreditAmount() {
+ return (
+
+ )
+}
+
+export async function getServerSideProps({req, locale}) {
+ const {isBot} = parse(req.headers["user-agent"]);
+ return {
+ props: {
+ messages: (await import(`&/locales/${locale}/app.json`)).default,
+ title: "Dashboard.passenger_boss_page",
+ isBot,
+ locale,
+ layout: {name: 'PrintLayout'}
+ },
+ };
+}
\ No newline at end of file
diff --git a/src/pages/dashboard/passenger-office-chief/index.jsx b/src/pages/dashboard/passenger-office-chief/index.jsx
index 03aa612..5ca084e 100644
--- a/src/pages/dashboard/passenger-office-chief/index.jsx
+++ b/src/pages/dashboard/passenger-office-chief/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.passenger_office_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_passenger_office_navgan"]}}
},
};
diff --git a/src/pages/dashboard/province-head-expert/index.jsx b/src/pages/dashboard/province-head-expert/index.jsx
index 64bffb1..b556d20 100644
--- a/src/pages/dashboard/province-head-expert/index.jsx
+++ b/src/pages/dashboard/province-head-expert/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.province_head_expert",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_province_headquarter_refahi"]}}
},
};
diff --git a/src/pages/dashboard/refahi-loan-management/index.jsx b/src/pages/dashboard/refahi-loan-management/index.jsx
index 1ae6954..4aab9a0 100644
--- a/src/pages/dashboard/refahi-loan-management/index.jsx
+++ b/src/pages/dashboard/refahi-loan-management/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.loan_management_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_refahi_loan"]}}
},
};
diff --git a/src/pages/dashboard/refahi-province-manager/index.jsx b/src/pages/dashboard/refahi-province-manager/index.jsx
index 9922b50..cc5c1eb 100644
--- a/src/pages/dashboard/refahi-province-manager/index.jsx
+++ b/src/pages/dashboard/refahi-province-manager/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.province_manager_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_province_affairs_refahi"]}}
},
};
diff --git a/src/pages/dashboard/role-management/index.jsx b/src/pages/dashboard/role-management/index.jsx
index 97238db..7ae6d45 100644
--- a/src/pages/dashboard/role-management/index.jsx
+++ b/src/pages/dashboard/role-management/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.role_management_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_roles"]}}
},
};
diff --git a/src/pages/dashboard/transportation-assistant/index.jsx b/src/pages/dashboard/transportation-assistant/index.jsx
index eccb330..7c0b4b6 100644
--- a/src/pages/dashboard/transportation-assistant/index.jsx
+++ b/src/pages/dashboard/transportation-assistant/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.transportation_assistance",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_transportation_navgan"]}}
},
};
diff --git a/src/pages/dashboard/user-management/index.jsx b/src/pages/dashboard/user-management/index.jsx
index f0f9b24..e92c2b2 100644
--- a/src/pages/dashboard/user-management/index.jsx
+++ b/src/pages/dashboard/user-management/index.jsx
@@ -14,6 +14,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Dashboard.user_management_page",
isBot,
+ locale,
layout: {name: 'DashboardLayout', props: {permissions: ["manage_users"]}}
},
};
diff --git a/src/pages/index.jsx b/src/pages/index.jsx
index 2e146c6..996ba03 100644
--- a/src/pages/index.jsx
+++ b/src/pages/index.jsx
@@ -16,6 +16,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "first_page",
isBot,
+ locale
},
};
}
diff --git a/src/pages/login-expert.jsx b/src/pages/login-expert.jsx
index 1cc6abf..393f916 100644
--- a/src/pages/login-expert.jsx
+++ b/src/pages/login-expert.jsx
@@ -17,6 +17,7 @@ export async function getServerSideProps({req, locale}) {
messages: (await import(`&/locales/${locale}/app.json`)).default,
title: "Titles.title_login_expert_page",
isBot,
+ locale
},
};
}