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 (
SupaAuth Verify your email address
{
"Thanks for starting the new account creation process. We want to make sure it's really you. Please enter the following verification code when prompted. If you don't want to create an account, you can ignore this message."
}
Verification code
{verificationCode}
(This code is valid for 1 hour)
);
}
const main = {
backgroundColor: "#fff",
color: "#212121",
};
const container = {
padding: "20px",
margin: "0 auto",
backgroundColor: "#eee",
};
const h1 = {
color: "#333",
fontFamily:
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
fontSize: "20px",
fontWeight: "bold",
marginBottom: "15px",
};
const link = {
color: "#2754C5",
fontFamily:
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
fontSize: "14px",
textDecoration: "underline",
};
const text = {
color: "#333",
fontFamily:
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
fontSize: "14px",
margin: "24px 0",
};
const imageSection = {
backgroundColor: "#252f3d",
display: "flex",
padding: "20px 0",
alignItems: "center",
justifyContent: "center",
};
const coverSection = { backgroundColor: "#fff" };
const upperSection = { padding: "25px 35px" };
const lowerSection = { padding: "25px 35px" };
const footerText = {
...text,
fontSize: "12px",
padding: "0 20px",
};
const verifyText = {
...text,
margin: 0,
fontWeight: "bold",
textAlign: "center" as const,
};
const codeText = {
...text,
fontWeight: "bold",
fontSize: "36px",
margin: "10px 0",
textAlign: "center" as const,
};
const validityText = {
...text,
margin: "0px",
textAlign: "center" as const,
};
const verificationSection = {
display: "flex",
alignItems: "center",
justifyContent: "center",
};
const mainText = { ...text, marginBottom: "14px" };
================================================
FILE: middleware.ts
================================================
import { updateSession } from "@/lib/supabase/supabaseMiddleware";
import { type NextRequest } from "next/server";
export async function middleware(request: NextRequest) {
return await updateSession(request);
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* Feel free to modify this pattern to include more paths.
*/
"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
],
};
================================================
FILE: next.config.mjs
================================================
/** @type {import('next').NextConfig} */
const nextConfig = {
};
export default nextConfig;
================================================
FILE: package.json
================================================
{
"name": "client",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
"@hookform/resolvers": "^3.9.0",
"@iconify/react": "^4.1.1",
"@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.1.0",
"@react-email/components": "0.0.22",
"@supabase/ssr": "^0.4.0",
"@supabase/supabase-js": "^2.45.1",
"@types/node": "20.6.0",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"@vercel/og": "^0.5.17",
"autoprefixer": "10.4.15",
"canvas-confetti": "^1.9.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"encoding": "^0.1.13",
"eslint": "8.49.0",
"eslint-config-next": "13.4.19",
"firebase": "^10.4.0",
"framer-motion": "^11.2.13",
"input-otp": "^1.2.4",
"js-base64": "^3.7.5",
"lucide-react": "^0.276.0",
"next": "^14.1.4",
"next-auth": "^5.0.0-beta.20",
"postcss": "8.4.29",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-email": "2.1.6",
"react-hook-form": "^7.52.2",
"react-icons": "^4.12.0",
"resend": "^3.5.0",
"sonner": "^1.5.0",
"tailwind-merge": "^1.14.0",
"tailwindcss": "3.3.3",
"tailwindcss-animate": "^1.0.7",
"typescript": "5.2.2",
"vaul": "^0.7.1",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/canvas-confetti": "^1.6.4"
}
}
================================================
FILE: postcss.config.js
================================================
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
================================================
FILE: src/app/1/[slug]/page.tsx
================================================
import { decodeData } from "@/lib/utils";
import { BACKGROUND_OPTIONS } from "@/components/Background/BgSnippets";
import DisplayScreen from "@/components/screen/DisplayScreen";
import { supabaseServer } from "@/lib/supabase/supabaseServer";
import NotFound from "@/app/Not-found";
import DataLoading from "../loading";
type Props = {
params: {
slug: string;
};
};
export async function generateMetadata({ params }: Props) {
const path = await supabaseServer()
.from("links")
.select("*")
.eq("path", params.slug);
if (path.data?.length === 0) return NotFound();
const data = decodeData(path?.data?.[0].link);
if (!data) {
return {};
}
return {
title: `${data.n}'s`,
description: `Find all of ${data.n}'s links in one place.`,
openGraph: {
type: "website",
locale: "en_US",
url: "https://itZmyLink.vercel.app",
title: `${data.n}'s - itZmyLink`,
description: `Find all of ${data.n}'s links in one place.`,
images: `https://itZmyLink.vercel.app/api/og?data=${encodeURI(data.n)}`,
siteName: `${data.n}'s - itZmyLink`,
},
twitter: {
card: "summary_large_image",
title: `${data.n} - itZmyLink`,
description: `Find all of ${data.n}'s links in one place.`,
images: `https://itZmyLink.vercel.app/api/og?data=${encodeURI(data.n)}`,
creator: "@Taquiimam14",
},
};
}
const linkLandingPage: React.FC = async ({ params }) => {
const path = await supabaseServer()
.from("links")
.select("*")
.eq("path", params.slug);
if (path.data?.length === 0) return NotFound();
const data = decodeData(path?.data?.[0].link);
const selectedBgOption = data
? BACKGROUND_OPTIONS.find((option) => option.code === data.bg)
: null;
const selectedBgComponent = selectedBgOption
? selectedBgOption.component
: null;
return (
<>
{selectedBgComponent}
{data ? : }
>
);
};
export default linkLandingPage;
================================================
FILE: src/app/1/error.tsx
================================================
'use client'
import { Button } from "@/components/ui/button"
import Link from "next/link"
export default function Error() {
return (
Sorry, there is mistake in url.
Create new page
)
}
================================================
FILE: src/app/1/loading.tsx
================================================
import { Skeleton } from '@/components/ui/skeleton'
import React from 'react'
export default function DataLoading() {
return (
)
}
================================================
FILE: src/app/1/page.tsx
================================================
import { decodeData } from "@/lib/utils";
import NotFound from "../Not-found";
import { BACKGROUND_OPTIONS } from "@/components/Background/BgSnippets";
import DataLoading from "./loading";
import DisplayScreen from "@/components/screen/DisplayScreen";
export async function generateMetadata({ searchParams }: any) {
const data = decodeData(searchParams.data);
if (!data) {
return {};
}
return {
title: `${data.n}'s`,
description: `Find all of ${data.n}'s links in one place.`,
openGraph: {
type: "website",
locale: "en_US",
url: "https://itZmyLink.vercel.app",
title: `${data.n}'s - itZmyLink`,
description: `Find all of ${data.n}'s links in one place.`,
images: `https://itZmyLink.vercel.app/api/og?data=${encodeURI(data.n)}`,
siteName: `${data.n}'s - itZmyLink`,
},
twitter: {
card: "summary_large_image",
title: `${data.n} - itZmyLink`,
description: `Find all of ${data.n}'s links in one place.`,
images: `https://itZmyLink.vercel.app/api/og?data=${encodeURI(data.n)}`,
creator: "@Taquiimam14",
},
};
}
const linkLandingPage = ({ searchParams }: any) => {
if (!searchParams.data) NotFound();
const data = decodeData(searchParams.data);
const selectedBgOption = data
? BACKGROUND_OPTIONS.find((option) => option.code === data.bg)
: null;
const selectedBgComponent = selectedBgOption
? selectedBgOption.component
: null;
return (
<>
{selectedBgComponent}
{data ? : }
>
);
};
export default linkLandingPage;
================================================
FILE: src/app/Not-found.tsx
================================================
import { Button } from '@/components/ui/button'
import Link from 'next/link'
import React from 'react'
export default function NotFound() {
return (
Opps! Page not found.
Organize your links with itZmyLink and make them easy to find and share.
Create your itZmyLink
)
}
================================================
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(
(
),
{
width: 1200,
height: 630,
}
)
} catch (error) {
console.error(error);
return new Response(`Failed to generate the image`, {
status: 500,
})
}
}
================================================
FILE: src/app/auth/signin/page.tsx
================================================
import React from 'react'
import Link from "next/link"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import LoginGoogleBtn from '@/components/forms/LoginGoogleBtn';
type Props = {
searchParams:{
callbackUrl?:string
}
};
const page: React.FC = async({searchParams}) => {
return (
Welcome Back
Select your registered email to continue.
Don't have an account?{" "}
Sign up
)
}
export default page
================================================
FILE: src/app/create/loading.tsx
================================================
import { Skeleton } from '@/components/ui/skeleton'
import React from 'react'
export default function SiteLoading() {
return (
)
}
================================================
FILE: src/app/create/page.tsx
================================================
import PreviewButton from "@/components/ActionButtons/ResponsivePreviewBtn";
import HomeEditor from "@/components/HomeEditor";
const CreateLink = async() => {
return (
);
};
export default CreateLink;
================================================
FILE: src/app/globals.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 20 14.3% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 20 14.3% 4.1%;
--primary: 47.9 95.8% 53.1%;
--primary-foreground: 26 83.3% 14.1%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--ring: 20 14.3% 4.1%;
--radius: 0.5rem;
}
.dark {
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--primary: 47.9 95.8% 53.1%;
--primary-foreground: 26 83.3% 14.1%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--ring: 35.5 91.7% 32.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
html, body, :root{
height: 100%;
}
.hide_scrollbar::-webkit-scrollbar {
display: none;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
body::-webkit-scrollbar {
display: none;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
/* MOBILE TEMPLATE DESIGN */
.container{
max-width: 600px;
margin: 4px auto;
}
.phone{
position: relative;
background: #1e1e1e;
height: 625px;
width: 345px;
border-radius: 25px;
margin: 0 auto;
}
.camera{
position: absolute;
background: #7A7A7A;
height: 15px;
width: 15px;
border-radius: 15px;
top: 7px;
left: 166px;
z-index: +9;
}
.speaker{
position: absolute;
background: #000;
height: 23px;
width: 45px;
border-radius: 0 0 27px 27px;
top: 7px;
left: 151px;
z-index: +8;
}
.sleep-button{
position: absolute;
background: #1e1e1e;
height: 35px;
width: 3px;
border-top-right-radius: 3px 3px;
border-bottom-right-radius: 3px 3px;
top: 111px;
left: 345px;
}
.silent-switch{
position: absolute;
background: #1e1e1e;
height: 25px;
width: 3px;
border-top-left-radius: 3px 3px;
border-bottom-left-radius: 3px 3px;
top: 60px;
left: -3px;
}
.volume{
position: absolute;
background: #1e1e1e;
width: 3px;
height: 35px;
border-top-left-radius: 3px 3px;
border-bottom-left-radius: 3px 3px;
left: -3px;
}
.up{
top: 105px;
}
.down{
top: 145px;
}
.screen{
position: absolute;
overflow: hidden;
height: 593px;
width: 325px;
top: 10px;
left: 10px;
border-radius: 15px;
}
.home-button{
position: absolute;
border: 1px solid #7A7A7A;
height: 35px;
width: 35px;
border-radius: 25px;
bottom: 12px;
left: 50%;
margin-left: -18px;
display: none;
}
================================================
FILE: src/app/layout.tsx
================================================
import "./globals.css";
import type { Metadata } from "next";
import { siteConfig } from "./page";
import { Inter } from "next/font/google";
import { Providers } from "@/components/Provider";
const inter = Inter({ subsets: ["latin"] });
// Original source: https://github.com/sadmann7/skateshop/blob/main/src/app/layout.tsx
export const metadata: Metadata = {
metadataBase: new URL("https://itzmylink.vercel.app"),
title: {
default: siteConfig.name,
template: `%s - itZmyLink`,
},
description: siteConfig.description,
// added new keywords for seo
keywords: [
"bitly url shortener",
"bitly link shortener",
"link shortener",
"url shortener",
"bitly link",
"tinyurls",
"all in one link",
"free url shortener",
"linknode",
"onelink",
"social links",
"free linktree",
"link in bio",
"short my url",
"my links",
"itzmylink",
"itZmyLink",
"mtLink",
],
authors: [
{
name: "Taqui Imam",
url: "https://github.com/taqui-786",
},
],
creator: "Taqui imam",
openGraph: {
type: "website",
locale: "en_US",
url: siteConfig.url,
title: siteConfig.name,
description: siteConfig.description,
images: [`${siteConfig.url}/og-image.png`],
siteName: siteConfig.name,
},
twitter: {
card: "summary_large_image",
title: siteConfig.name,
description: siteConfig.description,
images: [`${siteConfig.url}/og-image.png`],
creator: "@Taquiimam14",
},
icons: {
icon: "/favicon.ico",
},
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{/* */}
{children}
{/* */}
);
}
================================================
FILE: src/app/links/page.tsx
================================================
import { Card, CardContent } from "@/components/ui/card"
import { Button, buttonVariants } from "@/components/ui/button"
import { Construction } from "lucide-react"
import Link from "next/link"
import { cn } from "@/lib/utils"
export default function Component() {
return (
Under Construction
This page will be available soon.
Go Home
)
}
================================================
FILE: src/app/page.tsx
================================================
import Link from "next/link";
import landingPageImg from '../../public/landingPage.png'
import Image from "next/image";
import { auth } from "@/components/auth";
import { redirect } from "next/navigation";
export const siteConfig = {
name: "itZmyLink - one page, many links.",
description: "itZmyLink help you to Create a personalized page to showcase all your social media profiles in one place.",
ogImage: "https://itzmylink.vercel.app/og-image.png",
url: "https://itzmylink.vercel.app",
}
export default async function Home() {
const session = await auth()
if(session?.user) return redirect('/create')
return (
Simplify your online presence!
Take control of your links with itzmylink
Your all-in-one link management solution
Get your Link
Already joined us? Log in
);
}
================================================
FILE: src/app/preview/layout.tsx
================================================
export default function PreviewLayout({
children,
}: {
children: React.ReactNode
}) {
return
{children}
}
================================================
FILE: src/app/preview/page.tsx
================================================
import React from "react";
import NotFound from "../Not-found";
import { decodeData } from "@/lib/utils";
import ComputerMockup from "@/components/mockup/ComputerMockup";
import { BACKGROUND_OPTIONS } from "@/components/Background/BgSnippets";
import PreviewPage from "@/components/PreviewPage";
import PreviewFooter from "@/components/ActionButtons/PreviewFooter";
function page({ searchParams }: any) {
if (!searchParams.data) NotFound();
const data = decodeData(searchParams.data);
const selectedBgOption = data
? BACKGROUND_OPTIONS.find((option) => option.code === data.bg)
: null;
const selectedBgComponent = selectedBgOption
? selectedBgOption.component
: null;
return (
<>
{selectedBgComponent}
>
);
}
export default page;
================================================
FILE: src/components/ActionButtons/CustomLinkDialog.tsx
================================================
"use client";
import { useState, useCallback } from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ClipboardCopy, Check, PenSquare } from "lucide-react";
import { createCustomPath } from "@/lib/supabase/actions";
// Mock function to simulate checking if a path exists in the database
const checkPathExists = async (path: string): Promise => {
await new Promise((resolve) => setTimeout(resolve, 1000));
return path.toLowerCase().includes("taken");
};
export default function Component({localLink}:{localLink:string}) {
const [customPath, setCustomPath] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(null);
const [isCopied, setIsCopied] = useState(false);
const baseUrl = "itzmylink.vercel.app/1";
const fullUrl = `${baseUrl}/${customPath}`;
const copyToClipboard = useCallback(() => {
navigator.clipboard
.writeText(fullUrl)
.then(() => {
setIsCopied(true);
// toast({
// title: "Copied!",
// description: "Link copied to clipboard",
// })
setTimeout(() => setIsCopied(false), 2000);
})
.catch((err) => {
console.error("Failed to copy text: ", err);
// toast({
// title: "Error",
// description: "Failed to copy link",
// variant: "destructive",
// })
});
}, [fullUrl]);
const handleCreate = useCallback(async () => {
if (!customPath) {
setError("Please enter a custom path");
return;
}
setIsLoading(true);
setError(null);
setSuccess(null);
try {
const exists = await checkPathExists(customPath);
if (exists) {
setError("This custom path is already taken. Please try another.");
} else {
const res = await createCustomPath(customPath,localLink);
if (res.status !== "notAuthenticated") {
res.status === 'created'?
setSuccess(res.message):
setError(res.message)
}
}
} catch (err) {
console.error("Error creating custom path:", err);
setError("An error occurred while creating the custom path");
} finally {
setIsLoading(false);
}
}, [customPath]);
return (
<>
Create Custom URL
Create Custom Link
Enter your custom path to create a unique link.
{error ? "Already Exists 😅" : "Congratulation🎉"}
{error || success}
{success && (
)}
{
setError(null);
setSuccess(null);
}}
>
Close
{success && (
Copy URL
)}
>
);
}
================================================
FILE: src/components/ActionButtons/DemoBtn.tsx
================================================
"use client"
import React, { FC } from 'react'
import { Button } from '@/components/ui/button'
import { Play } from 'lucide-react'
import { useData } from '@/lib/Context'
interface DemoDataProps { }
const DemoBtn: FC = ({ }) => {
const { showDemo } = useData()
return (
Demo
)
}
export default DemoBtn
================================================
FILE: src/components/ActionButtons/PreviewFooter.tsx
================================================
"use client";
import React from "react";
import { Button } from "../ui/button";
import { Check, Clipboard, Share } from "lucide-react";
import CustomLinkDialog from "./CustomLinkDialog";
function PreviewFooter({
MyLink,
inputLink,
}: {
MyLink: any;
inputLink: string;
}) {
const [hasCopied, setHasCopied] = React.useState(false);
const copyToClipboard = React.useCallback(() => {
const url = `${window.location.origin}/1?data=${inputLink}`;
navigator.clipboard.writeText(url);
return url;
}, [MyLink]);
return (
{
navigator.share({
title: `${MyLink.n} - itZmyLink`,
text: `Find all of ${MyLink.n}'s links in one place.`,
url: `${inputLink}`,
});
}}
>
Share
{
copyToClipboard();
setHasCopied(true);
}}
>
{hasCopied ? (
<>
Copied
>
) : (
<>
Local URL
>
)}
);
}
export default PreviewFooter;
================================================
FILE: src/components/ActionButtons/PublishBtn.tsx
================================================
"use client"
import React, { FC } from 'react'
import { Button, buttonVariants } from '@/components/ui/button'
import { cn, encodeData } from '@/lib/utils';
import { Check, Copy, Send, Share2 } from 'lucide-react';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { DialogClose } from '@radix-ui/react-dialog';
import { useData } from '@/lib/Context';
import Link from 'next/link';
interface PublishProps {
loggedIn:any
}
const Publish: FC = ({ loggedIn}) => {
const { MyLink } = useData()
const isEmpty = isEmptyValues(MyLink)
const [inputLink, setInputLink] = React.useState("")
const [hasCopied, setHasCopied] = React.useState(false)
function isEmptyValues(obj: any) {
for (let key in obj) {
if (obj[key] !== "" && obj[key].length !== 0) {
return false;
}
}
return true;
}
const copyToClipboard = React.useCallback(() => {
const url = `${window.location.origin}/1?data=${encodeData(MyLink)}`;
navigator.clipboard.writeText(url)
return url
}, [MyLink]);
React.useEffect(() => {
setHasCopied(false);
}, [MyLink]);
function publish() {
if (!isEmpty) {
const getUrl = copyToClipboard()
setInputLink(getUrl)
}
}
return (
Publish
Share your page
You can share your page with others and make it accessible from anywhere.
{!isEmpty ? (
loggedIn ?
Confirm to publish:
Login To Continue
// <>
//
//
//
// {
// navigator.share({
// title: `${MyLink.n} - itZmyLink`,
// text: `Find all of ${MyLink.n}'s links in one place.`,
// url: `${inputLink}`,
// })
// }}
// >
//
// Share
//
// {
// copyToClipboard()
// setHasCopied(true);
// }}
// >
// {
// hasCopied
// ? (
// <>
//
// Copied
// >
// )
// : (
// <>
//
// Copy Link
// >
// )
// }
//
//
//
// >
) : (
Can't publish with empty fields!
)}
)
}
export default Publish
================================================
FILE: src/components/ActionButtons/ResponsivePreviewBtn.tsx
================================================
"use client"
import React, { FC } from 'react'
import { Button } from "@/components/ui/button"
import { Drawer } from "vaul"
import { useData } from '@/lib/Context'
import { BACKGROUND_OPTIONS } from '../Background/BgSnippets'
import { DrawerContent, DrawerTrigger } from '../ui/Drrawer'
import DisplayData from '../DisplayData'
interface PreviewButtonProps { }
const PreviewButton: FC = () => {
const { MyLink } = useData();
const [isEmpty, setIsEmpty] = React.useState(false)
React.useEffect(() => {
function isEmptyValues(obj: any) {
for (let key in obj) {
if (obj[key] !== "" && obj[key].length !== 0) {
return false;
}
}
return true;
}
setIsEmpty(isEmptyValues(MyLink))
}, [MyLink])
const selectedBgOption = MyLink
? BACKGROUND_OPTIONS.find((option) => option.code === MyLink.bg)
: null;
const selectedBgComponent = selectedBgOption ? selectedBgOption.component : null;
return (
Preview page
{
isEmpty
? Nothing to show...
: (
<>
{!isEmpty && selectedBgComponent}
>
)
}
)
}
export default PreviewButton
================================================
FILE: src/components/AdditionalLinkCards.tsx
================================================
import React, { FC } from 'react'
import { Icon } from '@iconify/react';
interface AdditionalLinkCardProps {
label: string;
url: string;
icon?: string;
}
const AdditionalLinkCard: FC = ({ label, url, icon }) => {
return (
{label && url && (
{icon ? (
) : (
)}
)}
)
}
export default AdditionalLinkCard
================================================
FILE: src/components/AdditionalLinkForm.tsx
================================================
"use client"
import React, { FC } from 'react'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import {
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import {
restrictToVerticalAxis,
restrictToParentElement,
} from '@dnd-kit/modifiers';
import {SortableLinks} from '@/components/ui/SortableLink'
import { useData } from '@/lib/Context';
const AdditionalLinkForm = () =>{
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
}),
);
const scrollDownRef = React.useRef(null)
const [shouldScroll, setShouldScroll] = React.useState(false);
const { MyLink, addNewData, updateIndex } = useData();
const addLinkDetailForm = () => {
const newLink: AdditionalLinkProps = { id: Date.now(), i: '', l: '', u: '' };
addNewData(newLink);
setShouldScroll(true);
};
React.useEffect(() => {
if (shouldScroll && scrollDownRef.current) {
scrollDownRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
setShouldScroll(false);
}
}, [shouldScroll]);
// "handleDragEnd" function written by chatGPT
function handleDragEnd(event: any) {
const { active, over } = event;
if (active.id !== over.id) {
const updatedItems = [...MyLink.ls]; // Accessing items from the context
const draggedItem: any = updatedItems.find((item) => item.id === active.id);
const targetItem: any = updatedItems.find((item) => item.id === over.id);
const draggedIndex = updatedItems.indexOf(draggedItem);
const targetIndex = updatedItems.indexOf(targetItem);
if (draggedIndex !== -1 && targetIndex !== -1) {
// Remove the dragged item from its original position
updatedItems.splice(draggedIndex, 1);
// Insert the dragged item at the target position
updatedItems.splice(targetIndex, 0, draggedItem);
updateIndex(updatedItems);
}
}
}
return(
<>
Extra Links
{/* */}
Enter your additional link details here.
link.id)}
strategy={verticalListSortingStrategy}
>
{MyLink.ls.map((link, index) => {
return
})}
+
>
)
}
export default AdditionalLinkForm;
================================================
FILE: src/components/Animation/BlurText.tsx
================================================
"use client";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
interface BlurIntProps {
word: string;
className?: string;
variant?: {
hidden: { filter: string; opacity: number };
visible: { filter: string; opacity: number };
};
duration?: number;
}
const BlurIn = ({ word, className, variant, duration = 1 }: BlurIntProps) => {
const defaultVariants = {
hidden: { filter: "blur(10px)", opacity: 0 },
visible: { filter: "blur(0px)", opacity: 1 },
};
const combinedVariants = variant || defaultVariants;
return (
{word}
);
};
export default BlurIn;
================================================
FILE: src/components/Animation/FlipText.tsx
================================================
"use client";
import { AnimatePresence, motion, Variants } from "framer-motion";
import { cn } from "@/lib/utils";
interface SlightFlipProps {
word: string;
duration?: number;
delayMultiple?: number;
framerProps?: Variants;
className?: string;
}
export default function SlightFlip({
word,
duration = 0.5,
delayMultiple = 0.08,
framerProps = {
hidden: { rotateX: -90, opacity: 0 },
visible: { rotateX: 0, opacity: 1 },
},
className,
}: SlightFlipProps) {
return (
{word.split("").map((char, i) => (
{char}
))}
);
}
================================================
FILE: src/components/Animation/FramerWrapper.tsx
================================================
"use client";
import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
type FramerMotionProps = {
children: React.ReactNode,
className?:any,
y?:number
x?:number
delay?:number
duration?: number
scale?:number
}
function FramerWrapper({children,delay = 0.25 ,y = 15, x = 0,duration = 0.20,scale = 0, className}:FramerMotionProps) {
const [animateConfig, setAnimateConfig] = useState({
opacity:1, y:0, x:0
})
return (
{children}
);
}
export default FramerWrapper;
================================================
FILE: src/components/Animation/TextEffect.tsx
================================================
'use client';
import { motion, Variants } from 'framer-motion';
import React from 'react';
type PresetType = 'blur' | 'shake' | 'scale' | 'fade' | 'slide';
type TextEffectProps = {
children: string;
per?: 'word' | 'char';
as?: keyof JSX.IntrinsicElements;
variants?: {
container?: Variants;
item?: Variants;
};
className?: string;
preset?: PresetType;
};
const defaultContainerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.05,
},
},
};
const defaultItemVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
},
};
const presetVariants: Record<
PresetType,
{ container: Variants; item: Variants }
> = {
blur: {
container: defaultContainerVariants,
item: {
hidden: { opacity: 0, filter: 'blur(12px)' },
visible: { opacity: 1, filter: 'blur(0px)' },
},
},
shake: {
container: defaultContainerVariants,
item: {
hidden: { x: 0 },
visible: { x: [-5, 5, -5, 5, 0], transition: { duration: 0.5 } },
},
},
scale: {
container: defaultContainerVariants,
item: {
hidden: { opacity: 0, scale: 0 },
visible: { opacity: 1, scale: 1 },
},
},
fade: {
container: defaultContainerVariants,
item: {
hidden: { opacity: 0 },
visible: { opacity: 1 },
},
},
slide: {
container: defaultContainerVariants,
item: {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
},
},
};
const AnimationComponent: React.FC<{
word: string;
variants: Variants;
per: 'word' | 'char';
}> = React.memo(({ word, variants, per }) => {
if (per === 'word') {
return (
{word}
);
}
return (
{word.split('').map((char, charIndex) => (
{char}
))}
);
});
AnimationComponent.displayName = 'AnimationComponent';
export function TextEffect({
children,
per = 'word',
as = 'p',
variants,
className,
preset,
}: TextEffectProps) {
const words = children.split(/(\S+)/);
const MotionTag = motion[as as keyof typeof motion];
const selectedVariants = preset
? presetVariants[preset]
: { container: defaultContainerVariants, item: defaultItemVariants };
const containerVariants = variants?.container || selectedVariants.container;
const itemVariants = variants?.item || selectedVariants.item;
return (
{words.map((word, wordIndex) => (
))}
);
}
================================================
FILE: src/components/Background/BackgroundCards.tsx
================================================
import React from 'react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { BACKGROUND_OPTIONS } from './BgSnippets';
import { useData } from '@/lib/Context';
type BackgroundCardProps = {};
const BackgroundCard: React.FC = () => {
const { MyLink,selectBackground} = useData()
return (
{BACKGROUND_OPTIONS.map((background, index) => {
return (
{
selectBackground(background.code)
}}
>
{background.name}
);
})}
);
};
export default BackgroundCard
================================================
FILE: src/components/Background/BackgroundForm.tsx
================================================
"use client"
import React from 'react'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '@/components/ui/card'
import BackgroundCard from './BackgroundCards'
export default function Background() {
return (
Background
Customize your background theme from here.
)
}
================================================
FILE: src/components/Background/BgSnippets.tsx
================================================
// original source: https://github.com/ibelick/background-snippets/blob/main/app/components/background.tsx
const BgTheme1 = () => {
return (
);
};
const BgTheme8 = () => {
return (
);
};
const BgTheme2 = () => {
return (
);
};
const BgTheme9 = () => {
return (
);
};
const BgTheme7 = () => {
return (
);
};
const BgTheme10 = () => {
return (
);
};
const BgTheme11 = () => {
return (
);
};
const BgTheme12 = () => {
return (
);
};
const BgTheme4 = () => {
return (
);
};
const BgTheme5 = () => {
return (
);
};
const BgTheme6 = () => {
return (
);
};
const BgTheme3 = () => {
return (
);
};
export const BACKGROUND_OPTIONS = [
{
code: '#FFFFFF',
component: ,
name: 'WhiteCanvas'
},
{
code: '#4F4F4F',
component: ,
name: 'ShadowyGray'
},
{
code: '#C9EBFF',
component: ,
name: 'LinearSky',
},
{
code: '#E6E7EB',
component: ,
name: 'SubtleGrayDots',
},
{
code: '#FF00FF',
component: ,
name: 'BlurredFuchsia',
},
{
code: '#E5E7EB',
component: ,
name: 'MaskedGray',
},
{
code: '#808080',
component: ,
name: 'GradientGrid',
},
{
code: '#F0F0F0',
component: ,
name: 'LightGrayLines',
},
{
code: '#00A3FF',
component: ,
name: 'RadiantBlue',
},
{
code: '#AD6DF4',
component: ,
name: 'GradientOrb',
},
{
code: '#63E',
component: ,
name: 'RadialHalo',
},
{
code: '#D5C5FF',
component: ,
name: 'VividCircles',
}
] as const;
================================================
FILE: src/components/DisplayData.tsx
================================================
"use client";
import { ImageIcon } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
import { Icon } from "@iconify/react/dist/iconify.js";
import AdditionalLinkCard from "./AdditionalLinkCards";
const DisplayData:React.FC = ({ myData }) => {
const EmptySocialLiks =
!myData.fb &&
!myData.ig &&
!myData.tg &&
!myData.em &&
!myData.tw &&
!myData.lk &&
!myData.yt &&
!myData.gt &&
!myData.wh;
const iconMap: Record = {
fb: "ph:facebook-logo-duotone",
tw: "ph:twitter-logo-duotone",
ig: "ph:instagram-logo-duotone",
tg: "ph:telegram-logo-duotone",
wh: "ph:whatsapp-logo-duotone",
yt: "ph:youtube-logo-duotone",
em: "ph:envelope-duotone",
gt: "ph:github-logo-duotone",
lk: "ph:linkedin-logo-duotone",
};
return (
<>
{/* USER IMAGE */}
{/* USER NAME AND BIO */}
{myData.n}
{myData.a}
{/* {!EmptySocialLiks && ( */}
{ Object.entries(myData).map(([key, value]) => {
const excludedKeys = ["i", "n", "a", "bg"];
if (key !== "ls" && value && !excludedKeys.includes(key)) {
const propIcon = iconMap[key as keyof typeof iconMap];
if (key === "em") {
// Handle email link generation
return (
);
} else if (key === "wh") {
// Handle WhatsApp link generation
const whatsappValue = value.startsWith("https://wa.me/")
? value // If it already starts with the correct prefix
: `https://wa.me/${value}`;
return (
);
} else {
return (
);
}
}
return null;
})}
{/* )} */}
{myData.ls && myData.ls.map((link, id) => (
))}
>
);
};
export default DisplayData;
================================================
FILE: src/components/HomeEditor.tsx
================================================
import PersonalInfo from "./PersonalInfo";
import SocialLinksForm from "./SocialLinkForm";
import Background from "./Background/BackgroundForm";
import AdditionalLinkForm from "./AdditionalLinkForm";
import Publish from "./ActionButtons/PublishBtn";
import DemoBtn from "./ActionButtons/DemoBtn";
import Link from "next/link";
import { ShoppingCart ,Link2, Github, Coffee} from "lucide-react";
import { buttonVariants } from "./ui/button";
import Navbar from "./Navbar";
import { auth } from "./auth";
import MobileMockup from "./mockup/MobileMockup";
const HomeEditor = async() => {
const session = await auth()
return (
<>
{/* MOBILE MOCKUP */}
>
);
};
export default HomeEditor;
================================================
FILE: src/components/Navbar.tsx
================================================
import Link from "next/link";
import React from "react";
import { auth, signOut } from "./auth";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
import { buttonVariants } from "./ui/button";
import { supabaseServer } from "@/lib/supabase/supabaseServer";
async function Navbar() {
const session = await auth();
let linkCount = 0
const supabase = await supabaseServer().from('links').select('*').eq('email',session?.user?.email)
if(supabase.data?.length){
linkCount = supabase.data.length
}
return (
itZmyLink
{/*
(555) 412-1234 */}
{session?.user ? (
My Links
{linkCount}
Ti
{session?.user.name}
{session?.user.email}
Setting
Logout
) : (
Login
)}
);
}
export default Navbar;
================================================
FILE: src/components/PersonalInfo.tsx
================================================
"use client";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Label } from "./ui/label";
import { Input } from "./ui/input";
import { Textarea } from "./ui/textarea";
import { useData } from "@/lib/Context";
import PhotoUpload from "./PhotoUpload";
type InputChangeEvent = React.ChangeEvent<
HTMLInputElement | HTMLTextAreaElement
>;
const PersonalInfo = () => {
const { MyLink, updateProfileInfo } = useData();
const handleInfoChange = (event: InputChangeEvent) => {
const { name, value } = event.target;
updateProfileInfo(name, value);
};
return (
<>
Profile Information
Enter your profile or title information here.
About yourself
>
);
};
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(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) => {
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(<>
Profile Url
{loading ? :
}
>)
}
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 ? : }
>
)
}
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 (
{children}
{/* */}
);
}
================================================
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;
interface SocialLinksFormProps { }
const SocialLinksForm: FC = () => {
const { MyLink, updateProfileInfo } = useData();
const handleInfoChange = (event: InputChangeEvent) => {
const { name, value } = event.target;
updateProfileInfo(name, value);
};
return (
Social Links
Enter your social media links here.
{socialLinksProvider.map((link) => {
return (
);
})}
)
}
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 (
//
)
}
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 (
)
}
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 (
https.itzmylink.vercel.app/...
{children}
)
}
export default ComputerMockup
================================================
FILE: src/components/mockup/MobileMockup.tsx
================================================
import MobileScreen from "./MobileScreen";
const MobileMockup = () => {
return (
);
};
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(false)
React.useEffect(() => {
function isEmptyValues(obj: any) {
for (let key in obj) {
if (obj[key] !== "" && obj[key].length !== 0) {
return false;
}
}
return true;
}
setIsEmpty(isEmptyValues(MyLink))
}, [MyLink])
const selectedBgOption = MyLink
? BACKGROUND_OPTIONS.find((option) => option.code === MyLink.bg)
: null;
const selectedBgComponent = selectedBgOption ? selectedBgOption.component : null;
return(
{
isEmpty
?
Nothing to show...
: (
<>
{!isEmpty && selectedBgComponent}
>
)
}
)
}
export default 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 = ({ myData }) => {
const EmptySocialLiks =
!myData.fb &&
!myData.ig &&
!myData.tg &&
!myData.em &&
!myData.tw &&
!myData.lk &&
!myData.yt &&
!myData.gt &&
!myData.wh;
const iconMap: Record = {
fb: "ph:facebook-logo-duotone",
tw: "ph:twitter-logo-duotone",
ig: "ph:instagram-logo-duotone",
tg: "ph:telegram-logo-duotone",
wh: "ph:whatsapp-logo-duotone",
yt: "ph:youtube-logo-duotone",
em: "ph:envelope-duotone",
gt: "ph:github-logo-duotone",
lk: "ph:linkedin-logo-duotone",
};
return (
{/* Avatar------- */}
{/* Name & About------- */}
{myData.n as string}
{/* Social Icons------- */}
{!EmptySocialLiks && (
{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 (
{/* */}
);
} else if (key === "wh") {
// Handle WhatsApp link generation
const whatsappValue = value.startsWith("https://wa.me/")
? value // If it already starts with the correct prefix
: `https://wa.me/${value}`;
return (
);
} else {
return (
);
}
}
return null;
})}
)}
{/* Links------- */}
{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 (
);
})}
);
};
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,
React.ComponentPropsWithoutRef
>(({ className, children, ...props }, ref) => (
{children}
))
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 {
icon: string
}
const SocialInput = React.forwardRef(
({ className, icon: propIcon, ...props }, ref) => {
return (
)
}
)
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 = ({ 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 (
);
};
================================================
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,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
const AlertDialogContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
const AlertDialogHeader = ({
className,
...props
}: React.HTMLAttributes) => (
)
AlertDialogHeader.displayName = "AlertDialogHeader"
const AlertDialogFooter = ({
className,
...props
}: React.HTMLAttributes) => (
)
AlertDialogFooter.displayName = "AlertDialogFooter"
const AlertDialogTitle = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
const AlertDialogDescription = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName
const AlertDialogAction = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
const AlertDialogCancel = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
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,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
Avatar.displayName = AvatarPrimitive.Root.displayName
const AvatarImage = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
AvatarImage.displayName = AvatarPrimitive.Image.displayName
const AvatarFallback = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
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,
VariantProps {
isLoading?: boolean
}
const Button = React.forwardRef(
({ className, children, variant, isLoading, size, ...props }, ref) => {
return (
{isLoading ? : null}
{children}
)
}
)
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
>(({ className, ...props }, ref) => (
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => (
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => (
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => (
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => (
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => (
))
CardFooter.displayName = "CardFooter"
export { Card, 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,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, children, ...props }, ref) => (
{children}
Close
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes) => (
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes) => (
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
const DialogDescription = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
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,
React.ComponentPropsWithoutRef & {
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
{children}
))
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName
const DropdownMenuContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, sideOffset = 4, ...props }, ref) => (
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, children, checked, ...props }, ref) => (
{children}
))
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, children, ...props }, ref) => (
{children}
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
const DropdownMenuLabel = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes) => {
return (
)
}
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 = FieldPath
> = {
name: TName
}
const FormFieldContext = React.createContext(
{} as FormFieldContextValue
)
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath = FieldPath
>({
...props
}: ControllerProps) => {
return (
)
}
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 ")
}
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(
{} as FormItemContextValue
)
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => {
const id = React.useId()
return (
)
})
FormItem.displayName = "FormItem"
const FormLabel = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField()
return (
)
})
FormLabel.displayName = "FormLabel"
const FormControl = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
return (
)
})
FormControl.displayName = "FormControl"
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField()
return (
)
})
FormDescription.displayName = "FormDescription"
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : children
if (!body) {
return null
}
return (
{body}
)
})
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,
React.ComponentPropsWithoutRef
>(({ className, containerClassName, ...props }, ref) => (
))
InputOTP.displayName = "InputOTP"
const InputOTPGroup = React.forwardRef<
React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div">
>(({ className, ...props }, ref) => (
))
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 (
{char}
{hasFakeCaret && (
)}
)
})
InputOTPSlot.displayName = "InputOTPSlot"
const InputOTPSeparator = React.forwardRef<
React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div">
>(({ ...props }, ref) => (
))
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 {}
const Input = React.forwardRef(
({ className, type, ...props }, ref) => {
return (
)
}
)
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,
React.ComponentPropsWithoutRef &
VariantProps
>(({ className, ...props }, ref) => (
))
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,
React.ComponentPropsWithoutRef
>(({ className, children, ...props }, ref) => (
{children}
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, children, position = "popper", ...props }, ref) => (
{children}
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectLabel = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
SelectLabel.displayName = SelectPrimitive.Label.displayName
const SelectItem = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, children, ...props }, ref) => (
{children}
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
))
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) {
return (
)
}
export { Skeleton }
================================================
FILE: src/components/ui/textarea.tsx
================================================
import * as React from "react"
import { cn } from "@/lib/utils"
export interface TextareaProps
extends React.TextareaHTMLAttributes {}
const Textarea = React.forwardRef(
({ className, ...props }, ref) => {
return (
)
}
)
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(undefined);
export const DataProvider = ({ children }: { children: React.ReactNode }) => {
const [data, setData] = useState("");
const [MyLink, setMyLink] = useState(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 (
{children}
);
};
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; // In-memory storage for request timestamps
constructor(limit: number, interval: number) {
this.limit = limit;
this.interval = interval;
this.requests = new Map();
}
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 => {
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"]
}