diff --git a/apps/admin/package.json b/apps/admin/package.json
index b40fc70..e22a181 100644
--- a/apps/admin/package.json
+++ b/apps/admin/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev",
+ "dev": "next dev --port 3001",
"build": "next build",
"start": "next start",
"lint": "next lint"
diff --git a/apps/application/package.json b/apps/application/package.json
index bff1bd4..2617038 100644
--- a/apps/application/package.json
+++ b/apps/application/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": " next dev",
+ "dev": " next dev --port 3002",
"build": "next build",
"start": "next start",
"lint": "next lint",
diff --git a/apps/auth/app/(auth)/layout.tsx b/apps/auth/app/(auth)/layout.tsx
new file mode 100644
index 0000000..5b2a7f8
--- /dev/null
+++ b/apps/auth/app/(auth)/layout.tsx
@@ -0,0 +1,16 @@
+import { Metadata } from "next";
+import { ReactNode } from "react";
+
+export const metadata: Metadata = {
+ title: "Login | Saroh",
+ description:
+ "Login to saroh. A platform for managing your portfolios, marketing websites and more.",
+};
+
+export default function AuthLayout({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/auth/app/(auth)/login/github-login-button.tsx b/apps/auth/app/(auth)/login/github-login-button.tsx
new file mode 100644
index 0000000..e292f74
--- /dev/null
+++ b/apps/auth/app/(auth)/login/github-login-button.tsx
@@ -0,0 +1,54 @@
+"use client";
+
+import { signIn } from "next-auth/react";
+import { useSearchParams } from "next/navigation";
+import { useEffect, useState } from "react";
+import LoadingDots from "./loading-dots";
+// import { toast } from "sonner";
+
+export default function GithubLoginButton() {
+ const [loading, setLoading] = useState(false);
+
+ // Get error message added by next/auth in URL.
+ const searchParams = useSearchParams();
+ const error = searchParams?.get("error");
+
+ useEffect(() => {
+ const errorMessage = Array.isArray(error) ? error.pop() : error;
+ errorMessage;
+ // && toast.error(errorMessage);
+ }, [error]);
+
+ return (
+
+ );
+}
diff --git a/apps/auth/app/(auth)/login/google-login-button.tsx b/apps/auth/app/(auth)/login/google-login-button.tsx
new file mode 100644
index 0000000..064de2d
--- /dev/null
+++ b/apps/auth/app/(auth)/login/google-login-button.tsx
@@ -0,0 +1,47 @@
+"use client";
+
+import { signIn } from "next-auth/react";
+import { useSearchParams } from "next/navigation";
+import { useEffect, useState } from "react";
+import { SiGoogle } from "react-icons/si";
+import LoadingDots from "./loading-dots";
+// import { toast } from "sonner";
+export default function GoogleLoginButton() {
+ const [loading, setLoading] = useState(false);
+
+ // Get error message added by next/auth in URL.
+ const searchParams = useSearchParams();
+ const error = searchParams?.get("error");
+
+ useEffect(() => {
+ const errorMessage = Array.isArray(error) ? error.pop() : error;
+ errorMessage;
+ // && toast.error(errorMessage);
+ }, [error]);
+
+ return (
+
+ );
+}
diff --git a/apps/auth/app/(auth)/login/loading-dots.module.css b/apps/auth/app/(auth)/login/loading-dots.module.css
new file mode 100644
index 0000000..3b63902
--- /dev/null
+++ b/apps/auth/app/(auth)/login/loading-dots.module.css
@@ -0,0 +1,40 @@
+.loading {
+ display: inline-flex;
+ align-items: center;
+}
+
+.loading .spacer {
+ margin-right: 2px;
+}
+
+.loading span {
+ animation-name: blink;
+ animation-duration: 1.4s;
+ animation-iteration-count: infinite;
+ animation-fill-mode: both;
+ width: 5px;
+ height: 5px;
+ border-radius: 50%;
+ display: inline-block;
+ margin: 0 1px;
+}
+
+.loading span:nth-of-type(2) {
+ animation-delay: 0.2s;
+}
+
+.loading span:nth-of-type(3) {
+ animation-delay: 0.4s;
+}
+
+@keyframes blink {
+ 0% {
+ opacity: 0.2;
+ }
+ 20% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0.2;
+ }
+}
diff --git a/apps/auth/app/(auth)/login/loading-dots.tsx b/apps/auth/app/(auth)/login/loading-dots.tsx
new file mode 100644
index 0000000..b3ab26b
--- /dev/null
+++ b/apps/auth/app/(auth)/login/loading-dots.tsx
@@ -0,0 +1,17 @@
+import styles from "./loading-dots.module.css";
+
+interface LoadingDotsProps {
+ color?: string;
+}
+
+const LoadingDots = ({ color = "#000" }: LoadingDotsProps) => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default LoadingDots;
diff --git a/apps/auth/app/(auth)/login/page.tsx b/apps/auth/app/(auth)/login/page.tsx
new file mode 100644
index 0000000..5d6f658
--- /dev/null
+++ b/apps/auth/app/(auth)/login/page.tsx
@@ -0,0 +1,27 @@
+"use client";
+import { LoginForm } from "@/components/auth/login-form";
+
+export default function LoginPage() {
+ return ;
+ // return (
+ //
+ //
+ // Saroh
+ //
+ //
+ // Build your storefronts, portfolio and blog websites with
+ // saroh.io.
+ //
+
+ //
+ //
+ //
+ //
+ // );
+}
diff --git a/apps/auth/app/api/auth/[...nextauth]/route.ts b/apps/auth/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 0000000..73228a0
--- /dev/null
+++ b/apps/auth/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1,2 @@
+import { handlers } from "@/lib/auth";
+export const { GET, POST } = handlers;
diff --git a/apps/auth/app/apps/page.tsx b/apps/auth/app/apps/page.tsx
new file mode 100644
index 0000000..f3c108d
--- /dev/null
+++ b/apps/auth/app/apps/page.tsx
@@ -0,0 +1,12 @@
+import Link from "next/link";
+
+export default function AppsListPage() {
+ return (
+
+ Select the app to open
+
+ Dashboard
+
+
+ );
+}
diff --git a/apps/auth/app/layout.tsx b/apps/auth/app/layout.tsx
index 2918f93..a914cc0 100644
--- a/apps/auth/app/layout.tsx
+++ b/apps/auth/app/layout.tsx
@@ -1,12 +1,14 @@
+import "@saroh/ui/globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
-import "./globals.css";
+
+import Providers from "./providers";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
+ title: "Saroh Auth",
+ description: "login to your saroh account.",
};
export default function RootLayout({
@@ -16,7 +18,9 @@ export default function RootLayout({
}>) {
return (
- {children}
+
+ {children}
+
);
}
diff --git a/apps/auth/app/providers.tsx b/apps/auth/app/providers.tsx
new file mode 100644
index 0000000..20db51f
--- /dev/null
+++ b/apps/auth/app/providers.tsx
@@ -0,0 +1,7 @@
+"use client";
+import { SessionProvider } from "next-auth/react";
+import React from "react";
+
+export default function Providers({ children }: { children: React.ReactNode }) {
+ return {children};
+}
diff --git a/apps/auth/components/auth/login-form.tsx b/apps/auth/components/auth/login-form.tsx
new file mode 100644
index 0000000..e296f09
--- /dev/null
+++ b/apps/auth/components/auth/login-form.tsx
@@ -0,0 +1,67 @@
+"use client";
+import { Button } from "@saroh/ui/button";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@saroh/ui/card";
+import { Input } from "@saroh/ui/input";
+import { Label } from "@saroh/ui/label";
+import { signIn } from "next-auth/react";
+import Link from "next/link";
+
+export function LoginForm() {
+ return (
+
+
+ Login
+
+ Enter your email below to login to your account
+
+
+
+
+
+
+
+
+
+
+
+
+ Forgot your password?
+
+
+
+
+
+
+
+
+ Don't have an account?{" "}
+
+ Sign up
+
+
+
+
+ );
+}
diff --git a/apps/auth/lib/auth.config.ts b/apps/auth/lib/auth.config.ts
new file mode 100644
index 0000000..28d7d06
--- /dev/null
+++ b/apps/auth/lib/auth.config.ts
@@ -0,0 +1,4 @@
+import type { NextAuthConfig } from "next-auth";
+import GitHub from "next-auth/providers/github";
+
+export default { providers: [GitHub] } satisfies NextAuthConfig;
diff --git a/apps/auth/lib/auth.ts b/apps/auth/lib/auth.ts
new file mode 100644
index 0000000..f9fd029
--- /dev/null
+++ b/apps/auth/lib/auth.ts
@@ -0,0 +1,12 @@
+import prisma from "@/lib/prisma";
+import { PrismaAdapter } from "@auth/prisma-adapter";
+import NextAuth from "next-auth";
+import authConfig from "./auth.config";
+const VERCEL_DEPLOYMENT = !!process.env.VERCEL_URL;
+
+export const { auth, handlers, signIn, signOut } = NextAuth({
+ // providers: [GitHub, Google],
+ adapter: PrismaAdapter(prisma),
+ session: { strategy: "jwt" },
+ ...authConfig,
+});
diff --git a/apps/auth/lib/prisma.ts b/apps/auth/lib/prisma.ts
new file mode 100644
index 0000000..25a9564
--- /dev/null
+++ b/apps/auth/lib/prisma.ts
@@ -0,0 +1,11 @@
+// import { PrismaClient } from "@prisma/client";
+import { prisma } from "@saroh/database";
+// declare global {
+// var prisma: PrismaClient | undefined;
+// }
+
+// const prisma = global.prisma || new PrismaClient();
+
+// if (process.env.NODE_ENV === "development") global.prisma = prisma;
+
+export default prisma;
diff --git a/apps/auth/middleware.ts b/apps/auth/middleware.ts
new file mode 100644
index 0000000..80b5ff6
--- /dev/null
+++ b/apps/auth/middleware.ts
@@ -0,0 +1,52 @@
+import NextAuth from "next-auth";
+import { NextResponse } from "next/server";
+import authConfig from "./lib/auth.config";
+
+const { auth } = NextAuth(authConfig);
+export default auth(async function (req) {
+ // Your custom middleware logic goes here
+ const { nextUrl } = req;
+ const isLoggedIn = !!req.auth;
+ const protectedRoutes = ["/", "/apps"];
+ console.log(nextUrl.pathname);
+ const path = nextUrl.pathname;
+
+ const authRoutes = ["/login"];
+ const isProtectedRoute = protectedRoutes.includes(nextUrl.pathname);
+ const isAuthRoute = authRoutes.includes(nextUrl.pathname);
+ if (isAuthRoute) {
+ if (isLoggedIn) {
+ return NextResponse.redirect(new URL("/apps", nextUrl));
+ }
+ return;
+ }
+ if (isProtectedRoute) {
+ if (isLoggedIn) {
+ if (path === "/apps") {
+ return;
+ }
+ return NextResponse.redirect(new URL("/apps", nextUrl));
+ }
+ if (!isLoggedIn) {
+ return NextResponse.redirect(new URL("/login", nextUrl));
+ }
+ return;
+ }
+
+ if (!isLoggedIn) {
+ return NextResponse.redirect(new URL("/login", nextUrl));
+ }
+ return;
+});
+export const config = {
+ matcher: [
+ /*
+ * Match all paths except for:
+ * 1. /api routes
+ * 2. /_next (Next.js internals)
+ * 3. /_static (inside /public)
+ * 4. all root files inside /public (e.g. /favicon.ico)
+ */
+ "/((?!api/|_next/|_static/|_vercel|[\\w-]+\\.\\w+).*)",
+ ],
+};
diff --git a/apps/auth/next-auth.d.ts b/apps/auth/next-auth.d.ts
new file mode 100644
index 0000000..072cca5
--- /dev/null
+++ b/apps/auth/next-auth.d.ts
@@ -0,0 +1,13 @@
+import { type DefaultSession } from "next-auth";
+
+export type ExtendedUser = DefaultSession["user"] & {
+ // role: UserRole;
+ isTwoFactorEnabled: boolean;
+ isOAuth: boolean;
+};
+
+declare module "next-auth" {
+ interface Session {
+ user: ExtendedUser;
+ }
+}
diff --git a/apps/auth/next.config.mjs b/apps/auth/next.config.mjs
index 4678774..5c91d05 100644
--- a/apps/auth/next.config.mjs
+++ b/apps/auth/next.config.mjs
@@ -1,4 +1,13 @@
/** @type {import('next').NextConfig} */
-const nextConfig = {};
+import { PrismaPlugin } from '@prisma/nextjs-monorepo-workaround-plugin';
+const nextConfig = {
+ webpack: (config, { isServer }) => {
+ if (isServer) {
+ config.plugins = [...config.plugins, new PrismaPlugin()];
+ }
+
+ return config;
+ },
+};
export default nextConfig;
diff --git a/apps/auth/package.json b/apps/auth/package.json
index 034446d..babb865 100644
--- a/apps/auth/package.json
+++ b/apps/auth/package.json
@@ -1,26 +1,36 @@
{
- "name": "auth",
- "version": "0.1.0",
- "private": true,
- "scripts": {
- "dev": "next dev",
- "build": "next build",
- "start": "next start",
- "lint": "next lint"
- },
- "dependencies": {
- "react": "^18",
- "react-dom": "^18",
- "next": "14.2.3"
- },
- "devDependencies": {
- "typescript": "^5",
- "@types/node": "^20",
- "@types/react": "^18",
- "@types/react-dom": "^18",
- "postcss": "^8",
- "tailwindcss": "^3.4.1",
- "eslint": "^8",
- "eslint-config-next": "14.2.3"
- }
+ "name": "auth",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev && prisma generate",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint",
+ "postinstall": "prisma generate"
+ },
+ "dependencies": {
+ "@auth/prisma-adapter": "^2.2.0",
+ "@prisma/nextjs-monorepo-workaround-plugin": "^5.14.0",
+ "next": "14.2.3",
+ "next-auth": "5.0.0-beta.19",
+ "react": "^18",
+ "react-dom": "^18",
+ "react-icons": "^5.2.1",
+ "@saroh/ui": "workspace:*"
+ },
+ "devDependencies": {
+ "@saroh/database": "workspace:*",
+ "@saroh/tailwind-config": "workspace:*",
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "autoprefixer": "^10.4.19",
+ "prisma": "^5.14.0",
+ "eslint": "^8",
+ "eslint-config-next": "14.2.3",
+ "postcss": "^8",
+ "tailwindcss": "^3.4.1",
+ "typescript": "^5"
+ }
}
diff --git a/apps/auth/postcss.config.mjs b/apps/auth/postcss.config.mjs
index 1a69fd2..88ac031 100644
--- a/apps/auth/postcss.config.mjs
+++ b/apps/auth/postcss.config.mjs
@@ -1,8 +1,9 @@
/** @type {import('postcss-load-config').Config} */
const config = {
- plugins: {
- tailwindcss: {},
- },
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
};
export default config;
diff --git a/apps/auth/tailwind.config.ts b/apps/auth/tailwind.config.ts
index 86359dc..d0a055b 100644
--- a/apps/auth/tailwind.config.ts
+++ b/apps/auth/tailwind.config.ts
@@ -1,20 +1 @@
-import type { Config } from "tailwindcss";
-
-const config: Config = {
- content: [
- "./pages/**/*.{js,ts,jsx,tsx,mdx}",
- "./components/**/*.{js,ts,jsx,tsx,mdx}",
- "./app/**/*.{js,ts,jsx,tsx,mdx}",
- ],
- theme: {
- extend: {
- backgroundImage: {
- "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
- "gradient-conic":
- "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
- },
- },
- },
- plugins: [],
-};
-export default config;
+export * from "@saroh/tailwind-config/tailwind.config";
diff --git a/apps/auth/tsconfig.json b/apps/auth/tsconfig.json
index e7ff90f..1fce257 100644
--- a/apps/auth/tsconfig.json
+++ b/apps/auth/tsconfig.json
@@ -1,26 +1,26 @@
{
- "compilerOptions": {
- "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": {
- "@/*": ["./*"]
- }
- },
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
+ "compilerOptions": {
+ "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": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
}
diff --git a/apps/chatbot/app/layout.tsx b/apps/chatbot/app/layout.tsx
index 2918f93..3970456 100644
--- a/apps/chatbot/app/layout.tsx
+++ b/apps/chatbot/app/layout.tsx
@@ -5,7 +5,7 @@ import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
- title: "Create Next App",
+ title: "Saroh chatbot",
description: "Generated by create next app",
};
diff --git a/apps/chatbot/package.json b/apps/chatbot/package.json
index 815a899..1c0ea01 100644
--- a/apps/chatbot/package.json
+++ b/apps/chatbot/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev",
+ "dev": "next dev --port 3003",
"build": "next build",
"start": "next start",
"lint": "next lint"
diff --git a/apps/dashboard/app/[teamId]/[projectId]/page.tsx b/apps/dashboard/app/[teamId]/[projectId]/page.tsx
new file mode 100644
index 0000000..326bbbc
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/[projectId]/page.tsx
@@ -0,0 +1,3 @@
+export default function ProjectHomePage() {
+ return ProjectHomePage
;
+}
diff --git a/apps/dashboard/app/[teamId]/[projectId]/settings/page.tsx b/apps/dashboard/app/[teamId]/[projectId]/settings/page.tsx
new file mode 100644
index 0000000..531035b
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/[projectId]/settings/page.tsx
@@ -0,0 +1,3 @@
+export default function ProjectSettingsPage() {
+ return ProjectSettingsPage
;
+}
diff --git a/apps/dashboard/app/[teamId]/layout.tsx b/apps/dashboard/app/[teamId]/layout.tsx
new file mode 100644
index 0000000..e31d7fb
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/layout.tsx
@@ -0,0 +1,7 @@
+export default function TeamRootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return {children}
;
+}
diff --git a/apps/dashboard/app/[teamId]/page.tsx b/apps/dashboard/app/[teamId]/page.tsx
new file mode 100644
index 0000000..a02b958
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/page.tsx
@@ -0,0 +1,10 @@
+import Dashboard from "@/components/dashboard";
+
+export default function TeamHomePage() {
+ return (
+
+ {" "}
+
+
+ );
+}
diff --git a/apps/dashboard/app/[teamId]/settings/account/page.tsx b/apps/dashboard/app/[teamId]/settings/account/page.tsx
new file mode 100644
index 0000000..ecacd29
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/settings/account/page.tsx
@@ -0,0 +1,18 @@
+import { AccountForm } from "@/components/settings/account/account-form";
+import { Separator } from "@saroh/ui/separator";
+
+export default function SettingsAccountPage() {
+ return (
+
+
+
Account
+
+ Update your account settings. Set your preferred language
+ and timezone.
+
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/app/[teamId]/settings/appearance/page.tsx b/apps/dashboard/app/[teamId]/settings/appearance/page.tsx
new file mode 100644
index 0000000..9598fd9
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/settings/appearance/page.tsx
@@ -0,0 +1,18 @@
+import { AppearanceForm } from "@/components/settings/appearance/appearance-form";
+import { Separator } from "@saroh/ui/separator";
+
+export default function SettingsAppearancePage() {
+ return (
+
+
+
Appearance
+
+ Customize the appearance of the app. Automatically switch
+ between day and night themes.
+
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/app/[teamId]/settings/display/page.tsx b/apps/dashboard/app/[teamId]/settings/display/page.tsx
new file mode 100644
index 0000000..61c7943
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/settings/display/page.tsx
@@ -0,0 +1,18 @@
+import { DisplayForm } from "@/components/settings/display/display-form";
+import { Separator } from "@saroh/ui/separator";
+
+export default function SettingsDisplayPage() {
+ return (
+
+
+
Display
+
+ Turn items on or off to control what's displayed in the
+ app.
+
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/app/[teamId]/settings/layout.tsx b/apps/dashboard/app/[teamId]/settings/layout.tsx
new file mode 100644
index 0000000..305640b
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/settings/layout.tsx
@@ -0,0 +1,95 @@
+import { Metadata } from "next";
+import Image from "next/image";
+
+import { MainNav } from "@/components/dashboard/main-nav";
+import { Search } from "@/components/dashboard/search";
+import TeamSwitcher from "@/components/dashboard/team-switcher";
+import { UserNav } from "@/components/dashboard/user-nav";
+import { SidebarNav } from "@/components/settings/sidebar-nav";
+import { Separator } from "@saroh/ui/separator";
+
+export const metadata: Metadata = {
+ title: "Forms",
+ description: "Advanced form example using react-hook-form and Zod.",
+};
+
+const sidebarNavItems = [
+ {
+ title: "Profile",
+ href: "/settings",
+ },
+ {
+ title: "Account",
+ href: "/settings/account",
+ },
+ {
+ title: "Appearance",
+ href: "/settings/appearance",
+ },
+ {
+ title: "Notifications",
+ href: "/settings/notifications",
+ },
+ {
+ title: "Display",
+ href: "/settings/display",
+ },
+];
+
+interface SettingsLayoutProps {
+ children: React.ReactNode;
+}
+
+export default function SettingsLayout({ children }: SettingsLayoutProps) {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ Settings
+
+
+ Manage your account settings and set e-mail
+ preferences.
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/dashboard/app/[teamId]/settings/notifications/page.tsx b/apps/dashboard/app/[teamId]/settings/notifications/page.tsx
new file mode 100644
index 0000000..33ee628
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/settings/notifications/page.tsx
@@ -0,0 +1,17 @@
+import { NotificationsForm } from "@/components/settings/notifications/notifications-form";
+import { Separator } from "@saroh/ui/separator";
+
+export default function SettingsNotificationsPage() {
+ return (
+
+
+
Notifications
+
+ Configure how you receive notifications.
+
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/app/[teamId]/settings/page.tsx b/apps/dashboard/app/[teamId]/settings/page.tsx
new file mode 100644
index 0000000..9495476
--- /dev/null
+++ b/apps/dashboard/app/[teamId]/settings/page.tsx
@@ -0,0 +1,17 @@
+import { ProfileForm } from "@/components/settings/profile-form";
+import { Separator } from "@saroh/ui/separator";
+
+export default function SettingsProfilePage() {
+ return (
+
+
+
Profile
+
+ This is how others will see you on the site.
+
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/app/api/auth/[...nextauth]/route.ts b/apps/dashboard/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 0000000..73228a0
--- /dev/null
+++ b/apps/dashboard/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1,2 @@
+import { handlers } from "@/lib/auth";
+export const { GET, POST } = handlers;
diff --git a/apps/dashboard/app/layout.tsx b/apps/dashboard/app/layout.tsx
index e401839..34a8e40 100644
--- a/apps/dashboard/app/layout.tsx
+++ b/apps/dashboard/app/layout.tsx
@@ -1,12 +1,12 @@
import "@saroh/ui/globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
-
+import Providers from "./providers";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
+ title: "Build your store with Saroh",
+ description: "Build ecommerce store with saroh",
};
export default function RootLayout({
@@ -16,7 +16,9 @@ export default function RootLayout({
}>) {
return (
- {children}
+
+ {children}
+
);
}
diff --git a/apps/dashboard/app/page.tsx b/apps/dashboard/app/page.tsx
index 71906ba..d6171df 100644
--- a/apps/dashboard/app/page.tsx
+++ b/apps/dashboard/app/page.tsx
@@ -1,9 +1,18 @@
-import { Button } from "@saroh/ui/components/ui/button";
-
-export default function Home() {
+import { auth } from "@/lib/auth";
+export default async function Home() {
+ const session = await auth();
return (
-
+ {/*
+
+
+ CN
+
+ {session?.user?.email}
+ */}
);
}
diff --git a/apps/dashboard/app/products/[id]/page.tsx b/apps/dashboard/app/products/[id]/page.tsx
new file mode 100644
index 0000000..774e7b8
--- /dev/null
+++ b/apps/dashboard/app/products/[id]/page.tsx
@@ -0,0 +1,3 @@
+export default function ProductsDetailsPage() {
+ return page
;
+}
diff --git a/apps/dashboard/app/products/page.tsx b/apps/dashboard/app/products/page.tsx
new file mode 100644
index 0000000..e824f60
--- /dev/null
+++ b/apps/dashboard/app/products/page.tsx
@@ -0,0 +1,750 @@
+import {
+ File,
+ Home,
+ LineChart,
+ ListFilter,
+ MoreHorizontal,
+ Package,
+ Package2,
+ PanelLeft,
+ PlusCircle,
+ Search,
+ Settings,
+ ShoppingCart,
+ Users2,
+} from "lucide-react";
+import Image from "next/image";
+import Link from "next/link";
+
+import { Badge } from "@saroh/ui/badge";
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@saroh/ui/breadcrumb";
+import { Button } from "@saroh/ui/button";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@saroh/ui/card";
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@saroh/ui/dropdown-menu";
+import { Input } from "@saroh/ui/input";
+import { Sheet, SheetContent, SheetTrigger } from "@saroh/ui/sheet";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@saroh/ui/table";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@saroh/ui/tabs";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@saroh/ui/tooltip";
+
+export default function ProductsPage() {
+ return (
+
+
+
+
+
+
+
+
+ All
+ Active
+ Draft
+
+ Archived
+
+
+
+
+
+
+
+
+
+ Filter by
+
+
+
+ Active
+
+
+ Draft
+
+
+ Archived
+
+
+
+
+
+
+
+
+
+
+ Products
+
+ Manage your products and view their
+ sales performance.
+
+
+
+
+
+
+
+
+ Image
+
+
+ Name
+ Status
+
+ Price
+
+
+ Total Sales
+
+
+ Created at
+
+
+
+ Actions
+
+
+
+
+
+
+
+
+
+
+ Laser Lemonade Machine
+
+
+
+ Draft
+
+
+
+ $499.99
+
+
+ 25
+
+
+ 2023-07-12 10:42 AM
+
+
+
+
+
+
+
+
+ Actions
+
+
+ Edit
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+
+ Hypernova Headphones
+
+
+
+ Active
+
+
+
+ $129.99
+
+
+ 100
+
+
+ 2023-10-18 03:21 PM
+
+
+
+
+
+
+
+
+ Actions
+
+
+ Edit
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+
+ AeroGlow Desk Lamp
+
+
+
+ Active
+
+
+
+ $39.99
+
+
+ 50
+
+
+ 2023-11-29 08:15 AM
+
+
+
+
+
+
+
+
+ Actions
+
+
+ Edit
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+
+ TechTonic Energy Drink
+
+
+
+ Draft
+
+
+
+ $2.99
+
+
+ 0
+
+
+ 2023-12-25 11:59 PM
+
+
+
+
+
+
+
+
+ Actions
+
+
+ Edit
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+
+ Gamer Gear Pro Controller
+
+
+
+ Active
+
+
+
+ $59.99
+
+
+ 75
+
+
+ 2024-01-01 12:00 AM
+
+
+
+
+
+
+
+
+ Actions
+
+
+ Edit
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+
+ Luminous VR Headset
+
+
+
+ Active
+
+
+
+ $199.99
+
+
+ 30
+
+
+ 2024-02-14 02:14 PM
+
+
+
+
+
+
+
+
+ Actions
+
+
+ Edit
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+
+ Showing 1-10 of{" "}
+ 32 products
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/app/providers.tsx b/apps/dashboard/app/providers.tsx
new file mode 100644
index 0000000..20db51f
--- /dev/null
+++ b/apps/dashboard/app/providers.tsx
@@ -0,0 +1,7 @@
+"use client";
+import { SessionProvider } from "next-auth/react";
+import React from "react";
+
+export default function Providers({ children }: { children: React.ReactNode }) {
+ return {children};
+}
diff --git a/apps/dashboard/components/dashboard/calendar-date-range-picker.tsx b/apps/dashboard/components/dashboard/calendar-date-range-picker.tsx
new file mode 100644
index 0000000..f515fee
--- /dev/null
+++ b/apps/dashboard/components/dashboard/calendar-date-range-picker.tsx
@@ -0,0 +1,61 @@
+"use client";
+
+import { addDays, format } from "date-fns";
+import { Calendar as CalendarIcon } from "lucide-react";
+import * as React from "react";
+import { DateRange } from "react-day-picker";
+
+import { Button } from "@saroh/ui/button";
+import { Calendar } from "@saroh/ui/calendar";
+import { cn } from "@saroh/ui/lib/utils";
+import { Popover, PopoverContent, PopoverTrigger } from "@saroh/ui/popover";
+
+export function CalendarDateRangePicker({
+ className,
+}: React.HTMLAttributes) {
+ const [date, setDate] = React.useState({
+ from: new Date(2022, 0, 20),
+ to: addDays(new Date(2022, 0, 20), 20),
+ });
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/components/dashboard/index.tsx b/apps/dashboard/components/dashboard/index.tsx
new file mode 100644
index 0000000..7d4eaea
--- /dev/null
+++ b/apps/dashboard/components/dashboard/index.tsx
@@ -0,0 +1,220 @@
+import Image from "next/image";
+
+import { Button } from "@saroh/ui/button";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@saroh/ui/card";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@saroh/ui/tabs";
+import { CalendarDateRangePicker } from "./calendar-date-range-picker";
+import { MainNav } from "./main-nav";
+import { Overview } from "./overview";
+import { RecentSales } from "./recent-sales";
+import { Search } from "./search";
+import TeamSwitcher from "./team-switcher";
+import { UserNav } from "./user-nav";
+
+export default function Dashboard() {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ Dashboard
+
+
+
+
+
+
+
+
+ Overview
+
+ Analytics
+
+
+ Reports
+
+
+ Notifications
+
+
+
+
+
+
+
+ Total Revenue
+
+
+
+
+
+ $45,231.89
+
+
+ +20.1% from last month
+
+
+
+
+
+
+ Subscriptions
+
+
+
+
+
+ +2350
+
+
+ +180.1% from last month
+
+
+
+
+
+
+ Sales
+
+
+
+
+
+ +12,234
+
+
+ +19% from last month
+
+
+
+
+
+
+ Active Now
+
+
+
+
+
+ +573
+
+
+ +201 since last hour
+
+
+
+
+
+
+
+ Overview
+
+
+
+
+
+
+
+ Recent Sales
+
+ You made 265 sales this month.
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/dashboard/components/dashboard/main-nav.tsx b/apps/dashboard/components/dashboard/main-nav.tsx
new file mode 100644
index 0000000..d513243
--- /dev/null
+++ b/apps/dashboard/components/dashboard/main-nav.tsx
@@ -0,0 +1,46 @@
+"use client";
+import { cn } from "@saroh/ui/lib/utils";
+import Link from "next/link";
+import { useParams } from "next/navigation";
+
+export function MainNav({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ const params = useParams();
+ const domain = params.teamId as string;
+ return (
+
+ );
+}
diff --git a/apps/dashboard/components/dashboard/overview.tsx b/apps/dashboard/components/dashboard/overview.tsx
new file mode 100644
index 0000000..389187f
--- /dev/null
+++ b/apps/dashboard/components/dashboard/overview.tsx
@@ -0,0 +1,83 @@
+"use client";
+
+import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from "recharts";
+
+const data = [
+ {
+ name: "Jan",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Feb",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Mar",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Apr",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "May",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Jun",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Jul",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Aug",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Sep",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Oct",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Nov",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Dec",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+];
+
+export function Overview() {
+ return (
+
+
+
+ `$${value}`}
+ />
+
+
+
+ );
+}
diff --git a/apps/dashboard/components/dashboard/recent-sales.tsx b/apps/dashboard/components/dashboard/recent-sales.tsx
new file mode 100644
index 0000000..d939bfd
--- /dev/null
+++ b/apps/dashboard/components/dashboard/recent-sales.tsx
@@ -0,0 +1,83 @@
+import { Avatar, AvatarFallback, AvatarImage } from "@saroh/ui/avatar";
+
+export function RecentSales() {
+ return (
+
+
+
+
+ OM
+
+
+
+ Olivia Martin
+
+
+ olivia.martin@email.com
+
+
+
+$1,999.00
+
+
+
+
+ JL
+
+
+
+ Jackson Lee
+
+
+ jackson.lee@email.com
+
+
+
+$39.00
+
+
+
+
+ IN
+
+
+
+ Isabella Nguyen
+
+
+ isabella.nguyen@email.com
+
+
+
+$299.00
+
+
+
+
+ WK
+
+
+
+ William Kim
+
+
+ will@email.com
+
+
+
+$99.00
+
+
+
+
+ SD
+
+
+
+ Sofia Davis
+
+
+ sofia.davis@email.com
+
+
+
+$39.00
+
+
+ );
+}
diff --git a/apps/dashboard/components/dashboard/search.tsx b/apps/dashboard/components/dashboard/search.tsx
new file mode 100644
index 0000000..772a73e
--- /dev/null
+++ b/apps/dashboard/components/dashboard/search.tsx
@@ -0,0 +1,13 @@
+import { Input } from "@saroh/ui/input";
+
+export function Search() {
+ return (
+
+
+
+ );
+}
diff --git a/apps/dashboard/components/dashboard/team-switcher.tsx b/apps/dashboard/components/dashboard/team-switcher.tsx
new file mode 100644
index 0000000..1f23a52
--- /dev/null
+++ b/apps/dashboard/components/dashboard/team-switcher.tsx
@@ -0,0 +1,224 @@
+"use client";
+
+import {
+ CaretSortIcon,
+ CheckIcon,
+ PlusCircledIcon,
+} from "@radix-ui/react-icons";
+import * as React from "react";
+
+import { Avatar, AvatarFallback, AvatarImage } from "@saroh/ui/avatar";
+import { Button } from "@saroh/ui/button";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+ CommandSeparator,
+} from "@saroh/ui/command";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@saroh/ui/dialog";
+import { Input } from "@saroh/ui/input";
+import { Label } from "@saroh/ui/label";
+import { cn } from "@saroh/ui/lib/utils";
+import { Popover, PopoverContent, PopoverTrigger } from "@saroh/ui/popover";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@saroh/ui/select";
+
+const groups = [
+ {
+ label: "Personal Account",
+ teams: [
+ {
+ label: "Alicia Koch",
+ value: "personal",
+ },
+ ],
+ },
+ {
+ label: "Teams",
+ teams: [
+ {
+ label: "Acme Inc.",
+ value: "acme-inc",
+ },
+ {
+ label: "Monsters Inc.",
+ value: "monsters",
+ },
+ ],
+ },
+];
+
+type Team = (typeof groups)[number]["teams"][number];
+
+type PopoverTriggerProps = React.ComponentPropsWithoutRef<
+ typeof PopoverTrigger
+>;
+
+interface TeamSwitcherProps extends PopoverTriggerProps {}
+
+export default function TeamSwitcher({ className }: TeamSwitcherProps) {
+ const [open, setOpen] = React.useState(false);
+ const [showNewTeamDialog, setShowNewTeamDialog] = React.useState(false);
+ const [selectedTeam, setSelectedTeam] = React.useState(
+ groups[0].teams[0],
+ );
+
+ return (
+
+ );
+}
diff --git a/apps/dashboard/components/dashboard/user-nav.tsx b/apps/dashboard/components/dashboard/user-nav.tsx
new file mode 100644
index 0000000..71d4343
--- /dev/null
+++ b/apps/dashboard/components/dashboard/user-nav.tsx
@@ -0,0 +1,84 @@
+"use client";
+import useCurrentUser from "@/lib/hooks/use-current-user";
+import { Avatar, AvatarFallback, AvatarImage } from "@saroh/ui/avatar";
+import { Button } from "@saroh/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuTrigger,
+} from "@saroh/ui/dropdown-menu";
+import { Skeleton } from "@saroh/ui/skeleton";
+import { signOut } from "next-auth/react";
+
+export function UserNav() {
+ const { user, isLoading } = useCurrentUser();
+
+ if (isLoading)
+ return (
+
+
+
+ );
+ if (!user) return null;
+
+ return (
+
+
+
+
+
+
+
+
+ {user.name}
+
+
+ {user.email}
+
+
+
+
+
+
+ Profile
+ ⇧⌘P
+
+
+ Billing
+ ⌘B
+
+
+ Settings
+ ⌘S
+
+ New Team
+
+
+ {
+ await signOut();
+ }}
+ >
+ Log out
+ ⇧⌘Q
+
+
+
+ );
+}
diff --git a/apps/dashboard/components/logout-button.tsx b/apps/dashboard/components/logout-button.tsx
new file mode 100644
index 0000000..b27579a
--- /dev/null
+++ b/apps/dashboard/components/logout-button.tsx
@@ -0,0 +1,6 @@
+"use client";
+import { Button } from "@saroh/ui/button";
+import { signOut } from "next-auth/react";
+export default function LogoutButton() {
+ return ;
+}
diff --git a/apps/dashboard/components/settings/account/account-form.tsx b/apps/dashboard/components/settings/account/account-form.tsx
new file mode 100644
index 0000000..e0d7649
--- /dev/null
+++ b/apps/dashboard/components/settings/account/account-form.tsx
@@ -0,0 +1,233 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import { CalendarIcon, CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
+import { format } from "date-fns";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+
+import { Button } from "@saroh/ui/button";
+import { Calendar } from "@saroh/ui/calendar";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+} from "@saroh/ui/command";
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@saroh/ui/form";
+import { Input } from "@saroh/ui/input";
+import { cn } from "@saroh/ui/lib/utils";
+import { Popover, PopoverContent, PopoverTrigger } from "@saroh/ui/popover";
+// import { toast } from "@saroh/ui/use-toast";
+
+const languages = [
+ { label: "English", value: "en" },
+ { label: "French", value: "fr" },
+ { label: "German", value: "de" },
+ { label: "Spanish", value: "es" },
+ { label: "Portuguese", value: "pt" },
+ { label: "Russian", value: "ru" },
+ { label: "Japanese", value: "ja" },
+ { label: "Korean", value: "ko" },
+ { label: "Chinese", value: "zh" },
+] as const;
+
+const accountFormSchema = z.object({
+ name: z
+ .string()
+ .min(2, {
+ message: "Name must be at least 2 characters.",
+ })
+ .max(30, {
+ message: "Name must not be longer than 30 characters.",
+ }),
+ dob: z.date({
+ required_error: "A date of birth is required.",
+ }),
+ language: z.string({
+ required_error: "Please select a language.",
+ }),
+});
+
+type AccountFormValues = z.infer;
+
+// This can come from your database or API.
+const defaultValues: Partial = {
+ // name: "Your name",
+ // dob: new Date("2023-01-23"),
+};
+
+export function AccountForm() {
+ const form = useForm({
+ resolver: zodResolver(accountFormSchema),
+ defaultValues,
+ });
+
+ function onSubmit(data: AccountFormValues) {
+ // toast({
+ // title: "You submitted the following values:",
+ // description: (
+ //
+ //
+ // {JSON.stringify(data, null, 2)}
+ //
+ //
+ // ),
+ // });
+ }
+
+ return (
+
+
+ );
+}
diff --git a/apps/dashboard/components/settings/appearance/appearance-form.tsx b/apps/dashboard/components/settings/appearance/appearance-form.tsx
new file mode 100644
index 0000000..7b8227c
--- /dev/null
+++ b/apps/dashboard/components/settings/appearance/appearance-form.tsx
@@ -0,0 +1,174 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import { ChevronDownIcon } from "@radix-ui/react-icons";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+
+import { Button, buttonVariants } from "@saroh/ui/button";
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@saroh/ui/form";
+import { cn } from "@saroh/ui/lib/utils";
+import { RadioGroup, RadioGroupItem } from "@saroh/ui/radio-group";
+// import { toast } from "@saroh/ui/use-toast";
+
+const appearanceFormSchema = z.object({
+ theme: z.enum(["light", "dark"], {
+ required_error: "Please select a theme.",
+ }),
+ font: z.enum(["inter", "manrope", "system"], {
+ invalid_type_error: "Select a font",
+ required_error: "Please select a font.",
+ }),
+});
+
+type AppearanceFormValues = z.infer;
+
+// This can come from your database or API.
+const defaultValues: Partial = {
+ theme: "light",
+};
+
+export function AppearanceForm() {
+ const form = useForm({
+ resolver: zodResolver(appearanceFormSchema),
+ defaultValues,
+ });
+
+ function onSubmit(data: AppearanceFormValues) {
+ // toast({
+ // title: "You submitted the following values:",
+ // description: (
+ //
+ //
+ // {JSON.stringify(data, null, 2)}
+ //
+ //
+ // ),
+ // });
+ }
+
+ return (
+
+
+ );
+}
diff --git a/apps/dashboard/components/settings/display/display-form.tsx b/apps/dashboard/components/settings/display/display-form.tsx
new file mode 100644
index 0000000..c162cdf
--- /dev/null
+++ b/apps/dashboard/components/settings/display/display-form.tsx
@@ -0,0 +1,150 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+
+import { Button } from "@saroh/ui/button";
+import { Checkbox } from "@saroh/ui/checkbox";
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@saroh/ui/form";
+// import { toast } from "@saroh/ui/use-toast";
+
+const items = [
+ {
+ id: "recents",
+ label: "Recents",
+ },
+ {
+ id: "home",
+ label: "Home",
+ },
+ {
+ id: "applications",
+ label: "Applications",
+ },
+ {
+ id: "desktop",
+ label: "Desktop",
+ },
+ {
+ id: "downloads",
+ label: "Downloads",
+ },
+ {
+ id: "documents",
+ label: "Documents",
+ },
+] as const;
+
+const displayFormSchema = z.object({
+ items: z.array(z.string()).refine((value) => value.some((item) => item), {
+ message: "You have to select at least one item.",
+ }),
+});
+
+type DisplayFormValues = z.infer;
+
+// This can come from your database or API.
+const defaultValues: Partial = {
+ items: ["recents", "home"],
+};
+
+export function DisplayForm() {
+ const form = useForm({
+ resolver: zodResolver(displayFormSchema),
+ defaultValues,
+ });
+
+ function onSubmit(data: DisplayFormValues) {
+ // toast({
+ // title: "You submitted the following values:",
+ // description: (
+ //
+ //
+ // {JSON.stringify(data, null, 2)}
+ //
+ //
+ // ),
+ // });
+ }
+
+ return (
+
+
+ );
+}
diff --git a/apps/dashboard/components/settings/notifications/notifications-form.tsx b/apps/dashboard/components/settings/notifications/notifications-form.tsx
new file mode 100644
index 0000000..d47e8f0
--- /dev/null
+++ b/apps/dashboard/components/settings/notifications/notifications-form.tsx
@@ -0,0 +1,240 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import Link from "next/link";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+
+import { Button } from "@saroh/ui/button";
+import { Checkbox } from "@saroh/ui/checkbox";
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@saroh/ui/form";
+import { RadioGroup, RadioGroupItem } from "@saroh/ui/radio-group";
+import { Switch } from "@saroh/ui/switch";
+// import { toast } from "@saroh/ui/use-toast";
+
+const notificationsFormSchema = z.object({
+ type: z.enum(["all", "mentions", "none"], {
+ required_error: "You need to select a notification type.",
+ }),
+ mobile: z.boolean().default(false).optional(),
+ communication_emails: z.boolean().default(false).optional(),
+ social_emails: z.boolean().default(false).optional(),
+ marketing_emails: z.boolean().default(false).optional(),
+ security_emails: z.boolean(),
+});
+
+type NotificationsFormValues = z.infer;
+
+// This can come from your database or API.
+const defaultValues: Partial = {
+ communication_emails: false,
+ marketing_emails: false,
+ social_emails: true,
+ security_emails: true,
+};
+
+export function NotificationsForm() {
+ const form = useForm({
+ resolver: zodResolver(notificationsFormSchema),
+ defaultValues,
+ });
+
+ function onSubmit(data: NotificationsFormValues) {
+ // toast({
+ // title: "You submitted the following values:",
+ // description: (
+ //
+ //
+ // {JSON.stringify(data, null, 2)}
+ //
+ //
+ // ),
+ // });
+ }
+
+ return (
+
+
+ );
+}
diff --git a/apps/dashboard/components/settings/profile-form/index.tsx b/apps/dashboard/components/settings/profile-form/index.tsx
new file mode 100644
index 0000000..8c33e61
--- /dev/null
+++ b/apps/dashboard/components/settings/profile-form/index.tsx
@@ -0,0 +1,211 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import Link from "next/link";
+import { useFieldArray, useForm } from "react-hook-form";
+import { z } from "zod";
+
+import { Button } from "@saroh/ui/button";
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@saroh/ui/form";
+import { Input } from "@saroh/ui/input";
+import { cn } from "@saroh/ui/lib/utils";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@saroh/ui/select";
+import { Textarea } from "@saroh/ui/textarea";
+// import { toast } from "@saroh/ui/";
+
+const profileFormSchema = z.object({
+ username: z
+ .string()
+ .min(2, {
+ message: "Username must be at least 2 characters.",
+ })
+ .max(30, {
+ message: "Username must not be longer than 30 characters.",
+ }),
+ email: z
+ .string({
+ required_error: "Please select an email to display.",
+ })
+ .email(),
+ bio: z.string().max(160).min(4),
+ urls: z
+ .array(
+ z.object({
+ value: z.string().url({ message: "Please enter a valid URL." }),
+ }),
+ )
+ .optional(),
+});
+
+type ProfileFormValues = z.infer;
+
+// This can come from your database or API.
+const defaultValues: Partial = {
+ bio: "I own a computer.",
+ urls: [
+ { value: "https://shadcn.com" },
+ { value: "http://twitter.com/shadcn" },
+ ],
+};
+
+export function ProfileForm() {
+ const form = useForm({
+ resolver: zodResolver(profileFormSchema),
+ defaultValues,
+ mode: "onChange",
+ });
+
+ const { fields, append } = useFieldArray({
+ name: "urls",
+ control: form.control,
+ });
+
+ function onSubmit(data: ProfileFormValues) {
+ // toast({
+ // title: "You submitted the following values:",
+ // description: (
+ //
+ //
+ // {JSON.stringify(data, null, 2)}
+ //
+ //
+ // ),
+ // });
+ }
+
+ return (
+
+
+ );
+}
diff --git a/apps/dashboard/components/settings/sidebar-nav.tsx b/apps/dashboard/components/settings/sidebar-nav.tsx
new file mode 100644
index 0000000..2c74034
--- /dev/null
+++ b/apps/dashboard/components/settings/sidebar-nav.tsx
@@ -0,0 +1,45 @@
+"use client";
+
+import Link from "next/link";
+import { useParams, usePathname } from "next/navigation";
+
+import { buttonVariants } from "@saroh/ui/button";
+import { cn } from "@saroh/ui/lib/utils";
+
+interface SidebarNavProps extends React.HTMLAttributes {
+ items: {
+ href: string;
+ title: string;
+ }[];
+}
+
+export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
+ const pathname = usePathname();
+ const params = useParams();
+
+ return (
+
+ );
+}
diff --git a/apps/dashboard/lib/auth.config.ts b/apps/dashboard/lib/auth.config.ts
new file mode 100644
index 0000000..28d7d06
--- /dev/null
+++ b/apps/dashboard/lib/auth.config.ts
@@ -0,0 +1,4 @@
+import type { NextAuthConfig } from "next-auth";
+import GitHub from "next-auth/providers/github";
+
+export default { providers: [GitHub] } satisfies NextAuthConfig;
diff --git a/apps/dashboard/lib/auth.ts b/apps/dashboard/lib/auth.ts
new file mode 100644
index 0000000..f9fd029
--- /dev/null
+++ b/apps/dashboard/lib/auth.ts
@@ -0,0 +1,12 @@
+import prisma from "@/lib/prisma";
+import { PrismaAdapter } from "@auth/prisma-adapter";
+import NextAuth from "next-auth";
+import authConfig from "./auth.config";
+const VERCEL_DEPLOYMENT = !!process.env.VERCEL_URL;
+
+export const { auth, handlers, signIn, signOut } = NextAuth({
+ // providers: [GitHub, Google],
+ adapter: PrismaAdapter(prisma),
+ session: { strategy: "jwt" },
+ ...authConfig,
+});
diff --git a/apps/dashboard/lib/hooks/use-current-user.ts b/apps/dashboard/lib/hooks/use-current-user.ts
new file mode 100644
index 0000000..622537a
--- /dev/null
+++ b/apps/dashboard/lib/hooks/use-current-user.ts
@@ -0,0 +1,16 @@
+import { useSession } from "next-auth/react";
+
+export default function useCurrentUser() {
+ const { data, status } = useSession();
+ const user = data?.user;
+ if (status === "loading") return { isLoading: true };
+ if (status === "authenticated")
+ return {
+ user,
+ isLoading: false,
+ };
+ if (status === "unauthenticated" || status === "error")
+ return { isLoading: true, user: null };
+
+ return { isLoading: true, user: null };
+}
diff --git a/apps/dashboard/lib/prisma.ts b/apps/dashboard/lib/prisma.ts
new file mode 100644
index 0000000..25a9564
--- /dev/null
+++ b/apps/dashboard/lib/prisma.ts
@@ -0,0 +1,11 @@
+// import { PrismaClient } from "@prisma/client";
+import { prisma } from "@saroh/database";
+// declare global {
+// var prisma: PrismaClient | undefined;
+// }
+
+// const prisma = global.prisma || new PrismaClient();
+
+// if (process.env.NODE_ENV === "development") global.prisma = prisma;
+
+export default prisma;
diff --git a/apps/dashboard/lib/utils/get-current-user.ts b/apps/dashboard/lib/utils/get-current-user.ts
new file mode 100644
index 0000000..4ac1c33
--- /dev/null
+++ b/apps/dashboard/lib/utils/get-current-user.ts
@@ -0,0 +1,8 @@
+import { auth } from "../auth";
+
+export async function getCurrentUser() {
+ const session = await auth();
+ const user = session?.user;
+ if (user) return user;
+ return null;
+}
diff --git a/apps/dashboard/middleware.ts b/apps/dashboard/middleware.ts
new file mode 100644
index 0000000..a8966c3
--- /dev/null
+++ b/apps/dashboard/middleware.ts
@@ -0,0 +1,28 @@
+import NextAuth from "next-auth";
+import { NextResponse } from "next/server";
+import authConfig from "./lib/auth.config";
+
+const { auth } = NextAuth(authConfig);
+export default auth(async function (req) {
+ // Your custom middleware logic goes here
+ const isLoggedIn = !!req.auth;
+
+ if (!isLoggedIn) {
+ return NextResponse.redirect(
+ new URL("/login", process.env.NEXT_PUBLIC_AUTH_APP_DOMAIN),
+ );
+ }
+ return;
+});
+export const config = {
+ matcher: [
+ /*
+ * Match all paths except for:
+ * 1. /api routes
+ * 2. /_next (Next.js internals)
+ * 3. /_static (inside /public)
+ * 4. all root files inside /public (e.g. /favicon.ico)
+ */
+ "/((?!api/|_next/|_static/|_vercel|[\\w-]+\\.\\w+).*)",
+ ],
+};
diff --git a/apps/dashboard/next-auth.d.ts b/apps/dashboard/next-auth.d.ts
new file mode 100644
index 0000000..072cca5
--- /dev/null
+++ b/apps/dashboard/next-auth.d.ts
@@ -0,0 +1,13 @@
+import { type DefaultSession } from "next-auth";
+
+export type ExtendedUser = DefaultSession["user"] & {
+ // role: UserRole;
+ isTwoFactorEnabled: boolean;
+ isOAuth: boolean;
+};
+
+declare module "next-auth" {
+ interface Session {
+ user: ExtendedUser;
+ }
+}
diff --git a/apps/dashboard/next.config.mjs b/apps/dashboard/next.config.mjs
index 4678774..5c91d05 100644
--- a/apps/dashboard/next.config.mjs
+++ b/apps/dashboard/next.config.mjs
@@ -1,4 +1,13 @@
/** @type {import('next').NextConfig} */
-const nextConfig = {};
+import { PrismaPlugin } from '@prisma/nextjs-monorepo-workaround-plugin';
+const nextConfig = {
+ webpack: (config, { isServer }) => {
+ if (isServer) {
+ config.plugins = [...config.plugins, new PrismaPlugin()];
+ }
+
+ return config;
+ },
+};
export default nextConfig;
diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json
index f075d02..2fa5ec6 100644
--- a/apps/dashboard/package.json
+++ b/apps/dashboard/package.json
@@ -3,27 +3,40 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev --port 3005",
+ "dev": "next dev --port 3004",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
+ "@auth/prisma-adapter": "^2.2.0",
+ "@hookform/resolvers": "^3.6.0",
+ "@prisma/nextjs-monorepo-workaround-plugin": "^5.15.0",
+ "@radix-ui/react-icons": "^1.3.0",
+ "@saroh/ui": "workspace:*",
+ "date-fns": "^3.6.0",
+ "lucide-react": "^0.394.0",
+ "next": "14.2.3",
+ "next-auth": "5.0.0-beta.19",
"react": "^18",
+ "react-day-picker": "^8.10.1",
"react-dom": "^18",
- "next": "14.2.3",
- "@saroh/ui": "workspace:*"
+ "react-hook-form": "^7.51.5",
+ "recharts": "^2.12.7",
+ "zod": "^3.23.8"
},
"devDependencies": {
- "typescript": "^5",
+ "@saroh/database": "workspace:*",
+ "@saroh/tailwind-config": "workspace:*",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.4.19",
- "postcss": "^8",
- "tailwindcss": "^3.4.1",
"eslint": "^8",
"eslint-config-next": "14.2.3",
- "@saroh/tailwind-config": "workspace:*"
+ "postcss": "^8",
+ "prisma": "^5.14.0",
+ "tailwindcss": "^3.4.1",
+ "typescript": "^5"
}
}
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 32a91d5..d7a358c 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -3,7 +3,7 @@
"version": "1.0.0",
"private": true,
"scripts": {
- "dev": "next dev --port 3001",
+ "dev": "next dev --port 3005",
"build": "next build",
"start": "next start",
"lint": "next lint"
diff --git a/apps/ecom-templates/app/layout.tsx b/apps/ecom-templates/app/layout.tsx
index 2918f93..f5116d8 100644
--- a/apps/ecom-templates/app/layout.tsx
+++ b/apps/ecom-templates/app/layout.tsx
@@ -5,7 +5,7 @@ import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
- title: "Create Next App",
+ title: "Ecommerce templates by saroh",
description: "Generated by create next app",
};
@@ -15,7 +15,7 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
+
{children}
);
diff --git a/apps/ecom-templates/app/page.tsx b/apps/ecom-templates/app/page.tsx
index 5ce7002..cb8a0a6 100644
--- a/apps/ecom-templates/app/page.tsx
+++ b/apps/ecom-templates/app/page.tsx
@@ -1,3 +1,4 @@
+"use client";
import Image from "next/image";
export default function Home() {
diff --git a/apps/ecom-templates/next.config.mjs b/apps/ecom-templates/next.config.mjs
index 4678774..085671b 100644
--- a/apps/ecom-templates/next.config.mjs
+++ b/apps/ecom-templates/next.config.mjs
@@ -1,4 +1,6 @@
/** @type {import('next').NextConfig} */
-const nextConfig = {};
+const nextConfig = {
+ basePath: "/ecommerce",
+};
export default nextConfig;
diff --git a/apps/ecom-templates/package.json b/apps/ecom-templates/package.json
index fed329d..5cbc3ef 100644
--- a/apps/ecom-templates/package.json
+++ b/apps/ecom-templates/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev --port 3004",
+ "dev": "next dev --port 3006",
"build": "next build",
"start": "next start",
"lint": "next lint"
diff --git a/apps/email-service/.eslintrc.json b/apps/email-service/.eslintrc.json
new file mode 100644
index 0000000..bffb357
--- /dev/null
+++ b/apps/email-service/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "next/core-web-vitals"
+}
diff --git a/apps/email-service/.gitignore b/apps/email-service/.gitignore
new file mode 100644
index 0000000..fd3dbb5
--- /dev/null
+++ b/apps/email-service/.gitignore
@@ -0,0 +1,36 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+.yarn/install-state.gz
+
+# 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
diff --git a/apps/email-service/README.md b/apps/email-service/README.md
new file mode 100644
index 0000000..5ce4a7c
--- /dev/null
+++ b/apps/email-service/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
diff --git a/apps/email-service/app/api/send-email/route.ts b/apps/email-service/app/api/send-email/route.ts
new file mode 100644
index 0000000..a2fc282
--- /dev/null
+++ b/apps/email-service/app/api/send-email/route.ts
@@ -0,0 +1,42 @@
+import { render } from "@react-email/components";
+
+import { FirstEmail } from "@saroh/emails/emails/first-email";
+import { NextRequest } from "next/server";
+
+import nodemailer from "nodemailer";
+
+const USER_ACCOUNT = process.env.USER_ACCOUNT;
+const USER_PASSWORD = process.env.USER_PASSWORD;
+const SENDER_EMAIL_ID = process.env.SENDER_EMAIL_ID;
+
+const transporter = nodemailer.createTransport({
+ service: "Gmail",
+ host: "smtp.gmail.com",
+ port: 465,
+ secure: true,
+ auth: {
+ user: USER_ACCOUNT,
+
+ pass: USER_PASSWORD,
+ },
+});
+const emailHTML = render(FirstEmail());
+const options = {
+ from: `Mohit <${SENDER_EMAIL_ID}>`,
+
+ subject: "Testing mail from Saroh",
+ html: emailHTML,
+};
+
+export async function GET(req: NextRequest) {
+ const searchParams = await req.nextUrl.searchParams;
+ const receiverName = searchParams.get("receiverName");
+ const receiverEmail = searchParams.get("receiverEmail");
+
+ const result = await transporter.sendMail({
+ ...options,
+ to: `${receiverName} <${receiverEmail}>`,
+ });
+ return Response.json({ result, status: "success" });
+}
+// await transporter.sendMail(options);
diff --git a/apps/email-service/app/favicon.ico b/apps/email-service/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/apps/email-service/app/favicon.ico differ
diff --git a/apps/auth/app/globals.css b/apps/email-service/app/globals.css
similarity index 100%
rename from apps/auth/app/globals.css
rename to apps/email-service/app/globals.css
diff --git a/apps/email-service/app/layout.tsx b/apps/email-service/app/layout.tsx
new file mode 100644
index 0000000..2918f93
--- /dev/null
+++ b/apps/email-service/app/layout.tsx
@@ -0,0 +1,22 @@
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import "./globals.css";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+ title: "Create Next App",
+ description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/email-service/app/page.tsx b/apps/email-service/app/page.tsx
new file mode 100644
index 0000000..f01aafe
--- /dev/null
+++ b/apps/email-service/app/page.tsx
@@ -0,0 +1,8 @@
+import { render } from "@react-email/components";
+
+import { FirstEmail } from "@saroh/emails/emails/first-email";
+
+export default function Home() {
+ const emailHTML = render(FirstEmail());
+ return ;
+}
diff --git a/apps/email-service/next.config.mjs b/apps/email-service/next.config.mjs
new file mode 100644
index 0000000..4678774
--- /dev/null
+++ b/apps/email-service/next.config.mjs
@@ -0,0 +1,4 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {};
+
+export default nextConfig;
diff --git a/apps/email-service/package.json b/apps/email-service/package.json
new file mode 100644
index 0000000..3550d3f
--- /dev/null
+++ b/apps/email-service/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "email-service",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --port 3011",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "@react-email/components": "^0.0.19",
+ "next": "14.2.3",
+ "nodemailer": "^6.9.13",
+ "react": "^18",
+ "react-dom": "^18"
+ },
+ "devDependencies": {
+ "@types/nodemailer": "^6.4.15",
+ "@saroh/emails": "workspace:*",
+ "@types/node": "^20.13.0",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "eslint": "^8",
+ "eslint-config-next": "14.2.3",
+ "postcss": "^8",
+ "tailwindcss": "^3.4.1",
+ "typescript": "^5"
+ }
+}
diff --git a/apps/email-service/postcss.config.mjs b/apps/email-service/postcss.config.mjs
new file mode 100644
index 0000000..1a69fd2
--- /dev/null
+++ b/apps/email-service/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('postcss-load-config').Config} */
+const config = {
+ plugins: {
+ tailwindcss: {},
+ },
+};
+
+export default config;
diff --git a/apps/email-service/public/next.svg b/apps/email-service/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/apps/email-service/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/email-service/public/vercel.svg b/apps/email-service/public/vercel.svg
new file mode 100644
index 0000000..d2f8422
--- /dev/null
+++ b/apps/email-service/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/email-service/tailwind.config.ts b/apps/email-service/tailwind.config.ts
new file mode 100644
index 0000000..86359dc
--- /dev/null
+++ b/apps/email-service/tailwind.config.ts
@@ -0,0 +1,20 @@
+import type { Config } from "tailwindcss";
+
+const config: Config = {
+ content: [
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
+ ],
+ theme: {
+ extend: {
+ backgroundImage: {
+ "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
+ "gradient-conic":
+ "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
+ },
+ },
+ },
+ plugins: [],
+};
+export default config;
diff --git a/apps/email-service/tsconfig.json b/apps/email-service/tsconfig.json
new file mode 100644
index 0000000..e7ff90f
--- /dev/null
+++ b/apps/email-service/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "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": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/apps/sites/app/layout.tsx b/apps/sites/app/layout.tsx
index 40e027f..d739795 100644
--- a/apps/sites/app/layout.tsx
+++ b/apps/sites/app/layout.tsx
@@ -1,22 +1,22 @@
-import type { Metadata } from 'next'
-import { Inter } from 'next/font/google'
-import './globals.css'
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import "./globals.css";
-const inter = Inter({ subsets: ['latin'] })
+const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
- title: 'Create Next App',
- description: 'Generated by create next app',
-}
+ title: "Saroh sites",
+ description: "Generated by create next app",
+};
export default function RootLayout({
- children,
+ children,
}: {
- children: React.ReactNode
+ children: React.ReactNode;
}) {
- return (
-
- {children}
-
- )
+ return (
+
+ {children}
+
+ );
}
diff --git a/apps/sites/package.json b/apps/sites/package.json
index 5c9b1e9..1dbb1bd 100644
--- a/apps/sites/package.json
+++ b/apps/sites/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev --port 3003",
+ "dev": "next dev --port 3007",
"build": "next build",
"start": "next start",
"lint": "next lint",
diff --git a/apps/ui/.eslintrc.json b/apps/ui/.eslintrc.json
new file mode 100644
index 0000000..bffb357
--- /dev/null
+++ b/apps/ui/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "next/core-web-vitals"
+}
diff --git a/apps/ui/.gitignore b/apps/ui/.gitignore
new file mode 100644
index 0000000..fd3dbb5
--- /dev/null
+++ b/apps/ui/.gitignore
@@ -0,0 +1,36 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+.yarn/install-state.gz
+
+# 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
diff --git a/apps/ui/README.md b/apps/ui/README.md
new file mode 100644
index 0000000..5ce4a7c
--- /dev/null
+++ b/apps/ui/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
diff --git a/apps/ui/app/favicon.ico b/apps/ui/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/apps/ui/app/favicon.ico differ
diff --git a/apps/ui/app/globals.css b/apps/ui/app/globals.css
new file mode 100644
index 0000000..875c01e
--- /dev/null
+++ b/apps/ui/app/globals.css
@@ -0,0 +1,33 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+ --foreground-rgb: 0, 0, 0;
+ --background-start-rgb: 214, 219, 220;
+ --background-end-rgb: 255, 255, 255;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --foreground-rgb: 255, 255, 255;
+ --background-start-rgb: 0, 0, 0;
+ --background-end-rgb: 0, 0, 0;
+ }
+}
+
+body {
+ color: rgb(var(--foreground-rgb));
+ background: linear-gradient(
+ to bottom,
+ transparent,
+ rgb(var(--background-end-rgb))
+ )
+ rgb(var(--background-start-rgb));
+}
+
+@layer utilities {
+ .text-balance {
+ text-wrap: balance;
+ }
+}
diff --git a/apps/ui/app/layout.tsx b/apps/ui/app/layout.tsx
new file mode 100644
index 0000000..2918f93
--- /dev/null
+++ b/apps/ui/app/layout.tsx
@@ -0,0 +1,22 @@
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import "./globals.css";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+ title: "Create Next App",
+ description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/auth/app/page.tsx b/apps/ui/app/page.tsx
similarity index 100%
rename from apps/auth/app/page.tsx
rename to apps/ui/app/page.tsx
diff --git a/apps/ui/next.config.mjs b/apps/ui/next.config.mjs
new file mode 100644
index 0000000..4678774
--- /dev/null
+++ b/apps/ui/next.config.mjs
@@ -0,0 +1,4 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {};
+
+export default nextConfig;
diff --git a/apps/ui/package.json b/apps/ui/package.json
new file mode 100644
index 0000000..d700612
--- /dev/null
+++ b/apps/ui/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "ui",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --port 3009",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "react": "^18",
+ "react-dom": "^18",
+ "next": "14.2.3"
+ },
+ "devDependencies": {
+ "typescript": "^5",
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "postcss": "^8",
+ "tailwindcss": "^3.4.1",
+ "eslint": "^8",
+ "eslint-config-next": "14.2.3"
+ }
+}
diff --git a/apps/ui/postcss.config.mjs b/apps/ui/postcss.config.mjs
new file mode 100644
index 0000000..1a69fd2
--- /dev/null
+++ b/apps/ui/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('postcss-load-config').Config} */
+const config = {
+ plugins: {
+ tailwindcss: {},
+ },
+};
+
+export default config;
diff --git a/apps/ui/public/next.svg b/apps/ui/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/apps/ui/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/ui/public/vercel.svg b/apps/ui/public/vercel.svg
new file mode 100644
index 0000000..d2f8422
--- /dev/null
+++ b/apps/ui/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/ui/tailwind.config.ts b/apps/ui/tailwind.config.ts
new file mode 100644
index 0000000..86359dc
--- /dev/null
+++ b/apps/ui/tailwind.config.ts
@@ -0,0 +1,20 @@
+import type { Config } from "tailwindcss";
+
+const config: Config = {
+ content: [
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
+ ],
+ theme: {
+ extend: {
+ backgroundImage: {
+ "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
+ "gradient-conic":
+ "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
+ },
+ },
+ },
+ plugins: [],
+};
+export default config;
diff --git a/apps/ui/tsconfig.json b/apps/ui/tsconfig.json
new file mode 100644
index 0000000..e7ff90f
--- /dev/null
+++ b/apps/ui/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "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": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/apps/web/components/home/join-waitlist.tsx b/apps/web/components/home/join-waitlist.tsx
index 166702d..43cab6c 100644
--- a/apps/web/components/home/join-waitlist.tsx
+++ b/apps/web/components/home/join-waitlist.tsx
@@ -1,118 +1,119 @@
"use client";
-import React, { FC } from "react";
-import { Input } from "../ui/input";
import {
- Form,
- FormControl,
- FormDescription,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormMessage,
} from "@/components/ui/form";
import { toast } from "@/components/ui/use-toast";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Button } from "../ui/button";
-import prisma from "@/lib/prisma";
-const FormSchema = z.object({
- email: z.string().email({
- message: "Please enter a valid email address!",
- }),
+import { Input } from "../ui/input";
+const formSchema = z.object({
+ email: z.string().email({
+ message: "Please enter a valid email address!",
+ }),
});
export default function JoinWaitlist() {
- const form = useForm>({
- resolver: zodResolver(FormSchema),
- defaultValues: {
- email: "",
- },
- });
- function onSubmit(data: z.infer) {
- console.log({ data });
- fetch("/api/waitlist", {
- method: "POST",
- body: JSON.stringify(data),
- headers: {
- "Content-Type": "application/json",
- },
- })
- .then((res) => res.json())
- .then((json) => {
- if (json.status === "success") {
- toast({
- description:
- "Thanks for joining the waitlist! We'll be in touch soon.",
- });
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ email: "",
+ },
+ });
+ function onSubmit(data: z.infer) {
+ console.log({ data });
+ fetch("/api/waitlist", {
+ method: "POST",
+ body: JSON.stringify(data),
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+ .then((res) => res.json())
+ .then((json) => {
+ if (json.status === "success") {
+ toast({
+ description:
+ "Thanks for joining the waitlist! We'll be in touch soon.",
+ });
- form.reset();
- }
- if (json.status === "failure") {
- if (json.reason.code === "P2002") {
- return toast({
- title: "Email already exists in the waitlist.",
- variant: "destructive",
- });
- }
- toast({
- title: "An error occurred. Please try again later.",
- variant: "destructive",
- // description: json.reason.,
- });
- console.error(json.reason);
- }
- })
- .catch((error) => {
- toast({
- title: "An error occurred. Please try again later.",
- variant: "destructive",
- description: error.message,
- });
- });
- }
+ form.reset();
+ }
+ if (json.status === "failure") {
+ if (json.reason.code === "P2002") {
+ return toast({
+ title: "Email already exists in the waitlist.",
+ variant: "destructive",
+ });
+ }
+ toast({
+ title: "An error occurred. Please try again later.",
+ variant: "destructive",
+ // description: json.reason.,
+ });
+ console.error(json.reason);
+ }
+ })
+ .catch((error) => {
+ toast({
+ title: "An error occurred. Please try again later.",
+ variant: "destructive",
+ description: error.message,
+ });
+ });
+ }
- return (
-
-
-
- Join the waitlist
-
-
-
- Welcome to saroh.io, a full stack platform for building your
- personal portfolios, blogs or storefronts with ease.
-
- {/*
+
+
+ Join the waitlist
+
+
+
+ Welcome to saroh.io, a full stack platform for building your
+ personal portfolios, blogs or storefronts with ease.
+
+ {/*
*/}
-
-
-
-
- );
+
+
+
+
+ );
}
diff --git a/apps/web/components/home/spotlight.tsx b/apps/web/components/home/spotlight.tsx
index 1d7e121..c1e5cc8 100644
--- a/apps/web/components/home/spotlight.tsx
+++ b/apps/web/components/home/spotlight.tsx
@@ -2,38 +2,42 @@ import { MovingBorderButton } from "../ui/moving-borders";
import { Spotlight } from "../ui/spotlight";
export function SpotlightPreview() {
- return (
-
-
-
-
-
- {/* {latestPost.title} */}
- Introducing Saroh - a new way to manage your online
- storefronts.
-
- {/* */}
-
+ return (
+
+
+
+
+
+ {/* {latestPost.title} */}
+ Introducing Saroh - a new way to manage your online
+ storefronts.
+
+ {/* */}
+
-
- Storefronts With
-
Superpowers
-
-
- Saroh is an open-source storefront management tool for
- businesses to create, share, and track their online
- business.
-
-
- Coming Soon
-
-
-
- );
+
+ Storefronts With
+
Superpowers
+
+
+ Saroh is an open-source storefront management tool for
+ businesses to create, share, and track their online
+ business.
+
+
+
+
+ );
}
diff --git a/apps/web/next.config.js b/apps/web/next.config.js
index 195eda6..4b051a9 100644
--- a/apps/web/next.config.js
+++ b/apps/web/next.config.js
@@ -2,30 +2,40 @@
const { PrismaPlugin } = require("@prisma/nextjs-monorepo-workaround-plugin");
const nextConfig = {
- webpack: (config, { isServer }) => {
- if (isServer) {
- config.plugins = [...config.plugins, new PrismaPlugin()];
- }
+ webpack: (config, { isServer }) => {
+ if (isServer) {
+ config.plugins = [...config.plugins, new PrismaPlugin()];
+ }
- return config;
- },
- reactStrictMode: false,
- experimental: {
- serverActions: true,
- },
- images: {
- domains: [
- "public.blob.vercel-storage.com",
- "res.cloudinary.com",
- "abs.twimg.com",
- "pbs.twimg.com",
- "avatars.githubusercontent.com",
- "www.google.com",
- "flag.vercel.app",
- "illustrations.popsy.co",
- "lh3.googleusercontent.com",
- ],
- },
+ return config;
+ },
+ reactStrictMode: false,
+
+ images: {
+ domains: [
+ "public.blob.vercel-storage.com",
+ "res.cloudinary.com",
+ "abs.twimg.com",
+ "pbs.twimg.com",
+ "avatars.githubusercontent.com",
+ "www.google.com",
+ "flag.vercel.app",
+ "illustrations.popsy.co",
+ "lh3.googleusercontent.com",
+ ],
+ },
+ async rewrites() {
+ return [
+ {
+ source: "/ecommerce",
+ destination: "http://localhost:3006/ecommerce",
+ },
+ {
+ source: "/ecommerce/:path*",
+ destination: "http://localhost:3006/ecommerce/:path*",
+ },
+ ];
+ },
};
module.exports = nextConfig;
diff --git a/apps/web/package.json b/apps/web/package.json
index 8850ee3..f9642e0 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -3,7 +3,7 @@
"version": "1.0.0",
"private": true,
"scripts": {
- "dev": "next dev --port 3002",
+ "dev": "next dev --port 3008",
"build": " next build",
"start": "next start",
"lint": "next lint",
@@ -31,23 +31,23 @@
"prisma": "5.12.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-hook-form": "^7.51.3",
+ "react-hook-form": "^7.51.5",
"react-icons": "^5.0.1",
"simplex-noise": "^4.0.1",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
- "zod": "^3.22.4"
+ "zod": "^3.23.8"
},
"devDependencies": {
"@next/eslint-plugin-next": "^14.2.1",
+ "@saroh/database": "workspace:*",
+ "@saroh/tailwind-config": "workspace:*",
"@types/node": "^20.12.7",
"@types/react": "^18.2.78",
"@types/react-dom": "^18.2.25",
"autoprefixer": "^10.4.19",
- "@saroh/database": "workspace:*",
"eslint-config-custom": "workspace:*",
"postcss": "^8.4.38",
- "@saroh/tailwind-config": "workspace:*",
"tailwindcss": "^3.4.3",
"tsconfig": "workspace:*",
"typescript": "^5.4.5"
diff --git a/package.json b/package.json
index c3f203b..c5cb407 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,md}\""
},
"prisma": {
- "schema": "packages/database/prisma/schema.prisma",
+ "schema": "./packages/database/src/prisma/schema.prisma",
"seed": "tsx packages/database/src/seed.ts"
},
"devDependencies": {
@@ -25,7 +25,7 @@
"pretty-quick": "^3.1.3",
"tsconfig": "workspace:*",
"tsx": "^4.10.5",
- "turbo": "^1.13.3"
+ "turbo": "^2.0.3"
},
"lint-staged": {
"*.js": "eslint --cache --fix",
diff --git a/packages/auth/README.md b/packages/auth/README.md
new file mode 100644
index 0000000..c60ccbb
--- /dev/null
+++ b/packages/auth/README.md
@@ -0,0 +1 @@
+@saroh/auth
diff --git a/packages/auth/package.json b/packages/auth/package.json
new file mode 100644
index 0000000..0880197
--- /dev/null
+++ b/packages/auth/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@saroh/auth",
+ "description": "Authentication module for Saroh, built with Next AUth V5",
+ "version": "1.0.0",
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist/**"
+ ],
+ "license": "MIT",
+ "scripts": {
+ "build": "tsup",
+ "lint": "eslint src/",
+ "dev": "tsup --watch",
+ "check-types": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@auth/prisma-adapter": "^2.2.0",
+ "@saroh/database": "workspace:*",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "next": "^14.2.3",
+ "next-auth": "5.0.0-beta.19",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@types/node": "^20.12.12",
+ "config": "^3.3.11",
+ "eslint": "^9.3.0",
+ "prisma": "^5.14.0",
+ "rimraf": "^5.0.7",
+ "tsconfig": "*",
+ "tsup": "^8.0.2",
+ "tsx": "^4.10.5",
+ "typescript": "^5.4.5"
+ },
+ "author": "Mohit Kumar ",
+ "homepage": "https://saroh.io",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/himohitmehta/saroh.io.git"
+ },
+ "bugs": {
+ "url": "https://github.com/himohitmehta/saroh.io/issues"
+ },
+ "keywords": [
+ "saroh",
+ "saroh.io",
+ "next-auth",
+ "saroh auth"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "exports": {
+ "./index": "./src/index.ts",
+ "./auth.ts": "./src/auth.ts",
+ "./auth.config": "./src/auth.config.ts"
+ }
+}
diff --git a/packages/auth/src/auth.config.ts b/packages/auth/src/auth.config.ts
new file mode 100644
index 0000000..e69de29
diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts
new file mode 100644
index 0000000..7ca90fa
--- /dev/null
+++ b/packages/auth/src/auth.ts
@@ -0,0 +1,12 @@
+import { PrismaAdapter } from "@auth/prisma-adapter";
+import NextAuth, { NextAuthResult } from "next-auth";
+import GitHub from "next-auth/providers/github";
+import Google from "next-auth/providers/google";
+import prisma from "./prisma";
+const VERCEL_DEPLOYMENT = !!process.env.VERCEL_URL;
+
+export const nextAuthInstance: NextAuthResult = NextAuth({
+ providers: [GitHub, Google],
+ adapter: PrismaAdapter(prisma),
+ session: { strategy: "jwt" },
+});
diff --git a/packages/auth/src/index.tsx b/packages/auth/src/index.tsx
new file mode 100644
index 0000000..2d183a4
--- /dev/null
+++ b/packages/auth/src/index.tsx
@@ -0,0 +1,10 @@
+// export * from './auth'
+import { SessionProvider } from "next-auth/react";
+import React from "react";
+export default function SarohAuthProvider({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return {children};
+}
diff --git a/packages/auth/src/prisma.ts b/packages/auth/src/prisma.ts
new file mode 100644
index 0000000..c59fbe7
--- /dev/null
+++ b/packages/auth/src/prisma.ts
@@ -0,0 +1,2 @@
+import { prisma } from "@saroh/database";
+export default prisma;
diff --git a/packages/auth/tsconfig.json b/packages/auth/tsconfig.json
new file mode 100644
index 0000000..f361a61
--- /dev/null
+++ b/packages/auth/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "tsconfig/react-library.json",
+ "include": ["."],
+ "exclude": ["dist", "build", "node_modules"],
+ "compilerOptions": {
+ "baseUrl": "."
+ // // "paths": {
+ // // "src/*": ["src/*"]
+ // // }
+ }
+}
diff --git a/packages/auth/tsup.config.ts b/packages/auth/tsup.config.ts
new file mode 100644
index 0000000..a6844bd
--- /dev/null
+++ b/packages/auth/tsup.config.ts
@@ -0,0 +1,17 @@
+import { defineConfig, Options } from "tsup";
+
+export default defineConfig((options: Options) => ({
+ entry: ["src/index.tsx"],
+ format: ["esm", "cjs"],
+ esbuildOptions(options) {
+ options.banner = {
+ js: '"use client"',
+ };
+ },
+ dts: true,
+ target: "es2019",
+ clean: true,
+ minify: true,
+ external: ["react"],
+ ...options,
+}));
diff --git a/packages/charts/README.md b/packages/charts/README.md
new file mode 100644
index 0000000..d217aae
--- /dev/null
+++ b/packages/charts/README.md
@@ -0,0 +1 @@
+package - @saroh/charts
diff --git a/packages/charts/package.json b/packages/charts/package.json
index 35e5e6d..386f8d9 100644
--- a/packages/charts/package.json
+++ b/packages/charts/package.json
@@ -8,5 +8,8 @@
},
"keywords": [],
"author": "",
- "license": "ISC"
+ "license": "ISC",
+ "dependencies": {
+ "recharts": "^2.12.7"
+ }
}
diff --git a/packages/chatbot/README.md b/packages/chatbot/README.md
new file mode 100644
index 0000000..519a81f
--- /dev/null
+++ b/packages/chatbot/README.md
@@ -0,0 +1 @@
+@saroh/chatbot
diff --git a/packages/database/README.md b/packages/database/README.md
new file mode 100644
index 0000000..d0d1e32
--- /dev/null
+++ b/packages/database/README.md
@@ -0,0 +1 @@
+@saroh/database
diff --git a/packages/database/src/client.ts b/packages/database/src/client.ts
index 3d434b7..7cfc9bb 100644
--- a/packages/database/src/client.ts
+++ b/packages/database/src/client.ts
@@ -1,12 +1,13 @@
import { PrismaClient } from "@prisma/client";
declare global {
- var prisma: PrismaClient | undefined;
+ namespace globalThis {
+ var prisma: PrismaClient | undefined;
+ }
}
-export const prisma = global.prisma || new PrismaClient();
+export const prisma = globalThis.prisma || new PrismaClient();
-if (process.env.NODE_ENV !== "production") global.prisma = prisma;
+if (process.env.NODE_ENV !== "production") globalThis.prisma = prisma;
export * from "@prisma/client";
-
diff --git a/packages/emails/emails/first-email.tsx b/packages/emails/emails/first-email.tsx
new file mode 100644
index 0000000..cc461df
--- /dev/null
+++ b/packages/emails/emails/first-email.tsx
@@ -0,0 +1,20 @@
+import { Button, Html } from "@react-email/components";
+
+export const FirstEmail = () => {
+ return (
+
+
+
+ );
+};
+
+export default FirstEmail;
diff --git a/packages/emails/package.json b/packages/emails/package.json
new file mode 100644
index 0000000..13a0da7
--- /dev/null
+++ b/packages/emails/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "@saroh/emails",
+ "version": "1.0.0",
+ "description": "Email templates for saroh",
+ "main": "index.js",
+ "scripts": {
+ "dev": "email dev -p 3010",
+ "export": "email export"
+ },
+ "workspaces": [
+ ".react-email"
+ ],
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@react-email/components": "^0.0.19",
+ "@types/nodemailer": "^6.4.15",
+ "nodemailer": "^6.9.13",
+ "react-email": "^2.1.4"
+ }
+}
diff --git a/packages/emails/tsconfig.json b/packages/emails/tsconfig.json
new file mode 100644
index 0000000..81eae47
--- /dev/null
+++ b/packages/emails/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "tsconfig/react-library.json",
+ "include": ["."],
+ "exclude": ["dist", "build", "node_modules"],
+ "compilerOptions": {
+ "strict": true,
+ "jsx": "react-jsx"
+ }
+}
diff --git a/packages/eslint-config-custom/README.md b/packages/eslint-config-custom/README.md
index 8b42d90..71434cc 100644
--- a/packages/eslint-config-custom/README.md
+++ b/packages/eslint-config-custom/README.md
@@ -1,3 +1,3 @@
-# `@turbo/eslint-config`
+# `eslint-config-custom`
Collection of internal eslint configurations.
diff --git a/packages/tailwind-config/README.md b/packages/tailwind-config/README.md
new file mode 100644
index 0000000..fc3224e
--- /dev/null
+++ b/packages/tailwind-config/README.md
@@ -0,0 +1 @@
+@saroh/tailwind-config
diff --git a/packages/templates/README.md b/packages/templates/README.md
new file mode 100644
index 0000000..c92fd0a
--- /dev/null
+++ b/packages/templates/README.md
@@ -0,0 +1 @@
+@saroh/templates
diff --git a/packages/tsconfig/README.md b/packages/tsconfig/README.md
new file mode 100644
index 0000000..8520097
--- /dev/null
+++ b/packages/tsconfig/README.md
@@ -0,0 +1 @@
+tsconfig
diff --git a/packages/ui/.eslintrc.js b/packages/ui/.eslintrc.js
index b1c0275..8b56da2 100644
--- a/packages/ui/.eslintrc.js
+++ b/packages/ui/.eslintrc.js
@@ -5,5 +5,6 @@ module.exports = {
parser: "@typescript-eslint/parser",
rules: {
"no-redeclare": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
},
};
diff --git a/packages/ui/README.md b/packages/ui/README.md
new file mode 100644
index 0000000..85f6557
--- /dev/null
+++ b/packages/ui/README.md
@@ -0,0 +1,3 @@
+@saroh/ui
+
+UI Components for saroh
diff --git a/packages/ui/components.json b/packages/ui/components.json
index 5bdb637..9240739 100644
--- a/packages/ui/components.json
+++ b/packages/ui/components.json
@@ -11,7 +11,7 @@
"prefix": ""
},
"aliases": {
- "components": "@/ui/components",
- "utils": "@/ui/lib/utils"
+ "components": "src/components",
+ "utils": "src/lib/utils"
}
}
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 5eec2c7..0da9ee0 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -28,20 +28,57 @@
"typescript": "^5.4.5"
},
"dependencies": {
+ "@hookform/resolvers": "^3.4.2",
+ "@radix-ui/react-accordion": "^1.1.2",
+ "@radix-ui/react-alert-dialog": "^1.0.4",
+ "@radix-ui/react-aspect-ratio": "^1.0.3",
+ "@radix-ui/react-avatar": "^1.0.3",
+ "@radix-ui/react-checkbox": "^1.0.4",
+ "@radix-ui/react-collapsible": "^1.0.3",
+ "@radix-ui/react-context-menu": "^2.1.4",
+ "@radix-ui/react-dialog": "^1.0.5",
+ "@radix-ui/react-dropdown-menu": "^2.0.5",
+ "@radix-ui/react-hover-card": "^1.0.6",
+ "@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-menubar": "^1.0.3",
+ "@radix-ui/react-popover": "^1.0.6",
+ "@radix-ui/react-progress": "^1.0.3",
+ "@radix-ui/react-radio-group": "^1.1.3",
+ "@radix-ui/react-scroll-area": "^1.0.4",
+ "@radix-ui/react-select": "^2.0.0",
+ "@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-toggle": "^1.0.3",
+ "@radix-ui/react-toggle-group": "^1.0.4",
+ "@radix-ui/react-tooltip": "^1.0.6",
+ "@tanstack/react-table": "^8.17.3",
"autoprefixer": "^10.4.19",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
+ "cmdk": "^1.0.0",
+ "date-fns": "^3.6.0",
+ "embla-carousel-react": "^8.1.3",
+ "lucide-react": "^0.383.0",
+ "next-themes": "^0.3.0",
"postcss": "^8.4.38",
+ "react-day-picker": "^8.10.1",
+ "react-hook-form": "^7.51.5",
+ "react-resizable-panels": "^2.0.19",
+ "sonner": "^1.4.41",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.3",
- "tsup": "^8.0.2"
+ "tsup": "^8.0.2",
+ "vaul": "^0.9.1",
+ "zod": "^3.23.8"
},
"author": "Mohit Kumar ",
"homepage": "https://saroh.io",
"repository": {
"type": "git",
- "url": "git+https://github.com/himohitmehta/saroh.io.git"
+ "url": "https://github.com/himohitmehta/saroh.io.git"
},
"bugs": {
"url": "https://github.com/himohitmehta/saroh.io/issues"
@@ -59,6 +96,6 @@
"./postcss.config": "./postcss.config.js",
"./tailwind.config": "./tailwind.config.ts",
"./lib/*": "./src/lib/*.ts",
- "./components/*": "./src/components/*.tsx"
+ "./*": "./src/components/ui/*.tsx"
}
}
diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts
index 4aedf59..91cda97 100644
--- a/packages/ui/src/components/index.ts
+++ b/packages/ui/src/components/index.ts
@@ -1 +1,2 @@
export * from "./ui";
+export { Button } from "./ui";
diff --git a/packages/ui/src/components/ui/accordion.tsx b/packages/ui/src/components/ui/accordion.tsx
new file mode 100644
index 0000000..b93d82f
--- /dev/null
+++ b/packages/ui/src/components/ui/accordion.tsx
@@ -0,0 +1,57 @@
+"use client";
+
+import * as AccordionPrimitive from "@radix-ui/react-accordion";
+import { ChevronDown } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Accordion = AccordionPrimitive.Root;
+
+const AccordionItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AccordionItem.displayName = "AccordionItem";
+
+const AccordionTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ svg]:rotate-180",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+));
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
+
+const AccordionContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+));
+
+AccordionContent.displayName = AccordionPrimitive.Content.displayName;
+
+export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
diff --git a/packages/ui/src/components/ui/alert-dialog.tsx b/packages/ui/src/components/ui/alert-dialog.tsx
new file mode 100644
index 0000000..02c62f1
--- /dev/null
+++ b/packages/ui/src/components/ui/alert-dialog.tsx
@@ -0,0 +1,143 @@
+"use client";
+import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+import { buttonVariants } from "./button";
+
+const AlertDialog = AlertDialogPrimitive.Root;
+
+const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
+
+const AlertDialogPortal = AlertDialogPrimitive.Portal;
+
+const AlertDialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
+
+const AlertDialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+));
+AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
+
+function AlertDialogHeader({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+AlertDialogHeader.displayName = "AlertDialogHeader";
+
+function AlertDialogFooter({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+AlertDialogFooter.displayName = "AlertDialogFooter";
+
+const AlertDialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
+
+const AlertDialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AlertDialogDescription.displayName =
+ AlertDialogPrimitive.Description.displayName;
+
+const AlertDialogAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
+
+const AlertDialogCancel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
+
+export {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogOverlay,
+ AlertDialogPortal,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+};
diff --git a/packages/ui/src/components/ui/alert.tsx b/packages/ui/src/components/ui/alert.tsx
new file mode 100644
index 0000000..6ee3d49
--- /dev/null
+++ b/packages/ui/src/components/ui/alert.tsx
@@ -0,0 +1,63 @@
+import { cva, type VariantProps } from "class-variance-authority";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const alertVariants = cva(
+ "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
+ {
+ 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 & VariantProps
+>(({ className, variant, ...props }, ref) => (
+
+));
+Alert.displayName = "Alert";
+
+const AlertTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+ {props.children}
+
+));
+AlertTitle.displayName = "AlertTitle";
+
+const AlertDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+AlertDescription.displayName = "AlertDescription";
+
+export { Alert, AlertDescription, AlertTitle };
diff --git a/packages/ui/src/components/ui/aspect-ratio.tsx b/packages/ui/src/components/ui/aspect-ratio.tsx
new file mode 100644
index 0000000..359bc94
--- /dev/null
+++ b/packages/ui/src/components/ui/aspect-ratio.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio";
+
+const AspectRatio = AspectRatioPrimitive.Root;
+
+export { AspectRatio };
diff --git a/packages/ui/src/components/ui/avatar.tsx b/packages/ui/src/components/ui/avatar.tsx
new file mode 100644
index 0000000..577b475
--- /dev/null
+++ b/packages/ui/src/components/ui/avatar.tsx
@@ -0,0 +1,49 @@
+"use client";
+
+import * as AvatarPrimitive from "@radix-ui/react-avatar";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Avatar = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+Avatar.displayName = AvatarPrimitive.Root.displayName;
+
+const AvatarImage = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AvatarImage.displayName = AvatarPrimitive.Image.displayName;
+
+const AvatarFallback = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
+
+export { Avatar, AvatarFallback, AvatarImage };
diff --git a/packages/ui/src/components/ui/badge.tsx b/packages/ui/src/components/ui/badge.tsx
new file mode 100644
index 0000000..fbed6a3
--- /dev/null
+++ b/packages/ui/src/components/ui/badge.tsx
@@ -0,0 +1,35 @@
+import { cva, type VariantProps } from "class-variance-authority";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-full 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 hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ );
+}
+
+export { Badge, badgeVariants };
diff --git a/packages/ui/src/components/ui/breadcrumb.tsx b/packages/ui/src/components/ui/breadcrumb.tsx
new file mode 100644
index 0000000..726a830
--- /dev/null
+++ b/packages/ui/src/components/ui/breadcrumb.tsx
@@ -0,0 +1,121 @@
+import { Slot } from "@radix-ui/react-slot";
+import { ChevronRight, MoreHorizontal } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Breadcrumb = React.forwardRef<
+ HTMLElement,
+ React.ComponentPropsWithoutRef<"nav"> & {
+ separator?: React.ReactNode;
+ }
+>(({ ...props }, ref) => );
+Breadcrumb.displayName = "Breadcrumb";
+
+const BreadcrumbList = React.forwardRef<
+ HTMLOListElement,
+ React.ComponentPropsWithoutRef<"ol">
+>(({ className, ...props }, ref) => (
+
+));
+BreadcrumbList.displayName = "BreadcrumbList";
+
+const BreadcrumbItem = React.forwardRef<
+ HTMLLIElement,
+ React.ComponentPropsWithoutRef<"li">
+>(({ className, ...props }, ref) => (
+
+));
+BreadcrumbItem.displayName = "BreadcrumbItem";
+
+const BreadcrumbLink = React.forwardRef<
+ HTMLAnchorElement,
+ React.ComponentPropsWithoutRef<"a"> & {
+ asChild?: boolean;
+ }
+>(({ asChild, className, ...props }, ref) => {
+ const Comp = asChild ? Slot : "a";
+
+ return (
+
+ );
+});
+BreadcrumbLink.displayName = "BreadcrumbLink";
+
+const BreadcrumbPage = React.forwardRef<
+ HTMLSpanElement,
+ React.ComponentPropsWithoutRef<"span">
+>(({ className, ...props }, ref) => (
+
+));
+BreadcrumbPage.displayName = "BreadcrumbPage";
+
+function BreadcrumbSeparator({
+ children,
+ className,
+ ...props
+}: React.ComponentProps<"li">) {
+ return (
+ svg]:size-3.5", className)}
+ {...props}
+ >
+ {children ?? }
+
+ );
+}
+BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
+
+function BreadcrumbEllipsis({
+ className,
+ ...props
+}: React.ComponentProps<"span">) {
+ return (
+
+
+ More
+
+ );
+}
+BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";
+
+export {
+ Breadcrumb,
+ BreadcrumbEllipsis,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+};
diff --git a/packages/ui/src/components/ui/button.tsx b/packages/ui/src/components/ui/button.tsx
index de863ad..41162f8 100644
--- a/packages/ui/src/components/ui/button.tsx
+++ b/packages/ui/src/components/ui/button.tsx
@@ -1,7 +1,6 @@
-/* eslint-disable import/no-named-as-default-member -- showing unnecessary errors*/
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
-import React from "react";
+import * as React from "react";
import { cn } from "../../lib/utils";
const buttonVariants = cva(
diff --git a/packages/ui/src/components/ui/calendar.tsx b/packages/ui/src/components/ui/calendar.tsx
new file mode 100644
index 0000000..1f6de16
--- /dev/null
+++ b/packages/ui/src/components/ui/calendar.tsx
@@ -0,0 +1,66 @@
+/* eslint-disable react/no-unstable-nested-components -- not required */
+"use client";
+
+import { ChevronLeft, ChevronRight } from "lucide-react";
+import * as React from "react";
+import { DayPicker } from "react-day-picker";
+import { cn } from "../../lib/utils";
+import { buttonVariants } from "./button";
+
+export type CalendarProps = React.ComponentProps;
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ ...props
+}: CalendarProps) {
+ return (
+ ,
+ IconRight: () => ,
+ }}
+ {...props}
+ />
+ );
+}
+Calendar.displayName = "Calendar";
+
+export { Calendar };
diff --git a/packages/ui/src/components/ui/card.tsx b/packages/ui/src/components/ui/card.tsx
new file mode 100644
index 0000000..ac3b7fb
--- /dev/null
+++ b/packages/ui/src/components/ui/card.tsx
@@ -0,0 +1,87 @@
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+Card.displayName = "Card";
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardHeader.displayName = "CardHeader";
+
+const CardTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+ {props.children}
+
+));
+CardTitle.displayName = "CardTitle";
+
+const CardDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardDescription.displayName = "CardDescription";
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardContent.displayName = "CardContent";
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardFooter.displayName = "CardFooter";
+
+export {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+};
diff --git a/packages/ui/src/components/ui/carousel.tsx b/packages/ui/src/components/ui/carousel.tsx
new file mode 100644
index 0000000..e558e50
--- /dev/null
+++ b/packages/ui/src/components/ui/carousel.tsx
@@ -0,0 +1,264 @@
+/* eslint-disable @typescript-eslint/no-unnecessary-condition -- not required */
+/* eslint-disable @typescript-eslint/no-shadow -- not required*/
+"use client";
+
+import useEmblaCarousel, {
+ type UseEmblaCarouselType,
+} from "embla-carousel-react";
+import { ArrowLeft, ArrowRight } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+import { Button } from "./button";
+
+type CarouselApi = UseEmblaCarouselType[1];
+type UseCarouselParameters = Parameters;
+type CarouselOptions = UseCarouselParameters[0];
+type CarouselPlugin = UseCarouselParameters[1];
+
+interface CarouselProps {
+ opts?: CarouselOptions;
+ plugins?: CarouselPlugin;
+ orientation?: "horizontal" | "vertical";
+ setApi?: (api: CarouselApi) => void;
+}
+
+type CarouselContextProps = {
+ carouselRef: ReturnType[0];
+ api: ReturnType[1];
+ scrollPrev: () => void;
+ scrollNext: () => void;
+ canScrollPrev: boolean;
+ canScrollNext: boolean;
+} & CarouselProps;
+
+const CarouselContext = React.createContext(null);
+
+function useCarousel() {
+ const context = React.useContext(CarouselContext);
+
+ if (!context) {
+ throw new Error("useCarousel must be used within a ");
+ }
+
+ return context;
+}
+
+const Carousel = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes & CarouselProps
+>(
+ (
+ {
+ orientation = "horizontal",
+ opts,
+ setApi,
+ plugins,
+ className,
+ children,
+ ...props
+ },
+ ref,
+ ) => {
+ const [carouselRef, api] = useEmblaCarousel(
+ {
+ ...opts,
+ axis: orientation === "horizontal" ? "x" : "y",
+ },
+ plugins,
+ );
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false);
+ const [canScrollNext, setCanScrollNext] = React.useState(false);
+
+ const onSelect = React.useCallback((api: CarouselApi) => {
+ if (!api) {
+ return;
+ }
+
+ setCanScrollPrev(api.canScrollPrev());
+ setCanScrollNext(api.canScrollNext());
+ }, []);
+
+ const scrollPrev = React.useCallback(() => {
+ api?.scrollPrev();
+ }, [api]);
+
+ const scrollNext = React.useCallback(() => {
+ api?.scrollNext();
+ }, [api]);
+
+ const handleKeyDown = React.useCallback(
+ (event: React.KeyboardEvent) => {
+ if (event.key === "ArrowLeft") {
+ event.preventDefault();
+ scrollPrev();
+ } else if (event.key === "ArrowRight") {
+ event.preventDefault();
+ scrollNext();
+ }
+ },
+ [scrollPrev, scrollNext],
+ );
+
+ React.useEffect(() => {
+ if (!api || !setApi) {
+ return;
+ }
+
+ setApi(api);
+ }, [api, setApi]);
+
+ React.useEffect(() => {
+ if (!api) {
+ return;
+ }
+
+ onSelect(api);
+ api.on("reInit", onSelect);
+ api.on("select", onSelect);
+
+ return () => {
+ api?.off("select", onSelect);
+ };
+ }, [api, onSelect]);
+
+ return (
+
+
+ {children}
+
+
+ );
+ },
+);
+Carousel.displayName = "Carousel";
+
+const CarouselContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { carouselRef, orientation } = useCarousel();
+
+ return (
+
+ );
+});
+CarouselContent.displayName = "CarouselContent";
+
+const CarouselItem = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { orientation } = useCarousel();
+
+ return (
+
+ );
+});
+CarouselItem.displayName = "CarouselItem";
+
+const CarouselPrevious = React.forwardRef<
+ HTMLButtonElement,
+ React.ComponentProps
+>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel();
+
+ return (
+
+ );
+});
+CarouselPrevious.displayName = "CarouselPrevious";
+
+const CarouselNext = React.forwardRef<
+ HTMLButtonElement,
+ React.ComponentProps
+>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
+ const { orientation, scrollNext, canScrollNext } = useCarousel();
+
+ return (
+
+ );
+});
+CarouselNext.displayName = "CarouselNext";
+
+export {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+ type CarouselApi,
+};
diff --git a/packages/ui/src/components/ui/checkbox.tsx b/packages/ui/src/components/ui/checkbox.tsx
new file mode 100644
index 0000000..9aeffe7
--- /dev/null
+++ b/packages/ui/src/components/ui/checkbox.tsx
@@ -0,0 +1,29 @@
+"use client";
+
+import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
+import { Check } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Checkbox = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+));
+Checkbox.displayName = CheckboxPrimitive.Root.displayName;
+
+export { Checkbox };
diff --git a/packages/ui/src/components/ui/collapsible.tsx b/packages/ui/src/components/ui/collapsible.tsx
new file mode 100644
index 0000000..1fe76f5
--- /dev/null
+++ b/packages/ui/src/components/ui/collapsible.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
+
+const Collapsible = CollapsiblePrimitive.Root;
+
+const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
+
+const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
+
+export { Collapsible, CollapsibleContent, CollapsibleTrigger };
diff --git a/packages/ui/src/components/ui/command.tsx b/packages/ui/src/components/ui/command.tsx
new file mode 100644
index 0000000..fd36ba4
--- /dev/null
+++ b/packages/ui/src/components/ui/command.tsx
@@ -0,0 +1,158 @@
+/* eslint-disable react/no-unknown-property -- no */
+"use client";
+
+import { type DialogProps } from "@radix-ui/react-dialog";
+import { Command as CommandPrimitive } from "cmdk";
+import { Search } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+import { Dialog, DialogContent } from "./dialog";
+
+const Command = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+Command.displayName = CommandPrimitive.displayName;
+
+type CommandDialogProps = DialogProps;
+
+function CommandDialog({ children, ...props }: CommandDialogProps) {
+ return (
+
+ );
+}
+
+const CommandInput = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+));
+
+CommandInput.displayName = CommandPrimitive.Input.displayName;
+
+const CommandList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandList.displayName = CommandPrimitive.List.displayName;
+
+const CommandEmpty = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>((props, ref) => (
+
+));
+
+CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
+
+const CommandGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandGroup.displayName = CommandPrimitive.Group.displayName;
+
+const CommandSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
+
+const CommandItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandItem.displayName = CommandPrimitive.Item.displayName;
+
+function CommandShortcut({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+CommandShortcut.displayName = "CommandShortcut";
+
+export {
+ Command,
+ CommandDialog,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+ CommandSeparator,
+ CommandShortcut,
+};
diff --git a/packages/ui/src/components/ui/context-menu.tsx b/packages/ui/src/components/ui/context-menu.tsx
new file mode 100644
index 0000000..c9afcbb
--- /dev/null
+++ b/packages/ui/src/components/ui/context-menu.tsx
@@ -0,0 +1,199 @@
+"use client";
+
+import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
+import { Check, ChevronRight, Circle } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const ContextMenu = ContextMenuPrimitive.Root;
+
+const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
+
+const ContextMenuGroup = ContextMenuPrimitive.Group;
+
+const ContextMenuPortal = ContextMenuPrimitive.Portal;
+
+const ContextMenuSub = ContextMenuPrimitive.Sub;
+
+const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
+
+const ContextMenuSubTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean;
+ }
+>(({ className, inset, children, ...props }, ref) => (
+
+ {children}
+
+
+));
+ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
+
+const ContextMenuSubContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
+
+const ContextMenuContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
+
+const ContextMenuItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean;
+ }
+>(({ className, inset, ...props }, ref) => (
+
+));
+ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
+
+const ContextMenuCheckboxItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, checked, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+));
+ContextMenuCheckboxItem.displayName =
+ ContextMenuPrimitive.CheckboxItem.displayName;
+
+const ContextMenuRadioItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+));
+ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
+
+const ContextMenuLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean;
+ }
+>(({ className, inset, ...props }, ref) => (
+
+));
+ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
+
+const ContextMenuSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
+
+function ContextMenuShortcut({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+ContextMenuShortcut.displayName = "ContextMenuShortcut";
+
+export {
+ ContextMenu,
+ ContextMenuCheckboxItem,
+ ContextMenuContent,
+ ContextMenuGroup,
+ ContextMenuItem,
+ ContextMenuLabel,
+ ContextMenuPortal,
+ ContextMenuRadioGroup,
+ ContextMenuRadioItem,
+ ContextMenuSeparator,
+ ContextMenuShortcut,
+ ContextMenuSub,
+ ContextMenuSubContent,
+ ContextMenuSubTrigger,
+ ContextMenuTrigger,
+};
diff --git a/packages/ui/src/components/ui/dialog.tsx b/packages/ui/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..6a1a8c7
--- /dev/null
+++ b/packages/ui/src/components/ui/dialog.tsx
@@ -0,0 +1,125 @@
+"use client";
+
+import * as DialogPrimitive from "@radix-ui/react-dialog";
+import { X } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Dialog = DialogPrimitive.Root;
+
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = DialogPrimitive.Portal;
+
+const DialogClose = DialogPrimitive.Close;
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+));
+DialogContent.displayName = DialogPrimitive.Content.displayName;
+
+function DialogHeader({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+DialogHeader.displayName = "DialogHeader";
+
+function DialogFooter({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+DialogFooter.displayName = "DialogFooter";
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
+
+export {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogOverlay,
+ DialogPortal,
+ DialogTitle,
+ DialogTrigger,
+};
diff --git a/packages/ui/src/components/ui/drawer.tsx b/packages/ui/src/components/ui/drawer.tsx
new file mode 100644
index 0000000..ed7a7cd
--- /dev/null
+++ b/packages/ui/src/components/ui/drawer.tsx
@@ -0,0 +1,126 @@
+"use client";
+
+import * as React from "react";
+import { Drawer as DrawerPrimitive } from "vaul";
+import { cn } from "../../lib/utils";
+
+function Drawer({
+ shouldScaleBackground = true,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+Drawer.displayName = "Drawer";
+
+const DrawerTrigger = DrawerPrimitive.Trigger;
+
+const DrawerPortal = DrawerPrimitive.Portal;
+
+const DrawerClose = DrawerPrimitive.Close;
+
+const DrawerOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
+
+const DrawerContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+));
+DrawerContent.displayName = "DrawerContent";
+
+function DrawerHeader({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+DrawerHeader.displayName = "DrawerHeader";
+
+function DrawerFooter({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+DrawerFooter.displayName = "DrawerFooter";
+
+const DrawerTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
+
+const DrawerDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
+
+export {
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerOverlay,
+ DrawerPortal,
+ DrawerTitle,
+ DrawerTrigger,
+};
diff --git a/packages/ui/src/components/ui/dropdown-menu.tsx b/packages/ui/src/components/ui/dropdown-menu.tsx
new file mode 100644
index 0000000..d6b3b2e
--- /dev/null
+++ b/packages/ui/src/components/ui/dropdown-menu.tsx
@@ -0,0 +1,202 @@
+"use client";
+
+import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
+import { Check, ChevronRight, Circle } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const DropdownMenu = DropdownMenuPrimitive.Root;
+
+const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
+
+const DropdownMenuGroup = DropdownMenuPrimitive.Group;
+
+const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
+
+const DropdownMenuSub = DropdownMenuPrimitive.Sub;
+
+const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
+
+const DropdownMenuSubTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean;
+ }
+>(({ className, inset, children, ...props }, ref) => (
+
+ {children}
+
+
+));
+DropdownMenuSubTrigger.displayName =
+ DropdownMenuPrimitive.SubTrigger.displayName;
+
+const DropdownMenuSubContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DropdownMenuSubContent.displayName =
+ DropdownMenuPrimitive.SubContent.displayName;
+
+const DropdownMenuContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+
+
+));
+DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
+
+const DropdownMenuItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean;
+ }
+>(({ className, inset, ...props }, ref) => (
+
+));
+DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
+
+const DropdownMenuCheckboxItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, checked, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+));
+DropdownMenuCheckboxItem.displayName =
+ DropdownMenuPrimitive.CheckboxItem.displayName;
+
+const DropdownMenuRadioItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+));
+DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
+
+const DropdownMenuLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean;
+ }
+>(({ className, inset, ...props }, ref) => (
+
+));
+DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
+
+const DropdownMenuSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
+
+function DropdownMenuShortcut({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
+
+export {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuPortal,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuTrigger,
+};
diff --git a/packages/ui/src/components/ui/form.tsx b/packages/ui/src/components/ui/form.tsx
new file mode 100644
index 0000000..87d178d
--- /dev/null
+++ b/packages/ui/src/components/ui/form.tsx
@@ -0,0 +1,175 @@
+/* eslint-disable @typescript-eslint/no-unnecessary-condition -- no*/
+import type * as LabelPrimitive from "@radix-ui/react-label";
+import { Slot } from "@radix-ui/react-slot";
+import * as React from "react";
+import {
+ Controller,
+ FormProvider,
+ useFormContext,
+ type ControllerProps,
+ type FieldPath,
+ type FieldValues,
+} from "react-hook-form";
+import { cn } from "../../lib/utils";
+import { Label } from "./label";
+
+const Form = FormProvider;
+
+interface FormFieldContextValue<
+ TFieldValues extends FieldValues = FieldValues,
+ TName extends FieldPath = FieldPath,
+> {
+ name: TName;
+}
+
+const FormFieldContext = React.createContext(
+ {} as FormFieldContextValue,
+);
+
+function FormField<
+ TFieldValues extends FieldValues = FieldValues,
+ TName extends FieldPath = FieldPath,
+>({ ...props }: ControllerProps) {
+ return (
+
+
+
+ );
+}
+
+const useFormField = () => {
+ const fieldContext = React.useContext(FormFieldContext);
+ const itemContext = React.useContext(FormItemContext);
+ const { getFieldState, formState } = useFormContext();
+
+ const fieldState = getFieldState(fieldContext.name, formState);
+
+ if (!fieldContext) {
+ throw new Error("useFormField should be used within ");
+ }
+
+ const { id } = itemContext;
+
+ return {
+ id,
+ name: fieldContext.name,
+ formItemId: `${id}-form-item`,
+ formDescriptionId: `${id}-form-item-description`,
+ formMessageId: `${id}-form-item-message`,
+ ...fieldState,
+ };
+};
+
+interface FormItemContextValue {
+ id: string;
+}
+
+const FormItemContext = React.createContext(
+ {} as FormItemContextValue,
+);
+
+const FormItem = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const id = React.useId();
+
+ return (
+
+
+
+ );
+});
+FormItem.displayName = "FormItem";
+
+const FormLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ const { error, formItemId } = useFormField();
+
+ return (
+
+ );
+});
+FormLabel.displayName = "FormLabel";
+
+const FormControl = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ ...props }, ref) => {
+ const { error, formItemId, formDescriptionId, formMessageId } =
+ useFormField();
+
+ return (
+
+ );
+});
+FormControl.displayName = "FormControl";
+
+const FormDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { formDescriptionId } = useFormField();
+
+ return (
+
+ );
+});
+FormDescription.displayName = "FormDescription";
+
+const FormMessage = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, children, ...props }, ref) => {
+ const { error, formMessageId } = useFormField();
+ const body = error ? String(error?.message) : children;
+
+ if (!body) {
+ return null;
+ }
+
+ return (
+
+ {body}
+
+ );
+});
+FormMessage.displayName = "FormMessage";
+
+export {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+ useFormField,
+};
diff --git a/packages/ui/src/components/ui/hover-card.tsx b/packages/ui/src/components/ui/hover-card.tsx
new file mode 100644
index 0000000..09524f4
--- /dev/null
+++ b/packages/ui/src/components/ui/hover-card.tsx
@@ -0,0 +1,28 @@
+"use client";
+
+import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const HoverCard = HoverCardPrimitive.Root;
+
+const HoverCardTrigger = HoverCardPrimitive.Trigger;
+
+const HoverCardContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+));
+HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
+
+export { HoverCard, HoverCardContent, HoverCardTrigger };
diff --git a/packages/ui/src/components/ui/index.ts b/packages/ui/src/components/ui/index.ts
index 98d55ac..91d037f 100644
--- a/packages/ui/src/components/ui/index.ts
+++ b/packages/ui/src/components/ui/index.ts
@@ -1 +1,42 @@
+export * from "./accordion";
+export * from "./alert";
+export * from "./alert-dialog";
+export * from "./aspect-ratio";
+export * from "./avatar";
+export * from "./badge";
+export * from "./breadcrumb";
export * from "./button";
+export * from "./calendar";
+export * from "./card";
+export * from "./carousel";
+export * from "./checkbox";
+export * from "./collapsible";
+export * from "./command";
+export * from "./context-menu";
+export * from "./dialog";
+export * from "./drawer";
+export * from "./dropdown-menu";
+export * from "./form";
+export * from "./hover-card";
+export * from "./input";
+export * from "./label";
+export * from "./menubar";
+export * from "./pagination";
+export * from "./popover";
+export * from "./progress";
+export * from "./radio-group";
+export * from "./resizable";
+export * from "./scroll-area";
+export * from "./select";
+export * from "./separator";
+export * from "./sheet";
+export * from "./skeleton";
+export * from "./slider";
+export * from "./sonner";
+export * from "./switch";
+export * from "./table";
+export * from "./tabs";
+export * from "./textarea";
+export * from "./toggle";
+export * from "./toggle-group";
+export * from "./tooltip";
diff --git a/packages/ui/src/components/ui/input.tsx b/packages/ui/src/components/ui/input.tsx
new file mode 100644
index 0000000..ee2017f
--- /dev/null
+++ b/packages/ui/src/components/ui/input.tsx
@@ -0,0 +1,23 @@
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+export type InputProps = React.InputHTMLAttributes;
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+Input.displayName = "Input";
+
+export { Input };
diff --git a/packages/ui/src/components/ui/label.tsx b/packages/ui/src/components/ui/label.tsx
new file mode 100644
index 0000000..39edf51
--- /dev/null
+++ b/packages/ui/src/components/ui/label.tsx
@@ -0,0 +1,25 @@
+"use client";
+
+import * as LabelPrimitive from "@radix-ui/react-label";
+import { cva, type VariantProps } from "class-variance-authority";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
+);
+
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, ...props }, ref) => (
+
+));
+Label.displayName = LabelPrimitive.Root.displayName;
+
+export { Label };
diff --git a/packages/ui/src/components/ui/menubar.tsx b/packages/ui/src/components/ui/menubar.tsx
new file mode 100644
index 0000000..6561648
--- /dev/null
+++ b/packages/ui/src/components/ui/menubar.tsx
@@ -0,0 +1,241 @@
+"use client";
+
+import * as MenubarPrimitive from "@radix-ui/react-menubar";
+import { Check, ChevronRight, Circle } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const MenubarMenu = MenubarPrimitive.Menu;
+
+const MenubarGroup = MenubarPrimitive.Group;
+
+const MenubarPortal = MenubarPrimitive.Portal;
+
+const MenubarSub = MenubarPrimitive.Sub;
+
+const MenubarRadioGroup = MenubarPrimitive.RadioGroup;
+
+const Menubar = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+Menubar.displayName = MenubarPrimitive.Root.displayName;
+
+const MenubarTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName;
+
+const MenubarSubTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean;
+ }
+>(({ className, inset, children, ...props }, ref) => (
+
+ {children}
+
+
+));
+MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName;
+
+const MenubarSubContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName;
+
+const MenubarContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(
+ (
+ {
+ className,
+ align = "start",
+ alignOffset = -4,
+ sideOffset = 8,
+ ...props
+ },
+ ref,
+ ) => (
+
+
+
+ ),
+);
+MenubarContent.displayName = MenubarPrimitive.Content.displayName;
+
+const MenubarItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean;
+ }
+>(({ className, inset, ...props }, ref) => (
+
+));
+MenubarItem.displayName = MenubarPrimitive.Item.displayName;
+
+const MenubarCheckboxItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, checked, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+));
+MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName;
+
+const MenubarRadioItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+));
+MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName;
+
+const MenubarLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean;
+ }
+>(({ className, inset, ...props }, ref) => (
+
+));
+MenubarLabel.displayName = MenubarPrimitive.Label.displayName;
+
+const MenubarSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName;
+
+function MenubarShortcut({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+MenubarShortcut.displayname = "MenubarShortcut";
+
+export {
+ Menubar,
+ MenubarCheckboxItem,
+ MenubarContent,
+ MenubarGroup,
+ MenubarItem,
+ MenubarLabel,
+ MenubarMenu,
+ MenubarPortal,
+ MenubarRadioGroup,
+ MenubarRadioItem,
+ MenubarSeparator,
+ MenubarShortcut,
+ MenubarSub,
+ MenubarSubContent,
+ MenubarSubTrigger,
+ MenubarTrigger,
+};
diff --git a/packages/ui/src/components/ui/pagination.tsx b/packages/ui/src/components/ui/pagination.tsx
new file mode 100644
index 0000000..18662b1
--- /dev/null
+++ b/packages/ui/src/components/ui/pagination.tsx
@@ -0,0 +1,131 @@
+import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
+import * as React from "react";
+import { buttonVariants, type ButtonProps } from "src/components/ui/button";
+import { cn } from "../../lib/utils";
+
+function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
+ return (
+
+ );
+}
+Pagination.displayName = "Pagination";
+
+const PaginationContent = React.forwardRef<
+ HTMLUListElement,
+ React.ComponentProps<"ul">
+>(({ className, ...props }, ref) => (
+
+));
+PaginationContent.displayName = "PaginationContent";
+
+const PaginationItem = React.forwardRef<
+ HTMLLIElement,
+ React.ComponentProps<"li">
+>(({ className, ...props }, ref) => (
+
+));
+PaginationItem.displayName = "PaginationItem";
+
+type PaginationLinkProps = {
+ isActive?: boolean;
+} & Pick &
+ React.ComponentProps<"a">;
+
+function PaginationLink({
+ className,
+ isActive,
+ size = "icon",
+ ...props
+}: PaginationLinkProps) {
+ return (
+
+ {props.children}
+
+ );
+}
+PaginationLink.displayName = "PaginationLink";
+
+function PaginationPrevious({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+ Previous
+
+ );
+}
+PaginationPrevious.displayName = "PaginationPrevious";
+
+function PaginationNext({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ Next
+
+
+ );
+}
+PaginationNext.displayName = "PaginationNext";
+
+function PaginationEllipsis({
+ className,
+ ...props
+}: React.ComponentProps<"span">) {
+ return (
+
+
+ More pages
+
+ );
+}
+PaginationEllipsis.displayName = "PaginationEllipsis";
+
+export {
+ Pagination,
+ PaginationContent,
+ PaginationEllipsis,
+ PaginationItem,
+ PaginationLink,
+ PaginationNext,
+ PaginationPrevious,
+};
diff --git a/packages/ui/src/components/ui/popover.tsx b/packages/ui/src/components/ui/popover.tsx
new file mode 100644
index 0000000..ed7a449
--- /dev/null
+++ b/packages/ui/src/components/ui/popover.tsx
@@ -0,0 +1,30 @@
+"use client";
+
+import * as PopoverPrimitive from "@radix-ui/react-popover";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Popover = PopoverPrimitive.Root;
+
+const PopoverTrigger = PopoverPrimitive.Trigger;
+
+const PopoverContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+
+
+));
+PopoverContent.displayName = PopoverPrimitive.Content.displayName;
+
+export { Popover, PopoverContent, PopoverTrigger };
diff --git a/packages/ui/src/components/ui/progress.tsx b/packages/ui/src/components/ui/progress.tsx
new file mode 100644
index 0000000..7a08211
--- /dev/null
+++ b/packages/ui/src/components/ui/progress.tsx
@@ -0,0 +1,28 @@
+/* eslint-disable @typescript-eslint/restrict-template-expressions -- fix */
+"use client";
+
+import * as ProgressPrimitive from "@radix-ui/react-progress";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Progress = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, value, ...props }, ref) => (
+
+
+
+));
+Progress.displayName = ProgressPrimitive.Root.displayName;
+
+export { Progress };
diff --git a/packages/ui/src/components/ui/radio-group.tsx b/packages/ui/src/components/ui/radio-group.tsx
new file mode 100644
index 0000000..f43346b
--- /dev/null
+++ b/packages/ui/src/components/ui/radio-group.tsx
@@ -0,0 +1,43 @@
+"use client";
+
+import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
+import { Circle } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const RadioGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ return (
+
+ );
+});
+RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
+
+const RadioGroupItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ return (
+
+
+
+
+
+ );
+});
+RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
+
+export { RadioGroup, RadioGroupItem };
diff --git a/packages/ui/src/components/ui/resizable.tsx b/packages/ui/src/components/ui/resizable.tsx
new file mode 100644
index 0000000..dcf6d1b
--- /dev/null
+++ b/packages/ui/src/components/ui/resizable.tsx
@@ -0,0 +1,48 @@
+"use client";
+
+import { GripVertical } from "lucide-react";
+import * as ResizablePrimitive from "react-resizable-panels";
+import { cn } from "../../lib/utils";
+
+function ResizablePanelGroup({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
+
+const ResizablePanel = ResizablePrimitive.Panel;
+
+function ResizableHandle({
+ withHandle,
+ className,
+ ...props
+}: React.ComponentProps & {
+ withHandle?: boolean;
+}) {
+ return (
+ div]:rotate-90",
+ className,
+ )}
+ {...props}
+ >
+ {withHandle ? (
+
+
+
+ ) : null}
+
+ );
+}
+
+export { ResizableHandle, ResizablePanel, ResizablePanelGroup };
diff --git a/packages/ui/src/components/ui/scroll-area.tsx b/packages/ui/src/components/ui/scroll-area.tsx
new file mode 100644
index 0000000..3822f0f
--- /dev/null
+++ b/packages/ui/src/components/ui/scroll-area.tsx
@@ -0,0 +1,49 @@
+"use client";
+
+import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const ScrollArea = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+ {children}
+
+
+
+
+));
+ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
+
+const ScrollBar = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef<
+ typeof ScrollAreaPrimitive.ScrollAreaScrollbar
+ >
+>(({ className, orientation = "vertical", ...props }, ref) => (
+
+
+
+));
+ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
+
+export { ScrollArea, ScrollBar };
diff --git a/packages/ui/src/components/ui/select.tsx b/packages/ui/src/components/ui/select.tsx
new file mode 100644
index 0000000..a30e917
--- /dev/null
+++ b/packages/ui/src/components/ui/select.tsx
@@ -0,0 +1,159 @@
+"use client";
+
+import * as SelectPrimitive from "@radix-ui/react-select";
+import { Check, ChevronDown, ChevronUp } from "lucide-react";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Select = SelectPrimitive.Root;
+
+const SelectGroup = SelectPrimitive.Group;
+
+const SelectValue = SelectPrimitive.Value;
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName;
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = "popper", ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName;
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+
+ {children}
+
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
+
+export {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectScrollDownButton,
+ SelectScrollUpButton,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue,
+};
diff --git a/packages/ui/src/components/ui/separator.tsx b/packages/ui/src/components/ui/separator.tsx
new file mode 100644
index 0000000..1a1c624
--- /dev/null
+++ b/packages/ui/src/components/ui/separator.tsx
@@ -0,0 +1,32 @@
+"use client";
+
+import * as SeparatorPrimitive from "@radix-ui/react-separator";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Separator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(
+ (
+ { className, orientation = "horizontal", decorative = true, ...props },
+ ref,
+ ) => (
+
+ ),
+);
+Separator.displayName = SeparatorPrimitive.Root.displayName;
+
+export { Separator };
diff --git a/packages/ui/src/components/ui/sheet.tsx b/packages/ui/src/components/ui/sheet.tsx
new file mode 100644
index 0000000..203d115
--- /dev/null
+++ b/packages/ui/src/components/ui/sheet.tsx
@@ -0,0 +1,141 @@
+"use client";
+
+import * as SheetPrimitive from "@radix-ui/react-dialog";
+import { cva, type VariantProps } from "class-variance-authority";
+import { X } from "lucide-react";
+import * as React from "react";
+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,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, 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,
+ VariantProps {}
+
+const SheetContent = React.forwardRef<
+ React.ElementRef,
+ SheetContentProps
+>(({ side = "right", className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+));
+SheetContent.displayName = SheetPrimitive.Content.displayName;
+
+function SheetHeader({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+SheetHeader.displayName = "SheetHeader";
+
+function SheetFooter({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+SheetFooter.displayName = "SheetFooter";
+
+const SheetTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SheetTitle.displayName = SheetPrimitive.Title.displayName;
+
+const SheetDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SheetDescription.displayName = SheetPrimitive.Description.displayName;
+
+export {
+ Sheet,
+ SheetClose,
+ SheetContent,
+ SheetDescription,
+ SheetFooter,
+ SheetHeader,
+ SheetOverlay,
+ SheetPortal,
+ SheetTitle,
+ SheetTrigger,
+};
diff --git a/packages/ui/src/components/ui/skeleton.tsx b/packages/ui/src/components/ui/skeleton.tsx
new file mode 100644
index 0000000..67b058c
--- /dev/null
+++ b/packages/ui/src/components/ui/skeleton.tsx
@@ -0,0 +1,15 @@
+import { cn } from "../../lib/utils";
+
+function Skeleton({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+
+export { Skeleton };
diff --git a/packages/ui/src/components/ui/slider.tsx b/packages/ui/src/components/ui/slider.tsx
new file mode 100644
index 0000000..a743dd6
--- /dev/null
+++ b/packages/ui/src/components/ui/slider.tsx
@@ -0,0 +1,27 @@
+"use client";
+
+import * as SliderPrimitive from "@radix-ui/react-slider";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Slider = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+
+));
+Slider.displayName = SliderPrimitive.Root.displayName;
+
+export { Slider };
diff --git a/packages/ui/src/components/ui/sonner.tsx b/packages/ui/src/components/ui/sonner.tsx
new file mode 100644
index 0000000..6d338ce
--- /dev/null
+++ b/packages/ui/src/components/ui/sonner.tsx
@@ -0,0 +1,30 @@
+"use client";
+
+import { useTheme } from "next-themes";
+import { Toaster as Sonner } from "sonner";
+
+type ToasterProps = React.ComponentProps;
+
+function Toaster({ ...props }: ToasterProps) {
+ const { theme = "system" } = useTheme();
+
+ return (
+
+ );
+}
+
+export { Toaster };
diff --git a/packages/ui/src/components/ui/switch.tsx b/packages/ui/src/components/ui/switch.tsx
new file mode 100644
index 0000000..8329f08
--- /dev/null
+++ b/packages/ui/src/components/ui/switch.tsx
@@ -0,0 +1,28 @@
+"use client";
+
+import * as SwitchPrimitives from "@radix-ui/react-switch";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Switch = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+Switch.displayName = SwitchPrimitives.Root.displayName;
+
+export { Switch };
diff --git a/packages/ui/src/components/ui/table.tsx b/packages/ui/src/components/ui/table.tsx
new file mode 100644
index 0000000..a819ddd
--- /dev/null
+++ b/packages/ui/src/components/ui/table.tsx
@@ -0,0 +1,119 @@
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Table = React.forwardRef<
+ HTMLTableElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+Table.displayName = "Table";
+
+const TableHeader = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+TableHeader.displayName = "TableHeader";
+
+const TableBody = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+TableBody.displayName = "TableBody";
+
+const TableFooter = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+ tr]:last:border-b-0",
+ className,
+ )}
+ {...props}
+ />
+));
+TableFooter.displayName = "TableFooter";
+
+const TableRow = React.forwardRef<
+ HTMLTableRowElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+TableRow.displayName = "TableRow";
+
+const TableHead = React.forwardRef<
+ HTMLTableCellElement,
+ React.ThHTMLAttributes
+>(({ className, ...props }, ref) => (
+ |
+));
+TableHead.displayName = "TableHead";
+
+const TableCell = React.forwardRef<
+ HTMLTableCellElement,
+ React.TdHTMLAttributes
+>(({ className, ...props }, ref) => (
+ |
+));
+TableCell.displayName = "TableCell";
+
+const TableCaption = React.forwardRef<
+ HTMLTableCaptionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+TableCaption.displayName = "TableCaption";
+
+export {
+ Table,
+ TableBody,
+ TableCaption,
+ TableCell,
+ TableFooter,
+ TableHead,
+ TableHeader,
+ TableRow,
+};
diff --git a/packages/ui/src/components/ui/tabs.tsx b/packages/ui/src/components/ui/tabs.tsx
new file mode 100644
index 0000000..7c505d9
--- /dev/null
+++ b/packages/ui/src/components/ui/tabs.tsx
@@ -0,0 +1,54 @@
+"use client";
+
+import * as TabsPrimitive from "@radix-ui/react-tabs";
+import * as React from "react";
+import { cn } from "../../lib/utils";
+
+const Tabs = TabsPrimitive.Root;
+
+const TabsList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+TabsList.displayName = TabsPrimitive.List.displayName;
+
+const TabsTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+