feat(ui): Dragonflight redesign — screen components batch 1: screens-projects.jsx
This commit is contained in:
parent
3392a8944d
commit
ed3084e60f
1 changed files with 98 additions and 0 deletions
98
services/web-ui/public/screens-projects.jsx
Normal file
98
services/web-ui/public/screens-projects.jsx
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
// screens-projects.jsx — projects browse + project detail
|
||||||
|
|
||||||
|
const { PROJECTS: ALL_PROJECTS, ASSETS } = window.ZAMPP_DATA;
|
||||||
|
|
||||||
|
function Projects({ onOpenProject, navigate }) {
|
||||||
|
const [search, setSearch] = React.useState("");
|
||||||
|
const [view, setView] = React.useState("grid");
|
||||||
|
let projects = ALL_PROJECTS;
|
||||||
|
if (search) projects = projects.filter(p => p.name.toLowerCase().includes(search.toLowerCase()));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="page">
|
||||||
|
<div className="page-header">
|
||||||
|
<h1>Projects</h1>
|
||||||
|
<span className="subtitle">{projects.length} projects</span>
|
||||||
|
<div className="spacer" />
|
||||||
|
<div className="search" style={{ width: 240 }}>
|
||||||
|
<Icon name="search" className="search-icon" />
|
||||||
|
<input value={search} onChange={e => setSearch(e.target.value)} placeholder="Search projects…" />
|
||||||
|
</div>
|
||||||
|
<div className="tab-group">
|
||||||
|
<button className={view === "grid" ? "active" : ""} onClick={() => setView("grid")}><Icon name="grid" size={12} /></button>
|
||||||
|
<button className={view === "list" ? "active" : ""} onClick={() => setView("list")}><Icon name="list" size={12} /></button>
|
||||||
|
</div>
|
||||||
|
<button className="btn primary"><Icon name="plus" />New project</button>
|
||||||
|
</div>
|
||||||
|
<div className="page-body">
|
||||||
|
{view === "grid" ? (
|
||||||
|
<div className="projects-grid">
|
||||||
|
{projects.map(p => <ProjectCard key={p.id} project={p} onOpen={() => onOpenProject(p)} />)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="panel">
|
||||||
|
<div className="list-row head" style={{ padding: "12px 16px", gridTemplateColumns: "1fr 100px 120px 120px 100px 80px" }}>
|
||||||
|
<div>Project</div>
|
||||||
|
<div>Assets</div>
|
||||||
|
<div>Storage</div>
|
||||||
|
<div>Updated</div>
|
||||||
|
<div>Members</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
{projects.map(p => (
|
||||||
|
<div key={p.id} className="list-row" style={{ padding: "12px 16px", gridTemplateColumns: "1fr 100px 120px 120px 100px 80px", borderBottom: "1px solid var(--border)" }} onClick={() => onOpenProject(p)}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
||||||
|
<div style={{ width: 8, height: 32, borderRadius: 2, background: p.color }} />
|
||||||
|
<div className="name">{p.name}</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-sub">{p.assets}</div>
|
||||||
|
<div className="col-sub">{(p.assets * 1.8).toFixed(1)} GB</div>
|
||||||
|
<div className="col-sub">{p.updated}</div>
|
||||||
|
<div style={{ display: "flex" }}>
|
||||||
|
{["KM", "ZG", "JT"].slice(0, 3).map((a, i) => (
|
||||||
|
<div key={i} className="avatar" style={{ width: 22, height: 22, fontSize: 10, marginLeft: i > 0 ? -6 : 0, background: avatarColor(a), border: "2px solid var(--bg-1)" }}>{a}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button className="icon-btn" onClick={e => e.stopPropagation()}><Icon name="more" /></button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProjectCard({ project, onOpen }) {
|
||||||
|
const thumbs = ASSETS.slice(0, project.thumbs || 4);
|
||||||
|
return (
|
||||||
|
<div className="project-card" onClick={onOpen}>
|
||||||
|
<div className="project-thumb-grid">
|
||||||
|
{thumbs.map((a, i) => (
|
||||||
|
<div key={i} className="project-thumb-cell">
|
||||||
|
<FauxFrame seed={(a.seed + i) % 6} />
|
||||||
|
<div className="scanlines" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="project-card-body">
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||||
|
<span style={{ width: 10, height: 10, borderRadius: 2, background: project.color }} />
|
||||||
|
<span style={{ fontWeight: 600, fontSize: 14 }}>{project.name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="project-meta">
|
||||||
|
<span>{project.assets} assets</span>
|
||||||
|
<span>·</span>
|
||||||
|
<span>updated {project.updated}</span>
|
||||||
|
</div>
|
||||||
|
<div className="project-bar">
|
||||||
|
<div className="project-segment" style={{ width: "60%", background: "#5B7CFA" }} />
|
||||||
|
<div className="project-segment" style={{ width: "25%", background: "#2DD4A8" }} />
|
||||||
|
<div className="project-segment" style={{ width: "10%", background: "#FF3B30" }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Projects = Projects;
|
||||||
Loading…
Reference in a new issue