recode and add editor config file

This commit is contained in:
Amirhossein Mahmoodi
2023-07-31 09:34:59 +03:30
parent ba91b97e7c
commit 51b21957b9
108 changed files with 5993 additions and 5770 deletions

View File

@@ -1,8 +1,12 @@
{ {
"compilerOptions": { "compilerOptions": {
"paths": { "paths": {
"@/*": ["./src/*"], "@/*": [
"&/*": ["./public/*"], "./src/*"
],
"&/*": [
"./public/*"
]
} }
} }
} }

View File

@@ -1,12 +1,12 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
i18n: { i18n: {
defaultLocale: 'fa', defaultLocale: 'fa',
locales: ['fa'], locales: ['fa'],
localeDetection: false, localeDetection: false,
}, },
} }
module.exports = nextConfig module.exports = nextConfig

View File

@@ -17,145 +17,153 @@ This set of fonts are used in this project under the license: (.....)
* *
**/ **/
@font-face { @font-face {
font-family: IRANSansFaNum; font-family: IRANSansFaNum;
font-style: normal; font-style: normal;
font-weight: 900; font-weight: 900;
src: url('./fonts/eot/IRANSansWeb(FaNum)_Black.eot'); src: url('./fonts/eot/IRANSansWeb(FaNum)_Black.eot');
src: url('./fonts/eot/IRANSansWeb(FaNum)_Black.eot?#iefix') format('embedded-opentype'),/* IE6-8 */ src: url('./fonts/eot/IRANSansWeb(FaNum)_Black.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb(FaNum)_Black.woff2') format('woff2'),/* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb(FaNum)_Black.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb(FaNum)_Black.woff') format('woff'),/* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb(FaNum)_Black.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb(FaNum)_Black.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb(FaNum)_Black.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSansFaNum; font-family: IRANSansFaNum;
font-style: normal; font-style: normal;
font-weight: bold; font-weight: bold;
src: url('./fonts/eot/IRANSansWeb(FaNum)_Bold.eot'); src: url('./fonts/eot/IRANSansWeb(FaNum)_Bold.eot');
src: url('./fonts/eot/IRANSansWeb(FaNum)_Bold.eot?#iefix') format('embedded-opentype'),/* IE6-8 */ src: url('./fonts/eot/IRANSansWeb(FaNum)_Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2') format('woff2'),/* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb(FaNum)_Bold.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb(FaNum)_Bold.woff') format('woff'),/* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb(FaNum)_Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb(FaNum)_Bold.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSansFaNum; font-family: IRANSansFaNum;
font-style: normal; font-style: normal;
font-weight: 500; font-weight: 500;
src: url('./fonts/eot/IRANSansWeb(FaNum)_Medium.eot'); src: url('./fonts/eot/IRANSansWeb(FaNum)_Medium.eot');
src: url('./fonts/eot/IRANSansWeb(FaNum)_Medium.eot?#iefix') format('embedded-opentype'),/* IE6-8 */ src: url('./fonts/eot/IRANSansWeb(FaNum)_Medium.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2') format('woff2'),/* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb(FaNum)_Medium.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb(FaNum)_Medium.woff') format('woff'),/* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb(FaNum)_Medium.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb(FaNum)_Medium.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSansFaNum; font-family: IRANSansFaNum;
font-style: normal; font-style: normal;
font-weight: 300; font-weight: 300;
src: url('./fonts/eot/IRANSansWeb(FaNum)_Light.eot'); src: url('./fonts/eot/IRANSansWeb(FaNum)_Light.eot');
src: url('./fonts/eot/IRANSansWeb(FaNum)_Light.eot?#iefix') format('embedded-opentype'),/* IE6-8 */ src: url('./fonts/eot/IRANSansWeb(FaNum)_Light.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb(FaNum)_Light.woff2') format('woff2'),/* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb(FaNum)_Light.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb(FaNum)_Light.woff') format('woff'),/* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb(FaNum)_Light.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb(FaNum)_Light.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb(FaNum)_Light.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSansFaNum; font-family: IRANSansFaNum;
font-style: normal; font-style: normal;
font-weight: 200; font-weight: 200;
src: url('./fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot'); src: url('./fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot');
src: url('./fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot?#iefix') format('embedded-opentype'),/* IE6-8 */ src: url('./fonts/eot/IRANSansWeb(FaNum)_UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2') format('woff2'),/* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb(FaNum)_UltraLight.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff') format('woff'),/* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb(FaNum)_UltraLight.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb(FaNum)_UltraLight.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSansFaNum; font-family: IRANSansFaNum;
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
src: url('./fonts/eot/IRANSansWeb(FaNum).eot'); src: url('./fonts/eot/IRANSansWeb(FaNum).eot');
src: url('./fonts/eot/IRANSansWeb(FaNum).eot?#iefix') format('embedded-opentype'),/* IE6-8 */ src: url('./fonts/eot/IRANSansWeb(FaNum).eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb(FaNum).woff2') format('woff2'),/* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb(FaNum).woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb(FaNum).woff') format('woff'),/* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb(FaNum).woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb(FaNum).ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb(FaNum).ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSans; font-family: IRANSans;
font-style: normal; font-style: normal;
font-weight: 900; font-weight: 900;
src: url('./fonts/eot/IRANSansWeb_Black.eot'); src: url('./fonts/eot/IRANSansWeb_Black.eot');
src: url('./fonts/eot/IRANSansWeb_Black.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ src: url('./fonts/eot/IRANSansWeb_Black.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb_Black.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb_Black.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb_Black.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb_Black.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb_Black.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb_Black.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSans; font-family: IRANSans;
font-style: normal; font-style: normal;
font-weight: bold; font-weight: bold;
src: url('./fonts/eot/IRANSansWeb_Bold.eot'); src: url('./fonts/eot/IRANSansWeb_Bold.eot');
src: url('./fonts/eot/IRANSansWeb_Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ src: url('./fonts/eot/IRANSansWeb_Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb_Bold.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb_Bold.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb_Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb_Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb_Bold.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb_Bold.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSans; font-family: IRANSans;
font-style: normal; font-style: normal;
font-weight: 500; font-weight: 500;
src: url('./fonts/eot/IRANSansWeb_Medium.eot'); src: url('./fonts/eot/IRANSansWeb_Medium.eot');
src: url('./fonts/eot/IRANSansWeb_Medium.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ src: url('./fonts/eot/IRANSansWeb_Medium.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb_Medium.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb_Medium.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb_Medium.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb_Medium.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb_Medium.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb_Medium.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSans; font-family: IRANSans;
font-style: normal; font-style: normal;
font-weight: 300; font-weight: 300;
src: url('./fonts/eot/IRANSansWeb_Light.eot'); src: url('./fonts/eot/IRANSansWeb_Light.eot');
src: url('./fonts/eot/IRANSansWeb_Light.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ src: url('./fonts/eot/IRANSansWeb_Light.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb_Light.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb_Light.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb_Light.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb_Light.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb_Light.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb_Light.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSans; font-family: IRANSans;
font-style: normal; font-style: normal;
font-weight: 200; font-weight: 200;
src: url('./fonts/eot/IRANSansWeb_UltraLight.eot'); src: url('./fonts/eot/IRANSansWeb_UltraLight.eot');
src: url('./fonts/eot/IRANSansWeb_UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ src: url('./fonts/eot/IRANSansWeb_UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb_UltraLight.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb_UltraLight.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb_UltraLight.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb_UltraLight.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb_UltraLight.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb_UltraLight.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: IRANSans; font-family: IRANSans;
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
src: url('./fonts/eot/IRANSansWeb.eot'); src: url('./fonts/eot/IRANSansWeb.eot');
src: url('./fonts/eot/IRANSansWeb.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ src: url('./fonts/eot/IRANSansWeb.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff2/IRANSansWeb.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/ url('./fonts/woff2/IRANSansWeb.woff2') format('woff2'), /* FF39+,Chrome36+, Opera24+*/
url('./fonts/woff/IRANSansWeb.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/IRANSansWeb.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/IRANSansWeb.ttf') format('truetype'); url('./fonts/ttf/IRANSansWeb.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: Parastoo; font-family: Parastoo;
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
src: url('./fonts/eot/Parastoo.eot'); src: url('./fonts/eot/Parastoo.eot');
src: url('./fonts/eot/Parastoo.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ src: url('./fonts/eot/Parastoo.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff/Parastoo.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/Parastoo.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/Parastoo.ttf') format('truetype'); url('./fonts/ttf/Parastoo.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: Parastoo; font-family: Parastoo;
font-style: normal; font-style: normal;
font-weight: bold; font-weight: bold;
src: url('./fonts/eot/Parastoo-Bold.eot'); src: url('./fonts/eot/Parastoo-Bold.eot');
src: url('./fonts/eot/Parastoo-Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */ src: url('./fonts/eot/Parastoo-Bold.eot?#iefix') format('embedded-opentype'), /* IE6-8 */
url('./fonts/woff/Parastoo-Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/ url('./fonts/woff/Parastoo-Bold.woff') format('woff'), /* FF3.6+, IE9, Chrome6+, Saf5.1+*/
url('./fonts/ttf/Parastoo-Bold.ttf') format('truetype'); url('./fonts/ttf/Parastoo-Bold.ttf') format('truetype');
} }

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -1,10 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="611.781" height="634.738" viewBox="0 0 611.781 634.738"> <svg xmlns="http://www.w3.org/2000/svg" width="611.781" height="634.738" viewBox="0 0 611.781 634.738">
<g id="Group_67" data-name="Group 67" transform="translate(17717.998 5548)"> <g id="Group_67" data-name="Group 67" transform="translate(17717.998 5548)">
<g id="Group_35" data-name="Group 35" transform="translate(-18337.291 -5635.72)"> <g id="Group_35" data-name="Group 35" transform="translate(-18337.291 -5635.72)">
<path id="Path_5" data-name="Path 5" d="M126.892,461.042c-1.676-6.24-3.057-12-4.776-17.668-6.5-21.426-17.146-40.936-27.846-60.456-10.362-18.9-20.22-38.081-30.31-57.132-.389-.734-.9-1.406-1.791-2.8C36.884,364.062,18.983,407.918,1.474,451.962l-1.21-.256a8.878,8.878,0,0,1-.214-2.524c2.581-12.372,4.479-24.951,8.05-37.034a334.761,334.761,0,0,1,13.91-37.364Q35.923,342.5,51.02,310.733c3.525-7.389,1.922-13.976.051-20.991a185.716,185.716,0,0,1-6.18-54.9,183.572,183.572,0,0,1,21.089-78.7A248.249,248.249,0,0,1,132.8,75.26a320,320,0,0,1,59.11-36.67c23.209-11.317,47.033-21.1,72.336-26.513A487.808,487.808,0,0,1,314.282,3.7C333.731,1.624,353.352,1.16,372.9.018a22,22,0,0,1,3.3.145c.4.037.775.291,1.236,1.13L360.926,4.642c-26.4,5.318-52.988,9.831-79.147,16.136a295.335,295.335,0,0,0-105.32,48.21c-24.648,17.9-45.935,39.123-61.823,65.3-11.419,18.813-17.813,39.38-20.064,61.275-1.445,14.062-1.024,28.1.166,42.133a44.216,44.216,0,0,0,1,4.8c1.418-1.165,2.427-1.875,3.3-2.728,10.2-10.038,20.041-20.48,30.661-30.055,14.1-12.713,29.419-23.882,46.9-31.678,21.42-9.55,43.69-13.568,66.553-6.9,10.553,3.079,20.531,8.393,30.44,13.353,20.075,10.051,39.841,20.721,59.892,30.821a182.512,182.512,0,0,0,68.478,19.421c33.07,2.549,64.407-3.894,93.662-19.748,25.04-13.571,46.544-31.674,66.252-52.041,15.44-15.956,28.916-33.487,41.494-51.746,2.217-3.219,4.962-6.073,7.463-9.1l.983.382a15.039,15.039,0,0,1-.828,3.67c-3.9,7.643-7.67,15.361-11.911,22.812-17.8,31.272-39.019,60-64.85,85.13-26.732,26.009-56.556,47.619-90.953,62.526a162.5,162.5,0,0,1-74.241,14.106c-24.876-.775-48.015,5.5-69.742,17.063-14.861,7.91-29.333,16.559-43.879,25.049-13.773,8.039-28.189,13.431-44.484,11.949-2.667-.242-2.49,1.854-2.988,3.479-2.151,7.019-3.92,14.193-6.627,20.993-3.8,9.547-10.327,17.4-17.557,24.572-15.526,15.4-28.738,32.778-42.59,49.608-4.68,5.687-9.284,11.436-14.28,17.6M284.4,266.987c-.334-.819-.375-1.206-.591-1.393a13.8,13.8,0,0,0-1.854-1.356c-22.793-13.885-46.277-26.286-71.807-34.532-20.915-6.755-41.455-6.841-61.848,1.941-15.741,6.779-28.925,17.112-41,29.01-5.591,5.508-5.981,8.93-2.421,15.921q12.355,24.272,24.657,48.572c10.087,19.93,20.125,39.886,30.279,59.783.543,1.062,1.967,1.674,2.98,2.5a12.859,12.859,0,0,0,1.933-3.081,49.022,49.022,0,0,0-2.4-33.292c-6.476-14.911-14.075-29.337-21.275-43.93-4.967-10.065-10.144-20.026-15.07-30.112-1.63-3.338-2.558-6.917.574-10.1,10.226-10.371,22.1-18.1,36.115-22.335,3.993-1.206,5.77-.416,7.975,3.16,3.927,6.365,7.687,12.846,11.888,19.028,9.727,14.31,23.437,21.051,40.7,20.974,16.93-.075,32.491-5.033,47.273-12.854,4.715-2.495,9.282-5.27,13.894-7.9" transform="translate(619.257 87.713)" fill="#231955"/> <path id="Path_5" data-name="Path 5"
<path id="Path_6" data-name="Path 6" d="M104.479,767.41c2.322-11.049,4.27-22.193,7.046-33.127,5.72-22.532,15.277-43.572,26.263-63.957,16.423-30.474,34.631-59.844,55.63-87.405,20.939-27.481,43.511-53.464,70.53-75.261,24.13-19.467,50.887-33.842,81.027-41.582,4.676-1.2,9.437-2.118,14.2-2.939a15,15,0,0,0,11.744-9.6c4.286-10.252,11.9-14.085,22.732-13.761a202.3,202.3,0,0,0,89.232-17.644A257.328,257.328,0,0,0,528.458,395.6c1.4-1.009,2.885-1.912,5.222-3.451-.953,3.026-1.265,5.258-2.293,7.089-14.936,26.6-31.633,51.891-54.057,72.956-18.879,17.734-40.265,31.046-65.744,36.862-15.1,3.446-30.348,6.253-45.529,9.349-18.622,3.8-37.406,6.945-55.825,11.553-27.566,6.9-51.782,20.673-73.654,38.685-23.608,19.441-42.473,43.054-59.8,68-24.96,35.939-44.814,74.717-63.24,114.278q-3.9,8.368-7.793,16.738l-1.263-.245" transform="translate(583.845 -45.198)" fill="#231955"/> d="M126.892,461.042c-1.676-6.24-3.057-12-4.776-17.668-6.5-21.426-17.146-40.936-27.846-60.456-10.362-18.9-20.22-38.081-30.31-57.132-.389-.734-.9-1.406-1.791-2.8C36.884,364.062,18.983,407.918,1.474,451.962l-1.21-.256a8.878,8.878,0,0,1-.214-2.524c2.581-12.372,4.479-24.951,8.05-37.034a334.761,334.761,0,0,1,13.91-37.364Q35.923,342.5,51.02,310.733c3.525-7.389,1.922-13.976.051-20.991a185.716,185.716,0,0,1-6.18-54.9,183.572,183.572,0,0,1,21.089-78.7A248.249,248.249,0,0,1,132.8,75.26a320,320,0,0,1,59.11-36.67c23.209-11.317,47.033-21.1,72.336-26.513A487.808,487.808,0,0,1,314.282,3.7C333.731,1.624,353.352,1.16,372.9.018a22,22,0,0,1,3.3.145c.4.037.775.291,1.236,1.13L360.926,4.642c-26.4,5.318-52.988,9.831-79.147,16.136a295.335,295.335,0,0,0-105.32,48.21c-24.648,17.9-45.935,39.123-61.823,65.3-11.419,18.813-17.813,39.38-20.064,61.275-1.445,14.062-1.024,28.1.166,42.133a44.216,44.216,0,0,0,1,4.8c1.418-1.165,2.427-1.875,3.3-2.728,10.2-10.038,20.041-20.48,30.661-30.055,14.1-12.713,29.419-23.882,46.9-31.678,21.42-9.55,43.69-13.568,66.553-6.9,10.553,3.079,20.531,8.393,30.44,13.353,20.075,10.051,39.841,20.721,59.892,30.821a182.512,182.512,0,0,0,68.478,19.421c33.07,2.549,64.407-3.894,93.662-19.748,25.04-13.571,46.544-31.674,66.252-52.041,15.44-15.956,28.916-33.487,41.494-51.746,2.217-3.219,4.962-6.073,7.463-9.1l.983.382a15.039,15.039,0,0,1-.828,3.67c-3.9,7.643-7.67,15.361-11.911,22.812-17.8,31.272-39.019,60-64.85,85.13-26.732,26.009-56.556,47.619-90.953,62.526a162.5,162.5,0,0,1-74.241,14.106c-24.876-.775-48.015,5.5-69.742,17.063-14.861,7.91-29.333,16.559-43.879,25.049-13.773,8.039-28.189,13.431-44.484,11.949-2.667-.242-2.49,1.854-2.988,3.479-2.151,7.019-3.92,14.193-6.627,20.993-3.8,9.547-10.327,17.4-17.557,24.572-15.526,15.4-28.738,32.778-42.59,49.608-4.68,5.687-9.284,11.436-14.28,17.6M284.4,266.987c-.334-.819-.375-1.206-.591-1.393a13.8,13.8,0,0,0-1.854-1.356c-22.793-13.885-46.277-26.286-71.807-34.532-20.915-6.755-41.455-6.841-61.848,1.941-15.741,6.779-28.925,17.112-41,29.01-5.591,5.508-5.981,8.93-2.421,15.921q12.355,24.272,24.657,48.572c10.087,19.93,20.125,39.886,30.279,59.783.543,1.062,1.967,1.674,2.98,2.5a12.859,12.859,0,0,0,1.933-3.081,49.022,49.022,0,0,0-2.4-33.292c-6.476-14.911-14.075-29.337-21.275-43.93-4.967-10.065-10.144-20.026-15.07-30.112-1.63-3.338-2.558-6.917.574-10.1,10.226-10.371,22.1-18.1,36.115-22.335,3.993-1.206,5.77-.416,7.975,3.16,3.927,6.365,7.687,12.846,11.888,19.028,9.727,14.31,23.437,21.051,40.7,20.974,16.93-.075,32.491-5.033,47.273-12.854,4.715-2.495,9.282-5.27,13.894-7.9"
<path id="Path_7" data-name="Path 7" d="M405.392,690.855l-53.546,54.494-55.389-51.215,55.1-53.062L405.4,690.854" transform="translate(518.777 -129.57)" fill="#ff9100"/> transform="translate(619.257 87.713)" fill="#231955"/>
<path id="Path_6" data-name="Path 6"
d="M104.479,767.41c2.322-11.049,4.27-22.193,7.046-33.127,5.72-22.532,15.277-43.572,26.263-63.957,16.423-30.474,34.631-59.844,55.63-87.405,20.939-27.481,43.511-53.464,70.53-75.261,24.13-19.467,50.887-33.842,81.027-41.582,4.676-1.2,9.437-2.118,14.2-2.939a15,15,0,0,0,11.744-9.6c4.286-10.252,11.9-14.085,22.732-13.761a202.3,202.3,0,0,0,89.232-17.644A257.328,257.328,0,0,0,528.458,395.6c1.4-1.009,2.885-1.912,5.222-3.451-.953,3.026-1.265,5.258-2.293,7.089-14.936,26.6-31.633,51.891-54.057,72.956-18.879,17.734-40.265,31.046-65.744,36.862-15.1,3.446-30.348,6.253-45.529,9.349-18.622,3.8-37.406,6.945-55.825,11.553-27.566,6.9-51.782,20.673-73.654,38.685-23.608,19.441-42.473,43.054-59.8,68-24.96,35.939-44.814,74.717-63.24,114.278q-3.9,8.368-7.793,16.738l-1.263-.245"
transform="translate(583.845 -45.198)" fill="#231955"/>
<path id="Path_7" data-name="Path 7"
d="M405.392,690.855l-53.546,54.494-55.389-51.215,55.1-53.062L405.4,690.854"
transform="translate(518.777 -129.57)" fill="#ff9100"/>
</g>
<text id="MARHABA" transform="translate(-17322 -4931)" fill="#bcbcbc" font-size="50"
font-family="Montserrat-ExtraBold, Montserrat" font-weight="800">
<tspan x="-281.35" y="0">MARHABA</tspan>
</text>
</g> </g>
<text id="MARHABA" transform="translate(-17322 -4931)" fill="#bcbcbc" font-size="50" font-family="Montserrat-ExtraBold, Montserrat" font-weight="800"><tspan x="-281.35" y="0">MARHABA</tspan></text>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,92 +1,92 @@
export const FA_DATATABLE_LOCALIZATION = { export const FA_DATATABLE_LOCALIZATION = {
actions: "عملیات", actions: "عملیات",
and: "و", and: "و",
cancel: "لغو", cancel: "لغو",
changeFilterMode: "تغییر حالت فیلتر", changeFilterMode: "تغییر حالت فیلتر",
changeSearchMode: "تغییر حالت جستجو", changeSearchMode: "تغییر حالت جستجو",
clearFilter: "پاک کردن فیلتر", clearFilter: "پاک کردن فیلتر",
clearSearch: "پاک کردن جستجو", clearSearch: "پاک کردن جستجو",
clearSort: "پاک کردن مرتب سازی", clearSort: "پاک کردن مرتب سازی",
clickToCopy: "کلیک برای کپی", clickToCopy: "کلیک برای کپی",
collapse: "جمع شدن", collapse: "جمع شدن",
collapseAll: "جمع شدن همه", collapseAll: "جمع شدن همه",
columnActions: "عملیات ستون", columnActions: "عملیات ستون",
copiedToClipboard: "کپی شد", copiedToClipboard: "کپی شد",
dropToGroupBy: "رها کردن برای گروه بندی بر اساس {column}", dropToGroupBy: "رها کردن برای گروه بندی بر اساس {column}",
edit: "ویرایش", edit: "ویرایش",
expand: "باز شدن", expand: "باز شدن",
expandAll: "باز شدن همه", expandAll: "باز شدن همه",
filterArrIncludes: "شامل", filterArrIncludes: "شامل",
filterArrIncludesAll: "شامل همه", filterArrIncludesAll: "شامل همه",
filterArrIncludesSome: "شامل", filterArrIncludesSome: "شامل",
filterBetween: "میان", filterBetween: "میان",
filterBetweenInclusive: "میان با احتساب هر دو", filterBetweenInclusive: "میان با احتساب هر دو",
filterByColumn: "فیلتر بر اساس {column}", filterByColumn: "فیلتر بر اساس {column}",
filterContains: "شامل", filterContains: "شامل",
filterEmpty: "خالی", filterEmpty: "خالی",
filterEndsWith: "به پایان می‌رسد با", filterEndsWith: "به پایان می‌رسد با",
filterEquals: "برابر", filterEquals: "برابر",
filterEqualsString: "برابر", filterEqualsString: "برابر",
filterFuzzy: "نزدیک", filterFuzzy: "نزدیک",
filterGreaterThan: "بزرگتر از", filterGreaterThan: "بزرگتر از",
filterGreaterThanOrEqualTo: "بزرگتر یا مساوی", filterGreaterThanOrEqualTo: "بزرگتر یا مساوی",
filterInNumberRange: "میان", filterInNumberRange: "میان",
filterIncludesString: "شامل", filterIncludesString: "شامل",
filterIncludesStringSensitive: "شامل", filterIncludesStringSensitive: "شامل",
filterLessThan: "کوچکتر از", filterLessThan: "کوچکتر از",
filterLessThanOrEqualTo: "کوچکتر یا مساوی", filterLessThanOrEqualTo: "کوچکتر یا مساوی",
filterMode: "حالت فیلتر: {filterType}", filterMode: "حالت فیلتر: {filterType}",
filterNotEmpty: "غیر خالی", filterNotEmpty: "غیر خالی",
filterNotEquals: "نا برابر", filterNotEquals: "نا برابر",
filterStartsWith: "شروع می‌شود با", filterStartsWith: "شروع می‌شود با",
filterWeakEquals: "برابر", filterWeakEquals: "برابر",
filteringByColumn: "فیلتر بر اساس {column} - {filterType} {filterValue}", filteringByColumn: "فیلتر بر اساس {column} - {filterType} {filterValue}",
goToFirstPage: "رفتن به صفحه اول", goToFirstPage: "رفتن به صفحه اول",
goToLastPage: "رفتن به صفحه آخر", goToLastPage: "رفتن به صفحه آخر",
goToNextPage: "رفتن به صفحه بعدی", goToNextPage: "رفتن به صفحه بعدی",
goToPreviousPage: "رفتن به صفحه قبلی", goToPreviousPage: "رفتن به صفحه قبلی",
grab: "گرفتن", grab: "گرفتن",
groupByColumn: "گروه بندی بر اساس {column}", groupByColumn: "گروه بندی بر اساس {column}",
groupedBy: "گروه بندی شده بر اساس", groupedBy: "گروه بندی شده بر اساس",
hideAll: "پنهان کردن همه", hideAll: "پنهان کردن همه",
hideColumn: "پنهان کردن ستون {column}", hideColumn: "پنهان کردن ستون {column}",
max: "حداکثر", max: "حداکثر",
min: "حداقل", min: "حداقل",
move: "انتقال", move: "انتقال",
noRecordsToDisplay: "هیچ رکوردی برای نمایش وجود ندارد", noRecordsToDisplay: "هیچ رکوردی برای نمایش وجود ندارد",
noResultsFound: "نتیجه‌ای یافت نشد", noResultsFound: "نتیجه‌ای یافت نشد",
of: "از", of: "از",
or: "یا", or: "یا",
pinToLeft: "چسباندن به سمت چپ", pinToLeft: "چسباندن به سمت چپ",
pinToRight: "چسباندن به سمت راست", pinToRight: "چسباندن به سمت راست",
resetColumnSize: "بازنشانی اندازه ستون", resetColumnSize: "بازنشانی اندازه ستون",
resetOrder: "بازنشانی ترتیب", resetOrder: "بازنشانی ترتیب",
rowActions: "عملیات ردیف", rowActions: "عملیات ردیف",
rowNumber: "#", rowNumber: "#",
rowNumbers: "شماره ردیف", rowNumbers: "شماره ردیف",
rowsPerPage: "ردیف در هر صفحه", rowsPerPage: "ردیف در هر صفحه",
save: "ذخیره", save: "ذخیره",
search: "جستجو", search: "جستجو",
selectedCountOfRowCountRowsSelected: selectedCountOfRowCountRowsSelected:
"{selectedCount} از {rowCount} ردیف انتخاب شده", "{selectedCount} از {rowCount} ردیف انتخاب شده",
select: "انتخاب", select: "انتخاب",
showAll: "نمایش همه", showAll: "نمایش همه",
showAllColumns: "نمایش همه ستون‌ها", showAllColumns: "نمایش همه ستون‌ها",
showHideColumns: "نمایش/مخفی کردن ستون‌ها", showHideColumns: "نمایش/مخفی کردن ستون‌ها",
showHideFilters: "نمایش/مخفی کردن فیلترها", showHideFilters: "نمایش/مخفی کردن فیلترها",
showHideSearch: "نمایش/مخفی کردن جستجو", showHideSearch: "نمایش/مخفی کردن جستجو",
sortByColumnAsc: "مرتب سازی بر اساس {column} صعودی", sortByColumnAsc: "مرتب سازی بر اساس {column} صعودی",
sortByColumnDesc: "مرتب سازی بر اساس {column} نزولی", sortByColumnDesc: "مرتب سازی بر اساس {column} نزولی",
sortedByColumnAsc: "مرتب شده بر اساس {column} صعودی", sortedByColumnAsc: "مرتب شده بر اساس {column} صعودی",
sortedByColumnDesc: "مرتب شده بر اساس {column} نزولی", sortedByColumnDesc: "مرتب شده بر اساس {column} نزولی",
thenBy: "، سپس بر اساس ", thenBy: "، سپس بر اساس ",
toggleDensity: "تغییر تراکم", toggleDensity: "تغییر تراکم",
toggleFullScreen: "تغییر حالت تمام صفحه", toggleFullScreen: "تغییر حالت تمام صفحه",
toggleSelectAll: "انتخاب/عدم انتخاب همه", toggleSelectAll: "انتخاب/عدم انتخاب همه",
toggleSelectRow: "انتخاب/عدم انتخاب ردیف", toggleSelectRow: "انتخاب/عدم انتخاب ردیف",
toggleVisibility: "تغییر پیدا/پنهان", toggleVisibility: "تغییر پیدا/پنهان",
ungroupByColumn: "لغو گروه بندی بر اساس {column}", ungroupByColumn: "لغو گروه بندی بر اساس {column}",
unpin: "رها کردن", unpin: "رها کردن",
unpinAll: "رها کردن همه", unpinAll: "رها کردن همه",
unsorted: "بدون مرتب سازی", unsorted: "بدون مرتب سازی",
}; };

View File

@@ -1,184 +1,177 @@
import CenterLayout from "@/layouts/CenterLayout"; import CenterLayout from "@/layouts/CenterLayout";
import DashboardLayouts from "@/layouts/dashboardLayouts"; import DashboardLayouts from "@/layouts/dashboardLayouts";
import { import {Box, Button, Container, Paper, Stack, Typography,} from "@mui/material";
Button,
Typography,
Stack,
Paper,
Container,
Box,
} from "@mui/material";
import Image from "next/image"; import Image from "next/image";
import { Formik } from "formik"; import {Formik} from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import PasswordField from "@/core/components/PasswordField"; import PasswordField from "@/core/components/PasswordField";
import StyledForm from "@/core/components/StyledForm"; import StyledForm from "@/core/components/StyledForm";
import { CHANGE_PASSWORD } from "@/core/data/apiRoutes"; import {CHANGE_PASSWORD} from "@/core/data/apiRoutes";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
import axios from "axios"; import axios from "axios";
import Notifications from "@/core/components/notifications"; import Notifications from "@/core/components/notifications";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
import { toast } from "react-toastify"; import {toast} from "react-toastify";
const DashboardChangePasswordComponent = () => { const DashboardChangePasswordComponent = () => {
const t = useTranslations(); const t = useTranslations();
const { token } = useUser(); const {token} = useUser();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const handleSubmit = (values, { setSubmitting, resetForm }) => { const handleSubmit = (values, {setSubmitting, resetForm}) => {
toast.dismiss(); toast.dismiss();
const pendingToast = toast(t("notifications.pending"), { const pendingToast = toast(t("notifications.pending"), {
position: directionApp === "ltr" ? "top-left" : "top-right", position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: false, autoClose: false,
closeOnClick: false, closeOnClick: false,
draggable: false, draggable: false,
});
axios
.post(
CHANGE_PASSWORD,
{
current_password: values.current_password,
new_password: values.new_password,
new_password_confirmation: values.new_password_confirmation,
},
{
headers: {authorization: `Bearer ${token}`},
}
)
.then((response) => {
toast.dismiss(pendingToast); // Dismiss the pending toast notification
Notifications(directionApp, response, t);
resetForm();
})
.catch((error) => {
toast.dismiss(pendingToast); // Dismiss the pending toast notification
Notifications(directionApp, error.response, t);
})
.finally(() => {
setSubmitting(false); // Set `setSubmitting` to false after the API request completes
});
};
const initialValues = {
current_password: "",
new_password: "",
new_password_confirmation: "",
};
const validationSchema = Yup.object().shape({
current_password: Yup.string().required(
t("ChangePassword.error_message_required")
),
new_password: Yup.string()
.min(8, t("ChangePassword.error_message_password_length"))
.required(t("ChangePassword.error_message_required")),
new_password_confirmation: Yup.string()
.required(t("ChangePassword.error_message_required"))
.test(
t("ChangePassword.error_message_password_match"),
t("ChangePassword.error_message_password_not_match"),
function (value) {
return this.parent.new_password === value;
}
),
}); });
axios
.post(
CHANGE_PASSWORD,
{
current_password: values.current_password,
new_password: values.new_password,
new_password_confirmation: values.new_password_confirmation,
},
{
headers: { authorization: `Bearer ${token}` },
}
)
.then((response) => {
toast.dismiss(pendingToast); // Dismiss the pending toast notification
Notifications(directionApp, response, t);
resetForm();
})
.catch((error) => {
toast.dismiss(pendingToast); // Dismiss the pending toast notification
Notifications(directionApp, error.response, t);
})
.finally(() => {
setSubmitting(false); // Set `setSubmitting` to false after the API request completes
});
};
const initialValues = {
current_password: "",
new_password: "",
new_password_confirmation: "",
};
const validationSchema = Yup.object().shape({
current_password: Yup.string().required(
t("ChangePassword.error_message_required")
),
new_password: Yup.string()
.min(8, t("ChangePassword.error_message_password_length"))
.required(t("ChangePassword.error_message_required")),
new_password_confirmation: Yup.string()
.required(t("ChangePassword.error_message_required"))
.test(
t("ChangePassword.error_message_password_match"),
t("ChangePassword.error_message_password_not_match"),
function (value) {
return this.parent.new_password === value;
}
),
});
return ( return (
<DashboardLayouts> <DashboardLayouts>
<CenterLayout> <CenterLayout>
<Container maxWidth="sm"> <Container maxWidth="sm">
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
onSubmit={handleSubmit} onSubmit={handleSubmit}
validationSchema={validationSchema} validationSchema={validationSchema}
>
{(props) => (
<StyledForm
onSubmit={(e) => {
e.preventDefault();
props.handleSubmit();
}}
>
<Paper elevation={0}>
<Stack spacing={3} sx={{ p: 5 }} component="div">
<Box
sx={{ position: "relative", width: "100%", height: 200 }}
> >
<Image fill src="/images/change-password.svg" alt="" /> {(props) => (
</Box> <StyledForm
<Typography margin={2} variant="h4" textAlign="center"> onSubmit={(e) => {
{t("ChangePassword.typography_change_password")} e.preventDefault();
</Typography> props.handleSubmit();
<Stack spacing={1} component="div"> }}
<PasswordField >
name="current_password" <Paper elevation={0}>
label={t("ChangePassword.label_current_password")} <Stack spacing={3} sx={{p: 5}} component="div">
error={ <Box
props.touched.current_password && sx={{position: "relative", width: "100%", height: 200}}
props.errors.current_password >
? true <Image fill src="/images/change-password.svg" alt=""/>
: false </Box>
} <Typography margin={2} variant="h4" textAlign="center">
helperText={ {t("ChangePassword.typography_change_password")}
props.touched.current_password </Typography>
? props.errors.current_password <Stack spacing={1} component="div">
: null <PasswordField
} name="current_password"
/> label={t("ChangePassword.label_current_password")}
</Stack> error={
<Stack spacing={1} component="div"> props.touched.current_password &&
<PasswordField props.errors.current_password
name="new_password" ? true
label={t("ChangePassword.label_new_password")} : false
error={ }
props.touched.new_password && helperText={
props.errors.new_password props.touched.current_password
? true ? props.errors.current_password
: false : null
} }
helperText={ />
props.touched.new_password </Stack>
? props.errors.new_password <Stack spacing={1} component="div">
: null <PasswordField
} name="new_password"
/> label={t("ChangePassword.label_new_password")}
</Stack> error={
<Stack spacing={1} component="div"> props.touched.new_password &&
<PasswordField props.errors.new_password
name="new_password_confirmation" ? true
label={t("ChangePassword.label_confirm_password")} : false
error={ }
props.touched.new_password_confirmation && helperText={
props.errors.new_password_confirmation props.touched.new_password
? true ? props.errors.new_password
: false : null
} }
helperText={ />
props.touched.new_password_confirmation </Stack>
? props.errors.new_password_confirmation <Stack spacing={1} component="div">
: null <PasswordField
} name="new_password_confirmation"
/> label={t("ChangePassword.label_confirm_password")}
</Stack> error={
<Button props.touched.new_password_confirmation &&
type="submit" props.errors.new_password_confirmation
fullWidth ? true
variant="contained" : false
color="primary" }
size="large" helperText={
disabled={props.isSubmitting} props.touched.new_password_confirmation
> ? props.errors.new_password_confirmation
{props.isSubmitting : null
? t("SubmitButton.button_while_submit") }
: t("SubmitButton.button_submit")} />
</Button> </Stack>
</Stack> <Button
</Paper> type="submit"
</StyledForm> fullWidth
)} variant="contained"
</Formik> color="primary"
</Container> size="large"
</CenterLayout> disabled={props.isSubmitting}
</DashboardLayouts> >
); {props.isSubmitting
? t("SubmitButton.button_while_submit")
: t("SubmitButton.button_submit")}
</Button>
</Stack>
</Paper>
</StyledForm>
)}
</Formik>
</Container>
</CenterLayout>
</DashboardLayouts>
);
}; };
export default DashboardChangePasswordComponent; export default DashboardChangePasswordComponent;

View File

@@ -2,277 +2,268 @@ import StyledForm from "@/core/components/StyledForm";
import CenterLayout from "@/layouts/CenterLayout"; import CenterLayout from "@/layouts/CenterLayout";
import DashboardLayouts from "@/layouts/dashboardLayouts"; import DashboardLayouts from "@/layouts/dashboardLayouts";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
import { import {Box, Container, Grid, Paper, Stack, TextField, Typography,} from "@mui/material";
Box,
Button,
Container,
Grid,
Paper,
Stack,
TextField,
Typography,
} from "@mui/material";
import * as Yup from "yup"; import * as Yup from "yup";
import { Field, Formik } from "formik"; import {Field, Formik} from "formik";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import AvatarUpload from "@/core/components/AvatarUpload"; import AvatarUpload from "@/core/components/AvatarUpload";
import axios from "axios"; import axios from "axios";
import { UPDATE_PROFILE, UPDATE_AVATAR } from "@/core/data/apiRoutes"; import {UPDATE_AVATAR} from "@/core/data/apiRoutes";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
import Notifications from "@/core/components/notifications"; import Notifications from "@/core/components/notifications";
import ImageResizer from "@/core/components/ImageConvertor"; import ImageResizer from "@/core/components/ImageConvertor";
const DashboardEditProfile = () => { const DashboardEditProfile = () => {
const t = useTranslations(); const t = useTranslations();
const { user, token, getUser, changeUser } = useUser(); const {user, token, getUser, changeUser} = useUser();
console.log(user); console.log(user);
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const editAvatar = async (avatar) => { const editAvatar = async (avatar) => {
try { try {
const formData = new FormData(); const formData = new FormData();
if (avatar != null) { if (avatar != null) {
var resizedAvatar; var resizedAvatar;
resizedAvatar = await ImageResizer(avatar); resizedAvatar = await ImageResizer(avatar);
formData.append("avatar", resizedAvatar); formData.append("avatar", resizedAvatar);
} }
await axios.post(UPDATE_AVATAR, formData, { await axios.post(UPDATE_AVATAR, formData, {
headers: { headers: {
authorization: `Bearer ${token}`, authorization: `Bearer ${token}`,
"Content-Type": "multipart/form-data", "Content-Type": "multipart/form-data",
}, },
}); });
} catch (error) { } catch (error) {
Notifications(directionApp, error.response, t); Notifications(directionApp, error.response, t);
throw error; throw error;
} }
}; };
const handleSubmit = (values, { setSubmitting }) => { const handleSubmit = (values, {setSubmitting}) => {
// const formData = new FormData(); // const formData = new FormData();
// formData.append("email", values.expert_email); // formData.append("email", values.expert_email);
// if (values.change_avatar !== false) { // if (values.change_avatar !== false) {
// editAvatar(values.expert_avatar) // editAvatar(values.expert_avatar)
// .then(() => { // .then(() => {
// return axios.post(UPDATE_PROFILE, formData, { // return axios.post(UPDATE_PROFILE, formData, {
// headers: { // headers: {
// authorization: `Bearer ${token}`, // authorization: `Bearer ${token}`,
// "Content-Type": "multipart/form-data", // "Content-Type": "multipart/form-data",
// }, // },
// }); // });
// }) // })
// .then((response) => { // .then((response) => {
// Notifications(directionApp, response, t); // Notifications(directionApp, response, t);
// getUser((data) => { // getUser((data) => {
// changeUser(data); // changeUser(data);
// }); // });
// }) // })
// .catch((error) => { // .catch((error) => {
// Notifications(directionApp, error.response, t); // Notifications(directionApp, error.response, t);
// }) // })
// .finally(() => { // .finally(() => {
// setSubmitting(false); // setSubmitting(false);
// }); // });
// } else { // } else {
// axios // axios
// .post(UPDATE_PROFILE, formData, { // .post(UPDATE_PROFILE, formData, {
// headers: { // headers: {
// authorization: `Bearer ${token}`, // authorization: `Bearer ${token}`,
// "Content-Type": "multipart/form-data", // "Content-Type": "multipart/form-data",
// }, // },
// }) // })
// .then((response) => { // .then((response) => {
// Notifications(directionApp, response, t); // Notifications(directionApp, response, t);
// getUser((data) => { // getUser((data) => {
// changeUser(data); // changeUser(data);
// }); // });
// }) // })
// .catch((error) => { // .catch((error) => {
// Notifications(directionApp, error.response, t); // Notifications(directionApp, error.response, t);
// }) // })
// .finally(() => { // .finally(() => {
// setSubmitting(false); // setSubmitting(false);
// }); // });
// } // }
}; };
const initialValues = { const initialValues = {
expert_avatar: null, expert_avatar: null,
username: user.username, username: user.username,
name: user.name, name: user.name,
email: user.email, email: user.email,
province_name: user.province_name, province_name: user.province_name,
position: user.position, position: user.position,
}; };
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
// username: Yup.string().required(t("UpdateProfile.error_message_required")), // username: Yup.string().required(t("UpdateProfile.error_message_required")),
// name: Yup.string().required(t("UpdateProfile.error_message_required")), // name: Yup.string().required(t("UpdateProfile.error_message_required")),
// email: Yup.string() // email: Yup.string()
// .email(t("UpdateProfile.error_invalid_email")) // .email(t("UpdateProfile.error_invalid_email"))
// .required(t("UpdateProfile.error_message_required")), // .required(t("UpdateProfile.error_message_required")),
// province_name: Yup.string().required( // province_name: Yup.string().required(
// t("UpdateProfile.error_message_required") // t("UpdateProfile.error_message_required")
// ), // ),
// position: Yup.string().required(t("UpdateProfile.error_message_required")), // position: Yup.string().required(t("UpdateProfile.error_message_required")),
}); });
return ( return (
<DashboardLayouts> <DashboardLayouts>
<CenterLayout> <CenterLayout>
<Container maxWidth="sm"> <Container maxWidth="sm">
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
onSubmit={handleSubmit} onSubmit={handleSubmit}
validationSchema={validationSchema} validationSchema={validationSchema}
>
{(props) => (
<StyledForm
onSubmit={(e) => {
e.preventDefault();
props.handleSubmit();
}}
>
<Paper elevation={0}>
<Stack spacing={3} sx={{ p: 5 }} component="div">
<Typography margin={2} variant="h4" textAlign="center">
{t("UpdateProfile.typography_edit_profile")}
</Typography>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
}}
> >
<AvatarUpload {(props) => (
user={user} <StyledForm
setFieldValue={props.setFieldValue} onSubmit={(e) => {
valueAvatar="expert_avatar" e.preventDefault();
changeFlag="change_avatar" props.handleSubmit();
/> }}
</Box> >
<Grid container spacing={2} sx={{ pb: 2 }}> <Paper elevation={0}>
<Grid item xs={12} sm={6}> <Stack spacing={3} sx={{p: 5}} component="div">
<Field <Typography margin={2} variant="h4" textAlign="center">
as={TextField} {t("UpdateProfile.typography_edit_profile")}
name="username" </Typography>
label={t("UpdateProfile.text_field_username")} <Box
variant="outlined" sx={{
margin="normal" display: "flex",
size="small" flexDirection: "column",
disabled={true} alignItems: "center",
error={ justifyContent: "center",
props.touched.username && props.errors.username }}
? true >
: false <AvatarUpload
} user={user}
helperText={ setFieldValue={props.setFieldValue}
props.touched.username valueAvatar="expert_avatar"
? props.errors.username changeFlag="change_avatar"
: null />
} </Box>
sx={{ <Grid container spacing={2} sx={{pb: 2}}>
width: "100%", <Grid item xs={12} sm={6}>
}} <Field
/> as={TextField}
</Grid> name="username"
<Grid item xs={12} sm={6}> label={t("UpdateProfile.text_field_username")}
<Field variant="outlined"
as={TextField} margin="normal"
name="name" size="small"
label={t("UpdateProfile.text_field_name")} disabled={true}
variant="outlined" error={
margin="normal" props.touched.username && props.errors.username
size="small" ? true
disabled={true} : false
error={ }
props.touched.name && props.errors.name helperText={
? true props.touched.username
: false ? props.errors.username
} : null
helperText={ }
props.touched.name ? props.errors.name : null sx={{
} width: "100%",
sx={{ }}
width: "100%", />
}} </Grid>
/> <Grid item xs={12} sm={6}>
</Grid> <Field
<Grid item xs={12} sm={6}> as={TextField}
<Field name="name"
as={TextField} label={t("UpdateProfile.text_field_name")}
name="email" variant="outlined"
label={t("UpdateProfile.text_field_email")} margin="normal"
variant="outlined" size="small"
margin="normal" disabled={true}
size="small" error={
disabled={true} props.touched.name && props.errors.name
error={ ? true
props.touched.email && props.errors.email : false
? true }
: false helperText={
} props.touched.name ? props.errors.name : null
helperText={ }
props.touched.email ? props.errors.email : null sx={{
} width: "100%",
sx={{ }}
width: "100%", />
}} </Grid>
/> <Grid item xs={12} sm={6}>
</Grid> <Field
as={TextField}
name="email"
label={t("UpdateProfile.text_field_email")}
variant="outlined"
margin="normal"
size="small"
disabled={true}
error={
props.touched.email && props.errors.email
? true
: false
}
helperText={
props.touched.email ? props.errors.email : null
}
sx={{
width: "100%",
}}
/>
</Grid>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={6}>
<Field <Field
as={TextField} as={TextField}
name="province_name" name="province_name"
label={t("UpdateProfile.text_field_province_name")} label={t("UpdateProfile.text_field_province_name")}
variant="outlined" variant="outlined"
margin="normal" margin="normal"
size="small" size="small"
disabled={true} disabled={true}
error={ error={
props.touched.province_name && props.touched.province_name &&
props.errors.province_name props.errors.province_name
? true ? true
: false : false
} }
helperText={ helperText={
props.touched.province_name props.touched.province_name
? props.errors.province_name ? props.errors.province_name
: null : null
} }
sx={{ sx={{
width: "100%", width: "100%",
}} }}
/> />
</Grid> </Grid>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={6}>
<Field <Field
as={TextField} as={TextField}
name="position" name="position"
label={t("UpdateProfile.text_field_position")} label={t("UpdateProfile.text_field_position")}
variant="outlined" variant="outlined"
margin="normal" margin="normal"
size="small" size="small"
disabled={true} disabled={true}
error={ error={
props.touched.position && props.errors.position props.touched.position && props.errors.position
? true ? true
: false : false
} }
helperText={ helperText={
props.touched.position props.touched.position
? props.errors.position ? props.errors.position
: null : null
} }
sx={{ sx={{
width: "100%", width: "100%",
}} }}
/> />
</Grid> </Grid>
</Grid> </Grid>
{/* <Button {/* <Button
type="submit" type="submit"
fullWidth fullWidth
variant="contained" variant="contained"
@@ -285,15 +276,15 @@ const DashboardEditProfile = () => {
? t("SubmitButton.button_while_submit") ? t("SubmitButton.button_while_submit")
: t("SubmitButton.button_submit")} : t("SubmitButton.button_submit")}
</Button> */} </Button> */}
</Stack> </Stack>
</Paper> </Paper>
</StyledForm> </StyledForm>
)} )}
</Formik> </Formik>
</Container> </Container>
</CenterLayout> </CenterLayout>
</DashboardLayouts> </DashboardLayouts>
); );
}; };
export default DashboardEditProfile; export default DashboardEditProfile;

