import { ArrowLeft, ArrowRight } from 'lucide-react' import { useTranslation } from '../../../i18n/TranslationContext' import { DOCUMENT_PROVIDER_ICONS } from '../../shared/DocumentProviderIcons' import TrekMark from '../../shared/TrekMark' import { Badge } from './DocSyncBits' export type SyncDirection = 'both' | 'pull' | 'push' export interface FlowHoldings { /** Documents this trip has in TREK. */ inTrek: number /** How many of them the store is holding. */ atProvider: number paired: number missing: number } /** * The direction control, drawn as what it actually is. * * A document sync has one characteristic picture — two places and things moving * between them — and a dropdown reading "Both ways" hides it behind a word. * Here the two ends carry what each side is holding, the lanes between them * carry the direction, and switching direction means switching a lane off. It * is the one loud element in the dialog; everything around it stays quiet. * * The numbers are standing counts, not the last run's transfers: a binding that * is already in step transferred nothing, and a panel answering "0" on a * healthy connection says nothing at the moment somebody looks at it. */ export default function DocSyncFlow({ direction, providerId, providerName, holdings, running, disabled, onChange, }: { direction: SyncDirection providerId: string providerName: string holdings: FlowHoldings /** A run is in flight: the active lanes move while it is. */ running?: boolean disabled?: boolean onChange: (next: SyncDirection) => void }) { const { t } = useTranslation() const Icon = DOCUMENT_PROVIDER_ICONS[providerId] const pushOn = direction === 'both' || direction === 'push' const pullOn = direction === 'both' || direction === 'pull' const hint = disabled ? t(`docsync.flow.summary.${direction}`, { provider: providerName }) : t(`docsync.flow.summaryEditable.${direction}`, { provider: providerName }) /** Turning a lane off leaves the other one; turning the last one off is refused. */ const toggle = (lane: 'push' | 'pull') => { if (disabled) return const nextPush = lane === 'push' ? !pushOn : pushOn const nextPull = lane === 'pull' ? !pullOn : pullOn if (!nextPush && !nextPull) return onChange(nextPush && nextPull ? 'both' : nextPush ? 'push' : 'pull') } return (
} />
toggle('push')} > toggle('pull')} > {/* Under the lanes rather than under the whole bar: it explains what the two buttons above it are doing, and sitting anywhere else makes the reader look for what it refers to. */}

{hint}

: null} />
) } /** * One end of the flow: the mark, the count, the name. * * The count is the largest thing in the box on purpose — "is my stuff over * there" is the question that makes somebody open this dialog, and it should be * answered before they read a word. */ function End({ name, count, glyph }: { name: string; count: number; glyph: React.ReactNode }) { const { t } = useTranslation() return (
{glyph} {count} {t('docsync.flow.documents')} {name}
) } /** * One direction, as a track that is either carrying or idle. * * An idle track stays visible as a dashed line, so switching a direction off * reads as a road nobody drives on rather than as something that disappeared. */ function Lane({ active, running, disabled, caption, reverse, onClick, children, }: { active: boolean running?: boolean disabled?: boolean caption: string reverse?: boolean onClick: () => void children: React.ReactNode }) { const moving = active && running return ( ) }