From 7389de4c76bf84df42647469f8c6576c4672627c Mon Sep 17 00:00:00 2001 From: i701 Date: Sun, 24 Nov 2024 23:30:44 +0500 Subject: [PATCH] first commit --- .gitignore | 4 + actions/auth-actions.ts | 79 + app/(auth)/login/page.tsx | 19 + app/(auth)/signup/page.tsx | 19 + app/(auth)/verify-otp/page.tsx | 19 + app/(dashboard)/devices/page.tsx | 5 + app/(dashboard)/layout.tsx | 9 + app/(dashboard)/payments/page.tsx | 17 + app/api/auth/[...all]/route.ts | 4 + app/favicon.ico | Bin 25931 -> 15406 bytes app/globals.css | 87 +- app/layout.tsx | 51 +- app/page.tsx | 102 +- components.json | 21 + components/auth/account-popver.tsx | 49 + components/auth/application-layout.tsx | 60 + components/auth/login-form.tsx | 47 + components/auth/signup-form.tsx | 167 ++ components/auth/verify-otp-form.tsx | 89 + components/theme-provider.tsx | 10 + components/theme-toggle.tsx | 40 + components/ui/app-sidebar.tsx | 91 + components/ui/breadcrumb.tsx | 115 + components/ui/button.tsx | 57 + components/ui/calendar.tsx | 72 + components/ui/card.tsx | 76 + components/ui/collapsible.tsx | 11 + components/ui/command.tsx | 153 + components/ui/datepicker.tsx | 129 + components/ui/dialog.tsx | 122 + components/ui/dropdown-menu.tsx | 201 ++ components/ui/form.tsx | 178 ++ components/ui/input.tsx | 22 + components/ui/label.tsx | 26 + components/ui/phone-input.tsx | 168 ++ components/ui/popover.tsx | 33 + components/ui/scroll-area.tsx | 48 + components/ui/search-form.tsx | 28 + components/ui/select.tsx | 159 ++ components/ui/separator.tsx | 31 + components/ui/sheet.tsx | 140 + components/ui/sidebar.tsx | 772 +++++ components/ui/skeleton.tsx | 15 + components/ui/tooltip.tsx | 32 + hooks/use-mobile.tsx | 19 + lib/auth-client.ts | 7 + lib/auth.ts | 30 + lib/db.ts | 17 + lib/schemas.ts | 12 + lib/utils.ts | 6 + middleware.ts | 25 + package-lock.json | 2544 ++++++++++++++++- package.json | 36 +- .../20241124133417_add/migration.sql | 63 + .../20241124133512_add/migration.sql | 25 + prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 77 + public/logo.png | Bin 0 -> 163825 bytes tailwind.config.ts | 86 +- 59 files changed, 6368 insertions(+), 159 deletions(-) create mode 100644 actions/auth-actions.ts create mode 100644 app/(auth)/login/page.tsx create mode 100644 app/(auth)/signup/page.tsx create mode 100644 app/(auth)/verify-otp/page.tsx create mode 100644 app/(dashboard)/devices/page.tsx create mode 100644 app/(dashboard)/layout.tsx create mode 100644 app/(dashboard)/payments/page.tsx create mode 100644 app/api/auth/[...all]/route.ts create mode 100644 components.json create mode 100644 components/auth/account-popver.tsx create mode 100644 components/auth/application-layout.tsx create mode 100644 components/auth/login-form.tsx create mode 100644 components/auth/signup-form.tsx create mode 100644 components/auth/verify-otp-form.tsx create mode 100644 components/theme-provider.tsx create mode 100644 components/theme-toggle.tsx create mode 100644 components/ui/app-sidebar.tsx create mode 100644 components/ui/breadcrumb.tsx create mode 100644 components/ui/button.tsx create mode 100644 components/ui/calendar.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/collapsible.tsx create mode 100644 components/ui/command.tsx create mode 100644 components/ui/datepicker.tsx create mode 100644 components/ui/dialog.tsx create mode 100644 components/ui/dropdown-menu.tsx create mode 100644 components/ui/form.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/label.tsx create mode 100644 components/ui/phone-input.tsx create mode 100644 components/ui/popover.tsx create mode 100644 components/ui/scroll-area.tsx create mode 100644 components/ui/search-form.tsx create mode 100644 components/ui/select.tsx create mode 100644 components/ui/separator.tsx create mode 100644 components/ui/sheet.tsx create mode 100644 components/ui/sidebar.tsx create mode 100644 components/ui/skeleton.tsx create mode 100644 components/ui/tooltip.tsx create mode 100644 hooks/use-mobile.tsx create mode 100644 lib/auth-client.ts create mode 100644 lib/auth.ts create mode 100644 lib/db.ts create mode 100644 lib/schemas.ts create mode 100644 lib/utils.ts create mode 100644 middleware.ts create mode 100644 prisma/migrations/20241124133417_add/migration.sql create mode 100644 prisma/migrations/20241124133512_add/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma create mode 100644 public/logo.png diff --git a/.gitignore b/.gitignore index d32cc78..381079b 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,7 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + + +#sqlite +*.db \ No newline at end of file diff --git a/actions/auth-actions.ts b/actions/auth-actions.ts new file mode 100644 index 0000000..1b16f1f --- /dev/null +++ b/actions/auth-actions.ts @@ -0,0 +1,79 @@ +"use server"; + +import { authClient } from "@/lib/auth-client"; +import prisma from "@/lib/db"; +import type { signUpFormSchema } from "@/lib/schemas"; +import { redirect } from "next/navigation"; +import { z } from "zod"; + +const formSchema = z.object({ + phoneNumber: z + .string() + .regex(/^[7|9][0-9]{2}-[0-9]{4}$/, "Please enter a valid phone number"), +}); + +export async function signin( + currentState: { message: string; status: string }, + formData: FormData, +) { + const phoneNumber = formData.get("phoneNumber") as string; + const result = formSchema.safeParse({ phoneNumber }); + console.log(phoneNumber); + + if (!result.success) { + return { + message: result.error.errors[0].message, // Get the error message from Zod + status: "error", + }; + } + + if (!phoneNumber) { + return { + message: "Please enter a phone number", + status: "error", + }; + } + const NUMBER_WITH_COUNTRY_CODE: string = `+960${phoneNumber.split("-").join("")}`; + + const userExists = await prisma.user.findUnique({ + where: { + phoneNumber: NUMBER_WITH_COUNTRY_CODE, + }, + }); + if (!userExists) { + return redirect(`/signup?phone_number=${phoneNumber}`); + } + await authClient.phoneNumber.sendOtp({ + phoneNumber: NUMBER_WITH_COUNTRY_CODE, + }); + redirect("/verify-otp"); +} + +export async function signup({ + userData, +}: { userData: z.infer }) { + const newUser = await prisma.user.create({ + data: { ...userData, email: "" }, + }); + + redirect("/login"); +} + +export const sendOtp = async (phoneNumber: string, code: string) => { + // Implement sending OTP code via SMS + console.log("Send OTP server fn", phoneNumber, code); + const respose = await fetch("https://smsapi.sarlink.link/send", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + api_key: process.env.SMS_API_KEY, + number: phoneNumber, + text: `Your OTP code is ${code}`, + }), + }); + const data = await respose.json(); + console.log(data); + return data; +}; diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx new file mode 100644 index 0000000..4f10988 --- /dev/null +++ b/app/(auth)/login/page.tsx @@ -0,0 +1,19 @@ +import LoginForm from "@/components/auth/login-form"; +import Image from "next/image"; +import React from "react"; + +export default function LoginPage() { + return
+
+ Sar Link Logo +
+ +

