Sfoglia il codice sorgente

成り代わり機能 追加

develop
sosuke.iwabuchi 2 anni fa
parent
commit
c5601247ba
11 ha cambiato i file con 155 aggiunte e 32 eliminazioni
  1. +12
    -2
      src/api/auth.ts
  2. +2
    -0
      src/api/index.ts
  3. +1
    -0
      src/api/url.ts
  4. +1
    -0
      src/codes/page.ts
  5. +41
    -9
      src/contexts/AuthContext.tsx
  6. +26
    -6
      src/layouts/dashbord/navigator.tsx
  7. +26
    -0
      src/pages/auth/clear-contract.tsx
  8. +12
    -3
      src/pages/dashboard/contract/list.tsx
  9. +28
    -12
      src/routes/auth.ts
  10. +5
    -0
      src/routes/index.tsx
  11. +1
    -0
      src/routes/path.ts

+ 12
- 2
src/api/auth.ts Vedi File

@@ -1,12 +1,12 @@
import { UserRole } from "codes/user"; import { UserRole } from "codes/user";
import { APICommonResponse, ApiId, HttpMethod, request } from ".";
import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from ".";
import { getUrl } from "./url"; import { getUrl } from "./url";
import { CustomCode } from "codes/custom"; import { CustomCode } from "codes/custom";


type MeResponse = { type MeResponse = {
data: { data: {
id: string; id: string;
contract_id: string;
contract_id: string | null;
role: UserRole; role: UserRole;
name: string; name: string;
custom?: CustomCode[]; custom?: CustomCode[];
@@ -45,3 +45,13 @@ export const logout = async () => {
}); });
return res; return res;
}; };

// 成り代わり
export const changeContract = async (param: { contract_id: string | null }) => {
const res = await request<MeResponse>({
url: getUrl(ApiId.CHANGE_CONTRACT),
method: HttpMethod.POST,
data: makeParam(param),
});
return res;
};

+ 2
- 0
src/api/index.ts Vedi File

