feat: sidebar, mobile sidebar, ui components

This commit is contained in:
mehedi-hasan 2024-04-07 22:56:22 +06:00
parent 00d85806fe
commit 48d007f079
76 changed files with 20501 additions and 0 deletions

20
.env Normal file
View File

@ -0,0 +1,20 @@
DATABASE_URL=mongodb+srv://freshbitemehedi:freshbitemehedi2810@project1.lispgny.mongodb.net/skilledai
UPLOADTHING_SECRET=sk_live_938397c2e99554ab0bc541ba01114334d2ed523a0dbb9988870bcfc05d6ad9df
UPLOADTHING_APP_ID=h07pj3xjyv
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=2aqrEwXp57xY5dA6WN5akp3ceFDVg64Qu0Ufm/Dew8g=
# Go to github and setup the oauth configuration
# https://next-auth.js.org/providers/github#configuration
# https://github.com/settings/developers
GITHUB_ID=674c632636000fe44d40
GITHUB_SECRET=0ec0b70e902ae00021514e21427108275ef4244b
NEXT_PUBLIC_SUPABASE_URL=https://ckngsmtjumbnwksjutdo.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNrbmdzbXRqdW1ibndrc2p1dGRvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTAzNDIwNjMsImV4cCI6MjAyNTkxODA2M30.ZkykkQeBh8-Ej8_2KMdFPNelEF6jzAgb1Nqd64vwAvs
NEXT_PUBLIC_SITE_URL=http://localhost:3000

10
.eslintrc.json Normal file
View File

@ -0,0 +1,10 @@
{
"extends": "next/core-web-vitals",
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/no-unused-vars": ["warn", { "args": "none" }],
"import/no-unresolved": "off",
"import/named": "off",
"no-console": "warn"
}
}

39
.gitignore vendored Normal file
View File

@ -0,0 +1,39 @@
# 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
# Contentlayer
.contentlayer

1
.prettierrc Normal file
View File

@ -0,0 +1 @@
{}

16
components.json Normal file
View File

@ -0,0 +1,16 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app/globals.css",
"baseColor": "zinc",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}

20
env.example.txt Normal file
View File

@ -0,0 +1,20 @@
#Add Uploadthing secret and appId to setup fileupload https://uploadthing.com/
UPLOADTHING_SECRET=
UPLOADTHING_APP_ID=
# Next auth https://next-auth.js.org/configuration/options
# Run this command to generate a a new NEXTAUTH_SECRET
# $ openssl rand -base64 32
NEXTAUTH_URL = http://localhost:3000
NEXTAUTH_SECRET=
# Go to github and setup the oauth configuration
# https://next-auth.js.org/providers/github#configuration
# https://github.com/settings/developers
GITHUB_ID =
GITHUB_SECRET =

8
next.config.js Normal file
View File

@ -0,0 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: ["utfs.io"],
},
};
module.exports = nextConfig;

16651
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

87
package.json Normal file
View File

@ -0,0 +1,87 @@
{
"name": "nextshcdndashboardstarter",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"prettier": "prettier . --write"
},
"dependencies": {
"@babel/parser": "^7.24.4",
"@babel/standalone": "^7.24.4",
"@babel/traverse": "^7.24.1",
"@hookform/resolvers": "^3.3.2",
"@monaco-editor/react": "^4.6.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^1.2.2",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@supabase/ssr": "^0.1.0",
"@supabase/supabase-js": "^2.41.1",
"@tanstack/react-table": "^8.10.7",
"@types/babel__standalone": "^7.1.7",
"@types/node": "20.5.7",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"@uploadthing/react": "^5.7.0",
"autoprefixer": "10.4.15",
"axios": "^1.6.8",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"contentlayer": "^0.3.4",
"eslint": "8.48.0",
"eslint-config-next": "^14.0.1",
"gray-matter": "^4.0.3",
"lucide-react": "^0.291.0",
"monaco-jsx-highlighter": "^2.77.77",
"monaco-themes": "^0.4.4",
"next": "^14.0.1",
"next-mdx-remote": "^4.4.1",
"next-themes": "^0.2.1",
"postcss": "8.4.28",
"react": "^18.2.0",
"react-day-picker": "^8.9.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.13",
"react-error-overlay": "^6.0.11",
"react-hook-form": "^7.47.0",
"react-resizable-panels": "^2.0.16",
"rehype-pretty-code": "^0.13.1",
"sharp": "^0.32.5",
"shiki": "^1.2.4",
"tailwind-merge": "^1.14.0",
"tailwindcss": "^3.4.0",
"tailwindcss-animate": "^1.0.7",
"typescript": "5.2.2",
"unist-util-visit": "^5.0.0",
"uploadthing": "^5.7.4",
"uuid": "^9.0.1",
"zod": "^3.22.4",
"zustand": "^4.5.2"
},
"devDependencies": {
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"prettier": "3.0.3",
"rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0",
"remark-gfm": "^4.0.0"
}
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

1
public/next.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

1
public/vercel.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

After

Width:  |  Height:  |  Size: 629 B

View File

@ -0,0 +1,27 @@
import { createUploadthing, type FileRouter } from "uploadthing/next";
const f = createUploadthing();
const auth = () => ({ id: "fakeId" }); // Fake auth function
// FileRouter for your app, can contain multiple FileRoutes
export const ourFileRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f(["pdf"])
// Set permissions and file types for this FileRoute
.middleware(async ({}) => {
// This code runs on your server before upload
const user = await auth();
// If you throw, the user will not be able to upload
if (!user) throw new Error("Unauthorized");
// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId: user.id };
})
.onUploadComplete(async () => {
// This code RUNS ON YOUR SERVER after upload
}),
} satisfies FileRouter;
export type OurFileRouter = typeof ourFileRouter;

View File

@ -0,0 +1,8 @@
import { createNextRouteHandler } from "uploadthing/next";
import { ourFileRouter } from "./core";
// Export routes for Next App Router
export const { GET, POST } = createNextRouteHandler({
router: ourFileRouter,
});

View File

@ -0,0 +1,19 @@
import { createClient } from "@/utils/supabase/server";
import { NextResponse } from "next/server";
export async function GET(request: Request) {
// The `/auth/callback` route is required for the server-side auth flow implemented
// by the SSR package. It exchanges an auth code for the user's session.
// https://supabase.com/docs/guides/auth/server-side/nextjs
const requestUrl = new URL(request.url);
const code = requestUrl.searchParams.get("code");
// const origin = requestUrl.origin;
if (code) {
const supabase = createClient();
await supabase.auth.exchangeCodeForSession(code);
}
// URL to redirect to after sign up process completes
return NextResponse.redirect(`${process.env.NEXT_PUBLIC_SITE_URL}/dashboard`);
}

BIN
src/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

82
src/app/globals.css Normal file
View File

@ -0,0 +1,82 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 221.2 83.2% 53.3%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 224.3 76.3% 48%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground box-border;
}
}
@layer utilities {
.min-h-screen {
min-height: 100vh; /* Fallback */
min-height: 100dvh;
}
.h-screen {
height: 100vh; /* Fallback */
height: 100dvh;
}
}
[data-ut-element="label"] {
@apply text-primary;
}
[data-ut-element="allowed-content"] {
@apply text-muted-foreground;
}
[data-ut-element="upload-icon"] {
@apply text-muted-foreground;
}

View File

@ -0,0 +1,42 @@
import { cn } from "@/lib/utils";
import { ChevronRightIcon } from "@radix-ui/react-icons";
import Link from "next/link";
import React from "react";
type BreadCrumbType = {
title: string;
link: string;
};
type BreadCrumbPropsType = {
items: BreadCrumbType[];
};
export default function BreadCrumb({ items }: BreadCrumbPropsType) {
return (
<div className="mb-4 flex items-center space-x-1 text-sm text-muted-foreground">
<Link
href={"/dashboard"}
className="overflow-hidden text-ellipsis whitespace-nowrap"
>
Dashboard
</Link>
{items?.map((item: BreadCrumbType, index: number) => (
<React.Fragment key={item.title}>
<ChevronRightIcon className="h-4 w-4" />
<Link
href={item.link}
className={cn(
"font-medium",
index === items.length - 1
? "text-foreground pointer-events-none"
: "text-muted-foreground",
)}
>
{item.title}
</Link>
</React.Fragment>
))}
</div>
);
}