View File

@@ -1,7 +1,7 @@
import DashboardLayouts from "@/layouts/dashboardLayouts"; import DashboardLayouts from "@/layouts/dashboardLayouts";
const DashboardFirstComponent = () => { const DashboardFirstComponent = () => {
return <DashboardLayouts></DashboardLayouts>; return <DashboardLayouts></DashboardLayouts>;
}; };
export default DashboardFirstComponent; export default DashboardFirstComponent;

View File

@@ -1,140 +1,132 @@
import { CONFIRM_MACHINARY_OFFICE } from "@/core/data/apiRoutes"; import {CONFIRM_MACHINARY_OFFICE} from "@/core/data/apiRoutes";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useTranslations} from "next-intl";
DialogTitle, import {useState} from "react";
DialogContent, import {useFormik} from "formik";
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { useFormik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
const ConfirmForm = ({ open, handleClose, rowId, confirmData }) => { const ConfirmForm = ({open, handleClose, rowId, confirmData}) => {
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const [proposedAmount, setProposedAmount] = useState(""); const [proposedAmount, setProposedAmount] = useState("");
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
//formik //formik
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
proposed_amount: Yup.string().required(t("ConfirmDialog.amount_error")), proposed_amount: Yup.string().required(t("ConfirmDialog.amount_error")),
}); });
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
proposed_amount: "", proposed_amount: "",
description: "", description: "",
}, },
validationSchema, validationSchema,
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
formData.append("proposed_amount", proposedAmount); formData.append("proposed_amount", proposedAmount);
if (description != "") formData.append("expert_description", description); if (description != "") formData.append("expert_description", description);
handleClose(); handleClose();
confirmData(CONFIRM_MACHINARY_OFFICE, rowId, formData); confirmData(CONFIRM_MACHINARY_OFFICE, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
}; };
const handleAmountChange = (event) => { const handleAmountChange = (event) => {
if (/^\d*$/.test(event.target.value)) { if (/^\d*$/.test(event.target.value)) {
setProposedAmount(event.target.value); setProposedAmount(event.target.value);
} }
formik.handleChange(event); formik.handleChange(event);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("confirm_img", uploadedFile); formik.setFieldValue("confirm_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle> <DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("ConfirmDialog.context")}</DialogContentText> <DialogContentText>{t("ConfirmDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("ConfirmDialog.description")} placeholder={t("ConfirmDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<TextField <TextField
name="proposed_amount" name="proposed_amount"
label={t("ConfirmDialog.proposed_amount")} label={t("ConfirmDialog.proposed_amount")}
type="number" type="number"
inputProps={{ inputProps={{
min: 0, min: 0,
inputMode: "numeric", inputMode: "numeric",
pattern: "[0-9]*", pattern: "[0-9]*",
}} }}
variant="outlined" variant="outlined"
value={proposedAmount} value={proposedAmount}
onChange={handleAmountChange} onChange={handleAmountChange}
onBlur={formik.handleBlur("proposed_amount")} onBlur={formik.handleBlur("proposed_amount")}
error={ error={
formik.touched.proposed_amount && formik.touched.proposed_amount &&
Boolean(formik.errors.proposed_amount) Boolean(formik.errors.proposed_amount)
} }
helperText={ helperText={
formik.touched.proposed_amount && formik.errors.proposed_amount formik.touched.proposed_amount && formik.errors.proposed_amount
} }
sx={{ mt: 1 }} sx={{mt: 1}}
fullWidth fullWidth
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="confirm_img" fieldname="confirm_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("ConfirmDialog.button-cancel")} {t("ConfirmDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("ConfirmDialog.button-confirm")} {t("ConfirmDialog.button-confirm")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default ConfirmForm; export default ConfirmForm;

View File

@@ -1,116 +1,108 @@
import { REJECT_MACHINARY_OFFICE } from "@/core/data/apiRoutes"; import {REJECT_MACHINARY_OFFICE} from "@/core/data/apiRoutes";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useTranslations} from "next-intl";
DialogTitle, import {useFormik} from "formik";
DialogContent,
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useTranslations } from "next-intl";
import { useFormik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import { useState } from "react"; import {useState} from "react";
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
const RejectForm = ({ open, handleClose, rowId, rejectData }) => { const RejectForm = ({open, handleClose, rowId, rejectData}) => {
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
description: Yup.string().required(t("RejectDialog.description_error")), description: Yup.string().required(t("RejectDialog.description_error")),
}); });
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
description: "", description: "",
}, },
validationSchema, validationSchema,
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
formData.append("expert_description", description); formData.append("expert_description", description);
handleClose(); handleClose();
rejectData(REJECT_MACHINARY_OFFICE, rowId, formData); rejectData(REJECT_MACHINARY_OFFICE, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
formik.handleChange(event); formik.handleChange(event);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("reject_img", uploadedFile); formik.setFieldValue("reject_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("RejectDialog.reject")}</DialogTitle> <DialogTitle>{t("RejectDialog.reject")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("RejectDialog.context")}</DialogContentText> <DialogContentText>{t("RejectDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("RejectDialog.description")} placeholder={t("RejectDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
onBlur={formik.handleBlur("description")} onBlur={formik.handleBlur("description")}
error={ error={
formik.touched.description && Boolean(formik.errors.description) formik.touched.description && Boolean(formik.errors.description)
} }
helperText={formik.touched.description && formik.errors.description} helperText={formik.touched.description && formik.errors.description}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="reject_img" fieldname="reject_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("RejectDialog.button-cancel")} {t("RejectDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("RejectDialog.button-reject")} {t("RejectDialog.button-reject")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default RejectForm; export default RejectForm;

View File

@@ -1,60 +1,60 @@
import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt"; import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import ThumbDownIcon from "@mui/icons-material/ThumbDown"; import ThumbDownIcon from "@mui/icons-material/ThumbDown";
import { Box, IconButton, Tooltip } from "@mui/material"; import {Box, IconButton, Tooltip} from "@mui/material";
import { useContext } from "react"; import {useContext} from "react";
import ConfirmForm from "./Form/ConfirmForm"; import ConfirmForm from "./Form/ConfirmForm";
import RejectForm from "./Form/RejectForm"; import RejectForm from "./Form/RejectForm";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import { DataTableContext } from "@/lib/app/contexts/DatatableContext"; import {DataTableContext} from "@/lib/app/contexts/DatatableContext";
const TableRowActions = ({ row }) => { const TableRowActions = ({row}) => {
const t = useTranslations(); const t = useTranslations();
const { const {
openConfirmDialog, openConfirmDialog,
openRejectDialog, openRejectDialog,
handleOpenConfirmDialog, handleOpenConfirmDialog,
handleOpenRejectDialog, handleOpenRejectDialog,
handleCloseConfirmDialog, handleCloseConfirmDialog,
handleCloseRejectDialog, handleCloseRejectDialog,
rowId, rowId,
confirmData, confirmData,
rejectData, rejectData,
} = useContext(DataTableContext); } = useContext(DataTableContext);
return ( return (
<Box sx={{ display: "flex", flexWrap: "nowrap", gap: "8px" }}> <Box sx={{display: "flex", flexWrap: "nowrap", gap: "8px"}}>
<Tooltip title={t("ConfirmDialog.confirm")}> <Tooltip title={t("ConfirmDialog.confirm")}>
<IconButton <IconButton
color="primary" color="primary"
onClick={() => { onClick={() => {
handleOpenConfirmDialog(row); handleOpenConfirmDialog(row);
}} }}
> >
<ThumbUpAltIcon /> <ThumbUpAltIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openConfirmDialog && ( {openConfirmDialog && (
<ConfirmForm <ConfirmForm
rowId={rowId} rowId={rowId}
open={openConfirmDialog} open={openConfirmDialog}
handleClose={handleCloseConfirmDialog} handleClose={handleCloseConfirmDialog}
confirmData={confirmData} confirmData={confirmData}
/> />
)} )}
<Tooltip title={t("RejectDialog.reject")}> <Tooltip title={t("RejectDialog.reject")}>
<IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}> <IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}>
<ThumbDownIcon /> <ThumbDownIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openRejectDialog && ( {openRejectDialog && (
<RejectForm <RejectForm
rowId={rowId} rowId={rowId}
open={openRejectDialog} open={openRejectDialog}
handleClose={handleCloseRejectDialog} handleClose={handleCloseRejectDialog}
rejectData={rejectData} rejectData={rejectData}
/> />
)} )}
</Box> </Box>
); );
}; };
export default TableRowActions; export default TableRowActions;

View File

@@ -1,13 +1,13 @@
import { Stack, Tooltip } from "@mui/material"; import {Stack, Tooltip} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
function TableToolbar() { function TableToolbar() {
const t = useTranslations(); const t = useTranslations();
return ( return (
<Stack direction={"row"} spacing={2}> <Stack direction={"row"} spacing={2}>
<Tooltip title={t("add")} arrow placement="right"></Tooltip> <Tooltip title={t("add")} arrow placement="right"></Tooltip>
</Stack> </Stack>
); );
} }
export default TableToolbar; export default TableToolbar;

View File

