feat(releases-widget): add Select/Deselect All to import from docker screen (#3674)
This commit is contained in:
@@ -2273,7 +2273,9 @@
|
|||||||
"listFoundImages": "List of found images",
|
"listFoundImages": "List of found images",
|
||||||
"listAlreadyImportedImages": "List of already imported images",
|
"listAlreadyImportedImages": "List of already imported images",
|
||||||
"allImagesAlreadyImported": "All images already imported",
|
"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": {
|
"provider": {
|
||||||
"label": "Provider"
|
"label": "Provider"
|
||||||
|
|||||||
@@ -19,18 +19,19 @@ import {
|
|||||||
Title,
|
Title,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import type { CheckboxProps } from "@mantine/core";
|
|
||||||
import type { FormErrors } from "@mantine/form";
|
import type { FormErrors } from "@mantine/form";
|
||||||
import { useDebouncedValue } from "@mantine/hooks";
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
import {
|
import {
|
||||||
IconAlertTriangleFilled,
|
IconAlertTriangleFilled,
|
||||||
IconBrandDocker,
|
IconBrandDocker,
|
||||||
|
IconCopy,
|
||||||
|
IconCopyCheckFilled,
|
||||||
IconEdit,
|
IconEdit,
|
||||||
|
IconPackageImport,
|
||||||
IconPlus,
|
IconPlus,
|
||||||
IconSquare,
|
|
||||||
IconSquareCheck,
|
|
||||||
IconTrash,
|
IconTrash,
|
||||||
IconTriangleFilled,
|
IconTriangleFilled,
|
||||||
|
IconZoomScan,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { escapeForRegEx } from "@tiptap/react";
|
import { escapeForRegEx } from "@tiptap/react";
|
||||||
|
|
||||||
@@ -511,33 +512,37 @@ interface ReleasesRepositoryImport extends ReleasesRepository {
|
|||||||
|
|
||||||
interface ImportRepositorySelectProps {
|
interface ImportRepositorySelectProps {
|
||||||
repository: ReleasesRepositoryImport;
|
repository: ReleasesRepositoryImport;
|
||||||
|
checked: boolean;
|
||||||
integration?: Integration;
|
integration?: Integration;
|
||||||
versionFilterPrecisionOptions: string[];
|
versionFilterPrecisionOptions: string[];
|
||||||
|
disabled: boolean;
|
||||||
onImageSelectionChanged?: (isSelected: boolean) => void;
|
onImageSelectionChanged?: (isSelected: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImportRepositorySelect = ({
|
const ImportRepositorySelect = ({
|
||||||
repository,
|
repository,
|
||||||
|
checked,
|
||||||
integration,
|
integration,
|
||||||
versionFilterPrecisionOptions,
|
versionFilterPrecisionOptions,
|
||||||
onImageSelectionChanged,
|
disabled = false,
|
||||||
|
onImageSelectionChanged = undefined,
|
||||||
}: ImportRepositorySelectProps) => {
|
}: ImportRepositorySelectProps) => {
|
||||||
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
||||||
const checkBoxProps: CheckboxProps = !onImageSelectionChanged
|
|
||||||
? {
|
|
||||||
disabled: true,
|
|
||||||
checked: true,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
onChange: (event) => onImageSelectionChanged(event.currentTarget.checked),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group gap="xl" justify="space-between">
|
<Group gap="xl" justify="space-between">
|
||||||
<Group gap="md">
|
<Group gap="md" align="center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
checked={checked}
|
||||||
|
disabled={disabled}
|
||||||
|
readOnly={disabled}
|
||||||
|
onChange={() => {
|
||||||
|
if (onImageSelectionChanged) {
|
||||||
|
onImageSelectionChanged(!checked);
|
||||||
|
}
|
||||||
|
}}
|
||||||
label={
|
label={
|
||||||
<Group>
|
<Group align="center">
|
||||||
<Image
|
<Image
|
||||||
src={repository.iconUrl}
|
src={repository.iconUrl}
|
||||||
style={{
|
style={{
|
||||||
@@ -548,7 +553,6 @@ const ImportRepositorySelect = ({
|
|||||||
<Text>{repository.identifier}</Text>
|
<Text>{repository.identifier}</Text>
|
||||||
</Group>
|
</Group>
|
||||||
}
|
}
|
||||||
{...checkBoxProps}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{repository.versionFilter && (
|
{repository.versionFilter && (
|
||||||
@@ -693,7 +697,7 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
|
|||||||
<Stack>
|
<Stack>
|
||||||
<Accordion defaultValue={!allImagesImported ? "foundImages" : anyImagesImported ? "alreadyImported" : ""}>
|
<Accordion defaultValue={!allImagesImported ? "foundImages" : anyImagesImported ? "alreadyImported" : ""}>
|
||||||
<Accordion.Item value="foundImages">
|
<Accordion.Item value="foundImages">
|
||||||
<Accordion.Control disabled={allImagesImported} icon={<IconSquare stroke={1.25} />}>
|
<Accordion.Control disabled={allImagesImported} icon={<IconZoomScan />}>
|
||||||
<Group>
|
<Group>
|
||||||
{tRepository("importRepositories.listFoundImages")}
|
{tRepository("importRepositories.listFoundImages")}
|
||||||
{allImagesImported && (
|
{allImagesImported && (
|
||||||
@@ -704,52 +708,85 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
|
|||||||
</Group>
|
</Group>
|
||||||
</Accordion.Control>
|
</Accordion.Control>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
{!allImagesImported &&
|
{!allImagesImported && (
|
||||||
importRepositories
|
<Stack justify="center" gap="xs">
|
||||||
.filter((repository) => !repository.alreadyImported)
|
<Group>
|
||||||
.map((repository) => {
|
<Button
|
||||||
const integration = repository.providerIntegrationId
|
leftSection={<IconCopyCheckFilled size="1em" />}
|
||||||
? innerProps.integrations[repository.providerIntegrationId]
|
onClick={() =>
|
||||||
: undefined;
|
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 (
|
<Divider />
|
||||||
<ImportRepositorySelect
|
|
||||||
key={repository.id}
|
{importRepositories
|
||||||
repository={repository}
|
.filter((repository) => !repository.alreadyImported)
|
||||||
integration={integration}
|
.map((repository) => {
|
||||||
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
|
const integration = repository.providerIntegrationId
|
||||||
onImageSelectionChanged={(isSelected) =>
|
? innerProps.integrations[repository.providerIntegrationId]
|
||||||
isSelected
|
: undefined;
|
||||||
? setSelectedImages([...selectedImages, repository])
|
|
||||||
: setSelectedImages(selectedImages.filter((img) => img !== repository))
|
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.Panel>
|
||||||
</Accordion.Item>
|
</Accordion.Item>
|
||||||
<Accordion.Item value="alreadyImported">
|
<Accordion.Item value="alreadyImported">
|
||||||
<Accordion.Control disabled={!anyImagesImported} icon={<IconSquareCheck stroke={1.25} />}>
|
<Accordion.Control disabled={!anyImagesImported} icon={<IconPackageImport />}>
|
||||||
{tRepository("importRepositories.listAlreadyImportedImages")}
|
{tRepository("importRepositories.listAlreadyImportedImages")}
|
||||||
</Accordion.Control>
|
</Accordion.Control>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
{anyImagesImported &&
|
{anyImagesImported && (
|
||||||
importRepositories
|
<Stack justify="center" gap="xs">
|
||||||
.filter((repository) => repository.alreadyImported)
|
{importRepositories
|
||||||
.map((repository) => {
|
.filter((repository) => repository.alreadyImported)
|
||||||
const integration = repository.providerIntegrationId
|
.map((repository) => {
|
||||||
? innerProps.integrations[repository.providerIntegrationId]
|
const integration = repository.providerIntegrationId
|
||||||
: undefined;
|
? innerProps.integrations[repository.providerIntegrationId]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ImportRepositorySelect
|
<ImportRepositorySelect
|
||||||
key={repository.id}
|
key={repository.id}
|
||||||
repository={repository}
|
repository={repository}
|
||||||
integration={integration}
|
integration={integration}
|
||||||
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
|
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
|
||||||
/>
|
checked
|
||||||
);
|
disabled
|
||||||
})}
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
</Accordion.Panel>
|
</Accordion.Panel>
|
||||||
</Accordion.Item>
|
</Accordion.Item>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|||||||
Reference in New Issue
Block a user