View File

@ -0,0 +1,52 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Icons } from "@/components/icons";
import { cn } from "@/lib/utils";
import { NavItem } from "@/types";
import { Dispatch, SetStateAction } from "react";
interface DashboardNavProps {
items: NavItem[];
setOpen?: Dispatch<SetStateAction<boolean>>;
}
export function DashboardNav({ items, setOpen }: DashboardNavProps) {
const path = usePathname();
if (!items?.length) {
return null;
}
return (
<nav className="grid items-start gap-2">
{items.map((item, index) => {
const Icon = Icons[item.icon || "arrowRight"];
return (
item.href && (
<Link
key={index}
href={item.disabled ? "/" : item.href}
onClick={() => {
if (setOpen) setOpen(false);
}}
>
<span
className={cn(
"group flex items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground",
path === item.href ? "bg-accent" : "transparent",
item.disabled && "cursor-not-allowed opacity-80",
)}
>
<Icon className="mr-2 h-4 w-4" />
<span>{item.title}</span>
</span>
</Link>
)
);
})}
</nav>
);
}

View File

@ -0,0 +1,102 @@
"use client";
import { OurFileRouter } from "@/app/api/uploadthing/core";
import { UploadDropzone } from "@uploadthing/react";
import { UploadFileResponse } from "uploadthing/client";
// import { IMG_MAX_LIMIT } from "./forms/product-form";
import { useToast } from "./ui/use-toast";
interface ImageUploadProps {
onChange?: any;
onRemove: (value: UploadFileResponse[]) => void;
value: UploadFileResponse[];
}
export default function FileUpload({
onChange,
onRemove,
value,
}: ImageUploadProps) {
const { toast } = useToast();
// const onDeleteFile = (key: string) => {
// const files = value;
// let filteredFiles = files.filter((item) => item.key !== key);
// onRemove(filteredFiles);
// };
const onUpdateFile = (newFiles: UploadFileResponse[]) => {
onChange([...value, ...newFiles]);
};
return (
<div>
{/* <div className="flex items-center gap-4">
{!!value.length &&
value?.map((item) => (
<div
key={item.key}
className="relative w-[200px] h-[200px] rounded-md overflow-hidden"
>
<div className="z-10 absolute top-2 right-2">
<Button
type="button"
onClick={() => onDeleteFile(item.key)}
variant="destructive"
size="sm"
>
<Trash className="h-4 w-4" />
</Button>
</div>
<div>
<Image
fill
className="object-cover"
alt="Image"
src={item.fileUrl || ""}
/>
</div>
</div>
))}
</div> */}
<div>
{/* {value.length < IMG_MAX_LIMIT && ( */}
<UploadDropzone<OurFileRouter>
className="dark:bg-secondary py-2 ut-label:text-sm ut-allowed-content:ut-uploading:text-red-300 cursor-pointer data-[state=ready]:!text-red-600"
endpoint="imageUploader"
config={{ mode: "auto" }}
content={{
allowedContent({ isUploading }) {
if (isUploading)
return (
<>
<p className="mt-2 text-sm text-slate-400 animate-pulse">
File Uploading...
</p>
</>
);
},
}}
onClientUploadComplete={(res) => {
toast({
title: "Successfully uploaded file",
});
// Do something with the response
const data: UploadFileResponse[] | undefined = res;
if (data) {
onUpdateFile(data);
}
}}
onUploadError={(error: Error) => {
// console.log(error);
toast({
title: "Error",
variant: "destructive",
description: error.message,
});
}}
onUploadBegin={() => {
// Do something once upload begins
}}
/>
{/* )} */}
</div>
</div>
);
}

View File

