diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 27adc68..7245d9b 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -2,136 +2,89 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { z } from 'zod'; import { toast } from 'sonner'; -import { api } from '@/lib/api'; -import { useAuthStore } from '@/lib/auth-store'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Loader2 } from 'lucide-react'; - -const loginSchema = z.object({ - tenantSlug: z.string().min(1, 'Tenant slug is required'), - email: z.string().email('Invalid email address'), - password: z.string().min(6, 'Password must be at least 6 characters'), -}); - -type LoginForm = z.infer; +import api from '@/lib/api'; +import { useAuthStore } from '@/lib/auth-store'; export default function LoginPage() { const router = useRouter(); const setAuth = useAuthStore((s) => s.setAuth); const [loading, setLoading] = useState(false); + const [form, setForm] = useState({ tenantSlug: '', email: '', password: '' }); + const [errors, setErrors] = useState>({}); - const { - register, - handleSubmit, - formState: { errors }, - } = useForm({ - resolver: zodResolver(loginSchema), - }); + const validate = () => { + const e: Record = {}; + if (!form.tenantSlug) e.tenantSlug = 'Required'; + if (!form.email || !/\S+@\S+\.\S+/.test(form.email)) e.email = 'Valid email required'; + if (!form.password || form.password.length < 6) e.password = 'Min 6 characters'; + setErrors(e); + return Object.keys(e).length === 0; + }; - const onSubmit = async (data: LoginForm) => { + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!validate()) return; setLoading(true); try { - const res = await api.post('/api/v1/auth/login', data); + const res = await api.post('/api/v1/auth/login', form); const { accessToken, user } = res.data; - setAuth({ accessToken, tenantSlug: data.tenantSlug, user }); - toast.success('Welcome back, ' + user.firstName + '!'); + setAuth({ accessToken, tenantSlug: form.tenantSlug, user }); + toast.success(`Welcome back, ${user.firstName}!`); router.push('/dashboard'); - } catch (err: unknown) { - const error = err as { response?: { data?: { message?: string } } }; - toast.error(error.response?.data?.message || 'Login failed. Check your credentials.'); + } catch (err: any) { + toast.error(err.response?.data?.message || 'Login failed. Check your credentials.'); } finally { setLoading(false); } }; + const field = (key: keyof typeof form) => ({ + value: form[key], + onChange: (e: React.ChangeEvent) => + setForm(f => ({ ...f, [key]: e.target.value })), + }); + + const inputClass = (key: string) => + `w-full border rounded-lg px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 ${errors[key] ? 'border-red-400' : 'border-gray-300'}`; + return ( -
-
+
+
{/* Logo */}
-
- F -
-

FiberOps

-

ISP Management Platform

+
F
+

FiberOps

+

ISP Management Platform

- - - Sign in to your account - Enter your tenant and credentials to continue - - -
-
- - - {errors.tenantSlug && ( -

{errors.tenantSlug.message}

- )} -
- -
- - - {errors.email && ( -

{errors.email.message}

- )} -
- -
- - - {errors.password && ( -

{errors.password.message}

- )} -
- - -
-
-
+
+

Sign in to your account

+
+
+ + + {errors.tenantSlug &&

{errors.tenantSlug}

} +
+
+ + + {errors.email &&

{errors.email}

} +
+
+ + + {errors.password &&

{errors.password}

} +
+ +
+
); diff --git a/components/layout/topbar.tsx b/components/layout/topbar.tsx index 7246497..5931000 100644 --- a/components/layout/topbar.tsx +++ b/components/layout/topbar.tsx @@ -2,7 +2,6 @@ import { useRouter } from 'next/navigation'; import { useAuthStore } from '@/lib/auth-store'; -import { Button } from '@/components/ui/button'; import { LogOut, User } from 'lucide-react'; export default function Topbar() { @@ -14,33 +13,27 @@ export default function Topbar() { router.push('/login'); }; - const fullName = user ? `${user.firstName} ${user.lastName}` : 'Admin'; + const fullName = user ? `${user.firstName ?? ''} ${user.lastName ?? ''}`.trim() : 'Admin'; return ( -
+
{fullName} {user?.roles?.[0] && ( - + {user.roles[0]} )}
- +
); diff --git a/components/providers.tsx b/components/providers.tsx index 7452291..ff021d1 100644 --- a/components/providers.tsx +++ b/components/providers.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { Toaster } from '@/components/ui/sonner'; +import { Toaster } from 'sonner'; export default function Providers({ children }: { children: React.ReactNode }) { // Must be created inside component — NOT at module level diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx deleted file mode 100644 index f1a08c8..0000000 --- a/components/ui/badge.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils" - -const badgeVariants = cva( - "inline-flex items-center rounded-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/components/ui/button.tsx b/components/ui/button.tsx deleted file mode 100644 index 208363e..0000000 --- a/components/ui/button.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils" - -const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", - outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", - secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: "h-10 px-4 py-2", - sm: "h-9 rounded-md px-3", - lg: "h-11 rounded-md px-8", - icon: "h-10 w-10", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" - return ( - - ) - } -) -Button.displayName = "Button" - -export { Button, buttonVariants } diff --git a/components/ui/card.tsx b/components/ui/card.tsx deleted file mode 100644 index 1fc994d..0000000 --- a/components/ui/card.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import * as React from "react" -import { cn } from "@/lib/utils" - -const Card = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -) -Card.displayName = "Card" - -const CardHeader = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -) -CardHeader.displayName = "CardHeader" - -const CardTitle = React.forwardRef>( - ({ className, ...props }, ref) => ( -

- ) -) -CardTitle.displayName = "CardTitle" - -const CardDescription = React.forwardRef>( - ({ className, ...props }, ref) => ( -

- ) -) -CardDescription.displayName = "CardDescription" - -const CardContent = React.forwardRef>( - ({ className, ...props }, ref) => ( -

- ) -) -CardContent.displayName = "CardContent" - -const CardFooter = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -) -CardFooter.displayName = "CardFooter" - -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx deleted file mode 100644 index fddfb53..0000000 --- a/components/ui/dialog.tsx +++ /dev/null @@ -1,89 +0,0 @@ -"use client" - -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { X } from "lucide-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 - -const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( -
-) -DialogHeader.displayName = "DialogHeader" - -const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( -
-) -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, DialogPortal, DialogOverlay, DialogClose, DialogTrigger, - DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, -} diff --git a/components/ui/input.tsx b/components/ui/input.tsx deleted file mode 100644 index 20abc02..0000000 --- a/components/ui/input.tsx +++ /dev/null @@ -1,23 +0,0 @@ -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/components/ui/label.tsx b/components/ui/label.tsx deleted file mode 100644 index 6918437..0000000 --- a/components/ui/label.tsx +++ /dev/null @@ -1,25 +0,0 @@ -"use client" - -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { cva, type VariantProps } from "class-variance-authority" -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/components/ui/select.tsx b/components/ui/select.tsx deleted file mode 100644 index 17da16a..0000000 --- a/components/ui/select.tsx +++ /dev/null @@ -1,149 +0,0 @@ -"use client" - -import * as React from "react" -import * as SelectPrimitive from "@radix-ui/react-select" -import { Check, ChevronDown, ChevronUp } from "lucide-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, - SelectGroup, - SelectValue, - SelectTrigger, - SelectScrollUpButton, - SelectScrollDownButton, - SelectContent, - SelectLabel, - SelectItem, - SelectSeparator, -} diff --git a/components/ui/sheet.tsx b/components/ui/sheet.tsx deleted file mode 100644 index cb9ae31..0000000 --- a/components/ui/sheet.tsx +++ /dev/null @@ -1,100 +0,0 @@ -"use client" - -import * as React from "react" -import * as SheetPrimitive from "@radix-ui/react-dialog" -import { cva, type VariantProps } from "class-variance-authority" -import { X } from "lucide-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 - -const SheetHeader = ({ className, ...props }: React.HTMLAttributes) => ( -
-) -SheetHeader.displayName = "SheetHeader" - -const SheetFooter = ({ className, ...props }: React.HTMLAttributes) => ( -
-) -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, SheetPortal, SheetOverlay, SheetTrigger, SheetClose, - SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, -} diff --git a/components/ui/skeleton.tsx b/components/ui/skeleton.tsx deleted file mode 100644 index 01b8b6d..0000000 --- a/components/ui/skeleton.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { cn } from "@/lib/utils" - -function Skeleton({ - className, - ...props -}: React.HTMLAttributes) { - return ( -
- ) -} - -export { Skeleton } diff --git a/components/ui/sonner.tsx b/components/ui/sonner.tsx deleted file mode 100644 index 6f75e01..0000000 --- a/components/ui/sonner.tsx +++ /dev/null @@ -1,26 +0,0 @@ -"use client" - -import { Toaster as Sonner } from "sonner" - -type ToasterProps = React.ComponentProps - -const Toaster = ({ ...props }: ToasterProps) => { - return ( - - ) -} - -export { Toaster } diff --git a/components/ui/table.tsx b/components/ui/table.tsx deleted file mode 100644 index 40c3e1d..0000000 --- a/components/ui/table.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import * as React from "react" -import { cn } from "@/lib/utils" - -const Table = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- - - ) -) -Table.displayName = "Table" - -const TableHeader = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ) -) -TableHeader.displayName = "TableHeader" - -const TableBody = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ) -) -TableBody.displayName = "TableBody" - -const TableFooter = React.forwardRef>( - ({ className, ...props }, ref) => ( - tr]:last:border-b-0", className)} {...props} /> - ) -) -TableFooter.displayName = "TableFooter" - -const TableRow = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ) -) -TableRow.displayName = "TableRow" - -const TableHead = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -) -TableHead.displayName = "TableHead" - -const TableCell = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ) -) -TableCell.displayName = "TableCell" - -const TableCaption = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -) -TableCaption.displayName = "TableCaption" - -export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption } diff --git a/components/ui/tabs.tsx b/components/ui/tabs.tsx deleted file mode 100644 index 5547bd8..0000000 --- a/components/ui/tabs.tsx +++ /dev/null @@ -1,54 +0,0 @@ -"use client" - -import * as React from "react" -import * as TabsPrimitive from "@radix-ui/react-tabs" -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) => ( - -)) -TabsTrigger.displayName = TabsPrimitive.Trigger.displayName - -const TabsContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -TabsContent.displayName = TabsPrimitive.Content.displayName - -export { Tabs, TabsList, TabsTrigger, TabsContent }