- CalendarForm: max-w-3xl when sharing (was sm:max-w-2xl, overridden by base max-w-xl) - SharedCalendarSettings: max-w-2xl (was sm:max-w-lg) - CalendarMemberRow: back to single-line with PermissionToggle inline (less cramped) - Use unprefixed max-w classes so twMerge properly overrides DialogContent base Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
89 lines
3.3 KiB
TypeScript
89 lines
3.3 KiB
TypeScript
import { X, UserPlus } from 'lucide-react';
|
|
import type { CalendarMemberInfo, CalendarPermission } from '@/types';
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
import { useConfirmAction } from '@/hooks/useConfirmAction';
|
|
import PermissionBadge from './PermissionBadge';
|
|
import PermissionToggle from './PermissionToggle';
|
|
|
|
interface CalendarMemberRowProps {
|
|
member: CalendarMemberInfo;
|
|
isOwner: boolean;
|
|
readOnly?: boolean;
|
|
onUpdatePermission?: (memberId: number, permission: CalendarPermission) => void;
|
|
onUpdateCanAddOthers?: (memberId: number, canAddOthers: boolean) => void;
|
|
onRemove?: (memberId: number) => void;
|
|
}
|
|
|
|
export default function CalendarMemberRow({
|
|
member,
|
|
isOwner,
|
|
readOnly = false,
|
|
onUpdatePermission,
|
|
onUpdateCanAddOthers,
|
|
onRemove,
|
|
}: CalendarMemberRowProps) {
|
|
const { confirming, handleClick: handleRemoveClick } = useConfirmAction(
|
|
() => onRemove?.(member.id)
|
|
);
|
|
|
|
const displayName = member.preferred_name || member.umbral_name;
|
|
const initial = displayName.charAt(0).toUpperCase();
|
|
|
|
return (
|
|
<div className="flex items-center gap-3 rounded-lg border border-border p-3 transition-all duration-200 hover:border-border/80">
|
|
<div className="h-8 w-8 rounded-full bg-violet-500/15 flex items-center justify-center shrink-0">
|
|
<span className="text-sm text-violet-400 font-medium">{initial}</span>
|
|
</div>
|
|
|
|
<div className="min-w-0 flex-1">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm font-medium truncate">{displayName}</span>
|
|
{member.status === 'pending' && (
|
|
<span className="text-[9px] px-1.5 py-0.5 rounded-full bg-orange-500/10 text-orange-400 font-medium">
|
|
Pending
|
|
</span>
|
|
)}
|
|
</div>
|
|
{member.preferred_name && (
|
|
<span className="text-xs text-muted-foreground">{member.umbral_name}</span>
|
|
)}
|
|
</div>
|
|
|
|
{readOnly ? (
|
|
<PermissionBadge permission={member.permission} />
|
|
) : isOwner ? (
|
|
<div className="flex items-center gap-2.5 shrink-0">
|
|
<PermissionToggle
|
|
value={member.permission}
|
|
onChange={(p) => onUpdatePermission?.(member.id, p)}
|
|
/>
|
|
{(member.permission === 'create_modify' || member.permission === 'full_access') && (
|
|
<label className="flex items-center gap-1.5 cursor-pointer shrink-0" title="Can add others">
|
|
<Checkbox
|
|
checked={member.can_add_others}
|
|
onChange={() => onUpdateCanAddOthers?.(member.id, !member.can_add_others)}
|
|
className="h-3.5 w-3.5"
|
|
/>
|
|
<UserPlus className="h-3.5 w-3.5 text-muted-foreground" />
|
|
</label>
|
|
)}
|
|
<button
|
|
type="button"
|
|
onClick={handleRemoveClick}
|
|
className="text-muted-foreground hover:text-destructive transition-colors"
|
|
title={confirming ? 'Click again to confirm' : 'Remove member'}
|
|
>
|
|
{confirming ? (
|
|
<span className="text-[10px] text-destructive font-medium px-1">Sure?</span>
|
|
) : (
|
|
<X className="h-4 w-4" />
|
|
)}
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<PermissionBadge permission={member.permission} />
|
|
)}
|
|
</div>
|
|
);
|
|
}
|