feat(event): display user calendars on event creation and edit pages
All checks were successful
container-scan / Container Scan (pull_request) Successful in 8m37s
docker-build / docker (pull_request) Successful in 8m29s
tests / Tests (pull_request) Successful in 6m23s

This commit is contained in:
Dominik 2025-06-28 17:11:56 +02:00
parent 2e31d935a6
commit 6231d6cd45
Signed by: dominik
GPG key ID: 06A4003FC5049644
12 changed files with 615 additions and 337 deletions

View file

@ -7,7 +7,6 @@ import '@/components/react-big-calendar.css';
import 'react-big-calendar/lib/addons/dragAndDrop/styles.css';
import CustomToolbar from '@/components/custom-toolbar';
import React from 'react';
import { useGetApiUserUserCalendar } from '@/generated/api/user/user';
import { useRouter } from 'next/navigation';
import { usePatchApiEventEventID } from '@/generated/api/event/event';
import { useSession } from 'next-auth/react';
@ -17,6 +16,11 @@ import { ErrorBoundary } from 'react-error-boundary';
import { Button } from '@/components/ui/button';
import { fromZodIssue } from 'zod-validation-error/v4';
import type { $ZodIssue } from 'zod/v4/core';
import { useGetApiCalendar } from '@/generated/api/calendar/calendar';
//import {
// generateColor,
// generateSecondaryColor,
//} from '@marko19907/string-to-color';
moment.updateLocale('en', {
week: {
@ -25,12 +29,31 @@ moment.updateLocale('en', {
},
});
function eventPropGetter() {
// event: {
// id: string;
// start: Date;
// end: Date;
// type: UserCalendarSchemaItem['type'];
// userId?: string;
// }
return {
// style: {
// backgroundColor: generateColor(event.userId || 'defaultColor', {
// saturation: 0.7,
// lightness: 0.5,
// }),
// },
};
}
const DaDRBCalendar = withDragAndDrop<
{
id: string;
start: Date;
end: Date;
type: UserCalendarSchemaItem['type'];
userId?: string;
},
{
id: string;
@ -44,9 +67,20 @@ const localizer = momentLocalizer(moment);
export default function Calendar({
userId,
height,
additionalEvents = [],
className,
}: {
userId?: string;
userId?: string | string[];
height: string;
additionalEvents?: {
id: string;
title: string;
start: Date;
end: Date;
type: UserCalendarSchemaItem['type'];
userId?: string;
}[];
className?: string;
}) {
return (
<QueryErrorResetBoundary>
@ -67,10 +101,26 @@ export default function Calendar({
</div>
)}
>
{userId ? (
<CalendarWithUserEvents userId={userId} height={height} />
{typeof userId === 'string' ? (
<CalendarWithUserEvents
userId={userId}
height={height}
additionalEvents={additionalEvents}
className={className}
/>
) : Array.isArray(userId) && userId.length > 0 ? (
<CalendarWithMultiUserEvents
userIds={userId}
height={height}
additionalEvents={additionalEvents}
className={className}
/>
) : (
<CalendarWithoutUserEvents height={height} />
<CalendarWithoutUserEvents
height={height}
additionalEvents={additionalEvents}
className={className}
/>
)}
</ErrorBoundary>
)}
@ -81,9 +131,20 @@ export default function Calendar({
function CalendarWithUserEvents({
userId,
height,
additionalEvents,
className,
}: {
userId: string;
height: string;
additionalEvents?: {
id: string;
title: string;
start: Date;
end: Date;
type: UserCalendarSchemaItem['type'];
userId?: string;
}[];
className?: string;
}) {
const sesstion = useSession();
const [currentView, setCurrentView] = React.useState<
@ -92,9 +153,9 @@ function CalendarWithUserEvents({
const [currentDate, setCurrentDate] = React.useState<Date>(new Date());
const router = useRouter();
const { data, refetch, error, isError } = useGetApiUserUserCalendar(
userId,
const { data, refetch, error, isError } = useGetApiCalendar(
{
userIds: [userId, userId + '_blocked'],
start: moment(currentDate)
.startOf(
currentView === 'agenda'
@ -137,6 +198,8 @@ function CalendarWithUserEvents({
return (
<DaDRBCalendar
className={className}
eventPropGetter={eventPropGetter}
localizer={localizer}
culture='de-DE'
defaultView='week'
@ -152,15 +215,17 @@ function CalendarWithUserEvents({
onNavigate={(date) => {
setCurrentDate(date);
}}
events={
data?.data.calendar.map((event) => ({
events={[
...(data?.data.calendar.map((event) => ({
id: event.id,
title: event.type === 'event' ? event.title : 'Blocker',
start: new Date(event.start_time),
end: new Date(event.end_time),
type: event.type,
})) ?? []
}
userId: event.users[0],
})) ?? []),
...(additionalEvents ?? []),
]}
onSelectEvent={(event) => {
router.push(`/events/${event.id}`);
}}
@ -228,7 +293,114 @@ function CalendarWithUserEvents({
);
}
function CalendarWithoutUserEvents({ height }: { height: string }) {
function CalendarWithMultiUserEvents({
userIds,
height,
additionalEvents,
className,
}: {
userIds: string[];
height: string;
additionalEvents?: {
id: string;
title: string;
start: Date;
end: Date;
type: UserCalendarSchemaItem['type'];
userId?: string;
}[];
className?: string;
}) {
const [currentView, setCurrentView] = React.useState<
'month' | 'week' | 'day' | 'agenda' | 'work_week'
>('week');
const [currentDate, setCurrentDate] = React.useState<Date>(new Date());
const { data, error, isError } = useGetApiCalendar(
{
userIds: userIds,
start: moment(currentDate)
.startOf(
currentView === 'agenda'
? 'month'
: currentView === 'work_week'
? 'week'
: currentView,
)
.toISOString(),
end: moment(currentDate)
.endOf(
currentView === 'agenda'
? 'month'
: currentView === 'work_week'
? 'week'
: currentView,
)
.toISOString(),
},
{
query: {
refetchOnWindowFocus: true,
refetchOnReconnect: true,
refetchOnMount: true,
},
},
);
if (isError) {
throw error.response?.data || 'Failed to fetch calendar data';
}
return (
<DaDRBCalendar
className={className}
eventPropGetter={eventPropGetter}
localizer={localizer}
culture='de-DE'
defaultView='week'
components={{
toolbar: CustomToolbar,
}}
style={{
height: height,
}}
onView={setCurrentView}
view={currentView}
date={currentDate}
onNavigate={(date) => {
setCurrentDate(date);
}}
events={[
...(data?.data.calendar.map((event) => ({
id: event.id,
title: event.type === 'event' ? event.title : 'Blocker',
start: new Date(event.start_time),
end: new Date(event.end_time),
type: event.type,
userId: event.users[0],
})) ?? []),
...(additionalEvents ?? []),
]}
/>
);
}
function CalendarWithoutUserEvents({
height,
additionalEvents,
className,
}: {
height: string;
additionalEvents?: {
id: string;
title: string;
start: Date;
end: Date;
type: UserCalendarSchemaItem['type'];
userId?: string;
}[];
className?: string;
}) {
const [currentView, setCurrentView] = React.useState<
'month' | 'week' | 'day' | 'agenda' | 'work_week'
>('week');
@ -236,6 +408,8 @@ function CalendarWithoutUserEvents({ height }: { height: string }) {
return (
<DaDRBCalendar
className={className}
eventPropGetter={eventPropGetter}
localizer={localizer}
culture='de-DE'
defaultView='week'
@ -251,6 +425,7 @@ function CalendarWithoutUserEvents({ height }: { height: string }) {
onNavigate={(date) => {
setCurrentDate(date);
}}
events={additionalEvents}
/>
);
}