small changes
This commit is contained in:
+129
-65
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,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({
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
Reference in New Issue
Block a user