Browse Source

全体的に整備

develop
sosuke.iwabuchi 2 years ago
parent
commit
1ad855768c
22 changed files with 689 additions and 303 deletions
  1. +3
    -0
      src/api/auth.ts
  2. +7
    -0
      src/api/custom/hello-techno/receipt-issuing-order.ts
  3. +7
    -0
      src/codes/custom.ts
  4. +3
    -3
      src/codes/page.ts
  5. +43
    -9
      src/components/hook-form/RHFCheckbox.tsx
  6. +105
    -3
      src/components/hook-form/RHFSelect.tsx
  7. +16
    -1
      src/contexts/AuthContext.tsx
  8. +8
    -3
      src/contexts/SearchConditionContext.tsx
  9. +2
    -2
      src/layouts/dashbord/navigator.tsx
  10. +1
    -1
      src/pages/dashboard/receipt-issuing-order/custom/hello-techno/create.tsx
  11. +1
    -1
      src/pages/dashboard/receipt-issuing-order/custom/hello-techno/detail.tsx
  12. +0
    -0
      src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useConfirm.tsx
  13. +0
    -0
      src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useInputReceiptStep.tsx
  14. +0
    -0
      src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useInputSMSSendAddress.tsx
  15. +0
    -0
      src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useMailPostCompleteDialog.tsx
  16. +0
    -0
      src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useSelectParkingStep.tsx
  17. +425
    -0
      src/pages/dashboard/receipt-issuing-order/custom/hello-techno/list.tsx
  18. +0
    -255
      src/pages/dashboard/receipt-issuing-order/list.tsx
  19. +12
    -3
      src/routes/auth.ts
  20. +21
    -12
      src/routes/index.tsx
  21. +9
    -6
      src/routes/path.ts
  22. +26
    -4
      src/utils/datetime.ts

+ 3
- 0
src/api/auth.ts View File

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

type MeResponse = {
data: {
id: string;
contract_id: string;
role: UserRole;
name: string;
custom: CustomCode[];
};
} & APICommonResponse;



+ 7
- 0
src/api/custom/hello-techno/receipt-issuing-order.ts View File

@@ -3,6 +3,7 @@ import {
ApiId,
HttpMethod,
makeFormData,
makeParam,
request,
} from "api";
import { ReceiptIssuingOrder } from "api/receipt-issuing-order";
@@ -65,6 +66,12 @@ export type ReceiptIssuingOrdersRequest = {
customer_name?: string;
parking_management_code?: string;
parking_name?: string;
status?: string;
order_date_from?: string;
order_date_to?: string;
done?: string;
sms_phone_number?: string;
handler_name?: string;
};

