Skip to content

Commit ab83620

Browse files
authored
feat: now with delete (#177)
* now with delete * addressing @gemini-code-assist comment * adding required items for code review, but most probably are not needed because the API does not require it
1 parent 30d620e commit ab83620

File tree

2 files changed

+96
-4
lines changed

2 files changed

+96
-4
lines changed

src/app/api/wiki/projects/route.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,29 @@ interface ApiProcessedProject {
1010
submittedAt: number;
1111
language: string;
1212
}
13+
// Payload for deleting a project cache
14+
interface DeleteProjectCachePayload {
15+
owner: string;
16+
repo: string;
17+
repo_type: string;
18+
language: string;
19+
}
20+
21+
/** Type guard to validate DeleteProjectCachePayload at runtime */
22+
function isDeleteProjectCachePayload(obj: any): obj is DeleteProjectCachePayload {
23+
return (
24+
obj != null &&
25+
typeof obj.owner === 'string' && obj.owner.trim() !== '' &&
26+
typeof obj.repo === 'string' && obj.repo.trim() !== '' &&
27+
typeof obj.repo_type === 'string' && obj.repo_type.trim() !== '' &&
28+
typeof obj.language === 'string' && obj.language.trim() !== ''
29+
);
30+
}
1331

1432
// Ensure this matches your Python backend configuration
1533
const PYTHON_BACKEND_URL = process.env.PYTHON_BACKEND_HOST || 'http://localhost:8001';
1634
const PROJECTS_API_ENDPOINT = `${PYTHON_BACKEND_URL}/api/processed_projects`;
35+
const CACHE_API_ENDPOINT = `${PYTHON_BACKEND_URL}/api/wiki_cache`;
1736

1837
export async function GET() {
1938
try {
@@ -50,4 +69,35 @@ export async function GET() {
5069
{ status: 503 } // Service Unavailable
5170
);
5271
}
53-
}
72+
}
73+
74+
export async function DELETE(request: Request) {
75+
try {
76+
const body: unknown = await request.json();
77+
if (!isDeleteProjectCachePayload(body)) {
78+
return NextResponse.json(
79+
{ error: 'Invalid request body: owner, repo, repo_type, and language are required and must be non-empty strings.' },
80+
{ status: 400 }
81+
);
82+
}
83+
const { owner, repo, repo_type, language } = body;
84+
const params = new URLSearchParams({ owner, repo, repo_type, language });
85+
const response = await fetch(`${CACHE_API_ENDPOINT}?${params}`, {
86+
method: 'DELETE',
87+
headers: { 'Content-Type': 'application/json' },
88+
});
89+
if (!response.ok) {
90+
let errorBody = { error: response.statusText };
91+
try {
92+
errorBody = await response.json();
93+
} catch {}
94+
console.error(`Error deleting project cache (${CACHE_API_ENDPOINT}): ${response.status} - ${JSON.stringify(errorBody)}`);
95+
return NextResponse.json(errorBody, { status: response.status });
96+
}
97+
return NextResponse.json({ message: 'Project deleted successfully' });
98+
} catch (error: unknown) {
99+
console.error('Error in DELETE /api/wiki/projects:', error);
100+
const message = error instanceof Error ? error.message : 'An unknown error occurred';
101+
return NextResponse.json({ error: `Failed to delete project: ${message}` }, { status: 500 });
102+
}
103+
}

src/components/ProcessedProjects.tsx

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,32 @@ export default function ProcessedProjects({
101101
setSearchQuery('');
102102
};
103103

104+
const handleDelete = async (project: ProcessedProject) => {
105+
if (!confirm(`Are you sure you want to delete project ${project.name}?`)) {
106+
return;
107+
}
108+
try {
109+
const response = await fetch('/api/wiki/projects', {
110+
method: 'DELETE',
111+
headers: { 'Content-Type': 'application/json' },
112+
body: JSON.stringify({
113+
owner: project.owner,
114+
repo: project.repo,
115+
repo_type: project.repo_type,
116+
language: project.language,
117+
}),
118+
});
119+
if (!response.ok) {
120+
const errorBody = await response.json().catch(() => ({ error: response.statusText }));
121+
throw new Error(errorBody.error || response.statusText);
122+
}
123+
setProjects(prev => prev.filter(p => p.id !== project.id));
124+
} catch (e: unknown) {
125+
console.error('Failed to delete project:', e);
126+
alert(`Failed to delete project: ${e instanceof Error ? e.message : 'Unknown error'}`);
127+
}
128+
};
129+
104130
return (
105131
<div className={`${className}`}>
106132
{showHeader && (
@@ -167,9 +193,17 @@ export default function ProcessedProjects({
167193

168194
{!isLoading && !error && filteredProjects.length > 0 && (
169195
<div className={viewMode === 'card' ? 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4' : 'space-y-2'}>
170-
{filteredProjects.map((project) => (
196+
{filteredProjects.map((project) => (
171197
viewMode === 'card' ? (
172-
<div key={project.id} className="p-4 border border-[var(--border-color)] rounded-lg bg-[var(--card-bg)] shadow-sm hover:shadow-md transition-all duration-200 hover:scale-[1.02]">
198+
<div key={project.id} className="relative p-4 border border-[var(--border-color)] rounded-lg bg-[var(--card-bg)] shadow-sm hover:shadow-md transition-all duration-200 hover:scale-[1.02]">
199+
<button
200+
type="button"
201+
onClick={() => handleDelete(project)}
202+
className="absolute top-2 right-2 text-[var(--muted)] hover:text-[var(--foreground)]"
203+
title="Delete project"
204+
>
205+
<FaTimes className="h-4 w-4" />
206+
</button>
173207
<Link
174208
href={`/${project.owner}/${project.repo}?type=${project.repo_type}&language=${project.language}`}
175209
className="block"
@@ -191,7 +225,15 @@ export default function ProcessedProjects({
191225
</Link>
192226
</div>
193227
) : (
194-
<div key={project.id} className="p-3 border border-[var(--border-color)] rounded-lg bg-[var(--card-bg)] hover:bg-[var(--background)] transition-colors">
228+
<div key={project.id} className="relative p-3 border border-[var(--border-color)] rounded-lg bg-[var(--card-bg)] hover:bg-[var(--background)] transition-colors">
229+
<button
230+
type="button"
231+
onClick={() => handleDelete(project)}
232+
className="absolute top-2 right-2 text-[var(--muted)] hover:text-[var(--foreground)]"
233+
title="Delete project"
234+
>
235+
<FaTimes className="h-4 w-4" />
236+
</button>
195237
<Link
196238
href={`/${project.owner}/${project.repo}?type=${project.repo_type}&language=${project.language}`}
197239
className="flex items-center justify-between"

0 commit comments

Comments
 (0)