浏览代码

領収証発行依頼申請 モック対応

develop
sosuke.iwabuchi 2 年前
父节点
当前提交
f5fd456e0d
共有 15 个文件被更改,包括 843 次插入8 次删除
  1. +2
    -0
      src/codes/page.ts
  2. +8
    -6
      src/components/table/index.tsx
  3. +157
    -0
      src/contexts/ReceiptRequestContext.tsx
  4. +3
    -0
      src/hooks/useNavigateCustom.ts
  5. +8
    -0
      src/hooks/useReceiptRequest.ts
  6. +153
    -0
      src/pages/receipt-request/hooks/useAgreement.tsx
  7. +114
    -0
      src/pages/receipt-request/hooks/useConfirm.tsx
  8. +249
    -0
      src/pages/receipt-request/hooks/useInput.tsx
  9. +54
    -0
      src/pages/receipt-request/index.tsx
  10. +2
    -0
      src/routes/index.tsx
  11. +3
    -0
      src/routes/path.ts
  12. +23
    -0
      src/routes/sub/receipt-request.tsx
  13. +57
    -0
      src/utils/form.ts
  14. +8
    -0
      src/utils/number.ts
  15. +2
    -2
      src/utils/page.ts

+ 2
- 0
src/codes/page.ts 查看文件

@@ -30,6 +30,8 @@ export const PageID = {
DASHBOARD_LOGIN_USER_CREATE: id++,
DASHBOARD_LOGIN_USER_CHANGE_PASSWORD: id++,

RECEIPT_REQUEST: id++,

PAGE_403: id++,
PAGE_404: id++,
} as const;


+ 8
- 6
src/components/table/index.tsx 查看文件

@@ -14,12 +14,14 @@ import { ReactNode, useEffect, useMemo } from "react";

export { default as TableHeadCustom } from "./TableHeadCustom";

type SimpleDataListProps = {
data: {
title: string;
value?: string;
end?: ReactNode;
}[];
export type SimpleData = {
title: string;
value?: string;
end?: ReactNode;
};

export type SimpleDataListProps = {
data: SimpleData[];
tableSx?: SxProps;
};
export const SimpleDataList = ({ data, tableSx }: SimpleDataListProps) => {


+ 157
- 0
src/contexts/ReceiptRequestContext.tsx 查看文件

@@ -0,0 +1,157 @@
import { yupResolver } from "@hookform/resolvers/yup";
import { HasChildren } from "@types";
import { createContext, useMemo, useState } from "react";
import { UseFormReturn, useForm } from "react-hook-form";
import { useParams } from "react-router-dom";
import * as Yup from "yup";

const schema = Yup.object().shape({
reason: Yup.string().required("入力してください"),
reason_text: Yup.string(),
user_company_name: Yup.string(),
user_first_name: Yup.string().required("入力してください"),
user_last_name: Yup.string().required("入力してください"),

put_in_date: Yup.date()
.required("必須項目です")
.typeError("正しく入力してください"),
put_in_hour: Yup.string().required("入力してください"),
put_in_minute: Yup.string().required("入力してください"),
put_out_date: Yup.date()
.required("必須項目です")
.typeError("正しく入力してください"),
put_out_hour: Yup.string().required("入力してください"),
put_out_minute: Yup.string().required("入力してください"),

parking_name: Yup.string().required("入力してください"),
room_no: Yup.string().required("入力してください"),

receipt_amount: Yup.number()
.typeError("数値を入力してください")

.test("range", "1-50000まで入力できます", function (value) {
const val = Number(value);
return 1 <= val && val <= 50000;
}),
paying_type: Yup.string().required("入力してください"),
phone_number: Yup.string()
.required("必須項目です")
.matches(/^[0-9]{10,11}$/, "正しい電話番号を入力してください"),

email: Yup.string()
.required("必須項目です")
.matches(/^.+@.+$/, "正しいEmailを入力してください"),

memo: Yup.string(),
});

export type ReceiptRequestFormProps = {
reason: string;
reason_text: string;
user_company_name: string;
user_first_name: string;
user_last_name: string;
put_in_date: Date | null;
put_in_hour: string;
put_in_minute: string;
put_out_date: Date | null;
put_out_hour: string;
put_out_minute: string;

parking_name: string;
room_no: string;

receipt_amount: string;
paying_type: string;

phone_number: string;
email: string;

memo: string;
};

export function getDefaultReceiptRequestFormValues(): ReceiptRequestFormProps {
return {
reason: "",
reason_text: "",
user_company_name: "",
user_first_name: "",
user_last_name: "",
put_in_date: null,
put_in_hour: "",
put_in_minute: "",
put_out_date: null,
put_out_hour: "",
put_out_minute: "",

parking_name: "",
room_no: "",

receipt_amount: "",
paying_type: "",

phone_number: "",
email: "",

memo: "",
};
}

type Mode = "agree" | "input" | "confirm" | "complete";

type ReceiptRequest = {
step: number;
mode: Mode;
inputData: ReceiptRequestFormProps;
form: UseFormReturn<ReceiptRequestFormProps> | undefined;
setMode: (mode: Mode) => void;
setInputData: (input: ReceiptRequestFormProps) => void;
};

export const ReceiptRequestContext = createContext<ReceiptRequest>({
step: 0,
mode: "agree",
inputData: getDefaultReceiptRequestFormValues(),
form: undefined,
setMode: (mode: Mode) => {},
setInputData: (input: ReceiptRequestFormProps) => {},
});

type Props = HasChildren;
export function ReceiptRequestContextProvider({ children }: Props) {
const { contractId: paramContractId } = useParams();

const [mode, setMode] = useState<Mode>("agree");

const [inputData, setInputData] = useState<ReceiptRequestFormProps>(
getDefaultReceiptRequestFormValues()
);

const step = useMemo(() => {
if (mode === "agree") return 0;
if (mode === "input") return 1;
if (mode === "confirm") return 2;
if (mode === "complete") return 3;
throw new Error("ステップ不正");
}, [mode]);

const form = useForm<ReceiptRequestFormProps>({
defaultValues: getDefaultReceiptRequestFormValues(),
resolver: yupResolver(schema),
});

return (
<ReceiptRequestContext.Provider
value={{
step,
mode,
inputData,
form,
setMode,
setInputData,
}}
>
{children}
</ReceiptRequestContext.Provider>
);
}

+ 3
- 0
src/hooks/useNavigateCustom.ts 查看文件

@@ -2,6 +2,7 @@ import { useLocation, useNavigate } from "react-router";
import { Dictionary } from "@types";
import { PageID } from "codes/page";
import { getPath } from "routes/path";
import { scrollToTop } from "utils/page";

export default function useNavigateCustom() {
const navigate = useNavigate();
@@ -54,6 +55,8 @@ export default function useNavigateCustom() {
navigate(newPath);
}
}

scrollToTop();
};
return { navigate, navigateWhenChanged };
}

