Feature/amiriis teleport setad
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -39,3 +39,4 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
/.idea
|
||||
|
||||
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
47
.idea/codeStyles/Project.xml
generated
47
.idea/codeStyles/Project.xml
generated
@@ -1,47 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="120" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="TAB_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
12
.idea/crm-app.iml
generated
12
.idea/crm-app.iml
generated
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
12
.idea/inspectionProfiles/Project_Default.xml
generated
12
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,12 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JSAnnotator" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/crm-app.iml" filepath="$PROJECT_DIR$/.idea/crm-app.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/prettier.xml
generated
6
.idea/prettier.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
10
src/app/(withoutAuth)/teleport/page.js
Normal file
10
src/app/(withoutAuth)/teleport/page.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import TeleportPage from "@/components/Teleport";
|
||||
|
||||
export const metadata = {
|
||||
title: "درحال انتقال به استان مورد نظر",
|
||||
};
|
||||
|
||||
const Page = () => {
|
||||
return <TeleportPage />;
|
||||
};
|
||||
export default Page;
|
||||
@@ -46,7 +46,7 @@ const LoginForm = () => {
|
||||
data: formData,
|
||||
});
|
||||
setToken(response.data.data.token);
|
||||
} catch (error) {}
|
||||
} catch (error) { }
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
51
src/components/Teleport/index.jsx
Normal file
51
src/components/Teleport/index.jsx
Normal file
@@ -0,0 +1,51 @@
|
||||
'use client'
|
||||
import LoadingHardPage from "@/core/components/LoadingHardPage";
|
||||
import { TELEPORT_USER_ROUTE } from "@/core/utils/routes";
|
||||
import { useAuth } from "@/lib/contexts/auth";
|
||||
import useRequest from "@/lib/hooks/useRequest";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const TelePortPage = () => {
|
||||
const { setToken } = useAuth()
|
||||
const searchParams = useSearchParams();
|
||||
const token = searchParams.get("token");
|
||||
const user_id = searchParams.get("user_id");
|
||||
|
||||
const [error, setError] = useState(false)
|
||||
const requestServer = useRequest({ notificationShow: false });
|
||||
|
||||
useEffect(() => {
|
||||
const login = async () => {
|
||||
try {
|
||||
const response = await requestServer(TELEPORT_USER_ROUTE, "post", {
|
||||
data: {
|
||||
token: decodeURIComponent(token),
|
||||
user_id: decodeURIComponent(user_id)
|
||||
}
|
||||
});
|
||||
setToken(response.data.data.token);
|
||||
} catch (error) { setError(true) }
|
||||
}
|
||||
|
||||
login()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<LoadingHardPage
|
||||
authState={error}
|
||||
label={
|
||||
error ? (
|
||||
<Stack justifyContent={"center"} alignItems={"center"} spacing={2}>
|
||||
<Typography variant={"body1"}>مشکلی در انتقال پیش آمده است!</Typography>
|
||||
</Stack>
|
||||
) : (
|
||||
<Typography variant={"body1"}>درحال انتقال به مرکز مورد نظر...</Typography>
|
||||
)
|
||||
}
|
||||
loading={true}
|
||||
/>
|
||||
)
|
||||
}
|
||||
export default TelePortPage
|
||||
@@ -34,6 +34,18 @@ const DataTable = () => {
|
||||
grow: false,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: "نام انگلیسی",
|
||||
id: "name",
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
filterMode: "equals",
|
||||
sortDescFirst: false,
|
||||
columnFilterModeOptions: ["equals", "contains"],
|
||||
grow: false,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
accessorKey: "created_at",
|
||||
header: "تاریخ ساخت",
|
||||
|
||||
@@ -39,6 +39,7 @@ const DataTable = () => {
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
accessorKey: "telephone_id",
|
||||
header: "شماره تماس داخلی",
|
||||
id: "telephone_id",
|
||||
enableColumnFilter: true,
|
||||
@@ -48,7 +49,6 @@ const DataTable = () => {
|
||||
columnFilterModeOptions: ["equals", "contains"],
|
||||
grow: false,
|
||||
size: 100,
|
||||
Cell: ({ row }) => <TelephoneId user_id={row.original.id} />,
|
||||
},
|
||||
{
|
||||
accessorKey: "is_online",
|
||||
|
||||
@@ -5,7 +5,7 @@ import OnlineUsersReport from "./OnlineUsersReport";
|
||||
const DashboardPage = () => {
|
||||
return (
|
||||
<Stack>
|
||||
<OnlineUsersReport />
|
||||
{/* <OnlineUsersReport /> */}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
import Profile from "@/core/components/Profile";
|
||||
import { filterMenuItems } from "@/core/utils/filterMenuItems";
|
||||
import { pageMenu } from "@/core/utils/pageMenu";
|
||||
import { usePermissions } from "@/lib/hooks/usePermissions";
|
||||
import { List } from "@mui/material";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useEffect, useReducer, useState } from "react";
|
||||
import { filterMenuItems } from "@/core/utils/filterMenuItems";
|
||||
import { pageMenu } from "@/core/utils/pageMenu";
|
||||
import SidebarListItems from "./SidebarListItems";
|
||||
import Profile from "@/core/components/Profile";
|
||||
|
||||
function selectPage(item, route) {
|
||||
if (item.type === "page") {
|
||||
@@ -53,16 +53,16 @@ function reducer(state, action) {
|
||||
return state.map((item) =>
|
||||
item.hasSubitems
|
||||
? {
|
||||
...item,
|
||||
Subitems: item.Subitems.map((subitem) =>
|
||||
subitem.hasSubitems
|
||||
? {
|
||||
...subitem,
|
||||
Subitems: toggleSubitems(subitem.Subitems, action.id),
|
||||
}
|
||||
: subitem
|
||||
),
|
||||
}
|
||||
...item,
|
||||
Subitems: item.Subitems.map((subitem) =>
|
||||
subitem.hasSubitems
|
||||
? {
|
||||
...subitem,
|
||||
Subitems: toggleSubitems(subitem.Subitems, action.id),
|
||||
}
|
||||
: subitem
|
||||
),
|
||||
}
|
||||
: item
|
||||
);
|
||||
|
||||
|
||||
@@ -8,8 +8,11 @@ import HeaderMenu from "./HeaderMenu";
|
||||
import moment from "jalali-moment";
|
||||
import { headerMenu } from "@/core/utils/headerMenu";
|
||||
import SidebarMenu from "./Sidebar/SidebarMenu";
|
||||
import Teleporting from "@/core/components/Teleporting";
|
||||
import { usePermissions } from "@/lib/hooks/usePermissions";
|
||||
import WithWidgetMiddleware from "@/core/middlewares/withWidget";
|
||||
|
||||
const drawerWidth = 300;
|
||||
const drawerWidth = 270;
|
||||
|
||||
const Main = styled(Stack, { shouldForwardProp: (prop) => prop !== "open" })(({ theme, open }) => ({
|
||||
flexGrow: 1,
|
||||
@@ -50,11 +53,13 @@ const DrawerHeader = styled("div")(({ theme }) => ({
|
||||
alignItems: "center",
|
||||
padding: theme.spacing(0, 1),
|
||||
...theme.mixins.toolbar,
|
||||
minHeight: "40px !important",
|
||||
minHeight: "43px !important",
|
||||
justifyContent: "flex-end",
|
||||
}));
|
||||
|
||||
const HeaderWithSidebar = ({ children }) => {
|
||||
const { data: permissions } = usePermissions();
|
||||
const isTeleportToProvince = permissions?.some((permission) => permission === "teleport_to_province");
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("md")); // Detect mobile/tablet screen
|
||||
const [open, setOpen] = useState(!isMobile); // Initial state based on screen size
|
||||
@@ -103,9 +108,16 @@ const HeaderWithSidebar = ({ children }) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
{headerMenu.map((menu) => (
|
||||
<HeaderMenu key={menu.title} menu={menu} />
|
||||
))}
|
||||
<Stack sx={{ flex: 1 }} direction={'row'} justifyContent={'space-between'}>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
{headerMenu.map((menu) => (
|
||||
<HeaderMenu key={menu.title} menu={menu} />
|
||||
))}
|
||||
</Box>
|
||||
<WithWidgetMiddleware enable={isTeleportToProvince}>
|
||||
<Teleporting />
|
||||
</WithWidgetMiddleware>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
102
src/core/components/Teleporting/index.jsx
Normal file
102
src/core/components/Teleporting/index.jsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { GET_TOKEN_TELEPORT_USER_ROUTE } from "@/core/utils/routes";
|
||||
import { useAuth } from "@/lib/contexts/auth";
|
||||
import useProvinces from "@/lib/hooks/useProvince";
|
||||
import useRequest from "@/lib/hooks/useRequest";
|
||||
import { Apartment } from "@mui/icons-material";
|
||||
import { Box, CircularProgress, FormControl, InputLabel, MenuItem, Select, Stack, Typography, Zoom } from "@mui/material";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
const Teleporting = () => {
|
||||
const { user } = useAuth()
|
||||
const { provinces, errorProvinces, loadingProvinces } = useProvinces()
|
||||
const getColumnSelectOptions = useMemo(() => {
|
||||
if (loadingProvinces) {
|
||||
return [{ value: "loading", label: "در حال بارگذاری..." }];
|
||||
}
|
||||
if (errorProvinces) {
|
||||
return [{ value: "error", label: "خطا در بارگذاری" }];
|
||||
}
|
||||
return [
|
||||
...provinces.map((province) => ({
|
||||
value: province.id,
|
||||
label: province.name,
|
||||
})),
|
||||
];
|
||||
}, [provinces, errorProvinces, loadingProvinces]);
|
||||
const requestServer = useRequest();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [province, setProvince] = useState(user.server_province_id);
|
||||
|
||||
const handleChange = async ({ target: { value } }) => {
|
||||
setLoading(true);
|
||||
setProvince(value)
|
||||
try {
|
||||
const { data } = await requestServer(GET_TOKEN_TELEPORT_USER_ROUTE, "post", {
|
||||
data: {
|
||||
province_id: value
|
||||
},
|
||||
});
|
||||
|
||||
const url = `http://${data.data.ip}/teleport`
|
||||
const params = `?token=${encodeURIComponent(data.data.token)}&user_id=${encodeURIComponent(data.data.user_id)}`;
|
||||
window.location.href = url + params
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
setProvince(user.server_province_id)
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Zoom in>
|
||||
<Stack
|
||||
sx={{ width: 170, height: 43 }}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
direction="row"
|
||||
spacing={1}
|
||||
>
|
||||
<CircularProgress size="14px" color="inherit" />
|
||||
<Typography variant="caption">در حال انتقال به {provinces.find(p => p.id == province).name}</Typography>
|
||||
</Stack>
|
||||
</Zoom>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack sx={{ width: 170, height: 43 }}>
|
||||
<FormControl
|
||||
variant="filled"
|
||||
size="small"
|
||||
sx={{
|
||||
minWidth: 170,
|
||||
"& .MuiInputBase-root, & .MuiInputLabel-root, & .MuiSvgIcon-root": {
|
||||
color: "white",
|
||||
fontSize: "12px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<InputLabel id="teleport-province-label">مرکز</InputLabel>
|
||||
<Select
|
||||
labelId="teleport-province-label"
|
||||
id="teleport-province"
|
||||
value={loadingProvinces ? "loading" : errorProvinces ? "error" : province}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{getColumnSelectOptions.map(option => (
|
||||
<MenuItem dense key={option.value} value={option.value}>
|
||||
<Stack direction={'row'} alignItems={'center'} spacing={2}>
|
||||
<Apartment />
|
||||
<Box>
|
||||
{option.label}
|
||||
</Box>
|
||||
</Stack>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default Teleporting;
|
||||
@@ -1,5 +1,8 @@
|
||||
const api = process.env.NEXT_PUBLIC_API_URL;
|
||||
|
||||
export const TELEPORT_USER_ROUTE = api + "/verify_authentication";
|
||||
export const GET_TOKEN_TELEPORT_USER_ROUTE = api + "/login_to_province";
|
||||
|
||||
export const GET_USER_ROUTE = api + "/profile/info";
|
||||
export const CHANGE_USER_PASSWORD = api + "/profile/change_password";
|
||||
export const GET_USER_LOGIN_ROUTE = api + "/auth/login";
|
||||
|
||||
@@ -36,6 +36,7 @@ const AuthContext = createContext();
|
||||
export const AuthProvider = ({ children }) => {
|
||||
const [errorState, setErrorState] = useState({ status: null, message: "" });
|
||||
const [state, dispatch] = useReducer(authReducer, initAuth);
|
||||
const [tokenChecked, setTokenChecked] = useState(false);
|
||||
|
||||
const clearUser = useCallback(() => {
|
||||
dispatch({ type: "CLEAR_USER" });
|
||||
@@ -95,9 +96,12 @@ export const AuthProvider = ({ children }) => {
|
||||
useEffect(() => {
|
||||
const localToken = localStorage.getItem("_token");
|
||||
if (localToken) dispatch({ type: "SET_TOKEN", token: localToken });
|
||||
setTokenChecked(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!tokenChecked) return;
|
||||
|
||||
if (!state.token) {
|
||||
clearUser();
|
||||
changeAuthState(false);
|
||||
@@ -105,7 +109,7 @@ export const AuthProvider = ({ children }) => {
|
||||
return;
|
||||
}
|
||||
getUser();
|
||||
}, [state.token]);
|
||||
}, [tokenChecked, state.token]);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
|
||||
Reference in New Issue
Block a user