From 383bf09d27a7df22dd38430ff6fbe0094cb6b80f Mon Sep 17 00:00:00 2001 From: "sosuke.iwabuchi" Date: Mon, 25 Mar 2024 14:31:56 +0900 Subject: [PATCH] =?UTF-8?q?=E9=A1=A7=E5=AE=A2=E4=B8=80=E8=A6=A7=20?= =?UTF-8?q?=E7=99=BB=E9=8C=B2=20=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/customer.ts | 2 +- src/api/login-user.ts | 18 ++- src/auth/route.ts | 12 +- src/components/table/TableHeadCustom.tsx | 7 +- src/hooks/useNavigateCustom.ts | 2 +- src/layouts/dashbord/navigator.tsx | 15 ++ src/pages/dashboard/empty.tsx | 3 + .../SearchBox.tsx | 87 ++++++++++ .../TableBox.tsx | 93 +++++++++++ .../index.tsx | 38 +++++ .../index.tsx | 148 ++++++++++++++++++ src/pages/index.ts | 12 ++ src/routes/path.ts | 7 + src/routes/sub/dashboard.tsx | 21 +++ src/storage/cache/顧客マスタ.ts | 15 ++ 15 files changed, 466 insertions(+), 14 deletions(-) create mode 100644 src/pages/dashboard/empty.tsx create mode 100644 src/pages/dashboard/login-user/顧客ログインユーザ一覧/SearchBox.tsx create mode 100644 src/pages/dashboard/login-user/顧客ログインユーザ一覧/TableBox.tsx create mode 100644 src/pages/dashboard/login-user/顧客ログインユーザ一覧/index.tsx create mode 100644 src/pages/dashboard/login-user/顧客ログインユーザ新規登録/index.tsx create mode 100644 src/storage/cache/顧客マスタ.ts diff --git a/src/api/customer.ts b/src/api/customer.ts index 5f77034..b66f395 100644 --- a/src/api/customer.ts +++ b/src/api/customer.ts @@ -1,7 +1,7 @@ import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "api"; import { getUrl } from "./url"; -type 顧客マスタ = { +export type 顧客マスタ = { id: number; customer_id: string; customer_name: string; diff --git a/src/api/login-user.ts b/src/api/login-user.ts index 0131065..96b34e4 100644 --- a/src/api/login-user.ts +++ b/src/api/login-user.ts @@ -1,7 +1,7 @@ import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "api"; import { getUrl } from "./url"; -type 運営会社ログインユーザ = { +export type 運営会社ログインユーザ = { id: string; name: string; email: string; @@ -14,7 +14,7 @@ export type 顧客ログインユーザ新規登録Request = { name: string; email: string; password: string; - customerCode: string; + customer_code: string; }; export type 顧客ログインユーザ新規登録Response = { data: { @@ -55,19 +55,21 @@ export const 店舗ログインユーザ新規登録 = async ( return res; }; -// -------顧客ログインユーザ新規登録--------------- -export type 顧客一覧取得Request = { +// -------顧客ログインユーザ一覧取得--------------- +export type 顧客ログインユーザ一覧取得Request = { email?: string; name?: string; }; -export type 顧客一覧取得Response = { +export type 顧客ログインユーザ一覧取得Response = { data: { list: 運営会社ログインユーザ[]; }; } & APICommonResponse; -export const 顧客一覧取得 = async (param: 顧客一覧取得Request) => { - const res = await request<顧客一覧取得Response>({ - url: getUrl(ApiId.QRサービス券取得), +export const 顧客ログインユーザ一覧取得 = async ( + param: 顧客ログインユーザ一覧取得Request +) => { + const res = await request<顧客ログインユーザ一覧取得Response>({ + url: getUrl(ApiId.顧客ログインユーザ一覧取得), method: HttpMethod.GET, data: new URLSearchParams(param), }); diff --git a/src/auth/route.ts b/src/auth/route.ts index 8c263e6..d0396b3 100644 --- a/src/auth/route.ts +++ b/src/auth/route.ts @@ -2,18 +2,24 @@ import { PageID as P } from "pages"; import { UserRole } from "./UserRole"; const 共通ルート = [P.LOGIN, P.LOGOUT]; +const 認証後共通ルート = [P.DASHBOARD_ENPTY, P.DASHBOARD_OVERVIEW]; const 認可別許可ルート: { [route: string]: P[]; } = { [UserRole.NONE]: [...共通ルート], - [UserRole.ADMIN]: [...共通ルート, P.DASHBOARD_OVERVIEW], + [UserRole.ADMIN]: [ + ...共通ルート, + ...認証後共通ルート, + P.ログインユーザ_顧客一覧, + P.ログインユーザ_顧客新規登録, + ], - [UserRole.CUSTOMER]: [...共通ルート, P.DASHBOARD_OVERVIEW], + [UserRole.CUSTOMER]: [...共通ルート, ...認証後共通ルート], [UserRole.SHOP]: [ ...共通ルート, - P.DASHBOARD_OVERVIEW, + ...認証後共通ルート, P.サービス券発行用QRコード, P.サービス券利用履歴, ], diff --git a/src/components/table/TableHeadCustom.tsx b/src/components/table/TableHeadCustom.tsx index 33e2c8b..f8d8f3c 100644 --- a/src/components/table/TableHeadCustom.tsx +++ b/src/components/table/TableHeadCustom.tsx @@ -57,7 +57,12 @@ export default function TableHeadCustom({ sx, }: Props) { return ( - + {onSelectAllRows && ( diff --git a/src/hooks/useNavigateCustom.ts b/src/hooks/useNavigateCustom.ts index 6e684c8..01f7e29 100644 --- a/src/hooks/useNavigateCustom.ts +++ b/src/hooks/useNavigateCustom.ts @@ -47,7 +47,7 @@ export default function useNavigateCustom() { // 同じURLで遷移要求があった場合、reload設定されていれば同じページを読み込みなおす // 一旦、空白のページを経由する必要がある if (currentUrl === newPath && option?.reload) { - navigate(getPath(PageID.NONE)); + navigate(getPath(PageID.DASHBOARD_ENPTY)); setTimeout(() => { navigate(newPath); }, 50); diff --git a/src/layouts/dashbord/navigator.tsx b/src/layouts/dashbord/navigator.tsx index 91d518d..184de6c 100644 --- a/src/layouts/dashbord/navigator.tsx +++ b/src/layouts/dashbord/navigator.tsx @@ -78,6 +78,21 @@ export default function Navigator(props: DrawerProps) { label: "管理メニュー", children: [], }, + { + label: "ログインユーザ管理", + children: [ + { + label: "顧客一覧", + icon: , + id: PageID.ログインユーザ_顧客一覧, + }, + { + label: "顧客新規登録", + icon: , + id: PageID.ログインユーザ_顧客新規登録, + }, + ], + }, { label: "QRサービス券", children: [ diff --git a/src/pages/dashboard/empty.tsx b/src/pages/dashboard/empty.tsx new file mode 100644 index 0000000..fe5d088 --- /dev/null +++ b/src/pages/dashboard/empty.tsx @@ -0,0 +1,3 @@ +export default function Empty() { + return null; +} diff --git a/src/pages/dashboard/login-user/顧客ログインユーザ一覧/SearchBox.tsx b/src/pages/dashboard/login-user/顧客ログインユーザ一覧/SearchBox.tsx new file mode 100644 index 0000000..211279b --- /dev/null +++ b/src/pages/dashboard/login-user/顧客ログインユーザ一覧/SearchBox.tsx @@ -0,0 +1,87 @@ +import { Box, Grid, Stack, Typography } from "@mui/material"; +import { Dictionary } from "@types"; +import { + 運営会社ログインユーザ, + 顧客ログインユーザ一覧取得, +} from "api/login-user"; +import { FormProvider, RHFTextField } from "components/hook-form"; +import useAPICall from "hooks/useAPICall"; +import useSearchConditionContext from "hooks/useSearchConditionContext"; +import { UseTableReturn } from "hooks/useTable"; +import { isEqual } from "lodash"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; + +type FormProps = { + name: string; + email: string; +}; + +type CommonProps = { + table: UseTableReturn<運営会社ログインユーザ>; +}; +export default function SearchBox({ table }: CommonProps) { + const [lastSendSearchCondition, setLastSendSearchCondition] = + useState({ default: false }); + const { condition, initialized, get, addCondition } = + useSearchConditionContext(); + const form = useForm({ + defaultValues: { + name: "", + email: "", + }, + }); + + const { callAPI: call顧客一覧取得, makeSendData } = useAPICall({ + apiMethod: 顧客ログインユーザ一覧取得, + form, + backDrop: true, + onSuccess: ({ data }) => { + table.setRowData(data.list); + }, + }); + + const handleBlur = () => { + addCondition(form.getValues()); + }; + const handleSubmit = async (data: FormProps) => { + addCondition(data); + }; + + const fetch = async (data: Dictionary) => { + const sendData = makeSendData(data); + console.log({ sendData }); + if (!isEqual(sendData, lastSendSearchCondition)) { + setLastSendSearchCondition(sendData); + call顧客一覧取得(sendData); + } + }; + + useEffect(() => { + console.log({ initialized, condition }); + if (initialized) { + fetch(condition); + } + }, [condition, initialized]); + + return ( + + + + + + 名前 + + + + + + Email + + + + + + + ); +} diff --git a/src/pages/dashboard/login-user/顧客ログインユーザ一覧/TableBox.tsx b/src/pages/dashboard/login-user/顧客ログインユーザ一覧/TableBox.tsx new file mode 100644 index 0000000..97dd592 --- /dev/null +++ b/src/pages/dashboard/login-user/顧客ログインユーザ一覧/TableBox.tsx @@ -0,0 +1,93 @@ +import { + Box, + Table, + TableBody, + TableCell, + TableContainer, + TablePagination, + TableRow, +} from "@mui/material"; +import { 運営会社ログインユーザ } from "api/login-user"; +import TableHeadCustom, { + HeadLabelProps, +} from "components/table/TableHeadCustom"; +import { UseTableReturn } from "hooks/useTable"; + +type CommonProps = { + table: UseTableReturn<運営会社ログインユーザ>; +}; +export default function TableBox({ table }: CommonProps) { + const TABLE_HEAD: HeadLabelProps[] = [ + { id: "name", label: "名前", align: "left", needSort: false }, + { id: "email", label: "Email", align: "left", needSort: false }, + { id: "customer_name", label: "顧客名", align: "left", needSort: false }, + ]; + const { + order, + page, + sort, + rowsPerPage, + fetched, + fillteredRow, + isNotFound, + dataLength, + // + onSort, + onChangePage, + onChangeRowsPerPage, + // + setRowData, + // + ROWS_PER_PAGES, + } = table; + + return ( + <> + + + + + + {fillteredRow.map((row, index) => ( + + ))} + +
+
+ + + + + + ); +} + +type RowProps = { + data: 運営会社ログインユーザ; +}; +function Row({ data }: RowProps) { + return ( + + {data.name} + {data.email} + + {data.customer_name}({data.customer_code}) + + + ); +} diff --git a/src/pages/dashboard/login-user/顧客ログインユーザ一覧/index.tsx b/src/pages/dashboard/login-user/顧客ログインユーザ一覧/index.tsx new file mode 100644 index 0000000..57e34c1 --- /dev/null +++ b/src/pages/dashboard/login-user/顧客ログインユーザ一覧/index.tsx @@ -0,0 +1,38 @@ +import { Box } from "@mui/material"; +import { 運営会社ログインユーザ } from "api/login-user"; +import { SearchConditionContextProvider } from "contexts/SearchConditionContext"; +import useDashboard from "hooks/useDashBoard"; +import useTable from "hooks/useTable"; +import { PageID, TabID } from "pages"; +import { useEffect } from "react"; +import SearchBox from "./SearchBox"; +import TableBox from "./TableBox"; + +export default function 顧客一覧() { + const { setHeaderTitle, setTabs } = useDashboard( + PageID.ログインユーザ_顧客一覧, + TabID.NONE + ); + + useEffect(() => { + setHeaderTitle("ログインユーザ一覧"); + setTabs(null); + }, []); + + return ( + + + + ); +} + +function Page() { + const table = useTable<運営会社ログインユーザ>(); + + return ( + + + + + ); +} diff --git a/src/pages/dashboard/login-user/顧客ログインユーザ新規登録/index.tsx b/src/pages/dashboard/login-user/顧客ログインユーザ新規登録/index.tsx new file mode 100644 index 0000000..b0ca88f --- /dev/null +++ b/src/pages/dashboard/login-user/顧客ログインユーザ新規登録/index.tsx @@ -0,0 +1,148 @@ +import { yupResolver } from "@hookform/resolvers/yup"; +import { Box, Button, Stack, Typography } from "@mui/material"; +import { 顧客マスタ, 顧客マスタ一覧取得 } from "api/customer"; +import { 顧客ログインユーザ新規登録 } from "api/login-user"; +import { + FormProvider, + RHFAutoComplete, + RHFTextField, +} from "components/hook-form"; +import { + AutoCompleteOption, + AutoCompleteOptionType, + getValue, +} from "components/hook-form/RHFAutoComplete"; +import StackRow from "components/stack/StackRow"; +import useAPICall from "hooks/useAPICall"; +import useDashboard from "hooks/useDashBoard"; +import useNavigateCustom from "hooks/useNavigateCustom"; +import useSnackbarCustom from "hooks/useSnackbarCustom"; +import { PageID, TabID } from "pages"; +import { useEffect, useMemo, useState } from "react"; +import { useForm } from "react-hook-form"; +import { getPath } from "routes/path"; +import 顧客マスタストア from "storage/cache/顧客マスタ"; +import { object, string } from "yup"; + +type FormProps = { + name: string; + email: string; + password: string; + password_retype: string; + customer_code: AutoCompleteOptionType; +}; + +export default function 顧客ログインユーザ新規登録_() { + const { setHeaderTitle, setTabs } = useDashboard( + PageID.ログインユーザ_顧客新規登録, + TabID.NONE + ); + + const { success, error } = useSnackbarCustom(); + const { navigateWhenChanged } = useNavigateCustom(); + + const [顧客マスタ, set顧客マスタ] = useState<顧客マスタ[]>([]); + + const 顧客マスタOptions: AutoCompleteOption[] = useMemo(() => { + return 顧客マスタ.map((data) => { + return { + label: data.customer_name, + value: data.customer_id, + }; + }); + }, [顧客マスタ]); + + const form = useForm({ + defaultValues: { + name: "", + email: "", + password: "", + password_retype: "", + customer_code: "", + }, + resolver: yupResolver( + object().shape({ + name: string().required("必須項目です"), + email: string().required("必須項目です"), + password: string().required("必須項目です"), + password_retype: string() + .required("必須項目です") + .test("retype", "入力が一致しません", function (value) { + return this.parent.password === value; + }), + customer_code: object().required("必須項目です"), + }) + ), + }); + + const { callAPI: call顧客ログインユーザ新規登録 } = useAPICall({ + apiMethod: 顧客ログインユーザ新規登録, + form, + backDrop: true, + onSuccess: ({ data }, sendData) => { + success("登録しました"); + navigateWhenChanged( + getPath(PageID.ログインユーザ_顧客一覧), + new URLSearchParams({ email: sendData.email }) + ); + }, + onFailed: () => { + error("失敗しました"); + }, + }); + + const handleSubmit = (data: FormProps) => { + const customer_code = getValue(data.customer_code); + call顧客ログインユーザ新規登録({ + ...data, + customer_code, + }); + }; + + useEffect(() => { + setHeaderTitle("ログインユーザ登録"); + setTabs(null); + + 顧客マスタストア.get().then((data) => { + if (data) { + set顧客マスタ(data); + } + }); + }, []); + + return ( + + + + + 名称 + + + + Email + + + + パスワード + + + + パスワード(再入力) + + + + 顧客 + + + + + + + + + ); +} diff --git a/src/pages/index.ts b/src/pages/index.ts index 6bb7252..4f0ac2f9 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -2,15 +2,27 @@ let id = 0; export const PageID = { NONE: id++, + // 認証関連 ---------------------------------- LOGIN: id++, LOGOUT: id++, + // ダッシュボード系 START ---------------------------------- + DASHBOARD_ENPTY: id++, DASHBOARD_OVERVIEW: id++, + // 顧客管理 + 顧客一覧: id++, + + // ログインユーザー管理 + ログインユーザ_顧客一覧: id++, + ログインユーザ_顧客新規登録: id++, + ログインユーザ_店舗一覧: id++, + サービス券発行用QRコード: id++, サービス券利用履歴: id++, QRサービス券発行申請: id++, + // ダッシュボード系 END ---------------------------------- PAGE_403: id++, PAGE_404: id++, diff --git a/src/routes/path.ts b/src/routes/path.ts index bce6f1e..05df51b 100644 --- a/src/routes/path.ts +++ b/src/routes/path.ts @@ -29,7 +29,14 @@ const getTabId = (key: PathKey): TabID => { }; const PATHS_DASHBOARD = { + [makePathKey(PageID.DASHBOARD_ENPTY)]: "/dashboard/loading", [makePathKey(PageID.DASHBOARD_OVERVIEW)]: "/dashboard", + [makePathKey(PageID.ログインユーザ_顧客一覧)]: + "/dashboard/login-user/customer/list", + [makePathKey(PageID.ログインユーザ_顧客新規登録)]: + "/dashboard/login-user/customer/register", + [makePathKey(PageID.ログインユーザ_店舗一覧)]: + "/dashboard/login-user/shop/list", [makePathKey(PageID.サービス券発行用QRコード)]: "/dashboard/qrcode/generate", [makePathKey(PageID.サービス券利用履歴)]: "/dashboard/qrcode/history", diff --git a/src/routes/sub/dashboard.tsx b/src/routes/sub/dashboard.tsx index 3d10b0c..ddbc0f8 100644 --- a/src/routes/sub/dashboard.tsx +++ b/src/routes/sub/dashboard.tsx @@ -12,6 +12,7 @@ export default function DashboardRoutes(): RouteObject[] { const { currentRole } = useAuth(); const children: RouteObject[] = useMemo(() => { + const Enpty = Loadable(lazy(() => import("pages/dashboard/empty"))); const Dashboard = Loadable(lazy(() => import("pages/dashboard"))); const サービス券発行用QRコード = Loadable( @@ -20,12 +21,32 @@ export default function DashboardRoutes(): RouteObject[] { const サービス券利用履歴 = Loadable( lazy(() => import("pages/dashboard/qrcode/サービス券利用履歴")) ); + const 顧客ログインユーザ一覧 = Loadable( + lazy(() => import("pages/dashboard/login-user/顧客ログインユーザ一覧")) + ); + const 顧客ログインユーザ新規登録 = Loadable( + lazy( + () => import("pages/dashboard/login-user/顧客ログインユーザ新規登録") + ) + ); const allChildren = [ + { + pageId: PageID.DASHBOARD_ENPTY, + element: , + }, { pageId: PageID.DASHBOARD_OVERVIEW, element: , }, + { + pageId: PageID.ログインユーザ_顧客一覧, + element: <顧客ログインユーザ一覧 />, + }, + { + pageId: PageID.ログインユーザ_顧客新規登録, + element: <顧客ログインユーザ新規登録 />, + }, { pageId: PageID.サービス券発行用QRコード, element: <サービス券発行用QRコード />, diff --git a/src/storage/cache/顧客マスタ.ts b/src/storage/cache/顧客マスタ.ts new file mode 100644 index 0000000..1cd2d22 --- /dev/null +++ b/src/storage/cache/顧客マスタ.ts @@ -0,0 +1,15 @@ +import { 顧客マスタ, 顧客マスタ一覧取得 } from "api/customer"; + +class 顧客マスタストア { + private list: 顧客マスタ[] | undefined = undefined; + + async get() { + if (this.list === undefined) { + const res = await 顧客マスタ一覧取得({}); + this.list = res?.data.list ?? []; + } + return this.list; + } +} + +export default new 顧客マスタストア();