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

4
package-lock.json generated
View file

@ -35,7 +35,7 @@
"devDependencies": {
"@types/node": "^20",
"@types/prop-types": "^15.7.11",
"@types/react": "^18",
"@types/react": "^18.2.55",
"@types/react-dom": "^18",
"autoprefixer": "^10.4.17",
"eslint": "^8",
@ -44,7 +44,7 @@
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"tailwindcss": "^3.4.1",
"typescript": "^5"
"typescript": "^5.3.3"
}
},
"node_modules/@aashutoshrathi/word-wrap": {

View file

@ -36,7 +36,7 @@
"devDependencies": {
"@types/node": "^20",
"@types/prop-types": "^15.7.11",
"@types/react": "^18",
"@types/react": "^18.2.55",
"@types/react-dom": "^18",
"autoprefixer": "^10.4.17",
"eslint": "^8",

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"

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"

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"
@ -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
}