فهرست منبع

Updated +layout.svelte to include token handling from Svelte and a small function checking localStorage for a token.

liontix 10 ماه پیش
والد
کامیت
335107ba92

+ 1 - 0
package.json

@@ -33,6 +33,7 @@
 	"dependencies": {
 		"@sveltejs/adapter-vercel": "^5.5.2",
 		"beercss": "^3.8.0",
+		"moment": "^2.30.1",
 		"validator": "^13.12.0"
 	}
 }

+ 0 - 1
src/lib/dbdata.ts

@@ -20,7 +20,6 @@ export interface AuthTokenResponse {
 
 export interface AppointmentDateTimes {
     date: Date;
-    startTime: string;
 }
 
 export interface AppointmentInputs {

+ 8 - 0
src/lib/funcs.ts

@@ -0,0 +1,8 @@
+import moment from "moment";
+
+export function isSameDate(dateA: Date, dateB: Date) {
+    const date1 = moment(dateA).format('YYYY-MM-DD');
+    const date2 = moment(dateB).format('YYYY-MM-DD');
+
+    return date1 === date2;
+}

+ 10 - 1
src/lib/index.ts

@@ -1,6 +1,15 @@
 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.
 export const api_host = "http://localhost:3000";
  //export let authToken: string = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY3OTIxYTcyMTNiM2NkYjc5NGZmNzQ2MiIsImlhdCI6MTczNzcyOTYwMiwiZXhwIjoxNzM3NzUxMjAyfQ.-TlU0izsSJnZYE79yG01fcRgzgBirZBJBsiQf2alwZ4";
-export let authToken: Writable<string> = writable("");
+
+export let authToken: Writable<string>;
+ 
+ if (browser) {
+   authToken = writable(localStorage.getItem("token") || "");
+ } else {
+   authToken = writable(""); // Fallback value for SSR
+ }
+ 

+ 1 - 1
src/lib/modules/Auth.svelte

@@ -31,7 +31,7 @@
                 const data: AuthTokenResponse = await response.json();
                 localStorage.setItem('token', data.token);
                 $authToken = data.token;
-                goto("/events");
+                goto("/user/events");
             } else {
                 errorMessage = "invalid credentials";
             }

+ 167 - 123
src/lib/modules/DatePicker.svelte

@@ -1,132 +1,176 @@
 <script lang="ts">
-    import { onMount } from 'svelte';
-  
-    let currentDate: Date = new Date();
-    let days: Array<{ day: number; isActive: boolean, isAvailable: boolean }> = [];
-    let currentMonth: string = '';
+	import type { AppointmentDateTimes } from '$lib/dbdata';
+	import { isSameDate } from '$lib/funcs';
+	import moment, { type Moment } from 'moment';
+	import { onMount } from 'svelte';
 
-    export let selectedDate: Date;
-  
-    /**
-     * Updates the calendar display by calculating the days of the current month.
-     */
-    const renderCalendar = (): void => {
-      const year: number = currentDate.getFullYear();
-      const month: number = currentDate.getMonth();
-      currentMonth = currentDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
-  
-      const firstDayOfMonth: Date = new Date(year, month, 1);
-      const lastDayOfMonth: Date = new Date(year, month + 1, 0);
-      const firstDayIndex: number = firstDayOfMonth.getDay();
-      const daysInMonth: number = lastDayOfMonth.getDate();
-  
-      // Reset days array
-      days = [];
-  
-      // Add empty cells for days before the first of the month
-      for (let i = 0; i < firstDayIndex; i++) {
-        days.push({ day: 0, isActive: false, isAvailable: false });
-      }
-  
-      // Add days of the month
-      for (let day = 1; day <= daysInMonth; day++) {
-        days.push({ day, isActive: day === 20, isAvailable: day / 2 == 5.5 }); // Example: Make day 20 active
-      }
-    };
-  
-    /**
-     * Changes the current month by a given offset and re-renders the calendar.
-     * @param offset - The number of months to move forward or backward.
-     */
-    const changeMonth = (offset: number): void => {
-      currentDate.setMonth(currentDate.getMonth() + offset);
-      renderCalendar();
-    };
-  
-    onMount(() => {
+	let currentDate: Date = new Date();
+	let days: Array<{ day: number; isActive: boolean; isAvailable: boolean }> = [];
+	
+  let currentMonth: string = '';
+	export let selectedDate: Date;
+  export let appointmentDateTimes: AppointmentDateTimes[];
+
+  $: {
+    if (appointmentDateTimes.length > 0) {
+      console.log(appointmentDateTimes);
       renderCalendar();
-    });
-  
-    /**
-     * Handles the selection of a day in the calendar.
-     * @param selectedDay - The day number that was clicked.
-     */
-    const selectDay = (selectedDay: number): void => {
-      days = days.map((d) => ({ ...d, isActive: d.day === selectedDay}));
-      const selectedDateValue = new Date(currentDate.getFullYear(), currentDate.getMonth(), selectedDay);
-      selectedDate = selectedDateValue;
-    };
-  </script>
-  
-  <style>
-    .date-picker {
-      border: 1px solid var(--border-color);
-      border-radius: var(--border-radius);
-      padding: 1rem;
-      max-width: 600px;
-      background: var(--background);
-      box-shadow: var(--shadow);
-    }
-    .date-picker-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 1rem;
-    }
-    .date-picker-grid {
-      display: grid;
-      grid-template-columns: repeat(7, 1fr);
-      gap: 0.5rem;
-      text-align: center;
-    }
-    .date-picker-day {
-      font-weight: bold;
-      color: var(--secondary);
     }
-    .date-picker-cell {
-      padding: 0.5rem;
-      border-radius: var(--border-radius);
-      cursor: pointer;
-      user-select: none;
-    }
-    .date-picker-cell:hover {
-      background-color: var(--hover-background);
-    }
-    .date-picker-cell.active {
-        background-color: var(--primary);
-        color: var(--background);
-    }
-    .empty {
-      pointer-events: none;
-      visibility: hidden;
+  } 
+
+  function isDayAvailable(year: number, month: number, day: number) {
+    const dateStr = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`;
+    const testDate: Date = moment(dateStr).toDate();
+
+    return appointmentDateTimes.some(date => isSameDate(testDate, date.date));    
+  }
+
+	/**
+	 * Updates the calendar display by calculating the days of the current month.
+	 */
+	const renderCalendar = (): void => {
+    console.log(currentDate);
+    const mDate: Moment = moment(currentDate);
+		const year: number = currentDate.getFullYear();
+		const month: number = mDate.month() + 1;
+		currentMonth = currentDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
+
+		const firstDayOfMonth: Date = new Date(year, month, 1);
+		const lastDayOfMonth: Date = new Date(year, month + 1, 0);
+		const firstDayIndex: number = firstDayOfMonth.getDay();
+		const daysInMonth: number = lastDayOfMonth.getDate();
+
+		// Reset days array
+		days = [];
+
+		// Add empty cells for days before the first of the month
+		for (let i = 0; i < firstDayIndex; i++) {
+			days.push({ day: 0, isActive: false, isAvailable: false });
+		}
+
+		// Add days of the month
+		for (let day = 1; day <= daysInMonth; day++) {
+			days.push({ day, isActive: false, isAvailable: isDayAvailable(year, month, day) }); // Example: Make day 20 active
+		}
+	};
+
+	/**
+	 * Changes the current month by a given offset and re-renders the calendar.
+	 * @param offset - The number of months to move forward or backward.
+	 */
+	const changeMonth = (offset: number): void => {
+		//currentDate.setMonth(currentDate.getMonth() + offset);
+    if (offset === -1) {
+      currentDate = moment(currentDate).subtract(1, "month").toDate();
+    } else {
+      currentDate = moment(currentDate).add(1, "month").toDate();
     }
-  </style>
-  
+    
+		renderCalendar();
+	};
+
+	onMount(() => {
+    currentDate = new Date();
+		renderCalendar();
+	});
+
+	/**
+	 * Handles the selection of a day in the calendar.
+	 * @param selectedDay - The day number that was clicked.
+	 */
+	const selectDay = (selectedDay: number): void => {
+		days = days.map((d) => ({ ...d, isActive: d.day === selectedDay }));
+		const selectedDateValue = new Date(
+			currentDate.getFullYear(),
+			currentDate.getMonth(),
+			selectedDay
+		);
+		selectedDate = selectedDateValue;
+	};
+</script>
 
 <div class="date-picker border center">
-    <div class="date-picker-header">
-        <button class="button square secondary medium" on:click={() => changeMonth(-1)}>&lt;</button>
-        <h4 id="currentMonth" class="small wrap center-align">{currentMonth}</h4>
-        <button class="button square secondary medium" on:click={() => changeMonth(1)}>&gt;</button>
-    </div>
-    <div class="date-picker-grid">
-        <!-- Days of the week -->
-        <div class="date-picker-day">Sun</div>
-        <div class="date-picker-day">Mon</div>
-        <div class="date-picker-day">Tue</div>
-        <div class="date-picker-day">Wed</div>
-        <div class="date-picker-day">Thu</div>
-        <div class="date-picker-day">Fri</div>
-        <div class="date-picker-day">Sat</div>
+	<div class="date-picker-header">
+		<button class="button square secondary medium" on:click={() => changeMonth(-1)}>&lt;</button>
+		<h4 id="currentMonth" class="small wrap center-align">{currentMonth}</h4>
+		<button class="button square secondary medium" on:click={() => changeMonth(1)}>&gt;</button>
+	</div>
+	<div class="date-picker-grid">
+		<!-- Days of the week -->
+		<div class="date-picker-day">Sun</div>
+		<div class="date-picker-day">Mon</div>
+		<div class="date-picker-day">Tue</div>
+		<div class="date-picker-day">Wed</div>
+		<div class="date-picker-day">Thu</div>
+		<div class="date-picker-day">Fri</div>
+		<div class="date-picker-day">Sat</div>
 
-        <!-- Render days -->
-        {#each days as { day, isActive, isAvailable }, index}
-            <div
-                class="date-picker-cell {isAvailable && !isActive ? 'secondary': ''} {isActive ? 'active' : ''} {day === 0 ? 'empty' : ''}"
-                on:click={() => day && selectDay(day)}
-            >
-                {day || ''}
-            </div>
-        {/each}
-    </div>
+		<!-- Render days -->
+		{#each days as { day, isActive, isAvailable }, index}
+			{#if isAvailable}
+      <!-- svelte-ignore a11y_click_events_have_key_events -->
+      <!-- svelte-ignore a11y_no_static_element_interactions -->
+				<div
+					class="date-picker-cell {!isActive ? 'secondary' : ''} {isActive
+						? 'active'
+						: ''} {day === 0 ? 'empty' : ''}"
+					on:click={() => day && selectDay(day)}
+				>
+					{day || ''}
+				</div>
+			{:else}
+      <!-- svelte-ignore a11y_click_events_have_key_events -->
+      <!-- svelte-ignore a11y_no_static_element_interactions -->
+				<div
+					class="date-picker-cell"
+				>
+					{day || ''}
+				</div>
+			{/if}
+		{/each}
+	</div>
 </div>
+
+<style>
+	.date-picker {
+		border: 1px solid var(--border-color);
+		border-radius: var(--border-radius);
+		padding: 1rem;
+		max-width: 600px;
+		background: var(--background);
+		box-shadow: var(--shadow);
+	}
+	.date-picker-header {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		margin-bottom: 1rem;
+	}
+	.date-picker-grid {
+		display: grid;
+		grid-template-columns: repeat(7, 1fr);
+		gap: 0.5rem;
+		text-align: center;
+	}
+	.date-picker-day {
+		font-weight: bold;
+		color: var(--secondary);
+	}
+	.date-picker-cell {
+		padding: 0.5rem;
+		border-radius: var(--border-radius);
+		cursor: pointer;
+		user-select: none;
+	}
+	.date-picker-cell:hover {
+		background-color: var(--hover-background);
+	}
+	.date-picker-cell.active {
+		background-color: var(--primary);
+		color: var(--background);
+	}
+	.empty {
+		pointer-events: none;
+		visibility: hidden;
+	}
+</style>

+ 17 - 4
src/lib/modules/DateTimes.svelte

@@ -1,18 +1,31 @@
 <script lang="ts">
 	import type { AppointmentDateTimes } from "$lib/dbdata";
+	import { isSameDate } from "$lib/funcs";
+	import moment from "moment";
+	import { onMount } from "svelte";
 
     export let availableTimes: AppointmentDateTimes[] = [];
+    export let selectedDate: Date = new Date();
 
-    function redirectToBooking(date: Date, startTime: string) {
-        console.log(date, startTime);
+    function redirectToBooking(date: Date) {
+        console.log(date);
     }
+
+    function extractTime(date: Date): string {
+        return moment(date).format("HH:mm");
+    }
+
+
 </script>
 
 
 <h4 class="center-align top-margin">Times</h4>
+
 <article>
     {#each availableTimes as time}
-        <button on:click={() => redirectToBooking(time.date, time.startTime)} 
-            class="responsive wave border bottom-margin">{time.startTime}</button>
+        {#if isSameDate(time.date, selectedDate)}
+            <button on:click={() => redirectToBooking(time.date)} 
+                class="responsive wave border bottom-margin">{extractTime(time.date)}</button>
+        {/if}
     {/each}
 </article>

+ 13 - 1
src/routes/+layout.svelte

@@ -1,3 +1,15 @@
 <main class="responsive max">
     <slot/>
-</main>
+</main>
+
+<script lang="ts">
+	import { authToken } from "$lib";
+	import { onMount } from "svelte";
+
+    onMount(() => {
+        if (localStorage) {
+            console.log("execute");
+            $authToken = localStorage.getItem("token") || "";
+        }
+    });
+</script>

+ 3 - 0
src/routes/+page.server.ts

@@ -0,0 +1,3 @@
+export const ssr = false;
+export const csr = true;
+export const prerender = true;

+ 26 - 6
src/routes/appointment/+page.svelte

@@ -1,9 +1,11 @@
 <script lang="ts">
-	import type { AppointmentDateTimes } from '$lib/dbdata';
+	import { api_host } from '$lib';
+	import type { Appointment, AppointmentDateTimes } from '$lib/dbdata';
 	import AppointmentConfirmDialog from '$lib/modules/AppointmentConfirmDialog.svelte';
     import AppointmentInfo from '$lib/modules/AppointmentInfo.svelte';
     import DatePicker from '$lib/modules/DatePicker.svelte';
 	import DateTimes from '$lib/modules/DateTimes.svelte';
+	import { onMount } from 'svelte';
 
     let title: string = "Appointment";
     let location: string = "Somewhere";
@@ -12,9 +14,7 @@
     let selectedDate: Date = new Date();
 
     let appointmentDateTimes: AppointmentDateTimes[] = [
-        {date: new Date(), startTime: "16:00"},
-        {date: new Date(), startTime: "17:00"},
-        {date: new Date(), startTime: "18:00"}
+
     ];
 
     $: {
@@ -27,6 +27,24 @@
         console.log(date);
     }
 
+    async function getAppointmentInfo(appointmentId: string) {
+        const response = await fetch(`${api_host}/api/schedule?appointmentId=${appointmentId}`, {
+                method: "GET",
+                headers: {
+                    "Content-Type": "application/json"
+                }
+            })
+        const js: Appointment = await response.json();
+        appointmentDateTimes = js.times;
+        title = js.title;
+        description = js.description;
+        location = js.place;
+    }
+
+    onMount(async () => {
+        await getAppointmentInfo("6799f8a6b9285925c01491e0");
+    });
+
 </script>
   
 <style>
@@ -39,11 +57,13 @@
     }
 </style>
 
-<DatePicker bind:selectedDate />
+<DatePicker bind:appointmentDateTimes bind:selectedDate />
 
 <div class="top-margin w-80 inline-margin"> 
     <AppointmentInfo bind:title bind:description bind:location />
-    <DateTimes bind:availableTimes={appointmentDateTimes} />
+    {#key selectedDate}
+        <DateTimes bind:selectedDate bind:availableTimes={appointmentDateTimes} />    
+    {/key}
 </div>
 
 <AppointmentConfirmDialog active={true} />