+ 8
- 0
src/hooks/useReceiptRequest.ts 查看文件

@@ -0,0 +1,8 @@
import { ReceiptRequestContext } from "contexts/ReceiptRequestContext";
import { useContext } from "react";

export default function useReceiptRequest() {
const context = useContext(ReceiptRequestContext);

return context;
}

+ 153
- 0
src/pages/receipt-request/hooks/useAgreement.tsx 查看文件

@@ -0,0 +1,153 @@
import { yupResolver } from "@hookform/resolvers/yup";
import { Box, Button, Stack, Typography } from "@mui/material";
import { PageID } from "codes/page";
import { FormProvider, RHFCheckbox } from "components/hook-form";
import useReceiptRequest from "hooks/useReceiptRequest";
import { useMemo } from "react";
import { useForm } from "react-hook-form";
import { getPath } from "routes/path";
import { scrollToTop } from "utils/page";
import * as Yup from "yup";

type FormProps = {
accept_privacy_policy: boolean;
accept_correct_entry: boolean;
accept_site_policy: boolean;
};

const schema = Yup.object().shape({
accept_privacy_policy: Yup.boolean().test(
"accept",
"同意が必要です",
function (value) {
return !!value;
}
),
accept_correct_entry: Yup.boolean().test(
"accept",
"同意が必要です",
function (value) {
return !!value;
}
),
accept_site_policy: Yup.boolean().test(
"accept",
"同意が必要です",
function (value) {
return !!value;
}
),
});

