feat: sidebar, mobile sidebar, ui components
This commit is contained in:
parent
00d85806fe
commit
48d007f079
|
@ -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
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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 =
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
images: {
|
||||
domains: ["utfs.io"],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
|
@ -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 |
|
@ -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 |
|
@ -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;
|
|
@ -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,
|
||||
});
|
|
@ -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`);
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -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;
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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,
|
||||
};
|
|
@ -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>;
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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 };
|
|
@ -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,
|
||||
};
|
|
@ -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 };
|
|
@ -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 };
|
|
@ -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 };
|
|
@ -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 };
|
|
@ -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 };
|
|
@ -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,
|
||||
};
|
|
@ -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 };
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -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,
|
||||
};
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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 };
|
|
@ -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 }
|
|
@ -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 }
|
|
@ -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 };
|
|
@ -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,
|
||||
};
|
|
@ -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 };
|
|
@ -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,
|
||||
};
|
|
@ -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 };
|
|
@ -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 };
|
|
@ -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 };
|
|
@ -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,
|
||||
};
|
|
@ -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>
|
||||
);
|
||||
}`,
|
||||
};
|
||||
|
|
@ -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,
|
||||
},
|
||||
];
|
|
@ -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",
|
||||
// },
|
||||
// ];
|
||||
|
|
@ -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",
|
||||
};
|
||||
|
|
@ -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",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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>;
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
},
|
||||
}
|
|
@ -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>;
|
||||
}
|
|
@ -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]);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
|
@ -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)$).*)",
|
||||
],
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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 };
|
|
@ -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!,
|
||||
);
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
|
@ -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.
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
|
@ -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")],
|
||||
};
|
|
@ -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"]
|
||||
}
|
Loading…
Reference in New Issue