Skip to content

Commit

Permalink
Add trash notes
Browse files Browse the repository at this point in the history
Signed-off-by: m4rc3l05 <15786310+M4RC3L05@users.noreply.github.com>
  • Loading branch information
M4RC3L05 committed May 25, 2024
1 parent 218e53a commit 2cbdffc
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 38 deletions.
16 changes: 9 additions & 7 deletions src/apps/api/routes/notes/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,27 @@ import type { Hono } from "hono";
import vine from "@vinejs/vine";
import { sql } from "@m4rc3l05/sqlite-tag";

const getNotesQuerySchema = vine.object({
includeDeleted: vine.string().optional(),
});
const getNotesQuerySchema = vine.object({ trashed: vine.string().optional() });
const getNotesQueryValidator = vine.compile(getNotesQuerySchema);

export const all = (app: Hono) => {
app.get("/", async (c) => {
const { includeDeleted } = await getNotesQueryValidator.validate(
const { trashed } = await getNotesQueryValidator.validate(
c.req.query(),
);
const showDeleted = typeof includeDeleted === "string";
const showTrashed = typeof trashed === "string";

const notes = c.get("db").all(
sql`
select
id, name, created_at, updated_at
${sql.if(showDeleted, () => sql`, deleted_at`)}
${sql.if(showTrashed, () => sql`, deleted_at`)}
from notes
${sql.if(showDeleted, () => sql`where deleted_at is null`)}
${
sql.ternary(showTrashed, () =>
sql`where deleted_at is not null`, () =>
sql`where deleted_at is null`)
}
order by updated_at desc`,
);

Expand Down
8 changes: 8 additions & 0 deletions src/apps/api/routes/notes/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ const deleteNoteParamsSchema = vine.object({ id: vine.string() });
const deleteNoteParamsValidator = vine.compile(deleteNoteParamsSchema);

export const del = (app: Hono) => {
app.delete("/trashed", (c) => {
c.get("db").execute(
sql`delete from notes where deleted_at is not null`,
);

return c.body(null, 204);
});

app.delete("/:id", async (c) => {
const { id } = await deleteNoteParamsValidator.validate(c.req.param());

Expand Down
6 changes: 5 additions & 1 deletion src/apps/web/routes/notes/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export const edit = (app: Hono) => {
signal: c.req.raw.signal,
});

return c.redirect(`/notes/${note.id}`);
if (!data.has("delete")) {
return c.redirect(`/notes/${note.id}`);
}

return c.redirect(c.req.header("Referer") ?? "/");
});
};
3 changes: 2 additions & 1 deletion src/apps/web/routes/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ export const index = (app: Hono) => {
app.get("/", async (c) => {
const { data: notes } = await c.get("services").notesService.getNotes({
signal: c.req.raw.signal,
trashed: false,
});

return c.render(<NotesIndexPage notes={notes} />);
return c.render(<NotesIndexPage notes={notes} trash={false} />);
});
};
2 changes: 2 additions & 0 deletions src/apps/web/routes/pages/mod.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Hono } from "hono";
import { index } from "#src/apps/web/routes/pages/index.tsx";
import { trash } from "#src/apps/web/routes/pages/trash.tsx";

export const pagesRoutes = () => {
const router = new Hono();

index(router);
trash(router);

return router;
};
21 changes: 21 additions & 0 deletions src/apps/web/routes/pages/trash.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Hono } from "hono";
import { NotesIndexPage } from "#src/apps/web/views/notes/pages/index.tsx";

export const trash = (app: Hono) => {
app.get("/trash", async (c) => {
const { data: notes } = await c.get("services").notesService.getNotes({
signal: c.req.raw.signal,
trashed: true,
});

return c.render(<NotesIndexPage notes={notes} trash={true} />);
});

app.post("/trash/delete", async (c) => {
await c.get("services").notesService.deleteTrashed({
signal: c.req.raw.signal,
});

return c.redirect(c.req.header("Referer") ?? "/");
});
};
22 changes: 20 additions & 2 deletions src/apps/web/services/notes-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,20 @@ import { BaseService } from "#src/apps/web/services/base-service.ts";

export class NotesService extends BaseService {
getNotes(
{ signal }: { signal: AbortSignal },
{ trashed, signal }: {
signal: AbortSignal;
trashed?: boolean;
},
): Promise<{ data: Omit<NotesTable, "content">[] }> {
return this.request({ path: "/api/notes", init: { signal } });
const searchParams = new URLSearchParams();
if (trashed) searchParams.set("trashed", "true");

return this.request({
path: `/api/notes${
searchParams.size > 0 ? `?${searchParams.toString()}` : ``
}`,
init: { signal },
});
}

getNote({ id, signal }: { id: string; signal: AbortSignal }) {
Expand Down Expand Up @@ -50,4 +61,11 @@ export class NotesService extends BaseService {
init: { signal, method: "DELETE" },
});
}

deleteTrashed({ signal }: { signal: AbortSignal }) {
return this.request({
path: `/api/notes/trashed`,
init: { signal, method: "DELETE" },
});
}
}
1 change: 1 addition & 0 deletions src/apps/web/views/notes/pages/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const NotesCreatePage: FC = () => (
<header>
<nav>
<a href="/">Home</a>
<a href="/trash">Trash 🗑</a>
</nav>

<h1>Create note</h1>
Expand Down
1 change: 1 addition & 0 deletions src/apps/web/views/notes/pages/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const NotesEditPage: FC<NotesEditPageProps> = ({ note }) => (
<header>
<nav>
<a href="/">Home</a>
<a href="/trash">Trash 🗑</a>
</nav>

<h1>Edit {note.name}</h1>
Expand Down
127 changes: 100 additions & 27 deletions src/apps/web/views/notes/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import type { NotesTable } from "#src/database/types/mod.ts";

type NotesIndexPageProps = {
notes: Omit<NotesTable, "content">[];
trash: boolean;
};

const ArticleItem: FC<{ note: NotesIndexPageProps["notes"][0] }> = (
{ note },
const ArticleItem: FC<
{ note: NotesIndexPageProps["notes"][0]; trash: boolean }
> = (
{ note, trash },
) => (
<article>
<h2>{note.name}</h2>
Expand All @@ -16,49 +19,119 @@ const ArticleItem: FC<{ note: NotesIndexPageProps["notes"][0] }> = (
Show
</a>

<dialog id={`dialog-${note.id}`}>
<p>Are you sure you want to delete note "{note.name}"?</p>
{trash
? (
<>
<dialog id={`dialog-${note.id}`}>
<p>Are you sure you want to delete note "{note.name}"?</p>

<form
style="display: inline; margin-right: 8px"
action={`/notes/${note.id}/delete`}
method="post"
>
<button type="submit">
Yes
</button>
</form>
<form
style="display: inline; margin-right: 8px"
action={`/notes/${note.id}/delete`}
method="post"
>
<button type="submit">
Yes
</button>
</form>

<form method="dialog" style="display: inline">
<button>No</button>
</form>
</dialog>
<form method="dialog" style="display: inline">
<button>No</button>
</form>
</dialog>

<button
style="display: inline; margin-right: 8px"
onclick={`getElementById("dialog-${note.id}").show()`}
>
Delete ⨯?
</button>
<button
style="display: inline; margin-right: 8px"
onclick={`getElementById("dialog-${note.id}").show()`}
>
Delete ⨯?
</button>

<form
method="post"
action={`/notes/${note.id}/edit`}
style="display: inline"
>
<input type="hidden" name="delete" value="false" />
<button>Restore</button>
</form>
</>
)
: (
<form
method="post"
action={`/notes/${note.id}/edit`}
style="display: inline"
>
<input type="hidden" name="delete" value="true" />
<button>Trash</button>
</form>
)}
</div>
</article>
);

export const NotesIndexPage: FC<NotesIndexPageProps> = ({ notes }) => (
export const NotesIndexPage: FC<NotesIndexPageProps> = ({ notes, trash }) => (
<>
<header>
<h1>Noted</h1>
<nav>
<a href="/" class={!trash ? "current" : ""}>Home</a>
<a href="/trash" class={trash ? "current" : ""}>Trash 🗑</a>
</nav>

<h1>{trash ? `Noted's Trash` : `Noted`}</h1>
</header>

<header
id="header-actions"
style="position: sticky; top: 0; z-index: 2; padding: 0px;"
>
<a href="/notes/create" class="button">Add note +</a>
{trash
? (
<>
<dialog id={`dialog-empty`}>
<p>
Are you sure you want to delete <strong>ALL TRASHED</strong>
{" "}
notes?
</p>

<form
style="display: inline; margin-right: 8px"
action={`/trash/delete`}
method="post"
>
<button type="submit">
Yes
</button>
</form>

<form
method="dialog"
style="display: inline"
>
<button>No</button>
</form>
</dialog>

<a
class="button"
style="margin-right: 8px"
onclick={`getElementById("dialog-empty").show()`}
>
Delete all ⨯?
</a>
</>
)
: (
<a href="/notes/create" class="button" style="margin-right: 8px">
Add note +
</a>
)}
</header>

<main>
{notes.map((note) => <ArticleItem note={note} />)}
{notes.map((note) => <ArticleItem note={note} trash={trash} />)}
{notes.length <= 0 ? <p>No notes</p> : undefined}
</main>
</>
Expand Down
1 change: 1 addition & 0 deletions src/apps/web/views/notes/pages/show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const NotesShowPage: FC<NotesShowPageProps> = ({ note, rendered }) => (
<header>
<nav>
<a href="/">Home</a>
<a href="/trash">Trash 🗑</a>
</nav>

<h1>{note.name}</h1>
Expand Down

0 comments on commit 2cbdffc

Please sign in to comment.