Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:部門の一括登録をできるようにした #112

Merged
merged 25 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7cf87ca
feat;一括エントリーのページを追加した
Suzune2741 Jul 15, 2024
4f4649a
feat: CSVをドロップしたときにその内容を表示させるようにした
Suzune2741 Jul 18, 2024
a145e69
feat:データチェックの関数とJsonに変換する関数を作成wip
Suzune2741 Jul 18, 2024
92bcb99
feat:entrytypeの型を書いた
Suzune2741 Jul 19, 2024
60e5598
feat: CSVに間違いがあるとセルが赤くなるようにした
Suzune2741 Jul 26, 2024
c9e305d
fix: ファイル名をbulkEntryからenrtyBulkに変更.
Suzune2741 Jul 30, 2024
defd31a
Merge branch 'main' of https://github.com/poporonnet/kcmsx into feat/…
Suzune2741 Jul 30, 2024
e5d928a
fix: コンポーネントの名前をEntryBulkに変更
Suzune2741 Jul 30, 2024
9dd6c69
fix:routerのpathを間違えていたので修正
Suzune2741 Jul 30, 2024
ccc061e
fix:mainをmergeしたときに外れていた依存の追加
Suzune2741 Aug 1, 2024
dc66af5
chore:routerのpathをいい感じにした
Suzune2741 Aug 1, 2024
ed3f3af
refactor: 変数名の変更,disabledにiserrorを渡すようにした
Suzune2741 Aug 1, 2024
1fa2014
refactor: エラーメッセージをオブジェクトにした
Suzune2741 Aug 5, 2024
8e7b19f
feat: homeに一括登録へのリンクを追加した
Suzune2741 Aug 5, 2024
fa55808
feat: READMEに利用者向け情報の追加
Suzune2741 Aug 5, 2024
1985ea1
refactor: checkするときの条件式を修正
Suzune2741 Aug 5, 2024
223687f
feat: 複数回登録ボタンを押せなくした.
Suzune2741 Aug 5, 2024
ba1f18f
feat: データチェックの時に最初にアサーションをするようにした
Suzune2741 Aug 5, 2024
c30a8d8
Merge branch 'main' into feat/51-bulk-registration
Suzune2741 Aug 5, 2024
8c286ea
Merge branch 'main' of https://github.com/poporonnet/kcmsx into feat/…
Suzune2741 Aug 5, 2024
63ef01e
fix: EntryTableをコンポーネントにした,エラーメッセージを変えた
Suzune2741 Aug 7, 2024
3d98750
Merge branch 'feat/51-bulk-registration' of https://github.com/poporo…
Suzune2741 Aug 7, 2024
4d8059c
chore(format): formatをかけた
Suzune2741 Aug 7, 2024
b32f9f9
Merge branch 'main' into feat/51-bulk-registration
Suzune2741 Aug 7, 2024
224f797
Merge branch 'main' into feat/51-bulk-registration
kiharu3112 Aug 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/kcmsf/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"dependencies": {
"@mantine/carousel": "^7.3.1",
"@mantine/core": "^7.3.1",
"@mantine/dropzone": "^7.11.2",
"@mantine/hooks": "^7.3.1",
"@mantine/notifications": "7.11.1",
"@tabler/icons-react": "^3.0.0",
Expand Down
241 changes: 241 additions & 0 deletions packages/kcmsf/src/pages/bulkEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import { Box, Button, Group, Table, Text, rem } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { notifications } from "@mantine/notifications";
import {
IconFileTypeCsv,
IconSend,
IconUpload,
IconX,
} from "@tabler/icons-react";
import { useEffect, useState } from "react";
import { Entry as entryType } from "../types/entry";
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved

const ErrorMessage = (errorNum: number) => {
const message: string[] = [
"チーム名が短すぎます。",
"メンバーの名前が短すぎます。",
"ロボットのカテゴリーは車輪型または歩行型にしてください。",
"部門が不正です。",
];
notifications.show({
title: "登録失敗",
message: "登録に失敗しました。" + message[errorNum],
color: "red",
});
};
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved

export const BulkEntry = () => {
const [csvData, setCsvData] = useState<string[][]>([]);
const [error, setError] = useState<boolean>(true);
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved
const [errors, setErrors] = useState<boolean[][]>([]);

useEffect(() => {
if (csvData.length > 0) {
const newErrors = checkData(csvData);
setErrors(newErrors);
}
}, [csvData]);

const handleDrop = (files: File[]) => {
const file = files[0];
if (file) {
const reader = new FileReader();
reader.onload = () => {
const text = reader.result as string;
const data = parseCSV(text);
setCsvData(data);
};
reader.readAsText(file);
}
};
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved

const parseCSV = (text: string): string[][] => {
const rows = text
.replace(/\r\n/g, "\n")
.split("\n")
.map((row) => row.split(","));
return rows;
};
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved

const checkData = (data: string[][]): boolean[][] => {
const newErrors = data.map((row) => new Array(row.length).fill(false));

data.forEach((row, i) => {
const [teamName, member1, member2, isMultiWalk, category] = row;
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved
if (teamName === undefined || teamName === "") {
ErrorMessage(0);
newErrors[i][0] = true;
setError(false);
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved
}
if (member1 === undefined || member1.length < 2) {
ErrorMessage(1);
newErrors[i][1] = true;
}
if (
member2 === undefined ||
(member2.length < 2 && member2.length !== 0)
) {
ErrorMessage(1);
newErrors[i][2] = true;
}
if (
isMultiWalk === undefined ||
(isMultiWalk !== "歩行型" && isMultiWalk !== "車輪型")
) {
ErrorMessage(2);
newErrors[i][3] = true;
setError(false);
}
if (
category === undefined ||
(category !== "Elementary" && category !== "Open")
) {
ErrorMessage(3);
newErrors[i][4] = true;
setError(false);
}
});

return newErrors;
};

const sendData = () => {
const data = csvData.map((row) => {
const entry: entryType = {
teamName: row[0],
members: [row[1], row[2]],
isMultiWalk: row[3] === "歩行型" ? true : false,
category: row[4] as "Elementary" | "Open",
};
return entry;
});
const json = JSON.stringify(data);
const obj = JSON.parse(json);
return obj;
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved
};

const clear = () => {
setCsvData([]);
};

const showDetails = (data: string[][], errors: boolean[][]) => {
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved
return (
<Box>
<Table>
<Table.Thead>
<Table.Tr>
<Table.Th>チーム名</Table.Th>
<Table.Th>メンバー1</Table.Th>
<Table.Th>メンバー2</Table.Th>
<Table.Th>歩行型</Table.Th>
<Table.Th>部門</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{data.map((row, i) => (
<Table.Tr key={i}>
{i === 0
? null
: row.map((cell, j) => (
<Table.Td
ta={"left"}
key={j}
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved
style={{
backgroundColor:
errors[i] && errors[i][j] ? "#EC777E" : "inherit",
}}
>
{cell}
</Table.Td>
))}
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Box>
);
};

return (
<>
<h1>一括エントリー</h1>
{csvData.length > 0 ? (
<>
<p>この内容で登録します</p>
<Box>{showDetails(csvData, errors)}</Box>
<Button m={"2rem"} onClick={clear} variant="default">
リセット
</Button>
{error ? (
<Button m={"2rem"} onClick={sendData} >
<IconSend stroke={2} />
登録
</Button>
) : (
<Button m={"2rem"} onClick={sendData} disabled>
<IconSend stroke={2} />
登録
</Button>
)}
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved
</>
) : (
<Box maw={620} mx={"auto"} bd="3px solid gray.6" p={rem(10)}>
<Dropzone
onDrop={handleDrop}
onReject={() => (
<Text c={"red"} fw={700}>
CSVファイル以外のものは登録できません。
</Text>
)}
maxSize={5 * 1024 ** 2}
accept={["text/csv"]}
>
<Group
justify="center"
gap="xl"
mih={220}
style={{ pointerEvents: "none" }}
>
<Dropzone.Accept>
<IconUpload
style={{
width: rem(52),
height: rem(52),
color: "var(--mantine-color-blue-6)",
}}
stroke={1.5}
/>
</Dropzone.Accept>
<Dropzone.Reject>
<IconX
style={{
width: rem(52),
height: rem(52),
color: "var(--mantine-color-red-6)",
}}
stroke={1.5}
/>
</Dropzone.Reject>
<Dropzone.Idle>
<IconFileTypeCsv
style={{
width: rem(52),
height: rem(52),
color: "var(--mantine-color-dimmed)",
}}
stroke={1.5}
/>
</Dropzone.Idle>

<div>
<Text size="xl" inline>
ここにCSVファイルをドラッグ&ドロップしてください
</Text>
</div>
</Group>
</Dropzone>
</Box>
)}
</>
);
};
2 changes: 2 additions & 0 deletions packages/kcmsf/src/routes/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
createBrowserRouter,
createRoutesFromElements,
} from "react-router-dom";
import { BulkEntry } from "../pages/bulkEntry.tsx";
import { Entry } from "../pages/entry.tsx";
import { EntryList } from "../pages/entryList.tsx";
import { Home } from "../pages/home.tsx";
Expand All @@ -19,6 +20,7 @@ export const router = createBrowserRouter(
<Route path="/entrylist" element={<EntryList />} />
<Route path="/ranking" element={<Ranking />} />
<Route path="/entry" element={<Entry />} />
<Route path="/entry/bulk" element={<BulkEntry />} />
Suzune2741 marked this conversation as resolved.
Show resolved Hide resolved
<Route path="/matchlist" element={<MatchList />} />
<Route path="/match" element={<Match />} />
<Route path="/result" element={<Result />} />
Expand Down
6 changes: 6 additions & 0 deletions packages/kcmsf/src/types/entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type Entry = {
teamName: string;
members: string[];
isMultiWalk: boolean;
category: "Elementary" | "Open";
};
Loading