export type ReceiptIssuingOrdersResponse = {


+ 7
- 0
src/codes/custom.ts View File

@@ -0,0 +1,7 @@
export const CustomCode = {
NONE: "",

HELLO_TECHNO: "HELLO_TECHNO",
} as const;

export type CustomCode = (typeof CustomCode)[keyof typeof CustomCode];

+ 3
- 3
src/codes/page.ts View File

@@ -13,9 +13,9 @@ export const PageID = {
DASHBOARD_CONTRACT_LIST: id++,
DASHBOARD_CONTRACT_DETAIL: id++,

DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE: id++,
DASHBOARD_RECEIPT_ISSUING_ORDER_LIST: id++,
DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL: id++,
DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO: id++,
DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO: id++,
DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL_CUSTOM_HELLO_TECHNO: id++,

PAGE_403: id++,
PAGE_404: id++,


+ 43
- 9
src/components/hook-form/RHFCheckbox.tsx View File

@@ -1,18 +1,47 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
import { useFormContext, Controller } from "react-hook-form";
// @mui
import { Checkbox, FormControlLabel, FormGroup, FormControlLabelProps } from '@mui/material';
import {
Checkbox,
FormControlLabel,
FormGroup,
FormControlLabelProps,
} from "@mui/material";
import { useMemo } from "react";

// ----------------------------------------------------------------------

interface RHFCheckboxProps extends Omit<FormControlLabelProps, 'control'> {
interface RHFCheckboxProps extends Omit<FormControlLabelProps, "control"> {
name: string;
readOnly?: boolean;
}

export function RHFCheckbox({ name, readOnly, ...other }: RHFCheckboxProps) {
const { control, watch } = useFormContext();
const formValue: boolean = watch(name);

const _formValue = watch(name);

// const formValue : boolean = useMemo(()=>{
// if(_formValue typeof 'boolean') {
// return _formValue;
// }

// return false
// },[_formValue])

const formValue = useMemo(() => {
if (typeof _formValue === "boolean") {
console.log("boolean", _formValue);
return _formValue;
}
if (typeof _formValue === "string") {
console.log("string", _formValue);
return _formValue === "1" || _formValue === "true";
}

console.log("else");
return false;
}, [_formValue]);

if (readOnly) {
return (
@@ -24,12 +53,12 @@ export function RHFCheckbox({ name, readOnly, ...other }: RHFCheckboxProps) {
disableFocusRipple
checked={formValue}
sx={{
cursor: 'default',
cursor: "default",
}}
/>
}
sx={{
cursor: 'default',
cursor: "default",
}}
{...other}
/>
@@ -42,7 +71,7 @@ export function RHFCheckbox({ name, readOnly, ...other }: RHFCheckboxProps) {
<Controller
name={name}
control={control}
render={({ field }) => <Checkbox {...field} checked={field.value} />}
render={({ field }) => <Checkbox {...field} checked={formValue} />}
/>
}
{...other}
@@ -52,7 +81,8 @@ export function RHFCheckbox({ name, readOnly, ...other }: RHFCheckboxProps) {

// ----------------------------------------------------------------------

interface RHFMultiCheckboxProps extends Omit<FormControlLabelProps, 'control' | 'label'> {
interface RHFMultiCheckboxProps
extends Omit<FormControlLabelProps, "control" | "label"> {
name: string;
options: {
label: string;
@@ -60,7 +90,11 @@ interface RHFMultiCheckboxProps extends Omit<FormControlLabelProps, 'control' |
}[];
}

export function RHFMultiCheckbox({ name, options, ...other }: RHFMultiCheckboxProps) {
export function RHFMultiCheckbox({
name,
options,
...other
}: RHFMultiCheckboxProps) {
const { control } = useFormContext();

return (


+ 105
- 3
src/components/hook-form/RHFSelect.tsx View File

@@ -1,14 +1,26 @@
import { useFormContext, Controller } from "react-hook-form";
import { MenuItem, TextField, TextFieldProps } from "@mui/material";
import {
Box,
Chip,
FormControl,
FormHelperText,
IconButton,
MenuItem,
OutlinedInput,
Select,
TextField,
TextFieldProps,
} from "@mui/material";
import { useCallback, useEffect, useMemo, useState } from "react";
import TextFieldEx from "../form/TextFieldEx";
import { Dictionary } from "@types";
import { Clear, Label } from "@mui/icons-material";

// ----------------------------------------------------------------------

export type SelectOptionProps = {
value: string;
label: string;
label?: string;
};

export const makeOptions = (list: Dictionary[]): SelectOptionProps[] => {
@@ -60,7 +72,7 @@ export default function RHFSelect({
return options.map((option, index) => {
return (
<MenuItem value={option.value} key={index}>
{option.label}
{option.label ?? option.value}
</MenuItem>
);
});
@@ -117,3 +129,93 @@ export default function RHFSelect({
/>
);
}

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};

export function RHFSelectMuiliple({
name,
children,
readOnly,
options,
onFix,
...other
}: Props) {
const { control, watch, setValue } = useFormContext();

const formValue: string[] = watch(name);

const getOptionElements = useCallback(() => {
if (options) {
if (options.length === 0) {
return [
<MenuItem key="disabled" disabled>
候補がありません
</MenuItem>,
];
} else {
return options.map((option, index) => {
return (
<MenuItem value={option.value} key={index}>
{option.label ?? option.value}
</MenuItem>
);
});
}
}
return [];
}, [options]);

const clearButton = useMemo(() => {
if (formValue.length === 0) return null;

const handleClick = () => {
setValue(name, []);
};

return (
<IconButton sx={{ mr: 1 }} onClick={handleClick}>
<Clear />
</IconButton>
);
}, [formValue]);

return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl>
<Select
{...field}
size="small"
multiple
value={formValue}
error={!!error}
input={<OutlinedInput endAdornment={clearButton} />}
renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} size="small" />
))}
</Box>
)}
MenuProps={MenuProps}
>
{children}
{getOptionElements()}
</Select>
{!!error?.message && <FormHelperText>{error.message}</FormHelperText>}
</FormControl>
)}
/>
);
}

+ 16
- 1
src/contexts/AuthContext.tsx View File

@@ -1,6 +1,7 @@
import { HasChildren } from "@types";
import { ResultCode } from "api";
import { login as APILogin, logout as APILogout, me } from "api/auth";
import { CustomCode } from "codes/custom";
import { PageID } from "codes/page";
import { UserRole } from "codes/user";
import useAPICall from "hooks/useAPICall";
@@ -21,6 +22,9 @@ type Auth = {

role: UserRole;
contractId: string | null;
name: string;
custom: CustomCode[];
customerName: string;

login: (email: string, password: string) => Promise<boolean>;
logout: VoidFunction;
@@ -37,6 +41,9 @@ export const AuthContext = createContext<Auth>({

role: UserRole.NONE,
contractId: null,
name: "",
custom: [],
customerName: "",

login: async (email: string, password: string) => false,
logout: () => {},
@@ -50,6 +57,9 @@ function AuthContextProvider({ children }: Props) {
const [initialized, setInitialized] = useState(false);
const [role, setRole] = useState<UserRole>(UserRole.NONE);
const [contractId, setContractId] = useState<string | null>(null);
const [name, setName] = useState("");
const [custom, setCustom] = useState<CustomCode[]>([]);
const [customerName, setCustomerName] = useState("");

const authenticated = useMemo(() => {
return role !== UserRole.NONE;
@@ -60,7 +70,8 @@ function AuthContextProvider({ children }: Props) {
onSuccess: (res) => {
setContractId(res.data.contract_id);
setRole(res.data.role);

setName(res.data.name);
setCustom(res.data.custom);
setInitialized(true);
},
onFailed: () => {
@@ -132,6 +143,10 @@ function AuthContextProvider({ children }: Props) {
authenticated,
role,
contractId,
name,
custom,

customerName,

// Func
login,


+ 8
- 3
src/contexts/SearchConditionContext.tsx View File

@@ -72,7 +72,15 @@ export function SearchConditionContextProvider({ children }: Props) {
}
});

console.log("compare condition", {
before,
after,
});
if (!isEqual(before, after)) {
console.log("add condition", {
before,
after,
});
setCondition(after, "addCondition");
}
};
@@ -90,9 +98,6 @@ export function SearchConditionContextProvider({ children }: Props) {

const applyToURL = () => {
if (!initialized) return;
const params = searchParams;
const searchStr = params.toString();
const url = pathname + (searchStr ? "?" + searchStr : "");

navigateWhenChanged(pathname, condition, "applyToURL");
};


+ 2
- 2
src/layouts/dashbord/navigator.tsx View File

@@ -60,11 +60,11 @@ const categories: Group[] = [
icon: <PeopleIcon />,
children: [
{
id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST,
id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO,
label: "一覧",
},
{
id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE,
id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO,
label: "新規",
},
],


src/pages/dashboard/receipt-issuing-order/create.tsx → src/pages/dashboard/receipt-issuing-order/custom/hello-techno/create.tsx View File

@@ -14,7 +14,7 @@ import { getValue } from "components/hook-form/RHFAutoComplete";

export default function ReceiptIssuingOrderCreate() {
const { setHeaderTitle, setTabs } = useDashboard(
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE,
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO,
TabID.NONE
);


src/pages/dashboard/receipt-issuing-order/detail.tsx → src/pages/dashboard/receipt-issuing-order/custom/hello-techno/detail.tsx View File

@@ -30,7 +30,7 @@ import { ApiId } from "api";

export default function ReceiptIssuingOrderDetail() {
const { setHeaderTitle, setTabs } = useDashboard(
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL_CUSTOM_HELLO_TECHNO
);

const { navigate } = useNavigateCustom();

src/pages/dashboard/receipt-issuing-order/hooks/useConfirm.tsx → src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useConfirm.tsx View File


src/pages/dashboard/receipt-issuing-order/hooks/useInputReceiptStep.tsx → src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useInputReceiptStep.tsx View File


src/pages/dashboard/receipt-issuing-order/hooks/useInputSMSSendAddress.tsx → src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useInputSMSSendAddress.tsx View File


src/pages/dashboard/receipt-issuing-order/hooks/useMailPostCompleteDialog.tsx → src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useMailPostCompleteDialog.tsx View File


src/pages/dashboard/receipt-issuing-order/hooks/useSelectParkingStep.tsx → src/pages/dashboard/receipt-issuing-order/custom/hello-techno/hooks/useSelectParkingStep.tsx View File


+ 425
- 0
src/pages/dashboard/receipt-issuing-order/custom/hello-techno/list.tsx View File

@@ -0,0 +1,425 @@
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Drawer,
FormControl,
Grid,
IconButton,
Stack,
Table,
TableBody,
TableCell,
TableContainer,
TablePagination,
TableRow,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { Dictionary } from "@types";
import {
ReceiptIssuingOrderHTCustom,
getReceiptIssuingOrders,
} from "api/custom/hello-techno/receipt-issuing-order";
import { PageID, TabID } from "codes/page";
import {
FormProvider,
RHFCheckbox,
RHFSelect,
RHFTextField,
} from "components/hook-form";
import { TableHeadCustom } from "components/table";
import { SearchConditionContextProvider } from "contexts/SearchConditionContext";
import useAPICall from "hooks/useAPICall";
import useBackDrop from "hooks/useBackDrop";
import useDashboard from "hooks/useDashBoard";
import useNavigateCustom from "hooks/useNavigateCustom";
import useSearchConditionContext from "hooks/useSearchConditionContext";
import useTable, { UseTableReturn } from "hooks/useTable";
import { useEffect, useMemo, useState } from "react";
import { useForm } from "react-hook-form";
import { getPath } from "routes/path";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
import { RHFSelectMuiliple } from "components/hook-form/RHFSelect";
import useAuth from "hooks/useAuth";
import RHFDatePicker from "components/hook-form/RHFDatePicker";
import { dateParse, formatDateStr } from "utils/datetime";
import { Clear } from "@mui/icons-material";

export default function ReceiptIssuingOrderList() {
const { setHeaderTitle, setTabs } = useDashboard(
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO,
TabID.NONE
);

useEffect(() => {
setHeaderTitle("領収証発行依頼一覧");
setTabs(null);
}, []);

return (
<SearchConditionContextProvider>
<Page />
</SearchConditionContextProvider>
);
}

function Page() {
const table = useTable<ReceiptIssuingOrderHTCustom>();

return (
<Box>
<SearchBox table={table} />
<TableBox table={table} />
</Box>
);
}

type FormProps = {
customer_name: string;
parking_name: string;
include_done: boolean;
status: string;
sms_phone_number: string;
handler_name: string;
order_date_from: Date | null;
order_date_to: Date | null;
};
type CommonProps = {
table: UseTableReturn<ReceiptIssuingOrderHTCustom>;
};
function SearchBox({ table }: CommonProps) {
const {
condition,
initialized,
get,
addCondition: add,
} = useSearchConditionContext();

const { setShowBackDrop } = useBackDrop();

const { name: myName } = useAuth();

const [openDateDialog, setOpenDateDialog] = useState(false);

const form = useForm<FormProps>({
defaultValues: {
customer_name: "",
parking_name: "",
include_done: false,
status: "",
sms_phone_number: "",
handler_name: "",
order_date_from: null,
order_date_to: null,
},
});

const {
callAPI: callGetReceiptIssuingOrders,
makeSendData,
sending,
} = useAPICall({
apiMethod: getReceiptIssuingOrders,
form,
onSuccess: (res) => {
table.setRowData(res.data.records);
},
});

const includeDone = form.watch("include_done");
const orderDateFrom = form.watch("order_date_from");
const orderDateTo = form.watch("order_date_to");

const isSetOrderDateConditions = useMemo(() => {
return !!orderDateFrom || !!orderDateTo;
}, [orderDateFrom, orderDateTo]);

const handleSubmit = async (data: FormProps) => {
addCondition(data);
};

const addCondition = (data: FormProps) => {
add({
...data,
include_done: data.include_done ? "1" : "0",
order_date_from: formatDateStr(data.order_date_from),
order_date_to: formatDateStr(data.order_date_to),
});
};

const handleBlur = () => {
addCondition(form.getValues());
};

const setMyName = () => {
form.setValue("handler_name", myName);
addCondition(form.getValues());
};

const fetch = async (data: Dictionary) => {
const sendData = makeSendData({
...data,
// 完了を含む場合は、条件設定しない。含まない場合は未完了のみとする
done: data.include_done === "1" ? "" : "0",
});
callGetReceiptIssuingOrders(sendData);
};

// 初期値設定
useEffect(() => {
if (initialized) {
form.setValue("customer_name", get("customer_name"));
form.setValue("parking_name", get("parking_name"));
form.setValue("include_done", get("include_done") === "1");
form.setValue("status", get("status"));
form.setValue("handler_name", get("handler_name"));
form.setValue("sms_phone_number", get("sms_phone_number"));
form.setValue("order_date_from", dateParse(get("order_date_from")));
form.setValue("order_date_to", dateParse(get("order_date_to")));
}
}, [initialized, condition]);

// Fetchアクション
useEffect(() => {
if (initialized) {
fetch(condition);
}
}, [condition, initialized]);

// バックドロップ
useEffect(() => {
setShowBackDrop(sending);
}, [sending]);

useEffect(() => {
addCondition(form.getValues());
}, [includeDone, orderDateFrom, orderDateTo]);

return (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Box sx={{ p: 1, m: 1 }}>
<Grid container spacing={2}>
<Grid item xs={3} lg={2}>
<Stack>
<Typography>担当者</Typography>
<Stack direction="row">
<RHFTextField
name="handler_name"
fullWidth={false}
onBlur={handleBlur}
/>
<Box>
<Tooltip title="私が担当" placement="top">
<IconButton aria-label="私が担当" onClick={setMyName}>
<AccountCircleIcon />
</IconButton>
</Tooltip>
</Box>
</Stack>
</Stack>
</Grid>
<Grid item xs={3} lg={2}>
<Stack>
<Typography>ステータス</Typography>
<RHFTextField name="status" onBlur={handleBlur} />
</Stack>
</Grid>
<Grid item>
<Stack>
<Typography>完了を含む</Typography>
<RHFCheckbox name="include_done" label="" sx={{ mx: "auto" }} />
</Stack>
</Grid>
</Grid>
<Grid container spacing={2} mt={1}>
<Grid item xs={3} lg={2}>
<Stack>
<Typography>運営会社</Typography>
<RHFTextField name="customer_name" onBlur={handleBlur} />
</Stack>
</Grid>
<Grid item xs={3} lg={2}>
<Stack>
<Typography>駐車場名</Typography>
<RHFTextField name="parking_name" onBlur={handleBlur} />
</Stack>
</Grid>
{/* <Grid item xs={3} lg={2}>
<Stack>
<Typography>ああ</Typography>
<RHFSelectMuiliple
name="aa"
options={[
{ value: "aaaaaaaaaaaaaaaaa" },
{ value: "bbbbbbbbbbbbbbbbbbbb" },
]}
/>
</Stack>
</Grid> */}
<Grid item xs={3} lg={2}>
<Typography>電話番号</Typography>
<RHFTextField name="sms_phone_number" />
</Grid>
<Grid item xs={3} lg={2}>
<Typography>受付時刻</Typography>
<Button
variant={isSetOrderDateConditions ? "contained" : "text"}
color={isSetOrderDateConditions ? "secondary" : "info"}
onClick={() => {
setOpenDateDialog(true);
}}
>
{isSetOrderDateConditions ? "設定あり" : "設定"}
</Button>
{isSetOrderDateConditions && (
<IconButton
onClick={() => {
form.setValue("order_date_from", null);
form.setValue("order_date_to", null);
}}
>
<Clear />
</IconButton>
)}
</Grid>
</Grid>
</Box>
<Dialog
open={openDateDialog}
onClose={() => {
setOpenDateDialog(false);
}}
>
<DialogTitle>受付時刻検索条件</DialogTitle>
<DialogContent>
<Stack direction="row" spacing={2}>
<Box>
<Typography>From</Typography>
<RHFDatePicker name="order_date_from" />
</Box>
<Box>
<Typography>&nbsp;</Typography>
<Typography>~</Typography>
</Box>
<Box>
<Typography>To</Typography>
<RHFDatePicker name="order_date_to" />
</Box>
</Stack>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setOpenDateDialog(false);
}}
>
決定
</Button>
</DialogActions>
</Dialog>
</FormProvider>
);
}

function TableBox({ table }: CommonProps) {
const TABLE_HEAD = [
{ id: "order_datetime", label: "受付時刻", align: "left" },
{ id: "customer_name", label: "運営会社名", align: "left" },
{ id: "parking_name", label: "駐車場名", align: "left" },
{ id: "status_name", label: "ステータス", align: "left" },
{ id: "handler_name", label: "担当者", align: "left" },
];
const {
order,
page,
sort,
rowsPerPage,
fetched,
fillteredRow,
isNotFound,
dataLength,
//
onSort,
onChangePage,
onChangeRowsPerPage,
//
setRowData,
//
ROWS_PER_PAGES,
} = table;

return (
<>
<TableContainer
sx={{
// minWidth: 800,
position: "relative",
}}
>
<Table 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: ReceiptIssuingOrderHTCustom;
};
function Row({ data }: RowProps) {
const { navigateWhenChanged } = useNavigateCustom();

const handleClick = () => {
navigateWhenChanged(
getPath(
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL_CUSTOM_HELLO_TECHNO,
{
query: {
id: data.id,
},
}
)
);
};

return (
<TableRow hover sx={{ cursor: "pointer" }} onClick={handleClick}>
<TableCell>{data.order_datetime}</TableCell>
<TableCell>{data.customer_name}</TableCell>
<TableCell>{data.parking_name}</TableCell>
<TableCell>{data.status_name}</TableCell>
<TableCell>{data.handler_name}</TableCell>
</TableRow>
);
}

+ 0
- 255
src/pages/dashboard/receipt-issuing-order/list.tsx View File

@@ -1,255 +0,0 @@
import {
Box,
Grid,
Table,
TableBody,
TableCell,
TableContainer,
TablePagination,
TableRow,
} from "@mui/material";
import { Dictionary } from "@types";
import {
ReceiptIssuingOrderHTCustom,
getReceiptIssuingOrders,
} from "api/custom/hello-techno/receipt-issuing-order";
import { PageID, TabID } from "codes/page";
import { FormProvider, RHFTextField } from "components/hook-form";
import { TableHeadCustom } from "components/table";
import { SearchConditionContextProvider } from "contexts/SearchConditionContext";
import useAPICall from "hooks/useAPICall";
import useBackDrop from "hooks/useBackDrop";
import useDashboard from "hooks/useDashBoard";
import useNavigateCustom from "hooks/useNavigateCustom";
import useSearchConditionContext from "hooks/useSearchConditionContext";
import useTable, { UseTableReturn } from "hooks/useTable";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { getPath } from "routes/path";

export default function ReceiptIssuingOrderList() {
const { setHeaderTitle, setTabs } = useDashboard(
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST,
TabID.NONE
);

useEffect(() => {
setHeaderTitle("領収証発行依頼一覧");
setTabs(null);
}, []);

return (
<SearchConditionContextProvider>
<Page />
</SearchConditionContextProvider>
);
}

function Page() {
const table = useTable<ReceiptIssuingOrderHTCustom>();

return (
<Box>
<SearchBox table={table} />
<TableBox table={table} />
</Box>
);
}

type FormProps = {
customer_name: string;
parking_name: string;
};
type CommonProps = {
table: UseTableReturn<ReceiptIssuingOrderHTCustom>;
};
function SearchBox({ table }: CommonProps) {
const {
condition,
initialized,
get,
addCondition: add,
} = useSearchConditionContext();

const { setShowBackDrop } = useBackDrop();

const form = useForm<FormProps>({
defaultValues: {
customer_name: "",
parking_name: "",
},
});

const {
callAPI: calGetReceiptIssuingOrders,
makeSendData,
sending,
} = useAPICall({
apiMethod: getReceiptIssuingOrders,
form,
onSuccess: (res) => {
table.setRowData(res.data.records);
},
});

const handleSubmit = async (data: FormProps) => {
addCondition(data);
};

const addCondition = (data: FormProps) => {
add({ ...data });
};

const handleBlur = () => {
addCondition(form.getValues());
};

const fetch = async (data: Dictionary) => {
const sendData = makeSendData({
...data,
});
calGetReceiptIssuingOrders(sendData);
};

// 初期値設定
useEffect(() => {
if (initialized) {
form.setValue("customer_name", get("customer_name"));
form.setValue("parking_name", get("parking_name"));
}
}, [initialized, condition]);

// Fetchアクション
useEffect(() => {
if (initialized) {
fetch(condition);
}
}, [condition, initialized]);

// バックドロップ
useEffect(() => {
setShowBackDrop(sending);
}, [sending]);

return (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Box sx={{ p: 1, m: 1 }}>
<Grid container spacing={1}>
<Grid item xs={3}>
<RHFTextField
label="運営会社名"
name="customer_name"
fullWidth
size="small"
onBlur={handleBlur}
/>
</Grid>
<Grid item xs={3}>
<RHFTextField
label="駐車場名"
name="parking_name"
fullWidth
size="small"
onBlur={handleBlur}
/>
</Grid>
</Grid>
</Box>
</FormProvider>
);
}

function TableBox({ table }: CommonProps) {
const TABLE_HEAD = [
{ id: "order_datetime", label: "受付時刻", align: "left" },
{ id: "customer_name", label: "運営会社名", align: "left" },
{ id: "parking_name", label: "駐車場名", align: "left" },
{ id: "status", label: "ステータス", align: "left" },
{ id: "handler_name", label: "担当者", align: "left" },
];
const {
order,
page,
sort,
rowsPerPage,
fetched,
fillteredRow,
isNotFound,
dataLength,
//
onSort,
onChangePage,
onChangeRowsPerPage,
//
setRowData,
//
ROWS_PER_PAGES,
} = table;

return (
<>
<TableContainer
sx={{
// minWidth: 800,
position: "relative",
}}
>
<Table 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: ReceiptIssuingOrderHTCustom;
};
function Row({ data }: RowProps) {
const { navigateWhenChanged } = useNavigateCustom();

const handleClick = () => {
navigateWhenChanged(
getPath(PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL, {
query: {
id: data.id,
},
})
);
};

return (
<TableRow hover sx={{ cursor: "pointer" }} onClick={handleClick}>
<TableCell>{data.order_datetime}</TableCell>
<TableCell>{data.customer_name}</TableCell>
<TableCell>{data.parking_name}</TableCell>
<TableCell>{data.status_name}</TableCell>
<TableCell>{data.handler_name}</TableCell>
</TableRow>
);
}

+ 12
- 3
src/routes/auth.ts View File

@@ -11,9 +11,18 @@ export const AUTH = {
[P.DASHBOARD_CONTRACT_LIST]: setAuth("ge", R.SUPER_ADMIN),
[P.DASHBOARD_CONTRACT_DETAIL]: setAuth("ge", R.SUPER_ADMIN),

[P.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE]: setAuth("ge", R.NORMAL_ADMIN),
[P.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST]: setAuth("ge", R.NORMAL_ADMIN),
[P.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL]: setAuth("ge", R.NORMAL_ADMIN),
[P.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO]: setAuth(
"ge",
R.NORMAL_ADMIN
),
[P.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO]: setAuth(
"ge",
R.NORMAL_ADMIN
),
[P.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL_CUSTOM_HELLO_TECHNO]: setAuth(
"ge",
R.NORMAL_ADMIN
),

[P.PAGE_403]: setAuth("all"),
[P.PAGE_404]: setAuth("all"),


+ 21
- 12
src/routes/index.tsx View File

@@ -69,18 +69,18 @@ const DashboardRoutes = (): RouteObject => {
target: UserRole.SUPER_ADMIN,
},
{
pageId: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE,
element: <ReceiptIssuingOrderCreate />,
pageId: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO,
element: <ReceiptIssuingOrderCreateHelloTechno />,
target: UserRole.NORMAL_ADMIN,
},
{
pageId: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST,
element: <ReceiptIssuingOrderList />,
pageId: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO,
element: <ReceiptIssuingOrderListHelloTechno />,
target: UserRole.NORMAL_ADMIN,
},
{
pageId: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL,
element: <ReceiptIssuingOrderDetail />,
pageId: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL_CUSTOM_HELLO_TECHNO,
element: <ReceiptIssuingOrderDetailHelloTechno />,
target: UserRole.NORMAL_ADMIN,
},
];
@@ -137,14 +137,23 @@ const ContractDetail = Loadable(
lazy(() => import("pages/dashboard/contract/detail"))
);
// 領収証発行依頼
const ReceiptIssuingOrderCreate = Loadable(
lazy(() => import("pages/dashboard/receipt-issuing-order/create"))
const ReceiptIssuingOrderCreateHelloTechno = Loadable(
lazy(
() =>
import("pages/dashboard/receipt-issuing-order/custom/hello-techno/create")
)
);
const ReceiptIssuingOrderList = Loadable(
lazy(() => import("pages/dashboard/receipt-issuing-order/list"))
const ReceiptIssuingOrderListHelloTechno = Loadable(
lazy(
() =>
import("pages/dashboard/receipt-issuing-order/custom/hello-techno/list")
)
);
const ReceiptIssuingOrderDetail = Loadable(
lazy(() => import("pages/dashboard/receipt-issuing-order/detail"))
const ReceiptIssuingOrderDetailHelloTechno = Loadable(
lazy(
() =>
import("pages/dashboard/receipt-issuing-order/custom/hello-techno/detail")
)
);

// その他 ---------------------------------


+ 9
- 6
src/routes/path.ts View File

@@ -49,12 +49,15 @@ const PATHS = {
[makePathKey(PageID.DASHBOARD_CONTRACT_DETAIL)]: "/dashboard/contract/detail",

// 領収証発行依頼関連
[makePathKey(PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE)]:
"/dashboard/receipt-issusing-order/create",
[makePathKey(PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST)]:
"/dashboard/receipt-issusing-order/list/:page",
[makePathKey(PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL)]:
"/dashboard/receipt-issusing-order/detail/:id",
[makePathKey(
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE_CUSTOM_HELLO_TECHNO
)]: "/dashboard/receipt-issusing-order/create",
[makePathKey(
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST_CUSTOM_HELLO_TECHNO
)]: "/dashboard/receipt-issusing-order/list/:page",
[makePathKey(
PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL_CUSTOM_HELLO_TECHNO
)]: "/dashboard/receipt-issusing-order/detail/:id",

// その他
[makePathKey(PageID.PAGE_403)]: "403",


+ 26
- 4
src/utils/datetime.ts View File

@@ -1,17 +1,20 @@
import { format, parseISO } from 'date-fns';
import { format, isValid, parse, parseISO } from "date-fns";

export const DEFAULT_DATE_FORMAT = "yyyy/MM/dd";
export const DEFAULT_DATET_TIME_FORMAT = "yyyy/MM/dd HH:mm:ss";

type Input = Date | string | null | undefined;

export const formatDateStr = (source: Input) => {
return formatToStr(source, 'yyyy/MM/dd');
return formatToStr(source, DEFAULT_DATE_FORMAT);
};

export const formatDateTimeStr = (source: Date | string | null | undefined) => {
return formatToStr(source, 'yyyy/MM/dd HH:mm:ss');
return formatToStr(source, DEFAULT_DATET_TIME_FORMAT);
};

const formatToStr = (source: Input, formatStr: string) => {
if (source === null || source === undefined) return '';
if (source === null || source === undefined) return "";
if (source instanceof Date) return format(source, formatStr);
return format(parseISO(source), formatStr);
};
@@ -22,3 +25,22 @@ export const now = () => {
export const nowStr = (): string => {
return formatDateTimeStr(now());
};

export const dateParse = (source: Input): Date | null => {
return parseFromFormat(source, DEFAULT_DATE_FORMAT);
};

export const dateTimeParse = (source: Input): Date | null => {
return parseFromFormat(source, DEFAULT_DATET_TIME_FORMAT);
};

const parseFromFormat = (source: Input, format: string): Date | null => {
if (source === null || source === undefined) return null;
if (source instanceof Date) return source;

const ret = parse(source, format, new Date());
if (isValid(ret)) {
return ret;
}
return null;
};

Loading…
Cancel
Save