Skip to content

Commit

Permalink
add all notes table, adjust schema and add helpers to fetch and delet…
Browse files Browse the repository at this point in the history
…e note(s)
  • Loading branch information
bercivarga committed Mar 9, 2024
1 parent 999e0d2 commit eb4bfc1
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 49 deletions.
2 changes: 1 addition & 1 deletion app/(platform)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function RootLayout({
return (
<div className="flex h-screen divide-x">
<SideNav />
<div className="px-6 py-2">{children}</div>
<div className="w-full p-4">{children}</div>
</div>
);
}
15 changes: 1 addition & 14 deletions app/(platform)/notes/[noteId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
import { redirect } from "next/navigation";

import { prisma } from "@/lib/db";

async function getNote(noteId: string) {
try {
const note = await prisma.note.findUnique({
where: { id: noteId },
});

return note;
} catch (error) {
return null;
}
}
import { getNote } from "@/helpers/notes/getNote";

export default async function EditNotePage({
params,
}: {
params: { noteId: string };
}) {
const { noteId } = params;

const note = await getNote(noteId);

if (!note) {
Expand Down
80 changes: 46 additions & 34 deletions app/(platform)/notes/page.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
import { auth } from "@clerk/nextjs";
import { CrossCircledIcon } from "@radix-ui/react-icons";
import { Edit2Icon, Edit3Icon } from "lucide-react";
import Link from "next/link";
import { redirect } from "next/navigation";

import { prisma } from "@/lib/db";

async function getAllNotes() {
const { userId } = auth();

if (!userId) {
redirect("/sign-in");
}

try {
const dbUser = await prisma.user.findUnique({
where: { clerkId: userId },
include: { notes: true },
});

if (!dbUser) {
return null;
}

return dbUser.notes;
} catch (error) {
return null;
}
}
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { getAllNotes } from "@/helpers/notes/getAllNotes";

export default async function AllNotesPage() {
const notes = await getAllNotes();

return (
<main>
<h1>All notes</h1>
<ul>
{notes?.map((note) => (
<li key={note.id}>
<Link href={`/notes/${note.id}`}>{note.title}</Link>
</li>
))}
</ul>
<Table>
<TableCaption>A list of your recent notes.</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Title</TableHead>
<TableHead className="w-[300px]">Snippet</TableHead>
<TableHead className="w-[200px]">Tags</TableHead>
<TableHead className="text-right">Updated</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{notes?.map((note) => (
<TableRow key={note.id}>
<TableCell>
<Link href={`/notes/${note.id}`}>{note.title}</Link>
</TableCell>
<TableCell>
<Link href={`/notes/${note.id}`}>
{note.content.slice(0, 50)}
</Link>
</TableCell>
<TableCell>
{note.tags.map((tag) => tag.name).join(", ")}
</TableCell>
<TableCell className="text-right">
<Link href={`/notes/${note.id}`}>
{new Date(note.updatedAt).toLocaleString()}
</Link>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</main>
);
}
123 changes: 123 additions & 0 deletions components/ui/table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/no-unknown-property */

import * as React from "react";

import { cn } from "@/lib/utils";

const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
));
Table.displayName = "Table";

const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
));
TableHeader.displayName = "TableHeader";

const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
));
TableBody.displayName = "TableBody";

const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t bg-slate-100/50 font-medium dark:bg-slate-800/50 [&>tr]:last:border-b-0",
className
)}
{...props}
/>
));
TableFooter.displayName = "TableFooter";

const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-slate-100/50 data-[state=selected]:bg-slate-100 dark:hover:bg-slate-800/50 dark:data-[state=selected]:bg-slate-800",
className
)}
{...props}
/>
));
TableRow.displayName = "TableRow";

const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-10 px-2 text-left align-middle font-medium text-slate-500 dark:text-slate-400 [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className
)}
{...props}
/>
));
TableHead.displayName = "TableHead";

const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className
)}
{...props}
/>
));
TableCell.displayName = "TableCell";

const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-slate-500 dark:text-slate-400", className)}
{...props}
/>
));
TableCaption.displayName = "TableCaption";

export {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
};
32 changes: 32 additions & 0 deletions helpers/notes/deleteNote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"use server";

import { auth } from "@clerk/nextjs";

import { prisma } from "@/lib/db";

export async function deleteNote(noteId: string) {
const { userId } = auth();

if (!userId) {
return null;
}

try {
const dbUser = await prisma.user.findUnique({
where: { clerkId: userId },
});

if (!dbUser) {
return null;
}

const note = await prisma.note.update({
where: { id: noteId, authorId: dbUser.id },
data: { deleted: true },
});

return note;
} catch (error) {
return null;
}
}
35 changes: 35 additions & 0 deletions helpers/notes/getAllNotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use server";

import { auth } from "@clerk/nextjs";
import { redirect } from "next/navigation";

import { prisma } from "@/lib/db";

export async function getAllNotes() {
const { userId } = auth();

if (!userId) {
redirect("/sign-in");
}

try {
const dbUser = await prisma.user.findUnique({
where: { clerkId: userId },
});

if (!dbUser) {
return null;
}

const notes = await prisma.note.findMany({
where: { authorId: dbUser.id, deleted: false },
include: { tags: true },
orderBy: { updatedAt: "desc" },
take: 10,
});

return notes;
} catch (error) {
return null;
}
}
31 changes: 31 additions & 0 deletions helpers/notes/getNote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use server";

import { auth } from "@clerk/nextjs";

import { prisma } from "@/lib/db";

export async function getNote(noteId: string) {
const { userId } = auth();

if (!userId) {
return null;
}

try {
const dbUser = await prisma.user.findUnique({
where: { clerkId: userId },
});

if (!dbUser) {
return null;
}

const note = await prisma.note.findUnique({
where: { id: noteId, authorId: dbUser.id },
});

return note;
} catch (error) {
return null;
}
}
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ model Note {
sentimentAnalysis SentimentAnalysis? // One-to-one relationship: one note can have one sentiment analysis
relatedNotes Note[] @relation("RelatedNotes") // Many-to-many relationship: a note can be related to multiple other notes
relatedTo Note[] @relation("RelatedNotes") // Reverse relation for related notes
deleted Boolean @default(false)
@@index([authorId], name: "author_index")
}

0 comments on commit eb4bfc1

Please sign in to comment.