feat: adicionar página de anotações arquivadas e componente de notificação

- Implementa a página de anotações arquivadas, que busca as notas
  arquivadas do usuário e as exibe utilizando o componente NotesPage.

- Cria o componente NotificationBell para gerenciar e exibir
  notificações de pagamentos, incluindo a formatação de datas e
  valores monetários. O componente também apresenta um sistema de
  tooltip e dropdown para interação do usuário.
This commit is contained in:
Felipe Coutinho
2025-12-24 19:36:39 +00:00
parent e7cb9c9db1
commit 3eca48c71a
23 changed files with 848 additions and 1029 deletions

View File

@@ -1,8 +1,8 @@
"use server";
import { anotacoes } from "@/db/schema";
import { type ActionResult, handleActionError } from "@/lib/actions/helpers";
import { revalidateForEntity } from "@/lib/actions/helpers";
import { handleActionError, revalidateForEntity } from "@/lib/actions/helpers";
import type { ActionResult } from "@/lib/actions/types";
import { uuidSchema } from "@/lib/schemas/common";
import { db } from "@/lib/db";
import { getUser } from "@/lib/auth/server";
@@ -142,3 +142,45 @@ export async function deleteNoteAction(
return handleActionError(error);
}
}
const arquivarNoteSchema = z.object({
id: uuidSchema("Anotação"),
arquivada: z.boolean(),
});
type NoteArquivarInput = z.infer<typeof arquivarNoteSchema>;
export async function arquivarAnotacaoAction(
input: NoteArquivarInput
): Promise<ActionResult> {
try {
const user = await getUser();
const data = arquivarNoteSchema.parse(input);
const [updated] = await db
.update(anotacoes)
.set({
arquivada: data.arquivada,
})
.where(and(eq(anotacoes.id, data.id), eq(anotacoes.userId, user.id)))
.returning({ id: anotacoes.id });
if (!updated) {
return {
success: false,
error: "Anotação não encontrada.",
};
}
revalidateForEntity("anotacoes");
return {
success: true,
message: data.arquivada
? "Anotação arquivada com sucesso."
: "Anotação desarquivada com sucesso."
};
} catch (error) {
return handleActionError(error);
}
}

View File

@@ -0,0 +1,14 @@
import { NotesPage } from "@/components/anotacoes/notes-page";
import { getUserId } from "@/lib/auth/server";
import { fetchArquivadasForUser } from "../data";
export default async function ArquivadasPage() {
const userId = await getUserId();
const notes = await fetchArquivadasForUser(userId);
return (
<main className="flex flex-col items-start gap-6">
<NotesPage notes={notes} isArquivadas={true} />
</main>
);
}

View File

@@ -1,6 +1,6 @@
import { anotacoes, type Anotacao } from "@/db/schema";
import { db } from "@/lib/db";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
export type Task = {
id: string;
@@ -14,12 +14,13 @@ export type NoteData = {
description: string;
type: "nota" | "tarefa";
tasks?: Task[];
arquivada: boolean;
createdAt: string;
};
export async function fetchNotesForUser(userId: string): Promise<NoteData[]> {
const noteRows = await db.query.anotacoes.findMany({
where: eq(anotacoes.userId, userId),
where: and(eq(anotacoes.userId, userId), eq(anotacoes.arquivada, false)),
orderBy: (note: typeof anotacoes.$inferSelect, { desc }: { desc: (field: unknown) => unknown }) => [desc(note.createdAt)],
});
@@ -42,6 +43,38 @@ export async function fetchNotesForUser(userId: string): Promise<NoteData[]> {
description: (note.description ?? "").trim(),
type: (note.type ?? "nota") as "nota" | "tarefa",
tasks,
arquivada: note.arquivada,
createdAt: note.createdAt.toISOString(),
};
});
}
export async function fetchArquivadasForUser(userId: string): Promise<NoteData[]> {
const noteRows = await db.query.anotacoes.findMany({
where: and(eq(anotacoes.userId, userId), eq(anotacoes.arquivada, true)),
orderBy: (note: typeof anotacoes.$inferSelect, { desc }: { desc: (field: unknown) => unknown }) => [desc(note.createdAt)],
});
return noteRows.map((note: Anotacao) => {
let tasks: Task[] | undefined;
// Parse tasks if they exist
if (note.tasks) {
try {
tasks = JSON.parse(note.tasks);
} catch (error) {
console.error("Failed to parse tasks for note", note.id, error);
tasks = undefined;
}
}
return {
id: note.id,
title: (note.title ?? "").trim(),
description: (note.description ?? "").trim(),
type: (note.type ?? "nota") as "nota" | "tarefa",
tasks,
arquivada: note.arquivada,
createdAt: note.createdAt.toISOString(),
};
});