Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom hooks for collections #3

Merged
merged 4 commits into from
Jan 8, 2025
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: 2 additions & 2 deletions public/sitemap-0.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url><loc>https://memer.shobhitnagpal.com/icon.ico</loc><lastmod>2025-01-05T15:46:59.631Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
</urlset>
<url><loc>https://memer.shobhitnagpal.com/icon.ico</loc><lastmod>2025-01-08T08:13:37.369Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
</urlset>
19 changes: 19 additions & 0 deletions src/app/dashboard/_components/collection-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";

interface CollectionCardProps {
name: string;
onClick: () => void;
}

export function CollectionCard({ name, onClick }: CollectionCardProps) {
return (
<div
className="border border-gray-300 rounded-lg p-4 shadow-sm hover:shadow-md dark:shadow-sm dark:hover:shadow-md dark:hover:shadow-gray-500 transition-shadow duration-300 ease-in-out cursor-pointer dark:bg-background"
onClick={() => onClick()}
>
<p className="text-2xl font-semibold text-gray-800 text-center dark:text-white">
{name}
</p>
</div>
);
}
32 changes: 12 additions & 20 deletions src/app/dashboard/_components/collection-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,25 @@ import {
FormMessage,
} from "@/components/ui/form";
import { useToast } from "@/components/ui/use-toast";
import { useEffect, useState } from "react";
import { useDashboardCtx } from "@/context/DashboardContext";
import { useState } from "react";
import { usePostCollection } from "@/hooks/collections/usePostCollection";

const collectionFormSchema = z.object({
name: z.string().min(3, {
message: "Name must be at least 3 characters",
}),
});

