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

Legg til romfordeling #322

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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: 3 additions & 4 deletions components/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FaTrash } from "react-icons/fa";
import React from "react";
import { useRouter } from "next/router";

type ColumnType = {
export type ColumnType = {
label: string;
field: string;
};
Expand Down Expand Up @@ -52,9 +52,8 @@ const Table = ({ rows, columns, onDelete }: TableProps) => {
{columns.map((column) => (
<td
key={row.id + "-" + column.field}
className={`px-4 py-2 text-xs ${
columns.indexOf(column) === 0 ? "font-medium" : ""
} sm:px-6 sm:py-4 sm:text-sm`}
className={`px-4 py-2 text-xs ${columns.indexOf(column) === 0 ? "font-medium" : ""
} sm:px-6 sm:py-4 sm:text-sm`}
>
{column.field === "delete" && onDelete ? (
<div className="flex items-center justify-center">
Expand Down
171 changes: 171 additions & 0 deletions components/admin/RoomOverview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { useEffect, useState } from "react";
import { periodType, RoomBooking } from "../../lib/types/types";
import Button from "../Button";
import Table, { ColumnType } from "../Table";
import DateInput from "../form/DateInput";
import TextInput from "../form/TextInput";
import TimeRangeInput from "../form/TimeRangeInput";

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { WithId } from "mongodb";
import toast from "react-hot-toast";
import { createRoom, deleteRoom, fetchRoomsByPeriodId } from "../../lib/api/roomApi";

interface Props {
period?: periodType;
}

const RoomInterview = ({
period
}: Props) => {

const queryClient = useQueryClient();

// TODO: Fix correct tabbing
const [roomBookings, setRoomBookings] = useState<WithId<RoomBooking>[]>([])

const [date, setDate] = useState<string>("");
const [startTime, setStartTime] = useState<string>("");
const [endTime, setEndTime] = useState<string>("");
const [room, setRoom] = useState<string>("");

const isValidBooking = () => {
if (!room) {
toast.error("Vennligst fyll inn rom")
return false;
}
if (!date) {
toast.error("Vennligst velg dato")
return false;
}
if (!startTime || !endTime) {
toast.error("Vennligst velg tidspunkt")
return false;
}
if (Date.parse("2003-07-26T" + startTime) - Date.parse("2003-07-26T" + endTime) > 0) {
toast.error("Starttid må være før sluttid")
return false;
}

// TODO: Make sure time is within interviewPeriod

return true;
}

const {
data: roomData,
} = useQuery({
queryKey: ["rooms", period?._id],
queryFn: fetchRoomsByPeriodId
})

const createRoomBookingMutation = useMutation({
mutationFn: createRoom,
onSuccess: () =>
queryClient.invalidateQueries({
// TODO: try to update cache instead
queryKey: ["rooms", period?._id],
}),
});

const deleteRoomBookingMutation = useMutation({
mutationFn: (roomId: string) => deleteRoom(roomId, String(period!._id)),
onSuccess: () =>
queryClient.invalidateQueries({
// TODO: try to update cache instead
queryKey: ["rooms", period?._id],
}),
});

useEffect(() => {
if (!roomData) return
const { rooms } = roomData
console.log(rooms)
setRoomBookings(rooms)
}, [roomData])

const handleAddBooking = () => {
if (!isValidBooking()) return;
addBooking()
setRoom("")
}

const addBooking = () => {
const booking: RoomBooking = {
periodId: String(period?._id),
room: room,
startDate: new Date(date.split("T")[0] + "T" + startTime),
endDate: new Date(date.split("T")[0] + "T" + endTime)
}

createRoomBookingMutation.mutate(booking)
}

const handleDeleteBooking = async (booking: WithId<RoomBooking>) => {
if (!period) return;
const isConfirmed = window.confirm(
`Er det sikker på at du ønsker å fjerne bookingen av ${booking.room} fra ${booking.startDate} til ${booking.endDate}?`
);
if (!isConfirmed) return;

setRoomBookings(roomBookings.filter((bookingA) => bookingA._id != booking._id))

deleteRoomBookingMutation.mutate(String(booking._id))
};

const columns: ColumnType[] = [
{ label: "Rom", field: "room" },
{ label: "Dato", field: "date" },
{ label: "Fra", field: "from" },
{ label: "Til", field: "to" },
{ label: "Slett", field: "delete" },
]

return <div className="flex flex-col items-center">
<h2 className="px-5 mt-5 mb-6 text-2xl font-semibold text-center">Legg inn romvalg</h2>
<div className="mb-5 flex flex-col justify-center">
<div className="flex flex-row justify-center gap-10 items-baseline w-full">
<TextInput
defaultValue={room}
updateInputValues={(input: string) => {
setRoom(input)
}}
label="Romnavn"
className="mx-0"
/>
<DateInput
updateDate={(date: string) => {
setDate(date)
}}
label="Test"
/>
<TimeRangeInput
updateTimes={(times: { start: string, end: string }) => {
setStartTime(times.start)
setEndTime(times.end)
}}
className="mx-0"
/>
</div>
<Button title="Legg til" color="blue" onClick={handleAddBooking}></Button>
</div>
<h2 className="px-5 mt-5 mb-6 text-2xl font-semibold text-center">Alle tilgjengelige romvalg</h2>
{roomBookings?.length ? <Table columns={columns} rows={roomBookings.map((roomBooking) => {
return {
id: String(roomBooking._id),
room: roomBooking.room,
date: new Date(roomBooking.startDate).toLocaleDateString(),
from: new Date(roomBooking.startDate).toLocaleTimeString(),
to: new Date(roomBooking.endDate).toLocaleTimeString()
}
})} onDelete={(id: string, name: string) => {
const deletedBooking = roomBookings.find((booking, index, array) => {
return String(booking._id) == id
})
handleDeleteBooking(deletedBooking!)
}} />
: <p><i>Legg inn et rom, så dukker det opp her.</i></p>}
</div>
};

export default RoomInterview;
29 changes: 29 additions & 0 deletions components/form/DateInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useEffect, useState } from "react";
interface Props {
label?: string;
updateDate: (date: string) => void;
}

const DateRangeInput = (props: Props) => {
const [date, setDate] = useState("");

useEffect(() => {
const dateString = date ? `${date}T00:00` : "";
props.updateDate(dateString);
}, [date]);

return (
<div className="flex items-center ">
<input
type="date"
id={`${props.label}`}
name={`${props.label}`}
value={date}
onChange={(e) => setDate(e.target.value)}
className="border text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 border-gray-300 text-gray-900 dark:border-gray-600 dark:bg-online-darkBlue dark:text-gray-200"
/>
</div>
);
};

export default DateRangeInput;
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ interface Props {
updateDates: (dates: { start: string; end: string }) => void;
}

const DatePickerInput = (props: Props) => {
const DateRangeInput = (props: Props) => {
const [fromDate, setFromDate] = useState("");
const [toDate, setToDate] = useState("");

Expand Down Expand Up @@ -42,4 +42,4 @@ const DatePickerInput = (props: Props) => {
);
};

export default DatePickerInput;
export default DateRangeInput;
3 changes: 2 additions & 1 deletion components/form/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ interface Props {
disabled?: boolean;
placeholder?: string;
defaultValue?: string;
className?: string;
}

const TextInput = (props: Props) => {
Expand All @@ -12,7 +13,7 @@ const TextInput = (props: Props) => {
};

return (
<div className="w-full max-w-xs mx-auto my-6">
<div className={"w-full max-w-xs mx-auto my-6 " + props.className ?? ""}>
<div className="relative">
<input
disabled={props.disabled}
Expand Down
47 changes: 47 additions & 0 deletions components/form/TimeRangeInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useEffect, useState } from "react";
interface Props {
label?: string;
updateTimes: (times: { start: string; end: string }) => void;
className?: string;
}

const TimeRangeInput = (props: Props) => {
const [fromTime, setFromTime] = useState("");
const [toTime, setToTime] = useState("");
new Date()

useEffect(() => {
const startTime = fromTime ? `${fromTime}` : "";
const endTime = toTime ? `${toTime}` : "";
props.updateTimes({ start: startTime, end: endTime });
}, [fromTime, toTime]);

return (
<div className={"w-full max-w-xs mx-auto my-3 " + props.className ?? ""}>
<label className="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-200">
{props.label}
</label>
<div className="flex items-center ">
<input
type="time"
id={`${props.label}-from`}
name={`${props.label}-from`}
value={fromTime}
onChange={(e) => setFromTime(e.target.value)}
className="border text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 border-gray-300 text-gray-900 dark:border-gray-600 dark:bg-online-darkBlue dark:text-gray-200"
/>
<span className="mx-4 text-gray-500 dark:text-gray-300">til</span>
<input
type="time"
id={`${props.label}-to`}
name={`${props.label}-to`}
value={toTime}
onChange={(e) => setToTime(e.target.value)}
className="border text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 border-gray-300 text-gray-900 dark:border-gray-600 dark:bg-online-darkBlue dark:text-gray-200"
/>
</div>
</div>
);
};

export default TimeRangeInput;
41 changes: 41 additions & 0 deletions lib/api/roomApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { QueryFunctionContext } from "@tanstack/react-query";
import { RoomBooking } from "../types/types";

export const fetchRoomByPeriodAndId = async (context: QueryFunctionContext) => {
const periodId = context.queryKey[1];
const roomId = context.queryKey[2];
return fetch(`/api/rooms/${periodId}/${roomId}`).then((res) => res.json());
};

export const fetchRoomsByPeriodId = async (context: QueryFunctionContext) => {
const periodId = context.queryKey[1];
return fetch(`/api/rooms/${periodId}`).then((res) => res.json());
};

export const createRoom = async (room: RoomBooking): Promise<RoomBooking> => {
const response = await fetch(`/api/rooms/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(room),
});

const { message, data, error } = await response.json();
if (!response.ok) {
throw new Error(error || "Unknown error occurred");
}
return data;
};

export const deleteRoom = async (roomId: string, periodId: string) => {
const response = await fetch(`/api/rooms/${periodId}/${roomId}`, {
method: "DELETE",
});

if (!response.ok) {
throw new Error("Failed to delete the room");
}

return response;
};
Loading