20-calendar-system #37
@@ -9,7 +9,7 @@ app.use(morgan('dev'))
|
|||||||
|
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: ['https://aj17thdev.nexuszone.net', 'http://localhost:5173'], // your SPA origins
|
origin: ['https://aj17thdev.nexuszone.net', 'http://localhost:5173'], // your SPA origins
|
||||||
credentials: true
|
credentials: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { createEvent, getEventAttendance, getEventDetails, getShortEventsInRange, setAttendanceStatus, setEventCancelled } from "../services/calendarService";
|
import { createEvent, getEventAttendance, getEventDetails, getShortEventsInRange, setAttendanceStatus, setEventCancelled, updateEvent } from "../services/calendarService";
|
||||||
import { CalendarAttendance, CalendarEvent } from "@app/shared/types/calendar";
|
import { CalendarAttendance, CalendarEvent } from "@app/shared/types/calendar";
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
@@ -92,7 +92,6 @@ r.post('/', async (req: Request, res: Response) => {
|
|||||||
event.creator_id = member;
|
event.creator_id = member;
|
||||||
event.start = new Date(event.start);
|
event.start = new Date(event.start);
|
||||||
event.end = new Date(event.end);
|
event.end = new Date(event.end);
|
||||||
console.log(event);
|
|
||||||
createEvent(event);
|
createEvent(event);
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -101,5 +100,19 @@ r.post('/', async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r.put('/', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
let event: CalendarEvent = req.body;
|
||||||
|
event.start = new Date(event.start);
|
||||||
|
event.end = new Date(event.end);
|
||||||
|
console.log(event);
|
||||||
|
updateEvent(event);
|
||||||
|
res.sendStatus(200);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update event:', error);
|
||||||
|
res.status(500).json(error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
module.exports.calendarRouter = r;
|
module.exports.calendarRouter = r;
|
||||||
@@ -34,7 +34,7 @@ export async function updateEvent(eventObject: CalendarEvent) {
|
|||||||
end = ?,
|
end = ?,
|
||||||
location = ?,
|
location = ?,
|
||||||
color = ?,
|
color = ?,
|
||||||
description = ?,
|
description = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -54,7 +54,6 @@ export async function updateEvent(eventObject: CalendarEvent) {
|
|||||||
|
|
||||||
export async function setEventCancelled(eventID: number, cancelled: boolean) {
|
export async function setEventCancelled(eventID: number, cancelled: boolean) {
|
||||||
const input = cancelled ? 1 : 0;
|
const input = cancelled ? 1 : 0;
|
||||||
console.log(cancelled, input);
|
|
||||||
const sql = `
|
const sql = `
|
||||||
UPDATE calendar_events
|
UPDATE calendar_events
|
||||||
SET cancelled = ?
|
SET cancelled = ?
|
||||||
@@ -73,7 +72,6 @@ export async function getShortEventsInRange(startDate: string, endDate: string):
|
|||||||
ORDER BY start ASC
|
ORDER BY start ASC
|
||||||
`;
|
`;
|
||||||
const res: CalendarEventShort[] = await pool.query(sql, [startDate, endDate]);
|
const res: CalendarEventShort[] = await pool.query(sql, [startDate, endDate]);
|
||||||
console.log(res);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,18 @@ export async function createCalendarEvent(eventData: CalendarEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function editCalendarEvent(eventData: CalendarEvent) {
|
export async function editCalendarEvent(eventData: CalendarEvent) {
|
||||||
|
let res = await fetch(`${addr}/calendar`, {
|
||||||
|
method: "PUT",
|
||||||
|
credentials: "include",
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(eventData)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
throw new Error(`Failed to set attendance: ${res.status} ${res.statusText}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setCancelCalendarEvent(eventID: number, cancel: boolean) {
|
export async function setCancelCalendarEvent(eventID: number, cancel: boolean) {
|
||||||
|
|||||||
@@ -45,13 +45,18 @@ function roundToNextHour(d = new Date()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
import { calendarEventSchema, parseLocalDateTime } from '@shared/schemas/calendarEventSchema'
|
import { calendarEventSchema, parseLocalDateTime } from '@shared/schemas/calendarEventSchema'
|
||||||
import { createCalendarEvent } from "@/api/calendar"
|
import { createCalendarEvent, editCalendarEvent } from "@/api/calendar"
|
||||||
|
import DialogDescription from "../ui/dialog/DialogDescription.vue"
|
||||||
const formSchema = toTypedSchema(calendarEventSchema)
|
const formSchema = toTypedSchema(calendarEventSchema)
|
||||||
|
|
||||||
// ---------- dialog state & defaults ----------
|
// ---------- dialog state & defaults ----------
|
||||||
const clickedDate = ref<string | null>(null);
|
const clickedDate = ref<string | null>(null);
|
||||||
const dialogOpen = ref(false)
|
const dialogOpen = ref(false)
|
||||||
function openDialog(dateStr?: string) {
|
const dialogMode = ref<'create' | 'edit'>('create');
|
||||||
|
const editEvent = ref<CalendarEvent | null>();
|
||||||
|
function openDialog(dateStr?: string, mode?: 'create' | 'edit', event?: CalendarEvent) {
|
||||||
|
dialogMode.value = mode ?? 'create';
|
||||||
|
editEvent.value = event ?? null;
|
||||||
clickedDate.value = dateStr ?? null;
|
clickedDate.value = dateStr ?? null;
|
||||||
dialogOpen.value = true
|
dialogOpen.value = true
|
||||||
initialValues.value = makeInitialValues()
|
initialValues.value = makeInitialValues()
|
||||||
@@ -59,6 +64,22 @@ function openDialog(dateStr?: string) {
|
|||||||
defineExpose({ openDialog })
|
defineExpose({ openDialog })
|
||||||
|
|
||||||
function makeInitialValues() {
|
function makeInitialValues() {
|
||||||
|
|
||||||
|
if (dialogMode.value === 'edit' && editEvent.value) {
|
||||||
|
const e = editEvent.value;
|
||||||
|
return {
|
||||||
|
name: e.name,
|
||||||
|
startDate: toLocalDateString(new Date(e.start)),
|
||||||
|
startTime: toLocalTimeString(new Date(e.start)),
|
||||||
|
endDate: toLocalDateString(new Date(e.end)),
|
||||||
|
endTime: toLocalTimeString(new Date(e.end)),
|
||||||
|
location: e.location,
|
||||||
|
color: e.color,
|
||||||
|
description: e.description,
|
||||||
|
id: e.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let start: Date;
|
let start: Date;
|
||||||
if (clickedDate.value) {
|
if (clickedDate.value) {
|
||||||
const local = new Date(clickedDate.value + "T00:00:00");
|
const local = new Date(clickedDate.value + "T00:00:00");
|
||||||
@@ -95,6 +116,7 @@ async function onSubmit(vals: z.infer<typeof calendarEventSchema>) {
|
|||||||
const end = parseLocalDateTime(vals.endDate, vals.endTime)
|
const end = parseLocalDateTime(vals.endDate, vals.endTime)
|
||||||
|
|
||||||
const event: CalendarEvent = {
|
const event: CalendarEvent = {
|
||||||
|
id: vals.id ?? null,
|
||||||
name: vals.name,
|
name: vals.name,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
@@ -104,8 +126,11 @@ async function onSubmit(vals: z.infer<typeof calendarEventSchema>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("Submitting CalendarEvent:", event)
|
if (dialogMode.value === "edit") {
|
||||||
await createCalendarEvent(event);
|
await editCalendarEvent(event);
|
||||||
|
} else {
|
||||||
|
await createCalendarEvent(event);
|
||||||
|
}
|
||||||
emit('reload');
|
emit('reload');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -128,6 +153,7 @@ const formRef = ref(null);
|
|||||||
<DialogContent class="sm:max-w-[520px]">
|
<DialogContent class="sm:max-w-[520px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Create Event</DialogTitle>
|
<DialogTitle>Create Event</DialogTitle>
|
||||||
|
<DialogDescription></DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<form id="dialogForm" class="grid grid-cols-1 gap-4"
|
<form id="dialogForm" class="grid grid-cols-1 gap-4"
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ watch(
|
|||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'close'): void
|
(e: 'close'): void
|
||||||
(e: 'reload'): void
|
(e: 'reload'): void
|
||||||
|
(e: 'edit', event: CalendarEvent): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// const activeEvent = computed(() => props.event)
|
// const activeEvent = computed(() => props.event)
|
||||||
@@ -91,6 +92,12 @@ async function setCancel(isCancelled: boolean) {
|
|||||||
emit("reload");
|
emit("reload");
|
||||||
activeEvent.value = await getCalendarEvent(activeEvent.value.id);
|
activeEvent.value = await getCalendarEvent(activeEvent.value.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function forceReload() {
|
||||||
|
activeEvent.value = await getCalendarEvent(activeEvent.value.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({forceReload})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -109,7 +116,7 @@ async function setCancel(isCancelled: boolean) {
|
|||||||
</button>
|
</button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem @click="emit('edit', activeEvent)">
|
||||||
Edit
|
Edit
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem v-if="activeEvent.cancelled"
|
<DropdownMenuItem v-if="activeEvent.cancelled"
|
||||||
|
|||||||
@@ -128,7 +128,6 @@ const calendarOptions = ref({
|
|||||||
calendarOptions.value.datesSet = onDatesSet
|
calendarOptions.value.datesSet = onDatesSet
|
||||||
|
|
||||||
watch(() => route.params.id, async (newID) => {
|
watch(() => route.params.id, async (newID) => {
|
||||||
console.log(newID)
|
|
||||||
if (newID === undefined) {
|
if (newID === undefined) {
|
||||||
panelOpen.value = false;
|
panelOpen.value = false;
|
||||||
currentEventID.value = null;
|
currentEventID.value = null;
|
||||||
@@ -150,6 +149,8 @@ function onCreateEvent() {
|
|||||||
onDateClick({ dateStr: iso })
|
onDateClick({ dateStr: iso })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eventViewRef = ref(null);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
onDatesSet()
|
onDatesSet()
|
||||||
})
|
})
|
||||||
@@ -158,7 +159,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<CreateCalendarEvent ref="dialogRef" @reload="loadEvents()"></CreateCalendarEvent>
|
<CreateCalendarEvent ref="dialogRef" @reload="loadEvents(); eventViewRef.forceReload();"></CreateCalendarEvent>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="flex-1 min-h-0 mt-5">
|
<div class="flex-1 min-h-0 mt-5">
|
||||||
<div class="h-[80vh] min-h-0">
|
<div class="h-[80vh] min-h-0">
|
||||||
@@ -215,7 +216,7 @@ onMounted(() => {
|
|||||||
<aside v-if="panelOpen"
|
<aside v-if="panelOpen"
|
||||||
class="3xl:w-lg 2xl:w-md border-l bg-card text-foreground flex flex-col overflow-auto scrollbar-themed"
|
class="3xl:w-lg 2xl:w-md border-l bg-card text-foreground flex flex-col overflow-auto scrollbar-themed"
|
||||||
:style="{ height: 'calc(100vh - 61px)', position: 'sticky', top: '64px' }">
|
:style="{ height: 'calc(100vh - 61px)', position: 'sticky', top: '64px' }">
|
||||||
<ViewCalendarEvent @close="() => { router.push('/calendar'); }" @reload="loadEvents()">
|
<ViewCalendarEvent ref="eventViewRef" @close="() => { router.push('/calendar'); }" @reload="loadEvents()" @edit="(val) => {dialogRef.openDialog(null, 'edit', val)}">
|
||||||
</ViewCalendarEvent>
|
</ViewCalendarEvent>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user