@@ -1,244 +1,241 @@
import DataTableStructure from "@/core/components/DatatableStructure"; import DataTableStructure from "@/core/components/DatatableStructure";
import DashboardLayouts from "@/layouts/dashboardLayouts"; import DashboardLayouts from "@/layouts/dashboardLayouts";
import { Box, IconButton, Typography } from "@mui/material"; import {Box, IconButton, Typography} from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear"; import ClearIcon from "@mui/icons-material/Clear";
import { useMemo } from "react"; import {useMemo} from "react";
// import TableToolbar from "./TableTollbar"; // import TableToolbar from "./TableTollbar";
import { GET_MACHINARY_OFFICE } from "@/core/data/apiRoutes"; import {GET_MACHINARY_OFFICE} from "@/core/data/apiRoutes";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import TableRowActions from "./TableRowActions"; import TableRowActions from "./TableRowActions";
import { import {LocalizationProvider, MobileDateTimePicker,} from "@mui/x-date-pickers";
LocalizationProvider, import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali";
MobileDateTimePicker,
} from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import moment from "jalali-moment"; import moment from "jalali-moment";
import { faIR } from "@mui/x-date-pickers/locales"; import {faIR} from "@mui/x-date-pickers/locales";
function DashboardMachinaryOfficeComponent() { function DashboardMachinaryOfficeComponent() {
const t = useTranslations(); const t = useTranslations();
const columns = useMemo( const columns = useMemo(
() => [ () => [
{ {
accessorFn: (row) => row.id, accessorFn: (row) => row.id,
id: "id", id: "id",
header: t("PassengerOffice.id"), header: t("PassengerOffice.id"),
enableColumnFilter: true, enableColumnFilter: true,
datatype: "numeric", datatype: "numeric",
filterFn: "equals", filterFn: "equals",
maxSize: 100, maxSize: 100,
columnFilterModeOptions: [ columnFilterModeOptions: [
"equals", "equals",
"notEquals", "notEquals",
"contains", "contains",
"lessThan", "lessThan",
"greaterThan", "greaterThan",
"between", "between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.name,
id: "name",
header: t("MachinaryOffice.name"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.national_id,
id: "national_id",
header: t("MachinaryOffice.national_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.phone_number,
id: "phone_number",
header: t("MachinaryOffice.phone_number"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("MachinaryOffice.created_at"),
enableColumnFilter: true,
datatype: "date",
filterFn: "lessThan",
minSize: 200,
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({renderedCellValue}) => {
return <Typography variant="body2">{renderedCellValue}</Typography>;
},
Header: ({column}) => <em>{column.columnDef.header}</em>,
Filter: ({column}) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{display: "flex", alignItems: "start"}}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: {minWidth: "120px"},
variant: "standard",
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/>
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon/>
</IconButton>
</Box>
);
},
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "updated_at",
header: t("MachinaryOffice.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("MachinaryOffice.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
maxSize: 100,
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("MachinaryOffice.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("MachinaryOffice.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
], ],
Cell: ({ renderedCellValue }) => ( []
<Typography variant="body2">{renderedCellValue}</Typography> );
), return (
}, <DashboardLayouts>
{ <Box sx={{px: 3}}>
accessorFn: (row) => row.name, <DataTableStructure
id: "name", tableUrl={GET_MACHINARY_OFFICE}
header: t("MachinaryOffice.name"), columns={columns}
enableColumnFilter: true, selectableRow={false}
datatype: "text", enableCustomToolbar={true}
filterFn: "contains", // CustomToolbar={<TableToolbar />}
columnFilterModeOptions: ["contains", "equals", "notEquals"], enableLastUpdate={true}
Cell: ({ renderedCellValue }) => ( enablePinning={true}
<Typography variant="body2">{renderedCellValue}</Typography> enableDensityToggle={true}
), enableColumnFilters={true}
}, enableHiding={true}
{ enableFullScreenToggle={false}
accessorFn: (row) => row.national_id, enableGlobalFilter={false}
id: "national_id", enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
header: t("MachinaryOffice.national_id"), enableRowActions={true}
enableColumnFilter: true, renderRowActions={({row}) => <TableRowActions row={row}/>}
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.phone_number,
id: "phone_number",
header: t("MachinaryOffice.phone_number"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("MachinaryOffice.created_at"),
enableColumnFilter: true,
datatype: "date",
filterFn: "lessThan",
minSize: 200,
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({ renderedCellValue }) => {
return <Typography variant="body2">{renderedCellValue}</Typography>;
},
Header: ({ column }) => <em>{column.columnDef.header}</em>,
Filter: ({ column }) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{ display: "flex", alignItems: "start" }}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: { minWidth: "120px" },
variant: "standard",
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/> />
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon />
</IconButton>
</Box> </Box>
); </DashboardLayouts>
}, );
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "updated_at",
header: t("MachinaryOffice.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("MachinaryOffice.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
maxSize: 100,
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("MachinaryOffice.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("MachinaryOffice.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
],
[]
);
return (
<DashboardLayouts>
<Box sx={{ px: 3 }}>
<DataTableStructure
tableUrl={GET_MACHINARY_OFFICE}
columns={columns}
selectableRow={false}
enableCustomToolbar={true}
// CustomToolbar={<TableToolbar />}
enableLastUpdate={true}
enablePinning={true}
enableDensityToggle={true}
enableColumnFilters={true}
enableHiding={true}
enableFullScreenToggle={false}
enableGlobalFilter={false}
enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
enableRowActions={true}
renderRowActions={({ row }) => <TableRowActions row={row} />}
/>
</Box>
</DashboardLayouts>
);
} }
export default DashboardMachinaryOfficeComponent; export default DashboardMachinaryOfficeComponent;

View File

@@ -1,142 +1,134 @@
import { CONFIRM_PASSENGER_BOSS } from "@/core/data/apiRoutes"; import {CONFIRM_PASSENGER_BOSS} from "@/core/data/apiRoutes";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useTranslations} from "next-intl";
DialogTitle, import {useState} from "react";
DialogContent, import {useFormik} from "formik";
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { useFormik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
const ConfirmForm = ({ open, handleClose, rowId, confirmData }) => { const ConfirmForm = ({open, handleClose, rowId, confirmData}) => {
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const [proposedAmount, setProposedAmount] = useState(""); const [proposedAmount, setProposedAmount] = useState("");
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
//formik //formik
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
proposed_amount: Yup.string().required( proposed_amount: Yup.string().required(
t("ConfirmDialog.approved_amount_error") t("ConfirmDialog.approved_amount_error")
), ),
}); });
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
proposed_amount: "", proposed_amount: "",
description: "", description: "",
}, },
validationSchema, validationSchema,
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
formData.append("approved_amount", proposedAmount); formData.append("approved_amount", proposedAmount);
if (description != "") formData.append("expert_description", description); if (description != "") formData.append("expert_description", description);
handleClose(); handleClose();
confirmData(CONFIRM_PASSENGER_BOSS, rowId, formData); confirmData(CONFIRM_PASSENGER_BOSS, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
}; };
const handleAmountChange = (event) => { const handleAmountChange = (event) => {
if (/^\d*$/.test(event.target.value)) { if (/^\d*$/.test(event.target.value)) {
setProposedAmount(event.target.value); setProposedAmount(event.target.value);
} }
formik.handleChange(event); formik.handleChange(event);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("confirm_img", uploadedFile); formik.setFieldValue("confirm_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle> <DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("ConfirmDialog.context")}</DialogContentText> <DialogContentText>{t("ConfirmDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("ConfirmDialog.description")} placeholder={t("ConfirmDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<TextField <TextField
name="proposed_amount" name="proposed_amount"
label={t("ConfirmDialog.approved_amount")} label={t("ConfirmDialog.approved_amount")}
type="number" type="number"
inputProps={{ inputProps={{
min: 0, min: 0,
inputMode: "numeric", inputMode: "numeric",
pattern: "[0-9]*", pattern: "[0-9]*",
}} }}
variant="outlined" variant="outlined"
value={proposedAmount} value={proposedAmount}
onChange={handleAmountChange} onChange={handleAmountChange}
onBlur={formik.handleBlur("proposed_amount")} onBlur={formik.handleBlur("proposed_amount")}
error={ error={
formik.touched.proposed_amount && formik.touched.proposed_amount &&
Boolean(formik.errors.proposed_amount) Boolean(formik.errors.proposed_amount)
} }
helperText={ helperText={
formik.touched.proposed_amount && formik.errors.proposed_amount formik.touched.proposed_amount && formik.errors.proposed_amount
} }
sx={{ mt: 1 }} sx={{mt: 1}}
fullWidth fullWidth
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="confirm_img" fieldname="confirm_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("ConfirmDialog.button-cancel")} {t("ConfirmDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("ConfirmDialog.button-confirm")} {t("ConfirmDialog.button-confirm")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default ConfirmForm; export default ConfirmForm;

View File

@@ -1,116 +1,108 @@
import { REJECT_PASSENGER_BOSS } from "@/core/data/apiRoutes"; import {REJECT_PASSENGER_BOSS} from "@/core/data/apiRoutes";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useTranslations} from "next-intl";
DialogTitle, import {useFormik} from "formik";
DialogContent,
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useTranslations } from "next-intl";
import { useFormik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import { useState } from "react"; import {useState} from "react";
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
const RejectForm = ({ open, handleClose, rowId, rejectData }) => { const RejectForm = ({open, handleClose, rowId, rejectData}) => {
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
description: Yup.string().required(t("RejectDialog.description_error")), description: Yup.string().required(t("RejectDialog.description_error")),
}); });
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
description: "", description: "",
}, },
validationSchema, validationSchema,
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
formData.append("expert_description", description); formData.append("expert_description", description);
handleClose(); handleClose();
rejectData(REJECT_PASSENGER_BOSS, rowId, formData); rejectData(REJECT_PASSENGER_BOSS, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
formik.handleChange(event); formik.handleChange(event);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("reject_img", uploadedFile); formik.setFieldValue("reject_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("RejectDialog.reject")}</DialogTitle> <DialogTitle>{t("RejectDialog.reject")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("RejectDialog.context")}</DialogContentText> <DialogContentText>{t("RejectDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("RejectDialog.description")} placeholder={t("RejectDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
onBlur={formik.handleBlur("description")} onBlur={formik.handleBlur("description")}
error={ error={
formik.touched.description && Boolean(formik.errors.description) formik.touched.description && Boolean(formik.errors.description)
} }
helperText={formik.touched.description && formik.errors.description} helperText={formik.touched.description && formik.errors.description}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="reject_img" fieldname="reject_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("RejectDialog.button-cancel")} {t("RejectDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("RejectDialog.button-reject")} {t("RejectDialog.button-reject")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default RejectForm; export default RejectForm;

View File

@@ -1,60 +1,60 @@
import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt"; import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import ThumbDownIcon from "@mui/icons-material/ThumbDown"; import ThumbDownIcon from "@mui/icons-material/ThumbDown";
import { Box, IconButton, Tooltip } from "@mui/material"; import {Box, IconButton, Tooltip} from "@mui/material";
import { useContext } from "react"; import {useContext} from "react";
import ConfirmForm from "./Form/ConfirmForm"; import ConfirmForm from "./Form/ConfirmForm";
import RejectForm from "./Form/RejectForm"; import RejectForm from "./Form/RejectForm";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import { DataTableContext } from "@/lib/app/contexts/DatatableContext"; import {DataTableContext} from "@/lib/app/contexts/DatatableContext";
const TableRowActions = ({ row }) => { const TableRowActions = ({row}) => {
const t = useTranslations(); const t = useTranslations();
const { const {
openConfirmDialog, openConfirmDialog,
openRejectDialog, openRejectDialog,
handleOpenConfirmDialog, handleOpenConfirmDialog,
handleOpenRejectDialog, handleOpenRejectDialog,
handleCloseConfirmDialog, handleCloseConfirmDialog,
handleCloseRejectDialog, handleCloseRejectDialog,
rowId, rowId,
confirmData, confirmData,
rejectData, rejectData,
} = useContext(DataTableContext); } = useContext(DataTableContext);
return ( return (
<Box sx={{ display: "flex", flexWrap: "nowrap", gap: "8px" }}> <Box sx={{display: "flex", flexWrap: "nowrap", gap: "8px"}}>
<Tooltip title={t("ConfirmDialog.confirm")}> <Tooltip title={t("ConfirmDialog.confirm")}>
<IconButton <IconButton
color="primary" color="primary"
onClick={() => { onClick={() => {
handleOpenConfirmDialog(row); handleOpenConfirmDialog(row);
}} }}
> >
<ThumbUpAltIcon /> <ThumbUpAltIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openConfirmDialog && ( {openConfirmDialog && (
<ConfirmForm <ConfirmForm
rowId={rowId} rowId={rowId}
open={openConfirmDialog} open={openConfirmDialog}
handleClose={handleCloseConfirmDialog} handleClose={handleCloseConfirmDialog}
confirmData={confirmData} confirmData={confirmData}
/> />
)} )}
<Tooltip title={t("RejectDialog.reject")}> <Tooltip title={t("RejectDialog.reject")}>
<IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}> <IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}>
<ThumbDownIcon /> <ThumbDownIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openRejectDialog && ( {openRejectDialog && (
<RejectForm <RejectForm
rowId={rowId} rowId={rowId}
open={openRejectDialog} open={openRejectDialog}
handleClose={handleCloseRejectDialog} handleClose={handleCloseRejectDialog}
rejectData={rejectData} rejectData={rejectData}
/> />
)} )}
</Box> </Box>
); );
}; };
export default TableRowActions; export default TableRowActions;

View File

@@ -1,13 +1,13 @@
import { Stack, Tooltip } from "@mui/material"; import {Stack, Tooltip} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
function TableToolbar() { function TableToolbar() {
const t = useTranslations(); const t = useTranslations();
return ( return (
<Stack direction={"row"} spacing={2}> <Stack direction={"row"} spacing={2}>
<Tooltip title={t("add")} arrow placement="right"></Tooltip> <Tooltip title={t("add")} arrow placement="right"></Tooltip>
</Stack> </Stack>
); );
} }
export default TableToolbar; export default TableToolbar;

View File

@@ -1,245 +1,242 @@
import DataTableStructure from "@/core/components/DatatableStructure"; import DataTableStructure from "@/core/components/DatatableStructure";
import DashboardLayouts from "@/layouts/dashboardLayouts"; import DashboardLayouts from "@/layouts/dashboardLayouts";
import { Box, IconButton, Typography } from "@mui/material"; import {Box, IconButton, Typography} from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear"; import ClearIcon from "@mui/icons-material/Clear";
import { useMemo } from "react"; import {useMemo} from "react";
// import TableToolbar from "./TableTollbar"; // import TableToolbar from "./TableTollbar";
import { GET_PASSENGER_BOSS } from "@/core/data/apiRoutes"; import {GET_PASSENGER_BOSS} from "@/core/data/apiRoutes";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import TableRowActions from "./TableRowActions"; import TableRowActions from "./TableRowActions";
import { import {LocalizationProvider, MobileDateTimePicker,} from "@mui/x-date-pickers";
LocalizationProvider, import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali";
MobileDateTimePicker,
} from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import moment from "jalali-moment"; import moment from "jalali-moment";
import { faIR } from "@mui/x-date-pickers/locales"; import {faIR} from "@mui/x-date-pickers/locales";
function DashboardPassengerOfficeComponent() { function DashboardPassengerOfficeComponent() {
const t = useTranslations(); const t = useTranslations();
const columns = useMemo( const columns = useMemo(
() => [ () => [
{ {
accessorFn: (row) => row.id, accessorFn: (row) => row.id,
id: "id", id: "id",
header: t("PassengerBoss.id"), header: t("PassengerBoss.id"),
enableColumnFilter: true, enableColumnFilter: true,
datatype: "numeric", datatype: "numeric",
filterFn: "equals", filterFn: "equals",
maxSize: 100, maxSize: 100,
columnFilterModeOptions: [ columnFilterModeOptions: [
"equals", "equals",
"notEquals", "notEquals",
"contains", "contains",
"lessThan", "lessThan",
"greaterThan", "greaterThan",
"between", "between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.name,
id: "name",
header: t("PassengerBoss.name"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.national_id,
id: "national_id",
header: t("PassengerBoss.national_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.phone_number,
id: "phone_number",
header: t("PassengerBoss.phone_number"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("PassengerBoss.created_at"),
enableColumnFilter: true,
datatype: "date",
filterFn: "lessThan",
minSize: 200,
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({renderedCellValue}) => {
return <Typography variant="body2">{renderedCellValue}</Typography>;
},
Header: ({column}) => <em>{column.columnDef.header}</em>,
Filter: ({column}) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{display: "flex", alignItems: "start"}}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: {minWidth: "120px"},
variant: "standard",
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/>
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon/>
</IconButton>
</Box>
);
},
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "updated_at",
header: t("PassengerBoss.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("PassengerBoss.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
maxSize: 100,
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("PassengerBoss.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("PassengerBoss.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
], ],
Cell: ({ renderedCellValue }) => ( []
<Typography variant="body2">{renderedCellValue}</Typography> );
), return (
}, <DashboardLayouts>
{ <Box sx={{px: 3}}>
accessorFn: (row) => row.name, <DataTableStructure
id: "name", tableUrl={GET_PASSENGER_BOSS}
header: t("PassengerBoss.name"), columns={columns}
enableColumnFilter: true, selectableRow={false}
datatype: "text", enableCustomToolbar={true}
filterFn: "contains", // CustomToolbar={<TableToolbar />}
columnFilterModeOptions: ["contains", "equals", "notEquals"], enableLastUpdate={true}
Cell: ({ renderedCellValue }) => ( enablePinning={true}
<Typography variant="body2">{renderedCellValue}</Typography> enableDensityToggle={true}
), enableColumnFilters={true}
}, enableHiding={true}
{ enableFullScreenToggle={false}
accessorFn: (row) => row.national_id, enableGlobalFilter={false}
id: "national_id", enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
header: t("PassengerBoss.national_id"), enableRowActions={true}
enableColumnFilter: true, renderRowActions={({row}) => <TableRowActions row={row}/>}
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.phone_number,
id: "phone_number",
header: t("PassengerBoss.phone_number"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("PassengerBoss.created_at"),
enableColumnFilter: true,
datatype: "date",
filterFn: "lessThan",
minSize: 200,
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({ renderedCellValue }) => {
return <Typography variant="body2">{renderedCellValue}</Typography>;
},
Header: ({ column }) => <em>{column.columnDef.header}</em>,
Filter: ({ column }) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{ display: "flex", alignItems: "start" }}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: { minWidth: "120px" },
variant: "standard",
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/> />
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon />
</IconButton>
</Box> </Box>
); </DashboardLayouts>
}, );
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "updated_at",
header: t("PassengerBoss.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("PassengerBoss.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
maxSize: 100,
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("PassengerBoss.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("PassengerBoss.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
],
[]
);
return (
<DashboardLayouts>
<Box sx={{ px: 3 }}>
<DataTableStructure
tableUrl={GET_PASSENGER_BOSS}
columns={columns}
selectableRow={false}
enableCustomToolbar={true}
// CustomToolbar={<TableToolbar />}
enableLastUpdate={true}
enablePinning={true}
enableDensityToggle={true}
enableColumnFilters={true}
enableHiding={true}
enableFullScreenToggle={false}
enableGlobalFilter={false}
enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
enableRowActions={true}
renderRowActions={({ row }) => <TableRowActions row={row} />}
/>
</Box>
</DashboardLayouts>
);
} }
export default DashboardPassengerOfficeComponent; export default DashboardPassengerOfficeComponent;

View File

@@ -1,106 +1,98 @@
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import { CONFIRM_PASSENGER_OFFICE } from "@/core/data/apiRoutes"; import {CONFIRM_PASSENGER_OFFICE} from "@/core/data/apiRoutes";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useFormik} from "formik";
DialogTitle, import {useTranslations} from "next-intl";
DialogContent, import {useState} from "react";
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useFormik } from "formik";
import { useTranslations } from "next-intl";
import { useRef, useState } from "react";
const ConfirmForm = ({ open, handleClose, rowId, confirmData }) => { const ConfirmForm = ({open, handleClose, rowId, confirmData}) => {
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
description: "", description: "",
}, },
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
if (description !== "") if (description !== "")
formData.append("expert_description", description); formData.append("expert_description", description);
handleClose(); handleClose();
confirmData(CONFIRM_PASSENGER_OFFICE, rowId, formData); confirmData(CONFIRM_PASSENGER_OFFICE, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
formik.handleChange(event); formik.handleChange(event);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("confirm_img", uploadedFile); formik.setFieldValue("confirm_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle> <DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("ConfirmDialog.context")}</DialogContentText> <DialogContentText>{t("ConfirmDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("ConfirmDialog.description")} placeholder={t("ConfirmDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
onBlur={formik.handleBlur("description")} onBlur={formik.handleBlur("description")}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="confirm_img" fieldname="confirm_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("ConfirmDialog.button-cancel")} {t("ConfirmDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("ConfirmDialog.button-confirm")} {t("ConfirmDialog.button-confirm")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default ConfirmForm; export default ConfirmForm;

View File

@@ -1,114 +1,106 @@
import { REJECT_PASSENGER_OFFICE } from "@/core/data/apiRoutes"; import {REJECT_PASSENGER_OFFICE} from "@/core/data/apiRoutes";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useTranslations} from "next-intl";
DialogTitle, import {useFormik} from "formik";
DialogContent,
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useTranslations } from "next-intl";
import { useFormik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import { useState } from "react"; import {useState} from "react";
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
const RejectForm = ({ open, handleClose, rowId, rejectData }) => { const RejectForm = ({open, handleClose, rowId, rejectData}) => {
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
description: Yup.string().required(t("RejectDialog.description_error")), description: Yup.string().required(t("RejectDialog.description_error")),
}); });
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
description: "", description: "",
}, },
validationSchema, validationSchema,
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
formData.append("expert_description", description); formData.append("expert_description", description);
handleClose(); handleClose();
rejectData(REJECT_PASSENGER_OFFICE, rowId, formData); rejectData(REJECT_PASSENGER_OFFICE, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
formik.handleChange(event); formik.handleChange(event);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("reject_img", uploadedFile); formik.setFieldValue("reject_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("RejectDialog.reject")}</DialogTitle> <DialogTitle>{t("RejectDialog.reject")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("RejectDialog.context")}</DialogContentText> <DialogContentText>{t("RejectDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("RejectDialog.description")} placeholder={t("RejectDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
onBlur={formik.handleBlur("description")} onBlur={formik.handleBlur("description")}
error={ error={
formik.touched.description && Boolean(formik.errors.description) formik.touched.description && Boolean(formik.errors.description)
} }
helperText={formik.touched.description && formik.errors.description} helperText={formik.touched.description && formik.errors.description}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="reject_img" fieldname="reject_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("RejectDialog.button-cancel")} {t("RejectDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("RejectDialog.button-reject")} {t("RejectDialog.button-reject")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default RejectForm; export default RejectForm;

View File

@@ -1,60 +1,60 @@
import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt"; import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import ThumbDownIcon from "@mui/icons-material/ThumbDown"; import ThumbDownIcon from "@mui/icons-material/ThumbDown";
import { Box, IconButton, Tooltip } from "@mui/material"; import {Box, IconButton, Tooltip} from "@mui/material";
import { useContext } from "react"; import {useContext} from "react";
import ConfirmForm from "./Form/ConfirmForm"; import ConfirmForm from "./Form/ConfirmForm";
import RejectForm from "./Form/RejectForm"; import RejectForm from "./Form/RejectForm";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import { DataTableContext } from "@/lib/app/contexts/DatatableContext"; import {DataTableContext} from "@/lib/app/contexts/DatatableContext";
const TableRowActions = ({ row }) => { const TableRowActions = ({row}) => {
const t = useTranslations(); const t = useTranslations();
const { const {
openConfirmDialog, openConfirmDialog,
openRejectDialog, openRejectDialog,
handleOpenConfirmDialog, handleOpenConfirmDialog,
handleOpenRejectDialog, handleOpenRejectDialog,
handleCloseConfirmDialog, handleCloseConfirmDialog,
handleCloseRejectDialog, handleCloseRejectDialog,
rowId, rowId,
confirmData, confirmData,
rejectData, rejectData,
} = useContext(DataTableContext); } = useContext(DataTableContext);
return ( return (
<Box sx={{ display: "flex", flexWrap: "nowrap", gap: "8px" }}> <Box sx={{display: "flex", flexWrap: "nowrap", gap: "8px"}}>
<Tooltip title={t("ConfirmDialog.confirm")}> <Tooltip title={t("ConfirmDialog.confirm")}>
<IconButton <IconButton
color="primary" color="primary"
onClick={() => { onClick={() => {
handleOpenConfirmDialog(row); handleOpenConfirmDialog(row);
}} }}
> >
<ThumbUpAltIcon /> <ThumbUpAltIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openConfirmDialog && ( {openConfirmDialog && (
<ConfirmForm <ConfirmForm
rowId={rowId} rowId={rowId}
open={openConfirmDialog} open={openConfirmDialog}
handleClose={handleCloseConfirmDialog} handleClose={handleCloseConfirmDialog}
confirmData={confirmData} confirmData={confirmData}
/> />
)} )}
<Tooltip title={t("RejectDialog.reject")}> <Tooltip title={t("RejectDialog.reject")}>
<IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}> <IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}>
<ThumbDownIcon /> <ThumbDownIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openRejectDialog && ( {openRejectDialog && (
<RejectForm <RejectForm
rowId={rowId} rowId={rowId}
open={openRejectDialog} open={openRejectDialog}
handleClose={handleCloseRejectDialog} handleClose={handleCloseRejectDialog}
rejectData={rejectData} rejectData={rejectData}
/> />
)} )}
</Box> </Box>
); );
}; };
export default TableRowActions; export default TableRowActions;

View File

@@ -1,13 +1,13 @@
import { Stack, Tooltip } from "@mui/material"; import {Stack, Tooltip} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
function TableToolbar() { function TableToolbar() {
const t = useTranslations(); const t = useTranslations();
return ( return (
<Stack direction={"row"} spacing={2}> <Stack direction={"row"} spacing={2}>
<Tooltip title={t("add")} arrow placement="right"></Tooltip> <Tooltip title={t("add")} arrow placement="right"></Tooltip>
</Stack> </Stack>
); );
} }
export default TableToolbar; export default TableToolbar;

View File

@@ -1,247 +1,243 @@
import DataTableStructure from "@/core/components/DatatableStructure"; import DataTableStructure from "@/core/components/DatatableStructure";
import DashboardLayouts from "@/layouts/dashboardLayouts"; import DashboardLayouts from "@/layouts/dashboardLayouts";
import { Box, IconButton, Typography } from "@mui/material"; import {Box, IconButton, Typography} from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear"; import ClearIcon from "@mui/icons-material/Clear";
import { useMemo } from "react"; import {useMemo} from "react";
import { faIR } from "@mui/x-date-pickers/locales"; import {faIR} from "@mui/x-date-pickers/locales";
// import TableToolbar from "./TableTollbar"; // import TableToolbar from "./TableTollbar";
import { GET_PASSENGER_OFFICE } from "@/core/data/apiRoutes"; import {GET_PASSENGER_OFFICE} from "@/core/data/apiRoutes";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import TableRowActions from "./TableRowActions"; import TableRowActions from "./TableRowActions";
import { import {LocalizationProvider, MobileDateTimePicker,} from "@mui/x-date-pickers";
LocalizationProvider, import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali";
MobileDateTimePicker,
} from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import moment from "jalali-moment"; import moment from "jalali-moment";
import dayjs from "dayjs";
function DashboardPassengerOfficeComponent() { function DashboardPassengerOfficeComponent() {
const t = useTranslations(); const t = useTranslations();
const columns = useMemo( const columns = useMemo(
() => [ () => [
{ {
accessorFn: (row) => row.id, accessorFn: (row) => row.id,
id: "id", id: "id",
header: t("PassengerOffice.id"), header: t("PassengerOffice.id"),
enableColumnFilter: true, enableColumnFilter: true,
datatype: "numeric", datatype: "numeric",
maxSize: 100, maxSize: 100,
filterFn: "equals", filterFn: "equals",
columnFilterModeOptions: [ columnFilterModeOptions: [
"equals", "equals",
"notEquals", "notEquals",
"contains", "contains",
"lessThan", "lessThan",
"greaterThan", "greaterThan",
"between", "between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.name,
id: "name",
header: t("PassengerOffice.name"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.national_id,
id: "national_id",
header: t("PassengerOffice.national_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.phone_number,
id: "phone_number",
header: t("PassengerOffice.phone_number"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("PassengerOffice.created_at"),
enableColumnFilter: true,
datatype: "date",
filterFn: "lessThan",
minSize: 200,
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({renderedCellValue}) => {
return <Typography variant="body2">{renderedCellValue}</Typography>;
},
Header: ({column}) => <em>{column.columnDef.header}</em>,
Filter: ({column}) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{display: "flex", alignItems: "start"}}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: {minWidth: "120px"},
variant: "standard",
actionBar: {actions: ["accept", "today"]},
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/>
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon/>
</IconButton>
</Box>
);
},
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | YYYY/MM/DD"),
id: "updated_at",
header: t("PassengerOffice.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("PassengerOffice.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
maxSize: 100,
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("PassengerOffice.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("PassengerOffice.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
], ],
Cell: ({ renderedCellValue }) => ( []
<Typography variant="body2">{renderedCellValue}</Typography> );
), return (
}, <DashboardLayouts>
{ <Box sx={{px: 3}}>
accessorFn: (row) => row.name, <DataTableStructure
id: "name", tableUrl={GET_PASSENGER_OFFICE}
header: t("PassengerOffice.name"), columns={columns}
enableColumnFilter: true, selectableRow={false}
datatype: "text", enableCustomToolbar={true}
filterFn: "contains", // CustomToolbar={<TableToolbar />}
columnFilterModeOptions: ["contains", "equals", "notEquals"], enableLastUpdate={true}
Cell: ({ renderedCellValue }) => ( enablePinning={true}
<Typography variant="body2">{renderedCellValue}</Typography> enableDensityToggle={true}
), enableColumnFilters={true}
}, enableHiding={true}
{ enableFullScreenToggle={false}
accessorFn: (row) => row.national_id, enableGlobalFilter={false}
id: "national_id", enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
header: t("PassengerOffice.national_id"), enableRowActions={true}
enableColumnFilter: true, renderRowActions={({row}) => <TableRowActions row={row}/>}
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.phone_number,
id: "phone_number",
header: t("PassengerOffice.phone_number"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("PassengerOffice.created_at"),
enableColumnFilter: true,
datatype: "date",
filterFn: "lessThan",
minSize: 200,
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({ renderedCellValue }) => {
return <Typography variant="body2">{renderedCellValue}</Typography>;
},
Header: ({ column }) => <em>{column.columnDef.header}</em>,
Filter: ({ column }) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{ display: "flex", alignItems: "start" }}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: { minWidth: "120px" },
variant: "standard",
actionBar: { actions: ["accept", "today"] },
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/> />
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon />
</IconButton>
</Box> </Box>
); </DashboardLayouts>
}, );
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | YYYY/MM/DD"),
id: "updated_at",
header: t("PassengerOffice.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("PassengerOffice.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
maxSize: 100,
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("PassengerOffice.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("PassengerOffice.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
],
[]
);
return (
<DashboardLayouts>
<Box sx={{ px: 3 }}>
<DataTableStructure
tableUrl={GET_PASSENGER_OFFICE}
columns={columns}
selectableRow={false}
enableCustomToolbar={true}
// CustomToolbar={<TableToolbar />}
enableLastUpdate={true}
enablePinning={true}
enableDensityToggle={true}
enableColumnFilters={true}
enableHiding={true}
enableFullScreenToggle={false}
enableGlobalFilter={false}
enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
enableRowActions={true}
renderRowActions={({ row }) => <TableRowActions row={row} />}
/>
</Box>
</DashboardLayouts>
);
} }
export default DashboardPassengerOfficeComponent; export default DashboardPassengerOfficeComponent;

View File

@@ -1,106 +1,98 @@
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import { CONFIRM_PROVINCE_MANAGER } from "@/core/data/apiRoutes"; import {CONFIRM_PROVINCE_MANAGER} from "@/core/data/apiRoutes";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useFormik} from "formik";
DialogTitle, import {useTranslations} from "next-intl";
DialogContent, import {useState} from "react";
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useFormik } from "formik";
import { useTranslations } from "next-intl";
import { useRef, useState } from "react";
const ConfirmForm = ({ open, handleClose, rowId, confirmData }) => { const ConfirmForm = ({open, handleClose, rowId, confirmData}) => {
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
description: "", description: "",
}, },
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
if (description !== "") if (description !== "")
formData.append("expert_description", description); formData.append("expert_description", description);
handleClose(); handleClose();
confirmData(CONFIRM_PROVINCE_MANAGER, rowId, formData); confirmData(CONFIRM_PROVINCE_MANAGER, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
formik.handleChange(event); formik.handleChange(event);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("confirm_img", uploadedFile); formik.setFieldValue("confirm_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle> <DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("ConfirmDialog.context")}</DialogContentText> <DialogContentText>{t("ConfirmDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("ConfirmDialog.description")} placeholder={t("ConfirmDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
onBlur={formik.handleBlur("description")} onBlur={formik.handleBlur("description")}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="confirm_img" fieldname="confirm_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("ConfirmDialog.button-cancel")} {t("ConfirmDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("ConfirmDialog.button-confirm")} {t("ConfirmDialog.button-confirm")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default ConfirmForm; export default ConfirmForm;

View File

@@ -1,115 +1,107 @@
import { REJECT_PROVINCE_MANAGER } from "@/core/data/apiRoutes"; import {REJECT_PROVINCE_MANAGER} from "@/core/data/apiRoutes";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useTranslations} from "next-intl";
DialogTitle, import {useFormik} from "formik";
DialogContent,
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useTranslations } from "next-intl";
import { useFormik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import { useState } from "react"; import {useState} from "react";
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
const RejectForm = ({ open, handleClose, rowId, rejectData }) => { const RejectForm = ({open, handleClose, rowId, rejectData}) => {
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
description: Yup.string().required(t("RejectDialog.description_error")), description: Yup.string().required(t("RejectDialog.description_error")),
}); });
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
description: "", description: "",
}, },
validationSchema, validationSchema,
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
formData.append("expert_description", description); formData.append("expert_description", description);
handleClose(); handleClose();
rejectData(REJECT_PROVINCE_MANAGER, rowId, formData); rejectData(REJECT_PROVINCE_MANAGER, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
formik.handleChange(event); formik.handleChange(event);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("reject_img", uploadedFile); formik.setFieldValue("reject_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("RejectDialog.reject")}</DialogTitle> <DialogTitle>{t("RejectDialog.reject")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("RejectDialog.context")}</DialogContentText> <DialogContentText>{t("RejectDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("RejectDialog.description")} placeholder={t("RejectDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
onBlur={formik.handleBlur("description")} onBlur={formik.handleBlur("description")}
error={ error={
formik.touched.description && Boolean(formik.errors.description) formik.touched.description && Boolean(formik.errors.description)
} }
helperText={formik.touched.description && formik.errors.description} helperText={formik.touched.description && formik.errors.description}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="reject_img" fieldname="reject_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("RejectDialog.button-cancel")} {t("RejectDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("RejectDialog.button-reject")} {t("RejectDialog.button-reject")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default RejectForm; export default RejectForm;

View File

@@ -1,60 +1,60 @@
import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt"; import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import ThumbDownIcon from "@mui/icons-material/ThumbDown"; import ThumbDownIcon from "@mui/icons-material/ThumbDown";
import { Box, IconButton, Tooltip } from "@mui/material"; import {Box, IconButton, Tooltip} from "@mui/material";
import { useContext } from "react"; import {useContext} from "react";
import ConfirmForm from "./Form/ConfirmForm"; import ConfirmForm from "./Form/ConfirmForm";
import RejectForm from "./Form/RejectForm"; import RejectForm from "./Form/RejectForm";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import { DataTableContext } from "@/lib/app/contexts/DatatableContext"; import {DataTableContext} from "@/lib/app/contexts/DatatableContext";
const TableRowActions = ({ row }) => { const TableRowActions = ({row}) => {
const t = useTranslations(); const t = useTranslations();
const { const {
openConfirmDialog, openConfirmDialog,
openRejectDialog, openRejectDialog,
handleOpenConfirmDialog, handleOpenConfirmDialog,
handleOpenRejectDialog, handleOpenRejectDialog,
handleCloseConfirmDialog, handleCloseConfirmDialog,
handleCloseRejectDialog, handleCloseRejectDialog,
rowId, rowId,
confirmData, confirmData,
rejectData, rejectData,
} = useContext(DataTableContext); } = useContext(DataTableContext);
return ( return (
<Box sx={{ display: "flex", flexWrap: "nowrap", gap: "8px" }}> <Box sx={{display: "flex", flexWrap: "nowrap", gap: "8px"}}>
<Tooltip title={t("ConfirmDialog.confirm")}> <Tooltip title={t("ConfirmDialog.confirm")}>
<IconButton <IconButton
color="primary" color="primary"
onClick={() => { onClick={() => {
handleOpenConfirmDialog(row); handleOpenConfirmDialog(row);
}} }}
> >
<ThumbUpAltIcon /> <ThumbUpAltIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openConfirmDialog && ( {openConfirmDialog && (
<ConfirmForm <ConfirmForm
rowId={rowId} rowId={rowId}
open={openConfirmDialog} open={openConfirmDialog}
handleClose={handleCloseConfirmDialog} handleClose={handleCloseConfirmDialog}
confirmData={confirmData} confirmData={confirmData}
/> />
)} )}
<Tooltip title={t("RejectDialog.reject")}> <Tooltip title={t("RejectDialog.reject")}>
<IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}> <IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}>
<ThumbDownIcon /> <ThumbDownIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openRejectDialog && ( {openRejectDialog && (
<RejectForm <RejectForm
rowId={rowId} rowId={rowId}
open={openRejectDialog} open={openRejectDialog}
handleClose={handleCloseRejectDialog} handleClose={handleCloseRejectDialog}
rejectData={rejectData} rejectData={rejectData}
/> />
)} )}
</Box> </Box>
); );
}; };
export default TableRowActions; export default TableRowActions;

