From 46d6dc6bb6d1809d90c0979e808d956b85e9c595 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 5 Nov 2023 11:21:03 +0330 Subject: [PATCH 01/42] LFFE-14 Testing config --- .gitlab-ci.yml | 81 ++++++++++++++++++++++++++++ cypress.config.js | 16 ++++++ cypress/support/commands.js | 25 +++++++++ cypress/support/component-index.html | 14 +++++ cypress/support/component.js | 27 ++++++++++ cypress/support/e2e.js | 20 +++++++ jest.config.mjs | 17 ++++++ jest.setup.js | 22 ++++++++ mocks/AppWithProvider.jsx | 13 +++++ mocks/handler.js | 1 + mocks/server.js | 4 ++ package.json | 18 ++++++- 12 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 cypress.config.js create mode 100644 cypress/support/commands.js create mode 100644 cypress/support/component-index.html create mode 100644 cypress/support/component.js create mode 100644 cypress/support/e2e.js create mode 100644 jest.config.mjs create mode 100644 jest.setup.js create mode 100644 mocks/AppWithProvider.jsx create mode 100644 mocks/handler.js create mode 100644 mocks/server.js 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/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..eda0ea5 --- /dev/null +++ b/mocks/handler.js @@ -0,0 +1 @@ +export const handler = []; 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 a560c0c..479f98d 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", @@ -50,7 +54,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": "^2.0.3", + "next-router-mock": "^0.9.10", "run-script-os": "^1.1.6" } } From 374c6a4d6bf5c3ff86e225c246cb82298ae38e93 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 5 Nov 2023 11:50:41 +0330 Subject: [PATCH 02/42] LFFE-14 Create test for first page --- example.env.local | 1 + mocks/handler.js | 6 +- mocks/handlers/user.js | 19 ++++++ package.json | 2 +- public/locales/fa/app.json | 1 + src/components/first/__tests__/index.test.js | 61 ++++++++++++++++++++ src/components/first/index.jsx | 51 ++++++++-------- src/pages/_app.jsx | 2 +- src/pages/index.jsx | 1 + 9 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 mocks/handlers/user.js create mode 100644 src/components/first/__tests__/index.test.js diff --git a/example.env.local b/example.env.local index b9a7e4c..7eb8d64 100644 --- a/example.env.local +++ b/example.env.local @@ -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/mocks/handler.js b/mocks/handler.js index eda0ea5..a4e5e8f 100644 --- a/mocks/handler.js +++ b/mocks/handler.js @@ -1 +1,5 @@ -export const handler = []; +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/package.json b/package.json index 479f98d..6459d69 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "eslint-plugin-testing-library": "^6.1.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "msw": "^2.0.3", + "msw": "^1.3.1", "next-router-mock": "^0.9.10", "run-script-os": "^1.1.6" } diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 2a47ea5..9d3297f 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": "ورود کارشناس", 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/pages/_app.jsx b/src/pages/_app.jsx index 342f2a4..d7c2169 100644 --- a/src/pages/_app.jsx +++ b/src/pages/_app.jsx @@ -15,7 +15,7 @@ const App = ({Component, pageProps}) => { <> - + {pageProps.messages ? : ''} 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 }, }; } From 5f915555261b88b9c99acda192e2a1088c6b8534 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 5 Nov 2023 11:56:40 +0330 Subject: [PATCH 03/42] LFFE-14 Fixed bug imports --- src/components/layouts/Dashboard/index.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/layouts/Dashboard/index.jsx b/src/components/layouts/Dashboard/index.jsx index f54a894..953dd3c 100644 --- a/src/components/layouts/Dashboard/index.jsx +++ b/src/components/layouts/Dashboard/index.jsx @@ -1,10 +1,10 @@ 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 BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs"; const drawerWidth = 240; From 9ce4cd8f6e1bb318fcd46a1b473d671e7034a35e Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 5 Nov 2023 13:27:25 +0330 Subject: [PATCH 04/42] LFFE-12 add locales in pages --- src/pages/dashboard/change-password/index.jsx | 1 + src/pages/dashboard/commercial-chief/index.jsx | 1 + src/pages/dashboard/development-assistant/index.jsx | 1 + src/pages/dashboard/edit-profile/index.jsx | 1 + src/pages/dashboard/expert-management/index.jsx | 1 + src/pages/dashboard/index.jsx | 1 + src/pages/dashboard/inspector-expert/index.jsx | 1 + src/pages/dashboard/machinery-expert/index.jsx | 1 + src/pages/dashboard/navgan-loan-management/index.jsx | 1 + src/pages/dashboard/navgan-province-manager/index.jsx | 1 + src/pages/dashboard/passenger-boss/index.jsx | 1 + src/pages/dashboard/passenger-office-chief/index.jsx | 1 + src/pages/dashboard/province-head-expert/index.jsx | 1 + src/pages/dashboard/refahi-loan-management/index.jsx | 1 + src/pages/dashboard/refahi-province-manager/index.jsx | 1 + src/pages/dashboard/role-management/index.jsx | 1 + src/pages/dashboard/transportation-assistant/index.jsx | 1 + src/pages/dashboard/user-management/index.jsx | 1 + src/pages/login-expert.jsx | 1 + 19 files changed, 19 insertions(+) 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 eab73fc..7b2de1e 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_transportation_navgan"]}} }, }; 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/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-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/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 }, }; } From f08e83907ea5194bdfe39442313e295bc559c2cb Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 5 Nov 2023 13:41:27 +0330 Subject: [PATCH 05/42] LFFE-12 structured password field --- src/components/login-expert/index.jsx | 2 +- .../components/{PasswordField.jsx => PasswordField/index.jsx} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/core/components/{PasswordField.jsx => PasswordField/index.jsx} (100%) 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/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 From c12154368a3c2caadac60c217b5951159a24c466 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 5 Nov 2023 13:44:28 +0330 Subject: [PATCH 06/42] LFFE-12 create toast and useToast --- src/lib/app/contexts/toast.jsx | 37 ++++++++++++++++++++++++++++++++++ src/lib/app/hooks/useToast.jsx | 21 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/lib/app/contexts/toast.jsx create mode 100644 src/lib/app/hooks/useToast.jsx 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/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 From 8833120e319fb67e2ba95b9802867ce803f288c0 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 5 Nov 2023 14:37:22 +0330 Subject: [PATCH 07/42] LFFE-12 add toast and correct the user and language bsaed on it --- src/core/components/DataTable.jsx | 16 +++++---- src/core/utils/errorHandler.js | 59 ++++++++++++++++--------------- src/core/utils/succesHandler.js | 9 +++-- src/lib/app/contexts/language.jsx | 8 ++--- src/lib/app/contexts/user.jsx | 9 +---- src/lib/app/hooks/useRequest.jsx | 19 ++++++---- src/pages/_app.jsx | 13 ++++--- 7 files changed, 68 insertions(+), 65 deletions(-) diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx index 70cee09..8e3fe63 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) => 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/lib/app/contexts/language.jsx b/src/lib/app/contexts/language.jsx index a6df674..03e3bc3 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/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/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/pages/_app.jsx b/src/pages/_app.jsx index d7c2169..8d2f3b0 100644 --- a/src/pages/_app.jsx +++ b/src/pages/_app.jsx @@ -8,6 +8,7 @@ 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"; const App = ({Component, pageProps}) => { @@ -19,11 +20,13 @@ const App = ({Component, pageProps}) => { {pageProps.messages ? : ''} - - - - - + + + + + + + From 2e2aabc528cade0b343ee75eb24349ace6bb61c8 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 5 Nov 2023 14:51:50 +0330 Subject: [PATCH 08/42] LFFE-12 add add isLoading in permissionLists --- public/locales/fa/app.json | 6 +- .../Form/CreateForm/CreateContent.jsx | 61 +++++++++++-------- .../Form/UpdateForm/UpdateContent.jsx | 61 +++++++++++-------- src/lib/app/hooks/usePermissions.jsx | 7 ++- 4 files changed, 80 insertions(+), 55 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 9d3297f..9fd2984 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -360,7 +360,8 @@ "permission_min_error": "حداقل باید یک دسترسی انتخاب شود", "type_id": "نوع کاربر", "navgan_id": "کد ناوگان", - "button-update": "ویرایش" + "button-update": "ویرایش", + "loading_permissions_list": "درحال دریافت لیست دسترسی ها" }, "UploadSystem": { "upload_file": "فایل خود را بارگذاری کنید", @@ -467,6 +468,7 @@ "type_id": "نوع کاربر", "navgan_id": "کد ناوگان", "button-cancel": "انصراف", - "button-add": "ثبت" + "button-add": "ثبت", + "loading_permissions_list": "درحال دریافت لیست دسترسی ها" } } 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/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/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 From c745791c5952c331cc1905178647c69d34a4808d Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Sun, 5 Nov 2023 14:53:25 +0330 Subject: [PATCH 09/42] LFFE-6 sidebar improvement --- .../dashboardLayouts/sidebar/SidebarList.jsx | 64 +++++++++++++------ .../sidebar/SidebarListItem.jsx | 45 ++++++------- .../sidebar/SidebarListSubItem.jsx | 60 ++++++++++++----- .../dashboardLayouts/sidebar/index.jsx | 2 + 4 files changed, 111 insertions(+), 60 deletions(-) diff --git a/src/layouts/dashboardLayouts/sidebar/SidebarList.jsx b/src/layouts/dashboardLayouts/sidebar/SidebarList.jsx index a030b52..265da6c 100644 --- a/src/layouts/dashboardLayouts/sidebar/SidebarList.jsx +++ b/src/layouts/dashboardLayouts/sidebar/SidebarList.jsx @@ -1,5 +1,5 @@ import {Divider, List} from "@mui/material"; -import {useEffect, useReducer} from "react"; +import {Fragment, useEffect, useReducer, useRef, useState} from "react"; import SidebarListItem from "./SidebarListItem"; import sidebarMenu from "@/core/data/sidebarMenu"; import {useRouter} from "next/router"; @@ -10,18 +10,27 @@ function reducer(state, action) { case "COLLAPSE_MENU": return state.map((itemsArr) => 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 } + : item.subItem && Array.isArray(item.subItem) + ? { + ...item, + subItem: item.subItem.map((subitem) => ({ + ...subitem, + selected: subitem.route === action.route, + })), + showSubItem: item.subItem.some( + (subitem) => subitem.selected + ), + } + : item; + }) ); default: throw new Error(); @@ -32,26 +41,39 @@ 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}); + setSelectedKey(router.pathname); }, [router.pathname]); - const filteredItemMenu = itemMenu[0].filter( - (item) => user.permissions.includes(item.permission) || item.permission === "all" - ); + + 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/layouts/dashboardLayouts/sidebar/SidebarListItem.jsx b/src/layouts/dashboardLayouts/sidebar/SidebarListItem.jsx index ebeb14c..5e9039b 100644 --- a/src/layouts/dashboardLayouts/sidebar/SidebarListItem.jsx +++ b/src/layouts/dashboardLayouts/sidebar/SidebarListItem.jsx @@ -9,23 +9,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 ? ( + + 0 ? notification_count[item.name] : 0} + color="error" + variant="standard" + anchorOrigin={{ + vertical: "top", + horizontal: "right", + }} + /> + + ) : null; + }; return ( - - - - - }> + <> + { }, })} onClick={() => { - if (item.type == "menu") { - dispatch({type: "COLLAPSE_MENU", key: item.key}); + if (hasSubItems) { + dispatch({ type: "COLLAPSE_MENU", key: item.key }); } - handleDrawerToggle(); }} sx={{ minHeight: 48, @@ -66,9 +69,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { } /> - - {item.type == "menu" && - (item.showSubItem ? : )} + {hasSubItems && (item.showSubItem ? : )} {item.subItem && ( @@ -77,7 +78,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { handleDrawerToggle={handleDrawerToggle} /> )} - + ); }; diff --git a/src/layouts/dashboardLayouts/sidebar/SidebarListSubItem.jsx b/src/layouts/dashboardLayouts/sidebar/SidebarListSubItem.jsx index 3407081..c94d797 100644 --- a/src/layouts/dashboardLayouts/sidebar/SidebarListSubItem.jsx +++ b/src/layouts/dashboardLayouts/sidebar/SidebarListSubItem.jsx @@ -1,18 +1,43 @@ import {NextLinkComposed} from "@/core/components/LinkRouting"; -import InboxIcon from "@mui/icons-material/MoveToInbox"; -import {Collapse, List, ListItemButton, ListItemIcon, ListItemText,} from "@mui/material"; +import { + Badge, + 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 SidebarListSubItem = ({item, handleDrawerToggle }) => { const t = useTranslations(); + const {notification_count} = useNotification(); + const renderBadge = (subitem) => ( + + 0 ? notification_count[subitem.name] : 0} + color="error" + variant="standard" + anchorOrigin={{ + vertical: "top", + horizontal: "right", + }} + /> + + ); 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(); - }} > { pr: 2, }} > - + {subitem.icon} - + + {t(subitem.secondary)} + + ) : null + }/> - + ))} + ); }; diff --git a/src/layouts/dashboardLayouts/sidebar/index.jsx b/src/layouts/dashboardLayouts/sidebar/index.jsx index 2498dfd..bdab3a2 100644 --- a/src/layouts/dashboardLayouts/sidebar/index.jsx +++ b/src/layouts/dashboardLayouts/sidebar/index.jsx @@ -21,6 +21,7 @@ const Sidebar = (props) => { "& .MuiDrawer-paper": { boxSizing: "border-box", width: props.drawerWidth, + overflow: "hidden", }, }} > @@ -33,6 +34,7 @@ const Sidebar = (props) => { "& .MuiDrawer-paper": { boxSizing: "border-box", width: props.drawerWidth, + overflow: "hidden", }, }} open From e56f78d16e9d732e435787488854b071874723f5 Mon Sep 17 00:00:00 2001 From: Yasiu1376 Date: Sun, 5 Nov 2023 15:08:29 +0330 Subject: [PATCH 10/42] LFFE-15 fixed sidebar bug --- src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx | 2 +- src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx index 5e9039b..e9657a9 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx @@ -15,7 +15,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { return !hasSubItems ? ( 0 ? notification_count[item.name] : 0} + badgeContent={notification_count ? notification_count[item.name] : 0} color="error" variant="standard" anchorOrigin={{ diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx index c94d797..d663d2c 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx @@ -19,7 +19,7 @@ const SidebarListSubItem = ({item, handleDrawerToggle }) => { const renderBadge = (subitem) => ( 0 ? notification_count[subitem.name] : 0} + badgeContent={notification_count ? notification_count[subitem.name] : 0} color="error" variant="standard" anchorOrigin={{ From 893975c63ba03abf407aa5fc501187f3f5ab7c78 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Sun, 5 Nov 2023 15:42:00 +0330 Subject: [PATCH 11/42] LFFE-12 debugs and testing manually --- .../Form/ConfirmForm/ConfirmContent.jsx | 2 +- .../Form/RejectForm/RejectContent.jsx | 6 +- .../Form/ConfirmForm/ConfirmContent.jsx | 2 +- .../Form/RejectForm/RejectContent.jsx | 2 +- .../Form/ConfirmForm/ConfirmContent.jsx | 2 +- .../Form/RejectForm/RejectContent.jsx | 2 +- .../Form/RejectForm/RejectContent.jsx | 2 +- .../Form/ConfirmForm/ConfirmContent.jsx | 4 +- .../Form/RejectForm/RejectContent.jsx | 2 +- .../notifications/ErrorNotification.jsx | 8 ++- .../notifications/PendingNotification.jsx | 8 ++- .../notifications/SuccessNotification.jsx | 8 ++- .../notifications/UploadFileNotification.jsx | 4 +- .../notifications/WarningNotification.jsx | 8 ++- src/core/components/notifications/index.jsx | 63 ++++++------------- .../dashboard/expert-management/index.jsx | 2 +- 16 files changed, 54 insertions(+), 71 deletions(-) diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx index 261c083..da0acda 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx @@ -37,7 +37,7 @@ 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', { 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/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx index 5de9eb3..af9e93a 100644 --- a/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx @@ -24,7 +24,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { }, 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', { 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/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx index 66ccd27..d189a6b 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx @@ -37,7 +37,7 @@ 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.description != "") formData.append("expert_description", values.description); if (values.confirm_img != null) formData.append("attachment", values.confirm_img); requestServer(`${CONFIRM_PASSENGER_BOSS}/${rowId}`, 'post', { 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-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/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx index dc4d093..52137c2 100644 --- a/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx @@ -24,7 +24,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { }, 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', { @@ -106,7 +106,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { {t("ConfirmDialog.button-cancel")} 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/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/pages/dashboard/expert-management/index.jsx b/src/pages/dashboard/expert-management/index.jsx index 7b2de1e..5f1c29a 100644 --- a/src/pages/dashboard/expert-management/index.jsx +++ b/src/pages/dashboard/expert-management/index.jsx @@ -15,7 +15,7 @@ export async function getServerSideProps({req, locale}) { title: "Dashboard.expert_management", isBot, locale, - layout: {name: 'DashboardLayout', props: {permissions: ["manage_transportation_navgan"]}} + layout: {name: 'DashboardLayout', props: {permissions: ["manage_experts"]}} }, }; } From 07dff0c742d6333709813e5b7a84e32e16a783e7 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 5 Nov 2023 15:59:08 +0330 Subject: [PATCH 12/42] LFFE-16 Fixed bug swr in DataTable.jsx --- src/core/components/DataTable.jsx | 2 +- src/layouts/index.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/components/DataTable.jsx b/src/core/components/DataTable.jsx index 70cee09..d4b93e7 100644 --- a/src/core/components/DataTable.jsx +++ b/src/core/components/DataTable.jsx @@ -60,7 +60,7 @@ function DataTable(props) { }).then((response) => response.data).catch(() => { }) , { - revalidateIfStale: false, + revalidateIfStale: true, revalidateOnFocus: false, revalidateOnReconnect: true, keepPreviousData: true diff --git a/src/layouts/index.jsx b/src/layouts/index.jsx index 1506661..4d4ed7f 100644 --- a/src/layouts/index.jsx +++ b/src/layouts/index.jsx @@ -8,7 +8,7 @@ const layoutList = { const Layout = ({layout = {}, children}) => { const Component = layoutList[layout?.name] || Fragment - const props = layout.props || {} + const props = layout?.props || {} return ( From d973c72db8e4dfb32646508db25e7fa29de9a25c Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 5 Nov 2023 16:20:33 +0330 Subject: [PATCH 13/42] change version --- example.env.local | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.env.local b/example.env.local index 7eb8d64..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" From e4abfb9d950dfa9ffe39f6b45ca51ad9d939ecac Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 6 Nov 2023 14:17:16 +0330 Subject: [PATCH 14/42] LFFE-17 Fixed bug setOpenConfirmDialog --- src/components/dashboard/role-management/Form/DeleteForm.jsx | 1 + 1 file changed, 1 insertion(+) 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(() => { From ad256ae6600796643034976e8e2dde17fab563bf Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 6 Nov 2023 14:17:54 +0330 Subject: [PATCH 15/42] LFFE-17 Implementation of loading for navigation pages dashboard --- public/locales/fa/app.json | 1 + .../layouts/Dashboard/Content/index.jsx | 65 +++++++++++++++++++ .../layouts/Dashboard/Sidebar/SidebarList.jsx | 40 ++++++++++-- .../Dashboard/Sidebar/SidebarListItem.jsx | 22 +++++-- .../Dashboard/Sidebar/SidebarListSubItem.jsx | 4 +- .../layouts/Dashboard/Sidebar/index.jsx | 37 ++++++----- src/components/layouts/Dashboard/index.jsx | 11 +--- src/core/components/LoadingHardPage.jsx | 31 +++++---- src/core/data/sidebarMenu.jsx | 32 ++++----- 9 files changed, 181 insertions(+), 62 deletions(-) create mode 100644 src/components/layouts/Dashboard/Content/index.jsx diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 9fd2984..7960a14 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -16,6 +16,7 @@ "between": "میان", "online_message": "شما به اینترنت وصل هستید", "offline_message": "اتصال شما به اینترنت قطع شده است", + "routing_to": "در حال انتقال به صفحه", "header": { "open_profile": "پروفایل", "edit_profile": " پروفایل", 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/Sidebar/SidebarList.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx index 265da6c..3a2eb44 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx @@ -1,5 +1,5 @@ import {Divider, List} from "@mui/material"; -import {Fragment, useEffect, useReducer, useRef, useState} from "react"; +import {Fragment, useEffect, useReducer, useState} from "react"; import SidebarListItem from "./SidebarListItem"; import sidebarMenu from "@/core/data/sidebarMenu"; import {useRouter} from "next/router"; @@ -10,20 +10,41 @@ function reducer(state, action) { case "COLLAPSE_MENU": return state.map((itemsArr) => 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) => { return item.type === "page" - ? { ...item, selected: action.route === item.route } + ? {...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 @@ -46,7 +67,16 @@ export default function SidebarList({handleDrawerToggle}) { useEffect(() => { dispatch({type: "SELECTED", route: router.pathname}); setSelectedKey(router.pathname); - }, [router.pathname]); + const handlerStartRoute = (url) => { + dispatch({type: "SELECTING", route: url}); + } + + router.events.on('routeChangeStart', handlerStartRoute) + + return () => { + router.events.off('routeChangeStart', handlerStartRoute) + } + }, [router]); useEffect(() => { @@ -57,7 +87,7 @@ export default function SidebarList({handleDrawerToggle}) { }, [selectedKey]); return ( - + {itemMenu.map((itemArr, index) => ( {itemArr.map((item) => diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListItem.jsx index e9657a9..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,7 +18,7 @@ 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 ? ( @@ -40,7 +49,7 @@ const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => { })} onClick={() => { if (hasSubItems) { - dispatch({ type: "COLLAPSE_MENU", key: item.key }); + dispatch({type: "COLLAPSE_MENU", key: item.key}); } }} sx={{ @@ -53,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} { } /> - {hasSubItems && (item.showSubItem ? : )} + {hasSubItems && (item.showSubItem ? : )} {item.subItem && ( diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx index d663d2c..0543d8c 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx @@ -13,7 +13,7 @@ import { import {useTranslations} from "next-intl"; import useNotification from "@/lib/app/hooks/useNotification"; -const SidebarListSubItem = ({item, handleDrawerToggle }) => { +const SidebarListSubItem = ({item, handleDrawerToggle}) => { const t = useTranslations(); const {notification_count} = useNotification(); const renderBadge = (subitem) => ( @@ -50,6 +50,8 @@ const SidebarListSubItem = ({item, handleDrawerToggle }) => { sx={{ minWidth: 0, justifyContent: "center", + width: 40, + height: 24, pr: 2, }} > diff --git a/src/components/layouts/Dashboard/Sidebar/index.jsx b/src/components/layouts/Dashboard/Sidebar/index.jsx index bdab3a2..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 ? ( + + + + ) : ( { > - - - + )} ); }; diff --git a/src/components/layouts/Dashboard/index.jsx b/src/components/layouts/Dashboard/index.jsx index 953dd3c..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 {Toolbar} from "@mui/material"; 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 BreadCrumbs from "@/components/layouts/Dashboard/Breadcrumbs"; +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/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/data/sidebarMenu.jsx b/src/core/data/sidebarMenu.jsx index db8e0b8..c54bbca 100644 --- a/src/core/data/sidebarMenu.jsx +++ b/src/core/data/sidebarMenu.jsx @@ -20,7 +20,7 @@ const sidebarMenu = [ key: "sidebar.dashboard", type: "page", route: "/dashboard", - icon: , + icon: , selected: false, permission: "all", }, @@ -30,7 +30,7 @@ const sidebarMenu = [ name: "passenger_office_chief", type: "page", route: "/dashboard/passenger-office-chief", - icon: , + icon: , selected: false, permission: "manage_passenger_office_navgan", }, @@ -39,7 +39,7 @@ const sidebarMenu = [ name: "machinery_expert", type: "page", route: "/dashboard/machinery-expert", - icon: , + icon: , selected: false, permission: "manage_machinery_navgan", }, @@ -49,7 +49,7 @@ const sidebarMenu = [ name: "province_working_group", type: "page", route: "/dashboard/passenger-boss", - icon: , + icon: , selected: false, permission: "manage_province_working_group_navgan", }, @@ -59,7 +59,7 @@ const sidebarMenu = [ name: "transportation_assistant", type: "page", route: "/dashboard/transportation-assistant", - icon: , + icon: , selected: false, permission: "manage_transportation_navgan", }, @@ -69,7 +69,7 @@ const sidebarMenu = [ name: "province_manager_navgan", type: "page", route: "/dashboard/navgan-province-manager", - icon: , + icon: , selected: false, permission: "manage_province_affairs_navgan", }, @@ -78,7 +78,7 @@ const sidebarMenu = [ name: "province_head_expert", type: "page", route: "/dashboard/province-head-expert", - icon: , + icon: , selected: false, permission: "manage_province_headquarter_refahi", }, @@ -87,7 +87,7 @@ const sidebarMenu = [ name: "inspector_expert", type: "page", route: "/dashboard/inspector-expert", - icon: , + icon: , selected: false, permission: "manage_inspector_refahi", }, @@ -96,7 +96,7 @@ const sidebarMenu = [ name: "commercial_chief", type: "page", route: "/dashboard/commercial-chief", - icon: , + icon: , selected: false, permission: "manage_commercial_refahi", }, @@ -105,7 +105,7 @@ const sidebarMenu = [ name: "development_assistant", type: "page", route: "/dashboard/development-assistant", - icon: , + icon: , selected: false, permission: "manage_development_refahi", }, @@ -115,7 +115,7 @@ const sidebarMenu = [ name: "province_manager_refahi", type: "page", route: "/dashboard/refahi-province-manager", - icon: , + icon: , selected: false, permission: "manage_province_affairs_refahi", }, @@ -125,7 +125,7 @@ const sidebarMenu = [ name: "refahi_loan_management", type: "page", route: "/dashboard/refahi-loan-management", - icon: , + icon: , selected: false, permission: "manage_refahi_loan", }, @@ -135,7 +135,7 @@ const sidebarMenu = [ name: "navgan_loan_management", type: "page", route: "/dashboard/navgan-loan-management", - icon: , + icon: , selected: false, permission: "manage_navgan_loan", }, @@ -144,7 +144,7 @@ const sidebarMenu = [ name: "expert_management", type: "page", route: "/dashboard/expert-management", - icon: , + icon: , selected: false, permission: "manage_navgan_loan", }, @@ -153,7 +153,7 @@ const sidebarMenu = [ name: "user_management", type: "page", route: "/dashboard/user-management", - icon: , + icon: , selected: false, permission: "manage_users", }, @@ -162,7 +162,7 @@ const sidebarMenu = [ name: "role_management", type: "page", route: "/dashboard/role-management", - icon: , + icon: , selected: false, permission: "manage_roles", }, From dd6da5437944346233d9ba19f2d966f61390da1a Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 6 Nov 2023 14:58:50 +0330 Subject: [PATCH 16/42] LFFE-17 Implementation of loading for navigation pages dashboard --- .../layouts/Dashboard/Sidebar/SidebarListSubItem.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx index 0543d8c..3ef8731 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarListSubItem.jsx @@ -1,6 +1,7 @@ import {NextLinkComposed} from "@/core/components/LinkRouting"; import { Badge, + CircularProgress, Collapse, IconButton, List, @@ -55,7 +56,8 @@ const SidebarListSubItem = ({item, handleDrawerToggle}) => { pr: 2, }} > - {subitem.icon} + {subitem.routing ? + : subitem.icon} Date: Mon, 6 Nov 2023 15:05:17 +0330 Subject: [PATCH 17/42] LFFE-12 change confirm title --- public/locales/fa/app.json | 19 ++++++++++++------- .../Form/ConfirmForm/index.jsx | 7 +++---- .../Form/ConfirmForm/index.jsx | 7 +++---- .../passenger-boss/Form/ConfirmForm/index.jsx | 7 +++---- .../Form/ConfirmForm/index.jsx | 7 +++---- .../Form/ConfirmForm/index.jsx | 7 +++---- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 9fd2984..4f32987 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -160,7 +160,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "ارجاع به ماشین آلات" }, "CommercialChief": { "name": "نام", @@ -182,7 +183,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "تایید نهایی و ارجاع به بانک" }, "PassengerBoss": { "name": "نام", @@ -193,7 +195,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "ارجاع به معاونت حمل و نقل" }, "RefahiProvinceManager": { "name": "نام", @@ -261,7 +264,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "ارجاع به مدیر کل استانی" }, "InspectorExpert": { "name": "نام", @@ -283,7 +287,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "ارجاع به کارگروه استانی" }, "UserManagement": { "name": "نام", @@ -305,10 +310,10 @@ "updated_at": "تاریخ بروزرسانی" }, "ConfirmDialog": { - "confirm": "تایید", + "confirm": "ارجاع", "context": "آیا از تایید این آیتم اطمینان دارید؟", "button-cancel": "بستن", - "button-confirm": "تایید", + "button-confirm": "ارجاع", "description_error": "وارد کردن توضیحات الزامی است!", "optional": " (اختیاری)", "unit": " (ریال)", diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx index 95a6367..5ed809f 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx +++ b/src/components/dashboard/machinary-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) }} > - + - {t("ConfirmDialog.confirm")} + {t("MachinaryOffice.confirm")} 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) }} > - + - {t("ConfirmDialog.confirm")} + {t("NavganProvinceManager.confirm")} diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx index 95a6367..30f15dd 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx +++ b/src/components/dashboard/passenger-boss/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) }} > - + - {t("ConfirmDialog.confirm")} + {t("PassengerBoss.confirm")} 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) }} > - + - {t("ConfirmDialog.confirm")} + {t("PassengerOffice.confirm")} 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) }} > - + - {t("ConfirmDialog.confirm")} + {t("TransportationAssistance.confirm")} From 1cd81a77ba03576f6a3e47329a1db097d5ceedeb Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Mon, 6 Nov 2023 15:05:17 +0330 Subject: [PATCH 18/42] LFFE-18 change confirm title --- public/locales/fa/app.json | 19 ++++++++++++------- .../Form/ConfirmForm/index.jsx | 7 +++---- .../Form/ConfirmForm/index.jsx | 7 +++---- .../passenger-boss/Form/ConfirmForm/index.jsx | 7 +++---- .../Form/ConfirmForm/index.jsx | 7 +++---- .../Form/ConfirmForm/index.jsx | 7 +++---- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 9fd2984..4f32987 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -160,7 +160,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "ارجاع به ماشین آلات" }, "CommercialChief": { "name": "نام", @@ -182,7 +183,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "تایید نهایی و ارجاع به بانک" }, "PassengerBoss": { "name": "نام", @@ -193,7 +195,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "ارجاع به معاونت حمل و نقل" }, "RefahiProvinceManager": { "name": "نام", @@ -261,7 +264,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "ارجاع به مدیر کل استانی" }, "InspectorExpert": { "name": "نام", @@ -283,7 +287,8 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm" : "ارجاع به کارگروه استانی" }, "UserManagement": { "name": "نام", @@ -305,10 +310,10 @@ "updated_at": "تاریخ بروزرسانی" }, "ConfirmDialog": { - "confirm": "تایید", + "confirm": "ارجاع", "context": "آیا از تایید این آیتم اطمینان دارید؟", "button-cancel": "بستن", - "button-confirm": "تایید", + "button-confirm": "ارجاع", "description_error": "وارد کردن توضیحات الزامی است!", "optional": " (اختیاری)", "unit": " (ریال)", diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx index 95a6367..5ed809f 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx +++ b/src/components/dashboard/machinary-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) }} > - + - {t("ConfirmDialog.confirm")} + {t("MachinaryOffice.confirm")} 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) }} > - + - {t("ConfirmDialog.confirm")} + {t("NavganProvinceManager.confirm")} diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx index 95a6367..30f15dd 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx +++ b/src/components/dashboard/passenger-boss/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) }} > - + - {t("ConfirmDialog.confirm")} + {t("PassengerBoss.confirm")} 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) }} > - + - {t("ConfirmDialog.confirm")} + {t("PassengerOffice.confirm")} 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) }} > - + - {t("ConfirmDialog.confirm")} + {t("TransportationAssistance.confirm")} From 1074f2be1f9889c955bc70d37064bac143f3f869 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Mon, 6 Nov 2023 16:48:08 +0330 Subject: [PATCH 19/42] LFFE-18 validation on confirm img in machinary expert --- public/locales/fa/app.json | 17 +++++++-- .../Form/ConfirmForm/ConfirmContent.jsx | 38 +++++++++++++++++-- src/core/components/UploadSystem.jsx | 1 - 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 4f32987..ba420e7 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -161,7 +161,7 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm" : "ارجاع به ماشین آلات" + "confirm" : "ارجاع به کارشناس ماشین آلات" }, "CommercialChief": { "name": "نام", @@ -196,7 +196,7 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm" : "ارجاع به معاونت حمل و نقل" + "confirm" : "ارجاع به معاون حمل و نقل" }, "RefahiProvinceManager": { "name": "نام", @@ -288,7 +288,17 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm" : "ارجاع به کارگروه استانی" + "confirm" : "ارجاع به کارگروه استانی", + "upload_file": "گزارش کارشناسی را وارد کنید", + "upload_file_required": "وارد کردن گزارش کارشناسی الزامیست", + "upload_file_unit": "حداکثر 2Mb", + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", + "delete": "پاک کردن", + "phone_number": "شماره تلفن", + "national_id": "کد ملی", + "navgan_id": "کد ناوگان", + "type_id": "نوع کاربر", + "uploadfile_error": "حجم فایل بیشتر از 2 مگابایت می باشد" }, "UserManagement": { "name": "نام", @@ -325,6 +335,7 @@ "approved_amount_number": "مبلغ تصویب شده باید عدد باشد", "toman": "تومان", "proposed_amount_error": "وارد کردن مقدار پیشنهادی الزامیست", + "proposed_amount_number": "مقدار پیشنهادی باید عدد باشد", "proposed_amount": "مقدار پیشنهادی", "proposed_amount_positive": "مقدار پیشنهادی باید مثبت باشد" }, diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx index da0acda..94106a1 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx @@ -1,7 +1,15 @@ 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"; @@ -25,7 +33,18 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { `${t("ConfirmDialog.proposed_amount_number")}`, (value) => !isNaN(value) ).test("positive", `${t("ConfirmDialog.proposed_amount_positive")}`, (value) => value >= 0) - .required(t("ConfirmDialog.proposed_amount_error")) + .required(t("ConfirmDialog.proposed_amount_error")), + confirm_img: Yup.mixed() + .required(t("MachinaryOffice.upload_file_required")) + .test('fileSize', `${t("MachinaryOffice.upload_file_unit")}`, (value) => { + // if (!value) return true; // Skip if no file is provided + return value.size <= 2 * 1024 * 1024; // 100KB limit (adjust as needed) + }) + .test('fileType', `${t("MachinaryOffice.upload_file_format")}`, (value) => { + // if (!value) return true; // Skip if no file is provided + const allowedTypes = ['image/jpg', 'image/png', 'application/pdf']; // Define your allowed file types + return allowedTypes.includes(value.type); + }) }); const formik = useFormik({ @@ -129,7 +148,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) + } + helperText={ + formik.touched.confirm_img && formik.errors.confirm_img + } setShowAddIcon={setShowAddIcon} showAddIcon={showAddIcon} /> + + {formik.errors.confirm_img ? formik.errors.confirm_img : null} + diff --git a/src/core/components/UploadSystem.jsx b/src/core/components/UploadSystem.jsx index b40fb46..5bdec75 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, From a0efa89304fb152a215149cf8f2bb6e9aa014bd7 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 7 Nov 2023 10:54:45 +0330 Subject: [PATCH 20/42] LFFE-18 make confir img to be required in machinary and passenger boss --- public/locales/fa/app.json | 16 +++++--- .../Form/ConfirmForm/ConfirmContent.jsx | 27 +++---------- .../Form/ConfirmForm/ConfirmContent.jsx | 40 ++++++++++++------- src/core/components/UploadSystem.jsx | 1 - 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index ba420e7..0500aa2 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -161,7 +161,7 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm" : "ارجاع به کارشناس ماشین آلات" + "confirm": "ارجاع به کارشناس ماشین آلات" }, "CommercialChief": { "name": "نام", @@ -184,7 +184,7 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm" : "تایید نهایی و ارجاع به بانک" + "confirm": "تایید نهایی و ارجاع به بانک" }, "PassengerBoss": { "name": "نام", @@ -196,7 +196,11 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm" : "ارجاع به معاون حمل و نقل" + "confirm": "ارجاع به معاون حمل و نقل", + "upload_file": "صورت جلسه کارگروه را وارد کنید", + "upload_file_required": "وارد کردن صورت جلسه کارگروه الزامیست", + "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" }, "RefahiProvinceManager": { "name": "نام", @@ -265,7 +269,7 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm" : "ارجاع به مدیر کل استانی" + "confirm": "ارجاع به مدیر کل استانی" }, "InspectorExpert": { "name": "نام", @@ -288,10 +292,10 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm" : "ارجاع به کارگروه استانی", + "confirm": "ارجاع به کارگروه استانی", "upload_file": "گزارش کارشناسی را وارد کنید", "upload_file_required": "وارد کردن گزارش کارشناسی الزامیست", - "upload_file_unit": "حداکثر 2Mb", + "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", "delete": "پاک کردن", "phone_number": "شماره تلفن", diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx index 94106a1..75dd563 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx @@ -1,19 +1,10 @@ import UploadSystem from "@/core/components/UploadSystem"; import useNotification from "@/lib/app/hooks/useNotification"; import useRequest from "@/lib/app/hooks/useRequest"; -import { - Button, - DialogActions, - DialogContent, - FormHelperText, - 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"; @@ -79,19 +70,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); } }; @@ -164,16 +150,13 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { formik.touched.confirm_img && Boolean(formik.errors.confirm_img) } - helperText={ - formik.touched.confirm_img && formik.errors.confirm_img - } setShowAddIcon={setShowAddIcon} showAddIcon={showAddIcon} /> - {formik.errors.confirm_img ? formik.errors.confirm_img : null} + {formik.touched.confirm_img && formik.errors.confirm_img} diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx index d189a6b..e51d01c 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx @@ -1,11 +1,10 @@ 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"; @@ -25,7 +24,16 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { `${t("ConfirmDialog.approved_amount_number")}`, (value) => !isNaN(value) ).test("positive", `${t("ConfirmDialog.approved_amount_positive")}`, (value) => value >= 0) - .required(t("ConfirmDialog.approved_amount_error")) + .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/png', 'application/pdf']; // Define your allowed file types + return allowedTypes.includes(value.type); + }) }); const formik = useFormik({ @@ -38,7 +46,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { const formData = new FormData(); formData.append("approved_amount", values.approved_amount); if (values.description != "") formData.append("expert_description", values.description); - if (values.confirm_img != null) formData.append("attachment", values.confirm_img); + formData.append("attachment", values.confirm_img); requestServer(`${CONFIRM_PASSENGER_BOSS}/${rowId}`, 'post', { data: formData, @@ -60,19 +68,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 +86,6 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { formik.setFieldValue("approved_amount", formik.values.approved_amount) } }; - return ( <> @@ -129,7 +131,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/core/components/UploadSystem.jsx b/src/core/components/UploadSystem.jsx index 5bdec75..7a25890 100644 --- a/src/core/components/UploadSystem.jsx +++ b/src/core/components/UploadSystem.jsx @@ -18,7 +18,6 @@ const UploadSystem = ({ }) => { const t = useTranslations(); const fileInputRef = useRef(null); - const handleClick = () => { fileInputRef.current.click(); }; From 1bd2f4938b8a0e012290cd823ad865cda765aa1c Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 7 Nov 2023 11:25:41 +0330 Subject: [PATCH 21/42] LFFE-18 make cofirm img validation on format and size in passenger office --- .../Form/ConfirmForm/ConfirmContent.jsx | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx index 46a735f..2a6084b 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("PassengerBoss.upload_file_unit")}`, (value) => { + if (!value) return true + return value.size <= 2 * 1024 * 1024; + }) + .test('fileType', `${t("PassengerBoss.upload_file_format")}`, (value) => { + if (!value) return true + const allowedTypes = ['image/jpg', '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")} From 8e067bcb58467b443f3c9bca7c652d5a13965234 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 7 Nov 2023 11:38:52 +0330 Subject: [PATCH 22/42] LFFE-18 make cofirm img validation on format and size in province manager and transportation --- public/locales/fa/app.json | 12 ++++-- .../Form/ConfirmForm/ConfirmContent.jsx | 42 +++++++++++++------ .../Form/ConfirmForm/ConfirmContent.jsx | 4 +- .../Form/ConfirmForm/ConfirmContent.jsx | 42 +++++++++++++------ 4 files changed, 71 insertions(+), 29 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 0500aa2..9381558 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -161,7 +161,9 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm": "ارجاع به کارشناس ماشین آلات" + "confirm": "ارجاع به کارشناس ماشین آلات", + "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" }, "CommercialChief": { "name": "نام", @@ -184,7 +186,9 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm": "تایید نهایی و ارجاع به بانک" + "confirm": "تایید نهایی و ارجاع به بانک", + "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" }, "PassengerBoss": { "name": "نام", @@ -269,7 +273,9 @@ "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", - "confirm": "ارجاع به مدیر کل استانی" + "confirm": "ارجاع به مدیر کل استانی", + "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" }, "InspectorExpert": { "name": "نام", 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 af9e93a..8ca532e 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,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("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/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("expert_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/ConfirmContent.jsx b/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx index 2a6084b..458f8b5 100644 --- a/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx @@ -19,11 +19,11 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { const validationSchema = Yup.object().shape({ confirm_img: Yup.mixed().notRequired(t("ConfirmDialog.approved_amount_error")) - .test('fileSize', `${t("PassengerBoss.upload_file_unit")}`, (value) => { + .test('fileSize', `${t("PassengerOffice.upload_file_unit")}`, (value) => { if (!value) return true return value.size <= 2 * 1024 * 1024; }) - .test('fileType', `${t("PassengerBoss.upload_file_format")}`, (value) => { + .test('fileType', `${t("PassengerOffice.upload_file_format")}`, (value) => { if (!value) return true const allowedTypes = ['image/jpg', 'image/png', 'application/pdf']; // Define your allowed file types return allowedTypes.includes(value.type); diff --git a/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx index 52137c2..949dd04 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,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("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/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("expert_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")} From 38ee0622a605d185147a0e816395f2a41bc96769 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 7 Nov 2023 12:08:44 +0330 Subject: [PATCH 23/42] LFFE-18 make change text in file upload --- public/locales/fa/app.json | 8 ++++---- src/core/components/UploadSystem.jsx | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 9381558..6aef6ea 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -201,7 +201,7 @@ "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", "confirm": "ارجاع به معاون حمل و نقل", - "upload_file": "صورت جلسه کارگروه را وارد کنید", + "upload_file": "صورت جلسه کارگروه را بارگذاری کنید", "upload_file_required": "وارد کردن صورت جلسه کارگروه الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" @@ -299,7 +299,7 @@ "vehicle_type": "نوع ماشین", "state_name": "وضعیت درخواست", "confirm": "ارجاع به کارگروه استانی", - "upload_file": "گزارش کارشناسی را وارد کنید", + "upload_file": "گزارش کارشناسی را بارگذاری کنید", "upload_file_required": "وارد کردن گزارش کارشناسی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", @@ -390,9 +390,9 @@ "loading_permissions_list": "درحال دریافت لیست دسترسی ها" }, "UploadSystem": { - "upload_file": "فایل خود را بارگذاری کنید", + "upload_file": "بارگذاری فایل", "optional": " (اختیاری)", - "upload_file_unit": "حداکثر 2Mb", + "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", "delete": "پاک کردن", "phone_number": "شماره تلفن", diff --git a/src/core/components/UploadSystem.jsx b/src/core/components/UploadSystem.jsx index 7a25890..ea573d7 100644 --- a/src/core/components/UploadSystem.jsx +++ b/src/core/components/UploadSystem.jsx @@ -67,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")} From 1d1f0a1ffec15bc8200a4e9799cc1b06c58a8575 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 7 Nov 2023 13:46:46 +0330 Subject: [PATCH 24/42] LFFE-18 appJson --- public/locales/fa/app.json | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index a29657b..f3effaf 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -185,7 +185,10 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm": "تایید نهایی و ارجاع به بانک", + "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" }, "PassengerBoss": { "name": "نام", @@ -196,7 +199,12 @@ "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" }, "RefahiProvinceManager": { "name": "نام", @@ -264,7 +272,10 @@ "updated_at": "تاریخ بروزرسانی", "navgan_id": "کد ناوگان", "vehicle_type": "نوع ماشین", - "state_name": "وضعیت درخواست" + "state_name": "وضعیت درخواست", + "confirm": "ارجاع به مدیر کل استانی", + "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" }, "InspectorExpert": { "name": "نام", @@ -286,7 +297,12 @@ "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" }, "UserManagement": { "name": "نام", @@ -324,7 +340,8 @@ "toman": "تومان", "proposed_amount_error": "وارد کردن مقدار پیشنهادی الزامیست", "proposed_amount": "مقدار پیشنهادی", - "proposed_amount_positive": "مقدار پیشنهادی باید مثبت باشد" + "proposed_amount_positive": "مقدار پیشنهادی باید مثبت باشد", + "proposed_amount_number": "مقدار پیشنهادی باید عدد باشد" }, "ReviseDialog": { "revise": " نیاز به ویرایش", From 5fabccf339c2a10dd52f174da94cec7f734b90b4 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 7 Nov 2023 14:04:49 +0330 Subject: [PATCH 25/42] LFFE-18 upload file jpeg --- public/locales/fa/app.json | 4 ++-- .../machinary-office/Form/ConfirmForm/ConfirmContent.jsx | 2 +- .../Form/ConfirmForm/ConfirmContent.jsx | 4 ++-- .../passenger-boss/Form/ConfirmForm/ConfirmContent.jsx | 2 +- .../passenger-office/Form/ConfirmForm/ConfirmContent.jsx | 2 +- .../Form/ConfirmForm/ConfirmContent.jsx | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index f3effaf..70fb49e 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -324,10 +324,10 @@ "updated_at": "تاریخ بروزرسانی" }, "ConfirmDialog": { - "confirm": "تایید", + "confirm": "ارجاع", "context": "آیا از تایید این آیتم اطمینان دارید؟", "button-cancel": "بستن", - "button-confirm": "تایید", + "button-confirm": "ارجاع", "description_error": "وارد کردن توضیحات الزامی است!", "optional": " (اختیاری)", "unit": " (ریال)", diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx index 75dd563..ea8687d 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx @@ -33,7 +33,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { }) .test('fileType', `${t("MachinaryOffice.upload_file_format")}`, (value) => { // if (!value) return true; // Skip if no file is provided - const allowedTypes = ['image/jpg', 'image/png', 'application/pdf']; // Define your allowed file types + const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types return allowedTypes.includes(value.type); }) }); 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 8ca532e..44106b4 100644 --- a/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx @@ -25,7 +25,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { }) .test('fileType', `${t("NavganProvinceManager.upload_file_format")}`, (value) => { if (!value) return true - const allowedTypes = ['image/jpg', 'image/png', 'application/pdf']; // Define your allowed file types + const allowedTypes = ['image/jpeg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types return allowedTypes.includes(value.type); }) }) @@ -71,7 +71,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { setShowAddIcon(false); } }; - + console.log(formik.values.confirm_img) return ( <> diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx index e51d01c..d86a0e6 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx @@ -31,7 +31,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { return value.size <= 2 * 1024 * 1024; }) .test('fileType', `${t("PassengerBoss.upload_file_format")}`, (value) => { - const allowedTypes = ['image/jpg', 'image/png', 'application/pdf']; // Define your allowed file types + const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types return allowedTypes.includes(value.type); }) }); diff --git a/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx index 458f8b5..d86c4f2 100644 --- a/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/passenger-office/Form/ConfirmForm/ConfirmContent.jsx @@ -25,7 +25,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { }) .test('fileType', `${t("PassengerOffice.upload_file_format")}`, (value) => { if (!value) return true - const allowedTypes = ['image/jpg', 'image/png', 'application/pdf']; // Define your allowed file types + const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types return allowedTypes.includes(value.type); }) }) diff --git a/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx index 949dd04..0dcec2a 100644 --- a/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/transportation-assistance/Form/ConfirmForm/ConfirmContent.jsx @@ -25,7 +25,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { }) .test('fileType', `${t("TransportationAssistance.upload_file_format")}`, (value) => { if (!value) return true - const allowedTypes = ['image/jpg', 'image/png', 'application/pdf']; // Define your allowed file types + const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types return allowedTypes.includes(value.type); }) }) From 354632f171454273e59ffc475d7338094dbaf740 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 7 Nov 2023 14:10:27 +0330 Subject: [PATCH 26/42] LFFE-18 debuging --- .../Form/ConfirmForm/ConfirmContent.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 44106b4..daa4df9 100644 --- a/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/navgan-province-manager/Form/ConfirmForm/ConfirmContent.jsx @@ -25,7 +25,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { }) .test('fileType', `${t("NavganProvinceManager.upload_file_format")}`, (value) => { if (!value) return true - const allowedTypes = ['image/jpeg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types + const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types return allowedTypes.includes(value.type); }) }) @@ -71,7 +71,6 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { setShowAddIcon(false); } }; - console.log(formik.values.confirm_img) return ( <> From 72afdef3dd4f769e19270fa6d1c864f13416383b Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 7 Nov 2023 15:21:23 +0330 Subject: [PATCH 27/42] LFFE-19 add maximum amount in validation of machinary and passenger boss --- public/locales/fa/app.json | 6 ++++-- .../machinary-office/Form/ConfirmForm/ConfirmContent.jsx | 3 ++- .../passenger-boss/Form/ConfirmForm/ConfirmContent.jsx | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 87ae5d6..0f56b9a 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -205,7 +205,8 @@ "upload_file": "صورت جلسه کارگروه استانی را بارگذاری کنید", "upload_file_required": "وارد کردن صورت جلسه کارگروه استانی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", - "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", + "max_amount": "حداکثر مبلغ پیشنهادی {value} هزار تومان می باشد" }, "RefahiProvinceManager": { "name": "نام", @@ -303,7 +304,8 @@ "upload_file": "گزارش کارشناسی را بارگذاری کنید", "upload_file_required": "وارد کردن گزارش کارشناسی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", - "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", + "max_amount": "حداکثر مبلغ پیشنهادی {value} هزار تومان می باشد" }, "UserManagement": { "name": "نام", diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx index ea8687d..86e8a5e 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx @@ -23,7 +23,8 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { "is-number", `${t("ConfirmDialog.proposed_amount_number")}`, (value) => !isNaN(value) - ).test("positive", `${t("ConfirmDialog.proposed_amount_positive")}`, (value) => value >= 0) + ).test("max-amount", `${t("MachinaryOffice.max_amount", {value: 7000})}`, (value) => value <= 7000) + .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")) diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx index d86a0e6..4e7332b 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx @@ -23,7 +23,8 @@ 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) + ).test("max-amount", `${t("PassengerBoss.max_amount", {value: 7000})}`, (value) => value <= 7000) + .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")) From ff5e0430da27103cf342b9682638d2deb0511ddd Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 7 Nov 2023 15:50:02 +0330 Subject: [PATCH 28/42] LFFE-19 set maximum limit for max number --- public/locales/fa/app.json | 2 +- .../machinary-office/Form/ConfirmForm/ConfirmContent.jsx | 3 ++- .../passenger-boss/Form/ConfirmForm/ConfirmContent.jsx | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 0f56b9a..2e60776 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -206,7 +206,7 @@ "upload_file_required": "وارد کردن صورت جلسه کارگروه استانی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", - "max_amount": "حداکثر مبلغ پیشنهادی {value} هزار تومان می باشد" + "max_amount": "حداکثر مبلغ پیشنهادی {value} تومان می باشد" }, "RefahiProvinceManager": { "name": "نام", diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx index 86e8a5e..62b704c 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx @@ -10,6 +10,7 @@ import PriceField from "@/core/components/PriceField"; import * as Yup from "yup"; const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { + const max_amount_number = 70000000000 const t = useTranslations(); const [selectedImage, setSelectedImage] = useState(""); const [fileType, setfileType] = useState(null); @@ -23,7 +24,7 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { "is-number", `${t("ConfirmDialog.proposed_amount_number")}`, (value) => !isNaN(value) - ).test("max-amount", `${t("MachinaryOffice.max_amount", {value: 7000})}`, (value) => value <= 7000) + ).test("max-amount", `${t("MachinaryOffice.max_amount", {value: parseInt(max_amount_number).toLocaleString('en')})}`, (value) => value <= max_amount_number) .test("positive", `${t("ConfirmDialog.proposed_amount_positive")}`, (value) => value >= 0) .required(t("ConfirmDialog.proposed_amount_error")), confirm_img: Yup.mixed() diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx index 4e7332b..9269ebc 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx @@ -10,6 +10,7 @@ import PriceField from "@/core/components/PriceField"; import * as Yup from "yup"; const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { + const max_amount_number = 70000000000 const t = useTranslations(); const [selectedImage, setSelectedImage] = useState(""); const [fileType, setfileType] = useState(null); @@ -18,12 +19,13 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { const requestServer = useRequest({auth: true}) const {update_notification} = useNotification() + const validationSchema = Yup.object().shape({ approved_amount: Yup.mixed().test( "is-number", `${t("ConfirmDialog.approved_amount_number")}`, (value) => !isNaN(value) - ).test("max-amount", `${t("PassengerBoss.max_amount", {value: 7000})}`, (value) => value <= 7000) + ).test("max-amount", `${t("PassengerBoss.max_amount", {value: parseInt(max_amount_number).toLocaleString('en')})}`, (value) => value <= max_amount_number) .test("positive", `${t("ConfirmDialog.approved_amount_positive")}`, (value) => value >= 0) .required(t("ConfirmDialog.approved_amount_error")), confirm_img: Yup.mixed() From 30ce8485261b1a3f6cadc4174faa65d9cc4feb82 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 7 Nov 2023 15:55:05 +0330 Subject: [PATCH 29/42] Fixed bug toast network --- src/core/components/NetworkComponent.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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]); From 4faa579c9116b85805519a895aa4942733e31ff3 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 7 Nov 2023 15:58:55 +0330 Subject: [PATCH 30/42] Fixed bug toast container --- src/layouts/AppLayout.jsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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} ); From 720dc78a10b74dbdbd3a36dbea2924bf1ac93c9e Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 11 Nov 2023 12:04:43 +0330 Subject: [PATCH 31/42] LFFE-23 Change direction style in price component to LTR --- src/core/components/LtrTextField.jsx | 11 +++++++++++ src/core/components/PriceField.jsx | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/core/components/LtrTextField.jsx 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/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 ( - From 976e144a4ad4f49d25775d7480d31b83984e6fbd Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 13 Nov 2023 10:53:50 +0330 Subject: [PATCH 32/42] LFFE-25 remove useless files --- public/images/logo.svg | 19 ------------------- .../machinary-office/TableTolbar.jsx | 13 ------------- .../navgan-province-manager/TableTollbar.jsx | 13 ------------- .../dashboard/passenger-boss/TableTollbar.jsx | 13 ------------- .../passenger-office/TableTollbar.jsx | 13 ------------- .../TableTollbar.jsx | 13 ------------- 6 files changed, 84 deletions(-) delete mode 100644 public/images/logo.svg delete mode 100644 src/components/dashboard/machinary-office/TableTolbar.jsx delete mode 100644 src/components/dashboard/navgan-province-manager/TableTollbar.jsx delete mode 100644 src/components/dashboard/passenger-boss/TableTollbar.jsx delete mode 100644 src/components/dashboard/passenger-office/TableTollbar.jsx delete mode 100644 src/components/dashboard/transportation-assistance/TableTollbar.jsx 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 @@ - - - - - - - - - MARHABA - - - diff --git a/src/components/dashboard/machinary-office/TableTolbar.jsx b/src/components/dashboard/machinary-office/TableTolbar.jsx deleted file mode 100644 index e267691..0000000 --- a/src/components/dashboard/machinary-office/TableTolbar.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/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/TableTollbar.jsx b/src/components/dashboard/passenger-boss/TableTollbar.jsx deleted file mode 100644 index e267691..0000000 --- a/src/components/dashboard/passenger-boss/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-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/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; From 7a636aed0773e80a2530d3b94496b15d35851148 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 13 Nov 2023 13:44:26 +0330 Subject: [PATCH 33/42] LFFE-25 remove useless files --- src/components/dashboard/refahi-province-manager/TableTollbar.jsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/components/dashboard/refahi-province-manager/TableTollbar.jsx 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 From 2a7707152e3d461b74c23bc5abda79985185b104 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 13 Nov 2023 13:46:20 +0330 Subject: [PATCH 34/42] LFFE-25 Implementation of print layout and Printable Page --- public/fontiran.scss | 7 ++ public/fonts/ttf/BNazanin.ttf | Bin 0 -> 60852 bytes public/images/logo.png | Bin 0 -> 13143 bytes public/locales/fa/app.json | 7 +- public/print.scss | 9 ++ .../layouts/Print/Header/ProfileData.jsx | 18 ++++ .../layouts/Print/Header/ProfileMenu.jsx | 62 +++++++++++++ .../Print/Header/ProfileOptionLogOut.jsx | 49 +++++++++++ .../layouts/Print/Header/ProfileOptions.jsx | 49 +++++++++++ src/core/components/PrintablePage.jsx | 82 ++++++++++++++++++ src/layouts/PrintLayout.jsx | 12 +++ src/layouts/index.jsx | 4 +- 12 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 public/fonts/ttf/BNazanin.ttf create mode 100644 public/images/logo.png create mode 100644 public/print.scss create mode 100644 src/components/layouts/Print/Header/ProfileData.jsx create mode 100644 src/components/layouts/Print/Header/ProfileMenu.jsx create mode 100644 src/components/layouts/Print/Header/ProfileOptionLogOut.jsx create mode 100644 src/components/layouts/Print/Header/ProfileOptions.jsx create mode 100644 src/core/components/PrintablePage.jsx create mode 100644 src/layouts/PrintLayout.jsx 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 0000000000000000000000000000000000000000..4e59251727ccee3c2c680fcbda2a3185d45d7e2b GIT binary patch literal 60852 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 z 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 + + + {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 ( + <> + + + + + + + {t("header.logout")} + + + + + ); +} 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) => ( + + + + {profile_item.icon} + + + {t(profile_item.name)} + + + + ))} + + + ); +} 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/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 4d4ed7f..a69d886 100644 --- a/src/layouts/index.jsx +++ b/src/layouts/index.jsx @@ -1,8 +1,10 @@ import DashboardLayout from "@/layouts/DashboardLayout"; import {Fragment} from "react"; +import PrintLayout from "@/layouts/PrintLayout"; const layoutList = { - DashboardLayout + DashboardLayout, + PrintLayout } const Layout = ({layout = {}, children}) => { From 8355a781bbe5951ec2a20a2c5f2e8e39dcb4c1ac Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 13 Nov 2023 13:47:00 +0330 Subject: [PATCH 35/42] LFFE-25 Implementation of print layout and Printable Page --- .../layouts/Dashboard/Header/index.jsx | 3 +- src/components/layouts/Print/Header/index.jsx | 47 +++++++++++++++++++ src/components/layouts/Print/index.jsx | 16 +++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 src/components/layouts/Print/Header/index.jsx create mode 100644 src/components/layouts/Print/index.jsx 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 ( <> - + + + + + + + + + + + + ); +} + +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..6c66fd9 --- /dev/null +++ b/src/components/layouts/Print/index.jsx @@ -0,0 +1,16 @@ +import Header from "@/components/layouts/Print/Header"; +import {Paper, Toolbar} from "@mui/material"; + +const Print = (props) => { + return ( + <> +
+ + + {props.children} + + + ) +} + +export default Print \ No newline at end of file From e4f1509de4d071e2fbdf0aa8601bae20f128975e Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 13 Nov 2023 14:05:01 +0330 Subject: [PATCH 36/42] LFFE-25 remove useless files --- src/components/dashboard/commercial-chief/TableTollbar.jsx | 0 src/components/dashboard/development-assistant/TableTollbar.jsx | 0 src/components/dashboard/inspector-expert/TableTollbar.jsx | 0 src/components/dashboard/province-head-expert/TableTollbar.jsx | 0 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/components/dashboard/commercial-chief/TableTollbar.jsx delete mode 100644 src/components/dashboard/development-assistant/TableTollbar.jsx delete mode 100644 src/components/dashboard/inspector-expert/TableTollbar.jsx delete mode 100644 src/components/dashboard/province-head-expert/TableTollbar.jsx 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/province-head-expert/TableTollbar.jsx b/src/components/dashboard/province-head-expert/TableTollbar.jsx deleted file mode 100644 index e69de29..0000000 From 380eaf37beb2d4628fc61d65dc20dfbd6a397b12 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 13 Nov 2023 14:06:39 +0330 Subject: [PATCH 37/42] LFFE-25 implemenation of appraisal-confirmation and final-credit-amount print page --- .../Buttons/printAppraisalConfirmation.jsx | 32 +++++ .../Buttons/printFinalCreditAmount.jsx | 32 +++++ .../Prints/appraisal-confirmation/Content.jsx | 135 +++++++++++++++++ .../Prints/appraisal-confirmation/index.jsx | 34 +++++ .../Prints/final-credit-amount/Content.jsx | 136 ++++++++++++++++++ .../Prints/final-credit-amount/index.jsx | 40 ++++++ .../dashboard/passenger-boss/TableToolbar.jsx | 14 ++ .../dashboard/passenger-boss/index.jsx | 4 +- .../prints/appraisal-confirmation.jsx | 21 +++ .../prints/final-credit-amount.jsx | 21 +++ 10 files changed, 468 insertions(+), 1 deletion(-) create mode 100644 src/components/dashboard/passenger-boss/Buttons/printAppraisalConfirmation.jsx create mode 100644 src/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount.jsx create mode 100644 src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/Content.jsx create mode 100644 src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/index.jsx create mode 100644 src/components/dashboard/passenger-boss/Prints/final-credit-amount/Content.jsx create mode 100644 src/components/dashboard/passenger-boss/Prints/final-credit-amount/index.jsx create mode 100644 src/components/dashboard/passenger-boss/TableToolbar.jsx create mode 100644 src/pages/dashboard/passenger-boss/prints/appraisal-confirmation.jsx create mode 100644 src/pages/dashboard/passenger-boss/prints/final-credit-amount.jsx diff --git a/src/components/dashboard/passenger-boss/Buttons/printAppraisalConfirmation.jsx b/src/components/dashboard/passenger-boss/Buttons/printAppraisalConfirmation.jsx new file mode 100644 index 0000000..76da30b --- /dev/null +++ b/src/components/dashboard/passenger-boss/Buttons/printAppraisalConfirmation.jsx @@ -0,0 +1,32 @@ +import PrintIcon from "@mui/icons-material/Print"; +import {Button, CircularProgress} from "@mui/material"; +import {useTranslations} from "next-intl"; +import {useState} from "react"; +import {useRouter} from "next/router"; + +const PrintAppraisalConfirmation = () => { + const t = useTranslations(); + const router = useRouter() + const [loading, setLoading] = useState(false) + const clickHandler = () => { + setLoading(true) + sessionStorage.setItem('appraisal-confirmation-print', JSON.stringify([])) + router.push('/dashboard/passenger-boss/prints/appraisal-confirmation') + } + + return ( + + ) +} + +export default PrintAppraisalConfirmation \ No newline at end of file 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..3c43cf3 --- /dev/null +++ b/src/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount.jsx @@ -0,0 +1,32 @@ +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"; + +const PrintFinalCreditAmount = () => { + const t = useTranslations(); + const router = useRouter() + const [loading, setLoading] = useState(false) + const clickHandler = () => { + setLoading(true) + sessionStorage.setItem('final-credit-amount-print', JSON.stringify([])) + router.push('/dashboard/passenger-boss/prints/final-credit-amount') + } + + return ( + + ) +} + +export default PrintFinalCreditAmount \ No newline at end of file diff --git a/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/Content.jsx b/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/Content.jsx new file mode 100644 index 0000000..3374d2a --- /dev/null +++ b/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/Content.jsx @@ -0,0 +1,135 @@ +import PrintablePage from "@/core/components/PrintablePage"; +import {Box, Stack, Typography} from "@mui/material"; + +const Content = ({data}) => { + return ( + <> + + + {'اداره کل راهداری و حمل و نقل جاده ای'} + {`استان ${data.province_name}`} + + {'موضوع: تاییدیه ارزیابی و تخصیص اعتبار'} + {'با سلام و احترام'} + + {'با توجه به نامه شماره'} + 35482 + {'مورخ'} + 1402/08/20 + {'معاون محترم برنامه ریزی سازمان راهداری و حمل و نقل جاده ای در خصوص تسهیلات بند (الف) تبصره (18) قانون بودجه سال'} + 1402 + {'موضوع بازسازی ناوگان اتوبوس و مینی بوس و نیز تصویب آن درشورای برنامه ریزی و توسعه استان'} + تهران + {'و نامه شماره'} + 35482 + {'مورخ'} + 1402/08/20 + {'اداره کل راهداری و حمل و نقل جاده ای'} + تهران + {'در خصوص استفاده از ظرفیت انجمن های صنفی رانندگان در تاریخ'} + 1402/08/20 + {'از ناوگان اتوبوس/مینی بوس به شماره پلاک انتظامی'} + 25پ4582 + {'و شماره هوشمند ناوگان'} + 5875698725 + {'به مالکیت آقای'} + امیرحسین محمودی + {'با کد ملی'} + 0311318897 + {'و شماره تماس'} + 09120630655 + {'ساکن شهرستان'} + تهران + {'بازدید بعمل آمد که گزارش بازدید به شرخ ذیل مورد تایید می باشد.'} + + + {'* با توجه به یک فقره پیش فاکتور پیوست ارائه شده از تعمیرگاه مجاز'} + + {'اصالت فاکتورهای شماره'} + 7563842 + {'و صحت اقلام لازم و قیمت آنها جهت نیازمندی بازسازی ناوگان مذکور به مبلغ'} + {parseInt(2300000000).toLocaleString('en')} + {'ریال معادل'} + {parseInt(230000000).toLocaleString('en')} + {'تومان بابت هزینه کرد تعمیرات ناوگان فوق الذکر مورد تأیید می باشد.'} + + + + رئیس انجمن آقای ....... + انجمن صنفی رانندگان حمل و نقل مسافر + ...... + + + {'تعهدنامه'} + + {'اینجانب'} + امیرحسین محمودی + {'مالک ناوگان اتوبوس | مینی بوس با مشخصات فوق الذکر بدینوسیله تعهد می نمایم مبلغ دریافتی وام را برابر پیش فاکتور ارائه شده صرف تعمیرات و بازسازی ناوگان نمایم و اگر خلاف این ثابت شود برابر مقررات با بنده برخورد شود و حق هیچگونه اعتراضی ندارم.'} + تاریخ و امضاء + + + + + ) +} + +export default Content \ No newline at end of file diff --git a/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/index.jsx b/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/index.jsx new file mode 100644 index 0000000..901e84a --- /dev/null +++ b/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/index.jsx @@ -0,0 +1,34 @@ +import {useEffect, useState} from "react"; +import CenterLayout from "@/layouts/CenterLayout"; +import {useTranslations} from "next-intl"; +import {Typography} from "@mui/material"; +import Content from "@/components/dashboard/passenger-boss/Prints/appraisal-confirmation/Content"; + +const AppraisalConfirmationComponent = () => { + const t = useTranslations() + const [data, setData] = useState(null) + useEffect(() => { + if (sessionStorage.getItem('appraisal-confirmation-print') === null) { + sessionStorage.setItem('appraisal-confirmation-print', 'seen') + } else if (sessionStorage.getItem('appraisal-confirmation-print') === 'seen') { + setData(null) + } else if (data) { + sessionStorage.setItem('appraisal-confirmation-print', 'seen') + } else { + setData(JSON.parse(sessionStorage.getItem('appraisal-confirmation-print'))) + } + }, [data]); + return ( + <> + {!data ? ( + + + {t('data_not_found')} + + + ) : data.map(item => )} + + ) +} + +export default AppraisalConfirmationComponent \ No newline at end of file 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..3c61db7 --- /dev/null +++ b/src/components/dashboard/passenger-boss/Prints/final-credit-amount/Content.jsx @@ -0,0 +1,136 @@ +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..927f8b9 --- /dev/null +++ b/src/components/dashboard/passenger-boss/Prints/final-credit-amount/index.jsx @@ -0,0 +1,40 @@ +import {useEffect, useState} from "react"; +import CenterLayout from "@/layouts/CenterLayout"; +import {useTranslations} from "next-intl"; +import {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) + useEffect(() => { + if (sessionStorage.getItem('final-credit-amount-print') === null) { + sessionStorage.setItem('final-credit-amount-print', 'seen') + } else if (sessionStorage.getItem('final-credit-amount-print') === 'seen') { + setData(null) + } else if (data) { + sessionStorage.setItem('final-credit-amount-print', 'seen') + } else { + setData(JSON.parse(sessionStorage.getItem('final-credit-amount-print'))) + } + }, [data]); + return ( + <> + {!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/TableToolbar.jsx b/src/components/dashboard/passenger-boss/TableToolbar.jsx new file mode 100644 index 0000000..09f2f57 --- /dev/null +++ b/src/components/dashboard/passenger-boss/TableToolbar.jsx @@ -0,0 +1,14 @@ +import {Stack} from "@mui/material"; +import PrintAppraisalConfirmation from "@/components/dashboard/passenger-boss/Buttons/printAppraisalConfirmation"; +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/pages/dashboard/passenger-boss/prints/appraisal-confirmation.jsx b/src/pages/dashboard/passenger-boss/prints/appraisal-confirmation.jsx new file mode 100644 index 0000000..c2a021a --- /dev/null +++ b/src/pages/dashboard/passenger-boss/prints/appraisal-confirmation.jsx @@ -0,0 +1,21 @@ +import {parse} from "next-useragent"; +import AppraisalConfirmationComponent from "@/components/dashboard/passenger-boss/Prints/appraisal-confirmation"; + +export default function AppraisalConfirmation() { + 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-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 From 87dd2844cc0517f30c732130e84825cd58c2ea2c Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 13 Nov 2023 14:15:00 +0330 Subject: [PATCH 38/42] LFFE-24 Change unit price from (Toman, Rial) to (Milion Toman, Milion Rial) --- public/locales/fa/app.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 87ae5d6..0ec9394 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -331,14 +331,14 @@ "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": "مقدار پیشنهادی باید مثبت باشد", From ff6a9607596114461a86e14804e4f98954f94f8b Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 13 Nov 2023 15:55:17 +0330 Subject: [PATCH 39/42] change style list sidebar --- src/components/layouts/Dashboard/Sidebar/SidebarList.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx index 3a2eb44..1c76f07 100644 --- a/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx +++ b/src/components/layouts/Dashboard/Sidebar/SidebarList.jsx @@ -87,7 +87,7 @@ export default function SidebarList({handleDrawerToggle}) { }, [selectedKey]); return ( - + {itemMenu.map((itemArr, index) => ( {itemArr.map((item) => From fb7bb856b5f7d59ea10e5800e0b6b25f3278b0d2 Mon Sep 17 00:00:00 2001 From: AminGhasempoor Date: Tue, 14 Nov 2023 11:10:27 +0330 Subject: [PATCH 40/42] LFFE-19 get vehicle type and set maximum number base on it --- public/locales/fa/app.json | 4 ++-- .../Form/ConfirmForm/ConfirmContent.jsx | 22 +++++++++++-------- .../Form/ConfirmForm/index.jsx | 6 +++-- .../machinary-office/TableRowActions.jsx | 2 +- .../Form/ConfirmForm/ConfirmContent.jsx | 15 +++++++++---- .../passenger-boss/Form/ConfirmForm/index.jsx | 6 +++-- .../passenger-boss/TableRowActions.jsx | 1 + 7 files changed, 36 insertions(+), 20 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 27d4b0e..0481f1a 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -206,7 +206,7 @@ "upload_file_required": "وارد کردن صورت جلسه کارگروه استانی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", - "max_amount": "حداکثر مبلغ پیشنهادی {value} تومان می باشد" + "max_amount": "حداکثر مبلغ تصویب شده {value} میلیون تومان می باشد" }, "RefahiProvinceManager": { "name": "نام", @@ -305,7 +305,7 @@ "upload_file_required": "وارد کردن گزارش کارشناسی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", - "max_amount": "حداکثر مبلغ پیشنهادی {value} هزار تومان می باشد" + "max_amount": "حداکثر مبلغ پیشنهادی {value} میلیون تومان می باشد" }, "UserManagement": { "name": "نام", diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx index 62b704c..93ee5a2 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx @@ -9,8 +9,7 @@ import {useState} from "react"; import PriceField from "@/core/components/PriceField"; import * as Yup from "yup"; -const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { - const max_amount_number = 70000000000 +const ConfirmContent = ({mutate, setOpenConfirmDialog, rowId, vehicle_type}) => { const t = useTranslations(); const [selectedImage, setSelectedImage] = useState(""); const [fileType, setfileType] = useState(null); @@ -18,24 +17,30 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { const [showAddIcon, setShowAddIcon] = useState(true); const requestServer = useRequest({auth: true}) const {update_notification} = useNotification() - + let max_amount_number + if (vehicle_type === "اتوبوس") { + max_amount_number = 7000 + } else if (vehicle_type === "مینی بوس") { + max_amount_number = 2000 + } else { + max_amount_number = 5000 + } const validationSchema = Yup.object().shape({ proposed_amount: Yup.mixed().test( "is-number", `${t("ConfirmDialog.proposed_amount_number")}`, (value) => !isNaN(value) - ).test("max-amount", `${t("MachinaryOffice.max_amount", {value: parseInt(max_amount_number).toLocaleString('en')})}`, (value) => value <= max_amount_number) + ).test("max-amount", `${t("MachinaryOffice.max_amount", {value: parseInt(max_amount_number / 10).toLocaleString('en')})}`, + (value) => value <= max_amount_number) .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) => { - // if (!value) return true; // Skip if no file is provided - return value.size <= 2 * 1024 * 1024; // 100KB limit (adjust as needed) + return value.size <= 2 * 1024 * 1024; }) .test('fileType', `${t("MachinaryOffice.upload_file_format")}`, (value) => { - // if (!value) return true; // Skip if no file is provided - const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; // Define your allowed file types + const allowedTypes = ['image/jpg', 'image/jpeg', 'image/png', 'application/pdf']; return allowedTypes.includes(value.type); }) }); @@ -51,7 +56,6 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { formData.append("proposed_amount", values.proposed_amount); 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) => { diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx index 5ed809f..3affc7b 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/index.jsx @@ -3,7 +3,8 @@ import {useTranslations} from "next-intl"; 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 ( @@ -21,7 +22,8 @@ const Confirm = ({rowId, mutate}) => { {t("MachinaryOffice.confirm")} - + ) 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 ( { - const max_amount_number = 70000000000 +const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog, vehicle_type}) => { const t = useTranslations(); const [selectedImage, setSelectedImage] = useState(""); const [fileType, setfileType] = useState(null); @@ -18,14 +17,22 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog}) => { const [showAddIcon, setShowAddIcon] = useState(true); const requestServer = useRequest({auth: true}) const {update_notification} = useNotification() - + let max_amount_number + if (vehicle_type === "اتوبوس") { + max_amount_number = 7000 + } else if (vehicle_type === "مینی بوس") { + max_amount_number = 2000 + } else { + max_amount_number = 5000 + } const validationSchema = Yup.object().shape({ approved_amount: Yup.mixed().test( "is-number", `${t("ConfirmDialog.approved_amount_number")}`, (value) => !isNaN(value) - ).test("max-amount", `${t("PassengerBoss.max_amount", {value: parseInt(max_amount_number).toLocaleString('en')})}`, (value) => value <= max_amount_number) + ).test("max-amount", `${t("PassengerBoss.max_amount", {value: parseInt(max_amount_number / 10).toLocaleString('en')})}`, + (value) => value <= max_amount_number) .test("positive", `${t("ConfirmDialog.approved_amount_positive")}`, (value) => value >= 0) .required(t("ConfirmDialog.approved_amount_error")), confirm_img: Yup.mixed() diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx index 30f15dd..004d119 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx +++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm/index.jsx @@ -3,7 +3,8 @@ import {useTranslations} from "next-intl"; 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 ( @@ -21,7 +22,8 @@ const Confirm = ({rowId, mutate}) => { {t("PassengerBoss.confirm")} - + ) 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}) => { Date: Tue, 14 Nov 2023 11:29:59 +0330 Subject: [PATCH 41/42] LFFE-19 maximum Rials --- public/locales/fa/app.json | 4 ++-- .../Form/ConfirmForm/ConfirmContent.jsx | 16 ++++++---------- .../Form/ConfirmForm/ConfirmContent.jsx | 16 ++++++---------- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index 0481f1a..c905835 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -206,7 +206,7 @@ "upload_file_required": "وارد کردن صورت جلسه کارگروه استانی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", - "max_amount": "حداکثر مبلغ تصویب شده {value} میلیون تومان می باشد" + "max_amount": "حداکثر مبلغ تصویب شده {value} میلیون ریال می باشد" }, "RefahiProvinceManager": { "name": "نام", @@ -305,7 +305,7 @@ "upload_file_required": "وارد کردن گزارش کارشناسی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", - "max_amount": "حداکثر مبلغ پیشنهادی {value} میلیون تومان می باشد" + "max_amount": "حداکثر مبلغ پیشنهادی {value} میلیون ریال می باشد" }, "UserManagement": { "name": "نام", diff --git a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx index 93ee5a2..16b7ca3 100644 --- a/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/machinary-office/Form/ConfirmForm/ConfirmContent.jsx @@ -9,6 +9,10 @@ import {useState} from "react"; import PriceField from "@/core/components/PriceField"; import * as Yup from "yup"; +const vehicle_amount = { + "اتوبوس": 7000, + "مینی بوس": 2000, +} const ConfirmContent = ({mutate, setOpenConfirmDialog, rowId, vehicle_type}) => { const t = useTranslations(); const [selectedImage, setSelectedImage] = useState(""); @@ -17,21 +21,13 @@ const ConfirmContent = ({mutate, setOpenConfirmDialog, rowId, vehicle_type}) => const [showAddIcon, setShowAddIcon] = useState(true); const requestServer = useRequest({auth: true}) const {update_notification} = useNotification() - let max_amount_number - if (vehicle_type === "اتوبوس") { - max_amount_number = 7000 - } else if (vehicle_type === "مینی بوس") { - max_amount_number = 2000 - } else { - max_amount_number = 5000 - } const validationSchema = Yup.object().shape({ proposed_amount: Yup.mixed().test( "is-number", `${t("ConfirmDialog.proposed_amount_number")}`, (value) => !isNaN(value) - ).test("max-amount", `${t("MachinaryOffice.max_amount", {value: parseInt(max_amount_number / 10).toLocaleString('en')})}`, - (value) => value <= max_amount_number) + ).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() diff --git a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx index ade15d7..f0a8c12 100644 --- a/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/passenger-boss/Form/ConfirmForm/ConfirmContent.jsx @@ -9,6 +9,10 @@ import {useState} from "react"; import PriceField from "@/core/components/PriceField"; import * as Yup from "yup"; +const vehicle_amount = { + "اتوبوس": 7000, + "مینی بوس": 2000, +} const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog, vehicle_type}) => { const t = useTranslations(); const [selectedImage, setSelectedImage] = useState(""); @@ -17,22 +21,14 @@ const ConfirmContent = ({rowId, mutate, setOpenConfirmDialog, vehicle_type}) => const [showAddIcon, setShowAddIcon] = useState(true); const requestServer = useRequest({auth: true}) const {update_notification} = useNotification() - let max_amount_number - if (vehicle_type === "اتوبوس") { - max_amount_number = 7000 - } else if (vehicle_type === "مینی بوس") { - max_amount_number = 2000 - } else { - max_amount_number = 5000 - } const validationSchema = Yup.object().shape({ approved_amount: Yup.mixed().test( "is-number", `${t("ConfirmDialog.approved_amount_number")}`, (value) => !isNaN(value) - ).test("max-amount", `${t("PassengerBoss.max_amount", {value: parseInt(max_amount_number / 10).toLocaleString('en')})}`, - (value) => value <= max_amount_number) + ).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() From e09e174558f1dcfb33896c121ae1b80af00c16bf Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 14 Nov 2023 15:55:59 +0330 Subject: [PATCH 42/42] LFFE-25 print passenger boss and machinery expert --- public/locales/fa/app.json | 5 +- .../machinary-office/Buttons/printReport.jsx | 48 +++++++++++ .../Prints/report}/Content.jsx | 81 +++++-------------- .../machinary-office/Prints/report/index.jsx | 60 ++++++++++++++ .../machinary-office/TableToolbar.jsx | 12 +++ .../dashboard/machinary-office/index.jsx | 4 +- .../Buttons/printAppraisalConfirmation.jsx | 32 -------- .../Buttons/printFinalCreditAmount.jsx | 20 ++++- .../Prints/appraisal-confirmation/index.jsx | 34 -------- .../Prints/final-credit-amount/Content.jsx | 21 +---- .../Prints/final-credit-amount/index.jsx | 28 ++++++- .../dashboard/passenger-boss/TableToolbar.jsx | 2 - src/components/layouts/Print/Header/index.jsx | 31 ++++++- src/components/layouts/Print/index.jsx | 6 +- src/lib/app/contexts/print.jsx | 24 ++++++ src/lib/app/hooks/usePrint.jsx | 18 +++++ src/pages/_app.jsx | 9 ++- .../prints/report.jsx} | 8 +- 18 files changed, 275 insertions(+), 168 deletions(-) create mode 100644 src/components/dashboard/machinary-office/Buttons/printReport.jsx rename src/components/dashboard/{passenger-boss/Prints/appraisal-confirmation => machinary-office/Prints/report}/Content.jsx (52%) create mode 100644 src/components/dashboard/machinary-office/Prints/report/index.jsx create mode 100644 src/components/dashboard/machinary-office/TableToolbar.jsx delete mode 100644 src/components/dashboard/passenger-boss/Buttons/printAppraisalConfirmation.jsx delete mode 100644 src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/index.jsx create mode 100644 src/lib/app/contexts/print.jsx create mode 100644 src/lib/app/hooks/usePrint.jsx rename src/pages/dashboard/{passenger-boss/prints/appraisal-confirmation.jsx => machinery-expert/prints/report.jsx} (59%) diff --git a/public/locales/fa/app.json b/public/locales/fa/app.json index d070036..f02bb6a 100644 --- a/public/locales/fa/app.json +++ b/public/locales/fa/app.json @@ -19,6 +19,7 @@ "routing_to": "در حال انتقال به صفحه", "data_not_found": "اطلاعاتی یافت نشد!", "btn_print": "چاپ", + "print_loading": "درحال پردازش اطلاعات ...", "button_back_to_previous": "بازگشت به صفحه قبل", "header": { "open_profile": "پروفایل", @@ -209,7 +210,6 @@ "upload_file_required": "وارد کردن صورت جلسه کارگروه استانی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", - "print_appraisal_confirmation": "چاپ تاییدیه ارزیابی", "print_final_credit_amount": "چاپ صورت جلسه تعیین مبلغ نهایی" }, "RefahiProvinceManager": { @@ -308,7 +308,8 @@ "upload_file": "گزارش کارشناسی را بارگذاری کنید", "upload_file_required": "وارد کردن گزارش کارشناسی الزامیست", "upload_file_unit": "فایل بارگذاری شده باید حداکثر 2Mb باشد", - "upload_file_format": "فرمت قابل قبول : png,jpg,pdf" + "upload_file_format": "فرمت قابل قبول : png,jpg,pdf", + "print_report": "چاپ گزارش کارشناسی ماشین آلات" }, "UserManagement": { "name": "نام", 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 ( + + ) +} + +export default PrintReport \ No newline at end of file diff --git a/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/Content.jsx b/src/components/dashboard/machinary-office/Prints/report/Content.jsx similarity index 52% rename from src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/Content.jsx rename to src/components/dashboard/machinary-office/Prints/report/Content.jsx index 3374d2a..8098ecb 100644 --- a/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/Content.jsx +++ b/src/components/dashboard/machinary-office/Prints/report/Content.jsx @@ -5,27 +5,16 @@ const Content = ({data}) => { return ( <> - - {'اداره کل راهداری و حمل و نقل جاده ای'} - {`استان ${data.province_name}`} - - {'موضوع: تاییدیه ارزیابی و تخصیص اعتبار'} - {'با سلام و احترام'} - + + {'گزارش کارشناسی ماشین آلات'} + + {'با توجه به نامه شماره'} { sx={{fontFamily: 'Bnazanin', fontWeight: 'bold'}}>تهران {'بازدید بعمل آمد که گزارش بازدید به شرخ ذیل مورد تایید می باشد.'} - + component={'span'}> {'در جلسه مطرح شد که نتیجه آن به شرح ذیل مورد تایید می باشد.'} + {'* با توجه به یک فقره پیش فاکتور پیوست ارائه شده از تعمیرگاه مجاز'} - - {'اصالت فاکتورهای شماره'} - 7563842 - {'و صحت اقلام لازم و قیمت آنها جهت نیازمندی بازسازی ناوگان مذکور به مبلغ'} - {'مبلغ پیشنهادی به عدد .............................................. ریال و به حروف ...................................................................... ریال می باشد.'} + {parseInt(2300000000).toLocaleString('en')} - {'ریال معادل'} - {parseInt(230000000).toLocaleString('en')} + }}> {'توضیحات : ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................'} + {'تومان بابت هزینه کرد تعمیرات ناوگان فوق الذکر مورد تأیید می باشد.'} - - - - رئیس انجمن آقای ....... - انجمن صنفی رانندگان حمل و نقل مسافر - ...... - - - {'تعهدنامه'} - - {'اینجانب'} - امیرحسین محمودی + sx={{fontFamily: 'Bnazanin'}}> {'نام و نام خانوادگی کارشناس:'} {'مالک ناوگان اتوبوس | مینی بوس با مشخصات فوق الذکر بدینوسیله تعهد می نمایم مبلغ دریافتی وام را برابر پیش فاکتور ارائه شده صرف تعمیرات و بازسازی ناوگان نمایم و اگر خلاف این ثابت شود برابر مقررات با بنده برخورد شود و حق هیچگونه اعتراضی ندارم.'} - تاریخ و امضاء - + sx={{fontFamily: 'Bnazanin'}}> {'تاریخ و امضاء'} + 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/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/passenger-boss/Buttons/printAppraisalConfirmation.jsx b/src/components/dashboard/passenger-boss/Buttons/printAppraisalConfirmation.jsx deleted file mode 100644 index 76da30b..0000000 --- a/src/components/dashboard/passenger-boss/Buttons/printAppraisalConfirmation.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import PrintIcon from "@mui/icons-material/Print"; -import {Button, CircularProgress} from "@mui/material"; -import {useTranslations} from "next-intl"; -import {useState} from "react"; -import {useRouter} from "next/router"; - -const PrintAppraisalConfirmation = () => { - const t = useTranslations(); - const router = useRouter() - const [loading, setLoading] = useState(false) - const clickHandler = () => { - setLoading(true) - sessionStorage.setItem('appraisal-confirmation-print', JSON.stringify([])) - router.push('/dashboard/passenger-boss/prints/appraisal-confirmation') - } - - return ( - - ) -} - -export default PrintAppraisalConfirmation \ No newline at end of file diff --git a/src/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount.jsx b/src/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount.jsx index 3c43cf3..58a97af 100644 --- a/src/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount.jsx +++ b/src/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount.jsx @@ -3,15 +3,31 @@ 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) - sessionStorage.setItem('final-credit-amount-print', JSON.stringify([])) - router.push('/dashboard/passenger-boss/prints/final-credit-amount') + 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 ( diff --git a/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/index.jsx b/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/index.jsx deleted file mode 100644 index 901e84a..0000000 --- a/src/components/dashboard/passenger-boss/Prints/appraisal-confirmation/index.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import {useEffect, useState} from "react"; -import CenterLayout from "@/layouts/CenterLayout"; -import {useTranslations} from "next-intl"; -import {Typography} from "@mui/material"; -import Content from "@/components/dashboard/passenger-boss/Prints/appraisal-confirmation/Content"; - -const AppraisalConfirmationComponent = () => { - const t = useTranslations() - const [data, setData] = useState(null) - useEffect(() => { - if (sessionStorage.getItem('appraisal-confirmation-print') === null) { - sessionStorage.setItem('appraisal-confirmation-print', 'seen') - } else if (sessionStorage.getItem('appraisal-confirmation-print') === 'seen') { - setData(null) - } else if (data) { - sessionStorage.setItem('appraisal-confirmation-print', 'seen') - } else { - setData(JSON.parse(sessionStorage.getItem('appraisal-confirmation-print'))) - } - }, [data]); - return ( - <> - {!data ? ( - - - {t('data_not_found')} - - - ) : data.map(item => )} - - ) -} - -export default AppraisalConfirmationComponent \ No newline at end of file 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 index 3c61db7..e9844ee 100644 --- a/src/components/dashboard/passenger-boss/Prints/final-credit-amount/Content.jsx +++ b/src/components/dashboard/passenger-boss/Prints/final-credit-amount/Content.jsx @@ -5,7 +5,7 @@ const Content = ({data}) => { return ( <> - + { fontSize: '1.2rem', textAlign: 'center' }}> {'فرم صورتجلسه تعیین مبلغ نهایی اعتبار'} - - + + {'با توجه به نامه شماره'} { {'در جلسه مطرح شد که نتیجه آن به شرح ذیل مورد تایید می باشد.'} - + {'با توجه به گزارش واصله از اداره ماشین آلات (نامه در پیوست موجود می باشد) جهت بازسازی ناوگان مذکور مبلغ .............................................. ریال معادل .............................................. تومان مورد تایید می باشد.'} @@ -115,19 +115,6 @@ const Content = ({data}) => { - - {'تعهدنامه'} - - {'اینجانب'} - امیرحسین محمودی - {'مالک ناوگان اتوبوس | مینی بوس با مشخصات فوق الذکر بدینوسیله تعهد می نمایم مبلغ دریافتی وام را برابر پیش فاکتور ارائه شده صرف تعمیرات و بازسازی ناوگان نمایم و اگر خلاف این ثابت شود برابر مقررات با بنده برخورد شود و حق هیچگونه اعتراضی ندارم.'} - تاریخ و امضاء - - ) 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 index 927f8b9..9fd6a78 100644 --- a/src/components/dashboard/passenger-boss/Prints/final-credit-amount/index.jsx +++ b/src/components/dashboard/passenger-boss/Prints/final-credit-amount/index.jsx @@ -1,26 +1,46 @@ import {useEffect, useState} from "react"; import CenterLayout from "@/layouts/CenterLayout"; import {useTranslations} from "next-intl"; -import {Typography} from "@mui/material"; +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 ( - <> - {!data ? ( + + {loading ? ( + + + + {t('print_loading')} + + + + ) : !data ? ( {t('data_not_found')} @@ -33,7 +53,7 @@ const FinalCreditAmountComponent = () => { ) : data.map(item => )} - + ) } diff --git a/src/components/dashboard/passenger-boss/TableToolbar.jsx b/src/components/dashboard/passenger-boss/TableToolbar.jsx index 09f2f57..ed224f4 100644 --- a/src/components/dashboard/passenger-boss/TableToolbar.jsx +++ b/src/components/dashboard/passenger-boss/TableToolbar.jsx @@ -1,11 +1,9 @@ import {Stack} from "@mui/material"; -import PrintAppraisalConfirmation from "@/components/dashboard/passenger-boss/Buttons/printAppraisalConfirmation"; import PrintFinalCreditAmount from "@/components/dashboard/passenger-boss/Buttons/printFinalCreditAmount"; const TableToolbar = () => { return ( - ) diff --git a/src/components/layouts/Print/Header/index.jsx b/src/components/layouts/Print/Header/index.jsx index a2928bc..7c0d990 100644 --- a/src/components/layouts/Print/Header/index.jsx +++ b/src/components/layouts/Print/Header/index.jsx @@ -1,11 +1,15 @@ -import {AppBar, Button, Container, Stack, Toolbar, useTheme} from "@mui/material"; +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} + - diff --git a/src/components/layouts/Print/index.jsx b/src/components/layouts/Print/index.jsx index 6c66fd9..afa6585 100644 --- a/src/components/layouts/Print/index.jsx +++ b/src/components/layouts/Print/index.jsx @@ -1,14 +1,12 @@ import Header from "@/components/layouts/Print/Header"; -import {Paper, Toolbar} from "@mui/material"; +import {Toolbar} from "@mui/material"; const Print = (props) => { return ( <>
- - {props.children} - + {props.children} ) } 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/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/pages/_app.jsx b/src/pages/_app.jsx index 8d2f3b0..9824687 100644 --- a/src/pages/_app.jsx +++ b/src/pages/_app.jsx @@ -9,6 +9,7 @@ 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}) => { @@ -22,9 +23,11 @@ const App = ({Component, pageProps}) => { - - - + + + + + diff --git a/src/pages/dashboard/passenger-boss/prints/appraisal-confirmation.jsx b/src/pages/dashboard/machinery-expert/prints/report.jsx similarity index 59% rename from src/pages/dashboard/passenger-boss/prints/appraisal-confirmation.jsx rename to src/pages/dashboard/machinery-expert/prints/report.jsx index c2a021a..526851b 100644 --- a/src/pages/dashboard/passenger-boss/prints/appraisal-confirmation.jsx +++ b/src/pages/dashboard/machinery-expert/prints/report.jsx @@ -1,9 +1,9 @@ import {parse} from "next-useragent"; -import AppraisalConfirmationComponent from "@/components/dashboard/passenger-boss/Prints/appraisal-confirmation"; +import ReportComponent from "@/components/dashboard/machinary-office/Prints/report"; -export default function AppraisalConfirmation() { +export default function Report() { return ( - + ) } @@ -12,7 +12,7 @@ export async function getServerSideProps({req, locale}) { return { props: { messages: (await import(`&/locales/${locale}/app.json`)).default, - title: "Dashboard.passenger_boss_page", + title: "Dashboard.machinary_office_page", isBot, locale, layout: {name: 'PrintLayout'}