improved calendar design

This commit is contained in:
2025-03-04 16:51:59 +01:00
parent 0e663ce24e
commit 5735dc906d
3 changed files with 123 additions and 182 deletions
+112 -172
View File
@@ -1,191 +1,131 @@
export interface CalendarSchedule {
date: Date;
platforms: { user: string }[];
id: string;
}
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from '@/components/ui/context-menu';
import { PlusCircle, Trash2 } from 'lucide-react';
import axios from 'axios';
import { Pencil, PlusCircle, Trash } from 'lucide-react';
import React from 'react';
export interface CalendarSchedule {
date: Date;
platforms: { user: string }[];
const hours = Array.from({ length: 24 }, (_, i) => `${i}:00`);
const days = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
export interface Event {
time: Date;
title: string;
id: string;
}
// Function to get the current week's dates (Monday - Sunday)
const getCurrentWeek = (): { label: string; date: Date }[] => {
const today = new Date();
const dayOfWeek = today.getDay(); // 0 (Sun) - 6 (Sat)
const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; // Adjust for Monday start
interface CalendarProps {
events: Event[];
setDate: (date: Date) => void;
setDialog: (dialog: boolean) => void;
setEvents: (events: Event[]) => void;
}
return Array.from({ length: 7 }, (_, i) => {
const date = new Date(today);
date.setDate(today.getDate() + mondayOffset + i);
return {
label: date.toLocaleDateString('en-US', { weekday: 'short' }), // 'Mon', 'Tue', etc.
date,
};
});
};
// Time slots from 12 PM to 11 PM
const times: { label: string; hour: number }[] = Array.from(
{ length: 12 },
(_, i) => ({
label: `${(i + 12) % 12 || 12} PM`, // '12 PM', '1 PM', ..., '11 PM'
hour: i + 12,
})
);
export default function Calendar({
setDialog,
const Calendar: React.FC<CalendarProps> = ({
events,
setDate,
setDialog,
setEvents,
}: {
setDialog: (b: boolean) => void;
events: CalendarSchedule[];
setDate: (d: Date) => void;
setEvents: (events: CalendarSchedule[]) => void;
}) {
const weekdays = getCurrentWeek();
}) => {
return (
<div className="flex flex-col">
<div className="grid grid-cols-1 w-full h-20 mb-2">
<div className="flex gap-2">
{/* Time Labels */}
<div className="w-20 flex flex-col gap-2">
<div className="h-24"></div>{' '}
{/* Empty space for alignment */}
<div className="grid grid-rows-12 h-full gap-2">
{times.map((time) => (
<div
key={time.hour}
className="flex pr-2 h-16 border rounded-xl"
>
<p className="size-max m-auto">
{time.label}
</p>
</div>
))}
</div>
</div>
{/* Calendar Grid */}
<div className="grid grid-cols-7 gap-2 flex-1">
{weekdays.map((weekday) => (
<div
key={weekday.date.toDateString()}
className="flex flex-col h-full gap-2"
>
{/* Day Header */}
<div className="border rounded-md h-24 flex">
<p className="size-max mx-auto">
{weekday.label}
</p>
</div>
{/* Time Slots */}
<div className="grid grid-rows-12 h-full gap-2">
{times.map((time) => {
const event = events.find(
(e) =>
e.date.getFullYear() ===
weekday.date.getFullYear() &&
e.date.getMonth() ===
weekday.date.getMonth() &&
e.date.getDate() ===
weekday.date.getDate() &&
e.date.getHours() === time.hour
);
return (
<ContextMenu
key={`${weekday.date.toDateString()}-${
time.hour
}`}
>
<ContextMenuTrigger>
<div
className={`border rounded-xl w-full h-16 ${
event
? 'bg-primary/20'
: ''
}`}
>
{event && (
<div className="p-2 text-sm flex items-center justify-center">
{/* Event indicator */}
</div>
)}
</div>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem
onClick={() => {
setDialog(true);
setDate(
new Date(
weekday.date.getFullYear(),
weekday.date.getMonth(),
weekday.date.getDate(),
time.hour,
0,
0
)
);
}}
>
<PlusCircle className="mr-2 h-4 w-4" />
<p>New post</p>
</ContextMenuItem>
{event && (
<ContextMenuItem
onClick={() => {
axios
.delete(
`/api/posts`,
{
params: {
schedule:
event.id,
},
}
)
.then(
() => {
setEvents(
events.filter(
(
e
) =>
e.id !==
event.id
)
);
}
);
}}
>
<Trash2 className="mr-2 h-4 w-4 text-red-500" />
<p className="text-red-500">
Delete post
</p>
</ContextMenuItem>
)}
</ContextMenuContent>
</ContextMenu>
);
})}
</div>
</div>
))}
</div>
<div className="grid grid-cols-[auto,repeat(7,1fr)] border text-center text-sm">
<div className="border p-2 font-bold">Time</div>
{days.map((day) => (
<div key={day} className="border p-2 font-bold">
{day}
</div>
</div>
))}
{hours.map((hour, rowIdx) => (
<React.Fragment key={hour}>
<div className="border p-2">{hour}</div>
{days.map((_, colIdx) => {
const event = events.find(
(e: Event) =>
e.time.getDay() === colIdx &&
e.time.getHours() === rowIdx
);
if (!event) {
return (
<ContextMenu key={`${colIdx}-${rowIdx}`}>
<ContextMenuTrigger asChild>
<div className="relative border h-16"></div>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem
onClick={() => {
setDate(
new Date(2024, 3, 4, rowIdx)
);
setDialog(true);
}}
>
<PlusCircle className="w-4 h-4 mr-1" />
New Post
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
);
}
return (
<ContextMenu key={`${colIdx}-${rowIdx}`}>
<ContextMenuTrigger asChild>
<div className="relative border h-16">
{event && (
<div className="absolute inset-0 bg-blue-500 text-white p-1 text-xs rounded">
{event.title}
</div>
)}
</div>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem>
<Pencil className="w-4 h-4 mr-1" />
Edit
</ContextMenuItem>
<ContextMenuItem
className="text-red-600"
onClick={() => {
axios.delete(
`/api/posts?schedule=${event.id}`
);
setEvents(
events.filter(
(e) => e.id !== event.id
)
);
}}
>
<Trash className="w-4 h-4 mr-1" />
Delete
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
);
})}
</React.Fragment>
))}
</div>
);
}
};
export default Calendar;
+6 -5
View File
@@ -7,12 +7,12 @@ import { format, parse } from 'date-fns';
import PostDialog, { today } from './postDialog';
import axios from 'axios';
import Cookies from 'js-cookie';
import Calendar, { CalendarSchedule } from './calendar';
import Calendar, { CalendarSchedule, Event } from './calendar';
import { DATE_FORMAT } from '@/lib/types';
export default function CalendarApp() {
const sessionId = Cookies.get('session_id') || '';
const [events, setEvents] = useState<CalendarSchedule[]>([]);
const [events, setEvents] = useState<Event[]>([]);
useEffect(() => {
axios
@@ -20,10 +20,11 @@ export default function CalendarApp() {
withCredentials: true,
})
.then((response) => {
const v = response.data.schedules.map((e: any) => ({
date: parse(e.scheduled, DATE_FORMAT, new Date()),
const v: Event[] = response.data.schedules.map((e: any) => ({
time: parse(e.scheduled, DATE_FORMAT, new Date()),
platforms: e.platforms,
id: e._id,
title: 'Post',
}));
setEvents(v);
})
@@ -43,7 +44,7 @@ export default function CalendarApp() {
setDate={setDate}
newPostDialogOpen={postDialog}
setNewPostDialogOpen={setDialog}
setEvents={(e) => {
addEvents={(e) => {
setEvents([...events, e]);
}}
/>
+5 -5
View File
@@ -26,14 +26,14 @@ import { Label } from '@/components/ui/label';
import { useState } from 'react';
import axios from 'axios';
import { DATE_FORMAT, PostRequest, ScheduleRequest } from '@/lib/types';
import { CalendarSchedule } from './calendar';
import { CalendarSchedule, Event } from './calendar';
type PostDialogProps = {
newPostDialogOpen: boolean;
setNewPostDialogOpen: (b: boolean) => void;
date: Date;
setDate: (d: Date) => void;
setEvents: (e: CalendarSchedule) => void;
addEvents: (e: Event) => void;
};
export const today = new Date();
@@ -44,7 +44,7 @@ export default function PostDialog({
setNewPostDialogOpen,
date,
setDate,
setEvents: addEvent,
addEvents: addEvent,
}: PostDialogProps) {
const [content, setContent] = useState('');
@@ -154,8 +154,8 @@ export default function PostDialog({
addEvent({
id: data.id,
date,
platforms: [{ user: 'andjksds' }],
time: date,
title: 'New Post',
});
setNewPostDialogOpen(false);