mirror of
https://github.com/ajnart/homarr.git
synced 2026-02-26 16:30:57 +01:00
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",
|
||||
"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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user