@ -0,0 +1,57 @@
"use client";
import { Button } from "./ui/button";
import { createClient } from "@/utils/supabase/client";
export default function GoogleSignInButton() {
const handleGoogleSignIn = async () => {
const supabase = createClient();
const { data, error } = await supabase.auth.signInWithOAuth({
provider: "google",
options: {
queryParams: {
access_type: "offline",
prompt: "consent",
},
redirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback`,
},
});
};
return (
<Button
className="w-full flex items-center gap-2"
variant="outline"
type="button"
onClick={handleGoogleSignIn}
>
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="20"
height="20"
viewBox="0 0 48 48"
>
<path
fill="#FFC107"
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
></path>
<path
fill="#FF3D00"
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
></path>
<path
fill="#4CAF50"
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
></path>
<path
fill="#1976D2"
d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
></path>
</svg>
Continue with google
</Button>
);
}

84
src/components/icons.tsx Normal file
View File

@ -0,0 +1,84 @@
import {
AlertTriangle,
ArrowRight,
Check,
ChevronLeft,
ChevronRight,
CircuitBoardIcon,
Command,
CreditCard,
File,
FileText,
HelpCircle,
Image,
Laptop,
LayoutDashboardIcon,
Loader2,
LogIn,
LucideIcon,
LucideProps,
Moon,
MoreVertical,
Pizza,
Plus,
Settings,
ShieldQuestion,
SunMedium,
Trash,
Twitter,
User,
User2Icon,
UserX2Icon,
X,
} from "lucide-react";
export type Icon = LucideIcon;
export const Icons = {
dashboard: LayoutDashboardIcon,
logo: Command,
login: LogIn,
close: X,
profile: User2Icon,
spinner: Loader2,
kanban: CircuitBoardIcon,
chevronLeft: ChevronLeft,
chevronRight: ChevronRight,
trash: Trash,
employee: UserX2Icon,
post: FileText,
page: File,
media: Image,
settings: Settings,
billing: CreditCard,
ellipsis: MoreVertical,
add: Plus,
warning: AlertTriangle,
user: User,
arrowRight: ArrowRight,
help: HelpCircle,
pizza: Pizza,
sun: SunMedium,
moon: Moon,
laptop: Laptop,
shieldQuestion: ShieldQuestion,
gitHub: ({ ...props }: LucideProps) => (
<svg
aria-hidden="true"
focusable="false"
data-prefix="fab"
data-icon="github"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 496 512"
{...props}
>
<path
fill="currentColor"
d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"
></path>
</svg>
),
twitter: Twitter,
check: Check,
};

View File

@ -0,0 +1,11 @@
"use client";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes/dist/types";
export default function ThemeProvider({
children,
...props
}: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

View File

@ -0,0 +1,37 @@
"use client";
import { MoonIcon, SunIcon } from "@radix-ui/react-icons";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
type CompProps = {};
export default function ThemeToggle({}: CompProps) {
const { setTheme } = useTheme();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<SunIcon className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<MoonIcon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@ -0,0 +1,30 @@
import ThemeToggle from "@/components/layout/ThemeToggle/theme-toggle";
import { cn } from "@/lib/utils";
import { MobileSidebar } from "./mobile-sidebar";
import { UserNav } from "./user-nav";
import Link from "next/link";
export default function Header() {
return (
<div className="fixed top-0 left-0 right-0 supports-backdrop-blur:bg-background/60 border-b bg-background/95 backdrop-blur z-20">
<nav className="h-14 flex items-center justify-between px-4">
<div className="hidden lg:block">
<Link
href={"/dashboard"}
>
SkilledAi
</Link>
</div>
<div className={cn("block lg:!hidden")}>
<MobileSidebar />
</div>
<div className="flex items-center gap-2">
<UserNav />
<ThemeToggle />
</div>
</nav>
</div>
);
}

View File

@ -0,0 +1,37 @@
"use client";
import { DashboardNav } from "@/components/dashboard-nav";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import { navItems } from "@/constants/data";
import { MenuIcon } from "lucide-react";
import { useState } from "react";
// import { Playlist } from "../data/playlists";
interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
// playlists: Playlist[];
}
export function MobileSidebar({ className }: SidebarProps) {
const [open, setOpen] = useState(false);
return (
<>
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<MenuIcon />
</SheetTrigger>
<SheetContent side="left" className="!px-0">
<div className="space-y-4 py-4">
<div className="px-3 py-2">
<h2 className="mb-2 px-4 text-lg font-semibold tracking-tight">
Overview
</h2>
<div className="space-y-1">
<DashboardNav items={navItems} setOpen={setOpen} />
</div>
</div>
</div>
</SheetContent>
</Sheet>
</>
);
}

View File

@ -0,0 +1,12 @@
"use client";
import React from "react";
import ThemeProvider from "./ThemeToggle/theme-provider";
export default function Providers({ children }: { children: React.ReactNode }) {
return (
<>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</ThemeProvider>
</>
);
}

View File

@ -0,0 +1,22 @@
import { DashboardNav } from "@/components/dashboard-nav";
import { navItems } from "@/constants/data";
import { cn } from "@/lib/utils";
export default function Sidebar() {
return (
<nav
className={cn(`relative hidden h-screen border-r pt-16 lg:block w-72`)}
>
<div className="space-y-4 py-4">
<div className="px-3 py-2">
<div className="space-y-1">
<h2 className="mb-2 px-4 text-xl font-semibold tracking-tight">
Overview
</h2>
<DashboardNav items={navItems} />
</div>
</div>
</div>
</nav>
);
}

View File

@ -0,0 +1,69 @@
"use client";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { createClient } from "@/utils/supabase/client";
import { useRouter } from "next/navigation";
export function UserNav() {
const router = useRouter();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
<Avatar className="h-8 w-8">
<AvatarImage src={""} alt={""} />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" align="end" forceMount>
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium leading-none">John Doe</p>
<p className="text-xs leading-none text-muted-foreground">
john@doe.com
</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
Profile
<DropdownMenuShortcut>P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Billing
<DropdownMenuShortcut>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
Settings
<DropdownMenuShortcut>S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>New Team</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem
className="cursor-pointer"
onClick={async () => {
const supabase = createClient();
const { error } = await supabase.auth.signOut();
console.log(error);
router.replace("/");
}}
>
Log out
<DropdownMenuShortcut>Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@ -0,0 +1,46 @@
"use client";
import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Modal } from "@/components/ui/modal";
interface AlertModalProps {
isOpen: boolean;
onClose: () => void;
onConfirm: () => void;
loading: boolean;
}
export const AlertModal: React.FC<AlertModalProps> = ({
isOpen,
onClose,
onConfirm,
loading,
}) => {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
if (!isMounted) {
return null;
}
return (
<Modal
title="Are you sure?"
description="This action cannot be undone."
isOpen={isOpen}
onClose={onClose}
>
<div className="pt-6 space-x-2 flex items-center justify-end w-full">
<Button disabled={loading} variant="outline" onClick={onClose}>
Cancel
</Button>
<Button disabled={loading} variant="destructive" onClick={onConfirm}>
Continue
</Button>
</div>
</Modal>
);
};

View File

@ -0,0 +1,57 @@
"use client";
import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils";
const Accordion = AccordionPrimitive.Root;
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
));
AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className,
)}
{...props}
>
{children}
<ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };

View File

@ -0,0 +1,145 @@
"use client";
import * as React from "react";
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
import { cn } from "@/lib/utils";
import { buttonVariants } from "@/components/ui/button";
const AlertDialog = AlertDialogPrimitive.Root;
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
const AlertDialogPortal = AlertDialogPrimitive.Portal;
const AlertDialogOverlay = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
ref={ref}
/>
));
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
const AlertDialogContent = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
>(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
)}
{...props}
/>
</AlertDialogPortal>
));
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
const AlertDialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className,
)}
{...props}
/>
);
AlertDialogHeader.displayName = "AlertDialogHeader";
const AlertDialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className,
)}
{...props}
/>
);
AlertDialogFooter.displayName = "AlertDialogFooter";
const AlertDialogTitle = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold", className)}
{...props}
/>
));
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
const AlertDialogDescription = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName;
const AlertDialogAction = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Action>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action
ref={ref}
className={cn(buttonVariants(), className)}
{...props}
/>
));
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
const AlertDialogCancel = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(
buttonVariants({ variant: "outline" }),
"mt-2 sm:mt-0",
className,
)}
onClick={() =>
// yes, you have to set a timeout
setTimeout(() => (document.body.style.pointerEvents = ""), 100)
}
{...props}
/>
));
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
};

View File

@ -0,0 +1,59 @@
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
},
);
const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
));
Alert.displayName = "Alert";
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
));
AlertTitle.displayName = "AlertTitle";
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
));
AlertDescription.displayName = "AlertDescription";
export { Alert, AlertTitle, AlertDescription };

View File

@ -0,0 +1,50 @@
"use client";
import * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar";
import { cn } from "@/lib/utils";
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className,
)}
{...props}
/>
));
Avatar.displayName = AvatarPrimitive.Root.displayName;
const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className,
)}
{...props}
/>
));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
export { Avatar, AvatarImage, AvatarFallback };

View File

@ -0,0 +1,36 @@
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
);
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
);
}
export { Badge, badgeVariants };

View File

@ -0,0 +1,57 @@
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
},
);
Button.displayName = "Button";
export { Button, buttonVariants };

View File

@ -0,0 +1,71 @@
"use client";
import * as React from "react";
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
import { DayPicker } from "react-day-picker";
import { cn } from "@/lib/utils";
import { buttonVariants } from "@/components/ui/button";
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4",
caption: "flex justify-center pt-1 relative items-center",
caption_label: "text-sm font-medium",
nav: "space-x-1 flex items-center",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-y-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent",
props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md",
),
day: cn(
buttonVariants({ variant: "ghost" }),
"h-8 w-8 p-0 font-normal aria-selected:opacity-100",
),
day_range_start: "day-range-start",
day_range_end: "day-range-end",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside: "text-muted-foreground opacity-50",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
IconLeft: ({ ...props }) => <ChevronLeftIcon className="h-4 w-4" />,
IconRight: ({ ...props }) => <ChevronRightIcon className="h-4 w-4" />,
}}
{...props}
/>
);
}
Calendar.displayName = "Calendar";
export { Calendar };

View File

@ -0,0 +1,83 @@
import * as React from "react";
import { cn } from "@/lib/utils";
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border bg-card text-card-foreground shadow",
className,
)}
{...props}
/>
));
Card.displayName = "Card";
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
));
CardHeader.displayName = "CardHeader";
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
));
CardTitle.displayName = "CardTitle";
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
CardDescription.displayName = "CardDescription";
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
));
CardContent.displayName = "CardContent";
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
));
CardFooter.displayName = "CardFooter";
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent,
};

View File

@ -0,0 +1,30 @@
"use client";
import * as React from "react";
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { CheckIcon } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils";
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className,
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
>
<CheckIcon className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
export { Checkbox };

View File

@ -0,0 +1,131 @@
"use client";
import {
ColumnDef,
flexRender,
getCoreRowModel,
getFilteredRowModel,
useReactTable,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Input } from "./input";
import { Button } from "./button";
import { ScrollArea, ScrollBar } from "./scroll-area";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
searchKey: string;
}
export function DataTable<TData, TValue>({
columns,
data,
searchKey,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
});
/* this can be used to get the selectedrows
console.log("value", table.getFilteredSelectedRowModel()); */
return (
<>
<Input
placeholder={`Search ${searchKey}...`}
value={(table.getColumn(searchKey)?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn(searchKey)?.setFilterValue(event.target.value)
}
className="w-full md:max-w-sm"
/>
<ScrollArea className="rounded-md border h-[calc(80vh-220px)]">
<Table className="relative">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<ScrollBar orientation="horizontal" />
</ScrollArea>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</>
);
}

181
src/components/ui/form.tsx Normal file
View File

@ -0,0 +1,181 @@
import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot";
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
} from "react-hook-form";
import { cn } from "@/lib/utils";
import { Label } from "@/components/ui/label";
const Form = FormProvider;
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue,
);
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
);
};
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();
const fieldState = getFieldState(fieldContext.name, formState);
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>");
}
const { id } = itemContext;
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};
type FormItemContextValue = {
id: string;
};
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue,
);
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId();
return (
<FormItemContext.Provider value={{ id }}>
<div
ref={ref}
className={cn("space-y-2 mb-2 lg:mb-0", className)}
{...props}
/>
</FormItemContext.Provider>
);
});
FormItem.displayName = "FormItem";
const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField();
return (
<Label
ref={ref}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
);
});
FormLabel.displayName = "FormLabel";
const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } =
useFormField();
return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
);
});
FormControl.displayName = "FormControl";
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField();
return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-[0.8rem] text-muted-foreground", className)}
{...props}
/>
);
});
FormDescription.displayName = "FormDescription";
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;
if (!body) {
return null;
}
return (
<p
ref={ref}
id={formMessageId}
className={cn("text-[0.8rem] font-medium text-destructive", className)}
{...props}
>
{body}
</p>
);
});
FormMessage.displayName = "FormMessage";
export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
};

View File

@ -0,0 +1,13 @@
interface HeadingProps {
title: string;
description: string;
}
export const Heading: React.FC<HeadingProps> = ({ title, description }) => {
return (
<div>
<h2 className="text-3xl font-bold tracking-tight">{title}</h2>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
);
};

View File

@ -0,0 +1,42 @@
"use client";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
interface ModalProps {
title: string;
description: string;
isOpen: boolean;
onClose: () => void;
children?: React.ReactNode;
}
export const Modal: React.FC<ModalProps> = ({
title,
description,
isOpen,
onClose,
children,
}) => {
const onChange = (open: boolean) => {
if (!open) {
onClose();
}
};
return (
<Dialog open={isOpen} onOpenChange={onChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
<div>{children}</div>
</DialogContent>
</Dialog>
);
};

View File

@ -0,0 +1,31 @@
"use client";
import * as React from "react";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import { cn } from "@/lib/utils";
const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent };

View File

@ -0,0 +1,28 @@
"use client"
import * as React from "react"
import * as ProgressPrimitive from "@radix-ui/react-progress"
import { cn } from "@/lib/utils"
const Progress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn(
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
className
)}
{...props}
>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
))
Progress.displayName = ProgressPrimitive.Root.displayName
export { Progress }

View File

@ -0,0 +1,45 @@
"use client"
import { DragHandleDots2Icon } from "@radix-ui/react-icons"
import * as ResizablePrimitive from "react-resizable-panels"
import { cn } from "@/lib/utils"
const ResizablePanelGroup = ({
className,
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
<ResizablePrimitive.PanelGroup
className={cn(
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
className
)}
{...props}
/>
)
const ResizablePanel = ResizablePrimitive.Panel
const ResizableHandle = ({
withHandle,
className,
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
withHandle?: boolean
}) => (
<ResizablePrimitive.PanelResizeHandle
className={cn(
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
className
)}
{...props}
>
{withHandle && (
<div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
<DragHandleDots2Icon className="h-2.5 w-2.5" />
</div>
)}
</ResizablePrimitive.PanelResizeHandle>
)
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }

View File

@ -0,0 +1,48 @@
"use client";
import * as React from "react";
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import { cn } from "@/lib/utils";
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("relative", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
));
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
className,
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
));
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
export { ScrollArea, ScrollBar };

View File

@ -0,0 +1,120 @@
"use client";
import * as React from "react";
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
import * as SelectPrimitive from "@radix-ui/react-select";
import { cn } from "@/lib/utils";
const Select = SelectPrimitive.Root;
const SelectGroup = SelectPrimitive.Group;
const SelectValue = SelectPrimitive.Value;
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<CaretSortIcon className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className,
)}
position={position}
{...props}
>
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
)}
>
{children}
</SelectPrimitive.Viewport>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("px-2 py-1.5 text-sm font-semibold", className)}
{...props}
/>
));
SelectLabel.displayName = SelectPrimitive.Label.displayName;
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
{...props}
>
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
};

View File

@ -0,0 +1,31 @@
"use client";
import * as React from "react";
import * as SeparatorPrimitive from "@radix-ui/react-separator";
import { cn } from "@/lib/utils";
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(
(
{ className, orientation = "horizontal", decorative = true, ...props },
ref,
) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className,
)}
{...props}
/>
),
);
Separator.displayName = SeparatorPrimitive.Root.displayName;
export { Separator };

140
src/components/ui/sheet.tsx Normal file
View File

@ -0,0 +1,140 @@
"use client";
import * as React from "react";
import * as SheetPrimitive from "@radix-ui/react-dialog";
import { Cross2Icon } from "@radix-ui/react-icons";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const Sheet = SheetPrimitive.Root;
const SheetTrigger = SheetPrimitive.Trigger;
const SheetClose = SheetPrimitive.Close;
const SheetPortal = SheetPrimitive.Portal;
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
ref={ref}
/>
));
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
{
variants: {
side: {
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
},
);
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
));
SheetContent.displayName = SheetPrimitive.Content.displayName;
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className,
)}
{...props}
/>
);
SheetHeader.displayName = "SheetHeader";
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className,
)}
{...props}
/>
);
SheetFooter.displayName = "SheetFooter";
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold text-foreground", className)}
{...props}
/>
));
SheetTitle.displayName = SheetPrimitive.Title.displayName;
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
SheetDescription.displayName = SheetPrimitive.Description.displayName;
export {
Sheet,
SheetPortal,
SheetOverlay,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
};

View File

@ -0,0 +1,15 @@
import { cn } from "@/lib/utils";
function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("animate-pulse rounded-md bg-primary/10", className)}
{...props}
/>
);
}
export { Skeleton };

View File

@ -0,0 +1,28 @@
"use client";
import * as React from "react";
import * as SliderPrimitive from "@radix-ui/react-slider";
import { cn } from "@/lib/utils";
const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
<SliderPrimitive.Root
ref={ref}
className={cn(
"relative flex w-full touch-none select-none items-center",
className,
)}
{...props}
>
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
<SliderPrimitive.Range className="absolute h-full bg-primary" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
</SliderPrimitive.Root>
));
Slider.displayName = SliderPrimitive.Root.displayName;
export { Slider };

View File

@ -0,0 +1,29 @@
"use client";
import * as React from "react";
import * as SwitchPrimitives from "@radix-ui/react-switch";
import { cn } from "@/lib/utils";
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-[20px] w-[36px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className,
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
)}
/>
</SwitchPrimitives.Root>
));
Switch.displayName = SwitchPrimitives.Root.displayName;
export { Switch };

117
src/components/ui/table.tsx Normal file
View File

@ -0,0 +1,117 @@
import * as React from "react";
import { cn } from "@/lib/utils";
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
));
Table.displayName = "Table";
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
));
TableHeader.displayName = "TableHeader";
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
));
TableBody.displayName = "TableBody";
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn("bg-primary font-medium text-primary-foreground", className)}
{...props}
/>
));
TableFooter.displayName = "TableFooter";
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className,
)}
{...props}
/>
));
TableRow.displayName = "TableRow";
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}
/>
));
TableHead.displayName = "TableHead";
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}
/>
));
TableCell.displayName = "TableCell";
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
));
TableCaption.displayName = "TableCaption";
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
};

View File

@ -0,0 +1,32 @@
export const CODE_SNIPPETS = {
javascript: `\nfunction greet(name) {\n\tconsole.log("Hello, " + name + "!");\n}\n\ngreet("Mehedi");\n`,
typescript: `\ntype Params = {\n\tname: string;\n}\n\nfunction greet(data: Params) {\n\tconsole.log("Hello, " + data.name + "!");\n}\n\ngreet({ name: "Mehedi" });\n`,
python: `\ndef greet(name):\n\tprint("Hello, " + name + "!")\n\ngreet("Mehedi")\n`,
java: `\npublic class HelloWorld {\n\tpublic static void main(String[] args) {\n\t\tSystem.out.println("Hello World");\n\t}\n}\n`,
csharp:
'using System;\n\nnamespace HelloWorld\n{\n\tclass Hello { \n\t\tstatic void Main(string[] args) {\n\t\t\tConsole.WriteLine("Hello World in C#");\n\t\t}\n\t}\n}\n',
php: "<?php\n\n$name = 'Mehedi';\necho $name;\n",
react: `import * as React from 'react'; //don't change this line
function MyComponent() {
const [counter, setCounter] = React.useState(0);
const handleIncreaseClick = React.useCallback(() => {
setCounter(counter + 1);
}, [counter]);
const handleDecreaseClick = React.useCallback(() => {
setCounter(counter - 1);
}, [counter]);
return (
<div className="text-2xl w-fit p-4">
<div className="mx-auto w-fit">{counter}</div>
<div className="space-x-8">
<button onClick={handleIncreaseClick}>+</button>
<button onClick={handleDecreaseClick}>-</button>
</div>
</div>
);
}`,
};

133
src/constants/data.ts Normal file
View File

@ -0,0 +1,133 @@
import { NavItem } from "@/types";
export type User = {
id: number;
name: string;
company: string;
role: string;
verified: boolean;
status: string;
};
// show customised outline when an element has focus (but only if the user is
// using the keyboard)
// TODO: move this to a global css rule
export const FOCUS_VISIBLE_OUTLINE = `focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-500/70`;
export const LINK_STYLES = `text-rose-100/90 underline decoration-rose-200/30 underline-offset-2 transition-all hover:text-rose-100 hover:decoration-rose-200/50`;
export const LINK_SUBTLE_STYLES = `hover:underline hover:decoration-rose-300/30 hover:underline-offset-2 hover:text-rose-200/90`;
export const HEADING_LINK_ANCHOR = `before:content-['#'] before:absolute before:-ml-[1em] before:text-rose-100/0 hover:before:text-rose-100/50 pl-[1em] -ml-[1em]`;
export const navItems: NavItem[] = [
{
title: "Dashboard",
href: "/dashboard",
icon: "dashboard",
label: "Dashboard",
},
{
title: "Take Quiz",
href: "/dashboard/quiz",
icon: "shieldQuestion",
label: "Take Quiz",
},
{
title: "Your Task",
href: "/dashboard/task/react/1",
icon: "shieldQuestion",
label: "Your Task",
},
{
title: "Profile",
href: "/dashboard/profile",
icon: "profile",
label: "profile",
},
// {
// title: "Login",
// href: "/",
// icon: "login",
// label: "login",
// },
];
export const questions = [
{
category: "python",
id: "6233861762eaad73716a8cb1",
correctAnswer: "A programming language",
incorrectAnswers: [
"A type of snake",
"A software development company",
"A HTML competitor",
],
question: {
text: "What is Python?",
},
tags: ["python"],
type: "text_choice",
difficulty: "easy",
regions: [],
isNiche: false,
},
{
category: "python",
id: "62602e064b176d54800e3cbc",
correctAnswer: "#",
incorrectAnswers: ["//", "@", ">"],
question: {
text: "What symbol is used for comments in Python?",
},
tags: ["python"],
type: "text_choice",
difficulty: "easy",
regions: [],
isNiche: false,
},
{
category: "python",
id: "623740cccb85f7ce9e949d23",
correctAnswer: "Character",
incorrectAnswers: ["String", "Integer", "Boolean"],
question: {
text: "Which of the following is not a valid data type in Python?",
},
tags: ["python"],
type: "text_choice",
difficulty: "easy",
regions: [],
isNiche: false,
},
{
category: "python",
id: "622a1c3c7cc59eab6f951977",
correctAnswer: "x = 5",
incorrectAnswers: ["var x;", "const x = 5", "int x = 5"],
question: {
text: "How do you declare a variable in Python?",
},
tags: ["words", "mythology", "python"],
type: "text_choice",
difficulty: "easy",
regions: [],
isNiche: false,
},
{
category: "python",
id: "65056e927a97013de78b5313",
correctAnswer: "input()",
incorrectAnswers: ["get_input()", "read_input()", "foo_input()"],
question: {
text: "What function is used to get user input in Python?",
},
tags: ["python"],
type: "text_choice",
difficulty: "easy",
regions: [],
isNiche: false,
},
];

View File

@ -0,0 +1,291 @@
export const LANGUAGE_VERSIONS = {
react: "18",
javascript: "18.15.0",
typescript: "5.0.3",
python: "3.10.0",
java: "15.0.2",
csharp: "6.12.0",
php: "8.2.3",
};
// export const languageOptions = [
// {
// id: 63,
// name: "JavaScript (Node.js 12.14.0)",
// label: "JavaScript (Node.js 12.14.0)",
// value: "javascript",
// },
// {
// id: 45,
// name: "Assembly (NASM 2.14.02)",
// label: "Assembly (NASM 2.14.02)",
// value: "assembly",
// },
// {
// id: 46,
// name: "Bash (5.0.0)",
// label: "Bash (5.0.0)",
// value: "bash",
// },
// {
// id: 47,
// name: "Basic (FBC 1.07.1)",
// label: "Basic (FBC 1.07.1)",
// value: "basic",
// },
// {
// id: 75,
// name: "C (Clang 7.0.1)",
// label: "C (Clang 7.0.1)",
// value: "c",
// },
// {
// id: 76,
// name: "C++ (Clang 7.0.1)",
// label: "C++ (Clang 7.0.1)",
// value: "cpp",
// },
// {
// id: 48,
// name: "C (GCC 7.4.0)",
// label: "C (GCC 7.4.0)",
// value: "c",
// },
// {
// id: 52,
// name: "C++ (GCC 7.4.0)",
// label: "C++ (GCC 7.4.0)",
// value: "cpp",
// },
// {
// id: 49,
// name: "C (GCC 8.3.0)",
// label: "C (GCC 8.3.0)",
// value: "c",
// },
// {
// id: 53,
// name: "C++ (GCC 8.3.0)",
// label: "C++ (GCC 8.3.0)",
// value: "cpp",
// },
// {
// id: 50,
// name: "C (GCC 9.2.0)",
// label: "C (GCC 9.2.0)",
// value: "c",
// },
// {
// id: 54,
// name: "C++ (GCC 9.2.0)",
// label: "C++ (GCC 9.2.0)",
// value: "cpp",
// },
// {
// id: 86,
// name: "Clojure (1.10.1)",
// label: "Clojure (1.10.1)",
// value: "clojure",
// },
// {
// id: 51,
// name: "C# (Mono 6.6.0.161)",
// label: "C# (Mono 6.6.0.161)",
// value: "csharp",
// },
// {
// id: 77,
// name: "COBOL (GnuCOBOL 2.2)",
// label: "COBOL (GnuCOBOL 2.2)",
// value: "cobol",
// },
// {
// id: 55,
// name: "Common Lisp (SBCL 2.0.0)",
// label: "Common Lisp (SBCL 2.0.0)",
// value: "lisp",
// },
// {
// id: 56,
// name: "D (DMD 2.089.1)",
// label: "D (DMD 2.089.1)",
// value: "d",
// },
// {
// id: 57,
// name: "Elixir (1.9.4)",
// label: "Elixir (1.9.4)",
// value: "elixir",
// },
// {
// id: 58,
// name: "Erlang (OTP 22.2)",
// label: "Erlang (OTP 22.2)",
// value: "erlang",
// },
// {
// id: 44,
// label: "Executable",
// name: "Executable",
// value: "exe",
// },
// {
// id: 87,
// name: "F# (.NET Core SDK 3.1.202)",
// label: "F# (.NET Core SDK 3.1.202)",
// value: "fsharp",
// },
// {
// id: 59,
// name: "Fortran (GFortran 9.2.0)",
// label: "Fortran (GFortran 9.2.0)",
// value: "fortran",
// },
// {
// id: 60,
// name: "Go (1.13.5)",
// label: "Go (1.13.5)",
// value: "go",
// },
// {
// id: 88,
// name: "Groovy (3.0.3)",
// label: "Groovy (3.0.3)",
// value: "groovy",
// },
// {
// id: 61,
// name: "Haskell (GHC 8.8.1)",
// label: "Haskell (GHC 8.8.1)",
// value: "haskell",
// },
// {
// id: 62,
// name: "Java (OpenJDK 13.0.1)",
// label: "Java (OpenJDK 13.0.1)",
// value: "java",
// },
// {
// id: 78,
// name: "Kotlin (1.3.70)",
// label: "Kotlin (1.3.70)",
// value: "kotlin",
// },
// {
// id: 64,
// name: "Lua (5.3.5)",
// label: "Lua (5.3.5)",
// value: "lua",
// },
// {
// id: 79,
// name: "Objective-C (Clang 7.0.1)",
// label: "Objective-C (Clang 7.0.1)",
// value: "objectivec",
// },
// {
// id: 65,
// name: "OCaml (4.09.0)",
// label: "OCaml (4.09.0)",
// value: "ocaml",
// },
// {
// id: 66,
// name: "Octave (5.1.0)",
// label: "Octave (5.1.0)",
// value: "octave",
// },
// {
// id: 67,
// name: "Pascal (FPC 3.0.4)",
// label: "Pascal (FPC 3.0.4)",
// value: "pascal",
// },
// {
// id: 85,
// name: "Perl (5.28.1)",
// label: "Perl (5.28.1)",
// value: "perl",
// },
// {
// id: 68,
// name: "PHP (7.4.1)",
// label: "PHP (7.4.1)",
// value: "php",
// },
// {
// id: 43,
// label: "Plain Text",
// name: "Plain Text",
// value: "text",
// },
// {
// id: 69,
// name: "Prolog (GNU Prolog 1.4.5)",
// label: "Prolog (GNU Prolog 1.4.5)",
// value: "prolog",
// },
// {
// id: 70,
// name: "Python (2.7.17)",
// label: "Python (2.7.17)",
// value: "python",
// },
// {
// id: 71,
// name: "Python (3.8.1)",
// label: "Python (3.8.1)",
// value: "python",
// },
// {
// id: 80,
// name: "R (4.0.0)",
// label: "R (4.0.0)",
// value: "r",
// },
// {
// id: 72,
// name: "Ruby (2.7.0)",
// label: "Ruby (2.7.0)",
// value: "ruby",
// },
// {
// id: 73,
// name: "Rust (1.40.0)",
// label: "Rust (1.40.0)",
// value: "rust",
// },
// {
// id: 81,
// name: "Scala (2.13.2)",
// label: "Scala (2.13.2)",
// value: "scala",
// },
// {
// id: 82,
// name: "SQL (SQLite 3.27.2)",
// label: "SQL (SQLite 3.27.2)",
// value: "sql",
// },
// {
// id: 83,
// name: "Swift (5.2.3)",
// label: "Swift (5.2.3)",
// value: "swift",
// },
// {
// id: 74,
// name: "TypeScript (3.7.4)",
// label: "TypeScript (3.7.4)",
// value: "typescript",
// },
// {
// id: 84,
// name: "Visual Basic.Net (vbnc 0.0.0.5943)",
// label: "Visual Basic.Net (vbnc 0.0.0.5943)",
// value: "vbnet",
// },
// ];

View File

@ -0,0 +1,56 @@
export const monacoThemes = {
active4d: "Active4D",
"all-hallows-eve": "All Hallows Eve",
amy: "Amy",
"birds-of-paradise": "Birds of Paradise",
blackboard: "Blackboard",
"brilliance-black": "Brilliance Black",
"brilliance-dull": "Brilliance Dull",
"chrome-devtools": "Chrome DevTools",
"clouds-midnight": "Clouds Midnight",
clouds: "Clouds",
cobalt: "Cobalt",
cobalt2: "Cobalt2",
dracula: "Dracula",
"github-dark": "GitHub Dark",
"github-light": "GitHub Light",
nord: "Nord",
dawn: "Dawn",
dreamweaver: "Dreamweaver",
eiffel: "Eiffel",
"espresso-libre": "Espresso Libre",
github: "GitHub",
idle: "IDLE",
katzenmilch: "Katzenmilch",
"kuroir-theme": "Kuroir Theme",
lazy: "LAZY",
"magicwb--amiga-": "MagicWB (Amiga)",
"merbivore-soft": "Merbivore Soft",
merbivore: "Merbivore",
"monokai-bright": "Monokai Bright",
monokai: "Monokai",
"night-owl": "Night Owl",
"oceanic-next": "Oceanic Next",
"pastels-on-dark": "Pastels on Dark",
"slush-and-poppies": "Slush and Poppies",
"solarized-dark": "Solarized-dark",
"solarized-light": "Solarized-light",
spacecadet: "SpaceCadet",
sunburst: "Sunburst",
"textmate--mac-classic-": "Textmate (Mac Classic)",
"tomorrow-night-blue": "Tomorrow-Night-Blue",
"tomorrow-night-bright": "Tomorrow-Night-Bright",
"tomorrow-night-eighties": "Tomorrow-Night-Eighties",
"tomorrow-night": "Tomorrow-Night",
tomorrow: "Tomorrow",
twilight: "Twilight",
"upstream-sunburst": "Upstream Sunburst",
"vibrant-ink": "Vibrant Ink",
"xcode-default": "Xcode_default",
zenburnesque: "Zenburnesque",
iplastic: "iPlastic",
idlefingers: "idleFingers",
krtheme: "krTheme",
monoindustrial: "monoindustrial",
};

View File

@ -0,0 +1,186 @@
import { language } from "gray-matter";
const md =
"# Beginner-Friendly React Tutorial: A Basic React Component\n\nWelcome to this beginner-friendly tutorial to get you started with React. React is a powerful JavaScript library for building user interfaces, most commonly single-page applications.\n\n## Setting Up a New React Project\n\nYou will need Node.js and npm (Node Package Manager) installed on your computer. To create a new React project, you can use the Create React App CLI tool. In your terminal, type:\n\n```bash\nnpx create-react-app my-first-react-app\n```\n\nThis will create a new directory called `my-first-react-app` with all the starter files you need.\n\n## Creating a Simple React Component\n\nEach React component is like a building block for your application. Components can be either class-based or functional. With the introduction of React Hooks, functional components are now powerful and can manage state and side effects.\n\nLet's create a simple functional component that displays a greeting message. In your `src` folder, create a new file named `Greeting.js` and add the following code:\n\n```jsx\nimport React from 'react';\n\nfunction Greeting() {\n return (\n <div>\n <h1>Hello, React!</h1>\n </div>\n );\n}\n\nexport default Greeting;\n```\n\n## Using the Greeting Component in App.js\n\nNow let's use the `Greeting` component we just made in our main `App.js` file. Replace the code in `App.js` with the following:\n\n```jsx\nimport React from 'react';\nimport Greeting from './Greeting';\n\nfunction App() {\n return (\n <div className='App'>\n <Greeting />\n </div>\n );\n}\n\nexport default App;\n```\n\nNow, if you run `npm start` in your project directory, you should see the greeting \"Hello, React!\" on your browser. This is your first React component in action!\n\n## Conclusion\n\nThis is just the beginning of your journey with React! There are many more concepts to explore such as props, state, lifecycles, hooks, and more. Keep experimenting, building, and learning! React documentation and the community are great resources to help you along your path.\n\nHappy coding!";
export const tutorialData = [
{
id: 1,
language: "react",
tutorial: [
{
id: 1,
content:
"Welcome to the React documentation! This page will give you an introduction to the 80% of React concepts that you will use on a daily basis.\r\n" +
"\r\n" +
"- How to create and nest components\r\n" +
"- How to add markup and styles\r\n" +
"- How to display data\r\n" +
"- How to render conditions and lists\r\n" +
"- How to respond to events and update the screen\r\n" +
"- How to share data between components\r\n",
code: `
function Welcome() {
return (
<button>
Welcome
</button>
);
}
`,
},
{
id: 2,
content:
"## Creating and nesting components\r\n" +
"\r\n" +
"React apps are made out of _components_. A component is a piece of the UI (user interface) that has its own logic and appearance. A component can be as small as a button, or as large as an entire page.\r\n" +
"\r\n" +
"React components are JavaScript functions that return markup:\r\n" +
"\r\n" +
"```js\r\n" +
"function MyButton() {\r\n" +
" return <button>I'm a button</button>;\r\n" +
"}\r\n" +
"```\r\n" +
"\r\n" +
"Now that you've declared `MyButton`, you can nest it into another component:\r\n" +
"\r\n" +
"```js {5}\r\n" +
"export default function MyApp() {\r\n" +
" return (\r\n" +
" <div>\r\n" +
" <h1>Welcome to my app</h1>\r\n" +
" <MyButton />\r\n" +
" </div>\r\n" +
" );\r\n" +
"}\r\n" +
"```\r\n" +
"\r\n" +
"Notice that `<MyButton />` starts with a capital letter. That's how you know it's a React component. React component names must always start with a capital letter, while HTML tags must be lowercase.\r\n" +
"\r\n" +
"**_Have a look at the result(in the right side code editor)_**\r\n" +
"\r\n" +
"The `export default` keywords specify the main component in the file. If you're not familiar with some piece of JavaScript syntax, [MDN](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export) and [javascript.info](https://javascript.info/import-export) have great references.\r\n",
code: `
import * as React from 'react'; //don't change this line
function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
function MyButton() {
return (
<button>
I'm a button
</button>
);
}
`,
},
{
id: 3,
content:
"## Writing markup with JSX\r\n" +
"\r\n" +
"The markup syntax you've seen above is called *JSX*. It is optional, but most React projects use JSX for its convenience. All of the [tools we recommend for local development](/learn/installation) support JSX out of the box.\r\n" +
"\r\n" +
"JSX is stricter than HTML. You have to close tags like `<br />`. Your component also can't return multiple JSX tags. You have to wrap them into a shared parent, like a `<div>...</div>` or an empty `<>...</>` wrapper:\r\n" +
"\r\n" +
"```js\r\n" +
"function AboutPage() {\r\n" +
" return (\r\n" +
" <>\r\n" +
" <h1>About</h1>\r\n" +
" <p>Hello there.<br />How do you do?</p>\r\n" +
" </>\r\n" +
" );\r\n" +
"}\r\n" +
"```\r\n" +
"\r\n" +
"If you have a lot of HTML to port to JSX, you can use an [online converter.](https://transform.tools/html-to-jsx)",
code: `
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}
`,
},
],
},
{
id: 2,
language: "javascript",
tutorial: [
{
id: 1,
content: "# Welcome to `javascript` tutorial",
code: `\nfunction greet(name) {\n\tconsole.log("Hello, " + name + "!");\n}\n\ngreet("Mehedi");\n`,
},
],
},
{
id: 3,
language: "typescript",
tutorial: [
{
id: 1,
content: "# Welcome to `typescript` tutorial",
code: `\ntype Params = {\n\tname: string;\n}\n\nfunction greet(data: Params) {\n\tconsole.log("Hello, " + data.name + "!");\n}\n\ngreet({ name: "Mehedi" });\n`,
},
],
},
{
id: 4,
language: "python",
tutorial: [
{
id: 1,
content: "# Welcome to `python` tutorial",
code: `\ndef greet(name):\n\tprint("Hello, " + name + "!")\n\ngreet("Mehedi")\n`,
},
],
},
{
id: 5,
language: "java",
tutorial: [
{
id: 1,
content: "# Welcome to `java` tutorial",
code: `\npublic class HelloWorld {\n\tpublic static void main(String[] args) {\n\t\tSystem.out.println("Hello World");\n\t}\n}\n`,
},
],
},
{
id: 6,
language: "csharp",
tutorial: [
{
id: 1,
content: "# Welcome to `csharp` tutorial",
code: 'using System;\n\nnamespace HelloWorld\n{\n\tclass Hello { \n\t\tstatic void Main(string[] args) {\n\t\t\tConsole.WriteLine("Hello World in C#");\n\t\t}\n\t}\n}\n',
},
],
},
{
id: 7,
language: "php",
tutorial: [
{
id: 1,
content: "# Welcome to `php` tutorial",
code: "<?php\n\n$name = 'Mehedi';\necho $name;\n",
},
],
},
];

View File

@ -0,0 +1,28 @@
import { ReactElement, useCallback, useState } from "react";
export default function useMultistepForm(steps: ReactElement[]) {
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const next = useCallback(() => {
setCurrentStepIndex((i) => Math.min(i + 1, steps.length - 1));
}, [steps.length]);
const back = useCallback(() => {
setCurrentStepIndex((i) => Math.max(i - 1, 0));
}, []);
const goTo = useCallback((index: number) => {
setCurrentStepIndex(index);
}, []);
return {
currentStepIndex,
step: steps[currentStepIndex],
steps,
isFirstStep: currentStepIndex === 0,
isLastStep: currentStepIndex === steps.length - 1,
goTo,
next,
back,
};
}

20
src/lib/api.ts Normal file
View File

@ -0,0 +1,20 @@
import axios from "axios";
import { LANGUAGE_VERSIONS } from "../constants/language-options";
const API = axios.create({
baseURL: "https://emkc.org/api/v2/piston",
});
export const executeCode = async (language: string, sourceCode: string) => {
const response = await API.post("/execute", {
language: language,
// @ts-ignore
version: LANGUAGE_VERSIONS[language],
files: [
{
content: sourceCode,
},
],
});
return response.data;
};

39
src/lib/form-schema.ts Normal file
View File

@ -0,0 +1,39 @@
import * as z from "zod";
export const profileSchema = z.object({
firstname: z
.string()
.min(3, { message: "Product Name must be at least 3 characters" }),
lastname: z
.string()
.min(3, { message: "Product Name must be at least 3 characters" }),
email: z
.string()
.email({ message: "Product Name must be at least 3 characters" }),
contactno: z.coerce.number(),
country: z.string().min(1, { message: "Please select a category" }),
city: z.string().min(1, { message: "Please select a category" }),
// jobs array is for the dynamic fields
jobs: z.array(
z.object({
jobcountry: z.string().min(1, { message: "Please select a category" }),
jobcity: z.string().min(1, { message: "Please select a category" }),
jobtitle: z
.string()
.min(3, { message: "Product Name must be at least 3 characters" }),
employer: z
.string()
.min(3, { message: "Product Name must be at least 3 characters" }),
startdate: z
.string()
.refine((value) => /^\d{4}-\d{2}-\d{2}$/.test(value), {
message: "Start date should be in the format YYYY-MM-DD",
}),
enddate: z.string().refine((value) => /^\d{4}-\d{2}-\d{2}$/.test(value), {
message: "End date should be in the format YYYY-MM-DD",
}),
}),
),
});
export type ProfileFormValues = z.infer<typeof profileSchema>;

View File

@ -0,0 +1,7 @@
import { format, isThisYear } from "date-fns"
export const formatShortDate = (date: string) => {
const _date = new Date(date)
return isThisYear(_date) ? format(_date, "MMM d") : format(_date, "MMM d, y")
}

View File

@ -0,0 +1,123 @@
import { type Options } from "rehype-pretty-code"
import { visit } from "unist-util-visit"
// div.BLOCK > pre.PRE > code.CODE
const BLOCK =
"overflow-hidden rounded-lg bg-rose-100/5 shadow-surface-elevation-low ring-1 ring-rose-100/[3%] ring-inset"
const TITLE =
"mb-0.5 rounded-md bg-rose-100/10 px-3 py-1 font-mono text-xs text-rose-100/70 shadow-sm"
const PRE = "overflow-x-auto py-2 text-[13px] leading-6 [color-scheme:dark]"
const CODE =
"grid [&>span]:border-l-4 [&>span]:border-l-transparent [&>span]:pl-2 [&>span]:pr-3"
const INLINE_BLOCK =
"whitespace-nowrap border border-rose-200/10 px-1.5 py-px text-[12px] rounded-full bg-white/5 whitespace-nowrap text-rose-300/90"
const INLINE_CODE = ""
const NUMBERED_LINES =
"[counter-reset:line] before:[&>span]:mr-3 before:[&>span]:inline-block before:[&>span]:w-4 before:[&>span]:text-right before:[&>span]:text-white/20 before:[&>span]:![content:counter(line)] before:[&>span]:[counter-increment:line]"
const HIGHLIGHTED_LINE =
"!border-l-rose-300/70 bg-rose-200/10 before:!text-white/70"
export function rehypePrettyCodeClasses() {
return (tree: any) => {
visit(
tree,
(node: any) =>
Boolean(
node.tagName === "code" &&
Object.keys(node.properties).length === 0 &&
node.children.some((n: any) => n.type === "text"),
),
(node: any) => {
const textNode = node.children.find((n: any) => n.type === "text")
textNode.type = "element"
textNode.tagName = "code"
textNode.properties = { className: [INLINE_CODE] }
textNode.children = [{ type: "text", value: textNode.value }]
node.properties.className = [INLINE_BLOCK]
node.tagName = "span"
},
)
visit(
tree,
(node: any) =>
Boolean(
typeof node?.properties?.["data-rehype-pretty-code-fragment"] !==
"undefined",
),
(node: any) => {
if (node.tagName === "span") {
node.properties.className = [
...(node.properties.className || []),
INLINE_BLOCK,
]
node.children[0].properties.className = [
...(node.children[0].properties.className || []),
INLINE_CODE,
]
return node
}
if (node.tagName === "div") {
node.properties.className = [
...(node.properties.className || []),
BLOCK,
]
node.children = node.children.map((node: any) => {
if (
node.tagName === "div" &&
typeof node.properties?.["data-rehype-pretty-code-title"] !==
"undefined"
) {
node.properties.className = [
...(node.properties.className || []),
TITLE,
]
}
if (node.tagName === "pre") {
node.properties.className = [PRE]
if (node.children[0].tagName === "code") {
node.children[0].properties.className = [
...(node.children[0].properties.className || []),
CODE,
]
if (
typeof node.children[0].properties["data-line-numbers"] !==
"undefined"
) {
node.children[0].properties.className.push(NUMBERED_LINES)
}
}
}
return node
})
return node
}
},
)
}
}
export const rehypePrettyCodeOptions: Partial<Options> = {
theme: "one-dark-pro",
tokensMap: {
// VScode command palette: Inspect Editor Tokens and Scopes
// https://github.com/Binaryify/OneDark-Pro/blob/47c66a2f2d3e5c85490e1aaad96f5fab3293b091/themes/OneDark-Pro.json
fn: "entity.name.function",
objKey: "meta.object-literal.key",
},
onVisitLine(node) {
// Prevent lines from collapsing in `display: grid` mode, and
// allow empty lines to be copy/pasted
if (node.children.length === 0) {
node.children = [{ type: "text", value: " " }]
}
node.properties.className = [""]
},
onVisitHighlightedLine(node) {
node.properties.className?.push(HIGHLIGHTED_LINE)
},
}

View File

@ -0,0 +1,21 @@
"use client";
import { type PropsWithChildren, useRef } from "react";
import type { StoreInterface, StoreType } from "./quiz-store";
import { initializeStore, Provider } from "./quiz-store";
export interface PreloadedStoreInterface
extends Pick<StoreInterface, "lastUpdate"> {}
export default function StoreProvider({
children,
...props
}: PropsWithChildren<PreloadedStoreInterface>) {
const storeRef = useRef<StoreType>();
if (!storeRef.current) {
storeRef.current = initializeStore(props);
}
return <Provider value={storeRef.current}>{children}</Provider>;
}

23
src/lib/useInterval.ts Normal file
View File

@ -0,0 +1,23 @@
"use client";
import { useEffect, useRef } from "react";
// https://overreacted.io/making-setinterval-declarative-with-react-hooks/
export default function useInterval(
callback: () => void,
delay: number | undefined,
) {
const savedCallback = useRef<typeof callback>();
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
const handler = () => savedCallback.current?.();
if (delay !== null) {
const id = setInterval(handler, delay);
return () => clearInterval(id);
}
}, [delay]);
}

