feat(ui): add select all / clear all shortcuts for inbound multi-select (#5175)

* feat(ui): add select all / clear all shortcuts for inbound multi-select

Adds 'Select all' and 'Clear all' buttons above the inbound multi-select in:
- ClientFormModal (add/edit client)
- BulkAttachInboundsModal (bulk attach clients to inbounds)
- BulkDetachInboundsModal (bulk detach clients from inbounds)
- ClientBulkAddModal (add bulk clients)

Extracts the repeated button logic into a reusable SelectAllClearButtons component.

Includes i18n keys for all 13 supported languages with proper translations.

Closes #5144

* refactor(form): decouple SelectAllClearButtons labels and harden select-all

Accept optional selectAllLabel/clearLabel props so the generic form component is not tied to the client-inbound i18n keys (defaults unchanged). Compute the all-selected state by checking every option is present and union the current value on select-all, so it stays correct if value holds ids outside options.

---------

Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
Nikan Zeyaei
2026-06-11 14:39:58 +03:30
committed by GitHub
parent ffde2f7ebf
commit 07e5e8498e
19 changed files with 126 additions and 24 deletions
@@ -0,0 +1,51 @@
import { useTranslation } from 'react-i18next';
import { Button } from 'antd';
interface Option {
value: number;
}
interface SelectAllClearButtonsProps {
options: Option[];
value: number[];
onChange: (value: number[]) => void;
/** Override the default "Select all" label (defaults to the inbound copy). */
selectAllLabel?: string;
/** Override the default "Clear all" label (defaults to the inbound copy). */
clearLabel?: string;
}
export default function SelectAllClearButtons({
options,
value,
onChange,
selectAllLabel,
clearLabel,
}: SelectAllClearButtonsProps) {
const { t } = useTranslation();
const optionValues = options.map((o) => o.value);
// Treat as "all selected" when every option is chosen, rather than comparing
// lengths — this stays correct even if `value` holds ids outside `options`.
const allSelected = options.length > 0 && optionValues.every((v) => value.includes(v));
return (
<div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
<Button
size="small"
disabled={allSelected}
// Union with the current value so selections outside `options` are kept.
onClick={() => onChange(Array.from(new Set([...value, ...optionValues])))}
>
{selectAllLabel ?? t('pages.clients.selectAllInbounds')}
</Button>
<Button
size="small"
disabled={value.length === 0}
onClick={() => onChange([])}
>
{clearLabel ?? t('pages.clients.clearAllInbounds')}
</Button>
</div>
);
}
+1
View File
@@ -1,3 +1,4 @@
export { default as DateTimePicker } from './DateTimePicker';
export { default as JsonEditor } from './JsonEditor';
export { default as HeaderMapEditor } from './HeaderMapEditor';
export { default as SelectAllClearButtons } from './SelectAllClearButtons';