improved calendar design
This commit is contained in:
+83
-143
@@ -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 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>
|
||||
</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>
|
||||
{hours.map((hour, rowIdx) => (
|
||||
<React.Fragment key={hour}>
|
||||
<div className="border p-2">{hour}</div>
|
||||
|
||||
{/* Time Slots */}
|
||||
<div className="grid grid-rows-12 h-full gap-2">
|
||||
{times.map((time) => {
|
||||
{days.map((_, colIdx) => {
|
||||
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
|
||||
(e: Event) =>
|
||||
e.time.getDay() === colIdx &&
|
||||
e.time.getHours() === rowIdx
|
||||
);
|
||||
|
||||
if (!event) {
|
||||
return (
|
||||
<ContextMenu
|
||||
key={`${weekday.date.toDateString()}-${
|
||||
time.hour
|
||||
}`}
|
||||
>
|
||||
<ContextMenuTrigger>
|
||||
<div
|
||||
className={`border rounded-xl w-full h-16 ${
|
||||
event
|
||||
? 'bg-primary/20'
|
||||
: ''
|
||||
}`}
|
||||
<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="p-2 text-sm flex items-center justify-center">
|
||||
{/* Event indicator */}
|
||||
<div className="absolute inset-0 bg-blue-500 text-white p-1 text-xs rounded">
|
||||
{event.title}
|
||||
</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>
|
||||
<Pencil className="w-4 h-4 mr-1" />
|
||||
Edit
|
||||
</ContextMenuItem>
|
||||
|
||||
{event && (
|
||||
<ContextMenuItem
|
||||
className="text-red-600"
|
||||
onClick={() => {
|
||||
axios
|
||||
.delete(
|
||||
`/api/posts`,
|
||||
{
|
||||
params: {
|
||||
schedule:
|
||||
event.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
axios.delete(
|
||||
`/api/posts?schedule=${event.id}`
|
||||
);
|
||||
setEvents(
|
||||
events.filter(
|
||||
(
|
||||
e
|
||||
) =>
|
||||
e.id !==
|
||||
event.id
|
||||
(e) => e.id !== event.id
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4 text-red-500" />
|
||||
<p className="text-red-500">
|
||||
Delete post
|
||||
</p>
|
||||
<Trash className="w-4 h-4 mr-1" />
|
||||
Delete
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Calendar;
|
||||
|
||||
@@ -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]);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user