diff --git a/src/app/id/[slug]/page.tsx b/src/app/id/[slug]/page.tsx new file mode 100644 index 0000000..417310c --- /dev/null +++ b/src/app/id/[slug]/page.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { Props } from "@/hooks/useValidateFrameUrl"; +import { notFound, useParams, useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import LoadingOverlay from "../go/_components/form/loading-overlay"; + +export default function SlugPage() { + const [isNotFound, setIsNotFound] = useState(false); + const { slug } = useParams(); + const router = useRouter(); + + useEffect(() => { + const params = localStorage.getItem(slug as string); + + if (!params) setIsNotFound(true); + else { + const searchParams = JSON.parse(params) as Props["searchParams"]; + + router.push(`/go?${new URLSearchParams(searchParams).toString()}`); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [slug]); + + return isNotFound ? notFound() : ; +} diff --git a/src/app/id/_components/footer.tsx b/src/app/id/_components/footer.tsx new file mode 100644 index 0000000..31da912 --- /dev/null +++ b/src/app/id/_components/footer.tsx @@ -0,0 +1,11 @@ +export const Footer = () => { + return ( + + ); +}; diff --git a/src/app/id/_components/global/button.tsx b/src/app/id/_components/global/button.tsx new file mode 100644 index 0000000..a259d0d --- /dev/null +++ b/src/app/id/_components/global/button.tsx @@ -0,0 +1,90 @@ +import { cva, type VariantProps } from "class-variance-authority"; +import Link, { LinkProps as NextLinkProps } from "next/link"; +import { + ComponentProps, + HTMLAttributeAnchorTarget, + MouseEventHandler, + ReactNode, +} from "react"; + +import cn from "@/lib/clsx"; + +const buttonVariants = cva( + "inline-flex items-center rounded-full transition-all duration-500", + { + variants: { + variant: { + primary: + "bg-primary-400 px-4 py-2 md:px-6 md:py-3 hover:bg-primary-200 text-base text-white disabled:text-neutral-500 disabled:bg-neutral-300", + secondary: + "border-primary-400 px-4 py-2 md:px-6 md:py-3 hover:bg-primary-50 text-base text-primary-400 disabled:bg-neutral-300 text-primary-400 disabled:text-neutral-500", + tertiary: + "text-base text-black hover:text-primary-400 text-black disabled:text-neutral-500", + quartiary: + "text-base px-4 py-2 md:px-6 md:py-3 text-primary-400 bg-white hover:bg-primary-50 disabled:bg-neutral-400 disabled:text-white", + }, + }, + } +); + +interface LinkButtonProps + extends NextLinkProps, + VariantProps { + children?: ReactNode; + href: string; + scroll?: boolean; + target?: HTMLAttributeAnchorTarget; + className?: string; +} + +interface ButtonProps + extends ComponentProps<"button">, + VariantProps { + children?: ReactNode; + type?: "button" | "reset" | "submit"; + onClick?: MouseEventHandler; + isDisabled?: boolean; + className?: string; +} + +export function LinkButton({ + children, + href, + variant, + className, + target, + scroll, +}: Readonly) { + return ( + + {children} + + ); +} + +export function Button({ + children, + type, + onClick, + isDisabled, + className, + variant, + ...props +}: Readonly) { + return ( + + ); +} diff --git a/src/app/id/_components/global/card.tsx b/src/app/id/_components/global/card.tsx new file mode 100644 index 0000000..c994bea --- /dev/null +++ b/src/app/id/_components/global/card.tsx @@ -0,0 +1,82 @@ +import cn from "@/lib/clsx"; +import * as React from "react"; + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = "CardFooter"; + +export { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +}; diff --git a/src/app/id/_components/global/image.tsx b/src/app/id/_components/global/image.tsx new file mode 100644 index 0000000..9dfcf07 --- /dev/null +++ b/src/app/id/_components/global/image.tsx @@ -0,0 +1,16 @@ +import { default as NextImage, ImageProps } from "next/image"; + +import cn from "@/lib/clsx"; + +export default function Image(props: Readonly) { + return ( + + ); +} diff --git a/src/app/id/_components/global/input.tsx b/src/app/id/_components/global/input.tsx new file mode 100644 index 0000000..160c25f --- /dev/null +++ b/src/app/id/_components/global/input.tsx @@ -0,0 +1,274 @@ +"use client"; + +import { ChangeEvent, KeyboardEventHandler, useState } from "react"; +import { FaEye, FaEyeSlash } from "react-icons/fa"; + +import cn from "@/lib/clsx"; + +interface InputProps { + label?: string; + placeholder?: string; + className?: string; + required?: boolean; + name?: string; + value?: string; + defaultValue?: string; + handleChange?: (event: ChangeEvent) => void; + onKeyDown?: KeyboardEventHandler; + disabled?: boolean; +} + +interface OptionFieldProps { + label: string; + required?: boolean; + options: { id: string; value: string }[]; + className?: string; + value?: string | Array; + name: string; + handleChange?: (event: ChangeEvent) => void; + disabled?: boolean; +} + +interface SelectFieldProps { + label?: string; + required?: boolean; + options: { value: string; label: string }[]; + className?: string; + value?: string | Array; + name: string; + handleChange?: (event: ChangeEvent) => void; + disabled?: boolean; +} + +interface TextFieldProps extends InputProps { + type: "email" | "text" | "password" | "number" | "url"; +} + +export function TextField({ + label, + placeholder, + className, + name, + required, + type = "text", + handleChange, + value, + defaultValue, + onKeyDown, + disabled, +}: Readonly) { + const [showPassword, setShowPassword] = useState(false); + return ( +
+ {label && ( + + )} +
+ {type == "password" && ( + + )} + +
+
+ ); +} + +export function TextArea({ + label, + placeholder, + className, + required, + name, + value, + disabled, +}: Readonly) { + return ( +
+ {label && ( + + )} +