@@ -21,6 +21,8 @@ export const ApiId = {
RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++,


// DASHBOARD向け------------------------------- // DASHBOARD向け-------------------------------
CHANGE_CONTRACT: id++,

RECEIPT_ISSUING_ORDERS: id++, RECEIPT_ISSUING_ORDERS: id++,
RECEIPT_ISSUING_ORDER: id++, RECEIPT_ISSUING_ORDER: id++,
RECEIPT_ISSUING_ORDER_CREATE: id++, RECEIPT_ISSUING_ORDER_CREATE: id++,


+ 1
- 0
src/api/url.ts Vedi File

@@ -7,6 +7,7 @@ const urls = {
[A.ME]: "me", [A.ME]: "me",
[A.LOGIN]: "login", [A.LOGIN]: "login",
[A.LOGOUT]: "logout", [A.LOGOUT]: "logout",
[A.CHANGE_CONTRACT]: "change-contract",


[A.APP_TOKEN_CHECK]: "app-token-check", [A.APP_TOKEN_CHECK]: "app-token-check",
[A.RECEIPT_ISSUING_ORDER_CONFIRM]: "receipt-issuing-order/confirm", [A.RECEIPT_ISSUING_ORDER_CONFIRM]: "receipt-issuing-order/confirm",


+ 1
- 0
src/codes/page.ts Vedi File

@@ -4,6 +4,7 @@ export const PageID = {


LOGIN: id++, LOGIN: id++,
LOGOUT: id++, LOGOUT: id++,
CLEAR_CHANGE_CONTRACT: id++,


APP_RECEIPT_ISSUING_ORDER_INDEX: id++, APP_RECEIPT_ISSUING_ORDER_INDEX: id++,
APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++,


+ 41
- 9
src/contexts/AuthContext.tsx Vedi File

@@ -1,6 +1,11 @@
import { HasChildren } from "@types"; import { HasChildren } from "@types";
import { ResultCode } from "api"; import { ResultCode } from "api";
import { login as APILogin, logout as APILogout, me } from "api/auth";
import {
login as APILogin,
logout as APILogout,
changeContract as APIChangeContract,
me,
} from "api/auth";
import { CustomCode } from "codes/custom"; import { CustomCode } from "codes/custom";
import { PageID } from "codes/page"; import { PageID } from "codes/page";
import { UserRole } from "codes/user"; import { UserRole } from "codes/user";
@@ -27,10 +32,12 @@ type Auth = {
custom: CustomCode[]; custom: CustomCode[];
customerName: string; customerName: string;


isChangedContractId: boolean;

login: (email: string, password: string) => Promise<boolean>; login: (email: string, password: string) => Promise<boolean>;
logout: VoidFunction; logout: VoidFunction;


changeContractId: (contractId: string) => Promise<boolean>;
changeContractId: (contractId: string | null) => Promise<boolean>;


checkRole: (role?: UserRole) => boolean; checkRole: (role?: UserRole) => boolean;
canAccess: (pageId: PageID) => boolean; canAccess: (pageId: PageID) => boolean;
@@ -47,9 +54,11 @@ export const AuthContext = createContext<Auth>({
custom: [], custom: [],
customerName: "", customerName: "",


isChangedContractId: false,

login: async (email: string, password: string) => false, login: async (email: string, password: string) => false,
logout: () => {}, logout: () => {},
changeContractId: async (contractId: string) => false,
changeContractId: async (contractId: string | null) => false,
checkRole: (role?: UserRole) => false, checkRole: (role?: UserRole) => false,
canAccess: (pageId: PageID) => false, canAccess: (pageId: PageID) => false,
}); });
@@ -64,18 +73,24 @@ function AuthContextProvider({ children }: Props) {
const [custom, setCustom] = useState<CustomCode[]>([]); const [custom, setCustom] = useState<CustomCode[]>([]);
const [customerName, setCustomerName] = useState(""); const [customerName, setCustomerName] = useState("");


const isChangedContractId = useMemo(() => {
return role === UserRole.SUPER_ADMIN && contractId !== null;
}, [contractId]);

const authenticated = useMemo(() => { const authenticated = useMemo(() => {
return role !== UserRole.NONE; return role !== UserRole.NONE;
}, [role]); }, [role]);


const { callAPI: callMe } = useAPICall({ const { callAPI: callMe } = useAPICall({
apiMethod: me, apiMethod: me,
backDrop: true,
onSuccess: (res) => { onSuccess: (res) => {
setContractId(res.data.contract_id); setContractId(res.data.contract_id);
setUserId(res.data.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 ?? []);
setCustomerName(res.data.contract_name ?? "");
setInitialized(true); setInitialized(true);
}, },
onFailed: () => { onFailed: () => {
@@ -86,12 +101,14 @@ function AuthContextProvider({ children }: Props) {


const { callAPI: callLogin } = useAPICall({ const { callAPI: callLogin } = useAPICall({
apiMethod: APILogin, apiMethod: APILogin,
backDrop: true,
onSuccess: (res) => { onSuccess: (res) => {
setContractId(res.data.contract_id); setContractId(res.data.contract_id);
setUserId(res.data.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 ?? []);
setCustomerName(res.data.contract_name ?? "");
setInitialized(true); setInitialized(true);
}, },
}); });
@@ -102,6 +119,16 @@ function AuthContextProvider({ children }: Props) {
clear(); clear();
}, },
}); });
const { callAPI: callChangeContract } = useAPICall({
apiMethod: APIChangeContract,
backDrop: true,
onSuccess: (res) => {
setContractId(res.data.contract_id);
setCustom(res.data.custom ?? []);
setCustomerName(res.data.contract_name ?? "");
setInitialized(true);
},
});


const clear = () => { const clear = () => {
setRole(UserRole.NONE); setRole(UserRole.NONE);
@@ -121,9 +148,9 @@ function AuthContextProvider({ children }: Props) {
callLogout({}); callLogout({});
console.info("ログアウト"); console.info("ログアウト");
}; };
const changeContractId = async (contractId: string) => {
console.error("未実装 成り代わり");
return false;
const changeContractId = async (contractId: string | null) => {
const res = await callChangeContract({ contract_id: contractId });
return res?.result === ResultCode.SUCCESS;
}; };


const checkRole = useCallback( const checkRole = useCallback(
@@ -162,14 +189,19 @@ function AuthContextProvider({ children }: Props) {


return ( return (
!!authorization && !!authorization &&
// 権限条件
authorization.role.includes(role) && authorization.role.includes(role) &&
// カスタム条件
(authorization.custom.length === 0 || (authorization.custom.length === 0 ||
!!custom.find((c) => { !!custom.find((c) => {
return authorization.custom.includes(c); return authorization.custom.includes(c);
}))
})) &&
// 成り代わり条件
(authorization.allowChangedContract === undefined ||
isChangedContractId === authorization.allowChangedContract)
); );
}, },
[initialized, role, custom]
[initialized, role, custom, isChangedContractId]
); );


useEffect(() => { useEffect(() => {
@@ -187,8 +219,8 @@ function AuthContextProvider({ children }: Props) {
userId, userId,
name, name,
custom, custom,

customerName, customerName,
isChangedContractId,


// Func // Func
login, login,


+ 26
- 6
src/layouts/dashbord/navigator.tsx Vedi File

@@ -4,8 +4,9 @@ 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 AccountBoxIcon from "@mui/icons-material/AccountBox";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
import AccountBalanceIcon from "@mui/icons-material/AccountBalance"; import AccountBalanceIcon from "@mui/icons-material/AccountBalance";
import { Collapse } from "@mui/material";
import { Collapse, Typography } 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";
import Drawer, { DrawerProps } from "@mui/material/Drawer"; import Drawer, { DrawerProps } from "@mui/material/Drawer";
@@ -51,6 +52,12 @@ const item = {
}, },
}; };


const viewItem = {
py: "2px",
px: 3,
color: "rgba(255, 255, 255, 0.7)",
};

const itemCategory = { const itemCategory = {
boxShadow: "0 -1px 0 rgb(255,255,255,0.1) inset", boxShadow: "0 -1px 0 rgb(255,255,255,0.1) inset",
py: 1.5, py: 1.5,
@@ -60,7 +67,7 @@ const itemCategory = {
export default function Navigator(props: DrawerProps) { export default function Navigator(props: DrawerProps) {
const { ...other } = props; const { ...other } = props;


const { userId } = useAuth();
const { userId, customerName, name, isChangedContractId } = useAuth();


const categories: Group[] = [ const categories: Group[] = [
{ {
@@ -138,6 +145,11 @@ export default function Navigator(props: DrawerProps) {
}, },
], ],
}, },
{
label: "成り代わり解除",
icon: <SettingsIcon />,
id: PageID.CLEAR_CHANGE_CONTRACT,
},
{ label: "ログアウト", icon: <SettingsIcon />, id: PageID.LOGOUT }, { label: "ログアウト", icon: <SettingsIcon />, id: PageID.LOGOUT },
], ],
}, },
@@ -151,11 +163,18 @@ export default function Navigator(props: DrawerProps) {
> >
EasyReceipt EasyReceipt
</ListItem> </ListItem>
<ListItem sx={{ ...item, ...itemCategory }}>
<ListItem sx={{ ...viewItem, ...itemCategory }}>
<ListItemIcon> <ListItemIcon>
<HomeIcon />
<AccountCircleIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText>Project Overview</ListItemText>
<ListItemText>{}</ListItemText>
<ListItemText>
<Typography>{customerName}</Typography>
<Typography>{name}</Typography>
{isChangedContractId && (
<Typography color="error">!!成り代わり中!!</Typography>
)}
</ListItemText>
</ListItem> </ListItem>


{categories.map((group, index) => { {categories.map((group, index) => {
@@ -188,6 +207,7 @@ function Group(group: Group) {


function SubGroup({ icon, label, id, children, option }: SubGroup) { function SubGroup({ icon, label, id, children, option }: SubGroup) {
const { pageId } = usePage(); const { pageId } = usePage();
const { canAccess } = useAuth();
const { navigateWhenChanged } = useNavigateCustom(); const { navigateWhenChanged } = useNavigateCustom();


const { elements, shouldOpen } = useContents(children ?? []); const { elements, shouldOpen } = useContents(children ?? []);
@@ -219,7 +239,7 @@ function SubGroup({ icon, label, id, children, option }: SubGroup) {
); );
} }
// 子要素なしの場合 // 子要素なしの場合
if (id !== undefined) {
if (id !== undefined && canAccess(id)) {
const handleClick = () => { const handleClick = () => {
if (id) { if (id) {
const path = getPath(id, option); const path = getPath(id, option);


+ 26
- 0
src/pages/auth/clear-contract.tsx Vedi File

@@ -0,0 +1,26 @@
import { PageID } from "codes/page";
import useAuth from "hooks/useAuth";
import useNavigateCustom from "hooks/useNavigateCustom";
import useSnackbarCustom from "hooks/useSnackbarCustom";
import { useEffect } from "react";
import { getPath } from "routes/path";

export default function ClearChangeContract() {
const { changeContractId } = useAuth();
const { info, error } = useSnackbarCustom();

const { navigateWhenChanged } = useNavigateCustom();

useEffect(() => {
changeContractId(null).then((ret) => {
if (ret) {
info("成り代わり解除しました");
} else {
error("失敗しました");
}
navigateWhenChanged(getPath(PageID.DASHBOARD_OVERVIEW));
});
}, []);

return null;
}

+ 12
- 3
src/pages/dashboard/contract/list.tsx Vedi File

@@ -11,16 +11,19 @@ import {
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import { Dictionary } from "@types"; import { Dictionary } from "@types";
import { changeContract } from "api/auth";
import { Contract, getContracts } from "api/contract"; import { Contract, getContracts } from "api/contract";
import { PageID, TabID } from "codes/page"; import { PageID, TabID } from "codes/page";
import { FormProvider, RHFTextField } from "components/hook-form"; import { FormProvider, RHFTextField } from "components/hook-form";
import { TableHeadCustom } from "components/table"; import { TableHeadCustom } from "components/table";
import { SearchConditionContextProvider } from "contexts/SearchConditionContext"; import { SearchConditionContextProvider } from "contexts/SearchConditionContext";
import useAPICall from "hooks/useAPICall"; import useAPICall from "hooks/useAPICall";
import useAuth from "hooks/useAuth";
import useBackDrop from "hooks/useBackDrop"; import useBackDrop from "hooks/useBackDrop";
import useDashboard from "hooks/useDashBoard"; import useDashboard from "hooks/useDashBoard";
import useNavigateCustom from "hooks/useNavigateCustom"; import useNavigateCustom from "hooks/useNavigateCustom";
import useSearchConditionContext from "hooks/useSearchConditionContext"; import useSearchConditionContext from "hooks/useSearchConditionContext";
import useSnackbarCustom from "hooks/useSnackbarCustom";
import useTable, { UseTableReturn } from "hooks/useTable"; import useTable, { UseTableReturn } from "hooks/useTable";
import { useEffect } from "react"; import { useEffect } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
@@ -221,10 +224,16 @@ type RowProps = {
}; };
function Row({ data }: RowProps) { function Row({ data }: RowProps) {
const { navigateWhenChanged } = useNavigateCustom(); const { navigateWhenChanged } = useNavigateCustom();

const { changeContractId } = useAuth();
const { info } = useSnackbarCustom();
const handleClick = () => { const handleClick = () => {
navigateWhenChanged(getPath(PageID.DASHBOARD_CONTRACT_DETAIL), {
id: data.id,
// navigateWhenChanged(getPath(PageID.DASHBOARD_CONTRACT_DETAIL), {
// id: data.id,
// });

changeContractId(data.id).then((res) => {
info("成り代わりました");
navigateWhenChanged(getPath(PageID.DASHBOARD_OVERVIEW));
}); });
}; };




+ 28
- 12
src/routes/auth.ts Vedi File

@@ -5,45 +5,56 @@ import { UserRole as R } from "codes/user";
type AuthConfiguration = { type AuthConfiguration = {
role: R[]; role: R[];
custom: C[]; custom: C[];

allowChangedContract?: boolean;
}; };
export const AUTH = { export const AUTH = {
[P.NONE]: setAuth("all"), [P.NONE]: setAuth("all"),
[P.LOGIN]: setAuth("all"), [P.LOGIN]: setAuth("all"),
[P.LOGOUT]: setAuth("all"), [P.LOGOUT]: setAuth("all"),
[P.CLEAR_CHANGE_CONTRACT]: setAuth("eq", R.SUPER_ADMIN, {
allowChangedContract: true,
}),


[P.DASHBOARD_OVERVIEW]: setAuth("ge", R.NORMAL_ADMIN), [P.DASHBOARD_OVERVIEW]: setAuth("ge", R.NORMAL_ADMIN),


[P.DASHBOARD_CONTRACT_LIST]: setAuth("eq", R.SUPER_ADMIN),
[P.DASHBOARD_CONTRACT_DETAIL]: setAuth("eq", R.SUPER_ADMIN),
[P.DASHBOARD_CONTRACT_CREATE]: setAuth("eq", R.SUPER_ADMIN),
[P.DASHBOARD_CONTRACT_LIST]: setAuth("eq", R.SUPER_ADMIN, {
allowChangedContract: false,
}),
[P.DASHBOARD_CONTRACT_DETAIL]: setAuth("eq", R.SUPER_ADMIN, {
allowChangedContract: false,
}),
[P.DASHBOARD_CONTRACT_CREATE]: setAuth("eq", R.SUPER_ADMIN, {
allowChangedContract: false,
}),


[P.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO]: setAuth( [P.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO]: setAuth(
"ge", "ge",
R.NORMAL_ADMIN, R.NORMAL_ADMIN,
[C.HELLO_TECHNO]
{ custom: [C.HELLO_TECHNO] }
), ),
[P.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO]: setAuth( [P.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO]: setAuth(
"ge", "ge",
R.NORMAL_ADMIN, R.NORMAL_ADMIN,
[C.HELLO_TECHNO]
{ custom: [C.HELLO_TECHNO] }
), ),
[P.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL_CUSTOM_HELLO_TECHNO]: setAuth( [P.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL_CUSTOM_HELLO_TECHNO]: setAuth(
"ge", "ge",
R.NORMAL_ADMIN, R.NORMAL_ADMIN,
[C.HELLO_TECHNO]
{ custom: [C.HELLO_TECHNO] }
), ),
[P.DASHBOARD_USE_SUMMARY_LIST_CUSTOM_HELLO_TECHNO]: setAuth( [P.DASHBOARD_USE_SUMMARY_LIST_CUSTOM_HELLO_TECHNO]: setAuth(
"ge", "ge",
R.NORMAL_ADMIN, R.NORMAL_ADMIN,
[C.HELLO_TECHNO]
{ custom: [C.HELLO_TECHNO] }
), ),
[P.DASHBOARD_USE_SUMMARY_DETAIL_CUSTOM_HELLO_TECHNO]: setAuth( [P.DASHBOARD_USE_SUMMARY_DETAIL_CUSTOM_HELLO_TECHNO]: setAuth(
"ge", "ge",
R.NORMAL_ADMIN, R.NORMAL_ADMIN,
[C.HELLO_TECHNO]
{ custom: [C.HELLO_TECHNO] }
), ),
[P.DASHBOARD_LOGIN_USER_LIST]: setAuth("ge", R.CONTRACT_ADMIN),
[P.DASHBOARD_LOGIN_USER_CREATE]: setAuth("ge", R.CONTRACT_ADMIN),
[P.DASHBOARD_LOGIN_USER_LIST]: setAuth("eq", R.CONTRACT_ADMIN),
[P.DASHBOARD_LOGIN_USER_CREATE]: setAuth("eq", R.CONTRACT_ADMIN),
[P.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD]: setAuth("ge", R.NORMAL_ADMIN), [P.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD]: setAuth("ge", R.NORMAL_ADMIN),


[P.PAGE_403]: setAuth("all"), [P.PAGE_403]: setAuth("all"),
@@ -52,10 +63,14 @@ export const AUTH = {


type Target = "ge" | "le" | "eq" | "all"; type Target = "ge" | "le" | "eq" | "all";
type UserRoleKey = keyof typeof R; type UserRoleKey = keyof typeof R;
type OptionProps = {
custom?: C[];
allowChangedContract?: boolean;
};
function setAuth( function setAuth(
target: Target, target: Target,
targetRole?: R, targetRole?: R,
custom: C[] = []
option?: OptionProps
): AuthConfiguration { ): AuthConfiguration {
const roles: R[] = []; const roles: R[] = [];


@@ -87,6 +102,7 @@ function setAuth(


return { return {
role: roles, role: roles,
custom: custom,
custom: option?.custom ?? [],
allowChangedContract: option?.allowChangedContract,
}; };
} }

+ 5
- 0
src/routes/index.tsx Vedi File

@@ -28,6 +28,10 @@ const AuthRoutes = (): RouteObject => ({
path: getRoute(PageID.LOGOUT), path: getRoute(PageID.LOGOUT),
element: <Logout />, element: <Logout />,
}, },
{
path: getRoute(PageID.CLEAR_CHANGE_CONTRACT),
element: <ClearContract />,
},
], ],
}); });


@@ -138,6 +142,7 @@ export function Routes() {
// 認証関連 ------------------------------- // 認証関連 -------------------------------
const Login = Loadable(lazy(() => import("pages/auth/login"))); const Login = Loadable(lazy(() => import("pages/auth/login")));
const Logout = Loadable(lazy(() => import("pages/auth/logout"))); const Logout = Loadable(lazy(() => import("pages/auth/logout")));
const ClearContract = Loadable(lazy(() => import("pages/auth/clear-contract")));


// App --------------------------- // App ---------------------------
const ReceiptIssuingOrder = Loadable( const ReceiptIssuingOrder = Loadable(


+ 1
- 0
src/routes/path.ts Vedi File

@@ -34,6 +34,7 @@ const PATHS = {
// 認証 // 認証
[makePathKey(PageID.LOGIN)]: "/login", [makePathKey(PageID.LOGIN)]: "/login",
[makePathKey(PageID.LOGOUT)]: "/logout", [makePathKey(PageID.LOGOUT)]: "/logout",
[makePathKey(PageID.CLEAR_CHANGE_CONTRACT)]: "/clear-change-contract",


[makePathKey(PageID.DASHBOARD_OVERVIEW)]: "/dashboard", [makePathKey(PageID.DASHBOARD_OVERVIEW)]: "/dashboard",




Loading…
Annulla
Salva