url -> long_url

This commit is contained in:
Elijah McMorris 2024-02-15 19:14:45 -08:00
parent b15cac7ed0
commit dbf0b5d670
Signed by: NexVeridian
SSH key fingerprint: SHA256:bsA1SKZxuEcEVHAy3gY1HUeM5ykRJl0U0kQHQn0hMg8
21 changed files with 237 additions and 175 deletions

12
src/app/[slug]/db.tsx Normal file
View file

@ -0,0 +1,12 @@
"use server";
import { initConnection } from "@/components/db-utils";
export async function querydb(slug: string) {
let db = await initConnection();
let long_url = await db.query(`
select * from url:[$id];
`, { id: slug });
console.log(long_url, slug)
return long_url;
}

10
src/app/[slug]/page.tsx Normal file
View file

@ -0,0 +1,10 @@
import { redirect } from "next/navigation";
import { querydb } from "./db";
// export default function Page({ params }: { params: { slug: string } }) {
// redirect(`https://${params.slug}`)
// }
export default function Page({ params }: { params: { slug: string } }) {
redirect(`https://${querydb(params.slug)}`)
}

113
src/app/create/create.tsx Normal file
View file

@ -0,0 +1,113 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle
} from "@/components/ui/card";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { zodResolver } from "@hookform/resolvers/zod";
import { CopyIcon } from "@radix-ui/react-icons";
import { useFormState, useFormStatus } from "react-dom";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { querydb } from "./db";
import { formSchema } from "./schema";
const initialState = {
url: null,
}
function SubmitButton() {
const { pending } = useFormStatus();
return (
<Button type="submit">Submit</Button>
);
}
export default function CreateCard() {
// @ts-ignore
const [state, formAction] = useFormState(querydb, initialState);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
url: "",
},
})
const handleCopyUrl = () => {
if (state && state.url) {
const currentSiteName = window.location.hostname;
const url = `https://${currentSiteName}/${state.url.toString()}`;
navigator.clipboard.writeText(url)
.catch(err => {
console.error('Failed to copy URL to clipboard:', err);
});
}
};
return (
// <CardGrid>
<Card>
<CardHeader>
<CardTitle>Create a Shortened URL</CardTitle>
</CardHeader>
<CardContent>
<Form {...form}>
{/* @ts-ignore */}
<form action={form.handleSubmit(formAction)} className="space-y-8">
<FormField
control={form.control}
name="url"
render={({ field }) => (
<FormItem>
<FormLabel>Enter a url</FormLabel>
<FormControl>
<Input placeholder="nexveridian.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<SubmitButton />
</form>
</Form>
</CardContent>
<CardFooter>
{state && state.url && (
<Popover>
<PopoverTrigger>
<Button onClick={handleCopyUrl}>
<CopyIcon className="mr-2 h-4 w-4" /> Copy Url
</Button>
</PopoverTrigger>
<PopoverContent>
<p className="text-lg">
Url added to clipboard
</p>
</PopoverContent>
</Popover>
)}
</CardFooter>
</Card>
// </CardGrid>
);
}

View file

@ -3,7 +3,7 @@ import { initConnection } from "@/components/db-utils";
import { formSchema } from "./schema";
export async function querydb(prevState: any, formData: FormData) {
const values = formSchema.safeParse({ url: formData.get("url") })
const values = formSchema.safeParse(formData)
if (!values.success) {
return { error: values.error };
}
@ -12,7 +12,7 @@ export async function querydb(prevState: any, formData: FormData) {
let db = await initConnection();
let url = await db.query(`
create url:[rand::string(8)] CONTENT {
long_url: string::replace($long_url, "http://", "https://"),
long_url: string::replace(string::replace($long_url, "https://", ""), "http://", ""),
clicks: 0,
date_added: time::now(),
date_accessed: <future> { time::now() }

View file

@ -1,109 +0,0 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle
} from "@/components/ui/card";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { zodResolver } from "@hookform/resolvers/zod";
import { CopyIcon } from "@radix-ui/react-icons";
import { useFormState, useFormStatus } from "react-dom";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { querydb } from "./db";
import { formSchema } from "./schema";
const initialState = {
url: null,
}
function SubmitButton() {
const { pending } = useFormStatus();
return (
<Button type="submit">Submit</Button>
);
}
export default function Home() {
const [state, formAction] = useFormState(querydb, initialState);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
url: "",
},
})
const handleCopyUrl = () => {
if (state && state.url) {
navigator.clipboard.writeText(state.url)
.catch(err => {
console.error('Failed to copy URL to clipboard:', err);
});
}
};
return (
<div className="hidden items-start justify-center gap-6 rounded-lg p-8 md:grid lg:grid-cols-3 xl:grid-cols-3">
<Card>
<CardHeader>
<CardTitle>Create a Shortened URL</CardTitle>
</CardHeader>
<CardContent>
<Form {...form}>
<form action={form.handleSubmit(querydb)} className="space-y-8">
<FormField
control={form.control}
name="url"
render={({ field }) => (
<FormItem>
<FormLabel>Enter a url</FormLabel>
<FormControl>
<Input placeholder="nexveridian.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<SubmitButton />
</form>
</Form>
</CardContent>
<CardFooter>
{state && state.url && (
<Popover>
<PopoverTrigger>
<Button onClick={handleCopyUrl}>
<CopyIcon className="mr-2 h-4 w-4" /> Copy Url
</Button>
</PopoverTrigger>
<PopoverContent>
<p className="text-lg">
Url added to clipboard
</p>
</PopoverContent>
</Popover>
)}
</CardFooter>
</Card>
</div >
);
}

23
src/app/global-error.tsx Normal file
View file

@ -0,0 +1,23 @@
"use client";
import CardGrid from "@/components/card-grid";
import {
Card,
CardHeader,
CardTitle
} from "@/components/ui/card";
export default function GlobalError({
error,
}: {
error: Error & { digest?: string }
}) {
return (
<CardGrid>
<Card>
<CardHeader>
<CardTitle className="text-center text-2xl text-red-400">Error</CardTitle>
</CardHeader>
</Card>
</CardGrid>
);
}

23
src/app/not-found.tsx Normal file
View file

@ -0,0 +1,23 @@
"use client";
import CardGrid from "@/components/card-grid";
import {
Card,
CardHeader,
CardTitle
} from "@/components/ui/card";
export default function GlobalError({
error,
}: {
error: Error & { digest?: string }
}) {
return (
<CardGrid>
<Card>
<CardHeader>
<CardTitle className="text-center text-2xl text-red-400">404 - Not Found</CardTitle>
</CardHeader>
</Card>
</CardGrid>
);
}

View file

@ -1,21 +1,24 @@
"use client";
import CardGrid from "@/components/card-grid";
import {
Card,
CardHeader,
CardTitle
} from "@/components/ui/card";
import Link from "next/link";
import CreateCard from "./create/create";
export default function Home() {
return (
<div className="hidden items-start justify-center gap-6 rounded-lg p-8 md:grid lg:grid-cols-2 xl:grid-cols-3">
<Card>
<CardGrid>
{/* <Card>
<Link href="/create">
<CardHeader>
<CardTitle>Create a Shortened URL</CardTitle>
</CardHeader>
</Link>
</Card>
</Card> */}
<CreateCard />
<Card>
<Link href="/stats">
@ -24,6 +27,6 @@ export default function Home() {
</CardHeader>
</Link>
</Card>
</div >
</CardGrid>
);
}

View file

@ -8,9 +8,9 @@ export default function Nav() {
<Link href="/">Next Url Shortener</Link>
</div>
<div className="transition-colors text-foreground/50 hover:text-foreground/100">
{/* <div className="transition-colors text-foreground/50 hover:text-foreground/100">
<Link href="/create">Create</Link>
</div>
</div> */}
<div className="transition-colors text-foreground/50 hover:text-foreground/100">
<Link href="/stats">Stats</Link>

View file

@ -0,0 +1,18 @@
export default function CardGrid({
children,
className,
...props
}: {
children?: React.ReactNode;
className?: string;
}) {
return (
<div
className={`hidden items-start justify-center gap-6 rounded-lg p-8 md:grid lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4" ${className}`}
{...props}
>
{children}
</div>
);
}

View file

@ -1,6 +1,6 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react"
import { cn } from "@/lib/utils"
@ -36,7 +36,7 @@ const buttonVariants = cva(
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
VariantProps<typeof buttonVariants> {
asChild?: boolean
}

View file

@ -73,4 +73,4 @@ const CardFooter = React.forwardRef<
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }

View file

@ -1,10 +1,10 @@
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import {
CheckIcon,
ChevronRightIcon,
DotFilledIcon,
} from "@radix-ui/react-icons"
import * as React from "react"
import { cn } from "@/lib/utils"
@ -185,19 +185,8 @@ const DropdownMenuShortcut = ({
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator,
DropdownMenuShortcut, DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
DropdownMenuSubTrigger, DropdownMenuTrigger
}

View file

@ -1,6 +1,6 @@
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import * as React from "react"
import {
Controller,
ControllerProps,
@ -10,8 +10,8 @@ import {
useFormContext,
} from "react-hook-form"
import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
import { cn } from "@/lib/utils"
const Form = FormProvider
@ -165,12 +165,7 @@ const FormMessage = React.forwardRef<
FormMessage.displayName = "FormMessage"
export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
Form, FormControl,
FormDescription, FormField, FormItem,
FormLabel, FormMessage, useFormField
}

View file

@ -3,7 +3,7 @@ import * as React from "react"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
extends React.InputHTMLAttributes<HTMLInputElement> { }
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {

View file

@ -1,6 +1,6 @@
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react"
import { cn } from "@/lib/utils"
@ -11,7 +11,7 @@ const labelVariants = cva(
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}

View file

@ -1,5 +1,5 @@
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import * as React from "react"
import { cn } from "@/lib/utils"
@ -28,4 +28,4 @@ const PopoverContent = React.forwardRef<
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger }

View file

@ -1,4 +1,3 @@
import * as React from "react"
import {
CaretSortIcon,
CheckIcon,
@ -6,6 +5,7 @@ import {
ChevronUpIcon,
} from "@radix-ui/react-icons"
import * as SelectPrimitive from "@radix-ui/react-select"
import * as React from "react"
import { cn } from "@/lib/utils"
@ -80,7 +80,7 @@ const SelectContent = React.forwardRef<
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
@ -91,7 +91,7 @@ const SelectContent = React.forwardRef<
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
@ -149,14 +149,5 @@ const SelectSeparator = React.forwardRef<
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue
}

View file

@ -109,12 +109,6 @@ const TableCaption = React.forwardRef<
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
Table, TableBody, TableCaption, TableCell, TableFooter,
TableHead, TableHeader, TableRow
}