export default function useAgreement() {
const { setMode } = useReceiptRequest();

const privacyPolicyUrl: string = useMemo(() => {
return getPath(PageID.APP_PRIVACY_POLICY);
}, []);

const form = useForm<FormProps>({
defaultValues: {
accept_privacy_policy: false,
accept_correct_entry: false,
accept_site_policy: false,
},
resolver: yupResolver(schema),
});

const acceptPrivacyPolicy = form.watch("accept_privacy_policy");
const acceptCorrectEntry = form.watch("accept_correct_entry");
const acceptSitePolicy = form.watch("accept_site_policy");

const canSubmit = useMemo(() => {
return acceptPrivacyPolicy && acceptCorrectEntry && acceptSitePolicy;
}, [acceptPrivacyPolicy, acceptCorrectEntry, acceptSitePolicy]);

const handleSubmit = () => {
scrollToTop("auto");
setMode("input");
};

const element = (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Box sx={{ m: 1, p: 1 }}>
<Box>
<Typography variant="body2">
このページは当社時間貸し駐車場・駐輪場ご利用者の方が領収書の発行(再発行)を申請するためのページです。
</Typography>
<Typography variant="body2">
※上記以外(月極駐車場、予約制駐車場等)ではご利用になれませんのでご注意ください。
</Typography>
<Typography variant="body2">
領収書の受け取り方法は①WEB発行(メール)②郵送のどちらかになります。
</Typography>
<Typography variant="body2">
以下、各項目についてご確認およびご同意頂き、申請フォームへお進みください。
</Typography>
</Box>

<Box mt={3}>
<Typography variant="subtitle1"><ご申請の流れ></Typography>
<Typography variant="body2">1.ご申請(ご利用者様)</Typography>
<Typography variant="body2">
2.ご申請内容の確認(運営会社)
</Typography>
<Typography variant="body2">
3.メールにて領収証取得用URL送付(運営会社)
</Typography>
</Box>

<Stack spacing={1} mt={3}>
<Box>
<Stack
direction="row"
spacing={0}
alignItems="center"
// justifyContent="center"
>
<RHFCheckbox
label=" 個人情報保護方針への同意"
name="accept_privacy_policy"
/>
<Typography variant="body1">
<a href={privacyPolicyUrl} target="_blank">
【 規約 】
</a>
</Typography>
</Stack>
</Box>
<Box>
<RHFCheckbox label="注意事項の確認" name="accept_site_policy" />
<Typography variant="body1"><注意事項></Typography>
<Typography variant="body2">
・申請内容に不明な点がある場合、別途お電話等で確認をとらせて頂く場合がございますのでご了承ください。
</Typography>
<Typography variant="body2">
・WEB上で領収証PDFをダウンロード可能です。
</Typography>
<Typography variant="body2">
・郵送を希望する場合、到着までに時間を要します。お急ぎの場合はダウンロードやEmail送付をご利用ください。
</Typography>
</Box>
<Box>
<RHFCheckbox
label={
<Typography variant="body1" color="error">
申請内容に虚偽・誤りが無いことへの同意
</Typography>
}
name="accept_correct_entry"
/>
</Box>
<Box>
<Button type="submit" variant="contained" disabled={!canSubmit}>
申請内容の入力へ進む
</Button>
</Box>
</Stack>
</Box>
</FormProvider>
);

return { element };
}

+ 114
- 0
src/pages/receipt-request/hooks/useConfirm.tsx 查看文件

@@ -0,0 +1,114 @@
import { yupResolver } from "@hookform/resolvers/yup";
import { Box, Button, Stack, Typography } from "@mui/material";
import { PageID } from "codes/page";
import { FormProvider, RHFCheckbox } from "components/hook-form";
import StackRow from "components/stack/StackRow";
import { SimpleData, SimpleDataList } from "components/table";
import { intlFormat } from "date-fns";
import useReceiptRequest from "hooks/useReceiptRequest";
import { useMemo } from "react";
import { useForm } from "react-hook-form";
import { getPath } from "routes/path";
import { sprintf } from "sprintf-js";
import { formatDateStr } from "utils/datetime";
import { numberFormat } from "utils/number";
import { scrollToTop } from "utils/page";
import * as Yup from "yup";

