| @@ -16,23 +16,23 @@ import BackDropContextProvider from "contexts/BackDropContext"; | |||
| export default function App() { | |||
| return ( | |||
| <LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale={ja}> | |||
| <AuthContextProvider> | |||
| <PageContextProvider> | |||
| <WindowSizeContextProvider> | |||
| <BrowserRouter> | |||
| <AppThemeProvider> | |||
| <SnackbarProvider> | |||
| <BackDropContextProvider> | |||
| <PageContextProvider> | |||
| <WindowSizeContextProvider> | |||
| <BrowserRouter> | |||
| <AppThemeProvider> | |||
| <SnackbarProvider> | |||
| <BackDropContextProvider> | |||
| <AuthContextProvider> | |||
| <CsrfTokenProvider /> | |||
| <CssBaseline /> | |||
| <Routes /> | |||
| </BackDropContextProvider> | |||
| </SnackbarProvider> | |||
| </AppThemeProvider> | |||
| </BrowserRouter> | |||
| </WindowSizeContextProvider> | |||
| </PageContextProvider> | |||
| </AuthContextProvider> | |||
| </AuthContextProvider> | |||
| </BackDropContextProvider> | |||
| </SnackbarProvider> | |||
| </AppThemeProvider> | |||
| </BrowserRouter> | |||
| </WindowSizeContextProvider> | |||
| </PageContextProvider> | |||
| </LocalizationProvider> | |||
| ); | |||
| } | |||
| @@ -8,6 +8,9 @@ export type Me = { | |||
| email: string; | |||
| role: UserRole; | |||
| shop_id: string | null; | |||
| switched_user_id: string | null; | |||
| switched_name: string | null; | |||
| switched_role: UserRole | null; | |||
| }; | |||
| type MeResponse = { | |||
| @@ -45,3 +48,54 @@ export const logout = async () => { | |||
| }); | |||
| return res; | |||
| }; | |||
| // -------顧客成り代わり開始--------------- | |||
| export type 顧客成り代わり開始Request = { | |||
| user_id: string; | |||
| }; | |||
| export type 顧客成り代わり開始Response = { | |||
| data: { | |||
| user_id: string; | |||
| name: string; | |||
| role: string; | |||
| }; | |||
| } & APICommonResponse; | |||
| export const 顧客成り代わり開始 = async (param: 顧客成り代わり開始Request) => { | |||
| const res = await request<顧客成り代わり開始Response>({ | |||
| url: getUrl(ApiId.顧客成り代わり開始), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(param), | |||
| }); | |||
| return res; | |||
| }; | |||
| // -------店舗成り代わり開始--------------- | |||
| export type 店舗成り代わり開始Request = { | |||
| user_id: string; | |||
| }; | |||
| export type 店舗成り代わり開始Response = { | |||
| data: { | |||
| user_id: string; | |||
| name: string; | |||
| role: string; | |||
| }; | |||
| } & APICommonResponse; | |||
| export const 店舗成り代わり開始 = async (param: 店舗成り代わり開始Request) => { | |||
| const res = await request<店舗成り代わり開始Response>({ | |||
| url: getUrl(ApiId.店舗成り代わり開始), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(param), | |||
| }); | |||
| return res; | |||
| }; | |||
| // -------成り代わり終了--------------- | |||
| export type 成り代わり終了Request = {}; | |||
| export type 成り代わり終了Response = {} & APICommonResponse; | |||
| export const 成り代わり終了 = async (param: 成り代わり終了Request) => { | |||
| const res = await request<成り代わり終了Response>({ | |||
| url: getUrl(ApiId.成り代わり終了), | |||
| method: HttpMethod.GET, | |||
| data: new URLSearchParams(param), | |||
| }); | |||
| return res; | |||
| }; | |||
| @@ -14,6 +14,9 @@ export const ApiId = { | |||
| ME: id++, | |||
| LOGIN: id++, | |||
| LOGOUT: id++, | |||
| 顧客成り代わり開始: id++, | |||
| 店舗成り代わり開始: id++, | |||
| 成り代わり終了: id++, | |||
| // ログインユーザ関連 ---------------------------------- | |||
| 顧客ログインユーザ新規登録: id++, | |||
| @@ -29,6 +32,7 @@ export const ApiId = { | |||
| 店舗新規登録: id++, | |||
| デポジット情報取得: id++, | |||
| デポジットチャージ: id++, | |||
| 店舗設定: id++, | |||
| // QRサービス券関連------------------------------- | |||
| QRサービス券取得: id++, | |||
| @@ -1,13 +1,18 @@ | |||
| import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "api"; | |||
| import { getUrl } from "./url"; | |||
| type 店舗 = { | |||
| shp_id: string; | |||
| deposit: string; | |||
| export type 店舗 = { | |||
| shop_id: string; | |||
| deposit: number; | |||
| name: string; | |||
| qr_service_expire_min: number; | |||
| under_amount_when_create: number; | |||
| under_amount_when_auth: number; | |||
| under_amount_when_use: number; | |||
| }; | |||
| // -------店舗一覧取得--------------- | |||
| export type 店舗一覧取得Request = { | |||
| shop_id?: string; | |||
| email?: string; | |||
| name?: string; | |||
| }; | |||
| @@ -61,9 +66,7 @@ export const デポジット情報取得 = async (param: デポジット情報 | |||
| // -------デポジットチャージ--------------- | |||
| export type デポジットチャージRequest = { | |||
| shop_id: string; | |||
| name: string; | |||
| email: string; | |||
| password: string; | |||
| amount: string; | |||
| }; | |||
| export type デポジットチャージResponse = { | |||
| data: { | |||
| @@ -79,3 +82,25 @@ export const デポジットチャージ = async (param: デポジットチャ | |||
| }); | |||
| return res; | |||
| }; | |||
| // -------店舗設定--------------- | |||
| export type 店舗設定Request = { | |||
| shop_id: string; | |||
| qr_service_expire_min: string; | |||
| under_amount_when_create: string; | |||
| under_amount_when_auth: string; | |||
| under_amount_when_use: string; | |||
| }; | |||
| export type 店舗設定Response = { | |||
| data: { | |||
| shop_id: string; | |||
| }; | |||
| } & APICommonResponse; | |||
| export const 店舗設定 = async (param: 店舗設定Request) => { | |||
| const res = await request<店舗設定Response>({ | |||
| url: getUrl(ApiId.店舗設定), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(param), | |||
| }); | |||
| return res; | |||
| }; | |||
| @@ -9,6 +9,9 @@ const urls = { | |||
| [A.ME]: "me", | |||
| [A.LOGIN]: "login", | |||
| [A.LOGOUT]: "logout", | |||
| [A.顧客成り代わり開始]: "role/switch/customer", | |||
| [A.店舗成り代わり開始]: "role/switch/shop", | |||
| [A.成り代わり終了]: "role/switch/end", | |||
| // ログインユーザ関連 ---------------------------------- | |||
| [A.顧客ログインユーザ新規登録]: "login-user/customer/register", | |||
| @@ -24,6 +27,7 @@ const urls = { | |||
| [A.店舗新規登録]: "shop/register", | |||
| [A.デポジット情報取得]: "shop/deposit", | |||
| [A.デポジットチャージ]: "shop/deposit/charge", | |||
| [A.店舗設定]: "shop/config", | |||
| // QRサービス券関連------------------------------- | |||
| [A.QRサービス券取得]: "qr-service/get-ticket", | |||
| @@ -3,6 +3,7 @@ import { UserRole } from "./UserRole"; | |||
| const 共通ルート = [P.LOGIN, P.LOGOUT]; | |||
| const 認証後共通ルート = [P.DASHBOARD_ENPTY, P.DASHBOARD_OVERVIEW]; | |||
| const 管理者顧客共通ルート = [P.成り代わり終了]; | |||
| const 認可別許可ルート: { | |||
| [route: string]: P[]; | |||
| @@ -11,11 +12,21 @@ const 認可別許可ルート: { | |||
| [UserRole.ADMIN]: [ | |||
| ...共通ルート, | |||
| ...認証後共通ルート, | |||
| ...管理者顧客共通ルート, | |||
| P.ログインユーザ_顧客一覧, | |||
| P.ログインユーザ_顧客新規登録, | |||
| ], | |||
| [UserRole.CUSTOMER]: [...共通ルート, ...認証後共通ルート], | |||
| [UserRole.CUSTOMER]: [ | |||
| ...共通ルート, | |||
| ...認証後共通ルート, | |||
| ...管理者顧客共通ルート, | |||
| P.ログインユーザ_店舗一覧, | |||
| P.ログインユーザ_店舗新規登録, | |||
| P.店舗一覧, | |||
| P.店舗新規登録, | |||
| P.店舗詳細, | |||
| ], | |||
| [UserRole.SHOP]: [ | |||
| ...共通ルート, | |||
| @@ -1,14 +1,24 @@ | |||
| import { HasChildren } from "@types"; | |||
| import { ResultCode } from "api"; | |||
| import { login as APILogin, logout as APILogout, me } from "api/auth"; | |||
| import { APICommonResponse, ResultCode } from "api"; | |||
| import { | |||
| login as APILogin, | |||
| logout as APILogout, | |||
| me, | |||
| 店舗成り代わり開始, | |||
| 成り代わり終了, | |||
| 顧客成り代わり開始, | |||
| } from "api/auth"; | |||
| import { UserRole } from "auth/UserRole"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import { PageID } from "pages"; | |||
| import { createContext, memo, useEffect, useMemo, useState } from "react"; | |||
| import { string } from "yup"; | |||
| import { getPath } from "routes/path"; | |||
| type ChangedRole = { | |||
| type SwitchedUser = { | |||
| user_id: string; | |||
| name: string; | |||
| role: UserRole; | |||
| customerCode: string; | |||
| }; | |||
| type Auth = { | |||
| initialized: boolean; | |||
| @@ -20,19 +30,20 @@ type Auth = { | |||
| role: UserRole; // デフォルトロール | |||
| currentRole: UserRole; // 現在のロール | |||
| currentName: string; // 現在のロール | |||
| changedRole: ChangedRole | null; | |||
| shopId: number | null; | |||
| switchedUser: SwitchedUser | null; | |||
| isSwitched: boolean; | |||
| login: (email: string, password: string) => Promise<boolean>; | |||
| logout: VoidFunction; | |||
| logout: () => Promise<void>; | |||
| me: VoidFunction; | |||
| changeRole: (changedRole: ChangedRole | null) => void; | |||
| switchCustomerRole: (userId: string) => Promise<void>; | |||
| switchShopRole: (userId: string) => Promise<void>; | |||
| switchEnd: () => Promise<void>; | |||
| }; | |||
| export const AuthContext = createContext<Auth>({ | |||
| initialized: false, | |||
| authenticated: false, | |||
| id: "", | |||
| @@ -40,14 +51,18 @@ export const AuthContext = createContext<Auth>({ | |||
| email: "", | |||
| role: UserRole.NONE, | |||
| changedRole: null, | |||
| currentRole: UserRole.NONE, | |||
| shopId: null, | |||
| currentName: "", | |||
| switchedUser: null, | |||
| isSwitched: false, | |||
| login: async (email: string, password: string) => false, | |||
| logout: () => {}, | |||
| logout: async () => {}, | |||
| me: () => {}, | |||
| changeRole: (changedRole: ChangedRole | null) => {}, | |||
| switchCustomerRole: async (userId: string) => {}, | |||
| switchShopRole: async (userId: string) => {}, | |||
| switchEnd: async () => {}, | |||
| }); | |||
| type Props = HasChildren; | |||
| @@ -57,34 +72,47 @@ function AuthContextProvider({ children }: Props) { | |||
| const [name, setName] = useState(""); | |||
| const [email, setEmail] = useState(""); | |||
| const [role, setRole] = useState<UserRole>(UserRole.NONE); | |||
| const [changedRole, setChangedRole] = useState<ChangedRole | null>(null); | |||
| const [switchedUser, setSwitchedUser] = useState<SwitchedUser | null>(null); | |||
| const [shopId, setShopId] = useState<number | null>(null); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const authenticated = useMemo(() => { | |||
| return !!email; | |||
| }, [email]); | |||
| const currentRole = useMemo(() => { | |||
| if (changedRole) { | |||
| return changedRole.role; | |||
| if (switchedUser) { | |||
| return switchedUser.role; | |||
| } | |||
| return role; | |||
| }, [changedRole, role]); | |||
| }, [switchedUser, role]); | |||
| const currentName = useMemo(() => { | |||
| if (switchedUser) { | |||
| return switchedUser.name; | |||
| } | |||
| return name; | |||
| }, [switchedUser, name]); | |||
| const { callAPI: callMe } = useAPICall({ | |||
| apiMethod: me, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| console.log("認証有"); | |||
| setInitialized(true); | |||
| setId(data.id); | |||
| setEmail(data.email); | |||
| setName(data.name); | |||
| setRole(data.role); | |||
| if (data.switched_user_id) { | |||
| setSwitchedUser({ | |||
| name: data.switched_name ?? "", | |||
| user_id: data.switched_user_id ?? "", | |||
| role: data.switched_role ?? UserRole.NONE, | |||
| }); | |||
| } | |||
| }, | |||
| onFailed: () => { | |||
| console.log("認証無"); | |||
| clear(); | |||
| setInitialized(true); | |||
| }, | |||
| @@ -100,32 +128,78 @@ function AuthContextProvider({ children }: Props) { | |||
| const { callAPI: callLogout } = useAPICall({ | |||
| apiMethod: APILogout, | |||
| backDrop: true, | |||
| onSuccess: () => { | |||
| clear(); | |||
| }, | |||
| }); | |||
| const { callAPI: call顧客成り代わり開始 } = useAPICall({ | |||
| apiMethod: 顧客成り代わり開始, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| const role = data.role as UserRole; | |||
| setSwitchedUser({ | |||
| ...data, | |||
| role, | |||
| }); | |||
| }, | |||
| }); | |||
| const { callAPI: call店舗成り代わり開始 } = useAPICall({ | |||
| apiMethod: 店舗成り代わり開始, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| const role = data.role as UserRole; | |||
| setSwitchedUser({ | |||
| ...data, | |||
| role, | |||
| }); | |||
| }, | |||
| }); | |||
| const { callAPI: call成り代わり終了 } = useAPICall({ | |||
| apiMethod: 成り代わり終了, | |||
| backDrop: true, | |||
| onSuccess: () => { | |||
| setSwitchedUser(null); | |||
| }, | |||
| }); | |||
| const clear = () => { | |||
| setId(""); | |||
| setName(""); | |||
| setEmail(""); | |||
| setRole(UserRole.NONE); | |||
| setChangedRole(null); | |||
| setSwitchedUser(null); | |||
| setShopId(null); | |||
| }; | |||
| const login = async (email: string, password: string) => { | |||
| const res = await callLogin({ email, password }); | |||
| const res: APICommonResponse | null = await callLogin({ email, password }); | |||
| return res?.result === ResultCode.SUCCESS; | |||
| }; | |||
| const logout = () => { | |||
| callLogout({}); | |||
| const logout = async () => { | |||
| await callLogout({}); | |||
| console.info("ログアウト"); | |||
| }; | |||
| const changeRole = (changedRole: ChangedRole | null) => { | |||
| setChangedRole(changedRole); | |||
| const switchCustomerRole = async (user_id: string) => { | |||
| await call顧客成り代わり開始({ | |||
| user_id, | |||
| }); | |||
| navigateWhenChanged(getPath(PageID.DASHBOARD_OVERVIEW)); | |||
| }; | |||
| const switchShopRole = async (user_id: string) => { | |||
| await call店舗成り代わり開始({ user_id }); | |||
| navigateWhenChanged(getPath(PageID.DASHBOARD_OVERVIEW)); | |||
| }; | |||
| const switchEnd = async () => { | |||
| await call成り代わり終了({}); | |||
| navigateWhenChanged(getPath(PageID.DASHBOARD_OVERVIEW)); | |||
| }; | |||
| const isSwitched = useMemo(() => { | |||
| return switchedUser !== null; | |||
| }, [switchedUser]); | |||
| const meFetch = () => { | |||
| callMe({}); | |||
| @@ -147,14 +221,17 @@ function AuthContextProvider({ children }: Props) { | |||
| role, | |||
| currentRole, | |||
| changedRole, | |||
| shopId, | |||
| currentName, | |||
| switchedUser, | |||
| // Func | |||
| login, | |||
| logout, | |||
| me: meFetch, | |||
| changeRole, | |||
| switchCustomerRole, | |||
| switchShopRole, | |||
| switchEnd, | |||
| isSwitched, | |||
| }} | |||
| > | |||
| {children} | |||
| @@ -0,0 +1,102 @@ | |||
| import { HasChildren } from "@types"; | |||
| import { 店舗, 店舗一覧取得 } from "api/shop"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import useShopTabs from "layouts/dashbord/tab/ShopTabs"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { createContext, useContext, useEffect, useState } from "react"; | |||
| import { useParams } from "react-router-dom"; | |||
| import { getPath } from "routes/path"; | |||
| type Context = { | |||
| shop: 店舗 | null; | |||
| fetch: () => Promise<void>; | |||
| moveToMain: VoidFunction; | |||
| }; | |||
| export const 店舗詳細Context = createContext<Context>({ | |||
| shop: null, | |||
| fetch: async () => {}, | |||
| moveToMain: () => {}, | |||
| }); | |||
| type Props = HasChildren; | |||
| function 店舗詳細ContextProvider({ children }: Props) { | |||
| const { shopId: paramShopId } = useParams(); | |||
| const [shop, setShop] = useState<店舗 | null>(null); | |||
| const { success, error } = useSnackbarCustom(); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const { callAPI: call店舗一覧取得 } = useAPICall({ | |||
| apiMethod: 店舗一覧取得, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| if (data.list.length !== 1) { | |||
| error("データ取得失敗"); | |||
| navigateWhenChanged(getPath(PageID.店舗一覧)); | |||
| return; | |||
| } | |||
| setShop(data.list[0]); | |||
| }, | |||
| onFailed: () => { | |||
| error("データ取得失敗"); | |||
| navigateWhenChanged(getPath(PageID.店舗一覧)); | |||
| }, | |||
| }); | |||
| const fetch = async () => { | |||
| await call店舗一覧取得({ | |||
| shop_id: paramShopId, | |||
| }); | |||
| }; | |||
| const moveToMain = () => { | |||
| navigateWhenChanged( | |||
| getPath([PageID.店舗詳細, TabID.店舗詳細_メイン], { | |||
| query: { | |||
| shopId: shop?.shop_id ?? "BBBBB", | |||
| }, | |||
| }) | |||
| ); | |||
| }; | |||
| const { setHeaderTitle } = useDashboard(); | |||
| useEffect(() => { | |||
| setHeaderTitle("店舗詳細"); | |||
| }, []); | |||
| useEffect(() => { | |||
| fetch(); | |||
| }, []); | |||
| return ( | |||
| <店舗詳細Context.Provider | |||
| value={{ | |||
| shop, | |||
| fetch, | |||
| moveToMain, | |||
| }} | |||
| > | |||
| <TabInit /> | |||
| {shop && children} | |||
| </店舗詳細Context.Provider> | |||
| ); | |||
| } | |||
| function TabInit() { | |||
| const { setTabs, tabId } = useDashboard(); | |||
| const { shop } = useContext(店舗詳細Context); | |||
| const { element } = useShopTabs(); | |||
| useEffect(() => { | |||
| setTabs(element); | |||
| }, [shop, tabId]); | |||
| return null; | |||
| } | |||
| export default 店舗詳細ContextProvider; | |||
| @@ -11,8 +11,10 @@ export default function useDashboard(pageId?: PageID, tabId?: TabID) { | |||
| } | |||
| if (tabId) { | |||
| context.setTabId(tabId); | |||
| } else { | |||
| context.setTabId(TabID.NONE); | |||
| } | |||
| }, []); | |||
| }, [pageId, tabId]); | |||
| return context; | |||
| } | |||
| @@ -17,18 +17,19 @@ import { ページアクセス許可判定 } from "auth/route"; | |||
| import useAuth from "hooks/useAuth"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import usePage from "hooks/usePage"; | |||
| import { PageID } from "pages"; | |||
| import { PageID, TabID } from "pages"; | |||
| import * as React from "react"; | |||
| import { PathOption, getPath } from "routes/path"; | |||
| import { PathKey, PathOption, getPath } from "routes/path"; | |||
| type Group = { | |||
| label: string; | |||
| children: SubGroup[]; | |||
| }; | |||
| type SubGroup = { | |||
| label: string; | |||
| label: string | React.ReactNode; | |||
| icon: React.ReactNode; | |||
| children?: Child[]; | |||
| whenIsSwitched?: boolean; | |||
| // 子要素を持たない場合は設定 | |||
| id?: PageID; | |||
| @@ -37,7 +38,7 @@ type SubGroup = { | |||
| type Child = { | |||
| label: string; | |||
| id: PageID; | |||
| id: PathKey; | |||
| option?: PathOption; | |||
| }; | |||
| @@ -65,7 +66,7 @@ const itemCategory = { | |||
| export default function Navigator(props: DrawerProps) { | |||
| const { ...other } = props; | |||
| const { name } = useAuth(); | |||
| const { currentName, isSwitched } = useAuth(); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| @@ -94,13 +95,23 @@ export default function Navigator(props: DrawerProps) { | |||
| ], | |||
| }, | |||
| { | |||
| label: "QRサービス券", | |||
| label: "店舗管理", | |||
| children: [ | |||
| { | |||
| label: "券発行用QRコード", | |||
| label: "店舗新規登録", | |||
| icon: <ArticleIcon />, | |||
| id: PageID.店舗新規登録, | |||
| }, | |||
| { | |||
| label: "店舗一覧", | |||
| icon: <ArticleIcon />, | |||
| id: PageID.サービス券発行用QRコード, | |||
| id: PageID.店舗一覧, | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| label: "QRサービス券", | |||
| children: [ | |||
| { | |||
| label: "利用履歴", | |||
| icon: <ArticleIcon />, | |||
| @@ -111,6 +122,12 @@ export default function Navigator(props: DrawerProps) { | |||
| { | |||
| label: "アカウント", | |||
| children: [ | |||
| { | |||
| label: "成り代わり終了", | |||
| icon: <SettingsIcon />, | |||
| id: PageID.成り代わり終了, | |||
| whenIsSwitched: true, | |||
| }, | |||
| { label: "ログアウト", icon: <SettingsIcon />, id: PageID.LOGOUT }, | |||
| ], | |||
| }, | |||
| @@ -131,7 +148,8 @@ export default function Navigator(props: DrawerProps) { | |||
| </ListItemIcon> | |||
| <ListItemText>{}</ListItemText> | |||
| <ListItemText> | |||
| <Typography>{name} 様</Typography> | |||
| <Typography>{currentName} 様</Typography> | |||
| {!!isSwitched && <Typography color="red">成り代わり中</Typography>} | |||
| </ListItemText> | |||
| </ListItem> | |||
| @@ -144,11 +162,12 @@ export default function Navigator(props: DrawerProps) { | |||
| } | |||
| function Group(group: Group) { | |||
| const { currentRole } = useAuth(); | |||
| const { currentRole, isSwitched } = useAuth(); | |||
| const { label, children } = group; | |||
| const elements = children | |||
| .filter(({ id }) => ページアクセス許可判定(currentRole, id ?? -1)) | |||
| .filter(({ whenIsSwitched }) => whenIsSwitched !== true || isSwitched) | |||
| .map((ele, index) => <SubGroup {...ele} key={index} />); | |||
| if (elements.length === 0) return null; | |||
| @@ -1,31 +0,0 @@ | |||
| import { Tabs } from "@mui/material"; | |||
| import { TabProps, useTab } from "./tabutil"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { getPath } from "routes/path"; | |||
| const tabs: TabProps[] = [ | |||
| { | |||
| label: "一覧", | |||
| tabId: TabID.NONE, | |||
| }, | |||
| { | |||
| label: "詳細", | |||
| tabId: TabID.A, | |||
| }, | |||
| ]; | |||
| export default function ContractTabs() { | |||
| const { elements, getTabIndex } = useTab(tabs); | |||
| return ( | |||
| <Tabs | |||
| value={getTabIndex} | |||
| textColor="inherit" | |||
| // scrollButtons | |||
| // allowScrollButtonsMobile | |||
| variant="scrollable" | |||
| > | |||
| {elements} | |||
| </Tabs> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| import { Tabs } from "@mui/material"; | |||
| import { TabProps, useTab } from "./tabutil"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { getPath } from "routes/path"; | |||
| import { useContext, useEffect, useMemo } from "react"; | |||
| import { 店舗詳細Context } from "contexts/page/dashboard/shop/店舗詳細Context"; | |||
| export default function useShopTabs() { | |||
| const { shop } = useContext(店舗詳細Context); | |||
| const tabs: TabProps[] = useMemo(() => { | |||
| return [ | |||
| { | |||
| label: "詳細", | |||
| tabId: TabID.店舗詳細_メイン, | |||
| path: getPath([PageID.店舗詳細, TabID.店舗詳細_メイン], { | |||
| query: { | |||
| shopId: shop?.shop_id ?? "aaaaa", | |||
| }, | |||
| }), | |||
| }, | |||
| { | |||
| label: "設定", | |||
| tabId: TabID.店舗詳細_設定, | |||
| path: getPath([PageID.店舗詳細, TabID.店舗詳細_設定], { | |||
| query: { | |||
| shopId: shop?.shop_id ?? "aaaaa", | |||
| }, | |||
| }), | |||
| }, | |||
| ]; | |||
| }, [shop]); | |||
| const { elements, getTabIndex } = useTab(tabs); | |||
| return { | |||
| element: ( | |||
| <Tabs | |||
| value={getTabIndex} | |||
| textColor="inherit" | |||
| // scrollButtons | |||
| // allowScrollButtonsMobile | |||
| variant="scrollable" | |||
| > | |||
| {elements} | |||
| </Tabs> | |||
| ), | |||
| }; | |||
| } | |||
| @@ -1,30 +1,31 @@ | |||
| import { PageID, TabID } from "pages"; | |||
| import usePage from "hooks/usePage"; | |||
| import { useMemo } from "react"; | |||
| import { useEffect, useMemo } from "react"; | |||
| import { Tab } from "."; | |||
| import { getPath } from "routes/path"; | |||
| import userEvent from "@testing-library/user-event"; | |||
| export type TabProps = { | |||
| label: string; | |||
| tabId: TabID; | |||
| path: string; | |||
| }; | |||
| export function useTab(tabs: TabProps[]) { | |||
| const { pageId, tabId } = usePage(); | |||
| const elements = useMemo(() => { | |||
| return tabs.map(({ label, tabId: elementTabId }, index) => { | |||
| const path = getPath([pageId, tabId]); | |||
| return tabs.map(({ label, path }, index) => { | |||
| return <Tab {...{ label, navigate: path, key: index }} />; | |||
| }); | |||
| }, [tabs]); | |||
| }, [pageId, tabId, tabs]); | |||
| const getTabIndex = useMemo(() => { | |||
| return ( | |||
| tabs.findIndex((tab) => { | |||
| return tab.tabId === tabId; | |||
| }) ?? 0 | |||
| ); | |||
| const index = tabs.findIndex((tab) => { | |||
| return tab.tabId === tabId; | |||
| }); | |||
| return index < 0 ? 0 : index; | |||
| }, [tabId, tabs]); | |||
| return { | |||
| @@ -14,7 +14,7 @@ export default function SimpleLayout() { | |||
| <Grid container> | |||
| <Grid item xs /> | |||
| <Grid item xs={5} textAlign="center"> | |||
| MyPage | |||
| HT Dashboard | |||
| </Grid> | |||
| <Grid item xs /> | |||
| </Grid> | |||
| @@ -6,16 +6,21 @@ import { useEffect } from "react"; | |||
| import { getPath } from "routes/path"; | |||
| export default function Logout() { | |||
| const { logout } = useAuth(); | |||
| const { info, success } = useSnackbarCustom(); | |||
| const { authenticated, logout } = useAuth(); | |||
| const { info } = useSnackbarCustom(); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| useEffect(() => { | |||
| logout(); | |||
| info("ログアウトしました"); | |||
| navigateWhenChanged(getPath(PageID.LOGIN)); | |||
| }, []); | |||
| useEffect(() => { | |||
| if (authenticated === false) { | |||
| navigateWhenChanged(getPath(PageID.LOGIN)); | |||
| } | |||
| }, [authenticated]); | |||
| return null; | |||
| } | |||
| @@ -50,7 +50,6 @@ export default function SearchBox({ table }: CommonProps) { | |||
| const fetch = async (data: Dictionary) => { | |||
| const sendData = makeSendData(data); | |||
| console.log({ sendData }); | |||
| if (!isEqual(sendData, lastSendSearchCondition)) { | |||
| setLastSendSearchCondition(sendData); | |||
| call顧客一覧取得(sendData); | |||
| @@ -58,7 +57,6 @@ export default function SearchBox({ table }: CommonProps) { | |||
| }; | |||
| useEffect(() => { | |||
| console.log({ initialized, condition }); | |||
| if (initialized) { | |||
| fetch(condition); | |||
| } | |||
| @@ -11,6 +11,8 @@ import { 運営会社ログインユーザ } from "api/login-user"; | |||
| import TableHeadCustom, { | |||
| HeadLabelProps, | |||
| } from "components/table/TableHeadCustom"; | |||
| import useAuth from "hooks/useAuth"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { UseTableReturn } from "hooks/useTable"; | |||
| type CommonProps = { | |||
| @@ -81,8 +83,15 @@ type RowProps = { | |||
| data: 運営会社ログインユーザ; | |||
| }; | |||
| function Row({ data }: RowProps) { | |||
| const { switchCustomerRole } = useAuth(); | |||
| const { info } = useSnackbarCustom(); | |||
| const handleClick = () => { | |||
| switchCustomerRole(data.id); | |||
| info("成り代わり開始しました"); | |||
| }; | |||
| return ( | |||
| <TableRow hover sx={{ cursor: "pointer" }}> | |||
| <TableRow hover sx={{ cursor: "pointer" }} onClick={handleClick}> | |||
| <TableCell>{data.name}</TableCell> | |||
| <TableCell>{data.email}</TableCell> | |||
| <TableCell> | |||
| @@ -0,0 +1,81 @@ | |||
| import { Box, Grid, Stack, Typography } from "@mui/material"; | |||
| import { Dictionary } from "@types"; | |||
| import { 店舗, 店舗一覧取得 } from "api/shop"; | |||
| 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; | |||
| }; | |||
| type CommonProps = { | |||
| table: UseTableReturn<店舗>; | |||
| }; | |||
| export default function SearchBox({ table }: CommonProps) { | |||
| const [lastSendSearchCondition, setLastSendSearchCondition] = | |||
| useState<object>({ default: false }); | |||
| const { condition, initialized, get, addCondition } = | |||
| useSearchConditionContext(); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| name: "", | |||
| }, | |||
| }); | |||
| 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); | |||
| if (!isEqual(sendData, lastSendSearchCondition)) { | |||
| setLastSendSearchCondition(sendData); | |||
| call店舗一覧取得(sendData); | |||
| } | |||
| }; | |||
| // 初期値設定 | |||
| useEffect(() => { | |||
| if (initialized) { | |||
| form.setValue("name", get("name")); | |||
| } | |||
| }, [initialized, condition]); | |||
| useEffect(() => { | |||
| if (initialized) { | |||
| fetch(condition); | |||
| } | |||
| }, [condition, initialized]); | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Box sx={{ p: 1, m: 1 }}> | |||
| <Grid container spacing={2} mt={-1}> | |||
| <Grid item xs={12} lg={6}> | |||
| <Stack> | |||
| <Typography>名前</Typography> | |||
| <RHFTextField name="name" onBlur={handleBlur} /> | |||
| </Stack> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </FormProvider> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,113 @@ | |||
| import { | |||
| Box, | |||
| Table, | |||
| TableBody, | |||
| TableCell, | |||
| TableContainer, | |||
| TablePagination, | |||
| TableRow, | |||
| } from "@mui/material"; | |||
| import { 運営会社ログインユーザ } from "api/login-user"; | |||
| import { 店舗 } from "api/shop"; | |||
| import TableHeadCustom, { | |||
| HeadLabelProps, | |||
| } from "components/table/TableHeadCustom"; | |||
| import useAuth from "hooks/useAuth"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { UseTableReturn } from "hooks/useTable"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { useMemo } from "react"; | |||
| import { getPath } from "routes/path"; | |||
| import { numberFormat } from "utils/string"; | |||
| type CommonProps = { | |||
| table: UseTableReturn<店舗>; | |||
| }; | |||
| export default function TableBox({ table }: CommonProps) { | |||
| const TABLE_HEAD: HeadLabelProps[] = [ | |||
| { id: "name", label: "名前", align: "left", needSort: false }, | |||
| { id: "deposit", label: "デポジット", align: "left", needSort: false }, | |||
| ]; | |||
| const { | |||
| order, | |||
| page, | |||
| sort, | |||
| rowsPerPage, | |||
| fetched, | |||
| fillteredRow, | |||
| isNotFound, | |||
| dataLength, | |||
| // | |||
| onSort, | |||
| onChangePage, | |||
| onChangeRowsPerPage, | |||
| // | |||
| setRowData, | |||
| // | |||
| ROWS_PER_PAGES, | |||
| } = table; | |||
| return ( | |||
| <> | |||
| <TableContainer> | |||
| <Table sx={{ minWidth: 1200 }} size="small"> | |||
| <TableHeadCustom | |||
| order={order} | |||
| orderBy={sort} | |||
| headLabel={TABLE_HEAD} | |||
| rowCount={1} | |||
| numSelected={0} | |||
| onSort={onSort} | |||
| /> | |||
| <TableBody> | |||
| {fillteredRow.map((row, index) => ( | |||
| <Row data={row} key={index} /> | |||
| ))} | |||
| </TableBody> | |||
| </Table> | |||
| </TableContainer> | |||
| <Box sx={{ position: "relative" }}> | |||
| <TablePagination | |||
| rowsPerPageOptions={ROWS_PER_PAGES} | |||
| component="div" | |||
| count={dataLength} | |||
| rowsPerPage={rowsPerPage} | |||
| page={page} | |||
| onPageChange={onChangePage} | |||
| onRowsPerPageChange={onChangeRowsPerPage} | |||
| /> | |||
| </Box> | |||
| </> | |||
| ); | |||
| } | |||
| type RowProps = { | |||
| data: 店舗; | |||
| }; | |||
| function Row({ data: { deposit, name, shop_id } }: RowProps) { | |||
| const { info } = useSnackbarCustom(); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const handleClick = () => { | |||
| navigateWhenChanged( | |||
| getPath([PageID.店舗詳細, TabID.店舗詳細_メイン], { | |||
| query: { | |||
| shopId: shop_id, | |||
| }, | |||
| }) | |||
| ); | |||
| }; | |||
| const formatDeposit = useMemo(() => { | |||
| return numberFormat(deposit) + "円"; | |||
| }, [deposit]); | |||
| return ( | |||
| <TableRow hover sx={{ cursor: "pointer" }} onClick={handleClick}> | |||
| <TableCell>{name}</TableCell> | |||
| <TableCell>{formatDeposit}</TableCell> | |||
| </TableRow> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| import { Box } from "@mui/material"; | |||
| import { 店舗 } from "api/shop"; | |||
| 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 ( | |||
| <SearchConditionContextProvider> | |||
| <Page /> | |||
| </SearchConditionContextProvider> | |||
| ); | |||
| } | |||
| function Page() { | |||
| const table = useTable<店舗>(); | |||
| return ( | |||
| <Box> | |||
| <SearchBox table={table} /> | |||
| <TableBox table={table} /> | |||
| </Box> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,82 @@ | |||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||
| import { Box, Button, Stack, Typography } from "@mui/material"; | |||
| import { 店舗新規登録 } from "api/shop"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| 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 } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import { getPath } from "routes/path"; | |||
| import { object, string } from "yup"; | |||
| type FormProps = { | |||
| name: string; | |||
| }; | |||
| export default function 店舗新規登録_() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| PageID.店舗新規登録, | |||
| TabID.NONE | |||
| ); | |||
| const { success, error } = useSnackbarCustom(); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| name: "", | |||
| }, | |||
| resolver: yupResolver( | |||
| object().shape({ | |||
| name: string().required("必須項目です"), | |||
| }) | |||
| ), | |||
| }); | |||
| const { callAPI: call店舗新規登録 } = useAPICall({ | |||
| apiMethod: 店舗新規登録, | |||
| form, | |||
| backDrop: true, | |||
| onSuccess: ({ data }, sendData) => { | |||
| success("登録しました"); | |||
| navigateWhenChanged( | |||
| getPath(PageID.店舗一覧), | |||
| new URLSearchParams({ name: sendData.name }) | |||
| ); | |||
| }, | |||
| onFailed: () => { | |||
| error("失敗しました"); | |||
| }, | |||
| }); | |||
| const handleSubmit = (data: FormProps) => { | |||
| call店舗新規登録({ | |||
| ...data, | |||
| }); | |||
| }; | |||
| useEffect(() => { | |||
| setHeaderTitle("店舗新規登録"); | |||
| setTabs(null); | |||
| }, []); | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Box> | |||
| <Stack> | |||
| <Box> | |||
| <Typography>名称</Typography> | |||
| <RHFTextField name="name" /> | |||
| </Box> | |||
| <StackRow> | |||
| <Button type="submit">登録</Button> | |||
| </StackRow> | |||
| </Stack> | |||
| </Box> | |||
| </FormProvider> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| import { Box, Stack } from "@mui/material"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import { PageID, TabID } from "pages"; | |||
| import デポジットチャージ from "./デポジットチャージ"; | |||
| import 詳細情報 from "./詳細情報"; | |||
| export default function 店舗詳細() { | |||
| const {} = useDashboard(PageID.店舗詳細, TabID.店舗詳細_メイン); | |||
| return <Page />; | |||
| } | |||
| function Page() { | |||
| return ( | |||
| <Box> | |||
| <Stack spacing={2}> | |||
| <詳細情報 /> | |||
| <デポジットチャージ /> | |||
| {/* 詳細情報テーブル */} | |||
| {/* チャージ */} | |||
| {/* 利用履歴 */} | |||
| </Stack> | |||
| </Box> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,84 @@ | |||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||
| import { Box, Button, Card, Grid, Stack, Typography } from "@mui/material"; | |||
| import { デポジットチャージ } from "api/shop"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| import StackRow from "components/stack/StackRow"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { useContext } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import { number, object } from "yup"; | |||
| import { 店舗詳細Context } from "../../../../contexts/page/dashboard/shop/店舗詳細Context"; | |||
| type FormProps = { | |||
| amount: string; | |||
| }; | |||
| export default function デポジットチャージ_() { | |||
| const { shop, fetch } = useContext(店舗詳細Context); | |||
| const { success, error } = useSnackbarCustom(); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| amount: "", | |||
| }, | |||
| resolver: yupResolver( | |||
| object().shape({ | |||
| amount: number() | |||
| .typeError("数値を入力してください") | |||
| .min(1, "1円-10万円まで入力できます") | |||
| .max(100000, "1円-10万円まで入力できます"), | |||
| }) | |||
| ), | |||
| }); | |||
| const { callAPI: callデポジットチャージ } = useAPICall({ | |||
| apiMethod: デポジットチャージ, | |||
| backDrop: true, | |||
| form, | |||
| onSuccess: () => { | |||
| success("チャージしました"); | |||
| fetch(); | |||
| form.setValue("amount", ""); | |||
| }, | |||
| onFailed: () => { | |||
| error("失敗しました"); | |||
| }, | |||
| }); | |||
| const handleSubmit = (data: FormProps) => { | |||
| if (shop === null) return; | |||
| callデポジットチャージ({ | |||
| shop_id: shop.shop_id, | |||
| amount: data.amount, | |||
| }); | |||
| }; | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Card sx={{ p: 2 }} elevation={1}> | |||
| <Box> | |||
| <Stack spacing={2}> | |||
| <Typography variant="h6">デポジットチャージ</Typography> | |||
| <StackRow> | |||
| <Grid container columnGap={2}> | |||
| <Grid item xs={12} md={6}> | |||
| <Typography>チャージ金額</Typography> | |||
| <StackRow> | |||
| <RHFTextField type="number" name="amount" /> | |||
| <Typography>円</Typography> | |||
| </StackRow> | |||
| </Grid> | |||
| </Grid> | |||
| </StackRow> | |||
| <StackRow> | |||
| <Button type="submit" variant="contained"> | |||
| チャージ実行 | |||
| </Button> | |||
| </StackRow> | |||
| </Stack> | |||
| </Box> | |||
| </Card> | |||
| </FormProvider> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| import { Box, Stack } from "@mui/material"; | |||
| import { 店舗詳細Context } from "contexts/page/dashboard/shop/店舗詳細Context"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import ShopTabs from "layouts/dashbord/tab/ShopTabs"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { useContext, useEffect } from "react"; | |||
| import 設定 from "./設定"; | |||
| export default function 店舗詳細設定() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| PageID.店舗詳細, | |||
| TabID.店舗詳細_設定 | |||
| ); | |||
| return <Page />; | |||
| } | |||
| function Page() { | |||
| return ( | |||
| <Box> | |||
| <Stack spacing={2}> | |||
| <設定 /> | |||
| </Stack> | |||
| </Box> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,120 @@ | |||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||
| import { Box, Button, Card, Grid, Stack, Typography } from "@mui/material"; | |||
| import { 店舗設定 } from "api/shop"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| import StackRow from "components/stack/StackRow"; | |||
| import { 店舗詳細Context } from "contexts/page/dashboard/shop/店舗詳細Context"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { useContext } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import { number, object } from "yup"; | |||
| type FormProps = { | |||
| qr_service_expire_min: number; | |||
| under_amount_when_create: number; | |||
| under_amount_when_auth: number; | |||
| under_amount_when_use: number; | |||
| }; | |||
| export default function 設定_() { | |||
| const { shop, fetch, moveToMain } = useContext(店舗詳細Context); | |||
| const { success, error } = useSnackbarCustom(); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| qr_service_expire_min: shop?.qr_service_expire_min ?? 0, | |||
| under_amount_when_create: shop?.under_amount_when_create ?? 0, | |||
| under_amount_when_auth: shop?.under_amount_when_auth ?? 0, | |||
| under_amount_when_use: shop?.under_amount_when_use ?? 0, | |||
| }, | |||
| resolver: yupResolver( | |||
| object().shape({ | |||
| qr_service_expire_min: number().typeError("数値を入力してください"), | |||
| under_amount_when_create: number().typeError("数値を入力してください"), | |||
| under_amount_when_auth: number().typeError("数値を入力してください"), | |||
| under_amount_when_use: number().typeError("数値を入力してください"), | |||
| }) | |||
| ), | |||
| }); | |||
| const { callAPI: call店舗設定 } = useAPICall({ | |||
| apiMethod: 店舗設定, | |||
| backDrop: true, | |||
| form, | |||
| onSuccess: () => { | |||
| success("設定しました"); | |||
| fetch(); | |||
| moveToMain(); | |||
| }, | |||
| onFailed: () => { | |||
| error("失敗しました"); | |||
| }, | |||
| }); | |||
| const handleSubmit = (data: FormProps) => { | |||
| if (shop === null) return; | |||
| call店舗設定({ | |||
| shop_id: shop.shop_id, | |||
| qr_service_expire_min: String(data.qr_service_expire_min), | |||
| under_amount_when_auth: String(data.under_amount_when_auth), | |||
| under_amount_when_create: String(data.under_amount_when_create), | |||
| under_amount_when_use: String(data.under_amount_when_use), | |||
| }); | |||
| }; | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Card sx={{ p: 2 }} elevation={1}> | |||
| <Box> | |||
| <Stack spacing={2}> | |||
| <Typography variant="h6">基本設定</Typography> | |||
| <Grid container columnGap={2}> | |||
| <Grid item xs={12} md={6}> | |||
| <Typography>QRサービス券有効期限</Typography> | |||
| <StackRow> | |||
| <RHFTextField type="number" name="qr_service_expire_min" /> | |||
| <Typography>分</Typography> | |||
| </StackRow> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid container columnGap={2}> | |||
| <Grid item xs={12} md={6}> | |||
| <Typography>発行時デポジット下限値</Typography> | |||
| <StackRow> | |||
| <RHFTextField type="number" name="under_amount_when_create" /> | |||
| <Typography>円</Typography> | |||
| </StackRow> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid container columnGap={2}> | |||
| <Grid item xs={12} md={6}> | |||
| <Typography>認証時デポジット下限値</Typography> | |||
| <StackRow> | |||
| <RHFTextField type="number" name="under_amount_when_auth" /> | |||
| <Typography>円</Typography> | |||
| </StackRow> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid container columnGap={2}> | |||
| <Grid item xs={12} md={6}> | |||
| <Typography>利用時デポジット下限値</Typography> | |||
| <StackRow> | |||
| <RHFTextField type="number" name="under_amount_when_use" /> | |||
| <Typography>円</Typography> | |||
| </StackRow> | |||
| </Grid> | |||
| </Grid> | |||
| <StackRow> | |||
| <Button type="submit" variant="contained"> | |||
| 設定 | |||
| </Button> | |||
| </StackRow> | |||
| </Stack> | |||
| </Box> | |||
| </Card> | |||
| </FormProvider> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,120 @@ | |||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||
| import { Box, Button, Card, Grid, Stack, Typography } from "@mui/material"; | |||
| import { 店舗設定 } from "api/shop"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| import StackRow from "components/stack/StackRow"; | |||
| import { 店舗詳細Context } from "contexts/page/dashboard/shop/店舗詳細Context"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { useContext } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import { number, object } from "yup"; | |||
| type FormProps = { | |||
| qr_service_expire_min: number; | |||
| under_amount_when_create: number; | |||
| under_amount_when_auth: number; | |||
| under_amount_when_use: number; | |||
| }; | |||
| export default function 設定_() { | |||
| const { shop, fetch, moveToMain } = useContext(店舗詳細Context); | |||
| const { success, error } = useSnackbarCustom(); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| qr_service_expire_min: shop?.qr_service_expire_min ?? 0, | |||
| under_amount_when_create: shop?.under_amount_when_create ?? 0, | |||
| under_amount_when_auth: shop?.under_amount_when_auth ?? 0, | |||
| under_amount_when_use: shop?.under_amount_when_use ?? 0, | |||
| }, | |||
| resolver: yupResolver( | |||
| object().shape({ | |||
| qr_service_expire_min: number().typeError("数値を入力してください"), | |||
| under_amount_when_create: number().typeError("数値を入力してください"), | |||
| under_amount_when_auth: number().typeError("数値を入力してください"), | |||
| under_amount_when_use: number().typeError("数値を入力してください"), | |||
| }) | |||
| ), | |||
| }); | |||
| const { callAPI: call店舗設定 } = useAPICall({ | |||
| apiMethod: 店舗設定, | |||
| backDrop: true, | |||
| form, | |||
| onSuccess: () => { | |||
| success("設定しました"); | |||
| fetch(); | |||
| moveToMain(); | |||
| }, | |||
| onFailed: () => { | |||
| error("失敗しました"); | |||
| }, | |||
| }); | |||
| const handleSubmit = (data: FormProps) => { | |||
| if (shop === null) return; | |||
| call店舗設定({ | |||
| shop_id: shop.shop_id, | |||
| qr_service_expire_min: String(data.qr_service_expire_min), | |||
| under_amount_when_auth: String(data.under_amount_when_auth), | |||
| under_amount_when_create: String(data.under_amount_when_create), | |||
| under_amount_when_use: String(data.under_amount_when_use), | |||
| }); | |||
| }; | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Card sx={{ p: 2 }} elevation={1}> | |||
| <Box> | |||
| <Stack spacing={2}> | |||
| <Typography variant="h6">基本設定</Typography> | |||
| <Grid container columnGap={2}> | |||
| <Grid item xs={12} md={6}> | |||
| <Typography>QRサービス券有効期限</Typography> | |||
| <StackRow> | |||
| <RHFTextField type="number" name="qr_service_expire_min" /> | |||
| <Typography>分</Typography> | |||
| </StackRow> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid container columnGap={2}> | |||
| <Grid item xs={12} md={6}> | |||
| <Typography>発行時デポジット下限値</Typography> | |||
| <StackRow> | |||
| <RHFTextField type="number" name="under_amount_when_create" /> | |||
| <Typography>円</Typography> | |||
| </StackRow> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid container columnGap={2}> | |||
| <Grid item xs={12} md={6}> | |||
| <Typography>認証時デポジット下限値</Typography> | |||
| <StackRow> | |||
| <RHFTextField type="number" name="under_amount_when_auth" /> | |||
| <Typography>円</Typography> | |||
| </StackRow> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid container columnGap={2}> | |||
| <Grid item xs={12} md={6}> | |||
| <Typography>利用時デポジット下限値</Typography> | |||
| <StackRow> | |||
| <RHFTextField type="number" name="under_amount_when_use" /> | |||
| <Typography>円</Typography> | |||
| </StackRow> | |||
| </Grid> | |||
| </Grid> | |||
| <StackRow> | |||
| <Button type="submit" variant="contained"> | |||
| 設定 | |||
| </Button> | |||
| </StackRow> | |||
| </Stack> | |||
| </Box> | |||
| </Card> | |||
| </FormProvider> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| import { Box, Card, Stack, Typography } from "@mui/material"; | |||
| import { SimpleDataList } from "components/table"; | |||
| import { useContext, useMemo } from "react"; | |||
| import { numberFormat } from "utils/string"; | |||
| import { 店舗詳細Context } from "../../../../contexts/page/dashboard/shop/店舗詳細Context"; | |||
| export default function 詳細情報() { | |||
| const { shop } = useContext(店舗詳細Context); | |||
| const listData = useMemo(() => { | |||
| return [ | |||
| { | |||
| title: "名称", | |||
| value: shop?.name ?? "", | |||
| }, | |||
| { | |||
| title: "デポジット", | |||
| value: numberFormat(shop?.deposit ?? 0) + "円", | |||
| }, | |||
| ]; | |||
| }, [shop]); | |||
| return ( | |||
| <Card elevation={1} sx={{ p: 2 }}> | |||
| <Stack spacing={2}> | |||
| <Typography variant="h6">基本情報</Typography> | |||
| <Box> | |||
| <SimpleDataList data={listData} /> | |||
| </Box> | |||
| </Stack> | |||
| </Card> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| import useAuth from "hooks/useAuth"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { PageID } from "pages"; | |||
| import { useEffect } from "react"; | |||
| import { getPath } from "routes/path"; | |||
| export default function 成り代わり終了() { | |||
| const { switchEnd } = useAuth(); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const { info } = useSnackbarCustom(); | |||
| useEffect(() => { | |||
| switchEnd(); | |||
| navigateWhenChanged(getPath(PageID.LOGIN)); | |||
| info("成り代わり終了しました"); | |||
| }, [switchEnd, navigateWhenChanged]); | |||
| return null; | |||
| } | |||
| @@ -5,6 +5,7 @@ export const PageID = { | |||
| // 認証関連 ---------------------------------- | |||
| LOGIN: id++, | |||
| LOGOUT: id++, | |||
| 成り代わり終了: id++, | |||
| // ダッシュボード系 START ---------------------------------- | |||
| DASHBOARD_ENPTY: id++, | |||
| @@ -17,6 +18,12 @@ export const PageID = { | |||
| ログインユーザ_顧客一覧: id++, | |||
| ログインユーザ_顧客新規登録: id++, | |||
| ログインユーザ_店舗一覧: id++, | |||
| ログインユーザ_店舗新規登録: id++, | |||
| // 店舗管理 | |||
| 店舗一覧: id++, | |||
| 店舗新規登録: id++, | |||
| 店舗詳細: id++, | |||
| サービス券発行用QRコード: id++, | |||
| サービス券利用履歴: id++, | |||
| @@ -33,7 +40,9 @@ export type PageID = (typeof PageID)[keyof typeof PageID]; | |||
| id = 0; | |||
| export const TabID = { | |||
| NONE: id++, | |||
| A: id++, | |||
| 店舗詳細_メイン: id++, | |||
| 店舗詳細_設定: id++, | |||
| } as const; | |||
| export type TabID = (typeof TabID)[keyof typeof TabID]; | |||
| @@ -7,6 +7,7 @@ import { RouteObject, useRoutes } from "react-router-dom"; | |||
| import { getRoute } from "./path"; | |||
| import DashboardRoutes from "./sub/dashboard"; | |||
| import QRServiceSimpleLayout from "layouts/qr-service"; | |||
| import AuthGuard from "guards/AuthGuard"; | |||
| export const Loadable = (Component: ElementType) => (props: any) => { | |||
| return ( | |||
| @@ -15,6 +16,9 @@ export const Loadable = (Component: ElementType) => (props: any) => { | |||
| </Suspense> | |||
| ); | |||
| }; | |||
| export const Load = (path: string) => { | |||
| return Loadable(lazy(() => import(path))); | |||
| }; | |||
| const CommonRoutes = (): RouteObject => ({ | |||
| element: <SimpleLayout />, | |||
| @@ -58,8 +62,19 @@ export function Routes() { | |||
| element: <Page403 />, | |||
| }, | |||
| { | |||
| path: "*", | |||
| element: initialized ? <Page404 /> : <LoadingScreen />, | |||
| element: <SimpleLayout />, | |||
| children: [ | |||
| { | |||
| path: "*", | |||
| element: initialized ? ( | |||
| <AuthGuard> | |||
| <Page404 /> | |||
| </AuthGuard> | |||
| ) : ( | |||
| <LoadingScreen /> | |||
| ), | |||
| }, | |||
| ], | |||
| }, | |||
| ]); | |||
| } | |||
| @@ -67,6 +82,7 @@ export function Routes() { | |||
| // 認証関連 ------------------------------- | |||
| const Login = Loadable(lazy(() => import("pages/auth/login"))); | |||
| const Logout = Loadable(lazy(() => import("pages/auth/logout"))); | |||
| // QRサービス券関連 | |||
| const QRサービス券発行申請 = Loadable( | |||
| lazy(() => import("pages/qr-service/QRサービス券発行申請")) | |||
| @@ -77,7 +93,4 @@ const QRサービス券発行申請 = Loadable( | |||
| // const Page403 = Loadable(lazy(() => import("pages/common/Page403"))); | |||
| const Page404 = Loadable(lazy(() => import("pages/common/Page404"))); | |||
| const PP = (path: string) => { | |||
| return Loadable(lazy(() => import(path))); | |||
| }; | |||
| const Page403 = PP("pages/common/Page403"); | |||
| const Page403 = Load("pages/common/Page403"); | |||
| @@ -2,7 +2,7 @@ import { Dictionary } from "@types"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { get, isArray, isString, replace } from "lodash"; | |||
| type PathKey = [PageID, TabID?] | PageID; | |||
| export type PathKey = [PageID, TabID?] | PageID; | |||
| const makePathKey = (arg: PathKey): string => { | |||
| if (isArray(arg)) { | |||
| const tabStr = arg[1] !== undefined ? "/" + String(arg[1]) : ""; | |||
| @@ -37,6 +37,16 @@ const PATHS_DASHBOARD = { | |||
| "/dashboard/login-user/customer/register", | |||
| [makePathKey(PageID.ログインユーザ_店舗一覧)]: | |||
| "/dashboard/login-user/shop/list", | |||
| [makePathKey(PageID.ログインユーザ_店舗新規登録)]: | |||
| "/dashboard/login-user/shop/register", | |||
| [makePathKey(PageID.店舗一覧)]: "/dashboard/shop/list", | |||
| [makePathKey(PageID.店舗新規登録)]: "/dashboard/shop/register", | |||
| [makePathKey([PageID.店舗詳細])]: "/dashboard/shop/detail/:shopId", | |||
| [makePathKey([PageID.店舗詳細, TabID.店舗詳細_メイン])]: | |||
| "/dashboard/shop/detail/:shopId", | |||
| [makePathKey([PageID.店舗詳細, TabID.店舗詳細_設定])]: | |||
| "/dashboard/shop/detail/setting/:shopId", | |||
| [makePathKey(PageID.サービス券発行用QRコード)]: "/dashboard/qrcode/generate", | |||
| [makePathKey(PageID.サービス券利用履歴)]: "/dashboard/qrcode/history", | |||
| @@ -44,10 +54,12 @@ const PATHS_DASHBOARD = { | |||
| const PATHS = { | |||
| [makePathKey(PageID.NONE)]: "/", | |||
| [makePathKey([PageID.NONE, TabID.NONE])]: "/", | |||
| // 認証 | |||
| [makePathKey(PageID.LOGIN)]: "/login", | |||
| [makePathKey(PageID.LOGOUT)]: "/logout", | |||
| [makePathKey(PageID.成り代わり終了)]: "/role/switch/end", | |||
| [makePathKey(PageID.QRサービス券発行申請)]: "qr-service/acquitision/:token", | |||
| @@ -67,7 +79,7 @@ export function getPath(key: PathKey, option?: PathOption) { | |||
| const pageId = getPageId(key); | |||
| const tabId = getTabId(key); | |||
| let path = getRoute(pageId); | |||
| let path = getRoute(key); | |||
| // ページ番号解決 | |||
| path = replacePathParam(path, "page", option?.page ?? 0); | |||
| @@ -90,8 +102,17 @@ export function getListPagePath(key: PathKey, page: number): string { | |||
| } | |||
| export function getRoute(key: PathKey, exclude?: string): string { | |||
| let path = get(PATHS, makePathKey(key)); | |||
| if (!path) throw new Error("ルート未定義:" + makePathKey(key)); | |||
| let path = ""; | |||
| if (getTabId(key) === TabID.NONE) { | |||
| path = get(PATHS, makePathKey(getPageId(key))); | |||
| } else { | |||
| path = get(PATHS, makePathKey(key)); | |||
| } | |||
| if (!path) { | |||
| // throw new Error("ルート未定義:" + makePathKey(key)); | |||
| // console.error("ルート未定義:" + makePathKey(key)); | |||
| return ""; | |||
| } | |||
| if (exclude) { | |||
| path = replace(path, "/" + exclude + "/", ""); | |||
| @@ -0,0 +1,124 @@ | |||
| import { ページアクセス許可判定 } from "auth/route"; | |||
| import AuthGuard from "guards/AuthGuard"; | |||
| import useAuth from "hooks/useAuth"; | |||
| import DashboardLayout from "layouts/dashbord"; | |||
| import { PageID, TabID } from "pages"; | |||
| import React, { lazy, useMemo } from "react"; | |||
| import { RouteObject } from "react-router-dom"; | |||
| import { Loadable } from "routes"; | |||
| import { getRoute } from "routes/path"; | |||
| 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 成り代わり終了 = Loadable( | |||
| lazy(() => import("pages/dashboard/成り代わり終了")) | |||
| ); | |||
| const サービス券発行用QRコード = Loadable( | |||
| lazy(() => import("pages/dashboard/qrcode/サービス券発行用QRコード")) | |||
| ); | |||
| const サービス券利用履歴 = Loadable( | |||
| lazy(() => import("pages/dashboard/qrcode/サービス券利用履歴")) | |||
| ); | |||
| const 顧客ログインユーザ一覧 = Loadable( | |||
| lazy(() => import("pages/dashboard/login-user/顧客ログインユーザ一覧")) | |||
| ); | |||
| const 顧客ログインユーザ新規登録 = Loadable( | |||
| lazy( | |||
| () => import("pages/dashboard/login-user/顧客ログインユーザ新規登録") | |||
| ) | |||
| ); | |||
| const 店舗新規登録 = Loadable( | |||
| lazy(() => import("pages/dashboard/shop/店舗新規登録")) | |||
| ); | |||
| const 店舗一覧 = Loadable( | |||
| lazy(() => import("pages/dashboard/shop/店舗一覧")) | |||
| ); | |||
| const 店舗詳細 = Loadable( | |||
| lazy(() => import("pages/dashboard/shop/店舗詳細")) | |||
| ); | |||
| const allChildren: { | |||
| pageId: PageID; | |||
| tabId?: TabID; | |||
| element: JSX.Element; | |||
| }[] = [ | |||
| { | |||
| pageId: PageID.DASHBOARD_ENPTY, | |||
| element: <Enpty />, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_OVERVIEW, | |||
| element: <Dashboard />, | |||
| }, | |||
| { | |||
| pageId: PageID.成り代わり終了, | |||
| element: <成り代わり終了 />, | |||
| }, | |||
| { | |||
| pageId: PageID.ログインユーザ_顧客一覧, | |||
| element: <顧客ログインユーザ一覧 />, | |||
| }, | |||
| { | |||
| pageId: PageID.ログインユーザ_顧客新規登録, | |||
| element: <顧客ログインユーザ新規登録 />, | |||
| }, | |||
| // { | |||
| // pageId: PageID.ログインユーザ_店舗一覧, | |||
| // element: <顧客ログインユーザ一覧 />, | |||
| // }, | |||
| // { | |||
| // pageId: PageID.ログインユーザ_店舗新規登録, | |||
| // element: <顧客ログインユーザ新規登録 />, | |||
| // }, | |||
| { | |||
| pageId: PageID.店舗新規登録, | |||
| element: <店舗新規登録 />, | |||
| }, | |||
| { | |||
| pageId: PageID.店舗一覧, | |||
| element: <店舗一覧 />, | |||
| }, | |||
| { | |||
| pageId: PageID.店舗詳細, | |||
| tabId: TabID.店舗詳細_メイン, | |||
| element: <店舗詳細 />, | |||
| }, | |||
| { | |||
| pageId: PageID.サービス券発行用QRコード, | |||
| element: <サービス券発行用QRコード />, | |||
| }, | |||
| { | |||
| pageId: PageID.サービス券利用履歴, | |||
| element: <サービス券利用履歴 />, | |||
| }, | |||
| ]; | |||
| return allChildren | |||
| .filter(({ pageId }) => { | |||
| if (currentRole === null) { | |||
| return false; | |||
| } | |||
| return ページアクセス許可判定(currentRole, pageId); | |||
| }) | |||
| .map(({ pageId, tabId, ...others }) => ({ | |||
| ...others, | |||
| path: getRoute([pageId, tabId]), | |||
| })); | |||
| }, [currentRole]); | |||
| return [ | |||
| { | |||
| element: ( | |||
| <AuthGuard> | |||
| <DashboardLayout /> | |||
| </AuthGuard> | |||
| ), | |||
| children: children, | |||
| }, | |||
| ]; | |||
| } | |||
| @@ -1,12 +1,13 @@ | |||
| import { ページアクセス許可判定 } from "auth/route"; | |||
| import 店舗詳細ContextProvider from "contexts/page/dashboard/shop/店舗詳細Context"; | |||
| import AuthGuard from "guards/AuthGuard"; | |||
| import useAuth from "hooks/useAuth"; | |||
| import DashboardLayout from "layouts/dashbord"; | |||
| import { PageID } from "pages"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { lazy, useMemo } from "react"; | |||
| import { RouteObject } from "react-router-dom"; | |||
| import { Outlet, RouteObject } from "react-router-dom"; | |||
| import { Loadable } from "routes"; | |||
| import { getRoute } from "routes/path"; | |||
| import { getPath } from "routes/path"; | |||
| export default function DashboardRoutes(): RouteObject[] { | |||
| const { currentRole } = useAuth(); | |||
| @@ -14,6 +15,9 @@ export default function DashboardRoutes(): RouteObject[] { | |||
| const children: RouteObject[] = useMemo(() => { | |||
| const Enpty = Loadable(lazy(() => import("pages/dashboard/empty"))); | |||
| const Dashboard = Loadable(lazy(() => import("pages/dashboard"))); | |||
| const 成り代わり終了 = Loadable( | |||
| lazy(() => import("pages/dashboard/成り代わり終了")) | |||
| ); | |||
| const サービス券発行用QRコード = Loadable( | |||
| lazy(() => import("pages/dashboard/qrcode/サービス券発行用QRコード")) | |||
| @@ -29,31 +33,99 @@ export default function DashboardRoutes(): RouteObject[] { | |||
| () => import("pages/dashboard/login-user/顧客ログインユーザ新規登録") | |||
| ) | |||
| ); | |||
| const 店舗新規登録 = Loadable( | |||
| lazy(() => import("pages/dashboard/shop/店舗新規登録")) | |||
| ); | |||
| const 店舗一覧 = Loadable( | |||
| lazy(() => import("pages/dashboard/shop/店舗一覧")) | |||
| ); | |||
| const 店舗詳細 = Loadable( | |||
| lazy(() => import("pages/dashboard/shop/店舗詳細")) | |||
| ); | |||
| const 店舗詳細設定 = Loadable( | |||
| lazy(() => import("pages/dashboard/shop/店舗詳細/設定")) | |||
| ); | |||
| const allChildren = [ | |||
| const allChildren: { | |||
| pageId: PageID; | |||
| ele: RouteObject; | |||
| }[] = [ | |||
| { | |||
| pageId: PageID.DASHBOARD_ENPTY, | |||
| element: <Enpty />, | |||
| ele: { | |||
| element: <Enpty />, | |||
| path: getPath(PageID.DASHBOARD_ENPTY), | |||
| }, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_OVERVIEW, | |||
| element: <Dashboard />, | |||
| ele: { | |||
| element: <Dashboard />, | |||
| path: getPath(PageID.DASHBOARD_OVERVIEW), | |||
| }, | |||
| }, | |||
| { | |||
| pageId: PageID.成り代わり終了, | |||
| ele: { | |||
| element: <成り代わり終了 />, | |||
| path: getPath(PageID.成り代わり終了), | |||
| }, | |||
| }, | |||
| { | |||
| pageId: PageID.ログインユーザ_顧客一覧, | |||
| element: <顧客ログインユーザ一覧 />, | |||
| ele: { | |||
| element: <顧客ログインユーザ一覧 />, | |||
| path: getPath(PageID.ログインユーザ_顧客一覧), | |||
| }, | |||
| }, | |||
| { | |||
| pageId: PageID.ログインユーザ_顧客新規登録, | |||
| element: <顧客ログインユーザ新規登録 />, | |||
| ele: { | |||
| element: <顧客ログインユーザ新規登録 />, | |||
| path: getPath(PageID.ログインユーザ_顧客新規登録), | |||
| }, | |||
| }, | |||
| // { | |||
| // pageId: PageID.ログインユーザ_店舗一覧, | |||
| // element: <顧客ログインユーザ一覧 />, | |||
| // }, | |||
| // { | |||
| // pageId: PageID.ログインユーザ_店舗新規登録, | |||
| // element: <顧客ログインユーザ新規登録 />, | |||
| // }, | |||
| { | |||
| pageId: PageID.店舗新規登録, | |||
| ele: { | |||
| element: <店舗新規登録 />, | |||
| path: getPath(PageID.店舗新規登録), | |||
| }, | |||
| }, | |||
| { | |||
| pageId: PageID.サービス券発行用QRコード, | |||
| element: <サービス券発行用QRコード />, | |||
| pageId: PageID.店舗一覧, | |||
| ele: { | |||
| element: <店舗一覧 />, | |||
| path: getPath(PageID.店舗一覧), | |||
| }, | |||
| }, | |||
| { | |||
| pageId: PageID.サービス券利用履歴, | |||
| element: <サービス券利用履歴 />, | |||
| pageId: PageID.店舗詳細, | |||
| ele: { | |||
| element: ( | |||
| <店舗詳細ContextProvider> | |||
| <Outlet /> | |||
| </店舗詳細ContextProvider> | |||
| ), | |||
| children: [ | |||
| { | |||
| element: <店舗詳細 />, | |||
| path: getPath([PageID.店舗詳細, TabID.店舗詳細_メイン]), | |||
| }, | |||
| { | |||
| element: <店舗詳細設定 />, | |||
| path: getPath([PageID.店舗詳細, TabID.店舗詳細_設定]), | |||
| }, | |||
| ], | |||
| }, | |||
| }, | |||
| ]; | |||
| @@ -64,10 +136,7 @@ export default function DashboardRoutes(): RouteObject[] { | |||
| } | |||
| return ページアクセス許可判定(currentRole, pageId); | |||
| }) | |||
| .map(({ pageId, ...others }) => ({ | |||
| ...others, | |||
| path: getRoute(pageId), | |||
| })); | |||
| .map(({ ele }) => ele); | |||
| }, [currentRole]); | |||
| return [ | |||