feat: initialize project with Next.js, Prisma, and admin dashboard functionality

This commit is contained in:
Michael committed 2026-04-10 09:58:09 +02:00
commit b73aa8616b
37 files changed
+8711

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
.card {
background: var(--glass-bg);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
box-shadow: var(--card-shadow);
border-radius: 24px;
padding: 2rem;
transition: var(--transition-smooth);
}
.hoverable:hover {
transform: translateY(-4px);
box-shadow: 0 12px 40px 0 rgba(0, 0, 0, 0.15);
border-color: var(--accent);
}
+32
View File
@@ -0,0 +1,32 @@
'use client';
import React from 'react';
import styles from './GlassCard.module.css';
interface GlassCardProps {
children: React.ReactNode;
className?: string;
hoverable?: boolean;
style?: React.CSSProperties;
as?: any;
onClick?: () => void;
}
export const GlassCard: React.FC<GlassCardProps> = ({
children,
className = '',
hoverable = false,
style,
as: Component = 'div',
onClick
}) => {
return (
<Component
className={`${styles.card} ${hoverable ? styles.hoverable : ''} ${className}`}
style={style}
onClick={onClick}
>
{children}
</Component>
);
};
+37
View File
@@ -0,0 +1,37 @@
.menuButton {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2.5rem;
text-decoration: none;
text-align: center;
color: var(--foreground);
gap: 1.5rem;
font-size: 1.25rem;
font-weight: 600;
cursor: pointer;
aspect-ratio: 1 / 1;
}
.iconWrapper {
background: var(--accent);
color: white;
padding: 1rem;
border-radius: 18px;
display: flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
transition: var(--transition-smooth);
}
.menuButton:hover .iconWrapper {
transform: scale(1.1);
box-shadow: 0 0 20px var(--accent);
}
.label {
letter-spacing: -0.01em;
}
+28
View File
@@ -0,0 +1,28 @@
'use client';
import React from 'react';
import Link from 'next/link';
import * as Icons from 'lucide-react';
import { GlassCard } from './GlassCard';
import styles from './MenuButton.module.css';
interface MenuButtonProps {
name: string;
url: string;
iconName?: keyof typeof Icons;
}
export const MenuButton: React.FC<MenuButtonProps> = ({ name, url, iconName = 'ExternalLink' }) => {
const Icon = (Icons[iconName] as React.ElementType) || Icons.ExternalLink;
return (
<Link href={url} target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none' }}>
<GlassCard hoverable className={styles.menuButton}>
<div className={styles.iconWrapper}>
<Icon size={32} />
</div>
<span className={styles.label}>{name}</span>
</GlassCard>
</Link>
);
};
+11
View File
@@ -0,0 +1,11 @@
'use client';
import * as React from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
+35
View File
@@ -0,0 +1,35 @@
'use client';
import * as React from 'react';
import { Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import { GlassCard } from './GlassCard';
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = React.useState(false);
// Avoid hydration mismatch
React.useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return (
<GlassCard className="glass" style={{ padding: '0.6rem', border: 'none' }}>
<div style={{ width: '24px', height: '24px' }}></div>
</GlassCard>
);
}
return (
<GlassCard
as="button"
className="glass"
style={{ padding: '0.6rem', cursor: 'pointer' }}
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
>
{theme === 'dark' ? <Sun size={24} /> : <Moon size={24} />}
</GlassCard>
);
}