export default function useConfirm() {
const { setMode, inputData } = useReceiptRequest();

const data: SimpleData[] = useMemo(() => {
return [
{
title: "申請理由",
value: inputData.reason,
},
{
title: "申請者名(会社名)",
value: inputData.user_company_name,
},
{
title: "申請者名",
value: sprintf(
"%s %s",
inputData.user_first_name,
inputData.user_last_name
),
},
{
title: "入庫時刻",
value: sprintf(
"%s %s:%s",
formatDateStr(inputData.put_in_date),
inputData.put_in_hour,
inputData.put_in_minute
),
},
{
title: "精算時刻",
value: sprintf(
"%s %s:%s",
formatDateStr(inputData.put_out_date),
inputData.put_out_hour,
inputData.put_out_minute
),
},
{
title: "駐車場名",
value: inputData.parking_name,
},
{
title: "車室番号",
value: inputData.room_no,
},
{
title: "ご利用金額",
value: sprintf("%s円", numberFormat(inputData.receipt_amount)),
},
{
title: "支払方法",
value: inputData.paying_type,
},
{
title: "ご連絡先電話番号",
value: inputData.phone_number,
},
{
title: "ご連絡先Email",
value: inputData.email,
},
{
title: "その他伝達事項",
value: inputData.memo,
},
];
}, [inputData]);

const handlePrev = () => {
scrollToTop("auto");
setMode("input");
};

const element = (
<Box sx={{ m: 1, p: 1 }}>
<Stack spacing={3} sx={{ p: 1, m: 1 }} textAlign="left">
<Box>
下記の内容で申請を行います。
内容にお間違いがないか確認したうえ、確定を行ってください。
</Box>
<SimpleDataList data={data}></SimpleDataList>
<StackRow>
<Button variant="outlined" onClick={handlePrev}>
戻る
</Button>
<Button variant="contained" type="submit">
確定
</Button>
</StackRow>
</Stack>
</Box>
);

return { element };
}

+ 249
- 0
src/pages/receipt-request/hooks/useInput.tsx 查看文件

@@ -0,0 +1,249 @@
import { yupResolver } from "@hookform/resolvers/yup";
import {
Box,
Button,
Divider,
Link,
Stack,
Typography,
TypographyProps,
} from "@mui/material";
import { HasChildren } from "@types";
import { PageID } from "codes/page";
import RequireChip from "components/chip/RequireChip";
import {
FormProvider,
RHFCheckbox,
RHFSelect,
RHFTextField,
} from "components/hook-form";
import RHFDatePicker from "components/hook-form/RHFDatePicker";
import { SelectOptionProps } from "components/hook-form/RHFSelect";
import RHFPrefCodeSelect from "components/hook-form/ex/RHFPrefCodeSelect";
import StackRow from "components/stack/StackRow";
import {
ReceiptRequestFormProps,
getDefaultReceiptRequestFormValues,
} from "contexts/ReceiptRequestContext";
import useReceiptRequest from "hooks/useReceiptRequest";
import { range } from "lodash";
import { useMemo } from "react";
import { useForm } from "react-hook-form";
import { getPath } from "routes/path";
import { sprintf } from "sprintf-js";
import { scrollToTop } from "utils/page";
import * as Yup from "yup";

type AreaBoxProps = {
title: string;
require?: boolean;
} & HasChildren;
function AreaBox({ title, children, require }: AreaBoxProps) {
return (
<Stack sx={{ maxWidth: 500 }}>
<StackRow>
<Typography variant="subtitle1">〇 {title} </Typography>
<RequireChip require={require ?? false} />
</StackRow>
{children}
</Stack>
);
}

function Explain({ children, ...props }: TypographyProps) {
return (
<Typography variant="body2" {...props}>
{children}
</Typography>
);
}

