Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions app/(public)/kamp/[id]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


export default function Loading() {
return (
"Laster kamp ..."
)
}
38 changes: 29 additions & 9 deletions app/(public)/kamp/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { currentUser } from "@/lib/auth";
import { UserRole } from "@prisma/client";
import { EditMatchEventsModal } from "@/app/components/matches/EditMatchEventsModal";
import { getClubPlayers } from "@/data/getClubPlayers";
import { Suspense } from "react";
import { EditMatchSquadModal } from "@/app/components/matches/EditMatchSquadModal";
import { EditMatchGoalsModal } from "@/app/components/matches/EditMatchGoalsModal";

type MatchPageProps = {
params: { id: string };
Expand All @@ -43,9 +46,13 @@ const MatchPage = async ({ params: params }: MatchPageProps) => {
const awayClubPlayers = await getClubPlayers(match.awayClubId);

const canEditHome =
user && (user.role === UserRole.ADMIN || user.club === match.homeClubId);
user &&
(user.role === UserRole.ADMIN || user.club === match.homeClubId) &&
!match.isMatchEventsConfirmed;
const canEditAway =
user && (user.role === UserRole.ADMIN || user.club === match.awayClubId);
user &&
(user.role === UserRole.ADMIN || user.club === match.awayClubId) &&
!match.isMatchEventsConfirmed;

return (
<Card className="flex flex-col justify-center space-y-4 w-full px-1 sm:px-6 py-4 max-w-3xl">
Expand All @@ -57,26 +64,39 @@ const MatchPage = async ({ params: params }: MatchPageProps) => {
!isFuture(match.kickoffTime) &&
homeSquad &&
awaySquad && (
<div>
<div className="flex flex-col justify-center space-y-2">
{user && !match.isMatchEventsConfirmed && (
<EditMatchGoalsModal
homeGoals={match.homeGoals}
awayGoals={match.awayGoals}
matchId={match.id}
/>
)}
<MatchEvents
matchEvents={matchEvents}
homeClubId={match.homeClubId}
awayClubId={match.awayClubId}
/>
{(canEditHome || canEditAway) && (
<div className="flex flex-row justify-between space-x-4 pt-3">
<div className={canEditHome ? "" : "hidden"}>
<div
className={`flex flex-row pt-3 ${
canEditHome ? "justify-between" : "justify-end"
}`}
>
{canEditHome && (
<div>
<EditMatchEventsModal squad={homeSquad}>
<>{match.homeTeam.name} - Ny hendelse</>
</EditMatchEventsModal>
</div>
<div className={canEditAway ? "" : "hidden"}>
)}
{canEditAway && (
<div>
<EditMatchEventsModal squad={awaySquad}>
<>{match.awayTeam.name} - Ny hendelse</>
</EditMatchEventsModal>
</div>
</div>
)}
)}
</div>
</div>
)}
<Divider />
Expand Down
49 changes: 49 additions & 0 deletions app/api/match/[id]/result/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { MATCHES_CACHE_TAG } from "@/data/getCompetitionMatchesWithResults";
import { MATCH_EVENTS_CACHE_TAG } from "@/data/getExtendedMatchEvents";
import { MATCH_SQUADS_CACHE_TAG } from "@/data/getExtendedMatchSquads";
import { getUserById } from "@/data/user";
import { currentUser } from "@/lib/auth";
import { db } from "@/lib/db";
import { MatchGoalsSchema } from "@/schemas";
import { UserRole } from "@prisma/client";
import { revalidateTag } from "next/cache";
import { NextResponse } from "next/server";

export const POST = async (
request: Request,
{ params }: { params: { id: string } },
) => {
const { data } = await request.json();

const parsedMatchId = Number(params.id);

const validatedFields = MatchGoalsSchema.safeParse(data);

if (!validatedFields.success) {
return NextResponse.json({ error: "Bad request" }, { status: 400 });
}
const user = await currentUser();

if (!user || !user.id) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const dbUser = await getUserById(user.id);

if (!dbUser) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const match = await db.match.update({
where: {
id: parsedMatchId,
},
data: { ...validatedFields.data },
});

revalidateTag(MATCHES_CACHE_TAG);

return new Response(JSON.stringify({ message: "Kampresultat oppdatert" }), {
status: 200,
});
};
2 changes: 1 addition & 1 deletion app/api/squad/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const POST = async (
return NextResponse.json({ error: "Bad request" }, { status: 400 });
}

if (dbUser.role !== UserRole.ADMIN && player.id !== dbUser.clubId) {
if (dbUser.role !== UserRole.ADMIN && player.clubId !== dbUser.clubId) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

Expand Down
53 changes: 0 additions & 53 deletions app/components/Alert.tsx

This file was deleted.

19 changes: 1 addition & 18 deletions app/components/admin/NIFUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { NIFSchema } from "@/schemas";
import { updateNIFData } from "@/actions/update-NIF-data";
import { z } from "zod";
import { useRouter } from "next/navigation";
import { Alert } from "@/app/components/Alert";

export const NIFUpload = () => {
const router = useRouter();
Expand Down Expand Up @@ -116,23 +115,7 @@ export const NIFUpload = () => {
onChange={handleFileUpload}
disabled={isPending}
/>
{error ? (
<Alert
variant="danger"
title="Kunne ikke oppdatere spillerdatabasen"
>
<>{error}</>
</Alert>
) : (
success && (
<Alert variant="success" title="Databasen ble oppdatert">
<>
{rowsAdded} spillere ble lagt til, og {rowsUpdated} ble
oppdatert!
</>
</Alert>
)
)}

</ModalBody>
<ModalFooter>
<Button onClick={onClose} isDisabled={isPending}>
Expand Down
163 changes: 163 additions & 0 deletions app/components/matches/EditMatchGoalsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
"use client";

import {
Button,
Input,
Modal,
ModalBody,
ModalContent,
ModalHeader,
Select,
SelectItem,
useDisclosure,
} from "@nextui-org/react";
import { useRouter } from "next/navigation";
import { useCallback, useState, useTransition } from "react";
import axios, { AxiosError } from "axios";

import { Controller, useForm } from "react-hook-form";
import { MatchGoalsSchema } from "@/schemas";

import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { matchEventTypesList } from "@/lib/enum-mappings";

type EditMatchGoalsModalProps = {
homeGoals: number | null;
awayGoals: number | null;
matchId: number;
};
export const EditMatchGoalsModal = ({
homeGoals,
awayGoals,
matchId,
}: EditMatchGoalsModalProps) => {
const router = useRouter();

const { isOpen, onOpen, onClose } = useDisclosure();

const [error, setError] = useState<string | undefined>();
const [isPending, startTransition] = useTransition();

const { handleSubmit, control } = useForm<z.infer<typeof MatchGoalsSchema>>({
resolver: zodResolver(MatchGoalsSchema),
defaultValues: {
homeGoals: homeGoals !== null ? homeGoals : 0,
awayGoals: awayGoals !== null ? awayGoals : 0,
},
});

const onSubmit = useCallback(
async (formData: z.infer<typeof MatchGoalsSchema>) => {
setError("");

startTransition(async () => {
await axios
.post(`/api/match/${matchId}/result`, { data: formData })
.then(() => {
onClose();
router.refresh();
})
.catch((err: Error | AxiosError) => {
if (axios.isAxiosError(err)) {
setError(err.message);
} else {
setError("Noe gikk galt under endring av kampresultat.");
}
});
});
},
[matchId, onClose, router],
);

return (
<>
<Button
onClick={onOpen}
size="sm"
color="primary"
className="w-fit mx-auto"
variant="bordered"
>
Endre kampresultat
</Button>
<Modal
isOpen={isOpen}
onClose={onClose}
className="pb-5 sm:min-w-[600px]"
scrollBehavior="inside"
>
<ModalContent className="bg-background text-textPrimary">
<ModalHeader className="pb-1">
<h2 className="h2 text-textPrimary">Endre kampresultat</h2>
</ModalHeader>
<ModalBody>
<form
onSubmit={handleSubmit(onSubmit)}
className="flex flex-row space-x-2 "
>
<Controller
control={control}
name="homeGoals"
render={({
field: { onChange, value },
fieldState,
formState,
}) => (
<Input
isInvalid={!formState.isValid}
type="number"
value={value === null ? "" : value.toString()}
onChange={(e) =>
onChange(
e.target.value === "" ? null : Number(e.target.value),
)
}
/>
)}
/>
<Controller
control={control}
name="awayGoals"
render={({
field: { onChange, value },
fieldState,
formState,
}) => (
<Input
isInvalid={!formState.isValid}
type="number"
value={value === null ? "" : value.toString()}
onChange={(e) =>
onChange(
e.target.value === "" ? null : Number(e.target.value),
)
}
/>
)}
/>
<div className="flex flex-row justify-between">
<Button
color="danger"
onPress={onClose}
isDisabled={isPending}
variant="flat"
>
Avbryt
</Button>
<Button
className="ml-2"
color="primary"
isDisabled={isPending}
type="submit"
>
Lagre
</Button>
</div>
</form>
</ModalBody>
</ModalContent>
</Modal>
</>
);
};
Loading