SAR Link Portal

+

Pay for your devices and track your bills.

+
+ +
+
; +} + + diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx new file mode 100644 index 0000000..ea634bd --- /dev/null +++ b/app/(auth)/signup/page.tsx @@ -0,0 +1,19 @@ +import SignUpForm from "@/components/auth/signup-form"; +import Image from "next/image"; +import React from "react"; + +export default function LoginPage() { + return
+
+ Sar Link Logo +
+ +

SAR Link Portal

+

Pay for your devices and track your bills.

+
+ +
+
; +} + + diff --git a/app/(auth)/verify-otp/page.tsx b/app/(auth)/verify-otp/page.tsx new file mode 100644 index 0000000..575cf75 --- /dev/null +++ b/app/(auth)/verify-otp/page.tsx @@ -0,0 +1,19 @@ +import VerifyOTPForm from "@/components/auth/verify-otp-form"; +import Image from "next/image"; +import React from "react"; + +export default function VerifyOTP() { + return
+
+ Sar Link Logo +
+ +

SAR Link Portal

+

Pay for your devices and track your bills.

+
+ +
+
; +} + + diff --git a/app/(dashboard)/devices/page.tsx b/app/(dashboard)/devices/page.tsx new file mode 100644 index 0000000..eeae826 --- /dev/null +++ b/app/(dashboard)/devices/page.tsx @@ -0,0 +1,5 @@ +export default async function Devices() { + return
+

Devices

+
; +} diff --git a/app/(dashboard)/layout.tsx b/app/(dashboard)/layout.tsx new file mode 100644 index 0000000..dc3f380 --- /dev/null +++ b/app/(dashboard)/layout.tsx @@ -0,0 +1,9 @@ +import { ApplicationLayout } from "@/components/auth/application-layout"; + +export default function DashboardLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return {children}; +} diff --git a/app/(dashboard)/payments/page.tsx b/app/(dashboard)/payments/page.tsx new file mode 100644 index 0000000..90bbe8d --- /dev/null +++ b/app/(dashboard)/payments/page.tsx @@ -0,0 +1,17 @@ +'use client' +import { PhoneInput } from '@/components/ui/phone-input' +import React from 'react' + +export default function MyPayments() { + return ( +
+ +
+ + ) +} diff --git a/app/api/auth/[...all]/route.ts b/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..e11351a --- /dev/null +++ b/app/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { auth } from "@/lib/auth"; +import { toNextJsHandler } from "better-auth/next-js"; + +export const { GET, POST } = toNextJsHandler(auth.handler); diff --git a/app/favicon.ico b/app/favicon.ico index 718d6fea4835ec2d246af9800eddb7ffb276240c..34f1657fd5001e655ed769a66ba35325b08a8fd6 100644 GIT binary patch literal 15406 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 L|7ZT+?}7gUW1vjt literal 25931 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 LrJugUA?W`A8`#=m diff --git a/app/globals.css b/app/globals.css index 6b717ad..d4528b5 100644 --- a/app/globals.css +++ b/app/globals.css @@ -2,20 +2,87 @@ @tailwind components; @tailwind utilities; -:root { - --background: #ffffff; - --foreground: #171717; +body { + font-family: Arial, Helvetica, sans-serif; } -@media (prefers-color-scheme: dark) { +@layer base { :root { - --background: #0a0a0a; - --foreground: #ededed; + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 10% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + } + .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; } } -body { - color: var(--foreground); - background: var(--background); - font-family: Arial, Helvetica, sans-serif; +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } } diff --git a/app/layout.tsx b/app/layout.tsx index a36cde0..5358e7c 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,35 +1,40 @@ import type { Metadata } from "next"; -import localFont from "next/font/local"; import "./globals.css"; +import { ThemeProvider } from "@/components/theme-provider"; +import { Barlow } from "next/font/google"; +import NextTopLoader from 'nextjs-toploader'; +import { Toaster } from 'sonner' -const geistSans = localFont({ - src: "./fonts/GeistVF.woff", - variable: "--font-geist-sans", - weight: "100 900", -}); -const geistMono = localFont({ - src: "./fonts/GeistMonoVF.woff", - variable: "--font-geist-mono", - weight: "100 900", +const barlow = Barlow({ + subsets: ["latin"], + weight: ["100", "300", "400", "500", "600", "700", "800", "900"], + variable: "--font-barlow", }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Create Next App", + description: "Generated by create next app", }; export default function RootLayout({ - children, + children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode; }>) { - return ( - - - {children} - - - ); + return ( + + + + + + {children} + + + + ); } diff --git a/app/page.tsx b/app/page.tsx index 9007252..a8d17e8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,101 +1,5 @@ -import Image from "next/image"; +import { redirect } from "next/navigation"; -export default function Home() { - return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - app/page.tsx - - . -
  2. -
  3. Save and see your changes instantly.
  4. -
