feat: vollständiges HeroUI v3 Redesign — Types, Utils, Layout, Sections, Dark SaaS Design
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled
This commit is contained in:
26
frontend/src/components/shared/ActivityTile.tsx
Normal file
26
frontend/src/components/shared/ActivityTile.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Card, CardContent } from '@heroui/react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type Props = {
|
||||
icon: ReactNode;
|
||||
label: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
export function ActivityTile({ icon, label, value }: Props) {
|
||||
return (
|
||||
<Card className="border border-default-100 bg-default-50/20">
|
||||
<CardContent className="flex flex-row items-center justify-between gap-4 p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex size-10 items-center justify-center rounded-xl bg-primary-500/10 text-primary-400">
|
||||
{icon}
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-tiny uppercase tracking-widest text-default-500">{label}</div>
|
||||
<div className="text-2xl font-black">{value}</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
18
frontend/src/components/shared/FormPanel.tsx
Normal file
18
frontend/src/components/shared/FormPanel.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Card, CardHeader, CardContent } from '@heroui/react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export function FormPanel({ title, children }: Props) {
|
||||
return (
|
||||
<Card className="border border-default-100 bg-default-50/20">
|
||||
<CardHeader className="px-5 pt-5 pb-0">
|
||||
<h3 className="text-base font-semibold">{title}</h3>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4 px-5 py-5">{children}</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
21
frontend/src/components/shared/ListPanel.tsx
Normal file
21
frontend/src/components/shared/ListPanel.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Card, CardHeader, CardContent } from '@heroui/react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function ListPanel({ title, children, className }: Props) {
|
||||
return (
|
||||
<Card className={`border border-default-100 bg-default-50/20 ${className ?? ''}`}>
|
||||
<CardHeader className="px-5 pt-5 pb-0">
|
||||
<h3 className="text-base font-semibold">{title}</h3>
|
||||
</CardHeader>
|
||||
<CardContent className="px-5 py-5">
|
||||
{children}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
34
frontend/src/components/shared/LoadingSkeleton.tsx
Normal file
34
frontend/src/components/shared/LoadingSkeleton.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Card, CardContent } from '@heroui/react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type Props = {
|
||||
lines?: number;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export function LoadingSkeleton({ lines = 3, children }: Props) {
|
||||
if (children) {
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
{Array.from({ length: lines }).map((_, i) => (
|
||||
<Card key={i} className="animate-pulse border border-default-100 bg-default-50/30">
|
||||
<CardContent className="p-4">{children}</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
{Array.from({ length: lines }).map((_, i) => (
|
||||
<Card key={i} className="animate-pulse border border-default-100 bg-default-50/30">
|
||||
<CardContent className="p-5">
|
||||
<div className="h-4 w-3/4 rounded-lg bg-default-200/50" />
|
||||
{i < 2 && <div className="mt-3 h-3 w-1/2 rounded-lg bg-default-200/30" />}
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
24
frontend/src/components/shared/SectionCard.tsx
Normal file
24
frontend/src/components/shared/SectionCard.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Card, CardHeader, CardContent } from '@heroui/react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
children: ReactNode;
|
||||
action?: ReactNode;
|
||||
};
|
||||
|
||||
export function SectionCard({ title, subtitle, children, action }: Props) {
|
||||
return (
|
||||
<Card className="border border-default-100 bg-gradient-to-b from-default-50/40 to-background">
|
||||
<CardHeader className="flex items-start justify-between gap-4 px-6 pt-6 pb-0">
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-xl font-bold tracking-tight">{title}</h2>
|
||||
{subtitle && <p className="mt-1 text-small text-default-500">{subtitle}</p>}
|
||||
</div>
|
||||
{action && <div className="shrink-0">{action}</div>}
|
||||
</CardHeader>
|
||||
<CardContent className="px-6 py-5">{children}</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
22
frontend/src/components/shared/StatCard.tsx
Normal file
22
frontend/src/components/shared/StatCard.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Card, CardContent } from '@heroui/react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type Props = {
|
||||
icon: ReactNode;
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export function StatCard({ icon, label, value }: Props) {
|
||||
return (
|
||||
<Card className="border border-default-100 bg-default-50/20">
|
||||
<CardContent className="flex flex-col gap-2 p-4">
|
||||
<div className="flex items-center gap-2 text-tiny uppercase tracking-widest text-default-500">
|
||||
<span className="text-primary-400">{icon}</span>
|
||||
{label}
|
||||
</div>
|
||||
<div className="text-2xl font-bold tracking-tight">{value}</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user