6
src/lib/utils.ts Normal file
View File

@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

20
src/middleware.ts Normal file
View File

@ -0,0 +1,20 @@
import { type NextRequest } from "next/server";
import { updateSession } from "@/utils/supabase/middleware";
export async function middleware(request: NextRequest) {
return await updateSession(request);
}
export const config = {
matcher: [
/*
* Match all request paths except:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - images - .svg, .png, .jpg, .jpeg, .gif, .webp
* Feel free to modify this pattern to include more paths.
*/
"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
],
};

View File

@ -0,0 +1,55 @@
.markdown h1 {
@apply my-3 text-xl font-medium sm:text-3xl;
}
.markdown h2 {
@apply my-3 text-xl font-medium sm:text-2xl;
}
.markdown h3 {
@apply text-xl font-medium my-3;
}
.markdown h4 {
@apply text-lg font-medium my-3;
}
.markdown h5 {
@apply text-lg font-medium my-3;
}
.markdown h6 {
@apply text-base font-medium my-3;
}
.markdown p {
@apply my-4
}
.markdown a {
@apply underline;
}
.markdown ul {
@apply space-y-3 [li>&]:mt-3 [&>li]:relative [&>li]:pl-7 before:[&>li]:absolute before:[&>li]:left-1 before:[&>li]:top-3 before:[&>li]:h-1.5 before:[&>li]:w-1.5 before:[&>li]:rounded-full before:[&>li]:bg-secondary my-4 ;
}
.markdown ol {
@apply list-decimal space-y-3 pl-10 mt-4;
}
.markdown strong {
@apply font-semibold;
}
.markdown blockquote {
@apply border-l-2 border-secondary text-xl italic xl:!col-start-2 xl:!col-end-3;
}
.markdown del {
@apply text-rose-100/70 line-through;
}
.markdown pre {
@apply p-4 rounded-md my-4;
}

