refactor: strip sidebar down to project name + status only
Removed all product layer sections (Build, Layouts, Infrastructure, Growth, Monetize, Support, Analytics) from the left sidebar — these are now handled by the in-page left nav inside each tab. Sidebar now shows: logo, Projects/Activity/Settings global nav, project name + colored status dot when inside a project, and the user avatar/sign out at the bottom. Nothing else. Cleaned up all dead code: SectionHeading, SectionRow, SectionDivider, SURFACE_LABELS, SURFACE_ICONS, AppEntry interface, apps state, apps fetch, surfaces/infraApps variables. Made-with: Cursor
This commit is contained in:
@@ -14,104 +14,8 @@ interface ProjectData {
|
|||||||
productName?: string;
|
productName?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
giteaRepo?: string;
|
|
||||||
giteaRepoUrl?: string;
|
|
||||||
surfaces?: string[];
|
|
||||||
surfaceThemes?: Record<string, string>;
|
|
||||||
apps?: Array<{ name: string; path: string; coolifyServiceUuid?: string | null; domain?: string | null }>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AppEntry {
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Section helpers ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function SectionHeading({ label, collapsed }: { label: string; collapsed: boolean }) {
|
|
||||||
if (collapsed) return null;
|
|
||||||
return (
|
|
||||||
<div style={{
|
|
||||||
fontSize: "0.58rem", fontWeight: 700, color: "#b5b0a6",
|
|
||||||
letterSpacing: "0.1em", textTransform: "uppercase",
|
|
||||||
padding: "14px 12px 5px",
|
|
||||||
}}>
|
|
||||||
{label}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SectionRow({
|
|
||||||
icon, label, href, dim, collapsed,
|
|
||||||
}: {
|
|
||||||
icon: string;
|
|
||||||
label: string;
|
|
||||||
href?: string;
|
|
||||||
dim?: boolean;
|
|
||||||
collapsed: boolean;
|
|
||||||
}) {
|
|
||||||
const style: React.CSSProperties = {
|
|
||||||
display: "flex", alignItems: "center",
|
|
||||||
justifyContent: collapsed ? "center" : "flex-start",
|
|
||||||
gap: 8, padding: collapsed ? "7px 0" : "5px 12px",
|
|
||||||
borderRadius: 5, textDecoration: "none",
|
|
||||||
color: dim ? "#c5c0b8" : "#4a4640",
|
|
||||||
fontSize: "0.78rem", fontWeight: 450,
|
|
||||||
transition: "background 0.1s",
|
|
||||||
width: "100%", boxSizing: "border-box" as const,
|
|
||||||
};
|
|
||||||
|
|
||||||
const inner = (
|
|
||||||
<>
|
|
||||||
<span style={{ fontSize: "0.72rem", opacity: 0.6, flexShrink: 0, width: collapsed ? "auto" : 14, textAlign: "center" }}>
|
|
||||||
{icon}
|
|
||||||
</span>
|
|
||||||
{!collapsed && (
|
|
||||||
<span style={{
|
|
||||||
overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
|
|
||||||
fontStyle: dim ? "italic" : "normal",
|
|
||||||
}}>
|
|
||||||
{label}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (href) {
|
|
||||||
return (
|
|
||||||
<Link href={href} title={collapsed ? label : undefined} style={style}
|
|
||||||
onMouseEnter={e => { if (!dim) (e.currentTarget as HTMLElement).style.background = "#f6f4f0"; }}
|
|
||||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = "transparent"; }}
|
|
||||||
>
|
|
||||||
{inner}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div title={collapsed ? label : undefined} style={{ ...style, cursor: "default" }}>
|
|
||||||
{inner}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SectionDivider() {
|
|
||||||
return <div style={{ height: 1, background: "#eae6de", margin: "8px 12px" }} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Surface label map ────────────────────────────────────────────────────────
|
|
||||||
const SURFACE_LABELS: Record<string, string> = {
|
|
||||||
"marketing": "Marketing site",
|
|
||||||
"web-app": "Web app",
|
|
||||||
"admin": "Admin panel",
|
|
||||||
"api": "API layer",
|
|
||||||
};
|
|
||||||
|
|
||||||
const SURFACE_ICONS: Record<string, string> = {
|
|
||||||
"marketing": "◎",
|
|
||||||
"web-app": "⬡",
|
|
||||||
"admin": "◫",
|
|
||||||
"api": "⌁",
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── Main sidebar ─────────────────────────────────────────────────────────────
|
// ── Main sidebar ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -128,13 +32,11 @@ export function VIBNSidebar({ workspace }: VIBNSidebarProps) {
|
|||||||
|
|
||||||
// Project-specific data
|
// Project-specific data
|
||||||
const [project, setProject] = useState<ProjectData | null>(null);
|
const [project, setProject] = useState<ProjectData | null>(null);
|
||||||
const [apps, setApps] = useState<AppEntry[]>([]);
|
|
||||||
|
|
||||||
// Global projects list (used when NOT inside a project)
|
// Global projects list (used when NOT inside a project)
|
||||||
const [projects, setProjects] = useState<Array<{ id: string; productName: string; status?: string }>>([]);
|
const [projects, setProjects] = useState<Array<{ id: string; productName: string; status?: string }>>([]);
|
||||||
|
|
||||||
const activeProjectId = pathname?.match(/\/project\/([^/]+)/)?.[1] ?? null;
|
const activeProjectId = pathname?.match(/\/project\/([^/]+)/)?.[1] ?? null;
|
||||||
const activeTab = pathname?.match(/\/project\/[^/]+\/([^/]+)/)?.[1] ?? null;
|
|
||||||
|
|
||||||
// Restore collapse state
|
// Restore collapse state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -161,17 +63,12 @@ export function VIBNSidebar({ workspace }: VIBNSidebarProps) {
|
|||||||
|
|
||||||
// Fetch project-specific data when inside a project
|
// Fetch project-specific data when inside a project
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!activeProjectId) { setProject(null); setApps([]); return; }
|
if (!activeProjectId) { setProject(null); return; }
|
||||||
|
|
||||||
fetch(`/api/projects/${activeProjectId}`)
|
fetch(`/api/projects/${activeProjectId}`)
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(d => setProject(d.project ?? null))
|
.then(d => setProject(d.project ?? null))
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
|
||||||
fetch(`/api/projects/${activeProjectId}/apps`)
|
|
||||||
.then(r => r.json())
|
|
||||||
.then(d => setApps(d.apps ?? []))
|
|
||||||
.catch(() => {});
|
|
||||||
}, [activeProjectId]);
|
}, [activeProjectId]);
|
||||||
|
|
||||||
const isProjects = !activeProjectId && (pathname?.includes("/projects") || pathname?.includes("/project"));
|
const isProjects = !activeProjectId && (pathname?.includes("/projects") || pathname?.includes("/project"));
|
||||||
@@ -193,11 +90,6 @@ export function VIBNSidebar({ workspace }: VIBNSidebarProps) {
|
|||||||
|
|
||||||
const base = `/${workspace}/project/${activeProjectId}`;
|
const base = `/${workspace}/project/${activeProjectId}`;
|
||||||
|
|
||||||
// Surfaces locked in on design page
|
|
||||||
const surfaces = project?.surfaces ?? [];
|
|
||||||
// Coolify/monorepo apps
|
|
||||||
const infraApps = project?.apps ?? [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav style={{
|
<nav style={{
|
||||||
width: w, height: "100vh",
|
width: w, height: "100vh",
|
||||||
@@ -287,135 +179,36 @@ export function VIBNSidebar({ workspace }: VIBNSidebarProps) {
|
|||||||
<div style={{ flex: 1, overflow: "auto", paddingBottom: 8 }}>
|
<div style={{ flex: 1, overflow: "auto", paddingBottom: 8 }}>
|
||||||
|
|
||||||
{activeProjectId && project ? (
|
{activeProjectId && project ? (
|
||||||
/* ── PROJECT VIEW: 7 product layer sections ── */
|
/* ── PROJECT VIEW: name + status only ── */
|
||||||
<>
|
<>
|
||||||
{/* Project name */}
|
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<div style={{ padding: "4px 12px 10px" }}>
|
<div style={{ padding: "6px 12px 8px" }}>
|
||||||
<div style={{ fontSize: "0.82rem", fontWeight: 700, color: "#1a1a1a", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
<div style={{ fontSize: "0.82rem", fontWeight: 700, color: "#1a1a1a", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
||||||
{project.productName || project.name || "Project"}
|
{project.productName || project.name || "Project"}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 5, marginTop: 3 }}>
|
<div style={{ display: "flex", alignItems: "center", gap: 5, marginTop: 3 }}>
|
||||||
<span style={{
|
<span style={{
|
||||||
width: 6, height: 6, borderRadius: "50%", flexShrink: 0,
|
width: 6, height: 6, borderRadius: "50%", flexShrink: 0, display: "inline-block",
|
||||||
background: project.status === "live" ? "#2e7d32"
|
background: project.status === "live" ? "#2e7d32"
|
||||||
: project.status === "building" ? "#3d5afe"
|
: project.status === "building" ? "#3d5afe"
|
||||||
: "#d4a04a",
|
: "#d4a04a",
|
||||||
display: "inline-block",
|
|
||||||
}} />
|
}} />
|
||||||
<span style={{ fontSize: "0.68rem", color: "#8a8478" }}>
|
<span style={{ fontSize: "0.68rem", color: "#8a8478" }}>
|
||||||
{project.status === "live" ? "Live"
|
{project.status === "live" ? "Live" : project.status === "building" ? "Building" : "Defining"}
|
||||||
: project.status === "building" ? "Building"
|
|
||||||
: "Defining"}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{collapsed && (
|
||||||
{/* ── Build ── */}
|
<div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}>
|
||||||
<SectionHeading label="Build" collapsed={collapsed} />
|
<span style={{
|
||||||
{apps.length > 0 ? (
|
width: 7, height: 7, borderRadius: "50%", display: "inline-block",
|
||||||
apps.map(app => (
|
background: project.status === "live" ? "#2e7d32"
|
||||||
<SectionRow
|
: project.status === "building" ? "#3d5afe"
|
||||||
key={app.name}
|
: "#d4a04a",
|
||||||
icon="▢"
|
}} title={project.productName || project.name} />
|
||||||
label={app.name}
|
</div>
|
||||||
href={`${base}/build?app=${encodeURIComponent(app.name)}&root=${encodeURIComponent(app.path)}`}
|
|
||||||
collapsed={collapsed}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<SectionRow icon="▢" label="No apps yet" dim href={`${base}/build`} collapsed={collapsed} />
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
|
|
||||||
{/* ── Layouts ── */}
|
|
||||||
<SectionHeading label="Layouts" collapsed={collapsed} />
|
|
||||||
{surfaces.length > 0 ? (
|
|
||||||
surfaces.map(s => (
|
|
||||||
<SectionRow
|
|
||||||
key={s}
|
|
||||||
icon={SURFACE_ICONS[s] ?? "◌"}
|
|
||||||
label={SURFACE_LABELS[s] ?? s}
|
|
||||||
href={`${base}/design?surface=${encodeURIComponent(s)}`}
|
|
||||||
collapsed={collapsed}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<SectionRow icon="◌" label="Not configured" dim href={`${base}/design`} collapsed={collapsed} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
|
|
||||||
{/* ── Infrastructure ── */}
|
|
||||||
<SectionHeading label="Infrastructure" collapsed={collapsed} />
|
|
||||||
<SectionRow
|
|
||||||
icon="⬡"
|
|
||||||
label="Builds"
|
|
||||||
href={`${base}/infrastructure?tab=builds`}
|
|
||||||
dim={infraApps.length === 0}
|
|
||||||
collapsed={collapsed}
|
|
||||||
/>
|
|
||||||
<SectionRow
|
|
||||||
icon="◫"
|
|
||||||
label="Databases"
|
|
||||||
href={`${base}/infrastructure?tab=databases`}
|
|
||||||
dim
|
|
||||||
collapsed={collapsed}
|
|
||||||
/>
|
|
||||||
<SectionRow
|
|
||||||
icon="◎"
|
|
||||||
label="Services"
|
|
||||||
href={`${base}/infrastructure?tab=services`}
|
|
||||||
dim
|
|
||||||
collapsed={collapsed}
|
|
||||||
/>
|
|
||||||
<SectionRow
|
|
||||||
icon="≡"
|
|
||||||
label="Environment"
|
|
||||||
href={`${base}/infrastructure?tab=environment`}
|
|
||||||
dim
|
|
||||||
collapsed={collapsed}
|
|
||||||
/>
|
|
||||||
<SectionRow
|
|
||||||
icon="◬"
|
|
||||||
label="Domains"
|
|
||||||
href={`${base}/infrastructure?tab=domains`}
|
|
||||||
dim={!infraApps.some(a => a.domain)}
|
|
||||||
collapsed={collapsed}
|
|
||||||
/>
|
|
||||||
<SectionRow
|
|
||||||
icon="≈"
|
|
||||||
label="Logs"
|
|
||||||
href={`${base}/infrastructure?tab=logs`}
|
|
||||||
dim={infraApps.length === 0}
|
|
||||||
collapsed={collapsed}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
|
|
||||||
{/* ── Growth ── */}
|
|
||||||
<SectionHeading label="Growth" collapsed={collapsed} />
|
|
||||||
<SectionRow icon="↗" label="Not set up" dim href={`${base}/grow`} collapsed={collapsed} />
|
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
|
|
||||||
{/* ── Monetize ── */}
|
|
||||||
<SectionHeading label="Monetize" collapsed={collapsed} />
|
|
||||||
<SectionRow icon="◉" label="Not set up" dim collapsed={collapsed} />
|
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
|
|
||||||
{/* ── Support ── */}
|
|
||||||
<SectionHeading label="Support" collapsed={collapsed} />
|
|
||||||
<SectionRow icon="?" label="Not set up" dim collapsed={collapsed} />
|
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
|
|
||||||
{/* ── Analytics ── */}
|
|
||||||
<SectionHeading label="Analytics" collapsed={collapsed} />
|
|
||||||
<SectionRow icon="∿" label="Not set up" dim href={`${base}/insights`} collapsed={collapsed} />
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
/* ── GLOBAL VIEW: projects list ── */
|
/* ── GLOBAL VIEW: projects list ── */
|
||||||
|
|||||||
Reference in New Issue
Block a user