small changes

This commit is contained in:
2025-03-07 15:52:24 +01:00
parent 05a94d7192
commit 2886272634
5 changed files with 156 additions and 74 deletions
+129 -65
View File
@@ -3,25 +3,31 @@ export interface CalendarSchedule {
platforms: { user: string }[]; platforms: { user: string }[];
id: string; id: string;
} }
import { Button } from '@/components/ui/button';
import { import {
ContextMenu, ContextMenu,
ContextMenuContent, ContextMenuContent,
ContextMenuItem, ContextMenuItem,
ContextMenuTrigger, ContextMenuTrigger,
} from '@/components/ui/context-menu'; } from '@/components/ui/context-menu';
import { Popover } from '@/components/ui/popover';
import { PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { cn } from '@/lib/utils';
import axios from 'axios'; import axios from 'axios';
import { Pencil, PlusCircle, Trash } from 'lucide-react'; import { format } from 'date-fns';
import React from 'react'; import { CalendarIcon, Pencil, Plus, PlusCircle, Trash } from 'lucide-react';
import React, { useState } from 'react';
import { Calendar as ShadcnCalendar } from '@/components/ui/calendar';
const hours = Array.from({ length: 24 }, (_, i) => `${i}:00`); const hours = Array.from({ length: 24 }, (_, i) => `${i}:00`);
const days = [ const days = [
'Sunday',
'Monday', 'Monday',
'Tuesday', 'Tuesday',
'Wednesday', 'Wednesday',
'Thursday', 'Thursday',
'Friday', 'Friday',
'Saturday', 'Saturday',
'Sunday',
]; ];
export interface Event { export interface Event {
@@ -37,93 +43,151 @@ interface CalendarProps {
setEvents: (events: Event[]) => void; setEvents: (events: Event[]) => void;
} }
const today = new Date();
today.setHours(0, 0, 0, 0);
const Calendar: React.FC<CalendarProps> = ({ const Calendar: React.FC<CalendarProps> = ({
events, events,
setDate, setDate,
setDialog, setDialog,
setEvents, setEvents,
}) => { }) => {
const [calendarDate, setCalendarDate] = useState(new Date());
return ( return (
<div className="grid grid-cols-[auto,repeat(7,1fr)] border text-center text-sm"> <div className="flex flex-col flex-grow">
<div className="border p-2 font-bold">Time</div> <header>
{days.map((day) => ( <div className="w-full">
<div key={day} className="border p-2 font-bold"> <Popover>
{day} <PopoverTrigger asChild>
<Button
variant={'outline'}
className={cn(
'w-[280px] justify-start text-left font-normal',
!calendarDate && 'text-muted-foreground'
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{calendarDate ? (
format(calendarDate, 'MMMM dd y')
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<ShadcnCalendar
selected={calendarDate}
onSelect={(d: Date | undefined) => {
if (d) {
setCalendarDate(d);
}
}}
mode="single"
disabled={(d) => d < today}
/>
</PopoverContent>
</Popover>
</div> </div>
))} </header>
<div className="mt-5 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>
))}
{hours.map((hour, rowIdx) => (
<React.Fragment key={hour}>
<div className="border p-2">{hour}</div>
{days.map((_, colIdx) => {
const currentDate = new Date(calendarDate);
currentDate.setDate(
calendarDate.getDate() -
calendarDate.getDay() +
colIdx
);
currentDate.setHours(rowIdx, 0, 0, 0);
{hours.map((hour, rowIdx) => ( const event = events.find(
<React.Fragment key={hour}> (e: Event) =>
<div className="border p-2">{hour}</div> e.time.toDateString() ===
currentDate.toDateString() &&
e.time.getHours() === rowIdx
);
{days.map((_, colIdx) => { if (!event) {
const event = events.find( return (
(e: Event) => <ContextMenu key={`${colIdx}-${rowIdx}`}>
e.time.getDay() === colIdx && <ContextMenuTrigger asChild>
e.time.getHours() === rowIdx <div
); className="relative border h-16 group"
onClick={() => {
setDate(
new Date(currentDate)
);
setDialog(true);
}}
>
<Plus className="absolute inset-0 text-white p-1 text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity duration-300 size-10 m-auto" />
</div>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem
onClick={() => {
setDate(
new Date(currentDate)
);
setDialog(true);
}}
>
<PlusCircle className="w-4 h-4 mr-1" />
New Post
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
);
}
if (!event) {
return ( return (
<ContextMenu key={`${colIdx}-${rowIdx}`}> <ContextMenu key={`${colIdx}-${rowIdx}`}>
<ContextMenuTrigger asChild> <ContextMenuTrigger asChild>
<div className="relative border h-16"></div> <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> </ContextMenuTrigger>
<ContextMenuContent> <ContextMenuContent>
<ContextMenuItem>
<Pencil className="w-4 h-4 mr-1" />
Edit
</ContextMenuItem>
<ContextMenuItem <ContextMenuItem
className="text-red-600"
onClick={() => { onClick={() => {
setDate( axios.delete(
new Date(2024, 3, 4, rowIdx) `/api/posts?schedule=${event.id}`
);
setEvents(
events.filter(
(e) => e.id !== event.id
)
); );
setDialog(true);
}} }}
> >
<PlusCircle className="w-4 h-4 mr-1" /> <Trash className="w-4 h-4 mr-1" />
New Post Delete
</ContextMenuItem> </ContextMenuItem>
</ContextMenuContent> </ContextMenuContent>
</ContextMenu> </ContextMenu>
); );
} })}
</React.Fragment>
return ( ))}
<ContextMenu key={`${colIdx}-${rowIdx}`}> </div>
<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> </div>
); );
}; };
+24 -6
View File
@@ -34,6 +34,9 @@ import {
} from '@/lib/types'; } from '@/lib/types';
import { CalendarSchedule, Event } from './calendar'; import { CalendarSchedule, Event } from './calendar';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import RecentPosts from '../dashboard/recentPosts';
import PlatformIcon from '@/components/icons';
import { Input } from '@/components/ui/input';
type PostDialogProps = { type PostDialogProps = {
newPostDialogOpen: boolean; newPostDialogOpen: boolean;
@@ -55,6 +58,7 @@ export default function PostDialog({
}: PostDialogProps) { }: PostDialogProps) {
const [content, setContent] = useState(''); const [content, setContent] = useState('');
const [platforms, setPlatforms] = useState<UserPlatforms>({}); const [platforms, setPlatforms] = useState<UserPlatforms>({});
const [title, setTitle] = useState('');
const [selectedPlatforms, setSelectedPlatforms] = useState<Set<string>>( const [selectedPlatforms, setSelectedPlatforms] = useState<Set<string>>(
new Set() new Set()
@@ -126,10 +130,9 @@ export default function PostDialog({
</div> </div>
<div className="grid w-full gap-1.5"> <div className="grid w-full gap-1.5">
<Label htmlFor="message">Platforms</Label>
<ToggleGroup <ToggleGroup
type="multiple" type="multiple"
className="flex flex-wrap gap-2" className="flex flex-wrap gap-2 mr-auto"
> >
{Object.entries(platforms).map(([name, platform]) => ( {Object.entries(platforms).map(([name, platform]) => (
<ToggleGroupItem <ToggleGroupItem
@@ -148,15 +151,30 @@ export default function PostDialog({
}); });
}} }}
> >
<img <div className="relative w-10 h-10">
src={platform.icon} <img
className="w-10 h-10 rounded-full" src={platform.icon}
/> className="w-10 h-10 rounded-full"
/>
<PlatformIcon
platform={platform.platform}
className="absolute bottom-0 right-0 bg-background"
/>
</div>
</ToggleGroupItem> </ToggleGroupItem>
))} ))}
</ToggleGroup> </ToggleGroup>
</div> </div>
<div className="grid w-full gap-1.5">
<Label htmlFor="title">Title</Label>
<Input
type="text"
placeholder="Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
<div className="grid w-full gap-1.5"> <div className="grid w-full gap-1.5">
<Label htmlFor="message">Content</Label> <Label htmlFor="message">Content</Label>
<Textarea <Textarea
+1 -1
View File
@@ -2,7 +2,7 @@
import { PlatformKind } from '@/lib/types'; import { PlatformKind } from '@/lib/types';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { PlatformIcon } from '@/components/icons'; import PlatformIcon from '@/components/icons';
export default function AccountStatus({ export default function AccountStatus({
accounts, accounts,
+1 -1
View File
@@ -1,6 +1,6 @@
import { PlatformKind } from '@/lib/types'; import { PlatformKind } from '@/lib/types';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { PlatformIcon } from '@/components/icons'; import PlatformIcon from '@/components/icons';
import { format } from 'date-fns'; import { format } from 'date-fns';
export default function RecentPosts({ export default function RecentPosts({
+1 -1
View File
@@ -4,7 +4,7 @@ interface PlatformProps extends React.SVGProps<SVGSVGElement> {
platform: PlatformKind; platform: PlatformKind;
} }
export function PlatformIcon({ platform, ...props }: PlatformProps) { export default function PlatformIcon({ platform, ...props }: PlatformProps) {
switch (platform) { switch (platform) {
case 'x': case 'x':
return ( return (