Skip to content

Commit 30f040d

Browse files
ok, la les fichier sont bien gen, mais genre jpp les view et donc les ed
1 parent d9d949f commit 30f040d

1 file changed

Lines changed: 50 additions & 55 deletions

File tree

  • src/app/(app)/projects/[id]/codespace/files/[[...path]]

src/app/(app)/projects/[id]/codespace/files/[[...path]]/page.tsx

Lines changed: 50 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import remarkGfm from 'remark-gfm';
3434
import { Badge } from '@/components/ui/badge';
3535
import NextImage from 'next/image';
3636
import { useForm } from 'react-hook-form';
37-
import { useActionState } from 'react';
37+
import { useActionState, startTransition } from 'react';
3838
import { zodResolver } from '@hookform/resolvers/zod';
3939
import * as z from 'zod';
4040

@@ -104,32 +104,31 @@ function FileExplorerContent() {
104104
const newFileForm = useForm<NewFileFormValues>({ resolver: zodResolver(newFileFormSchema), defaultValues: { fileName: '', initialContent: ''}});
105105
const newFolderForm = useForm<NewFolderFormValues>({ resolver: zodResolver(newFolderFormSchema), defaultValues: { folderName: ''}});
106106

107-
108-
const forceReloadContent = useCallback(async (newPath?: string) => {
109-
const pathToLoad = newPath !== undefined ? newPath : currentPath;
107+
const loadContent = useCallback(async (pathToLoad: string) => {
110108
if (!project || !project.githubRepoName || authLoading || isLoadingProject) {
111-
return;
109+
return;
112110
}
113111
setIsLoadingPathContent(true);
114112
setError(null);
115-
113+
setFileData(null);
114+
setIsViewingFile(false);
115+
116116
try {
117-
const isFileViewCandidate = (newPath ? newPath.includes('.') : (filePathArray.length > 0 && (isViewingFile || filePathArray[filePathArray.length-1].includes('.')))) && (!newPath || !newPath.endsWith('/'));
117+
const extension = pathToLoad.includes('.') ? getFileExtension(pathToLoad.split('/').pop()!) : null;
118+
const isFileCandidate = extension && !pathToLoad.endsWith('/');
118119

119-
if (isFileViewCandidate && (fileData || newPath?.includes('.'))) {
120-
const actualFilePath = newPath || fileData!.path;
121-
const fetchedFileData = await getFileContentAction(projectUuid, actualFilePath);
120+
if (isFileCandidate) {
121+
const fetchedFileData = await getFileContentAction(projectUuid, pathToLoad);
122122
if ('content' in fetchedFileData && fetchedFileData.sha) {
123-
const extension = getFileExtension(actualFilePath.split('/').pop()!);
124123
let fileDisplayType: 'md' | 'image' | 'html' | 'text' | 'other' = 'other';
125124
if (MARKDOWN_EXTENSIONS.includes(`.${extension}`)) fileDisplayType = 'md';
126125
else if (IMAGE_EXTENSIONS.includes(`.${extension}`)) fileDisplayType = 'image';
127126
else if (extension === 'html') fileDisplayType = 'html';
128127
else if (TEXT_EXTENSIONS.includes(`.${extension}`) || fetchedFileData.encoding === 'utf-8' || !fetchedFileData.encoding) fileDisplayType = 'text';
129128

130129
setFileData({
131-
name: actualFilePath.split('/').pop()!,
132-
path: actualFilePath,
130+
name: pathToLoad.split('/').pop()!,
131+
path: pathToLoad,
133132
content: fetchedFileData.content,
134133
type: fileDisplayType,
135134
downloadUrl: fetchedFileData.download_url,
@@ -138,34 +137,29 @@ function FileExplorerContent() {
138137
});
139138
setEditingContent(fetchedFileData.content);
140139
setIsViewingFile(true);
141-
if (newPath) router.push(`/projects/${projectUuid}/codespace/files/${newPath}`);
142140
} else {
143-
setError(fetchedFileData.error || "Failed to refresh file content.");
141+
setError(fetchedFileData.error || "Failed to load file content.");
144142
setIsViewingFile(false);
145-
setFileData(null);
146143
}
147-
} else {
148-
setIsViewingFile(false);
149-
setFileData(null);
144+
} else { // Is a directory or root
150145
const dirData = await getRepoContentsAction(projectUuid, pathToLoad);
151146
if (Array.isArray(dirData)) {
152147
setContents(dirData.sort((a, b) => {
153148
if (a.type === 'dir' && b.type !== 'dir') return -1;
154149
if (a.type !== 'dir' && b.type === 'dir') return 1;
155150
return a.name.localeCompare(b.name);
156151
}));
157-
if (newPath !== undefined && newPath !== currentPath) router.push(`/projects/${projectUuid}/codespace/files${newPath ? '/' + newPath : ''}`);
158152
} else {
159153
setError(dirData.error || "Failed to fetch repository contents.");
160154
}
161155
}
162156
} catch (e: any) {
163-
setError(e.message || 'An unexpected error occurred while reloading content.');
157+
setError(e.message || 'An unexpected error occurred while loading content.');
164158
toast({ variant: 'destructive', title: 'Error', description: e.message });
165159
} finally {
166160
setIsLoadingPathContent(false);
167161
}
168-
}, [project, currentPath, projectUuid, authLoading, isLoadingProject, toast, filePathArray, isViewingFile, fileData, router]);
162+
}, [project, projectUuid, authLoading, isLoadingProject, toast]);
169163

170164

171165
useEffect(() => {
@@ -184,6 +178,7 @@ function FileExplorerContent() {
184178
setProject(fetchedProject);
185179
if (!fetchedProject.githubRepoName) {
186180
setError("Project is not linked to a GitHub repository.");
181+
setIsLoadingPathContent(false);
187182
}
188183
}
189184
})
@@ -197,13 +192,12 @@ function FileExplorerContent() {
197192
});
198193
}, [projectUuid, user, authLoading, router]);
199194

