diff --git a/app/(authenticated)/league/page.tsx b/app/(authenticated)/league/page.tsx deleted file mode 100644 index f39e61e..0000000 --- a/app/(authenticated)/league/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import {Stack, Grid, Typography, Link, Breadcrumbs} from "@mui/material"; -import CardBackground from "@/components/layout/cardBackground"; -import {sportFactory} from "@/src/models/SportModel"; -import LeagueSportsList from "@/components/league/leagueSportsList"; - - -export default async function LeaguePage() { - const sports = await sportFactory().index() - return ( - - - - 管理者のダッシュボード - - リーグ管理 - - - - - - - - ); -} diff --git a/app/(authenticated)/locations/page.tsx b/app/(authenticated)/locations/page.tsx index e8973bb..10f93c6 100644 --- a/app/(authenticated)/locations/page.tsx +++ b/app/(authenticated)/locations/page.tsx @@ -1,6 +1,5 @@ import {Breadcrumbs, Link, Stack, Typography} from "@mui/material"; import CardBackground from "@/components/layout/cardBackground"; -import TeamsAgGrid from "@/components/teams/teamsTable"; export default function LocationPage() { return ( @@ -12,7 +11,7 @@ export default function LocationPage() { 場所管理 - none + この機能は開発中です。 ) diff --git a/app/(authenticated)/page.tsx b/app/(authenticated)/page.tsx index 559ba9d..204f1e0 100644 --- a/app/(authenticated)/page.tsx +++ b/app/(authenticated)/page.tsx @@ -12,17 +12,17 @@ export default async function Home() { 管理者のダッシュボード - - - - - この機能は開発中です - + + + + + + + + + 管理者のダッシュボード + + + 競技管理 + + + {sport.name} + + + {game.name}(ID:{gameId}) + + 試合(ID:{match.id}) + + + + + + ) +} \ No newline at end of file diff --git a/app/(authenticated)/sports/[id]/[gameId]/page.tsx b/app/(authenticated)/sports/[id]/[gameId]/page.tsx new file mode 100644 index 0000000..bde3291 --- /dev/null +++ b/app/(authenticated)/sports/[id]/[gameId]/page.tsx @@ -0,0 +1,36 @@ +import CardBackground from "@/components/layout/cardBackground"; +import {Stack, Grid, Link, Typography, Breadcrumbs} from "@mui/material"; +import CardList from "@/components/layout/cardList"; +import {sportFactory} from "@/src/models/SportModel"; +import {gameFactory} from "@/src/models/GameModel"; + +export default async function GamePage({params}: { params: { gameId:string, id: string } }) { + const gameId = parseInt(params.gameId, 10) + const game = await gameFactory().show(gameId) + const sportId = parseInt(params.id, 10) + const sport = await sportFactory().show(sportId) + + return( + + + + 管理者のダッシュボード + + + 競技管理 + + + {sport.name} + + {game.name}(ID:{gameId}) + + + + + + + + + + ) +} \ No newline at end of file diff --git a/app/(authenticated)/league/[id]/page.tsx b/app/(authenticated)/sports/[id]/league/page.tsx similarity index 64% rename from app/(authenticated)/league/[id]/page.tsx rename to app/(authenticated)/sports/[id]/league/page.tsx index 4347fa5..2806e9e 100644 --- a/app/(authenticated)/league/[id]/page.tsx +++ b/app/(authenticated)/sports/[id]/league/page.tsx @@ -3,19 +3,23 @@ import CardBackground from "@/components/layout/cardBackground"; import LeagueDnd from "@/components/league/leagueDnd"; import {sportFactory} from "@/src/models/SportModel"; -export default async function LeagueTestPage({params}: { params: { id: string } }) { +export default async function LeaguePage({params}: { params: { id: string } }) { const sportId = parseInt(params.id, 10) const sport = await sportFactory().show(sportId) + const sportLink = `/sports/${sportId}` return ( 管理者のダッシュボード - - リーグ管理 + + 競技管理 - {sport.name} + + {sport.name} + + リーグを作成・編集 diff --git a/app/(authenticated)/sports/[id]/page.tsx b/app/(authenticated)/sports/[id]/page.tsx index 468a3bb..bc57aec 100644 --- a/app/(authenticated)/sports/[id]/page.tsx +++ b/app/(authenticated)/sports/[id]/page.tsx @@ -1,6 +1,5 @@ import CardBackground from "@/components/layout/cardBackground"; import {Stack, Grid, Link, Typography, Breadcrumbs} from "@mui/material"; -import {ButtonLarge} from "@/components/layout/buttonLarge"; import CardList from "@/components/layout/cardList"; import {sportFactory} from "@/src/models/SportModel"; import SportEditor from "@/components/sports/sportEditor"; @@ -12,6 +11,7 @@ export default async function SportPage({params}: { params: { id: string } }) { const sport = await sportFactory().show(sportId) const games = await gameFactory().index() const filteredGames = games.filter((game) => game.sportId == sportId) + const gameType = filteredGames.find(game => game)?.type; return( @@ -24,15 +24,24 @@ export default async function SportPage({params}: { params: { id: string } }) { {sport.name} + {gameType === "league" && ( + + + + + + )} + {gameType === "tournament" && ( + + + + + + )} - - - - - - + diff --git a/app/(authenticated)/tournament/[id]/page.tsx b/app/(authenticated)/sports/[id]/tournament/page.tsx similarity index 100% rename from app/(authenticated)/tournament/[id]/page.tsx rename to app/(authenticated)/sports/[id]/tournament/page.tsx diff --git a/app/(authenticated)/tournament/page.tsx b/app/(authenticated)/tournament/page.tsx deleted file mode 100644 index 98e6c58..0000000 --- a/app/(authenticated)/tournament/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import {Stack, Grid, Typography, Link, Breadcrumbs} from "@mui/material"; -import CardBackground from "@/components/layout/cardBackground"; -import CardLarge from "@/components/layout/cardLarge"; -import {ButtonLarge} from "@/components/layout/buttonLarge"; - -export default function LeaguePage() { - return ( - - - - 管理者のダッシュボード - - トーナメント管理 - - - - - フットサル - - - - - ); -} diff --git a/components/layout/navigation.tsx b/components/layout/navigation.tsx index 9674d2b..0b9ca9f 100644 --- a/components/layout/navigation.tsx +++ b/components/layout/navigation.tsx @@ -8,8 +8,6 @@ import { HiMiniNewspaper, HiUser, HiUserGroup, - HiTableCells, - HiRectangleGroup, HiTrophy, HiMapPin, HiIdentification } from "react-icons/hi2"; @@ -101,107 +99,78 @@ export const Navigation = () => { - ユーザー + スポーツ - - 競技 + 情報 - - - - - 編成 + diff --git a/components/league/leagueSportsList.tsx b/components/league/leagueSportsList.tsx deleted file mode 100644 index 0e180f7..0000000 --- a/components/league/leagueSportsList.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import {ButtonLarge} from "@/components/layout/buttonLarge"; -import {Sport} from "@/src/models/SportModel"; - -export type SportsListProps = { - sports: Sport[] -} - -export default function LeagueSportsList(props: SportsListProps) { - props.sports.sort((a, b) => b.weight - a.weight); - return ( - <> - {props.sports.map((sport) => ( - - {sport.name} - - ))} - - ) -} \ No newline at end of file diff --git a/components/match/matchEditor.tsx b/components/match/matchEditor.tsx new file mode 100644 index 0000000..daa9197 --- /dev/null +++ b/components/match/matchEditor.tsx @@ -0,0 +1,499 @@ +'use client' +import React, {useRef} from "react"; +import {useAsync} from "react-use"; +import {useRouter} from "next/navigation"; +import { + Accordion, + AccordionDetails, + AccordionSummary, + Alert, + Button, + Card, Divider, FormControl, + InputLabel, + MenuItem, + Select, + Snackbar, + Stack, + TextField, + TextFieldProps, + Typography, + ToggleButton, + ToggleButtonGroup, Chip, SvgIcon, Avatar +} from "@mui/material"; +import CardBackground from "@/components/layout/cardBackground"; +import {HiCheck, HiChevronDown, HiArrowPath, HiFlag, HiMapPin, HiClock} from "react-icons/hi2"; + +import {Sport} from "@/src/models/SportModel"; +import {Game} from "@/src/models/GameModel"; +import {Match, matchFactory, MatchResult, MatchStatus} from "@/src/models/MatchModel"; +import {Team, teamFactory} from "@/src/models/TeamModel"; +import {Location, locationFactory} from "@/src/models/LocationModel"; +import Loading from "@/app/(authenticated)/loading"; +import Link from "next/link"; + +export type MatchEditorProps = { + sport: Sport + game: Game + match: Match +} + +export default function MatchEditor(props: MatchEditorProps) { + const router = useRouter() + const useState = React.useState; + + const teamUndefined = { + id: 0, + name: "取得中", + description: "取得中", + classId: 0, + teamTagId: 0, + userIds: [], + enteredGameIds: [], + createdAt: "0", + updatedAt: "0" + }; + + // state + const [isFetching, setIsFetching] = useState(true) + const [leftTeam, setLeftTeam] = useState(teamUndefined); + const [rightTeam, setRightTeam] = useState(teamUndefined); + const [judgeTeam, setJudgeTeam] = useState(teamUndefined); + const [matchResult, setMatchResult] = useState(props.match.result); + const [matchStatus, setMatchStatus] = useState(props.match.status); + const [locations, setLocations] = useState([]); + const [locationId, setLocationId] = useState(props.match.locationId); + const [teams, setTeams] = useState([]); + const [judgeTeamId, setJudgeTeamId] = useState(props.match.judgeTeamId); + const [updateSnackOpen, setUpdateSnackOpen] = React.useState(false) + const [cancelSnackOpen, setCancelSnackOpen] = React.useState(false) + const [scoreError, setScoreError] = useState(false); + const leftRef = useRef(null) + const rightRef = useRef(null) + const noteRef = useRef(null) + + + // fetch data + useAsync(async () => { + const fetchMatchEditor = async () => { + const left = await teamFactory().show(Number(props.match.leftTeamId)); + const right = await teamFactory().show(Number(props.match.rightTeamId)); + const judge = await teamFactory().show(Number(props.match.judgeTeamId)); + const location = await locationFactory().index(); + const teams = await teamFactory().index(); + setLeftTeam(left); + setRightTeam(right); + setJudgeTeam(judge); + setLocations(location); + setTeams(teams); + setIsFetching(false); + }; + + return fetchMatchEditor(); + }) + + // reformat data + const locationName = locations.find(location => location.id === props.match.locationId)?.name; + const date = new Date(props.match.startAt); + const formattedDate = `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}時${date.getMinutes() < 10 ? '0' : ''}${date.getMinutes()}分`; + + // handler + const handleUpdateSnackClose = () => { + setUpdateSnackOpen(false) + } + const handleCancelSnackClose = () => { + setCancelSnackOpen(false) + } + const handleResultChange = ( + event: React.MouseEvent, + newResult: string, + ) => { + setMatchResult(newResult as MatchResult); + }; + const handleStatusChange = ( + event: React.MouseEvent, + newStatus: string, + ) => { + setMatchStatus(newStatus as MatchStatus); + }; + const handleCompare = () => { + const leftScore = Number(leftRef.current?.value); + const rightScore = Number(rightRef.current?.value); + + if (leftScore !== 0 || rightScore !== 0) { + setMatchStatus('finished'); + } else { + setMatchStatus('standby'); + } + + if (leftScore > rightScore) { + setMatchResult('left_win'); + } else if (leftScore < rightScore) { + setMatchResult('right_win'); + } else { + setMatchResult('draw'); + } + setScoreError(false) + } + + // cancel + const handleCancel = () => { + setMatchResult(props.match.result) + setMatchStatus(props.match.status) + setLocationId(props.match.locationId) + setJudgeTeamId(props.match.judgeTeamId) + noteRef.current!.value = props.match.note + leftRef.current!.value = props.match.leftScore + rightRef.current!.value = props.match.rightScore + setCancelSnackOpen(true) + } + + // update data + const handleUpdate = async () => { + const leftScoreValue = leftRef.current?.value; + const rightScoreValue = rightRef.current?.value; + + if (!leftScoreValue || !rightScoreValue || isNaN(Number(leftScoreValue)) || isNaN(Number(rightScoreValue || Number(leftScoreValue) < 0 || Number(rightScoreValue) < 0))) { + setScoreError(true); + return; + } + + const leftScore = Number(leftScoreValue); + const rightScore = Number(rightScoreValue); + + await matchFactory().update(props.match.id, { + locationId: locationId, + gameId: props.match.gameId, + sportId: props.match.sportId, + startAt: props.match.startAt, + leftTeamId: props.match.leftTeamId, + rightTeamId: props.match.rightTeamId, + leftScore: leftScore, + rightScore: rightScore, + result: matchResult, + status: matchStatus, + note: noteRef.current?.value as string, + judgeTeamId: judgeTeamId + }) + + router.refresh() + setUpdateSnackOpen(true) + setScoreError(false) + } + + if (isFetching) { + return ( + + ) + } else { + return ( + + + + + + + } color={"secondary"} + /> + } color={"secondary"} + /> + } color={"secondary"} + /> + + + + + + + {leftTeam.name}のスコア + + + + VS + + + {rightTeam.name}のスコア + + + + + + + + + 勝ったのは + + {leftTeam.name} + 引き分け + {rightTeam.name} + + + + 試合の状態 + + 中止 + スタンバイ + 進行中 + 完了 + + + + + + + + + + + + + + + } + aria-controls="panel-content" + id="panel-header" + > + 編集する + + + + + 補足 + + + 審判 + + 審判 + + + + 試合の場所 + + 場所 + + + + + + + + + + + + + + + + + + + 変更が保存されました + + + + + + + + + + + + + 変更を元に戻しました + + + + + + + ) + } +} \ No newline at end of file diff --git a/components/sports/leagueList.tsx b/components/sports/leagueList.tsx index bb27b74..298fd11 100644 --- a/components/sports/leagueList.tsx +++ b/components/sports/leagueList.tsx @@ -3,9 +3,11 @@ import {Game} from "@/src/models/GameModel"; export type LeagueListProps = { games: Game[] + sportId: number } export default function LeagueList(props: LeagueListProps) { + const sportId = props.sportId return ( @@ -14,7 +16,7 @@ export default function LeagueList(props: LeagueListProps) { {game.name} diff --git a/components/sports/sportEditor.tsx b/components/sports/sportEditor.tsx index 1386b7e..efce5ae 100644 --- a/components/sports/sportEditor.tsx +++ b/components/sports/sportEditor.tsx @@ -67,7 +67,7 @@ export default function SportEditor(props: SportEditorProps) { <> - 競技の情報 + 競技を編集する