landing + single company + auth
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
VITE_TOKEN_NAME = 'dzone_token'
|
VITE_TOKEN_NAME = 'dzone_token'
|
||||||
VITE_REFRESH_TOKEN_NAME = 'dzone_refresh_token'
|
VITE_REFRESH_TOKEN_NAME = 'dzone_refresh_token'
|
||||||
VITE_BASE_URL = 'https://dzone-api.danakcorp.com'
|
# VITE_BASE_URL = 'https://dzone-api.danakcorp.com'
|
||||||
# VITE_BASE_URL = 'http://192.168.1.109:4000'
|
VITE_BASE_URL = 'http://192.168.1.113:4001'
|
||||||
|
|
||||||
|
VITE_SLUG_ID = 'slug_id'
|
||||||
Generated
+61
@@ -16,12 +16,14 @@
|
|||||||
"i18next": "^24.2.1",
|
"i18next": "^24.2.1",
|
||||||
"iconsax-react": "^0.0.8",
|
"iconsax-react": "^0.0.8",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
|
"moment-jalaali": "^0.10.4",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-dropzone": "^14.3.5",
|
"react-dropzone": "^14.3.5",
|
||||||
"react-i18next": "^15.4.0",
|
"react-i18next": "^15.4.0",
|
||||||
"react-loading": "^2.0.3",
|
"react-loading": "^2.0.3",
|
||||||
"react-multi-date-picker": "^4.5.2",
|
"react-multi-date-picker": "^4.5.2",
|
||||||
|
"react-otp-input": "^3.1.1",
|
||||||
"react-router-dom": "^7.1.3",
|
"react-router-dom": "^7.1.3",
|
||||||
"react-toastify": "^11.0.3",
|
"react-toastify": "^11.0.3",
|
||||||
"swiper": "^11.2.1",
|
"swiper": "^11.2.1",
|
||||||
@@ -32,6 +34,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
|
"@types/moment-jalaali": "^0.7.9",
|
||||||
"@types/react": "^18.3.18",
|
"@types/react": "^18.3.18",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
"@types/swiper": "^5.4.3",
|
"@types/swiper": "^5.4.3",
|
||||||
@@ -1864,6 +1867,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/moment-jalaali": {
|
||||||
|
"version": "0.7.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/moment-jalaali/-/moment-jalaali-0.7.9.tgz",
|
||||||
|
"integrity": "sha512-gsDOoAzRnCfQTbfdlUrCvX6R0wIto6CvwfvV2C3j4qJLK+DEiTK8Rl/xlOCBO9C6qeUfX8oyZ2UfjnXJTOvHSA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": ">=2.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/prop-types": {
|
"node_modules/@types/prop-types": {
|
||||||
"version": "15.7.14",
|
"version": "15.7.14",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
||||||
@@ -3516,6 +3529,12 @@
|
|||||||
"@pkgjs/parseargs": "^0.11.0"
|
"@pkgjs/parseargs": "^0.11.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jalaali-js": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-Jl/EwY84JwjW2wsWqeU4pNd22VNQ7EkjI36bDuLw31wH98WQW4fPjD0+mG7cdCK+Y8D6s9R3zLiQ3LaKu6bD8A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/jiti": {
|
"node_modules/jiti": {
|
||||||
"version": "1.21.7",
|
"version": "1.21.7",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
||||||
@@ -3776,6 +3795,38 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.30.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moment-jalaali": {
|
||||||
|
"version": "0.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment-jalaali/-/moment-jalaali-0.10.4.tgz",
|
||||||
|
"integrity": "sha512-/eD0HeyvATznb5iE0G1BHjKRZAFEpJ9ZNUkcHwXhNgt1WJJVVzHD7+uDmqzZWVFLdbGme2gvIXKb3ezDYOXcZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"jalaali-js": "^1.2.7",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"moment-timezone": "^0.5.46"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moment-timezone": {
|
||||||
|
"version": "0.5.48",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz",
|
||||||
|
"integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": "^2.29.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -4370,6 +4421,16 @@
|
|||||||
"react-dom": ">=16.8.0"
|
"react-dom": ">=16.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-otp-input": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-otp-input/-/react-otp-input-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-bjPavgJ0/Zmf/AYi4onj8FbH93IjeD+e8pWwxIJreDEWsU1ILR5fs8jEJmMGWSBe/yyvPP6X/W6Mk9UkOCkTPw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.6 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": ">=16.8.6 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.14.2",
|
"version": "0.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"i18next": "^24.2.1",
|
"i18next": "^24.2.1",
|
||||||
"iconsax-react": "^0.0.8",
|
"iconsax-react": "^0.0.8",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
|
"moment-jalaali": "^0.10.4",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-dropzone": "^14.3.5",
|
"react-dropzone": "^14.3.5",
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
|
"@types/moment-jalaali": "^0.7.9",
|
||||||
"@types/react": "^18.3.18",
|
"@types/react": "^18.3.18",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
"@types/swiper": "^5.4.3",
|
"@types/swiper": "^5.4.3",
|
||||||
|
|||||||
+5
-33
@@ -1,18 +1,16 @@
|
|||||||
import MainRouter from './router/Main'
|
import MainRouter from './router/Main'
|
||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
import { I18nextProvider } from 'react-i18next'
|
import { I18nextProvider } from 'react-i18next'
|
||||||
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
|
import { BrowserRouter, Routes, Route } from 'react-router-dom'
|
||||||
import 'swiper/swiper-bundle.css';
|
import 'swiper/swiper-bundle.css';
|
||||||
import FaJson from './langs/fa.json'
|
import FaJson from './langs/fa.json'
|
||||||
import { useState } from 'react';
|
|
||||||
import { Pages } from './config/Pages';
|
|
||||||
import { getRefreshToken, getToken, removeRefreshToken, removeToken, setRefreshToken, setToken } from './config/func';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import AuthRouter from './router/Auth';
|
import AuthRouter from './router/Auth';
|
||||||
import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { IApiErrorRepsonse } from './types/error.types';
|
import { IApiErrorRepsonse } from './types/error.types';
|
||||||
import { refreshToken } from './pages/auth/service/AuthService';
|
import { refreshToken } from './pages/auth/service/AuthService';
|
||||||
import ToastContainer from './components/Toast';
|
import ToastContainer from './components/Toast';
|
||||||
|
import { getRefreshToken, removeRefreshToken, removeToken, setRefreshToken, setToken } from './config/func';
|
||||||
|
|
||||||
i18next.init({
|
i18next.init({
|
||||||
interpolation: { escapeValue: false },
|
interpolation: { escapeValue: false },
|
||||||
lng: 'fa',
|
lng: 'fa',
|
||||||
@@ -30,23 +28,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
const [isLogin, setIsLogin] = useState<'checking' | 'isLogin' | 'isNotLogin'>('checking')
|
|
||||||
useEffect(() => {
|
|
||||||
const checkLogin = async () => {
|
|
||||||
const token = await getToken();
|
|
||||||
if (token || true) {
|
|
||||||
setIsLogin('isLogin')
|
|
||||||
} else {
|
|
||||||
setIsLogin('isNotLogin')
|
|
||||||
if (window.location.href.split('auth').length === 1) {
|
|
||||||
window.location.href = Pages.auth.login
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkLogin();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
queryCache: new QueryCache({
|
queryCache: new QueryCache({
|
||||||
onError: async (error: IApiErrorRepsonse) => {
|
onError: async (error: IApiErrorRepsonse) => {
|
||||||
@@ -105,7 +86,6 @@ function App() {
|
|||||||
queries: {
|
queries: {
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
retry: false
|
retry: false
|
||||||
// staleTime: 86400000 // 1 day in milliseconds
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -115,19 +95,10 @@ function App() {
|
|||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<I18nextProvider i18n={i18next}>
|
<I18nextProvider i18n={i18next}>
|
||||||
{
|
|
||||||
isLogin === 'checking' ?
|
|
||||||
null
|
|
||||||
:
|
|
||||||
isLogin === 'isLogin' ?
|
|
||||||
<Routes>
|
<Routes>
|
||||||
|
<Route path="/:slug/auth/*" element={<AuthRouter />} />
|
||||||
<Route path="/:slug/*" element={<MainRouter />} />
|
<Route path="/:slug/*" element={<MainRouter />} />
|
||||||
<Route path="/" element={<Navigate to="/fa/dashboard" replace />} />
|
|
||||||
<Route path="/auth/*" element={<AuthRouter />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
:
|
|
||||||
<AuthRouter />
|
|
||||||
}
|
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
@@ -135,4 +106,5 @@ function App() {
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
|||||||
+3
-4
@@ -1,8 +1,7 @@
|
|||||||
export const Pages = {
|
export const Pages = {
|
||||||
auth: {
|
auth: {
|
||||||
login: "auth/login",
|
login: "login",
|
||||||
register: "auth/register",
|
register: "register",
|
||||||
forgotPassword: "auth/forgot",
|
|
||||||
},
|
},
|
||||||
dashboard: "dashboard",
|
dashboard: "dashboard",
|
||||||
services: {
|
services: {
|
||||||
@@ -25,7 +24,7 @@ export const Pages = {
|
|||||||
detail: "announcement/",
|
detail: "announcement/",
|
||||||
},
|
},
|
||||||
company: {
|
company: {
|
||||||
detail: "company/detail/",
|
detail: "company/",
|
||||||
},
|
},
|
||||||
grade: {
|
grade: {
|
||||||
records: "grade-records",
|
records: "grade-records",
|
||||||
|
|||||||
+3
-1
@@ -16,8 +16,10 @@ axiosInstance.interceptors.response.use(
|
|||||||
axiosInstance.interceptors.request.use(async function (config) {
|
axiosInstance.interceptors.request.use(async function (config) {
|
||||||
const tokenValue = await getToken();
|
const tokenValue = await getToken();
|
||||||
const token = tokenValue ? tokenValue : "";
|
const token = tokenValue ? tokenValue : "";
|
||||||
|
const slug = localStorage.getItem(import.meta.env.VITE_SLUG_ID);
|
||||||
|
const slug_parse = slug ? JSON.parse(slug) : null;
|
||||||
config.headers.Authorization = "Bearer " + token;
|
config.headers.Authorization = "Bearer " + token;
|
||||||
|
config.headers["x-business-id"] = slug_parse?.danakSubscriptionId;
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -32,7 +32,7 @@ export const timeAgo = (date: string | Date): string => {
|
|||||||
return "همین الان";
|
return "همین الان";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getToken = () => {
|
export const getToken = async () => {
|
||||||
return Cookie.get(import.meta.env.VITE_TOKEN_NAME);
|
return Cookie.get(import.meta.env.VITE_TOKEN_NAME);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,8 +40,7 @@ export const getRefreshToken = () => {
|
|||||||
return Cookie.get(import.meta.env.VITE_REFRESH_TOKEN_NAME);
|
return Cookie.get(import.meta.env.VITE_REFRESH_TOKEN_NAME);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isProduction = import.meta.env.MODE === "production";
|
const domain = window.location.hostname;
|
||||||
const domain = isProduction ? ".danakcorp.com" : undefined;
|
|
||||||
|
|
||||||
export const setToken = (token: string) => {
|
export const setToken = (token: string) => {
|
||||||
Cookie.set(import.meta.env.VITE_TOKEN_NAME, token, { domain });
|
Cookie.set(import.meta.env.VITE_TOKEN_NAME, token, { domain });
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenates truthy classes into a space-separated string.
|
* Concatenates truthy classes into a space-separated string.
|
||||||
@@ -9,3 +10,12 @@ import { twMerge } from "tailwind-merge";
|
|||||||
export const clx = (...classes: (string | boolean | undefined)[]): string => {
|
export const clx = (...classes: (string | boolean | undefined)[]): string => {
|
||||||
return twMerge(classes.filter(Boolean).join(" "));
|
return twMerge(classes.filter(Boolean).join(" "));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const buildPath = (path: string, slug2?: string): string => {
|
||||||
|
if (slug2) {
|
||||||
|
return `/${slug2}/${path}`;
|
||||||
|
} else {
|
||||||
|
const { slug } = useParams() as { slug: string };
|
||||||
|
return `/${slug}/${path}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { FC, useEffect } from 'react'
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
|
import { getToken } from '../config/func'
|
||||||
|
import { buildPath } from '../helpers/utils'
|
||||||
|
|
||||||
|
// تایپ برای کامپوننتهای React
|
||||||
|
type ComponentType = FC<any>
|
||||||
|
|
||||||
|
// HOC برای محافظت از کامپوننتهای نیازمند لاگین
|
||||||
|
const withAuth = (WrappedComponent: ComponentType): ComponentType => {
|
||||||
|
// کامپوننت جدید که لاگین را چک میکند
|
||||||
|
const WithAuthComponent: FC<any> = (props) => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const { slug } = useParams()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkAuth = async () => {
|
||||||
|
const token = await getToken()
|
||||||
|
if (!token) {
|
||||||
|
const loginPath = buildPath('auth/login', slug)
|
||||||
|
navigate(loginPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAuth()
|
||||||
|
}, [navigate, slug])
|
||||||
|
|
||||||
|
// کامپوننت اصلی را با props خودش رندر کن
|
||||||
|
return <WrappedComponent {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
// نام نمایشی کامپوننت را برای دیباگ بهتر تنظیم کن
|
||||||
|
WithAuthComponent.displayName = `WithAuth(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`
|
||||||
|
|
||||||
|
return WithAuthComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withAuth
|
||||||
@@ -12,6 +12,7 @@ import { Pages } from '../../../config/Pages'
|
|||||||
import { isEmail } from '../../../config/func'
|
import { isEmail } from '../../../config/func'
|
||||||
import { useCheckHasAccount, useLoginWithOtp } from '../hooks/useAuthData'
|
import { useCheckHasAccount, useLoginWithOtp } from '../hooks/useAuthData'
|
||||||
import { ErrorType } from '../../../helpers/types'
|
import { ErrorType } from '../../../helpers/types'
|
||||||
|
import { ArrowLeft } from 'iconsax-react'
|
||||||
import { toast } from '../../../components/Toast'
|
import { toast } from '../../../components/Toast'
|
||||||
const LoginStep1: FC = () => {
|
const LoginStep1: FC = () => {
|
||||||
|
|
||||||
@@ -101,6 +102,13 @@ const LoginStep1: FC = () => {
|
|||||||
<div>{t('auth.register')}</div>
|
<div>{t('auth.register')}</div>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a href='https://accounts.danakcorp.com' target='_blank' className='mt-4 flex gap-1 text-sm justify-center text-description'>
|
||||||
|
<div>
|
||||||
|
{t('auth.old_version')}
|
||||||
|
</div>
|
||||||
|
<ArrowLeft size={20} color='black' />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,16 @@ import { useLoginWithOtp, useOtpVerify } from '../hooks/useAuthData'
|
|||||||
import { ErrorType } from '../../../helpers/types'
|
import { ErrorType } from '../../../helpers/types'
|
||||||
import { setRefreshToken } from '../../../config/func'
|
import { setRefreshToken } from '../../../config/func'
|
||||||
import { setToken } from '../../../config/func'
|
import { setToken } from '../../../config/func'
|
||||||
import { getRedirectUrl } from '../../../config/func'
|
|
||||||
import { toast } from '../../../components/Toast'
|
import { toast } from '../../../components/Toast'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
|
import { buildPath } from '../../../helpers/utils'
|
||||||
|
|
||||||
const LoginStep2: FC = () => {
|
const LoginStep2: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const { phone, setStepLogin } = useAuthStore()
|
const { phone, setStepLogin } = useAuthStore()
|
||||||
const { value, reset } = useCountDown(2, true)
|
const { value, reset } = useCountDown(2, true)
|
||||||
|
const { slug } = useParams()
|
||||||
const otpVerify = useOtpVerify()
|
const otpVerify = useOtpVerify()
|
||||||
const loginWithOtp = useLoginWithOtp()
|
const loginWithOtp = useLoginWithOtp()
|
||||||
|
|
||||||
@@ -41,13 +44,7 @@ const LoginStep2: FC = () => {
|
|||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
setToken(data?.data?.accessToken?.token)
|
setToken(data?.data?.accessToken?.token)
|
||||||
setRefreshToken(data?.data?.refreshToken?.token)
|
setRefreshToken(data?.data?.refreshToken?.token)
|
||||||
// Check if there's a redirect parameter in the URL
|
window.location.href = buildPath(Pages.dashboard, slug)
|
||||||
const redirectUrl = getRedirectUrl();
|
|
||||||
if (redirectUrl) {
|
|
||||||
window.location.href = redirectUrl;
|
|
||||||
return; // Prevent the default navigation
|
|
||||||
}
|
|
||||||
window.location.href = Pages.dashboard
|
|
||||||
},
|
},
|
||||||
onError(error: ErrorType) {
|
onError(error: ErrorType) {
|
||||||
toast(error?.response?.data?.error?.message[0], 'error')
|
toast(error?.response?.data?.error?.message[0], 'error')
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import { Link } from 'react-router-dom'
|
|||||||
import { Pages } from '../../../config/Pages'
|
import { Pages } from '../../../config/Pages'
|
||||||
import ArrowLeftIcon from '../../../assets/images/arrow-left.svg'
|
import ArrowLeftIcon from '../../../assets/images/arrow-left.svg'
|
||||||
import { useLoginWithPassword } from '../hooks/useAuthData'
|
import { useLoginWithPassword } from '../hooks/useAuthData'
|
||||||
|
import { toast } from '../../../components/Toast'
|
||||||
import { ErrorType } from '../../../helpers/types'
|
import { ErrorType } from '../../../helpers/types'
|
||||||
import { setRefreshToken } from '../../../config/func'
|
import { setRefreshToken } from '../../../config/func'
|
||||||
import { setToken } from '../../../config/func'
|
import { setToken } from '../../../config/func'
|
||||||
import { getRedirectUrl } from '../../../config/func'
|
import { getRedirectUrl } from '../../../config/func'
|
||||||
import { toast } from '../../../components/Toast'
|
|
||||||
|
|
||||||
const LoginStep3: FC = () => {
|
const LoginStep3: FC = () => {
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { useAuthStore } from '../store/AuthStore'
|
|||||||
import Error from '../../../components/Error'
|
import Error from '../../../components/Error'
|
||||||
import Button from '../../../components/Button'
|
import Button from '../../../components/Button'
|
||||||
import { useCheckHasAccountRegister, useLoginWithOtp } from '../hooks/useAuthData'
|
import { useCheckHasAccountRegister, useLoginWithOtp } from '../hooks/useAuthData'
|
||||||
import { toast } from '../../../components/Toast'
|
import { toast } from 'react-toastify'
|
||||||
import { ErrorType } from '../../../helpers/types'
|
import { ErrorType } from '../../../helpers/types'
|
||||||
|
|
||||||
const RegisterStep1: FC = () => {
|
const RegisterStep1: FC = () => {
|
||||||
@@ -33,13 +33,12 @@ const RegisterStep1: FC = () => {
|
|||||||
setStepLogin(2)
|
setStepLogin(2)
|
||||||
},
|
},
|
||||||
onError(error: ErrorType) {
|
onError(error: ErrorType) {
|
||||||
toast(error?.response?.data?.error?.message[0], 'error')
|
toast.error(error?.response?.data?.error?.message[0])
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex-1'>
|
<div className='flex-1'>
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ import { toast } from 'react-toastify'
|
|||||||
import { Pages } from '../../../config/Pages'
|
import { Pages } from '../../../config/Pages'
|
||||||
import { setToken } from '../../../config/func'
|
import { setToken } from '../../../config/func'
|
||||||
import { setRefreshToken } from '../../../config/func'
|
import { setRefreshToken } from '../../../config/func'
|
||||||
import { getRedirectUrl } from '../../../config/func'
|
import { useParams } from 'react-router-dom'
|
||||||
|
|
||||||
const RegisterStep2: FC = () => {
|
const RegisterStep2: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const { phone } = useAuthStore()
|
const { phone } = useAuthStore()
|
||||||
const register = useRegister()
|
const register = useRegister()
|
||||||
|
const { slug } = useParams()
|
||||||
|
|
||||||
const formik = useFormik<RegisterType>({
|
const formik = useFormik<RegisterType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
@@ -51,12 +53,7 @@ const RegisterStep2: FC = () => {
|
|||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
setToken(data?.data?.accessToken?.token)
|
setToken(data?.data?.accessToken?.token)
|
||||||
setRefreshToken(data?.data?.refreshToken?.token)
|
setRefreshToken(data?.data?.refreshToken?.token)
|
||||||
const redirectUrl = getRedirectUrl();
|
window.location.href = `/${slug}/${Pages.dashboard}`
|
||||||
if (redirectUrl) {
|
|
||||||
window.location.href = redirectUrl;
|
|
||||||
return; // Prevent the default navigation
|
|
||||||
}
|
|
||||||
window.location.href = Pages.dashboard
|
|
||||||
},
|
},
|
||||||
onError(error: ErrorType) {
|
onError(error: ErrorType) {
|
||||||
toast.error(error?.response?.data?.error?.message[0])
|
toast.error(error?.response?.data?.error?.message[0])
|
||||||
@@ -65,6 +62,7 @@ const RegisterStep2: FC = () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className='mt-5 rowTwoInput'>
|
<div className='mt-5 rowTwoInput'>
|
||||||
|
|||||||
@@ -1,22 +1,29 @@
|
|||||||
import { Calendar, CallCalling, Location, Map1, MessageMinus, Profile, Setting2, Sms } from 'iconsax-react'
|
import { Calendar, CallCalling, Location, Map1, MessageMinus, Profile, Setting2, Sms } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
|
import { useGetCompanyDetail } from '../home/hooks/useHomeData'
|
||||||
|
import moment from 'moment-jalaali'
|
||||||
|
import Products from './components/Products'
|
||||||
const CompanyDetail: FC = () => {
|
const CompanyDetail: FC = () => {
|
||||||
|
|
||||||
|
const { id } = useParams()
|
||||||
|
const { data } = useGetCompanyDetail(id || '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4 pb-20'>
|
<div className='mt-4 pb-20'>
|
||||||
<img
|
<img
|
||||||
src='https://picsum.photos/200/300'
|
src={data?.data?.company?.coverImageUrl}
|
||||||
className='w-full h-[220px] rounded-xl'
|
className='w-full h-[220px] rounded-xl'
|
||||||
/>
|
/>
|
||||||
<div className='p-6 bg-white rounded-xl -mt-4 relative'>
|
<div className='p-6 bg-white rounded-xl -mt-4 relative'>
|
||||||
<div className='flex gap-3'>
|
<div className='flex gap-3'>
|
||||||
<img
|
<img
|
||||||
src='https://picsum.photos/200/300'
|
src={data?.data?.company?.profileImageUrl}
|
||||||
className='size-16 rounded-xl object-cover'
|
className='size-16 rounded-xl object-cover'
|
||||||
/>
|
/>
|
||||||
<div className='flex-1 items-center flex justify-between '>
|
<div className='flex-1 items-center flex justify-between '>
|
||||||
<div className='text-sm'>
|
<div className='text-sm'>
|
||||||
شرکت شماره ۱
|
{data?.data?.company?.name}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-1.5 text-xs'>
|
<div className='flex items-center gap-1.5 text-xs'>
|
||||||
<Map1
|
<Map1
|
||||||
@@ -38,7 +45,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
مدیرعامل: مهرداد مظفری
|
مدیرعامل: {data?.data?.company?.user?.fullName}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-1 items-center'>
|
<div className='flex gap-1 items-center'>
|
||||||
@@ -47,7 +54,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
صنعت : نفت و گاز
|
صنعت : {data?.data?.company?.industry?.title}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,7 +65,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
شماره تماس : ۰۸۶۳۲۳۴۵۶۷۸
|
شماره تماس : {data?.data?.company?.user?.phone}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-1 items-center'>
|
<div className='flex gap-1 items-center'>
|
||||||
@@ -67,7 +74,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
شناسه ملی : ۱۲۳۴
|
شناسه ملی : {data?.data?.company?.identificationNumber}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,7 +85,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
ایمیل : info@example.com
|
ایمیل : {data?.data?.company?.user?.email}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-1 items-center'>
|
<div className='flex gap-1 items-center'>
|
||||||
@@ -87,7 +94,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
تاریخ تاسیس : ۱۳۸۹
|
تاریخ تاسیس : {moment(data?.data?.company?.dateOfEstablishment, 'jYYYY/jMM/jDD').format('jYYYY/jMM/jDD')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,50 +105,13 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
آدرس : لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
|
آدرس : {data?.data?.company?.address}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6 flex justify-center gap-2 items-center'>
|
<Products />
|
||||||
<div className='flex h-8 bg-white px-6 items-center rounded-full text-xs border-2 border-white pt-[1px]'>
|
|
||||||
محصولات
|
|
||||||
</div>
|
|
||||||
<div className='flex h-8 bg-white px-6 items-center rounded-full text-xs border-2 border-white bg-opacity-45 pt-[1px]'>
|
|
||||||
خدمات
|
|
||||||
</div>
|
|
||||||
<div className='flex h-8 bg-white px-6 items-center rounded-full text-xs border-2 border-white bg-opacity-45 pt-[1px]'>
|
|
||||||
گالری
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex gap-2 flex-wrap mt-6'>
|
|
||||||
<div className='flex-1 gap-2.5 bg-white min-w-[28%] h-20 flex items-center p-2 rounded-xl'>
|
|
||||||
<div className='size-12 rounded-xl bg-red-100'></div>
|
|
||||||
<div className='text-xs'>
|
|
||||||
<div>
|
|
||||||
شیر صنعتی
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex-1 gap-2.5 bg-white min-w-[28%] h-20 flex items-center p-2 rounded-xl'>
|
|
||||||
<div className='size-12 rounded-xl bg-red-100'></div>
|
|
||||||
<div className='text-xs'>
|
|
||||||
<div>
|
|
||||||
شیر صنعتی
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex-1 gap-2.5 bg-white min-w-[28%] h-20 flex items-center p-2 rounded-xl'>
|
|
||||||
<div className='size-12 rounded-xl bg-red-100'></div>
|
|
||||||
<div className='text-xs'>
|
|
||||||
<div>
|
|
||||||
شیر صنعتی
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { FC, useState } from 'react'
|
||||||
|
import { useGetCompanyDetail } from '../../home/hooks/useHomeData'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
|
import { ProductItemType } from '../types/CompanyTypes'
|
||||||
|
const Products: FC = () => {
|
||||||
|
|
||||||
|
const { id } = useParams()
|
||||||
|
const { data } = useGetCompanyDetail(id || '')
|
||||||
|
const [activeTab, setActiveTab] = useState<'products' | 'services' | 'gallery'>('products')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='mt-6 flex justify-center gap-2 items-center'>
|
||||||
|
<div className={`flex cursor-pointer h-8 bg-white px-6 items-center rounded-full text-xs border-2 border-white pt-[1px] ${activeTab === 'products' ? 'bg-opacity-100' : 'bg-opacity-45'}`} onClick={() => setActiveTab('products')}>
|
||||||
|
محصولات
|
||||||
|
</div>
|
||||||
|
<div className={`flex cursor-pointer h-8 bg-white px-6 items-center rounded-full text-xs border-2 border-white pt-[1px] ${activeTab === 'services' ? 'bg-opacity-100' : 'bg-opacity-45'}`} onClick={() => setActiveTab('services')}>
|
||||||
|
خدمات
|
||||||
|
</div>
|
||||||
|
{/* <div className={`flex cursor-pointer h-8 bg-white px-6 items-center rounded-full text-xs border-2 border-white pt-[1px] ${activeTab === 'gallery' ? 'bg-opacity-100' : 'bg-opacity-45'}`} onClick={() => setActiveTab('gallery')}>
|
||||||
|
گالری
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
activeTab === 'products' && (
|
||||||
|
<div className='flex gap-2 flex-wrap mt-6'>
|
||||||
|
{
|
||||||
|
data?.data?.company?.products?.map((item: ProductItemType) => (
|
||||||
|
<div className='flex-1 gap-2.5 bg-white min-w-[28%] h-20 flex items-center p-2 rounded-xl'>
|
||||||
|
<div className='size-12 rounded-xl overflow-hidden'>
|
||||||
|
<img src={item.imageUrl} alt={item.title} className='w-full h-full object-cover' />
|
||||||
|
</div>
|
||||||
|
<div className='text-xs'>
|
||||||
|
<div>
|
||||||
|
{item.title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
activeTab === 'services' && (
|
||||||
|
<div className='flex gap-2 flex-wrap mt-6'>
|
||||||
|
{
|
||||||
|
data?.data?.company?.services?.map((item: ProductItemType) => (
|
||||||
|
<div className='flex-1 gap-2.5 bg-white min-w-[28%] h-20 flex items-center p-2 rounded-xl'>
|
||||||
|
<div className='size-12 rounded-xl overflow-hidden'>
|
||||||
|
<img src={item.imageUrl} alt={item.title} className='w-full h-full object-cover' />
|
||||||
|
</div>
|
||||||
|
<div className='text-xs'>
|
||||||
|
<div>
|
||||||
|
{item.title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Products
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export type ProductItemType = {
|
||||||
|
id: string;
|
||||||
|
imageUrl: string;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
+38
-75
@@ -1,13 +1,19 @@
|
|||||||
import { Building, InfoCircle } from 'iconsax-react'
|
import { Building, InfoCircle } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { clx } from '../../helpers/utils'
|
import { buildPath, clx } from '../../helpers/utils'
|
||||||
import StatusWithText from '../../components/StatusWithText'
|
import StatusWithText from '../../components/StatusWithText'
|
||||||
import TitleSeeAll from '../../components/TitleSeeAll'
|
import TitleSeeAll from '../../components/TitleSeeAll'
|
||||||
|
import { useGetCompanies, useGetIndustry } from './hooks/useHomeData'
|
||||||
|
import { CompanyItemType, IndustryItemType } from './types/HomeTypes'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import { Pages } from '../../config/Pages'
|
||||||
const Home: FC = () => {
|
const Home: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
|
const [activeIndustry, setActiveIndustry] = useState<string>('')
|
||||||
|
const { data: industry } = useGetIndustry()
|
||||||
|
const { data: companies } = useGetCompanies(activeIndustry)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className='mt-4'>
|
||||||
@@ -70,7 +76,10 @@ const Home: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-2 mt-4 overflowX'>
|
<div className='flex gap-2 mt-4 overflowX'>
|
||||||
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
|
<div className={clx(
|
||||||
|
'h-9 bg-white rounded-full px-5 flex gap-2 items-center cursor-pointer',
|
||||||
|
activeIndustry === '' ? 'bg-opacity-100' : 'bg-opacity-50'
|
||||||
|
)} onClick={() => setActiveIndustry('')}>
|
||||||
<Building
|
<Building
|
||||||
size={20}
|
size={20}
|
||||||
color='black'
|
color='black'
|
||||||
@@ -79,89 +88,43 @@ const Home: FC = () => {
|
|||||||
همه
|
همه
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
|
{
|
||||||
<Building
|
industry?.data?.industries?.map((item: IndustryItemType) => {
|
||||||
size={20}
|
return (
|
||||||
color='black'
|
<div key={item.id} className={clx(
|
||||||
/>
|
'h-9 bg-white rounded-full px-5 flex gap-2 items-center cursor-pointer',
|
||||||
|
activeIndustry === item.id ? 'bg-opacity-100' : 'bg-opacity-50'
|
||||||
|
)} onClick={() => setActiveIndustry(item.id)}>
|
||||||
|
<img src={item.iconUrl} alt={item.title} className='size-5' />
|
||||||
<div className='text-sm'>
|
<div className='text-sm'>
|
||||||
همه
|
{item.title}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
|
|
||||||
<Building
|
|
||||||
size={20}
|
|
||||||
color='black'
|
|
||||||
/>
|
|
||||||
<div className='text-sm'>
|
|
||||||
همه
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
|
|
||||||
<Building
|
|
||||||
size={20}
|
|
||||||
color='black'
|
|
||||||
/>
|
|
||||||
<div className='text-sm'>
|
|
||||||
همه
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='h-9 bg-white rounded-full px-5 flex gap-2 items-center'>
|
|
||||||
<Building
|
|
||||||
size={20}
|
|
||||||
color='black'
|
|
||||||
/>
|
|
||||||
<div className='text-sm'>
|
|
||||||
همه
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6 flex gap-4 flex-wrap'>
|
<div className='mt-6 flex gap-4 flex-wrap'>
|
||||||
<div className='flex-1 gap-2.5 bg-white min-w-[40%] h-20 flex items-center p-2 rounded-xl'>
|
{
|
||||||
<div className='size-[64px] rounded-xl bg-red-100'></div>
|
companies?.data?.companies?.map((item: CompanyItemType) => {
|
||||||
|
return (
|
||||||
|
<Link to={buildPath(Pages.company.detail + item.id)} key={item.id} className='flex-1 gap-2.5 bg-white min-w-[40%] h-20 flex items-center p-2 rounded-xl'>
|
||||||
|
<div className='size-[64px] rounded-xl overflow-hidden'>
|
||||||
|
<img src={item.profileImageUrl} alt={item.name} className='size-full object-cover' />
|
||||||
|
</div>
|
||||||
<div className='text-xs'>
|
<div className='text-xs'>
|
||||||
<div>
|
<div>
|
||||||
شرکت
|
{item.name}
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description mt-2'>
|
<div className='text-description mt-2'>
|
||||||
شهرک شماره ۱
|
{item.address}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex-1 gap-2.5 bg-white min-w-[40%] h-20 flex items-center p-2 rounded-xl'>
|
|
||||||
<div className='size-[64px] rounded-xl bg-red-100'></div>
|
|
||||||
<div className='text-xs'>
|
|
||||||
<div>
|
|
||||||
هنرمند
|
|
||||||
</div>
|
|
||||||
<div className='text-description mt-2'>
|
|
||||||
درامز
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex-1 gap-2.5 bg-white min-w-[40%] h-20 flex items-center p-2 rounded-xl'>
|
|
||||||
<div className='size-[64px] rounded-xl bg-red-100'></div>
|
|
||||||
<div className='text-xs'>
|
|
||||||
<div>
|
|
||||||
هنرمند
|
|
||||||
</div>
|
|
||||||
<div className='text-description mt-2'>
|
|
||||||
درامز
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex-1 gap-2.5 bg-white min-w-[40%] h-20 flex items-center p-2 rounded-xl'>
|
|
||||||
<div className='size-[64px] rounded-xl bg-red-100'></div>
|
|
||||||
<div className='text-xs'>
|
|
||||||
<div>
|
|
||||||
هنرمند
|
|
||||||
</div>
|
|
||||||
<div className='text-description mt-2'>
|
|
||||||
درامز
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
|
import {
|
||||||
|
getCompanies,
|
||||||
|
getIdWithSlug,
|
||||||
|
getIndustry,
|
||||||
|
getCompanyDetail,
|
||||||
|
} from "../service/HomeService";
|
||||||
|
|
||||||
|
export const useGetSlugId = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (slug: string) => getIdWithSlug(slug),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetIndustry = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["industry"],
|
||||||
|
queryFn: () => getIndustry(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetCompanies = (industryId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["companies", industryId],
|
||||||
|
queryFn: () => getCompanies(industryId),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetCompanyDetail = (id: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["company", id],
|
||||||
|
queryFn: () => getCompanyDetail(id),
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import axios from "../../../config/axios";
|
||||||
|
|
||||||
|
export const getIdWithSlug = async (slug: string) => {
|
||||||
|
const { data } = await axios.get(`/business/slug/${slug}`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getIndustry = async () => {
|
||||||
|
const { data } = await axios.get(`/industries/list/public`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCompanies = async (industryId: string) => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (industryId) {
|
||||||
|
params.append("industryId", industryId);
|
||||||
|
}
|
||||||
|
const { data } = await axios.get(
|
||||||
|
`/companies/list/public?${params.toString()}`
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCompanyDetail = async (id: string) => {
|
||||||
|
const { data } = await axios.get(`/companies/public/${id}`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
export type IndustryItemType = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
title: string;
|
||||||
|
iconUrl: string;
|
||||||
|
isActive: boolean;
|
||||||
|
business: string;
|
||||||
|
companiesCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CompanyItemType = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
name: string;
|
||||||
|
identificationNumber: string;
|
||||||
|
dateOfEstablishment: string;
|
||||||
|
address: string;
|
||||||
|
mapAddressLink: string;
|
||||||
|
description: string;
|
||||||
|
websiteUrl: string;
|
||||||
|
profileImageUrl: string;
|
||||||
|
coverImageUrl: string;
|
||||||
|
isActive: boolean;
|
||||||
|
status: string;
|
||||||
|
industry: IndustryItemType;
|
||||||
|
user: string;
|
||||||
|
business: string;
|
||||||
|
invoiceCount: number;
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@ import Select from '../../components/Select'
|
|||||||
import Textarea from '../../components/Textarea'
|
import Textarea from '../../components/Textarea'
|
||||||
import { useGetProfile } from './hooks/useProfileData'
|
import { useGetProfile } from './hooks/useProfileData'
|
||||||
import Username from './components/Username'
|
import Username from './components/Username'
|
||||||
|
import withAuth from '../../hoc/withAuth'
|
||||||
|
|
||||||
const Profile: FC = () => {
|
const Profile: FC = () => {
|
||||||
|
|
||||||
@@ -171,4 +172,4 @@ const Profile: FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Profile
|
export default withAuth(Profile)
|
||||||
@@ -5,6 +5,7 @@ import Tabs from '../../components/Tabs'
|
|||||||
import Online from './components/Online'
|
import Online from './components/Online'
|
||||||
import CardtoCard from './components/CardtoCard'
|
import CardtoCard from './components/CardtoCard'
|
||||||
import Sheba from './components/Sheba'
|
import Sheba from './components/Sheba'
|
||||||
|
import withAuth from '../../hoc/withAuth'
|
||||||
|
|
||||||
const Wallet: FC = () => {
|
const Wallet: FC = () => {
|
||||||
|
|
||||||
@@ -90,4 +91,5 @@ const Wallet: FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Wallet
|
// استفاده از HOC با آپشنهای سفارشی
|
||||||
|
export default withAuth(Wallet)
|
||||||
+29
-2
@@ -1,7 +1,7 @@
|
|||||||
import Header from "../shared/Header"
|
import Header from "../shared/Header"
|
||||||
import SideBar from "../shared/SideBar"
|
import SideBar from "../shared/SideBar"
|
||||||
import '../assets/fonts/irancell/style.css'
|
import '../assets/fonts/irancell/style.css'
|
||||||
import { Route, Routes } from 'react-router-dom'
|
import { Route, Routes, useParams } from 'react-router-dom'
|
||||||
import Home from '../pages/home/Home'
|
import Home from '../pages/home/Home'
|
||||||
import ReceiptsList from '../pages/receipts/List'
|
import ReceiptsList from '../pages/receipts/List'
|
||||||
import AddCriticisms from '../pages/criticisms/Add'
|
import AddCriticisms from '../pages/criticisms/Add'
|
||||||
@@ -17,8 +17,35 @@ import ReportCardList from "../pages/ReportCard/List"
|
|||||||
import TransactionList from "../pages/transaction/List"
|
import TransactionList from "../pages/transaction/List"
|
||||||
import Profile from "../pages/profile/Profile"
|
import Profile from "../pages/profile/Profile"
|
||||||
import CompanyDetail from "../pages/company/Detail"
|
import CompanyDetail from "../pages/company/Detail"
|
||||||
|
import { useEffect } from "react"
|
||||||
|
import { useGetSlugId } from "../pages/home/hooks/useHomeData"
|
||||||
|
import { ErrorType } from "../helpers/types"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
const MainRouter = () => {
|
const MainRouter = () => {
|
||||||
|
const { slug } = useParams()
|
||||||
|
const getSlugId = useGetSlugId()
|
||||||
|
useEffect(() => {
|
||||||
|
if (slug) {
|
||||||
|
const slug_id = localStorage.getItem(import.meta.env.VITE_SLUG_ID)
|
||||||
|
const slug_parse = slug_id ? JSON.parse(slug_id) : null
|
||||||
|
if (slug_parse && slug_parse.slug === slug) {
|
||||||
|
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
getSlugId.mutate(slug, {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
localStorage.setItem(import.meta.env.VITE_SLUG_ID, JSON.stringify(data?.data?.business))
|
||||||
|
window.location.reload()
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(error.response?.data?.error.message[0])
|
||||||
|
window.location.href = 'https://danakcorp.com'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [slug])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='p-4 overflow-hidden'>
|
<div className='p-4 overflow-hidden'>
|
||||||
<SideBar />
|
<SideBar />
|
||||||
|
|||||||
@@ -4,4 +4,6 @@ import { SharedStoreType } from "../types/SharedTypes";
|
|||||||
export const useSharedStore = create<SharedStoreType>((set) => ({
|
export const useSharedStore = create<SharedStoreType>((set) => ({
|
||||||
openSidebar: false,
|
openSidebar: false,
|
||||||
setOpenSidebar: (value) => set({ openSidebar: value }),
|
setOpenSidebar: (value) => set({ openSidebar: value }),
|
||||||
|
slug: "",
|
||||||
|
setSlug: (value) => set({ slug: value }),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export type SharedStoreType = {
|
export type SharedStoreType = {
|
||||||
openSidebar: boolean;
|
openSidebar: boolean;
|
||||||
setOpenSidebar: (value: boolean) => void;
|
setOpenSidebar: (value: boolean) => void;
|
||||||
};
|
slug: string;
|
||||||
|
setSlug: (value: string) => void;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user