View File

@@ -1,13 +1,13 @@
import { Stack, Tooltip } from "@mui/material"; import {Stack, Tooltip} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
function TableToolbar() { function TableToolbar() {
const t = useTranslations(); const t = useTranslations();
return ( return (
<Stack direction={"row"} spacing={2}> <Stack direction={"row"} spacing={2}>
<Tooltip title={t("add")} arrow placement="right"></Tooltip> <Tooltip title={t("add")} arrow placement="right"></Tooltip>
</Stack> </Stack>
); );
} }
export default TableToolbar; export default TableToolbar;

View File

@@ -1,245 +1,242 @@
import DataTableStructure from "@/core/components/DatatableStructure"; import DataTableStructure from "@/core/components/DatatableStructure";
import DashboardLayouts from "@/layouts/dashboardLayouts"; import DashboardLayouts from "@/layouts/dashboardLayouts";
import { Box, IconButton, Typography } from "@mui/material"; import {Box, IconButton, Typography} from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear"; import ClearIcon from "@mui/icons-material/Clear";
import { useMemo } from "react"; import {useMemo} from "react";
// import TableToolbar from "./TableTollbar"; // import TableToolbar from "./TableTollbar";
import { GET_PROVINCE_MANAGER } from "@/core/data/apiRoutes"; import {GET_PROVINCE_MANAGER} from "@/core/data/apiRoutes";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import TableRowActions from "./TableRowActions"; import TableRowActions from "./TableRowActions";
import { import {LocalizationProvider, MobileDateTimePicker,} from "@mui/x-date-pickers";
LocalizationProvider, import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali";
MobileDateTimePicker,
} from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import moment from "jalali-moment"; import moment from "jalali-moment";
import { faIR } from "@mui/x-date-pickers/locales"; import {faIR} from "@mui/x-date-pickers/locales";
function DashboardProvinceManagerComponent() { function DashboardProvinceManagerComponent() {
const t = useTranslations(); const t = useTranslations();
const columns = useMemo( const columns = useMemo(
() => [ () => [
{ {
accessorFn: (row) => row.id, accessorFn: (row) => row.id,
id: "id", id: "id",
header: t("ProvinceManager.id"), header: t("ProvinceManager.id"),
enableColumnFilter: true, enableColumnFilter: true,
datatype: "numeric", datatype: "numeric",
filterFn: "equals", filterFn: "equals",
maxSize: 100, maxSize: 100,
columnFilterModeOptions: [ columnFilterModeOptions: [
"equals", "equals",
"notEquals", "notEquals",
"contains", "contains",
"lessThan", "lessThan",
"greaterThan", "greaterThan",
"between", "between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.name,
id: "name",
header: t("ProvinceManager.name"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.national_id,
id: "national_id",
header: t("ProvinceManager.national_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.phone_number,
id: "phone_number",
header: t("ProvinceManager.phone_number"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("ProvinceManager.created_at"),
enableColumnFilter: true,
datatype: "date",
filterFn: "lessThan",
minSize: 200,
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({renderedCellValue}) => {
return <Typography variant="body2">{renderedCellValue}</Typography>;
},
Header: ({column}) => <em>{column.columnDef.header}</em>,
Filter: ({column}) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{display: "flex", alignItems: "start"}}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: {minWidth: "120px"},
variant: "standard",
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/>
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon/>
</IconButton>
</Box>
);
},
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "updated_at",
header: t("ProvinceManager.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("ProvinceManager.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
maxSize: 100,
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("ProvinceManager.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("ProvinceManager.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
], ],
Cell: ({ renderedCellValue }) => ( []
<Typography variant="body2">{renderedCellValue}</Typography> );
), return (
}, <DashboardLayouts>
{ <Box sx={{px: 3}}>
accessorFn: (row) => row.name, <DataTableStructure
id: "name", tableUrl={GET_PROVINCE_MANAGER}
header: t("ProvinceManager.name"), columns={columns}
enableColumnFilter: true, selectableRow={false}
datatype: "text", enableCustomToolbar={true}
filterFn: "contains", // CustomToolbar={<TableToolbar />}
columnFilterModeOptions: ["contains", "equals", "notEquals"], enableLastUpdate={true}
Cell: ({ renderedCellValue }) => ( enablePinning={true}
<Typography variant="body2">{renderedCellValue}</Typography> enableDensityToggle={true}
), enableColumnFilters={true}
}, enableHiding={true}
{ enableFullScreenToggle={false}
accessorFn: (row) => row.national_id, enableGlobalFilter={false}
id: "national_id", enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
header: t("ProvinceManager.national_id"), enableRowActions={true}
enableColumnFilter: true, renderRowActions={({row}) => <TableRowActions row={row}/>}
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.phone_number,
id: "phone_number",
header: t("ProvinceManager.phone_number"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("ProvinceManager.created_at"),
enableColumnFilter: true,
datatype: "date",
filterFn: "lessThan",
minSize: 200,
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({ renderedCellValue }) => {
return <Typography variant="body2">{renderedCellValue}</Typography>;
},
Header: ({ column }) => <em>{column.columnDef.header}</em>,
Filter: ({ column }) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{ display: "flex", alignItems: "start" }}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: { minWidth: "120px" },
variant: "standard",
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/> />
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon />
</IconButton>
</Box> </Box>
); </DashboardLayouts>
}, );
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "updated_at",
header: t("ProvinceManager.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("ProvinceManager.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
maxSize: 100,
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("ProvinceManager.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("ProvinceManager.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
],
[]
);
return (
<DashboardLayouts>
<Box sx={{ px: 3 }}>
<DataTableStructure
tableUrl={GET_PROVINCE_MANAGER}
columns={columns}
selectableRow={false}
enableCustomToolbar={true}
// CustomToolbar={<TableToolbar />}
enableLastUpdate={true}
enablePinning={true}
enableDensityToggle={true}
enableColumnFilters={true}
enableHiding={true}
enableFullScreenToggle={false}
enableGlobalFilter={false}
enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
enableRowActions={true}
renderRowActions={({ row }) => <TableRowActions row={row} />}
/>
</Box>
</DashboardLayouts>
);
} }
export default DashboardProvinceManagerComponent; export default DashboardProvinceManagerComponent;

View File

@@ -1,102 +1,94 @@
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import { CONFIRM_TRANSPORTATION_ASSISTANCE } from "@/core/data/apiRoutes"; import {CONFIRM_TRANSPORTATION_ASSISTANCE} from "@/core/data/apiRoutes";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useFormik} from "formik";
DialogTitle, import {useTranslations} from "next-intl";
DialogContent, import {useState} from "react";
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useFormik } from "formik";
import { useTranslations } from "next-intl";
import { useState } from "react";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
const ConfirmForm = ({ open, handleClose, rowId, confirmData }) => { const ConfirmForm = ({open, handleClose, rowId, confirmData}) => {
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
description: "", description: "",
}, },
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
if (description != "") formData.append("expert_description", description); if (description != "") formData.append("expert_description", description);
handleClose(); handleClose();
confirmData(CONFIRM_TRANSPORTATION_ASSISTANCE, rowId, formData); confirmData(CONFIRM_TRANSPORTATION_ASSISTANCE, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("confirm_img", uploadedFile); formik.setFieldValue("confirm_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle> <DialogTitle>{t("ConfirmDialog.confirm")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("ConfirmDialog.context")}</DialogContentText> <DialogContentText>{t("ConfirmDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("ConfirmDialog.description")} placeholder={t("ConfirmDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="confirm_img" fieldname="confirm_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("ConfirmDialog.button-cancel")} {t("ConfirmDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("ConfirmDialog.button-confirm")} {t("ConfirmDialog.button-confirm")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default ConfirmForm; export default ConfirmForm;

View File

@@ -1,113 +1,105 @@
import { REJECT_TRANSPORTATION_ASSISTANCE } from "@/core/data/apiRoutes"; import {REJECT_TRANSPORTATION_ASSISTANCE} from "@/core/data/apiRoutes";
import { import {Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField,} from "@mui/material";
Dialog, import {useTranslations} from "next-intl";
DialogTitle, import {useFormik} from "formik";
DialogContent,
DialogContentText,
DialogActions,
Button,
TextField,
} from "@mui/material";
import { useTranslations } from "next-intl";
import { useFormik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import { useState } from "react"; import {useState} from "react";
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import UploadFileNotification from "@/core/components/notifications/UploadFileNotification"; import UploadFileNotification from "@/core/components/notifications/UploadFileNotification";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
const RejectForm = ({ open, handleClose, rowId, rejectData }) => { const RejectForm = ({open, handleClose, rowId, rejectData}) => {
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [selectedImage, setSelectedImage] = useState(""); const [selectedImage, setSelectedImage] = useState("");
const [fileType, setfileType] = useState(null); const [fileType, setfileType] = useState(null);
const [fileName, setfileName] = useState(null); const [fileName, setfileName] = useState(null);
const [showAddIcon, setShowAddIcon] = useState(true); const [showAddIcon, setShowAddIcon] = useState(true);
const t = useTranslations(); const t = useTranslations();
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
description: Yup.string().required(t("RejectDialog.description_error")), description: Yup.string().required(t("RejectDialog.description_error")),
}); });
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
description: "", description: "",
}, },
validationSchema, validationSchema,
onSubmit: () => { onSubmit: () => {
const formData = new FormData(); const formData = new FormData();
formData.append("expert_description", description); formData.append("expert_description", description);
handleClose(); handleClose();
rejectData(REJECT_TRANSPORTATION_ASSISTANCE, rowId, formData); rejectData(REJECT_TRANSPORTATION_ASSISTANCE, rowId, formData);
}, },
}); });
const handleDescriptionChange = (event) => { const handleDescriptionChange = (event) => {
setDescription(event.target.value); setDescription(event.target.value);
formik.handleChange(event); formik.handleChange(event);
}; };
const handleUploadChange = (event) => { const handleUploadChange = (event) => {
const uploadedFile = event.target?.files?.[0]; const uploadedFile = event.target?.files?.[0];
if (uploadedFile) { if (uploadedFile) {
const maxFileSize = 2 * 1024 * 1024; const maxFileSize = 2 * 1024 * 1024;
if (uploadedFile.size > maxFileSize) { if (uploadedFile.size > maxFileSize) {
UploadFileNotification(directionApp, t); UploadFileNotification(directionApp, t);
event.target.value = ""; event.target.value = "";
return; return;
} }
const fileType = event.target?.files?.[0].type; const fileType = event.target?.files?.[0].type;
const fileName = event.target?.files?.[0].name; const fileName = event.target?.files?.[0].name;
setSelectedImage(URL.createObjectURL(uploadedFile)); setSelectedImage(URL.createObjectURL(uploadedFile));
setfileType(fileType); setfileType(fileType);
setfileName(fileName); setfileName(fileName);
formik.setFieldValue("reject_img", uploadedFile); formik.setFieldValue("reject_img", uploadedFile);
setShowAddIcon(false); setShowAddIcon(false);
} }
}; };
return ( return (
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>{t("RejectDialog.reject")}</DialogTitle> <DialogTitle>{t("RejectDialog.reject")}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{t("RejectDialog.context")}</DialogContentText> <DialogContentText>{t("RejectDialog.context")}</DialogContentText>
<TextField <TextField
name="description" name="description"
multiline multiline
rows={8} rows={8}
placeholder={t("RejectDialog.description")} placeholder={t("RejectDialog.description")}
value={description} value={description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
onBlur={formik.handleBlur("description")} onBlur={formik.handleBlur("description")}
error={ error={
formik.touched.description && Boolean(formik.errors.description) formik.touched.description && Boolean(formik.errors.description)
} }
helperText={formik.touched.description && formik.errors.description} helperText={formik.touched.description && formik.errors.description}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{mt: 1}}
/> />
<UploadSystem <UploadSystem
selectedImage={selectedImage} selectedImage={selectedImage}
handleUploadChange={handleUploadChange} // Pass the updated function directly handleUploadChange={handleUploadChange} // Pass the updated function directly
setselectedImage={setSelectedImage} setselectedImage={setSelectedImage}
setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue setFieldValue={formik.setFieldValue} // Remove props.setFieldValue, use formik.setFieldValue
fieldname="reject_img" fieldname="reject_img"
fileType={fileType} fileType={fileType}
fileName={fileName} fileName={fileName}
imageAlt={t("app_name")} imageAlt={t("app_name")}
imageSize={[250 /*width*/, 150 /*height*/]} imageSize={[250 /*width*/, 150 /*height*/]}
setShowAddIcon={setShowAddIcon} setShowAddIcon={setShowAddIcon}
showAddIcon={showAddIcon} showAddIcon={showAddIcon}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose} color="secondary" autoFocus> <Button onClick={handleClose} color="secondary" autoFocus>
{t("RejectDialog.button-cancel")} {t("RejectDialog.button-cancel")}
</Button> </Button>
<Button onClick={formik.handleSubmit} color="primary"> <Button onClick={formik.handleSubmit} color="primary">
{t("RejectDialog.button-reject")} {t("RejectDialog.button-reject")}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
}; };
export default RejectForm; export default RejectForm;

View File

@@ -1,61 +1,61 @@
import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt"; import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import ThumbDownIcon from "@mui/icons-material/ThumbDown"; import ThumbDownIcon from "@mui/icons-material/ThumbDown";
import { Box, IconButton, Tooltip } from "@mui/material"; import {Box, IconButton, Tooltip} from "@mui/material";
import { useContext } from "react"; import {useContext} from "react";
import ConfirmForm from "./Form/ConfirmForm"; import ConfirmForm from "./Form/ConfirmForm";
import RejectForm from "./Form/RejectForm"; import RejectForm from "./Form/RejectForm";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import { DataTableContext } from "@/lib/app/contexts/DatatableContext"; import {DataTableContext} from "@/lib/app/contexts/DatatableContext";
const TableRowActions = ({ row }) => { const TableRowActions = ({row}) => {
const t = useTranslations(); const t = useTranslations();
const { const {
openConfirmDialog, openConfirmDialog,
openRejectDialog, openRejectDialog,
handleOpenConfirmDialog, handleOpenConfirmDialog,
handleOpenRejectDialog, handleOpenRejectDialog,
handleCloseConfirmDialog, handleCloseConfirmDialog,
handleCloseRejectDialog, handleCloseRejectDialog,
rowId, rowId,
confirmData, confirmData,
rejectData, rejectData,
} = useContext(DataTableContext); } = useContext(DataTableContext);
return ( return (
<Box sx={{ display: "flex", flexWrap: "nowrap", gap: "8px" }}> <Box sx={{display: "flex", flexWrap: "nowrap", gap: "8px"}}>
<Tooltip title={t("ConfirmDialog.confirm")}> <Tooltip title={t("ConfirmDialog.confirm")}>
<IconButton <IconButton
color="primary" color="primary"
onClick={() => { onClick={() => {
handleOpenConfirmDialog(row); handleOpenConfirmDialog(row);
}} }}
> >
<ThumbUpAltIcon /> <ThumbUpAltIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openConfirmDialog && ( {openConfirmDialog && (
<ConfirmForm <ConfirmForm
rowId={rowId} rowId={rowId}
open={openConfirmDialog} open={openConfirmDialog}
handleClose={handleCloseConfirmDialog} handleClose={handleCloseConfirmDialog}
confirmData={confirmData} confirmData={confirmData}
/> />
)} )}
<Tooltip title={t("RejectDialog.reject")}> <Tooltip title={t("RejectDialog.reject")}>
<IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}> <IconButton color="primary" onClick={() => handleOpenRejectDialog(row)}>
<ThumbDownIcon /> <ThumbDownIcon/>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{openRejectDialog && ( {openRejectDialog && (
<RejectForm <RejectForm
rowId={rowId} rowId={rowId}
open={openRejectDialog} open={openRejectDialog}
handleClose={handleCloseRejectDialog} handleClose={handleCloseRejectDialog}
rejectData={rejectData} rejectData={rejectData}
/> />
)} )}
</Box> </Box>
); );
}; };
export default TableRowActions; export default TableRowActions;

View File

@@ -1,13 +1,13 @@
import { Stack, Tooltip } from "@mui/material"; import {Stack, Tooltip} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
function TableToolbar() { function TableToolbar() {
const t = useTranslations(); const t = useTranslations();
return ( return (
<Stack direction={"row"} spacing={2}> <Stack direction={"row"} spacing={2}>
<Tooltip title={t("add")} arrow placement="right"></Tooltip> <Tooltip title={t("add")} arrow placement="right"></Tooltip>
</Stack> </Stack>
); );
} }
export default TableToolbar; export default TableToolbar;

View File

@@ -1,245 +1,242 @@
import DataTableStructure from "@/core/components/DatatableStructure"; import DataTableStructure from "@/core/components/DatatableStructure";
import DashboardLayouts from "@/layouts/dashboardLayouts"; import DashboardLayouts from "@/layouts/dashboardLayouts";
import { Box, IconButton, Typography } from "@mui/material"; import {Box, IconButton, Typography} from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear"; import ClearIcon from "@mui/icons-material/Clear";
import { useMemo } from "react"; import {useMemo} from "react";
// import TableToolbar from "./TableTollbar"; // import TableToolbar from "./TableTollbar";
import { GET_TRANSPORTATION_ASSISTANCE } from "@/core/data/apiRoutes"; import {GET_TRANSPORTATION_ASSISTANCE} from "@/core/data/apiRoutes";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import TableRowActions from "./TableRowActions"; import TableRowActions from "./TableRowActions";
import { import {LocalizationProvider, MobileDateTimePicker,} from "@mui/x-date-pickers";
LocalizationProvider, import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali";
MobileDateTimePicker, import {faIR} from "@mui/x-date-pickers/locales";
} from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import { faIR } from "@mui/x-date-pickers/locales";
import moment from "jalali-moment"; import moment from "jalali-moment";
function DashboardTransportationAssistanceComponent() { function DashboardTransportationAssistanceComponent() {
const t = useTranslations(); const t = useTranslations();
const columns = useMemo( const columns = useMemo(
() => [ () => [
{ {
accessorFn: (row) => row.id, accessorFn: (row) => row.id,
id: "id", id: "id",
header: t("TransportationAssistance.id"), header: t("TransportationAssistance.id"),
enableColumnFilter: true, enableColumnFilter: true,
datatype: "numeric", datatype: "numeric",
filterFn: "equals", filterFn: "equals",
maxSize: 100, maxSize: 100,
columnFilterModeOptions: [ columnFilterModeOptions: [
"equals", "equals",
"notEquals", "notEquals",
"contains", "contains",
"lessThan", "lessThan",
"greaterThan", "greaterThan",
"between", "between",
], ],
Cell: ({ renderedCellValue }) => ( Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography> <Typography variant="body2">{renderedCellValue}</Typography>
), ),
}, },
{ {
accessorFn: (row) => row.name, accessorFn: (row) => row.name,
id: "name", id: "name",
header: t("TransportationAssistance.name"), header: t("TransportationAssistance.name"),
enableColumnFilter: true, enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => ( Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography> <Typography variant="body2">{renderedCellValue}</Typography>
), ),
}, },
{ {
accessorFn: (row) => row.national_id, accessorFn: (row) => row.national_id,
id: "national_id", id: "national_id",
header: t("TransportationAssistance.national_id"), header: t("TransportationAssistance.national_id"),
enableColumnFilter: true, enableColumnFilter: true,
datatype: "numeric", datatype: "numeric",
filterFn: "equals", filterFn: "equals",
columnFilterModeOptions: [ columnFilterModeOptions: [
"equals", "equals",
"notEquals", "notEquals",
"contains", "contains",
"lessThan", "lessThan",
"greaterThan", "greaterThan",
"between", "between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.phone_number,
id: "phone_number",
header: t("TransportationAssistance.phone_number"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("TransportationAssistance.created_at"),
enableColumnFilter: true,
datatype: "date",
minSize: 200,
filterFn: "lessThan",
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
Header: ({column}) => <em>{column.columnDef.header}</em>,
Filter: ({column}) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{display: "flex", alignItems: "start"}}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: {minWidth: "120px"},
variant: "standard",
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/>
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon/>
</IconButton>
</Box>
);
},
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "updated_at",
header: t("TransportationAssistance.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("TransportationAssistance.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
maxSize: 100,
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("TransportationAssistance.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("TransportationAssistance.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({renderedCellValue}) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
], ],
Cell: ({ renderedCellValue }) => ( []
<Typography variant="body2">{renderedCellValue}</Typography> );
), return (
}, <DashboardLayouts>
{ <Box sx={{px: 3}}>
accessorFn: (row) => row.phone_number, <DataTableStructure
id: "phone_number", tableUrl={GET_TRANSPORTATION_ASSISTANCE}
header: t("TransportationAssistance.phone_number"), columns={columns}
enableColumnFilter: true, selectableRow={false}
datatype: "numeric", enableCustomToolbar={true}
filterFn: "equals", // CustomToolbar={<TableToolbar />}
columnFilterModeOptions: [ enableLastUpdate={true}
"equals", enablePinning={true}
"notEquals", enableDensityToggle={true}
"contains", enableColumnFilters={true}
"lessThan", enableHiding={true}
"greaterThan", enableFullScreenToggle={false}
"between", enableGlobalFilter={false}
], enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
Cell: ({ renderedCellValue }) => ( enableRowActions={true}
<Typography variant="body2">{renderedCellValue}</Typography> renderRowActions={({row}) => <TableRowActions row={row}/>}
),
},
{
accessorFn: (row) =>
moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "created_at",
header: t("TransportationAssistance.created_at"),
enableColumnFilter: true,
datatype: "date",
minSize: 200,
filterFn: "lessThan",
columnFilterModeOptions: ["lessThan", "greaterThan"],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
Header: ({ column }) => <em>{column.columnDef.header}</em>,
Filter: ({ column }) => {
const filterFnValue = column.columnDef._filterFn;
return (
<Box sx={{ display: "flex", alignItems: "start" }}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps
.localeText
}
>
<MobileDateTimePicker
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
column.setFilterValue(formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: `${t("filter_mode")}: ${t(filterFnValue)}`,
sx: { minWidth: "120px" },
variant: "standard",
},
}}
value={
column.getFilterValue()
? new Date(column.getFilterValue())
: null
}
/> />
</LocalizationProvider>
<IconButton
size="small"
onClick={() => {
column.setFilterValue(null);
}}
sx={{
color: column.getFilterValue()
? "rgba(0, 0, 0, 0.54)"
: "#bfbfbf",
}}
>
<ClearIcon />
</IconButton>
</Box> </Box>
); </DashboardLayouts>
}, );
},
{
accessorFn: (row) =>
moment(row.updated_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
id: "updated_at",
header: t("TransportationAssistance.updated_at"),
enableColumnFilter: false,
datatype: "numeric",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.navgan_id,
id: "navgan_id",
header: t("TransportationAssistance.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
maxSize: 100,
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("TransportationAssistance.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
{
accessorFn: (row) => row.state_name,
id: "state_id",
header: t("TransportationAssistance.state_name"),
enableColumnFilter: false,
datatype: "numeric",
minSize: 300,
// filterFn: "equals",
// filterSelectOptions: [
// ],
// filterVariant: "select",
Cell: ({ renderedCellValue }) => (
<Typography variant="body2">{renderedCellValue}</Typography>
),
},
],
[]
);
return (
<DashboardLayouts>
<Box sx={{ px: 3 }}>
<DataTableStructure
tableUrl={GET_TRANSPORTATION_ASSISTANCE}
columns={columns}
selectableRow={false}
enableCustomToolbar={true}
// CustomToolbar={<TableToolbar />}
enableLastUpdate={true}
enablePinning={true}
enableDensityToggle={true}
enableColumnFilters={true}
enableHiding={true}
enableFullScreenToggle={false}
enableGlobalFilter={false}
enableColumnResizing={false} // if you want true this you shold change renderRowActions props ** see https://www.material-react-table.com/docs/guides/row-actions
enableRowActions={true}
renderRowActions={({ row }) => <TableRowActions row={row} />}
/>
</Box>
</DashboardLayouts>
);
} }
export default DashboardTransportationAssistanceComponent; export default DashboardTransportationAssistanceComponent;

View File

@@ -1,53 +1,53 @@
import dahsboardImage from "&/images/dashboard.svg"; import dahsboardImage from "&/images/dashboard.svg";
import { NextLinkComposed } from "@/core/components/LinkRouting"; import {NextLinkComposed} from "@/core/components/LinkRouting";
import StyledImage from "@/core/components/StyledImage"; import StyledImage from "@/core/components/StyledImage";
import CenterLayout from "@/layouts/CenterLayout"; import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout"; import FullPageLayout from "@/layouts/FullPageLayout";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
import { Button, Typography } from "@mui/material"; import {Button, Typography} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
const FirstComponent = () => { const FirstComponent = () => {
const t = useTranslations(); const t = useTranslations();
const { isAuth } = useUser(); const {isAuth} = useUser();
return ( return (
<FullPageLayout sx={{ p: 1 }}> <FullPageLayout sx={{p: 1}}>
<CenterLayout spacing={3}> <CenterLayout spacing={3}>
<StyledImage <StyledImage
src={dahsboardImage} src={dahsboardImage}
alt={t("app_name")} alt={t("app_name")}
width={300} width={300}
height={200} height={200}
/> />
<Typography variant="h5" sx={{ textAlign: "center" }}> <Typography variant="h5" sx={{textAlign: "center"}}>
{t("app_name")} {t("app_name")}
</Typography> </Typography>
{isAuth ? ( {isAuth ? (
<Button <Button
variant="outlined" variant="outlined"
component={NextLinkComposed} component={NextLinkComposed}
to={{ to={{
pathname: "/dashboard", pathname: "/dashboard",
}} }}
> >
{t("dashboard")} {t("dashboard")}
</Button> </Button>
) : ( ) : (
<Button <Button
sx={{ mx: 2 }} sx={{mx: 2}}
variant="contained" variant="contained"
component={NextLinkComposed} component={NextLinkComposed}
to={{ to={{
pathname: "/login-expert", pathname: "/login-expert",
}} }}
> >
{t("login_expert")} {t("login_expert")}
</Button> </Button>
)} )}
</CenterLayout> </CenterLayout>
</FullPageLayout> </FullPageLayout>
); );
}; };
export default FirstComponent; export default FirstComponent;

View File

@@ -2,158 +2,149 @@ import LinkRouting from "@/core/components/LinkRouting";
// import Notifications from "@/core/components/notifications"; // import Notifications from "@/core/components/notifications";
import PasswordField from "@/core/components/PasswordField"; import PasswordField from "@/core/components/PasswordField";
import StyledForm from "@/core/components/StyledForm"; import StyledForm from "@/core/components/StyledForm";
import { GET_USER_TOKEN } from "@/core/data/apiRoutes"; import {GET_USER_TOKEN} from "@/core/data/apiRoutes";
import CenterLayout from "@/layouts/CenterLayout"; import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout"; import FullPageLayout from "@/layouts/FullPageLayout";
import useDirection from "@/lib/app/hooks/useDirection";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
import LoginIcon from "@mui/icons-material/Login"; import LoginIcon from "@mui/icons-material/Login";
import { import {Box, Button, Container, Paper, Stack, TextField, Typography,} from "@mui/material";
Box,
Button,
Container,
Paper,
Stack,
TextField,
Typography,
} from "@mui/material";
import axios from "axios"; import axios from "axios";
import { Field, Formik } from "formik"; import {Field, Formik} from "formik";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import Image from "next/image"; import Image from "next/image";
import { useSearchParams } from "next/navigation"; import {useSearchParams} from "next/navigation";
import * as Yup from "yup"; import * as Yup from "yup";
const LoginComponent = () => { const LoginComponent = () => {
const t = useTranslations(); const t = useTranslations();
// const { directionApp } = useDirection(); // should delete because we dont have direction anymore // const { directionApp } = useDirection(); // should delete because we dont have direction anymore
const { setToken } = useUser(); // pass token to set token const {setToken} = useUser(); // pass token to set token
// gettin url query // gettin url query
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const backUrlDecodedPath = searchParams.get("back_url"); const backUrlDecodedPath = searchParams.get("back_url");
//formik properties //formik properties
const handleSubmit = async (values, props) => { const handleSubmit = async (values, props) => {
await axios await axios
.post(GET_USER_TOKEN, { .post(GET_USER_TOKEN, {
username: values.username, username: values.username,
password: values.password, password: values.password,
}) })
.then(function (response) { .then(function (response) {
setToken(response.data.token); setToken(response.data.token);
}) })
.catch(function (error) { .catch(function (error) {
// Notifications(directionApp, error.response, t); // Notifications(directionApp, error.response, t);
props.setSubmitting(false); props.setSubmitting(false);
}); });
}; };
const initialValues = { const initialValues = {
username: "", username: "",
password: "", password: "",
}; };
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
username: Yup.string().required(t("LoginPage.error_message_required")), username: Yup.string().required(t("LoginPage.error_message_required")),
password: Yup.string().required(t("LoginPage.error_message_required")), password: Yup.string().required(t("LoginPage.error_message_required")),
}); });
return ( return (
<FullPageLayout sx={{ p: 1 }}> <FullPageLayout sx={{p: 1}}>
<CenterLayout> <CenterLayout>
<Container maxWidth="sm"> <Container maxWidth="sm">
<Paper elevation={0}> <Paper elevation={0}>
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
onSubmit={handleSubmit} onSubmit={handleSubmit}
validationSchema={validationSchema} validationSchema={validationSchema}
>
{(props) => (
<Stack spacing={2} sx={{ p: 2 }}>
<Box
sx={{ position: "relative", width: "100%", height: 200 }}
>
<Image fill src="/images/login.svg" alt={t("app_name")} />
</Box>
<Typography margin={2} variant="h4" textAlign="center">
{t("login_expert")}
</Typography>
<StyledForm sx={{ width: "100%" }}>
<Stack spacing={3} sx={{ p: 2 }}>
<Field
as={TextField}
name="username"
variant="outlined"
label={t("LoginPage.text_field_user_name")}
placeholder={t(
"LoginPage.text_field_enter_your_username"
)}
type={"text"}
error={
props.touched.username && props.errors.username
? true
: false
}
fullWidth
helperText={
props.touched.username ? props.errors.username : null
}
/>
<PasswordField
name="password"
label={t("LoginPage.text_field_password")}
error={
props.touched.password && props.errors.password
? true
: false
}
helperText={
props.touched.password ? props.errors.password : null
}
placeholder={t(
"LoginPage.text_field_enter_your_password"
)}
/>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<Button
type="submit"
variant="contained"
fullWidth
size="medium"
endIcon={<LoginIcon />}
disabled={props.isSubmitting ? true : false}
> >
{t("LoginPage.button_submit")} {(props) => (
</Button> <Stack spacing={2} sx={{p: 2}}>
</Box> <Box
</Stack> sx={{position: "relative", width: "100%", height: 200}}
</StyledForm> >
</Stack> <Image fill src="/images/login.svg" alt={t("app_name")}/>
)} </Box>
</Formik> <Typography margin={2} variant="h4" textAlign="center">
</Paper> {t("login_expert")}
</Container> </Typography>
</CenterLayout> <StyledForm sx={{width: "100%"}}>
<Stack direction="row" alignItems="center" justifyContent="center"> <Stack spacing={3} sx={{p: 2}}>
<LinkRouting <Field
sx={{ margin: 2 }} as={TextField}
href={ name="username"
backUrlDecodedPath ? decodeURIComponent(backUrlDecodedPath) : "/" variant="outlined"
} label={t("LoginPage.text_field_user_name")}
> placeholder={t(
{t("LoginPage.link_routing_back_to")}{" "} "LoginPage.text_field_enter_your_username"
{backUrlDecodedPath )}
? t("LoginPage.link_routing_previuos_page") type={"text"}
: t("LoginPage.link_routing_main_page")} error={
</LinkRouting> props.touched.username && props.errors.username
</Stack> ? true
</FullPageLayout> : false
); }
fullWidth
helperText={
props.touched.username ? props.errors.username : null
}
/>
<PasswordField
name="password"
label={t("LoginPage.text_field_password")}
error={
props.touched.password && props.errors.password
? true
: false
}
helperText={
props.touched.password ? props.errors.password : null
}
placeholder={t(
"LoginPage.text_field_enter_your_password"
)}
/>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<Button
type="submit"
variant="contained"
fullWidth
size="medium"
endIcon={<LoginIcon/>}
disabled={props.isSubmitting ? true : false}
>
{t("LoginPage.button_submit")}
</Button>
</Box>
</Stack>
</StyledForm>
</Stack>
)}
</Formik>
</Paper>
</Container>
</CenterLayout>
<Stack direction="row" alignItems="center" justifyContent="center">
<LinkRouting
sx={{margin: 2}}
href={
backUrlDecodedPath ? decodeURIComponent(backUrlDecodedPath) : "/"
}
>
{t("LoginPage.link_routing_back_to")}{" "}
{backUrlDecodedPath
? t("LoginPage.link_routing_previuos_page")
: t("LoginPage.link_routing_main_page")}
</LinkRouting>
</Stack>
</FullPageLayout>
);
}; };
export default LoginComponent; export default LoginComponent;

