diff --git a/src/api/index.ts b/src/api/index.ts index a80537c..f3052d5 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -31,6 +31,7 @@ export const ApiId = { LOGIN_USERS: id++, LOGIN_USER_CREATE: id++, + LOGIN_USER_CHANGE_PASSWORD: id++, // FOR CUSTOM HT_CUSTOM_CUSTOMERS: id++, @@ -55,6 +56,10 @@ export const ResultCode = { }; export type ResultCode = (typeof ResultCode)[keyof typeof ResultCode]; +export interface TimestampRequest { + timestamp: string; +} + export interface APICommonResponse { result: ResultCode; messages: { diff --git a/src/api/login-user.ts b/src/api/login-user.ts index 4b9903e..dd1ed44 100644 --- a/src/api/login-user.ts +++ b/src/api/login-user.ts @@ -1,4 +1,11 @@ -import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "."; +import { + APICommonResponse, + ApiId, + HttpMethod, + TimestampRequest, + makeParam, + request, +} from "."; import { getUrl } from "./url"; export type LoginUser = { @@ -46,3 +53,18 @@ export const createLoginUser = async (data: LoginUserCreateRequest) => { }); 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; +}; diff --git a/src/api/url.ts b/src/api/url.ts index 4c1b193..3a5410d 100644 --- a/src/api/url.ts +++ b/src/api/url.ts @@ -21,6 +21,7 @@ const urls = { [A.CONTRACTS]: "contracts", [A.LOGIN_USERS]: "users", [A.LOGIN_USER_CREATE]: "user/create", + [A.LOGIN_USER_CHANGE_PASSWORD]: "user/change-password", // FOR CUSTOM [A.HT_CUSTOM_CUSTOMERS]: "custom/hello-techno/customers", diff --git a/src/codes/page.ts b/src/codes/page.ts index f6e1bd2..c6eb24d 100644 --- a/src/codes/page.ts +++ b/src/codes/page.ts @@ -20,6 +20,7 @@ export const PageID = { DASHBOARD_LOGIN_USER_LIST: id++, DASHBOARD_LOGIN_USER_CREATE: id++, + DASHBOARD_LOGIN_USER_CHANGE_PASSWORD: id++, PAGE_403: id++, PAGE_404: id++, diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 1403bcb..41c6e1b 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -22,6 +22,7 @@ type Auth = { role: UserRole; contractId: string | null; + userId: string | null; name: string; custom: CustomCode[]; customerName: string; @@ -41,6 +42,7 @@ export const AuthContext = createContext({ role: UserRole.NONE, contractId: null, + userId: null, name: "", custom: [], customerName: "", @@ -57,6 +59,7 @@ function AuthContextProvider({ children }: Props) { const [initialized, setInitialized] = useState(false); const [role, setRole] = useState(UserRole.NONE); const [contractId, setContractId] = useState(null); + const [userId, setUserId] = useState(null); const [name, setName] = useState(""); const [custom, setCustom] = useState([]); const [customerName, setCustomerName] = useState(""); @@ -69,6 +72,7 @@ function AuthContextProvider({ children }: Props) { apiMethod: me, onSuccess: (res) => { setContractId(res.data.contract_id); + setUserId(res.data.id); setRole(res.data.role); setName(res.data.name); setCustom(res.data.custom ?? []); @@ -84,6 +88,7 @@ function AuthContextProvider({ children }: Props) { apiMethod: APILogin, onSuccess: (res) => { setContractId(res.data.contract_id); + setUserId(res.data.id); setRole(res.data.role); setName(res.data.name); setCustom(res.data.custom ?? []); @@ -101,6 +106,7 @@ function AuthContextProvider({ children }: Props) { const clear = () => { setRole(UserRole.NONE); setContractId(null); + setUserId(null); setName(""); setCustom([]); }; @@ -178,6 +184,7 @@ function AuthContextProvider({ children }: Props) { authenticated, role, contractId, + userId, name, custom, diff --git a/src/layouts/dashbord/navigator.tsx b/src/layouts/dashbord/navigator.tsx index d45efeb..1c26580 100644 --- a/src/layouts/dashbord/navigator.tsx +++ b/src/layouts/dashbord/navigator.tsx @@ -3,6 +3,7 @@ import HomeIcon from "@mui/icons-material/Home"; import PeopleIcon from "@mui/icons-material/People"; import ArticleIcon from "@mui/icons-material/Article"; import SettingsIcon from "@mui/icons-material/Settings"; +import AccountBoxIcon from "@mui/icons-material/AccountBox"; import { Collapse } from "@mui/material"; import Box from "@mui/material/Box"; import Divider from "@mui/material/Divider"; @@ -17,7 +18,7 @@ import useAuth from "hooks/useAuth"; import useNavigateCustom from "hooks/useNavigateCustom"; import usePage from "hooks/usePage"; import * as React from "react"; -import { getPath } from "routes/path"; +import { PathOption, getPath } from "routes/path"; type Group = { label: string; @@ -31,69 +32,15 @@ type SubGroup = { // 子要素を持たない場合は設定 id?: PageID; + option?: PathOption; }; type Child = { label: string; id: PageID; + option?: PathOption; }; -const categories: Group[] = [ - { - label: "管理", - children: [ - { - label: "契約", - icon: , - children: [ - { - id: PageID.DASHBOARD_CONTRACT_LIST, - label: "一覧", - }, - { - id: PageID.DASHBOARD_CONTRACT_CREATE, - label: "作成", - }, - ], - }, - { - label: "領収証発行依頼", - icon: , - 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: , - children: [ - { - id: PageID.DASHBOARD_LOGIN_USER_LIST, - label: "一覧", - }, - { - id: PageID.DASHBOARD_LOGIN_USER_CREATE, - label: "作成", - }, - ], - }, - ], - }, - { - label: "アカウント", - children: [ - { label: "ログアウト", icon: , id: PageID.LOGOUT }, - ], - }, -]; - const item = { py: "2px", px: 3, @@ -112,6 +59,79 @@ const itemCategory = { export default function Navigator(props: DrawerProps) { const { ...other } = props; + const { userId } = useAuth(); + + const categories: Group[] = [ + { + label: "管理", + children: [ + { + label: "契約", + icon: , + children: [ + { + id: PageID.DASHBOARD_CONTRACT_LIST, + label: "一覧", + }, + { + id: PageID.DASHBOARD_CONTRACT_CREATE, + label: "作成", + }, + ], + }, + { + label: "領収証発行依頼", + icon: , + 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: , + children: [ + { + id: PageID.DASHBOARD_LOGIN_USER_LIST, + label: "一覧", + }, + { + id: PageID.DASHBOARD_LOGIN_USER_CREATE, + label: "作成", + }, + ], + }, + ], + }, + { + label: "アカウント", + children: [ + { + label: "アカウント編集", + icon: , + children: [ + { + id: PageID.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD, + label: "パスワード変更", + option: { + query: { + id: userId ?? "empty", + }, + }, + }, + ], + }, + { label: "ログアウト", icon: , id: PageID.LOGOUT }, + ], + }, + ]; + return ( @@ -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 { navigateWhenChanged } = useNavigateCustom(); @@ -191,7 +211,7 @@ function SubGroup({ icon, label, id, children }: SubGroup) { if (id !== undefined) { const handleClick = () => { if (id) { - const path = getPath(id); + const path = getPath(id, option); navigateWhenChanged(path); } }; @@ -217,14 +237,15 @@ function useContents(children: Child[]) { setShouldOpen(false); return children .filter(({ id }) => canAccess(id)) - .map(({ label, id }, index) => { + .map(({ label, id, option }, index) => { const selected = id === pageId; if (selected) { setShouldOpen(true); } const handleClick = () => { - const path = getPath(id); + const path = getPath(id, option); + console.log(path, id, option); navigateWhenChanged(path); }; diff --git a/src/pages/dashboard/login-user/change-password.tsx b/src/pages/dashboard/login-user/change-password.tsx new file mode 100644 index 0000000..2507cb0 --- /dev/null +++ b/src/pages/dashboard/login-user/change-password.tsx @@ -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 ( + + {title} + {children} + + ); +} + +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({ + 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" && ( + + + + + + + + + + + + + + + + )} + {mode === "done" && 変更完了しました} + + ); +} diff --git a/src/pages/dashboard/login-user/create.tsx b/src/pages/dashboard/login-user/create.tsx index 65f0276..6975ac0 100644 --- a/src/pages/dashboard/login-user/create.tsx +++ b/src/pages/dashboard/login-user/create.tsx @@ -102,7 +102,7 @@ export default function LoginUserCreate() { - + diff --git a/src/pages/dashboard/login-user/list.tsx b/src/pages/dashboard/login-user/list.tsx index 1bb9a88..a6927d2 100644 --- a/src/pages/dashboard/login-user/list.tsx +++ b/src/pages/dashboard/login-user/list.tsx @@ -288,16 +288,13 @@ function Row({ data }: RowProps) { const { navigateWhenChanged } = useNavigateCustom(); 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 ( diff --git a/src/routes/auth.ts b/src/routes/auth.ts index e7c9248..599b995 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -34,6 +34,7 @@ export const AUTH = { ), [P.DASHBOARD_LOGIN_USER_LIST]: 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_404]: setAuth("all"), diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 71162c9..42fe350 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -89,6 +89,10 @@ const DashboardRoutes = (): RouteObject => { pageId: PageID.DASHBOARD_LOGIN_USER_CREATE, element: , }, + { + pageId: PageID.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD, + element: , + }, ]; const children: RouteObject[] = useMemo(() => { @@ -172,6 +176,9 @@ const LoginUserList = Loadable( const LoginUserCreate = Loadable( 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"))); diff --git a/src/routes/path.ts b/src/routes/path.ts index 2c267b1..79946db 100644 --- a/src/routes/path.ts +++ b/src/routes/path.ts @@ -66,6 +66,8 @@ const PATHS = { "/dashboard/login-user/list/:page", [makePathKey(PageID.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",