fix(codebase): remove legacy single-codebase hint description from api endpoints
This commit is contained in:
@@ -48,95 +48,124 @@ export default function CodeTab() {
|
|||||||
const showLoading = loading && !anatomy;
|
const showLoading = loading && !anatomy;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ minHeight: "100vh", background: THEME.canvasGradient, fontFamily: THEME.font, padding: "36px 48px" }}>
|
<div
|
||||||
|
style={{
|
||||||
|
minHeight: "100vh",
|
||||||
|
background: THEME.canvasGradient,
|
||||||
|
fontFamily: THEME.font,
|
||||||
|
padding: "36px 48px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div style={{ maxWidth: 1400, margin: "0 auto" }}>
|
<div style={{ maxWidth: 1400, margin: "0 auto" }}>
|
||||||
<PageHeader title="Codebase" subtitle="Explore the repository files for your application." />
|
<PageHeader
|
||||||
<div style={grid}>
|
title="Codebase"
|
||||||
{/* ── Left rail ── */}
|
subtitle="Explore the repository files for your application."
|
||||||
<section style={leftCol}>
|
/>
|
||||||
{showLoading && (
|
<div style={grid}>
|
||||||
|
{/* ── Left rail ── */}
|
||||||
|
<section style={leftCol}>
|
||||||
|
{showLoading && (
|
||||||
<Card>
|
<Card>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 8, color: THEME.mid, fontSize: "0.875rem" }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 8,
|
||||||
|
color: THEME.mid,
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Loader2 size={15} className="animate-spin" /> Loading…
|
<Loader2 size={15} className="animate-spin" /> Loading…
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
{error && !showLoading && (
|
{error && !showLoading && (
|
||||||
<Card>
|
<Card>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 8, color: THEME.danger, fontSize: "0.875rem" }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 8,
|
||||||
|
color: THEME.danger,
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<AlertCircle size={15} /> {error}
|
<AlertCircle size={15} /> {error}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{anatomy && (
|
{anatomy && (
|
||||||
<>
|
<>
|
||||||
{/* Code Files */}
|
{/* Code Files */}
|
||||||
<RailGroup title="Code files" count={codebases?.length ?? 0}>
|
<RailGroup title="Code files" count={codebases?.length ?? 0}>
|
||||||
{codebases && codebases.length === 0 && (
|
{codebases && codebases.length === 0 && (
|
||||||
<RailEmpty>
|
<RailEmpty>
|
||||||
{reason === "no_repo" ? (
|
{reason === "no_repo" ? (
|
||||||
<>
|
<>
|
||||||
No codebase yet.{" "}
|
No codebase yet.{" "}
|
||||||
<span style={nudge}>
|
<span style={nudge}>
|
||||||
Try: "Start building my app"
|
Try: "Start building my app"
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
Repo is empty — push a first commit.{" "}
|
Repo is empty — push a first commit.{" "}
|
||||||
<span style={nudge}>
|
<span style={nudge}>
|
||||||
Try: "Scaffold a Next.js app"
|
Try: "Scaffold a Next.js app"
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</RailEmpty>
|
</RailEmpty>
|
||||||
)}
|
)}
|
||||||
{codebases?.map((cb) => {
|
{codebases?.map((cb) => {
|
||||||
return (
|
return (
|
||||||
<article key={cb.id} style={codebaseTile}>
|
<article key={cb.id} style={codebaseTile}>
|
||||||
<div style={tileHeader}>
|
<div style={tileHeader}>
|
||||||
<span style={chevronCell}>
|
<span style={chevronCell}>
|
||||||
<ChevronDown size={13} style={{ color: THEME.mid }} />
|
<ChevronDown
|
||||||
</span>
|
size={13}
|
||||||
<Box
|
style={{ color: THEME.mid }}
|
||||||
size={13}
|
/>
|
||||||
style={{ color: THEME.mid, flexShrink: 0 }}
|
</span>
|
||||||
/>
|
<Box
|
||||||
<div style={{ minWidth: 0, textAlign: "left" }}>
|
size={13}
|
||||||
<div style={tileLabel}>{cb.label}</div>
|
style={{ color: THEME.mid, flexShrink: 0 }}
|
||||||
{cb.hint && <div style={tileHint}>{cb.hint}</div>}
|
/>
|
||||||
|
<div style={{ minWidth: 0, textAlign: "left" }}>
|
||||||
|
<div style={tileLabel}>{cb.label}</div>
|
||||||
|
{cb.hint && <div style={tileHint}>{cb.hint}</div>}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div style={tileBody}>
|
||||||
<div style={tileBody}>
|
<GiteaFileTree
|
||||||
<GiteaFileTree
|
projectId={projectId}
|
||||||
projectId={projectId}
|
rootPath={cb.path}
|
||||||
rootPath={cb.path}
|
selectedPath={
|
||||||
selectedPath={
|
selection?.type === "file" &&
|
||||||
selection?.type === "file" &&
|
selection.codebaseId === cb.id
|
||||||
selection.codebaseId === cb.id
|
? selection.path
|
||||||
? selection.path
|
: undefined
|
||||||
: undefined
|
}
|
||||||
}
|
onSelectFile={(p) =>
|
||||||
onSelectFile={(p) =>
|
setSelection({
|
||||||
setSelection({
|
type: "file",
|
||||||
type: "file",
|
codebaseId: cb.id,
|
||||||
codebaseId: cb.id,
|
path: p,
|
||||||
path: p,
|
})
|
||||||
})
|
}
|
||||||
}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
</article>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
</RailGroup>
|
||||||
</RailGroup>
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
</section>
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* ── Right pane ── */}
|
{/* ── Right pane ── */}
|
||||||
<aside style={rightCol}>
|
<aside style={rightCol}>
|
||||||
<Card
|
<Card
|
||||||
padding={0}
|
padding={0}
|
||||||
style={{
|
style={{
|
||||||
@@ -150,13 +179,24 @@ export default function CodeTab() {
|
|||||||
<GiteaFileViewer projectId={projectId} path={selection.path} />
|
<GiteaFileViewer projectId={projectId} path={selection.path} />
|
||||||
)}
|
)}
|
||||||
{!selection && (
|
{!selection && (
|
||||||
<div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", color: THEME.muted, fontSize: "0.85rem", padding: "32px 16px", textAlign: "center" }}>
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
color: THEME.muted,
|
||||||
|
fontSize: "0.85rem",
|
||||||
|
padding: "32px 16px",
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
Pick a codebase file on the left.
|
Pick a codebase file on the left.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ async function discoverCodebases(giteaRepo: string): Promise<{
|
|||||||
id: "root",
|
id: "root",
|
||||||
label: repoName,
|
label: repoName,
|
||||||
path: "",
|
path: "",
|
||||||
hint: "Single-codebase project — repository root.",
|
hint: "",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,14 +35,17 @@ interface GiteaItem {
|
|||||||
type: "file" | "dir" | "symlink";
|
type: "file" | "dir" | "symlink";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function giteaList(repo: string, path: string): Promise<GiteaItem[] | null> {
|
async function giteaList(
|
||||||
|
repo: string,
|
||||||
|
path: string,
|
||||||
|
): Promise<GiteaItem[] | null> {
|
||||||
const encoded = path ? encodeURIComponent(path).replace(/%2F/g, "/") : "";
|
const encoded = path ? encodeURIComponent(path).replace(/%2F/g, "/") : "";
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${GITEA_API_URL}/api/v1/repos/${repo}/contents/${encoded}`,
|
`${GITEA_API_URL}/api/v1/repos/${repo}/contents/${encoded}`,
|
||||||
{
|
{
|
||||||
headers: { Authorization: `token ${GITEA_API_TOKEN}` },
|
headers: { Authorization: `token ${GITEA_API_TOKEN}` },
|
||||||
next: { revalidate: 30 },
|
next: { revalidate: 30 },
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
if (res.status === 404) return null;
|
if (res.status === 404) return null;
|
||||||
if (!res.ok) throw new Error(`Gitea ${res.status} listing ${repo}/${path}`);
|
if (!res.ok) throw new Error(`Gitea ${res.status} listing ${repo}/${path}`);
|
||||||
@@ -52,7 +55,7 @@ async function giteaList(repo: string, path: string): Promise<GiteaItem[] | null
|
|||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
_req: Request,
|
_req: Request,
|
||||||
{ params }: { params: Promise<{ projectId: string }> }
|
{ params }: { params: Promise<{ projectId: string }> },
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const { projectId } = await params;
|
const { projectId } = await params;
|
||||||
@@ -65,7 +68,7 @@ export async function GET(
|
|||||||
`SELECT p.data FROM fs_projects p
|
`SELECT p.data FROM fs_projects p
|
||||||
JOIN fs_users u ON u.id = p.user_id
|
JOIN fs_users u ON u.id = p.user_id
|
||||||
WHERE p.id = $1 AND u.data->>'email' = $2 LIMIT 1`,
|
WHERE p.id = $1 AND u.data->>'email' = $2 LIMIT 1`,
|
||||||
[projectId, session.user.email]
|
[projectId, session.user.email],
|
||||||
);
|
);
|
||||||
if (rows.length === 0) {
|
if (rows.length === 0) {
|
||||||
return NextResponse.json({ error: "Project not found" }, { status: 404 });
|
return NextResponse.json({ error: "Project not found" }, { status: 404 });
|
||||||
@@ -89,15 +92,17 @@ export async function GET(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const appsDir = root.find(item => item.type === "dir" && item.name === "apps");
|
const appsDir = root.find(
|
||||||
|
(item) => item.type === "dir" && item.name === "apps",
|
||||||
|
);
|
||||||
let codebases: Codebase[] = [];
|
let codebases: Codebase[] = [];
|
||||||
|
|
||||||
if (appsDir) {
|
if (appsDir) {
|
||||||
const appsChildren = await giteaList(giteaRepo, "apps");
|
const appsChildren = await giteaList(giteaRepo, "apps");
|
||||||
if (appsChildren) {
|
if (appsChildren) {
|
||||||
codebases = appsChildren
|
codebases = appsChildren
|
||||||
.filter(item => item.type === "dir")
|
.filter((item) => item.type === "dir")
|
||||||
.map(item => ({
|
.map((item) => ({
|
||||||
id: item.name,
|
id: item.name,
|
||||||
label: item.name,
|
label: item.name,
|
||||||
path: `apps/${item.name}`,
|
path: `apps/${item.name}`,
|
||||||
@@ -114,7 +119,7 @@ export async function GET(
|
|||||||
id: "root",
|
id: "root",
|
||||||
label: repoName,
|
label: repoName,
|
||||||
path: "",
|
path: "",
|
||||||
hint: "Single-codebase project — repository root.",
|
hint: "",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -122,6 +127,9 @@ export async function GET(
|
|||||||
return NextResponse.json({ codebases });
|
return NextResponse.json({ codebases });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("[codebases API]", err);
|
console.error("[codebases API]", err);
|
||||||
return NextResponse.json({ error: "Failed to list codebases" }, { status: 500 });
|
return NextResponse.json(
|
||||||
|
{ error: "Failed to list codebases" },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user