195+
200196
useEffect(() => {
201-
if (project && project.githubRepoName && !authLoading && !isLoadingProject) {
202-
forceReloadContent(currentPath);
203-
} else if (project && !project.githubRepoName && !isLoadingProject) {
204-
setIsLoadingPathContent(false);
197+
if (project && project.githubRepoName && !isLoadingProject) {
198+
loadContent(currentPath);
205199
}
206-
}, [project, authLoading, isLoadingProject, currentPath, forceReloadContent]);
200+
}, [project, isLoadingProject, currentPath, loadContent]);
207201

208202

209203
const handleSaveFile = async () => {
@@ -230,7 +224,7 @@ function FileExplorerContent() {
230224
toast({ title: "File Created", description: `${values.fileName} created successfully.`});
231225
setIsCreateFileModalOpen(false);
232226
newFileForm.reset();
233-
await forceReloadContent();
227+
loadContent(currentPath);
234228
} else {
235229
toast({ variant: "destructive", title: "Creation Error", description: result.error || "Failed to create file."});
236230
}
@@ -246,7 +240,7 @@ function FileExplorerContent() {
246240
toast({ title: "Folder Created", description: `${values.folderName} created successfully.`});
247241
setIsCreateFolderModalOpen(false);
248242
newFolderForm.reset();
249-
await forceReloadContent();
243+
loadContent(currentPath);
250244
} else {
251245
toast({ variant: "destructive", title: "Creation Error", description: result.error || "Failed to create folder."});
252246
}
@@ -262,10 +256,10 @@ function FileExplorerContent() {
262256
setContentToDelete(null);
263257
if (isViewingFile && fileData?.path === contentToDelete.path) {
264258
const parentPath = currentPath.substring(0, currentPath.lastIndexOf('/'));
265-
await forceReloadContent(parentPath);
266259
router.push(`/projects/${projectUuid}/codespace/files${parentPath ? '/' + parentPath : ''}`);
260+
// loadContent will be called by useEffect due to currentPath change
267261
} else {
268-
await forceReloadContent();
262+
loadContent(currentPath);
269263
}
270264
} else {
271265
toast({ variant: "destructive", title: "Deletion Error", description: result.error || "Failed to delete file."});
@@ -278,7 +272,7 @@ function FileExplorerContent() {
278272
formData.append('projectUuid', project.uuid);
279273
formData.append('prompt', values.prompt);
280274
formData.append('basePath', currentPath);
281-
ReactStartTransition(() => {
275+
startTransition(() => {
282276
aiScaffoldFormAction(formData);
283277
});
284278
};
@@ -289,13 +283,13 @@ function FileExplorerContent() {
289283
toast({ title: "AI Scaffold Success", description: aiScaffoldState.message });
290284
setIsAiScaffoldModalOpen(false);
291285
aiScaffoldForm.reset();
292-
forceReloadContent();
286+
loadContent(currentPath);
293287
}
294288
if (aiScaffoldState.error) {
295289
toast({ variant: "destructive", title: "AI Scaffold Error", description: aiScaffoldState.error });
296290
}
297291
}
298-
}, [aiScaffoldState, isAiScaffolding, toast, forceReloadContent, aiScaffoldForm]);
292+
}, [aiScaffoldState, isAiScaffolding, toast, loadContent, currentPath, aiScaffoldForm]);
299293

