import { useState, useRef } from 'react' import { createPortal } from 'react-dom' import { Users, UserRound, Share2, Check, Copy, HandHelping } from 'lucide-react' import { useTranslation } from '../../i18n' import type { PackingItem } from '../../types' import type { TripMember } from './usePackingListPanel' interface Props { item: PackingItem tripMembers: TripMember[] currentUserId?: number onSetSharing: (id: number, visibility: 'common' | 'personal' | 'shared', recipientIds: number[]) => void onClone: (id: number) => void onJoin: (id: number) => void onLeave: (id: number, userId: number) => void } /** * Per-item sharing control for the three-tier packing model (#858). The owner * (bringer) sets the tier — Common / Personal / Shared with specific people — via * a dropdown; everyone else can pledge to co-bring a Common item ("I can bring * that too") or clone it onto their own list. */ export default function PackingShareControl({ item, tripMembers, currentUserId, onSetSharing, onClone, onJoin, onLeave }: Props) { const { t } = useTranslation() const [open, setOpen] = useState(false) const [pos, setPos] = useState<{ top: number; right: number } | null>(null) const btnRef = useRef(null) // The dropdown is portaled to and fixed-positioned from the button so it // can't be clipped by the packing panel's overflow. const toggle = () => { if (open) { setOpen(false); return } const r = btnRef.current?.getBoundingClientRect() if (r) setPos({ top: r.bottom + 4, right: window.innerWidth - r.right }) setOpen(true) } const isCommon = !item.is_private const isOwner = item.owner_id == null || item.owner_id === currentUserId const recipientIds = (item.recipients || []).map(r => r.user_id) const visibility: 'common' | 'personal' | 'shared' = isCommon ? 'common' : recipientIds.length > 0 ? 'shared' : 'personal' const iAmContributor = (item.contributors || []).some(c => c.user_id === currentUserId) const others = tripMembers.filter(m => m.id !== item.owner_id && m.id !== currentUserId) const toggleRecipient = (uid: number) => { const next = recipientIds.includes(uid) ? recipientIds.filter(x => x !== uid) : [...recipientIds, uid] onSetSharing(item.id, 'shared', next) } const btn = (onClick: () => void, title: string, active: boolean, node: React.ReactNode) => ( ) // Non-owner on a Common item: pledge to co-bring + clone to personal list. if (!isOwner && isCommon) { return (
{btn(() => (iAmContributor ? onLeave(item.id, currentUserId!) : onJoin(item.id)), iAmContributor ? t('packing.alsoBringingStop') : t('packing.alsoBring'), iAmContributor, )} {btn(() => onClone(item.id), t('packing.cloneToMine'), false, )}
) } // A recipient of a shared item has no controls (it's the owner's responsibility). if (!isOwner) return null return (
{open && pos && createPortal( <>
setOpen(false)} />
} label={t('packing.viewCommon')} sub={t('packing.tierCommonHint')} active={visibility === 'common'} onClick={() => { onSetSharing(item.id, 'common', []); setOpen(false) }} /> } label={t('packing.tierPersonal')} sub={t('packing.tierPersonalHint')} active={visibility === 'personal'} onClick={() => { onSetSharing(item.id, 'personal', []); setOpen(false) }} />
{t('packing.tierShared')}
{others.length === 0 ? (
{t('packing.noOneToShare')}
) : others.map(m => { const on = recipientIds.includes(m.id) return ( ) })}
, document.body, )}
) } function Row({ icon, label, sub, active, onClick }: { icon: React.ReactNode; label: string; sub: string; active: boolean; onClick: () => void }) { return ( ) }