146 lines
4.8 KiB
TypeScript
146 lines
4.8 KiB
TypeScript
import { useState } from 'react';
|
|
import { LogOut } from 'lucide-react';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import api from '@/lib/api';
|
|
import type { SharedCalendarMembership, CalendarMemberInfo, Connection } from '@/types';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogClose,
|
|
} from '@/components/ui/dialog';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Button } from '@/components/ui/button';
|
|
import { useConfirmAction } from '@/hooks/useConfirmAction';
|
|
import { useSharedCalendars } from '@/hooks/useSharedCalendars';
|
|
import { useConnections } from '@/hooks/useConnections';
|
|
import PermissionBadge from './PermissionBadge';
|
|
import CalendarMemberList from './CalendarMemberList';
|
|
import CalendarMemberSearch from './CalendarMemberSearch';
|
|
|
|
const colorSwatches = [
|
|
'#3b82f6', '#ef4444', '#f97316', '#eab308',
|
|
'#22c55e', '#8b5cf6', '#ec4899', '#06b6d4',
|
|
];
|
|
|
|
interface SharedCalendarSettingsProps {
|
|
membership: SharedCalendarMembership;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export default function SharedCalendarSettings({ membership, onClose }: SharedCalendarSettingsProps) {
|
|
const [localColor, setLocalColor] = useState(membership.local_color || membership.calendar_color);
|
|
const { updateColor, leaveCalendar, invite, isInviting } = useSharedCalendars();
|
|
const { connections } = useConnections();
|
|
|
|
const membersQuery = useQuery({
|
|
queryKey: ['calendar-members', membership.calendar_id],
|
|
queryFn: async () => {
|
|
const { data } = await api.get<CalendarMemberInfo[]>(
|
|
`/shared-calendars/${membership.calendar_id}/members`
|
|
);
|
|
return data;
|
|
},
|
|
});
|
|
const members = membersQuery.data ?? [];
|
|
|
|
const { confirming: leaveConfirming, handleClick: handleLeaveClick } = useConfirmAction(
|
|
async () => {
|
|
await leaveCalendar({ calendarId: membership.calendar_id, memberId: membership.id });
|
|
onClose();
|
|
}
|
|
);
|
|
|
|
const handleColorSelect = async (color: string) => {
|
|
setLocalColor(color);
|
|
await updateColor({ calendarId: membership.calendar_id, localColor: color });
|
|
};
|
|
|
|
const handleInvite = async (conn: Connection) => {
|
|
await invite({
|
|
calendarId: membership.calendar_id,
|
|
connectionId: conn.id,
|
|
permission: 'read_only',
|
|
canAddOthers: false,
|
|
});
|
|
membersQuery.refetch();
|
|
};
|
|
|
|
return (
|
|
<Dialog open={true} onOpenChange={onClose}>
|
|
<DialogContent className="max-w-2xl">
|
|
<DialogClose onClick={onClose} />
|
|
<DialogHeader>
|
|
<DialogTitle>Shared Calendar Settings</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<h3 className="text-sm font-medium">{membership.calendar_name}</h3>
|
|
<div className="flex items-center gap-2 mt-1">
|
|
<span className="text-xs text-muted-foreground">Your permission:</span>
|
|
<PermissionBadge permission={membership.permission} />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label>Your Color</Label>
|
|
<div className="flex gap-2">
|
|
{colorSwatches.map((c) => (
|
|
<button
|
|
key={c}
|
|
type="button"
|
|
onClick={() => handleColorSelect(c)}
|
|
className="h-6 w-6 rounded-full border-2 transition-all duration-150 hover:scale-110"
|
|
style={{
|
|
backgroundColor: c,
|
|
borderColor: localColor === c ? 'hsl(0 0% 98%)' : 'transparent',
|
|
boxShadow: localColor === c ? `0 0 0 2px ${c}40` : 'none',
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label>Members ({members.length})</Label>
|
|
<CalendarMemberList
|
|
members={members}
|
|
isLoading={membersQuery.isLoading}
|
|
isOwner={false}
|
|
readOnly={true}
|
|
/>
|
|
</div>
|
|
|
|
{membership.can_add_others && (
|
|
<div className="space-y-2">
|
|
<Label>Add Members</Label>
|
|
<CalendarMemberSearch
|
|
connections={connections}
|
|
existingMembers={members}
|
|
onSelect={handleInvite}
|
|
isLoading={isInviting}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex items-center justify-between pt-2 border-t border-border">
|
|
<Button
|
|
variant="destructive"
|
|
size="sm"
|
|
onClick={handleLeaveClick}
|
|
>
|
|
<LogOut className="mr-2 h-4 w-4" />
|
|
{leaveConfirming ? 'Sure?' : 'Leave Calendar'}
|
|
</Button>
|
|
<Button variant="outline" onClick={onClose}>
|
|
Close
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|