- - -
- -
- ); +export default async function Home() { + return redirect("/devices"); } diff --git a/components.json b/components.json new file mode 100644 index 0000000..a312865 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/components/auth/account-popver.tsx b/components/auth/account-popver.tsx new file mode 100644 index 0000000..9991782 --- /dev/null +++ b/components/auth/account-popver.tsx @@ -0,0 +1,49 @@ +'use client' +import { Button } from "@/components/ui/button" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" +import { authClient } from "@/lib/auth-client" +import type { User } from "better-auth" +import { Loader, User as UserIcon } from "lucide-react" +import { useRouter } from "next/navigation" +import { useState } from "react" + +export function AccountPopover({ user }: { user?: User }) { + const [loading, setLoading] = useState(false) + const router = useRouter() + return ( + + + + + +
+
+

{user?.name}

+

+ {user?.email} +

+
+ +
+
+
+ ) +} diff --git a/components/auth/application-layout.tsx b/components/auth/application-layout.tsx new file mode 100644 index 0000000..4d66be2 --- /dev/null +++ b/components/auth/application-layout.tsx @@ -0,0 +1,60 @@ +import { ModeToggle } from "@/components/theme-toggle"; +import { AppSidebar } from "@/components/ui/app-sidebar"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from "@/components/ui/breadcrumb"; +import { Separator } from "@/components/ui/separator"; +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@/components/ui/sidebar"; +import { auth } from "@/lib/auth"; +import { headers } from "next/headers"; +import { AccountPopover } from "./account-popver"; + +export async function ApplicationLayout({ children }: { children: React.ReactNode }) { + const session = await auth.api.getSession({ + headers: await headers(), // you need to pass the headers object. + }); + return ( + + + +
+
+ + + + + + + MENU + + + + + Sar Link Portal v1.0.0 + + + +
+ +
+ + +
+
+
+ + {children} +
+
+
+ ); +} diff --git a/components/auth/login-form.tsx b/components/auth/login-form.tsx new file mode 100644 index 0000000..dfe4b19 --- /dev/null +++ b/components/auth/login-form.tsx @@ -0,0 +1,47 @@ +"use client"; + + +import { Button } from "@/components/ui/button"; + +import { signin } from "@/actions/auth-actions"; +import { Loader } from "lucide-react"; +import Link from "next/link"; +import { useActionState } from "react"; +import { PhoneInput } from "../ui/phone-input"; + +export default function LoginForm() { + const [state, formAction, isPending] = useActionState(signin, { + message: "", + status: "", + }); + + return ( +
+

Login

+
+
+ + + {state.status === "error" && ( +

{state.message}

+ )} + +
+
+ Don't have an account?{" "} + + Sign up + +
+
+
+ ); +} diff --git a/components/auth/signup-form.tsx b/components/auth/signup-form.tsx new file mode 100644 index 0000000..12bcfe2 --- /dev/null +++ b/components/auth/signup-form.tsx @@ -0,0 +1,167 @@ +"use client"; +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { signUpFormSchema } from "@/lib/schemas"; +import { zodResolver } from "@hookform/resolvers/zod"; +import Link from "next/link"; +import { useSearchParams } from "next/navigation"; + +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import type { z } from "zod"; +import { DatePicker } from "../ui/datepicker"; + + +export default function SignUpForm() { + const params = useSearchParams(); + console.log(params); + const phone_number = params.get("phone_number"); + const form = useForm>({ + resolver: zodResolver(signUpFormSchema), + defaultValues: { + phoneNumber: phone_number ?? "", + name: "", + id_card: "", + house_name: "", + island: "F.Dharanboodhoo", + }, + }); + + function onSubmit(values: z.infer) { + console.log(values); + } + + return ( +
+ +

+ Sign up +

+
+ ( + + Name + + + + + + )} + /> + ( + + ID Card + + + + + + )} + /> + ( + + Island + + + + + + )} + /> + ( + + House Name + + + + + + )} + /> + ( + + Date of birth + + + + + + )} + /> + ( + + Phone number + +
+
+ + + +
+ +
+
+ +
+ )} + /> + +
+
+ Already have an account?{" "} + + login + +
+ +
+ + ); +} diff --git a/components/auth/verify-otp-form.tsx b/components/auth/verify-otp-form.tsx new file mode 100644 index 0000000..4bb3df1 --- /dev/null +++ b/components/auth/verify-otp-form.tsx @@ -0,0 +1,89 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { authClient } from "@/lib/auth-client"; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Loader } from "lucide-react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { useTransition } from "react"; +import { type SubmitHandler, useForm } from "react-hook-form"; +import { toast } from 'sonner' +import { z } from "zod"; +const OTPSchema = z.object({ + pin: z.string().min(6, { + message: "Your one-time password must be 6 characters.", + + }), +}); + +export default function VerifyOTPForm() { + const [isPending, startTransition] = useTransition(); + const router = useRouter(); + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm>({ + defaultValues: { + pin: "", + }, + resolver: zodResolver(OTPSchema), + }); + + const onSubmit: SubmitHandler> = (data) => { + console.log(data); + + startTransition(async () => { + const isVerified = await authClient.phoneNumber.verify({ + phoneNumber: "+9607780588", + code: data.pin, + }); + if (!isVerified.error) { + router.push("/devices"); + } else { + toast.error(isVerified.error.message); + } + }); + } + + return ( +
+

