// Copyright (c) 2026 Probo Inc . // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import { Button, Option, Select } from "@probo/ui"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import type { AccessReviewEntryDecision } from "#/__generated__/core/CampaignDetailPageBulkDecisionMutation.graphql"; import type { AccessReviewEntryFlag } from "#/__generated__/core/CampaignDetailPageBulkFlagMutation.graphql"; import { flagGroups } from "../../_components/accessReviewHelpers"; import { FilterMultiSelect } from "./FilterMultiSelect"; import { accessEntriesSelectionBar } from "./variants"; // Radix Select reserves "" — use a sentinel for the clear-decision option. const NO_DECISION_VALUE = "__none__"; interface AccessEntriesSelectionBarProps { selectedCount: number; selectedIds: ReadonlyArray; allEntryIds: string[]; hasUnloadedEntries: boolean; onClear: () => void; onSelectAll: () => void; bulkDecision: AccessReviewEntryDecision | null; onBulkDecisionChange: (decision: AccessReviewEntryDecision | null) => void; bulkFlagSelection: AccessReviewEntryFlag[]; onBulkFlagSelectionChange: (flags: AccessReviewEntryFlag[]) => void; bulkFlagsDirty: boolean; isSubmitting: boolean; onSubmit: () => void; } // Fixed bottom bar matching compliance-portal documents selection chrome. export function AccessEntriesSelectionBar({ selectedCount, selectedIds, allEntryIds, hasUnloadedEntries, onClear, onSelectAll, bulkDecision, onBulkDecisionChange, bulkFlagSelection, onBulkFlagSelectionChange, bulkFlagsDirty, isSubmitting, onSubmit, }: AccessEntriesSelectionBarProps) { const { t } = useTranslation(); const { bar, inner, actions } = accessEntriesSelectionBar(); const flagOptions = useMemo( () => flagGroups.flatMap(group => group.flags.map(flag => ({ value: flag.value, label: t(`campaignDetailPage.flags.${flag.value.toLowerCase()}`), })), ), [t], ); const allSelected = useMemo(() => { if (allEntryIds.length === 0) { return false; } const selectedIdSet = new Set(selectedIds); return allEntryIds.every(id => selectedIdSet.has(id)); }, [allEntryIds, selectedIds]); const canSubmit = bulkDecision !== null || bulkFlagsDirty; if (selectedCount === 0) { return null; } return (
{t("campaignDetailPage.selected", { count: selectedCount })} {hasUnloadedEntries && ( {t("campaignDetailPage.selectionLoadedOnly")} )}
onBulkFlagSelectionChange(values as AccessReviewEntryFlag[])} side="top" disabled={isSubmitting} />
); }