| @@ -31,6 +31,7 @@ export const ApiId = { | |||||
| LOGIN_USERS: id++, | LOGIN_USERS: id++, | ||||
| LOGIN_USER_CREATE: id++, | LOGIN_USER_CREATE: id++, | ||||
| LOGIN_USER_CHANGE_PASSWORD: id++, | |||||
| // FOR CUSTOM | // FOR CUSTOM | ||||
| HT_CUSTOM_CUSTOMERS: id++, | HT_CUSTOM_CUSTOMERS: id++, | ||||
| @@ -55,6 +56,10 @@ export const ResultCode = { | |||||
| }; | }; | ||||
| export type ResultCode = (typeof ResultCode)[keyof typeof ResultCode]; | export type ResultCode = (typeof ResultCode)[keyof typeof ResultCode]; | ||||
| export interface TimestampRequest { | |||||
| timestamp: string; | |||||
| } | |||||
| export interface APICommonResponse { | export interface APICommonResponse { | ||||
| result: ResultCode; | result: ResultCode; | ||||
| messages: { | messages: { | ||||
| @@ -1,4 +1,11 @@ | |||||
| import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "."; | |||||
| import { | |||||
| APICommonResponse, | |||||
| ApiId, | |||||
| HttpMethod, | |||||
| TimestampRequest, | |||||
| makeParam, | |||||
| request, | |||||
| } from "."; | |||||
| import { getUrl } from "./url"; | import { getUrl } from "./url"; | ||||
| export type LoginUser = { | export type LoginUser = { | ||||
| @@ -46,3 +53,18 @@ export const createLoginUser = async (data: LoginUserCreateRequest) => { | |||||
| }); | }); | ||||
| return res; | return res; | ||||
| }; | }; | ||||
| // パスワード変更 | |||||
| export type ChangePasswordRequest = { | |||||
| password: string; | |||||
| id?: string; // UserID | |||||
| } & TimestampRequest; | |||||
| export const changeLoginPassword = async (data: ChangePasswordRequest) => { | |||||
| const res = await request({ | |||||
| url: getUrl(ApiId.LOGIN_USER_CHANGE_PASSWORD), | |||||
| method: HttpMethod.POST, | |||||
| data: makeParam(data), | |||||
| }); | |||||
| return res; | |||||
| }; | |||||
| @@ -21,6 +21,7 @@ const urls = { | |||||
| [A.CONTRACTS]: "contracts", | [A.CONTRACTS]: "contracts", | ||||
| [A.LOGIN_USERS]: "users", | [A.LOGIN_USERS]: "users", | ||||
| [A.LOGIN_USER_CREATE]: "user/create", | [A.LOGIN_USER_CREATE]: "user/create", | ||||
| [A.LOGIN_USER_CHANGE_PASSWORD]: "user/change-password", | |||||
| // FOR CUSTOM | // FOR CUSTOM | ||||
| [A.HT_CUSTOM_CUSTOMERS]: "custom/hello-techno/customers", | [A.HT_CUSTOM_CUSTOMERS]: "custom/hello-techno/customers", | ||||
| @@ -20,6 +20,7 @@ export const PageID = { | |||||
| DASHBOARD_LOGIN_USER_LIST: id++, | DASHBOARD_LOGIN_USER_LIST: id++, | ||||
| DASHBOARD_LOGIN_USER_CREATE: id++, | DASHBOARD_LOGIN_USER_CREATE: id++, | ||||
| DASHBOARD_LOGIN_USER_CHANGE_PASSWORD: id++, | |||||
| PAGE_403: id++, | PAGE_403: id++, | ||||
| PAGE_404: id++, | PAGE_404: id++, | ||||
| @@ -22,6 +22,7 @@ type Auth = { | |||||
| role: UserRole; | role: UserRole; | ||||
| contractId: string | null; | contractId: string | null; | ||||
| userId: string | null; | |||||
| name: string; | name: string; | ||||
| custom: CustomCode[]; | custom: CustomCode[]; | ||||
| customerName: string; | customerName: string; | ||||
| @@ -41,6 +42,7 @@ export const AuthContext = createContext<Auth>({ | |||||
| role: UserRole.NONE, | role: UserRole.NONE, | ||||
| contractId: null, | contractId: null, | ||||
| userId: null, | |||||
| name: "", | name: "", | ||||
| custom: [], | custom: [], | ||||
| customerName: "", | customerName: "", | ||||
| @@ -57,6 +59,7 @@ function AuthContextProvider({ children }: Props) { | |||||
| const [initialized, setInitialized] = useState(false); | const [initialized, setInitialized] = useState(false); | ||||
| const [role, setRole] = useState<UserRole>(UserRole.NONE); | const [role, setRole] = useState<UserRole>(UserRole.NONE); | ||||
| const [contractId, setContractId] = useState<string | null>(null); | const [contractId, setContractId] = useState<string | null>(null); | ||||
| const [userId, setUserId] = useState<string | null>(null); | |||||
| const [name, setName] = useState(""); | const [name, setName] = useState(""); | ||||
| const [custom, setCustom] = useState<CustomCode[]>([]); | const [custom, setCustom] = useState<CustomCode[]>([]); | ||||
| const [customerName, setCustomerName] = useState(""); | const [customerName, setCustomerName] = useState(""); | ||||
| @@ -69,6 +72,7 @@ function AuthContextProvider({ children }: Props) { | |||||
| apiMethod: me, | apiMethod: me, | ||||
| onSuccess: (res) => { | onSuccess: (res) => { | ||||
| setContractId(res.data.contract_id); | setContractId(res.data.contract_id); | ||||
| setUserId(res.data.id); | |||||
| setRole(res.data.role); | setRole(res.data.role); | ||||
| setName(res.data.name); | setName(res.data.name); | ||||
| setCustom(res.data.custom ?? []); | setCustom(res.data.custom ?? []); | ||||
| @@ -84,6 +88,7 @@ function AuthContextProvider({ children }: Props) { | |||||
| apiMethod: APILogin, | apiMethod: APILogin, | ||||
| onSuccess: (res) => { | onSuccess: (res) => { | ||||
| setContractId(res.data.contract_id); | setContractId(res.data.contract_id); | ||||
| setUserId(res.data.id); | |||||
| setRole(res.data.role); | setRole(res.data.role); | ||||
| setName(res.data.name); | setName(res.data.name); | ||||
| setCustom(res.data.custom ?? []); | setCustom(res.data.custom ?? []); | ||||
| @@ -101,6 +106,7 @@ function AuthContextProvider({ children }: Props) { | |||||
| const clear = () => { | const clear = () => { | ||||
| setRole(UserRole.NONE); | setRole(UserRole.NONE); | ||||
| setContractId(null); | setContractId(null); | ||||
| setUserId(null); | |||||
| setName(""); | setName(""); | ||||
| setCustom([]); | setCustom([]); | ||||
| }; | }; | ||||
| @@ -178,6 +184,7 @@ function AuthContextProvider({ children }: Props) { | |||||
| authenticated, | authenticated, | ||||
| role, | role, | ||||
| contractId, | contractId, | ||||
| userId, | |||||
| name, | name, | ||||
| custom, | custom, | ||||
| @@ -3,6 +3,7 @@ import HomeIcon from "@mui/icons-material/Home"; | |||||
| import PeopleIcon from "@mui/icons-material/People"; | import PeopleIcon from "@mui/icons-material/People"; | ||||
| import ArticleIcon from "@mui/icons-material/Article"; | import ArticleIcon from "@mui/icons-material/Article"; | ||||
| import SettingsIcon from "@mui/icons-material/Settings"; | import SettingsIcon from "@mui/icons-material/Settings"; | ||||
| import AccountBoxIcon from "@mui/icons-material/AccountBox"; | |||||
| import { Collapse } from "@mui/material"; | import { Collapse } from "@mui/material"; | ||||
| import Box from "@mui/material/Box"; | import Box from "@mui/material/Box"; | ||||
| import Divider from "@mui/material/Divider"; | import Divider from "@mui/material/Divider"; | ||||
| @@ -17,7 +18,7 @@ import useAuth from "hooks/useAuth"; | |||||
| import useNavigateCustom from "hooks/useNavigateCustom"; | import useNavigateCustom from "hooks/useNavigateCustom"; | ||||
| import usePage from "hooks/usePage"; | import usePage from "hooks/usePage"; | ||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import { getPath } from "routes/path"; | |||||
| import { PathOption, getPath } from "routes/path"; | |||||
| type Group = { | type Group = { | ||||
| label: string; | label: string; | ||||
| @@ -31,69 +32,15 @@ type SubGroup = { | |||||
| // 子要素を持たない場合は設定 | // 子要素を持たない場合は設定 | ||||
| id?: PageID; | id?: PageID; | ||||
| option?: PathOption; | |||||
| }; | }; | ||||
| type Child = { | type Child = { | ||||
| label: string; | label: string; | ||||
| id: PageID; | id: PageID; | ||||
| option?: PathOption; | |||||
| }; | }; | ||||
| const categories: Group[] = [ | |||||
| { | |||||
| label: "管理", | |||||
| children: [ | |||||
| { | |||||
| label: "契約", | |||||
| icon: <PeopleIcon />, | |||||
| children: [ | |||||
| { | |||||
| id: PageID.DASHBOARD_CONTRACT_LIST, | |||||
| label: "一覧", | |||||
| }, | |||||
| { | |||||
| id: PageID.DASHBOARD_CONTRACT_CREATE, | |||||
| label: "作成", | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: "領収証発行依頼", | |||||
| icon: <ArticleIcon />, | |||||
| children: [ | |||||
| { | |||||
| id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO, | |||||
| label: "一覧", | |||||
| }, | |||||
| { | |||||
| id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO, | |||||
| label: "新規", | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: "ユーザ管理", | |||||
| icon: <PeopleIcon />, | |||||
| children: [ | |||||
| { | |||||
| id: PageID.DASHBOARD_LOGIN_USER_LIST, | |||||
| label: "一覧", | |||||
| }, | |||||
| { | |||||
| id: PageID.DASHBOARD_LOGIN_USER_CREATE, | |||||
| label: "作成", | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: "アカウント", | |||||
| children: [ | |||||
| { label: "ログアウト", icon: <SettingsIcon />, id: PageID.LOGOUT }, | |||||
| ], | |||||
| }, | |||||
| ]; | |||||
| const item = { | const item = { | ||||
| py: "2px", | py: "2px", | ||||
| px: 3, | px: 3, | ||||
| @@ -112,6 +59,79 @@ const itemCategory = { | |||||
| export default function Navigator(props: DrawerProps) { | export default function Navigator(props: DrawerProps) { | ||||
| const { ...other } = props; | const { ...other } = props; | ||||
| const { userId } = useAuth(); | |||||
| const categories: Group[] = [ | |||||
| { | |||||
| label: "管理", | |||||
| children: [ | |||||
| { | |||||
| label: "契約", | |||||
| icon: <PeopleIcon />, | |||||
| children: [ | |||||
| { | |||||
| id: PageID.DASHBOARD_CONTRACT_LIST, | |||||
| label: "一覧", | |||||
| }, | |||||
| { | |||||
| id: PageID.DASHBOARD_CONTRACT_CREATE, | |||||
| label: "作成", | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: "領収証発行依頼", | |||||
| icon: <ArticleIcon />, | |||||
| children: [ | |||||
| { | |||||
| id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO, | |||||
| label: "一覧", | |||||
| }, | |||||
| { | |||||
| id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO, | |||||
| label: "新規", | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: "ユーザ管理", | |||||
| icon: <PeopleIcon />, | |||||
| children: [ | |||||
| { | |||||
| id: PageID.DASHBOARD_LOGIN_USER_LIST, | |||||
| label: "一覧", | |||||
| }, | |||||
| { | |||||
| id: PageID.DASHBOARD_LOGIN_USER_CREATE, | |||||
| label: "作成", | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| label: "アカウント", | |||||
| children: [ | |||||
| { | |||||
| label: "アカウント編集", | |||||
| icon: <AccountBoxIcon />, | |||||
| children: [ | |||||
| { | |||||
| id: PageID.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD, | |||||
| label: "パスワード変更", | |||||
| option: { | |||||
| query: { | |||||
| id: userId ?? "empty", | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { label: "ログアウト", icon: <SettingsIcon />, id: PageID.LOGOUT }, | |||||
| ], | |||||
| }, | |||||
| ]; | |||||
| return ( | return ( | ||||
| <Drawer variant="permanent" {...other}> | <Drawer variant="permanent" {...other}> | ||||
| <List disablePadding> | <List disablePadding> | ||||
| @@ -155,7 +175,7 @@ function Group(group: Group) { | |||||
| ); | ); | ||||
| } | } | ||||
| function SubGroup({ icon, label, id, children }: SubGroup) { | |||||
| function SubGroup({ icon, label, id, children, option }: SubGroup) { | |||||
| const { pageId } = usePage(); | const { pageId } = usePage(); | ||||
| const { navigateWhenChanged } = useNavigateCustom(); | const { navigateWhenChanged } = useNavigateCustom(); | ||||
| @@ -191,7 +211,7 @@ function SubGroup({ icon, label, id, children }: SubGroup) { | |||||
| if (id !== undefined) { | if (id !== undefined) { | ||||
| const handleClick = () => { | const handleClick = () => { | ||||
| if (id) { | if (id) { | ||||
| const path = getPath(id); | |||||
| const path = getPath(id, option); | |||||
| navigateWhenChanged(path); | navigateWhenChanged(path); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -217,14 +237,15 @@ function useContents(children: Child[]) { | |||||
| setShouldOpen(false); | setShouldOpen(false); | ||||
| return children | return children | ||||
| .filter(({ id }) => canAccess(id)) | .filter(({ id }) => canAccess(id)) | ||||
| .map(({ label, id }, index) => { | |||||
| .map(({ label, id, option }, index) => { | |||||
| const selected = id === pageId; | const selected = id === pageId; | ||||
| if (selected) { | if (selected) { | ||||
| setShouldOpen(true); | setShouldOpen(true); | ||||
| } | } | ||||
| const handleClick = () => { | const handleClick = () => { | ||||
| const path = getPath(id); | |||||
| const path = getPath(id, option); | |||||
| console.log(path, id, option); | |||||
| navigateWhenChanged(path); | navigateWhenChanged(path); | ||||
| }; | }; | ||||
| @@ -0,0 +1,132 @@ | |||||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||||
| import { Box, Button, Card, Stack, Typography } from "@mui/material"; | |||||
| import { HasChildren } from "@types"; | |||||
| import { | |||||
| changeLoginPassword, | |||||
| createLoginUser, | |||||
| getLoginUsers, | |||||
| } from "api/login-user"; | |||||
| import { PageID, TabID } from "codes/page"; | |||||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||||
| import useAPICall from "hooks/useAPICall"; | |||||
| import useDashboard from "hooks/useDashBoard"; | |||||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||||
| import { useSnackbar } from "notistack"; | |||||
| import { useEffect, useState } from "react"; | |||||
| import { useForm } from "react-hook-form"; | |||||
| import { useParams } from "react-router-dom"; | |||||
| import { getPath } from "routes/path"; | |||||
| import * as Yup from "yup"; | |||||
| type AreaBoxProps = { | |||||
| title: string; | |||||
| } & HasChildren; | |||||
| function AreaBox({ title, children }: AreaBoxProps) { | |||||
| return ( | |||||
| <Box sx={{ maxWidth: 500 }}> | |||||
| <Typography variant="subtitle1">{title}</Typography> | |||||
| {children} | |||||
| </Box> | |||||
| ); | |||||
| } | |||||
| type FormProps = { | |||||
| password: string; | |||||
| password_retype: string; | |||||
| }; | |||||
| export default function ChangePassword() { | |||||
| const { setHeaderTitle, setTabs } = useDashboard( | |||||
| PageID.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD, | |||||
| TabID.NONE | |||||
| ); | |||||
| const { success, error } = useSnackbarCustom(); | |||||
| const { id: paramUserId } = useParams(); | |||||
| const [timestamp, setTimestamp] = useState(""); | |||||
| const [mode, setMode] = useState<"input" | "done">("input"); | |||||
| const form = useForm<FormProps>({ | |||||
| defaultValues: { | |||||
| password: "", | |||||
| password_retype: "", | |||||
| }, | |||||
| resolver: yupResolver( | |||||
| Yup.object().shape({ | |||||
| password: Yup.string().required("必須項目です"), | |||||
| password_retype: Yup.string() | |||||
| .required("必須項目です") | |||||
| .test("retype", "入力が一致しません", function (value) { | |||||
| return this.parent.password === value; | |||||
| }), | |||||
| }) | |||||
| ), | |||||
| }); | |||||
| const { callAPI: callGetLoginUsers } = useAPICall({ | |||||
| apiMethod: getLoginUsers, | |||||
| onSuccess: ({ data: { records } }) => { | |||||
| if (records.length === 1) { | |||||
| setTimestamp(records[0].updated_at); | |||||
| } | |||||
| }, | |||||
| }); | |||||
| const { callAPI: callChangeLoginPassword } = useAPICall({ | |||||
| apiMethod: changeLoginPassword, | |||||
| backDrop: true, | |||||
| form, | |||||
| onSuccess: () => { | |||||
| success("登録しました"); | |||||
| setMode("done"); | |||||
| }, | |||||
| onFailed: () => { | |||||
| error("失敗しました"); | |||||
| }, | |||||
| }); | |||||
| const fetch = () => { | |||||
| callGetLoginUsers({ | |||||
| id: paramUserId, | |||||
| }); | |||||
| }; | |||||
| const handleSubmt = (data: FormProps) => { | |||||
| callChangeLoginPassword({ ...data, timestamp }); | |||||
| }; | |||||
| useEffect(() => { | |||||
| setHeaderTitle("パスワード変更"); | |||||
| setTabs(null); | |||||
| fetch(); | |||||
| }, []); | |||||
| return ( | |||||
| <> | |||||
| {mode === "input" && ( | |||||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmt)}> | |||||
| <Box> | |||||
| <Stack spacing={2}> | |||||
| <AreaBox title="ログインパスワード"> | |||||
| <RHFTextField name="password" type="password" /> | |||||
| </AreaBox> | |||||
| <AreaBox title="ログインパスワード(再入力)"> | |||||
| <RHFTextField name="password_retype" type="password" /> | |||||
| </AreaBox> | |||||
| <Stack direction="row"> | |||||
| <Button variant="contained" type="submit"> | |||||
| 登録 | |||||
| </Button> | |||||
| </Stack> | |||||
| </Stack> | |||||
| </Box> | |||||
| </FormProvider> | |||||
| )} | |||||
| {mode === "done" && <Box>変更完了しました</Box>} | |||||
| </> | |||||
| ); | |||||
| } | |||||
| @@ -102,7 +102,7 @@ export default function LoginUserCreate() { | |||||
| <AreaBox title="ログインパスワード"> | <AreaBox title="ログインパスワード"> | ||||
| <RHFTextField name="password" type="password" /> | <RHFTextField name="password" type="password" /> | ||||
| </AreaBox> | </AreaBox> | ||||
| <AreaBox title="ログインパスワード(再入力"> | |||||
| <AreaBox title="ログインパスワード(再入力)"> | |||||
| <RHFTextField name="password_retype" type="password" /> | <RHFTextField name="password_retype" type="password" /> | ||||
| </AreaBox> | </AreaBox> | ||||
| <Stack direction="row"> | <Stack direction="row"> | ||||
| @@ -288,16 +288,13 @@ function Row({ data }: RowProps) { | |||||
| const { navigateWhenChanged } = useNavigateCustom(); | const { navigateWhenChanged } = useNavigateCustom(); | ||||
| const handleClick = () => { | const handleClick = () => { | ||||
| // navigateWhenChanged( | |||||
| // getPath( | |||||
| // PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL_CUSTOM_HELLO_TECHNO, | |||||
| // { | |||||
| // query: { | |||||
| // id: data.id, | |||||
| // }, | |||||
| // } | |||||
| // ) | |||||
| // ); | |||||
| navigateWhenChanged( | |||||
| getPath(PageID.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD, { | |||||
| query: { | |||||
| id: data.id, | |||||
| }, | |||||
| }) | |||||
| ); | |||||
| }; | }; | ||||
| return ( | return ( | ||||
| @@ -34,6 +34,7 @@ export const AUTH = { | |||||
| ), | ), | ||||
| [P.DASHBOARD_LOGIN_USER_LIST]: setAuth("ge", R.CONTRACT_ADMIN), | [P.DASHBOARD_LOGIN_USER_LIST]: setAuth("ge", R.CONTRACT_ADMIN), | ||||
| [P.DASHBOARD_LOGIN_USER_CREATE]: setAuth("ge", R.CONTRACT_ADMIN), | [P.DASHBOARD_LOGIN_USER_CREATE]: setAuth("ge", R.CONTRACT_ADMIN), | ||||
| [P.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD]: setAuth("ge", R.NORMAL_ADMIN), | |||||
| [P.PAGE_403]: setAuth("all"), | [P.PAGE_403]: setAuth("all"), | ||||
| [P.PAGE_404]: setAuth("all"), | [P.PAGE_404]: setAuth("all"), | ||||
| @@ -89,6 +89,10 @@ const DashboardRoutes = (): RouteObject => { | |||||
| pageId: PageID.DASHBOARD_LOGIN_USER_CREATE, | pageId: PageID.DASHBOARD_LOGIN_USER_CREATE, | ||||
| element: <LoginUserCreate />, | element: <LoginUserCreate />, | ||||
| }, | }, | ||||
| { | |||||
| pageId: PageID.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD, | |||||
| element: <ChangePassword />, | |||||
| }, | |||||
| ]; | ]; | ||||
| const children: RouteObject[] = useMemo(() => { | const children: RouteObject[] = useMemo(() => { | ||||
| @@ -172,6 +176,9 @@ const LoginUserList = Loadable( | |||||
| const LoginUserCreate = Loadable( | const LoginUserCreate = Loadable( | ||||
| lazy(() => import("pages/dashboard/login-user/create")) | lazy(() => import("pages/dashboard/login-user/create")) | ||||
| ); | ); | ||||
| const ChangePassword = Loadable( | |||||
| lazy(() => import("pages/dashboard/login-user/change-password")) | |||||
| ); | |||||
| // その他 --------------------------------- | // その他 --------------------------------- | ||||
| const Page403 = Loadable(lazy(() => import("pages/common/Page403"))); | const Page403 = Loadable(lazy(() => import("pages/common/Page403"))); | ||||
| @@ -66,6 +66,8 @@ const PATHS = { | |||||
| "/dashboard/login-user/list/:page", | "/dashboard/login-user/list/:page", | ||||
| [makePathKey(PageID.DASHBOARD_LOGIN_USER_CREATE)]: | [makePathKey(PageID.DASHBOARD_LOGIN_USER_CREATE)]: | ||||
| "/dashboard/login-user/create", | "/dashboard/login-user/create", | ||||
| [makePathKey(PageID.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD)]: | |||||
| "/dashboard/login-user/change-password/:id", | |||||
| // その他 | // その他 | ||||
| [makePathKey(PageID.PAGE_403)]: "403", | [makePathKey(PageID.PAGE_403)]: "403", | ||||