Browse Source

全体整備

develop
sosuke.iwabuchi 2 years ago
parent
commit
ecadb7aa93
17 changed files with 857 additions and 41 deletions
  1. +8
    -4
      src/api/auth.ts
  2. +36
    -0
      src/api/customer.ts
  3. +47
    -0
      src/api/faq.ts
  4. +7
    -0
      src/api/index.ts
  5. +5
    -0
      src/api/url.ts
  6. +103
    -0
      src/components/hook-form/RHFUpload.tsx
  7. +6
    -1
      src/contexts/AuthContext.tsx
  8. +98
    -0
      src/hooks/useUpload.tsx
  9. +3
    -1
      src/pages/dashboard/contract/detail.tsx
  10. +22
    -8
      src/pages/dashboard/contract/sticker-re-order.tsx
  11. +217
    -25
      src/pages/dashboard/other/ask.tsx
  12. +22
    -2
      src/pages/dashboard/user/detail.tsx
  13. +133
    -0
      src/pages/dashboard/user/upload-other-license-images.tsx
  14. +130
    -0
      src/pages/dashboard/user/upload-student-license-images.tsx
  15. +2
    -0
      src/pages/index.ts
  16. +4
    -0
      src/routes/path.ts
  17. +14
    -0
      src/routes/sub/dashboard.tsx

+ 8
- 4
src/api/auth.ts View File

@@ -1,11 +1,15 @@
import { APICommonResponse, ApiId, HttpMethod, request } from ".";
import { getUrl } from "./url";

export type Me = {
customer_name: string;
email: string;
student_license_images_upload_datetime: string | null;
other_license_images_upload_datetime: string | null;
};

type MeResponse = {
data: {
customer_name: string;
email: string;
};
data: Me;
} & APICommonResponse;