View File

@@ -1,128 +1,128 @@
import { Avatar, Box, TextField } from "@mui/material"; import {Avatar, Box, TextField} from "@mui/material";
import { useState } from "react"; import {useState} from "react";
import DeleteIcon from "@mui/icons-material/Delete"; import DeleteIcon from "@mui/icons-material/Delete";
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
const AvatarUpload = ({ user, setFieldValue, valueAvatar, changeFlag }) => { const AvatarUpload = ({user, setFieldValue, valueAvatar, changeFlag}) => {
const [selectedImage, setSelectedImage] = useState(user.expert_avatar); const [selectedImage, setSelectedImage] = useState(user.expert_avatar);
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
const handleImageChange = (event) => { const handleImageChange = (event) => {
const newImage = event.target?.files?.[0]; const newImage = event.target?.files?.[0];
if (newImage) { if (newImage) {
setSelectedImage(URL.createObjectURL(newImage)); setSelectedImage(URL.createObjectURL(newImage));
setFieldValue(valueAvatar, newImage); setFieldValue(valueAvatar, newImage);
setFieldValue(changeFlag, true); setFieldValue(changeFlag, true);
} else { } else {
setSelectedImage(""); setSelectedImage("");
setFieldValue(valueAvatar, null); setFieldValue(valueAvatar, null);
} }
}; };
const handleDeleteImage = () => { const handleDeleteImage = () => {
setSelectedImage(""); setSelectedImage("");
setFieldValue(valueAvatar, null); setFieldValue(valueAvatar, null);
setFieldValue(changeFlag, true); setFieldValue(changeFlag, true);
}; };
const handleMouseEnter = () => { const handleMouseEnter = () => {
setIsHovered(true); setIsHovered(true);
}; };
const handleMouseLeave = () => { const handleMouseLeave = () => {
setIsHovered(false); setIsHovered(false);
}; };
return ( return (
<Box
sx={{
display: "inline-block",
position: "relative",
}}
>
<Box
component="label"
htmlFor="avatar-upload"
sx={{
display: "inline-block",
width: "fit-content",
height: "fit-content",
cursor: "pointer",
}}
>
<Box <Box
component="div"
className={`avatar-container ${isHovered ? "hovered" : ""}`}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
sx={{
position: "relative",
width: 150,
height: 150,
borderRadius: "50%",
overflow: "hidden",
transition: "transform 0.3s ease-in-out",
}}
>
<Avatar
alt="User Avatar"
src={selectedImage}
sx={{ sx={{
width: 150, display: "inline-block",
height: 150, position: "relative",
cursor: "pointer",
position: "relative",
}} }}
/> >
{isHovered && (
<Box <Box
component="div" component="label"
className="avatar-overlay" htmlFor="avatar-upload"
sx={{ sx={{
position: "absolute", display: "inline-block",
top: 0, width: "fit-content",
left: 0, height: "fit-content",
width: "100%", cursor: "pointer",
height: "100%", }}
backgroundColor: "rgba(0, 0, 0, 0.6)",
display: "flex",
alignItems: "center",
justifyContent: "center",
transition: "transform 0.3s ease-in-out",
transform: `scale(${isHovered ? 1 : 0})`,
}}
> >
{selectedImage ? ( <Box
<DeleteIcon component="div"
sx={{ className={`avatar-container ${isHovered ? "hovered" : ""}`}
color: "#fff", onMouseEnter={handleMouseEnter}
width: 35, onMouseLeave={handleMouseLeave}
height: 35, sx={{
}} position: "relative",
onClick={handleDeleteImage} width: 150,
/> height: 150,
) : ( borderRadius: "50%",
<AddIcon overflow: "hidden",
sx={{ transition: "transform 0.3s ease-in-out",
color: "#fff", }}
width: 35, >
height: 35, <Avatar
}} alt="User Avatar"
/> src={selectedImage}
)} sx={{
width: 150,
height: 150,
cursor: "pointer",
position: "relative",
}}
/>
{isHovered && (
<Box
component="div"
className="avatar-overlay"
sx={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
backgroundColor: "rgba(0, 0, 0, 0.6)",
display: "flex",
alignItems: "center",
justifyContent: "center",
transition: "transform 0.3s ease-in-out",
transform: `scale(${isHovered ? 1 : 0})`,
}}
>
{selectedImage ? (
<DeleteIcon
sx={{
color: "#fff",
width: 35,
height: 35,
}}
onClick={handleDeleteImage}
/>
) : (
<AddIcon
sx={{
color: "#fff",
width: 35,
height: 35,
}}
/>
)}
</Box>
)}
</Box>
</Box> </Box>
)} <TextField
id="avatar-upload"
type="file"
accept="image/*"
sx={{display: "none"}}
onChange={handleImageChange}
/>
</Box> </Box>
</Box> );
<TextField
id="avatar-upload"
type="file"
accept="image/*"
sx={{ display: "none" }}
onChange={handleImageChange}
/>
</Box>
);
}; };
export default AvatarUpload; export default AvatarUpload;

View File

@@ -1,174 +1,174 @@
import { ReloadDataTableContext } from "@/lib/app/contexts/ReloadDatatableContext"; import {ReloadDataTableContext} from "@/lib/app/contexts/ReloadDatatableContext";
import useLanguage from "@/lib/app/hooks/useLanguage"; import useLanguage from "@/lib/app/hooks/useLanguage";
import { Typography } from "@mui/material"; import {Typography} from "@mui/material";
import axios from "axios"; import axios from "axios";
import MaterialReactTable from "material-react-table"; import MaterialReactTable from "material-react-table";
import moment from "moment-jalaali"; import moment from "moment-jalaali";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import { useContext, useEffect, useMemo, useState } from "react"; import {useContext, useEffect, useMemo, useState} from "react";
import useSWR from "swr"; import useSWR from "swr";
const DataTable = (props) => { const DataTable = (props) => {
const { reloadDataTable, setReloadDataTable } = useContext( const {reloadDataTable, setReloadDataTable} = useContext(
ReloadDataTableContext ReloadDataTableContext
);
const fetcher = (...args) => {
return axios
.get(args, {
headers: {
Authorization: `Bearer ${props.token}`,
},
})
.then((res) => {
setRowCount(res.data.meta.totalRowCount);
return res.data.data;
});
};
const t = useTranslations();
const { languageApp, languageList } = useLanguage();
const [columnFilters, setColumnFilters] = useState([]);
const [columnFilterFns, setColumnFilterFns] = useState(() => {
let output = {};
const list = props.columns.map((item) =>
item.enableColumnFilter ? { [item.id]: item.filterFn } : { [item.id]: "" }
); );
for (var key in list) {
var nestedObj = list[key];
for (var nestedKey in nestedObj) {
output[nestedKey] = nestedObj[nestedKey];
}
}
return output;
});
const [sorting, setSorting] = useState([]);
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
const [rowCount, setRowCount] = useState(0);
const [updateTime, setupdateTime] = useState(
moment().format("HH:mm | jYYYY/jM/jD")
);
const tableLocalization = useMemo( const fetcher = (...args) => {
() => return axios
languageList.find((item) => item.key == languageApp).tableLocalization, .get(args, {
[languageApp, languageList] headers: {
); Authorization: `Bearer ${props.token}`,
},
const fetchUrl = useMemo(() => { })
const url = new URL(props.tableUrl /* send your api */); .then((res) => {
url.searchParams.set( setRowCount(res.data.meta.totalRowCount);
"start", return res.data.data;
`${pagination.pageIndex * pagination.pageSize}` });
); };
const filters = columnFilters.map((filter) => { const t = useTranslations();
let datatype; const {languageApp, languageList} = useLanguage();
for (const i in props.columns) { const [columnFilters, setColumnFilters] = useState([]);
if (props.columns[i].id == filter.id) { const [columnFilterFns, setColumnFilterFns] = useState(() => {
datatype = props.columns[i].datatype; let output = {};
const list = props.columns.map((item) =>
item.enableColumnFilter ? {[item.id]: item.filterFn} : {[item.id]: ""}
);
for (var key in list) {
var nestedObj = list[key];
for (var nestedKey in nestedObj) {
output[nestedKey] = nestedObj[nestedKey];
}
} }
} return output;
return {
...filter,
fn: columnFilterFns[filter.id],
datatype: datatype,
};
}); });
url.searchParams.set("size", pagination.pageSize); const [sorting, setSorting] = useState([]);
url.searchParams.set("filters", JSON.stringify(filters ?? [])); const [pagination, setPagination] = useState({pageIndex: 0, pageSize: 10});
url.searchParams.set("sorting", JSON.stringify(sorting ?? [])); const [rowCount, setRowCount] = useState(0);
return url; const [updateTime, setupdateTime] = useState(
}, [ moment().format("HH:mm | jYYYY/jM/jD")
props.tableUrl, );
columnFilters,
columnFilterFns,
pagination,
sorting,
props.columns,
]);
const { data, isLoading, isValidating, mutate } = useSWR(fetchUrl, fetcher, {
revalidateOnFocus: false,
});
useEffect(() => { const tableLocalization = useMemo(
if (reloadDataTable) { () =>
mutate(fetchUrl).then(() => { languageList.find((item) => item.key == languageApp).tableLocalization,
setReloadDataTable(false); // Set reloadDataTable to false after mutation is complete [languageApp, languageList]
}); );
}
}, [reloadDataTable, setReloadDataTable]);
useEffect(() => { const fetchUrl = useMemo(() => {
setupdateTime(moment().format("HH:mm | jYYYY/jM/jD")); const url = new URL(props.tableUrl /* send your api */);
}, [isValidating, languageApp]); url.searchParams.set(
"start",
return ( `${pagination.pageIndex * pagination.pageSize}`
<MaterialReactTable );
localization={tableLocalization} const filters = columnFilters.map((filter) => {
columns={props.columns} /* columns you send */ let datatype;
data={data ?? []} for (const i in props.columns) {
manualFiltering if (props.columns[i].id == filter.id) {
manualPagination datatype = props.columns[i].datatype;
manualSorting }
enableRowSelection={props.selectableRow} /* send condition */ }
enablePinning={props.enablePinning} /* send condition */ return {
enableColumnFilters={props.enableColumnFilters} /* send condition */ ...filter,
enableDensityToggle={props.enableDensityToggle} /* send condition */ fn: columnFilterFns[filter.id],
enableHiding={props.enableHiding} /* send condition */ datatype: datatype,
enableFullScreenToggle={props.enableFullScreenToggle} /* send condition */ };
enableColumnResizing={props.enableColumnResizing} });
muiTableHeadCellProps={{ url.searchParams.set("size", pagination.pageSize);
sx: { url.searchParams.set("filters", JSON.stringify(filters ?? []));
color: "primary.main", url.searchParams.set("sorting", JSON.stringify(sorting ?? []));
"& .Mui-TableHeadCell-Content": { justifyContent: "space-between" }, return url;
}, }, [
}} props.tableUrl,
enableColumnFilterModes
muiTablePaperProps={{ elevation: 0 }}
rowCount={rowCount}
onColumnFilterFnsChange={setColumnFilterFns}
onColumnFiltersChange={setColumnFilters}
onPaginationChange={setPagination}
onSortingChange={setSorting}
positionToolbarAlertBanner="bottom"
renderTopToolbarCustomActions={({ table }) => (
<>
{props.enableCustomToolbar /* send condition */
? props.CustomToolbar /* send component */
: ""}
</>
)}
renderBottomToolbarCustomActions={({ table }) => (
<>
{props.enableLastUpdate /* send condition */ ? (
<Typography
sx={{
color: "primary.main",
alignSelf: "center",
whiteSpace: "nowrap",
maxWidth: { xs: 100, sm: "100%" },
overflowX: "scroll",
}}
variant="caption"
>
{t("last_updated_at")}: {updateTime}
</Typography>
) : (
""
)}
</>
)}
state={{
isLoading,
columnFilters, columnFilters,
columnFilterFns, columnFilterFns,
pagination, pagination,
sorting, sorting,
}} props.columns,
positionActionsColumn={"last"} ]);
enableRowActions={props.enableRowActions} const {data, isLoading, isValidating, mutate} = useSWR(fetchUrl, fetcher, {
{...props} revalidateOnFocus: false,
/> });
);
useEffect(() => {
if (reloadDataTable) {
mutate(fetchUrl).then(() => {
setReloadDataTable(false); // Set reloadDataTable to false after mutation is complete
});
}
}, [reloadDataTable, setReloadDataTable]);
useEffect(() => {
setupdateTime(moment().format("HH:mm | jYYYY/jM/jD"));
}, [isValidating, languageApp]);
return (
<MaterialReactTable
localization={tableLocalization}
columns={props.columns} /* columns you send */
data={data ?? []}
manualFiltering
manualPagination
manualSorting
enableRowSelection={props.selectableRow} /* send condition */
enablePinning={props.enablePinning} /* send condition */
enableColumnFilters={props.enableColumnFilters} /* send condition */
enableDensityToggle={props.enableDensityToggle} /* send condition */
enableHiding={props.enableHiding} /* send condition */
enableFullScreenToggle={props.enableFullScreenToggle} /* send condition */
enableColumnResizing={props.enableColumnResizing}
muiTableHeadCellProps={{
sx: {
color: "primary.main",
"& .Mui-TableHeadCell-Content": {justifyContent: "space-between"},
},
}}
enableColumnFilterModes
muiTablePaperProps={{elevation: 0}}
rowCount={rowCount}
onColumnFilterFnsChange={setColumnFilterFns}
onColumnFiltersChange={setColumnFilters}
onPaginationChange={setPagination}
onSortingChange={setSorting}
positionToolbarAlertBanner="bottom"
renderTopToolbarCustomActions={({table}) => (
<>
{props.enableCustomToolbar /* send condition */
? props.CustomToolbar /* send component */
: ""}
</>
)}
renderBottomToolbarCustomActions={({table}) => (
<>
{props.enableLastUpdate /* send condition */ ? (
<Typography
sx={{
color: "primary.main",
alignSelf: "center",
whiteSpace: "nowrap",
maxWidth: {xs: 100, sm: "100%"},
overflowX: "scroll",
}}
variant="caption"
>
{t("last_updated_at")}: {updateTime}
</Typography>
) : (
""
)}
</>
)}
state={{
isLoading,
columnFilters,
columnFilterFns,
pagination,
sorting,
}}
positionActionsColumn={"last"}
enableRowActions={props.enableRowActions}
{...props}
/>
);
}; };
export default DataTable; export default DataTable;

View File

@@ -1,15 +1,15 @@
import { DataTableProvider } from "@/lib/app/contexts/DatatableContext"; import {DataTableProvider} from "@/lib/app/contexts/DatatableContext";
import DataTable from "./Datatable"; import DataTable from "./Datatable";
import { ReloadDataTableProvider } from "@/lib/app/contexts/ReloadDatatableContext"; import {ReloadDataTableProvider} from "@/lib/app/contexts/ReloadDatatableContext";
function DataTableStructure(props) { function DataTableStructure(props) {
return ( return (
<ReloadDataTableProvider> <ReloadDataTableProvider>
<DataTableProvider> <DataTableProvider>
<DataTable {...props} token={localStorage.getItem("_token")} /> <DataTable {...props} token={localStorage.getItem("_token")}/>
</DataTableProvider> </DataTableProvider>
</ReloadDataTableProvider> </ReloadDataTableProvider>
); );
} }
export default DataTableStructure; export default DataTableStructure;

View File

@@ -1,10 +1,11 @@
import { Typography } from "@mui/material"; import {Typography} from "@mui/material";
const FormErrorMessage = (props) => { const FormErrorMessage = (props) => {
return ( return (
<Typography variant="body2" color="error"> <Typography variant="body2" color="error">
{props.children} {props.children}
</Typography> </Typography>
); );
}; };
export default FormErrorMessage; export default FormErrorMessage;

View File

@@ -1,22 +1,22 @@
import ImageResize from "image-resize"; import ImageResize from "image-resize";
const ImageResizer = async (image) => { const ImageResizer = async (image) => {
const imageResize = new ImageResize({ const imageResize = new ImageResize({
quality: 1, quality: 1,
format: "jpg", format: "jpg",
outputType: "base64", outputType: "base64",
width: 400, width: 400,
}); });
const get = await imageResize.get(image); const get = await imageResize.get(image);
const resize = await imageResize.resize(get).then(); const resize = await imageResize.resize(get).then();
const output = await imageResize.output(resize).then(); const output = await imageResize.output(resize).then();
const avatar = await (await import("image-to-file-converter")) const avatar = await (await import("image-to-file-converter"))
.base64ToFile(output) .base64ToFile(output)
.then(); .then();
return avatar; return avatar;
}; };
export default ImageResizer; export default ImageResizer;

View File

@@ -1,116 +1,116 @@
import MuiLink from "@mui/material/Link"; import MuiLink from "@mui/material/Link";
import { styled } from "@mui/material/styles"; import {styled} from "@mui/material/styles";
import clsx from "clsx"; import clsx from "clsx";
import NextLink from "next/link"; import NextLink from "next/link";
import { useRouter } from "next/router"; import {useRouter} from "next/router";
import * as React from "react"; import * as React from "react";
// Add support for the sx prop for consistency with the other branches. // Add support for the sx prop for consistency with the other branches.
const Anchor = styled("a")({}); const Anchor = styled("a")({});
export const NextLinkComposed = React.forwardRef(function NextLinkComposed( export const NextLinkComposed = React.forwardRef(function NextLinkComposed(
props, props,
ref ref
) { ) {
const { const {
to, to,
linkAs, linkAs,
replace, replace,
scroll, scroll,
shallow, shallow,
prefetch, prefetch,
legacyBehavior = true, legacyBehavior = true,
locale, locale,
...other ...other
} = props; } = props;
return ( return (
<NextLink <NextLink
href={to} href={to}
prefetch={prefetch} prefetch={prefetch}
as={linkAs} as={linkAs}
replace={replace} replace={replace}
scroll={scroll} scroll={scroll}
shallow={shallow} shallow={shallow}
passHref passHref
locale={locale} locale={locale}
legacyBehavior={legacyBehavior} legacyBehavior={legacyBehavior}
> >
<Anchor ref={ref} {...other} /> <Anchor ref={ref} {...other} />
</NextLink> </NextLink>
); );
}); });
// A styled version of the Next.js Link component: // A styled version of the Next.js Link component:
// https://nextjs.org/docs/api-reference/next/link // https://nextjs.org/docs/api-reference/next/link
const LinkRouting = React.forwardRef(function Link(props, ref) { const LinkRouting = React.forwardRef(function Link(props, ref) {
const { const {
activeClassName = "active", activeClassName = "active",
as, as,
className: classNameProps, className: classNameProps,
href, href,
legacyBehavior, legacyBehavior,
linkAs: linkAsProp, linkAs: linkAsProp,
locale, locale,
noLinkStyle, noLinkStyle,
prefetch, prefetch,
replace, replace,
role, // Link don't have roles. role, // Link don't have roles.
scroll, scroll,
shallow, shallow,
...other ...other
} = props; } = props;
const router = useRouter(); const router = useRouter();
const pathname = typeof href === "string" ? href : href.pathname; const pathname = typeof href === "string" ? href : href.pathname;
const className = clsx(classNameProps, { const className = clsx(classNameProps, {
[activeClassName]: router.pathname === pathname && activeClassName, [activeClassName]: router.pathname === pathname && activeClassName,
}); });
const isExternal = const isExternal =
typeof href === "string" && typeof href === "string" &&
(href.indexOf("http") === 0 || href.indexOf("mailto:") === 0); (href.indexOf("http") === 0 || href.indexOf("mailto:") === 0);
if (isExternal) { if (isExternal) {
if (noLinkStyle) { if (noLinkStyle) {
return <Anchor className={className} href={href} ref={ref} {...other} />; return <Anchor className={className} href={href} ref={ref} {...other} />;
}
return <MuiLink className={className} href={href} ref={ref} {...other} />;
} }
return <MuiLink className={className} href={href} ref={ref} {...other} />; const linkAs = linkAsProp || as;
} const nextjsProps = {
to: href,
linkAs,
replace,
scroll,
shallow,
prefetch,
legacyBehavior,
locale,
};
const linkAs = linkAsProp || as; if (noLinkStyle) {
const nextjsProps = { return (
to: href, <NextLinkComposed
linkAs, className={className}
replace, ref={ref}
scroll, {...nextjsProps}
shallow, {...other}
prefetch, />
legacyBehavior, );
locale, }
};
if (noLinkStyle) {
return ( return (
<NextLinkComposed <MuiLink
className={className} component={NextLinkComposed}
ref={ref} className={className}
{...nextjsProps} ref={ref}
{...other} {...nextjsProps}
/> {...other}
/>
); );
}
return (
<MuiLink
component={NextLinkComposed}
className={className}
ref={ref}
{...nextjsProps}
{...other}
/>
);
}); });
export default LinkRouting; export default LinkRouting;

View File

@@ -1,44 +1,44 @@
import { Backdrop, Fade, styled } from "@mui/material"; import {Backdrop, Fade, styled} from "@mui/material";
import StyledImage from "./StyledImage"; import StyledImage from "./StyledImage";
const LoadingImage = styled(StyledImage)({ const LoadingImage = styled(StyledImage)({
"@keyframes load": { "@keyframes load": {
"0%": { "0%": {
// opacity: 0, // opacity: 0,
transform: "scale(1)", transform: "scale(1)",
},
"50%": {
// opacity: 1,
transform: "scale(2)",
},
"100%": {
// opacity: 0,
transform: "scale(1)",
},
}, },
"50%": { animation: "load 2s infinite",
// opacity: 1,
transform: "scale(2)",
},
"100%": {
// opacity: 0,
transform: "scale(1)",
},
},
animation: "load 2s infinite",
}); });
const LoadingHardPage = ({ children, loading }) => { const LoadingHardPage = ({children, loading}) => {
return ( return (
<> <>
<Backdrop <Backdrop
sx={{ bgcolor: "#fff", zIndex: (theme) => theme.zIndex.drawer + 1 }} sx={{bgcolor: "#fff", zIndex: (theme) => theme.zIndex.drawer + 1}}
open={loading} open={loading}
> >
<Fade in={true}> <Fade in={true}>
<LoadingImage <LoadingImage
src={"/images/loading_logo.svg"} src={"/images/loading_logo.svg"}
alt="loading marhaba" alt="loading marhaba"
priority priority
width={100} width={100}
height={100} height={100}
/> />
</Fade> </Fade>
</Backdrop> </Backdrop>
{children} {children}
</> </>
); );
}; };
export default LoadingHardPage; export default LoadingHardPage;

View File

@@ -2,21 +2,21 @@ import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout"; import FullPageLayout from "@/layouts/FullPageLayout";
import StyledImage from "./StyledImage"; import StyledImage from "./StyledImage";
const Message = ({ text, actions }) => { const Message = ({text, actions}) => {
return ( return (
<FullPageLayout sx={{ p: 1 }}> <FullPageLayout sx={{p: 1}}>
<CenterLayout spacing={3}> <CenterLayout spacing={3}>
<StyledImage <StyledImage
src={"/images/loading_logo.svg"} src={"/images/loading_logo.svg"}
alt="loading loan facilities" alt="loading loan facilities"
width={100} width={100}
height={100} height={100}
/> />
{text} {text}
{actions} {actions}
</CenterLayout> </CenterLayout>
</FullPageLayout> </FullPageLayout>
); );
}; };
export default Message; export default Message;

View File

@@ -1,35 +1,35 @@
import { LocalizationProvider, DatePicker } from "@mui/x-date-pickers"; import {DatePicker, LocalizationProvider} from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali"; import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali";
import moment from "jalali-moment"; import moment from "jalali-moment";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
export default function MuiDatePicker({ export default function MuiDatePicker({
setFieldValue, setFieldValue,
selectType, selectType,
error, error,
helperText, helperText,
}) { }) {
const t = useTranslations(); const t = useTranslations();
const handleDateChange = (date) => { const handleDateChange = (date) => {
const selectedDate = moment(date).locale("fa"); // should add .format() at the end to format the data const selectedDate = moment(date).locale("fa"); // should add .format() at the end to format the data
setFieldValue(selectType, selectedDate); setFieldValue(selectType, selectedDate);
}; };
return ( return (
<LocalizationProvider dateAdapter={AdapterDateFnsJalali}> <LocalizationProvider dateAdapter={AdapterDateFnsJalali}>
<DatePicker <DatePicker
sx={{ width: "100%" }} sx={{width: "100%"}}
label={t("MuiDatePicker.date_picker_birthday")} label={t("MuiDatePicker.date_picker_birthday")}
slotProps={{ slotProps={{
textField: { textField: {
size: "small", size: "small",
helperText: helperText ? `${helperText}` : "YYYY-MM-DD", helperText: helperText ? `${helperText}` : "YYYY-MM-DD",
error: error ? true : false, error: error ? true : false,
}, },
}} }}
onChange={handleDateChange} onChange={handleDateChange}
/> />
</LocalizationProvider> </LocalizationProvider>
); );
} }

View File

@@ -1,33 +1,33 @@
import { useState } from "react"; import {useState} from "react";
import { IconButton, InputAdornment, TextField } from "@mui/material"; import {IconButton, InputAdornment, TextField} from "@mui/material";
import { Visibility, VisibilityOff } from "@mui/icons-material"; import {Visibility, VisibilityOff} from "@mui/icons-material";
import { Field } from "formik"; import {Field} from "formik";
const PasswordField = (props) => { const PasswordField = (props) => {
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const handleClickShowPassword = () => { const handleClickShowPassword = () => {
setShowPassword(!showPassword); setShowPassword(!showPassword);
}; };
return ( return (
<Field <Field
variant="outlined" variant="outlined"
fullWidth fullWidth
as={TextField} as={TextField}
{...props} {...props}
type={showPassword ? "text" : "password"} type={showPassword ? "text" : "password"}
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<InputAdornment position="end"> <InputAdornment position="end">
<IconButton onClick={handleClickShowPassword}> <IconButton onClick={handleClickShowPassword}>
{showPassword ? <Visibility /> : <VisibilityOff />} {showPassword ? <Visibility/> : <VisibilityOff/>}
</IconButton> </IconButton>
</InputAdornment> </InputAdornment>
), ),
}} }}
/> />
); );
}; };
export default PasswordField; export default PasswordField;

View File

@@ -1,5 +1,5 @@
import { styled } from "@mui/material"; import {styled} from "@mui/material";
import { Form } from "formik"; import {Form} from "formik";
const StyledForm = styled(Form)``; const StyledForm = styled(Form)``;

View File

@@ -1,4 +1,4 @@
import { styled } from "@mui/material"; import {styled} from "@mui/material";
import Image from "next/image"; import Image from "next/image";
const StyledImage = styled(Image)``; const StyledImage = styled(Image)``;

View File

@@ -1,15 +1,15 @@
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import Head from "next/head"; import Head from "next/head";
const TitlePage = ({ text }) => { const TitlePage = ({text}) => {
const t = useTranslations(); const t = useTranslations();
return ( return (
<Head> <Head>
<title> <title>
{text ? `${t("app_short_name")} | ${t(text)}` : t("app_short_name")} {text ? `${t("app_short_name")} | ${t(text)}` : t("app_short_name")}
</title> </title>
</Head> </Head>
); );
}; };
export default TitlePage; export default TitlePage;

View File

