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 3 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
124 changes: 124 additions & 0 deletions components/admin/RoomOverview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { useState } from "react";
import { periodType, committeeInterviewType, RoomBooking } from "../../lib/types/types";
import Button from "../Button";
import Table, { RowType } from "../Table"
import { ColumnType } from "../Table";
import TextInput from "../form/TextInput";
import DateInput from "../form/DateInput";
import TimeRangeInput from "../form/TimeRangeInput";

import toast from "react-hot-toast";

interface Props {
period: periodType | null;
}

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

// TODO: Fix correct tabbing

const [roomBookings, setRoomBookings] = useState<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;
}

return true;
}

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

const addBooking = () => {
const booking: RoomBooking = {
room: room,
startDate: date.split("T")[0] + "T" + startTime,
endDate: date.split("T")[0] + "T" + endTime
}

roomBookings.push(booking)
setRoomBookings(roomBookings)
}

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: roomBooking.room + "]" + roomBooking.startDate + "]" + roomBooking.endDate,
room: roomBooking.room,
date: roomBooking.startDate.split("T")[0],
from: roomBooking.startDate.split("T")[1],
to: roomBooking.endDate.split("T")[1]
}
})} onDelete={(id: string, name: string) => {
const [room, startDate, endDate] = id.split("]")
setRoomBookings(roomBookings.filter((booking, index, array) => {
return !(booking.room == room
&& booking.startDate == startDate
&& booking.endDate == endDate)
}))
}} />
: <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;
15 changes: 14 additions & 1 deletion lib/api/periodApi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { QueryFunctionContext } from "@tanstack/react-query";
import { periodType } from "../types/types";
import { periodType, RoomBooking } from "../types/types";

export const fetchPeriodById = async (context: QueryFunctionContext) => {
const id = context.queryKey[1];
Expand All @@ -25,3 +25,16 @@ export const createPeriod = async (period: periodType) => {
},
});
};

export const updateRoomsForPeriod = async (
id: string,
rooms: RoomBooking[]
) => {
return fetch(`/api/periods${id}`, {
method: "PATCH",
body: JSON.stringify(rooms),
headers: {
"Content-Type": "application/json",
},
});
};
28 changes: 27 additions & 1 deletion lib/mongo/periods.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Collection, Db, MongoClient, ObjectId } from "mongodb";
import clientPromise from "./mongodb";
import { periodType } from "../types/types";
import { periodType, RoomBooking } from "../types/types";

let client: MongoClient;
let db: Db;
Expand Down Expand Up @@ -85,6 +85,32 @@ export const getCurrentPeriods = async () => {
}
};

export const updateRoomsForPeriod = async (
id: string | ObjectId,
rooms: RoomBooking[]
) => {
try {
if (!periods) await init();

// Checks if period exists
const period = await getPeriodById(id);
if (!period.exists) {
return { error: "Period not found" };
}

const response = await periods.updateOne(
{ _id: new ObjectId(id) },
{ $set: { rooms: rooms } }
);

return response.modifiedCount >= 1
? { message: "Period updated with rooms" }
: { message: "Period not updated" };
} catch (error) {
return { error: "Failed to update rooms for period" };
}
};

export const getPeriodById = async (id: string | ObjectId) => {
try {
if (!periods) await init();
Expand Down
7 changes: 7 additions & 0 deletions lib/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export type periodType = {
committees: string[];
optionalCommittees: string[];
hasSentInterviewTimes: boolean;
rooms?: RoomBooking[];
};

export type AvailableTime = {
Expand Down Expand Up @@ -141,3 +142,9 @@ export type emailApplicantInterviewType = {
};
}[];
};

export interface RoomBooking {
room: String;
startDate: String;
endDate: String;
}
6 changes: 6 additions & 0 deletions lib/utils/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
committeeInterviewType,
periodType,
preferencesType,
RoomBooking,
} from "../types/types";

export const isApplicantType = (
Expand Down Expand Up @@ -191,3 +192,8 @@ export const isPeriodType = (data: any): data is periodType => {

return hasBasicFields;
};

export const isRoomBookings = (data: any): data is RoomBooking => {
// TODO: Implement
return true;
};
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"mongodb": "^6.1.0",
"next": "^12.3.4",
"next-auth": "^4.24.5",
"online-opptak": "file:",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hot-toast": "^2.4.1",
Expand Down
Loading