aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-02-07 22:12:18 +0000
committerMohamedBassem <me@mbassem.com>2024-02-07 22:12:18 +0000
commitcdc05f85a6dc676e8af1227a56f65d6452488d82 (patch)
treeb3d1e133d5435e129637067749d0b7e6e55b6c01
parent8970b3a5375ccfd9b41c8a08722a2fc6bbbe3af9 (diff)
downloadkarakeep-cdc05f85a6dc676e8af1227a56f65d6452488d82.tar.zst
[feature] Render tags in the link card
-rw-r--r--web/app/bookmarks/components/LinkCard.tsx9
-rw-r--r--web/app/bookmarks/page.tsx5
-rw-r--r--web/app/layout.tsx4
-rw-r--r--web/components/ui/badge.tsx36
-rw-r--r--web/lib/api.ts12
-rw-r--r--web/lib/services/links.ts65
-rw-r--r--web/lib/types/api/links.ts2
-rw-r--r--web/lib/types/api/tags.ts6
8 files changed, 106 insertions, 33 deletions
diff --git a/web/app/bookmarks/components/LinkCard.tsx b/web/app/bookmarks/components/LinkCard.tsx
index 907acd19..da59d9da 100644
--- a/web/app/bookmarks/components/LinkCard.tsx
+++ b/web/app/bookmarks/components/LinkCard.tsx
@@ -1,5 +1,6 @@
"use client";
+import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
@@ -73,7 +74,13 @@ export default function LinkCard({ link }: { link: ZBookmarkedLink }) {
{link.details?.title ?? parsedUrl.host}
</Link>
</ImageCardTitle>
- <ImageCardBody />
+ <ImageCardBody className="py-2 overflow-clip">
+ {link.tags.map((t) => (
+ <Badge variant="default" className="bg-gray-300 text-gray-500" key={t.id}>
+ #{t.name}
+ </Badge>
+ ))}
+ </ImageCardBody>
<ImageCardFooter>
<div className="flex justify-between text-gray-500">
<div className="my-auto">
diff --git a/web/app/bookmarks/page.tsx b/web/app/bookmarks/page.tsx
index f0efa2e4..89a26122 100644
--- a/web/app/bookmarks/page.tsx
+++ b/web/app/bookmarks/page.tsx
@@ -1,5 +1,10 @@
import AddLink from "./components/AddLink";
import LinksGrid from "./components/LinksGrid";
+import type { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Remember - Bookmarks",
+};
export default async function Bookmarks() {
return (
diff --git a/web/app/layout.tsx b/web/app/layout.tsx
index a6543b1c..a2d34046 100644
--- a/web/app/layout.tsx
+++ b/web/app/layout.tsx
@@ -7,8 +7,8 @@ import { Toaster } from "@/components/ui/toaster";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
+ title: "Remember",
+ description: "Your AI powered second brain",
};
export default function RootLayout({
diff --git a/web/components/ui/badge.tsx b/web/components/ui/badge.tsx
new file mode 100644
index 00000000..d3d5d604
--- /dev/null
+++ b/web/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import * as React from "react";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "@/lib/utils";
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+export interface BadgeProps
+ extends React.HTMLAttributes<HTMLDivElement>,
+ VariantProps<typeof badgeVariants> {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
+ );
+}
+
+export { Badge, badgeVariants };
diff --git a/web/lib/api.ts b/web/lib/api.ts
index 2304826b..56686cde 100644
--- a/web/lib/api.ts
+++ b/web/lib/api.ts
@@ -15,18 +15,6 @@ export type FetchError = {
message?: string;
};
-async function doRequest<Schema extends ZodTypeAny>(
- _path: string,
- respSchema: Schema,
- _opts: RequestInit | undefined,
-): Promise<[z.infer<typeof respSchema>, undefined] | [undefined, FetchError]>;
-
-async function doRequest<_Schema>(
- _path: string,
- _respSchema: undefined,
- _opts: RequestInit | undefined,
-): Promise<[undefined, undefined] | [undefined, FetchError]>;
-
type InputSchema<T> = T extends ZodTypeAny ? T : undefined;
async function doRequest<T>(
diff --git a/web/lib/services/links.ts b/web/lib/services/links.ts
index dbcbe9c4..d273b118 100644
--- a/web/lib/services/links.ts
+++ b/web/lib/services/links.ts
@@ -1,5 +1,43 @@
import { LinkCrawlerQueue } from "@remember/shared/queues";
import prisma from "@remember/db";
+import { ZBookmarkedLink } from "@/lib/types/api/links";
+
+const defaultLinkFields = {
+ id: true,
+ url: true,
+ createdAt: true,
+ details: {
+ select: {
+ title: true,
+ description: true,
+ imageUrl: true,
+ favicon: true,
+ },
+ },
+ tags: {
+ include: {
+ tag: true,
+ },
+ },
+};
+
+async function dummyPrismaReturnType() {
+ return await prisma.bookmarkedLink.findFirstOrThrow({
+ select: defaultLinkFields,
+ });
+}
+
+function toZodSchema(
+ link: Awaited<ReturnType<typeof dummyPrismaReturnType>>,
+): ZBookmarkedLink {
+ return {
+ id: link.id,
+ url: link.url,
+ createdAt: link.createdAt,
+ details: link.details,
+ tags: link.tags.map((t) => t.tag),
+ };
+}
export async function unbookmarkLink(linkId: string, userId: string) {
await prisma.bookmarkedLink.delete({
@@ -16,6 +54,7 @@ export async function bookmarkLink(url: string, userId: string) {
url,
userId,
},
+ select: defaultLinkFields,
});
// Enqueue crawling request
@@ -24,26 +63,16 @@ export async function bookmarkLink(url: string, userId: string) {
url: link.url,
});
- return link;
+ return toZodSchema(link);
}
export async function getLinks(userId: string) {
- return await prisma.bookmarkedLink.findMany({
- where: {
- userId,
- },
- select: {
- id: true,
- url: true,
- createdAt: true,
- details: {
- select: {
- title: true,
- description: true,
- imageUrl: true,
- favicon: true,
- },
+ return (
+ await prisma.bookmarkedLink.findMany({
+ where: {
+ userId,
},
- },
- });
+ select: defaultLinkFields,
+ })
+ ).map(toZodSchema);
}
diff --git a/web/lib/types/api/links.ts b/web/lib/types/api/links.ts
index 644589b4..f84445f6 100644
--- a/web/lib/types/api/links.ts
+++ b/web/lib/types/api/links.ts
@@ -1,4 +1,5 @@
import { z } from "zod";
+import { zBookmarkTagSchema } from "@/lib/types/api/tags";
export const zBookmarkedLinkSchema = z.object({
id: z.string(),
@@ -13,6 +14,7 @@ export const zBookmarkedLinkSchema = z.object({
favicon: z.string().url().nullish(),
})
.nullish(),
+ tags: z.array(zBookmarkTagSchema),
});
export type ZBookmarkedLink = z.infer<typeof zBookmarkedLinkSchema>;
diff --git a/web/lib/types/api/tags.ts b/web/lib/types/api/tags.ts
new file mode 100644
index 00000000..f2d2bc18
--- /dev/null
+++ b/web/lib/types/api/tags.ts
@@ -0,0 +1,6 @@
+import { z } from "zod";
+
+export const zBookmarkTagSchema = z.object({
+ id: z.string(),
+ name: z.string(),
+});