Verify OTP

+
+
+ + + {errors.pin && ( +

{errors.pin.message}

+ )} +
+ +
+
+ Go back to{" "} + + login + +
+
+ ); +} diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx new file mode 100644 index 0000000..628df08 --- /dev/null +++ b/components/theme-provider.tsx @@ -0,0 +1,10 @@ +"use client"; + +import { ThemeProvider as NextThemesProvider } from "next-themes"; + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children}; +} diff --git a/components/theme-toggle.tsx b/components/theme-toggle.tsx new file mode 100644 index 0000000..3b027a0 --- /dev/null +++ b/components/theme-toggle.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { Moon, Sun } from "lucide-react"; +import { useTheme } from "next-themes"; +import * as React from "react"; + +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; + +export function ModeToggle() { + const { setTheme } = useTheme(); + + return ( + + + + + + setTheme("light")}> + Light + + setTheme("dark")}> + Dark + + setTheme("system")}> + System + + + + ); +} diff --git a/components/ui/app-sidebar.tsx b/components/ui/app-sidebar.tsx new file mode 100644 index 0000000..205d5ec --- /dev/null +++ b/components/ui/app-sidebar.tsx @@ -0,0 +1,91 @@ +import { ChevronRight } from "lucide-react"; + +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { + Sidebar, + SidebarContent, + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarRail, +} from "@/components/ui/sidebar"; +import Link from "next/link"; + +const data = { + navMain: [ + { + title: "MENU", + url: "#", + items: [ + { + title: "Devices", + url: "/devices", + }, + { + title: "Payments", + url: "/payments", + }, + ], + }, + + ], +}; + +export function AppSidebar({ ...props }: React.ComponentProps) { + return ( + + +

+ Sar Link Portal +

+
+ + {/* We create a collapsible SidebarGroup for each parent. */} + {data.navMain.map((item) => ( + + + + + {item.title}{" "} + + + + + + + {item.items.map((item) => ( + + + + {item.title} + + + + ))} + + + + + + ))} + + +
+ ); +} diff --git a/components/ui/breadcrumb.tsx b/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..60e6c96 --- /dev/null +++ b/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>