32
src/types/index.ts Normal file
View File

@ -0,0 +1,32 @@
import { Icons } from "@/components/icons";
export interface NavItem {
title: string;
href?: string;
disabled?: boolean;
external?: boolean;
icon?: keyof typeof Icons;
label?: string;
description?: string;
}
export interface NavItemWithChildren extends NavItem {
items: NavItemWithChildren[];
}
export interface NavItemWithOptionalChildren extends NavItem {
items?: NavItemWithChildren[];
}
export interface FooterItem {
title: string;
items: {
title: string;
href: string;
external?: boolean;
}[];
}
export type MainNavItem = NavItemWithOptionalChildren;
export type SidebarNavItem = NavItemWithChildren;

20
src/utils/define-theme.ts Normal file
View File

@ -0,0 +1,20 @@
import { loader } from "@monaco-editor/react";
import { monacoThemes } from "../constants/monaco-themes";
const defineTheme = (theme: string) => {
return new Promise((res) => {
Promise.all([
loader.init(),
// @ts-ignore
import(`monaco-themes/themes/${monacoThemes[theme]}.json`),
]).then(([monaco, themeData]) => {
monaco.editor.defineTheme(theme, themeData);
// @ts-ignore
res();
});
});
};
export { defineTheme };

View File

@ -0,0 +1,7 @@
import { createBrowserClient } from "@supabase/ssr";
export const createClient = () =>
createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);