export default function useInput() {
const { setMode, setInputData, form } = useReceiptRequest();

const hours: SelectOptionProps[] = useMemo(() => {
return [
{
label: "--",
value: "",
},
...range(0, 24).map((val) => ({
label: sprintf("%02d", val),
value: String(val),
})),
];
}, []);
const minutes: SelectOptionProps[] = useMemo(() => {
return [
{
label: "--",
value: "",
},
...range(0, 60).map((val) => ({
label: sprintf("%02d", val),
value: String(val),
})),
];
}, []);
const reasons: SelectOptionProps[] = useMemo(() => {
return [
{
label: "--",
value: "",
},
{
label: "取り忘れ",
value: "取り忘れ",
},
{
label: "紛失",
value: "紛失",
},
{
label: "その他",
value: "その他",
},
];
}, []);
const payingTypes: SelectOptionProps[] = useMemo(() => {
return [
{
label: "--",
value: "",
},
{
label: "現金",
value: "現金",
},
{
label: "クレジットカード",
value: "クレジットカード",
},
{
label: "電子マネー",
value: "電子マネー",
},
];
}, []);

const handleSubmit = () => {
if (!form) return;
setInputData(form.getValues());
scrollToTop("auto");
setMode("confirm");
};

const handlePrev = () => {
setMode("agree");
};

const element = !!form && (
<FormProvider
methods={form}
onSubmit={form.handleSubmit(handleSubmit, handleSubmit)}
>
<Box sx={{ m: 1, p: 1 }}>
<Stack spacing={3} sx={{ p: 1, m: 1 }} textAlign="left">
<AreaBox title="申請理由" require>
<RHFSelect name="reason" size="small" options={reasons} />
<Explain>&nbsp;</Explain>
<Explain>※その他の場合に入力してください</Explain>
<RHFTextField name="reason_text" size="small" />
</AreaBox>
<Divider />
<AreaBox title="申請者名(会社名)">
<Explain>※法人の方の場合のみ入力してください</Explain>
<RHFTextField name="user_company_name" size="small" />
</AreaBox>
<AreaBox title="申請者名" require>
<StackRow>
<StackRow alignItems="center">
<Typography variant="body2">姓</Typography>
<RHFTextField name="user_first_name" size="small" />
<Typography variant="body2">名</Typography>
<RHFTextField name="user_last_name" size="small" />
<Typography variant="body2">様</Typography>
</StackRow>
</StackRow>
</AreaBox>
<AreaBox title="入庫時刻" require>
<Explain>※不明の場合はおおよそで構いません</Explain>
<Stack spacing={1}>
<RHFDatePicker name="put_in_date" size="small" />
<StackRow alignItems="center">
<RHFSelect name="put_in_hour" size="small" options={hours} />
<Typography variant="body2">時</Typography>
<RHFSelect
name="put_in_minute"
size="small"
options={minutes}
/>
<Typography variant="body2">分</Typography>
</StackRow>
</Stack>
</AreaBox>
<AreaBox title="精算時刻" require>
<Explain>※不明の場合はおおよそで構いません</Explain>
<Stack spacing={1}>
<RHFDatePicker name="put_out_date" size="small" />
<StackRow alignItems="center">
<RHFSelect name="put_out_hour" size="small" options={hours} />
<Typography variant="body2">時</Typography>
<RHFSelect
name="put_out_minute"
size="small"
options={minutes}
/>
<Typography variant="body2">分</Typography>
</StackRow>
</Stack>
</AreaBox>
<AreaBox title="駐車場名" require>
<Explain>駐車場名はこちらで検索することができます</Explain>
<StackRow>
<Link
href="https://www.app-value.com/ypark/map.php"
target="_blank"
variant="body2"
>
ユアー・パーキング駐車場検索
</Link>
</StackRow>

<RHFTextField name="parking_name" size="small" />
</AreaBox>
<AreaBox title="車室番号" require>
<Explain>※分からない場合は「不明」と入力してください</Explain>
<RHFTextField name="room_no" size="small" />
</AreaBox>
<AreaBox title="ご利用金額" require>
<RHFTextField name="receipt_amount" type="number" size="small" />
</AreaBox>
<AreaBox title="支払方法" require>
<RHFSelect name="paying_type" size="small" options={payingTypes} />
</AreaBox>
<AreaBox title="ご連絡先電話番号" require>
<Explain>※ハイフン不要</Explain>
<RHFTextField name="phone_number" size="small" />
</AreaBox>
<AreaBox title="ご連絡先Email" require>
<RHFTextField name="email" size="small" />
</AreaBox>
<AreaBox title="その他伝達事項">
<RHFTextField name="memo" size="small" multiline rows={5} />
</AreaBox>
</Stack>
<StackRow>
<Button variant="outlined" onClick={handlePrev}>
戻る
</Button>
<Button variant="contained" type="submit">
次へ
</Button>
</StackRow>
</Box>
</FormProvider>
);

return { element };
}

+ 54
- 0
src/pages/receipt-request/index.tsx 查看文件

@@ -0,0 +1,54 @@
import {
Box,
Paper,
Step,
StepLabel,
Stepper,
Typography,
} from "@mui/material";
import { ReceiptRequestContextProvider } from "contexts/ReceiptRequestContext";
import useAgreement from "./hooks/useAgreement";
import useReceiptRequest from "hooks/useReceiptRequest";
import useInput from "./hooks/useInput";
import useConfirm from "./hooks/useConfirm";

