@@ -162,7 +165,7 @@ const NewPeriod = () => {
updateDates={updateInterviewPeriodDates}
/>
- {isLoadingCommittees ? (
+ {owCommitteeIsLoading ? (
Laster komiteer...
diff --git a/pages/api/applicants/index.ts b/pages/api/applicants/index.ts
index 022b72aa..5f971757 100644
--- a/pages/api/applicants/index.ts
+++ b/pages/api/applicants/index.ts
@@ -6,10 +6,10 @@ import { getServerSession } from "next-auth";
import { emailDataType } from "../../../lib/types/types";
import { isApplicantType } from "../../../lib/utils/validators";
import { isAdmin, hasSession, checkOwId } from "../../../lib/utils/apiChecks";
-import { SESClient } from "@aws-sdk/client-ses";
import capitalizeFirstLetter from "../../../lib/utils/capitalizeFirstLetter";
-import sendEmail from "../../../lib/utils/sendEmail";
+import sendEmail from "../../../lib/email/sendEmail";
import { changeDisplayName } from "../../../lib/utils/toString";
+import { generateApplicantEmail } from "../../../lib/email/applicantEmailTemplate";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getServerSession(req, res, authOptions);
@@ -55,8 +55,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { applicant, error } = await createApplicant(requestBody);
if (error) throw new Error(error);
- const sesClient = new SESClient({ region: "eu-north-1" });
-
if (applicant != null) {
let optionalCommitteesString = "";
if (applicant.optionalCommittees.length > 0) {
@@ -103,11 +101,9 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
await sendEmail({
- sesClient: sesClient,
- fromEmail: "opptak@online.ntnu.no",
toEmails: emailData.emails,
subject: "Vi har mottatt din søknad!",
- htmlContent: `Dette er en bekreftelse på at vi har mottatt din søknad. Du vil motta en ny e-post med intervjutider etter søkeperioden er over. Her er en oppsummering av din søknad:
E-post: ${emailData.emails[0]}
Fullt navn: ${emailData.name}
Telefonnummer: ${emailData.phone}
Trinn: ${emailData.grade}
Førstevalg: ${emailData.firstChoice}
Andrevalg: ${emailData.secondChoice}
Tredjevalg: ${emailData.thirdChoice}
Ønsker du å være økonomiansvarlig: ${emailData.bankom}
Andre valg: ${emailData.optionalCommittees}
Kort om deg selv:${emailData.about}`,
+ htmlContent: generateApplicantEmail(emailData),
});
console.log("Email sent to: ", emailData.emails);
diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts
index f32d487d..4252c98c 100644
--- a/pages/api/auth/[...nextauth].ts
+++ b/pages/api/auth/[...nextauth].ts
@@ -57,7 +57,7 @@ export const authOptions: NextAuthOptions = {
committees: committeeData.results.map((committee: any) =>
committee.name_short.toLowerCase()
),
- isCommitee: userInfo.is_committee,
+ isCommittee: userInfo.is_committee,
};
},
}),
@@ -75,7 +75,7 @@ export const authOptions: NextAuthOptions = {
token.grade = user.grade;
token.subId = user.subId;
token.committees = user.committees;
- token.isCommitee = user.isCommitee;
+ token.isCommittee = user.isCommittee;
token.role = adminEmails.includes(user.email) ? "admin" : "user";
}
return token;
@@ -91,7 +91,7 @@ export const authOptions: NextAuthOptions = {
session.user.grade = token.grade as number;
session.user.id = token.id as string;
session.user.committees = token.committees as string[];
- session.user.isCommitee = token.isCommitee as boolean;
+ session.user.isCommittee = token.isCommittee as boolean;
}
return session;
},
diff --git a/pages/api/committees/applicants/[period-id]/[committee].ts b/pages/api/committees/applicants/[period-id]/[committee].ts
index 29f91c82..ab0dcf17 100644
--- a/pages/api/committees/applicants/[period-id]/[committee].ts
+++ b/pages/api/committees/applicants/[period-id]/[committee].ts
@@ -23,7 +23,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
.json({ error: "Invalid or missing periodId parameter" });
}
- if (!session.user?.isCommitee || !session.user.committees) {
+ if (!session.user?.isCommittee || !session.user.committees) {
return res.status(403).json({ error: "Access denied, unauthorized" });
}
diff --git a/pages/api/committees/times/[period-id]/[committee].ts b/pages/api/committees/times/[period-id]/[committee].ts
index 62716157..ff20f15e 100644
--- a/pages/api/committees/times/[period-id]/[committee].ts
+++ b/pages/api/committees/times/[period-id]/[committee].ts
@@ -7,6 +7,7 @@ import {
import { getServerSession } from "next-auth";
import { authOptions } from "../../../auth/[...nextauth]";
import { hasSession, isInCommitee } from "../../../../../lib/utils/apiChecks";
+import { getPeriodById } from "../../../../../lib/mongo/periods";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getServerSession(req, res, authOptions);
@@ -47,6 +48,19 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const { message } = req.body;
try {
+ if (typeof message !== "string") {
+ return res.status(400).json({ error: "Invalid message parameter" });
+ }
+
+ const { period } = await getPeriodById(String(periodId));
+ if (!period) {
+ return res.status(400).json({ error: "Invalid periodId" });
+ }
+
+ if (new Date() > new Date(period.applicationPeriod.end)) {
+ return res.status(400).json({ error: "Application period has ended" });
+ }
+
const { updatedMessage, error } = await updateCommitteeMessage(
selectedCommittee,
periodId,
@@ -63,6 +77,15 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === "DELETE") {
try {
+ const { period } = await getPeriodById(String(periodId));
+ if (!period) {
+ return res.status(400).json({ error: "Invalid periodId" });
+ }
+
+ if (new Date() > new Date(period.applicationPeriod.end)) {
+ return res.status(400).json({ error: "Application period has ended" });
+ }
+
const { error } = await deleteCommittee(
selectedCommittee,
periodId,
diff --git a/pages/api/committees/times/[period-id]/index.ts b/pages/api/committees/times/[period-id]/index.ts
index 50221e43..15851b53 100644
--- a/pages/api/committees/times/[period-id]/index.ts
+++ b/pages/api/committees/times/[period-id]/index.ts
@@ -3,7 +3,12 @@ import {createCommittee } from "../../../../../lib/mongo/committees";
import { getServerSession } from "next-auth";
import { authOptions } from "../../../auth/[...nextauth]";
import { hasSession, isInCommitee } from "../../../../../lib/utils/apiChecks";
-import { isCommitteeType } from "../../../../../lib/utils/validators";
+import {
+ isCommitteeType,
+ validateCommittee,
+} from "../../../../../lib/utils/validators";
+import { commiteeType } from "../../../../../lib/types/types";
+import { getPeriodById } from "../../../../../lib/mongo/periods";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getServerSession(req, res, authOptions);
@@ -19,12 +24,25 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (!isInCommitee(res, session)) return;
if (req.method === "POST") {
- const committeeData = req.body;
+ const committeeData: commiteeType = req.body;
if (!isCommitteeType(req.body)) {
return res.status(400).json({ error: "Invalid data format" });
}
+ const { period } = await getPeriodById(String(committeeData.periodId));
+ if (!period) {
+ return res.status(400).json({ error: "Invalid periodId" });
+ }
+
+ if (new Date() > new Date(period.applicationPeriod.end)) {
+ return res.status(400).json({ error: "Application period has ended" });
+ }
+
+ if (!validateCommittee(committeeData, period)) {
+ return res.status(400).json({ error: "Invalid data format" });
+ }
+
try {
const { committee, error } = await createCommittee(
committeeData,
diff --git a/pages/application/[period-id].tsx b/pages/application/[period-id].tsx
index 9a469f51..88064337 100644
--- a/pages/application/[period-id].tsx
+++ b/pages/application/[period-id].tsx
@@ -16,24 +16,23 @@ import ApplicantCard from "../../components/applicantoverview/ApplicantCard";
import LoadingPage from "../../components/LoadingPage";
import { formatDateNorwegian } from "../../lib/utils/dateUtils";
import PageTitle from "../../components/PageTitle";
-
-interface FetchedApplicationData {
- exists: boolean;
- application: applicantType;
-}
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import { fetchPeriodById } from "../../lib/api/periodApi";
+import {
+ createApplicant,
+ deleteApplicant,
+ fetchApplicantByPeriodAndId,
+} from "../../lib/api/applicantApi";
+import ErrorPage from "../../components/ErrorPage";
const Application: NextPage = () => {
+ const queryClient = useQueryClient();
const { data: session } = useSession();
const router = useRouter();
const periodId = router.query["period-id"] as string;
-
- const [hasAlreadySubmitted, setHasAlreadySubmitted] = useState(true);
- const [periodExists, setPeriodExists] = useState(false);
- const [fetchedApplicationData, setFetchedApplicationData] =
- useState
(null);
+ const applicantId = session?.user?.owId;
const [activeTab, setActiveTab] = useState(0);
- const [isLoading, setIsLoading] = useState(true);
const [applicationData, setApplicationData] = useState<
DeepPartial
>({
@@ -53,114 +52,69 @@ const Application: NextPage = () => {
const [period, setPeriod] = useState();
const [isApplicationPeriodOver, setIsApplicationPeriodOver] = useState(false);
- useEffect(() => {
- if (!period) {
- return;
- }
-
- const currentDate = new Date().toISOString();
- if (new Date(period.applicationPeriod.end) < new Date(currentDate)) {
- setIsApplicationPeriodOver(true);
- }
- }, [period]);
-
- useEffect(() => {
- const checkPeriodAndApplicationStatus = async () => {
- if (!periodId || !session?.user?.owId) return;
-
- try {
- const periodResponse = await fetch(`/api/periods/${periodId}`);
- const periodData = await periodResponse.json();
- if (periodResponse.ok) {
- setPeriod(periodData.period);
- setPeriodExists(periodData.exists);
- fetchApplicationData();
- } else {
- throw new Error(periodData.error || "Unknown error");
- }
- } catch (error) {
- console.error("Error checking period:", error);
- }
-
- try {
- const applicationResponse = await fetch(
- `/api/applicants/${periodId}/${session.user.owId}`
- );
- const applicationData = await applicationResponse.json();
-
- if (!applicationResponse.ok) {
- throw new Error(applicationData.error || "Unknown error");
- }
- } catch (error) {
- console.error("Error checking application status:", error);
- } finally {
- setIsLoading(false);
- }
- };
-
- checkPeriodAndApplicationStatus();
- }, [session?.user?.owId, periodId]);
+ const {
+ data: periodData,
+ isError: periodIsError,
+ isLoading: periodIsLoading,
+ } = useQuery({
+ queryKey: ["periods", periodId],
+ queryFn: fetchPeriodById,
+ });
- const handleSubmitApplication = async () => {
- if (!validateApplication(applicationData)) return;
+ const {
+ data: applicantData,
+ isError: applicantIsError,
+ isLoading: applicantIsLoading,
+ } = useQuery({
+ queryKey: ["applicant", periodId, applicantId],
+ queryFn: fetchApplicantByPeriodAndId,
+ });
- try {
- applicationData.periodId = periodId as string;
- const response = await fetch("/api/applicants", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(applicationData),
+ const createApplicantMutation = useMutation({
+ mutationFn: createApplicant,
+ onSuccess: () => {
+ queryClient.setQueryData(["applicant", periodId, applicantId], {
+ applicant: applicationData,
+ exists: true,
});
-
- const responseData = await response.json();
-
- if (response.ok) {
- toast.success("Søknad sendt inn");
- setHasAlreadySubmitted(true);
- } else {
- if (
- responseData.error ===
- "409 Application already exists for this period"
- ) {
- toast.error("Du har allerede søkt for denne perioden");
- } else {
- throw new Error(`Error creating applicant: ${response.statusText}`);
- }
- }
- } catch (error) {
- if (error instanceof Error) {
- toast.error(error.message);
+ toast.success("Søknad sendt inn");
+ },
+ onError: (error) => {
+ if (error.message === "409 Application already exists for this period") {
+ toast.error("Du har allerede søkt for denne perioden");
} else {
toast.error("Det skjedde en feil, vennligst prøv igjen");
}
- } finally {
- fetchApplicationData();
- }
- };
+ },
+ });
- const fetchApplicationData = async () => {
- if (!session?.user?.owId || !periodId) return;
+ const deleteApplicantMutation = useMutation({
+ mutationFn: deleteApplicant,
+ onSuccess: () => {
+ queryClient.setQueryData(["applicant", periodId, applicantId], null);
+ toast.success("Søknad trukket tilbake");
+ setActiveTab(0);
+ },
+ onError: () => toast.error("Det skjedde en feil, vennligst prøv igjen"),
+ });
- try {
- const response = await fetch(
- `/api/applicants/${periodId}/${session.user.owId}`
- );
- const data = await response.json();
- if (!data.exists) {
- setHasAlreadySubmitted(false);
- } else {
- setFetchedApplicationData(data);
- }
+ useEffect(() => {
+ if (!periodData || !periodData.period) return;
- if (!response.ok) {
- throw new Error(data.error || "Unknown error");
- }
- } catch (error) {
- console.error("Error fetching application data:", error);
- toast.error("Failed to fetch application data.");
+ setPeriod(periodData.period);
+
+ const currentDate = new Date().toISOString();
+ if (
+ new Date(periodData.period.applicationPeriod.end) < new Date(currentDate)
+ ) {
+ setIsApplicationPeriodOver(true);
}
+ }, [periodData]);
+
+ const handleSubmitApplication = async () => {
+ if (!validateApplication(applicationData)) return;
+ applicationData.periodId = periodId as string;
+ createApplicantMutation.mutate(applicationData as applicantType);
};
const handleDeleteApplication = async () => {
@@ -171,28 +125,19 @@ const Application: NextPage = () => {
if (!confirm("Er du sikker på at du vil trekke tilbake søknaden?")) return;
- try {
- const response = await fetch(
- `/api/applicants/${periodId}/${session.user.owId}`,
- {
- method: "DELETE",
- }
- );
-
- if (!response.ok) {
- throw new Error("Failed to delete the application");
- }
-
- toast.success("Søknad trukket tilbake");
- setHasAlreadySubmitted(false);
- } catch (error) {
- toast.error("Det skjedde en feil, vennligst prøv igjen");
- }
+ deleteApplicantMutation.mutate({ periodId, owId: session?.user?.owId });
};
- if (isLoading) return ;
+ if (
+ periodIsLoading ||
+ applicantIsLoading ||
+ createApplicantMutation.isPending ||
+ deleteApplicantMutation.isPending
+ )
+ return ;
+ if (periodIsError || applicantIsError) return ;
- if (!periodExists) {
+ if (!periodData?.exists)
return (
@@ -200,9 +145,8 @@ const Application: NextPage = () => {
);
- }
- if (hasAlreadySubmitted) {
+ if (applicantData?.exists)
return (
@@ -224,22 +168,25 @@ const Application: NextPage = () => {
onClick={handleDeleteApplication}
/>
)}
- {fetchedApplicationData && (
+ {applicantData?.application && (
)}
);
- }
return (
-
+
{
- const { data: session } = useSession();
const [currentPeriods, setCurrentPeriods] = useState([]);
- const [isLoading, setIsLoading] = useState(false);
+
+ const {
+ data: periodsData,
+ isError: periodsIsError,
+ isLoading: periodsIsLoading,
+ } = useQuery({
+ queryKey: ["periods"],
+ queryFn: fetchPeriods,
+ });
useEffect(() => {
- const fetchPeriods = async () => {
- try {
- setIsLoading(true);
- const res = await fetch("/api/periods");
- const data = await res.json();
- const today = new Date();
+ if (!periodsData) return;
- setCurrentPeriods(
- data.periods.filter((period: periodType) => {
- const startDate = new Date(period.applicationPeriod.start || "");
- const endDate = new Date(period.applicationPeriod.end || "");
+ const today = new Date();
- return startDate <= today && endDate >= today;
- })
- );
- setIsLoading(false);
- } catch (error) {
- console.error("Failed to fetch application periods:", error);
- }
- };
+ setCurrentPeriods(
+ periodsData.periods.filter((period: periodType) => {
+ const startDate = new Date(period.applicationPeriod.start || "");
+ const endDate = new Date(period.applicationPeriod.end || "");
- session && fetchPeriods();
- }, [session]);
+ return startDate <= today && endDate >= today;
+ })
+ );
+ }, [periodsData]);
- if (isLoading) return ;
+ if (periodsIsLoading) return ;
+ if (periodsIsError) return ;
return (
@@ -44,29 +45,28 @@ const Apply = () => {
Ingen åpne opptak for øyeblikket
Opptak til{" "}
-
- komiteene
- {" "}
+
+
+ komiteene
+
+ {" "}
skjer vanligvis i august etter fadderuka. Noen komiteer har
- vanligvis suppleringsopptak i februar.{
}
Følg
- med på{" "}
-
- online.ntnu.no
- {" "}
+ vanligvis suppleringsopptak i februar.
+
+
+ Følg med på{" "}
+
+
+ online.ntnu.no
+
+ {" "}
eller på vår{" "}
-
- Facebook
- {" "}
- side for kunngjøringer!
+
+
+ Facebook-gruppe
+
+ {" "}
+ for kunngjøringer!
) : (
@@ -74,7 +74,7 @@ const Apply = () => {
Nåværende opptaksperioder
-
+
{currentPeriods.map((period: periodType, index: number) => (
))}
diff --git a/pages/committee/[period-id]/[committee]/index.tsx b/pages/committee/[period-id]/[committee]/index.tsx
index 60b74059..ac065304 100644
--- a/pages/committee/[period-id]/[committee]/index.tsx
+++ b/pages/committee/[period-id]/[committee]/index.tsx
@@ -20,6 +20,9 @@ import { changeDisplayName } from "../../../../lib/utils/toString";
import Custom404 from "../../../404";
import PageTitle from "../../../../components/PageTitle";
import Link from "next/link";
+import { useQuery } from "@tanstack/react-query";
+import { fetchPeriodById } from "../../../../lib/api/periodApi";
+import ErrorPage from "../../../../components/ErrorPage";
const CommitteeApplicantOverView: NextPage = () => {
const { data: session } = useSession();
@@ -38,21 +41,14 @@ const CommitteeApplicantOverView: NextPage = () => {
const [singleCommitteeInPeriod, setSingleCommitteeInPeriod] = useState
(true);
- useEffect(() => {
- if (!session || !periodId) return;
-
- const fetchPeriod = async () => {
- try {
- const res = await fetch(`/api/periods/${periodId}`);
- const data = await res.json();
- setPeriod(data.period);
- } catch (error) {
- console.error("Failed to fetch interview periods:", error);
- }
- };
+ const { data: periodData, isError: periodIsError, isLoading: periodIsLoading } = useQuery({
+ queryKey: ['periods', periodId],
+ queryFn: fetchPeriodById,
+ });
- fetchPeriod();
- }, [periodId]);
+ useEffect(() => {
+ setPeriod(periodData?.period);
+ }, [periodData]);
useEffect(() => {
const userCommittees = session?.user?.committees?.map(c => c.toLowerCase()) || [];
@@ -64,7 +60,7 @@ const CommitteeApplicantOverView: NextPage = () => {
if (!session || !periodId || !committee) return;
const fetchCommitteeInterviewTimes = async () => {
- if (!session) {
+ if (!session.user?.committees?.includes(committee)) {
return;
}
if (period?._id === undefined) return;
@@ -74,6 +70,7 @@ const CommitteeApplicantOverView: NextPage = () => {
`/api/committees/times/${period?._id}/${committee}`
);
const data = await response.json();
+
if (response.ok) {
setCommitteeInterviewTimes(data.committees[0]);
} else {
@@ -120,13 +117,9 @@ const CommitteeApplicantOverView: NextPage = () => {
checkAccess();
}, [period]);
- if (loading) {
- return ;
- }
-
- if (!session || !hasAccess) {
- return ;
- }
+ if (loading || periodIsLoading) return ;
+ if (!hasAccess) return ;
+ if (periodIsError) return ;
const interviewPeriodEnd = period?.interviewPeriod.end
? new Date(period.interviewPeriod.end)
diff --git a/pages/committee/[period-id]/index.tsx b/pages/committee/[period-id]/index.tsx
index 73637161..bbd34915 100644
--- a/pages/committee/[period-id]/index.tsx
+++ b/pages/committee/[period-id]/index.tsx
@@ -3,58 +3,51 @@ import { useEffect, useState } from "react";
import { useSession } from "next-auth/react";
import LoadingPage from "../../../components/LoadingPage";
import CommitteeCard from "../../../components/committee/CommitteeCard";
+import { useQuery } from "@tanstack/react-query";
+import { fetchPeriodById } from "../../../lib/api/periodApi";
+import ErrorPage from "../../../components/ErrorPage";
+import NotFound from "../../404";
const ChooseCommittee = () => {
const { data: session } = useSession();
const router = useRouter();
const periodId = router.query["period-id"];
+
const [committees, setCommittees] = useState(null);
- const [loading, setLoading] = useState(true);
+
+ const { data, isError, isLoading } = useQuery({
+ queryKey: ['periods', periodId],
+ queryFn: fetchPeriodById,
+ });
useEffect(() => {
- const fetchPeriod = async () => {
- if (!session || !periodId) return;
-
- try {
- const res = await fetch(`/api/periods/${periodId}`);
- const data = await res.json();
-
- if (data.period) {
- const userCommittees = session!.user!.committees;
- const periodCommittees = data.period.committees;
-
- if (data.period.optionalCommittees != null) {
- periodCommittees.push(...data.period.optionalCommittees);
- }
-
- const filteredCommittees = periodCommittees.filter(
- (committee: string) =>
- userCommittees?.includes(committee.toLowerCase())
- );
- setCommittees(filteredCommittees);
- }
- } catch (error) {
- console.error("Failed to fetch interview periods:", error);
- } finally {
- setLoading(false);
- }
- };
- fetchPeriod();
- }, [periodId, session]);
-
- if (loading) {
- return ;
- }
+ if(!data) return;
+
+ const userCommittees = session!.user!.committees;
+ const periodCommittees = [...data.period?.committees, ...data.period?.optionalCommittees];
+
+ const matchingCommittees = periodCommittees.filter(
+ (committee: string) =>
+ userCommittees?.includes(committee.toLowerCase())
+ );
+ setCommittees(matchingCommittees);
+
+ }, [data, session])
+
+ if (session?.user?.committees?.length === 0) return ;
+ if (isLoading) return ;
+ if (isError) return ;
return (
-
+
Velg komite
- {committees?.map((committee) =>
- CommitteeCard({
- committee,
- link: `${periodId}/${committee.toLowerCase()}`,
- })
- )}
+ {committees?.map((committee) => (
+
+ ))}
);
};
diff --git a/pages/committee/index.tsx b/pages/committee/index.tsx
index 0f88306b..79485a3e 100644
--- a/pages/committee/index.tsx
+++ b/pages/committee/index.tsx
@@ -5,75 +5,77 @@ import Table from "../../components/Table";
import { formatDate } from "../../lib/utils/dateUtils";
import { periodType } from "../../lib/types/types";
import LoadingPage from "../../components/LoadingPage";
+import { useQuery } from "@tanstack/react-query";
+import { fetchPeriods } from "../../lib/api/periodApi";
+import ErrorPage from "../../components/ErrorPage";
+import { TableSkeleton } from "../../components/skeleton/TableSkeleton";
const Committee: NextPage = () => {
const { data: session } = useSession();
const [periods, setPeriods] = useState([]);
- const [isLoading, setIsLoading] = useState(true);
- const fetchPeriods = async () => {
- try {
- const response = await fetch("/api/periods");
- const data = await response.json();
- const userCommittees = session?.user?.committees || [];
+ const {
+ data: periodsData,
+ isError: periodsIsError,
+ isLoading: periodsIsLoading,
+ } = useQuery({
+ queryKey: ["periods"],
+ queryFn: fetchPeriods,
+ });
- // Viser bare aktuelle perioder
- const filteredPeriods = data.periods.filter((period: periodType) =>
- period.committees.some((committee: string) =>
- userCommittees.includes(committee.toLowerCase())
- )
- );
+ useEffect(() => {
+ if (!periodsData) return;
- setPeriods(
- filteredPeriods.map((period: periodType) => {
- const userCommittees = session?.user?.committees?.map((committee) =>
- committee.toLowerCase()
- );
- const periodCommittees = period.committees.map((committee) =>
- committee.toLowerCase()
- );
+ const userCommittees = session?.user?.committees || [];
- period.optionalCommittees.forEach((committee) => {
- periodCommittees.push(committee.toLowerCase());
- });
+ // Viser bare aktuelle perioder
+ const filteredPeriods = periodsData.periods.filter((period: periodType) =>
+ period.committees.some((committee: string) =>
+ userCommittees.includes(committee.toLowerCase())
+ )
+ );
- const commonCommittees = userCommittees!.filter((committee) =>
- periodCommittees.includes(committee)
- );
+ setPeriods(
+ filteredPeriods.map((period: periodType) => {
+ const userCommittees = session?.user?.committees?.map((committee) =>
+ committee.toLowerCase()
+ );
+ const periodCommittees = period.committees.map((committee) =>
+ committee.toLowerCase()
+ );
- let uriLink = "";
+ period.optionalCommittees.forEach((committee) => {
+ periodCommittees.push(committee.toLowerCase());
+ });
- if (commonCommittees.length > 1) {
- uriLink = `committee/${period._id}`;
- } else {
- uriLink = `committee/${period._id}/${commonCommittees[0]}`;
- }
+ const commonCommittees = userCommittees!.filter((committee) =>
+ periodCommittees.includes(committee)
+ );
- return {
- name: period.name,
- application:
- formatDate(period.applicationPeriod.start) +
- " til " +
- formatDate(period.applicationPeriod.end),
- interview:
- formatDate(period.interviewPeriod.start) +
- " til " +
- formatDate(period.interviewPeriod.end),
- committees: period.committees,
- link: uriLink,
- };
- })
- );
- } catch (error) {
- console.error("Failed to fetch application periods:", error);
- } finally {
- setIsLoading(false);
- }
- };
+ let uriLink = "";
- useEffect(() => {
- fetchPeriods();
- }, []);
+ if (commonCommittees.length > 1) {
+ uriLink = `committee/${period._id}`;
+ } else {
+ uriLink = `committee/${period._id}/${commonCommittees[0]}`;
+ }
+
+ return {
+ name: period.name,
+ application:
+ formatDate(period.applicationPeriod.start) +
+ " til " +
+ formatDate(period.applicationPeriod.end),
+ interview:
+ formatDate(period.interviewPeriod.start) +
+ " til " +
+ formatDate(period.interviewPeriod.end),
+ committees: period.committees,
+ link: uriLink,
+ };
+ })
+ );
+ }, [periodsData, session]);
const periodsColumns = [
{ label: "Navn", field: "name" },
@@ -81,19 +83,16 @@ const Committee: NextPage = () => {
{ label: "Intervju", field: "interview" },
];
- if (!session || !session.user?.isCommitee) {
- return
Ingen tilgang!
;
- }
-
- if (isLoading) {
- return
;
- }
+ if (!session || !session.user?.isCommittee) return
Ingen tilgang!
;
+ if (periodsIsError) return
;
return (
Velg opptak
- {periods.length > 0 && (
+ {periodsIsLoading ? (
+
+ ) : (
)}
diff --git a/pages/committees.tsx b/pages/committees.tsx
index 0ab4a6b4..ce28e9e1 100644
--- a/pages/committees.tsx
+++ b/pages/committees.tsx
@@ -2,75 +2,54 @@ import { useEffect, useState } from "react";
import LoadingPage from "../components/LoadingPage";
import { owCommitteeType, periodType } from "../lib/types/types";
import CommitteeAboutCard from "../components/CommitteeAboutCard";
+import { useQuery } from "@tanstack/react-query";
+import { fetchOwCommittees } from "../lib/api/committees";
+import ErrorPage from "../components/ErrorPage";
+import { fetchPeriods } from "../lib/api/periodApi";
+
+const excludedCommittees = ["Faddere", "Output"];
const Committees = () => {
- const [isLoading, setIsLoading] = useState(true);
const [committees, setCommittees] = useState
([]);
const [periods, setPeriods] = useState([]);
- const excludedCommittees = ["Faddere"];
+ const {
+ data: owCommitteeData,
+ isError: owCommitteeIsError,
+ isLoading: owCommitteeIsLoading,
+ } = useQuery({
+ queryKey: ["ow-committees"],
+ queryFn: fetchOwCommittees,
+ });
+
+ const {
+ data: periodsData,
+ isError: periodsIsError,
+ isLoading: periodsIsLoading,
+ } = useQuery({
+ queryKey: ["periods"],
+ queryFn: fetchPeriods,
+ });
- const filterCommittees = (committees: owCommitteeType[]) => {
- return committees.filter(
- (committee) => !excludedCommittees.includes(committee.name_short)
- );
- };
-
- const fetchPeriods = async () => {
- try {
- const response = await fetch("/api/periods");
- const data = await response.json();
- setPeriods(data.periods);
- } catch (error) {
- console.error("Failed to fetch periods:", error);
- }
- };
-
- const fetchCommittees = async () => {
- try {
- const response = await fetch("/api/periods/ow-committees");
- const data = await response.json();
-
- const filteredData = filterCommittees(data);
+ useEffect(() => {
+ if (!owCommitteeData) return;
- const cachedData = JSON.parse(
- localStorage.getItem("committeesCache") || "[]"
- );
+ const filteredCommittees = owCommitteeData.filter(
+ (committee: owCommitteeType) =>
+ !excludedCommittees.includes(committee.name_short)
+ );
- if (JSON.stringify(filteredData) !== JSON.stringify(cachedData)) {
- localStorage.setItem("committeesCache", JSON.stringify(filteredData));
- setCommittees(filteredData);
- } else {
- setCommittees(cachedData);
- }
- console.log(filteredData);
- } catch (error) {
- console.error("Failed to fetch committees:", error);
- const cachedData = JSON.parse(
- localStorage.getItem("committeesCache") || "[]"
- );
- setCommittees(cachedData);
- } finally {
- setIsLoading(false);
- }
- };
+ setCommittees(filteredCommittees);
+ }, [owCommitteeData]);
useEffect(() => {
- const cachedData = JSON.parse(
- localStorage.getItem("committeesCache") || "[]"
- );
- if (cachedData.length > 0) {
- setCommittees(cachedData);
- setIsLoading(false);
- }
- fetchPeriods();
- fetchCommittees();
- }, []);
+ if (!periodsData) return;
- const hasPeriod = (committee: any) => {
- if (!Array.isArray(periods)) {
- return false;
- }
+ setPeriods(periodsData.periods);
+ }, [periodsData]);
+
+ const hasPeriod = (committee: owCommitteeType) => {
+ if (!Array.isArray(periods)) return false;
const today = new Date();
@@ -95,7 +74,8 @@ const Committees = () => {
});
};
- if (isLoading) return ;
+ if (owCommitteeIsLoading || periodsIsLoading) return ;
+ if (owCommitteeIsError || periodsIsError) return ;
return (