mirror of
https://github.com/YuzuZensai/Minikura.git
synced 2026-09-14 03:09:50 +00:00
🐛 fix: align dashboard with secured APIs
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { Loader2 } from "lucide-react";
|
||||
"use client";
|
||||
|
||||
import { useRef } from "react";
|
||||
import type * as React from "react";
|
||||
import { FadeIn } from "@/components/motion";
|
||||
import { BrandMark } from "@/components/brand";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { gsap, prefersReducedMotion, useGSAP } from "@/lib/motion";
|
||||
|
||||
type PageHeaderProps = {
|
||||
eyebrow: string;
|
||||
@@ -24,17 +29,19 @@ export function PageHeader({
|
||||
className,
|
||||
}: PageHeaderProps) {
|
||||
return (
|
||||
<header className={cn("page-heading", className)}>
|
||||
<div className={cn(leading && "flex items-center gap-4")}>
|
||||
{leading}
|
||||
<div>
|
||||
<span className="page-eyebrow">{eyebrow}</span>
|
||||
<h1 className="page-title">{title}</h1>
|
||||
{description && <p className="page-description">{description}</p>}
|
||||
<FadeIn y={12} duration={0.45}>
|
||||
<header className={cn("page-heading", className)}>
|
||||
<div className={cn(leading && "flex items-center gap-4")}>
|
||||
{leading}
|
||||
<div>
|
||||
<span className="page-eyebrow">{eyebrow}</span>
|
||||
<h1 className="page-title">{title}</h1>
|
||||
{description && <p className="page-description">{description}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{actions}
|
||||
</header>
|
||||
{actions}
|
||||
</header>
|
||||
</FadeIn>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +65,9 @@ export function StatePanel({
|
||||
...props
|
||||
}: StatePanelProps) {
|
||||
return (
|
||||
<div
|
||||
<FadeIn
|
||||
y={10}
|
||||
duration={0.4}
|
||||
className={cn(
|
||||
"flex min-h-48 items-center justify-center border border-dashed bg-card/60 p-6 text-center",
|
||||
className
|
||||
@@ -67,7 +76,7 @@ export function StatePanel({
|
||||
>
|
||||
<div className="flex max-w-lg flex-col items-center gap-2">
|
||||
{loading ? (
|
||||
<Loader2 className="mb-2 size-7 animate-spin text-muted-foreground" />
|
||||
<LoaderMark className="mb-2" />
|
||||
) : (
|
||||
icon && <div className="mb-2 text-muted-foreground">{icon}</div>
|
||||
)}
|
||||
@@ -75,16 +84,87 @@ export function StatePanel({
|
||||
{description && <div className="text-sm text-muted-foreground">{description}</div>}
|
||||
{action && <div className="mt-3">{action}</div>}
|
||||
</div>
|
||||
</FadeIn>
|
||||
);
|
||||
}
|
||||
|
||||
function LoaderMark({ className }: { className?: string }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useGSAP(
|
||||
() => {
|
||||
const el = ref.current;
|
||||
if (!el || prefersReducedMotion()) return;
|
||||
gsap.to(el, {
|
||||
rotate: 180,
|
||||
duration: 1.1,
|
||||
repeat: -1,
|
||||
yoyo: true,
|
||||
ease: "power1.inOut",
|
||||
});
|
||||
},
|
||||
{ scope: ref }
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={cn("text-muted-foreground", className)}>
|
||||
<BrandMark className="size-8 text-sm" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function FullScreenLoader({ label }: { label?: string }) {
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useGSAP(
|
||||
() => {
|
||||
const root = rootRef.current;
|
||||
if (!root || prefersReducedMotion()) return;
|
||||
const mark = root.querySelector("[data-loader-mark]");
|
||||
const ring = root.querySelector("[data-loader-ring]");
|
||||
const copy = root.querySelector("[data-loader-copy]");
|
||||
|
||||
gsap.fromTo(
|
||||
[mark, copy],
|
||||
{ autoAlpha: 0, y: 8 },
|
||||
{ autoAlpha: 1, y: 0, duration: 0.45, stagger: 0.08, ease: "power3.out" }
|
||||
);
|
||||
gsap.to(mark, {
|
||||
scale: 1.06,
|
||||
duration: 0.9,
|
||||
repeat: -1,
|
||||
yoyo: true,
|
||||
ease: "sine.inOut",
|
||||
});
|
||||
gsap.to(ring, {
|
||||
rotate: 360,
|
||||
duration: 2.4,
|
||||
repeat: -1,
|
||||
ease: "none",
|
||||
});
|
||||
},
|
||||
{ scope: rootRef }
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center gap-3 bg-sidebar text-sidebar-foreground">
|
||||
<Loader2 className="size-8 animate-spin text-sidebar-primary" />
|
||||
<div
|
||||
ref={rootRef}
|
||||
className="flex min-h-screen flex-col items-center justify-center gap-4 bg-sidebar text-sidebar-foreground"
|
||||
>
|
||||
<div className="relative grid size-16 place-items-center">
|
||||
<span
|
||||
data-loader-ring=""
|
||||
className="absolute inset-0 border border-sidebar-primary/35 border-t-sidebar-primary"
|
||||
/>
|
||||
<div data-loader-mark="">
|
||||
<BrandMark className="size-11" />
|
||||
</div>
|
||||
</div>
|
||||
{label && (
|
||||
<span className="font-mono text-[10px] font-bold uppercase tracking-widest text-sidebar-foreground/50">
|
||||
<span
|
||||
data-loader-copy=""
|
||||
className="font-mono text-[10px] font-bold uppercase tracking-widest text-sidebar-foreground/50"
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user