├── .gitattributes ├── public ├── logo.png └── placeholder-user.jpg ├── postcss.config.js ├── app ├── (clerk) │ ├── sign-in │ │ └── [[...sign-in]] │ │ │ └── page.tsx │ ├── sign-up │ │ └── [[...sign-up]] │ │ │ └── page.tsx │ └── layout.tsx ├── (admin) │ └── admin-dashboard │ │ ├── manage-admin │ │ ├── add-admin │ │ │ └── page.tsx │ │ ├── page.tsx │ │ ├── edit-admin │ │ │ └── [id] │ │ │ │ └── page.tsx │ │ ├── loading.tsx │ │ └── _components │ │ │ └── DeleteUserButton.tsx │ │ ├── manage-projects │ │ ├── add-project │ │ │ └── page.tsx │ │ ├── page.tsx │ │ ├── edit-project │ │ │ └── [id] │ │ │ │ └── page.tsx │ │ └── _components │ │ │ ├── projects-table.tsx │ │ │ └── delete-project.tsx │ │ ├── page.tsx │ │ ├── manage-techstack │ │ ├── add-techstack │ │ │ └── page.tsx │ │ ├── page.tsx │ │ ├── edit-techstack │ │ │ └── [id] │ │ │ │ └── page.tsx │ │ ├── _components │ │ │ ├── techstack-table.tsx │ │ │ └── delete-techstack.tsx │ │ └── _forms │ │ │ ├── AddTechstackForm.tsx │ │ │ └── EditTechstackForm.tsx │ │ ├── manage-experience │ │ ├── add-experience │ │ │ └── page.tsx │ │ ├── page.tsx │ │ ├── edit-experience │ │ │ └── [id] │ │ │ │ └── page.tsx │ │ ├── _components │ │ │ ├── experience-table.tsx │ │ │ └── delete-experience.tsx │ │ └── _forms │ │ │ ├── AddExperienceForm.tsx │ │ │ └── EditExperienceForm.tsx │ │ ├── manage-certification │ │ ├── add-certification │ │ │ └── page.tsx │ │ ├── page.tsx │ │ ├── edit-certification │ │ │ └── [id] │ │ │ │ └── page.tsx │ │ └── _components │ │ │ ├── certification-table.tsx │ │ │ └── delete-certification.tsx │ │ ├── _components │ │ └── StatsContainer.tsx │ │ ├── loading.tsx │ │ └── layout.tsx ├── (public) │ ├── projects │ │ ├── [id] │ │ │ ├── page.tsx │ │ │ └── loading.tsx │ │ ├── page.tsx │ │ └── _components │ │ │ ├── ProjectCardsContainer.tsx │ │ │ └── ProjectDetailContainer.tsx │ ├── experience │ │ ├── [id] │ │ │ ├── page.tsx │ │ │ └── loading.tsx │ │ ├── page.tsx │ │ └── _components │ │ │ ├── ExperienceCardContainer.tsx │ │ │ └── ExperienceDetailPage.tsx │ ├── page.tsx │ ├── about │ │ └── page.tsx │ ├── certification │ │ ├── [id] │ │ │ ├── page.tsx │ │ │ └── loading.tsx │ │ ├── page.tsx │ │ └── _components │ │ │ ├── CertificationCardContainer.tsx │ │ │ └── CertificationDetailContainer.tsx │ ├── techstack │ │ ├── page.tsx │ │ └── _components │ │ │ └── TechStackPageContainer.tsx │ ├── layout.tsx │ ├── _components │ │ ├── HomePageContainer.tsx │ │ ├── ContactMe.tsx │ │ ├── GTKM.tsx │ │ └── HeroMobile.tsx │ └── loading.tsx ├── providers.tsx ├── layout.tsx ├── MyContext.tsx └── globals.css ├── .eslintrc.json ├── lib ├── utils.ts ├── types │ ├── techstack-types │ │ └── index.ts │ ├── experience-types │ │ └── index.ts │ ├── certification-types │ │ └── index.ts │ ├── project-types │ │ └── index.ts │ └── admin-types │ │ └── index.ts └── sidelinks.tsx ├── components ├── Logo.tsx ├── theme-provider.tsx ├── ProjectMac.tsx ├── CertificationMac.tsx ├── PageHeader.tsx ├── FormHeader.tsx ├── StatsCard.tsx ├── ui │ ├── label.tsx │ ├── textarea.tsx │ ├── input.tsx │ ├── toaster.tsx │ ├── tag-popover.tsx │ ├── badge.tsx │ ├── popover.tsx │ ├── avatar.tsx │ ├── auto-complete.tsx │ ├── button.tsx │ ├── card.tsx │ ├── tag-list.tsx │ ├── card-hover-effect.tsx │ ├── calendar.tsx │ ├── table.tsx │ ├── tag.tsx │ ├── dialog.tsx │ ├── use-toast.ts │ └── sheet.tsx ├── Card.tsx ├── ThemeToggler.tsx ├── Navbar.tsx ├── spot-light.tsx ├── ExperienceCard.tsx ├── DatePicker.tsx ├── TechstackCard.tsx ├── NavbarLinks.tsx ├── AdminMobileNavbar.tsx └── FormComponents.tsx ├── site.config.ts ├── next.config.mjs ├── .env ├── components.json ├── db └── index.ts ├── middleware.ts ├── .gitignore ├── tsconfig.json ├── actions ├── stats.action.ts ├── admin.actions.ts ├── experience.actions.ts ├── techstack.actions.ts ├── certification.actions.ts └── project.actions.ts ├── package.json ├── prisma └── schema.prisma └── tailwind.config.ts /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeerUzairWasHere/open-source-portfolio/HEAD/public/logo.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/placeholder-user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeerUzairWasHere/open-source-portfolio/HEAD/public/placeholder-user.jpg -------------------------------------------------------------------------------- /app/(clerk)/sign-in/[[...sign-in]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignIn } from "@clerk/nextjs"; 2 | 3 | export default function Page() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /app/(clerk)/sign-up/[[...sign-up]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignUp } from "@clerk/nextjs"; 2 | 3 | export default function Page() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next", 3 | "rules": { 4 | "react/no-unescaped-entities": "off", 5 | "@next/next/no-page-custom-font": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /app/(clerk)/layout.tsx: -------------------------------------------------------------------------------- 1 | export default function ClerkLayout({ 2 | children, 3 | }: { 4 | children: React.ReactNode; 5 | }) { 6 | return ( 7 |
8 | {children} 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /components/Logo.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | export const Logo = ({ name }: { name: string }) => { 4 | return ( 5 | 6 | {name || "Your Logo"}. 7 | 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /site.config.ts: -------------------------------------------------------------------------------- 1 | export const siteConfig = { 2 | name: "Mir Uzair - Portfolio", 3 | description: "Driving end-to-end excellence with seamless user experiences through masterfully crafted code.", 4 | logo: { 5 | url: "/logo.png", 6 | href: "/logo.png" 7 | } 8 | } -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | remotePatterns: [ 5 | { 6 | protocol: "https", 7 | hostname: "res.cloudinary.com", 8 | }, 9 | ], 10 | }, 11 | }; 12 | 13 | export default nextConfig; 14 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL= 2 | 3 | NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME= 4 | NEXT_PUBLIC_UPLOAD_PRESET= 5 | 6 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= 7 | CLERK_SECRET_KEY= 8 | 9 | NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in 10 | NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up 11 | NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/ 12 | NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/ 13 | 14 | ADMIN_USER_ID= -------------------------------------------------------------------------------- /components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 5 | import { type ThemeProviderProps } from "next-themes/dist/types"; 6 | 7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 8 | return {children}; 9 | } 10 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /components/ProjectMac.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MacbookScroll } from "./ui/macbook-scroll"; 3 | import { Project } from "@/lib/types/project-types"; 4 | 5 | export function ProjectMac({ screenshot, title }: Project) { 6 | return ( 7 |
8 | {title}} 10 | src={screenshot} 11 | showGradient={false} 12 | /> 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /components/CertificationMac.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MacbookScroll } from "./ui/macbook-scroll"; 3 | import { CertificateType } from "@/lib/types/certification-types"; 4 | 5 | export function CertificationMac({ screenshot, title }: CertificateType) { 6 | return ( 7 |
8 | {title}} 10 | src={screenshot} 11 | showGradient={false} 12 | /> 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /components/PageHeader.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { Button } from "./ui/button"; 3 | 4 | export function PageHeader({ title, href }: { title: string; href: string }) { 5 | return ( 6 |
7 |

8 | Manage {title} 9 |

10 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /db/index.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | const prismaClientSingleton = () => { 4 | return new PrismaClient(); 5 | }; 6 | 7 | type PrismaClientSingleton = ReturnType; 8 | 9 | const globalForPrisma = globalThis as unknown as { 10 | prisma: PrismaClientSingleton | undefined; 11 | }; 12 | 13 | const prisma = globalForPrisma.prisma ?? prismaClientSingleton(); 14 | 15 | export default prisma; 16 | 17 | if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | import { authMiddleware } from "@clerk/nextjs"; 2 | 3 | export default authMiddleware({ 4 | publicRoutes: ['/', "/about", "/projects", "/projects/(.*)", "/experience", "/experience/(.*)", "/certification", "/certification/(.*)", "/techstack", "/techstack/(.*)"], 5 | 6 | }); 7 | 8 | export const config = { 9 | // Protects all routes, including api/trpc. 10 | // See https://clerk.com/docs/references/nextjs/auth-middleware 11 | // for more information about configuring your Middleware 12 | matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"], 13 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | demo.png 9 | 10 | # testing 11 | /coverage 12 | 13 | # next.js 14 | /.next/ 15 | /out/ 16 | 17 | # production 18 | /build 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | # local env files 30 | .env*.local 31 | .env.example 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | next-env.d.ts 39 | -------------------------------------------------------------------------------- /app/(admin)/admin-dashboard/manage-admin/add-admin/page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | dehydrate, 3 | HydrationBoundary, 4 | QueryClient, 5 | } from "@tanstack/react-query"; 6 | 7 | import { FormHeader } from "@/components/FormHeader"; 8 | import AddAdminForm from "../_forms/AddAdminForm"; 9 | 10 | const AddAdmin = () => { 11 | const queryClient = new QueryClient(); 12 | 13 | return ( 14 | <> 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | }; 22 | export default AddAdmin; 23 | -------------------------------------------------------------------------------- /app/(admin)/admin-dashboard/manage-projects/add-project/page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | dehydrate, 3 | HydrationBoundary, 4 | QueryClient, 5 | } from "@tanstack/react-query"; 6 | import AddProjectForm from "../_forms/AddProjectForm"; 7 | import { FormHeader } from "@/components/FormHeader"; 8 | 9 | const AddProject = () => { 10 | const queryClient = new QueryClient(); 11 | 12 | return ( 13 | <> 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | export default AddProject; 22 | -------------------------------------------------------------------------------- /app/(admin)/admin-dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | dehydrate, 3 | HydrationBoundary, 4 | QueryClient, 5 | } from "@tanstack/react-query"; 6 | 7 | import { getAllStats } from "@/actions/stats.action"; 8 | import StatsContainer from "./_components/StatsContainer"; 9 | 10 | export default async function Page() { 11 | const queryClient = new QueryClient(); 12 | 13 | await queryClient.prefetchQuery({ 14 | queryKey: ["stats"], 15 | queryFn: () => getAllStats(), 16 | }); 17 | 18 | return ( 19 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /app/(admin)/admin-dashboard/manage-techstack/add-techstack/page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | dehydrate, 3 | HydrationBoundary, 4 | QueryClient, 5 | } from "@tanstack/react-query"; 6 | import { FormHeader } from "@/components/FormHeader"; 7 | import AddTechstackForm from "../_forms/AddTechstackForm"; 8 | 9 | const AddTechstack = () => { 10 | const queryClient = new QueryClient(); 11 | 12 | return ( 13 | <> 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | export default AddTechstack; 22 | -------------------------------------------------------------------------------- /components/FormHeader.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { Button } from "./ui/button"; 3 | import { ArrowLeft } from "lucide-react"; 4 | type Prop = { 5 | title: string; 6 | href: string; 7 | }; 8 | export const FormHeader = ({ title, href }: Prop) => { 9 | return ( 10 |

11 | {title} 12 | 18 |

19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /app/(admin)/admin-dashboard/manage-experience/add-experience/page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | dehydrate, 3 | HydrationBoundary, 4 | QueryClient, 5 | } from "@tanstack/react-query"; 6 | import AddExperienceForm from "../_forms/AddExperienceForm"; 7 | import { FormHeader } from "@/components/FormHeader"; 8 | 9 | const AddExperience = () => { 10 | const queryClient = new QueryClient(); 11 | 12 | return ( 13 | <> 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | export default AddExperience; 22 | -------------------------------------------------------------------------------- /app/(admin)/admin-dashboard/manage-certification/add-certification/page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | dehydrate, 3 | HydrationBoundary, 4 | QueryClient, 5 | } from "@tanstack/react-query"; 6 | import AddCertificationForm from "../_forms/AddCertificationForm"; 7 | import { FormHeader } from "@/components/FormHeader"; 8 | 9 | const AddCertification = () => { 10 | const queryClient = new QueryClient(); 11 | 12 | return ( 13 | <> 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | export default AddCertification; 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /app/(admin)/admin-dashboard/manage-admin/page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | dehydrate, 3 | HydrationBoundary, 4 | QueryClient, 5 | } from "@tanstack/react-query"; 6 | import { PageHeader } from "@/components/PageHeader"; 7 | import { getAdminDetail } from "@/actions/admin.actions"; 8 | import { AdminDetailTable } from "./_components/AdminDetailTable"; 9 | 10 | export default async function Page() { 11 | const queryClient = new QueryClient(); 12 | 13 | await queryClient.prefetchQuery({ 14 | queryKey: ["admin"], 15 | queryFn: () => getAdminDetail(), 16 | }); 17 | 18 | return ( 19 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /components/StatsCard.tsx: -------------------------------------------------------------------------------- 1 | import { CardTitle, CardHeader, CardContent, Card } from "@/components/ui/card"; 2 | import { Waypoints } from "lucide-react"; 3 | 4 | type Props = { 5 | title: string; 6 | count: number; 7 | }; 8 | const StatsCard = ({ title, count }: Props) => { 9 | return ( 10 | 11 | 12 | 13 | {title} 14 | 15 | 16 | 17 | 18 |
{count}
19 |
20 |
21 | ); 22 | }; 23 | export default StatsCard; 24 | -------------------------------------------------------------------------------- /app/(admin)/admin-dashboard/manage-admin/edit-admin/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | dehydrate, 3 | HydrationBoundary, 4 | QueryClient, 5 | } from "@tanstack/react-query"; 6 | 7 | import { FormHeader } from "@/components/FormHeader"; 8 | import { getAdminDetail } from "@/actions/admin.actions"; 9 | import EditAdminForm from "../../_forms/EditAdminForm"; 10 | 11 | const EditProjectPage = async () => { 12 | const queryClient = new QueryClient(); 13 | 14 | await queryClient.prefetchQuery({ 15 | queryKey: ["admin"], 16 | queryFn: () => getAdminDetail(), 17 | }); 18 | 19 | return ( 20 | 21 | 22 | 23 | 24 | ); 25 | }; 26 | export default EditProjectPage; 27 | -------------------------------------------------------------------------------- /components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface TextareaProps 6 | extends React.TextareaHTMLAttributes {} 7 | 8 | const Textarea = React.forwardRef( 9 | ({ className, ...props }, ref) => { 10 | return ( 11 |