From d1974fe8cf060059d3df0e1789794adda078ebf0 Mon Sep 17 00:00:00 2001 From: biersoeckli Date: Sun, 8 Nov 2026 09:40:35 +0000 Subject: [PATCH 1/7] feat: implement advanced network settings and context for app drawer --- .../advanced-network-settings.test.ts | 47 +++++++++++++++++++ .../app-details-backups-tab.tsx | 27 +++++++---- .../app-details-drawer.tsx | 3 ++ .../app-details-settings.tsx | 35 ++++++++++++-- .../app-drawer-app-context.ts | 4 ++ .../advanced/health-check-settings.tsx | 2 +- 6 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts create mode 100644 src/app/project/[projectId]/app-components/app-drawer-components/app-drawer-app-context.ts diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts b/src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts new file mode 100644 index 00000000..9282c9f7 --- /dev/null +++ b/src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts @@ -0,0 +1,47 @@ +import React from 'react'; +import { fireEvent, render, screen, cleanup } from '@testing-library/react'; +import { afterEach, expect, it, vi } from 'vitest'; +import { NestedDrawerProvider } from './nested-drawer'; +import { DrawerSettings } from './app-details-settings'; +import { AppDrawerAppContext } from './app-drawer-app-context'; +import { RolePermissionEnum } from '@/shared/model/role-extended.model'; +import type { AppExtendedModel } from '@/shared/model/app-extended.model'; + +vi.mock('@/components/ui/drawer', () => ({ + Drawer: ({ children }: React.PropsWithChildren) => children, + DrawerContent: ({ children }: React.PropsWithChildren) => children, + DrawerDescription: ({ children }: React.PropsWithChildren) => children, + DrawerHeader: ({ children }: React.PropsWithChildren) => children, + DrawerTitle: ({ children }: React.PropsWithChildren) => children, +})); +vi.mock('@/components/ui/scroll-area', () => ({ ScrollArea: ({ children }: React.PropsWithChildren) => children })); +vi.mock('@/app/project/app/[appId]/domains/node-ports', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Ports: ${app.appNodePorts.length}`) })); +vi.mock('@/app/project/app/[appId]/advanced/basic-auth', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Credentials: ${app.appBasicAuths.length}`) })); +vi.mock('@/app/project/app/[appId]/advanced/actions', () => ({ saveHealthCheck: vi.fn() })); +vi.mock('@/app/project/app/[appId]/advanced/health-check-settings', () => ({ default: () => null })); +vi.mock('@/app/project/app/[appId]/advanced/network-policy', () => ({ default: () => null })); +vi.mock('@/app/project/app/[appId]/environment/env-edit', () => ({ default: () => null })); +vi.mock('@/app/project/app/[appId]/general/app-container-config', () => ({ default: () => null })); +vi.mock('@/app/project/app/[appId]/general/app-rate-limits', () => ({ default: () => null })); +vi.mock('@/app/project/app/[appId]/general/app-source', () => ({ default: () => null })); +vi.mock('@/app/project/app/[appId]/volumes/storages', () => ({ default: () => null })); +vi.mock('@/components/custom/domains-card', () => ({ default: () => null })); +vi.mock('@/components/custom/file-mounts-card', () => ({ default: () => null })); +vi.mock('./app-details-environment', () => ({ DrawerEnvironment: () => null })); + +afterEach(cleanup); + +it('updates network settings in an open drawer when app data changes', () => { + const app = { id: 'app', appNodePorts: [], appBasicAuths: [], appDomains: [], appFileMounts: [] } as unknown as AppExtendedModel; + const view = (value: AppExtendedModel) => React.createElement(AppDrawerAppContext.Provider, { value }, + React.createElement(NestedDrawerProvider, null, + React.createElement(DrawerSettings, { app: value, role: RolePermissionEnum.READWRITE, storageClasses: [] }), + ), + ); + const { rerender } = render(view(app)); + fireEvent.click(screen.getByRole('button', { name: 'Advanced network settings' })); + expect(screen.getByText('Ports: 0')).toBeTruthy(); + rerender(view({ ...app, appNodePorts: [{}], appBasicAuths: [{}] } as AppExtendedModel)); + expect(screen.getByText('Ports: 1')).toBeTruthy(); + expect(screen.getByText('Credentials: 1')).toBeTruthy(); +}); diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx index 8ab08868..b41709c1 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx +++ b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx @@ -1,13 +1,14 @@ 'use client'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { Archive, Download, Loader2, Pen, Play } from 'lucide-react'; +import { Archive, Database, Download, HardDrive, Loader2, Pen, Play } from 'lucide-react'; import type { S3Target } from '@prisma/client'; import { DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel } from '@/components/ui/dropdown-menu'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty'; import LoadingSpinner from '@/components/ui/loading-spinner'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { Button } from '@/components/ui/button'; import { SplitButton } from '@/components/custom/split-button'; import VolumeBackupList from '@/app/project/app/[appId]/volumes/volume-backup'; @@ -159,21 +160,31 @@ export function DrawerBackupsTab({ app, role, s3Targets, volumeBackups }: { No backups yet None of this app's backup schedules has created a backup yet. - : + :
- Created - Size + Created + Size Backup Location - Type - Actions + Type + Actions {backups.map((backup) => { const backupId = `${backup.schedule.id}:${backup.key}`; return {formatDateTime(backup.backupDate, true)} {backup.sizeBytes ? KubeSizeConverter.convertBytesToReadableSize(backup.sizeBytes) : 'Unknown'} - {backup.schedule.target.name} - {backupType(app, backup.schedule)} +
{backup.schedule.target.name}
+ + + }> + {app.appType !== 'APP' && backup.schedule.useDatabaseBackup + ? + {backupType(app, backup.schedule)} + + + + + Advanced Network Settings + Configure node ports and basic authentication. + + + + + -
Health Checks - Configure healthchecks so that k3s can automatically monitor when your application is fully started up and ready to receive traffic (In kubernetes terms, startup, readiness and liveness probes). + Configure healthchecks so that k3s can automatically monitor when your application is fully started up and ready to receive traffic. @@ -213,3 +230,13 @@ export function DrawerSettings({
); } + +function AdvancedNetworkSettings({ readonly }: { readonly: boolean }) { + const app = useContext(AppDrawerAppContext); + if (!app) return null; + + return
+ + +
; +} diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-drawer-app-context.ts b/src/app/project/[projectId]/app-components/app-drawer-components/app-drawer-app-context.ts new file mode 100644 index 00000000..92c88239 --- /dev/null +++ b/src/app/project/[projectId]/app-components/app-drawer-components/app-drawer-app-context.ts @@ -0,0 +1,4 @@ +import { createContext } from 'react'; +import type { AppExtendedModel } from '@/shared/model/app-extended.model'; + +export const AppDrawerAppContext = createContext(undefined); diff --git a/src/app/project/app/[appId]/advanced/health-check-settings.tsx b/src/app/project/app/[appId]/advanced/health-check-settings.tsx index 1aa58280..aaa84653 100644 --- a/src/app/project/app/[appId]/advanced/health-check-settings.tsx +++ b/src/app/project/app/[appId]/advanced/health-check-settings.tsx @@ -79,7 +79,7 @@ export default function HealthCheckSettings({ workload, readonly, saveHealthChec {!hideCard && Health Check Settings - Configure healthchecks so that k3s can automatically monitor when your application is fully started up and ready to receive traffic (In kubernetes terms, startup, readiness and liveness probes). + Configure healthchecks so that k3s can automatically monitor when your application is fully started up and ready to receive traffic. }
From 98f7b1b9e78af54c2336abcb3dd892a8af3676b8 Mon Sep 17 00:00:00 2001 From: biersoeckli Date: Sun, 8 Nov 2026 10:58:04 +0000 Subject: [PATCH 2/7] feat: add tooltip for copying environment variable values in app drawer --- .../app-details-environment.tsx | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-environment.tsx b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-environment.tsx index 5744297b..f1c9f383 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-environment.tsx +++ b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-environment.tsx @@ -3,6 +3,7 @@ import { Copy, Eye, EyeOff } from 'lucide-react'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import type { AppExtendedModel } from '@/shared/model/app-extended.model'; import { EnvVarUtils } from '@/shared/utils/env-var.utils'; import { toast } from 'sonner'; @@ -60,9 +61,30 @@ function VariableList({ - - {isVisible ? variable.value : '••••••••'} - + + + { + try { + await navigator.clipboard.writeText(variable.value); + toast.success('Variable value copied'); + } catch { + toast.error('Failed to copy variable value'); + } + }} + /> + } + > + {isVisible ? variable.value : '••••••••'} + + Click to copy + +
{app.appBasicAuths.length} Auth Credentials @@ -91,7 +89,7 @@ export default function BasicAuth({ app, readonly, hideCard = false }: {
} - {!readonly && + {!readonly && } - + ; } diff --git a/src/app/project/app/[appId]/advanced/network-policy-graph.tsx b/src/app/project/[projectId]/app-components/app-detail/advanced/network-policy-graph.tsx similarity index 100% rename from src/app/project/app/[appId]/advanced/network-policy-graph.tsx rename to src/app/project/[projectId]/app-components/app-detail/advanced/network-policy-graph.tsx diff --git a/src/app/project/app/[appId]/advanced/network-policy.tsx b/src/app/project/[projectId]/app-components/app-detail/advanced/network-policy.tsx similarity index 92% rename from src/app/project/app/[appId]/advanced/network-policy.tsx rename to src/app/project/[projectId]/app-components/app-detail/advanced/network-policy.tsx index 04f01a07..ccb9b210 100644 --- a/src/app/project/app/[appId]/advanced/network-policy.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/advanced/network-policy.tsx @@ -22,7 +22,7 @@ import NetworkPolicyGraph from './network-policy-graph'; type Project = { id: string; name: string; apps: { id: string; name: string; appType: AppExtendedModel['appType'] }[]; agents: { id: string; name: string }[] }; -export default function NetworkPolicy({ app, readonly, hideCard = false }: { app: AppExtendedModel; readonly: boolean; hideCard?: boolean }) { +export default function NetworkPolicy({ app, readonly }: { app: AppExtendedModel; readonly: boolean; }) { const router = useRouter(); const [draft, setDraft] = useState(() => AppNetworkPolicyDraftUtils.fromApp(app)); const [baseline, setBaseline] = useState(() => AppNetworkPolicyDraftUtils.fromApp(app)); @@ -94,11 +94,10 @@ export default function NetworkPolicy({ app, readonly, hideCard = false }: { app ))} onAdd={addRule} />, { maxWidth: 'max-w-md' }); - const CardWrapper = hideCard ? 'div' : Card; - return - {!hideCard && Network PolicyControl which traffic can reach this app and where it can connect.} - + return + {Network PolicyControl which traffic can reach this app and where it can connect.} + setDraft(current => ({ ...current, useNetworkPolicy }))} /> @@ -119,10 +118,10 @@ export default function NetworkPolicy({ app, readonly, hideCard = false }: { app } - {!readonly && dirty && + {!readonly && dirty && } - ; + ; } function SettingRow({ label, description, checked, disabled, onChange, hint }: { label: string; description: string; checked: boolean; disabled: boolean; onChange: (checked: boolean) => void; hint?: string }) { diff --git a/src/app/project/app/[appId]/credentials/actions.ts b/src/app/project/[projectId]/app-components/app-detail/credentials/actions.ts similarity index 100% rename from src/app/project/app/[appId]/credentials/actions.ts rename to src/app/project/[projectId]/app-components/app-detail/credentials/actions.ts diff --git a/src/app/project/app/[appId]/credentials/db-crendentials.tsx b/src/app/project/[projectId]/app-components/app-detail/credentials/db-crendentials.tsx similarity index 100% rename from src/app/project/app/[appId]/credentials/db-crendentials.tsx rename to src/app/project/[projectId]/app-components/app-detail/credentials/db-crendentials.tsx diff --git a/src/app/project/app/[appId]/credentials/db-gate-db-tool.tsx b/src/app/project/[projectId]/app-components/app-detail/credentials/db-gate-db-tool.tsx similarity index 100% rename from src/app/project/app/[appId]/credentials/db-gate-db-tool.tsx rename to src/app/project/[projectId]/app-components/app-detail/credentials/db-gate-db-tool.tsx diff --git a/src/app/project/app/[appId]/credentials/db-tools.tsx b/src/app/project/[projectId]/app-components/app-detail/credentials/db-tools.tsx similarity index 100% rename from src/app/project/app/[appId]/credentials/db-tools.tsx rename to src/app/project/[projectId]/app-components/app-detail/credentials/db-tools.tsx diff --git a/src/app/project/app/[appId]/credentials/phpmyadmin-db-tool.tsx b/src/app/project/[projectId]/app-components/app-detail/credentials/phpmyadmin-db-tool.tsx similarity index 100% rename from src/app/project/app/[appId]/credentials/phpmyadmin-db-tool.tsx rename to src/app/project/[projectId]/app-components/app-detail/credentials/phpmyadmin-db-tool.tsx diff --git a/src/app/project/app/[appId]/domains/actions.ts b/src/app/project/[projectId]/app-components/app-detail/domains/actions.ts similarity index 100% rename from src/app/project/app/[appId]/domains/actions.ts rename to src/app/project/[projectId]/app-components/app-detail/domains/actions.ts diff --git a/src/app/project/app/[appId]/domains/node-port-edit-dialog.tsx b/src/app/project/[projectId]/app-components/app-detail/domains/node-port-edit-dialog.tsx similarity index 100% rename from src/app/project/app/[appId]/domains/node-port-edit-dialog.tsx rename to src/app/project/[projectId]/app-components/app-detail/domains/node-port-edit-dialog.tsx diff --git a/src/app/project/app/[appId]/domains/node-ports.tsx b/src/app/project/[projectId]/app-components/app-detail/domains/node-ports.tsx similarity index 94% rename from src/app/project/app/[appId]/domains/node-ports.tsx rename to src/app/project/[projectId]/app-components/app-detail/domains/node-ports.tsx index 50b792ea..4e1e73fc 100644 --- a/src/app/project/app/[appId]/domains/node-ports.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/domains/node-ports.tsx @@ -11,10 +11,9 @@ import { Toast } from "@/frontend/utils/toast.utils"; import { useConfirmDialog, useDialog } from "@/frontend/states/zustand.states"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -export default function NodePortsCard({ app, readonly, hideCard = false }: { +export default function NodePortsCard({ app, readonly }: { app: AppExtendedModel; readonly: boolean; - hideCard?: boolean; }) { const { openConfirmDialog } = useConfirmDialog(); const { openDialog } = useDialog(); @@ -29,17 +28,16 @@ export default function NodePortsCard({ app, readonly, hideCard = false }: { await Toast.fromAction(() => deleteNodePort(nodePortId)); } }; - const CardWrapper = hideCard ? 'div' : Card; return ( - + Node Ports Expose this app directly on a node/host port, bypassing Traefik. Useful for non-HTTP workloads such as SFTP, game servers, or other TCP/UDP services. - {app.appNodePorts.length > 0 && + {app.appNodePorts.length > 0 && {app.appNodePorts.length} Node Port{app.appNodePorts.length !== 1 ? 's' : ''} @@ -80,7 +78,7 @@ export default function NodePortsCard({ app, readonly, hideCard = false }: {
} {!readonly && ( - + )} -
+ ); } diff --git a/src/app/project/app/[appId]/environment/actions.ts b/src/app/project/[projectId]/app-components/app-detail/environment/actions.ts similarity index 100% rename from src/app/project/app/[appId]/environment/actions.ts rename to src/app/project/[projectId]/app-components/app-detail/environment/actions.ts diff --git a/src/app/project/app/[appId]/environment/env-edit.tsx b/src/app/project/[projectId]/app-components/app-detail/environment/env-edit.tsx similarity index 83% rename from src/app/project/app/[appId]/environment/env-edit.tsx rename to src/app/project/[projectId]/app-components/app-detail/environment/env-edit.tsx index 65c22920..1a9c2a81 100644 --- a/src/app/project/app/[appId]/environment/env-edit.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/environment/env-edit.tsx @@ -2,7 +2,7 @@ import type { z } from "zod"; import { SubmitButton } from "@/components/custom/submit-button"; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { CardContent, CardFooter } from "@/components/ui/card"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { FormUtils } from "@/frontend/utils/form.utilts"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -16,10 +16,9 @@ import { Textarea } from "@/components/ui/textarea"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; -export default function EnvEdit({ app, readonly, hideCard = false }: { +export default function EnvEdit({ app, readonly }: { app: AppExtendedModel; readonly: boolean; - hideCard?: boolean; }) { const form = useForm, unknown, z.output>({ resolver: zodResolver(appEnvVariablesZodModel), @@ -38,23 +37,15 @@ export default function EnvEdit({ app, readonly, hideCard = false }: { }, [form, state]); const buildArgsEnabled = app.appType === 'APP' && app.buildMethod === 'DOCKERFILE'; - const CardWrapper = hideCard ? 'div' : Card; return <> - - {!hideCard && - Environment Variables - - Provide optional environment variables for your application. - {app.appType !== 'APP' &&
You should not change ENV variables for databases.
} - -
-
} +
+ form.handleSubmit((data) => { return formAction(data); })()}> - + - {!readonly && + {!readonly && Save } - +
; } diff --git a/src/app/project/app/[appId]/general/actions.ts b/src/app/project/[projectId]/app-components/app-detail/general/actions.ts similarity index 100% rename from src/app/project/app/[appId]/general/actions.ts rename to src/app/project/[projectId]/app-components/app-detail/general/actions.ts diff --git a/src/app/project/app/[appId]/general/app-container-config.tsx b/src/app/project/[projectId]/app-components/app-detail/general/app-container-config.tsx similarity index 97% rename from src/app/project/app/[appId]/general/app-container-config.tsx rename to src/app/project/[projectId]/app-components/app-detail/general/app-container-config.tsx index 6a8deb49..f6f3f3f8 100644 --- a/src/app/project/app/[appId]/general/app-container-config.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/general/app-container-config.tsx @@ -23,10 +23,9 @@ import { ContainerCommangArgsUtils } from "@/shared/utils/container-command-args export type AppContainerConfigInputModel = z.infer; -export default function GeneralAppContainerConfig({ app, readonly, hideCard = false }: { +export default function GeneralAppContainerConfig({ app, readonly }: { app: AppExtendedModel; readonly: boolean; - hideCard?: boolean; }) { const inputValue = (value: unknown) => typeof value === 'string' || typeof value === 'number' ? value : ''; // Parse containerArgs from JSON string to array @@ -61,11 +60,10 @@ export default function GeneralAppContainerConfig({ app, readonly, hideCard = fa }, [form, state]); const values = form.watch(); - const CardWrapper = hideCard ? 'div' : Card; return ( - - {!hideCard && + + { Container Configuration Override image defaults only when your workload needs custom startup behavior or Linux security settings. @@ -76,7 +74,7 @@ export default function GeneralAppContainerConfig({ app, readonly, hideCard = fa
form.handleSubmit((data) => { return formAction(data); })()}> - +

Runtime

@@ -212,7 +210,7 @@ export default function GeneralAppContainerConfig({ app, readonly, hideCard = fa
{!readonly && ( - + Save

{state?.message}

@@ -220,6 +218,6 @@ export default function GeneralAppContainerConfig({ app, readonly, hideCard = fa - + ); } diff --git a/src/app/project/app/[appId]/general/app-rate-limits.tsx b/src/app/project/[projectId]/app-components/app-detail/general/app-rate-limits.tsx similarity index 93% rename from src/app/project/app/[appId]/general/app-rate-limits.tsx rename to src/app/project/[projectId]/app-components/app-detail/general/app-rate-limits.tsx index 290aad00..111599e6 100644 --- a/src/app/project/app/[appId]/general/app-rate-limits.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/general/app-rate-limits.tsx @@ -2,7 +2,7 @@ import type { z } from "zod"; import { SubmitButton } from "@/components/custom/submit-button"; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { CardContent, CardFooter } from "@/components/ui/card"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { FormUtils } from "@/frontend/utils/form.utilts"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -22,10 +22,9 @@ import { KubeSizeConverter } from "@/shared/utils/kubernetes-size-converter.util import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -export default function GeneralAppRateLimits({ app, readonly, hideCard = false }: { +export default function GeneralAppRateLimits({ app, readonly }: { app: AppExtendedModel; readonly: boolean; - hideCard?: boolean; }) { const form = useForm, unknown, z.output>({ resolver: zodResolver(appRateLimitsZodModel), @@ -60,19 +59,15 @@ export default function GeneralAppRateLimits({ app, readonly, hideCard = false } FormUtils.mapValidationErrorsToForm(state, form); }, [form, state]); - const CardWrapper = hideCard ? 'div' : Card; return <> - - {!hideCard && - Container Rate Limits - Provide optional rate Limits per running container instance. - } +
+
form.handleSubmit((data) => { return formAction(data); })()}> - +
- {!readonly && + {!readonly && Save

{state?.message}

} - +
; } diff --git a/src/app/project/app/[appId]/general/app-source-wizard/app-source-wizard-dialog.tsx b/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/app-source-wizard-dialog.tsx similarity index 95% rename from src/app/project/app/[appId]/general/app-source-wizard/app-source-wizard-dialog.tsx rename to src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/app-source-wizard-dialog.tsx index d76875e8..97a3b7e2 100644 --- a/src/app/project/app/[appId]/general/app-source-wizard/app-source-wizard-dialog.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/app-source-wizard-dialog.tsx @@ -15,17 +15,17 @@ import { useEffect, useState } from "react"; import { toast } from "sonner"; import { detectDockerfilePath, ensureGitSshPublicKey, generateOrRegenerateGitSshKey, getGitBranches, saveGeneralAppSourceInfo } from "../actions"; import { BuildMethodStep } from "./build-method-step"; -import { ContainerImageStep } from "./container-image-step"; -import { DockerfilePathStep } from "./dockerfile-path-step"; +import { ContainerImageStep } from "../../../../../workload-components/source-wizard/container-image-step"; +import { DockerfilePathStep } from "../../../../../workload-components/source-wizard/dockerfile-path-step"; import { FrameworkConfigurationStep } from "./framework-configuration-step"; import { FrameworkStep } from "./framework-step"; -import { GitBranchStep } from "./git-branch-step"; -import { GitHttpsUrlStep } from "./git-https-url-step"; -import { GitSshUrlStep } from "./git-ssh-url-step"; -import { SourceSummaryStep } from "./source-summary-step"; -import { SourceTypeStep } from "./source-type-step"; -import { defaultDockerfilePath, sourceTypeLabels, SourceType, StepId } from "./types"; -import { WizardProgress } from "./wizard-progress"; +import { GitBranchStep } from "../../../../../workload-components/source-wizard/git-branch-step"; +import { GitHttpsUrlStep } from "../../../../../workload-components/source-wizard/git-https-url-step"; +import { GitSshUrlStep } from "../../../../../workload-components/source-wizard/git-ssh-url-step"; +import { SourceSummaryStep } from "../../../../../workload-components/source-wizard/source-summary-step"; +import { SourceTypeStep } from "../../../../../workload-components/source-wizard/source-type-step"; +import { defaultDockerfilePath, sourceTypeLabels, SourceType, StepId } from "../../../../../workload-components/source-wizard/types"; +import { WizardProgress } from "../../../../../workload-components/source-wizard/wizard-progress"; import { deploy } from "../../actions"; import LoadingSpinner from "@/components/ui/loading-spinner"; diff --git a/src/app/project/app/[appId]/general/app-source-wizard/build-method-step.tsx b/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/build-method-step.tsx similarity index 100% rename from src/app/project/app/[appId]/general/app-source-wizard/build-method-step.tsx rename to src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/build-method-step.tsx diff --git a/src/app/project/app/[appId]/general/app-source-wizard/framework-configuration-step.tsx b/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-configuration-step.tsx similarity index 97% rename from src/app/project/app/[appId]/general/app-source-wizard/framework-configuration-step.tsx rename to src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-configuration-step.tsx index 38bbb565..19519df1 100644 --- a/src/app/project/app/[appId]/general/app-source-wizard/framework-configuration-step.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-configuration-step.tsx @@ -4,7 +4,7 @@ import { JsFramework, jsFrameworkPresets } from "@/shared/model/js-framework.mod import { ChevronDown, Cpu, Download, FolderOutput, FolderTree, Hammer, Play } from "lucide-react"; import Image from "next/image"; import { IconInput } from "./source-wizard-fields"; -import { SourceWizardInput } from "./types"; +import { SourceWizardInput } from "../../../../../workload-components/source-wizard/types"; export function FrameworkConfigurationStep({ framework, formData, onChange, onChangeFramework }: { framework: JsFramework; diff --git a/src/app/project/app/[appId]/general/app-source-wizard/framework-step.tsx b/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-step.tsx similarity index 100% rename from src/app/project/app/[appId]/general/app-source-wizard/framework-step.tsx rename to src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-step.tsx diff --git a/src/app/project/app/[appId]/general/app-source-wizard/source-wizard-fields.tsx b/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields.tsx similarity index 100% rename from src/app/project/app/[appId]/general/app-source-wizard/source-wizard-fields.tsx rename to src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields.tsx diff --git a/src/app/project/app/[appId]/general/app-source.tsx b/src/app/project/[projectId]/app-components/app-detail/general/app-source.tsx similarity index 86% rename from src/app/project/app/[appId]/general/app-source.tsx rename to src/app/project/[projectId]/app-components/app-detail/general/app-source.tsx index f0b6a0d7..d4518973 100644 --- a/src/app/project/app/[appId]/general/app-source.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/general/app-source.tsx @@ -1,29 +1,25 @@ 'use client'; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { useDialog } from "@/frontend/states/zustand.states"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; import { AppBuildMethod } from "@/shared/model/app-source-info.model"; import { JsFramework, jsFrameworkPresets } from "@/shared/model/js-framework.model"; import { Boxes, Container, FileCode2, GitBranch, KeyRound, Link as LinkIcon, LockKeyhole, Package, Server } from "lucide-react"; import { AppSourceWizardDialog } from "./app-source-wizard/app-source-wizard-dialog"; -import { PublicDeployKeyDialog } from "./app-source-wizard/public-deploy-key-dialog"; -import { ReadonlyInfo } from "./app-source-wizard/readonly-info"; -import { buildMethodLabels, defaultDockerfilePath, SourceType, sourceTypeLabels } from "./app-source-wizard/types"; +import { PublicDeployKeyDialog } from "../../../../workload-components/source-wizard/public-deploy-key-dialog"; +import { ReadonlyInfo } from "../../../../workload-components/source-wizard/readonly-info"; +import { buildMethodLabels, defaultDockerfilePath, SourceType, sourceTypeLabels } from "../../../../workload-components/source-wizard/types"; import { AppSourceUtils } from "@/frontend/utils/app-source.utils"; import FrameworkConfigurationCard from './framework-configuration-card'; export default function GeneralAppSource({ app, readonly, - gitSshPublicKey, - hideCard = false -}: { + gitSshPublicKey }: { app: AppExtendedModel; readonly: boolean; gitSshPublicKey?: string; - hideCard?: boolean }) { const { openDialog } = useDialog(); const configured = AppSourceUtils.isConfiguredSource(app); @@ -47,27 +43,10 @@ export default function GeneralAppSource({ ) ); - if (hideCard) { - return
- {cardContent} - {configured && app.buildMethod === 'FRAMEWORK' && } -
; - } - - return ( - <> - - - Source - Connect the source QuickStack should build or run. - - - {cardContent} - - - {configured && app.buildMethod === 'FRAMEWORK' && } - - ); + return
+ {cardContent} + {configured && app.buildMethod === 'FRAMEWORK' && } +
; } function EmptySourceState({ readonly, onConnect }: { readonly: boolean; onConnect: () => void }) { diff --git a/src/app/project/app/[appId]/general/framework-configuration-card.tsx b/src/app/project/[projectId]/app-components/app-detail/general/framework-configuration-card.tsx similarity index 100% rename from src/app/project/app/[appId]/general/framework-configuration-card.tsx rename to src/app/project/[projectId]/app-components/app-detail/general/framework-configuration-card.tsx diff --git a/src/app/project/app/[appId]/overview/actions.ts b/src/app/project/[projectId]/app-components/app-detail/overview/actions.ts similarity index 100% rename from src/app/project/app/[appId]/overview/actions.ts rename to src/app/project/[projectId]/app-components/app-detail/overview/actions.ts diff --git a/src/app/project/app/[appId]/overview/deployments-grid-view.tsx b/src/app/project/[projectId]/app-components/app-detail/overview/deployments-grid-view.tsx similarity index 98% rename from src/app/project/app/[appId]/overview/deployments-grid-view.tsx rename to src/app/project/[projectId]/app-components/app-detail/overview/deployments-grid-view.tsx index 200f2114..d2151bc8 100644 --- a/src/app/project/app/[appId]/overview/deployments-grid-view.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/overview/deployments-grid-view.tsx @@ -11,7 +11,7 @@ import { Item, ItemContent, ItemTitle } from '@/components/ui/item'; import { formatDateTime } from '@/frontend/utils/format.utils'; import type { DeploymentInfoModel } from '@/shared/model/deployment-info.model'; import { appBuildMethodLabels } from '@/shared/model/app-source-info.model'; -import DeploymentStatusBadge from './deployment-status-badge'; +import DeploymentStatusBadge from '../../../../workload-components/deployment-status-badge'; import { Empty, EmptyDescription, diff --git a/src/app/project/app/[appId]/overview/deployments.tsx b/src/app/project/[projectId]/app-components/app-detail/overview/deployments.tsx similarity index 71% rename from src/app/project/app/[appId]/overview/deployments.tsx rename to src/app/project/[projectId]/app-components/app-detail/overview/deployments.tsx index ef641101..30fdc19f 100644 --- a/src/app/project/app/[appId]/overview/deployments.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/overview/deployments.tsx @@ -1,14 +1,7 @@ import { useCallback, useEffect, useState } from 'react'; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from '@/components/ui/card'; import FullLoadingSpinner from '@/components/ui/full-loading-spinnter'; import { usePolling } from '@/frontend/hooks/use-polling'; -import { useBuildStatus, useConfirmDialog, useDialog } from '@/frontend/states/zustand.states'; +import { useBuildStatus, useConfirmDialog } from '@/frontend/states/zustand.states'; import { Toast } from '@/frontend/utils/toast.utils'; import type { AppExtendedModel } from '@/shared/model/app-extended.model'; import type { DeploymentInfoModel } from '@/shared/model/deployment-info.model'; @@ -19,25 +12,18 @@ import { getDeploymentsAndBuildsForApp, rollbackToDeployment, } from './actions'; -import { BuildLogsDialogContent } from './build-logs-overlay'; -import { DeploymentsDefaultView } from './deployments-default-view'; import { DeploymentsGridView } from './deployments-grid-view'; -export type BuildsTabView = 'default' | 'grid'; - export default function BuildsTab({ app, role, - view = 'default', - onShowLogs: onShowLogsOverride, + onShowLogs, }: { app: AppExtendedModel; role: RolePermissionEnum; - view?: BuildsTabView; - onShowLogs?: (deployment: DeploymentInfoModel) => void; + onShowLogs: (deployment: DeploymentInfoModel) => void; }) { const { openConfirmDialog } = useConfirmDialog(); - const { openDialog } = useDialog(); const [appBuilds, setAppBuilds] = useState(); const [, setError] = useState(); @@ -80,15 +66,6 @@ export default function BuildsTab({ await updateBuilds(); }; - const showLogs = onShowLogsOverride ?? ((deployment: DeploymentInfoModel) => - openDialog( - , - { maxWidth: '1300px' }, - )); const canStopBuild = (deployment: DeploymentInfoModel) => role === RolePermissionEnum.READWRITE && !!deployment.buildJobName @@ -121,28 +98,12 @@ export default function BuildsTab({ deployments: appBuilds ?? [], canStopBuild, canRollback, - onShowLogs: showLogs, + onShowLogs, onStopBuild: (deployment: DeploymentInfoModel) => void stopBuild(deployment), onRollback: (deployment: DeploymentInfoModel) => void rollback(deployment), }; - if (view === 'grid') { - return !appBuilds - ? - : ; - } - - return ( - - - Deployments - - This is an overview of the last deplyoments for this App. - - - - {!appBuilds ? : } - - - ); + return !appBuilds + ? + : ; } diff --git a/src/app/project/app/[appId]/overview/logs.tsx b/src/app/project/[projectId]/app-components/app-detail/overview/logs.tsx similarity index 86% rename from src/app/project/app/[appId]/overview/logs.tsx rename to src/app/project/[projectId]/app-components/app-detail/overview/logs.tsx index 9a5c4ca9..11ddc0f4 100644 --- a/src/app/project/app/[appId]/overview/logs.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/overview/logs.tsx @@ -1,7 +1,7 @@ -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { CardContent } from "@/components/ui/card"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; -import { Fragment, useEffect, useState } from "react"; -import LogsStreamed from "../../../../../components/custom/logs-streamed"; +import { useEffect, useState } from "react"; +import LogsStreamed from "../../../../../../components/custom/logs-streamed"; import { getPodsForApp as getPodsForAppAction } from "./actions"; import { PodsInfoModel } from "@/shared/model/pods-info.model"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; @@ -26,13 +26,9 @@ import { export default function Logs({ app, role, - hideCard = false, - useFullHeight = false, }: { app: AppExtendedModel; role: RolePermissionEnum; - hideCard?: boolean; - useFullHeight?: boolean; }) { const [selectedPod, setSelectedPod] = useState(undefined); const [appPods, setAppPods] = useState(undefined); @@ -118,20 +114,16 @@ export default function Logs({ } }, [appPods, selectedPod]); - const ContentWrapper = hideCard ? Fragment : Card; const content = <> - {!hideCard && - Logs - Read logs from all running Containers. - } + {!appPods && } {appPods && appPods.length === 0 && ( - + @@ -182,23 +174,15 @@ export default function Logs({
} - {app.projectId && selectedPod &&
+ {app.projectId && selectedPod &&
} ; - if (hideCard && useFullHeight) { - return
{content}
; - } - - return <> - - {content} - - ; + return
{content}
; } diff --git a/src/app/project/app/[appId]/overview/logs.unit.spec.ts b/src/app/project/[projectId]/app-components/app-detail/overview/logs.unit.spec.ts similarity index 97% rename from src/app/project/app/[appId]/overview/logs.unit.spec.ts rename to src/app/project/[projectId]/app-components/app-detail/overview/logs.unit.spec.ts index 48157031..5ae4deb1 100644 --- a/src/app/project/app/[appId]/overview/logs.unit.spec.ts +++ b/src/app/project/[projectId]/app-components/app-detail/overview/logs.unit.spec.ts @@ -10,7 +10,7 @@ vi.mock('./actions', () => ({ getPodsForApp: vi.fn(), })); -vi.mock('../../../../../components/custom/logs-streamed', () => ({ default: () => null })); +vi.mock('../../../../../../components/custom/logs-streamed', () => ({ default: () => null })); vi.mock('./terminal-overlay', () => ({ TerminalDialog: () => null })); vi.mock('@/components/custom/logs-overlay', () => ({ LogsDialogContent: () => null })); diff --git a/src/app/project/app/[appId]/overview/monitoring-app.tsx b/src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.tsx similarity index 89% rename from src/app/project/app/[appId]/overview/monitoring-app.tsx rename to src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.tsx index d4881aa6..93eea6ba 100644 --- a/src/app/project/app/[appId]/overview/monitoring-app.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.tsx @@ -1,6 +1,6 @@ -import { Card, CardContent } from "@/components/ui/card"; +import { CardContent } from "@/components/ui/card"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; -import { Fragment, useCallback, useState } from "react"; +import { useCallback, useState } from "react"; import { getRessourceDataApp } from "./actions"; import FullLoadingSpinner from "@/components/ui/full-loading-spinnter"; import { usePolling } from "@/frontend/hooks/use-polling"; @@ -12,10 +12,8 @@ import { cn } from "@/frontend/utils/utils"; export default function MonitoringTab({ app, - hideCard = false, }: { app: AppExtendedModel; - hideCard?: boolean; }) { const [selectedPod, setSelectedPod] = useState(undefined); @@ -40,10 +38,8 @@ export default function MonitoringTab({ usePolling(updateValues, { intervalMs: 10000, runImmediately: true }); - const ContentWrapper = hideCard ? Fragment : Card; return <> - - + {!selectedPod ? : @@ -70,6 +66,5 @@ export default function MonitoringTab({
}
-
; } diff --git a/src/app/project/app/[appId]/overview/monitoring-app.unit.spec.ts b/src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.unit.spec.ts similarity index 100% rename from src/app/project/app/[appId]/overview/monitoring-app.unit.spec.ts rename to src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.unit.spec.ts diff --git a/src/app/project/app/[appId]/overview/terminal-overlay.tsx b/src/app/project/[projectId]/app-components/app-detail/overview/terminal-overlay.tsx similarity index 100% rename from src/app/project/app/[appId]/overview/terminal-overlay.tsx rename to src/app/project/[projectId]/app-components/app-detail/overview/terminal-overlay.tsx diff --git a/src/app/project/app/[appId]/overview/terminal-streamed.tsx b/src/app/project/[projectId]/app-components/app-detail/overview/terminal-streamed.tsx similarity index 100% rename from src/app/project/app/[appId]/overview/terminal-streamed.tsx rename to src/app/project/[projectId]/app-components/app-detail/overview/terminal-streamed.tsx diff --git a/src/app/project/app/[appId]/overview/webhook-deployment.tsx b/src/app/project/[projectId]/app-components/app-detail/overview/webhook-deployment.tsx similarity index 100% rename from src/app/project/app/[appId]/overview/webhook-deployment.tsx rename to src/app/project/[projectId]/app-components/app-detail/overview/webhook-deployment.tsx diff --git a/src/app/project/app/[appId]/volumes/actions.ts b/src/app/project/[projectId]/app-components/app-detail/volumes/actions.ts similarity index 100% rename from src/app/project/app/[appId]/volumes/actions.ts rename to src/app/project/[projectId]/app-components/app-detail/volumes/actions.ts diff --git a/src/app/project/app/[appId]/volumes/shared-storage-edit-overlay.tsx b/src/app/project/[projectId]/app-components/app-detail/volumes/shared-storage-edit-overlay.tsx similarity index 100% rename from src/app/project/app/[appId]/volumes/shared-storage-edit-overlay.tsx rename to src/app/project/[projectId]/app-components/app-detail/volumes/shared-storage-edit-overlay.tsx diff --git a/src/app/project/app/[appId]/volumes/storage-edit-overlay.tsx b/src/app/project/[projectId]/app-components/app-detail/volumes/storage-edit-overlay.tsx similarity index 100% rename from src/app/project/app/[appId]/volumes/storage-edit-overlay.tsx rename to src/app/project/[projectId]/app-components/app-detail/volumes/storage-edit-overlay.tsx diff --git a/src/app/project/app/[appId]/volumes/storages.tsx b/src/app/project/[projectId]/app-components/app-detail/volumes/storages.tsx similarity index 97% rename from src/app/project/app/[appId]/volumes/storages.tsx rename to src/app/project/[projectId]/app-components/app-detail/volumes/storages.tsx index 3ea0698d..8772f630 100644 --- a/src/app/project/app/[appId]/volumes/storages.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/volumes/storages.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; @@ -30,11 +30,10 @@ type AppVolumeWithCapacity = (AppVolume & { usedPercentage?: number; }); -export default function StorageList({ app, readonly, storageClasses, hideCard = false }: { +export default function StorageList({ app, readonly, storageClasses }: { app: AppExtendedModel; storageClasses: string[]; readonly: boolean; - hideCard?: boolean; }) { const [volumesWithStorage, setVolumesWithStorage] = React.useState(app.appVolumes as AppVolumeWithCapacity[]); @@ -145,15 +144,14 @@ export default function StorageList({ app, readonly, storageClasses, hideCard = } } - const CardWrapper = hideCard ? 'div' : Card; return <> - +
Volumes Add one or more volumes to to configure persistent storage within your container. - {volumesWithStorage.length > 0 && + {volumesWithStorage.length > 0 && {app.appVolumes.length} Storage @@ -310,7 +308,7 @@ export default function StorageList({ app, readonly, storageClasses, hideCard =
} - {!readonly && + {!readonly && } - +
; } diff --git a/src/app/project/app/[appId]/volumes/volume-backup-edit-overlay.tsx b/src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup-edit-overlay.tsx similarity index 100% rename from src/app/project/app/[appId]/volumes/volume-backup-edit-overlay.tsx rename to src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup-edit-overlay.tsx diff --git a/src/app/project/app/[appId]/volumes/volume-backup.tsx b/src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup.tsx similarity index 86% rename from src/app/project/app/[appId]/volumes/volume-backup.tsx rename to src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup.tsx index a6df0cb4..542cdc97 100644 --- a/src/app/project/app/[appId]/volumes/volume-backup.tsx +++ b/src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { CardContent, CardFooter } from "@/components/ui/card"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; @@ -21,14 +21,12 @@ export default function VolumeBackupList({ volumeBackups, s3Targets, readonly, - hideCard = false, onBackupScheduleClick, }: { app: AppExtendedModel, s3Targets: S3Target[], volumeBackups: VolumeBackupExtendedModel[]; readonly: boolean; - hideCard?: boolean; onBackupScheduleClick?: (volumeBackup: VolumeBackupExtendedModel) => void; }) { @@ -66,26 +64,20 @@ export default function VolumeBackupList({ } }; - const CardWrapper = hideCard ? 'div' : Card; return <> - - {!hideCard && - Backup Schedules - Configure backup schedules for your volumes. Backups can be stored in a S3 bucket. - } - {volumeBackups.length > 0 && - +
+ + {volumeBackups.length > 0 && +
{volumeBackups.length} Backup Rules - Cron - Retention + Cron + Retention Backup Method Backup Location - + RBD} /> @@ -99,15 +91,15 @@ export default function VolumeBackupList({ {volumeBackups.map(volumeBackup => ( - {volumeBackup.cron} + {volumeBackup.cron} {volumeBackup.retention} {app.appType !== 'APP' && volumeBackup.useDatabaseBackup ? `Database (${app.appType.toLocaleLowerCase()})` : 'Archive of Volume'} - {volumeBackup.target.name} - + {volumeBackup.target.name} + {volumeBackup.backupBeforeDeployment ? (volumeBackup.failSilently ? 'Yes (silent)' : 'Yes') : 'No'} @@ -157,7 +149,7 @@ export default function VolumeBackupList({
} - {!readonly && + {!readonly && } -
+
; } diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts b/src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts index 9282c9f7..a81cb445 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts +++ b/src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts @@ -15,16 +15,16 @@ vi.mock('@/components/ui/drawer', () => ({ DrawerTitle: ({ children }: React.PropsWithChildren) => children, })); vi.mock('@/components/ui/scroll-area', () => ({ ScrollArea: ({ children }: React.PropsWithChildren) => children })); -vi.mock('@/app/project/app/[appId]/domains/node-ports', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Ports: ${app.appNodePorts.length}`) })); -vi.mock('@/app/project/app/[appId]/advanced/basic-auth', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Credentials: ${app.appBasicAuths.length}`) })); -vi.mock('@/app/project/app/[appId]/advanced/actions', () => ({ saveHealthCheck: vi.fn() })); -vi.mock('@/app/project/app/[appId]/advanced/health-check-settings', () => ({ default: () => null })); -vi.mock('@/app/project/app/[appId]/advanced/network-policy', () => ({ default: () => null })); -vi.mock('@/app/project/app/[appId]/environment/env-edit', () => ({ default: () => null })); -vi.mock('@/app/project/app/[appId]/general/app-container-config', () => ({ default: () => null })); -vi.mock('@/app/project/app/[appId]/general/app-rate-limits', () => ({ default: () => null })); -vi.mock('@/app/project/app/[appId]/general/app-source', () => ({ default: () => null })); -vi.mock('@/app/project/app/[appId]/volumes/storages', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/app-components/app-detail/domains/node-ports', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Ports: ${app.appNodePorts.length}`) })); +vi.mock('@/app/project/[projectId]/app-components/app-detail/advanced/basic-auth', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Credentials: ${app.appBasicAuths.length}`) })); +vi.mock('@/app/project/[projectId]/app-components/app-detail/advanced/actions', () => ({ saveHealthCheck: vi.fn() })); +vi.mock('@/app/project/workload-components/health-check-settings', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/app-components/app-detail/advanced/network-policy', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/app-components/app-detail/environment/env-edit', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/app-components/app-detail/general/app-container-config', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/app-components/app-detail/general/app-rate-limits', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/app-components/app-detail/general/app-source', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/app-components/app-detail/volumes/storages', () => ({ default: () => null })); vi.mock('@/components/custom/domains-card', () => ({ default: () => null })); vi.mock('@/components/custom/file-mounts-card', () => ({ default: () => null })); vi.mock('./app-details-environment', () => ({ DrawerEnvironment: () => null })); diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backup-list.tsx b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backup-list.tsx index 509bb987..e4e67d1f 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backup-list.tsx +++ b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backup-list.tsx @@ -16,7 +16,7 @@ import type { VolumeBackupExtendedModel } from '@/shared/model/volume-backup-ext import { downloadBackupForVolumeSchedule, getBackupsForVolumeSchedule, -} from '@/app/project/app/[appId]/volumes/actions'; +} from '@/app/project/[projectId]/app-components/app-detail/volumes/actions'; export function DrawerBackupList({ volumeBackup, diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx index b41709c1..a01d6297 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx +++ b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx @@ -11,12 +11,12 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { Button } from '@/components/ui/button'; import { SplitButton } from '@/components/custom/split-button'; -import VolumeBackupList from '@/app/project/app/[appId]/volumes/volume-backup'; +import VolumeBackupList from '@/app/project/[projectId]/app-components/app-detail/volumes/volume-backup'; import { downloadBackupForVolumeSchedule, getBackupsForVolumeSchedule, runBackupVolumeSchedule, -} from '@/app/project/app/[appId]/volumes/actions'; +} from '@/app/project/[projectId]/app-components/app-detail/volumes/actions'; import { formatDateTime } from '@/frontend/utils/format.utils'; import { Actions } from '@/frontend/utils/nextjs-actions.utils'; import { Toast } from '@/frontend/utils/toast.utils'; @@ -77,7 +77,6 @@ export function DrawerBackupsTab({ app, role, s3Targets, volumeBackups }: { readonly={readonly} s3Targets={s3Targets} volumeBackups={volumeBackups} - hideCard /> ), }); diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-deployments-tab.tsx b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-deployments-tab.tsx index 29293f2f..72373f52 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-deployments-tab.tsx +++ b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-deployments-tab.tsx @@ -2,10 +2,10 @@ import { AppExtendedModel } from "@/shared/model/app-extended.model"; import { RolePermissionEnum } from "@/shared/model/role-extended.model"; import { useNestedDrawer } from "./nested-drawer"; import { DeploymentInfoModel } from "@/shared/model/deployment-info.model"; -import { BuildLogsDialogContent } from "@/app/project/app/[appId]/overview/build-logs-overlay"; -import BuildsTab from "@/app/project/app/[appId]/overview/deployments"; +import { BuildLogsDialogContent } from "@/app/project/workload-components/build-logs-overlay"; +import BuildsTab from "@/app/project/[projectId]/app-components/app-detail/overview/deployments"; import { formatDateTime } from "@/frontend/utils/format.utils"; -import WebhookDeploymentInfo from "@/app/project/app/[appId]/overview/webhook-deployment"; +import WebhookDeploymentInfo from "@/app/project/[projectId]/app-components/app-detail/overview/webhook-deployment"; import { Button } from "@/components/ui/button"; import { Webhook } from "lucide-react"; @@ -51,6 +51,6 @@ export default function DrawerDeploymentsTab({
Latest Deployments
- + ; } \ No newline at end of file diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-drawer.tsx b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-drawer.tsx index 3d650f61..44b76006 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-drawer.tsx +++ b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-drawer.tsx @@ -48,19 +48,19 @@ import { EmptyTitle, } from '@/components/ui/empty'; import PodStatusIndicator from '@/components/custom/pod-status-indicator'; -import { deploy, startApp, stopApp } from '@/app/project/app/[appId]/actions'; +import { deploy, startApp, stopApp } from '@/app/project/[projectId]/app-components/app-detail/actions'; import { usePodsStatus } from '@/frontend/states/zustand.states'; import { cn } from '@/frontend/utils/utils'; import { AppSourceUtils } from '@/frontend/utils/app-source.utils'; import { AppLifecycleUtils } from '@/frontend/utils/app-lifecycle.utils'; import { Toast } from '@/frontend/utils/toast.utils'; import type { AppExtendedModel } from '@/shared/model/app-extended.model'; -import Logs from '@/app/project/app/[appId]/overview/logs'; -import MonitoringTab from '@/app/project/app/[appId]/overview/monitoring-app'; +import Logs from '@/app/project/[projectId]/app-components/app-detail/overview/logs'; +import MonitoringTab from '@/app/project/[projectId]/app-components/app-detail/overview/monitoring-app'; import { RolePermissionEnum } from '@/shared/model/role-extended.model'; -import GeneralAppSource from '@/app/project/app/[appId]/general/app-source'; -import DbCredentials from '@/app/project/app/[appId]/credentials/db-crendentials'; -import DbToolsCard from '@/app/project/app/[appId]/credentials/db-tools'; +import GeneralAppSource from '@/app/project/[projectId]/app-components/app-detail/general/app-source'; +import DbCredentials from '@/app/project/[projectId]/app-components/app-detail/credentials/db-crendentials'; +import DbToolsCard from '@/app/project/[projectId]/app-components/app-detail/credentials/db-tools'; import { DrawerSettings } from './app-details-settings'; import { NestedDrawerProvider } from './nested-drawer'; import { AppDrawerAppContext } from './app-drawer-app-context'; @@ -425,7 +425,6 @@ export function AppDetailsDrawer({ ) : needsSourceConfiguration ? ( @@ -438,11 +437,11 @@ export function AppDetailsDrawer({ - + - + {hasVolumes && ( diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-settings.tsx b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-settings.tsx index a120b402..c77bc6fb 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-settings.tsx +++ b/src/app/project/[projectId]/app-components/app-drawer-components/app-details-settings.tsx @@ -10,17 +10,16 @@ import { } from 'lucide-react'; import { useCallback, useContext, useEffect } from 'react'; import { Button } from '@/components/ui/button'; -import { CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; -import BasicAuth from '@/app/project/app/[appId]/advanced/basic-auth'; -import { saveHealthCheck } from '@/app/project/app/[appId]/advanced/actions'; -import HealthCheckSettings from '@/app/project/app/[appId]/advanced/health-check-settings'; -import NetworkPolicy from '@/app/project/app/[appId]/advanced/network-policy'; -import NodePortsCard from '@/app/project/app/[appId]/domains/node-ports'; -import EnvEdit from '@/app/project/app/[appId]/environment/env-edit'; -import GeneralAppContainerConfig from '@/app/project/app/[appId]/general/app-container-config'; -import GeneralAppRateLimits from '@/app/project/app/[appId]/general/app-rate-limits'; -import GeneralAppSource from '@/app/project/app/[appId]/general/app-source'; -import StorageList from '@/app/project/app/[appId]/volumes/storages'; +import BasicAuth from '@/app/project/[projectId]/app-components/app-detail/advanced/basic-auth'; +import { saveHealthCheck } from '@/app/project/[projectId]/app-components/app-detail/advanced/actions'; +import HealthCheckSettings from '@/app/project/workload-components/health-check-settings'; +import NetworkPolicy from '@/app/project/[projectId]/app-components/app-detail/advanced/network-policy'; +import NodePortsCard from '@/app/project/[projectId]/app-components/app-detail/domains/node-ports'; +import EnvEdit from '@/app/project/[projectId]/app-components/app-detail/environment/env-edit'; +import GeneralAppContainerConfig from '@/app/project/[projectId]/app-components/app-detail/general/app-container-config'; +import GeneralAppRateLimits from '@/app/project/[projectId]/app-components/app-detail/general/app-rate-limits'; +import GeneralAppSource from '@/app/project/[projectId]/app-components/app-detail/general/app-source'; +import StorageList from '@/app/project/[projectId]/app-components/app-detail/volumes/storages'; import DomainsCard from '@/components/custom/domains-card'; import FileMountsCard from '@/components/custom/file-mounts-card'; import type { AppExtendedModel } from '@/shared/model/app-extended.model'; @@ -52,7 +51,7 @@ export function DrawerSettings({ openNestedDrawer({ title: 'Environment variables', description: <>Values are encrypted at rest and never shown. Names starting with QS_ are reserved by QuickStack., - content: , + content: , }); }, [app, openNestedDrawer, readonly]); @@ -71,7 +70,6 @@ export function DrawerSettings({ icon={Boxes} > - + @@ -130,7 +128,6 @@ export function DrawerSettings({ domains={app.appDomains} workloadId={app.id} workloadType="app" - hideCard /> @@ -148,7 +145,6 @@ export function DrawerSettings({ ), }) @@ -181,14 +177,12 @@ export function DrawerSettings({ app={app} readonly={readonly} storageClasses={storageClasses} - hideCard /> -
- - Health Checks - + + + Health Checks + Configure healthchecks so that k3s can automatically monitor when your application is fully started up and ready to receive traffic. - - - + + + - -
+ +
diff --git a/src/app/project/[projectId]/app-components/project-network-graph.tsx b/src/app/project/[projectId]/app-components/project-network-graph.tsx index 491f831f..65506e77 100644 --- a/src/app/project/[projectId]/app-components/project-network-graph.tsx +++ b/src/app/project/[projectId]/app-components/project-network-graph.tsx @@ -39,8 +39,8 @@ import { AppNetworkPolicyRuleEditModel, NetworkPolicySelectableTarget } from '@/ import { NetworkPolicyRuleUtils } from '@/shared/utils/network-policy-rule.utils'; import { AppNetworkPolicyDraft, AppNetworkPolicyDraftUtils } from '@/shared/utils/app-network-policy-draft.utils'; import { InternalHostnameUtils } from '@/shared/utils/internal-hostname.utils'; -import AppNetworkPolicyRuleDialog from '@/app/project/app/[appId]/advanced/app-network-policy-rule-dialog'; -import { saveAppNetworkPolicyConfiguration } from '@/app/project/app/[appId]/advanced/actions'; +import AppNetworkPolicyRuleDialog from '@/app/project/[projectId]/app-components/app-detail/advanced/app-network-policy-rule-dialog'; +import { saveAppNetworkPolicyConfiguration } from '@/app/project/[projectId]/app-components/app-detail/advanced/actions'; import { deleteApp } from '@/app/project/[projectId]/actions'; import type { ProjectNetworkGraphPositions } from '@/shared/model/project-network-graph-layout.model'; import type { S3Target } from '@prisma/client'; diff --git a/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-app-context-menu.tsx b/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-app-context-menu.tsx index c04f8f00..0810599b 100644 --- a/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-app-context-menu.tsx +++ b/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-app-context-menu.tsx @@ -12,7 +12,7 @@ import { ContextMenuSubTrigger, ContextMenuTrigger, } from '@/components/ui/context-menu'; -import { deploy, startApp, stopApp } from '@/app/project/app/[appId]/actions'; +import { deploy, startApp, stopApp } from '@/app/project/[projectId]/app-components/app-detail/actions'; import { usePodsStatus } from '@/frontend/states/zustand.states'; import { AppLifecycleUtils } from '@/frontend/utils/app-lifecycle.utils'; import { Toast } from '@/frontend/utils/toast.utils'; diff --git a/src/app/project/app/[appId]/app-action-buttons.tsx b/src/app/project/app/[appId]/app-action-buttons.tsx deleted file mode 100644 index 5f79613e..00000000 --- a/src/app/project/app/[appId]/app-action-buttons.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client' - -import { Button } from "@/components/ui/button"; -import { Card, CardContent } from "@/components/ui/card"; -import { deploy, startApp, stopApp } from "./actions"; -import { AppExtendedModel } from "@/shared/model/app-extended.model"; -import { Toast } from "@/frontend/utils/toast.utils"; -import { ExternalLink, Hammer, Play, Rocket, Square } from "lucide-react"; -import { AppEventsDialog } from "./app-events-dialog"; -import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; -import { UserSession } from "@/shared/model/sim-session.model"; -import { UserGroupUtils } from "@/shared/utils/role.utils"; -import PodStatusIndicator from "@/components/custom/pod-status-indicator"; -import { usePodsStatus } from "@/frontend/states/zustand.states"; -import { AppSourceUtils } from "@/frontend/utils/app-source.utils"; - -export default function AppActionButtons({ - app, - session -}: { - app: AppExtendedModel; - session: UserSession; -}) { - const hasWriteAccess = UserGroupUtils.sessionHasWriteAccessForApp(session, app.id); - const appSourceIsConfigured = AppSourceUtils.isConfiguredSource(app); - const deploymentStatus = usePodsStatus(state => state.podsStatus.get(app.id)?.deploymentStatus ?? 'UNKNOWN'); - - return - - -
-
- {hasWriteAccess && <> - {app.appType === 'APP' && (app.sourceType === 'GIT' || app.sourceType === 'GIT_SSH') && } - - - } - {app.appDomains.length > 0 && } -
- -
-
-
; -} diff --git a/src/app/project/app/[appId]/app-breadcrumbs.tsx b/src/app/project/app/[appId]/app-breadcrumbs.tsx deleted file mode 100644 index a90b920f..00000000 --- a/src/app/project/app/[appId]/app-breadcrumbs.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { AppExtendedModel } from "@/shared/model/app-extended.model"; -import WorkloadBreadcrumbs from "@/components/custom/workload-breadcrumbs"; - -export default function AppBreadcrumbs({ app, apps, tabName }: { app: AppExtendedModel; apps: { id: string; name: string }[]; tabName?: string }) { - return ( - - ); -} diff --git a/src/app/project/app/[appId]/app-events-dialog.tsx b/src/app/project/app/[appId]/app-events-dialog.tsx deleted file mode 100644 index 34eab994..00000000 --- a/src/app/project/app/[appId]/app-events-dialog.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog" -import React, { useCallback, useEffect } from "react"; -import { AppExtendedModel } from "@/shared/model/app-extended.model"; -import { getLatestAppEvents } from "./actions"; -import { toast } from "sonner"; -import FullLoadingSpinner from "@/components/ui/full-loading-spinnter"; -import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { EventInfoModel } from "@/shared/model/event-info.model"; -import { formatDateTime } from "@/frontend/utils/format.utils"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { cn } from "@/frontend/utils/utils"; - -export function AppEventsDialog({ - app, - children -}: { - app: AppExtendedModel; - children: React.ReactNode; -}) { - - const [isOpen, setIsOpen] = React.useState(false); - const [events, setEvents] = React.useState(undefined); - - const loadEvents = useCallback(async () => { - try { - const eventsResponse = await getLatestAppEvents(app.id); - if (eventsResponse.status === 'success') { - setEvents(eventsResponse.data!); - } else { - toast.error(eventsResponse.message); - } - } catch (error) { - console.error(error); - toast.error('An error occured while loading events.'); - } - }, [app.id]) - - useEffect(() => { - if (isOpen) { - loadEvents(); - } else { - setEvents(undefined); - } - }, [isOpen, loadEvents]); - - return (<> -
setIsOpen(true)} className="cursor-pointer"> {children}
- { - setIsOpen(isO); - }}> - - - App Events - - App events occur when changes are made to the deployment. For example, when a deployment is created, updated, or restarted. - Advanced users can read these events to understand what is happening in the background. Events are only available for a short period of time. - - -
- {!events && } - {events && <> - - - {events.length} recent Events - - - Time - Type - Action - Note - Pod Name - - - - {events.map((event, index) => ( - - {formatDateTime(event.eventTime, true)} - {event.action} - - {event.reason} - - {event.note} - {event.podName} - - ))} - - -
- } -
-
-
- ) -} diff --git a/src/app/project/app/[appId]/app-tabs.tsx b/src/app/project/app/[appId]/app-tabs.tsx deleted file mode 100644 index 7f05cfe9..00000000 --- a/src/app/project/app/[appId]/app-tabs.tsx +++ /dev/null @@ -1,119 +0,0 @@ -'use client' - -import { useEffect, useState } from "react"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" -import GeneralAppRateLimits from "./general/app-rate-limits"; -import GeneralAppSource from "./general/app-source"; -import GeneralAppContainerConfig from "./general/app-container-config"; -import EnvEdit from "./environment/env-edit"; -import { S3Target } from "@prisma/client"; -import DomainsCard from "../../../../components/custom/domains-card"; -import StorageList from "./volumes/storages"; -import { AppExtendedModel } from "@/shared/model/app-extended.model"; -import BuildsTab from "./overview/deployments"; -import Logs from "./overview/logs"; -import MonitoringTab from "./overview/monitoring-app"; -import NodePortsCard from "./domains/node-ports"; -import FileMountsCard from "@/components/custom/file-mounts-card"; -import WebhookDeploymentInfo from "./overview/webhook-deployment"; -import DbCredentials from "./credentials/db-crendentials"; -import VolumeBackupList from "./volumes/volume-backup"; -import { VolumeBackupExtendedModel } from "@/shared/model/volume-backup-extended.model"; -import BasicAuth from "./advanced/basic-auth"; -import NetworkPolicy from "./advanced/network-policy"; -import HealthCheckSettings from "./advanced/health-check-settings"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import DbToolsCard from "./credentials/db-tools"; -import { RolePermissionEnum } from "@/shared/model/role-extended.model"; -import { Eye, Key, Settings, Zap, Globe, HardDrive, Cog } from "lucide-react"; -import { AppSourceUtils } from "@/frontend/utils/app-source.utils"; -import { TabNavigationUtils } from "@/frontend/utils/tab-navigation.utils"; -import { saveHealthCheck } from "./advanced/actions"; - -export default function AppTabs({ - app, - role, - tabName, - s3Targets, - volumeBackups, - storageClasses, - gitSshPublicKey, -}: { - app: AppExtendedModel; - role: RolePermissionEnum; - tabName: string; - s3Targets: S3Target[]; - volumeBackups: VolumeBackupExtendedModel[]; - storageClasses: string[]; - gitSshPublicKey?: string; -}) { - const readonly = role !== RolePermissionEnum.READWRITE; - const appSourceIsConfigured = AppSourceUtils.isConfiguredSource(app); - const [activeTab, setActiveTab] = useState(tabName); - const openTab = (newTab: string) => { - setActiveTab(newTab); - TabNavigationUtils.replaceQuery(new URLSearchParams({ tabName: newTab }), `/project/app/${app.id}`); - } - - useEffect(() => { - setActiveTab(tabName); - }, [tabName]); - - if (!appSourceIsConfigured) { - return ( - - ) - } - - return ( - openTab(newTab)} className="space-y-4"> - - - Overview - {app.appType !== 'APP' && Credentials} - General - Environment - Domains & Networking - Storage - Advanced - - - - - - - - - {app.appType !== 'APP' && - {role === RolePermissionEnum.READWRITE && } - - } - - - - - - - - - - - - - - - - - - - - - - - - ) -} diff --git a/src/app/project/app/[appId]/layout.tsx b/src/app/project/app/[appId]/layout.tsx deleted file mode 100644 index a153e7dc..00000000 --- a/src/app/project/app/[appId]/layout.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { isAuthorizedReadForWorkload } from "@/server/utils/action-wrapper.utils"; -import appService from "@/server/services/app.service"; -import PageTitle from "@/components/custom/page-title"; -import AppActionButtons from "./app-action-buttons"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { AlertTriangle } from "lucide-react"; -import { AppSourceUtils } from "@/frontend/utils/app-source.utils"; -import Link from "next/link"; - -export default async function RootLayout({ - children, - params -}: Readonly<{ - params: Promise<{ appId: string }> - children: React.ReactNode; -}>) { - - const resolvedParams = await params; - const appId = resolvedParams?.appId; - if (!appId) { - return

Could not find app with id {appId}

- } - const session = await isAuthorizedReadForWorkload(appId); - const app = await appService.getExtendedById(appId); - - const hasIngressRule = (app.appNetworkPolicy?.rules ?? []).some(rule => rule.type === 'INGRESS'); - const appSourceIsConfigured = AppSourceUtils.isConfiguredSource(app); - const showIngressWarning = appSourceIsConfigured && app.useNetworkPolicy && app.appDomains.length === 0 && !hasIngressRule; - - return ( -
- - - {showIngressWarning && ( - - - No inbound traffic configured - - This app has no domain and no ingress network policy rule. It accepts no incoming traffic and cannot be reached - from other apps. Configure inbound connections in{" "} - - Domains & Networking - - . - - - )} - - {children} -
- ); -} diff --git a/src/app/project/app/[appId]/overview/build-status-badge.tsx b/src/app/project/app/[appId]/overview/build-status-badge.tsx deleted file mode 100644 index 10930952..00000000 --- a/src/app/project/app/[appId]/overview/build-status-badge.tsx +++ /dev/null @@ -1,67 +0,0 @@ -'use client' - - - -import { BuildJobStatus } from "@/shared/model/build-job"; - - -export default function BuildStatusBadge( - { - children - }: { - children: BuildJobStatus - } -) { - - return (<> - {getTextForStatus(children)} - - ) -} - -function getTextForStatus(status: BuildJobStatus) { - switch (status) { - case 'UNKNOWN': - return 'Unknown'; - case 'FAILED': - return 'Failed'; - case 'RUNNING': - return 'Running'; - case 'SUCCEEDED': - return 'Success'; - default: - return 'Unknown'; - } -} - -function getBackgroundColorForStatus(status: BuildJobStatus) { - switch (status) { - - case 'UNKNOWN': - return 'bg-slate-100'; - case 'FAILED': - return 'bg-red-100'; - case 'RUNNING': - return 'bg-blue-100'; - case 'SUCCEEDED': - return 'bg-green-100'; - default: - return 'bg-slate-100'; - } -} - -function getTextColorForStatus(status: BuildJobStatus) { - switch (status) { - - case 'UNKNOWN': - return 'text-slate-800'; - case 'FAILED': - return 'text-red-800'; - case 'RUNNING': - return 'text-blue-800'; - case 'SUCCEEDED': - return 'text-green-800'; - default: - return 'text-slate-800'; - } -} \ No newline at end of file diff --git a/src/app/project/app/[appId]/overview/deployments-default-view.tsx b/src/app/project/app/[appId]/overview/deployments-default-view.tsx deleted file mode 100644 index ee04023c..00000000 --- a/src/app/project/app/[appId]/overview/deployments-default-view.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { EllipsisVertical, RotateCcw } from 'lucide-react'; -import { SimpleDataTable } from '@/components/custom/simple-data-table'; -import ShortCommitHash from '@/components/custom/short-commit-hash'; -import { Button } from '@/components/ui/button'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import { formatDateTime } from '@/frontend/utils/format.utils'; -import type { DeploymentInfoModel } from '@/shared/model/deployment-info.model'; -import { appBuildMethodLabels } from '@/shared/model/app-source-info.model'; -import DeploymentStatusBadge from './deployment-status-badge'; - -type DeploymentsDefaultViewProps = { - deployments: DeploymentInfoModel[]; - canStopBuild: (deployment: DeploymentInfoModel) => boolean; - canRollback: (deployment: DeploymentInfoModel) => boolean; - onShowLogs: (deployment: DeploymentInfoModel) => void; - onStopBuild: (deployment: DeploymentInfoModel) => void; - onRollback: (deployment: DeploymentInfoModel) => void; -}; - -export function DeploymentsDefaultView({ - deployments, - canStopBuild, - canRollback, - onShowLogs, - onStopBuild, - onRollback, -}: DeploymentsDefaultViewProps) { - return ( - ( -
- {deployment.status} - {deployment.isRollback && ( - - Rollback - - )} -
- )], - ['buildMethod', 'Build Method', true, deployment => ( - - {deployment.buildMethod - ? appBuildMethodLabels[deployment.buildMethod] ?? '—' - : '—'} - - )], - ['startTime', 'Started At', true, deployment => formatDateTime(deployment.createdAt)], - ['gitCommit', 'Git Commit', true, deployment => ( - {deployment.gitCommit} - )], - ['gitCommitMessage', 'Commit Message', true, deployment => ( - - {deployment.gitCommitMessage ?? ''} - - )], - ]} - data={deployments} - hideSearchBar - actionCol={deployment => ( -
- - {canStopBuild(deployment) && ( - - )} - {canRollback(deployment) && ( - - - - Deployment actions - } /> - - onRollback(deployment)}> - - Rollback to this deployment - - - - )} -
- )} - /> - ); -} diff --git a/src/app/project/app/[appId]/page.tsx b/src/app/project/app/[appId]/page.tsx deleted file mode 100644 index 39528acc..00000000 --- a/src/app/project/app/[appId]/page.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { isAuthorizedReadForApp } from "@/server/utils/action-wrapper.utils"; -import appService from "@/server/services/app.service"; -import AppTabs from "./app-tabs"; -import AppBreadcrumbs from "./app-breadcrumbs"; -import s3TargetService from "@/server/services/s3-target.service"; -import volumeBackupService from "@/server/services/volume-backup.service"; -import { UserGroupUtils } from "@/shared/utils/role.utils"; -import clusterService from "@/server/services/cluster.service"; -import appGitSshKeyService from "@/server/services/app-git-ssh-key.service"; - -export default async function AppPage({ - searchParams, - params -}: { - searchParams?: Promise<{ [key: string]: string | undefined }>; - params: Promise<{ appId: string }> -}) { - const resolvedParams = await params; - const resolvedSearchParams = await searchParams; - const appId = resolvedParams?.appId; - if (!appId) { - return

Could not find app with id {appId}

- } - const session = await isAuthorizedReadForApp(appId); - const role = UserGroupUtils.getRolePermissionForApp(session, appId); - const app = await appService.getExtendedById(appId); - const [s3Targets, volumeBackups, storageClasses, apps, gitSshPublicKey] = await Promise.all([ - s3TargetService.getAll(), - volumeBackupService.getForApp(appId), - clusterService.getStorageClasses(), - appService.getAllAppsByProjectId(app.projectId), - appGitSshKeyService.getPublicKey(appId), - ]); - - return (<> - - - - ) -} diff --git a/src/app/project/app/[appId]/volumes/storage-restore-overlay.tsx b/src/app/project/app/[appId]/volumes/storage-restore-overlay.tsx deleted file mode 100644 index 963fa568..00000000 --- a/src/app/project/app/[appId]/volumes/storage-restore-overlay.tsx +++ /dev/null @@ -1,97 +0,0 @@ -'use client' - -import type { z } from "zod"; -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" -import { Input } from "@/components/ui/input" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" - -import { useActionState, useEffect, useState } from "react"; -import { FormUtils } from "@/frontend/utils/form.utilts"; -import { SubmitButton } from "@/components/custom/submit-button"; -import { AppVolume } from "@prisma/client" -import { ServerActionResult } from "@/shared/model/server-action-error-return.model" -import { restoreVolumeFromZip } from "./actions" -import { toast } from "sonner" -import { AppExtendedModel } from "@/shared/model/app-extended.model" -import { volumeUploadZodModel } from "@/shared/model/volume-upload.model" - -export default function StorageRestoreDialog({ children, volume, app }: { children: React.ReactNode; volume: AppVolume; app: AppExtendedModel; }) { - - const [isOpen, setIsOpen] = useState(false); - - - const form = useForm, unknown, z.output>({ - resolver: zodResolver(volumeUploadZodModel) - }); - - const [state, formAction] = useActionState((state: ServerActionResult, payload: FormData) => - restoreVolumeFromZip(state, payload, volume.id), FormUtils.getInitialFormState()); - - useEffect(() => { - if (state.status === 'success') { - form.reset(); - toast.success('Uploaded saved successfully', { - }); - setIsOpen(false); - } - FormUtils.mapValidationErrorsToForm(state, form); - }, [form, state]); - - useEffect(() => { - form.reset(); - }, [volume, app, children, form]); - - return ( - <> -
setIsOpen(true)}> - {children} -
- setIsOpen(false)}> - - - Restore Volume - - Provide a gzip file to restore the volume. - The archive will be extracted at the root of the mount path. - Make sure that the volume has enough space to store the extracted data. - - -
- -
- ( - - Restore File - - - - - - )} - /> - -

{state.message ?? 'All existing data on the volume will be lost!'}

- Restore -
-
- -
-
- - ) - - - -} diff --git a/src/app/project/app/[appId]/overview/build-logs-overlay.tsx b/src/app/project/workload-components/build-logs-overlay.tsx similarity index 100% rename from src/app/project/app/[appId]/overview/build-logs-overlay.tsx rename to src/app/project/workload-components/build-logs-overlay.tsx diff --git a/src/app/project/app/[appId]/overview/deployment-status-badge.tsx b/src/app/project/workload-components/deployment-status-badge.tsx similarity index 100% rename from src/app/project/app/[appId]/overview/deployment-status-badge.tsx rename to src/app/project/workload-components/deployment-status-badge.tsx diff --git a/src/app/project/app/[appId]/advanced/health-check-settings.tsx b/src/app/project/workload-components/health-check-settings.tsx similarity index 98% rename from src/app/project/app/[appId]/advanced/health-check-settings.tsx rename to src/app/project/workload-components/health-check-settings.tsx index aaa84653..17020430 100644 --- a/src/app/project/app/[appId]/advanced/health-check-settings.tsx +++ b/src/app/project/workload-components/health-check-settings.tsx @@ -19,11 +19,10 @@ import { SubmitButton } from "@/components/custom/submit-button"; import { FormUtils } from "@/frontend/utils/form.utilts"; import { ServerActionResult } from "@/shared/model/server-action-error-return.model"; -export default function HealthCheckSettings({ workload, readonly, saveHealthCheck, hideCard = false }: { +export default function HealthCheckSettings({ workload, readonly, saveHealthCheck }: { workload: HealthCheckWorkload; readonly: boolean; saveHealthCheck: (state: ServerActionResult, payload: HealthCheckModel) => Promise>; - hideCard?: boolean; }) { const defaultHeaders = workload.healthCheckHttpHeadersJson @@ -72,11 +71,10 @@ export default function HealthCheckSettings({ workload, readonly, saveHealthChec } FormUtils.mapValidationErrorsToForm(state, form); }, [form, state]); - const CardWrapper = hideCard ? 'div' : Card; return ( - - {!hideCard && + + { Health Check Settings Configure healthchecks so that k3s can automatically monitor when your application is fully started up and ready to receive traffic. @@ -86,7 +84,7 @@ export default function HealthCheckSettings({ workload, readonly, saveHealthChec
form.handleSubmit((data) => { formAction(data); })()}> - + )} - + Save -
+
); } diff --git a/src/app/project/app/[appId]/advanced/health-check.model.ts b/src/app/project/workload-components/health-check.model.ts similarity index 100% rename from src/app/project/app/[appId]/advanced/health-check.model.ts rename to src/app/project/workload-components/health-check.model.ts diff --git a/src/app/project/app/[appId]/general/app-source-wizard/container-image-step.tsx b/src/app/project/workload-components/source-wizard/container-image-step.tsx similarity index 92% rename from src/app/project/app/[appId]/general/app-source-wizard/container-image-step.tsx rename to src/app/project/workload-components/source-wizard/container-image-step.tsx index 2e32acc1..1550d6e8 100644 --- a/src/app/project/app/[appId]/general/app-source-wizard/container-image-step.tsx +++ b/src/app/project/workload-components/source-wizard/container-image-step.tsx @@ -1,5 +1,5 @@ import { LockKeyhole, Package, User } from "lucide-react"; -import { IconInput, SecretInput } from "./source-wizard-fields"; +import { IconInput, SecretInput } from "../../[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields"; import { SourceFormPatch, SourceWizardInput } from "./types"; export function ContainerImageStep({ formData, showCredentials, showPassword, setShowPassword, onChange }: { diff --git a/src/app/project/app/[appId]/general/app-source-wizard/dockerfile-path-step.tsx b/src/app/project/workload-components/source-wizard/dockerfile-path-step.tsx similarity index 94% rename from src/app/project/app/[appId]/general/app-source-wizard/dockerfile-path-step.tsx rename to src/app/project/workload-components/source-wizard/dockerfile-path-step.tsx index 26d47cfc..76b5015e 100644 --- a/src/app/project/app/[appId]/general/app-source-wizard/dockerfile-path-step.tsx +++ b/src/app/project/workload-components/source-wizard/dockerfile-path-step.tsx @@ -1,6 +1,6 @@ import { CheckCircle2, FileCode2, Loader2, RefreshCw, SearchX } from "lucide-react"; import { Button } from "@/components/ui/button"; -import { IconInput } from "./source-wizard-fields"; +import { IconInput } from "../../[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields"; import { defaultDockerfilePath } from "./types"; export function DockerfilePathStep({ value, isDetecting, detectionResult, onChange, onRetry }: { diff --git a/src/app/project/app/[appId]/general/app-source-wizard/git-branch-step.tsx b/src/app/project/workload-components/source-wizard/git-branch-step.tsx similarity index 100% rename from src/app/project/app/[appId]/general/app-source-wizard/git-branch-step.tsx rename to src/app/project/workload-components/source-wizard/git-branch-step.tsx diff --git a/src/app/project/app/[appId]/general/app-source-wizard/git-https-url-step.tsx b/src/app/project/workload-components/source-wizard/git-https-url-step.tsx similarity index 92% rename from src/app/project/app/[appId]/general/app-source-wizard/git-https-url-step.tsx rename to src/app/project/workload-components/source-wizard/git-https-url-step.tsx index f57b1df4..f17f83fb 100644 --- a/src/app/project/app/[appId]/general/app-source-wizard/git-https-url-step.tsx +++ b/src/app/project/workload-components/source-wizard/git-https-url-step.tsx @@ -1,5 +1,5 @@ import { Link as LinkIcon, LockKeyhole, User } from "lucide-react"; -import { BranchLoadingState, IconInput, SecretInput } from "./source-wizard-fields"; +import { BranchLoadingState, IconInput, SecretInput } from "../../[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields"; import { SourceFormPatch, SourceWizardInput } from "./types"; export function GitHttpsUrlStep({ formData, showCredentials, showToken, setShowToken, onChange, isLoadingBranches, branchError, onRetry }: { diff --git a/src/app/project/app/[appId]/general/app-source-wizard/git-ssh-url-step.tsx b/src/app/project/workload-components/source-wizard/git-ssh-url-step.tsx similarity index 96% rename from src/app/project/app/[appId]/general/app-source-wizard/git-ssh-url-step.tsx rename to src/app/project/workload-components/source-wizard/git-ssh-url-step.tsx index e2be7328..d95373e7 100644 --- a/src/app/project/app/[appId]/general/app-source-wizard/git-ssh-url-step.tsx +++ b/src/app/project/workload-components/source-wizard/git-ssh-url-step.tsx @@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Check, ClipboardCopy, KeyRound, Link as LinkIcon, RefreshCw } from "lucide-react"; -import { BranchLoadingState, IconInput } from "./source-wizard-fields"; +import { BranchLoadingState, IconInput } from "../../[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields"; import { SourceFormPatch, SourceWizardInput } from "./types"; export function GitSshUrlStep({ formData, publicKey, isEnsuringKey, isLoadingBranches, branchError, onChange, onCopy, onRegenerate, onRetry }: { diff --git a/src/app/project/app/[appId]/general/app-source-wizard/public-deploy-key-dialog.tsx b/src/app/project/workload-components/source-wizard/public-deploy-key-dialog.tsx similarity index 100% rename from src/app/project/app/[appId]/general/app-source-wizard/public-deploy-key-dialog.tsx rename to src/app/project/workload-components/source-wizard/public-deploy-key-dialog.tsx diff --git a/src/app/project/app/[appId]/general/app-source-wizard/readonly-info.tsx b/src/app/project/workload-components/source-wizard/readonly-info.tsx similarity index 100% rename from src/app/project/app/[appId]/general/app-source-wizard/readonly-info.tsx rename to src/app/project/workload-components/source-wizard/readonly-info.tsx diff --git a/src/app/project/app/[appId]/general/app-source-wizard/source-summary-step.tsx b/src/app/project/workload-components/source-wizard/source-summary-step.tsx similarity index 96% rename from src/app/project/app/[appId]/general/app-source-wizard/source-summary-step.tsx rename to src/app/project/workload-components/source-wizard/source-summary-step.tsx index 78e11f6c..d8194812 100644 --- a/src/app/project/app/[appId]/general/app-source-wizard/source-summary-step.tsx +++ b/src/app/project/workload-components/source-wizard/source-summary-step.tsx @@ -1,6 +1,6 @@ import { AppBuildMethod } from "@/shared/model/app-source-info.model"; import { JsFramework, jsFrameworkPresets } from "@/shared/model/js-framework.model"; -import { SecretSummaryRow, SummaryRow } from "./source-wizard-fields"; +import { SecretSummaryRow, SummaryRow } from "../../[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields"; import { buildMethodLabels, defaultDockerfilePath, SourceType, sourceTypeLabels, SourceWizardInput } from "./types"; export function SourceSummaryStep({ formData, publicKey, showGitToken, setShowGitToken, showRegistryPassword, setShowRegistryPassword }: { diff --git a/src/app/project/app/[appId]/general/app-source-wizard/source-type-step.tsx b/src/app/project/workload-components/source-wizard/source-type-step.tsx similarity index 100% rename from src/app/project/app/[appId]/general/app-source-wizard/source-type-step.tsx rename to src/app/project/workload-components/source-wizard/source-type-step.tsx diff --git a/src/app/project/app/[appId]/general/app-source-wizard/types.ts b/src/app/project/workload-components/source-wizard/types.ts similarity index 91% rename from src/app/project/app/[appId]/general/app-source-wizard/types.ts rename to src/app/project/workload-components/source-wizard/types.ts index f6adf8b4..1efb34ef 100644 --- a/src/app/project/app/[appId]/general/app-source-wizard/types.ts +++ b/src/app/project/workload-components/source-wizard/types.ts @@ -32,4 +32,4 @@ export const sourceTypeLabels: Record = { export const buildMethodLabels: Record = appBuildMethodLabels; -export const defaultDockerfilePath = './Dockerfile'; +export const defaultDockerfilePath = '../../[projectId]/app-components/app-detail/general/app-source-wizard/Dockerfile'; diff --git a/src/app/project/app/[appId]/general/app-source-wizard/wizard-progress.tsx b/src/app/project/workload-components/source-wizard/wizard-progress.tsx similarity index 100% rename from src/app/project/app/[appId]/general/app-source-wizard/wizard-progress.tsx rename to src/app/project/workload-components/source-wizard/wizard-progress.tsx diff --git a/src/app/settings/integrations/llm-gateways/llm-gateway-edit-overlay.tsx b/src/app/settings/integrations/llm-gateways/llm-gateway-edit-overlay.tsx index ef48098a..600801ca 100644 --- a/src/app/settings/integrations/llm-gateways/llm-gateway-edit-overlay.tsx +++ b/src/app/settings/integrations/llm-gateways/llm-gateway-edit-overlay.tsx @@ -35,7 +35,7 @@ import { import { Label } from "@/components/ui/label"; import { CloudUpload, FilePenLine } from "lucide-react"; import { ProjectExtendedModel } from "@/shared/model/project-extended.model"; -import { deploy } from "@/app/project/app/[appId]/actions"; +import { deploy } from "@/app/project/[projectId]/app-components/app-detail/actions"; function LlmGatewayForm({ gateway }: { gateway?: LlmGatewayModel }) { const { closeDialog } = useDialogContext(); diff --git a/src/app/sidebar-client.tsx b/src/app/sidebar-client.tsx index 6950f7a0..746926ca 100644 --- a/src/app/sidebar-client.tsx +++ b/src/app/sidebar-client.tsx @@ -68,14 +68,7 @@ export function SidebarCient({ const showSettingsNavigation = path.startsWith("/settings") useEffect(() => { - if (path.startsWith('/project/app/')) { - const appId = path.split('/')[3]; - const project = projects.find(p => p.apps.some(a => a.id === appId)); - setCurrentlySelectedProjectId(project?.id || null); - setCurrentlySelectedAppId(appId); - setCurrentlySelectedAgentId(null); - - } else if (path.startsWith("/project")) { + if (path.startsWith("/project")) { const projectId = path.split('/')[2]; setCurrentlySelectedProjectId(projectId); setCurrentlySelectedAppId(searchParams.get('drawerAppId')); diff --git a/src/components/custom/build-status-indicator.tsx b/src/components/custom/build-status-indicator.tsx index 7f42ae13..704523e0 100644 --- a/src/components/custom/build-status-indicator.tsx +++ b/src/components/custom/build-status-indicator.tsx @@ -5,7 +5,7 @@ import { CircleAlert } from 'lucide-react'; import { Spinner } from '@/components/ui/spinner'; import { cn } from '@/frontend/utils/utils'; import { useDialog } from '@/frontend/states/zustand.states'; -import { BuildLogsDialogContent } from '@/app/project/app/[appId]/overview/build-logs-overlay'; +import { BuildLogsDialogContent } from '@/app/project/workload-components/build-logs-overlay'; import { Tooltip, TooltipContent, diff --git a/src/components/custom/build-status-indicator.unit.spec.ts b/src/components/custom/build-status-indicator.unit.spec.ts index 44737b80..dee7f667 100644 --- a/src/components/custom/build-status-indicator.unit.spec.ts +++ b/src/components/custom/build-status-indicator.unit.spec.ts @@ -12,7 +12,7 @@ vi.mock('@/components/ui/tooltip', () => ({ TooltipContent: ({ children }: { children: React.ReactNode }) => children, })); -vi.mock('@/app/project/app/[appId]/overview/build-logs-overlay', () => ({ +vi.mock('@/app/project/workload-components/build-logs-overlay', () => ({ BuildLogsDialogContent: () => null, })); diff --git a/src/components/custom/domains-card.tsx b/src/components/custom/domains-card.tsx index 6f826495..848091d7 100644 --- a/src/components/custom/domains-card.tsx +++ b/src/components/custom/domains-card.tsx @@ -1,9 +1,9 @@ 'use client'; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; -import { CheckIcon, EditIcon, ExternalLink, Plus, TrashIcon, XIcon } from "lucide-react"; +import { EditIcon, ExternalLink, Plus, TrashIcon } from "lucide-react"; import { Toast } from "@/frontend/utils/toast.utils"; import { Code } from "@/components/custom/code"; import { useConfirmDialog, useDialog } from "@/frontend/states/zustand.states"; @@ -13,12 +13,11 @@ import DomainEditOverlay from "@/components/custom/domain-edit-overlay"; import { deleteDomain } from "@/app/project/actions"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -export default function DomainsCard({ domains, workloadId, workloadType, readonly, hideCard = false }: { +export default function DomainsCard({ domains, workloadId, workloadType, readonly }: { domains: DomainEditModel[]; workloadId: string; workloadType: WorkloadType; readonly: boolean; - hideCard?: boolean; }) { const { openConfirmDialog } = useConfirmDialog(); const { openDialog } = useDialog(); @@ -42,26 +41,21 @@ export default function DomainsCard({ domains, workloadId, workloadType, readonl maxWidth: 'max-w-2xl', }); } - const CardWrapper = hideCard ? 'div' : Card; return <> - +
Domains Add custom domains. If a domain is configured, it will be public and accessible via the internet. - {domains.length > 0 && + {domains.length > 0 && {domains.length} Domains Name - {!hideCard && <> - Port - SSL - Redirect HTTP to HTTPS - } + {!readonly && } @@ -74,11 +68,7 @@ export default function DomainsCard({ domains, workloadId, workloadType, readonl - {!hideCard && <> - {domain.port} - {domain.useSsl ? : } - {domain.useSsl && domain.redirectHttps ? : } - } + {!readonly &&
@@ -100,10 +90,10 @@ export default function DomainsCard({ domains, workloadId, workloadType, readonl
} - {!readonly && + {!readonly && } - +
; } diff --git a/src/components/custom/file-mounts-card.tsx b/src/components/custom/file-mounts-card.tsx index 5ad8a2f8..21044c17 100644 --- a/src/components/custom/file-mounts-card.tsx +++ b/src/components/custom/file-mounts-card.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; import { EditIcon, Plus, TrashIcon } from "lucide-react"; @@ -12,12 +12,11 @@ import FileMountEditOverlay from "@/components/custom/file-mount-edit-overlay"; import { deleteFileMount } from "@/app/project/actions"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -export default function FileMountsCard({ fileMounts, workloadId, workloadType, readonly, hideCard = false }: { +export default function FileMountsCard({ fileMounts, workloadId, workloadType, readonly }: { fileMounts: FileMountEditModel[]; workloadId: string; workloadType: WorkloadType; readonly: boolean; - hideCard?: boolean; }) { const { openConfirmDialog } = useConfirmDialog(); const { openDialog } = useDialog(); @@ -42,15 +41,14 @@ export default function FileMountsCard({ fileMounts, workloadId, workloadType, r }); }; - const CardWrapper = hideCard ? 'div' : Card; return <> - +
File Mounts Create files which are mounted into the container. - {fileMounts.length > 0 && + {fileMounts.length > 0 && {fileMounts.length} File Mounts @@ -74,9 +72,9 @@ export default function FileMountsCard({ fileMounts, workloadId, workloadType, r
} - {!readonly && + {!readonly && } - +
; } diff --git a/src/components/custom/workload-builds-table.tsx b/src/components/custom/workload-builds-table.tsx index 0ed402d4..291422c5 100644 --- a/src/components/custom/workload-builds-table.tsx +++ b/src/components/custom/workload-builds-table.tsx @@ -10,7 +10,7 @@ import FullLoadingSpinner from "@/components/ui/full-loading-spinnter"; import { SimpleDataTable } from "@/components/custom/simple-data-table"; import ShortCommitHash from "@/components/custom/short-commit-hash"; import BuildStatusBadge from "@/app/builds/build-status-badge"; -import { BuildLogsDialogContent } from "@/app/project/app/[appId]/overview/build-logs-overlay"; +import { BuildLogsDialogContent } from "@/app/project/workload-components/build-logs-overlay"; import { formatDateTime } from "@/frontend/utils/format.utils"; import { Toast } from "@/frontend/utils/toast.utils"; import { useConfirmDialog, useDialog } from "@/frontend/states/zustand.states"; From ed924159d28b072f5f4b4f4ccec43743e6776e8e Mon Sep 17 00:00:00 2001 From: biersoeckli Date: Sun, 8 Nov 2026 11:49:21 +0000 Subject: [PATCH 4/7] refactor: restructured app and agent ui files --- .../create}/create-agent-dialog.tsx | 2 +- .../create-template-agent-setup-dialog.tsx | 4 +- .../create}/rename-agent-dialog.tsx | 2 +- .../details}/agent-actions.ts | 0 .../details/agent-drawer.tsx} | 8 +- .../agent-sandbox-drawer-settings.tsx | 16 +- .../details}/agent-sandbox-status-actions.tsx | 0 .../details/builds}/agent-builds-grid.tsx | 4 +- .../details}/configuration/actions.ts | 0 .../agent-container-config-card.tsx | 0 .../configuration/agent-rate-limits-card.tsx | 0 .../agent-env-var-edit-overlay.tsx | 2 +- .../environment}/agent-env-vars-card.tsx | 2 +- .../model}/agent-model-configuration-card.tsx | 0 .../agent-model-configuration-editor.tsx | 2 +- .../network}/agent-network-policy-card.tsx | 2 +- ...etwork-policy-egress-rule-edit-overlay.tsx | 2 +- .../details}/sandboxes/actions.ts | 0 .../sandboxes/agent-access-dialog.tsx | 0 .../sandboxes/agent-sandboxes-card.tsx | 0 .../details/source}/agent-source-card.tsx | 0 .../source}/agent-source-wizard-dialog.tsx | 2 +- .../volumes}/agent-volume-edit-overlay.tsx | 2 +- .../details/volumes}/agent-volumes-card.tsx | 2 +- .../source-wizard-fields.tsx | 119 ---------- .../create-template-app-setup-dialog.tsx | 4 +- .../create}/edit-app-dialog.tsx | 2 +- .../new-network-policy-explanation-dialog.tsx | 2 +- .../app-detail => apps/details}/actions.ts | 0 .../advanced-network-settings.test.ts | 22 +- .../details}/app-details-drawer.tsx | 208 ++---------------- .../details}/app-details-settings.tsx | 24 +- .../details}/app-drawer-app-context.ts | 0 .../apps/details/app-status-actions.tsx | 188 ++++++++++++++++ .../details/configuration}/actions.ts | 0 .../configuration}/app-container-config.tsx | 0 .../configuration}/app-rate-limits.tsx | 2 +- .../app-source-wizard/build-method-step.tsx | 0 .../details}/credentials/actions.ts | 0 .../details/credentials/db-credentials.tsx} | 0 .../details}/credentials/db-gate-db-tool.tsx | 0 .../details}/credentials/db-tools.tsx | 0 .../credentials/phpmyadmin-db-tool.tsx | 0 .../details/deployments}/actions.ts | 0 .../app-details-deployments-tab.tsx | 8 +- .../deployments}/deployments-grid-view.tsx | 0 .../details/deployments}/deployments.tsx | 0 .../deployments}/webhook-deployment.tsx | 0 .../details}/environment/actions.ts | 0 .../environment}/app-details-environment.tsx | 0 .../details}/environment/env-edit.tsx | 0 .../details/network/policy}/actions.ts | 0 .../app-network-policy-rule-dialog.tsx | 0 .../app-network-policy-rule-section.tsx | 0 .../policy}/basic-auth-edit-dialog.tsx | 0 .../details/network/policy}/basic-auth.tsx | 0 .../network/policy}/network-policy-graph.tsx | 0 .../network/policy}/network-policy.tsx | 0 .../details/network/ports}/actions.ts | 0 .../network/ports}/node-port-edit-dialog.tsx | 0 .../network/ports/node-ports-card.tsx} | 0 .../details/runtime}/logs.tsx | 2 +- .../details/runtime}/logs.unit.spec.ts | 4 +- .../details/runtime}/monitoring-app.tsx | 2 +- .../runtime}/monitoring-app.unit.spec.ts | 4 +- .../details/runtime}/terminal-overlay.tsx | 0 .../details/runtime}/terminal-streamed.tsx | 0 .../app-source-wizard-dialog.tsx | 4 +- .../framework-configuration-step.tsx | 2 +- .../app-source-wizard/framework-step.tsx | 0 .../details/source}/app-source.tsx | 0 .../source}/framework-configuration-card.tsx | 2 +- .../details}/volumes/actions.ts | 0 .../backups}/app-details-backup-list.tsx | 2 +- .../backups}/app-details-backups-tab.tsx | 6 +- .../backups}/volume-backup-edit-overlay.tsx | 2 +- .../volumes/backups}/volume-backup.tsx | 2 +- .../volumes/shared-storage-edit-overlay.tsx | 0 .../details}/volumes/storage-edit-overlay.tsx | 0 .../details/volumes/volumes-list.tsx} | 0 .../[projectId]/choose-template-dialog.tsx | 4 +- .../[projectId]/create-project-actions.tsx | 4 +- .../network-graph/connection-edge.tsx | 23 ++ ...oject-network-graph-agent-context-menu.tsx | 2 +- ...project-network-graph-app-context-menu.tsx | 2 +- ...ject-network-graph-canvas-context-menu.tsx | 4 +- ...-network-graph-connection-context-menu.tsx | 0 .../overview/network-graph/graph-legend.tsx | 25 +++ ...ject-network-graph-agent-workload-node.tsx | 0 ...roject-network-graph-app-workload-node.tsx | 0 .../project-network-graph-internet-node.tsx | 0 ...oject-network-graph-drawer-session.spec.ts | 0 .../project-network-graph-drawer-session.ts | 0 .../project-network-graph-focus.spec.ts | 0 .../project-network-graph-focus.ts | 0 .../project-network-graph-projection.ts | 0 ...ect-network-graph-visual-semantics.spec.ts | 0 .../project-network-graph-visual-semantics.ts | 0 .../project-network-graph.spec.ts | 4 +- .../network-graph}/project-network-graph.tsx | 74 ++----- .../use-project-network-graph.ts | 0 .../project-overview.tsx | 6 +- .../{ => overview}/workloads-table.tsx | 6 +- src/app/project/[projectId]/page.tsx | 2 +- ...rlay.tsx => build-logs-dialog-content.tsx} | 0 .../drawer/drawer-tab-scroll-area.tsx | 16 ++ .../drawer}/nested-drawer.tsx | 0 .../drawer}/settings-section.tsx | 0 .../source-wizard/container-image-step.tsx | 3 +- .../source-wizard/dockerfile-path-step.tsx | 2 +- .../fields/branch-loading-state.tsx | 36 +++ .../source-wizard/fields/icon-input.tsx | 18 ++ .../source-wizard/fields/secret-input.tsx | 40 ++++ .../fields/secret-summary-row.tsx | 26 +++ .../source-wizard/fields/summary-row.tsx | 11 + .../source-wizard/git-https-url-step.tsx | 4 +- .../source-wizard/git-ssh-url-step.tsx | 3 +- .../source-wizard/source-summary-step.tsx | 3 +- .../llm-gateways/llm-gateway-edit-overlay.tsx | 2 +- .../custom/build-status-indicator.tsx | 2 +- .../build-status-indicator.unit.spec.ts | 2 +- .../custom/workload-builds-table.tsx | 2 +- .../utils/path-builder.utils.unit.spec.ts | 2 +- 123 files changed, 522 insertions(+), 469 deletions(-) rename src/app/project/[projectId]/{agent-components => agents/create}/create-agent-dialog.tsx (99%) rename src/app/project/[projectId]/{agent-components => agents/create}/create-template-agent-setup-dialog.tsx (99%) rename src/app/project/[projectId]/{agent-components => agents/create}/rename-agent-dialog.tsx (98%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details}/agent-actions.ts (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/agent-sandbox-drawer.tsx => agents/details/agent-drawer.tsx} (97%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details}/agent-sandbox-drawer-settings.tsx (90%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details}/agent-sandbox-status-actions.tsx (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details/builds}/agent-builds-grid.tsx (98%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details}/configuration/actions.ts (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details}/configuration/agent-container-config-card.tsx (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details}/configuration/agent-rate-limits-card.tsx (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/environment}/agent-env-var-edit-overlay.tsx (98%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/environment}/agent-env-vars-card.tsx (98%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/model}/agent-model-configuration-card.tsx (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/model}/agent-model-configuration-editor.tsx (99%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/network}/agent-network-policy-card.tsx (99%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/network}/agent-network-policy-egress-rule-edit-overlay.tsx (99%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details}/sandboxes/actions.ts (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details}/sandboxes/agent-access-dialog.tsx (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components => agents/details}/sandboxes/agent-sandboxes-card.tsx (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/source}/agent-source-card.tsx (100%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/source}/agent-source-wizard-dialog.tsx (99%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/volumes}/agent-volume-edit-overlay.tsx (99%) rename src/app/project/[projectId]/{agent-components/agent-drawer-components/configuration => agents/details/volumes}/agent-volumes-card.tsx (98%) delete mode 100644 src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields.tsx rename src/app/project/[projectId]/{app-components => apps/create}/create-template-app-setup-dialog.tsx (98%) rename src/app/project/[projectId]/{app-components => apps/create}/edit-app-dialog.tsx (97%) rename src/app/project/[projectId]/{app-components => apps/create}/new-network-policy-explanation-dialog.tsx (97%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/actions.ts (100%) rename src/app/project/[projectId]/{app-components/app-drawer-components => apps/details}/advanced-network-settings.test.ts (60%) rename src/app/project/[projectId]/{app-components/app-drawer-components => apps/details}/app-details-drawer.tsx (59%) rename src/app/project/[projectId]/{app-components/app-drawer-components => apps/details}/app-details-settings.tsx (90%) rename src/app/project/[projectId]/{app-components/app-drawer-components => apps/details}/app-drawer-app-context.ts (100%) create mode 100644 src/app/project/[projectId]/apps/details/app-status-actions.tsx rename src/app/project/[projectId]/{app-components/app-detail/general => apps/details/configuration}/actions.ts (100%) rename src/app/project/[projectId]/{app-components/app-detail/general => apps/details/configuration}/app-container-config.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/general => apps/details/configuration}/app-rate-limits.tsx (99%) rename src/app/project/[projectId]/{app-components/app-detail/general => apps/details/configuration}/app-source-wizard/build-method-step.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/credentials/actions.ts (100%) rename src/app/project/[projectId]/{app-components/app-detail/credentials/db-crendentials.tsx => apps/details/credentials/db-credentials.tsx} (100%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/credentials/db-gate-db-tool.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/credentials/db-tools.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/credentials/phpmyadmin-db-tool.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/deployments}/actions.ts (100%) rename src/app/project/[projectId]/{app-components/app-drawer-components => apps/details/deployments}/app-details-deployments-tab.tsx (88%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/deployments}/deployments-grid-view.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/deployments}/deployments.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/deployments}/webhook-deployment.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/environment/actions.ts (100%) rename src/app/project/[projectId]/{app-components/app-drawer-components => apps/details/environment}/app-details-environment.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/environment/env-edit.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/advanced => apps/details/network/policy}/actions.ts (100%) rename src/app/project/[projectId]/{app-components/app-detail/advanced => apps/details/network/policy}/app-network-policy-rule-dialog.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/advanced => apps/details/network/policy}/app-network-policy-rule-section.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/advanced => apps/details/network/policy}/basic-auth-edit-dialog.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/advanced => apps/details/network/policy}/basic-auth.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/advanced => apps/details/network/policy}/network-policy-graph.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/advanced => apps/details/network/policy}/network-policy.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/domains => apps/details/network/ports}/actions.ts (100%) rename src/app/project/[projectId]/{app-components/app-detail/domains => apps/details/network/ports}/node-port-edit-dialog.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/domains/node-ports.tsx => apps/details/network/ports/node-ports-card.tsx} (100%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/runtime}/logs.tsx (98%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/runtime}/logs.unit.spec.ts (97%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/runtime}/monitoring-app.tsx (98%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/runtime}/monitoring-app.unit.spec.ts (96%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/runtime}/terminal-overlay.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/overview => apps/details/runtime}/terminal-streamed.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/general => apps/details/source}/app-source-wizard/app-source-wizard-dialog.tsx (99%) rename src/app/project/[projectId]/{app-components/app-detail/general => apps/details/source}/app-source-wizard/framework-configuration-step.tsx (97%) rename src/app/project/[projectId]/{app-components/app-detail/general => apps/details/source}/app-source-wizard/framework-step.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/general => apps/details/source}/app-source.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/general => apps/details/source}/framework-configuration-card.tsx (98%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/volumes/actions.ts (100%) rename src/app/project/[projectId]/{app-components/app-drawer-components => apps/details/volumes/backups}/app-details-backup-list.tsx (98%) rename src/app/project/[projectId]/{app-components/app-drawer-components => apps/details/volumes/backups}/app-details-backups-tab.tsx (97%) rename src/app/project/[projectId]/{app-components/app-detail/volumes => apps/details/volumes/backups}/volume-backup-edit-overlay.tsx (99%) rename src/app/project/[projectId]/{app-components/app-detail/volumes => apps/details/volumes/backups}/volume-backup.tsx (99%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/volumes/shared-storage-edit-overlay.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail => apps/details}/volumes/storage-edit-overlay.tsx (100%) rename src/app/project/[projectId]/{app-components/app-detail/volumes/storages.tsx => apps/details/volumes/volumes-list.tsx} (100%) create mode 100644 src/app/project/[projectId]/overview/network-graph/connection-edge.tsx rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/context-menus/project-network-graph-agent-context-menu.tsx (97%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/context-menus/project-network-graph-app-context-menu.tsx (99%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/context-menus/project-network-graph-canvas-context-menu.tsx (96%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/context-menus/project-network-graph-connection-context-menu.tsx (100%) create mode 100644 src/app/project/[projectId]/overview/network-graph/graph-legend.tsx rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/nodes/project-network-graph-agent-workload-node.tsx (100%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/nodes/project-network-graph-app-workload-node.tsx (100%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/nodes/project-network-graph-internet-node.tsx (100%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/project-network-graph-drawer-session.spec.ts (100%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/project-network-graph-drawer-session.ts (100%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/project-network-graph-focus.spec.ts (100%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/project-network-graph-focus.ts (100%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/project-network-graph-projection.ts (100%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/project-network-graph-visual-semantics.spec.ts (100%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/project-network-graph-visual-semantics.ts (100%) rename src/app/project/[projectId]/{app-components => overview/network-graph}/project-network-graph.spec.ts (97%) rename src/app/project/[projectId]/{app-components => overview/network-graph}/project-network-graph.tsx (90%) rename src/app/project/[projectId]/{app-components/project-network-graph => overview/network-graph}/use-project-network-graph.ts (100%) rename src/app/project/[projectId]/{app-components => overview}/project-overview.tsx (97%) rename src/app/project/[projectId]/{ => overview}/workloads-table.tsx (97%) rename src/app/project/workload-components/{build-logs-overlay.tsx => build-logs-dialog-content.tsx} (100%) create mode 100644 src/app/project/workload-components/drawer/drawer-tab-scroll-area.tsx rename src/app/project/{[projectId]/app-components/app-drawer-components => workload-components/drawer}/nested-drawer.tsx (100%) rename src/app/project/{[projectId]/app-components/app-drawer-components => workload-components/drawer}/settings-section.tsx (100%) create mode 100644 src/app/project/workload-components/source-wizard/fields/branch-loading-state.tsx create mode 100644 src/app/project/workload-components/source-wizard/fields/icon-input.tsx create mode 100644 src/app/project/workload-components/source-wizard/fields/secret-input.tsx create mode 100644 src/app/project/workload-components/source-wizard/fields/secret-summary-row.tsx create mode 100644 src/app/project/workload-components/source-wizard/fields/summary-row.tsx diff --git a/src/app/project/[projectId]/agent-components/create-agent-dialog.tsx b/src/app/project/[projectId]/agents/create/create-agent-dialog.tsx similarity index 99% rename from src/app/project/[projectId]/agent-components/create-agent-dialog.tsx rename to src/app/project/[projectId]/agents/create/create-agent-dialog.tsx index fae2131b..cfb4d63a 100644 --- a/src/app/project/[projectId]/agent-components/create-agent-dialog.tsx +++ b/src/app/project/[projectId]/agents/create/create-agent-dialog.tsx @@ -20,7 +20,7 @@ import { import SelectBox from "@/components/custom/multiselect-field"; import { Toast } from "@/frontend/utils/toast.utils"; import { Actions } from "@/frontend/utils/nextjs-actions.utils"; -import { createAgent, getLlmGateways, getModelAliasesForGateway } from "../actions"; +import { createAgent, getLlmGateways, getModelAliasesForGateway } from "../../actions"; import { useRouter } from "next/navigation"; import { PathBuilderUtils } from "@/shared/utils/path-builder.utils"; import { Loader2 } from "lucide-react"; diff --git a/src/app/project/[projectId]/agent-components/create-template-agent-setup-dialog.tsx b/src/app/project/[projectId]/agents/create/create-template-agent-setup-dialog.tsx similarity index 99% rename from src/app/project/[projectId]/agent-components/create-template-agent-setup-dialog.tsx rename to src/app/project/[projectId]/agents/create/create-template-agent-setup-dialog.tsx index bea411cd..42d8fab8 100644 --- a/src/app/project/[projectId]/agent-components/create-template-agent-setup-dialog.tsx +++ b/src/app/project/[projectId]/agents/create/create-template-agent-setup-dialog.tsx @@ -17,9 +17,9 @@ import { Actions } from "@/frontend/utils/nextjs-actions.utils"; import { FormUtils } from "@/frontend/utils/form.utilts"; import { AgentTemplateModel, agentTemplateZodModel } from "@/shared/model/agent-template.model"; import { ServerActionResult } from "@/shared/model/server-action-error-return.model"; -import { createAgentFromTemplate, getLlmGateways, getModelAliasesForGateway } from "../actions"; +import { createAgentFromTemplate, getLlmGateways, getModelAliasesForGateway } from "../../actions"; import { useDialogContext } from "@/frontend/states/dialog-context"; -import { TemplateDetailsPanel } from "../template-details-panel"; +import { TemplateDetailsPanel } from "../../template-details-panel"; interface LlmGatewayOption { id: string; diff --git a/src/app/project/[projectId]/agent-components/rename-agent-dialog.tsx b/src/app/project/[projectId]/agents/create/rename-agent-dialog.tsx similarity index 98% rename from src/app/project/[projectId]/agent-components/rename-agent-dialog.tsx rename to src/app/project/[projectId]/agents/create/rename-agent-dialog.tsx index e137d91a..c066ecd8 100644 --- a/src/app/project/[projectId]/agent-components/rename-agent-dialog.tsx +++ b/src/app/project/[projectId]/agents/create/rename-agent-dialog.tsx @@ -16,7 +16,7 @@ import { useActionState, useEffect } from "react"; import { useForm } from "react-hook-form"; import type { z } from "zod"; import { toast } from "sonner"; -import { renameAgent } from "../actions"; +import { renameAgent } from "../../actions"; import { Agent } from "@prisma/client"; export function RenameAgentDialog({ agent }: { agent: AgentExtendedModel }) { diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/agent-actions.ts b/src/app/project/[projectId]/agents/details/agent-actions.ts similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/agent-actions.ts rename to src/app/project/[projectId]/agents/details/agent-actions.ts diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/agent-sandbox-drawer.tsx b/src/app/project/[projectId]/agents/details/agent-drawer.tsx similarity index 97% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/agent-sandbox-drawer.tsx rename to src/app/project/[projectId]/agents/details/agent-drawer.tsx index ff711be7..7f262d20 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/agent-sandbox-drawer.tsx +++ b/src/app/project/[projectId]/agents/details/agent-drawer.tsx @@ -26,11 +26,11 @@ import { } from '@/components/ui/empty'; import { ScrollArea } from '@/components/ui/scroll-area'; import { useDialog } from '@/frontend/states/zustand.states'; -import { DrawerTabScrollArea } from '@/app/project/[projectId]/app-components/app-drawer-components/app-details-drawer'; -import { NestedDrawerProvider } from '@/app/project/[projectId]/app-components/app-drawer-components/nested-drawer'; +import { DrawerTabScrollArea } from '@/app/project/workload-components/drawer/drawer-tab-scroll-area'; +import { NestedDrawerProvider } from '@/app/project/workload-components/drawer/nested-drawer'; import AgentSandboxesCard from './sandboxes/agent-sandboxes-card'; -import { AgentBuildsGrid } from './agent-builds-grid'; -import { RenameAgentDialog } from '@/app/project/[projectId]/agent-components/rename-agent-dialog'; +import { AgentBuildsGrid } from './builds/agent-builds-grid'; +import { RenameAgentDialog } from '@/app/project/[projectId]/agents/create/rename-agent-dialog'; import { AgentSandboxStatusActions } from './agent-sandbox-status-actions'; import AgentSandboxDrawerSettings from './agent-sandbox-drawer-settings'; import type { AgentExtendedModel } from '@/shared/model/agent-extended.model'; diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/agent-sandbox-drawer-settings.tsx b/src/app/project/[projectId]/agents/details/agent-sandbox-drawer-settings.tsx similarity index 90% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/agent-sandbox-drawer-settings.tsx rename to src/app/project/[projectId]/agents/details/agent-sandbox-drawer-settings.tsx index 063d2603..a56b50aa 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/agent-sandbox-drawer-settings.tsx +++ b/src/app/project/[projectId]/agents/details/agent-sandbox-drawer-settings.tsx @@ -8,16 +8,16 @@ import { Network, } from 'lucide-react'; import { Button } from '@/components/ui/button'; -import { SettingsSection } from '@/app/project/[projectId]/app-components/app-drawer-components/settings-section'; -import { useNestedDrawer } from '@/app/project/[projectId]/app-components/app-drawer-components/nested-drawer'; -import AgentSourceCard from './configuration/agent-source-card'; -import AgentModelConfigurationCard from './configuration/agent-model-configuration-card'; -import { AgentModelConfigurationEditor } from './configuration/agent-model-configuration-editor'; +import { SettingsSection } from '@/app/project/workload-components/drawer/settings-section'; +import { useNestedDrawer } from '@/app/project/workload-components/drawer/nested-drawer'; +import AgentSourceCard from './source/agent-source-card'; +import AgentModelConfigurationCard from './model/agent-model-configuration-card'; +import { AgentModelConfigurationEditor } from './model/agent-model-configuration-editor'; import AgentContainerConfigCard from './configuration/agent-container-config-card'; import AgentRateLimitsCard from './configuration/agent-rate-limits-card'; -import AgentVolumesCard from './configuration/agent-volumes-card'; -import AgentNetworkPolicyCard from './configuration/agent-network-policy-card'; -import AgentEnvVarsCard from './configuration/agent-env-vars-card'; +import AgentVolumesCard from './volumes/agent-volumes-card'; +import AgentNetworkPolicyCard from './network/agent-network-policy-card'; +import AgentEnvVarsCard from './environment/agent-env-vars-card'; import HealthCheckSettings from '@/app/project/workload-components/health-check-settings'; import { saveAgentHealthCheck } from './configuration/actions'; import DomainsCard from '@/components/custom/domains-card'; diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/agent-sandbox-status-actions.tsx b/src/app/project/[projectId]/agents/details/agent-sandbox-status-actions.tsx similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/agent-sandbox-status-actions.tsx rename to src/app/project/[projectId]/agents/details/agent-sandbox-status-actions.tsx diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/agent-builds-grid.tsx b/src/app/project/[projectId]/agents/details/builds/agent-builds-grid.tsx similarity index 98% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/agent-builds-grid.tsx rename to src/app/project/[projectId]/agents/details/builds/agent-builds-grid.tsx index 42d07aab..c92e7b84 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/agent-builds-grid.tsx +++ b/src/app/project/[projectId]/agents/details/builds/agent-builds-grid.tsx @@ -27,8 +27,8 @@ import { Item, ItemContent, ItemTitle } from '@/components/ui/item'; import FullLoadingSpinner from '@/components/ui/full-loading-spinnter'; import ShortCommitHash from '@/components/custom/short-commit-hash'; import BuildStatusBadge from '@/app/builds/build-status-badge'; -import { BuildLogsDialogContent } from '@/app/project/workload-components/build-logs-overlay'; -import { useNestedDrawer } from '@/app/project/[projectId]/app-components/app-drawer-components/nested-drawer'; +import { BuildLogsDialogContent } from '@/app/project/workload-components/build-logs-dialog-content'; +import { useNestedDrawer } from '@/app/project/workload-components/drawer/nested-drawer'; import { deleteWorkloadBuildAction, getWorkloadBuildsAction } from '@/app/project/actions'; import { usePolling } from '@/frontend/hooks/use-polling'; import { useConfirmDialog } from '@/frontend/states/zustand.states'; diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/actions.ts b/src/app/project/[projectId]/agents/details/configuration/actions.ts similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/actions.ts rename to src/app/project/[projectId]/agents/details/configuration/actions.ts diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-container-config-card.tsx b/src/app/project/[projectId]/agents/details/configuration/agent-container-config-card.tsx similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-container-config-card.tsx rename to src/app/project/[projectId]/agents/details/configuration/agent-container-config-card.tsx diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-rate-limits-card.tsx b/src/app/project/[projectId]/agents/details/configuration/agent-rate-limits-card.tsx similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-rate-limits-card.tsx rename to src/app/project/[projectId]/agents/details/configuration/agent-rate-limits-card.tsx diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-env-var-edit-overlay.tsx b/src/app/project/[projectId]/agents/details/environment/agent-env-var-edit-overlay.tsx similarity index 98% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-env-var-edit-overlay.tsx rename to src/app/project/[projectId]/agents/details/environment/agent-env-var-edit-overlay.tsx index 0c145bbb..081967fd 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-env-var-edit-overlay.tsx +++ b/src/app/project/[projectId]/agents/details/environment/agent-env-var-edit-overlay.tsx @@ -9,7 +9,7 @@ import { useDialogContext } from "@/frontend/states/dialog-context"; import { FormUtils } from "@/frontend/utils/form.utilts"; import { AgentEnvVarEditModel, agentEnvVarEditZodModel } from "@/shared/model/agent-config.model"; import { ServerActionResult } from "@/shared/model/server-action-error-return.model"; -import { saveAgentEnvVar } from "./actions"; +import { saveAgentEnvVar } from "../configuration/actions"; import { zodResolver } from "@hookform/resolvers/zod"; import { useActionState, useEffect } from "react"; import { useForm } from "react-hook-form"; diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-env-vars-card.tsx b/src/app/project/[projectId]/agents/details/environment/agent-env-vars-card.tsx similarity index 98% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-env-vars-card.tsx rename to src/app/project/[projectId]/agents/details/environment/agent-env-vars-card.tsx index a3f5e556..a7906e57 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-env-vars-card.tsx +++ b/src/app/project/[projectId]/agents/details/environment/agent-env-vars-card.tsx @@ -7,7 +7,7 @@ import { useConfirmDialog, useDialog } from "@/frontend/states/zustand.states"; import { Toast } from "@/frontend/utils/toast.utils"; import { AgentExtendedModel } from "@/shared/model/agent-extended.model"; import { EditIcon, Plus, TrashIcon } from "lucide-react"; -import { deleteAgentEnvVar } from "./actions"; +import { deleteAgentEnvVar } from "../configuration/actions"; import AgentEnvVarEditOverlay from "./agent-env-var-edit-overlay"; function getEnvironmentVariableNames(encryptedEnvVars: string | null | undefined): string[] { diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-model-configuration-card.tsx b/src/app/project/[projectId]/agents/details/model/agent-model-configuration-card.tsx similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-model-configuration-card.tsx rename to src/app/project/[projectId]/agents/details/model/agent-model-configuration-card.tsx diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-model-configuration-editor.tsx b/src/app/project/[projectId]/agents/details/model/agent-model-configuration-editor.tsx similarity index 99% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-model-configuration-editor.tsx rename to src/app/project/[projectId]/agents/details/model/agent-model-configuration-editor.tsx index 1f6deff7..c12dda29 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-model-configuration-editor.tsx +++ b/src/app/project/[projectId]/agents/details/model/agent-model-configuration-editor.tsx @@ -13,7 +13,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { useRouter } from "next/navigation"; import { toast } from "sonner"; -import { saveAgentModelConfiguration } from "./actions"; +import { saveAgentModelConfiguration } from "../configuration/actions"; import { getLlmGateways, getModelAliasesForGateway } from "@/app/project/[projectId]/actions"; import { AgentExtendedModel } from "@/shared/model/agent-extended.model"; import { LlmGatewayModel } from "@/shared/model/llm-gateway.model"; diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-network-policy-card.tsx b/src/app/project/[projectId]/agents/details/network/agent-network-policy-card.tsx similarity index 99% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-network-policy-card.tsx rename to src/app/project/[projectId]/agents/details/network/agent-network-policy-card.tsx index a0dc68be..82c8f4da 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-network-policy-card.tsx +++ b/src/app/project/[projectId]/agents/details/network/agent-network-policy-card.tsx @@ -11,7 +11,7 @@ import { Toast } from "@/frontend/utils/toast.utils"; import { useConfirmDialog, useDialog } from "@/frontend/states/zustand.states"; import { useState } from "react"; import { AgentExtendedModel } from "@/shared/model/agent-extended.model"; -import { deleteAgentNetworkPolicyEgressRule, saveAgentNetworkPolicySettings } from "./actions"; +import { deleteAgentNetworkPolicyEgressRule, saveAgentNetworkPolicySettings } from "../configuration/actions"; import AgentNetworkPolicyEgressRuleEditOverlay from "./agent-network-policy-egress-rule-edit-overlay"; import { Tooltip, TooltipProvider, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { InternalHostnameUtils } from "@/shared/utils/internal-hostname.utils"; diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-network-policy-egress-rule-edit-overlay.tsx b/src/app/project/[projectId]/agents/details/network/agent-network-policy-egress-rule-edit-overlay.tsx similarity index 99% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-network-policy-egress-rule-edit-overlay.tsx rename to src/app/project/[projectId]/agents/details/network/agent-network-policy-egress-rule-edit-overlay.tsx index 4fbaa5d9..1ae8ddf8 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-network-policy-egress-rule-edit-overlay.tsx +++ b/src/app/project/[projectId]/agents/details/network/agent-network-policy-egress-rule-edit-overlay.tsx @@ -17,7 +17,7 @@ import { agentNetworkPolicyEgressRuleEditZodModel, } from "@/shared/model/agent-network-policy-edit.model"; import { AgentExtendedModel } from "@/shared/model/agent-extended.model"; -import { getAppsForAgentNetworkPolicy, saveAgentNetworkPolicyEgressRule } from "./actions"; +import { getAppsForAgentNetworkPolicy, saveAgentNetworkPolicyEgressRule } from "../configuration/actions"; import { z } from "zod"; type AgentNetworkPolicyRule = NonNullable['rules'][number]; diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/sandboxes/actions.ts b/src/app/project/[projectId]/agents/details/sandboxes/actions.ts similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/sandboxes/actions.ts rename to src/app/project/[projectId]/agents/details/sandboxes/actions.ts diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/sandboxes/agent-access-dialog.tsx b/src/app/project/[projectId]/agents/details/sandboxes/agent-access-dialog.tsx similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/sandboxes/agent-access-dialog.tsx rename to src/app/project/[projectId]/agents/details/sandboxes/agent-access-dialog.tsx diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/sandboxes/agent-sandboxes-card.tsx b/src/app/project/[projectId]/agents/details/sandboxes/agent-sandboxes-card.tsx similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/sandboxes/agent-sandboxes-card.tsx rename to src/app/project/[projectId]/agents/details/sandboxes/agent-sandboxes-card.tsx diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-source-card.tsx b/src/app/project/[projectId]/agents/details/source/agent-source-card.tsx similarity index 100% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-source-card.tsx rename to src/app/project/[projectId]/agents/details/source/agent-source-card.tsx diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-source-wizard-dialog.tsx b/src/app/project/[projectId]/agents/details/source/agent-source-wizard-dialog.tsx similarity index 99% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-source-wizard-dialog.tsx rename to src/app/project/[projectId]/agents/details/source/agent-source-wizard-dialog.tsx index 0a925112..302585a5 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-source-wizard-dialog.tsx +++ b/src/app/project/[projectId]/agents/details/source/agent-source-wizard-dialog.tsx @@ -18,7 +18,7 @@ import { generateOrRegenerateAgentGitSshKey, getAgentGitBranches, saveAgentSource, -} from "./actions"; +} from "../configuration/actions"; import { deployAgent } from "../agent-actions"; import { PathBuilderUtils } from "@/shared/utils/path-builder.utils"; import { ContainerImageStep } from "@/app/project/workload-components/source-wizard/container-image-step"; diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-volume-edit-overlay.tsx b/src/app/project/[projectId]/agents/details/volumes/agent-volume-edit-overlay.tsx similarity index 99% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-volume-edit-overlay.tsx rename to src/app/project/[projectId]/agents/details/volumes/agent-volume-edit-overlay.tsx index 81783863..83d6d52a 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-volume-edit-overlay.tsx +++ b/src/app/project/[projectId]/agents/details/volumes/agent-volume-edit-overlay.tsx @@ -15,7 +15,7 @@ import { FormUtils } from "@/frontend/utils/form.utilts"; import { ServerActionResult } from "@/shared/model/server-action-error-return.model"; import { AgentVolumeEditModel, agentVolumeEditZodModel } from "@/shared/model/volume-edit.model"; import { useDialogContext } from "@/frontend/states/dialog-context"; -import { saveAgentVolume } from "./actions"; +import { saveAgentVolume } from "../configuration/actions"; import { StorageClassUtils } from "@/shared/utils/storage-class.utils"; import { Constants } from "@/shared/utils/constants"; diff --git a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-volumes-card.tsx b/src/app/project/[projectId]/agents/details/volumes/agent-volumes-card.tsx similarity index 98% rename from src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-volumes-card.tsx rename to src/app/project/[projectId]/agents/details/volumes/agent-volumes-card.tsx index f0c39d32..af5137b7 100644 --- a/src/app/project/[projectId]/agent-components/agent-drawer-components/configuration/agent-volumes-card.tsx +++ b/src/app/project/[projectId]/agents/details/volumes/agent-volumes-card.tsx @@ -9,7 +9,7 @@ import { useConfirmDialog, useDialog } from "@/frontend/states/zustand.states"; import AgentVolumeEditOverlay from "./agent-volume-edit-overlay"; import { AgentVolume } from "@prisma/client"; import { AgentVolumeEditModel } from "@/shared/model/volume-edit.model"; -import { deleteAgentVolume } from "./actions"; +import { deleteAgentVolume } from "../configuration/actions"; import { Constants } from "@/shared/utils/constants"; function formatSize(mb: number): string { diff --git a/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields.tsx b/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields.tsx deleted file mode 100644 index db742c1e..00000000 --- a/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { cn } from "@/frontend/utils/utils"; -import { Eye, EyeOff, Loader2, RefreshCw, type LucideIcon } from "lucide-react"; -import { type ComponentProps } from "react"; - -export function IconInput({ icon: Icon, label, className, ...props }: ComponentProps & { icon: LucideIcon; label: string }) { - return ( -
- -
- - -
-
- ); -} - -export function SecretInput({ icon: Icon, label, value, visible, onVisibleChange, onChange }: { - icon: LucideIcon; - label: string; - value: string; - visible: boolean; - onVisibleChange: (visible: boolean) => void; - onChange: (value: string) => void; -}) { - const EyeIcon = visible ? EyeOff : Eye; - return ( -
- -
- - onChange(event.target.value)} - /> - -
-
- ); -} - -export function BranchLoadingState({ loading, error, onRetry }: { - loading: boolean; - error: string | null; - onRetry: () => Promise; -}) { - if (loading) { - return ( -
- - Loading branches... -
- ); - } - - if (!error) { - return null; - } - - return ( - - Branches could not be loaded - -

{error}

- -
-
- ); -} - -export function SummaryRow({ label, value, mono }: { label: string; value: string; mono?: boolean }) { - return ( -
-

{label}

-

{value || 'Not configured'}

-
- ); -} - -export function SecretSummaryRow({ label, value, visible, onVisibleChange }: { - label: string; - value: string; - visible: boolean; - onVisibleChange: (visible: boolean) => void; -}) { - const EyeIcon = visible ? EyeOff : Eye; - const displayValue = value ? (visible ? value : '************') : 'Not configured'; - return ( -
-

{label}

-
-

{displayValue}

- {!!value && ( - - )} -
-
- ); -} diff --git a/src/app/project/[projectId]/app-components/create-template-app-setup-dialog.tsx b/src/app/project/[projectId]/apps/create/create-template-app-setup-dialog.tsx similarity index 98% rename from src/app/project/[projectId]/app-components/create-template-app-setup-dialog.tsx rename to src/app/project/[projectId]/apps/create/create-template-app-setup-dialog.tsx index 6d566b77..97d6bb15 100644 --- a/src/app/project/[projectId]/app-components/create-template-app-setup-dialog.tsx +++ b/src/app/project/[projectId]/apps/create/create-template-app-setup-dialog.tsx @@ -21,10 +21,10 @@ import { SubmitButton } from "@/components/custom/submit-button"; import { ServerActionResult } from "@/shared/model/server-action-error-return.model" import { toast } from "sonner" import { AppTemplateModel, appTemplateZodModel } from "@/shared/model/app-template.model" -import { createAppFromTemplate } from "../actions" +import { createAppFromTemplate } from "../../actions" import { ScrollArea } from "@/components/ui/scroll-area" import { useDialogContext } from "@/frontend/states/dialog-context" -import { TemplateDetailsPanel } from "../template-details-panel" +import { TemplateDetailsPanel } from "../../template-details-panel" export default function CreateTemplateAppSetupDialog({ appTemplate, diff --git a/src/app/project/[projectId]/app-components/edit-app-dialog.tsx b/src/app/project/[projectId]/apps/create/edit-app-dialog.tsx similarity index 97% rename from src/app/project/[projectId]/app-components/edit-app-dialog.tsx rename to src/app/project/[projectId]/apps/create/edit-app-dialog.tsx index eb4e9973..daaf7b8f 100644 --- a/src/app/project/[projectId]/app-components/edit-app-dialog.tsx +++ b/src/app/project/[projectId]/apps/create/edit-app-dialog.tsx @@ -1,7 +1,7 @@ 'use client' import { Toast } from "@/frontend/utils/toast.utils"; -import { createApp } from "../actions"; +import { createApp } from "../../actions"; import { useRouter } from "next/navigation"; import { cloneElement, type MouseEvent, type ReactElement } from "react"; import type { App } from "@prisma/client"; diff --git a/src/app/project/[projectId]/app-components/new-network-policy-explanation-dialog.tsx b/src/app/project/[projectId]/apps/create/new-network-policy-explanation-dialog.tsx similarity index 97% rename from src/app/project/[projectId]/app-components/new-network-policy-explanation-dialog.tsx rename to src/app/project/[projectId]/apps/create/new-network-policy-explanation-dialog.tsx index 2fec01da..19209049 100644 --- a/src/app/project/[projectId]/app-components/new-network-policy-explanation-dialog.tsx +++ b/src/app/project/[projectId]/apps/create/new-network-policy-explanation-dialog.tsx @@ -5,7 +5,7 @@ import { Button } from '@/components/ui/button'; import { DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { useDialogContext } from '@/frontend/states/dialog-context'; import { Toast } from '@/frontend/utils/toast.utils'; -import { acknowledgeNewNetworkPolicyExplanation } from '../actions'; +import { acknowledgeNewNetworkPolicyExplanation } from '../../actions'; export default function NewNetworkPolicyExplanationDialog() { const { closeDialog } = useDialogContext(); diff --git a/src/app/project/[projectId]/app-components/app-detail/actions.ts b/src/app/project/[projectId]/apps/details/actions.ts similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/actions.ts rename to src/app/project/[projectId]/apps/details/actions.ts diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts b/src/app/project/[projectId]/apps/details/advanced-network-settings.test.ts similarity index 60% rename from src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts rename to src/app/project/[projectId]/apps/details/advanced-network-settings.test.ts index a81cb445..744a3de8 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/advanced-network-settings.test.ts +++ b/src/app/project/[projectId]/apps/details/advanced-network-settings.test.ts @@ -1,7 +1,7 @@ import React from 'react'; import { fireEvent, render, screen, cleanup } from '@testing-library/react'; import { afterEach, expect, it, vi } from 'vitest'; -import { NestedDrawerProvider } from './nested-drawer'; +import { NestedDrawerProvider } from '../../../workload-components/drawer/nested-drawer'; import { DrawerSettings } from './app-details-settings'; import { AppDrawerAppContext } from './app-drawer-app-context'; import { RolePermissionEnum } from '@/shared/model/role-extended.model'; @@ -15,19 +15,19 @@ vi.mock('@/components/ui/drawer', () => ({ DrawerTitle: ({ children }: React.PropsWithChildren) => children, })); vi.mock('@/components/ui/scroll-area', () => ({ ScrollArea: ({ children }: React.PropsWithChildren) => children })); -vi.mock('@/app/project/[projectId]/app-components/app-detail/domains/node-ports', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Ports: ${app.appNodePorts.length}`) })); -vi.mock('@/app/project/[projectId]/app-components/app-detail/advanced/basic-auth', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Credentials: ${app.appBasicAuths.length}`) })); -vi.mock('@/app/project/[projectId]/app-components/app-detail/advanced/actions', () => ({ saveHealthCheck: vi.fn() })); +vi.mock('@/app/project/[projectId]/apps/details/network/ports/node-ports-card', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Ports: ${app.appNodePorts.length}`) })); +vi.mock('@/app/project/[projectId]/apps/details/network/policy/basic-auth', () => ({ default: ({ app }: { app: AppExtendedModel }) => React.createElement('div', null, `Credentials: ${app.appBasicAuths.length}`) })); +vi.mock('@/app/project/[projectId]/apps/details/network/policy/actions', () => ({ saveHealthCheck: vi.fn() })); vi.mock('@/app/project/workload-components/health-check-settings', () => ({ default: () => null })); -vi.mock('@/app/project/[projectId]/app-components/app-detail/advanced/network-policy', () => ({ default: () => null })); -vi.mock('@/app/project/[projectId]/app-components/app-detail/environment/env-edit', () => ({ default: () => null })); -vi.mock('@/app/project/[projectId]/app-components/app-detail/general/app-container-config', () => ({ default: () => null })); -vi.mock('@/app/project/[projectId]/app-components/app-detail/general/app-rate-limits', () => ({ default: () => null })); -vi.mock('@/app/project/[projectId]/app-components/app-detail/general/app-source', () => ({ default: () => null })); -vi.mock('@/app/project/[projectId]/app-components/app-detail/volumes/storages', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/apps/details/network/policy/network-policy', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/apps/details/environment/env-edit', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/apps/details/configuration/app-container-config', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/apps/details/configuration/app-rate-limits', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/apps/details/source/app-source', () => ({ default: () => null })); +vi.mock('@/app/project/[projectId]/apps/details/volumes/volumes-list', () => ({ default: () => null })); vi.mock('@/components/custom/domains-card', () => ({ default: () => null })); vi.mock('@/components/custom/file-mounts-card', () => ({ default: () => null })); -vi.mock('./app-details-environment', () => ({ DrawerEnvironment: () => null })); +vi.mock('./environment/app-details-environment', () => ({ DrawerEnvironment: () => null })); afterEach(cleanup); diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-drawer.tsx b/src/app/project/[projectId]/apps/details/app-details-drawer.tsx similarity index 59% rename from src/app/project/[projectId]/app-components/app-drawer-components/app-details-drawer.tsx rename to src/app/project/[projectId]/apps/details/app-details-drawer.tsx index 44b76006..50726e6a 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-drawer.tsx +++ b/src/app/project/[projectId]/apps/details/app-details-drawer.tsx @@ -1,36 +1,24 @@ 'use client'; -import { type ReactNode, type Ref } from 'react'; +import { type Ref } from 'react'; import { BarChart3, Bot, Boxes, - ExternalLink, - Hammer, Key, - Play, Rocket, Logs as LogsIcon, Pencil, Settings, - Square, X, RotateCwClock, } from 'lucide-react'; import { Button } from '@/components/ui/button'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from '@/components/ui/tooltip'; + + + + import { ScrollArea } from '@/components/ui/scroll-area'; import { Drawer, @@ -47,32 +35,27 @@ import { EmptyMedia, EmptyTitle, } from '@/components/ui/empty'; -import PodStatusIndicator from '@/components/custom/pod-status-indicator'; -import { deploy, startApp, stopApp } from '@/app/project/[projectId]/app-components/app-detail/actions'; -import { usePodsStatus } from '@/frontend/states/zustand.states'; import { cn } from '@/frontend/utils/utils'; import { AppSourceUtils } from '@/frontend/utils/app-source.utils'; -import { AppLifecycleUtils } from '@/frontend/utils/app-lifecycle.utils'; -import { Toast } from '@/frontend/utils/toast.utils'; import type { AppExtendedModel } from '@/shared/model/app-extended.model'; -import Logs from '@/app/project/[projectId]/app-components/app-detail/overview/logs'; -import MonitoringTab from '@/app/project/[projectId]/app-components/app-detail/overview/monitoring-app'; +import Logs from '@/app/project/[projectId]/apps/details/runtime/logs'; +import MonitoringTab from '@/app/project/[projectId]/apps/details/runtime/monitoring-app'; import { RolePermissionEnum } from '@/shared/model/role-extended.model'; -import GeneralAppSource from '@/app/project/[projectId]/app-components/app-detail/general/app-source'; -import DbCredentials from '@/app/project/[projectId]/app-components/app-detail/credentials/db-crendentials'; -import DbToolsCard from '@/app/project/[projectId]/app-components/app-detail/credentials/db-tools'; +import GeneralAppSource from '@/app/project/[projectId]/apps/details/source/app-source'; +import DbCredentials from '@/app/project/[projectId]/apps/details/credentials/db-credentials'; +import DbToolsCard from '@/app/project/[projectId]/apps/details/credentials/db-tools'; import { DrawerSettings } from './app-details-settings'; -import { NestedDrawerProvider } from './nested-drawer'; +import { NestedDrawerProvider } from '../../../workload-components/drawer/nested-drawer'; import { AppDrawerAppContext } from './app-drawer-app-context'; -import { DrawerBackupsTab } from './app-details-backups-tab'; -import { EditAppDialog } from '@/app/project/[projectId]/app-components/edit-app-dialog'; +import { DrawerBackupsTab } from './volumes/backups/app-details-backups-tab'; +import { EditAppDialog } from '@/app/project/[projectId]/apps/create/edit-app-dialog'; import type { S3Target } from '@prisma/client'; import type { VolumeBackupExtendedModel } from '@/shared/model/volume-backup-extended.model'; import { AppDrawerNavigationUtils, type AppDrawerTab, } from '@/shared/utils/app-drawer-navigation.utils'; -import DrawerDeploymentsTab from './app-details-deployments-tab'; +import DrawerDeploymentsTab from './deployments/app-details-deployments-tab'; export type AppDetailsDrawerNode = { id: string; @@ -82,167 +65,8 @@ export type AppDetailsDrawerNode = { caption?: string; }; -export function DrawerTabScrollArea({ children }: { children: ReactNode }) { - return ( - -
{children}
-
- ); -} - -function AppStatusActions({ - app, - role, -}: { - app: AppExtendedModel; - role?: RolePermissionEnum; -}) { - const deploymentStatus = usePodsStatus( - (state) => state.podsStatus.get(app.id)?.deploymentStatus ?? 'UNKNOWN', - ); - const lifecycle = AppLifecycleUtils.availability(app, role, deploymentStatus); - const openDomain = (domain: AppExtendedModel['appDomains'][number]) => { - const protocol = domain.useSsl ? 'https' : 'http'; - - window.open(`${protocol}://${domain.hostname}`, '_blank'); - }; - - return ( -
- - {(lifecycle.canManage || app.appDomains.length > 0) && ( - -
- {lifecycle.canManage && ( - <> - - void Toast.fromAction(() => deploy(app.id))} - > - - Deploy - - } - /> - Deploy - - {lifecycle.supportsRebuild && ( - - void Toast.fromAction(() => deploy(app.id, true))} - > - - Rebuild - - } - /> - Rebuild - - )} - - void Toast.fromAction(() => startApp(app.id))} - > - - Start - - } - /> - Start - - - void Toast.fromAction(() => stopApp(app.id))} - > - - Stop - - } - /> - Stop - - - )} - {app.appDomains.length === 1 && ( - - openDomain(app.appDomains[0])} - > - - Open domain - - } - /> - Open domain - - )} - {app.appDomains.length > 1 && ( - - - - - } - /> - - {app.appDomains.map((domain) => ( - openDomain(domain)} - > - - {domain.hostname} - - ))} - - - )} -
-
- )} -
- ); -} +import { DrawerTabScrollArea } from '@/app/project/workload-components/drawer/drawer-tab-scroll-area'; +import { AppStatusActions } from './app-status-actions'; export function AppDetailsDrawer({ contentRef, diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-settings.tsx b/src/app/project/[projectId]/apps/details/app-details-settings.tsx similarity index 90% rename from src/app/project/[projectId]/app-components/app-drawer-components/app-details-settings.tsx rename to src/app/project/[projectId]/apps/details/app-details-settings.tsx index c77bc6fb..cbd7196e 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-settings.tsx +++ b/src/app/project/[projectId]/apps/details/app-details-settings.tsx @@ -10,23 +10,23 @@ import { } from 'lucide-react'; import { useCallback, useContext, useEffect } from 'react'; import { Button } from '@/components/ui/button'; -import BasicAuth from '@/app/project/[projectId]/app-components/app-detail/advanced/basic-auth'; -import { saveHealthCheck } from '@/app/project/[projectId]/app-components/app-detail/advanced/actions'; +import BasicAuth from '@/app/project/[projectId]/apps/details/network/policy/basic-auth'; +import { saveHealthCheck } from '@/app/project/[projectId]/apps/details/network/policy/actions'; import HealthCheckSettings from '@/app/project/workload-components/health-check-settings'; -import NetworkPolicy from '@/app/project/[projectId]/app-components/app-detail/advanced/network-policy'; -import NodePortsCard from '@/app/project/[projectId]/app-components/app-detail/domains/node-ports'; -import EnvEdit from '@/app/project/[projectId]/app-components/app-detail/environment/env-edit'; -import GeneralAppContainerConfig from '@/app/project/[projectId]/app-components/app-detail/general/app-container-config'; -import GeneralAppRateLimits from '@/app/project/[projectId]/app-components/app-detail/general/app-rate-limits'; -import GeneralAppSource from '@/app/project/[projectId]/app-components/app-detail/general/app-source'; -import StorageList from '@/app/project/[projectId]/app-components/app-detail/volumes/storages'; +import NetworkPolicy from '@/app/project/[projectId]/apps/details/network/policy/network-policy'; +import NodePortsCard from '@/app/project/[projectId]/apps/details/network/ports/node-ports-card'; +import EnvEdit from '@/app/project/[projectId]/apps/details/environment/env-edit'; +import GeneralAppContainerConfig from '@/app/project/[projectId]/apps/details/configuration/app-container-config'; +import GeneralAppRateLimits from '@/app/project/[projectId]/apps/details/configuration/app-rate-limits'; +import GeneralAppSource from '@/app/project/[projectId]/apps/details/source/app-source'; +import StorageList from '@/app/project/[projectId]/apps/details/volumes/volumes-list'; import DomainsCard from '@/components/custom/domains-card'; import FileMountsCard from '@/components/custom/file-mounts-card'; import type { AppExtendedModel } from '@/shared/model/app-extended.model'; import { RolePermissionEnum } from '@/shared/model/role-extended.model'; -import { SettingsSection } from './settings-section'; -import { DrawerEnvironment } from './app-details-environment'; -import { useNestedDrawer } from './nested-drawer'; +import { SettingsSection } from '../../../workload-components/drawer/settings-section'; +import { DrawerEnvironment } from './environment/app-details-environment'; +import { useNestedDrawer } from '../../../workload-components/drawer/nested-drawer'; import { AppDrawerAppContext } from './app-drawer-app-context'; import { DrawerCard, DrawerCardDescription, DrawerCardFooter, DrawerCardHeader, DrawerCardTitle } from '@/components/custom/drawer-card'; diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-drawer-app-context.ts b/src/app/project/[projectId]/apps/details/app-drawer-app-context.ts similarity index 100% rename from src/app/project/[projectId]/app-components/app-drawer-components/app-drawer-app-context.ts rename to src/app/project/[projectId]/apps/details/app-drawer-app-context.ts diff --git a/src/app/project/[projectId]/apps/details/app-status-actions.tsx b/src/app/project/[projectId]/apps/details/app-status-actions.tsx new file mode 100644 index 00000000..0a88543a --- /dev/null +++ b/src/app/project/[projectId]/apps/details/app-status-actions.tsx @@ -0,0 +1,188 @@ +'use client'; + + +import { + ExternalLink, + Hammer, + Play, + Rocket, + Square, +} from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@/components/ui/tooltip'; + + + + +import PodStatusIndicator from '@/components/custom/pod-status-indicator'; +import { deploy, startApp, stopApp } from '@/app/project/[projectId]/apps/details/actions'; +import { usePodsStatus } from '@/frontend/states/zustand.states'; +import { AppLifecycleUtils } from '@/frontend/utils/app-lifecycle.utils'; +import { Toast } from '@/frontend/utils/toast.utils'; +import type { AppExtendedModel } from '@/shared/model/app-extended.model'; +import { RolePermissionEnum } from '@/shared/model/role-extended.model'; + + + +export function AppStatusActions({ + app, + role, +}: { + app: AppExtendedModel; + role?: RolePermissionEnum; +}) { + const deploymentStatus = usePodsStatus( + (state) => state.podsStatus.get(app.id)?.deploymentStatus ?? 'UNKNOWN', + ); + const lifecycle = AppLifecycleUtils.availability(app, role, deploymentStatus); + const openDomain = (domain: AppExtendedModel['appDomains'][number]) => { + const protocol = domain.useSsl ? 'https' : 'http'; + + window.open(`${protocol}://${domain.hostname}`, '_blank'); + }; + + return ( +
+ + {(lifecycle.canManage || app.appDomains.length > 0) && ( + +
+ {lifecycle.canManage && ( + <> + + void Toast.fromAction(() => deploy(app.id))} + > + + Deploy + + } + /> + Deploy + + {lifecycle.supportsRebuild && ( + + void Toast.fromAction(() => deploy(app.id, true))} + > + + Rebuild + + } + /> + Rebuild + + )} + + void Toast.fromAction(() => startApp(app.id))} + > + + Start + + } + /> + Start + + + void Toast.fromAction(() => stopApp(app.id))} + > + + Stop + + } + /> + Stop + + + )} + {app.appDomains.length === 1 && ( + + openDomain(app.appDomains[0])} + > + + Open domain + + } + /> + Open domain + + )} + {app.appDomains.length > 1 && ( + + + + + } + /> + + {app.appDomains.map((domain) => ( + openDomain(domain)} + > + + {domain.hostname} + + ))} + + + )} +
+
+ )} +
+ ); +} + diff --git a/src/app/project/[projectId]/app-components/app-detail/general/actions.ts b/src/app/project/[projectId]/apps/details/configuration/actions.ts similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/general/actions.ts rename to src/app/project/[projectId]/apps/details/configuration/actions.ts diff --git a/src/app/project/[projectId]/app-components/app-detail/general/app-container-config.tsx b/src/app/project/[projectId]/apps/details/configuration/app-container-config.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/general/app-container-config.tsx rename to src/app/project/[projectId]/apps/details/configuration/app-container-config.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/general/app-rate-limits.tsx b/src/app/project/[projectId]/apps/details/configuration/app-rate-limits.tsx similarity index 99% rename from src/app/project/[projectId]/app-components/app-detail/general/app-rate-limits.tsx rename to src/app/project/[projectId]/apps/details/configuration/app-rate-limits.tsx index 111599e6..6bc52457 100644 --- a/src/app/project/[projectId]/app-components/app-detail/general/app-rate-limits.tsx +++ b/src/app/project/[projectId]/apps/details/configuration/app-rate-limits.tsx @@ -16,7 +16,7 @@ import { useActionState, useEffect, useState } from "react"; import { toast } from "sonner"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; import { cn } from "@/frontend/utils/utils"; -import { getRessourceDataApp } from "../overview/actions"; +import { getRessourceDataApp } from "../deployments/actions"; import { PodsResourceInfoModel } from "@/shared/model/pods-resource-info.model"; import { KubeSizeConverter } from "@/shared/utils/kubernetes-size-converter.utils"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; diff --git a/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/build-method-step.tsx b/src/app/project/[projectId]/apps/details/configuration/app-source-wizard/build-method-step.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/build-method-step.tsx rename to src/app/project/[projectId]/apps/details/configuration/app-source-wizard/build-method-step.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/credentials/actions.ts b/src/app/project/[projectId]/apps/details/credentials/actions.ts similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/credentials/actions.ts rename to src/app/project/[projectId]/apps/details/credentials/actions.ts diff --git a/src/app/project/[projectId]/app-components/app-detail/credentials/db-crendentials.tsx b/src/app/project/[projectId]/apps/details/credentials/db-credentials.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/credentials/db-crendentials.tsx rename to src/app/project/[projectId]/apps/details/credentials/db-credentials.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/credentials/db-gate-db-tool.tsx b/src/app/project/[projectId]/apps/details/credentials/db-gate-db-tool.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/credentials/db-gate-db-tool.tsx rename to src/app/project/[projectId]/apps/details/credentials/db-gate-db-tool.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/credentials/db-tools.tsx b/src/app/project/[projectId]/apps/details/credentials/db-tools.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/credentials/db-tools.tsx rename to src/app/project/[projectId]/apps/details/credentials/db-tools.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/credentials/phpmyadmin-db-tool.tsx b/src/app/project/[projectId]/apps/details/credentials/phpmyadmin-db-tool.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/credentials/phpmyadmin-db-tool.tsx rename to src/app/project/[projectId]/apps/details/credentials/phpmyadmin-db-tool.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/actions.ts b/src/app/project/[projectId]/apps/details/deployments/actions.ts similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/overview/actions.ts rename to src/app/project/[projectId]/apps/details/deployments/actions.ts diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-deployments-tab.tsx b/src/app/project/[projectId]/apps/details/deployments/app-details-deployments-tab.tsx similarity index 88% rename from src/app/project/[projectId]/app-components/app-drawer-components/app-details-deployments-tab.tsx rename to src/app/project/[projectId]/apps/details/deployments/app-details-deployments-tab.tsx index 72373f52..c49194f3 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-deployments-tab.tsx +++ b/src/app/project/[projectId]/apps/details/deployments/app-details-deployments-tab.tsx @@ -1,11 +1,11 @@ import { AppExtendedModel } from "@/shared/model/app-extended.model"; import { RolePermissionEnum } from "@/shared/model/role-extended.model"; -import { useNestedDrawer } from "./nested-drawer"; +import { useNestedDrawer } from "../../../../workload-components/drawer/nested-drawer"; import { DeploymentInfoModel } from "@/shared/model/deployment-info.model"; -import { BuildLogsDialogContent } from "@/app/project/workload-components/build-logs-overlay"; -import BuildsTab from "@/app/project/[projectId]/app-components/app-detail/overview/deployments"; +import { BuildLogsDialogContent } from "@/app/project/workload-components/build-logs-dialog-content"; +import BuildsTab from "@/app/project/[projectId]/apps/details/deployments/deployments"; import { formatDateTime } from "@/frontend/utils/format.utils"; -import WebhookDeploymentInfo from "@/app/project/[projectId]/app-components/app-detail/overview/webhook-deployment"; +import WebhookDeploymentInfo from "@/app/project/[projectId]/apps/details/deployments/webhook-deployment"; import { Button } from "@/components/ui/button"; import { Webhook } from "lucide-react"; diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/deployments-grid-view.tsx b/src/app/project/[projectId]/apps/details/deployments/deployments-grid-view.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/overview/deployments-grid-view.tsx rename to src/app/project/[projectId]/apps/details/deployments/deployments-grid-view.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/deployments.tsx b/src/app/project/[projectId]/apps/details/deployments/deployments.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/overview/deployments.tsx rename to src/app/project/[projectId]/apps/details/deployments/deployments.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/webhook-deployment.tsx b/src/app/project/[projectId]/apps/details/deployments/webhook-deployment.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/overview/webhook-deployment.tsx rename to src/app/project/[projectId]/apps/details/deployments/webhook-deployment.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/environment/actions.ts b/src/app/project/[projectId]/apps/details/environment/actions.ts similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/environment/actions.ts rename to src/app/project/[projectId]/apps/details/environment/actions.ts diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-environment.tsx b/src/app/project/[projectId]/apps/details/environment/app-details-environment.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-drawer-components/app-details-environment.tsx rename to src/app/project/[projectId]/apps/details/environment/app-details-environment.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/environment/env-edit.tsx b/src/app/project/[projectId]/apps/details/environment/env-edit.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/environment/env-edit.tsx rename to src/app/project/[projectId]/apps/details/environment/env-edit.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/advanced/actions.ts b/src/app/project/[projectId]/apps/details/network/policy/actions.ts similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/advanced/actions.ts rename to src/app/project/[projectId]/apps/details/network/policy/actions.ts diff --git a/src/app/project/[projectId]/app-components/app-detail/advanced/app-network-policy-rule-dialog.tsx b/src/app/project/[projectId]/apps/details/network/policy/app-network-policy-rule-dialog.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/advanced/app-network-policy-rule-dialog.tsx rename to src/app/project/[projectId]/apps/details/network/policy/app-network-policy-rule-dialog.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/advanced/app-network-policy-rule-section.tsx b/src/app/project/[projectId]/apps/details/network/policy/app-network-policy-rule-section.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/advanced/app-network-policy-rule-section.tsx rename to src/app/project/[projectId]/apps/details/network/policy/app-network-policy-rule-section.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/advanced/basic-auth-edit-dialog.tsx b/src/app/project/[projectId]/apps/details/network/policy/basic-auth-edit-dialog.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/advanced/basic-auth-edit-dialog.tsx rename to src/app/project/[projectId]/apps/details/network/policy/basic-auth-edit-dialog.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/advanced/basic-auth.tsx b/src/app/project/[projectId]/apps/details/network/policy/basic-auth.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/advanced/basic-auth.tsx rename to src/app/project/[projectId]/apps/details/network/policy/basic-auth.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/advanced/network-policy-graph.tsx b/src/app/project/[projectId]/apps/details/network/policy/network-policy-graph.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/advanced/network-policy-graph.tsx rename to src/app/project/[projectId]/apps/details/network/policy/network-policy-graph.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/advanced/network-policy.tsx b/src/app/project/[projectId]/apps/details/network/policy/network-policy.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/advanced/network-policy.tsx rename to src/app/project/[projectId]/apps/details/network/policy/network-policy.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/domains/actions.ts b/src/app/project/[projectId]/apps/details/network/ports/actions.ts similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/domains/actions.ts rename to src/app/project/[projectId]/apps/details/network/ports/actions.ts diff --git a/src/app/project/[projectId]/app-components/app-detail/domains/node-port-edit-dialog.tsx b/src/app/project/[projectId]/apps/details/network/ports/node-port-edit-dialog.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/domains/node-port-edit-dialog.tsx rename to src/app/project/[projectId]/apps/details/network/ports/node-port-edit-dialog.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/domains/node-ports.tsx b/src/app/project/[projectId]/apps/details/network/ports/node-ports-card.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/domains/node-ports.tsx rename to src/app/project/[projectId]/apps/details/network/ports/node-ports-card.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/logs.tsx b/src/app/project/[projectId]/apps/details/runtime/logs.tsx similarity index 98% rename from src/app/project/[projectId]/app-components/app-detail/overview/logs.tsx rename to src/app/project/[projectId]/apps/details/runtime/logs.tsx index 11ddc0f4..a160ef44 100644 --- a/src/app/project/[projectId]/app-components/app-detail/overview/logs.tsx +++ b/src/app/project/[projectId]/apps/details/runtime/logs.tsx @@ -2,7 +2,7 @@ import { CardContent } from "@/components/ui/card"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; import { useEffect, useState } from "react"; import LogsStreamed from "../../../../../../components/custom/logs-streamed"; -import { getPodsForApp as getPodsForAppAction } from "./actions"; +import { getPodsForApp as getPodsForAppAction } from "../deployments/actions"; import { PodsInfoModel } from "@/shared/model/pods-info.model"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import FullLoadingSpinner from "@/components/ui/full-loading-spinnter"; diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/logs.unit.spec.ts b/src/app/project/[projectId]/apps/details/runtime/logs.unit.spec.ts similarity index 97% rename from src/app/project/[projectId]/app-components/app-detail/overview/logs.unit.spec.ts rename to src/app/project/[projectId]/apps/details/runtime/logs.unit.spec.ts index 5ae4deb1..e26fdac8 100644 --- a/src/app/project/[projectId]/app-components/app-detail/overview/logs.unit.spec.ts +++ b/src/app/project/[projectId]/apps/details/runtime/logs.unit.spec.ts @@ -2,11 +2,11 @@ import React from 'react'; import { act, cleanup, render } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import Logs from './logs'; -import { getPodsForApp } from './actions'; +import { getPodsForApp } from '../deployments/actions'; import { usePodsStatus } from '@/frontend/states/zustand.states'; import { RolePermissionEnum } from '@/shared/model/role-extended.model'; -vi.mock('./actions', () => ({ +vi.mock('../deployments/actions', () => ({ getPodsForApp: vi.fn(), })); diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.tsx b/src/app/project/[projectId]/apps/details/runtime/monitoring-app.tsx similarity index 98% rename from src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.tsx rename to src/app/project/[projectId]/apps/details/runtime/monitoring-app.tsx index 93eea6ba..52daa810 100644 --- a/src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.tsx +++ b/src/app/project/[projectId]/apps/details/runtime/monitoring-app.tsx @@ -1,7 +1,7 @@ import { CardContent } from "@/components/ui/card"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; import { useCallback, useState } from "react"; -import { getRessourceDataApp } from "./actions"; +import { getRessourceDataApp } from "../deployments/actions"; import FullLoadingSpinner from "@/components/ui/full-loading-spinnter"; import { usePolling } from "@/frontend/hooks/use-polling"; import { PodsResourceInfoModel } from "@/shared/model/pods-resource-info.model"; diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.unit.spec.ts b/src/app/project/[projectId]/apps/details/runtime/monitoring-app.unit.spec.ts similarity index 96% rename from src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.unit.spec.ts rename to src/app/project/[projectId]/apps/details/runtime/monitoring-app.unit.spec.ts index d56ba0f3..f4fa36fd 100644 --- a/src/app/project/[projectId]/app-components/app-detail/overview/monitoring-app.unit.spec.ts +++ b/src/app/project/[projectId]/apps/details/runtime/monitoring-app.unit.spec.ts @@ -2,9 +2,9 @@ import React from 'react'; import { act, cleanup, render } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import MonitoringTab from './monitoring-app'; -import { getRessourceDataApp } from './actions'; +import { getRessourceDataApp } from '../deployments/actions'; -vi.mock('./actions', () => ({ +vi.mock('../deployments/actions', () => ({ getRessourceDataApp: vi.fn(), })); diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/terminal-overlay.tsx b/src/app/project/[projectId]/apps/details/runtime/terminal-overlay.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/overview/terminal-overlay.tsx rename to src/app/project/[projectId]/apps/details/runtime/terminal-overlay.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/overview/terminal-streamed.tsx b/src/app/project/[projectId]/apps/details/runtime/terminal-streamed.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/overview/terminal-streamed.tsx rename to src/app/project/[projectId]/apps/details/runtime/terminal-streamed.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/app-source-wizard-dialog.tsx b/src/app/project/[projectId]/apps/details/source/app-source-wizard/app-source-wizard-dialog.tsx similarity index 99% rename from src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/app-source-wizard-dialog.tsx rename to src/app/project/[projectId]/apps/details/source/app-source-wizard/app-source-wizard-dialog.tsx index 97a3b7e2..13262228 100644 --- a/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/app-source-wizard-dialog.tsx +++ b/src/app/project/[projectId]/apps/details/source/app-source-wizard/app-source-wizard-dialog.tsx @@ -13,8 +13,8 @@ import { ChevronLeft, Loader2, Rocket, Save } from "lucide-react"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { toast } from "sonner"; -import { detectDockerfilePath, ensureGitSshPublicKey, generateOrRegenerateGitSshKey, getGitBranches, saveGeneralAppSourceInfo } from "../actions"; -import { BuildMethodStep } from "./build-method-step"; +import { detectDockerfilePath, ensureGitSshPublicKey, generateOrRegenerateGitSshKey, getGitBranches, saveGeneralAppSourceInfo } from "../../configuration/actions"; +import { BuildMethodStep } from "../../configuration/app-source-wizard/build-method-step"; import { ContainerImageStep } from "../../../../../workload-components/source-wizard/container-image-step"; import { DockerfilePathStep } from "../../../../../workload-components/source-wizard/dockerfile-path-step"; import { FrameworkConfigurationStep } from "./framework-configuration-step"; diff --git a/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-configuration-step.tsx b/src/app/project/[projectId]/apps/details/source/app-source-wizard/framework-configuration-step.tsx similarity index 97% rename from src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-configuration-step.tsx rename to src/app/project/[projectId]/apps/details/source/app-source-wizard/framework-configuration-step.tsx index 19519df1..79cf9a0c 100644 --- a/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-configuration-step.tsx +++ b/src/app/project/[projectId]/apps/details/source/app-source-wizard/framework-configuration-step.tsx @@ -3,7 +3,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/component import { JsFramework, jsFrameworkPresets } from "@/shared/model/js-framework.model"; import { ChevronDown, Cpu, Download, FolderOutput, FolderTree, Hammer, Play } from "lucide-react"; import Image from "next/image"; -import { IconInput } from "./source-wizard-fields"; +import { IconInput } from '@/app/project/workload-components/source-wizard/fields/icon-input'; import { SourceWizardInput } from "../../../../../workload-components/source-wizard/types"; export function FrameworkConfigurationStep({ framework, formData, onChange, onChangeFramework }: { diff --git a/src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-step.tsx b/src/app/project/[projectId]/apps/details/source/app-source-wizard/framework-step.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/general/app-source-wizard/framework-step.tsx rename to src/app/project/[projectId]/apps/details/source/app-source-wizard/framework-step.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/general/app-source.tsx b/src/app/project/[projectId]/apps/details/source/app-source.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/general/app-source.tsx rename to src/app/project/[projectId]/apps/details/source/app-source.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/general/framework-configuration-card.tsx b/src/app/project/[projectId]/apps/details/source/framework-configuration-card.tsx similarity index 98% rename from src/app/project/[projectId]/app-components/app-detail/general/framework-configuration-card.tsx rename to src/app/project/[projectId]/apps/details/source/framework-configuration-card.tsx index 552dd3c2..fee08167 100644 --- a/src/app/project/[projectId]/app-components/app-detail/general/framework-configuration-card.tsx +++ b/src/app/project/[projectId]/apps/details/source/framework-configuration-card.tsx @@ -14,7 +14,7 @@ import { useActionState, useEffect } from 'react'; import { Control, useForm } from 'react-hook-form'; import { toast } from 'sonner'; import { z } from 'zod'; -import { saveFrameworkConfiguration } from './actions'; +import { saveFrameworkConfiguration } from '../configuration/actions'; import { JsFramework, jsFrameworkPresets } from '@/shared/model/js-framework.model'; export default function FrameworkConfigurationCard({ app, readonly }: { app: AppExtendedModel; readonly: boolean }) { diff --git a/src/app/project/[projectId]/app-components/app-detail/volumes/actions.ts b/src/app/project/[projectId]/apps/details/volumes/actions.ts similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/volumes/actions.ts rename to src/app/project/[projectId]/apps/details/volumes/actions.ts diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backup-list.tsx b/src/app/project/[projectId]/apps/details/volumes/backups/app-details-backup-list.tsx similarity index 98% rename from src/app/project/[projectId]/app-components/app-drawer-components/app-details-backup-list.tsx rename to src/app/project/[projectId]/apps/details/volumes/backups/app-details-backup-list.tsx index e4e67d1f..8b30824f 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backup-list.tsx +++ b/src/app/project/[projectId]/apps/details/volumes/backups/app-details-backup-list.tsx @@ -16,7 +16,7 @@ import type { VolumeBackupExtendedModel } from '@/shared/model/volume-backup-ext import { downloadBackupForVolumeSchedule, getBackupsForVolumeSchedule, -} from '@/app/project/[projectId]/app-components/app-detail/volumes/actions'; +} from '@/app/project/[projectId]/apps/details/volumes/actions'; export function DrawerBackupList({ volumeBackup, diff --git a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx b/src/app/project/[projectId]/apps/details/volumes/backups/app-details-backups-tab.tsx similarity index 97% rename from src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx rename to src/app/project/[projectId]/apps/details/volumes/backups/app-details-backups-tab.tsx index a01d6297..b4b146d5 100644 --- a/src/app/project/[projectId]/app-components/app-drawer-components/app-details-backups-tab.tsx +++ b/src/app/project/[projectId]/apps/details/volumes/backups/app-details-backups-tab.tsx @@ -11,12 +11,12 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { Button } from '@/components/ui/button'; import { SplitButton } from '@/components/custom/split-button'; -import VolumeBackupList from '@/app/project/[projectId]/app-components/app-detail/volumes/volume-backup'; +import VolumeBackupList from '@/app/project/[projectId]/apps/details/volumes/backups/volume-backup'; import { downloadBackupForVolumeSchedule, getBackupsForVolumeSchedule, runBackupVolumeSchedule, -} from '@/app/project/[projectId]/app-components/app-detail/volumes/actions'; +} from '@/app/project/[projectId]/apps/details/volumes/actions'; import { formatDateTime } from '@/frontend/utils/format.utils'; import { Actions } from '@/frontend/utils/nextjs-actions.utils'; import { Toast } from '@/frontend/utils/toast.utils'; @@ -26,7 +26,7 @@ import type { AppExtendedModel } from '@/shared/model/app-extended.model'; import type { BackupEntry } from '@/shared/model/backup-info.model'; import { RolePermissionEnum } from '@/shared/model/role-extended.model'; import type { VolumeBackupExtendedModel } from '@/shared/model/volume-backup-extended.model'; -import { useNestedDrawer } from './nested-drawer'; +import { useNestedDrawer } from '../../../../../workload-components/drawer/nested-drawer'; type BackupRow = BackupEntry & { schedule: VolumeBackupExtendedModel }; diff --git a/src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup-edit-overlay.tsx b/src/app/project/[projectId]/apps/details/volumes/backups/volume-backup-edit-overlay.tsx similarity index 99% rename from src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup-edit-overlay.tsx rename to src/app/project/[projectId]/apps/details/volumes/backups/volume-backup-edit-overlay.tsx index 2f18a4bb..053b2b21 100644 --- a/src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup-edit-overlay.tsx +++ b/src/app/project/[projectId]/apps/details/volumes/backups/volume-backup-edit-overlay.tsx @@ -20,7 +20,7 @@ import { FormUtils } from "@/frontend/utils/form.utilts"; import { SubmitButton } from "@/components/custom/submit-button"; import { AppVolume, S3Target, VolumeBackup } from "@prisma/client" import { ServerActionResult } from "@/shared/model/server-action-error-return.model" -import { saveBackupVolume } from "./actions" +import { saveBackupVolume } from "../actions" import { toast } from "sonner" import { VolumeBackupEditModel, volumeBackupEditZodModel } from "@/shared/model/backup-volume-edit.model" import SelectFormField from "@/components/custom/select-form-field" diff --git a/src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup.tsx b/src/app/project/[projectId]/apps/details/volumes/backups/volume-backup.tsx similarity index 99% rename from src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup.tsx rename to src/app/project/[projectId]/apps/details/volumes/backups/volume-backup.tsx index 542cdc97..cf4c74d4 100644 --- a/src/app/project/[projectId]/app-components/app-detail/volumes/volume-backup.tsx +++ b/src/app/project/[projectId]/apps/details/volumes/backups/volume-backup.tsx @@ -6,7 +6,7 @@ import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, Tabl import { Button } from "@/components/ui/button"; import { EditIcon, List, Play, Plus, TrashIcon } from "lucide-react"; import { Toast } from "@/frontend/utils/toast.utils"; -import { deleteBackupVolume, runBackupVolumeSchedule } from "./actions"; +import { deleteBackupVolume, runBackupVolumeSchedule } from "../actions"; import { useConfirmDialog, useDialog } from "@/frontend/states/zustand.states"; import { S3Target } from "@prisma/client"; import React from "react"; diff --git a/src/app/project/[projectId]/app-components/app-detail/volumes/shared-storage-edit-overlay.tsx b/src/app/project/[projectId]/apps/details/volumes/shared-storage-edit-overlay.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/volumes/shared-storage-edit-overlay.tsx rename to src/app/project/[projectId]/apps/details/volumes/shared-storage-edit-overlay.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/volumes/storage-edit-overlay.tsx b/src/app/project/[projectId]/apps/details/volumes/storage-edit-overlay.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/volumes/storage-edit-overlay.tsx rename to src/app/project/[projectId]/apps/details/volumes/storage-edit-overlay.tsx diff --git a/src/app/project/[projectId]/app-components/app-detail/volumes/storages.tsx b/src/app/project/[projectId]/apps/details/volumes/volumes-list.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/app-detail/volumes/storages.tsx rename to src/app/project/[projectId]/apps/details/volumes/volumes-list.tsx diff --git a/src/app/project/[projectId]/choose-template-dialog.tsx b/src/app/project/[projectId]/choose-template-dialog.tsx index a3249e1f..c736f2ff 100644 --- a/src/app/project/[projectId]/choose-template-dialog.tsx +++ b/src/app/project/[projectId]/choose-template-dialog.tsx @@ -4,8 +4,8 @@ import { DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/di import { useEffect, useState } from "react"; import { AppTemplateModel } from "@/shared/model/app-template.model" import { appTemplates, databaseTemplates } from "@/shared/templates/all.templates" -import CreateTemplateAppSetupDialog from "./app-components/create-template-app-setup-dialog" -import CreateTemplateAgentSetupDialog from "./agent-components/create-template-agent-setup-dialog" +import CreateTemplateAppSetupDialog from "./apps/create/create-template-app-setup-dialog" +import CreateTemplateAgentSetupDialog from "./agents/create/create-template-agent-setup-dialog" import { ScrollArea } from "@/components/ui/scroll-area"; import { Input } from "@/components/ui/input"; import { ArrowUpRight, Blocks, Bot, Database, Search } from "lucide-react"; diff --git a/src/app/project/[projectId]/create-project-actions.tsx b/src/app/project/[projectId]/create-project-actions.tsx index 00c06691..20647aed 100644 --- a/src/app/project/[projectId]/create-project-actions.tsx +++ b/src/app/project/[projectId]/create-project-actions.tsx @@ -2,7 +2,7 @@ import { Button } from "@/components/ui/button"; -import { EditAppDialog } from "./app-components/edit-app-dialog"; +import { EditAppDialog } from "./apps/create/edit-app-dialog"; import { Blocks, Bot, Database, File, Plus } from "lucide-react"; import ChooseTemplateDialog from "./choose-template-dialog"; import { @@ -14,7 +14,7 @@ import { DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" -import { CreateAgentDialog } from "./agent-components/create-agent-dialog"; +import { CreateAgentDialog } from "./agents/create/create-agent-dialog"; import { useDialog } from "@/frontend/states/zustand.states"; diff --git a/src/app/project/[projectId]/overview/network-graph/connection-edge.tsx b/src/app/project/[projectId]/overview/network-graph/connection-edge.tsx new file mode 100644 index 00000000..4421f5d9 --- /dev/null +++ b/src/app/project/[projectId]/overview/network-graph/connection-edge.tsx @@ -0,0 +1,23 @@ +'use client'; + +import { SmoothStepEdge, type EdgeProps } from '@xyflow/react'; +import { ProjectNetworkGraphConnectionContextMenu } from './context-menus/project-network-graph-connection-context-menu'; + +type ConnectionEdgeData = { + onDelete?: () => void; + internalHostnames: { hostname: string; port: number }[]; +}; + +export function ConnectionEdge(props: EdgeProps) { + const data = props.data as ConnectionEdgeData | undefined; + if (!data) return ; + return ( + + + + ); +} + diff --git a/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-agent-context-menu.tsx b/src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-agent-context-menu.tsx similarity index 97% rename from src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-agent-context-menu.tsx rename to src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-agent-context-menu.tsx index 345ac8ff..8621a278 100644 --- a/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-agent-context-menu.tsx +++ b/src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-agent-context-menu.tsx @@ -11,7 +11,7 @@ import { ContextMenuTrigger, } from '@/components/ui/context-menu'; import { Toast } from '@/frontend/utils/toast.utils'; -import { saveAgentNetworkPolicySettings } from '@/app/project/[projectId]/agent-components/agent-drawer-components/configuration/actions'; +import { saveAgentNetworkPolicySettings } from '@/app/project/[projectId]/agents/details/configuration/actions'; import type { AgentExtendedModel } from '@/shared/model/agent-extended.model'; export type ProjectNetworkGraphAgentContextMenuProps = { diff --git a/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-app-context-menu.tsx b/src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-app-context-menu.tsx similarity index 99% rename from src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-app-context-menu.tsx rename to src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-app-context-menu.tsx index 0810599b..a0dbc2e3 100644 --- a/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-app-context-menu.tsx +++ b/src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-app-context-menu.tsx @@ -12,7 +12,7 @@ import { ContextMenuSubTrigger, ContextMenuTrigger, } from '@/components/ui/context-menu'; -import { deploy, startApp, stopApp } from '@/app/project/[projectId]/app-components/app-detail/actions'; +import { deploy, startApp, stopApp } from '@/app/project/[projectId]/apps/details/actions'; import { usePodsStatus } from '@/frontend/states/zustand.states'; import { AppLifecycleUtils } from '@/frontend/utils/app-lifecycle.utils'; import { Toast } from '@/frontend/utils/toast.utils'; diff --git a/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-canvas-context-menu.tsx b/src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-canvas-context-menu.tsx similarity index 96% rename from src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-canvas-context-menu.tsx rename to src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-canvas-context-menu.tsx index 87958896..58b0297d 100644 --- a/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-canvas-context-menu.tsx +++ b/src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-canvas-context-menu.tsx @@ -12,8 +12,8 @@ import { ContextMenuTrigger, } from '@/components/ui/context-menu'; import { useDialog } from '@/frontend/states/zustand.states'; -import { EditAppDialog } from '@/app/project/[projectId]/app-components/edit-app-dialog'; -import { CreateAgentDialog } from '@/app/project/[projectId]/agent-components/create-agent-dialog'; +import { EditAppDialog } from '@/app/project/[projectId]/apps/create/edit-app-dialog'; +import { CreateAgentDialog } from '@/app/project/[projectId]/agents/create/create-agent-dialog'; import ChooseTemplateDialog from '@/app/project/[projectId]/choose-template-dialog'; type ProjectNetworkGraphCanvasContextMenuProps = { diff --git a/src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-connection-context-menu.tsx b/src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-connection-context-menu.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/context-menus/project-network-graph-connection-context-menu.tsx rename to src/app/project/[projectId]/overview/network-graph/context-menus/project-network-graph-connection-context-menu.tsx diff --git a/src/app/project/[projectId]/overview/network-graph/graph-legend.tsx b/src/app/project/[projectId]/overview/network-graph/graph-legend.tsx new file mode 100644 index 00000000..5afa1fb2 --- /dev/null +++ b/src/app/project/[projectId]/overview/network-graph/graph-legend.tsx @@ -0,0 +1,25 @@ +'use client'; + +import { cn } from '@/frontend/utils/utils'; +import { graphLegendItems, NETWORK_GRAPH_COLORS } from './project-network-graph-visual-semantics'; + +export function GraphLegend() { + return ( +
+ {graphLegendItems.map(item => ( + + + {item.label} + + ))} + Arrows show allowed traffic direction +
+ ); +} + diff --git a/src/app/project/[projectId]/app-components/project-network-graph/nodes/project-network-graph-agent-workload-node.tsx b/src/app/project/[projectId]/overview/network-graph/nodes/project-network-graph-agent-workload-node.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/nodes/project-network-graph-agent-workload-node.tsx rename to src/app/project/[projectId]/overview/network-graph/nodes/project-network-graph-agent-workload-node.tsx diff --git a/src/app/project/[projectId]/app-components/project-network-graph/nodes/project-network-graph-app-workload-node.tsx b/src/app/project/[projectId]/overview/network-graph/nodes/project-network-graph-app-workload-node.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/nodes/project-network-graph-app-workload-node.tsx rename to src/app/project/[projectId]/overview/network-graph/nodes/project-network-graph-app-workload-node.tsx diff --git a/src/app/project/[projectId]/app-components/project-network-graph/nodes/project-network-graph-internet-node.tsx b/src/app/project/[projectId]/overview/network-graph/nodes/project-network-graph-internet-node.tsx similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/nodes/project-network-graph-internet-node.tsx rename to src/app/project/[projectId]/overview/network-graph/nodes/project-network-graph-internet-node.tsx diff --git a/src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-drawer-session.spec.ts b/src/app/project/[projectId]/overview/network-graph/project-network-graph-drawer-session.spec.ts similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-drawer-session.spec.ts rename to src/app/project/[projectId]/overview/network-graph/project-network-graph-drawer-session.spec.ts diff --git a/src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-drawer-session.ts b/src/app/project/[projectId]/overview/network-graph/project-network-graph-drawer-session.ts similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-drawer-session.ts rename to src/app/project/[projectId]/overview/network-graph/project-network-graph-drawer-session.ts diff --git a/src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-focus.spec.ts b/src/app/project/[projectId]/overview/network-graph/project-network-graph-focus.spec.ts similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-focus.spec.ts rename to src/app/project/[projectId]/overview/network-graph/project-network-graph-focus.spec.ts diff --git a/src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-focus.ts b/src/app/project/[projectId]/overview/network-graph/project-network-graph-focus.ts similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-focus.ts rename to src/app/project/[projectId]/overview/network-graph/project-network-graph-focus.ts diff --git a/src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-projection.ts b/src/app/project/[projectId]/overview/network-graph/project-network-graph-projection.ts similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-projection.ts rename to src/app/project/[projectId]/overview/network-graph/project-network-graph-projection.ts diff --git a/src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-visual-semantics.spec.ts b/src/app/project/[projectId]/overview/network-graph/project-network-graph-visual-semantics.spec.ts similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-visual-semantics.spec.ts rename to src/app/project/[projectId]/overview/network-graph/project-network-graph-visual-semantics.spec.ts diff --git a/src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-visual-semantics.ts b/src/app/project/[projectId]/overview/network-graph/project-network-graph-visual-semantics.ts similarity index 100% rename from src/app/project/[projectId]/app-components/project-network-graph/project-network-graph-visual-semantics.ts rename to src/app/project/[projectId]/overview/network-graph/project-network-graph-visual-semantics.ts diff --git a/src/app/project/[projectId]/app-components/project-network-graph.spec.ts b/src/app/project/[projectId]/overview/network-graph/project-network-graph.spec.ts similarity index 97% rename from src/app/project/[projectId]/app-components/project-network-graph.spec.ts rename to src/app/project/[projectId]/overview/network-graph/project-network-graph.spec.ts index c5d29f43..312eb7c0 100644 --- a/src/app/project/[projectId]/app-components/project-network-graph.spec.ts +++ b/src/app/project/[projectId]/overview/network-graph/project-network-graph.spec.ts @@ -1,7 +1,7 @@ import type { AppExtendedModel } from '@/shared/model/app-extended.model'; -import { buildProjectNetworkGraph, connectionDeletionProvenance } from './project-network-graph/project-network-graph-projection'; -import { graphEdgePresentation } from './project-network-graph/project-network-graph-visual-semantics'; +import { buildProjectNetworkGraph, connectionDeletionProvenance } from './project-network-graph-projection'; +import { graphEdgePresentation } from './project-network-graph-visual-semantics'; function app(overrides: Record = {}): AppExtendedModel { return { diff --git a/src/app/project/[projectId]/app-components/project-network-graph.tsx b/src/app/project/[projectId]/overview/network-graph/project-network-graph.tsx similarity index 90% rename from src/app/project/[projectId]/app-components/project-network-graph.tsx rename to src/app/project/[projectId]/overview/network-graph/project-network-graph.tsx index 65506e77..46b47088 100644 --- a/src/app/project/[projectId]/app-components/project-network-graph.tsx +++ b/src/app/project/[projectId]/overview/network-graph/project-network-graph.tsx @@ -1,6 +1,8 @@ 'use client'; import type { CSSProperties } from 'react'; +import { ConnectionEdge } from './connection-edge'; +import { GraphLegend } from './graph-legend'; import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { @@ -9,11 +11,9 @@ import { Controls, MarkerType, ReactFlow, - SmoothStepEdge, useNodesState, type Node, type NodeTypes, - type EdgeProps, type Connection, type ReactFlowInstance, } from '@xyflow/react'; @@ -22,25 +22,23 @@ import { Cloud, Info, RotateCcw } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardFooter } from '@/components/ui/card'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; -import { cn } from '@/frontend/utils/utils'; import type { AppExtendedModel } from '@/shared/model/app-extended.model'; import type { AgentExtendedModel } from '@/shared/model/agent-extended.model'; import type { UserSession } from '@/shared/model/sim-session.model'; import { UserGroupUtils } from '@/shared/utils/role.utils'; import { RolePermissionEnum } from '@/shared/model/role-extended.model'; -import { ProjectNetworkGraphConnectionContextMenu } from './project-network-graph/context-menus/project-network-graph-connection-context-menu'; -import { connectionDeletionProvenance, type NetworkGraphEdge, NetworkGraphNode } from './project-network-graph/project-network-graph-projection'; -import { useProjectNetworkGraph } from './project-network-graph/use-project-network-graph'; -import { graphEdgePresentation, graphLegendItems, NETWORK_GRAPH_COLORS } from './project-network-graph/project-network-graph-visual-semantics'; -import { getFocusedNodeViewport } from './project-network-graph/project-network-graph-focus'; +import { connectionDeletionProvenance, type NetworkGraphEdge, NetworkGraphNode } from './project-network-graph-projection'; +import { useProjectNetworkGraph } from './use-project-network-graph'; +import { graphEdgePresentation } from './project-network-graph-visual-semantics'; +import { getFocusedNodeViewport } from './project-network-graph-focus'; import { useConfirmDialog, useDialog } from '@/frontend/states/zustand.states'; import { Toast } from '@/frontend/utils/toast.utils'; import { AppNetworkPolicyRuleEditModel, NetworkPolicySelectableTarget } from '@/shared/model/app-network-policy-edit.model'; import { NetworkPolicyRuleUtils } from '@/shared/utils/network-policy-rule.utils'; import { AppNetworkPolicyDraft, AppNetworkPolicyDraftUtils } from '@/shared/utils/app-network-policy-draft.utils'; import { InternalHostnameUtils } from '@/shared/utils/internal-hostname.utils'; -import AppNetworkPolicyRuleDialog from '@/app/project/[projectId]/app-components/app-detail/advanced/app-network-policy-rule-dialog'; -import { saveAppNetworkPolicyConfiguration } from '@/app/project/[projectId]/app-components/app-detail/advanced/actions'; +import AppNetworkPolicyRuleDialog from '@/app/project/[projectId]/apps/details/network/policy/app-network-policy-rule-dialog'; +import { saveAppNetworkPolicyConfiguration } from '@/app/project/[projectId]/apps/details/network/policy/actions'; import { deleteApp } from '@/app/project/[projectId]/actions'; import type { ProjectNetworkGraphPositions } from '@/shared/model/project-network-graph-layout.model'; import type { S3Target } from '@prisma/client'; @@ -48,21 +46,16 @@ import type { VolumeBackupExtendedModel } from '@/shared/model/volume-backup-ext import { type DrawerTab, useProjectNetworkGraphDrawerSession, -} from './project-network-graph/project-network-graph-drawer-session'; -import { AppDetailsDrawer } from './app-drawer-components/app-details-drawer'; -import { AgentSandboxDrawer } from '@/app/project/[projectId]/agent-components/agent-drawer-components/agent-sandbox-drawer'; -import { deleteAgent } from '@/app/project/[projectId]/agent-components/agent-drawer-components/agent-actions'; +} from './project-network-graph-drawer-session'; +import { AppDetailsDrawer } from '../../apps/details/app-details-drawer'; +import { AgentSandboxDrawer } from '@/app/project/[projectId]/agents/details/agent-drawer'; +import { deleteAgent } from '@/app/project/[projectId]/agents/details/agent-actions'; import type { AgentSandboxTemplateInfo } from '@/shared/model/agent-sandbox-template-info.model'; import type { AgentDrawerTab } from '@/shared/utils/agent-drawer-navigation.utils'; -import { ProjectNetworkGraphCanvasContextMenu } from './project-network-graph/context-menus/project-network-graph-canvas-context-menu'; -import { ProjectNetworkGraphAppWorkloadNode } from './project-network-graph/nodes/project-network-graph-app-workload-node'; -import { ProjectNetworkGraphAgentWorkloadNode } from './project-network-graph/nodes/project-network-graph-agent-workload-node'; -import { ProjectNetworkGraphInternetNode } from './project-network-graph/nodes/project-network-graph-internet-node'; - -type ConnectionEdgeData = { - onDelete?: () => void; - internalHostnames: { hostname: string; port: number }[]; -}; +import { ProjectNetworkGraphCanvasContextMenu } from './context-menus/project-network-graph-canvas-context-menu'; +import { ProjectNetworkGraphAppWorkloadNode } from './nodes/project-network-graph-app-workload-node'; +import { ProjectNetworkGraphAgentWorkloadNode } from './nodes/project-network-graph-agent-workload-node'; +import { ProjectNetworkGraphInternetNode } from './nodes/project-network-graph-internet-node'; type ProjectNetworkGraphProps = { apps: AppExtendedModel[]; @@ -91,19 +84,6 @@ const nodeTypes = { internet: ProjectNetworkGraphInternetNode, } satisfies NodeTypes; -function ConnectionEdge(props: EdgeProps) { - const data = props.data as ConnectionEdgeData | undefined; - if (!data) return ; - return ( - - - - ); -} - function getInternalHostnames( edge: NetworkGraphEdge, drafts: Record, @@ -130,26 +110,6 @@ function getInternalHostnames( } const edgeTypes = { connection: ConnectionEdge }; -function Legend() { - return ( -
- {graphLegendItems.map(item => ( - - - {item.label} - - ))} - Arrows show allowed traffic direction -
- ); -} - export default function ProjectNetworkGraph(props: ProjectNetworkGraphProps) { return } /> - + {canEditLayout && + + + ); +} + diff --git a/src/app/project/workload-components/source-wizard/fields/icon-input.tsx b/src/app/project/workload-components/source-wizard/fields/icon-input.tsx new file mode 100644 index 00000000..a6aba94c --- /dev/null +++ b/src/app/project/workload-components/source-wizard/fields/icon-input.tsx @@ -0,0 +1,18 @@ +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { cn } from "@/frontend/utils/utils"; +import { type LucideIcon } from "lucide-react"; +import { type ComponentProps } from "react"; + +export function IconInput({ icon: Icon, label, className, ...props }: ComponentProps & { icon: LucideIcon; label: string }) { + return ( +
+ +
+ + +
+
+ ); +} + diff --git a/src/app/project/workload-components/source-wizard/fields/secret-input.tsx b/src/app/project/workload-components/source-wizard/fields/secret-input.tsx new file mode 100644 index 00000000..faaff6a7 --- /dev/null +++ b/src/app/project/workload-components/source-wizard/fields/secret-input.tsx @@ -0,0 +1,40 @@ +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Eye, EyeOff, type LucideIcon } from "lucide-react"; + +export function SecretInput({ icon: Icon, label, value, visible, onVisibleChange, onChange }: { + icon: LucideIcon; + label: string; + value: string; + visible: boolean; + onVisibleChange: (visible: boolean) => void; + onChange: (value: string) => void; +}) { + const EyeIcon = visible ? EyeOff : Eye; + return ( +
+ +
+ + onChange(event.target.value)} + /> + +
+
+ ); +} + diff --git a/src/app/project/workload-components/source-wizard/fields/secret-summary-row.tsx b/src/app/project/workload-components/source-wizard/fields/secret-summary-row.tsx new file mode 100644 index 00000000..2ac784e5 --- /dev/null +++ b/src/app/project/workload-components/source-wizard/fields/secret-summary-row.tsx @@ -0,0 +1,26 @@ +import { Button } from "@/components/ui/button"; +import { Eye, EyeOff } from "lucide-react"; + +export function SecretSummaryRow({ label, value, visible, onVisibleChange }: { + label: string; + value: string; + visible: boolean; + onVisibleChange: (visible: boolean) => void; +}) { + const EyeIcon = visible ? EyeOff : Eye; + const displayValue = value ? (visible ? value : '************') : 'Not configured'; + return ( +
+

{label}

+
+

{displayValue}

+ {!!value && ( + + )} +
+
+ ); +} diff --git a/src/app/project/workload-components/source-wizard/fields/summary-row.tsx b/src/app/project/workload-components/source-wizard/fields/summary-row.tsx new file mode 100644 index 00000000..acc76d26 --- /dev/null +++ b/src/app/project/workload-components/source-wizard/fields/summary-row.tsx @@ -0,0 +1,11 @@ +import { cn } from "@/frontend/utils/utils"; + +export function SummaryRow({ label, value, mono }: { label: string; value: string; mono?: boolean }) { + return ( +
+

{label}

+

{value || 'Not configured'}

+
+ ); +} + diff --git a/src/app/project/workload-components/source-wizard/git-https-url-step.tsx b/src/app/project/workload-components/source-wizard/git-https-url-step.tsx index f17f83fb..cefe7408 100644 --- a/src/app/project/workload-components/source-wizard/git-https-url-step.tsx +++ b/src/app/project/workload-components/source-wizard/git-https-url-step.tsx @@ -1,5 +1,7 @@ import { Link as LinkIcon, LockKeyhole, User } from "lucide-react"; -import { BranchLoadingState, IconInput, SecretInput } from "../../[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields"; +import { BranchLoadingState } from '@/app/project/workload-components/source-wizard/fields/branch-loading-state'; +import { IconInput } from '@/app/project/workload-components/source-wizard/fields/icon-input'; +import { SecretInput } from '@/app/project/workload-components/source-wizard/fields/secret-input'; import { SourceFormPatch, SourceWizardInput } from "./types"; export function GitHttpsUrlStep({ formData, showCredentials, showToken, setShowToken, onChange, isLoadingBranches, branchError, onRetry }: { diff --git a/src/app/project/workload-components/source-wizard/git-ssh-url-step.tsx b/src/app/project/workload-components/source-wizard/git-ssh-url-step.tsx index d95373e7..705dd182 100644 --- a/src/app/project/workload-components/source-wizard/git-ssh-url-step.tsx +++ b/src/app/project/workload-components/source-wizard/git-ssh-url-step.tsx @@ -3,7 +3,8 @@ import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Check, ClipboardCopy, KeyRound, Link as LinkIcon, RefreshCw } from "lucide-react"; -import { BranchLoadingState, IconInput } from "../../[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields"; +import { BranchLoadingState } from '@/app/project/workload-components/source-wizard/fields/branch-loading-state'; +import { IconInput } from '@/app/project/workload-components/source-wizard/fields/icon-input'; import { SourceFormPatch, SourceWizardInput } from "./types"; export function GitSshUrlStep({ formData, publicKey, isEnsuringKey, isLoadingBranches, branchError, onChange, onCopy, onRegenerate, onRetry }: { diff --git a/src/app/project/workload-components/source-wizard/source-summary-step.tsx b/src/app/project/workload-components/source-wizard/source-summary-step.tsx index d8194812..6c255e53 100644 --- a/src/app/project/workload-components/source-wizard/source-summary-step.tsx +++ b/src/app/project/workload-components/source-wizard/source-summary-step.tsx @@ -1,6 +1,7 @@ import { AppBuildMethod } from "@/shared/model/app-source-info.model"; import { JsFramework, jsFrameworkPresets } from "@/shared/model/js-framework.model"; -import { SecretSummaryRow, SummaryRow } from "../../[projectId]/app-components/app-detail/general/app-source-wizard/source-wizard-fields"; +import { SecretSummaryRow } from '@/app/project/workload-components/source-wizard/fields/secret-summary-row'; +import { SummaryRow } from '@/app/project/workload-components/source-wizard/fields/summary-row'; import { buildMethodLabels, defaultDockerfilePath, SourceType, sourceTypeLabels, SourceWizardInput } from "./types"; export function SourceSummaryStep({ formData, publicKey, showGitToken, setShowGitToken, showRegistryPassword, setShowRegistryPassword }: { diff --git a/src/app/settings/integrations/llm-gateways/llm-gateway-edit-overlay.tsx b/src/app/settings/integrations/llm-gateways/llm-gateway-edit-overlay.tsx index 600801ca..ac260ae3 100644 --- a/src/app/settings/integrations/llm-gateways/llm-gateway-edit-overlay.tsx +++ b/src/app/settings/integrations/llm-gateways/llm-gateway-edit-overlay.tsx @@ -35,7 +35,7 @@ import { import { Label } from "@/components/ui/label"; import { CloudUpload, FilePenLine } from "lucide-react"; import { ProjectExtendedModel } from "@/shared/model/project-extended.model"; -import { deploy } from "@/app/project/[projectId]/app-components/app-detail/actions"; +import { deploy } from "@/app/project/[projectId]/apps/details/actions"; function LlmGatewayForm({ gateway }: { gateway?: LlmGatewayModel }) { const { closeDialog } = useDialogContext(); diff --git a/src/components/custom/build-status-indicator.tsx b/src/components/custom/build-status-indicator.tsx index 704523e0..eac58add 100644 --- a/src/components/custom/build-status-indicator.tsx +++ b/src/components/custom/build-status-indicator.tsx @@ -5,7 +5,7 @@ import { CircleAlert } from 'lucide-react'; import { Spinner } from '@/components/ui/spinner'; import { cn } from '@/frontend/utils/utils'; import { useDialog } from '@/frontend/states/zustand.states'; -import { BuildLogsDialogContent } from '@/app/project/workload-components/build-logs-overlay'; +import { BuildLogsDialogContent } from '@/app/project/workload-components/build-logs-dialog-content'; import { Tooltip, TooltipContent, diff --git a/src/components/custom/build-status-indicator.unit.spec.ts b/src/components/custom/build-status-indicator.unit.spec.ts index dee7f667..f82f9772 100644 --- a/src/components/custom/build-status-indicator.unit.spec.ts +++ b/src/components/custom/build-status-indicator.unit.spec.ts @@ -12,7 +12,7 @@ vi.mock('@/components/ui/tooltip', () => ({ TooltipContent: ({ children }: { children: React.ReactNode }) => children, })); -vi.mock('@/app/project/workload-components/build-logs-overlay', () => ({ +vi.mock('@/app/project/workload-components/build-logs-dialog-content', () => ({ BuildLogsDialogContent: () => null, })); diff --git a/src/components/custom/workload-builds-table.tsx b/src/components/custom/workload-builds-table.tsx index 291422c5..13c05b52 100644 --- a/src/components/custom/workload-builds-table.tsx +++ b/src/components/custom/workload-builds-table.tsx @@ -10,7 +10,7 @@ import FullLoadingSpinner from "@/components/ui/full-loading-spinnter"; import { SimpleDataTable } from "@/components/custom/simple-data-table"; import ShortCommitHash from "@/components/custom/short-commit-hash"; import BuildStatusBadge from "@/app/builds/build-status-badge"; -import { BuildLogsDialogContent } from "@/app/project/workload-components/build-logs-overlay"; +import { BuildLogsDialogContent } from "@/app/project/workload-components/build-logs-dialog-content"; import { formatDateTime } from "@/frontend/utils/format.utils"; import { Toast } from "@/frontend/utils/toast.utils"; import { useConfirmDialog, useDialog } from "@/frontend/states/zustand.states"; diff --git a/src/shared/utils/path-builder.utils.unit.spec.ts b/src/shared/utils/path-builder.utils.unit.spec.ts index 7f2ebd39..527b0d63 100644 --- a/src/shared/utils/path-builder.utils.unit.spec.ts +++ b/src/shared/utils/path-builder.utils.unit.spec.ts @@ -1,5 +1,5 @@ import { PathBuilderUtils } from './path-builder.utils'; -import { DrawerSessionUtils } from '@/app/project/[projectId]/app-components/project-network-graph/project-network-graph-drawer-session'; +import { DrawerSessionUtils } from '@/app/project/[projectId]/overview/network-graph/project-network-graph-drawer-session'; describe('PathBuilderUtils.projectAppDrawer', () => { it('builds an encoded drawer URL with the default tab', () => { From d6ce3473ea03f2ea6100f7dd51cd02757c160edf Mon Sep 17 00:00:00 2001 From: biersoeckli Date: Sun, 8 Nov 2026 11:53:07 +0000 Subject: [PATCH 5/7] feat: enhance nested drawer and form components with improved descriptions, removed redundat cards and submit handling --- .../details/agent-sandbox-drawer-settings.tsx | 2 + .../agent-container-config-card.tsx | 205 ++++---- .../apps/details/app-details-settings.tsx | 3 + .../configuration/app-container-config.tsx | 285 ++++++----- .../deployments/deployments-grid-view.tsx | 14 +- .../apps/details/environment/env-edit.tsx | 17 +- .../details/network/policy/network-policy.tsx | 52 +- .../drawer/nested-drawer.tsx | 16 +- .../health-check-settings.tsx | 471 +++++++++--------- src/components/custom/submit-button.tsx | 5 +- 10 files changed, 537 insertions(+), 533 deletions(-) diff --git a/src/app/project/[projectId]/agents/details/agent-sandbox-drawer-settings.tsx b/src/app/project/[projectId]/agents/details/agent-sandbox-drawer-settings.tsx index a56b50aa..41d3fcb3 100644 --- a/src/app/project/[projectId]/agents/details/agent-sandbox-drawer-settings.tsx +++ b/src/app/project/[projectId]/agents/details/agent-sandbox-drawer-settings.tsx @@ -69,6 +69,7 @@ export default function AgentSandboxDrawerSettings({ onClick={() => openNestedDrawer({ title: 'Advanced container settings', + description: 'Configure agent sandbox startup.', content: , }) }> @@ -88,6 +89,7 @@ export default function AgentSandboxDrawerSettings({ onClick={() => openNestedDrawer({ title: 'Health checks', + description: 'Configure healthchecks so that k3s can automatically monitor when your agent sandbox is fully started up and ready to receive traffic.', content: ( typeof value === 'string' || typeof value === 'number' ? value : ''; const form = useForm, unknown, z.output>({ resolver: zodResolver(agentContainerConfigZodModel), @@ -61,116 +62,108 @@ export default function AgentContainerConfigCard({ agent, readonly, runtimeClass return (
- form.handleSubmit((data) => formAction(data))()}> - - - Container Configuration - - Configure agent sandbox startup. - - - -
-
-

Runtime

-

- Leave command and arguments empty to use the QuickStack default container startup. -

-
- + form.handleSubmit((data) => formAction(data))()}> +
+
+
+

Runtime

+

+ Leave command and arguments empty to use the QuickStack default container startup. +

- - ( - - Runtime Class - -

- Selects the Kubernetes RuntimeClass for new agent sandboxes. -

- -
- )} + +
- ( - - Working Directory + ( + + Runtime Class + + + + - - - )} - /> + + K3s default runtime + {runtimeClasses.map((runtimeClass) => ( + + {runtimeClass} + + ))} + + +

+ Selects the Kubernetes RuntimeClass for new agent sandboxes. +

+ +
+ )} + /> - ( - -
- Deploy File Browser -

- Adds a File Browser sidecar pod to each agent sandbox. -

-
- - - -
- )} - /> - - {!readonly && ( - - Save - {state?.status === 'error' && !state?.errors && ( -

{state.message}

- )} -
- )} - + ( + + Working Directory + + + + + + )} + /> + + ( + +
+ Deploy File Browser +

+ Adds a File Browser sidecar pod to each agent sandbox. +

+
+ + + +
+ )} + /> +
+ {!readonly && ( + + Save + {state?.status === 'error' && !state?.errors && ( +

{state.message}

+ )} +
+ )} diff --git a/src/app/project/[projectId]/apps/details/app-details-settings.tsx b/src/app/project/[projectId]/apps/details/app-details-settings.tsx index cbd7196e..b4334913 100644 --- a/src/app/project/[projectId]/apps/details/app-details-settings.tsx +++ b/src/app/project/[projectId]/apps/details/app-details-settings.tsx @@ -94,6 +94,7 @@ export function DrawerSettings({ onClick={() => openNestedDrawer({ title: 'Advanced settings', + description: 'Override image defaults only when your workload needs custom startup behavior or Linux security settings.', content: ( openNestedDrawer({ title: 'Network policies', + description: 'Control which traffic can reach this app and where it can connect.', content: ( openNestedDrawer({ title: 'Health checks', + description: 'Configure healthchecks so that k3s can automatically monitor when your application is fully started up and ready to receive traffic.', content: ( ; @@ -27,6 +27,7 @@ export default function GeneralAppContainerConfig({ app, readonly }: { app: AppExtendedModel; readonly: boolean; }) { + const formId = useId(); const inputValue = (value: unknown) => typeof value === 'string' || typeof value === 'number' ? value : ''; // Parse containerArgs from JSON string to array const form = useForm, unknown, z.output>({ @@ -62,162 +63,154 @@ export default function GeneralAppContainerConfig({ app, readonly }: { const values = form.watch(); return ( - - { - Container Configuration - - Override image defaults only when your workload needs custom startup behavior or Linux security settings. - - } -
- - form.handleSubmit((data) => { - return formAction(data); - })()}> - -
-
-

Runtime

-

- Leave these fields empty to keep the command and arguments from the container image. -

-
- - + + + form.handleSubmit((data) => { + return formAction(data); + })()}> +
+
+
+

Runtime

+

+ Leave these fields empty to keep the command and arguments from the container image. +

- + +
-
-
-

Security Context

-

- Change these values only when your image, mounted volumes, or tooling require specific Linux permissions. -

-
+ -
- ( - - - Run As User - - - field.onChange(e.target.value === '' ? null : Number(e.target.value))} - /> - - - - )} - /> - ( - - - Run As Group - - - field.onChange(e.target.value === '' ? null : Number(e.target.value))} - /> - - - - )} - /> - ( - - - FS Group - - - field.onChange(e.target.value === '' ? null : Number(e.target.value))} - /> - - - - )} - /> -
+
+
+

Security Context

+

+ Change these values only when your image, mounted volumes, or tooling require specific Linux permissions. +

+
+
( - -
- - - -
- -

- Removes most container isolation. The container gets all Linux capabilities, - access to host devices, and can interact with the node almost like a root - process on the host. -

-

- If the container is compromised, it can affect the Kubernetes node and - other workloads. Use this only for workloads such as Docker-in-Docker - or low-level system tooling. -

- - )} - > - Privileged Mode -
- - {values.securityContextPrivileged && - - Enable this only if you fully understand the implications and risks. - - } -
- -
+ + + Run As User + + + field.onChange(e.target.value === '' ? null : Number(e.target.value))} + /> + + + + )} + /> + ( + + + Run As Group + + + field.onChange(e.target.value === '' ? null : Number(e.target.value))} + /> + + + + )} + /> + ( + + + FS Group + + + field.onChange(e.target.value === '' ? null : Number(e.target.value))} + /> + )} />
- - {!readonly && ( - - Save -

{state?.message}

-
- )} - - - - + + ( + +
+ + + +
+ +

+ Removes most container isolation. The container gets all Linux capabilities, + access to host devices, and can interact with the node almost like a root + process on the host. +

+

+ If the container is compromised, it can affect the Kubernetes node and + other workloads. Use this only for workloads such as Docker-in-Docker + or low-level system tooling. +

+ + )} + > + Privileged Mode +
+ + {values.securityContextPrivileged && + + Enable this only if you fully understand the implications and risks. + + } +
+ +
+ +
+ )} + /> +
+
+ {!readonly && ( + + Save +

{state?.message}

+
+ )} + + + ); } diff --git a/src/app/project/[projectId]/apps/details/deployments/deployments-grid-view.tsx b/src/app/project/[projectId]/apps/details/deployments/deployments-grid-view.tsx index d2151bc8..7a72cb35 100644 --- a/src/app/project/[projectId]/apps/details/deployments/deployments-grid-view.tsx +++ b/src/app/project/[projectId]/apps/details/deployments/deployments-grid-view.tsx @@ -8,6 +8,7 @@ import { DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Item, ItemContent, ItemTitle } from '@/components/ui/item'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { formatDateTime } from '@/frontend/utils/format.utils'; import type { DeploymentInfoModel } from '@/shared/model/deployment-info.model'; import { appBuildMethodLabels } from '@/shared/model/app-source-info.model'; @@ -82,10 +83,15 @@ export function DeploymentsGridView({
)} - + + + onShowLogs(deployment)}> + + Show Deployment Logs + } /> + Show Deployment Logs + + {(canStopBuild(deployment) || canRollback(deployment)) && diff --git a/src/app/project/[projectId]/apps/details/environment/env-edit.tsx b/src/app/project/[projectId]/apps/details/environment/env-edit.tsx index 1a9c2a81..19234d8c 100644 --- a/src/app/project/[projectId]/apps/details/environment/env-edit.tsx +++ b/src/app/project/[projectId]/apps/details/environment/env-edit.tsx @@ -2,24 +2,25 @@ import type { z } from "zod"; import { SubmitButton } from "@/components/custom/submit-button"; -import { CardContent, CardFooter } from "@/components/ui/card"; +import { CardContent } from "@/components/ui/card"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { FormUtils } from "@/frontend/utils/form.utilts"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { saveEnvVariables } from "./actions"; import { ServerActionResult } from "@/shared/model/server-action-error-return.model"; -import { useActionState, useEffect } from "react"; +import { useActionState, useEffect, useId } from "react"; import { toast } from "sonner"; import { AppEnvVariablesModel, appEnvVariablesZodModel } from "@/shared/model/env-edit.model"; import { Textarea } from "@/components/ui/textarea"; import { AppExtendedModel } from "@/shared/model/app-extended.model"; - +import { NestedDrawerFooter } from "@/app/project/workload-components/drawer/nested-drawer"; export default function EnvEdit({ app, readonly }: { app: AppExtendedModel; readonly: boolean; }) { + const formId = useId(); const form = useForm, unknown, z.output>({ resolver: zodResolver(appEnvVariablesZodModel), defaultValues: app, @@ -40,9 +41,9 @@ export default function EnvEdit({ app, readonly }: { return <>
- +
- form.handleSubmit((data) => { + form.handleSubmit((data) => { return formAction(data); })()}> @@ -88,9 +89,9 @@ export default function EnvEdit({ app, readonly }: { )} /> - {!readonly && - Save - } + {!readonly && + Save + }
diff --git a/src/app/project/[projectId]/apps/details/network/policy/network-policy.tsx b/src/app/project/[projectId]/apps/details/network/policy/network-policy.tsx index ccb9b210..84aa710e 100644 --- a/src/app/project/[projectId]/apps/details/network/policy/network-policy.tsx +++ b/src/app/project/[projectId]/apps/details/network/policy/network-policy.tsx @@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from 'react'; import { useRouter } from 'next/navigation'; import { Info, List, Waypoints } from 'lucide-react'; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; +import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; @@ -19,6 +19,7 @@ import { useConfirmDialog, useDialog } from '@/frontend/states/zustand.states'; import AppNetworkPolicyRuleDialog from './app-network-policy-rule-dialog'; import AppNetworkPolicyRuleSection, { AppNetworkPolicyDirection } from './app-network-policy-rule-section'; import NetworkPolicyGraph from './network-policy-graph'; +import { NestedDrawerFooter } from '@/app/project/workload-components/drawer/nested-drawer'; type Project = { id: string; name: string; apps: { id: string; name: string; appType: AppExtendedModel['appType'] }[]; agents: { id: string; name: string }[] }; @@ -95,33 +96,30 @@ export default function NetworkPolicy({ app, readonly }: { app: AppExtendedModel onAdd={addRule} />, { maxWidth: 'max-w-md' }); - return - {Network PolicyControl which traffic can reach this app and where it can connect.} - - - - setDraft(current => ({ ...current, useNetworkPolicy }))} /> - - + return
+ + + setDraft(current => ({ ...current, useNetworkPolicy }))} /> + + - {draft.useNetworkPolicy && setView(value as 'rules' | 'graph')}> - - Rules - Network Graph - - - rule.type === 'INGRESS')} readonly={readonly} onAdd={() => openRuleDialog('INGRESS')} onDeleteRule={deleteRule} currentProjectId={app.project.id} projects={projects} /> - rule.type === 'EGRESS')} readonly={readonly} onAdd={() => openRuleDialog('EGRESS')} onDeleteRule={deleteRule} currentProjectId={app.project.id} projects={projects} internetAccess={draft.allowInternetAccess} onInternetAccessChange={changeInternetAccess} /> - - - `${domain.hostname}:${domain.port}`)} projects={projects} /> - - } - - {!readonly && dirty && - - } - ; + {draft.useNetworkPolicy && setView(value as 'rules' | 'graph')}> + + Rules + Network Graph + + + rule.type === 'INGRESS')} readonly={readonly} onAdd={() => openRuleDialog('INGRESS')} onDeleteRule={deleteRule} currentProjectId={app.project.id} projects={projects} /> + rule.type === 'EGRESS')} readonly={readonly} onAdd={() => openRuleDialog('EGRESS')} onDeleteRule={deleteRule} currentProjectId={app.project.id} projects={projects} internetAccess={draft.allowInternetAccess} onInternetAccessChange={changeInternetAccess} /> + + + `${domain.hostname}:${domain.port}`)} projects={projects} /> + + } + {!readonly && dirty && + + } +
; } function SettingRow({ label, description, checked, disabled, onChange, hint }: { label: string; description: string; checked: boolean; disabled: boolean; onChange: (checked: boolean) => void; hint?: string }) { diff --git a/src/app/project/workload-components/drawer/nested-drawer.tsx b/src/app/project/workload-components/drawer/nested-drawer.tsx index 8c1b3f99..f9177052 100644 --- a/src/app/project/workload-components/drawer/nested-drawer.tsx +++ b/src/app/project/workload-components/drawer/nested-drawer.tsx @@ -9,10 +9,12 @@ import { type ReactNode, } from 'react'; import { Button } from '@/components/ui/button'; +import { createPortal } from 'react-dom'; import { Drawer, DrawerContent, DrawerDescription, + DrawerFooter, DrawerHeader, DrawerTitle, } from '@/components/ui/drawer'; @@ -27,12 +29,14 @@ type NestedDrawerOptions = { type NestedDrawerContextValue = { openNestedDrawer: (options: NestedDrawerOptions) => void; closeNestedDrawer: () => void; + footerTarget: HTMLDivElement | null; }; const NestedDrawerContext = createContext(null); export function NestedDrawerProvider({ children }: { children: ReactNode }) { const [drawer, setDrawer] = useState(null); + const [footerTarget, setFooterTarget] = useState(null); const closeNestedDrawer = useCallback(() => setDrawer(null), []); const openNestedDrawer = useCallback( (options: NestedDrawerOptions) => setDrawer(options), @@ -41,7 +45,7 @@ export function NestedDrawerProvider({ children }: { children: ReactNode }) { return ( {children} +
); } +export function NestedDrawerFooter({ children }: { children: ReactNode }) { + const { footerTarget } = useNestedDrawer(); + + return footerTarget ? createPortal( + {children}, + footerTarget, + ) : null; +} + export function useNestedDrawer() { const context = useContext(NestedDrawerContext); diff --git a/src/app/project/workload-components/health-check-settings.tsx b/src/app/project/workload-components/health-check-settings.tsx index 17020430..edce5322 100644 --- a/src/app/project/workload-components/health-check-settings.tsx +++ b/src/app/project/workload-components/health-check-settings.tsx @@ -4,7 +4,6 @@ import type { z } from "zod"; import { HealthCheckModel, HealthCheckWorkload, healthCheckZodModel } from "@/shared/model/health-check.model"; import { useForm, useFieldArray } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"; import { Form, FormField, FormItem, FormControl, FormMessage, FormLabel } from "@/components/ui/form"; import { Switch } from "@/components/ui/switch"; import { Input } from "@/components/ui/input"; @@ -13,17 +12,19 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Trash, Plus } from "lucide-react"; import FormLabelWithQuestion from "@/components/custom/form-label-with-question"; -import { useActionState, useEffect } from "react"; +import { useActionState, useEffect, useId } from "react"; import { toast } from "sonner"; import { SubmitButton } from "@/components/custom/submit-button"; import { FormUtils } from "@/frontend/utils/form.utilts"; import { ServerActionResult } from "@/shared/model/server-action-error-return.model"; +import { NestedDrawerFooter } from "./drawer/nested-drawer"; export default function HealthCheckSettings({ workload, readonly, saveHealthCheck }: { workload: HealthCheckWorkload; readonly: boolean; saveHealthCheck: (state: ServerActionResult, payload: HealthCheckModel) => Promise>; }) { + const formId = useId(); const defaultHeaders = workload.healthCheckHttpHeadersJson ? JSON.parse(workload.healthCheckHttpHeadersJson) @@ -73,267 +74,259 @@ export default function HealthCheckSettings({ workload, readonly, saveHealthChec }, [form, state]); return ( - - { - Health Check Settings - - Configure healthchecks so that k3s can automatically monitor when your application is fully started up and ready to receive traffic. - - } -
- form.handleSubmit((data) => { - formAction(data); - })()}> - - ( - -
- Enable Health Check -
- - - -
- )} - /> - - {enabled && ( - <> - form.setValue('probeType', value as "HTTP" | "TCP")} className="w-full"> - - HTTP Probe - TCP Probe - - - -
- ( - - - HTTP Path - - - - - - - )} - /> - ( - - - HTTP Port - - - field.onChange(e.target.value)} /> - - - - )} - /> - ( - - -

Scheme to use for connecting to the container. Defaults to HTTP.

-

Possible enum values:

-
    -
  • "HTTP" means that the scheme used will be http://
  • -
  • "HTTPS" means that the scheme used will be https://
  • -
-
- }> - HTTP Scheme - - - - - )} - /> -
+ + form.handleSubmit((data) => { + formAction(data); + })()}> +
+ ( + +
+ Enable Health Check +
+ + + +
+ )} + /> -
- -

Custom headers to set in the request. HTTP allows repeated headers.

-
- }> - HTTP Headers - -
- {fields.map((item, index) => ( -
- ( - - {index === 0 &&
- Name - - {''} - -
} - - - - -
- )} - /> - ( - - {index === 0 &&
- Value - - {''} - -
} - - - - -
- )} - /> - -
- ))} - -
-
- + {enabled && ( + <> + form.setValue('probeType', value as "HTTP" | "TCP")} className="w-full"> + + HTTP Probe + TCP Probe + - + +
+ ( + + + HTTP Path + + + + + + + )} + /> ( - - TCP Port + + HTTP Port - field.onChange(e.target.value)} /> + field.onChange(e.target.value)} /> )} /> - - + ( + + +

Scheme to use for connecting to the container. Defaults to HTTP.

+

Possible enum values:

+
    +
  • "HTTP" means that the scheme used will be http://
  • +
  • "HTTPS" means that the scheme used will be https://
  • +
+
+ }> + HTTP Scheme + + + + + )} + /> +
-
- ( - - - Check Interval (periodSeconds) - - - field.onChange(e.target.value)} /> - - - - )} - /> - ( - - -

Number of seconds to wait for a connection to complete before timing out. Minimum value is 1.

-
- }> - Check Timeout (timeoutSeconds) - - - field.onChange(e.target.value)} /> - - - - )} - /> +
+ +

