Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@
## 2026-10-02 - Add loading state to Save Appearance button
**Learning:** Adding a loading state to async forms is critical. The "Appearance" dialog lacked a loading state on its "Save" button, which could lead to confusion or double submissions if the API call is slow.
**Action:** When creating or modifying dialogs that submit data to the backend, always add a `loading` (or similar) state that disables the primary action buttons and provides visual feedback (e.g., `CircularProgress` and updating button text) during execution.

## 2023-10-09 - Embed Primary and Secondary Actions in Empty States
**Learning:** Embedding primary and secondary actions (e.g., Upload, New Folder) directly into empty states ("No files in your account yet." or "This folder is empty.") significantly reduces cognitive load and intuitively guides users on their next steps, as opposed to forcing them to find buttons elsewhere on the page.
**Action:** When creating empty states for lists or containers, always embed primary and secondary actions directly into the empty state component to seamlessly guide the user's next steps.
44 changes: 40 additions & 4 deletions web/src/pages/FilesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -585,23 +585,59 @@ export default function FilesPage() {
Loading files…
</Typography>
) : !err && !hasAnyContent ? (
<Paper variant="outlined" sx={{ p: 3, textAlign: "center" }}>
<Typography variant="body1" color="text.secondary" gutterBottom>
<Paper variant="outlined" sx={{ p: 4, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 2 }}>
<Typography variant="body1" color="text.secondary">
No files in your account yet.
</Typography>
<Typography variant="body2" color="text.secondary">
<Typography variant="body2" color="text.secondary" sx={{ maxWidth: 400 }}>
Upload from here, create a folder, or sync from the desktop app — files are listed from
the server folder for your signed-in email.
</Typography>
<Stack direction="row" spacing={2} justifyContent="center" sx={{ mt: 1 }}>
<Button
variant="contained"
component="label"
disabled={uploadBusy}
startIcon={uploadBusy ? <CircularProgress size={20} color="inherit" /> : <UploadIcon />}
>
{uploadBusy ? "Uploading..." : "Upload file"}
<input type="file" hidden onChange={onUpload} disabled={uploadBusy} />
</Button>
<Button
variant="outlined"
startIcon={<CreateNewFolderIcon />}
onClick={openNewFolderDialog}
>
New folder
</Button>
</Stack>
</Paper>
) : entries.length === 0 || (entries.length === 1 && entries[0].kind === "parent") ? (
<Paper variant="outlined">
<List disablePadding>
{currentFolder ? renderParentRow(goUp, true) : null}
<Box sx={{ p: 3, textAlign: "center" }}>
<Box sx={{ p: 4, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 2 }}>
<Typography variant="body2" color="text.secondary">
This folder is empty.
</Typography>
<Stack direction="row" spacing={2} justifyContent="center" sx={{ mt: 1 }}>
<Button
variant="contained"
component="label"
disabled={uploadBusy}
startIcon={uploadBusy ? <CircularProgress size={20} color="inherit" /> : <UploadIcon />}
>
{uploadBusy ? "Uploading..." : "Upload file"}
<input type="file" hidden onChange={onUpload} disabled={uploadBusy} />
</Button>
<Button
variant="outlined"
startIcon={<CreateNewFolderIcon />}
onClick={openNewFolderDialog}
>
New folder
</Button>
</Stack>
</Box>
</List>
</Paper>
Expand Down
Loading