feat: deploy zopu single-node production

This commit is contained in:
sai karthik
2026-07-26 11:39:21 +05:30
parent 2a0487aa6e
commit 39b27a229f
15 changed files with 320 additions and 121 deletions

View File

@@ -0,0 +1,53 @@
import { cn } from "@code/ui/lib/utils";
import { ChevronDown } from "lucide-react";
import type { MobileProjectView } from "./models";
export interface MobileProjectSelectProps {
readonly activeCount: number;
readonly className?: string;
readonly onProjectSelect: (projectId: string) => void;
readonly projects: readonly MobileProjectView[];
readonly selectedProjectId?: string;
}
export const MobileProjectSelect = ({
activeCount,
className,
onProjectSelect,
projects,
selectedProjectId,
}: MobileProjectSelectProps) => (
<div
className={cn(
"mt-0.5 flex min-w-0 items-center gap-1.5 text-[12px] leading-4 text-[#7c7772]",
className
)}
>
<span className="size-1.5 shrink-0 rounded-full bg-[#64ad1f]" />
<label className="relative min-w-0">
<span className="sr-only">Connected project</span>
<select
aria-label="Connected project"
className="max-w-[132px] cursor-pointer appearance-none truncate bg-transparent pr-4 font-medium text-[#5d5955] outline-none focus-visible:ring-2 focus-visible:ring-[#315dc0]"
onChange={(event) => onProjectSelect(event.currentTarget.value)}
value={selectedProjectId ?? ""}
>
{projects.length === 0 ? (
<option value="">No connected project</option>
) : null}
{projects.map((project) => (
<option key={project.id} value={project.id}>
{project.name}
</option>
))}
</select>
<ChevronDown
aria-hidden="true"
className="pointer-events-none absolute top-1/2 right-0 size-3 -translate-y-1/2"
/>
</label>
<span aria-hidden="true">·</span>
<span className="shrink-0 tabular-nums">{activeCount} active</span>
</div>
);

View File

@@ -10,8 +10,10 @@ import {
} from "lucide-react";
import type { FormEvent, MouseEvent } from "react";
import { MobileProjectSelect } from "./mobile-project-select";
import { MobileSettingsPanel } from "./mobile-settings-panel";
import type {
MobileProjectView,
MobileWorkspaceView,
MobileWorkUnitTone,
MobileWorkUnitView,
@@ -41,12 +43,18 @@ const dateLabel = new Intl.DateTimeFormat("en", {
interface WorkStackHeaderProps {
readonly activeCount: number;
readonly onOpenSettings?: () => void;
readonly onProjectSelect: (projectId: string) => void;
readonly projects: readonly MobileProjectView[];
readonly selectedProjectId?: string;
readonly userInitials: string;
}
const WorkStackHeader = ({
activeCount,
onOpenSettings,
onProjectSelect,
projects,
selectedProjectId,
userInitials,
}: WorkStackHeaderProps) => (
<header className="flex h-[96px] shrink-0 items-center bg-white px-4">
@@ -57,10 +65,12 @@ const WorkStackHeader = ({
<h1 className="text-[20px] leading-6 font-semibold tracking-[-0.03em] text-[#0b0b0a]">
Zopu
</h1>
<p className="mt-0.5 flex items-center gap-2 text-[13px] leading-4 text-[#7c7772]">
<span className="size-1.5 rounded-full bg-[#64ad1f]" />
{activeCount} active work units
</p>
<MobileProjectSelect
activeCount={activeCount}
onProjectSelect={onProjectSelect}
projects={projects}
selectedProjectId={selectedProjectId}
/>
</div>
<button
aria-label="Open settings"
@@ -291,6 +301,7 @@ export interface MobileWorkStackScreenProps {
readonly onComposerChange: (value: string) => void;
readonly onComposerSubmit: (event: FormEvent<HTMLFormElement>) => void;
readonly onOpenUnit: (workUnitId: string) => void;
readonly onProjectSelect: (projectId: string) => void;
readonly onRestoreChecked: () => void;
readonly onSettingsClose: () => void;
readonly onSettingsOpen: () => void;
@@ -314,6 +325,7 @@ export const MobileWorkStackScreen = ({
onComposerChange,
onComposerSubmit,
onOpenUnit,
onProjectSelect,
onRestoreChecked,
onSettingsClose,
onSettingsOpen,
@@ -339,6 +351,9 @@ export const MobileWorkStackScreen = ({
<WorkStackHeader
activeCount={data.activeCount}
onOpenSettings={onSettingsOpen}
onProjectSelect={onProjectSelect}
projects={data.projects}
selectedProjectId={data.selectedProjectId}
userInitials={userInitials}
/>
<main className="relative min-h-0 flex-1 overflow-hidden bg-[#f7f8f5] px-5 pt-[26px]">