Custom headers to set in the request. HTTP allows repeated headers.

+
+ }> + HTTP Headers + +
+ {fields.map((item, index) => ( +
+ ( + + {index === 0 &&
+ Name + + {''} + +
} + + + + +
+ )} + /> + ( + + {index === 0 &&
+ Value + + {''} + +
} + + + + +
+ )} + /> + +
+ ))} + +
+
+ + + ( - - Failure Threshold + + TCP Port - field.onChange(e.target.value)} /> + field.onChange(e.target.value)} /> )} /> - - - )} - - - Save - - - - + + + +
+ ( + + + Check Interval (periodSeconds) + + + field.onChange(e.target.value)} /> + + + + )} + /> + ( + + +

Number of seconds to wait for a connection to complete before timing out. Minimum value is 1.

+
+ }> + Check Timeout (timeoutSeconds) + + + field.onChange(e.target.value)} /> + + + + )} + /> + ( + + + Failure Threshold + + + field.onChange(e.target.value)} /> + + + + )} + /> + + + )} + + {!readonly && + Save + } + + ); } diff --git a/src/components/custom/submit-button.tsx b/src/components/custom/submit-button.tsx index be6e7e02..2ee5b0fe 100644 --- a/src/components/custom/submit-button.tsx +++ b/src/components/custom/submit-button.tsx @@ -7,8 +7,9 @@ import { Button } from "../ui/button"; export function SubmitButton(props: { children: React.ReactNode, variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | null | undefined, - className?: string + className?: string, + form?: string, }) { const { pending } = useFormStatus(); - return + return } From 883ee9cb7a9a0b26ddd4bb6ad1c6c180c993ddad Mon Sep 17 00:00:00 2001 From: biersoeckli Date: Sun, 8 Nov 2026 11:54:17 +0000 Subject: [PATCH 6/7] fix: wrap status filter items in a dropdown menu group --- .../sandboxes/agent-sandboxes-card.tsx | 24 ++++++++++--------- src/components/custom/error-state.tsx | 1 + 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/app/project/[projectId]/agents/details/sandboxes/agent-sandboxes-card.tsx b/src/app/project/[projectId]/agents/details/sandboxes/agent-sandboxes-card.tsx index 15dd82ab..95bc3c20 100644 --- a/src/app/project/[projectId]/agents/details/sandboxes/agent-sandboxes-card.tsx +++ b/src/app/project/[projectId]/agents/details/sandboxes/agent-sandboxes-card.tsx @@ -439,17 +439,19 @@ export default function AgentSandboxesCard({ Filter{statusFilter.length > 0 && ` (${statusFilter.length})`} - Status - - {SANDBOX_STATUSES.map((status) => ( - toggleStatusFilter(status, checked)} - > - {SANDBOX_STATUS_LABELS[status]} - - ))} + + Status + + {SANDBOX_STATUSES.map((status) => ( + toggleStatusFilter(status, checked)} + > + {SANDBOX_STATUS_LABELS[status]} + + ))} + diff --git a/src/components/custom/error-state.tsx b/src/components/custom/error-state.tsx index c573aad0..5b42caad 100644 --- a/src/components/custom/error-state.tsx +++ b/src/components/custom/error-state.tsx @@ -48,6 +48,7 @@ export default function ErrorState({ )}