Improve workspace detail mobile layouts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
caa7550e9f
commit
0356040a29
2 changed files with 414 additions and 403 deletions
|
|
@ -161,9 +161,9 @@ function Field({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<label className="space-y-1.5">
|
<label className="space-y-1.5">
|
||||||
<div className="flex items-center justify-between gap-3">
|
<div className="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between sm:gap-3">
|
||||||
<span className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">{label}</span>
|
<span className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">{label}</span>
|
||||||
{hint ? <span className="text-[11px] text-muted-foreground">{hint}</span> : null}
|
{hint ? <span className="text-[11px] leading-relaxed text-muted-foreground sm:text-right">{hint}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -172,8 +172,8 @@ function Field({
|
||||||
|
|
||||||
function DetailRow({ label, children }: { label: string; children: React.ReactNode }) {
|
function DetailRow({ label, children }: { label: string; children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start gap-3 py-1.5">
|
<div className="flex flex-col gap-1.5 py-1.5 sm:flex-row sm:items-start sm:gap-3">
|
||||||
<div className="w-32 shrink-0 text-xs text-muted-foreground">{label}</div>
|
<div className="shrink-0 text-xs text-muted-foreground sm:w-32">{label}</div>
|
||||||
<div className="min-w-0 flex-1 text-sm">{children}</div>
|
<div className="min-w-0 flex-1 text-sm">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -189,7 +189,7 @@ function StatusPill({ children, className }: { children: React.ReactNode; classN
|
||||||
|
|
||||||
function MonoValue({ value, copy }: { value: string; copy?: boolean }) {
|
function MonoValue({ value, copy }: { value: string; copy?: boolean }) {
|
||||||
return (
|
return (
|
||||||
<div className="inline-flex max-w-full items-center gap-2">
|
<div className="inline-flex max-w-full items-start gap-2">
|
||||||
<span className="break-all font-mono text-xs">{value}</span>
|
<span className="break-all font-mono text-xs">{value}</span>
|
||||||
{copy ? (
|
{copy ? (
|
||||||
<CopyText text={value} className="shrink-0 text-muted-foreground hover:text-foreground" copiedLabel="Copied">
|
<CopyText text={value} className="shrink-0 text-muted-foreground hover:text-foreground" copiedLabel="Copied">
|
||||||
|
|
@ -384,7 +384,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
<div className="grid gap-6 lg:grid-cols-[minmax(0,1.4fr)_minmax(18rem,0.95fr)]">
|
<div className="grid gap-6 lg:grid-cols-[minmax(0,1.4fr)_minmax(18rem,0.95fr)]">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="rounded-2xl border border-border bg-card p-5">
|
<div className="rounded-2xl border border-border bg-card p-5">
|
||||||
<div className="flex flex-wrap items-start justify-between gap-4">
|
<div className="flex flex-col gap-4 sm:flex-row sm:flex-wrap sm:items-start sm:justify-between">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">
|
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">
|
||||||
Execution workspace
|
Execution workspace
|
||||||
|
|
@ -396,9 +396,10 @@ export function ExecutionWorkspaceDetail() {
|
||||||
and runtime-service behavior in sync with the actual workspace being reused.
|
and runtime-service behavior in sync with the actual workspace being reused.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex shrink-0 items-center gap-2">
|
<div className="flex w-full shrink-0 items-center gap-2 sm:w-auto">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
onClick={() => setCloseDialogOpen(true)}
|
onClick={() => setCloseDialogOpen(true)}
|
||||||
disabled={workspace.status === "archived"}
|
disabled={workspace.status === "archived"}
|
||||||
>
|
>
|
||||||
|
|
@ -496,7 +497,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<div className="rounded-xl border border-dashed border-border/70 bg-muted/20 px-3 py-3">
|
<div className="rounded-xl border border-dashed border-border/70 bg-muted/20 px-3 py-3">
|
||||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
<div className="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">
|
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">
|
||||||
Runtime config source
|
Runtime config source
|
||||||
|
|
@ -511,6 +512,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={!linkedProjectWorkspace?.runtimeConfig?.workspaceRuntime}
|
disabled={!linkedProjectWorkspace?.runtimeConfig?.workspaceRuntime}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
|
@ -548,13 +550,14 @@ export function ExecutionWorkspaceDetail() {
|
||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-5 flex flex-wrap items-center gap-3">
|
<div className="mt-5 flex flex-col items-stretch gap-3 sm:flex-row sm:flex-wrap sm:items-center">
|
||||||
<Button disabled={!isDirty || updateWorkspace.isPending} onClick={saveChanges}>
|
<Button className="w-full sm:w-auto" disabled={!isDirty || updateWorkspace.isPending} onClick={saveChanges}>
|
||||||
{updateWorkspace.isPending ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
|
{updateWorkspace.isPending ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
|
||||||
Save changes
|
Save changes
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
disabled={!isDirty || updateWorkspace.isPending}
|
disabled={!isDirty || updateWorkspace.isPending}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setForm(initialState);
|
setForm(initialState);
|
||||||
|
|
@ -631,12 +634,12 @@ export function ExecutionWorkspaceDetail() {
|
||||||
</DetailRow>
|
</DetailRow>
|
||||||
<DetailRow label="Repo URL">
|
<DetailRow label="Repo URL">
|
||||||
{workspace.repoUrl && isSafeExternalUrl(workspace.repoUrl) ? (
|
{workspace.repoUrl && isSafeExternalUrl(workspace.repoUrl) ? (
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="inline-flex max-w-full items-start gap-2">
|
||||||
<a href={workspace.repoUrl} target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 hover:underline">
|
<a href={workspace.repoUrl} target="_blank" rel="noreferrer" className="inline-flex min-w-0 items-center gap-1 break-all hover:underline">
|
||||||
{workspace.repoUrl}
|
{workspace.repoUrl}
|
||||||
<ExternalLink className="h-3.5 w-3.5" />
|
<ExternalLink className="h-3.5 w-3.5 shrink-0" />
|
||||||
</a>
|
</a>
|
||||||
<CopyText text={workspace.repoUrl} className="text-muted-foreground hover:text-foreground" copiedLabel="Copied">
|
<CopyText text={workspace.repoUrl} className="shrink-0 text-muted-foreground hover:text-foreground" copiedLabel="Copied">
|
||||||
<Copy className="h-3.5 w-3.5" />
|
<Copy className="h-3.5 w-3.5" />
|
||||||
</CopyText>
|
</CopyText>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -662,7 +665,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-border bg-card p-5">
|
<div className="rounded-2xl border border-border bg-card p-5">
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">Runtime services</div>
|
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">Runtime services</div>
|
||||||
<h2 className="text-lg font-semibold">Attached services</h2>
|
<h2 className="text-lg font-semibold">Attached services</h2>
|
||||||
|
|
@ -674,10 +677,11 @@ export function ExecutionWorkspaceDetail() {
|
||||||
: "none"}
|
: "none"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex shrink-0 flex-wrap gap-2">
|
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:flex-wrap">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
disabled={controlRuntimeServices.isPending || !effectiveRuntimeConfig || !workspace.cwd}
|
disabled={controlRuntimeServices.isPending || !effectiveRuntimeConfig || !workspace.cwd}
|
||||||
onClick={() => controlRuntimeServices.mutate("start")}
|
onClick={() => controlRuntimeServices.mutate("start")}
|
||||||
>
|
>
|
||||||
|
|
@ -687,6 +691,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
disabled={controlRuntimeServices.isPending || !effectiveRuntimeConfig || !workspace.cwd}
|
disabled={controlRuntimeServices.isPending || !effectiveRuntimeConfig || !workspace.cwd}
|
||||||
onClick={() => controlRuntimeServices.mutate("restart")}
|
onClick={() => controlRuntimeServices.mutate("restart")}
|
||||||
>
|
>
|
||||||
|
|
@ -695,6 +700,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
disabled={controlRuntimeServices.isPending || (workspace.runtimeServices?.length ?? 0) === 0}
|
disabled={controlRuntimeServices.isPending || (workspace.runtimeServices?.length ?? 0) === 0}
|
||||||
onClick={() => controlRuntimeServices.mutate("stop")}
|
onClick={() => controlRuntimeServices.mutate("stop")}
|
||||||
>
|
>
|
||||||
|
|
@ -707,7 +713,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{workspace.runtimeServices.map((service) => (
|
{workspace.runtimeServices.map((service) => (
|
||||||
<div key={service.id} className="rounded-xl border border-border/80 bg-background px-3 py-2">
|
<div key={service.id} className="rounded-xl border border-border/80 bg-background px-3 py-2">
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-sm font-medium">{service.serviceName}</div>
|
<div className="text-sm font-medium">{service.serviceName}</div>
|
||||||
<div className="text-xs text-muted-foreground">{service.status} · {service.lifecycle}</div>
|
<div className="text-xs text-muted-foreground">{service.status} · {service.lifecycle}</div>
|
||||||
|
|
@ -723,7 +729,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
{service.cwd ? <MonoValue value={service.cwd} copy /> : null}
|
{service.cwd ? <MonoValue value={service.cwd} copy /> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<StatusPill>{service.healthStatus}</StatusPill>
|
<StatusPill className="self-start">{service.healthStatus}</StatusPill>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -755,7 +761,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{workspaceOperationsQuery.data.slice(0, 6).map((operation) => (
|
{workspaceOperationsQuery.data.slice(0, 6).map((operation) => (
|
||||||
<div key={operation.id} className="rounded-xl border border-border/80 bg-background px-3 py-2">
|
<div key={operation.id} className="rounded-xl border border-border/80 bg-background px-3 py-2">
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-sm font-medium">{operation.command ?? operation.phase}</div>
|
<div className="text-sm font-medium">{operation.command ?? operation.phase}</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
|
|
@ -768,7 +774,7 @@ export function ExecutionWorkspaceDetail() {
|
||||||
<div className="whitespace-pre-wrap break-words text-xs text-muted-foreground">{operation.stdoutExcerpt}</div>
|
<div className="whitespace-pre-wrap break-words text-xs text-muted-foreground">{operation.stdoutExcerpt}</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<StatusPill>{operation.status}</StatusPill>
|
<StatusPill className="self-start">{operation.status}</StatusPill>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -186,9 +186,9 @@ function Field({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<label className="space-y-1.5">
|
<label className="space-y-1.5">
|
||||||
<div className="flex items-center justify-between gap-3">
|
<div className="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between sm:gap-3">
|
||||||
<span className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">{label}</span>
|
<span className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">{label}</span>
|
||||||
{hint ? <span className="text-[11px] text-muted-foreground">{hint}</span> : null}
|
{hint ? <span className="text-[11px] leading-relaxed text-muted-foreground sm:text-right">{hint}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -197,8 +197,8 @@ function Field({
|
||||||
|
|
||||||
function DetailRow({ label, children }: { label: string; children: React.ReactNode }) {
|
function DetailRow({ label, children }: { label: string; children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start gap-3 py-1.5">
|
<div className="flex flex-col gap-1.5 py-1.5 sm:flex-row sm:items-start sm:gap-3">
|
||||||
<div className="w-28 shrink-0 text-xs text-muted-foreground">{label}</div>
|
<div className="shrink-0 text-xs text-muted-foreground sm:w-28">{label}</div>
|
||||||
<div className="min-w-0 flex-1 text-sm">{children}</div>
|
<div className="min-w-0 flex-1 text-sm">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -364,7 +364,7 @@ export function ProjectWorkspaceDetail() {
|
||||||
<div className="grid gap-6 lg:grid-cols-[minmax(0,1.4fr)_minmax(18rem,0.9fr)]">
|
<div className="grid gap-6 lg:grid-cols-[minmax(0,1.4fr)_minmax(18rem,0.9fr)]">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="rounded-2xl border border-border bg-card p-5">
|
<div className="rounded-2xl border border-border bg-card p-5">
|
||||||
<div className="flex flex-wrap items-start justify-between gap-4">
|
<div className="flex flex-col gap-4 sm:flex-row sm:flex-wrap sm:items-start sm:justify-between">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">
|
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">
|
||||||
Project workspace
|
Project workspace
|
||||||
|
|
@ -379,6 +379,7 @@ export function ProjectWorkspaceDetail() {
|
||||||
{!workspace.isPrimary ? (
|
{!workspace.isPrimary ? (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
disabled={setPrimaryWorkspace.isPending}
|
disabled={setPrimaryWorkspace.isPending}
|
||||||
onClick={() => setPrimaryWorkspace.mutate()}
|
onClick={() => setPrimaryWorkspace.mutate()}
|
||||||
>
|
>
|
||||||
|
|
@ -388,7 +389,7 @@ export function ProjectWorkspaceDetail() {
|
||||||
Make primary
|
Make primary
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<div className="inline-flex items-center gap-2 rounded-xl border border-emerald-500/25 bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-300">
|
<div className="inline-flex items-center gap-2 rounded-xl border border-emerald-500/25 bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-300 sm:max-w-sm">
|
||||||
<Sparkles className="h-4 w-4" />
|
<Sparkles className="h-4 w-4" />
|
||||||
This is the project’s primary codebase workspace.
|
This is the project’s primary codebase workspace.
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -537,13 +538,14 @@ export function ProjectWorkspaceDetail() {
|
||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-5 flex flex-wrap items-center gap-3">
|
<div className="mt-5 flex flex-col items-stretch gap-3 sm:flex-row sm:flex-wrap sm:items-center">
|
||||||
<Button disabled={!isDirty || updateWorkspace.isPending} onClick={saveChanges}>
|
<Button className="w-full sm:w-auto" disabled={!isDirty || updateWorkspace.isPending} onClick={saveChanges}>
|
||||||
{updateWorkspace.isPending ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
|
{updateWorkspace.isPending ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
|
||||||
Save changes
|
Save changes
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
disabled={!isDirty || updateWorkspace.isPending}
|
disabled={!isDirty || updateWorkspace.isPending}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setForm(initialState);
|
setForm(initialState);
|
||||||
|
|
@ -590,7 +592,7 @@ export function ProjectWorkspaceDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-border bg-card p-5">
|
<div className="rounded-2xl border border-border bg-card p-5">
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">Runtime services</div>
|
<div className="text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground">Runtime services</div>
|
||||||
<h2 className="text-lg font-semibold">Attached services</h2>
|
<h2 className="text-lg font-semibold">Attached services</h2>
|
||||||
|
|
@ -598,10 +600,11 @@ export function ProjectWorkspaceDetail() {
|
||||||
Shared services for this project workspace. Execution workspaces inherit this config unless they override it.
|
Shared services for this project workspace. Execution workspaces inherit this config unless they override it.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex shrink-0 flex-wrap gap-2">
|
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:flex-wrap">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
disabled={controlRuntimeServices.isPending || !workspace.runtimeConfig?.workspaceRuntime || !workspace.cwd}
|
disabled={controlRuntimeServices.isPending || !workspace.runtimeConfig?.workspaceRuntime || !workspace.cwd}
|
||||||
onClick={() => controlRuntimeServices.mutate("start")}
|
onClick={() => controlRuntimeServices.mutate("start")}
|
||||||
>
|
>
|
||||||
|
|
@ -611,6 +614,7 @@ export function ProjectWorkspaceDetail() {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
disabled={controlRuntimeServices.isPending || !workspace.cwd}
|
disabled={controlRuntimeServices.isPending || !workspace.cwd}
|
||||||
onClick={() => controlRuntimeServices.mutate("restart")}
|
onClick={() => controlRuntimeServices.mutate("restart")}
|
||||||
>
|
>
|
||||||
|
|
@ -619,6 +623,7 @@ export function ProjectWorkspaceDetail() {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className="w-full sm:w-auto"
|
||||||
disabled={controlRuntimeServices.isPending || (workspace.runtimeServices?.length ?? 0) === 0}
|
disabled={controlRuntimeServices.isPending || (workspace.runtimeServices?.length ?? 0) === 0}
|
||||||
onClick={() => controlRuntimeServices.mutate("stop")}
|
onClick={() => controlRuntimeServices.mutate("stop")}
|
||||||
>
|
>
|
||||||
|
|
@ -631,7 +636,7 @@ export function ProjectWorkspaceDetail() {
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{workspace.runtimeServices.map((service) => (
|
{workspace.runtimeServices.map((service) => (
|
||||||
<div key={service.id} className="rounded-xl border border-border/80 bg-background px-3 py-2">
|
<div key={service.id} className="rounded-xl border border-border/80 bg-background px-3 py-2">
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-sm font-medium">{service.serviceName}</div>
|
<div className="text-sm font-medium">{service.serviceName}</div>
|
||||||
<div className="space-y-1 text-xs text-muted-foreground">
|
<div className="space-y-1 text-xs text-muted-foreground">
|
||||||
|
|
@ -646,7 +651,7 @@ export function ProjectWorkspaceDetail() {
|
||||||
{service.cwd ? <div className="break-all font-mono">{service.cwd}</div> : null}
|
{service.cwd ? <div className="break-all font-mono">{service.cwd}</div> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-[11px] uppercase tracking-[0.14em] text-muted-foreground">
|
<div className="text-[11px] uppercase tracking-[0.14em] text-muted-foreground sm:text-right">
|
||||||
{service.status} · {service.healthStatus}
|
{service.status} · {service.healthStatus}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue