diff --git a/src/app/(admin)/dashboard/_components/edit-url-form-card.tsx b/src/app/(admin)/dashboard/_components/edit-url-form-card.tsx new file mode 100644 index 0000000..4adf478 --- /dev/null +++ b/src/app/(admin)/dashboard/_components/edit-url-form-card.tsx @@ -0,0 +1,229 @@ +"use client" + +import { DatePicker } from "@/components/date-picker" +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form" +import { Input } from "@/components/ui/input" +import { Switch } from "@/components/ui/switch" +import { updateUrl } from "@/lib/actions/url" +import { urls } from "@/lib/drizzle/schema" +import { editUrlSchema } from "@/lib/schema/url" +import { zodResolver } from "@hookform/resolvers/zod" +import { useRouter } from "next/navigation" +import { useForm } from "react-hook-form" +import { toast } from "sonner" +import { z } from "zod" + +type EditUrlFormValues = z.infer + +export function EditUrlFormCard({ data }: { data: typeof urls.$inferSelect }) { + const router = useRouter() + const form = useForm({ + resolver: zodResolver(editUrlSchema), + defaultValues: { + url: data.url, + slug: data.slug, + title: data.title || "", + maxVisits: data.maxVisits || undefined, + expDate: data.expDate || undefined, + forwardQueryParams: data.forwardQueryParams || undefined, + crawlable: data.crawlable || undefined + } + }) + + async function handleSubmit(formData: EditUrlFormValues) { + const res = await updateUrl(data.id, formData) + + if (res.error) { + toast.error(res.message) + } else { + toast.success(res.message) + router.push("/dashboard/list") + } + } + + return ( + + + Edit Short Link + + Update the details of your short link. + + + +
+ +
+
+ ( + + Original URL + + + + + The URL you want to shorten. + + + + )} + /> + ( + + Custom Slug (Optional) + + + + + A unique identifier for your short link (max 10 characters). + + + + )} + /> + ( + +
+ + Forward Query Parameters + + + Forward query parameters from the short link to the destination URL. + +
+ + + +
+ )} + /> + ( + +
+ + Crawlable (Optional) + + + Allow search engines to crawl and index this link. + +
+ + + +
+ )} + /> +
+
+ ( + + Title (Optional) + + + + + A descriptive title for your link (max 100 characters). + + + + )} + /> + ( + + Max Visits (Optional) + + field.onChange(e.target.value ? parseInt(e.target.value) : undefined)} + /> + + + Maximum number of visits before the link expires. + + + + )} + /> + ( + + Expiration Date (Optional) + + + + + When this link should expire and become inaccessible. + + + + )} + /> +
+
+
+ + +
+
+ +
+
+ ) +} diff --git a/src/app/(admin)/dashboard/_components/urls-data-table.tsx b/src/app/(admin)/dashboard/_components/urls-data-table.tsx index 6d1e6e9..d9f2630 100644 --- a/src/app/(admin)/dashboard/_components/urls-data-table.tsx +++ b/src/app/(admin)/dashboard/_components/urls-data-table.tsx @@ -37,7 +37,7 @@ type UrlRecord = typeof urls.$inferSelect & { visits?: (typeof visits.$inferSelect)[] } -export const columns: ColumnDef[] = [ +const columns: ColumnDef[] = [ { accessorKey: "slug", header: ({ column }) => { @@ -282,6 +282,12 @@ export function UrlsDataTable({ data }: UrlsDataTableProps) { Copy URL + + + + Edit URL + + +
+ + + URL Not Found + + The short link you're trying to edit could not be found. + + + +
+

+ This could happen if: +

+
    +
  • The link has been deleted
  • +
  • You don't have permission to edit this link
  • +
  • The link ID is invalid
  • +
+
+ + +
+
+
+
+
+ + ) +} diff --git a/src/app/(admin)/dashboard/edit/[id]/page.tsx b/src/app/(admin)/dashboard/edit/[id]/page.tsx new file mode 100644 index 0000000..83b5630 --- /dev/null +++ b/src/app/(admin)/dashboard/edit/[id]/page.tsx @@ -0,0 +1,33 @@ +import { getSession } from "@/lib/auth/session" +import { getUrlById } from "@/lib/db/urls" +import { notFound } from "next/navigation" +import { EditUrlFormCard } from "../../_components/edit-url-form-card" + +export default async function EditPage({ + params +}: { + params: Promise<{ id: string }> +}) { + const { session, redirect } = await getSession() + + if (!session) { + redirect("/sign-in") + } + + const { id } = await params + + const url = await getUrlById(id) + + if (!url) { + notFound() + } + + return ( +
+
+

Edit Short Link

+
+ +
+ ) +} diff --git a/src/lib/db/urls.ts b/src/lib/db/urls.ts index 44868b2..50aa8da 100644 --- a/src/lib/db/urls.ts +++ b/src/lib/db/urls.ts @@ -27,26 +27,30 @@ export async function getUrlBySlug(slug: string) { }) } -export async function insertUrl(data: typeof urls.$inferInsert) { +export async function getUrlById(id: string) { "use cache" - revalidateTag("url") + cacheTag("urls") + + return await db.query.urls.findFirst({ + where: eq(urls.id, id) + }) +} + +export async function insertUrl(data: typeof urls.$inferInsert) { + revalidateTag("urls") return await db.insert(urls).values(data) } export async function updateUrl(id: string, data: Omit, "id">) { - "use cache" - - revalidateTag("url") + revalidateTag("urls") return await db.update(urls).set(data).where(eq(urls.id, id)) } export async function deleteUrl(id: string) { - "use cache" - - revalidateTag("url") + revalidateTag("urls") return await db.delete(urls).where(eq(urls.id, id)) } @@ -60,8 +64,6 @@ export async function getVisitsBySlugById(id: string) { } export async function trackVisit(data: typeof visits.$inferInsert) { - "use cache" - revalidateTag("visits") return await db.insert(visits).values(data)