300294

301295
const getBreadcrumbs = () => {
@@ -310,9 +304,10 @@ function FileExplorerContent() {
310304

311305
const breadcrumbs = getBreadcrumbs();
312306
const canEditCurrentFile = fileData && (fileData.type === 'text' || fileData.type === 'md' || fileData.type === 'html');
313-
const isLoading = authLoading || isLoadingProject || isLoadingPathContent;
307+
const isLoadingPage = authLoading || isLoadingProject || isLoadingPathContent;
308+
314309

315-
if (isLoading) {
310+
if (isLoadingPage && !error && !project?.githubRepoName) { // Initial loading state before project details are known
316311
return (
317312
<div className="space-y-6">
318313
<div className="flex justify-between items-center">
@@ -345,7 +340,7 @@ function FileExplorerContent() {
345340
<div className="flex items-center gap-2 flex-wrap">
346341
<Dialog open={isCreateFileModalOpen} onOpenChange={setIsCreateFileModalOpen}>
347342
<DialogTrigger asChild>
348-
<Button variant="outline" size="sm" disabled={isViewingFile || !project?.githubRepoName}>
343+
<Button variant="outline" size="sm" disabled={isViewingFile || !project?.githubRepoName || isLoadingPathContent}>
349344
<FilePlus className="mr-2 h-4 w-4" /> Create File
350345
</Button>
351346
</DialogTrigger>
@@ -372,7 +367,7 @@ function FileExplorerContent() {
372367
</Dialog>
373368
<Dialog open={isCreateFolderModalOpen} onOpenChange={setIsCreateFolderModalOpen}>
374369
<DialogTrigger asChild>
375-
<Button variant="outline" size="sm" disabled={isViewingFile || !project?.githubRepoName}>
370+
<Button variant="outline" size="sm" disabled={isViewingFile || !project?.githubRepoName || isLoadingPathContent}>
376371
<FolderPlus className="mr-2 h-4 w-4" /> Create Folder
377372
</Button>
378373
</DialogTrigger>
@@ -396,7 +391,7 @@ function FileExplorerContent() {
396391
</Dialog>
397392
<Dialog open={isAiScaffoldModalOpen} onOpenChange={setIsAiScaffoldModalOpen}>
398393
<DialogTrigger asChild>
399-
<Button variant="outline" size="sm" disabled={isViewingFile || !project?.githubRepoName}>
394+
<Button variant="outline" size="sm" disabled={isViewingFile || !project?.githubRepoName || isLoadingPathContent}>
400395
<Sparkles className="mr-2 h-4 w-4 text-primary" /> Scaffold with AI
401396
</Button>
402397
</DialogTrigger>
@@ -420,7 +415,7 @@ function FileExplorerContent() {
420415
</form>
421416
</DialogContent>
422417
</Dialog>
423-
<Button variant="outline" size="sm" onClick={() => forceReloadContent()} title="Refresh content" disabled={isLoadingPathContent || !project?.githubRepoName}>
418+
<Button variant="outline" size="sm" onClick={() => loadContent(currentPath)} title="Refresh content" disabled={isLoadingPathContent || !project?.githubRepoName}>
424419
{isLoadingPathContent ? <Loader2 className="mr-2 h-4 w-4 animate-spin"/> : <RefreshCw className="mr-2 h-4 w-4" />} Refresh
425420
</Button>
426421
</div>
@@ -442,7 +437,7 @@ function FileExplorerContent() {
442437
{project?.githubRepoName && (
443438
<div className="flex items-center space-x-1 text-sm text-muted-foreground mt-2 overflow-x-auto whitespace-nowrap pb-1 border-t pt-2">
444439
{breadcrumbs.map((crumb, index) => (
445-
<span key={index} className="inline-flex items-center">
440+
<span key={crumb.path} className="inline-flex items-center">
446441
{index > 0 && <ChevronRight className="h-4 w-4 inline-block mx-1 flex-shrink-0" />}
447442
{ (index === breadcrumbs.length - 1 && (isViewingFile || currentPath === crumb.path)) ? (
448443
<span className="font-medium text-foreground flex items-center">
@@ -460,14 +455,17 @@ function FileExplorerContent() {
460455
)}
461456
</CardHeader>
462457
<CardContent>
463-
{error && (
458+
{isLoadingPathContent && !error ? (
459+
<div className="flex justify-center items-center py-10">
460+
<Loader2 className="h-8 w-8 animate-spin text-primary" />
461+
</div>
462+
) : error ? (
464463
<Alert variant="destructive">
465464
<AlertTriangle className="h-4 w-4" />
466465
<AlertTitle>Error Loading Content</AlertTitle>
467466
<AlertDescription>{error}</AlertDescription>
468467
</Alert>
469-
)}
470-
{!error && !project?.githubRepoName && (
468+
) : !project?.githubRepoName ? (
471469
<Alert>
472470
<AlertTriangle className="h-4 w-4" />
473471
<AlertTitle>Repository Not Linked</AlertTitle>
@@ -478,8 +476,7 @@ function FileExplorerContent() {
478476
</Button>
479477
</AlertDescription>
480478
</Alert>
481-
)}
482-
{!error && project?.githubRepoName && isViewingFile && fileData && (
479+
) : isViewingFile && fileData ? (
483480
<div>
484481
<div className="flex justify-between items-center mb-4">
485482
<h3 className="text-xl font-semibold">{fileData.name}</h3>
@@ -557,8 +554,7 @@ function FileExplorerContent() {
557554
</Alert>
558555
)}
559556
</div>
560-
)}
561-
{!error && project?.githubRepoName && !isViewingFile && contents.length > 0 && (
557+
) : !isViewingFile && contents.length > 0 ? (
562558
<Table>
563559
<TableHeader>
564560
<TableRow>
@@ -609,7 +605,7 @@ function FileExplorerContent() {
609605
<Trash2 className="h-4 w-4" />
610606
</Button>
611607
</AlertDialogTrigger>
612-
{contentToDelete?.sha === item.sha && (
608+
{contentToDelete?.path === item.path && ( // Check path instead of sha for key consistency
613609
<AlertDialogContent>
614610
<AlertDialogHeader>
615611
<AlertDialogTitle>Delete File: "{contentToDelete.name}"?</AlertDialogTitle>
@@ -636,21 +632,19 @@ function FileExplorerContent() {
636632
))}
637633
</TableBody>
638634
</Table>
639-
)}
640-
{!error && project?.githubRepoName && !isViewingFile && contents.length === 0 && !currentPath && (
635+
) : !isViewingFile && contents.length === 0 && !currentPath ? (
641636
<Alert>
642637
<FileCode className="h-4 w-4" />
643638
<AlertTitle>Empty Repository</AlertTitle>
644-
<AlertDescription>This repository ('{project.githubRepoName}') appears to be empty. You can create files or folders using the buttons above.</AlertDescription>
639+
<AlertDescription>This repository ('{project?.githubRepoName || 'Unknown'}') appears to be empty. You can create files or folders using the buttons above.</AlertDescription>
645640
</Alert>
646-
)}
647-
{!error && project?.githubRepoName && !isViewingFile && contents.length === 0 && currentPath && (
641+
) : !isViewingFile && contents.length === 0 && currentPath ? (
648642
<Alert>
649643
<Folder className="h-4 w-4" />
650644
<AlertTitle>Empty Directory</AlertTitle>
651645
<AlertDescription>This directory ('/{currentPath}') is empty. You can create files or folders here using the buttons above.</AlertDescription>
652646
</Alert>
653-
)}
647+
) : null }
654648
</CardContent>
655649
</Card>
656650

@@ -692,3 +686,4 @@ export default function GitHubFilesPage() {
692686
)
693687
}
694688

689+

0 commit comments

Comments
 (0)