@@ -1,176 +1,175 @@
import { Box, Button, Paper, Typography } from "@mui/material"; import {Box, Button, Paper, Typography} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import Image from "next/image";
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import DeleteForeverIcon from "@mui/icons-material/DeleteForever"; import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
import { useRef } from "react"; import {useRef} from "react";
const UploadSystem = ({ const UploadSystem = ({
selectedImage, selectedImage,
setselectedImage, setselectedImage,
handleUploadChange, handleUploadChange,
fieldname, fieldname,
setFieldValue, setFieldValue,
imageAlt, imageAlt,
imageSize, imageSize,
fileType, fileType,
fileName, fileName,
setShowAddIcon, setShowAddIcon,
showAddIcon, showAddIcon,
}) => { }) => {
const t = useTranslations(); const t = useTranslations();
const fileInputRef = useRef(null); const fileInputRef = useRef(null);
const handleClick = () => { const handleClick = () => {
fileInputRef.current.click(); fileInputRef.current.click();
}; };
const handleDeleteImage = () => { const handleDeleteImage = () => {
setselectedImage(null); setselectedImage(null);
setFieldValue(fieldname, null); setFieldValue(fieldname, null);
setShowAddIcon(true); setShowAddIcon(true);
if (fileInputRef.current) { if (fileInputRef.current) {
fileInputRef.current.value = ""; fileInputRef.current.value = "";
} }
}; };
const isDocumentFormat = (fileType) => { const isDocumentFormat = (fileType) => {
const documentFormats = [ const documentFormats = [
"application/pdf", "application/pdf",
"application/msword", "application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel", "application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
]; ];
return documentFormats.includes(fileType); return documentFormats.includes(fileType);
}; };
return ( return (
<Box sx={{ width: "100%", my: 1 }}> <Box sx={{width: "100%", my: 1}}>
{showAddIcon ? ( {showAddIcon ? (
// Show the add icon and "Upload File" text when no image is selected // Show the add icon and "Upload File" text when no image is selected
<> <>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
border: "1px solid #b3b3b3", border: "1px solid #b3b3b3",
borderRadius: "10px", borderRadius: "10px",
cursor: "pointer", cursor: "pointer",
padding: "5px", padding: "5px",
height: imageSize[1], height: imageSize[1],
}} }}
onClick={handleClick} onClick={handleClick}
> >
<AddIcon sx={{ fontSize: "2rem", color: "#a19d9d" }} /> <AddIcon sx={{fontSize: "2rem", color: "#a19d9d"}}/>
<Typography <Typography
variant="subtitle2" variant="subtitle2"
sx={{ sx={{
fontWeight: 600, fontWeight: 600,
fontSize: "1rem", fontSize: "1rem",
color: "#a19d9d", color: "#a19d9d",
mt: 1, mt: 1,
}} }}
textAlign="center" textAlign="center"
> >
{t("UploadSystem.upload_file")} {t("UploadSystem.upload_file")}
</Typography> </Typography>
</Box> </Box>
</> </>
) : ( ) : (
// Show the uploaded content along with the delete button when an image or document is selected // Show the uploaded content along with the delete button when an image or document is selected
<> <>
{fileType && fileType.startsWith("image/") ? ( {fileType && fileType.startsWith("image/") ? (
<Box <Box
width="100%" width="100%"
height={imageSize[1]} height={imageSize[1]}
sx={{ sx={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
cursor: "pointer", cursor: "pointer",
objectFit: "contain", objectFit: "contain",
border: "1px solid #b3b3b3", border: "1px solid #b3b3b3",
borderTopRightRadius: "10px", borderTopRightRadius: "10px",
borderTopLeftRadius: "10px", borderTopLeftRadius: "10px",
borderBottom: "unset", borderBottom: "unset",
padding: "5px", padding: "5px",
overflow: "hidden", overflow: "hidden",
}} }}
onClick={handleClick} onClick={handleClick}
> >
<Paper <Paper
elevation={0} elevation={0}
sx={{ sx={{
width: "100%", width: "100%",
height: "100%", height: "100%",
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
backgroundSize: "contain", backgroundSize: "contain",
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
backgroundPosition: "center", backgroundPosition: "center",
backgroundImage: `url(${selectedImage})`, backgroundImage: `url(${selectedImage})`,
}} }}
></Paper> ></Paper>
</Box> </Box>
) : ( ) : (
fileType && fileType &&
isDocumentFormat(fileType) && ( isDocumentFormat(fileType) && (
<Box <Box
sx={{ sx={{
height: imageSize[1], height: imageSize[1],
display: "flex", display: "flex",
border: "1px solid #b3b3b3", border: "1px solid #b3b3b3",
borderTopRightRadius: "10px", borderTopRightRadius: "10px",
borderTopLeftRadius: "10px", borderTopLeftRadius: "10px",
borderBottom: "unset", borderBottom: "unset",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
cursor: "pointer", cursor: "pointer",
}} }}
onClick={handleClick} onClick={handleClick}
> >
<Typography <Typography
margin={2} margin={2}
sx={{ sx={{
fontWeight: 600, fontWeight: 600,
fontSize: "1rem", fontSize: "1rem",
color: "#a19d9d", color: "#a19d9d",
}} }}
textAlign="center" textAlign="center"
> >
{fileName} {fileName}
</Typography> </Typography>
</Box> </Box>
) )
)} )}
<Button <Button
sx={{ sx={{
width: "100%", width: "100%",
}} }}
color="error" color="error"
endIcon={<DeleteForeverIcon />} endIcon={<DeleteForeverIcon/>}
variant="contained" variant="contained"
onClick={handleDeleteImage} onClick={handleDeleteImage}
> >
{t("UploadSystem.delete")} {t("UploadSystem.delete")}
</Button> </Button>
</> </>
)} )}
<input <input
type="file" type="file"
accept="image/*, .pdf, .doc, .docx, .xls, .xlsx" accept="image/*, .pdf, .doc, .docx, .xls, .xlsx"
style={{ display: "none" }} style={{display: "none"}}
onChange={handleUploadChange} onChange={handleUploadChange}
ref={fileInputRef} ref={fileInputRef}
/> />
</Box> </Box>
); );
}; };
export default UploadSystem; export default UploadSystem;

View File

@@ -1,8 +1,8 @@
import { NoSsr } from "@mui/material"; import {NoSsr} from "@mui/material";
const NoSsrHandler = ({ isBot, children }) => { const NoSsrHandler = ({isBot, children}) => {
if (isBot) return children; if (isBot) return children;
return <NoSsr>{children}</NoSsr>; return <NoSsr>{children}</NoSsr>;
}; };
export default NoSsrHandler; export default NoSsrHandler;

View File

@@ -1,40 +1,40 @@
import DangerousIcon from "@mui/icons-material/Dangerous"; import DangerousIcon from "@mui/icons-material/Dangerous";
import { Box, Typography } from "@mui/material"; import {Box, Typography} from "@mui/material";
import { toast } from "react-toastify"; import {toast} from "react-toastify";
const ErrorNotification = (directionApp, status, t, message) => { const ErrorNotification = (directionApp, status, t, message) => {
toast( toast(
() => ( () => (
<> <>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "start", alignItems: "start",
justifyContent: "start", justifyContent: "start",
}} }}
> >
<Box sx={{ display: "flex", alignItems: "center" }}> <Box sx={{display: "flex", alignItems: "center"}}>
<DangerousIcon color="error" sx={{ mr: 1.6 }} /> <DangerousIcon color="error" sx={{mr: 1.6}}/>
<Box sx={{ display: "flex", flexDirection: "column" }}> <Box sx={{display: "flex", flexDirection: "column"}}>
<Typography color="error" variant="button"> <Typography color="error" variant="button">
{t("notifications.error")} ({t("notifications.code")}: {status}) {t("notifications.error")} ({t("notifications.code")}: {status})
</Typography> </Typography>
<Typography variant="caption"> <Typography variant="caption">
{message || t("notifications.error_static_text")} {message || t("notifications.error_static_text")}
</Typography> </Typography>
</Box> </Box>
</Box> </Box>
</Box> </Box>
</> </>
), ),
{ {
position: directionApp === "ltr" ? "top-left" : "top-right", position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: false, autoClose: false,
closeOnClick: false, closeOnClick: false,
draggable: false, draggable: false,
} }
); );
}; };
export default ErrorNotification; export default ErrorNotification;

View File

@@ -1,43 +1,43 @@
import BeenhereIcon from "@mui/icons-material/Beenhere"; import BeenhereIcon from "@mui/icons-material/Beenhere";
import { Box, Typography } from "@mui/material"; import {Box, Typography} from "@mui/material";
import { toast } from "react-toastify"; import {toast} from "react-toastify";
const SuccessNotification = (directionApp, status, t) => { const SuccessNotification = (directionApp, status, t) => {
toast( toast(
() => ( () => (
<> <>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "start", alignItems: "start",
justifyContent: "start", justifyContent: "start",
}} }}
> >
<Box sx={{ display: "flex", alignItems: "center" }}> <Box sx={{display: "flex", alignItems: "center"}}>
<BeenhereIcon color="success" sx={{ mr: 1.6 }} /> <BeenhereIcon color="success" sx={{mr: 1.6}}/>
<Box sx={{ display: "flex", flexDirection: "column" }}> <Box sx={{display: "flex", flexDirection: "column"}}>
<Typography color="success.main" variant="button"> <Typography color="success.main" variant="button">
{t("notifications.success")} ({t("notifications.code")}:{" "} {t("notifications.success")} ({t("notifications.code")}:{" "}
{status}) {status})
</Typography> </Typography>
<Typography variant="caption"> <Typography variant="caption">
{t("notifications.success_static_text")} {t("notifications.success_static_text")}
</Typography> </Typography>
</Box> </Box>
</Box> </Box>
</Box> </Box>
</> </>
), ),
{ {
position: directionApp === "ltr" ? "top-left" : "top-right", position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: 3000, autoClose: 3000,
hideProgressBar: true, hideProgressBar: true,
pauseOnHover: true, pauseOnHover: true,
closeOnClick: false, closeOnClick: false,
draggable: true, draggable: true,
} }
); );
}; };
export default SuccessNotification; export default SuccessNotification;

View File

@@ -1,37 +1,37 @@
import DangerousIcon from "@mui/icons-material/Dangerous"; import DangerousIcon from "@mui/icons-material/Dangerous";
import { Box, Typography } from "@mui/material"; import {Box, Typography} from "@mui/material";
import { toast } from "react-toastify"; import {toast} from "react-toastify";
const UploadFileNotification = (directionApp, t) => { const UploadFileNotification = (directionApp, t) => {
toast( toast(
({ closeToast }) => ( ({closeToast}) => (
<> <>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "start", alignItems: "start",
justifyContent: "start", justifyContent: "start",
}} }}
> >
<Box sx={{ display: "flex", alignItems: "center" }}> <Box sx={{display: "flex", alignItems: "center"}}>
<DangerousIcon color="error" sx={{ mr: 1.6 }} /> <DangerousIcon color="error" sx={{mr: 1.6}}/>
<Box sx={{ display: "flex", flexDirection: "column" }}> <Box sx={{display: "flex", flexDirection: "column"}}>
<Typography color="error" variant="button"> <Typography color="error" variant="button">
{t("UploadSystem.uploadfile_error")} {t("UploadSystem.uploadfile_error")}
</Typography> </Typography>
</Box> </Box>
</Box> </Box>
</Box> </Box>
</> </>
), ),
{ {
position: directionApp === "ltr" ? "top-left" : "top-right", position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: false, autoClose: false,
closeOnClick: false, closeOnClick: false,
draggable: false, draggable: false,
} }
); );
}; };
export default UploadFileNotification; export default UploadFileNotification;

View File

@@ -1,41 +1,41 @@
import ReportIcon from "@mui/icons-material/Report"; import ReportIcon from "@mui/icons-material/Report";
import { Box, Typography } from "@mui/material"; import {Box, Typography} from "@mui/material";
import { toast } from "react-toastify"; import {toast} from "react-toastify";
const WarningNotification = (directionApp, status, t) => { const WarningNotification = (directionApp, status, t) => {
toast( toast(
() => ( () => (
<> <>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "start", alignItems: "start",
justifyContent: "start", justifyContent: "start",
}} }}
> >
<Box sx={{ display: "flex", alignItems: "center" }}> <Box sx={{display: "flex", alignItems: "center"}}>
<ReportIcon color="warning" sx={{ mr: 1.6 }} /> <ReportIcon color="warning" sx={{mr: 1.6}}/>
<Box sx={{ display: "flex", flexDirection: "column" }}> <Box sx={{display: "flex", flexDirection: "column"}}>
<Typography color="warning.main" variant="button"> <Typography color="warning.main" variant="button">
{t("notifications.warning")} ({t("notifications.code")}:{" "} {t("notifications.warning")} ({t("notifications.code")}:{" "}
{status}) {status})
</Typography> </Typography>
<Typography variant="caption"> <Typography variant="caption">
{t("notifications.warning_static_text")} {t("notifications.warning_static_text")}
</Typography> </Typography>
</Box> </Box>
</Box> </Box>
</Box> </Box>
</> </>
), ),
{ {
position: directionApp === "ltr" ? "top-left" : "top-right", position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: false, autoClose: false,
closeOnClick: false, closeOnClick: false,
draggable: false, draggable: false,
} }
); );
}; };
export default WarningNotification; export default WarningNotification;

View File

@@ -3,37 +3,37 @@ import WarningNotification from "./WarningNotification";
import SuccessNotification from "./SuccessNotification"; import SuccessNotification from "./SuccessNotification";
const Notifications = async (directionApp, response, t) => { const Notifications = async (directionApp, response, t) => {
const { status, data } = response; const {status, data} = response;
switch (status) { switch (status) {
case 200: case 200:
SuccessNotification(directionApp, status, t); SuccessNotification(directionApp, status, t);
break; break;
case 400: case 400:
ErrorNotification(directionApp, status, t); ErrorNotification(directionApp, status, t);
break; break;
case 401: case 401:
ErrorNotification(directionApp, status, t); ErrorNotification(directionApp, status, t);
break; break;
case 403: case 403:
ErrorNotification(directionApp, status, t); ErrorNotification(directionApp, status, t);
break; break;
case 422: case 422:
ErrorNotification(directionApp, status, t, data.message); ErrorNotification(directionApp, status, t, data.message);
break; break;
case 500: case 500:
WarningNotification(directionApp, status, t); WarningNotification(directionApp, status, t);
break; break;
case 503: case 503:
WarningNotification(directionApp, status, t); WarningNotification(directionApp, status, t);
break; break;
case 504: case 504:
WarningNotification(directionApp, status, t); WarningNotification(directionApp, status, t);
break; break;
default: default:
// Handle other cases if needed // Handle other cases if needed
break; break;
} }
}; };
export default Notifications; export default Notifications;

View File

@@ -14,55 +14,55 @@ export const GET_USER_ROUTE = BASE_URL + "/dashboard/profile/info";
//passenger office //passenger office
export const GET_PASSENGER_OFFICE = export const GET_PASSENGER_OFFICE =
BASE_URL + "/dashboard/passenger_office_chief/show"; BASE_URL + "/dashboard/passenger_office_chief/show";
export const CONFIRM_PASSENGER_OFFICE = export const CONFIRM_PASSENGER_OFFICE =
BASE_URL + "/dashboard/passenger_office_chief/confirm"; BASE_URL + "/dashboard/passenger_office_chief/confirm";
export const REJECT_PASSENGER_OFFICE = export const REJECT_PASSENGER_OFFICE =
BASE_URL + "/dashboard/passenger_office_chief/reject"; BASE_URL + "/dashboard/passenger_office_chief/reject";
//passenger office //passenger office
//passenger boss //passenger boss
export const GET_PASSENGER_BOSS = export const GET_PASSENGER_BOSS =
BASE_URL + "/dashboard/province_working_group/show"; BASE_URL + "/dashboard/province_working_group/show";
export const CONFIRM_PASSENGER_BOSS = export const CONFIRM_PASSENGER_BOSS =
BASE_URL + "/dashboard/province_working_group/confirm"; BASE_URL + "/dashboard/province_working_group/confirm";
export const REJECT_PASSENGER_BOSS = export const REJECT_PASSENGER_BOSS =
BASE_URL + "/dashboard/province_working_group/reject"; BASE_URL + "/dashboard/province_working_group/reject";
//passenger boss //passenger boss
//transportation assistance //transportation assistance
export const GET_TRANSPORTATION_ASSISTANCE = export const GET_TRANSPORTATION_ASSISTANCE =
BASE_URL + "/dashboard/transportation_assistant/show"; BASE_URL + "/dashboard/transportation_assistant/show";
export const CONFIRM_TRANSPORTATION_ASSISTANCE = export const CONFIRM_TRANSPORTATION_ASSISTANCE =
BASE_URL + "/dashboard/transportation_assistant/confirm"; BASE_URL + "/dashboard/transportation_assistant/confirm";
export const REJECT_TRANSPORTATION_ASSISTANCE = export const REJECT_TRANSPORTATION_ASSISTANCE =
BASE_URL + "/dashboard/transportation_assistant/reject"; BASE_URL + "/dashboard/transportation_assistant/reject";
//transportation assistance //transportation assistance
//machinary office //machinary office
export const GET_MACHINARY_OFFICE = export const GET_MACHINARY_OFFICE =
BASE_URL + "/dashboard/machinery_expert/show"; BASE_URL + "/dashboard/machinery_expert/show";
export const CONFIRM_MACHINARY_OFFICE = export const CONFIRM_MACHINARY_OFFICE =
BASE_URL + "/dashboard/machinery_expert/confirm"; BASE_URL + "/dashboard/machinery_expert/confirm";
export const REJECT_MACHINARY_OFFICE = export const REJECT_MACHINARY_OFFICE =
BASE_URL + "/dashboard/machinery_expert/reject"; BASE_URL + "/dashboard/machinery_expert/reject";
//passenger office //passenger office
//province manager //province manager
export const GET_PROVINCE_MANAGER = export const GET_PROVINCE_MANAGER =
BASE_URL + "/dashboard/province_manager/show"; BASE_URL + "/dashboard/province_manager/show";
export const CONFIRM_PROVINCE_MANAGER = export const CONFIRM_PROVINCE_MANAGER =
BASE_URL + "/dashboard/province_manager/confirm"; BASE_URL + "/dashboard/province_manager/confirm";
export const REJECT_PROVINCE_MANAGER = export const REJECT_PROVINCE_MANAGER =
BASE_URL + "/dashboard/province_manager/reject"; BASE_URL + "/dashboard/province_manager/reject";
//province manager //province manager

View File

@@ -2,18 +2,18 @@ import BorderColorIcon from "@mui/icons-material/BorderColor";
import PasswordIcon from "@mui/icons-material/Password"; import PasswordIcon from "@mui/icons-material/Password";
const headerProfileItems = [ const headerProfileItems = [
{ {
key: 0, key: 0,
name: "header.edit_profile", name: "header.edit_profile",
route: "/dashboard/edit-profile", route: "/dashboard/edit-profile",
icon: <BorderColorIcon />, icon: <BorderColorIcon/>,
}, },
{ {
key: 1, key: 1,
name: "header.change_password", name: "header.change_password",
route: "/dashboard/change-password", route: "/dashboard/change-password",
icon: <PasswordIcon />, icon: <PasswordIcon/>,
}, },
]; ];
export default headerProfileItems; export default headerProfileItems;

View File

@@ -6,58 +6,58 @@ import AssignmentIndIcon from "@mui/icons-material/AssignmentInd";
import DesktopWindowsIcon from "@mui/icons-material/DesktopWindows"; import DesktopWindowsIcon from "@mui/icons-material/DesktopWindows";
const sidebarMenu = [ const sidebarMenu = [
[ [
{ {
key: "sidebar.dashboard", key: "sidebar.dashboard",
type: "page", type: "page",
route: "/dashboard", route: "/dashboard",
icon: <SpaceDashboardIcon />, icon: <SpaceDashboardIcon/>,
selected: false, selected: false,
role: "all", role: "all",
}, },
{ {
key: "sidebar.passenger-office-chief", key: "sidebar.passenger-office-chief",
secondary: "secondary.passenger-office", secondary: "secondary.passenger-office",
type: "page", type: "page",
route: "/dashboard/passenger-office-chief", route: "/dashboard/passenger-office-chief",
icon: <AirlineSeatReclineNormalIcon />, icon: <AirlineSeatReclineNormalIcon/>,
selected: false, selected: false,
role: "passenger_office_chief", role: "passenger_office_chief",
}, },
{ {
key: "sidebar.machinary-expert", key: "sidebar.machinary-expert",
type: "page", type: "page",
route: "/dashboard/machinary-expert", route: "/dashboard/machinary-expert",
icon: <DirectionsCarFilledIcon />, icon: <DirectionsCarFilledIcon/>,
selected: false, selected: false,
role: "machinery_expert", role: "machinery_expert",
}, },
{ {
key: "sidebar.passenger-boss", key: "sidebar.passenger-boss",
secondary: "secondary.passenger-boss", secondary: "secondary.passenger-boss",
type: "page", type: "page",
route: "/dashboard/passenger-boss", route: "/dashboard/passenger-boss",
icon: <AssignmentIndIcon />, icon: <AssignmentIndIcon/>,
selected: false, selected: false,
role: "passenger_office_chief", role: "passenger_office_chief",
}, },
{ {
key: "sidebar.transportation-assistant", key: "sidebar.transportation-assistant",
type: "page", type: "page",
route: "/dashboard/transportation-assistant", route: "/dashboard/transportation-assistant",
icon: <DirectionsRailwayIcon />, icon: <DirectionsRailwayIcon/>,
selected: false, selected: false,
role: "transportation_assistant", role: "transportation_assistant",
}, },
{ {
key: "sidebar.province-manager", key: "sidebar.province-manager",
type: "page", type: "page",
route: "/dashboard/province-manager", route: "/dashboard/province-manager",
icon: <DesktopWindowsIcon />, icon: <DesktopWindowsIcon/>,
selected: false, selected: false,
role: "province_manager", role: "province_manager",
}, },
], ],
]; ];
export default sidebarMenu; export default sidebarMenu;

View File

@@ -1,38 +1,38 @@
import createCache from "@emotion/cache"; import createCache from "@emotion/cache";
import { prefixer } from "stylis"; import {prefixer} from "stylis";
import stylisRTLPlugin from "stylis-plugin-rtl"; import stylisRTLPlugin from "stylis-plugin-rtl";
const isBrowser = typeof document !== "undefined"; const isBrowser = typeof document !== "undefined";
export const createEmotionCacheLtr = () => { export const createEmotionCacheLtr = () => {
let insertionPoint; let insertionPoint;
if (isBrowser) { if (isBrowser) {
const emotionInsertionPoint = document.querySelector( const emotionInsertionPoint = document.querySelector(
'meta[name="emotion-insertion-point"]' 'meta[name="emotion-insertion-point"]'
); );
insertionPoint = emotionInsertionPoint ?? undefined; insertionPoint = emotionInsertionPoint ?? undefined;
} }
return createCache({ return createCache({
key: "mui-style", key: "mui-style",
insertionPoint, insertionPoint,
}); });
}; };
export const createEmotionCacheRtl = () => { export const createEmotionCacheRtl = () => {
let insertionPoint; let insertionPoint;
if (isBrowser) { if (isBrowser) {
const emotionInsertionPoint = document.querySelector( const emotionInsertionPoint = document.querySelector(
'meta[name="emotion-insertion-point"]' 'meta[name="emotion-insertion-point"]'
); );
insertionPoint = emotionInsertionPoint ?? undefined; insertionPoint = emotionInsertionPoint ?? undefined;
} }
return createCache({ return createCache({
key: "muirtl", key: "muirtl",
stylisPlugins: [prefixer, stylisRTLPlugin], stylisPlugins: [prefixer, stylisRTLPlugin],
insertionPoint, insertionPoint,
}); });
}; };

View File

@@ -1,12 +1,12 @@
import { createTheme } from "@mui/material/styles"; import {createTheme} from "@mui/material/styles";
import theme from "./theme"; import theme from "./theme";
const themeRtl = createTheme({ const themeRtl = createTheme({
direction: "rtl", direction: "rtl",
typography: { typography: {
fontFamily: `IRANSansFaNum, sans-serif`, fontFamily: `IRANSansFaNum, sans-serif`,
}, },
...theme, ...theme,
}); });
export default themeRtl; export default themeRtl;

View File

@@ -1,30 +1,30 @@
import { faIR } from "@mui/x-date-pickers/locales"; import {faIR} from "@mui/x-date-pickers/locales";
const theme = { const theme = {
palette: { palette: {
primary: { primary: {
main: "#084070", main: "#084070",
contrastText: "#fff", contrastText: "#fff",
light: "#2c6291", light: "#2c6291",
dark: "#11293e", dark: "#11293e",
}, },
secondary: { secondary: {
main: "#FF4E00", main: "#FF4E00",
contrastText: "#fff", contrastText: "#fff",
light: "#ed743e", light: "#ed743e",
dark: "#ad3a07", dark: "#ad3a07",
}, },
}, },
faIR, faIR,
components: { components: {
MuiBackdrop: { MuiBackdrop: {
styleOverrides: { styleOverrides: {
root: { root: {
backgroundColor: "#02020226", // Replace with your desired backdrop color and opacity backgroundColor: "#02020226", // Replace with your desired backdrop color and opacity
},
},
}, },
},
}, },
},
}; };
export default theme; export default theme;

View File

@@ -4,96 +4,96 @@ import useLoading from "@/lib/app/hooks/useLoading";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
import Head from "next/head"; import Head from "next/head";
import NextNProgress from "nextjs-progressbar"; import NextNProgress from "nextjs-progressbar";
import { useEffect } from "react"; import {useEffect} from "react";
function AppLayout({ children, isBot }) { function AppLayout({children, isBot}) {
const { languageIsReady } = useLanguage(); const {languageIsReady} = useLanguage();
const { setLoadingPage } = useLoading(); const {setLoadingPage} = useLoading();
const { userChangedLanguage, token, isAuth } = useUser(); const {userChangedLanguage, token, isAuth} = useUser();
useEffect(() => { useEffect(() => {
if (languageIsReady) { if (languageIsReady) {
if (token) { if (token) {
if (isAuth) { if (isAuth) {
if (userChangedLanguage) { if (userChangedLanguage) {
setLoadingPage(true); setLoadingPage(true);
return; return;
} }
setLoadingPage(false); setLoadingPage(false);
return; return;
} }
setLoadingPage(true); setLoadingPage(true);
return; return;
} }
setLoadingPage(false); setLoadingPage(false);
return; return;
} }
setLoadingPage(true); setLoadingPage(true);
}, [languageIsReady, token, isAuth, userChangedLanguage]); }, [languageIsReady, token, isAuth, userChangedLanguage]);
if (!isBot) { if (!isBot) {
if (userChangedLanguage) return; if (userChangedLanguage) return;
if (!languageIsReady) return; if (!languageIsReady) return;
} }
return ( return (
<> <>
<Head> <Head>
<meta <meta
name="application-name" name="application-name"
content={process.env.NEXT_PUBLIC_API_NAME} content={process.env.NEXT_PUBLIC_API_NAME}
/> />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes"/>
<meta <meta
name="apple-mobile-web-app-status-bar-style" name="apple-mobile-web-app-status-bar-style"
content="black-translucent" content="black-translucent"
/> />
<meta <meta
name="apple-mobile-web-app-title" name="apple-mobile-web-app-title"
content={process.env.NEXT_PUBLIC_API_NAME} content={process.env.NEXT_PUBLIC_API_NAME}
/> />
<meta name="description" content="Marhaba does it for you" /> <meta name="description" content="Marhaba does it for you"/>
<meta name="format-detection" content="telephone=no" /> <meta name="format-detection" content="telephone=no"/>
<meta name="format-detection" content="date=no" /> <meta name="format-detection" content="date=no"/>
<meta name="format-detection" content="address=no" /> <meta name="format-detection" content="address=no"/>
<meta name="format-detection" content="email=no" /> <meta name="format-detection" content="email=no"/>
<meta name="mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes"/>
<link rel="apple-touch-icon" href="/icons/maskable_icon_x512.png" /> <link rel="apple-touch-icon" href="/icons/maskable_icon_x512.png"/>
<link <link
rel="apple-touch-icon" rel="apple-touch-icon"
sizes="120x120" sizes="120x120"
href="/icons/maskable_icon_x128.png" href="/icons/maskable_icon_x128.png"
/> />
<link <link
rel="apple-touch-icon" rel="apple-touch-icon"
sizes="180x180" sizes="180x180"
href="/icons/maskable_icon_x192.png" href="/icons/maskable_icon_x192.png"
/> />
<meta name="google" content="notranslate" /> <meta name="google" content="notranslate"/>
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex"/>
<meta <meta
name="viewport" name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no, user-scalable=no, viewport-fit=cover" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no, user-scalable=no, viewport-fit=cover"
/> />
<link <link
rel="icon" rel="icon"
type="image/svg" type="image/svg"
sizes="32x32" sizes="32x32"
href="/images/logo.png" href="/images/logo.png"
/> />
<link <link
rel="icon" rel="icon"
type="image/svg" type="image/svg"
sizes="16x16" sizes="16x16"
href="/images/logo.png" href="/images/logo.png"
/> />
</Head> </Head>
<NextNProgress <NextNProgress
color={theme.palette.secondary.dark} color={theme.palette.secondary.dark}
options={{ showSpinner: false }} options={{showSpinner: false}}
/> />
{children} {children}
</> </>
); );
} }
export default AppLayout; export default AppLayout;

View File

@@ -1,18 +1,18 @@
import { Fade, Stack } from "@mui/material"; import {Fade, Stack} from "@mui/material";
const CenterLayout = (props) => { const CenterLayout = (props) => {
return ( return (
<Fade in={true}> <Fade in={true}>
<Stack <Stack
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
spacing={props?.spacing} spacing={props?.spacing}
sx={{ flex: 1, ...props?.sx, py: 3 }} sx={{flex: 1, ...props?.sx, py: 3}}
> >
{props.children} {props.children}
</Stack> </Stack>
</Fade> </Fade>
); );
}; };
export default CenterLayout; export default CenterLayout;

View File

@@ -1,21 +1,21 @@
import { Stack } from "@mui/material"; import {Stack} from "@mui/material";
const FullPageLayout = (props) => { const FullPageLayout = (props) => {
return ( return (
<Stack <Stack
spacing={props?.spacing} spacing={props?.spacing}
direction={props?.direction} direction={props?.direction}
sx={{ sx={{
width: "100%", width: "100%",
height: "100%", height: "100%",
overflowY: "scroll", overflowY: "scroll",
overflowX: "scroll", overflowX: "scroll",
...props?.sx, ...props?.sx,
}} }}
> >
{props.children} {props.children}
</Stack> </Stack>
); );
}; };
export default FullPageLayout; export default FullPageLayout;

View File

@@ -1,76 +1,74 @@
import NoSsrHandler from "@/core/components/isBotHandler"; import NoSsrHandler from "@/core/components/isBotHandler";
import { import {createEmotionCacheRtl} from "@/core/utils/createEmotionCache";
createEmotionCacheRtl
} from "@/core/utils/createEmotionCache";
import themeRtl from "@/core/utils/theme-rtl"; import themeRtl from "@/core/utils/theme-rtl";
import { CacheProvider } from "@emotion/react"; import {CacheProvider} from "@emotion/react";
import { GlobalStyles } from "@mui/material"; import {GlobalStyles} from "@mui/material";
import CssBaseline from "@mui/material/CssBaseline"; import CssBaseline from "@mui/material/CssBaseline";
import { ThemeProvider } from "@mui/material/styles"; import {ThemeProvider} from "@mui/material/styles";
import Head from "next/head"; import Head from "next/head";
const clientSideEmotionCacheRtl = createEmotionCacheRtl(); const clientSideEmotionCacheRtl = createEmotionCacheRtl();
const MuiLayout = ({ children, isBot }) => { const MuiLayout = ({children, isBot}) => {
const emotionCache = clientSideEmotionCacheRtl; const emotionCache = clientSideEmotionCacheRtl;
const theme = themeRtl; const theme = themeRtl;
return ( return (
<NoSsrHandler isBot={isBot}> <NoSsrHandler isBot={isBot}>
<CacheProvider value={emotionCache}> <CacheProvider value={emotionCache}>
<Head> <Head>
<meta name="viewport" content="initial-scale=1, width=device-width" /> <meta name="viewport" content="initial-scale=1, width=device-width"/>
</Head> </Head>
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<GlobalStyles <GlobalStyles
styles={{ styles={{
"*:not(.MuiTableContainer-root)::-webkit-scrollbar": { "*:not(.MuiTableContainer-root)::-webkit-scrollbar": {
display: "none", display: "none",
}, },
"*::-webkit-scrollbar": { "*::-webkit-scrollbar": {
height: "8px", height: "8px",
}, },
"*::-webkit-scrollbar-thumb": { "*::-webkit-scrollbar-thumb": {
background: "#e1e1e1", background: "#e1e1e1",
borderRadius: "3px", borderRadius: "3px",
}, },
"*:not(.MuiTableContainer-root)": { "*:not(.MuiTableContainer-root)": {
scrollbarWidth: "thin", scrollbarWidth: "thin",
scrollbarColor: "transparent transparent", scrollbarColor: "transparent transparent",
}, },
"*": { "*": {
scrollbarWidth: "thin", scrollbarWidth: "thin",
scrollbarColor: "#e1e1e1 transparent", scrollbarColor: "#e1e1e1 transparent",
}, },
"*::-moz-scrollbar-thumb": { "*::-moz-scrollbar-thumb": {
backgroundColor: "#e1e1e1", backgroundColor: "#e1e1e1",
}, },
[`@media (max-width: ${theme.breakpoints.values.sm}px)`]: { [`@media (max-width: ${theme.breakpoints.values.sm}px)`]: {
"*::-webkit-scrollbar": { "*::-webkit-scrollbar": {
height: "4px", height: "4px",
}, },
}, },
body: { body: {
width: "100vw", width: "100vw",
height: "100vh", height: "100vh",
}, },
"#__next": { "#__next": {
width: "100%", width: "100%",
height: "100%", height: "100%",
}, },
}} }}
/> />
<CssBaseline /> <CssBaseline/>
{children} {children}
</ThemeProvider> </ThemeProvider>
</CacheProvider> </CacheProvider>
</NoSsrHandler> </NoSsrHandler>
); );
}; };
export default MuiLayout; export default MuiLayout;

