Files
livedash-node/components/magicui/neon-gradient-card.tsx
Kaj Kowalski ef71c9c06e 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
2025-06-28 06:53:14 +02:00

150 lines
4.1 KiB
TypeScript

"use client";
import {
CSSProperties,
ReactElement,
ReactNode,
useEffect,
useRef,
useState,
} from "react";
import { cn } from "@/lib/utils";
interface NeonColorsProps {
firstColor: string;
secondColor: string;
}
interface NeonGradientCardProps {
/**
* @default <div />
* @type ReactElement
* @description
* The component to be rendered as the card
* */
as?: ReactElement;
/**
* @default ""
* @type string
* @description
* The className of the card
*/
className?: string;
/**
* @default ""
* @type ReactNode
* @description
* The children of the card
* */
children?: ReactNode;
/**
* @default 5
* @type number
* @description
* The size of the border in pixels
* */
borderSize?: number;
/**
* @default 20
* @type number
* @description
* The size of the radius in pixels
* */
borderRadius?: number;
/**
* @default "{ firstColor: '#ff00aa', secondColor: '#00FFF1' }"
* @type string
* @description
* The colors of the neon gradient
* */
neonColors?: NeonColorsProps;
[key: string]: any;
}
export const NeonGradientCard: React.FC<NeonGradientCardProps> = ({
className,
children,
borderSize = 2,
borderRadius = 20,
neonColors = {
firstColor: "#ff00aa",
secondColor: "#00FFF1",
},
...props
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
useEffect(() => {
const updateDimensions = () => {
if (containerRef.current) {
const { offsetWidth, offsetHeight } = containerRef.current;
setDimensions({ width: offsetWidth, height: offsetHeight });
}
};
updateDimensions();
window.addEventListener("resize", updateDimensions);
return () => {
window.removeEventListener("resize", updateDimensions);
};
}, []);
useEffect(() => {
if (containerRef.current) {
const { offsetWidth, offsetHeight } = containerRef.current;
setDimensions({ width: offsetWidth, height: offsetHeight });
}
}, [children]);
return (
<div
ref={containerRef}
style={
{
"--border-size": `${borderSize}px`,
"--border-radius": `${borderRadius}px`,
"--neon-first-color": neonColors.firstColor,
"--neon-second-color": neonColors.secondColor,
"--card-width": `${dimensions.width}px`,
"--card-height": `${dimensions.height}px`,
"--card-content-radius": `${borderRadius - borderSize}px`,
"--pseudo-element-background-image": `linear-gradient(0deg, ${neonColors.firstColor}, ${neonColors.secondColor})`,
"--pseudo-element-width": `${dimensions.width + borderSize * 2}px`,
"--pseudo-element-height": `${dimensions.height + borderSize * 2}px`,
"--after-blur": `${dimensions.width / 3}px`,
} as CSSProperties
}
className={cn(
"relative z-10 size-full rounded-[var(--border-radius)]",
className
)}
{...props}
>
<div
className={cn(
"relative size-full min-h-[inherit] rounded-[var(--card-content-radius)] bg-gray-100 p-6",
"before:absolute before:-left-[var(--border-size)] before:-top-[var(--border-size)] before:-z-10 before:block",
"before:h-[var(--pseudo-element-height)] before:w-[var(--pseudo-element-width)] before:rounded-[var(--border-radius)] before:content-['']",
"before:bg-[linear-gradient(0deg,var(--neon-first-color),var(--neon-second-color))] before:bg-[length:100%_200%]",
"before:animate-background-position-spin",
"after:absolute after:-left-[var(--border-size)] after:-top-[var(--border-size)] after:-z-10 after:block",
"after:h-[var(--pseudo-element-height)] after:w-[var(--pseudo-element-width)] after:rounded-[var(--border-radius)] after:blur-[var(--after-blur)] after:content-['']",
"after:bg-[linear-gradient(0deg,var(--neon-first-color),var(--neon-second-color))] after:bg-[length:100%_200%] after:opacity-80",
"after:animate-background-position-spin",
"dark:bg-neutral-900"
)}
>
{children}
</div>
</div>
);
};