Repository: taqui-786/itZmyLink Branch: master Commit: a216201f8bec Files: 83 Total size: 149.0 KB Directory structure: gitextract_0vzabup5/ ├── .eslintrc.json ├── .gitignore ├── README.md ├── components.json ├── emails/ │ └── index.tsx ├── middleware.ts ├── next.config.mjs ├── package.json ├── postcss.config.js ├── src/ │ ├── app/ │ │ ├── 1/ │ │ │ ├── [slug]/ │ │ │ │ └── page.tsx │ │ │ ├── error.tsx │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ │ ├── Not-found.tsx │ │ ├── api/ │ │ │ ├── auth/ │ │ │ │ └── [...nextauth]/ │ │ │ │ └── route.ts │ │ │ └── og/ │ │ │ └── route.tsx │ │ ├── auth/ │ │ │ └── signin/ │ │ │ └── page.tsx │ │ ├── create/ │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── links/ │ │ │ └── page.tsx │ │ ├── page.tsx │ │ └── preview/ │ │ ├── layout.tsx │ │ └── page.tsx │ ├── components/ │ │ ├── ActionButtons/ │ │ │ ├── CustomLinkDialog.tsx │ │ │ ├── DemoBtn.tsx │ │ │ ├── PreviewFooter.tsx │ │ │ ├── PublishBtn.tsx │ │ │ └── ResponsivePreviewBtn.tsx │ │ ├── AdditionalLinkCards.tsx │ │ ├── AdditionalLinkForm.tsx │ │ ├── Animation/ │ │ │ ├── BlurText.tsx │ │ │ ├── FlipText.tsx │ │ │ ├── FramerWrapper.tsx │ │ │ └── TextEffect.tsx │ │ ├── Background/ │ │ │ ├── BackgroundCards.tsx │ │ │ ├── BackgroundForm.tsx │ │ │ └── BgSnippets.tsx │ │ ├── DisplayData.tsx │ │ ├── HomeEditor.tsx │ │ ├── Navbar.tsx │ │ ├── PersonalInfo.tsx │ │ ├── PhotoUpload.tsx │ │ ├── PreviewPage.tsx │ │ ├── Provider.tsx │ │ ├── SocialLinkForm.tsx │ │ ├── auth.ts │ │ ├── forms/ │ │ │ ├── LoginGoogleBtn.tsx │ │ │ ├── SignupForm.tsx │ │ │ └── formAction.ts │ │ ├── mockup/ │ │ │ ├── ComputerMockup.tsx │ │ │ ├── MobileMockup.tsx │ │ │ └── MobileScreen.tsx │ │ ├── screen/ │ │ │ └── DisplayScreen.tsx │ │ └── ui/ │ │ ├── Drrawer.tsx │ │ ├── SocialInput.tsx │ │ ├── SortableLink.tsx │ │ ├── alert-dialog.tsx │ │ ├── avatar.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── form.tsx │ │ ├── input-otp.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── select.tsx │ │ ├── skeleton.tsx │ │ └── textarea.tsx │ ├── lib/ │ │ ├── Context.tsx │ │ ├── Firebase.ts │ │ ├── RateLimiter.tsx │ │ ├── supabase/ │ │ │ ├── actions.ts │ │ │ ├── supabaseClient.ts │ │ │ ├── supabaseMiddleware.ts │ │ │ └── supabaseServer.ts │ │ └── utils.ts │ ├── middleware.ts │ └── types/ │ └── Types.ts ├── tailwind.config.ts └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "extends": "next/core-web-vitals" } ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env*.local # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts ================================================ FILE: README.md ================================================ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). ## Getting Started First, run the development server: ```bash npm run dev # or yarn dev # or pnpm dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. ## Learn More To learn more about Next.js, take a look at the following resources: - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! ## Deploy on Vercel The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. ================================================ FILE: components.json ================================================ { "$schema": "https://ui.shadcn.com/schema.json", "style": "default", "rsc": true, "tsx": true, "tailwind": { "config": "tailwind.config.ts", "css": "src/app/globals.css", "baseColor": "gray", "cssVariables": true }, "aliases": { "components": "@/components", "utils": "@/lib/utils" } } ================================================ FILE: emails/index.tsx ================================================ import { Body, Container, Head, Heading, Hr, Html, Img, Link, Preview, Section, Text, } from "@react-email/components"; import * as React from "react"; interface SupaAuthVerifyEmailProp { verificationCode?: string; } export default function SupaAuthVerifyEmail({ verificationCode = "596853", }: SupaAuthVerifyEmailProp) { return ( Supauth Email Verification
SupaAuth Verify your email address { "Thanks for starting the new account creation process. We want to make sure it's really you. Please enter the following verification code when prompted. If you don't want to create an account, you can ignore this message." }
Verification code {verificationCode} (This code is valid for 1 hour)
); } const main = { backgroundColor: "#fff", color: "#212121", }; const container = { padding: "20px", margin: "0 auto", backgroundColor: "#eee", }; const h1 = { color: "#333", fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", fontSize: "20px", fontWeight: "bold", marginBottom: "15px", }; const link = { color: "#2754C5", fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", fontSize: "14px", textDecoration: "underline", }; const text = { color: "#333", fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", fontSize: "14px", margin: "24px 0", }; const imageSection = { backgroundColor: "#252f3d", display: "flex", padding: "20px 0", alignItems: "center", justifyContent: "center", }; const coverSection = { backgroundColor: "#fff" }; const upperSection = { padding: "25px 35px" }; const lowerSection = { padding: "25px 35px" }; const footerText = { ...text, fontSize: "12px", padding: "0 20px", }; const verifyText = { ...text, margin: 0, fontWeight: "bold", textAlign: "center" as const, }; const codeText = { ...text, fontWeight: "bold", fontSize: "36px", margin: "10px 0", textAlign: "center" as const, }; const validityText = { ...text, margin: "0px", textAlign: "center" as const, }; const verificationSection = { display: "flex", alignItems: "center", justifyContent: "center", }; const mainText = { ...text, marginBottom: "14px" }; ================================================ FILE: middleware.ts ================================================ import { updateSession } from "@/lib/supabase/supabaseMiddleware"; import { type NextRequest } from "next/server"; export async function middleware(request: NextRequest) { return await updateSession(request); } export const config = { matcher: [ /* * Match all request paths except for the ones starting with: * - _next/static (static files) * - _next/image (image optimization files) * - favicon.ico (favicon file) * Feel free to modify this pattern to include more paths. */ "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", ], }; ================================================ FILE: next.config.mjs ================================================ /** @type {import('next').NextConfig} */ const nextConfig = { }; export default nextConfig; ================================================ FILE: package.json ================================================ { "name": "client", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@dnd-kit/core": "^6.0.8", "@dnd-kit/modifiers": "^6.0.1", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", "@hookform/resolvers": "^3.9.0", "@iconify/react": "^4.1.1", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.1.0", "@react-email/components": "0.0.22", "@supabase/ssr": "^0.4.0", "@supabase/supabase-js": "^2.45.1", "@types/node": "20.6.0", "@types/react": "18.2.21", "@types/react-dom": "18.2.7", "@vercel/og": "^0.5.17", "autoprefixer": "10.4.15", "canvas-confetti": "^1.9.3", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "encoding": "^0.1.13", "eslint": "8.49.0", "eslint-config-next": "13.4.19", "firebase": "^10.4.0", "framer-motion": "^11.2.13", "input-otp": "^1.2.4", "js-base64": "^3.7.5", "lucide-react": "^0.276.0", "next": "^14.1.4", "next-auth": "^5.0.0-beta.20", "postcss": "8.4.29", "react": "18.2.0", "react-dom": "18.2.0", "react-email": "2.1.6", "react-hook-form": "^7.52.2", "react-icons": "^4.12.0", "resend": "^3.5.0", "sonner": "^1.5.0", "tailwind-merge": "^1.14.0", "tailwindcss": "3.3.3", "tailwindcss-animate": "^1.0.7", "typescript": "5.2.2", "vaul": "^0.7.1", "zod": "^3.23.8" }, "devDependencies": { "@types/canvas-confetti": "^1.6.4" } } ================================================ FILE: postcss.config.js ================================================ module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, } ================================================ FILE: src/app/1/[slug]/page.tsx ================================================ import { decodeData } from "@/lib/utils"; import { BACKGROUND_OPTIONS } from "@/components/Background/BgSnippets"; import DisplayScreen from "@/components/screen/DisplayScreen"; import { supabaseServer } from "@/lib/supabase/supabaseServer"; import NotFound from "@/app/Not-found"; import DataLoading from "../loading"; type Props = { params: { slug: string; }; }; export async function generateMetadata({ params }: Props) { const path = await supabaseServer() .from("links") .select("*") .eq("path", params.slug); if (path.data?.length === 0) return NotFound(); const data = decodeData(path?.data?.[0].link); if (!data) { return {}; } return { title: `${data.n}'s`, description: `Find all of ${data.n}'s links in one place.`, openGraph: { type: "website", locale: "en_US", url: "https://itZmyLink.vercel.app", title: `${data.n}'s - itZmyLink`, description: `Find all of ${data.n}'s links in one place.`, images: `https://itZmyLink.vercel.app/api/og?data=${encodeURI(data.n)}`, siteName: `${data.n}'s - itZmyLink`, }, twitter: { card: "summary_large_image", title: `${data.n} - itZmyLink`, description: `Find all of ${data.n}'s links in one place.`, images: `https://itZmyLink.vercel.app/api/og?data=${encodeURI(data.n)}`, creator: "@Taquiimam14", }, }; } const linkLandingPage: React.FC = async ({ params }) => { const path = await supabaseServer() .from("links") .select("*") .eq("path", params.slug); if (path.data?.length === 0) return NotFound(); const data = decodeData(path?.data?.[0].link); const selectedBgOption = data ? BACKGROUND_OPTIONS.find((option) => option.code === data.bg) : null; const selectedBgComponent = selectedBgOption ? selectedBgOption.component : null; return ( <>
{selectedBgComponent}
{data ? : }
); }; export default linkLandingPage; ================================================ FILE: src/app/1/error.tsx ================================================ 'use client' import { Button } from "@/components/ui/button" import Link from "next/link" export default function Error() { return (

Sorry, there is mistake in url.

) } ================================================ FILE: src/app/1/loading.tsx ================================================ import { Skeleton } from '@/components/ui/skeleton' import React from 'react' export default function DataLoading() { return (
) } ================================================ FILE: src/app/1/page.tsx ================================================ import { decodeData } from "@/lib/utils"; import NotFound from "../Not-found"; import { BACKGROUND_OPTIONS } from "@/components/Background/BgSnippets"; import DataLoading from "./loading"; import DisplayScreen from "@/components/screen/DisplayScreen"; export async function generateMetadata({ searchParams }: any) { const data = decodeData(searchParams.data); if (!data) { return {}; } return { title: `${data.n}'s`, description: `Find all of ${data.n}'s links in one place.`, openGraph: { type: "website", locale: "en_US", url: "https://itZmyLink.vercel.app", title: `${data.n}'s - itZmyLink`, description: `Find all of ${data.n}'s links in one place.`, images: `https://itZmyLink.vercel.app/api/og?data=${encodeURI(data.n)}`, siteName: `${data.n}'s - itZmyLink`, }, twitter: { card: "summary_large_image", title: `${data.n} - itZmyLink`, description: `Find all of ${data.n}'s links in one place.`, images: `https://itZmyLink.vercel.app/api/og?data=${encodeURI(data.n)}`, creator: "@Taquiimam14", }, }; } const linkLandingPage = ({ searchParams }: any) => { if (!searchParams.data) NotFound(); const data = decodeData(searchParams.data); const selectedBgOption = data ? BACKGROUND_OPTIONS.find((option) => option.code === data.bg) : null; const selectedBgComponent = selectedBgOption ? selectedBgOption.component : null; return ( <>
{selectedBgComponent}
{data ? : }
); }; export default linkLandingPage; ================================================ FILE: src/app/Not-found.tsx ================================================ import { Button } from '@/components/ui/button' import Link from 'next/link' import React from 'react' export default function NotFound() { return (

Opps! Page not found.

Organize your links with itZmyLink and make them easy to find and share.

) } ================================================ FILE: src/app/api/auth/[...nextauth]/route.ts ================================================ import { handlers } from "@/components/auth"; export const {GET, POST} = handlers; ================================================ FILE: src/app/api/og/route.tsx ================================================ import { NextRequest } from "next/server"; import type { ServerRuntime} from "next" import { ImageResponse } from "@vercel/og" export const runtime: ServerRuntime = "edge" function truncateString({ str, maxLength }: { str: string, maxLength: number }) { if (str.length > maxLength) { return str.substring(0, maxLength); } return str; } export async function GET(req: NextRequest) { try { const url = new URL(req.url) const { data } = Object.fromEntries(url.searchParams) if (!data || !data.trim()) { return new Response("Data not in URL or empty.", { status: 400, }); } const maxNameLength = 25; let name = data.trim(); name = truncateString({ str: name, maxLength: maxNameLength }); return new ImageResponse( (
{name}'s - itZmyLink
), { width: 1200, height: 630, } ) } catch (error) { console.error(error); return new Response(`Failed to generate the image`, { status: 500, }) } } ================================================ FILE: src/app/auth/signin/page.tsx ================================================ import React from 'react' import Link from "next/link" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card" import LoginGoogleBtn from '@/components/forms/LoginGoogleBtn'; type Props = { searchParams:{ callbackUrl?:string } }; const page: React.FC = async({searchParams}) => { return (
Welcome Back Select your registered email to continue.
Don't have an account?{" "} Sign up
) } export default page ================================================ FILE: src/app/create/loading.tsx ================================================ import { Skeleton } from '@/components/ui/skeleton' import React from 'react' export default function SiteLoading() { return (
) } ================================================ FILE: src/app/create/page.tsx ================================================ import PreviewButton from "@/components/ActionButtons/ResponsivePreviewBtn"; import HomeEditor from "@/components/HomeEditor"; const CreateLink = async() => { return (
{/* EDITING PART */}
); }; export default CreateLink; ================================================ FILE: src/app/globals.css ================================================ @tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { --background: 0 0% 100%; --foreground: 20 14.3% 4.1%; --card: 0 0% 100%; --card-foreground: 20 14.3% 4.1%; --popover: 0 0% 100%; --popover-foreground: 20 14.3% 4.1%; --primary: 47.9 95.8% 53.1%; --primary-foreground: 26 83.3% 14.1%; --secondary: 60 4.8% 95.9%; --secondary-foreground: 24 9.8% 10%; --muted: 60 4.8% 95.9%; --muted-foreground: 25 5.3% 44.7%; --accent: 60 4.8% 95.9%; --accent-foreground: 24 9.8% 10%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 60 9.1% 97.8%; --border: 20 5.9% 90%; --input: 20 5.9% 90%; --ring: 20 14.3% 4.1%; --radius: 0.5rem; } .dark { --background: 20 14.3% 4.1%; --foreground: 60 9.1% 97.8%; --card: 20 14.3% 4.1%; --card-foreground: 60 9.1% 97.8%; --popover: 20 14.3% 4.1%; --popover-foreground: 60 9.1% 97.8%; --primary: 47.9 95.8% 53.1%; --primary-foreground: 26 83.3% 14.1%; --secondary: 12 6.5% 15.1%; --secondary-foreground: 60 9.1% 97.8%; --muted: 12 6.5% 15.1%; --muted-foreground: 24 5.4% 63.9%; --accent: 12 6.5% 15.1%; --accent-foreground: 60 9.1% 97.8%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 60 9.1% 97.8%; --border: 12 6.5% 15.1%; --input: 12 6.5% 15.1%; --ring: 35.5 91.7% 32.9%; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; } } html, body, :root{ height: 100%; } .hide_scrollbar::-webkit-scrollbar { display: none; -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ } body::-webkit-scrollbar { display: none; -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ } /* MOBILE TEMPLATE DESIGN */ .container{ max-width: 600px; margin: 4px auto; } .phone{ position: relative; background: #1e1e1e; height: 625px; width: 345px; border-radius: 25px; margin: 0 auto; } .camera{ position: absolute; background: #7A7A7A; height: 15px; width: 15px; border-radius: 15px; top: 7px; left: 166px; z-index: +9; } .speaker{ position: absolute; background: #000; height: 23px; width: 45px; border-radius: 0 0 27px 27px; top: 7px; left: 151px; z-index: +8; } .sleep-button{ position: absolute; background: #1e1e1e; height: 35px; width: 3px; border-top-right-radius: 3px 3px; border-bottom-right-radius: 3px 3px; top: 111px; left: 345px; } .silent-switch{ position: absolute; background: #1e1e1e; height: 25px; width: 3px; border-top-left-radius: 3px 3px; border-bottom-left-radius: 3px 3px; top: 60px; left: -3px; } .volume{ position: absolute; background: #1e1e1e; width: 3px; height: 35px; border-top-left-radius: 3px 3px; border-bottom-left-radius: 3px 3px; left: -3px; } .up{ top: 105px; } .down{ top: 145px; } .screen{ position: absolute; overflow: hidden; height: 593px; width: 325px; top: 10px; left: 10px; border-radius: 15px; } .home-button{ position: absolute; border: 1px solid #7A7A7A; height: 35px; width: 35px; border-radius: 25px; bottom: 12px; left: 50%; margin-left: -18px; display: none; } ================================================ FILE: src/app/layout.tsx ================================================ import "./globals.css"; import type { Metadata } from "next"; import { siteConfig } from "./page"; import { Inter } from "next/font/google"; import { Providers } from "@/components/Provider"; const inter = Inter({ subsets: ["latin"] }); // Original source: https://github.com/sadmann7/skateshop/blob/main/src/app/layout.tsx export const metadata: Metadata = { metadataBase: new URL("https://itzmylink.vercel.app"), title: { default: siteConfig.name, template: `%s - itZmyLink`, }, description: siteConfig.description, // added new keywords for seo keywords: [ "bitly url shortener", "bitly link shortener", "link shortener", "url shortener", "bitly link", "tinyurls", "all in one link", "free url shortener", "linknode", "onelink", "social links", "free linktree", "link in bio", "short my url", "my links", "itzmylink", "itZmyLink", "mtLink", ], authors: [ { name: "Taqui Imam", url: "https://github.com/taqui-786", }, ], creator: "Taqui imam", openGraph: { type: "website", locale: "en_US", url: siteConfig.url, title: siteConfig.name, description: siteConfig.description, images: [`${siteConfig.url}/og-image.png`], siteName: siteConfig.name, }, twitter: { card: "summary_large_image", title: siteConfig.name, description: siteConfig.description, images: [`${siteConfig.url}/og-image.png`], creator: "@Taquiimam14", }, icons: { icon: "/favicon.ico", }, }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return (
{/* */}
{children}
{/*
*/}
); } ================================================ FILE: src/app/links/page.tsx ================================================ import { Card, CardContent } from "@/components/ui/card" import { Button, buttonVariants } from "@/components/ui/button" import { Construction } from "lucide-react" import Link from "next/link" import { cn } from "@/lib/utils" export default function Component() { return (

Under Construction

This page will be available soon.

Go Home
) } ================================================ FILE: src/app/page.tsx ================================================ import Link from "next/link"; import landingPageImg from '../../public/landingPage.png' import Image from "next/image"; import { auth } from "@/components/auth"; import { redirect } from "next/navigation"; export const siteConfig = { name: "itZmyLink - one page, many links.", description: "itZmyLink help you to Create a personalized page to showcase all your social media profiles in one place.", ogImage: "https://itzmylink.vercel.app/og-image.png", url: "https://itzmylink.vercel.app", } export default async function Home() { const session = await auth() if(session?.user) return redirect('/create') return (

Simplify your online presence!

Take control of your links with itzmylink

Your all-in-one link management solution

Get your Link

Already joined us? Log in

heroImg
); } ================================================ FILE: src/app/preview/layout.tsx ================================================ export default function PreviewLayout({ children, }: { children: React.ReactNode }) { return
{children}
} ================================================ FILE: src/app/preview/page.tsx ================================================ import React from "react"; import NotFound from "../Not-found"; import { decodeData } from "@/lib/utils"; import ComputerMockup from "@/components/mockup/ComputerMockup"; import { BACKGROUND_OPTIONS } from "@/components/Background/BgSnippets"; import PreviewPage from "@/components/PreviewPage"; import PreviewFooter from "@/components/ActionButtons/PreviewFooter"; function page({ searchParams }: any) { if (!searchParams.data) NotFound(); const data = decodeData(searchParams.data); const selectedBgOption = data ? BACKGROUND_OPTIONS.find((option) => option.code === data.bg) : null; const selectedBgComponent = selectedBgOption ? selectedBgOption.component : null; return ( <>
{selectedBgComponent}
); } export default page; ================================================ FILE: src/components/ActionButtons/CustomLinkDialog.tsx ================================================ "use client"; import { useState, useCallback } from "react"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { ClipboardCopy, Check, PenSquare } from "lucide-react"; import { createCustomPath } from "@/lib/supabase/actions"; // Mock function to simulate checking if a path exists in the database const checkPathExists = async (path: string): Promise => { await new Promise((resolve) => setTimeout(resolve, 1000)); return path.toLowerCase().includes("taken"); }; export default function Component({localLink}:{localLink:string}) { const [customPath, setCustomPath] = useState(""); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); const [isCopied, setIsCopied] = useState(false); const baseUrl = "itzmylink.vercel.app/1"; const fullUrl = `${baseUrl}/${customPath}`; const copyToClipboard = useCallback(() => { navigator.clipboard .writeText(fullUrl) .then(() => { setIsCopied(true); // toast({ // title: "Copied!", // description: "Link copied to clipboard", // }) setTimeout(() => setIsCopied(false), 2000); }) .catch((err) => { console.error("Failed to copy text: ", err); // toast({ // title: "Error", // description: "Failed to copy link", // variant: "destructive", // }) }); }, [fullUrl]); const handleCreate = useCallback(async () => { if (!customPath) { setError("Please enter a custom path"); return; } setIsLoading(true); setError(null); setSuccess(null); try { const exists = await checkPathExists(customPath); if (exists) { setError("This custom path is already taken. Please try another."); } else { const res = await createCustomPath(customPath,localLink); if (res.status !== "notAuthenticated") { res.status === 'created'? setSuccess(res.message): setError(res.message) } } } catch (err) { console.error("Error creating custom path:", err); setError("An error occurred while creating the custom path"); } finally { setIsLoading(false); } }, [customPath]); return ( <> Create Custom Link Enter your custom path to create a unique link.
{baseUrl}/ setCustomPath(e.target.value)} className="col-span-3" placeholder="your-custom-path" />
{error ? "Already Exists 😅" : "Congratulation🎉"} {error || success} {success && (

Your custom link:

)}
{ setError(null); setSuccess(null); }} > Close {success && ( Copy URL )}
); } ================================================ FILE: src/components/ActionButtons/DemoBtn.tsx ================================================ "use client" import React, { FC } from 'react' import { Button } from '@/components/ui/button' import { Play } from 'lucide-react' import { useData } from '@/lib/Context' interface DemoDataProps { } const DemoBtn: FC = ({ }) => { const { showDemo } = useData() return ( ) } export default DemoBtn ================================================ FILE: src/components/ActionButtons/PreviewFooter.tsx ================================================ "use client"; import React from "react"; import { Button } from "../ui/button"; import { Check, Clipboard, Share } from "lucide-react"; import CustomLinkDialog from "./CustomLinkDialog"; function PreviewFooter({ MyLink, inputLink, }: { MyLink: any; inputLink: string; }) { const [hasCopied, setHasCopied] = React.useState(false); const copyToClipboard = React.useCallback(() => { const url = `${window.location.origin}/1?data=${inputLink}`; navigator.clipboard.writeText(url); return url; }, [MyLink]); return (
); } export default PreviewFooter; ================================================ FILE: src/components/ActionButtons/PublishBtn.tsx ================================================ "use client" import React, { FC } from 'react' import { Button, buttonVariants } from '@/components/ui/button' import { cn, encodeData } from '@/lib/utils'; import { Check, Copy, Send, Share2 } from 'lucide-react'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { DialogClose } from '@radix-ui/react-dialog'; import { useData } from '@/lib/Context'; import Link from 'next/link'; interface PublishProps { loggedIn:any } const Publish: FC = ({ loggedIn}) => { const { MyLink } = useData() const isEmpty = isEmptyValues(MyLink) const [inputLink, setInputLink] = React.useState("") const [hasCopied, setHasCopied] = React.useState(false) function isEmptyValues(obj: any) { for (let key in obj) { if (obj[key] !== "" && obj[key].length !== 0) { return false; } } return true; } const copyToClipboard = React.useCallback(() => { const url = `${window.location.origin}/1?data=${encodeData(MyLink)}`; navigator.clipboard.writeText(url) return url }, [MyLink]); React.useEffect(() => { setHasCopied(false); }, [MyLink]); function publish() { if (!isEmpty) { const getUrl = copyToClipboard() setInputLink(getUrl) } } return ( Share your page You can share your page with others and make it accessible from anywhere. {!isEmpty ? ( loggedIn ? Confirm to publish: Login To Continue // <> // // //
// // //
//
// ) : ( )}
) } export default Publish ================================================ FILE: src/components/ActionButtons/ResponsivePreviewBtn.tsx ================================================ "use client" import React, { FC } from 'react' import { Button } from "@/components/ui/button" import { Drawer } from "vaul" import { useData } from '@/lib/Context' import { BACKGROUND_OPTIONS } from '../Background/BgSnippets' import { DrawerContent, DrawerTrigger } from '../ui/Drrawer' import DisplayData from '../DisplayData' interface PreviewButtonProps { } const PreviewButton: FC = () => { const { MyLink } = useData(); const [isEmpty, setIsEmpty] = React.useState(false) React.useEffect(() => { function isEmptyValues(obj: any) { for (let key in obj) { if (obj[key] !== "" && obj[key].length !== 0) { return false; } } return true; } setIsEmpty(isEmptyValues(MyLink)) }, [MyLink]) const selectedBgOption = MyLink ? BACKGROUND_OPTIONS.find((option) => option.code === MyLink.bg) : null; const selectedBgComponent = selectedBgOption ? selectedBgOption.component : null; return (
{ isEmpty ?
Nothing to show...
: ( <> {!isEmpty && selectedBgComponent}
) }
) } export default PreviewButton ================================================ FILE: src/components/AdditionalLinkCards.tsx ================================================ import React, { FC } from 'react' import { Icon } from '@iconify/react'; interface AdditionalLinkCardProps { label: string; url: string; icon?: string; } const AdditionalLinkCard: FC = ({ label, url, icon }) => { return (
  • {label && url && (
    {icon ? ( ) : ( )}

    {label}

    )}
  • ) } export default AdditionalLinkCard ================================================ FILE: src/components/AdditionalLinkForm.tsx ================================================ "use client" import React, { FC } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, } from '@dnd-kit/core'; import { SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { restrictToVerticalAxis, restrictToParentElement, } from '@dnd-kit/modifiers'; import {SortableLinks} from '@/components/ui/SortableLink' import { useData } from '@/lib/Context'; const AdditionalLinkForm = () =>{ const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, }), ); const scrollDownRef = React.useRef(null) const [shouldScroll, setShouldScroll] = React.useState(false); const { MyLink, addNewData, updateIndex } = useData(); const addLinkDetailForm = () => { const newLink: AdditionalLinkProps = { id: Date.now(), i: '', l: '', u: '' }; addNewData(newLink); setShouldScroll(true); }; React.useEffect(() => { if (shouldScroll && scrollDownRef.current) { scrollDownRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' }); setShouldScroll(false); } }, [shouldScroll]); // "handleDragEnd" function written by chatGPT function handleDragEnd(event: any) { const { active, over } = event; if (active.id !== over.id) { const updatedItems = [...MyLink.ls]; // Accessing items from the context const draggedItem: any = updatedItems.find((item) => item.id === active.id); const targetItem: any = updatedItems.find((item) => item.id === over.id); const draggedIndex = updatedItems.indexOf(draggedItem); const targetIndex = updatedItems.indexOf(targetItem); if (draggedIndex !== -1 && targetIndex !== -1) { // Remove the dragged item from its original position updatedItems.splice(draggedIndex, 1); // Insert the dragged item at the target position updatedItems.splice(targetIndex, 0, draggedItem); updateIndex(updatedItems); } } } return( <> Extra Links {/* */} Enter your additional link details here. link.id)} strategy={verticalListSortingStrategy} > {MyLink.ls.map((link, index) => { return })}
    ) } export default AdditionalLinkForm; ================================================ FILE: src/components/Animation/BlurText.tsx ================================================ "use client"; import { motion } from "framer-motion"; import { cn } from "@/lib/utils"; interface BlurIntProps { word: string; className?: string; variant?: { hidden: { filter: string; opacity: number }; visible: { filter: string; opacity: number }; }; duration?: number; } const BlurIn = ({ word, className, variant, duration = 1 }: BlurIntProps) => { const defaultVariants = { hidden: { filter: "blur(10px)", opacity: 0 }, visible: { filter: "blur(0px)", opacity: 1 }, }; const combinedVariants = variant || defaultVariants; return ( {word} ); }; export default BlurIn; ================================================ FILE: src/components/Animation/FlipText.tsx ================================================ "use client"; import { AnimatePresence, motion, Variants } from "framer-motion"; import { cn } from "@/lib/utils"; interface SlightFlipProps { word: string; duration?: number; delayMultiple?: number; framerProps?: Variants; className?: string; } export default function SlightFlip({ word, duration = 0.5, delayMultiple = 0.08, framerProps = { hidden: { rotateX: -90, opacity: 0 }, visible: { rotateX: 0, opacity: 1 }, }, className, }: SlightFlipProps) { return (
    {word.split("").map((char, i) => ( {char} ))}
    ); } ================================================ FILE: src/components/Animation/FramerWrapper.tsx ================================================ "use client"; import React, { useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; type FramerMotionProps = { children: React.ReactNode, className?:any, y?:number x?:number delay?:number duration?: number scale?:number } function FramerWrapper({children,delay = 0.25 ,y = 15, x = 0,duration = 0.20,scale = 0, className}:FramerMotionProps) { const [animateConfig, setAnimateConfig] = useState({ opacity:1, y:0, x:0 }) return ( {children} ); } export default FramerWrapper; ================================================ FILE: src/components/Animation/TextEffect.tsx ================================================ 'use client'; import { motion, Variants } from 'framer-motion'; import React from 'react'; type PresetType = 'blur' | 'shake' | 'scale' | 'fade' | 'slide'; type TextEffectProps = { children: string; per?: 'word' | 'char'; as?: keyof JSX.IntrinsicElements; variants?: { container?: Variants; item?: Variants; }; className?: string; preset?: PresetType; }; const defaultContainerVariants: Variants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.05, }, }, }; const defaultItemVariants: Variants = { hidden: { opacity: 0 }, visible: { opacity: 1, }, }; const presetVariants: Record< PresetType, { container: Variants; item: Variants } > = { blur: { container: defaultContainerVariants, item: { hidden: { opacity: 0, filter: 'blur(12px)' }, visible: { opacity: 1, filter: 'blur(0px)' }, }, }, shake: { container: defaultContainerVariants, item: { hidden: { x: 0 }, visible: { x: [-5, 5, -5, 5, 0], transition: { duration: 0.5 } }, }, }, scale: { container: defaultContainerVariants, item: { hidden: { opacity: 0, scale: 0 }, visible: { opacity: 1, scale: 1 }, }, }, fade: { container: defaultContainerVariants, item: { hidden: { opacity: 0 }, visible: { opacity: 1 }, }, }, slide: { container: defaultContainerVariants, item: { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0 }, }, }, }; const AnimationComponent: React.FC<{ word: string; variants: Variants; per: 'word' | 'char'; }> = React.memo(({ word, variants, per }) => { if (per === 'word') { return ( ); } return ( {word.split('').map((char, charIndex) => ( ))} ); }); AnimationComponent.displayName = 'AnimationComponent'; export function TextEffect({ children, per = 'word', as = 'p', variants, className, preset, }: TextEffectProps) { const words = children.split(/(\S+)/); const MotionTag = motion[as as keyof typeof motion]; const selectedVariants = preset ? presetVariants[preset] : { container: defaultContainerVariants, item: defaultItemVariants }; const containerVariants = variants?.container || selectedVariants.container; const itemVariants = variants?.item || selectedVariants.item; return ( {words.map((word, wordIndex) => ( ))} ); } ================================================ FILE: src/components/Background/BackgroundCards.tsx ================================================ import React from 'react'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { BACKGROUND_OPTIONS } from './BgSnippets'; import { useData } from '@/lib/Context'; type BackgroundCardProps = {}; const BackgroundCard: React.FC = () => { const { MyLink,selectBackground} = useData() return (
    {BACKGROUND_OPTIONS.map((background, index) => { return ( ); })}
    ); }; export default BackgroundCard ================================================ FILE: src/components/Background/BackgroundForm.tsx ================================================ "use client" import React from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import BackgroundCard from './BackgroundCards' export default function Background() { return ( Background Customize your background theme from here. ) } ================================================ FILE: src/components/Background/BgSnippets.tsx ================================================ // original source: https://github.com/ibelick/background-snippets/blob/main/app/components/background.tsx const BgTheme1 = () => { return (
    ); }; const BgTheme8 = () => { return (
    ); }; const BgTheme2 = () => { return (
    ); }; const BgTheme9 = () => { return (
    ); }; const BgTheme7 = () => { return (
    ); }; const BgTheme10 = () => { return (
    ); }; const BgTheme11 = () => { return (
    ); }; const BgTheme12 = () => { return (
    ); }; const BgTheme4 = () => { return (
    ); }; const BgTheme5 = () => { return (
    ); }; const BgTheme6 = () => { return (
    ); }; const BgTheme3 = () => { return (
    ); }; export const BACKGROUND_OPTIONS = [ { code: '#FFFFFF', component: , name: 'WhiteCanvas' }, { code: '#4F4F4F', component: , name: 'ShadowyGray' }, { code: '#C9EBFF', component: , name: 'LinearSky', }, { code: '#E6E7EB', component: , name: 'SubtleGrayDots', }, { code: '#FF00FF', component: , name: 'BlurredFuchsia', }, { code: '#E5E7EB', component: , name: 'MaskedGray', }, { code: '#808080', component: , name: 'GradientGrid', }, { code: '#F0F0F0', component: , name: 'LightGrayLines', }, { code: '#00A3FF', component: , name: 'RadiantBlue', }, { code: '#AD6DF4', component: , name: 'GradientOrb', }, { code: '#63E', component: , name: 'RadialHalo', }, { code: '#D5C5FF', component: , name: 'VividCircles', } ] as const; ================================================ FILE: src/components/DisplayData.tsx ================================================ "use client"; import { ImageIcon } from "lucide-react"; import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; import { Icon } from "@iconify/react/dist/iconify.js"; import AdditionalLinkCard from "./AdditionalLinkCards"; const DisplayData:React.FC = ({ myData }) => { const EmptySocialLiks = !myData.fb && !myData.ig && !myData.tg && !myData.em && !myData.tw && !myData.lk && !myData.yt && !myData.gt && !myData.wh; const iconMap: Record = { fb: "ph:facebook-logo-duotone", tw: "ph:twitter-logo-duotone", ig: "ph:instagram-logo-duotone", tg: "ph:telegram-logo-duotone", wh: "ph:whatsapp-logo-duotone", yt: "ph:youtube-logo-duotone", em: "ph:envelope-duotone", gt: "ph:github-logo-duotone", lk: "ph:linkedin-logo-duotone", }; return ( <>
    {/* USER IMAGE */} {/* USER NAME AND BIO */}

    {myData.n}

    {myData.a}

    {/* {!EmptySocialLiks && ( */}
    { Object.entries(myData).map(([key, value]) => { const excludedKeys = ["i", "n", "a", "bg"]; if (key !== "ls" && value && !excludedKeys.includes(key)) { const propIcon = iconMap[key as keyof typeof iconMap]; if (key === "em") { // Handle email link generation return ( ); } else if (key === "wh") { // Handle WhatsApp link generation const whatsappValue = value.startsWith("https://wa.me/") ? value // If it already starts with the correct prefix : `https://wa.me/${value}`; return ( ); } else { return ( ); } } return null; })}
    {/* )} */}
      {myData.ls && myData.ls.map((link, id) => ( ))}
    ); }; export default DisplayData; ================================================ FILE: src/components/HomeEditor.tsx ================================================ import PersonalInfo from "./PersonalInfo"; import SocialLinksForm from "./SocialLinkForm"; import Background from "./Background/BackgroundForm"; import AdditionalLinkForm from "./AdditionalLinkForm"; import Publish from "./ActionButtons/PublishBtn"; import DemoBtn from "./ActionButtons/DemoBtn"; import Link from "next/link"; import { ShoppingCart ,Link2, Github, Coffee} from "lucide-react"; import { buttonVariants } from "./ui/button"; import Navbar from "./Navbar"; import { auth } from "./auth"; import MobileMockup from "./mockup/MobileMockup"; const HomeEditor = async() => { const session = await auth() return ( <>
    Github Buy Me a Coffee
    {/* MOBILE MOCKUP */}
    ); }; export default HomeEditor; ================================================ FILE: src/components/Navbar.tsx ================================================ import Link from "next/link"; import React from "react"; import { auth, signOut } from "./auth"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { cn } from "@/lib/utils"; import { buttonVariants } from "./ui/button"; import { supabaseServer } from "@/lib/supabase/supabaseServer"; async function Navbar() { const session = await auth(); let linkCount = 0 const supabase = await supabaseServer().from('links').select('*').eq('email',session?.user?.email) if(supabase.data?.length){ linkCount = supabase.data.length } return ( ); } export default Navbar; ================================================ FILE: src/components/PersonalInfo.tsx ================================================ "use client"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Label } from "./ui/label"; import { Input } from "./ui/input"; import { Textarea } from "./ui/textarea"; import { useData } from "@/lib/Context"; import PhotoUpload from "./PhotoUpload"; type InputChangeEvent = React.ChangeEvent< HTMLInputElement | HTMLTextAreaElement >; const PersonalInfo = () => { const { MyLink, updateProfileInfo } = useData(); const handleInfoChange = (event: InputChangeEvent) => { const { name, value } = event.target; updateProfileInfo(name, value); }; return ( <> Profile Information Enter your profile or title information here.