View File

@@ -1,25 +1,25 @@
import { Box, Fade } from "@mui/material"; import {Box, Fade} from "@mui/material";
const ScrollableLayout = (props) => { const ScrollableLayout = (props) => {
const overflowY = props?.y || "hidden"; const overflowY = props?.y || "hidden";
const overflowX = props?.x || "hidden"; const overflowX = props?.x || "hidden";
return ( return (
<Fade in={true}> <Fade in={true}>
<Box <Box
spacing={props?.spacing} spacing={props?.spacing}
sx={{ sx={{
width: "auto", width: "auto",
height: "auto", height: "auto",
overflowY: overflowY, overflowY: overflowY,
overflowX: overflowX, overflowX: overflowX,
...props?.sx, ...props?.sx,
}} }}
> >
{props.children} {props.children}
</Box> </Box>
</Fade> </Fade>
); );
}; };
export default ScrollableLayout; export default ScrollableLayout;

View File

@@ -1,19 +1,19 @@
import LinkRouting from "@/core/components/LinkRouting"; import LinkRouting from "@/core/components/LinkRouting";
import { Typography } from "@mui/material"; import {Typography} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
export default function BreadcrumbItem(props) { export default function BreadcrumbItem(props) {
const t = useTranslations(); const t = useTranslations();
const isLast = props.index === props.RouterArray.length - 1; const isLast = props.index === props.RouterArray.length - 1;
const url = `/${props.RouterArray.slice(0, props.index + 1).join("/")}`; const url = `/${props.RouterArray.slice(0, props.index + 1).join("/")}`;
return isLast ? ( return isLast ? (
<Typography variant="body2" color="primary"> <Typography variant="body2" color="primary">
{t("sidebar." + props.label)} {t("sidebar." + props.label)}
</Typography> </Typography>
) : ( ) : (
<LinkRouting underline="hover" color="inherit" passHref href={url}> <LinkRouting underline="hover" color="inherit" passHref href={url}>
<Typography variant="body2">{t("sidebar." + props.label)}</Typography> <Typography variant="body2">{t("sidebar." + props.label)}</Typography>
</LinkRouting> </LinkRouting>
); );
} }

View File

@@ -1,49 +1,49 @@
import { useRouter } from "next/router"; import {useRouter} from "next/router";
import { Box, Breadcrumbs } from "@mui/material"; import {Box, Breadcrumbs} from "@mui/material";
import { useTheme } from "@mui/material/styles"; import {useTheme} from "@mui/material/styles";
import { NavigateBefore, NavigateNext } from "@mui/icons-material"; import {NavigateBefore, NavigateNext} from "@mui/icons-material";
import BreadcrumbItem from "./BreadcrumbItem"; import BreadcrumbItem from "./BreadcrumbItem";
const BreadCrumbs = (props) => { const BreadCrumbs = (props) => {
const { isVisible } = props; const {isVisible} = props;
const theme = useTheme(); const theme = useTheme();
const router = useRouter(); const router = useRouter();
if (!isVisible) { if (!isVisible) {
return null; return null;
} }
const { pathname } = router; const {pathname} = router;
const RouterArray = pathname.split("/").filter((segment) => segment !== ""); const RouterArray = pathname.split("/").filter((segment) => segment !== "");
if (RouterArray.length === 1) { if (RouterArray.length === 1) {
return null; return null;
} }
return ( return (
<Box p={3} component="span"> <Box p={3} component="span">
<Breadcrumbs <Breadcrumbs
maxItems={2} maxItems={2}
separator={ separator={
theme.direction === "ltr" ? ( theme.direction === "ltr" ? (
<NavigateNext fontSize="small" /> <NavigateNext fontSize="small"/>
) : ( ) : (
<NavigateBefore fontSize="small" /> <NavigateBefore fontSize="small"/>
) )
} }
aria-label="breadcrumb" aria-label="breadcrumb"
> >
{RouterArray.map((segment, index) => ( {RouterArray.map((segment, index) => (
<BreadcrumbItem <BreadcrumbItem
RouterArray={RouterArray} RouterArray={RouterArray}
label={segment} label={segment}
key={segment} key={segment}
index={index} index={index}
/> />
))} ))}
</Breadcrumbs> </Breadcrumbs>
</Box> </Box>
); );
}; };
export default BreadCrumbs; export default BreadCrumbs;

View File

@@ -1,18 +1,18 @@
import { Avatar, Stack, Typography } from "@mui/material"; import {Avatar, Stack, Typography} from "@mui/material";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
export default function ProfileData() { export default function ProfileData() {
const { user } = useUser(); const {user} = useUser();
return ( return (
<Stack alignItems="center" spacing={2} sx={{ p: 3 }}> <Stack alignItems="center" spacing={2} sx={{p: 3}}>
<Avatar <Avatar
sx={{ width: "80px", height: "80px" }} sx={{width: "80px", height: "80px"}}
alt="User Image" alt="User Image"
src={user.avatar} src={user.avatar}
/> />
<Typography sx={{ fontSize: 15, fontWeight: 600 }} textAlign="center"> <Typography sx={{fontSize: 15, fontWeight: 600}} textAlign="center">
{user.username} {user.username}
</Typography> </Typography>
</Stack> </Stack>
); );
} }

View File

@@ -1,61 +1,62 @@
import { Avatar, Menu, IconButton, Tooltip } from "@mui/material"; import {Avatar, IconButton, Menu, Tooltip} from "@mui/material";
import { useState } from "react"; import {useState} from "react";
import ProfileData from "./ProfileData"; import ProfileData from "./ProfileData";
import ProfileOptions from "./ProfileOptions"; import ProfileOptions from "./ProfileOptions";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
function ProfileMenu() { function ProfileMenu() {
const t = useTranslations(); const t = useTranslations();
const [anchorElUser, setAnchorElUser] = useState(null); const [anchorElUser, setAnchorElUser] = useState(null);
const { user } = useUser(); const {user} = useUser();
const handleOpenUserMenu = (event) => { const handleOpenUserMenu = (event) => {
setAnchorElUser(event.currentTarget); setAnchorElUser(event.currentTarget);
}; };
const handleCloseUserMenu = () => { const handleCloseUserMenu = () => {
setAnchorElUser(null); setAnchorElUser(null);
}; };
return ( return (
<> <>
<Tooltip title={t("header.open_profile")} arrow> <Tooltip title={t("header.open_profile")} arrow>
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}> <IconButton onClick={handleOpenUserMenu} sx={{p: 0}}>
<Avatar <Avatar
sx={{ sx={{
width: 24, width: 24,
height: 24, height: 24,
backgroundColor: "#fff", backgroundColor: "#fff",
color: "primary.main", color: "primary.main",
}} }}
alt="User Image" alt="User Image"
src={user.avatar} src={user.avatar}
/> />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Menu <Menu
MenuListProps={{ sx: { py: 0 } }} MenuListProps={{sx: {py: 0}}}
sx={{ sx={{
mt: 6, mt: 6,
}} }}
id="menu-appbar" id="menu-appbar"
anchorEl={anchorElUser} anchorEl={anchorElUser}
anchorOrigin={{ anchorOrigin={{
vertical: "top", vertical: "top",
horizontal: "right", horizontal: "right",
}} }}
keepMounted keepMounted
transformOrigin={{ transformOrigin={{
vertical: "top", vertical: "top",
horizontal: "right", horizontal: "right",
}} }}
open={Boolean(anchorElUser)} open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu} onClose={handleCloseUserMenu}
> >
<ProfileData /> <ProfileData/>
<ProfileOptions handleCloseUserMenu={handleCloseUserMenu} /> <ProfileOptions handleCloseUserMenu={handleCloseUserMenu}/>
</Menu> </Menu>
</> </>
); );
} }
export default ProfileMenu; export default ProfileMenu;

View File

@@ -1,49 +1,49 @@
import { Box, Button, MenuItem, Typography } from "@mui/material"; import {Box, Button, MenuItem, Typography} from "@mui/material";
import MeetingRoomIcon from "@mui/icons-material/MeetingRoom"; import MeetingRoomIcon from "@mui/icons-material/MeetingRoom";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
export default function ProfileOptionLogOut({ handleCloseUserMenu }) { export default function ProfileOptionLogOut({handleCloseUserMenu}) {
const t = useTranslations(); const t = useTranslations();
const { clearToken } = useUser(); const {clearToken} = useUser();
const handleClickLogOut = () => { const handleClickLogOut = () => {
handleCloseUserMenu(); handleCloseUserMenu();
clearToken(); clearToken();
}; };
return ( return (
<> <>
<MenuItem <MenuItem
component={Button} component={Button}
to={{ to={{
pathname: "/dashboard/logout", pathname: "/dashboard/logout",
}} }}
sx={{ sx={{
display: "flex", display: "flex",
justifyContent: "center", justifyContent: "center",
borderTop: 1, borderTop: 1,
px: 3, px: 3,
py: 1.5, py: 1.5,
borderColor: "#e1e1e1", borderColor: "#e1e1e1",
textTransform: "unset", textTransform: "unset",
}} }}
onClick={handleClickLogOut} onClick={handleClickLogOut}
> >
<Box sx={{ display: "flex", alignItems: "center", flex: 1 }}> <Box sx={{display: "flex", alignItems: "center", flex: 1}}>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
color: "primary.main", color: "primary.main",
pr: 2, pr: 2,
}} }}
> >
<MeetingRoomIcon /> <MeetingRoomIcon/>
</Box> </Box>
<Typography sx={{ flex: 1 }} textAlign="start"> <Typography sx={{flex: 1}} textAlign="start">
{t("header.logout")} {t("header.logout")}
</Typography> </Typography>
</Box> </Box>
</MenuItem> </MenuItem>
</> </>
); );
} }

View File

@@ -1,49 +1,49 @@
import { Box, MenuItem, Typography } from "@mui/material"; import {Box, MenuItem, Typography} from "@mui/material";
import { NextLinkComposed } from "@/core/components/LinkRouting"; import {NextLinkComposed} from "@/core/components/LinkRouting";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import headerProfileItems from "@/core/data/headerProfileItems"; import headerProfileItems from "@/core/data/headerProfileItems";
import ProfileOptionLogOut from "./ProfileOptionLogOut"; import ProfileOptionLogOut from "./ProfileOptionLogOut";
export default function ProfileOptions({ handleCloseUserMenu }) { export default function ProfileOptions({handleCloseUserMenu}) {
const t = useTranslations(); const t = useTranslations();
return ( return (
<> <>
{headerProfileItems.map((profile_item) => ( {headerProfileItems.map((profile_item) => (
<MenuItem <MenuItem
component={NextLinkComposed} component={NextLinkComposed}
to={{ to={{
pathname: profile_item.route, pathname: profile_item.route,
}} }}
sx={{ sx={{
display: "flex", display: "flex",
justifyContent: "center", justifyContent: "center",
borderTop: 1, borderTop: 1,
px: 3, px: 3,
py: 1.5, py: 1.5,
borderColor: "#e1e1e1", borderColor: "#e1e1e1",
}} }}
key={profile_item.key} key={profile_item.key}
onClick={handleCloseUserMenu} onClick={handleCloseUserMenu}
> >
<Box sx={{ display: "flex", alignItems: "center", flex: 1 }}> <Box sx={{display: "flex", alignItems: "center", flex: 1}}>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
color: "primary.main", color: "primary.main",
pr: 2, pr: 2,
}} }}
> >
{profile_item.icon} {profile_item.icon}
</Box> </Box>
<Typography sx={{ flex: 1 }} textAlign="start"> <Typography sx={{flex: 1}} textAlign="start">
{t(profile_item.name)} {t(profile_item.name)}
</Typography> </Typography>
</Box> </Box>
</MenuItem> </MenuItem>
))} ))}
<ProfileOptionLogOut handleCloseUserMenu={handleCloseUserMenu} /> <ProfileOptionLogOut handleCloseUserMenu={handleCloseUserMenu}/>
</> </>
); );
} }

View File

@@ -1,80 +1,72 @@
import MenuIcon from "@mui/icons-material/Menu"; import MenuIcon from "@mui/icons-material/Menu";
import { import {AppBar, Box, Container, CssBaseline, IconButton, Stack, Toolbar, useTheme} from "@mui/material";
AppBar,
Box,
Container,
CssBaseline,
IconButton,
Stack,
Toolbar,
useTheme
} from "@mui/material";
import ProfileMenu from "./ProfileMenu"; import ProfileMenu from "./ProfileMenu";
function Header({ drawerWidth, handleDrawerToggle }) { function Header({drawerWidth, handleDrawerToggle}) {
const theme = useTheme(); const theme = useTheme();
return ( return (
<> <>
<CssBaseline /> <CssBaseline/>
<AppBar <AppBar
position="fixed" position="fixed"
sx={{
width: { md: `calc(100% - ${drawerWidth}px)` },
ml: { md: `${drawerWidth}px` },
}}
>
<Container maxWidth="xxl">
<Toolbar
disableGutters
sx={{
display: "flex",
}}
>
<Stack direction="row" justifyContent="flex-start" sx={{ flex: 1 }}>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerToggle}
edge="start"
sx={{ display: { md: "none" }, m: 0 }}
>
<MenuIcon />
</IconButton>
</Stack>
<Stack
direction="row"
justifyContent="center"
sx={{
flex: 1,
position: "relative",
...theme.mixins.toolbar,
}}
>
<Box
sx={{ sx={{
position: "relative", width: {md: `calc(100% - ${drawerWidth}px)`},
my: 1, ml: {md: `${drawerWidth}px`},
width: 56,
height: 56,
"@media (min-width:600px)": { maxWidth: 64, maxHeight: 64 },
"@media (min-width:0px)": {
"@media (orientation: landscape)": {
width: 48,
height: 48,
},
},
}} }}
> >
</Box> <Container maxWidth="xxl">
</Stack> <Toolbar
<Stack direction="row" justifyContent="flex-end" sx={{ flex: 1 }}> disableGutters
<ProfileMenu /> sx={{
</Stack> display: "flex",
</Toolbar> }}
</Container> >
</AppBar> <Stack direction="row" justifyContent="flex-start" sx={{flex: 1}}>
</> <IconButton
); color="inherit"
aria-label="open drawer"
onClick={handleDrawerToggle}
edge="start"
sx={{display: {md: "none"}, m: 0}}
>
<MenuIcon/>
</IconButton>
</Stack>
<Stack
direction="row"
justifyContent="center"
sx={{
flex: 1,
position: "relative",
...theme.mixins.toolbar,
}}
>
<Box
sx={{
position: "relative",
my: 1,
width: 56,
height: 56,
"@media (min-width:600px)": {maxWidth: 64, maxHeight: 64},
"@media (min-width:0px)": {
"@media (orientation: landscape)": {
width: 48,
height: 48,
},
},
}}
>
</Box>
</Stack>
<Stack direction="row" justifyContent="flex-end" sx={{flex: 1}}>
<ProfileMenu/>
</Stack>
</Toolbar>
</Container>
</AppBar>
</>
);
} }
export default Header; export default Header;

View File

@@ -1,50 +1,50 @@
import { useState } from "react"; import {useState} from "react";
import FullPageLayout from "../FullPageLayout"; import FullPageLayout from "../FullPageLayout";
import Header from "./header"; import Header from "./header";
import Sidebar from "./sidebar"; import Sidebar from "./sidebar";
import { Toolbar } from "@mui/material"; import {Toolbar} from "@mui/material";
import BreadCrumbs from "./breadcrumbs"; import BreadCrumbs from "./breadcrumbs";
import { ToastContainer } from "react-toastify"; import {ToastContainer} from "react-toastify";
import "react-toastify/dist/ReactToastify.css"; import "react-toastify/dist/ReactToastify.css";
import useDirection from "@/lib/app/hooks/useDirection"; import useDirection from "@/lib/app/hooks/useDirection";
const drawerWidth = 240; const drawerWidth = 240;
const DashboardLayouts = (props) => { const DashboardLayouts = (props) => {
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const { window } = props; const {window} = props;
const [mobileOpen, setMobileOpen] = useState(false); const [mobileOpen, setMobileOpen] = useState(false);
const container = const container =
window !== undefined ? () => window().document.body : undefined; window !== undefined ? () => window().document.body : undefined;
const handleDrawerToggle = () => { const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen); setMobileOpen(!mobileOpen);
}; };
return ( return (
<FullPageLayout direction="row"> <FullPageLayout direction="row">
<Header <Header
handleDrawerToggle={handleDrawerToggle} handleDrawerToggle={handleDrawerToggle}
drawerWidth={drawerWidth} drawerWidth={drawerWidth}
/> />
<Sidebar <Sidebar
container={container} container={container}
mobileOpen={mobileOpen} mobileOpen={mobileOpen}
handleDrawerToggle={handleDrawerToggle} handleDrawerToggle={handleDrawerToggle}
drawerWidth={drawerWidth} drawerWidth={drawerWidth}
/> />
<FullPageLayout <FullPageLayout
component="main" component="main"
sx={{ flexGrow: 1, width: { sm: `calc(100% - ${drawerWidth}px)` } }} sx={{flexGrow: 1, width: {sm: `calc(100% - ${drawerWidth}px)`}}}
> >
<Toolbar /> <Toolbar/>
<FullPageLayout sx={{ mt: 3 }}> <FullPageLayout sx={{mt: 3}}>
<BreadCrumbs isVisible={true} /> <BreadCrumbs isVisible={true}/>
{props.children} {props.children}
</FullPageLayout>
</FullPageLayout>
{directionApp === "rtl" ? <ToastContainer rtl/> : <ToastContainer ltr/>}
</FullPageLayout> </FullPageLayout>
</FullPageLayout> );
{directionApp === "rtl" ? <ToastContainer rtl /> : <ToastContainer ltr />}
</FullPageLayout>
);
}; };
export default DashboardLayouts; export default DashboardLayouts;

View File

@@ -1,29 +1,28 @@
import { Divider, Stack, Toolbar, Typography } from "@mui/material"; import {Divider, Stack, Toolbar, Typography} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import SidebarList from "./SidebarList"; import SidebarList from "./SidebarList";
import StyledImage from "@/core/components/StyledImage";
const SidebarDrawer = ({ handleDrawerToggle }) => { const SidebarDrawer = ({handleDrawerToggle}) => {
const t = useTranslations(); const t = useTranslations();
return ( return (
<> <>
<Toolbar> <Toolbar>
<Stack> <Stack>
<Typography variant="h6" sx={{ color: "primary.main" }}> <Typography variant="h6" sx={{color: "primary.main"}}>
{t("app_short_name")} {t("app_short_name")}
</Typography> </Typography>
<Typography variant="caption"> <Typography variant="caption">
{ {
t("expert") /* make if on thease t("user_welfare_services") t("expert") /* make if on thease t("user_welfare_services")
t("user_navy") */ t("user_navy") */
} }
</Typography> </Typography>
</Stack> </Stack>
</Toolbar> </Toolbar>
<Divider /> <Divider/>
<SidebarList handleDrawerToggle={handleDrawerToggle} /> <SidebarList handleDrawerToggle={handleDrawerToggle}/>
</> </>
); );
}; };
export default SidebarDrawer; export default SidebarDrawer;

View File

@@ -1,57 +1,57 @@
import { Divider, List } from "@mui/material"; import {Divider, List} from "@mui/material";
import { useEffect, useReducer } from "react"; import {useEffect, useReducer} from "react";
import SidebarListItem from "./SidebarListItem"; import SidebarListItem from "./SidebarListItem";
import sidebarMenu from "@/core/data/sidebarMenu"; import sidebarMenu from "@/core/data/sidebarMenu";
import { useRouter } from "next/router"; import {useRouter} from "next/router";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
function reducer(state, action) { function reducer(state, action) {
switch (action.type) { switch (action.type) {
case "COLLAPSE_MENU": case "COLLAPSE_MENU":
return state.map((itemsArr) => return state.map((itemsArr) =>
itemsArr.map((item) => itemsArr.map((item) =>
action.key == item.key action.key == item.key
? { ...item, showSubItem: !item.showSubItem } ? {...item, showSubItem: !item.showSubItem}
: item : item
) )
); );
case "SELECTED": case "SELECTED":
return state.map((itemsArr) => return state.map((itemsArr) =>
itemsArr.map((item) => itemsArr.map((item) =>
action.route == item.route action.route == item.route
? { ...item, selected: true } ? {...item, selected: true}
: { ...item, selected: false } : {...item, selected: false}
) )
); );
default: default:
throw new Error(); throw new Error();
} }
} }
export default function SidebarList({ handleDrawerToggle }) { export default function SidebarList({handleDrawerToggle}) {
const [itemMenu, dispatch] = useReducer(reducer, sidebarMenu); const [itemMenu, dispatch] = useReducer(reducer, sidebarMenu);
const { user } = useUser(); const {user} = useUser();
const router = useRouter(); const router = useRouter();
useEffect(() => { useEffect(() => {
dispatch({ type: "SELECTED", route: router.pathname }); dispatch({type: "SELECTED", route: router.pathname});
}, [router.pathname]); }, [router.pathname]);
const filteredItemMenu = itemMenu[0].filter( const filteredItemMenu = itemMenu[0].filter(
(item) => user.role.includes(item.role) || item.role === "all" (item) => user.role.includes(item.role) || item.role === "all"
); );
return ( return (
<List> <List>
{filteredItemMenu.map((item, index) => ( {filteredItemMenu.map((item, index) => (
<SidebarListItem <SidebarListItem
item={item} item={item}
dispatch={dispatch} dispatch={dispatch}
key={item.key} key={item.key}
handleDrawerToggle={handleDrawerToggle} handleDrawerToggle={handleDrawerToggle}
/> />
))} ))}
{filteredItemMenu.length > 0 && <Divider />} {filteredItemMenu.length > 0 && <Divider/>}
</List> </List>
); );
} }

View File

@@ -1,84 +1,77 @@
import { NextLinkComposed } from "@/core/components/LinkRouting"; import {NextLinkComposed} from "@/core/components/LinkRouting";
import ExpandLess from "@mui/icons-material/ExpandLess"; import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore"; import ExpandMore from "@mui/icons-material/ExpandMore";
import { import {Badge, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography,} from "@mui/material";
Badge, import {useTranslations} from "next-intl";
ListItem, import {Fragment} from "react";
ListItemButton,
ListItemIcon,
ListItemText,
Typography,
} from "@mui/material";
import { useTranslations } from "next-intl";
import { Fragment } from "react";
import SidebarListSubItem from "./SidebarListSubItem"; import SidebarListSubItem from "./SidebarListSubItem";
const SidebarListItem = ({ item, dispatch, handleDrawerToggle }) => { const SidebarListItem = ({item, dispatch, handleDrawerToggle}) => {
const t = useTranslations(); const t = useTranslations();
return ( return (
<Fragment key={item.key}> <Fragment key={item.key}>
<ListItem disablePadding sx={{ display: "block" }}> <ListItem disablePadding sx={{display: "block"}}>
<ListItemButton <ListItemButton
selected={item.selected} selected={item.selected}
{...(item.type == "page" && { {...(item.type == "page" && {
component: NextLinkComposed, component: NextLinkComposed,
to: { to: {
pathname: item.route, pathname: item.route,
}, },
})} })}
onClick={() => { onClick={() => {
if (item.type == "menu") { if (item.type == "menu") {
dispatch({ type: "COLLAPSE_MENU", key: item.key }); dispatch({type: "COLLAPSE_MENU", key: item.key});
} }
handleDrawerToggle(); handleDrawerToggle();
}} }}
sx={{ sx={{
minHeight: 48, minHeight: 48,
}} }}
> >
<ListItemIcon <ListItemIcon
sx={{ sx={{
minWidth: 0, minWidth: 0,
justifyContent: "center", justifyContent: "center",
color: "primary.main", color: "primary.main",
pr: 2, pr: 2,
}} }}
> >
{item.icon} {item.icon}
</ListItemIcon> </ListItemIcon>
<ListItemText <ListItemText
primary={t(item.key)} primary={t(item.key)}
secondary={ secondary={
item.secondary !== undefined ? ( item.secondary !== undefined ? (
<Typography variant="caption" color="textSecondary"> <Typography variant="caption" color="textSecondary">
{t(item.secondary)} {t(item.secondary)}
</Typography> </Typography>
) : null ) : null
} }
/> />
{item.type == "menu" && {item.type == "menu" &&
(item.showSubItem ? <ExpandLess /> : <ExpandMore />)} (item.showSubItem ? <ExpandLess/> : <ExpandMore/>)}
<Badge <Badge
badgeContent="21" badgeContent="21"
color="error" color="error"
variant="standard" variant="standard"
anchorOrigin={{ anchorOrigin={{
vertical: "top", vertical: "top",
horizontal: "right", horizontal: "right",
}} }}
/> />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
{item.subItem && ( {item.subItem && (
<SidebarListSubItem <SidebarListSubItem
item={item} item={item}
handleDrawerToggle={handleDrawerToggle} handleDrawerToggle={handleDrawerToggle}
/> />
)} )}
</Fragment> </Fragment>
); );
}; };
export default SidebarListItem; export default SidebarListItem;

View File

@@ -1,54 +1,48 @@
import { NextLinkComposed } from "@/core/components/LinkRouting"; import {NextLinkComposed} from "@/core/components/LinkRouting";
import InboxIcon from "@mui/icons-material/MoveToInbox"; import InboxIcon from "@mui/icons-material/MoveToInbox";
import { import {Collapse, List, ListItemButton, ListItemIcon, ListItemText,} from "@mui/material";
Collapse, import {useTranslations} from "next-intl";
List, import {Fragment} from "react";
ListItemButton,
ListItemIcon,
ListItemText,
} from "@mui/material";
import { useTranslations } from "next-intl";
import { Fragment } from "react";
const SidebarListSubItem = ({ item, handleDrawerToggle }) => { const SidebarListSubItem = ({item, handleDrawerToggle}) => {
const t = useTranslations(); const t = useTranslations();
return ( return (
<Collapse in={item.showSubItem} timeout="auto" unmountOnExit> <Collapse in={item.showSubItem} timeout="auto" unmountOnExit>
<List component="div" disablePadding sx={{ bgcolor: "#f6f6f6" }}> <List component="div" disablePadding sx={{bgcolor: "#f6f6f6"}}>
{item.subItem.map((subitem, index) => ( {item.subItem.map((subitem, index) => (
<Fragment key={subitem.key}> <Fragment key={subitem.key}>
<ListItemButton <ListItemButton
component={NextLinkComposed} component={NextLinkComposed}
to={{ to={{
pathname: subitem.route, pathname: subitem.route,
}} }}
sx={{ sx={{
minHeight: 48, minHeight: 48,
}} }}
onClick={(event) => { onClick={(event) => {
if (item.type == "menu") { if (item.type == "menu") {
dispatch({ type: "COLLAPSE_MENU", key: item.key }); dispatch({type: "COLLAPSE_MENU", key: item.key});
} }
handleDrawerToggle(); handleDrawerToggle();
}} }}
> >
<ListItemIcon <ListItemIcon
sx={{ sx={{
minWidth: 0, minWidth: 0,
justifyContent: "center", justifyContent: "center",
pr: 2, pr: 2,
}} }}
> >
<InboxIcon /> <InboxIcon/>
</ListItemIcon> </ListItemIcon>
<ListItemText primary={t(subitem.key)} /> <ListItemText primary={t(subitem.key)}/>
</ListItemButton> </ListItemButton>
</Fragment> </Fragment>
))} ))}
</List> </List>
</Collapse> </Collapse>
); );
}; };
export default SidebarListSubItem; export default SidebarListSubItem;

View File

@@ -1,46 +1,46 @@
import { Box, Drawer } from "@mui/material"; import {Box, Drawer} from "@mui/material";
import SidebarDrawer from "./SidebarDrawer"; import SidebarDrawer from "./SidebarDrawer";
const Sidebar = (props) => { const Sidebar = (props) => {
return ( return (
<Box <Box
component="nav" component="nav"
sx={{ width: { md: props.drawerWidth }, flexShrink: { sm: 0 } }} sx={{width: {md: props.drawerWidth}, flexShrink: {sm: 0}}}
aria-label="mailbox folders" aria-label="mailbox folders"
> >
<Drawer <Drawer
container={props.container} container={props.container}
variant="temporary" variant="temporary"
open={props.mobileOpen} open={props.mobileOpen}
onClose={props.handleDrawerToggle} onClose={props.handleDrawerToggle}
ModalProps={{ ModalProps={{
keepMounted: true, keepMounted: true,
}} }}
sx={{ sx={{
display: { xs: "block", md: "none" }, display: {xs: "block", md: "none"},
"& .MuiDrawer-paper": { "& .MuiDrawer-paper": {
boxSizing: "border-box", boxSizing: "border-box",
width: props.drawerWidth, width: props.drawerWidth,
}, },
}} }}
> >
<SidebarDrawer handleDrawerToggle={props.handleDrawerToggle} /> <SidebarDrawer handleDrawerToggle={props.handleDrawerToggle}/>
</Drawer> </Drawer>
<Drawer <Drawer
variant="permanent" variant="permanent"
sx={{ sx={{
display: { xs: "none", md: "block" }, display: {xs: "none", md: "block"},
"& .MuiDrawer-paper": { "& .MuiDrawer-paper": {
boxSizing: "border-box", boxSizing: "border-box",
width: props.drawerWidth, width: props.drawerWidth,
}, },
}} }}
open open
> >
<SidebarDrawer handleDrawerToggle={props.handleDrawerToggle} /> <SidebarDrawer handleDrawerToggle={props.handleDrawerToggle}/>
</Drawer> </Drawer>
</Box> </Box>
); );
}; };
export default Sidebar; export default Sidebar;

View File

@@ -1,14 +1,14 @@
import { createContext } from "react"; import {createContext} from "react";
import useDataTable from "../hooks/useDatatable"; import useDataTable from "../hooks/useDatatable";
export const DataTableContext = createContext(); export const DataTableContext = createContext();
export const DataTableProvider = (props) => { export const DataTableProvider = (props) => {
const contextValue = useDataTable(); const contextValue = useDataTable();
return ( return (
<DataTableContext.Provider value={contextValue}> <DataTableContext.Provider value={contextValue}>
{props.children} {props.children}
</DataTableContext.Provider> </DataTableContext.Provider>
); );
}; };

View File

@@ -1,15 +1,15 @@
import { createContext, useState } from "react"; import {createContext, useState} from "react";
export const ReloadDataTableContext = createContext(); export const ReloadDataTableContext = createContext();
export const ReloadDataTableProvider = ({ children }) => { export const ReloadDataTableProvider = ({children}) => {
const [reloadDataTable, setReloadDataTable] = useState(false); const [reloadDataTable, setReloadDataTable] = useState(false);
return ( return (
<ReloadDataTableContext.Provider <ReloadDataTableContext.Provider
value={{ reloadDataTable, setReloadDataTable }} value={{reloadDataTable, setReloadDataTable}}
> >
{children} {children}
</ReloadDataTableContext.Provider> </ReloadDataTableContext.Provider>
); );
}; };

View File