export default function CollectionDialog() {
interface CollectionDialogProps {
onSuccess: () => Promise<void>;
}

export default function CollectionDialog({ onSuccess }: CollectionDialogProps) {
const { toast } = useToast();
const { refreshCollections } = useDashboardCtx();
const [loading, setLoading] = useState<boolean>(false);
const { postCollection, loading } = usePostCollection();

const [open, setOpen] = useState<boolean>(false);

const collectionForm = useForm<z.infer<typeof collectionFormSchema>>({
resolver: zodResolver(collectionFormSchema),
defaultValues: {
Expand All @@ -43,21 +48,9 @@ export default function CollectionDialog() {
});

async function onSubmit(values: z.infer<typeof collectionFormSchema>) {
setLoading(true);
try {
const req = await fetch("/api/collection", {
method: "POST",
body: JSON.stringify({
collectionName: values.name,
}),
});
if (req.status === 201) {
await refreshCollections();
return toast({
title: "Created collection!",
description: `${values.name} was created successfully.`,
});
}
await postCollection(values.name);
await onSuccess();
} catch (err: any) {
console.error(err);
return toast({
Expand All @@ -67,7 +60,6 @@ export default function CollectionDialog() {
});
} finally {
setOpen(false);
setLoading(false);
collectionForm.reset();
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/app/dashboard/_components/empty-collections.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react";
import CollectionDialog from "./collection-dialog";

interface EmptyCollectionProps {
onCreateSuccess: () => Promise<void>;
}

export function EmptyCollections({ onCreateSuccess }: EmptyCollectionProps) {
return (
<>
<div className="flex items-center">
<h1 className="text-lg font-semibold md:text-2xl">Collections</h1>
</div>
<div
className="flex flex-1 items-center justify-center rounded-lg border border-dashed shadow-sm"
x-chunk="dashboard-02-chunk-1"
>
<div className="flex flex-col items-center gap-5 text-center p-10">
<h3 className="text-3xl font-bold tracking-tight">
You have no collections
</h3>
<p className="text-sm text-muted-foreground">
You can start saving memes to a collection
</p>
<CollectionDialog onSuccess={onCreateSuccess} />
</div>
</div>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";
import { EditCollection } from "./edit-collection";
import { DeleteCollection } from "./delete-collection";
import Link from "next/link";
import { ChevronLeft } from "lucide-react";

interface CollectionHeaderProps {
collectionId: string;
collectionName: string;
onEditCollectionSuccess: (id: string) => Promise<void>;
}

export function CollectionHeader({
collectionId,
collectionName,
onEditCollectionSuccess,
}: CollectionHeaderProps) {
return (
<div className="flex justify-between items-center mb-4">
<div className="flex items-center justify-between gap-4">
<h1 className="text-2xl font-bold text-gray-800 dark:text-white">
{collectionName}
</h1>
<EditCollection
collectionId={collectionId}
name={collectionName}
onSuccess={onEditCollectionSuccess}
/>
<DeleteCollection collectionId={collectionId} />
</div>
<Link
href="/dashboard/collections"
className="flex items-center text-gray-600 hover:text-gray-800 transition-colors duration-200 dark:text-white"
>
<ChevronLeft className="w-5 h-5 mr-1" />
<span className="hidden sm:block text-xl">Back to Collections</span>
<span className="block sm:hidden text-lg">Back</span>
</Link>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Image from "next/image";
import React from "react";
import MoreInfo from "./more-info";

interface CollectionMemeCardProps {
url: string;
name: string;
description: string;
collectionId: string;
memeCollectionId: string;
onDeleteSuccess: (id: string) => Promise<void>;
}

export function CollectionMemeCard({
url,
name,
description,
collectionId,
memeCollectionId,
onDeleteSuccess,
}: CollectionMemeCardProps) {
return (
<div className="border rounded-lg p-4 shadow-sm dark:bg-secondary dark:border-gray-700 relative">
<div className="relative aspect-square mb-2">
<Image
src={url}
alt={name}
layout="fill"
objectFit="cover"
className="rounded-md"
/>
</div>
<p className="text-center dark:text-white">{name ?? "Meme"}</p>
<MoreInfo
src={url}
name={name}
description={description}
collectionId={collectionId}
memeCollectionId={memeCollectionId}
className="absolute top-2 right-2 p-1 rounded-full bg-white dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors duration-200 border border-black/15"
onDeleteSuccess={onDeleteSuccess}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ChevronLeft } from "lucide-react";
import Link from "next/link";
import React from "react";

export function CollectionNotFound() {
return (
<div className="flex flex-col items-center justify-center flex-1">
<h1 className="text-2xl font-bold mb-4 dark:text-white">
Collection not found
</h1>
<Link
href="/dashboard/collections"
className="flex items-center text-gray-600 hover:text-gray-800 transition-colors duration-200 dark:text-white"
>
<ChevronLeft className="w-5 h-5 mr-1" />
<span className="hidden sm:block text-xl">Back to Collections</span>
<span className="block sm:hidden text-lg">Back</span>
</Link>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { useToast } from "@/components/ui/use-toast";
import { useDashboardCtx } from "@/context/DashboardContext";
import { useDeleteCollection } from "@/hooks/collections/useDeleteCollection";
import { useFetchCollections } from "@/hooks/collections/useFetchCollections";
import { Loader2, Trash } from "lucide-react";
import { useRouter } from "next/navigation";
import React, { useState } from "react";

interface DeleteCollectionProps {
Expand All @@ -21,28 +21,14 @@ interface DeleteCollectionProps {

export function DeleteCollection({ collectionId }: DeleteCollectionProps) {
const [open, setOpen] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
const { loading, deleteCollection } = useDeleteCollection();
const { refetch } = useFetchCollections();
const { toast } = useToast();
const { refreshCollections } = useDashboardCtx();
const router = useRouter();

async function handleDeleteCollection() {
try {
setLoading(true);
const res = await fetch("/api/collection", {
method: "DELETE",
body: JSON.stringify({
collectionId,
}),
});
if (res.ok) {
toast({
title: "Collection deleted!",
description: "Collection was successfully deleted",
});
await refreshCollections();
router.push("/dashboard/collections");
}
await deleteCollection(collectionId);
await refetch();
} catch (error: any) {
toast({
title: "Error",
Expand All @@ -51,7 +37,6 @@ export function DeleteCollection({ collectionId }: DeleteCollectionProps) {
});
} finally {
setOpen(false);
setLoading(false);
}
}

Expand Down
31 changes: 9 additions & 22 deletions src/app/dashboard/collections/[id]/_components/edit-collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Edit, FolderPlusIcon, Loader2 } from "lucide-react";
import { Edit, Loader2 } from "lucide-react";
import { Input } from "@/components/ui/input";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
Expand All @@ -20,13 +20,14 @@ import {
FormMessage,
} from "@/components/ui/form";
import { useToast } from "@/components/ui/use-toast";
import { useEffect, useState } from "react";
import { useDashboardCtx } from "@/context/DashboardContext";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { useUpdateCollection } from "@/hooks/collections/useUpdateCollection";

interface EditCollectionProps {
collectionId: string;
name: string;
onSuccess: (id: string) => Promise<void>;
}

const collectionFormSchema = z.object({
Expand All @@ -35,10 +36,9 @@ const collectionFormSchema = z.object({
}),
});

export function EditCollection({ name, collectionId }: EditCollectionProps) {
export function EditCollection({ name, collectionId, onSuccess }: EditCollectionProps) {
const { toast } = useToast();
const { refreshCollectionWithMemes } = useDashboardCtx();
const [loading, setLoading] = useState<boolean>(false);
const { updateCollection, loading } = useUpdateCollection();
const [open, setOpen] = useState<boolean>(false);
const collectionForm = useForm<z.infer<typeof collectionFormSchema>>({
resolver: zodResolver(collectionFormSchema),
Expand All @@ -48,21 +48,10 @@ export function EditCollection({ name, collectionId }: EditCollectionProps) {
});

async function onSubmit(values: z.infer<typeof collectionFormSchema>) {
setLoading(true);
try {
const req = await fetch(`/api/collection/${collectionId}`, {
method: "PATCH",
body: JSON.stringify({
collectionName: values.name,
}),
});
if (req.status === 200) {
await refreshCollectionWithMemes();
return toast({
title: "Edit collection!",
description: `${name} was renamed to ${values.name} successfully.`,
});
}
await updateCollection(collectionId, { collectionName: values.name });
setOpen(false);
await onSuccess(collectionId);
} catch (err: any) {
console.error(err);
return toast({
Expand All @@ -71,8 +60,6 @@ export function EditCollection({ name, collectionId }: EditCollectionProps) {
variant: "destructive",
});
} finally {
setOpen(false);
setLoading(false);
collectionForm.reset();
}
}
Expand Down
Loading
Loading