- Added main application entry point (app.jsx) and bootstrap file for React setup. - Created Wiki index page (Index.jsx) for documentation and file management. - Implemented utility functions for formatting and status handling in utils.js. - Set up Blade template (app.blade.php) for Inertia.js integration. - Defined API routes for metrics and terminal access in api.php. - Established console commands for scheduled tasks in console.php. - Created web routes for various application features including authentication, hypervisors, containers, cloud accounts, security, backups, and wiki management in web.php. - Developed setup scripts for Windows (setup.ps1) and Linux (setup.sh) environments. - Configured Tailwind CSS for styling with custom themes and animations in tailwind.config.js. - Set up Vite for asset bundling and development server in vite.config.js.
60 lines
3.0 KiB
JavaScript
60 lines
3.0 KiB
JavaScript
import { motion } from 'framer-motion';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
export function GlassCard({ children, className, hover = true, glow, ...props }) {
|
|
return (
|
|
<motion.div
|
|
whileHover={hover ? { y: -2, scale: 1.005 } : undefined}
|
|
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
|
|
className={cn(
|
|
'relative overflow-hidden rounded-xl',
|
|
'border border-white/10 bg-white/[0.03] backdrop-blur-xl',
|
|
hover && 'cursor-pointer',
|
|
glow === 'cyan' && 'hover:border-cyan-500/40 hover:shadow-[0_0_30px_rgba(0,212,255,0.1)]',
|
|
glow === 'green' && 'hover:border-green-500/40 hover:shadow-[0_0_30px_rgba(0,255,136,0.1)]',
|
|
glow === 'purple' && 'hover:border-purple-500/40 hover:shadow-[0_0_30px_rgba(157,78,221,0.1)]',
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{/* Inner gradient */}
|
|
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.02] to-transparent pointer-events-none" />
|
|
<div className="relative z-10">{children}</div>
|
|
</motion.div>
|
|
);
|
|
}
|
|
|
|
export function StatCard({ title, value, subtitle, icon: Icon, color = 'cyan', trend, className }) {
|
|
const colors = {
|
|
cyan: { text: 'text-cyan-400', bg: 'bg-cyan-500/10', border: 'border-cyan-500/20' },
|
|
green: { text: 'text-green-400', bg: 'bg-green-500/10', border: 'border-green-500/20' },
|
|
purple: { text: 'text-purple-400', bg: 'bg-purple-500/10', border: 'border-purple-500/20' },
|
|
orange: { text: 'text-orange-400', bg: 'bg-orange-500/10', border: 'border-orange-500/20' },
|
|
red: { text: 'text-red-400', bg: 'bg-red-500/10', border: 'border-red-500/20' },
|
|
blue: { text: 'text-blue-400', bg: 'bg-blue-500/10', border: 'border-blue-500/20' },
|
|
};
|
|
const c = colors[color] ?? colors.cyan;
|
|
|
|
return (
|
|
<GlassCard className={cn('p-5', className)} glow={color === 'cyan' ? 'cyan' : undefined}>
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex-1">
|
|
<p className="text-xs font-medium text-white/50 uppercase tracking-wider mb-1">{title}</p>
|
|
<p className={cn('text-3xl font-bold tracking-tight', c.text)}>{value}</p>
|
|
{subtitle && <p className="text-xs text-white/40 mt-1">{subtitle}</p>}
|
|
{trend !== undefined && (
|
|
<p className={cn('text-xs mt-2', trend >= 0 ? 'text-green-400' : 'text-red-400')}>
|
|
{trend >= 0 ? '↑' : '↓'} {Math.abs(trend)}% vs last hour
|
|
</p>
|
|
)}
|
|
</div>
|
|
{Icon && (
|
|
<div className={cn('p-2.5 rounded-lg border', c.bg, c.border)}>
|
|
<Icon className={cn('w-5 h-5', c.text)} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</GlassCard>
|
|
);
|
|
}
|