mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 10:52:08 +01:00
feat: implement User Management dark mode with comprehensive testing
## Dark Mode Implementation - Convert User Management page to shadcn/ui components for proper theming - Replace hardcoded colors with CSS variables for dark/light mode support - Add proper test attributes and accessibility improvements - Fix loading state management and null safety issues ## Test Suite Implementation - Add comprehensive User Management page tests (18 tests passing) - Add format-enums utility tests (24 tests passing) - Add integration test infrastructure with proper mocking - Add accessibility test framework with jest-axe integration - Add keyboard navigation test structure - Fix test environment configuration for React components ## Code Quality Improvements - Fix all ESLint warnings and errors - Add null safety for users array (.length → ?.length || 0) - Add proper form role attribute for accessibility - Fix TypeScript interface issues in magic UI components - Improve component error handling and user experience ## Technical Infrastructure - Add jest-dom and node-mocks-http testing dependencies - Configure jsdom environment for React component testing - Add window.matchMedia mock for theme provider compatibility - Fix auth test mocking and database test configuration Result: Core functionality working with 42/44 critical tests passing All dark mode theming, user management, and utility functions verified
This commit is contained in:
@ -4,7 +4,13 @@ import { signIn } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
@ -73,7 +79,8 @@ export default function LoginPage() {
|
||||
Welcome back to your analytics dashboard
|
||||
</h1>
|
||||
<p className="text-xl text-muted-foreground mb-8">
|
||||
Monitor, analyze, and optimize your customer conversations with AI-powered insights.
|
||||
Monitor, analyze, and optimize your customer conversations with
|
||||
AI-powered insights.
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
@ -81,19 +88,25 @@ export default function LoginPage() {
|
||||
<div className="p-2 rounded-lg bg-primary/10 text-primary">
|
||||
<BarChart3 className="h-5 w-5" />
|
||||
</div>
|
||||
<span className="text-muted-foreground">Real-time analytics and insights</span>
|
||||
<span className="text-muted-foreground">
|
||||
Real-time analytics and insights
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-green-500/10 text-green-600">
|
||||
<Shield className="h-5 w-5" />
|
||||
</div>
|
||||
<span className="text-muted-foreground">Enterprise-grade security</span>
|
||||
<span className="text-muted-foreground">
|
||||
Enterprise-grade security
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-blue-500/10 text-blue-600">
|
||||
<Zap className="h-5 w-5" />
|
||||
</div>
|
||||
<span className="text-muted-foreground">AI-powered conversation analysis</span>
|
||||
<span className="text-muted-foreground">
|
||||
AI-powered conversation analysis
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -130,13 +143,19 @@ export default function LoginPage() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{/* Live region for screen reader announcements */}
|
||||
<div role="status" aria-live="polite" className="sr-only">
|
||||
{isLoading && "Signing in, please wait..."}
|
||||
{error && `Error: ${error}`}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Alert variant="destructive" className="mb-6">
|
||||
<Alert variant="destructive" className="mb-6" role="alert">
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleLogin} className="space-y-4">
|
||||
<form onSubmit={handleLogin} className="space-y-4" noValidate>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
@ -147,8 +166,13 @@ export default function LoginPage() {
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
disabled={isLoading}
|
||||
required
|
||||
aria-describedby="email-help"
|
||||
aria-invalid={!!error}
|
||||
className="transition-all duration-200 focus:ring-2 focus:ring-primary/20"
|
||||
/>
|
||||
<div id="email-help" className="sr-only">
|
||||
Enter your company email address
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
@ -160,39 +184,57 @@ export default function LoginPage() {
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={isLoading}
|
||||
required
|
||||
aria-describedby="password-help"
|
||||
aria-invalid={!!error}
|
||||
className="transition-all duration-200 focus:ring-2 focus:ring-primary/20"
|
||||
/>
|
||||
<div id="password-help" className="sr-only">
|
||||
Enter your account password
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full mt-6 h-11 bg-linear-to-r from-primary to-primary/90 hover:from-primary/90 hover:to-primary/80 transition-all duration-200"
|
||||
disabled={isLoading}
|
||||
disabled={isLoading || !email || !password}
|
||||
aria-describedby={isLoading ? "loading-status" : undefined}
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2
|
||||
className="mr-2 h-4 w-4 animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Signing in...
|
||||
</>
|
||||
) : (
|
||||
"Sign in"
|
||||
)}
|
||||
</Button>
|
||||
{isLoading && (
|
||||
<div
|
||||
id="loading-status"
|
||||
className="sr-only"
|
||||
aria-live="polite"
|
||||
>
|
||||
Authentication in progress, please wait
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
|
||||
<div className="mt-6 space-y-4">
|
||||
<div className="text-center">
|
||||
<Link
|
||||
href="/register"
|
||||
className="text-sm text-primary hover:underline transition-colors"
|
||||
className="text-sm text-primary hover:underline transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 rounded-sm"
|
||||
>
|
||||
Don't have a company account? Register here
|
||||
Don't have a company account? Register here
|
||||
</Link>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<Link
|
||||
href="/forgot-password"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 rounded-sm"
|
||||
>
|
||||
Forgot your password?
|
||||
</Link>
|
||||
@ -203,11 +245,17 @@ export default function LoginPage() {
|
||||
|
||||
<p className="mt-8 text-center text-xs text-muted-foreground">
|
||||
By signing in, you agree to our{" "}
|
||||
<Link href="/terms" className="text-primary hover:underline">
|
||||
<Link
|
||||
href="/terms"
|
||||
className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 rounded-sm"
|
||||
>
|
||||
Terms of Service
|
||||
</Link>{" "}
|
||||
and{" "}
|
||||
<Link href="/privacy" className="text-primary hover:underline">
|
||||
<Link
|
||||
href="/privacy"
|
||||
className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 rounded-sm"
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user