export const csrfToken = async () => {


+ 36
- 0
src/api/customer.ts View File

@@ -0,0 +1,36 @@
import { ApiId, HttpMethod, makeFormData, request } from "api";
import { getUrl } from "./url";

// -------学生証アップロード---------------
export type UploadStudentLicenseImagesRequest = {
images: File[];
};
export const uploadStudentLicenseImages = async (
param: UploadStudentLicenseImagesRequest
) => {
const sendData = makeFormData(param);
const res = await request({
url: getUrl(ApiId.UPLOAD_STUDENT_LICENSE_IMAGES),
method: HttpMethod.POST,
data: sendData,
multipart: true,
});
return res;
};

// -------その他証明証アップロード---------------
export type OtherLicenseImagesRequest = {
images: File[];
};
export const uploadOtherLicenseImages = async (
param: OtherLicenseImagesRequest
) => {
const sendData = makeFormData(param);
const res = await request({
url: getUrl(ApiId.UPLOAD_OTHER_LICENSE_IMAGES),
method: HttpMethod.POST,
data: sendData,
multipart: true,
});
return res;
};

+ 47
- 0
src/api/faq.ts View File

@@ -0,0 +1,47 @@
import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "api";
import { getUrl } from "./url";

export type FAQ = {
genre: string | null;
question: string | null;
answer: string | null;
};

// -------FAQ一覧取得---------------
type FAQsResponse = {
data: FAQ[];
} & APICommonResponse;
export const getFAQs = async () => {
const res = await request<FAQsResponse>({
url: getUrl(ApiId.FAQ),
method: HttpMethod.GET,
});
return res;
};

// -------FAQジャンル一覧取得---------------
type FAQGenresResponse = {
data: string[];
} & APICommonResponse;
export const getFAQGenres = async () => {
const res = await request<FAQGenresResponse>({
url: getUrl(ApiId.FAQ_GENRES),
method: HttpMethod.GET,
});
return res;
};

// -------問い合わせ---------------
type AskRequest = {
genre: string;
ask: string;
};

export const ask = async (data: AskRequest) => {
const res = await request({
url: getUrl(ApiId.ASK),
method: HttpMethod.POST,
data: makeParam(data),
});
return res;
};

+ 7
- 0
src/api/index.ts View File

@@ -21,6 +21,13 @@ export const ApiId = {
PARKING_CERTIFICATE_ORDER: id++,
SEASON_TICKET_CONTRACT_TERMINATE_ORDER: id++,
UPDATE_VEHICLE_INFO_ORDER: id++,

FAQ: id++,
FAQ_GENRES: id++,
ASK: id++,

UPLOAD_STUDENT_LICENSE_IMAGES: id++,
UPLOAD_OTHER_LICENSE_IMAGES: id++,
} as const;
export type ApiId = (typeof ApiId)[keyof typeof ApiId];



+ 5
- 0
src/api/url.ts View File

@@ -15,6 +15,11 @@ const urls = {
"season-ticket-contract/termination-order",
[A.UPDATE_VEHICLE_INFO_ORDER]:
"season-ticket-contract/update-vehicle-info-order",
[A.FAQ]: "faq",
[A.FAQ_GENRES]: "faq/genres",
[A.ASK]: "ask",
[A.UPLOAD_STUDENT_LICENSE_IMAGES]: "upload/student-license-images",
[A.UPLOAD_OTHER_LICENSE_IMAGES]: "upload/other-license-images",
};

const prefixs = {


+ 103
- 0
src/components/hook-form/RHFUpload.tsx View File

@@ -0,0 +1,103 @@
import { Box, Button, Stack, Typography, styled } from "@mui/material";
import { useEffect, useRef, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";

interface Props {
name: string;
onChangeFile?: (param: { imageData: string; fileName: string }) => void;
}

export function RHFUpload({ name, onChangeFile }: Props) {
const fileInput = useRef<HTMLInputElement | null>(null);
const [fileName, setFileName] = useState("");
const [imageData, setImageData] = useState("");

const { register, setValue, watch } = useFormContext();

const file: File[] = watch(name);

const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files;
if (!files || files.length <= 0) return;
deployment(files);
};

const { ref, ...rest } = register(name, {
onChange,
});

const selectFile = () => {
if (!fileInput.current) return;
fileInput.current.removeAttribute("capture");
fileInput.current.click();
};
// ファイルを選択した時の処理
const deployment = (files: FileList) => {
const file = files[0];
const fileReader = new FileReader();
setFileName(file.name);
fileReader.onload = () => {
setImageData(fileReader.result as string);
if (onChangeFile) {
onChangeFile({ imageData, fileName });
}
};
fileReader.readAsDataURL(file);
};

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

useEffect(() => {
if (file.length === 0) {
setFileName("");
setImageData("");
}
}, [file]);

return (
<>
<div>
<input
type="file"
id="file"
ref={(e) => {
ref(e);
fileInput.current = e;
}}
accept="image/*"
style={{ display: "none" }}
{...rest}
/>
<button onClick={selectFile} type="button">
📁 ファイルから選択
</button>
</div>
<div>
{fileName && (
<>
<Box position="relative">
<img
src={imageData}
style={{ margin: "auto", maxWidth: "100%" }}
/>
<button
onClick={reset}
type="button"
style={{
position: "absolute",
top: 0,
right: 0,
}}
>
</button>
<div>{fileName}</div>
</Box>
</>
)}
</div>
</>
);
}

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

@@ -1,6 +1,6 @@
import { HasChildren } from "@types";
import { ResultCode } from "api";
import { login as APILogin, logout as APILogout, me } from "api/auth";
import { login as APILogin, logout as APILogout, Me, me } from "api/auth";
import useAPICall from "hooks/useAPICall";
import { createContext, memo, useEffect, useMemo, useState } from "react";

@@ -10,6 +10,7 @@ type Auth = {

name: string;
email: string;
user: Me | null;

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

name: "",
email: "",
user: null,

login: async (email: string, password: string) => false,
logout: () => {},
@@ -33,6 +35,7 @@ function AuthContextProvider({ children }: Props) {
const [initialized, setInitialized] = useState(false);
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [user, setUser] = useState<Me | null>(null);

const authenticated = useMemo(() => {
return !!email;
@@ -46,6 +49,7 @@ function AuthContextProvider({ children }: Props) {

setEmail(data.email);
setName(data.customer_name);
setUser(data);
},
onFailed: () => {
clear();
@@ -99,6 +103,7 @@ function AuthContextProvider({ children }: Props) {
authenticated,
name,
email,
user,

// Func
login,


+ 98
- 0
src/hooks/useUpload.tsx View File

@@ -0,0 +1,98 @@
import { Box, Button, Stack, Typography, styled } from "@mui/material";
import { useRef, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";

interface Props {}

export function useUpload(name: string, {}: Props = {}) {
const fileInput = useRef<HTMLInputElement | null>(null);
const [fileName, setFileName] = useState("");
const [imageData, setImageData] = useState("");

const { register } = useFormContext();

const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files;
if (!files || files.length <= 0) return;
deployment(files);
};

const { ref, ...rest } = register(name, {
onChange,
});

const selectFile = () => {
if (!fileInput.current) return;
fileInput.current.removeAttribute("capture");
fileInput.current.click();
};
// ファイルを選択した時の処理
const deployment = (files: FileList) => {
const file = files[0];
const fileReader = new FileReader();
setFileName(file.name);
fileReader.onload = () => {
setImageData(fileReader.result as string);
};
fileReader.readAsDataURL(file);
};

const reset = () => {
setFileName("");
setImageData("");
if (fileInput.current) {
fileInput.current.value = "";
}
};

const uploadButton = (
<div>
<input
type="file"
id="file"
ref={(e) => {
ref(e);
fileInput.current = e;
}}
accept="image/*"
style={{ display: "none" }}
{...rest}
/>
<button onClick={selectFile} type="button">
📁 ファイルから選択
</button>
</div>
);
const view = (
<div>
{fileName && (
<>
<Box position="relative">
<img src={imageData} style={{ margin: "auto", maxWidth: "100%" }} />
<button
onClick={reset}
style={{
position: "absolute",
top: 0,
right: 0,
}}
>
</button>
<div>{fileName}</div>
</Box>
</>
)}
</div>
);

return {
// element
uploadButton,
view,

// hook
fileName,
imageData,
};
}

+ 3
- 1
src/pages/dashboard/contract/detail.tsx View File

@@ -41,7 +41,9 @@ export default function ContractDetail() {
const { callAPI: callGetPaymentPlans } = useAPICall({
apiMethod: getPaymentPlans,
backDrop: true,
onSuccess: ({ data }) => {},
onSuccess: ({ data }) => {
setPaymentPlans(data);
},
onFailed: () => {
select(null);
moveToList();


+ 22
- 8
src/pages/dashboard/contract/sticker-re-order.tsx View File

@@ -2,11 +2,13 @@ import { Box, Button, Stack, Typography } from "@mui/material";
import { HasChildren } from "@types";
import { reOrderSticker } from "api/season-ticket-contract";
import { FormProvider, RHFTextField } from "components/hook-form";
import { RHFUpload } from "components/hook-form/RHFUpload";
import { useSeasonTicketContractContext } from "contexts/dashboard/SeasonTicketContractContext";
import useAPICall from "hooks/useAPICall";
import useDashboard from "hooks/useDashBoard";
import useNavigateCustom from "hooks/useNavigateCustom";
import useSnackbarCustom from "hooks/useSnackbarCustom";
import { useUpload } from "hooks/useUpload";
import { PageID, TabID } from "pages";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
@@ -24,7 +26,9 @@ function AreaBox({ label, children }: AreaBoxProps) {
);
}

type FormProps = {};
type FormProps = {
upload1: File[];
};

export default function StickerReOrder() {
const { setHeaderTitle, setTabs } = useDashboard(
@@ -32,7 +36,11 @@ export default function StickerReOrder() {
TabID.NONE
);

const form = useForm<FormProps>({});
const form = useForm<FormProps>({
defaultValues: {
upload1: [],
},
});

const { navigateWhenChanged, navigate } = useNavigateCustom();

@@ -53,12 +61,14 @@ export default function StickerReOrder() {
},
});

const handleSubmit = () => {
if (selectedseasonTicketContract === null) return;
callReOrderSticker({
season_ticket_contract_record_no:
selectedseasonTicketContract.season_ticekt_contract_record_no ?? "",
});
const handleSubmit = (data: FormProps) => {
console.log(data);
return;
// if (selectedseasonTicketContract === null) return;
// callReOrderSticker({
// season_ticket_contract_record_no:
// selectedseasonTicketContract.season_ticekt_contract_record_no ?? "",
// });
};

useEffect(() => {
@@ -119,6 +129,10 @@ export default function StickerReOrder() {
maxRows={10}
/>
</AreaBox>

<AreaBox label="アップロード">
<RHFUpload name="upload1" />
</AreaBox>
<Box>
<Button variant="contained" type="submit">
確定


+ 217
- 25
src/pages/dashboard/other/ask.tsx View File

@@ -6,11 +6,49 @@ import {
Box,
Button,
Stack,
Typography,
} from "@mui/material";
import useDashboard from "hooks/useDashBoard";
import { PageID, TabID } from "pages";
import { useEffect } from "react";
import { useEffect, useMemo, useState } from "react";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import useAPICall from "hooks/useAPICall";
import { FAQ, ask, getFAQGenres, getFAQs } from "api/faq";
import useAuth from "hooks/useAuth";
import { FormProvider, RHFSelect, RHFTextField } from "components/hook-form";
import { useForm } from "react-hook-form";
import { HasChildren } from "@types";
import StackRow from "components/stack/StackRow";
import RequireChip from "components/chip/RequireChip";
import { SelectOptionProps } from "components/hook-form/RHFSelect";
import useNavigateCustom from "hooks/useNavigateCustom";
import useSnackbarCustom from "hooks/useSnackbarCustom";

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

type FAQGroup = {
genre: string;
faq: FAQ[];
};

type FormProps = {
genre: string;
ask: string;
};

export default function Ask() {
const { setHeaderTitle, setTabs } = useDashboard(
@@ -18,36 +56,190 @@ export default function Ask() {
TabID.NONE
);

const { authenticated } = useAuth();
const { navigate } = useNavigateCustom();
const { error } = useSnackbarCustom();

const [FAQ, setFAQ] = useState<FAQGroup[]>([]);

const { callAPI: callGetFAQs } = useAPICall({
apiMethod: getFAQs,
backDrop: true,
onSuccess: ({ data }) => {
const list: FAQGroup[] = [];

let tmpGroup: FAQGroup | null = null;

data.forEach((ele) => {
if (tmpGroup === null) {
tmpGroup = {
genre: ele.genre ?? "",
faq: [],
};
}
if (tmpGroup.genre === ele.genre) {
tmpGroup.faq.push(ele);
} else {
list.push(tmpGroup);
tmpGroup = {
genre: ele.genre ?? "",
faq: [ele],
};
}
});

if (tmpGroup !== null) {
list.push(tmpGroup);
}
setFAQ(list);
},
});

const [asking, setAsking] = useState(false);
const [genres, setGenres] = useState<string[]>([]);
const [done, setDone] = useState(false);
const genreOptions: SelectOptionProps[] = useMemo(() => {
return genres.map((ele) => {
return {
label: ele,
value: ele,
};
});
}, [genres]);

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

const { callAPI: callGetFAQGenres } = useAPICall({
apiMethod: getFAQGenres,
backDrop: true,
onSuccess: ({ data }) => {
setGenres(data);
},
});

const { callAPI: callAsk } = useAPICall({
apiMethod: ask,
backDrop: true,
form,
onSuccess: () => {
setDone(true);
},
onFailed: () => {
error("失敗しました");
},
});

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

useEffect(() => {
if (authenticated) {
callGetFAQs({});
}
}, [authenticated]);

useEffect(() => {
setHeaderTitle("問い合わせ");
setTabs(null);
}, [setHeaderTitle, setTabs]);

return (
<Stack>
<Box>よくある質問</Box>
<Box>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
支払をしたか確認をしたい
</AccordionSummary>
<AccordionDetails>
契約一覧より支払状況が確認できます。
</AccordionDetails>
</Accordion>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
解約をしたい
</AccordionSummary>
<AccordionDetails>
契約一覧より解約申請をしてください
</AccordionDetails>
</Accordion>
</Box>
<Box>よくある質問で解決しない場合</Box>
useEffect(() => {
if (asking && genres.length === 0) {
callGetFAQGenres({});
}
}, [asking, genres]);

if (done) {
return (
<Box>
<Button>問い合わせへ進む</Button>
<Typography variant="h6">問い合わせしました。</Typography>
</Box>
</Stack>
);
}

return (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
{!asking && (
<Stack spacing={2}>
<Box>
<Typography variant="h6">よくある質問</Typography>
</Box>
{FAQ.map((group, index) => {
return (
<Box key={index}>
<Typography variant="subtitle1">
----{group.genre}-----
</Typography>
{group.faq.map((ele, index) => {
return (
<Accordion key={index}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
{ele.question}
</AccordionSummary>
<AccordionDetails>
{ele.answer &&
ele.answer.split("\n").map((line, index) => {
return <Typography key={index}>{line}</Typography>;
})}
</AccordionDetails>
</Accordion>
);
})}
</Box>
);
})}
<Box>よくある質問で解決しない場合</Box>
<Box>
<Button
onClick={() => {
setAsking(true);
}}
>
問い合わせへ進む
</Button>
</Box>
</Stack>
)}
{asking && (
<Box sx={{ mt: 1 }}>
<Stack spacing={2}>
<Box>
<Button
onClick={() => {
setAsking(false);
}}
>
戻る
</Button>
</Box>

<AreaBox label="ジャンル" require>
<RHFSelect name="genre" size="small" options={genreOptions} />
</AreaBox>
<AreaBox label="問い合わせ内容" require>
<RHFTextField
name="ask"
size="small"
multiline
minRows={3}
maxRows={10}
/>
</AreaBox>

<Box>
<Button variant="contained" type="submit">
確定
</Button>
</Box>
</Stack>
</Box>
)}
</FormProvider>
);
}

+ 22
- 2
src/pages/dashboard/user/detail.tsx View File

@@ -1,7 +1,9 @@
import { Button, Stack } from "@mui/material";
import useDashboard from "hooks/useDashBoard";
import useNavigateCustom from "hooks/useNavigateCustom";
import { PageID, TabID } from "pages";
import { useEffect } from "react";
import { getPath } from "routes/path";

export default function UserDetail() {
const { setHeaderTitle, setTabs } = useDashboard(
@@ -9,6 +11,8 @@ export default function UserDetail() {
TabID.NONE
);

const { navigateWhenChanged } = useNavigateCustom();

useEffect(() => {
setHeaderTitle("利用者情報");
setTabs(null);
@@ -18,8 +22,24 @@ export default function UserDetail() {
<Stack>
<Button>ユーザー情報変更</Button>
<Button>口座情報変更</Button>
<Button>学生証アップロード</Button>
<Button>障害者手帳アップロード</Button>
<Button
onClick={() => {
navigateWhenChanged(
getPath(PageID.DASHBOARD_USER_STUDENT_LICENSE_IMAGES_UPLOAD)
);
}}
>
学生証アップロード
</Button>
<Button
onClick={() => {
navigateWhenChanged(
getPath(PageID.DASHBOARD_USER_OTHER_LICENSE_IMAGES_UPLOAD)
);
}}
>
障害者手帳アップロード
</Button>
</Stack>
);
}

+ 133
- 0
src/pages/dashboard/user/upload-other-license-images.tsx View File

@@ -0,0 +1,133 @@
import { Box, Button, Stack, Typography } from "@mui/material";
import { HasChildren } from "@types";
import {
uploadOtherLicenseImages,
uploadStudentLicenseImages,
} from "api/customer";
import RequireChip from "components/chip/RequireChip";
import { FormProvider } from "components/hook-form";
import { RHFUpload } from "components/hook-form/RHFUpload";
import StackRow from "components/stack/StackRow";
import useAPICall from "hooks/useAPICall";
import useAuth from "hooks/useAuth";
import useDashboard from "hooks/useDashBoard";
import useSnackbarCustom from "hooks/useSnackbarCustom";
import { PageID, TabID } from "pages";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";

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

type FormProps = {
file1: File[];
file2: File[];
file3: File[];
};

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

const { user } = useAuth();

const [done, setDone] = useState(false);
const { error } = useSnackbarCustom();

const form = useForm<FormProps>({
defaultValues: {
file1: [],
file2: [],
file3: [],
},
});

const { callAPI: callUploadOtherLicenseImages } = useAPICall({
apiMethod: uploadOtherLicenseImages,
backDrop: true,
onSuccess: () => {
setDone(true);
},
onFailed: () => {
error("失敗しました");
},
});

const file1 = form.watch("file1");
const file2 = form.watch("file2");
const file3 = form.watch("file3");

const handleSubmit = (data: FormProps) => {
const files = [...file1, ...file2, ...file3];
callUploadOtherLicenseImages({ images: files });
};

useEffect(() => {
if (file1.length === 0) {
if (file2.length !== 0) {
form.setValue("file2", []);
}
if (file3.length !== 0) {
form.setValue("file3", []);
}
}
if (file2.length === 0) {
if (file3.length !== 0) {
form.setValue("file3", []);
}
}
}, [file1, file2, file3]);

useEffect(() => {
setHeaderTitle("障害者手帳アップロード");
setTabs(null);
}, []);
if (done) {
return <Box>アップロードしました</Box>;
}

return (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Stack spacing={2}>
<AreaBox label="前回アップロード日時">
<Typography>
{user?.other_license_images_upload_datetime ?? "-"}
</Typography>
</AreaBox>
<AreaBox label="1枚目">
<RHFUpload name="file1" />
</AreaBox>
{file1.length !== 0 && (
<AreaBox label="2枚目">
<RHFUpload name="file2" />
</AreaBox>
)}
{file2.length !== 0 && (
<AreaBox label="3枚目">
<RHFUpload name="file3" />
</AreaBox>
)}
<Box>
<Button type="submit" variant="contained">
送信
</Button>
</Box>
</Stack>
</FormProvider>
);
}

+ 130
- 0
src/pages/dashboard/user/upload-student-license-images.tsx View File

@@ -0,0 +1,130 @@
import { Box, Button, Stack, Typography } from "@mui/material";
import { HasChildren } from "@types";
import { uploadStudentLicenseImages } from "api/customer";
import RequireChip from "components/chip/RequireChip";
import { FormProvider } from "components/hook-form";
import { RHFUpload } from "components/hook-form/RHFUpload";
import StackRow from "components/stack/StackRow";
import useAPICall from "hooks/useAPICall";
import useAuth from "hooks/useAuth";
import useDashboard from "hooks/useDashBoard";
import useSnackbarCustom from "hooks/useSnackbarCustom";
import { PageID, TabID } from "pages";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";

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

type FormProps = {
file1: File[];
file2: File[];
file3: File[];
};

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

const { user } = useAuth();

const [done, setDone] = useState(false);
const { error } = useSnackbarCustom();

const form = useForm<FormProps>({
defaultValues: {
file1: [],
file2: [],
file3: [],
},
});

const { callAPI: callUploadStudentLicenseImages } = useAPICall({
apiMethod: uploadStudentLicenseImages,
backDrop: true,
onSuccess: () => {
setDone(true);
},
onFailed: () => {
error("失敗しました");
},
});

const file1 = form.watch("file1");
const file2 = form.watch("file2");
const file3 = form.watch("file3");

const handleSubmit = (data: FormProps) => {
const files = [...file1, ...file2, ...file3];
callUploadStudentLicenseImages({ images: files });
};

useEffect(() => {
if (file1.length === 0) {
if (file2.length !== 0) {
form.setValue("file2", []);
}
if (file3.length !== 0) {
form.setValue("file3", []);
}
}
if (file2.length === 0) {
if (file3.length !== 0) {
form.setValue("file3", []);
}
}
}, [file1, file2, file3]);

useEffect(() => {
setHeaderTitle("学生証画像アップロード");
setTabs(null);
}, []);
if (done) {
return <Box>アップロードしました</Box>;
}

return (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Stack spacing={2}>
<AreaBox label="前回アップロード日時">
<Typography>
{user?.student_license_images_upload_datetime ?? "-"}
</Typography>
</AreaBox>
<AreaBox label="1枚目">
<RHFUpload name="file1" />
</AreaBox>
{file1.length !== 0 && (
<AreaBox label="2枚目">
<RHFUpload name="file2" />
</AreaBox>
)}
{file2.length !== 0 && (
<AreaBox label="3枚目">
<RHFUpload name="file3" />
</AreaBox>
)}
<Box>
<Button type="submit" variant="contained">
送信
</Button>
</Box>
</Stack>
</FormProvider>
);
}

+ 2
- 0
src/pages/index.ts View File

@@ -18,6 +18,8 @@ export const PageID = {
DASHBOARD_RECEIPT_DOWNLOAD: id++,

DASHBOARD_USER_DETAIL: id++,
DASHBOARD_USER_STUDENT_LICENSE_IMAGES_UPLOAD: id++,
DASHBOARD_USER_OTHER_LICENSE_IMAGES_UPLOAD: id++,

DASHBOARD_ASK: id++,



+ 4
- 0
src/routes/path.ts View File

@@ -50,6 +50,10 @@ const PATHS_DASHBOARD = {
[makePathKey(PageID.DASHBOARD_RECEIPT_DOWNLOAD)]:
"/dashboard/receipt/download",
[makePathKey(PageID.DASHBOARD_USER_DETAIL)]: "/dashboard/user/detail",
[makePathKey(PageID.DASHBOARD_USER_STUDENT_LICENSE_IMAGES_UPLOAD)]:
"/dashboard/user/upload/student-license",
[makePathKey(PageID.DASHBOARD_USER_OTHER_LICENSE_IMAGES_UPLOAD)]:
"/dashboard/user/upload/other-license",
[makePathKey(PageID.DASHBOARD_ASK)]: "/dashboard/ask",
};



+ 14
- 0
src/routes/sub/dashboard.tsx View File

@@ -23,6 +23,12 @@ export default function DashboardRoutes(): RouteObject[] {
const UserDetail = Loadable(
lazy(() => import("pages/dashboard/user/detail"))
);
const StudentLicenseImagesUpload = Loadable(
lazy(() => import("pages/dashboard/user/upload-student-license-images"))
);
const OtherLicenseImagesUpload = Loadable(
lazy(() => import("pages/dashboard/user/upload-other-license-images"))
);
const Ask = Loadable(lazy(() => import("pages/dashboard/other/ask")));

const allChildren = [
@@ -42,6 +48,14 @@ export default function DashboardRoutes(): RouteObject[] {
pageId: PageID.DASHBOARD_USER_DETAIL,
element: <UserDetail />,
},
{
pageId: PageID.DASHBOARD_USER_STUDENT_LICENSE_IMAGES_UPLOAD,
element: <StudentLicenseImagesUpload />,
},
{
pageId: PageID.DASHBOARD_USER_OTHER_LICENSE_IMAGES_UPLOAD,
element: <OtherLicenseImagesUpload />,
},
{
pageId: PageID.DASHBOARD_ASK,
element: <Ask />,


Loading…
Cancel
Save