summaryrefslogblamecommitdiff
path: root/makima/frontend/src/routes/files.tsx
blob: 00c334d358c1650a0f0fa8e6e2f10cd8ed980971 (plain) (tree)
1
2
3
4
5
6
7
8




                                                            
                                                        
                                             
                                                                            




















































                                                                                












                                                      



                                                                          
                                                                                
                   
                                                                                                                   




                             













                                                                        















                                                                              
import { useState, useCallback, useEffect } from "react";
import { useParams, useNavigate } from "react-router";
import { Masthead } from "../components/Masthead";
import { FileList } from "../components/files/FileList";
import { FileDetail } from "../components/files/FileDetail";
import { CliInput } from "../components/files/CliInput";
import { useFiles } from "../hooks/useFiles";
import type { FileDetail as FileDetailType, BodyElement } from "../lib/api";

export default function FilesPage() {
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();
  const { files, loading, error, fetchFile, editFile, removeFile } = useFiles();
  const [fileDetail, setFileDetail] = useState<FileDetailType | null>(null);
  const [detailLoading, setDetailLoading] = useState(false);

  // Load file detail when URL has an id
  useEffect(() => {
    if (id) {
      setDetailLoading(true);
      fetchFile(id).then((detail) => {
        setFileDetail(detail);
        setDetailLoading(false);
      });
    } else {
      setFileDetail(null);
    }
  }, [id, fetchFile]);

  const handleSelectFile = useCallback(
    (fileId: string) => {
      navigate(`/files/${fileId}`);
    },
    [navigate]
  );

  const handleBack = useCallback(() => {
    navigate("/files");
  }, [navigate]);

  const handleDelete = useCallback(
    async (fileId: string) => {
      if (confirm("Are you sure you want to delete this file?")) {
        const success = await removeFile(fileId);
        if (success && id === fileId) {
          navigate("/files");
        }
      }
    },
    [removeFile, id, navigate]
  );

  const handleSave = useCallback(
    async (fileId: string, name: string, description: string) => {
      await editFile(fileId, { name, description });
      const detail = await fetchFile(fileId);
      setFileDetail(detail);
    },
    [editFile, fetchFile]
  );

  const handleBodyUpdate = useCallback(
    (body: BodyElement[], summary: string | null) => {
      if (fileDetail) {
        setFileDetail({
          ...fileDetail,
          body,
          summary,
        });
      }
    },
    [fileDetail]
  );

  return (
    <div className="relative z-10 h-screen flex flex-col overflow-hidden">
      <Masthead showTicker={false} showNav />

      <main className="flex-1 p-4 md:p-6 min-h-0 overflow-hidden flex flex-col">
        {error && (
          <div className="mb-4 p-3 border border-red-400/50 bg-red-400/10 text-red-400 font-mono text-sm shrink-0">
            {error}
          </div>
        )}

        {id && fileDetail ? (
          <div className="flex-1 flex flex-col min-h-0 overflow-hidden">
            <div className="flex-1 min-h-0 overflow-hidden">
              <FileDetail
                file={fileDetail}
                loading={detailLoading}
                onBack={handleBack}
                onSave={handleSave}
                onDelete={handleDelete}
              />
            </div>
            <div className="shrink-0">
              <CliInput fileId={id} onUpdate={handleBodyUpdate} />
            </div>
          </div>
        ) : id && detailLoading ? (
          <div className="panel h-full flex items-center justify-center">
            <div className="font-mono text-[#9bc3ff] text-sm">Loading...</div>
          </div>
        ) : (
          <FileList
            files={files}
            loading={loading}
            onSelect={handleSelectFile}
            onDelete={handleDelete}
          />
        )}
      </main>
    </div>
  );
}