diff --git a/app/(authenticated)/teams/[id]/page.tsx b/app/(authenticated)/teams/[id]/page.tsx index 4da4fba..361ce10 100644 --- a/app/(authenticated)/teams/[id]/page.tsx +++ b/app/(authenticated)/teams/[id]/page.tsx @@ -8,7 +8,8 @@ import {classFactory} from "@/src/models/ClassModel"; export default async function TeamDetailPage({ params }: { params: { id: string } }) { const teamId = parseInt(params.id, 10) const teamInfo = await teamFactory().show(teamId) - const classes = await classFactory().show(teamInfo.classId) + const classModel = await classFactory().show(teamInfo.classId) + const classUsers = await classFactory().getUsers(teamInfo.classId) const teamUsers = await teamFactory().getTeamUsers(teamId); return ( @@ -17,7 +18,7 @@ export default async function TeamDetailPage({ params }: { params: { id: string 管理者のダッシュボード - + チーム管理 {teamInfo.name} @@ -27,7 +28,8 @@ export default async function TeamDetailPage({ params }: { params: { id: string diff --git a/components/teams/addTeamMemberDialog.tsx b/components/teams/addTeamMemberDialog.tsx new file mode 100644 index 0000000..2908d91 --- /dev/null +++ b/components/teams/addTeamMemberDialog.tsx @@ -0,0 +1,131 @@ +'use client' +import {AgGridReact} from 'ag-grid-react'; +import "ag-grid-community/styles/ag-grid.css"; // Mandatory CSS required by the grid +import "ag-grid-community/styles/ag-theme-quartz.css"; // Optional Theme applied to the grid +import {ColDef, GridApi, GridReadyEvent, ModuleRegistry} from 'ag-grid-community'; +import {ClientSideRowModelModule} from 'ag-grid-community'; +import {Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography} from "@mui/material"; +import {User} from "@/src/models/UserModel"; +import {Team, teamFactory} from "@/src/models/TeamModel"; +import {useState} from "react"; +import {useRouter} from "next/navigation"; + +ModuleRegistry.registerModules([ClientSideRowModelModule]); + +export type AddTeamMemberDialogProps = { + isOpen: boolean + setClose: () => void + team: Team + users: User[] +} + +// Row Data Interface +type IRow = { + id: number, + username: string, + gender: string, + emailAccountName: string, +} + +export default function AddTeamMemberDialog(props: AddTeamMemberDialogProps) { + const router = useRouter() + const height = 'calc(100vh - 230px)'; + const [gridApi, setGridApi] = useState | null>(null) + // Column Definitions: Defines & controls grid columns. + const colDefs: ColDef[] = [ + { + field: "id", + headerName: "ID", + headerCheckboxSelection: true, + checkboxSelection: true, + showDisabledCheckboxes: true, + }, + {field: "username", headerName: "ユーザー名"}, + {field: "gender", headerName: "性別"}, + {field: "emailAccountName", headerName: "学籍番号"}, + ] + + const rowData: IRow[] = props.users.map((user) => { + return { + id: user.id, + username: user.name, + gender: user.gender == "male" ? "男" : "女", + emailAccountName: user.email.split("@")[0] + } as IRow + }) + + const handleGridReady = (params: GridReadyEvent) => { + setGridApi(params.api) + } + + const handleSubmit = async () => { + if (!gridApi) { + alert("エラーが発生しました") + return + } + + const selectedRowIds = gridApi + .getSelectedRows() + .map((row) => row.id) + + if (selectedRowIds.length === 0) { + alert("ユーザーを選択してください") + return + } + + // add team user + await teamFactory().addTeamUsers(props.team.id, selectedRowIds) + + // refresh + router.refresh() + // close + props.setClose() + } + + return ( + + + + チームメンバーの追加 + + + +
+ +
+
+ + + + +
+ ) +} \ No newline at end of file diff --git a/components/teams/teamEditor.tsx b/components/teams/teamEditor.tsx index 18f0719..cab1f7a 100644 --- a/components/teams/teamEditor.tsx +++ b/components/teams/teamEditor.tsx @@ -2,27 +2,30 @@ import { Box, Button, - FormControl, InputLabel, Paper, + FormControl, InputLabel, Paper, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography } from "@mui/material"; -import { HiCheck } from "react-icons/hi2"; +import {HiCheck, HiMiniTrash, HiPlus} from "react-icons/hi2"; import React, {useState} from "react"; import {useRouter} from "next/navigation"; import {Team, teamFactory} from "@/src/models/TeamModel"; import {Class} from "@/src/models/ClassModel"; import {User} from "@/src/models/UserModel"; import TeamDelete from "@/components/teams/teamDelete"; +import AddTeamMemberDialog from "@/components/teams/addTeamMemberDialog"; type TeamEditorProps = { - class : Class; - team : Team; - teamUser : User[]; + class: Class; + team: Team; + teamUser: User[]; + classUsers: User[]; } export default function TeamEditor(props: TeamEditorProps) { const router = useRouter() const [teamName, setTeamName] = useState(props.team.name) + const [isOpenTeamMemberAddDialog, setIsOpenTeamMemberAddDialog] = useState(false) const handleSubmit = async () => { await teamFactory().update(props.team.id, { @@ -37,6 +40,16 @@ export default function TeamEditor(props: TeamEditorProps) { } + const removeUser = async (userId: number) => { + await teamFactory().removeTeamUser(props.team.id, userId) + // refresh + router.refresh() + } + + const notTeamUsers = props.classUsers.filter((user) => { + return !props.teamUser.some((teamUser) => teamUser.id === user.id) + }) + return ( <> @@ -49,7 +62,7 @@ export default function TeamEditor(props: TeamEditorProps) { { @@ -82,14 +95,14 @@ export default function TeamEditor(props: TeamEditorProps) { > 所属クラス - + {props.class.name} - + - + - 学籍番号 - 名前 - 性別 + 学籍番号 + 名前 + 性別 + 削除 {props.teamUser.map((member) => { return ( - {member.id} - {member.name} - + {member.id} + {member.name} + {member.gender === "male" ? "男性" : "女性"} + + + ); })} @@ -135,6 +160,27 @@ export default function TeamEditor(props: TeamEditorProps) { + { + setIsOpenTeamMemberAddDialog(false) + }} + team={props.team} + users={notTeamUsers} + /> + + +