@@ -1,99 +1,99 @@
import { FA_DATATABLE_LOCALIZATION } from "&/locales/fa/datatable"; import {FA_DATATABLE_LOCALIZATION} from "&/locales/fa/datatable";
import { useRouter } from "next/router"; import {useRouter} from "next/router";
import { createContext, useEffect, useState } from "react"; import {createContext, useEffect, useState} from "react";
import useUser from "../hooks/useUser"; import useUser from "../hooks/useUser";
export const LanguageContext = createContext(); export const LanguageContext = createContext();
export const LanguageProvider = ({ children }) => { export const LanguageProvider = ({children}) => {
const router = useRouter(); const router = useRouter();
const languageList = [ const languageList = [
{
key: "fa",
dir: "rtl",
name: "فارسی",
fontFamily: `IRANSans, sans-serif`,
tableLocalization: FA_DATATABLE_LOCALIZATION,
}
];
const { user, userChangedLanguage, changeLanguageState } = useUser();
const [languageIsReady, setLanguageIsReady] = useState(false);
const [languageApp, setLanguageApp] = useState();
const [directionApp, setDirectionApp] = useState(
process.env.NEXT_PUBLIC_DEFAULT_DIRECTION
);
useEffect(() => {
const lang = localStorage.getItem("_language");
if (!lang && !languageApp) {
setLanguageApp(process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE);
} else if (lang) {
setLanguageApp(lang);
}
}, []);
useEffect(() => {
if (!languageApp) return;
const lang = localStorage.getItem("_language");
if (!lang) {
localStorage.setItem("_language", languageApp);
} else {
if (languageApp != lang) {
localStorage.setItem("_language", languageApp);
}
}
}, [languageApp]);
useEffect(() => {
if (!router.isReady) return;
if (user.user_language) {
if (user.user_language != languageApp) {
setLanguageApp(user.user_language);
return;
}
}
if (languageApp != router.locale) {
router.replace(
{ pathname: router.pathname, query: router.query },
router.asPath,
{ {
locale: languageApp, key: "fa",
dir: "rtl",
name: "فارسی",
fontFamily: `IRANSans, sans-serif`,
tableLocalization: FA_DATATABLE_LOCALIZATION,
} }
); ];
return; const {user, userChangedLanguage, changeLanguageState} = useUser();
} const [languageIsReady, setLanguageIsReady] = useState(false);
for (const lang of languageList) { const [languageApp, setLanguageApp] = useState();
if (languageApp != lang.key) continue; const [directionApp, setDirectionApp] = useState(
setDirectionApp(lang.dir); process.env.NEXT_PUBLIC_DEFAULT_DIRECTION
document.dir = lang.dir; );
}
const timer = setTimeout(() => { useEffect(() => {
changeLanguageState(false); const lang = localStorage.getItem("_language");
setLanguageIsReady(true);
}, 1000);
return () => { if (!lang && !languageApp) {
clearTimeout(timer); setLanguageApp(process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE);
}; } else if (lang) {
}, [router.locale, router.isReady, userChangedLanguage, languageApp]); setLanguageApp(lang);
}
}, []);
return ( useEffect(() => {
<LanguageContext.Provider if (!languageApp) return;
value={{
languageApp, const lang = localStorage.getItem("_language");
setLanguageApp,
directionApp, if (!lang) {
languageIsReady, localStorage.setItem("_language", languageApp);
setLanguageIsReady, } else {
languageList, if (languageApp != lang) {
}} localStorage.setItem("_language", languageApp);
> }
{children} }
</LanguageContext.Provider> }, [languageApp]);
);
useEffect(() => {
if (!router.isReady) return;
if (user.user_language) {
if (user.user_language != languageApp) {
setLanguageApp(user.user_language);
return;
}
}
if (languageApp != router.locale) {
router.replace(
{pathname: router.pathname, query: router.query},
router.asPath,
{
locale: languageApp,
}
);
return;
}
for (const lang of languageList) {
if (languageApp != lang.key) continue;
setDirectionApp(lang.dir);
document.dir = lang.dir;
}
const timer = setTimeout(() => {
changeLanguageState(false);
setLanguageIsReady(true);
}, 1000);
return () => {
clearTimeout(timer);
};
}, [router.locale, router.isReady, userChangedLanguage, languageApp]);
return (
<LanguageContext.Provider
value={{
languageApp,
setLanguageApp,
directionApp,
languageIsReady,
setLanguageIsReady,
languageList,
}}
>
{children}
</LanguageContext.Provider>
);
}; };

View File

@@ -1,14 +1,14 @@
import LoadingHardPage from "@/core/components/LoadingHardPage"; import LoadingHardPage from "@/core/components/LoadingHardPage";
import { createContext, useState } from "react"; import {createContext, useState} from "react";
export const LoadingContext = createContext(); export const LoadingContext = createContext();
export const LoadingProvider = ({ children }) => { export const LoadingProvider = ({children}) => {
const [loadingPage, setLoadingPage] = useState(false); const [loadingPage, setLoadingPage] = useState(false);
return ( return (
<LoadingContext.Provider value={{ loadingPage, setLoadingPage }}> <LoadingContext.Provider value={{loadingPage, setLoadingPage}}>
<LoadingHardPage loading={loadingPage} /> <LoadingHardPage loading={loadingPage}/>
{children} {children}
</LoadingContext.Provider> </LoadingContext.Provider>
); );
}; };

View File

@@ -1,124 +1,122 @@
import { import {GET_USER_ROUTE} from "@/core/data/apiRoutes";
GET_USER_ROUTE,
SET_LANGUAGE
} from "@/core/data/apiRoutes";
import axios from "axios"; import axios from "axios";
import { createContext, useCallback, useEffect, useReducer } from "react"; import {createContext, useCallback, useEffect, useReducer} from "react";
const initialUser = { const initialUser = {
isAuth: false, isAuth: false,
userChangedLanguage: false, userChangedLanguage: false,
token: null, token: null,
user: {}, user: {},
}; };
const reducer = (state, action) => { const reducer = (state, action) => {
switch (action.type) { switch (action.type) {
case "CLEAR_USER": case "CLEAR_USER":
return { ...state, user: {} }; return {...state, user: {}};
case "CHANGE_USER": case "CHANGE_USER":
return { ...state, user: action.user }; return {...state, user: action.user};
case "CHANGE_USER_LANGUAGE": case "CHANGE_USER_LANGUAGE":
return { return {
...state, ...state,
user: { ...state.user, user_language: action.language }, user: {...state.user, user_language: action.language},
}; };
case "CHANGE_AUTH_STATE": case "CHANGE_AUTH_STATE":
return { ...state, isAuth: action.isAuth }; return {...state, isAuth: action.isAuth};
case "CHANGE_LANGUAGE_STATE": case "CHANGE_LANGUAGE_STATE":
return { ...state, userChangedLanguage: action.userChangedLanguage }; return {...state, userChangedLanguage: action.userChangedLanguage};
case "CLEAR_TOKEN": case "CLEAR_TOKEN":
localStorage.removeItem("_token"); localStorage.removeItem("_token");
return { ...state, token: null }; return {...state, token: null};
case "SET_TOKEN": case "SET_TOKEN":
localStorage.setItem("_token", action.token); localStorage.setItem("_token", action.token);
return { ...state, token: action.token }; return {...state, token: action.token};
default: default:
return state; return state;
} }
}; };
export const UserContext = createContext(); export const UserContext = createContext();
export const UserProvider = ({ children }) => { export const UserProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer, initialUser); const [state, dispatch] = useReducer(reducer, initialUser);
const clearUser = useCallback(() => { const clearUser = useCallback(() => {
dispatch({ type: "CLEAR_USER" }); dispatch({type: "CLEAR_USER"});
}, []); }, []);
const changeUser = useCallback((user) => { const changeUser = useCallback((user) => {
dispatch({ type: "CHANGE_USER", user }); dispatch({type: "CHANGE_USER", user});
}, []); }, []);
const changeUserLanguage = useCallback(/* use in multi language app */); const changeUserLanguage = useCallback(/* use in multi language app */);
const changeAuthState = useCallback((isAuth) => { const changeAuthState = useCallback((isAuth) => {
dispatch({ type: "CHANGE_AUTH_STATE", isAuth }); dispatch({type: "CHANGE_AUTH_STATE", isAuth});
}, []); }, []);
const changeLanguageState = useCallback((userChangedLanguage) => { const changeLanguageState = useCallback((userChangedLanguage) => {
dispatch({ type: "CHANGE_LANGUAGE_STATE", userChangedLanguage }); dispatch({type: "CHANGE_LANGUAGE_STATE", userChangedLanguage});
}, []); }, []);
const clearToken = useCallback(() => { const clearToken = useCallback(() => {
dispatch({ type: "CLEAR_TOKEN" }); dispatch({type: "CLEAR_TOKEN"});
}, []); }, []);
const setToken = useCallback((token) => { const setToken = useCallback((token) => {
dispatch({ type: "SET_TOKEN", token }); dispatch({type: "SET_TOKEN", token});
}, []); }, []);
const getUser = useCallback( const getUser = useCallback(
(callback = () => {}) => { (callback = () => {
axios }) => {
.get(GET_USER_ROUTE, { axios
headers: { authorization: `Bearer ${state.token}` }, .get(GET_USER_ROUTE, {
}) headers: {authorization: `Bearer ${state.token}`},
.then(({ data }) => { })
if (typeof callback === "function") callback(data); .then(({data}) => {
if (typeof callback === "function") callback(data);
});
},
[state.token]
);
useEffect(() => {
const localToken = localStorage.getItem("_token");
if (localToken) dispatch({type: "SET_TOKEN", token: localToken});
}, []);
useEffect(() => {
if (!state.token) {
clearUser();
changeAuthState(false);
changeLanguageState(false);
return;
}
getUser((data) => {
changeUser(data);
changeAuthState(true);
changeLanguageState(true);
}); });
}, }, [state.token]);
[state.token]
);
useEffect(() => { return (
const localToken = localStorage.getItem("_token"); <UserContext.Provider
if (localToken) dispatch({ type: "SET_TOKEN", token: localToken }); value={{
}, []); isAuth: state.isAuth,
userChangedLanguage: state.userChangedLanguage,
useEffect(() => { token: state.token,
if (!state.token) { user: state.user,
clearUser(); getUser,
changeAuthState(false); clearUser,
changeLanguageState(false); changeUser,
return; changeUserLanguage,
} changeAuthState,
getUser((data) => { changeLanguageState,
changeUser(data); clearToken,
changeAuthState(true); setToken,
changeLanguageState(true); }}
}); >
}, [state.token]); {children}
</UserContext.Provider>
return ( );
<UserContext.Provider
value={{
isAuth: state.isAuth,
userChangedLanguage: state.userChangedLanguage,
token: state.token,
user: state.user,
getUser,
clearUser,
changeUser,
changeUserLanguage,
changeAuthState,
changeLanguageState,
clearToken,
setToken,
}}
>
{children}
</UserContext.Provider>
);
}; };

View File

@@ -1,113 +1,113 @@
import axios from "axios"; import axios from "axios";
import { useCallback, useContext, useReducer, useState } from "react"; import {useCallback, useContext, useReducer, useState} from "react";
import { ReloadDataTableContext } from "../contexts/ReloadDatatableContext"; import {ReloadDataTableContext} from "../contexts/ReloadDatatableContext";
import useUser from "./useUser"; import useUser from "./useUser";
import Notifications from "@/core/components/notifications"; import Notifications from "@/core/components/notifications";
import useDirection from "./useDirection"; import useDirection from "./useDirection";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
const initialValues = { const initialValues = {
url: "", url: "",
rowID: "", rowID: "",
values: {}, values: {},
}; };
const reducer = (state, action) => { const reducer = (state, action) => {
switch (action.type) { switch (action.type) {
case "CONFIRM_DATA": case "CONFIRM_DATA":
return { return {
...state, ...state,
url: action.url, url: action.url,
rowID: action.rowID, rowID: action.rowID,
}; };
case "REJECT_DATA": case "REJECT_DATA":
return { return {
...state, ...state,
url: action.url, url: action.url,
rowID: action.rowID, rowID: action.rowID,
values: {}, values: {},
}; };
default: default:
return state; return state;
} }
}; };
const useDataTable = () => { const useDataTable = () => {
const { setReloadDataTable } = useContext(ReloadDataTableContext); const {setReloadDataTable} = useContext(ReloadDataTableContext);
const [state, dispatch] = useReducer(reducer, initialValues); const [state, dispatch] = useReducer(reducer, initialValues);
const [openConfirmDialog, setOpenConfirmDialog] = useState(false); const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
const [openRejectDialog, setOpenRejectDialog] = useState(false); const [openRejectDialog, setOpenRejectDialog] = useState(false);
const [rowId, setRowId] = useState(null); const [rowId, setRowId] = useState(null);
const { directionApp } = useDirection(); const {directionApp} = useDirection();
const t = useTranslations(); const t = useTranslations();
const { token } = useUser(); const {token} = useUser();
const confirmData = useCallback(async (url, rowID, values) => { const confirmData = useCallback(async (url, rowID, values) => {
dispatch({ type: "CONFIRM_DATA", url: url, rowID: rowID, values: values }); dispatch({type: "CONFIRM_DATA", url: url, rowID: rowID, values: values});
axios axios
.post(`${url}/${rowID}`, values, { .post(`${url}/${rowID}`, values, {
headers: { authorization: `Bearer ${token}` }, headers: {authorization: `Bearer ${token}`},
}) })
.then((response) => { .then((response) => {
setReloadDataTable(true); setReloadDataTable(true);
Notifications(directionApp, response, t); Notifications(directionApp, response, t);
}) })
.catch((error) => { .catch((error) => {
Notifications(directionApp, error.response, t); Notifications(directionApp, error.response, t);
}); });
}, []); }, []);
const rejectData = useCallback(async (url, rowID, values) => { const rejectData = useCallback(async (url, rowID, values) => {
dispatch({ type: "REJECT_DATA", url: url, rowID: rowID, values: values }); dispatch({type: "REJECT_DATA", url: url, rowID: rowID, values: values});
axios axios
.post(`${url}/${rowID}`, values, { .post(`${url}/${rowID}`, values, {
headers: { authorization: `Bearer ${token}` }, headers: {authorization: `Bearer ${token}`},
}) })
.then((response) => { .then((response) => {
setReloadDataTable(true); setReloadDataTable(true);
Notifications(directionApp, response, t); Notifications(directionApp, response, t);
}) })
.catch((error) => { .catch((error) => {
console.log(error); console.log(error);
Notifications(directionApp, error.response, t); Notifications(directionApp, error.response, t);
}); });
}, []); }, []);
//Confirm Data //Confirm Data
const handleOpenConfirmDialog = (row) => { const handleOpenConfirmDialog = (row) => {
setRowId(row.getValue("id")); setRowId(row.getValue("id"));
setOpenConfirmDialog(true); setOpenConfirmDialog(true);
}; };
const handleCloseConfirmDialog = () => { const handleCloseConfirmDialog = () => {
setOpenConfirmDialog(false); setOpenConfirmDialog(false);
}; };
//End Confirm Data //End Confirm Data
// Reject Data // Reject Data
const handleOpenRejectDialog = (row) => { const handleOpenRejectDialog = (row) => {
setRowId(row.getValue("id")); setRowId(row.getValue("id"));
setOpenRejectDialog(true); setOpenRejectDialog(true);
}; };
const handleCloseRejectDialog = () => { const handleCloseRejectDialog = () => {
setOpenRejectDialog(false); setOpenRejectDialog(false);
}; };
// End Reject Data // End Reject Data
const contextValue = { const contextValue = {
handleOpenConfirmDialog, handleOpenConfirmDialog,
handleCloseConfirmDialog, handleCloseConfirmDialog,
handleOpenRejectDialog, handleOpenRejectDialog,
handleCloseRejectDialog, handleCloseRejectDialog,
rowId, rowId,
openConfirmDialog, openConfirmDialog,
openRejectDialog, openRejectDialog,
rejectData, rejectData,
confirmData, confirmData,
}; };
return contextValue; return contextValue;
}; };
export default useDataTable; export default useDataTable;

View File

@@ -1,10 +1,10 @@
import { useContext } from "react"; import {useContext} from "react";
import { LanguageContext } from "../contexts/language"; import {LanguageContext} from "../contexts/language";
const useDirection = () => { const useDirection = () => {
const { directionApp } = useContext(LanguageContext); const {directionApp} = useContext(LanguageContext);
return { directionApp }; return {directionApp};
}; };
export default useDirection; export default useDirection;

View File

@@ -1,28 +1,28 @@
import { useContext } from "react"; import {useContext} from "react";
import { LanguageContext } from "../contexts/language"; import {LanguageContext} from "../contexts/language";
import useUser from "./useUser"; import useUser from "./useUser";
const useLanguage = () => { const useLanguage = () => {
const { isAuth, changeUserLanguage } = useUser(); const {isAuth, changeUserLanguage} = useUser();
const { const {
languageApp, languageApp,
setLanguageApp, setLanguageApp,
languageIsReady, languageIsReady,
setLanguageIsReady, setLanguageIsReady,
languageList, languageList,
} = useContext(LanguageContext); } = useContext(LanguageContext);
const changeLanguage = (lang) => { const changeLanguage = (lang) => {
if (lang == languageApp) return; if (lang == languageApp) return;
setLanguageIsReady(false); setLanguageIsReady(false);
setLanguageApp(lang); setLanguageApp(lang);
if (isAuth) { if (isAuth) {
changeUserLanguage(lang); changeUserLanguage(lang);
} }
}; };
return { languageApp, changeLanguage, languageIsReady, languageList }; return {languageApp, changeLanguage, languageIsReady, languageList};
}; };
export default useLanguage; export default useLanguage;

View File

@@ -1,10 +1,10 @@
import { useContext } from "react"; import {useContext} from "react";
import { LoadingContext } from "../contexts/loading"; import {LoadingContext} from "../contexts/loading";
const useLoading = () => { const useLoading = () => {
const { setLoadingPage } = useContext(LoadingContext); const {setLoadingPage} = useContext(LoadingContext);
return { setLoadingPage }; return {setLoadingPage};
}; };
export default useLoading; export default useLoading;

View File

@@ -1,34 +1,34 @@
import { useContext } from "react"; import {useContext} from "react";
import { UserContext } from "../contexts/user"; import {UserContext} from "../contexts/user";
const useUser = () => { const useUser = () => {
const { const {
isAuth, isAuth,
userChangedLanguage, userChangedLanguage,
token, token,
user, user,
getUser, getUser,
clearUser, clearUser,
changeUser, changeUser,
changeAuthState, changeAuthState,
changeLanguageState, changeLanguageState,
clearToken, clearToken,
setToken, setToken,
} = useContext(UserContext); } = useContext(UserContext);
return { return {
isAuth, isAuth,
userChangedLanguage, userChangedLanguage,
token, token,
user, user,
getUser, getUser,
clearUser, clearUser,
changeUser, changeUser,
changeAuthState, changeAuthState,
changeLanguageState, changeLanguageState,
clearToken, clearToken,
setToken, setToken,
}; };
}; };
export default useUser; export default useUser;

View File

@@ -1,43 +1,43 @@
import { NextLinkComposed } from "@/core/components/LinkRouting"; import {NextLinkComposed} from "@/core/components/LinkRouting";
import Message from "@/core/components/Messages"; import Message from "@/core/components/Messages";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
import { Button, Typography } from "@mui/material"; import {Button, Typography} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
const RolePermission = ({ children, requiredPermissions }) => { const RolePermission = ({children, requiredPermissions}) => {
const { user } = useUser(); const {user} = useUser();
const t = useTranslations(); const t = useTranslations();
const hasPermission = requiredPermissions.some((permission) => const hasPermission = requiredPermissions.some((permission) =>
user?.role?.includes(permission) user?.role?.includes(permission)
);
if (!hasPermission) {
return (
<Message
text={
<Typography sx={{ textAlign: "center" }}>
{t("Permission.typography_you_dont_have_access")}
</Typography>
}
actions={
<>
<Button
variant="contained"
component={NextLinkComposed}
to={{
pathname: "/dashboard",
}}
>
{t("Permission.button_back_dashboard")}
</Button>
</>
}
/>
); );
}
return <>{children}</>; if (!hasPermission) {
return (
<Message
text={
<Typography sx={{textAlign: "center"}}>
{t("Permission.typography_you_dont_have_access")}
</Typography>
}
actions={
<>
<Button
variant="contained"
component={NextLinkComposed}
to={{
pathname: "/dashboard",
}}
>
{t("Permission.button_back_dashboard")}
</Button>
</>
}
/>
);
}
return <>{children}</>;
}; };
export default RolePermission; export default RolePermission;

View File

@@ -1,42 +1,42 @@
import { NextLinkComposed } from "@/core/components/LinkRouting"; import {NextLinkComposed} from "@/core/components/LinkRouting";
import Message from "@/core/components/Messages"; import Message from "@/core/components/Messages";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
import { Button, Typography } from "@mui/material"; import {Button, Typography} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import { useRouter } from "next/router"; import {useRouter} from "next/router";
const WithAuthMiddleware = ({ children }) => { const WithAuthMiddleware = ({children}) => {
const { isAuth } = useUser(); const {isAuth} = useUser();
const t = useTranslations(); const t = useTranslations();
const router = useRouter(); const router = useRouter();
if (!isAuth) if (!isAuth)
return ( return (
<Message <Message
text={ text={
<Typography sx={{ textAlign: "center" }}> <Typography sx={{textAlign: "center"}}>
{t( {t(
"Authorization.typography_your_access_to_this_page_has_expired_Please_login_again" "Authorization.typography_your_access_to_this_page_has_expired_Please_login_again"
)} )}
</Typography> </Typography>
} }
actions={ actions={
<> <>
<Button <Button
variant="contained" variant="contained"
component={NextLinkComposed} component={NextLinkComposed}
to={{ to={{
pathname: "/login-expert", pathname: "/login-expert",
query: { back_url: encodeURIComponent(router.asPath) }, query: {back_url: encodeURIComponent(router.asPath)},
}} }}
> >
{t("login")} {t("login")}
</Button> </Button>
</> </>
} }
/> />
); );
return <>{children}</>; return <>{children}</>;
}; };
export default WithAuthMiddleware; export default WithAuthMiddleware;

View File

@@ -1,56 +1,56 @@
import Message from "@/core/components/Messages"; import Message from "@/core/components/Messages";
import useUser from "@/lib/app/hooks/useUser"; import useUser from "@/lib/app/hooks/useUser";
import { Stack, Typography } from "@mui/material"; import {Stack, Typography} from "@mui/material";
import { useTranslations } from "next-intl"; import {useTranslations} from "next-intl";
import { useSearchParams } from "next/navigation"; import {useSearchParams} from "next/navigation";
import { useRouter } from "next/router"; import {useRouter} from "next/router";
import { useEffect } from "react"; import {useEffect} from "react";
const WithoutAuthMiddleware = ({ children }) => { const WithoutAuthMiddleware = ({children}) => {
const { isAuth } = useUser(); const {isAuth} = useUser();
const t = useTranslations(); const t = useTranslations();
const router = useRouter(); const router = useRouter();
// gettin url query // gettin url query
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const backUrlDecodedPath = searchParams.get("back_url"); const backUrlDecodedPath = searchParams.get("back_url");
useEffect(() => { useEffect(() => {
if (!isAuth) return; if (!isAuth) return;
const timer = setTimeout(() => { const timer = setTimeout(() => {
router.replace( router.replace(
backUrlDecodedPath backUrlDecodedPath
? decodeURIComponent(backUrlDecodedPath) ? decodeURIComponent(backUrlDecodedPath)
: "/dashboard" : "/dashboard"
); );
}, 2000); }, 2000);
return () => { return () => {
clearTimeout(timer); clearTimeout(timer);
}; };
}, [isAuth]); }, [isAuth]);
if (isAuth) if (isAuth)
return ( return (
<Message <Message
text={ text={
<Stack alignItems="center" spacing={2}> <Stack alignItems="center" spacing={2}>
<Typography sx={{ textAlign: "center" }}> <Typography sx={{textAlign: "center"}}>
{t( {t(
"Authorization.typography_your_login_is_valid_and_you_do_not_need_to_login_again" "Authorization.typography_your_login_is_valid_and_you_do_not_need_to_login_again"
)} )}
</Typography> </Typography>
<Typography> <Typography>
{t("Authorization.typography_redirect_to")}{" "} {t("Authorization.typography_redirect_to")}{" "}
{backUrlDecodedPath {backUrlDecodedPath
? t("Authorization.typography_routing_previuos_page") ? t("Authorization.typography_routing_previuos_page")
: t("Authorization.typography_routing_dashbaord_page")} : t("Authorization.typography_routing_dashbaord_page")}
</Typography> </Typography>
</Stack> </Stack>
} }
/> />
); );
return <>{children}</>; return <>{children}</>;
}; };
export default WithoutAuthMiddleware; export default WithoutAuthMiddleware;

View File

@@ -1,30 +1,30 @@
import "&/fontiran.scss"; import "&/fontiran.scss";
import AppLayout from "@/layouts/AppLayout"; import AppLayout from "@/layouts/AppLayout";
import MuiLayout from "@/layouts/MuiLayout"; import MuiLayout from "@/layouts/MuiLayout";
import { LanguageProvider } from "@/lib/app/contexts/language"; import {LanguageProvider} from "@/lib/app/contexts/language";
import { LoadingProvider } from "@/lib/app/contexts/loading"; import {LoadingProvider} from "@/lib/app/contexts/loading";
import { UserProvider } from "@/lib/app/contexts/user"; import {UserProvider} from "@/lib/app/contexts/user";
import "moment/locale/fa"; import "moment/locale/fa";
import { NextIntlProvider } from "next-intl"; import {NextIntlProvider} from "next-intl";
const App = ({ Component, pageProps }) => { const App = ({Component, pageProps}) => {
return ( return (
<> <>
<UserProvider> <UserProvider>
<LanguageProvider> <LanguageProvider>
<NextIntlProvider messages={pageProps.messages}> <NextIntlProvider messages={pageProps.messages}>
<MuiLayout isBot={pageProps.isBot}> <MuiLayout isBot={pageProps.isBot}>
<LoadingProvider> <LoadingProvider>
<AppLayout isBot={pageProps.isBot}> <AppLayout isBot={pageProps.isBot}>
<Component {...pageProps} /> <Component {...pageProps} />
</AppLayout> </AppLayout>
</LoadingProvider> </LoadingProvider>
</MuiLayout> </MuiLayout>
</NextIntlProvider> </NextIntlProvider>
</LanguageProvider> </LanguageProvider>
</UserProvider> </UserProvider>
</> </>
); );
}; };
export default App; export default App;

View File

@@ -1,67 +1,65 @@
import { import {createEmotionCacheLtr} from "@/core/utils/createEmotionCache";
createEmotionCacheLtr
} from "@/core/utils/createEmotionCache";
import theme from "@/core/utils/theme"; import theme from "@/core/utils/theme";
import createEmotionServer from "@emotion/server/create-instance"; import createEmotionServer from "@emotion/server/create-instance";
import Document, { Head, Html, Main, NextScript } from "next/document"; import Document, {Head, Html, Main, NextScript} from "next/document";
export default function MyDocument(props) { export default function MyDocument(props) {
const { emotionStyleTags } = props; const {emotionStyleTags} = props;
return ( return (
<Html> <Html>
<Head> <Head>
<meta name="theme-color" content={theme.palette.primary.main} /> <meta name="theme-color" content={theme.palette.primary.main}/>
<meta name="emotion-insertion-point" content="" /> <meta name="emotion-insertion-point" content=""/>
<link rel="shortcut icon" href="/icons/favicon.ico" /> <link rel="shortcut icon" href="/icons/favicon.ico"/>
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json"/>
<link <link
rel="stylesheet" rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/> />
{emotionStyleTags} {emotionStyleTags}
</Head> </Head>
<body> <body>
<Main /> <Main/>
<NextScript /> <NextScript/>
</body> </body>
</Html> </Html>
); );
} }
MyDocument.getInitialProps = async (ctx) => { MyDocument.getInitialProps = async (ctx) => {
const originalRenderPage = ctx.renderPage; const originalRenderPage = ctx.renderPage;
let cache; let cache;
switch (ctx.locale) { switch (ctx.locale) {
case "fa": case "fa":
cache = createEmotionCacheLtr(); cache = createEmotionCacheLtr();
break; break;
} }
const { extractCriticalToChunks } = createEmotionServer(cache); const {extractCriticalToChunks} = createEmotionServer(cache);
ctx.renderPage = () => ctx.renderPage = () =>
originalRenderPage({ originalRenderPage({
enhanceApp: (App) => enhanceApp: (App) =>
function EnhanceApp(props) { function EnhanceApp(props) {
return <App emotionCache={cache} {...props} />; return <App emotionCache={cache} {...props} />;
}, },
}); });
const initialProps = await Document.getInitialProps(ctx); const initialProps = await Document.getInitialProps(ctx);
const emotionStyles = extractCriticalToChunks(initialProps.html); const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => ( const emotionStyleTags = emotionStyles.styles.map((style) => (
<style <style
data-emotion={`${style.key} ${style.ids.join(" ")}`} data-emotion={`${style.key} ${style.ids.join(" ")}`}
key={style.key} key={style.key}
// eslint-disable-next-line react/no-danger // eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }} dangerouslySetInnerHTML={{__html: style.css}}
/> />
)); ));
return { return {
...initialProps, ...initialProps,
emotionStyleTags, emotionStyleTags,
}; };
}; };

View File

@@ -1,23 +1,23 @@
import DashboardChangePasswordComponent from "@/components/dashboard/change-password"; import DashboardChangePasswordComponent from "@/components/dashboard/change-password";
import TitlePage from "@/core/components/TitlePage"; import TitlePage from "@/core/components/TitlePage";
import WithAuthMiddleware from "@/middlewares/WithAuth"; import WithAuthMiddleware from "@/middlewares/WithAuth";
import { parse } from "next-useragent"; import {parse} from "next-useragent";
export default function LoanFollowUp() { export default function LoanFollowUp() {
return ( return (
<WithAuthMiddleware> <WithAuthMiddleware>
<TitlePage text="Dashboard.change_password" /> <TitlePage text="Dashboard.change_password"/>
<DashboardChangePasswordComponent /> <DashboardChangePasswordComponent/>
</WithAuthMiddleware> </WithAuthMiddleware>
); );
} }
export async function getServerSideProps({ req, locale }) { export async function getServerSideProps({req, locale}) {
const { isBot } = parse(req.headers["user-agent"]); const {isBot} = parse(req.headers["user-agent"]);
return { return {
props: { props: {
messages: (await import(`&/locales/${locale}/app.json`)).default, messages: (await import(`&/locales/${locale}/app.json`)).default,
isBot, isBot,
}, },
}; };
} }

View File

@@ -1,23 +1,23 @@
import DashboardEditProfile from "@/components/dashboard/edit-profile"; import DashboardEditProfile from "@/components/dashboard/edit-profile";
import TitlePage from "@/core/components/TitlePage"; import TitlePage from "@/core/components/TitlePage";
import WithAuthMiddleware from "@/middlewares/WithAuth"; import WithAuthMiddleware from "@/middlewares/WithAuth";
import { parse } from "next-useragent"; import {parse} from "next-useragent";
export default function LoanFollowUp() { export default function LoanFollowUp() {
return ( return (
<WithAuthMiddleware> <WithAuthMiddleware>
<TitlePage text="Dashboard.edit_profile" /> <TitlePage text="Dashboard.edit_profile"/>
<DashboardEditProfile /> <DashboardEditProfile/>
</WithAuthMiddleware> </WithAuthMiddleware>
); );
} }
export async function getServerSideProps({ req, locale }) { export async function getServerSideProps({req, locale}) {
const { isBot } = parse(req.headers["user-agent"]); const {isBot} = parse(req.headers["user-agent"]);
return { return {
props: { props: {
messages: (await import(`&/locales/${locale}/app.json`)).default, messages: (await import(`&/locales/${locale}/app.json`)).default,
isBot, isBot,
}, },
}; };
} }

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