small changes
This commit is contained in:
+129
-65
@@ -3,25 +3,31 @@ export interface CalendarSchedule {
|
||||
platforms: { user: string }[];
|
||||
id: string;
|
||||
}
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuTrigger,
|
||||
} 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 { Pencil, PlusCircle, Trash } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { format } from 'date-fns';
|
||||
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 days = [
|
||||
'Sunday',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
'Sunday',
|
||||
];
|
||||
|
||||
export interface Event {
|
||||
@@ -37,93 +43,151 @@ interface CalendarProps {
|
||||
setEvents: (events: Event[]) => void;
|
||||
}
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const Calendar: React.FC<CalendarProps> = ({
|
||||
events,
|
||||
setDate,
|
||||
setDialog,
|
||||
setEvents,
|
||||
}) => {
|
||||
const [calendarDate, setCalendarDate] = useState(new Date());
|
||||
|
||||
return (
|
||||
<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 className="flex flex-col flex-grow">
|
||||
<header>
|
||||
<div className="w-full">
|
||||
<Popover>
|
||||
<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>
|
||||
))}
|
||||
</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) => (
|
||||
<React.Fragment key={hour}>
|
||||
<div className="border p-2">{hour}</div>
|
||||
const event = events.find(
|
||||
(e: Event) =>
|
||||
e.time.toDateString() ===
|
||||
currentDate.toDateString() &&
|
||||
e.time.getHours() === rowIdx
|
||||
);
|
||||
|
||||
{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 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 (
|
||||
<ContextMenu key={`${colIdx}-${rowIdx}`}>
|
||||
<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>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem>
|
||||
<Pencil className="w-4 h-4 mr-1" />
|
||||
Edit
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
className="text-red-600"
|
||||
onClick={() => {
|
||||
setDate(
|
||||
new Date(2024, 3, 4, rowIdx)
|
||||
axios.delete(
|
||||
`/api/posts?schedule=${event.id}`
|
||||
);
|
||||
setEvents(
|
||||
events.filter(
|
||||
(e) => e.id !== event.id
|
||||
)
|
||||
);
|
||||
setDialog(true);
|
||||
}}
|
||||
>
|
||||
<PlusCircle className="w-4 h-4 mr-1" />
|
||||
New Post
|
||||
<Trash className="w-4 h-4 mr-1" />
|
||||
Delete
|
||||
</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>
|
||||
))}
|
||||
})}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -34,6 +34,9 @@ import {
|
||||
} from '@/lib/types';
|
||||
import { CalendarSchedule, Event } from './calendar';
|
||||
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 = {
|
||||
newPostDialogOpen: boolean;
|
||||
@@ -55,6 +58,7 @@ export default function PostDialog({
|
||||
}: PostDialogProps) {
|
||||
const [content, setContent] = useState('');
|
||||
const [platforms, setPlatforms] = useState<UserPlatforms>({});
|
||||
const [title, setTitle] = useState('');
|
||||
|
||||
const [selectedPlatforms, setSelectedPlatforms] = useState<Set<string>>(
|
||||
new Set()
|
||||
@@ -126,10 +130,9 @@ export default function PostDialog({
|
||||
</div>
|
||||
|
||||
<div className="grid w-full gap-1.5">
|
||||
<Label htmlFor="message">Platforms</Label>
|
||||
<ToggleGroup
|
||||
type="multiple"
|
||||
className="flex flex-wrap gap-2"
|
||||
className="flex flex-wrap gap-2 mr-auto"
|
||||
>
|
||||
{Object.entries(platforms).map(([name, platform]) => (
|
||||
<ToggleGroupItem
|
||||
@@ -148,15 +151,30 @@ export default function PostDialog({
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={platform.icon}
|
||||
className="w-10 h-10 rounded-full"
|
||||
/>
|
||||
<div className="relative w-10 h-10">
|
||||
<img
|
||||
src={platform.icon}
|
||||
className="w-10 h-10 rounded-full"
|
||||
/>
|
||||
<PlatformIcon
|
||||
platform={platform.platform}
|
||||
className="absolute bottom-0 right-0 bg-background"
|
||||
/>
|
||||
</div>
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
</ToggleGroup>
|
||||
</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">
|
||||
<Label htmlFor="message">Content</Label>
|
||||
<Textarea
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { PlatformKind } from '@/lib/types';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { PlatformIcon } from '@/components/icons';
|
||||
import PlatformIcon from '@/components/icons';
|
||||
|
||||
export default function AccountStatus({
|
||||
accounts,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PlatformKind } from '@/lib/types';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { PlatformIcon } from '@/components/icons';
|
||||
import PlatformIcon from '@/components/icons';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
export default function RecentPosts({
|
||||
|
||||
@@ -4,7 +4,7 @@ interface PlatformProps extends React.SVGProps<SVGSVGElement> {
|
||||
platform: PlatformKind;
|
||||
}
|
||||
|
||||
export function PlatformIcon({ platform, ...props }: PlatformProps) {
|
||||
export default function PlatformIcon({ platform, ...props }: PlatformProps) {
|
||||
switch (platform) {
|
||||
case 'x':
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user