feat(onboarding): add Docker scanning loading animation and fix hydration

- Show animated "Scanning Docker..." indicator with dots loader while
  the discovery query is pending
- Add isLoading prop to DockerDiscoveryIndicator
- Fix button hydration mismatch with suppressHydrationWarning
- Add scanning/scanningDescription translation keys
This commit is contained in:
Thomas Camlong committed 2026-05-27 16:42:24 +02:00
1 parent a89479ed6f
commit 4d801aa231
3 files changed
+45 -6

No files matched your search

@@ -7,6 +7,7 @@ import {
Card,
Collapse,
Group,
Loader,
Paper,
ScrollArea,
SimpleGrid,
@@ -43,6 +44,7 @@ interface DockerDiscoveryIndicatorProps {
apps: DiscoveredApp[];
selectedAppIds: Set<string>;
onToggleApp: (containerId: string) => void;
isLoading: boolean;
}
const APP_CARD_HEIGHT = 80;
@@ -52,6 +54,7 @@ export const DockerDiscoveryIndicator = ({
apps,
selectedAppIds,
onToggleApp,
isLoading,
}: DockerDiscoveryIndicatorProps) => {
const hasApps = apps.length > 0;
const [expanded, setExpanded] = useState(false);
@@ -67,10 +70,39 @@ export const DockerDiscoveryIndicator = ({
const t = useScopedI18n("init.step.integrations.docker");
const totalCount = integrations.length + apps.length;
if (totalCount === 0) return null;
if (!isLoading && totalCount === 0) return null;
const selectedAppCount = apps.filter((app) => selectedAppIds.has(app.containerId)).length;
if (isLoading) {
return (
<Paper
withBorder
p="sm"
radius="md"
style={{
borderColor: "var(--mantine-color-blue-4)",
background: "var(--mantine-color-blue-light)",
}}
>
<Group gap="sm" wrap="nowrap">
<ThemeIcon variant="light" color="blue" size="lg" radius="md">
<IconBrandDocker size={20} />
</ThemeIcon>
<Stack gap={0} style={{ flex: 1 }}>
<Text size="sm" fw={600}>
{t("scanning")}
</Text>
<Text size="xs" c="dimmed">
{t("scanningDescription")}
</Text>
</Stack>
<Loader size="sm" color="blue" type="dots" />
</Group>
</Paper>
);
}
return (
<Paper
withBorder
@@ -56,10 +56,13 @@ export const InitIntegrations = () => {
const { mutateAsync: setupIntegrations } = clientApi.onboard.setupIntegrations.useMutation();
const { mutateAsync: createApps } = clientApi.onboard.createAppsFromDiscovery.useMutation();
const { data: dockerData } = clientApi.onboard.discoverDockerServices.useQuery(undefined, {
retry: false,
refetchOnWindowFocus: false,
});
const { data: dockerData, isPending: isDockerLoading } = clientApi.onboard.discoverDockerServices.useQuery(
undefined,
{
retry: false,
refetchOnWindowFocus: false,
},
);
const dockerIntegrations = dockerData?.status === "success" ? dockerData.integrations : [];
const dockerApps = dockerData?.status === "success" ? dockerData.apps : [];
@@ -206,6 +209,7 @@ export const InitIntegrations = () => {
apps={dockerApps}
selectedAppIds={selectedAppIds}
onToggleApp={handleToggleApp}
isLoading={isDockerLoading}
/>
<Stack gap="xs">
@@ -249,6 +253,7 @@ export const InitIntegrations = () => {
onClick={handleContinueToConfig}
disabled={selectedKinds.length === 0 || undefined}
rightSection={<IconArrowRight size={16} stroke={1.5} />}
suppressHydrationWarning
>
{t("action.continue")} {selectedKinds.length > 0 && `(${selectedKinds.length})`}
</Button>
+3 -1
View File
@@ -125,7 +125,9 @@
"detected": "Docker detected {count} services",
"detectedBreakdown": "{integrations} integrations · {apps} apps",
"appBadge": "App",
"appSectionLabel": "Apps to add to dashboard ({count} selected)"
"appSectionLabel": "Apps to add to dashboard ({count} selected)",
"scanning": "Scanning Docker...",
"scanningDescription": "Looking for running containers on your system"
}
},
"finish": {