export default function ReceiptRequest() {
return (
<ReceiptRequestContextProvider>
<App />
</ReceiptRequestContextProvider>
);
}
function App() {
const { mode, step } = useReceiptRequest();
const agreement = useAgreement();
const input = useInput();
const confirm = useConfirm();

return (
<Paper sx={{ mx: 1, my: 2, px: 1, py: 3 }}>
<Box mt={3}>
<Typography variant="h5">領収証発行(再発行)申請</Typography>
</Box>
<Box mt={2}>
<Stepper activeStep={step}>
<Step>
<StepLabel>個人情報保護指針への同意</StepLabel>
</Step>
<Step>
<StepLabel>ご申請内容の入力</StepLabel>
</Step>
<Step>
<StepLabel>ご申請内容の確認</StepLabel>
</Step>
<Step>
<StepLabel>ご申請完了</StepLabel>
</Step>
</Stepper>
</Box>
{mode === "agree" && agreement.element}
{mode === "input" && input.element}
{mode === "confirm" && confirm.element}
</Paper>
);
}

+ 2
- 0
src/routes/index.tsx 查看文件

@@ -6,6 +6,7 @@ import AppRoutes from "./sub/app";
import AuthRoutes from "./sub/auth";
import CommonRoutes from "./sub/common";
import DashboardRoutes from "./sub/dashboard";
import RequestReceiptRoutes from "./sub/receipt-request";

export const Loadable = (Component: ElementType) => (props: any) => {
return (
@@ -22,6 +23,7 @@ export function Routes() {
AuthRoutes(),
AppRoutes(),
DashboardRoutes(),
RequestReceiptRoutes(),
{
path: "*",
element: initialized ? <Page404 /> : <LoadingScreen />,


+ 3
- 0
src/routes/path.ts 查看文件

@@ -83,6 +83,9 @@ const PATHS = {
[makePathKey(PageID.DASHBOARD_LOGIN_USER_CHANGE_PASSWORD)]:
"/dashboard/login-user/change-password/:id",

// 領収証リクエスト
[makePathKey(PageID.RECEIPT_REQUEST)]: "/request/receipt/:contractId",

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


+ 23
- 0
src/routes/sub/receipt-request.tsx 查看文件

@@ -0,0 +1,23 @@
import { PageID } from "codes/page";
import SimpleLayout from "layouts/simple";
import { lazy, useMemo } from "react";
import { RouteObject } from "react-router-dom";
import { Loadable } from "routes";
import { getRoute } from "routes/path";

export default function ReceiptRequestRoutes(): RouteObject {
const ReceiptRequest = Loadable(lazy(() => import("pages/receipt-request")));
const children: RouteObject[] = useMemo(() => {
return [
{
path: getRoute(PageID.RECEIPT_REQUEST),
element: <ReceiptRequest />,
},
];
}, []);

return {
element: <SimpleLayout />,
children,
};
}

+ 57
- 0
src/utils/form.ts 查看文件

@@ -0,0 +1,57 @@
import { sprintf } from "sprintf-js";
import * as Yup from "yup";

type Require = {
require?: boolean;
};

type Range = {
min?: number;
max?: number;
};

export const Rule = {
DEFAULT_STR_MAX_LENGTH: 200,
DEFAULT_NUMBER_MAX_LENGTH: 50000,

string: function (param: Require & Range) {
const ret = Yup.string();
if (param.require) {
ret.required("入力してください");
}

// 最大値最小値設定
const min = getMin(param);
const max = getMax(param, this.DEFAULT_STR_MAX_LENGTH);
const message = sprintf("%d-%d文字入力できます", min, max);
ret.test("range", message, function (value) {
const len = value ? value.length : 0;
return min <= len && len <= max;
});

ret.typeError("入力を確認してください");
return ret;
},

number: function (param: Require & Range) {
const ret = Yup.number();
if (param.require) {
ret.required("入力してください");
}
},
};

function getMin(param: Range & Require): number {
if (param.min !== undefined) return param.min;

if (param.require) {
return 1;
} else {
return 0;
}
}
function getMax(param: Range, defaultValue: number): number {
if (param.max !== undefined) return param.max;

return 200;
}

+ 8
- 0
src/utils/number.ts 查看文件

@@ -0,0 +1,8 @@
const NumberFormat = new Intl.NumberFormat();

export function numberFormat(value: string | number): string {
if (typeof value === "number") {
return NumberFormat.format(value);
}
return NumberFormat.format(Number(value));
}

+ 2
- 2
src/utils/page.ts 查看文件

@@ -1,3 +1,3 @@
export const scrollToTop = () => {
window.scroll({ top: 0, behavior: "smooth" });
export const scrollToTop = (behavior: ScrollBehavior = "smooth") => {
window.scroll({ top: 0, behavior });
};

正在加载...
取消
保存