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 (
<Html>
<Head />
<Preview>Supauth Email Verification</Preview>
<Body style={main}>
<Container style={container}>
<Section style={coverSection}>
<Section style={imageSection}></Section>
<Section style={upperSection}>
<Heading style={h1}>
SupaAuth Verify your email address
</Heading>
<Text style={mainText}>
{
"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."
}
</Text>
<Section style={verificationSection}>
<Text style={verifyText}>
Verification code
</Text>
<Text style={codeText}>{verificationCode}</Text>
<Text style={validityText}>
(This code is valid for 1 hour)
</Text>
</Section>
</Section>
</Section>
</Container>
</Body>
</Html>
);
}
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<Props> = 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 (
<>
<div className="fixed left-0 top-0 -z-10 h-full w-full">
{selectedBgComponent}
</div>
<div className="p-2 pt-10 hide_scrollbar">
{data ? <DisplayScreen myData={data} /> : <DataLoading />}
</div>
</>
);
};
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 (
<div className="grid h-screen px-4 bg-white place-content-center">
<div className="text-center space-y-5">
<h1 className="text-lg md:text-xl text-accent-foreground dark:text-gray-400">Sorry, there is mistake in url.</h1>
<Button size={'sm'}>
<Link href="/">Create new page</Link>
</Button>
</div>
</div>
)
}
================================================
FILE: src/app/1/loading.tsx
================================================
import { Skeleton } from '@/components/ui/skeleton'
import React from 'react'
export default function DataLoading() {
return (
<div className="p-2 h-full w-full space-y-8 max-w-lg mx-auto overflow-y-scroll hide_scrollbar pt-12">
<div className="flex flex-col gap-3 justify-center items-center">
<Skeleton className="h-20 w-20 rounded-full" />
<Skeleton className="h-[30px] w-[200px] rounded-2xl" />
<Skeleton className="h-[50px] w-full rounded-2xl" />
</div>
<div className="flex items-center justify-center flex-wrap gap-3">
<Skeleton className="h-8 w-8 rounded-full" />
<Skeleton className="h-8 w-8 rounded-full" />
<Skeleton className="h-8 w-8 rounded-full" />
<Skeleton className="h-8 w-8 rounded-full" />
<Skeleton className="h-8 w-8 rounded-full" />
<Skeleton className="h-8 w-8 rounded-full" />
</div>
<ul className="space-y-2">
<Skeleton className="h-[50px] w-full rounded-2xl" />
<Skeleton className="h-[50px] w-full rounded-2xl" />
<Skeleton className="h-[50px] w-full rounded-2xl" />
<Skeleton className="h-[50px] w-full rounded-2xl" />
</ul>
</div>
)
}
================================================
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 (
<>
<div className="fixed left-0 top-0 -z-10 h-full w-full">
{selectedBgComponent}
</div>
<div className="p-2 pt-10 hide_scrollbar">
{data ? <DisplayScreen myData={data} /> : <DataLoading />}
</div>
</>
);
};
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 (
<section className="bg-white dark:bg-gray-900 ">
<div className="container flex items-center min-h-screen px-6 py-12 mx-auto">
<div className="flex flex-col justify-center items-center max-w-sm mx-auto text-center">
<p className="p-3 text-sm font-medium text-blue-500 rounded-full bg-blue-50 dark:bg-gray-800">
<svg xmlns="https://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="2" stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
</svg>
</p>
<h1 className="mt-3 text-2xl font-semibold text-gray-800 dark:text-white md:text-3xl">Opps! Page not found.</h1>
<p className="mt-4 text-gray-500 dark:text-gray-400">Organize your links with itZmyLink and make them easy to find and share.</p>
<div className="flex justify-center items-center w-full mt-6 gap-x-3 shrink-0 sm:w-auto">
<Button size={'sm'} >
<Link href={'/'}>
Create your itZmyLink
</Link>
</Button>
</div>
</div>
</div>
</section>
)
}
================================================
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(
(
<div tw="flex flex-col w-full h-full items-center justify-center bg-white mx-auto">
<div tw="flex text-6xl font-bold tracking-tight leading-tight px-8" style={{ overflowWrap: "break-word" }}>
{name}'s - itZmyLink
</div>
</div>
),
{
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<Props> = async({searchParams}) => {
return (
<div className="grid place-content-center min-h-screen w-full">
<Card className="mx-auto max-w-sm">
<CardHeader>
<CardTitle className="text-2xl">Welcome Back</CardTitle>
<CardDescription>
Select your registered email to continue.
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-4">
<LoginGoogleBtn callbackUrl={searchParams.callbackUrl as string}/>
</div>
<div className="mt-4 text-center text-sm">
Don't have an account?{" "}
<Link href="/auth/signup" className="underline">
Sign up
</Link>
</div>
</CardContent>
</Card>
</div>
)
}
export default page
================================================
FILE: src/app/create/loading.tsx
================================================
import { Skeleton } from '@/components/ui/skeleton'
import React from 'react'
export default function SiteLoading() {
return (
<main className='relative grid lg:grid-cols-3 h-screen px-2 lg:px-0 md:container'>
<section className='lg:col-span-2 flex flex-col items-center justify-center py-6 lg:px-20 gap-6 h-screen '>
<div className='overflow-y-auto w-full hide_scrollbar flex flex-col gap-5 pb-20 lg:pb-0 '>
<Skeleton className="h-[250px] w-full rounded-2xl" />
<Skeleton className="h-[250px] w-full rounded-2xl" />
<Skeleton className="h-[250px] w-full rounded-2xl" />
</div>
</section>
<section className='hidden lg:flex pl-6 items-center'>
<Skeleton className="h-[700px] w-[350px] rounded-2xl" />
</section>
</main>
)
}
================================================
FILE: src/app/create/page.tsx
================================================
import PreviewButton from "@/components/ActionButtons/ResponsivePreviewBtn";
import HomeEditor from "@/components/HomeEditor";
const CreateLink = async() => {
return (
<div className="h-screen w-full ">
{/* EDITING PART */}
<div className="w-full flex ">
<HomeEditor />
</div>
<div className='lg:hidden'>
<PreviewButton />
</div>
</div>
);
};
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 (
<html lang="en">
<body className={inter.className}>
<main className="relative flex flex-col min-h-screen">
<Providers>
{/* <Navbar /> */}
<div className=" relative flex-grow flex-1">{children}</div>
{/* <Footer /> */}
</Providers>
</main>
</body>
</html>
);
}
================================================
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 (
<div className="flex items-center justify-center min-h-screen bg-gradient-to-br from-gray-100 to-gray-200 p-4">
<Card className="w-full max-w-3xl">
<CardContent className="flex flex-col items-center space-y-6 p-6 text-center">
<Construction className="w-24 h-24 text-primary" />
<h1 className="text-4xl sm:text-5xl md:text-6xl font-bold text-primary">
Under Construction
</h1>
<p className="text-xl sm:text-2xl text-muted-foreground">
This page will be available soon.
</p>
<Link href="/" className={cn(buttonVariants({size:'lg'}))}>Go Home</Link>
</CardContent>
</Card>
</div>
)
}
================================================
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 (
<div className=" relative h-screen w-full flex justify-center items-center">
<div className="bg-white">
<section className="bg-[#FCF8F1] bg-opacity-30 py-10 sm:py-16 lg:py-24">
<div className="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
<div className="grid items-center grid-cols-1 gap-12 lg:grid-cols-2">
<div>
<p className="text-base font-semibold tracking-wider text-blue-600 uppercase">Simplify your online presence!</p>
<h1 className="mt-4 text-4xl font-bold text-black lg:mt-8 sm:text-6xl xl:text-[5.3rem]">Take control of your links with itzmylink</h1>
<p className="mt-4 text-base text-black lg:mt-8 sm:text-xl">Your all-in-one link management solution</p>
<Link href="/create" title="signup" className="inline-flex items-center px-6 py-4 mt-8 font-semibold text-black transition-all duration-200 bg-yellow-300 rounded-full lg:mt-16 hover:bg-yellow-400 focus:bg-yellow-400" role="button">
Get your Link
<svg className="w-6 h-6 ml-8 -mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13 9l3 3m0 0l-3 3m3-3H8m13 0a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</Link>
<p className="mt-5 text-gray-600">Already joined us? <Link href="/auth/signin" title="signin" className="text-black transition-all duration-200 hover:underline">Log in</Link></p>
</div>
<div>
<Image className="w-full" src={landingPageImg} alt="heroImg" />
</div>
</div>
</div>
</section>
</div>
</div>
);
}
================================================
FILE: src/app/preview/layout.tsx
================================================
export default function PreviewLayout({
children,
}: {
children: React.ReactNode
}) {
return <main className="min-h-screen flex flex-col relative w-full">
{children}</main>
}
================================================
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 (
<>
<ComputerMockup>
<div className="absolute left-0 top-0 h-full w-full">
{selectedBgComponent}
</div>
<PreviewPage data={data} />
</ComputerMockup>
<PreviewFooter MyLink={data} inputLink={searchParams.data}/>
</>
);
}
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<boolean> => {
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<string | null>(null);
const [success, setSuccess] = useState<string | null>(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 (
<>
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost" className="rounded-full hover:bg-gray-100 transition-colors duration-200">
<PenSquare className="w-4 h-4 mr-2" />
Create Custom URL
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Create Custom Link</DialogTitle>
<DialogDescription>
Enter your custom path to create a unique link.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="flex items-center gap-2">
<span className="col-span-1 font-medium text-right">
{baseUrl}/
</span>
<Input
id="custom-path"
value={customPath}
onChange={(e) => setCustomPath(e.target.value)}
className="col-span-3"
placeholder="your-custom-path"
/>
</div>
<div className="flex items-center gap-2">
<Input value={fullUrl} readOnly className="flex-grow" />
<Button onClick={copyToClipboard} size="icon" variant="outline">
{isCopied ? (
<Check className="h-4 w-4" />
) : (
<ClipboardCopy className="h-4 w-4" />
)}
<span className="sr-only">
{isCopied ? "Copied" : "Copy to clipboard"}
</span>
</Button>
</div>
<Button onClick={handleCreate} disabled={isLoading}>
{isLoading ? "Creating..." : "Create Link"}
</Button>
</div>
</DialogContent>
</Dialog>
<AlertDialog open={!!error || !!success}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{error ? "Already Exists 😅" : "Congratulation🎉"}</AlertDialogTitle>
<AlertDialogDescription>
{error || success}
{success && (
<div className="mt-4">
<p className="font-semibold">Your custom link:</p>
<div className="flex items-center gap-2 mt-2">
<Input value={fullUrl} readOnly className="flex-grow" />
<Button
onClick={copyToClipboard}
size="icon"
variant="outline"
>
{isCopied ? (
<Check className="h-4 w-4" />
) : (
<ClipboardCopy className="h-4 w-4" />
)}
<span className="sr-only">
{isCopied ? "Copied" : "Copy to clipboard"}
</span>
</Button>
</div>
</div>
)}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel
onClick={() => {
setError(null);
setSuccess(null);
}}
>
Close
</AlertDialogCancel>
{success && (
<AlertDialogAction onClick={copyToClipboard}>
Copy URL
</AlertDialogAction>
)}
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}
================================================
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<DemoDataProps> = ({ }) => {
const { showDemo } = useData()
return (
<Button className='w-full' onClick={showDemo}>
<Play className='mr-2 h-4 w-4' />
Demo
</Button>
)
}
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<boolean>(false);
const copyToClipboard = React.useCallback(() => {
const url = `${window.location.origin}/1?data=${inputLink}`;
navigator.clipboard.writeText(url);
return url;
}, [MyLink]);
return (
<div className=" absolute z-50 bottom-0 left-0 right-0 m-auto w-fit flex flex-row gap-2 items-center justify-center mb-4 p-1 rounded-full bg-gradient-to-r from-purple-400 via-pink-500 to-red-500 animate-bounce">
<div className="bg-white rounded-full shadow-lg p-1">
<div className="flex space-x-1">
<Button
variant="ghost"
className="rounded-full hover:bg-gray-100 transition-colors duration-200"
onClick={() => {
navigator.share({
title: `${MyLink.n} - itZmyLink`,
text: `Find all of ${MyLink.n}'s links in one place.`,
url: `${inputLink}`,
});
}}
>
<Share className="w-4 h-4 mr-2" />
Share
</Button>
<Button
variant="ghost"
className="rounded-full hover:bg-gray-100 transition-colors duration-200"
onClick={() => {
copyToClipboard();
setHasCopied(true);
}}
>
{hasCopied ? (
<>
<Check className="w-4 h-4 mr-2" />
Copied
</>
) : (
<>
<Clipboard className="w-4 h-4 mr-2" /> Local URL
</>
)}
</Button>
<CustomLinkDialog localLink={inputLink} />
</div>
</div>
</div>
);
}
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<PublishProps> = ({ loggedIn}) => {
const { MyLink } = useData()
const isEmpty = isEmptyValues(MyLink)
const [inputLink, setInputLink] = React.useState<string>("")
const [hasCopied, setHasCopied] = React.useState<boolean>(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 (
<Dialog>
<DialogTrigger asChild>
<Button className='w-full' onClick={publish}>
<Send className='mr-2 h-4 w-4' />
Publish
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle className="flex items-center">
Share your page
</DialogTitle>
<DialogDescription className="text-left">
You can share your page with others and make it accessible from anywhere.
</DialogDescription>
</DialogHeader>
{!isEmpty ? (
loggedIn ?
<Link href={`/preview?data=${encodeData(MyLink)} ` } className={cn(buttonVariants())} >Confirm to publish</Link>:
<Link href={`/auth/signin?callbackUrl=/preview?data=${encodeData(MyLink)} ` } className={cn(buttonVariants())} >Login To Continue</Link>
// <>
// <Input
// value={inputLink}
// readOnly
// />
// <DialogFooter>
// <div className="flex gap-3 w-full justify-between items-center">
// <Button
// className="w-full"
// onClick={() => {
// navigator.share({
// title: `${MyLink.n} - itZmyLink`,
// text: `Find all of ${MyLink.n}'s links in one place.`,
// url: `${inputLink}`,
// })
// }}
// >
// <Share2 className="mr-2 h-4 w-4" />
// Share
// </Button>
// <Button
// className="w-full"
// onClick={() => {
// copyToClipboard()
// setHasCopied(true);
// }}
// >
// {
// hasCopied
// ? (
// <>
// <Check className="mr-2 h-4 w-4" />
// Copied
// </>
// )
// : (
// <>
// <Copy className="mr-2 h-4 w-4" />
// Copy Link
// </>
// )
// }
// </Button>
// </div>
// </DialogFooter>
// </>
) : (
<DialogClose>
<Button className="w-full">
Can't publish with empty fields!
</Button>
</DialogClose>
)}
</DialogContent>
</Dialog>
)
}
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<PreviewButtonProps> = () => {
const { MyLink } = useData();
const [isEmpty, setIsEmpty] = React.useState<boolean>(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 (
<div className="fixed inset-x-0 bottom-0 p-4 z-10 flex justify-center items-center backdrop-blur-sm ">
<Drawer.Root>
<DrawerTrigger asChild>
<Button className="rounded-full max-w-[350px] w-full tracking-wide overflow-y-auto" >
Preview page
</Button>
</DrawerTrigger>
<DrawerContent className="h-[75%] pb-2">
{
isEmpty
? <div className='w-full text-sm text-muted-foreground h-[90%] flex justify-center items-center'>Nothing to show...</div>
: (
<>
{!isEmpty && selectedBgComponent}
<div className='h-full pt-10 px-2'>
<DisplayData myData={MyLink} />
</div>
</>
)
}
</DrawerContent>
</Drawer.Root>
</div>
)
}
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<AdditionalLinkCardProps> = ({ label, url, icon }) => {
return (
<li>
{label && url && (
<a href={url} target="_blank">
<dt className="flex items-center space-x-2 p-1 -m-1 rounded-xl hover:bg-slate-100 bg-slate-50">
<div className="flex-shrink-0 flex h-10 w-10 items-center justify-center rounded-lg text-slate-500">
{icon ? (
<Icon icon={icon} className='h-5 w-5' />
) : (
<Icon icon="ph:link-simple" className='h-5 w-5' />
)}
</div>
<div className="w-full flex-grow min-w-0">
<p className="font-medium text-sm leading-6 text-accent-foreground">
{label}
</p>
</div>
</dt>
</a>
)}
</li>
)
}
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<HTMLDivElement | null>(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(
<>
<Card className='w-full'>
<CardHeader className="space-y-1">
<CardTitle className="text-2xl flex justify-between items-center">
Extra Links
{/* <GetIconInfo /> */}
</CardTitle>
<CardDescription>
Enter your additional link details here.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
modifiers={[restrictToVerticalAxis, restrictToParentElement]}
>
<SortableContext
items={MyLink.ls.map(link => link.id)}
strategy={verticalListSortingStrategy}
>
{MyLink.ls.map((link, index) => {
return <SortableLinks key={link.id} id={link} index={index} />
})}
</SortableContext>
</DndContext>
<Button
variant={"outline"}
onClick={addLinkDetailForm}
>
+
</Button>
</CardContent>
</Card>
<div ref={scrollDownRef}></div>
</>
)
}
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 (
<motion.p
initial="hidden"
animate="visible"
transition={{ duration }}
variants={combinedVariants}
className={cn(
className,
" drop-shadow-sm ",
)}
>
{word}
</motion.p>
);
};
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 (
<div className="flex justify-center space-x-1">
<AnimatePresence mode="wait">
{word.split("").map((char, i) => (
<motion.span
key={i}
initial="hidden"
animate="visible"
exit="hidden"
variants={framerProps}
transition={{ duration, delay: i * delayMultiple }}
className={cn("origin-center drop-shadow-sm", className)}
>
{char}
</motion.span>
))}
</AnimatePresence>
</div>
);
}
================================================
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 (
<AnimatePresence>
<motion.div
initial={scale === 0 ?{opacity:0, y:y, x:x,}:{opacity:0, y:y, x:x, scale:scale}}
animate={scale === 0 ? {opacity:1, y:0, x:0} : {opacity:1, y:0, x:0, scale: 1}}
exit={{opacity:0, y:15}}
transition={{delay:delay, duration:duration}}
className={className}
>{children}</motion.div>
</AnimatePresence>
);
}
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 (
<motion.span
aria-hidden='true'
variants={variants}
className='inline-block whitespace-pre'
>
{word}
</motion.span>
);
}
return (
<span className='inline-block whitespace-pre'>
{word.split('').map((char, charIndex) => (
<motion.span
key={`char-${charIndex}`}
aria-hidden='true'
variants={variants}
className='inline-block whitespace-pre'
>
{char}
</motion.span>
))}
</span>
);
});
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 (
<MotionTag
initial='hidden'
animate='visible'
aria-label={children}
variants={containerVariants}
className={className}
>
{words.map((word, wordIndex) => (
<AnimationComponent
key={`word-${wordIndex}`}
word={word}
variants={itemVariants}
per={per}
/>
))}
</MotionTag>
);
}
================================================
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<BackgroundCardProps> = () => {
const { MyLink,selectBackground} = useData()
return (
<div className="flex h-fit w-full flex-wrap justify-center items-center gap-4 space-x-4 pb-4">
{BACKGROUND_OPTIONS.map((background, index) => {
return (
<Button
key={index}
variant={"outline"}
className={cn("relative min-h-[60px] min-w-[110px] sm:min-w-[150px] overflow-hidden text-muted-foreground", {
'bg-accent text-accent-foreground': MyLink.bg === background.code
})}
onClick={() => {
selectBackground(background.code)
}}
>
{background.name}
</Button>
);
})}
</div>
);
};
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 (
<Card className='w-full'>
<CardHeader className="space-y-1">
<CardTitle className="text-2xl flex justify-between items-center">
Background
</CardTitle>
<CardDescription>
Customize your background theme from here.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4 pb-2.5">
<BackgroundCard />
</CardContent>
</Card>
)
}
================================================
FILE: src/components/Background/BgSnippets.tsx
================================================
// original source: https://github.com/ibelick/background-snippets/blob/main/app/components/background.tsx
const BgTheme1 = () => {
return (
<div className="absolute inset-0 h-full w-full bg-white" />
);
};
const BgTheme8 = () => {
return (
<div className="absolute h-full w-full bg-white"><div className="absolute bottom-0 left-0 right-0 top-0 bg-[linear-gradient(to_right,#4f4f4f2e_1px,transparent_1px),linear-gradient(to_bottom,#4f4f4f2e_1px,transparent_1px)] bg-[size:14px_24px] [mask-image:radial-gradient(ellipse_80%_50%_at_50%_0%,#000_70%,transparent_110%)]"></div></div>
);
};
const BgTheme2 = () => {
return (
<div className="absolute inset-0 h-full w-full bg-white bg-[linear-gradient(to_right,#f0f0f0_1px,transparent_1px),linear-gradient(to_bottom,#f0f0f0_1px,transparent_1px)] bg-[size:6rem_4rem]"><div className="absolute bottom-0 left-0 right-0 top-0 bg-[radial-gradient(circle_500px_at_50%_200px,#C9EBFF,transparent)]"></div></div>
);
};
const BgTheme9 = () => {
return (
<div className="absolute inset-0 h-full w-full bg-white bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] [background-size:16px_16px]"></div>
);
};
const BgTheme7 = () => {
return (
<div className="absolute inset-0 h-full w-full bg-white bg-[linear-gradient(to_right,#8080800a_1px,transparent_1px),linear-gradient(to_bottom,#8080800a_1px,transparent_1px)] bg-[size:14px_24px]"><div className="absolute left-0 right-0 top-0 m-auto h-[310px] w-[310px] rounded-full bg-fuchsia-400 opacity-20 blur-[100px]"></div></div>
);
};
const BgTheme10 = () => {
return (
<div className="absolute h-full w-full bg-white"><div className="absolute h-full w-full bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] [background-size:16px_16px] [mask-image:radial-gradient(ellipse_50%_50%_at_50%_50%,#000_70%,transparent_100%)]"></div></div>
);
};
const BgTheme11 = () => {
return (
<div className="absolute inset-0 h-full w-full bg-white bg-[linear-gradient(to_right,#8080800a_1px,transparent_1px),linear-gradient(to_bottom,#8080800a_1px,transparent_1px)] bg-[size:14px_24px]"></div>
);
};
const BgTheme12 = () => {
return (
<div className="absolute inset-0 h-full w-full bg-white bg-[linear-gradient(to_right,#f0f0f0_1px,transparent_1px),linear-gradient(to_bottom,#f0f0f0_1px,transparent_1px)] bg-[size:6rem_4rem]"></div>
);
};
const BgTheme4 = () => {
return (
<div className="absolute top-0 h-full w-full bg-white bg-[radial-gradient(100%_50%_at_50%_0%,rgba(0,163,255,0.13)_0,rgba(0,163,255,0)_50%,rgba(0,163,255,0)_100%)]"></div>
);
};
const BgTheme5 = () => {
return (
<div className="absolute top-0 h-full w-full bg-white"><div className="absolute bottom-auto left-auto right-0 top-0 h-[500px] w-[500px] -translate-x-[30%] translate-y-[20%] rounded-full bg-[rgba(173,109,244,0.5)] opacity-50 blur-[80px]"></div></div>
);
};
const BgTheme6 = () => {
return (
<div className="absolute inset-0 h-full w-full bg-white [background:radial-gradient(125%_125%_at_50%_10%,#fff_40%,#63e_100%)]"></div>
);
};
const BgTheme3 = () => {
return (
<div className="absolute inset-0 h-full w-full bg-white bg-[linear-gradient(to_right,#f0f0f0_1px,transparent_1px),linear-gradient(to_bottom,#f0f0f0_1px,transparent_1px)] bg-[size:6rem_4rem]"><div className="absolute bottom-0 left-0 right-0 top-0 bg-[radial-gradient(circle_800px_at_100%_200px,#d5c5ff,transparent)]"></div></div>
);
};
export const BACKGROUND_OPTIONS = [
{
code: '#FFFFFF',
component: <BgTheme1 />,
name: 'WhiteCanvas'
},
{
code: '#4F4F4F',
component: <BgTheme8 />,
name: 'ShadowyGray'
},
{
code: '#C9EBFF',
component: <BgTheme2 />,
name: 'LinearSky',
},
{
code: '#E6E7EB',
component: <BgTheme9 />,
name: 'SubtleGrayDots',
},
{
code: '#FF00FF',
component: <BgTheme7 />,
name: 'BlurredFuchsia',
},
{
code: '#E5E7EB',
component: <BgTheme10 />,
name: 'MaskedGray',
},
{
code: '#808080',
component: <BgTheme11 />,
name: 'GradientGrid',
},
{
code: '#F0F0F0',
component: <BgTheme12 />,
name: 'LightGrayLines',
},
{
code: '#00A3FF',
component: <BgTheme4 />,
name: 'RadiantBlue',
},
{
code: '#AD6DF4',
component: <BgTheme5 />,
name: 'GradientOrb',
},
{
code: '#63E',
component: <BgTheme6 />,
name: 'RadialHalo',
},
{
code: '#D5C5FF',
component: <BgTheme3 />,
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<DisplayDataProps> = ({ myData }) => {
const EmptySocialLiks =
!myData.fb &&
!myData.ig &&
!myData.tg &&
!myData.em &&
!myData.tw &&
!myData.lk &&
!myData.yt &&
!myData.gt &&
!myData.wh;
const iconMap: Record<string, string> = {
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 (
<>
<div className="p-[.5rem] h-full relative w-full space-y-8 max-w-lg mx-auto overflow-y-scroll hide_scrollbar">
<div className="text-center z-50">
{/* USER IMAGE */}
<Avatar className="h-20 w-20 rounded-full overflow-hidden ring ring-slate-200 mx-auto">
<AvatarImage
src={myData.i}
alt={`PIC`}
className="h-full w-full object-cover"
/>
<AvatarFallback>
<ImageIcon className="h-8 w-8 text-gray-300" />
</AvatarFallback>
</Avatar>
{/* USER NAME AND BIO */}
<h1 className="text-2xl font-bold mt-4 text-slate-800">{myData.n}</h1>
<p className="text-sm mt-2 text-slate-600">{myData.a}</p>
</div>
{/* {!EmptySocialLiks && ( */}
<div className="flex items-center justify-center flex-wrap">
{ 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 (
<span className="p-1" key={key}>
<a href={`mailto:${value}`}>
<Icon icon={propIcon} className="h-6 w-6" />
</a>
</span>
);
} 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 (
<span className="p-1" key={key}>
<a href={whatsappValue} target="_blank" rel="noopener noreferrer">
<Icon icon={propIcon} className="h-6 w-6" />
</a>
</span>
);
} else {
return (
<span className="p-1" key={key}>
<a href={value} target="_blank" rel="noopener noreferrer">
<Icon icon={propIcon} className="h-6 w-6" />
</a>
</span>
);
}
}
return null;
})}
</div>
{/* )} */}
<ul className="space-y-2">
{myData.ls && myData.ls.map((link, id) => (
<AdditionalLinkCard
label={link.l}
icon={link.i}
url={link.u}
key={id}
/>
))}
</ul>
</div>
</>
);
};
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 (
<>
<div className="h-screen min-h-screen w-full overflow-y-auto gap-4 flex flex-col bg-gray-100 py-1 px-1 sm:py-8 sm:px-16">
<Navbar/>
<PersonalInfo/>
<SocialLinksForm/>
<AdditionalLinkForm/>
<Background/>
<div className='grid grid-cols-2 md:grid-cols-4 gap-2 justify-center items-center w-full mb-20'>
<Publish loggedIn={session?.user || null} />
<DemoBtn/>
<Link
target='_blank'
href="https://github.com/taqui-786/itZmyLink"
className={buttonVariants()}>
<Github className='mr-2 h-4 w-4' />
Github
</Link>
<Link
target='_blank'
href="https://buymeacoffee.com/taquidevloper"
className={buttonVariants()}>
<Coffee className='mr-2 h-4 w-4' />
Buy Me a Coffee
</Link>
</div>
</div>
<div className="h-auto w-[35%] hidden bg-gray-100 lg:flex items-center justify-center">
{/* MOBILE MOCKUP */}
<MobileMockup />
</div>
</>
);
};
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 (
<nav className="bg-white border-gray-200 dark:bg-gray-900">
<div className="flex flex-wrap justify-between items-center mx-auto max-w-screen-xl p-4">
<a
href="https://flowbite.com"
className="flex items-center space-x-3 rtl:space-x-reverse"
>
<span className="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">
itZmyLink
</span>
</a>
<div className="flex items-center space-x-6 rtl:space-x-reverse">
{/* <a href="tel:5541251234" className="text-sm text-gray-500 dark:text-white hover:underline">(555) 412-1234</a> */}
{session?.user ? (
<div className="flex gap-3 h-full w-fit">
<Link href={'/links'} className={cn(buttonVariants({variant:"outline"}),"flex gap-1")}>My Links <span className="inline-flex items-center py-0.5 px-1.5 rounded-full text-xs font-medium bg-red-500 text-white">{linkCount}</span></Link>
<DropdownMenu>
<DropdownMenuTrigger>
<Avatar>
<AvatarImage src={session?.user.image as string}/>
<AvatarFallback>Ti</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel><div className="flex flex-col">
<h1 className="text-sm font-semibold">{session?.user.name}</h1>
<p className="text-xs font-semibold">{session?.user.email}</p>
</div></DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Setting</DropdownMenuItem>
<DropdownMenuItem ><Link href={'/api/auth/signout'}>Logout</Link></DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu></div>
) : (
<Link href={"/auth/signin"} className={cn(buttonVariants({variant:'default'}))}>Login</Link>
)}
</div>
</div>
</nav>
);
}
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 (
<>
<Card className="w-full">
<CardHeader className="space-y-1">
<CardTitle className="text-2xl">Profile Information</CardTitle>
<CardDescription>
Enter your profile or title information here.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<div className="grid md:grid-cols-2 gap-2">
<div>
<Label htmlFor="name">Name</Label>
<Input
id="name"
name="n"
type="text"
placeholder="John Doe"
value={MyLink.n}
onChange={handleInfoChange}
/>
</div>
<div className="relative">
<PhotoUpload />
</div>
</div>
<div className="grid gap-2">
<Label htmlFor="description">About yourself</Label>
<Textarea
id="description"
name="a"
placeholder="type something here..."
value={MyLink.a}
onChange={handleInfoChange}
/>
</div>
</CardContent>
</Card>
</>
);
};
export default PersonalInfo;
================================================
FILE: src/components/PhotoUpload.tsx
================================================
"use client"
import { storage } from "@/lib/Firebase";
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
import { Label } from "./ui/label";
import { Input } from "./ui/input";
import { useData } from "@/lib/Context";
import { useState } from "react";
import { Loader2 } from "lucide-react";
const PhotoUpload = () =>{
const { updateProfileInfo } = useData();
const [loading ,setLoading] = useState<boolean>(false)
async function handleImageUpload (file:File) {
const storageRef = ref(storage, `images/${file.name}`);
await uploadBytes(storageRef, file);
const res = await getDownloadURL(storageRef);
return res
};
// FUNCTION IMAGE UPLOAD
const onImageChange = async (e:React.ChangeEvent<HTMLInputElement>) => {
setLoading(true)
try {
const [file]:any = e.target.files;
if(!file) return
const uploading = await handleImageUpload(file)
updateProfileInfo('i', uploading);
} catch (error) {
console.log(error);
} finally{
setLoading(false)
}
};
return(<>
<Label htmlFor="Profile-url">Profile Url</Label>
{loading ? <Loader2 className='absolute top-0 bottom-0 left-0 right-0 m-auto h-4 w-4 animate-spin' /> :
<Input
id="Profile-url"
name="i"
type="file"
onChange={onImageChange}
/>
}
</>)
}
export default PhotoUpload
================================================
FILE: src/components/PreviewPage.tsx
================================================
'use client'
import React, { useEffect } from 'react'
import DisplayScreen from './screen/DisplayScreen'
import DataLoading from '@/app/1/loading'
import confetti from 'canvas-confetti';
function PreviewPage({data}:{data:any}) {
const handleClick = () => {
const duration = 5 * 1000;
const animationEnd = Date.now() + duration;
const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 100 };
const randomInRange = (min: number, max: number) =>
Math.random() * (max - min) + min;
const interval = window.setInterval(() => {
const timeLeft = animationEnd - Date.now();
if (timeLeft <= 0) {
return clearInterval(interval);
}
const particleCount = 50 * (timeLeft / duration);
confetti({
...defaults,
particleCount,
origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 },
});
confetti({
...defaults,
particleCount,
origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 },
});
}, 250);
};
useEffect(() => {
handleClick();
}, [data]);
return (
<>
{data ? <DisplayScreen myData={data} /> : <DataLoading />}
</>
)
}
export default PreviewPage
================================================
FILE: src/components/Provider.tsx
================================================
'use client';
import { DataProvider } from "@/lib/Context";
import React from "react";
// import { Toaster } from "@/components/ui/toaster";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<DataProvider>
{children}
{/* <Toaster /> */}
</DataProvider>
);
}
================================================
FILE: src/components/SocialLinkForm.tsx
================================================
"use client"
import React, { FC } from 'react'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '@/components/ui/card'
import { SocialInput } from './ui/SocialInput';
import { useData } from '@/lib/Context';
const socialLinksProvider: SocialLinkProviderProps[] = [
{ name: 'facebook', icon: "ph:facebook-logo-duotone", id: "fb" },
{ name: 'twitter', icon: "ph:twitter-logo-duotone", id: "tw" },
{ name: 'instagram', icon: "ph:instagram-logo-duotone", id: "ig" },
{ name: 'telegram', icon: "ph:telegram-logo-duotone", id: "tg" },
{ name: 'youtube', icon: "ph:youtube-logo-duotone", id: "yt" },
{ name: 'email', icon: "ph:envelope-duotone", id: "em" },
{ name: 'github', icon: "ph:github-logo-duotone", id: "gt" },
{ name: 'linkedin', icon: "ph:linkedin-logo-duotone", id: "lk" },
{ name: 'whatsapp', icon: "ph:whatsapp-logo-duotone", id: "wh" },
]
type InputChangeEvent = React.ChangeEvent<HTMLInputElement>;
interface SocialLinksFormProps { }
const SocialLinksForm: FC<SocialLinksFormProps> = () => {
const { MyLink, updateProfileInfo } = useData();
const handleInfoChange = (event: InputChangeEvent) => {
const { name, value } = event.target;
updateProfileInfo(name, value);
};
return (
<Card className='w-full'>
<CardHeader className="space-y-1">
<CardTitle className="text-2xl">Social Links</CardTitle>
<CardDescription>
Enter your social media links here.
</CardDescription>
</CardHeader>
<CardContent className="grid md:grid-cols-2 gap-4">
{socialLinksProvider.map((link) => {
return (
<SocialInput
key={link.name}
id={link.name}
name={link.id}
icon={link.icon}
placeholder={`${link.name}.com/johndoe`}
value={MyLink[link.id]}
onChange={handleInfoChange}
/>
);
})}
</CardContent>
</Card>
)
}
export default SocialLinksForm
================================================
FILE: src/components/auth.ts
================================================
import type { NextAuthConfig } from "next-auth";
import "next-auth/jwt";
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { createUser, isUserExists } from "@/lib/supabase/actions";
const config = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
callbacks: {
authorized({ request, auth }) {
const { pathname } = request.nextUrl;
if (pathname === "/middleware-example") return !!auth;
return true;
},
jwt({ token, trigger, session, account }) {
if (trigger === "update") token.name = session.user.name;
return token;
},
async session({ session, token }) {
if (token?.accessToken) {
session.accessToken = token.accessToken;
}
return session;
},
async signIn({ account, profile }: any) {
if (account?.provider === "google") {
const isExists = await isUserExists(profile?.email);
if (isExists.status === "success") {
await createUser(profile);
return true;
}
return true;
}
return true;
},
},
} satisfies NextAuthConfig;
export const { handlers, auth, signIn, signOut } = NextAuth(config);
declare module "next-auth" {
interface Session {
accessToken?: string;
}
}
declare module "next-auth/jwt" {
interface JWT {
accessToken?: string;
}
}
================================================
FILE: src/components/forms/LoginGoogleBtn.tsx
================================================
'use client'
import React from 'react'
import { Button } from '../ui/button'
import { handleGoogleAuth } from './formAction'
function LoginGoogleBtn({callbackUrl = '/'}:{callbackUrl:string}) {
return (
// <form className="w-full" action={() => handleGoogleAuth(callbackUrl)}>
<Button variant="subtle" type='submit' className="w-full bg-gray-200" onClick={() => handleGoogleAuth(callbackUrl)} >
<svg
xmlns="http://www.w3.org/2000/svg"
className="mr-2 h-5 w-5"
x="0px"
y="0px"
width="20"
height="20"
viewBox="0 0 48 48"
>
<path
fill="#FFC107"
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
></path>
<path
fill="#FF3D00"
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
></path>
<path
fill="#4CAF50"
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
></path>
<path
fill="#1976D2"
d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
></path>
</svg>
Continue with Google
</Button>
// </form>
)
}
export default LoginGoogleBtn
================================================
FILE: src/components/forms/SignupForm.tsx
================================================
'use client'
import React from 'react'
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Button } from "@/components/ui/button"
function SignupForm() {
return (
<div className="grid gap-4">
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="first-name">First name</Label>
<Input id="first-name" placeholder="Max" required className='border-input'/>
</div>
<div className="grid gap-2">
<Label htmlFor="last-name">Last name</Label>
<Input id="last-name" placeholder="Robinson" required />
</div>
</div>
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="grid gap-2">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" />
</div>
<Button type="submit" className="w-full">
Create an account
</Button>
<Button variant="outline" className="w-full">
<svg
xmlns="http://www.w3.org/2000/svg"
className="mr-2 h-5 w-5"
x="0px"
y="0px"
width="20"
height="20"
viewBox="0 0 48 48"
>
<path
fill="#FFC107"
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
></path>
<path
fill="#FF3D00"
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
></path>
<path
fill="#4CAF50"
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
></path>
<path
fill="#1976D2"
d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
></path>
</svg>
Sign up with GitHub
</Button>
</div>
)
}
export default SignupForm
================================================
FILE: src/components/forms/formAction.ts
================================================
'use server'
import { signIn, signOut } from "../auth";
export async function handleGoogleAuth(myCallbackUrl:string){
await signIn('google', {redirectTo:myCallbackUrl})
// await signOut({redirectTo:"/"})
}
================================================
FILE: src/components/mockup/ComputerMockup.tsx
================================================
import React from 'react'
function ComputerMockup({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="min-h-screen w-full flex items-center justify-center relative">
<div className="flex flex-col w-auto h-fit px-1 lg:px-0 ">
<figure className="ms-auto relative z-[1] w-full lg:w-[68rem] h-auto rounded-b-lg shadow-[0_2.75rem_3.5rem_-2rem_rgb(45_55_75_/_20%),_0_0_5rem_-2rem_rgb(45_55_75_/_15%)] dark:shadow-[0_2.75rem_3.5rem_-2rem_rgb(0_0_0_/_20%),_0_0_5rem_-2rem_rgb(0_0_0_/_15%)]">
<div className="relative flex items-center max-w-full bg-gray-800 rounded-t-lg py-2 px-24 dark:bg-neutral-700">
<div className="flex space-x-1 absolute top-2/4 start-4 -translate-y-1">
<span className="h-4 w-4 bg-red-600 rounded-full dark:bg-neutral-600"></span>
<span className="h-4 w-4 bg-green-600 rounded-full dark:bg-neutral-600"></span>
<span className="h-4 w-4 bg-yellow-600 rounded-full dark:bg-neutral-600"></span>
</div>
<div className="flex justify-center items-center w-full bg-gray-700 text-[.5rem] text-gray-400 rounded-sm sm:text-[1rem] dark:bg-neutral-600 dark:text-neutral-400">
https.itzmylink.vercel.app/...
</div>
</div>
<div className="bg-zinc-100 rounded-b-lg h-[38rem] border relative overflow-hidden">
{children}
</div>
</figure>
</div>
</div>
)
}
export default ComputerMockup
================================================
FILE: src/components/mockup/MobileMockup.tsx
================================================
import MobileScreen from "./MobileScreen";
const MobileMockup = () => {
return (
<div className="relative mx-auto border-gray-800 dark:border-gray-800 bg-gray-800 border-[14px] rounded-[2.5rem] h-[658px] w-[354px] shadow-xl">
<div className="w-[148px] h-[18px] bg-gray-800 top-0 rounded-b-[1rem] left-1/2 -translate-x-1/2 absolute z-50"></div>
<div className="h-[46px] w-[3px] bg-gray-800 absolute -start-[17px] top-[124px] rounded-s-lg"></div>
<div className="h-[46px] w-[3px] bg-gray-800 absolute -start-[17px] top-[178px] rounded-s-lg"></div>
<div className="h-[64px] w-[3px] bg-gray-800 absolute -end-[17px] top-[142px] rounded-e-lg"></div>
<MobileScreen />
</div>
);
};
export default MobileMockup;
================================================
FILE: src/components/mockup/MobileScreen.tsx
================================================
"use client"
import { cn } from "@/lib/utils"
import { useData } from "@/lib/Context"
import React from "react"
import { BACKGROUND_OPTIONS } from "../Background/BgSnippets"
import DisplayScreen from "../screen/DisplayScreen"
const MobileScreen = () =>{
const {MyLink} = useData()
const [isEmpty, setIsEmpty] = React.useState<boolean>(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(
<div className={cn("rounded-[2rem] overflow-hidden w-full h-full relative",{"bg-white": !MyLink.bg} )}>
{
isEmpty
? <div className='bg-white w-full text-sm text-muted-foreground h-full flex justify-center items-center z-20'>Nothing to show...</div>
: (
<>
{!isEmpty && selectedBgComponent}
<div className='h-full pt-10 px-2'>
<DisplayScreen myData={MyLink}/>
</div>
</>
)
}
</div>
)
}
export default MobileScreen
================================================
FILE: src/components/screen/DisplayScreen.tsx
================================================
"use client";
import { Icon } from "@iconify/react/dist/iconify.js";
import React from "react";
import AdditionalLinkCard from "../AdditionalLinkCards";
import { motion, AnimatePresence } from "framer-motion";
import SlightFlip from "../Animation/FlipText";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import BlurIn from "../Animation/BlurText";
import FramerWrapper from "../Animation/FramerWrapper";
import { TextEffect } from "../Animation/TextEffect";
import { ImageIcon } from "lucide-react";
const DisplayScreen: React.FC<DisplayDataProps> = ({ myData }) => {
const EmptySocialLiks =
!myData.fb &&
!myData.ig &&
!myData.tg &&
!myData.em &&
!myData.tw &&
!myData.lk &&
!myData.yt &&
!myData.gt &&
!myData.wh;
const iconMap: Record<string, string> = {
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 (
<AnimatePresence>
<motion.div className=" relative flex-grow flex-1 grid place-content-center">
<div className="flex flex-col items-center justify-center min-h-[100px] mx-auto w-full ">
{/* Avatar------- */}
<div className="w-full flex items-center justify-center h-fit px-2 py-4 ">
<motion.div
className="aspect-square relative h-[5.5rem] w-auto bg-gray-100 rounded-full overflow-hidden ring ring-slate-200"
initial={{ scale: 0 }}
animate={{ rotate: 360, scale: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
}}
>
<Avatar className="h-full w-auto object-fill">
<AvatarImage
src={myData.i}
alt={`PIC`}
className="h-full w-full object-cover"
/>
<AvatarFallback>
<ImageIcon className="h-8 w-8 text-gray-300" />
</AvatarFallback>
</Avatar>
</motion.div>
</div>
{/* Name & About------- */}
<div className="w-full flex my-2 flex-col items-center justify-center ">
<TextEffect
per="char"
preset="fade"
className="text-lg font-bold text-slate-800 dark:text-white md:text-xl"
>
{myData.n as string}
</TextEffect>
<BlurIn
word={myData.a as string}
className=" text-xs md:text-sm mt-2 text-slate-600 text-center max-w-[581px]"
/>
</div>
{/* Social Icons------- */}
{!EmptySocialLiks && (
<div className="w-full flex mt-5 mb-9 items-center justify-center flex-wrap ">
{Object.entries(myData).map(([key, value], indx) => {
const timing = 0.55 + indx * 0.125;
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 (
<FramerWrapper
key={indx}
className="p-1"
scale={0.8}
y={50}
delay={timing}
>
<span key={key}>
<a href={`mailto:${value}`}>
{/* <Icon icon={propIcon as any} className="h-6 w-6" /> */}
</a>
</span>
</FramerWrapper>
);
} 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 (
<FramerWrapper
key={indx}
className="p-1"
scale={0.8}
y={50}
delay={timing}
>
<span key={key}>
<a
href={whatsappValue}
target="_blank"
rel="noopener noreferrer"
>
<Icon
icon={propIcon as string}
className="h-6 w-6"
/>
</a>
</span>
</FramerWrapper>
);
} else {
return (
<FramerWrapper
key={indx}
className="p-1"
scale={0.8}
y={50}
delay={timing}
>
<span key={key}>
<a
href={value}
target="_blank"
rel="noopener noreferrer"
>
<Icon
icon={propIcon as string}
className="h-6 w-6"
/>
</a>
</span>
</FramerWrapper>
);
}
}
return null;
})}
</div>
)}
{/* Links------- */}
<div className="w-full min-h-[300px]">
<ul className="space-y-2">
{myData.ls &&
myData.ls.map((link, id) => {
// Define the alternating values for x and y
const positions = [
{ x: -200, y: 0 },
{ x: 200, y: 0 },
];
const timing = 0.55 + id * 0.125;
// Use modulus operator to cycle through the positions array
const { x, y } = positions[id % positions.length];
return (
<FramerWrapper y={y} x={x} delay={timing} key={id}>
<AdditionalLinkCard
label={link.l}
icon={link.i}
url={link.u}
key={id}
/>
</FramerWrapper>
);
})}
</ul>
</div>
</div>
</motion.div>
</AnimatePresence>
);
};
export default DisplayScreen;
================================================
FILE: src/components/ui/Drrawer.tsx
================================================
"use client"
import { forwardRef } from "react"
import { Drawer as DrawerPrimitive } from "vaul"
import { cn } from "@/lib/utils"
const DrawerTrigger = DrawerPrimitive.Trigger
const DrawerContent = forwardRef<
React.ElementRef<typeof DrawerPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DrawerPrimitive.Portal>
<DrawerPrimitive.Overlay className="fixed inset-0 z-50 bg-zinc-950/60" />
<DrawerPrimitive.Content
ref={ref}
className={cn(
"fixed inset-x-0 bottom-0 z-50 mt-24 h-[96%] rounded-t-[10px] bg-background overflow-hidden",
className
)}
{...props}
>
<div className="absolute left-1/2 top-3 h-1.5 w-[50px] translate-x-[-50%] rounded-full bg-muted z-50" />
{children}
</DrawerPrimitive.Content>
</DrawerPrimitive.Portal>
))
DrawerContent.displayName = "DrawerContent"
export { DrawerTrigger, DrawerContent }
================================================
FILE: src/components/ui/SocialInput.tsx
================================================
import * as React from "react"
import { cn } from "@/lib/utils"
import { Icon } from '@iconify/react';
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
icon: string
}
const SocialInput = React.forwardRef<HTMLInputElement, InputProps>(
({ className, icon: propIcon, ...props }, ref) => {
return (
<div className="relative">
<Icon icon={propIcon} className="h-5 w-5 absolute left-2.5 -translate-y-2/4 top-2/4 " />
<input
type="search"
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 pl-10",
className
)}
ref={ref}
{...props}
/>
</div>
)
}
)
SocialInput.displayName = "SocialInput"
export { SocialInput }
================================================
FILE: src/components/ui/SortableLink.tsx
================================================
"use client";
import React, { FC } from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useData } from "@/lib/Context";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
interface SortableLinksProps {
id: AdditionalLinkProps;
index: number;
}
export const SortableLinks: FC<SortableLinksProps> = ({ id, index }) => {
let uniqueID = id.id;
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id: uniqueID });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
const { MyLink, updateAdditionalInfo } = useData();
// ICONS LISTS
const iconsLists = [
{name:'My Portfolio',code:'ph:laptop-duotone'},
{name:'Chatbot project',code:'ant-design:robot-outlined'},
{name:'Ai project',code:'fluent:brain-circuit-20-regular'},
{name:'Web Project',code:'icon-park-outline:blockchain'},
{name:'My Blogs',code:'ph:pencil-duotone'},
]
const showWhatvalue = (e:any) =>{
const newLinks = [...MyLink.ls];
newLinks[index].i = e
updateAdditionalInfo(newLinks);
console.log(e);
}
return (
<div ref={setNodeRef} style={style} key={uniqueID}>
<Card className="p-4 relative">
<div className="space-y-4">
<div className="grid md:grid-cols-2 gap-2">
<div className="grid gap-2">
<Label htmlFor={`link-icon-${uniqueID}`}>Icon Key</Label>
<Select onValueChange={showWhatvalue} name="i" value={id.i}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select a Icons" />
</SelectTrigger>
<SelectContent >
<SelectGroup >
<SelectLabel>none</SelectLabel>
{iconsLists.map((val,i)=>{
return <SelectItem key={i} value={`${val.code}`}>{val.name}</SelectItem>
})}
</SelectGroup>
</SelectContent>
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor={`link-name-${uniqueID}`}>Label</Label>
<Input
id={`link-name-${uniqueID}`}
name="l"
type="text"
placeholder="Amazon"
value={id.l}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const newLinks = [...MyLink.ls];
newLinks[index].l = e.target.value;
updateAdditionalInfo(newLinks);
}}
/>
</div>
</div>
<div className="grid gap-2">
<Label htmlFor={`link-name-${uniqueID}`}>Destination URL</Label>
<Input
id={`link-url-${uniqueID}`}
name="u"
type="url"
placeholder="https://example.com"
value={id.u}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const newLinks = [...MyLink.ls];
newLinks[index].u = e.target.value;
updateAdditionalInfo(newLinks);
}}
/>
</div>
</div>
<button
className="DragHandle absolute -top-2 right-5 bg-white"
{...attributes}
{...listeners}
>
<svg viewBox="0 0 20 20" width="15">
<path
d="M7 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 2zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 14zm6-8a2 2 0 1 0-.001-4.001A2 2 0 0 0 13 6zm0 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 14z"
transform="rotate(-90,10,10)"
fill={"currentcolor"}
></path>
</svg>
</button>
</Card>
</div>
);
};
================================================
FILE: src/components/ui/alert-dialog.tsx
================================================
"use client"
import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
const AlertDialog = AlertDialogPrimitive.Root
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
const AlertDialogPortal = AlertDialogPrimitive.Portal
const AlertDialogOverlay = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
ref={ref}
/>
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
const AlertDialogContent = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
>(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props}
/>
</AlertDialogPortal>
))
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
const AlertDialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className
)}
{...props}
/>
)
AlertDialogHeader.displayName = "AlertDialogHeader"
const AlertDialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
AlertDialogFooter.displayName = "AlertDialogFooter"
const AlertDialogTitle = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold", className)}
{...props}
/>
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
const AlertDialogDescription = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName
const AlertDialogAction = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Action>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action
ref={ref}
className={cn(buttonVariants(), className)}
{...props}
/>
))
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
const AlertDialogCancel = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(
buttonVariants({ variant: "outline" }),
"mt-2 sm:mt-0",
className
)}
{...props}
/>
))
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
}
================================================
FILE: src/components/ui/avatar.tsx
================================================
"use client"
import * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar"
import { cn } from "@/lib/utils"
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className
)}
{...props}
/>
))
Avatar.displayName = AvatarPrimitive.Root.displayName
const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
))
AvatarImage.displayName = AvatarPrimitive.Image.displayName
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className
)}
{...props}
/>
))
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
export { Avatar, AvatarImage, AvatarFallback }
================================================
FILE: src/components/ui/button.tsx
================================================
import { cn } from '@/lib/utils'
import { cva, VariantProps } from 'class-variance-authority'
import { Loader2 } from 'lucide-react'
import * as React from 'react'
const buttonVariants = cva(
'active:scale-95 inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:opacity-50 dark:focus:ring-slate-400 disabled:pointer-events-none dark:focus:ring-offset-slate-900',
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
custom:
' bg-[#343e44] text-white hover:bg-[#5c6165] hover:shadow-lg',
subtle:
'border border-input bg-background hover:bg-[#6aa2e6] hover:text-white',
ghost:
'bg-transparent hover:bg-zinc-100 text-zinc-800 data-[state=open]:bg-transparent data-[state=open]:bg-transparent',
link: 'bg-transparent dark:bg-transparent underline-offset-4 hover:underline text-slate-900 dark:text-slate-100 hover:bg-transparent dark:hover:bg-transparent',
},
size: {
default: 'h-10 py-2 px-4',
sm: 'h-9 px-2 rounded-md',
xs: 'h-8 px-1.5 rounded-sm',
lg: 'h-11 px-8 rounded-md',
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
isLoading?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, children, variant, isLoading, size, ...props }, ref) => {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
disabled={isLoading}
{...props}>
{isLoading ? <Loader2 className='mr-2 h-4 w-4 animate-spin' /> : null}
{children}
</button>
)
}
)
Button.displayName = 'Button'
export { Button, buttonVariants }
================================================
FILE: src/components/ui/card.tsx
================================================
import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
================================================
FILE: src/components/ui/dialog.tsx
================================================
"use client"
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogPortal = DialogPrimitive.Portal
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
{...props}
/>
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
}
================================================
FILE: src/components/ui/dropdown-menu.tsx
================================================
"use client"
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { Check, ChevronRight, Circle } from "lucide-react"
import { cn } from "@/lib/utils"
const DropdownMenu = DropdownMenuPrimitive.Root
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
const DropdownMenuGroup = DropdownMenuPrimitive.Group
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
const DropdownMenuSub = DropdownMenuPrimitive.Sub
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
inset && "pl-8",
className
)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
))
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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",
className
)}
{...props}
/>
))
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 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",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className
)}
{...props}
/>
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className
)}
{...props}
/>
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props}
/>
)
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
}
================================================
FILE: src/components/ui/form.tsx
================================================
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
} from "react-hook-form"
import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
const Form = FormProvider
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
name: TName
}
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue
)
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
)
}
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext)
const itemContext = React.useContext(FormItemContext)
const { getFieldState, formState } = useFormContext()
const fieldState = getFieldState(fieldContext.name, formState)
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>")
}
const { id } = itemContext
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
}
}
type FormItemContextValue = {
id: string
}
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
)
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId()
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
)
})
FormItem.displayName = "FormItem"
const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField()
return (
<Label
ref={ref}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
)
})
FormLabel.displayName = "FormLabel"
const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
)
})
FormControl.displayName = "FormControl"
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField()
return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
)
})
FormDescription.displayName = "FormDescription"
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : children
if (!body) {
return null
}
return (
<p
ref={ref}
id={formMessageId}
className={cn("text-sm font-medium text-destructive", className)}
{...props}
>
{body}
</p>
)
})
FormMessage.displayName = "FormMessage"
export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
}
================================================
FILE: src/components/ui/input-otp.tsx
================================================
"use client"
import * as React from "react"
import { OTPInput, OTPInputContext } from "input-otp"
import { Dot } from "lucide-react"
import { cn } from "@/lib/utils"
const InputOTP = React.forwardRef<
React.ElementRef<typeof OTPInput>,
React.ComponentPropsWithoutRef<typeof OTPInput>
>(({ className, containerClassName, ...props }, ref) => (
<OTPInput
ref={ref}
containerClassName={cn(
"flex items-center gap-2 has-[:disabled]:opacity-50",
containerClassName
)}
className={cn("disabled:cursor-not-allowed", className)}
{...props}
/>
))
InputOTP.displayName = "InputOTP"
const InputOTPGroup = React.forwardRef<
React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div">
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex items-center", className)} {...props} />
))
InputOTPGroup.displayName = "InputOTPGroup"
const InputOTPSlot = React.forwardRef<
React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div"> & { index: number }
>(({ index, className, ...props }, ref) => {
const inputOTPContext = React.useContext(OTPInputContext)
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
return (
<div
ref={ref}
className={cn(
"relative flex h-10 w-10 items-center justify-center border-y border-r border-input text-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md",
isActive && "z-10 ring-2 ring-ring ring-offset-background",
className
)}
{...props}
>
{char}
{hasFakeCaret && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
</div>
)}
</div>
)
})
InputOTPSlot.displayName = "InputOTPSlot"
const InputOTPSeparator = React.forwardRef<
React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div">
>(({ ...props }, ref) => (
<div ref={ref} role="separator" {...props}>
<Dot />
</div>
))
InputOTPSeparator.displayName = "InputOTPSeparator"
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
================================================
FILE: src/components/ui/input.tsx
================================================
import * as React from "react"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }
================================================
FILE: src/components/ui/label.tsx
================================================
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }
================================================
FILE: src/components/ui/select.tsx
================================================
"use client"
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown } from "lucide-react"
import { cn } from "@/lib/utils"
const Select = SelectPrimitive.Root
const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 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",
className
)}
position={position}
{...props}
>
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
</SelectPrimitive.Viewport>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
))
SelectLabel.displayName = SelectPrimitive.Label.displayName
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
}
================================================
FILE: src/components/ui/skeleton.tsx
================================================
import { cn } from "@/lib/utils"
function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("animate-pulse rounded-md bg-muted", className)}
{...props}
/>
)
}
export { Skeleton }
================================================
FILE: src/components/ui/textarea.tsx
================================================
import * as React from "react"
import { cn } from "@/lib/utils"
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = "Textarea"
export { Textarea }
================================================
FILE: src/lib/Context.tsx
================================================
import React, { createContext, useContext, useState } from "react";
interface LinkProps {
n: string; //Name
i: string; // image
a: string; //About
bg: string; //Background
fb: string; //facebook
ig: string; //instagram
tg: string; //telegram
em: string; //email
tw: string; //twitter
lk: string; //linkedin
yt: string; //youtube
gt: string; //github
wh: string; //whatsup
ls: AdditionalLinkProps[]; // Additional Forms
}
const initialData: LinkProps = {
n: "", //Name
i: "", // image
a: "", //About
bg: "", //Background
fb: "", //facebook
ig: "", //instagram
tg: "", //telegram
em: "", //email
tw: "", //twitter
lk: "", //linkedin
yt: "", //youtube
gt: "", //github
wh: "", //whatsup
ls: [], //Additional Forms
};
interface DataContextType {
// Todo: fix type props
data: string;
MyLink: LinkProps;
addNewData: (linkData: AdditionalLinkProps) => void;
setData: (val: string) => void;
updateProfileInfo: (name: any, value: any) => void;
selectBackground: (bgcode: string) => void;
updateIndex: (updatedIndex: AdditionalLinkProps[]) => void;
updateAdditionalInfo: (updatedIndex: any) => void;
showDemo: () => void;
}
const demoData: LinkProps = {
n: "Md Taqui Imam",
i: "https://firebasestorage.googleapis.com/v0/b/projectfriendz-45b49.appspot.com/o/images%2FSocialLogo.png?alt=media&token=551c9a3c-07e4-486d-9d26-96c619d817a9&_gl=1*uluah4*_ga*NDUyNjQ5MjYxLjE2OTcyMjI3MDU.*_ga_CW55HF8NVT*MTY5NzIyMjcwNS4xLjEuMTY5NzIyMjg0Ni4xMy4wLjA.",
a: "I'm a self-taught Web developer who is always learning and creating cool Project stuffs.",
fb: "https://www.facebook.com/shahina.khatun.1044",
tw: "https://twitter.com/Taquiimam14",
ig: "https://www.instagram.com/taqui_imam_786/",
tg: "https://t.me/@Taqui_devloper",
gt: "https://github.com/taqui-786",
lk: "https://linkedin.com/in/taqui-imam",
em: "mdtaqui.jhar@gmail.com",
wh: "+916666666666",
yt: "https://youtube.com/@james_smith",
bg: "#4F4F4F",
ls: [
{
id: 1,
i: "ph:laptop-duotone",
l: "My Portfolio Website",
u: "https://example.com",
},
{
id: 2,
i: "ant-design:robot-outlined",
l: "My Chatbot Project",
u: "https://example.com/chatbot",
},
{
id: 3,
i: "fluent:brain-circuit-20-regular",
l: "My Machine Learning Project",
u: "https://example.com/ml",
},
{
id: 4,
i: "icon-park-outline:blockchain",
l: "My Blockchain Project",
u: "https://example.com/blockchain",
},
{
id: 5,
i: "ph:pencil-duotone",
l: "My Blog Posts",
u: "https://taquideveloper.hashnode.dev/",
},
],
};
const DataContext = createContext<DataContextType | undefined>(undefined);
export const DataProvider = ({ children }: { children: React.ReactNode }) => {
const [data, setData] = useState<string>("");
const [MyLink, setMyLink] = useState<LinkProps>(initialData);
// UPDATE PERSONAL INFORMATION
const updateProfileInfo = (name: any, value: any) => {
setMyLink((prevState) => ({
...prevState,
[name]: value,
}));
};
// SELECT BACKGROUND FUNCTION
const selectBackground = (bgcode: string) => {
setMyLink((prevState) => ({
...prevState,
bg: bgcode,
}));
};
// ADDITIONAL INFO FUNCTIONS
const updateIndex = (updatedIndex: AdditionalLinkProps[]) => {
setMyLink((prevState) => ({
...prevState,
ls: updatedIndex,
}));
};
const updateAdditionalInfo = (updatedIndex: any) => {
setMyLink((prevState) => ({
...prevState,
ls: updatedIndex,
}));
};
const addNewData = (linkData: AdditionalLinkProps) => {
setMyLink((prevData) => ({
...prevData,
ls: [...prevData.ls, linkData],
}));
};
// SHOW DEMO FUNCTION
const showDemo = () => {
setMyLink(demoData);
};
return (
<DataContext.Provider
value={{
data,
addNewData,
setData,
showDemo,
MyLink,
updateProfileInfo,
updateIndex,
selectBackground,
updateAdditionalInfo,
}}
>
{children}
</DataContext.Provider>
);
};
export const useData = () => {
const context = useContext(DataContext);
if (!context) {
throw new Error("useData must be used within a DataProvider");
}
return context;
};
================================================
FILE: src/lib/Firebase.ts
================================================
import { initializeApp } from 'firebase/app';
import { getStorage } from 'firebase/storage';
const firebaseConfig = {
apiKey: process.env.APIKEY,
authDomain:"projectfriendz-45b49.firebaseapp.com",
projectId: "projectfriendz-45b49",
storageBucket: "projectfriendz-45b49.appspot.com",
messagingSenderId: "186662584426",
appId: "1:186662584426:web:b37a6002f57c2af7578a13",
measurementId:"G-ZCMDL02FYD"
};
const app = initializeApp(firebaseConfig);
const storage = getStorage(app);
export{ storage };
================================================
FILE: src/lib/RateLimiter.tsx
================================================
// Function written by ChatGPT
class RateLimiter {
private limit: number; // Maximum allowed requests
private interval: number; // Time window (in milliseconds)
private requests: Map<string, number[]>; // In-memory storage for request timestamps
constructor(limit: number, interval: number) {
this.limit = limit;
this.interval = interval;
this.requests = new Map<string, number[]>();
}
isAllowed(key: string): boolean {
const now = Date.now();
const requests = this.requests.get(key) || [];
// Remove expired timestamps
const validRequests = requests.filter((timestamp) => now - timestamp <= this.interval);
if (validRequests.length < this.limit) {
// Add the current timestamp to the list
validRequests.push(now);
this.requests.set(key, validRequests);
return true;
} else {
return false; // Rate limit exceeded
}
}
}
export default RateLimiter;
================================================
FILE: src/lib/supabase/actions.ts
================================================
"use server";
import { auth } from "@/components/auth";
import { supabaseServer } from "./supabaseServer";
export const isUserExists = async (email: string) => {
const supabase = await supabaseServer()
.from("users")
.select("*")
.eq("email", email);
if (supabase.data?.length) {
return { status: "exists"};
}
return { status: "success" };
};
export const createUser = async (payload:any) => {
const supabase = await supabaseServer().from('users').insert({fullname:payload?.name, email: payload?.email})
if (supabase.statusText === 'Created') {
return { status: "success"};
}
return { status: "error", message: supabase.error };
};
type CreateCustomPathResponse =
| { status: "exists"; message: string }
| { status: "notAuthenticated"; message: string }
| { status: "created"; message: string };
export const createCustomPath = async (path: string,localLink:string): Promise<CreateCustomPathResponse> => {
const session = await auth();
if (session?.user) {
const supabase = await supabaseServer();
const isExists = await supabase.from('links').select('*').eq('path', path);
if (isExists.data?.length) {
return { status: "exists", message: `This '/${path}' already exists.` };
}
const create = await supabase.from('links').insert({ path, email: session.user?.email, link:localLink });
if (create.statusText === 'Created') {
return { status: "created", message: "Link created successfully" };
}
}
return { status: "notAuthenticated", message: "Not authenticated" };
};
================================================
FILE: src/lib/supabase/supabaseClient.ts
================================================
import { createBrowserClient } from '@supabase/ssr'
export function supabaseClient() {
// Create a supabase client on the browser with project's credentials
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
================================================
FILE: src/lib/supabase/supabaseMiddleware.ts
================================================
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'
export async function updateSession(request: NextRequest) {
let supabaseResponse = NextResponse.next({
request,
})
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll()
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
supabaseResponse = NextResponse.next({
request,
})
cookiesToSet.forEach(({ name, value, options }) =>
supabaseResponse.cookies.set(name, value, options)
)
},
},
}
)
// refreshing the auth token
await supabase.auth.getUser()
return supabaseResponse
}
================================================
FILE: src/lib/supabase/supabaseServer.ts
================================================
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'
export function supabaseServer() {
const cookieStore = cookies()
// Create a server's supabase client with newly configured cookie,
// which could be used to maintain user's session
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
} catch {
// The `setAll` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
},
}
)
}
================================================
FILE: src/lib/utils.ts
================================================
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
import { encode, decode } from 'js-base64';
export const encodeData = (obj: any): string => {
return encode(JSON.stringify(obj));
};
export const decodeData = (base64: string): any => {
return JSON.parse(decode(base64));
};
================================================
FILE: src/middleware.ts
================================================
import { type NextRequest } from 'next/server'
import { updateSession } from './lib/supabase/supabaseMiddleware'
export async function middleware(request: NextRequest) {
// update user's auth session
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: src/types/Types.ts
================================================
interface DataProps {
n?: string;
a?: string;
i?: string;
bg?: string;
fb?: string;
tg?: string;
ig?: string;
gt?: string;
tw?: string;
lk?: string;
em?: string;
wh?: string;
yt?: string;
ls?: AdditionalLinkProps[];
};
const socialLinksData = {
fb: 'facebook',
tw: 'twitter',
ig: 'instagram',
gt: 'github',
tg: 'telegram',
lk: 'linkedin',
em: 'email',
wh: 'whatsapp',
yt: 'youtube',
};
interface SocialLinkProviderProps {
name: string;
icon: string;
id: keyof typeof socialLinksData;
}
interface DisplayDataProps {
myData: DataProps
}
interface AdditionalLinkProps {
id: number
i: string;
l: string;
u: string;
}
================================================
FILE: tailwind.config.ts
================================================
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
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
SYMBOL INDEX (80 symbols across 57 files)
FILE: emails/index.tsx
type SupaAuthVerifyEmailProp (line 16) | interface SupaAuthVerifyEmailProp {
function SupaAuthVerifyEmail (line 20) | function SupaAuthVerifyEmail({
FILE: middleware.ts
function middleware (line 4) | async function middleware(request: NextRequest) {
FILE: src/app/1/[slug]/page.tsx
type Props (line 7) | type Props = {
function generateMetadata (line 12) | async function generateMetadata({ params }: Props) {
FILE: src/app/1/error.tsx
function Error (line 7) | function Error() {
FILE: src/app/1/loading.tsx
function DataLoading (line 4) | function DataLoading() {
FILE: src/app/1/page.tsx
function generateMetadata (line 7) | async function generateMetadata({ searchParams }: any) {
FILE: src/app/Not-found.tsx
function NotFound (line 5) | function NotFound() {
FILE: src/app/api/og/route.tsx
function truncateString (line 7) | function truncateString({ str, maxLength }: { str: string, maxLength: nu...
function GET (line 14) | async function GET(req: NextRequest) {
FILE: src/app/auth/signin/page.tsx
type Props (line 12) | type Props = {
FILE: src/app/create/loading.tsx
function SiteLoading (line 4) | function SiteLoading() {
FILE: src/app/layout.tsx
function RootLayout (line 68) | function RootLayout({
FILE: src/app/links/page.tsx
function Component (line 7) | function Component() {
FILE: src/app/page.tsx
function Home (line 14) | async function Home() {
FILE: src/app/preview/layout.tsx
function PreviewLayout (line 1) | function PreviewLayout({
FILE: src/app/preview/page.tsx
function page (line 9) | function page({ searchParams }: any) {
FILE: src/components/ActionButtons/CustomLinkDialog.tsx
function Component (line 33) | function Component({localLink}:{localLink:string}) {
FILE: src/components/ActionButtons/DemoBtn.tsx
type DemoDataProps (line 7) | interface DemoDataProps { }
FILE: src/components/ActionButtons/PreviewFooter.tsx
function PreviewFooter (line 8) | function PreviewFooter({
FILE: src/components/ActionButtons/PublishBtn.tsx
type PublishProps (line 22) | interface PublishProps {
function isEmptyValues (line 33) | function isEmptyValues(obj: any) {
function publish (line 52) | function publish() {
FILE: src/components/ActionButtons/ResponsivePreviewBtn.tsx
type PreviewButtonProps (line 11) | interface PreviewButtonProps { }
function isEmptyValues (line 19) | function isEmptyValues(obj: any) {
FILE: src/components/AdditionalLinkCards.tsx
type AdditionalLinkCardProps (line 4) | interface AdditionalLinkCardProps {
FILE: src/components/AdditionalLinkForm.tsx
function handleDragEnd (line 60) | function handleDragEnd(event: any) {
FILE: src/components/Animation/BlurText.tsx
type BlurIntProps (line 7) | interface BlurIntProps {
FILE: src/components/Animation/FlipText.tsx
type SlightFlipProps (line 7) | interface SlightFlipProps {
function SlightFlip (line 15) | function SlightFlip({
FILE: src/components/Animation/FramerWrapper.tsx
type FramerMotionProps (line 4) | type FramerMotionProps = {
function FramerWrapper (line 13) | function FramerWrapper({children,delay = 0.25 ,y = 15, x = 0,duration = ...
FILE: src/components/Animation/TextEffect.tsx
type PresetType (line 5) | type PresetType = 'blur' | 'shake' | 'scale' | 'fade' | 'slide';
type TextEffectProps (line 7) | type TextEffectProps = {
function TextEffect (line 112) | function TextEffect({
FILE: src/components/Background/BackgroundCards.tsx
type BackgroundCardProps (line 7) | type BackgroundCardProps = {};
FILE: src/components/Background/BackgroundForm.tsx
function Background (line 13) | function Background() {
FILE: src/components/Background/BgSnippets.tsx
constant BACKGROUND_OPTIONS (line 77) | const BACKGROUND_OPTIONS = [
FILE: src/components/Navbar.tsx
function Navbar (line 18) | async function Navbar() {
FILE: src/components/PersonalInfo.tsx
type InputChangeEvent (line 15) | type InputChangeEvent = React.ChangeEvent<
FILE: src/components/PhotoUpload.tsx
function handleImageUpload (line 18) | async function handleImageUpload (file:File) {
FILE: src/components/PreviewPage.tsx
function PreviewPage (line 7) | function PreviewPage({data}:{data:any}) {
FILE: src/components/Provider.tsx
function Providers (line 8) | function Providers({ children }: { children: React.ReactNode }) {
FILE: src/components/SocialLinkForm.tsx
type InputChangeEvent (line 26) | type InputChangeEvent = React.ChangeEvent<HTMLInputElement>;
type SocialLinksFormProps (line 28) | interface SocialLinksFormProps { }
FILE: src/components/auth.ts
method authorized (line 14) | authorized({ request, auth }) {
method jwt (line 19) | jwt({ token, trigger, session, account }) {
method session (line 24) | async session({ session, token }) {
method signIn (line 30) | async signIn({ account, profile }: any) {
type Session (line 48) | interface Session {
type JWT (line 54) | interface JWT {
FILE: src/components/forms/LoginGoogleBtn.tsx
function LoginGoogleBtn (line 6) | function LoginGoogleBtn({callbackUrl = '/'}:{callbackUrl:string}) {
FILE: src/components/forms/SignupForm.tsx
function SignupForm (line 6) | function SignupForm() {
FILE: src/components/forms/formAction.ts
function handleGoogleAuth (line 4) | async function handleGoogleAuth(myCallbackUrl:string){
FILE: src/components/mockup/ComputerMockup.tsx
function ComputerMockup (line 3) | function ComputerMockup({
FILE: src/components/mockup/MobileScreen.tsx
function isEmptyValues (line 14) | function isEmptyValues(obj: any) {
FILE: src/components/ui/SocialInput.tsx
type InputProps (line 6) | interface InputProps
FILE: src/components/ui/SortableLink.tsx
type SortableLinksProps (line 20) | interface SortableLinksProps {
FILE: src/components/ui/button.tsx
type ButtonProps (line 39) | interface ButtonProps
FILE: src/components/ui/form.tsx
type FormFieldContextValue (line 20) | type FormFieldContextValue<
type FormItemContextValue (line 67) | type FormItemContextValue = {
FILE: src/components/ui/input.tsx
type InputProps (line 5) | interface InputProps
FILE: src/components/ui/skeleton.tsx
function Skeleton (line 3) | function Skeleton({
FILE: src/components/ui/textarea.tsx
type TextareaProps (line 5) | interface TextareaProps
FILE: src/lib/Context.tsx
type LinkProps (line 3) | interface LinkProps {
type DataContextType (line 35) | interface DataContextType {
FILE: src/lib/RateLimiter.tsx
class RateLimiter (line 3) | class RateLimiter {
method constructor (line 8) | constructor(limit: number, interval: number) {
method isAllowed (line 14) | isAllowed(key: string): boolean {
FILE: src/lib/supabase/actions.ts
type CreateCustomPathResponse (line 29) | type CreateCustomPathResponse =
FILE: src/lib/supabase/supabaseClient.ts
function supabaseClient (line 3) | function supabaseClient() {
FILE: src/lib/supabase/supabaseMiddleware.ts
function updateSession (line 4) | async function updateSession(request: NextRequest) {
FILE: src/lib/supabase/supabaseServer.ts
function supabaseServer (line 4) | function supabaseServer() {
FILE: src/lib/utils.ts
function cn (line 4) | function cn(...inputs: ClassValue[]) {
FILE: src/middleware.ts
function middleware (line 5) | async function middleware(request: NextRequest) {
FILE: src/types/Types.ts
type DataProps (line 2) | interface DataProps {
type SocialLinkProviderProps (line 31) | interface SocialLinkProviderProps {
type DisplayDataProps (line 36) | interface DisplayDataProps {
type AdditionalLinkProps (line 39) | interface AdditionalLinkProps {
Condensed preview — 83 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (165K chars).
[
{
"path": ".eslintrc.json",
"chars": 40,
"preview": "{\n \"extends\": \"next/core-web-vitals\"\n}\n"
},
{
"path": ".gitignore",
"chars": 368,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "README.md",
"chars": 1370,
"preview": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js"
},
{
"path": "components.json",
"chars": 326,
"preview": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"default\",\n \"rsc\": true,\n \"tsx\": true,\n \"tailwind\": {\n"
},
{
"path": "emails/index.tsx",
"chars": 3079,
"preview": "import {\n\tBody,\n\tContainer,\n\tHead,\n\tHeading,\n\tHr,\n\tHtml,\n\tImg,\n\tLink,\n\tPreview,\n\tSection,\n\tText,\n} from \"@react-email/co"
},
{
"path": "middleware.ts",
"chars": 595,
"preview": "import { updateSession } from \"@/lib/supabase/supabaseMiddleware\";\nimport { type NextRequest } from \"next/server\";\n\nexpo"
},
{
"path": "next.config.mjs",
"chars": 97,
"preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n \n};\n\nexport default nextConfig;"
},
{
"path": "package.json",
"chars": 1812,
"preview": "{\n \"name\": \"client\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"next dev\",\n \"build\": \"next "
},
{
"path": "postcss.config.js",
"chars": 82,
"preview": "module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n}\n"
},
{
"path": "src/app/1/[slug]/page.tsx",
"chars": 2171,
"preview": "import { decodeData } from \"@/lib/utils\";\nimport { BACKGROUND_OPTIONS } from \"@/components/Background/BgSnippets\";\nimpor"
},
{
"path": "src/app/1/error.tsx",
"chars": 560,
"preview": "'use client'\n\nimport { Button } from \"@/components/ui/button\"\nimport Link from \"next/link\"\n\n\nexport default function Err"
},
{
"path": "src/app/1/loading.tsx",
"chars": 1370,
"preview": "import { Skeleton } from '@/components/ui/skeleton'\nimport React from 'react'\n\nexport default function DataLoading() {\n "
},
{
"path": "src/app/1/page.tsx",
"chars": 1768,
"preview": "import { decodeData } from \"@/lib/utils\";\nimport NotFound from \"../Not-found\";\nimport { BACKGROUND_OPTIONS } from \"@/com"
},
{
"path": "src/app/Not-found.tsx",
"chars": 1596,
"preview": "import { Button } from '@/components/ui/button'\nimport Link from 'next/link'\nimport React from 'react'\n\nexport default f"
},
{
"path": "src/app/api/auth/[...nextauth]/route.ts",
"chars": 83,
"preview": "import { handlers } from \"@/components/auth\";\n\nexport const {GET, POST} = handlers;"
},
{
"path": "src/app/api/og/route.tsx",
"chars": 1451,
"preview": "import { NextRequest } from \"next/server\";\nimport type { ServerRuntime} from \"next\"\nimport { ImageResponse } from \"@verc"
},
{
"path": "src/app/auth/signin/page.tsx",
"chars": 1126,
"preview": "import React from 'react'\nimport Link from \"next/link\"\n\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,"
},
{
"path": "src/app/create/loading.tsx",
"chars": 904,
"preview": "import { Skeleton } from '@/components/ui/skeleton'\nimport React from 'react'\n\nexport default function SiteLoading() {\n "
},
{
"path": "src/app/create/page.tsx",
"chars": 446,
"preview": "import PreviewButton from \"@/components/ActionButtons/ResponsivePreviewBtn\";\nimport HomeEditor from \"@/components/HomeEd"
},
{
"path": "src/app/globals.css",
"chars": 3314,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n \n \n\n\n\n@layer base {\n :root {\n --background: 0 0% 100%;\n"
},
{
"path": "src/app/layout.tsx",
"chars": 2002,
"preview": "import \"./globals.css\";\nimport type { Metadata } from \"next\";\nimport { siteConfig } from \"./page\";\nimport { Inter } from"
},
{
"path": "src/app/links/page.tsx",
"chars": 982,
"preview": "import { Card, CardContent } from \"@/components/ui/card\"\nimport { Button, buttonVariants } from \"@/components/ui/button\""
},
{
"path": "src/app/page.tsx",
"chars": 2503,
"preview": "import Link from \"next/link\";\nimport landingPageImg from '../../public/landingPage.png'\nimport Image from \"next/image\";\n"
},
{
"path": "src/app/preview/layout.tsx",
"chars": 198,
"preview": "export default function PreviewLayout({\n children,\n }: {\n children: React.ReactNode\n }) {\n return <main class"
},
{
"path": "src/app/preview/page.tsx",
"chars": 1010,
"preview": "import React from \"react\";\nimport NotFound from \"../Not-found\";\nimport { decodeData } from \"@/lib/utils\";\nimport Compute"
},
{
"path": "src/components/ActionButtons/CustomLinkDialog.tsx",
"chars": 6307,
"preview": "\"use client\";\n\nimport { useState, useCallback } from \"react\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n "
},
{
"path": "src/components/ActionButtons/DemoBtn.tsx",
"chars": 462,
"preview": "\"use client\"\nimport React, { FC } from 'react'\nimport { Button } from '@/components/ui/button'\nimport { Play } from 'luc"
},
{
"path": "src/components/ActionButtons/PreviewFooter.tsx",
"chars": 2023,
"preview": "\"use client\";\nimport React from \"react\";\nimport { Button } from \"../ui/button\";\nimport { Check, Clipboard, Share } from "
},
{
"path": "src/components/ActionButtons/PublishBtn.tsx",
"chars": 5396,
"preview": "\"use client\"\n\nimport React, { FC } from 'react'\nimport { Button, buttonVariants } from '@/components/ui/button'\nimport {"
},
{
"path": "src/components/ActionButtons/ResponsivePreviewBtn.tsx",
"chars": 2245,
"preview": "\"use client\"\n\nimport React, { FC } from 'react'\nimport { Button } from \"@/components/ui/button\"\nimport { Drawer } from \""
},
{
"path": "src/components/AdditionalLinkCards.tsx",
"chars": 1272,
"preview": "import React, { FC } from 'react'\nimport { Icon } from '@iconify/react';\n\ninterface AdditionalLinkCardProps {\n label:"
},
{
"path": "src/components/AdditionalLinkForm.tsx",
"chars": 4235,
"preview": "\"use client\"\n\nimport React, { FC } from 'react'\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,"
},
{
"path": "src/components/Animation/BlurText.tsx",
"chars": 845,
"preview": "\"use client\";\n\nimport { motion } from \"framer-motion\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface BlurIntProps {\n wo"
},
{
"path": "src/components/Animation/FlipText.tsx",
"chars": 1038,
"preview": "\"use client\";\n\nimport { AnimatePresence, motion, Variants } from \"framer-motion\";\n\nimport { cn } from \"@/lib/utils\";\n\nin"
},
{
"path": "src/components/Animation/FramerWrapper.tsx",
"chars": 916,
"preview": "\"use client\";\nimport React, { useState } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\ntype Fra"
},
{
"path": "src/components/Animation/TextEffect.tsx",
"chars": 3211,
"preview": "'use client';\nimport { motion, Variants } from 'framer-motion';\nimport React from 'react';\n\ntype PresetType = 'blur' | '"
},
{
"path": "src/components/Background/BackgroundCards.tsx",
"chars": 1319,
"preview": "import React from 'react';\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\nimport { B"
},
{
"path": "src/components/Background/BackgroundForm.tsx",
"chars": 780,
"preview": "\"use client\"\n\nimport React from 'react'\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n Car"
},
{
"path": "src/components/Background/BgSnippets.tsx",
"chars": 4829,
"preview": "// original source: https://github.com/ibelick/background-snippets/blob/main/app/components/background.tsx\n\nconst BgThem"
},
{
"path": "src/components/DisplayData.tsx",
"chars": 4111,
"preview": "\"use client\";\nimport { ImageIcon } from \"lucide-react\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"./ui/avatar"
},
{
"path": "src/components/HomeEditor.tsx",
"chars": 1756,
"preview": "import PersonalInfo from \"./PersonalInfo\";\nimport SocialLinksForm from \"./SocialLinkForm\";\nimport Background from \"./Bac"
},
{
"path": "src/components/Navbar.tsx",
"chars": 2809,
"preview": "\nimport Link from \"next/link\";\nimport React from \"react\";\nimport { auth, signOut } from \"./auth\";\nimport {\n DropdownMen"
},
{
"path": "src/components/PersonalInfo.tsx",
"chars": 1873,
"preview": "\"use client\";\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card"
},
{
"path": "src/components/PhotoUpload.tsx",
"chars": 1547,
"preview": "\"use client\"\nimport { storage } from \"@/lib/Firebase\";\nimport { ref, uploadBytes, getDownloadURL } from \"firebase/storag"
},
{
"path": "src/components/PreviewPage.tsx",
"chars": 1355,
"preview": "'use client'\nimport React, { useEffect } from 'react'\nimport DisplayScreen from './screen/DisplayScreen'\nimport DataLoad"
},
{
"path": "src/components/Provider.tsx",
"chars": 340,
"preview": "'use client';\n\nimport { DataProvider } from \"@/lib/Context\";\nimport React from \"react\";\n\n// import { Toaster } from \"@/c"
},
{
"path": "src/components/SocialLinkForm.tsx",
"chars": 2289,
"preview": "\"use client\"\n\nimport React, { FC } from 'react'\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,"
},
{
"path": "src/components/auth.ts",
"chars": 1490,
"preview": "import type { NextAuthConfig } from \"next-auth\";\nimport \"next-auth/jwt\";\nimport NextAuth from \"next-auth\";\nimport Google"
},
{
"path": "src/components/forms/LoginGoogleBtn.tsx",
"chars": 1949,
"preview": "'use client'\nimport React from 'react'\nimport { Button } from '../ui/button'\nimport { handleGoogleAuth } from './formAct"
},
{
"path": "src/components/forms/SignupForm.tsx",
"chars": 2762,
"preview": "'use client'\nimport React from 'react'\nimport { Input } from \"@/components/ui/input\"\nimport { Label } from \"@/components"
},
{
"path": "src/components/forms/formAction.ts",
"chars": 214,
"preview": "'use server'\nimport { signIn, signOut } from \"../auth\";\n\nexport async function handleGoogleAuth(myCallbackUrl:string){\n "
},
{
"path": "src/components/mockup/ComputerMockup.tsx",
"chars": 1521,
"preview": "import React from 'react'\n\nfunction ComputerMockup({\n children,\n }: {\n children: React.ReactNode;\n }) {\n retur"
},
{
"path": "src/components/mockup/MobileMockup.tsx",
"chars": 748,
"preview": "import MobileScreen from \"./MobileScreen\";\n\nconst MobileMockup = () => {\n return (\n <div className=\"relative mx-auto"
},
{
"path": "src/components/mockup/MobileScreen.tsx",
"chars": 1572,
"preview": "\"use client\"\nimport { cn } from \"@/lib/utils\"\nimport { useData } from \"@/lib/Context\"\nimport React from \"react\"\nimport {"
},
{
"path": "src/components/screen/DisplayScreen.tsx",
"chars": 7162,
"preview": "\"use client\";\nimport { Icon } from \"@iconify/react/dist/iconify.js\";\nimport React from \"react\";\nimport AdditionalLinkCar"
},
{
"path": "src/components/ui/Drrawer.tsx",
"chars": 1052,
"preview": "\"use client\"\n\nimport { forwardRef } from \"react\"\nimport { Drawer as DrawerPrimitive } from \"vaul\"\n\nimport { cn } from \"@"
},
{
"path": "src/components/ui/SocialInput.tsx",
"chars": 1207,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Icon } from '@iconify/react';\n\nexport interfac"
},
{
"path": "src/components/ui/SortableLink.tsx",
"chars": 4183,
"preview": "\"use client\";\nimport React, { FC } from \"react\";\nimport { useSortable } from \"@dnd-kit/sortable\";\nimport { CSS } from \"@"
},
{
"path": "src/components/ui/alert-dialog.tsx",
"chars": 4434,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\"\n\nimpor"
},
{
"path": "src/components/ui/avatar.tsx",
"chars": 1419,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\"\n\nimport { cn } fr"
},
{
"path": "src/components/ui/button.tsx",
"chars": 2244,
"preview": "import { cn } from '@/lib/utils'\nimport { cva, VariantProps } from 'class-variance-authority'\nimport { Loader2 } from 'l"
},
{
"path": "src/components/ui/card.tsx",
"chars": 1877,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Card = React.forwardRef<\n HTMLDivElement,\n Rea"
},
{
"path": "src/components/ui/dialog.tsx",
"chars": 3822,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X } from"
},
{
"path": "src/components/ui/dropdown-menu.tsx",
"chars": 7309,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimpo"
},
{
"path": "src/components/ui/form.tsx",
"chars": 4099,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\nimport { Slot } fro"
},
{
"path": "src/components/ui/input-otp.tsx",
"chars": 2168,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { OTPInput, OTPInputContext } from \"input-otp\"\nimport { Dot } from \""
},
{
"path": "src/components/ui/input.tsx",
"chars": 824,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface InputProps\n extends React.InputHTMLA"
},
{
"path": "src/components/ui/label.tsx",
"chars": 724,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\nimport { cva, type "
},
{
"path": "src/components/ui/select.tsx",
"chars": 4369,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { Check, C"
},
{
"path": "src/components/ui/skeleton.tsx",
"chars": 261,
"preview": "import { cn } from \"@/lib/utils\"\n\nfunction Skeleton({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {"
},
{
"path": "src/components/ui/textarea.tsx",
"chars": 772,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface TextareaProps\n extends React.Textare"
},
{
"path": "src/lib/Context.tsx",
"chars": 4365,
"preview": "import React, { createContext, useContext, useState } from \"react\";\n\ninterface LinkProps {\n n: string; //Name\n i: stri"
},
{
"path": "src/lib/Firebase.ts",
"chars": 541,
"preview": "\nimport { initializeApp } from 'firebase/app';\nimport { getStorage } from 'firebase/storage';\nconst firebaseConfig = {\n "
},
{
"path": "src/lib/RateLimiter.tsx",
"chars": 1020,
"preview": "// Function written by ChatGPT\n\nclass RateLimiter {\n private limit: number; // Maximum allowed requests\n private i"
},
{
"path": "src/lib/supabase/actions.ts",
"chars": 1603,
"preview": "\"use server\";\n\nimport { auth } from \"@/components/auth\";\nimport { supabaseServer } from \"./supabaseServer\";\n\n\n\nexport co"
},
{
"path": "src/lib/supabase/supabaseClient.ts",
"chars": 285,
"preview": "import { createBrowserClient } from '@supabase/ssr'\n\nexport function supabaseClient() {\n // Create a supabase client on"
},
{
"path": "src/lib/supabase/supabaseMiddleware.ts",
"chars": 949,
"preview": "import { createServerClient, type CookieOptions } from '@supabase/ssr'\nimport { NextResponse, type NextRequest } from 'n"
},
{
"path": "src/lib/supabase/supabaseServer.ts",
"chars": 920,
"preview": "import { createServerClient, type CookieOptions } from '@supabase/ssr'\nimport { cookies } from 'next/headers'\n\nexport fu"
},
{
"path": "src/lib/utils.ts",
"chars": 396,
"preview": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n \nexport function cn(...inputs: Cl"
},
{
"path": "src/middleware.ts",
"chars": 642,
"preview": "import { type NextRequest } from 'next/server'\nimport { updateSession } from './lib/supabase/supabaseMiddleware'\n\n\nexpor"
},
{
"path": "src/types/Types.ts",
"chars": 736,
"preview": "\ninterface DataProps {\n n?: string;\n a?: string;\n i?: string;\n bg?: string;\n fb?: string;\n tg?: string"
},
{
"path": "tailwind.config.ts",
"chars": 2124,
"preview": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n darkMode: [\"class\"],\n content: [\n './pages/**/*.{ts"
},
{
"path": "tsconfig.json",
"chars": 599,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"sk"
}
]
About this extraction
This page contains the full source code of the taqui-786/itZmyLink GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 83 files (149.0 KB), approximately 41.1k tokens, and a symbol index with 80 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.