feat(releases-widget): add Select/Deselect All to import from docker screen (#3674)

This commit is contained in:
Andre Silva
2025-08-01 10:12:56 +01:00
committed by GitHub
parent 6a819e38ed
commit 949c7a40d9
2 changed files with 94 additions and 55 deletions

View File

@@ -2273,7 +2273,9 @@
"listFoundImages": "List of found images",
"listAlreadyImportedImages": "List of already imported images",
"allImagesAlreadyImported": "All images already imported",
"onlyAdminCanImport": "Only administrators can import from docker"
"onlyAdminCanImport": "Only administrators can import from docker",
"selectAll": "Select all",
"deselectAll": "Deselect all"
},
"provider": {
"label": "Provider"

View File

@@ -19,18 +19,19 @@ import {
Title,
Tooltip,
} from "@mantine/core";
import type { CheckboxProps } from "@mantine/core";
import type { FormErrors } from "@mantine/form";
import { useDebouncedValue } from "@mantine/hooks";
import {
IconAlertTriangleFilled,
IconBrandDocker,
IconCopy,
IconCopyCheckFilled,
IconEdit,
IconPackageImport,
IconPlus,
IconSquare,
IconSquareCheck,
IconTrash,
IconTriangleFilled,
IconZoomScan,
} from "@tabler/icons-react";
import { escapeForRegEx } from "@tiptap/react";
@@ -511,33 +512,37 @@ interface ReleasesRepositoryImport extends ReleasesRepository {
interface ImportRepositorySelectProps {
repository: ReleasesRepositoryImport;
checked: boolean;
integration?: Integration;
versionFilterPrecisionOptions: string[];
disabled: boolean;
onImageSelectionChanged?: (isSelected: boolean) => void;
}
const ImportRepositorySelect = ({
repository,
checked,
integration,
versionFilterPrecisionOptions,
onImageSelectionChanged,
disabled = false,
onImageSelectionChanged = undefined,
}: ImportRepositorySelectProps) => {
const tRepository = useScopedI18n("widget.releases.option.repositories");
const checkBoxProps: CheckboxProps = !onImageSelectionChanged
? {
disabled: true,
checked: true,
}
: {
onChange: (event) => onImageSelectionChanged(event.currentTarget.checked),
};
return (
<Group gap="xl" justify="space-between">
<Group gap="md">
<Group gap="md" align="center">
<Checkbox
checked={checked}
disabled={disabled}
readOnly={disabled}
onChange={() => {
if (onImageSelectionChanged) {
onImageSelectionChanged(!checked);
}
}}
label={
<Group>
<Group align="center">
<Image
src={repository.iconUrl}
style={{
@@ -548,7 +553,6 @@ const ImportRepositorySelect = ({
<Text>{repository.identifier}</Text>
</Group>
}
{...checkBoxProps}
/>
{repository.versionFilter && (
@@ -693,7 +697,7 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
<Stack>
<Accordion defaultValue={!allImagesImported ? "foundImages" : anyImagesImported ? "alreadyImported" : ""}>
<Accordion.Item value="foundImages">
<Accordion.Control disabled={allImagesImported} icon={<IconSquare stroke={1.25} />}>
<Accordion.Control disabled={allImagesImported} icon={<IconZoomScan />}>
<Group>
{tRepository("importRepositories.listFoundImages")}
{allImagesImported && (
@@ -704,52 +708,85 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
</Group>
</Accordion.Control>
<Accordion.Panel>
{!allImagesImported &&
importRepositories
.filter((repository) => !repository.alreadyImported)
.map((repository) => {
const integration = repository.providerIntegrationId
? innerProps.integrations[repository.providerIntegrationId]
: undefined;
{!allImagesImported && (
<Stack justify="center" gap="xs">
<Group>
<Button
leftSection={<IconCopyCheckFilled size="1em" />}
onClick={() =>
setSelectedImages(importRepositories.filter((repository) => !repository.alreadyImported))
}
size="xs"
>
{tRepository("importRepositories.selectAll")}
</Button>
<Button
leftSection={<IconCopy size="1em" />}
onClick={() => setSelectedImages([])}
size="xs"
variant="default"
color="gray.5"
>
{tRepository("importRepositories.deselectAll")}
</Button>
</Group>
return (
<ImportRepositorySelect
key={repository.id}
repository={repository}
integration={integration}
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
onImageSelectionChanged={(isSelected) =>
isSelected
? setSelectedImages([...selectedImages, repository])
: setSelectedImages(selectedImages.filter((img) => img !== repository))
}
/>
);
})}
<Divider />
{importRepositories
.filter((repository) => !repository.alreadyImported)
.map((repository) => {
const integration = repository.providerIntegrationId
? innerProps.integrations[repository.providerIntegrationId]
: undefined;
return (
<ImportRepositorySelect
key={repository.id}
repository={repository}
checked={selectedImages.includes(repository)}
integration={integration}
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
disabled={false}
onImageSelectionChanged={(isSelected) =>
isSelected
? setSelectedImages([...selectedImages, repository])
: setSelectedImages(selectedImages.filter((img) => img !== repository))
}
/>
);
})}
</Stack>
)}
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item value="alreadyImported">
<Accordion.Control disabled={!anyImagesImported} icon={<IconSquareCheck stroke={1.25} />}>
<Accordion.Control disabled={!anyImagesImported} icon={<IconPackageImport />}>
{tRepository("importRepositories.listAlreadyImportedImages")}
</Accordion.Control>
<Accordion.Panel>
{anyImagesImported &&
importRepositories
.filter((repository) => repository.alreadyImported)
.map((repository) => {
const integration = repository.providerIntegrationId
? innerProps.integrations[repository.providerIntegrationId]
: undefined;
{anyImagesImported && (
<Stack justify="center" gap="xs">
{importRepositories
.filter((repository) => repository.alreadyImported)
.map((repository) => {
const integration = repository.providerIntegrationId
? innerProps.integrations[repository.providerIntegrationId]
: undefined;
return (
<ImportRepositorySelect
key={repository.id}
repository={repository}
integration={integration}
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
/>
);
})}
return (
<ImportRepositorySelect
key={repository.id}
repository={repository}
integration={integration}
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
checked
disabled
/>
);
})}
</Stack>
)}
</Accordion.Panel>
</Accordion.Item>
</Accordion>