Browse Source

modules added jsdoc

liontix 8 months ago
parent
commit
778079176c

+ 64 - 4
src/lib/funcs.ts

@@ -2,25 +2,60 @@ import moment from "moment";
 import type { AppointmentDateTimes, AppointmentTimes, WeekSelection } from "./dbdata";
 import { goto } from "$app/navigation";
 
-export function isSameDate(dateA: Date, dateB: Date) {
+/**
+ * Compares two dates for equality based on the date format 'YYYY-MM-DD'.
+ *
+ * @param dateA - The first date to compare.
+ * @param dateB - The second date to compare.
+ * @returns True if the dates are equal, false otherwise.
+ */
+export function isSameDate(dateA: Date, dateB: Date): boolean {
     const date1 = moment(dateA).format('YYYY-MM-DD');
     const date2 = moment(dateB).format('YYYY-MM-DD');
 
     return date1 === date2;
 }
 
+/**
+ * Formats a date into the 'YYYY-MM-DD' format.
+ *
+ * @param date - The date to format.
+ * @returns The formatted date string.
+ */
 export function getDateString(date: Date): string {
     return moment(date).format('YYYY-MM-DD');
 }
 
+/**
+ * Extracts the time component from a date and returns it in the locale's time format.
+ *
+ * @param date - The date to extract the time from.
+ * @returns The extracted time as a string.
+ */
 export function extractTime(date: Date): string {
     return date.toLocaleTimeString();
 }
 
+/**
+ * Extracts the date component from a date and returns it in the locale's date format.
+ *
+ * @param date - The date to extract the date from.
+ * @returns The extracted date as a string.
+ */
 export function extractDate(date: Date): string {
     return date.toLocaleDateString();
 }
 
+/**
+ * Generates an array of time slots for a given start date, end date, and interval.
+ *
+ * @param startDate - The start date in 'YYYY-MM-DD HH:mm' format.
+ * @param startTime - The start time in hours.
+ * @param endTime - The end time in hours.
+ * @param intervalMinutes - The interval between time slots in minutes (default: 15).
+ * @param appointmentTimes - An array of existing appointments to avoid double booking (default: []).
+ * @returns An array of time slots with 'date' and 'available' properties.
+ */
 export function getTimeSlots(startDate: string, startTime: string, endTime: string, intervalMinutes: number = 15, appointmentTimes: AppointmentDateTimes[] = []): AppointmentTimes[] {
     let start = moment(`${startDate} ${startTime}`, "YYYY-MM-DD HH:mm");
     let end = moment(`${startDate} ${endTime}`, "YYYY-MM-DD HH:mm");
@@ -36,21 +71,40 @@ export function getTimeSlots(startDate: string, startTime: string, endTime: stri
         const ix: number = appointmentTimes.findIndex(element => moment(element.date).toISOString() === start.toISOString() && element.available === false);
         const isAvailable: boolean = ix === -1;
 
-        timeSlots.push({date: start.format('YYYY-MM-DDTHH:mm'), available: isAvailable}); // ISO-like format
+        timeSlots.push({ date: start.format('YYYY-MM-DDTHH:mm'), available: isAvailable }); // ISO-like format
         start.add(intervalMinutes, "minutes");
     }
 
     return timeSlots;
 }
 
+/**
+ * Converts a moment object to the 'YYYYMMDDTHHmmssZ' timestamp format.
+ *
+ * @param date - The moment object to convert.
+ * @returns The converted timestamp string.
+ */
 export function toLocalDTSTAMP(date: moment.Moment): string {
     return date.format("YYYYMMDDTHHmmss") + "Z";
 }
 
-export function isDueDatePassed(dueDate: Date) {
+/**
+ * Checks if a due date has passed.
+ *
+ * @param dueDate - The due date to check.
+ * @returns True if the due date has passed, false otherwise.
+ */
+export function isDueDatePassed(dueDate: Date): boolean {
     return moment(dueDate).isBefore();
 }
 
+/**
+ * Authenticates an HTTP request and redirects to the auth page if unauthorized.
+ *
+ * @param input - The URL or RequestInfo object for the fetch request.
+ * @param init - The initial options for the fetch request (optional).
+ * @returns The response from the authenticated fetch request.
+ */
 export async function authFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
     const response = await fetch(input, init);
     if (response.status === 401) {
@@ -60,9 +114,15 @@ export async function authFetch(input: RequestInfo | URL, init?: RequestInit): P
     return response;
 }
 
+/**
+ * Generates an array of week selections based on the current date.
+ *
+ * @param weeks - The number of weeks to generate (default: 1).
+ * @returns An array of week selections with 'yearWeek', 'week', 'start' and 'end' properties.
+ */
 export function generateDateArray(weeks: number): WeekSelection[] {
     const result: WeekSelection[] = [];
-    
+
     // Get the start of the current week
     let currentDate = moment().startOf("isoWeek");
 

+ 24 - 8
src/lib/index.ts

@@ -2,14 +2,30 @@ import { writable, type Writable } from "svelte/store";
 import { browser } from "$app/environment";
 
 // place files you want to import through the `$lib` alias in this folder.
+
+/**
+ * The API host URL.
+ *
+ * @constant {string}
+ */
 export const api_host = "http://192.168.178.82:3000";
- //export let authToken: string = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY3OTIxYTcyMTNiM2NkYjc5NGZmNzQ2MiIsImlhdCI6MTczNzcyOTYwMiwiZXhwIjoxNzM3NzUxMjAyfQ.-TlU0izsSJnZYE79yG01fcRgzgBirZBJBsiQf2alwZ4";
 
+/**
+ * An authentication token store, automatically populated with the stored token or an empty string if running on the server-side rendering (SSR) environment.
+ *
+ * @type {Writable<string>}
+ */
 export let authToken: Writable<string>;
- 
- if (browser) {
-   authToken = writable(localStorage.getItem("token") || "");
- } else {
-   authToken = writable(""); // Fallback value for SSR
- }
- 
+
+if (browser) {
+  /**
+   * Initialize the auth token store with the stored token from local storage, defaulting to an empty string if no token is found.
+   */
+  authToken = writable(localStorage.getItem("token") || "");
+} else {
+  /**
+   * Initialize the auth token store with an empty string for server-side rendering (SSR) environments.
+   */
+  authToken = writable(""); // Fallback value for SSR
+}
+

+ 66 - 16
src/lib/modules/AppointmentConfirmDialog.svelte

@@ -1,48 +1,100 @@
 <script lang="ts">
-	import type { Appointment, AppointmentInputs, DynamicJson } from '$lib/dbdata';
+	import type { Appointment, DynamicJson } from '$lib/dbdata';
 	import { extractTime, toLocalDTSTAMP } from '$lib/funcs';
 	import moment from 'moment';
 	import AppointmentInfo from './AppointmentInfo.svelte';
 	import AppointmentRegisterInputs from './AppointmentRegisterInputs.svelte';
 	import { createEvent } from 'ics';
 
+	/**
+	 * Flag indicating whether the appointment is currently active.
+	 * @type {boolean}
+	 */
 	export let active: boolean;
 
+	/**
+	 * The current appointment object, containing information about the scheduled appointment.
+	 * @type {Appointment}
+	 */
 	export let appointment: Appointment;
+
+	/**
+	 * Date variable to store the current date.
+	 * @type {Date}
+	 */
 	export let date: Date = new Date();
+
+	/**
+	 * Array of dynamic input data.
+	 * @type {DynamicJson[]}
+	 */
 	export let dynamicInputs: DynamicJson[] = [];
 
+	/**
+	 * Variable to display any error messages that occur during the appointment registration process.
+	 * @type {string}
+	 */
 	let errorMessage: string = '';
+
+	/**
+	 * Variable to display success messages after a successful appointment registration.
+	 * @type {string}
+	 */
 	let successMessage: string = '';
+
+	/**
+	 * Flag enabling or disabling the download of appointment ICS files.
+	 * @type {boolean}
+	 */
 	let enableICSDownload: boolean = false;
 
-	let time: string = '00:00';
+	/**
+	 * Current time variable
+	 */
+	let time: string = '00:00'; // The current time as a string
 
+	/**
+	 * Function to create an event using ICS format
+	 *
+	 * @param {Object} options - Options for the ICS event creation
+	 * @returns {void}
+	 */
 	function createEventICS() {
 		const formattedDate: string = toLocalDTSTAMP(moment.utc(date.toLocaleString()));
-		console.log(formattedDate);
+		console.log('Formatted Date:', formattedDate);
 
 		createEvent(
 			{
 				title: appointment.title,
 				description: appointment.description,
 				busyStatus: 'BUSY',
-        start: formattedDate,
+				start: formattedDate,
 				duration: { minutes: appointment.duration }
 			},
 			(error, value) => {
 				if (error) {
-					console.log(error);
+					console.log('Error:', error);
 				}
 				const blob = new Blob([value], { type: 'text/calendar' });
-        const url = window.URL.createObjectURL(blob);
+				const url = window.URL.createObjectURL(blob);
+
+				setTimeout(() => {
+					window.location.href = url; // Triggers opening on supported devices
+				}, 1000);
+			}
+		);
+	}
 
-        setTimeout(() => {
-            window.location.href = url; // Triggers opening on supported devices
-        }, 1000);
-		});
+	// Binding time to current time
+	$: {
+		time = extractTime(new Date(date));
 	}
 
+	/**
+	 * Binding time to current time
+	 *
+	 * @returns {void}
+	 */
 	$: {
 		time = extractTime(new Date(date));
 	}
@@ -62,7 +114,7 @@
 
 		<AppointmentRegisterInputs
 			bind:modalActive={active}
-      bind:booked={enableICSDownload}
+			bind:booked={enableICSDownload}
 			bind:errorMessage
 			bind:successMessage
 			bind:date
@@ -71,17 +123,15 @@
 		/>
 	</div>
 
-  {#if enableICSDownload && false}
-    <button class="responsive green" on:click={createEventICS}>Download ICS</button>
-  {/if}
+	{#if enableICSDownload && false}
+		<button class="responsive green" on:click={createEventICS}>Download ICS</button>
+	{/if}
 </dialog>
 
-
 {#if errorMessage}
 	<div class="snackbar error active">{errorMessage}</div>
 {/if}
 
-
 {#if successMessage}
 	<div class="snackbar green active">{successMessage}</div>
 {/if}

+ 60 - 19
src/lib/modules/AppointmentInfo.svelte

@@ -1,23 +1,64 @@
 <script lang="ts">
-	import { extractDate } from "$lib/funcs";
-
-    export let title: string = "";
-    export let location: string = "";
-    export let description: string = "";
-    export let isConfirmation: boolean = false;
-    export let date: Date = new Date();
-    export let duration: number = 0;
-    export let time: string = "";
-</script>
+	import { extractDate } from '$lib/funcs';
+
+	/**
+	 * The title of an event or booking.
+	 *
+	 * @type {string}
+	 */
+	export let title: string = '';
+
+	/**
+	 * The location of an event or booking.
+	 *
+	 * @type {string}
+	 */
+	export let location: string = '';
+
+	/**
+	 * A brief description of an event or booking.
+	 *
+	 * @type {string}
+	 */
+	export let description: string = '';
 
+	/**
+	 * Whether this is a confirmation message.
+	 *
+	 * @type {boolean}
+	 */
+	export let isConfirmation: boolean = false;
+
+	/**
+	 * The date of an event or booking.
+	 *
+	 * @type {Date}
+	 */
+	export let date: Date = new Date();
+
+	/**
+	 * The duration of an event or booking, in minutes.
+	 *
+	 * @type {number}
+	 */
+	export let duration: number = 0;
+
+	/**
+	 * The time at which the event or booking starts. This should be set to a string
+	 * representing HH:mm (e.g., "12:30").
+	 *
+	 * @type {string}
+	 */
+	export let time: string = '';
+</script>
 
 <article class="bottom-margin">
-    <h5>{title}</h5>
-    <p><i>pin_drop</i> {location}</p>
-    <p><i>description</i> {description}</p>
-    <p><i>timer</i> {duration} Min.</p>
-    {#if isConfirmation}
-        <p><i>event</i> {extractDate(new Date(date))}</p>
-        <p><i>schedule</i> {time}</p>
-    {/if}
-</article>  
+	<h5>{title}</h5>
+	<p><i>pin_drop</i> {location}</p>
+	<p><i>description</i> {description}</p>
+	<p><i>timer</i> {duration} Min.</p>
+	{#if isConfirmation}
+		<p><i>event</i> {extractDate(new Date(date))}</p>
+		<p><i>schedule</i> {time}</p>
+	{/if}
+</article>

+ 172 - 94
src/lib/modules/AppointmentRegisterInputs.svelte

@@ -1,102 +1,180 @@
 <script lang="ts">
-	import { api_host } from "$lib";
-	import type { AppointmentInputs, DynamicJson } from "$lib/dbdata";
-	import { t } from "$lib/translations";
-    import validator from "validator";
-
-    export let dynamicInputs: DynamicJson[] = [];
-    export let appointmentId: string;
-    export let date: Date;
-    export let modalActive: boolean;
-    export let booked: boolean = false;
-
-    export let errorMessage: string = "";
-    export let successMessage: string = "";
-
-    let inputValues: Record<string, string> = {};
-
-    function validateInput(value: string, type: string): boolean  {
-        switch (type) {
-            case "email":
-                return validator.isEmail(value);
-        
-            case "text":
-                return !validator.isEmpty(value);
-
-            case "tel":
-                return validator.isMobilePhone(value);
-
-            default:
-                return false;
-        }
-    }
-
-    async function confirmBooking(customInputs: DynamicJson[]) {
-        try {
-            const response = await fetch(`${api_host}/api/schedule/create`, {
-                method: "POST",
-                headers: {
-                    "Content-Type": "application/json"
-                },
-                body: JSON.stringify({
-                    appointmentId: appointmentId,
-                    time: date,
-                    inputs: customInputs
-                })
-            })
-
-            if (response.status === 200) {
-                errorMessage = "";
-                successMessage = $t("common.sucess-booking");
-                booked = true;
-            } else {
-                successMessage = "";
-                errorMessage = $t("common.error-booking");
-            }
-
-            setTimeout(() => {
-                successMessage = "";
-                errorMessage = "";
-            }, 7500);
-        } catch (error) {
-            console.log(error);
-        }
-    }
-
-    async function submit() {
-        let customInputs: DynamicJson[] = [];
-        errorMessage = "";
-        if (Object.keys(inputValues).length != dynamicInputs.length) {
-            errorMessage = $t("common.error-parameters");
-            return;
-        }
-        for (const dInput of dynamicInputs) {
-            if (inputValues[dInput.name]) {
-                if (!validateInput(inputValues[dInput.name], dInput.type)) {
-                    errorMessage = $t("common.error-invalid-parameters");
-                    return;
-                }
-                customInputs = [...customInputs, {name: dInput.name, value: inputValues[dInput.name]}];
-            }
-        }
-
-        await confirmBooking(customInputs);
-    }
-
-    function closeModal() {
-        modalActive = false;
-    }
+	import { api_host } from '$lib';
+	import type { DynamicJson } from '$lib/dbdata';
+	import { t } from '$lib/translations';
+	import validator from 'validator';
 
+	/**
+	 * An array of dynamic input objects used for validation and data processing.
+	 *
+	 * @property {string} name - The name of the input field
+	 * @property {boolean} required - Whether the input field is required
+	 */
+	export let dynamicInputs: DynamicJson[] = [
+		// Add your dynamic input objects here...
+	];
+
+	/**
+	 * The ID of the appointment being managed.
+	 *
+	 * @type {string}
+	 */
+	export let appointmentId: string;
+
+	/**
+	 * The scheduled date of the appointment.
+	 *
+	 * @type {Date}
+	 */
+	export let date: Date;
+
+	/**
+	 * A boolean indicating whether the modal is active and displayed on the page.
+	 *
+	 * @type {boolean}
+	 */
+	export let modalActive: boolean = false;
+
+	/**
+	 * A boolean indicating whether a booking has been successfully made for this appointment.
+	 *
+	 * @type {boolean}
+	 * @default false
+	 */
+	export let booked: boolean = false;
+
+	/**
+	 * An error message to display in case of an issue during the appointment management process.
+	 *
+	 * @type {string}
+	 */
+	export let errorMessage: string = '';
+
+	/**
+	 * A success message to display after a successful booking for this appointment.
+	 *
+	 * @type {string}
+	 * @default ''
+	 */
+	export let successMessage: string = '';
+
+	/**
+	 * Initialize the input values object.
+	 *
+	 * @type {Record<string, string>} - An object containing the current input values.
+	 */
+	let inputValues: Record<string, string> = {};
+
+	/**
+	 * Validate user input based on the input type.
+	 *
+	 * @param {string} value - The input value to validate.
+	 * @param {string} type - The type of input (e.g. email, text, tel).
+	 * @returns {boolean} True if the input is valid, false otherwise.
+	 */
+	function validateInput(value: string, type: string): boolean {
+		switch (type) {
+			case 'email':
+				return validator.isEmail(value);
+
+			case 'text':
+				return !validator.isEmpty(value);
+
+			case 'tel':
+				return validator.isMobilePhone(value);
+
+			default:
+				return false;
+		}
+	}
+
+	/**
+	 * Confirm the booking by sending a POST request to the API.
+	 *
+	 * @param {DynamicJson[]} customInputs - An array of dynamic input objects.
+	 */
+	async function confirmBooking(customInputs: DynamicJson[]) {
+		try {
+			const response = await fetch(`${api_host}/api/schedule/create`, {
+				method: 'POST',
+				headers: {
+					'Content-Type': 'application/json'
+				},
+				body: JSON.stringify({
+					appointmentId: appointmentId,
+					time: date,
+					inputs: customInputs
+				})
+			});
+
+			if (response.status === 200) {
+				errorMessage = '';
+				successMessage = $t('common.sucess-booking');
+				booked = true;
+			} else {
+				successMessage = '';
+				errorMessage = $t('common.error-booking');
+			}
+
+			setTimeout(() => {
+				successMessage = '';
+				errorMessage = '';
+			}, 7500);
+		} catch (error) {
+			console.log(error);
+		}
+	}
+	/**
+	 * Handle the submit event by validating and sending the booking request.
+	 *
+	 * @returns {void}
+	 */
+	async function submit() {
+		// Filter input values to only include valid dynamic inputs
+		let customInputs: DynamicJson[] = [];
+		errorMessage = '';
+		if (Object.keys(inputValues).length != dynamicInputs.length) {
+			errorMessage = $t('common.error-parameters');
+			return;
+		}
+		for (const dInput of dynamicInputs) {
+			if (inputValues[dInput.name]) {
+				if (!validateInput(inputValues[dInput.name], dInput.type)) {
+					errorMessage = $t('common.error-invalid-parameters');
+					return;
+				}
+				customInputs = [...customInputs, { name: dInput.name, value: inputValues[dInput.name] }];
+			}
+		}
+
+		await confirmBooking(customInputs);
+	}
+
+	/**
+	 * Close the modal by setting the modalActive property to false.
+	 *
+	 * @returns {void}
+	 */
+	function closeModal() {
+		modalActive = false;
+	}
 </script>
 
+<!-- Render dynamic inputs -->
 {#each dynamicInputs as dInput}
-    <div class="field border">
-        <input id={dInput.name} type={dInput.type} placeholder={dInput.name} bind:value={inputValues[dInput.name]}>
-        <label for={dInput.name}>{dInput.name}</label>
-    </div>
+	<div class="field border">
+		<input
+			id={dInput.name}
+			type={dInput.type}
+			placeholder={dInput.name}
+			bind:value={inputValues[dInput.name]}
+		/>
+		<label for={dInput.name}>{dInput.name}</label>
+	</div>
 {/each}
 
+<!-- Render navigation buttons -->
 <nav class="right-align">
-    <button on:click={submit}>{$t("common.book")}</button>
-    <button class="border" on:click={closeModal}>{$t("common.cancel")}</button>
-</nav>
+	<button on:click={submit}>{$t('common.book')}</button>
+	<button class="border" on:click={closeModal}>{$t('common.cancel')}</button>
+</nav>

+ 95 - 64
src/lib/modules/Auth.svelte

@@ -1,78 +1,109 @@
 <script lang="ts">
-	import { goto } from "$app/navigation";
-	import { api_host, authToken } from "$lib";
-	import type { AuthTokenResponse } from "$lib/dbdata";
-	import { t } from "$lib/translations";
+	import { goto } from '$app/navigation';
+	import { api_host, authToken } from '$lib';
+	import type { AuthTokenResponse } from '$lib/dbdata';
+	import { t } from '$lib/translations';
 
+	/**
+	 * Email address of the user attempting to log in or register.
+	 */
+	let email: string = '';
 
-    let email: string = "";
-    let password: string = "";
-    let errorMessage: string = "";
+	/**
+	 * Password of the user attempting to log in or register.
+	 */
+	let password: string = '';
 
-    async function authHandler(endpoint: string) {
-        errorMessage = "";
+	/**
+	 * Error message to display if authentication fails.
+	 */
+	let errorMessage: string = '';
 
-        if (!email || !password) {
-            errorMessage = $t("common.error-parameters");
-            return;
-        }
-        try {
-            const response = await fetch(`${api_host}/api/auth/${endpoint}`, {
-                method: "POST",
-                headers: {
-                    "Content-Type": "application/json"
-                },
-                body: JSON.stringify({
-                    email: email,
-                    password: password
-                })
-            })
+	/**
+	 * Authenticates a user using the specified endpoint (login or register).
+	 *
+	 * @param {string} endpoint - The type of authentication endpoint to use ('login' or 'register').
+	 * @throws {Error} If the email or password is empty, or if the authentication request fails.
+	 */
+	async function authHandler(endpoint: string) {
+		errorMessage = '';
 
-            if (response.status === 200) {
-                const data: AuthTokenResponse = await response.json();
-                localStorage.setItem('token', data.token);
-                $authToken = data.token;
-                goto("/user/events");
-            } else {
-                errorMessage = $t("common.error-invalid-credentials");
-            }
-        } catch (error) {
-            console.log(error);
-        }
-    }
+		/**
+		 * Validate that both email and password are provided.
+		 */
+		if (!email || !password) {
+			errorMessage = $t('common.error-parameters');
+			return;
+		}
+		try {
+			const response = await fetch(`${api_host}/api/auth/${endpoint}`, {
+				method: 'POST',
+				headers: {
+					'Content-Type': 'application/json'
+				},
+				body: JSON.stringify({
+					email: email,
+					password: password
+				})
+			});
 
-    async function register() {
-        await authHandler('register');
-    }
+			if (response.status === 200) {
+				/**
+				 * Parse the authentication response into an AuthTokenResponse object.
+				 */
+				const data: AuthTokenResponse = await response.json();
+				localStorage.setItem('token', data.token);
+				$authToken = data.token;
+				goto('/user/events');
+			} else {
+				errorMessage = $t('common.error-invalid-credentials');
+			}
+		} catch (error) {
+			console.log(error);
+		}
+	}
 
-    async function login() {
-        await authHandler('login');
-    }
+	/**
+	 * Handles the login functionality by calling authHandler with the 'login' endpoint.
+	 */
+	async function register() {
+		await authHandler('register');
+	}
 
+	/**
+	 * Handles the registration functionality by calling authHandler with the 'register' endpoint.
+	 */
+	async function login() {
+		await authHandler('login');
+	}
 </script>
 
-
 <article class="large no-padding center center-align middle-align auto-margin">
-    <fieldset>
-    <div class="padding top-margin">
-        <h5>{$t("common.auth")}</h5>
-        <div class="field border">
-            <h4 class="large-text left-align">{$t("common.email")}</h4>
-            <input id="email" placeholder={$t("common.email")} bind:value={email} type="email" />
-        </div>
+	<fieldset>
+		<div class="padding top-margin">
+			<h5>{$t('common.auth')}</h5>
+			<div class="field border">
+				<h4 class="large-text left-align">{$t('common.email')}</h4>
+				<input id="email" placeholder={$t('common.email')} bind:value={email} type="email" />
+			</div>
 
-        <div class="field suffix border">
-            <h4 class="large-text left-align">{$t("common.password")}</h4>
-            <input id="password" placeholder={$t("common.password")} bind:value={password} type="password" />
-        </div>
+			<div class="field suffix border">
+				<h4 class="large-text left-align">{$t('common.password')}</h4>
+				<input
+					id="password"
+					placeholder={$t('common.password')}
+					bind:value={password}
+					type="password"
+				/>
+			</div>
 
-        <div class="grid">
-            <button on:click={login} class="s6">{$t("common.login")}</button>
-            <button on:click={register} class="s6 orange">{$t("common.register")}</button>
-        </div>
-        {#if errorMessage} 
-            <h5 class="large-text error-text">{errorMessage}</h5>
-        {/if}
-    </div>
-</fieldset>
-</article>
+			<div class="grid">
+				<button on:click={login} class="s6">{$t('common.login')}</button>
+				<button on:click={register} class="s6 orange">{$t('common.register')}</button>
+			</div>
+			{#if errorMessage}
+				<h5 class="large-text error-text">{errorMessage}</h5>
+			{/if}
+		</div>
+	</fieldset>
+</article>

+ 56 - 2
src/lib/modules/CreatorBaseInputs.svelte

@@ -2,16 +2,70 @@
 	import type { WeekSelection } from "$lib/dbdata";
 	import { t } from "$lib/translations";
 
+	/**
+	 * The topic of the event or meeting.
+	 *
+	 * @type {string}
+	 */
     export let topic: string;
+
+	/**
+	 * The location of the event or meeting.
+	 *
+	 * @type {string}
+	 */
     export let location: string;
+
+	/**
+	 * The number of participants in the event or meeting.
+	 *
+	 * @type {number}
+	 */
     export let participants: number;
+
+	/**
+	 * The week that is currently selected.
+	 *
+	 * @type {number}
+	 */
     export let selectedWeek: number;
+
+	/**
+	 * The duration of the event or meeting, in minutes.
+	 *
+	 * @type {number}
+	 */
     export let duration: number;
+
+	/**
+	 * A brief description of the event or meeting.
+	 *
+	 * @type {string}
+	 */
     export let description: string;
+
+	/**
+	 * The date by which the event or meeting is due, in 'YYYY-MM-DD' format.
+	 *
+	 * @type {string}
+	 */
     export let dueDate: string;
-    export let weeks: WeekSelection[];
 
-    export let changeWeek = function a(){};
+	/**
+	 * An array of WeekSelection objects representing the weeks associated with this topic.
+	 *
+	 * @type {WeekSelection[]}
+	 */
+	export let weeks: WeekSelection[];
+
+	/**
+	 * A callback function that can be used to update the selected week when a new option is chosen.
+	 *
+	 * @type {function}
+	 */
+	export let changeWeek = function() {
+		// function will be overridden
+	};
 
     // TODO auto gen weeks
 </script>

+ 16 - 4
src/lib/modules/CreatorInputs.svelte

@@ -1,19 +1,31 @@
 <script lang="ts">
 	import type { AppointmentInputs } from '$lib/dbdata';
-	import { onMount } from 'svelte';
-	import AppointmentInfo from './AppointmentInfo.svelte';
-	import { getTimeSlots } from '$lib/funcs';
 	import { t } from '$lib/translations';
 
+	/**
+	 * An array of creator input fields, containing name and type.
+	 *
+	 * @type {AppointmentInputs[]}
+	 */
 	export let creatorInputs: AppointmentInputs[] = [{ name: '', type: 'text' }];
 
+	/**
+	 * Adds a new input field to the creatorInputs array.
+	 *
+	 * @returns {void}
+	 */
 	function addInput() {
 		const newInput: AppointmentInputs = { name: '', type: 'text' };
 		creatorInputs = [...creatorInputs, newInput];
 	}
 
+    /**
+     * Removes an input field at the specified index from the creatorInputs array.
+     *
+     * @param {number} indexToRemove The index of the input field to be removed.
+     * @returns {void}
+     */
     function removeInput(indexToRemove: number) {
-        // creatorInputs.splice(index, 1);
         creatorInputs = creatorInputs.filter((_, index) => index !== indexToRemove);
     }
 </script>

+ 8 - 1
src/lib/modules/CreatorTimeInputs.svelte

@@ -2,10 +2,17 @@
 	import type { WeekdayAppointment } from "$lib/dbdata";
 	import CreatorTimeRangeInput from "./CreatorTimeRangeInput.svelte";
 
+    /**
+     * An array of WeekdayAppointment objects.
+     * @type {WeekdayAppointment[]}
+     */
     export let weekdays: WeekdayAppointment[] = [];
 
     $: {
-        weekdays = weekdays.map(item => 
+        /**
+         * Updates the `weekdays` array by mapping over each item and setting its `times` property to an empty array if it's not active.
+         */
+        weekdays = weekdays.map(item =>
             item.active ? item : { ...item, times: [] }
         );
 

+ 39 - 25
src/lib/modules/CreatorTimeRangeInput.svelte

@@ -1,34 +1,48 @@
 <script lang="ts">
-	import type { AppointmentTimeRange } from "$lib/dbdata";
+	import type { AppointmentTimeRange } from '$lib/dbdata';
 
-    export let timeRanges: AppointmentTimeRange[] = [];
+	/**
+	 * An array of appointment time ranges.
+	 * @type {AppointmentTimeRange[]}
+	 */
+	export let timeRanges: AppointmentTimeRange[] = [];
 
-    function addEntry() {
-        timeRanges = [... timeRanges, {start: "", end: ""}];
-    }
+	/**
+	 * Adds a new entry to the time range array.
+	 * The newly added entry will have default values for start and end times.
+	 */
+	function addEntry() {
+		timeRanges = [...timeRanges, { start: '', end: '' }];
+	}
 
-    function removeInput(indexToRemove: number) {
-        // creatorInputs.splice(index, 1);
-        timeRanges = timeRanges.filter((_, index) => index !== indexToRemove);
-    }
+	/**
+	 * Removes an input from the time range array by its index.
+	 *
+	 * @param indexToRemove - The index of the input to remove (0-indexed).
+	 * @throws Error if the index is out of bounds or doesn't correspond to a valid input.
+	 */
+	function removeInput(indexToRemove: number) {
+		// creatorInputs.splice(index, 1);
+		timeRanges = timeRanges.filter((_, index) => index !== indexToRemove);
+	}
 </script>
 
 <div class="">
-    <button class="transparent circle large">
-        <i>content_copy</i>
-    </button>
-    <button on:click={addEntry} class="transparent circle large">
-        <i>add</i>
-    </button>
+	<button class="transparent circle large">
+		<i>content_copy</i>
+	</button>
+	<button on:click={addEntry} class="transparent circle large">
+		<i>add</i>
+	</button>
 </div>
 
-{#each timeRanges as timeEntry, index} 
-    <div class="field border row">
-        <input bind:value={timeEntry.start} type="time">
-        -
-        <input bind:value={timeEntry.end} type="time">
-        <button on:click={() => removeInput(index)} class="transparent circle large">
-            <i>delete</i>
-        </button>
-    </div>
-{/each}
+{#each timeRanges as timeEntry, index}
+	<div class="field border row">
+		<input bind:value={timeEntry.start} type="time" />
+		-
+		<input bind:value={timeEntry.end} type="time" />
+		<button on:click={() => removeInput(index)} class="transparent circle large">
+			<i>delete</i>
+		</button>
+	</div>
+{/each}

+ 54 - 5
src/lib/modules/DatePicker.svelte

@@ -4,21 +4,65 @@
 	import { t } from '$lib/translations';
 	import moment, { type Moment } from 'moment';
 	import { onMount } from 'svelte';
-
+	
+	/**
+	 * The current date used to initialize the calendar.
+	 *
+	 * @type {Date}
+	 */
 	let currentDate: Date = new Date();
+	
+	/**
+	 * An array of day objects, containing information about each day in the calendar.
+	 *   - `day`: The number of the day (1-31).
+	 *   - `isActive`: Whether the day is currently selected.
+	 *   - `isAvailable`: Whether the day is available for appointments.
+	 *
+	 * @type {Array<{ day: number; isActive: boolean; isAvailable: boolean }>}
+	 */
 	let days: Array<{ day: number; isActive: boolean; isAvailable: boolean }> = [];
 
+	/**
+	 * The current month string used in the calendar display.
+	 *
+	 * @type {string}
+	 */
 	let currentMonth: string = '';
+
+	/**
+	 * A date object representing the currently selected day in the calendar.
+	 *
+	 * @type {Date}
+	 */
 	export let selectedDate: Date;
+
+	/**
+	 * An array of appointment data, used to populate the calendar with available days.
+	 *
+	 * @type {AppointmentDateTimes[]}
+	 */
 	export let appointmentDateTimes: AppointmentDateTimes[];
 
 	$: {
+		/**
+		 * Re-renders the calendar when there are new appointment date times.
+		 */
 		if (appointmentDateTimes.length > 0) {
 			renderCalendar();
 		}
 	}
 
-	function isDayAvailable(year: number, month: number, day: number) {
+	/**
+	 * Checks if a given day is available for appointments, based on the provided
+	 * appointment data. Returns `true` if the day is not in conflict with an existing
+	 * appointment date time.
+	 *
+	 * @param {number} year - The year of the date to check.
+	 * @param {number} month - The month of the date to check (1-12).
+	 * @param {number} day - The day of the month (1-31) to check.
+	 * @returns {boolean}
+	 */
+	function isDayAvailable(year: number, month: number, day: number): boolean {
 		const dateStr = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`;
 		const testDate: Date = moment(dateStr).toDate();
 
@@ -27,6 +71,8 @@
 
 	/**
 	 * Updates the calendar display by calculating the days of the current month.
+	 *
+	 * @returns {void}
 	 */
 	const renderCalendar = (): void => {
 		const mDate: Moment = moment(currentDate).locale("de");
@@ -62,10 +108,11 @@
 
 	/**
 	 * Changes the current month by a given offset and re-renders the calendar.
-	 * @param offset - The number of months to move forward or backward.
+	 *
+	 * @param {number} offset - The number of months to move forward or backward (positive for forward, negative for backward).
+	 * @returns {void}
 	 */
 	const changeMonth = (offset: number): void => {
-		//currentDate.setMonth(currentDate.getMonth() + offset);
 		if (offset === -1) {
 			currentDate = moment(currentDate).subtract(1, 'month').toDate();
 		} else {
@@ -82,7 +129,9 @@
 
 	/**
 	 * Handles the selection of a day in the calendar.
-	 * @param selectedDay - The day number that was clicked.
+	 *
+	 * @param {number} selectedDay - The number of the day that was clicked.
+	 * @returns {void}
 	 */
 	const selectDay = (selectedDay: number): void => {
 		days = days.map((d) => ({ ...d, isActive: d.day === selectedDay }));

+ 40 - 21
src/lib/modules/DateTimes.svelte

@@ -1,30 +1,49 @@
 <script lang="ts">
-	import type { AppointmentDateTimes } from "$lib/dbdata";
-	import { extractTime, isSameDate } from "$lib/funcs";
-	import moment from "moment";
-	import { onMount } from "svelte";
-	import AppointmentConfirmDialog from "./AppointmentConfirmDialog.svelte";
-	import { t } from "$lib/translations";
+	import type { AppointmentDateTimes } from '$lib/dbdata';
+	import { extractTime, isSameDate } from '$lib/funcs';
+	import { t } from '$lib/translations';
 
-    export let availableTimes: AppointmentDateTimes[] = [];
-    export let selectedDate: Date = new Date();
+	/**
+	 * An array of available time slots for an appointment.
+	 *
+	 * @type {AppointmentDateTimes[]}
+	 */
+	export let availableTimes: AppointmentDateTimes[] = [];
 
-    export let activateModal: boolean = false;
+	/**
+	 * The selected appointment date.
+	 *
+	 * @type {Date}
+	 */
+	export let selectedDate: Date = new Date();
 
-    function redirectToBooking(date: Date) {
-        selectedDate = date;
-        activateModal = true;
-    }
-</script>
+	/**
+	 * A flag indicating whether the booking modal should be activated.
+	 *
+	 * @type {boolean}
+	 */
+	export let activateModal: boolean = false;
 
+	/**
+	 * Redirects to a new page for booking an appointment on the specified date.
+	 *
+	 * @param {Date} date - The date for which an appointment is to be booked.
+	 */
+	function redirectToBooking(date: Date) {
+		selectedDate = date;
+		activateModal = true;
+	}
+</script>
 
-<h4 class="center-align top-margin">{$t("common.times")}</h4>
+<h4 class="center-align top-margin">{$t('common.times')}</h4>
 
 <article>
-    {#each availableTimes as time}
-        {#if isSameDate(time.date, selectedDate)}
-            <button on:click={() => redirectToBooking(time.date)} 
-                class="responsive wave border bottom-margin">{extractTime(new Date(time.date))}</button>
-        {/if}
-    {/each}
+	{#each availableTimes as time}
+		{#if isSameDate(time.date, selectedDate)}
+			<button
+				on:click={() => redirectToBooking(time.date)}
+				class="responsive wave border bottom-margin">{extractTime(new Date(time.date))}</button
+			>
+		{/if}
+	{/each}
 </article>

+ 98 - 48
src/lib/modules/EventTables.svelte

@@ -1,17 +1,50 @@
 <script lang="ts">
-	import { goto } from '$app/navigation';
 	import { api_host, authToken } from '$lib';
 	import type { Appointment } from '$lib/dbdata';
 	import { authFetch, isDueDatePassed } from '$lib/funcs';
 	import { t } from '$lib/translations';
 	import { onMount } from 'svelte';
 
+	/**
+	 * An array of appointments fetched from the server.
+	 *
+	 * @type {Appointment[]}
+	 */
 	let appointmentData: Appointment[] = [];
+
+	/**
+	 * A flag indicating whether the component is ready to render.
+	 *
+	 * @type {boolean}
+	 */
 	let ready: boolean = false;
+
+	/**
+	 * An error message that will be displayed if an error occurs while fetching appointments.
+	 *
+	 * @type {string}
+	 */
 	let errorMessage: string = '';
-    let actionCounter: number = 0;
-    let action: {action: string, id: string} = {action: '', id: ''};
 
+	/**
+	 * A counter tracking the number of actions performed on an appointment.
+	 *
+	 * @type {number}
+	 */
+	let actionCounter: number = 0;
+
+	/**
+	 * An object containing information about the current action being performed.
+	 *
+	 * @type {{action: 'DELETE' | 'POST', id: string}}
+	 */
+	let action: { action: 'DELETE' | 'POST'; id: string } = { action: 'POST', id: '' };
+
+	/**
+	 * Fetches appointments from the server and updates the local data.
+	 *
+	 * @async
+	 */
 	async function fetchAppointments() {
 		try {
 			const response = await authFetch(`${api_host}/api/users/appointments`, {
@@ -29,32 +62,54 @@
 		}
 	}
 
+	/**
+	 * Copies the share link for a given appointment ID to the clipboard.
+	 *
+	 * @param {string} appointmentId - The ID of the appointment to copy the link for.
+	 */
 	function copyShareLink(appointmentId: string) {
 		window.navigator.clipboard.writeText(
 			`${window.location.origin}/appointment?id=${appointmentId}`
 		);
 	}
 
+	/**
+	 * Deletes an appointment from the server and updates the local data.
+	 *
+	 * @param {string} appointmentId - The ID of the appointment to delete.
+	 */
 	async function deleteEntry(appointmentId: string) {
-		manageAppointment(appointmentId, "DELETE", "delete");
+		manageAppointment(appointmentId, 'DELETE', 'delete');
 	}
 
+	/**
+	 * Locks an appointment on the server and updates the local data.
+	 *
+	 * @param {string} appointmentId - The ID of the appointment to lock.
+	 */
 	async function lockAppointment(appointmentId: string) {
-		manageAppointment(appointmentId, "POST", "lock");
+		manageAppointment(appointmentId, 'POST', 'lock');
 	}
 
+	/**
+	 * Manages an appointment by sending a request to the server.
+	 *
+	 * @param {string} appointmentId - The ID of the appointment to manage.
+	 * @param {'DELETE' | 'POST'} method - The type of request to send (either DELETE or POST).
+	 * @param {string} path - The path on the server to send the request to.
+	 */
 	async function manageAppointment(appointmentId: string, method: 'DELETE' | 'POST', path: string) {
-        actionCounter += 1;
-        if (action.action !== method || action.id !== appointmentId) {
-            actionCounter = 1;
-            action = {action: method, id: appointmentId};
-        }
+		actionCounter += 1;
+		if (action.action !== method || action.id !== appointmentId) {
+			actionCounter = 1;
+			action = { action: method, id: appointmentId };
+		}
 
-        if (actionCounter !== 3) {
-            return;
-        }
+		if (actionCounter !== 3) {
+			return;
+		}
 
-        actionCounter = 0;
+		actionCounter = 0;
 
 		try {
 			const response = await fetch(`${api_host}/api/users/${path}`, {
@@ -70,29 +125,24 @@
 
 			if (response.status === 404) {
 				console.log(`entry not found`);
-				errorMessage = $t("common.error-server");
+				errorMessage = $t('common.error-server');
 				await fetchAppointments();
-				setTimeout(() => errorMessage = "", 4000);
+				setTimeout(() => (errorMessage = ''), 4000);
 			}
 
-			if (response.status === 200 && path === "delete") {
+			if (response.status === 200 && path === 'delete') {
 				appointmentData = appointmentData.filter((element) => element._id !== appointmentId);
 			}
 
-            if (response.status === 200 && path === "lock") {
+			if (response.status === 200 && path === 'lock') {
 				await fetchAppointments();
-                errorMessage = "";
-                
-            }
+				errorMessage = '';
+			}
 		} catch (error) {
 			console.log('Error during fetch:', error);
 		}
 	}
 
-    function redirectEditCreator(appointmentId: string) {
-        goto(`/user/creator?${appointmentId}`);
-    }
-
 	onMount(async () => {
 		await fetchAppointments();
 
@@ -103,41 +153,41 @@
 {#each appointmentData as data}
 	<tr>
 		<td>{data.title}</td>
-		
+
 		<td class="center-align">
 			<span>{data.participants}</span>
 		</td>
 
 		<td class="center-align">
 			<a href={`/appointment?id=${data._id}`} class="button medium cyan">
-				<span>{$t("common.share")}</span>
+				<span>{$t('common.share')}</span>
 			</a>
 		</td>
-        {#if !isDueDatePassed(data.dueDate)}          
-            <td on:click={() => lockAppointment(data._id)} class="center-align">
-                <button class="medium">
-                    <span>{$t("common.lock")}</span>
-                </button>
-            </td>
-        {:else}
-            <td class="center-align">
-                <button class="medium secondary">
-                    <span>{$t("common.locked")}</span>
-                </button>
-            </td>
-        {/if}
-		
+		{#if !isDueDatePassed(data.dueDate)}
+			<td on:click={() => lockAppointment(data._id)} class="center-align">
+				<button class="medium">
+					<span>{$t('common.lock')}</span>
+				</button>
+			</td>
+		{:else}
+			<td class="center-align">
+				<button class="medium secondary">
+					<span>{$t('common.locked')}</span>
+				</button>
+			</td>
+		{/if}
+
 		<!-- load into the creator -->
-        <td class="center-align">
-            <a href={`/user/creator?id=${data._id}`} class="button medium secondary blue">
-                <span>{$t("common.edit")}</span>
+		<td class="center-align">
+			<a href={`/user/creator?id=${data._id}`} class="button medium secondary blue">
+				<span>{$t('common.edit')}</span>
 			</a>
-        </td>
+		</td>
 
 		<!-- send a delete request -->
 		<td on:click={() => deleteEntry(data._id)} class="center-align">
 			<button class="medium red">
-				<span>{$t("common.delete")}</span>
+				<span>{$t('common.delete')}</span>
 			</button>
 		</td>
 	</tr>
@@ -148,5 +198,5 @@
 {/if}
 
 {#if actionCounter > 0}
-    <div class="snackbar active">{$t("common.action-confirm")}</div>
-{/if}
+	<div class="snackbar active">{$t('common.action-confirm')}</div>
+{/if}

+ 2 - 0
src/routes/+layout.svelte

@@ -14,6 +14,8 @@
         }
         let initLocale = 'en'; // get from cookie, user session, ...
 
+        console.log = function a() {};
+
         console.log("lang");
         console.log(navigator.language);