View File

@ -0,0 +1,78 @@
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { type NextRequest, NextResponse } from "next/server";
export const updateSession = async (request: NextRequest) => {
// This `try/catch` block is only here for the interactive tutorial.
// Feel free to remove once you have Supabase connected.
try {
// Create an unmodified response
let response = NextResponse.next({
request: {
headers: request.headers,
},
});
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return request.cookies.get(name)?.value;
},
set(name: string, value: string, options: CookieOptions) {
// If the cookie is updated, update the cookies for the request and response
request.cookies.set({
name,
value,
...options,
});
response = NextResponse.next({
request: {
headers: request.headers,
},
});
response.cookies.set({
name,
value,
...options,
});
},
remove(name: string, options: CookieOptions) {
// If the cookie is removed, update the cookies for the request and response
request.cookies.set({
name,
value: "",
...options,
});
response = NextResponse.next({
request: {
headers: request.headers,
},
});
response.cookies.set({
name,
value: "",
...options,
});
},
},
},
);
// This will refresh session if expired - required for Server Components
// https://supabase.com/docs/guides/auth/server-side/nextjs
await supabase.auth.getUser();
return response;
} catch (e) {
// If you are here, a Supabase client could not be created!
// This is likely because you have not set up environment variables.
// Check out http://localhost:3000 for Next Steps.
return NextResponse.next({
request: {
headers: request.headers,
},
});
}
};

View File

@ -0,0 +1,36 @@
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { cookies } from "next/headers";
export const createClient = () => {
const cookieStore = cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return cookieStore.get(name)?.value;
},
set(name: string, value: string, options: CookieOptions) {
try {
cookieStore.set({ name, value, ...options });
} catch (error) {
// The `set` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
remove(name: string, options: CookieOptions) {
try {
cookieStore.set({ name, value: "", ...options });
} catch (error) {
// The `delete` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
},
},
);
};

77
tailwind.config.js Normal file
View File

@ -0,0 +1,77 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./constants/**/*.{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")],
};

27
tsconfig.json Normal file
View File

@ -0,0 +1,27 @@
{
"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"]
}