diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 3b212035..6b405cfc 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -172,7 +172,7 @@ export default function Layout({ children }: LayoutProps) { className="flex items-center space-x-2 text-sm text-gray-700 hover:text-gray-900 focus:outline-none" > - {user?.name} + {user?.name || user?.full_name || user?.username} diff --git a/frontend/src/components/auth/ProtectedRoute.tsx b/frontend/src/components/auth/ProtectedRoute.tsx index 3478da31..7eebabfa 100644 --- a/frontend/src/components/auth/ProtectedRoute.tsx +++ b/frontend/src/components/auth/ProtectedRoute.tsx @@ -24,7 +24,7 @@ export default function ProtectedRoute({ children, requiredRole }: ProtectedRout } // Check role requirements - if (requiredRole && user?.role !== requiredRole) { + if (requiredRole && (user?.role || (user?.is_superuser ? 'Admin' : 'User')) !== requiredRole) { // Redirect to unauthorized or home page navigate('/', { replace: true }); return; @@ -47,7 +47,7 @@ export default function ProtectedRoute({ children, requiredRole }: ProtectedRout } // If role is required but user doesn't have it, don't render - if (requiredRole && user?.role !== requiredRole) { + if (requiredRole && (user?.role || (user?.is_superuser ? 'Admin' : 'User')) !== requiredRole) { return (
diff --git a/frontend/src/components/auth/UserProfile.tsx b/frontend/src/components/auth/UserProfile.tsx index 745d5b55..a450f3d3 100644 --- a/frontend/src/components/auth/UserProfile.tsx +++ b/frontend/src/components/auth/UserProfile.tsx @@ -19,7 +19,7 @@ export default function UserProfile({ isDropdown = false, onClose }: UserProfile const { user, logout } = useAuth(); const navigate = useNavigate(); const [isEditing, setIsEditing] = useState(false); - const [editedName, setEditedName] = useState(user?.name || ''); + const [editedName, setEditedName] = useState(user?.name || user?.full_name || ''); const handleSave = () => { // In a real app, this would make an API call to update user profile @@ -28,7 +28,7 @@ export default function UserProfile({ isDropdown = false, onClose }: UserProfile }; const handleCancel = () => { - setEditedName(user?.name || ''); + setEditedName(user?.name || user?.full_name || ''); setIsEditing(false); }; @@ -46,10 +46,10 @@ export default function UserProfile({ isDropdown = false, onClose }: UserProfile
-

{user.name}

+

{user.name || user.full_name || user.username}

@{user.username}

- {user.role} + {user.role || (user.is_superuser ? 'Admin' : 'User')}
@@ -98,10 +98,10 @@ export default function UserProfile({ isDropdown = false, onClose }: UserProfile
-

{user.name}

+

{user.name || user.full_name || user.username}

@{user.username}

- {user.role} + {user.role || (user.is_superuser ? 'Admin' : 'User')}
@@ -136,7 +136,7 @@ export default function UserProfile({ isDropdown = false, onClose }: UserProfile ) : (
- {user.name} + {user.name || user.full_name || user.username}
diff --git a/frontend/src/components/ui/alert.tsx b/frontend/src/components/ui/alert.tsx new file mode 100644 index 00000000..5a7ba0fd --- /dev/null +++ b/frontend/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border 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) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } \ No newline at end of file diff --git a/frontend/src/components/ui/checkbox.tsx b/frontend/src/components/ui/checkbox.tsx new file mode 100644 index 00000000..43ac6c4a --- /dev/null +++ b/frontend/src/components/ui/checkbox.tsx @@ -0,0 +1,28 @@ +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { Check } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)) +Checkbox.displayName = CheckboxPrimitive.Root.displayName + +export { Checkbox } \ No newline at end of file diff --git a/frontend/src/components/ui/dialog.tsx b/frontend/src/components/ui/dialog.tsx new file mode 100644 index 00000000..9e81a620 --- /dev/null +++ b/frontend/src/components/ui/dialog.tsx @@ -0,0 +1,120 @@ +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, +} \ No newline at end of file diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx index 560e9e2b..d25e89f5 100644 --- a/frontend/src/contexts/AuthContext.tsx +++ b/frontend/src/contexts/AuthContext.tsx @@ -10,6 +10,8 @@ interface User { username: string; email: string; full_name?: string; + name?: string; // For backward compatibility + role?: string; // For backward compatibility is_active: boolean; is_superuser: boolean; is_verified: boolean; diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 7d2453a4..9da4c97e 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -35,15 +35,10 @@ export default function Login() { setError(null); try { - const success = await login(credentials.username, credentials.password); - - if (success) { - navigate(returnPath); - } else { - setError('Invalid username or password'); - } - } catch (err) { - setError('Login failed. Please try again.'); + await login(credentials.username, credentials.password); + navigate(returnPath); + } catch (err: any) { + setError(err.message || 'Login failed. Please try again.'); } finally { setIsLoading(false); }