feature: Migrate SkeletonLoader and PatternSummaryCard to shadcn/ui

- Replace SkeletonLoader with shadcn Skeleton component
- Simplify gradient animation to use shadcn's built-in pulse
- Migrate PatternSummaryCard to shadcn Card and Button
- Replace custom delete button with shadcn Button (outline variant)
- Use Loader2 from lucide-react for loading spinner

Code reduction:
- SkeletonLoader: Removed 8 lines of custom gradient classes
- Delete button: ~70% reduction (200+ char className → cleaner Button)
- Card wrapper: Semantic Card structure vs verbose div classes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Henrik Bruhn 2025-12-20 15:44:51 +01:00
parent 365b0c7ae3
commit bd80e95004
2 changed files with 45 additions and 57 deletions

View file

@ -4,6 +4,9 @@ import { usePatternStore } from "../stores/usePatternStore";
import { canDeletePattern } from "../utils/machineStateHelpers"; import { canDeletePattern } from "../utils/machineStateHelpers";
import { PatternInfo } from "./PatternInfo"; import { PatternInfo } from "./PatternInfo";
import { DocumentTextIcon, TrashIcon } from "@heroicons/react/24/solid"; import { DocumentTextIcon, TrashIcon } from "@heroicons/react/24/solid";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Loader2 } from "lucide-react";
export function PatternSummaryCard() { export function PatternSummaryCard() {
// Machine store // Machine store
@ -27,7 +30,8 @@ export function PatternSummaryCard() {
const canDelete = canDeletePattern(machineStatus); const canDelete = canDeletePattern(machineStatus);
return ( return (
<div className="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md border-l-4 border-primary-600 dark:border-primary-500"> <Card className="border-l-4 border-primary-600 dark:border-primary-500">
<CardContent className="p-4">
<div className="flex items-start gap-3 mb-3"> <div className="flex items-start gap-3 mb-3">
<DocumentTextIcon className="w-6 h-6 text-primary-600 dark:text-primary-400 flex-shrink-0 mt-0.5" /> <DocumentTextIcon className="w-6 h-6 text-primary-600 dark:text-primary-400 flex-shrink-0 mt-0.5" />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
@ -46,32 +50,15 @@ export function PatternSummaryCard() {
<PatternInfo pesData={pesData} /> <PatternInfo pesData={pesData} />
{canDelete && ( {canDelete && (
<button <Button
onClick={deletePattern} onClick={deletePattern}
disabled={isDeleting} disabled={isDeleting}
className="w-full flex items-center justify-center gap-2 px-3 py-2.5 sm:py-2 bg-danger-50 dark:bg-danger-900/20 text-danger-700 dark:text-danger-300 rounded border border-danger-300 dark:border-danger-700 hover:bg-danger-100 dark:hover:bg-danger-900/30 text-sm font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer" variant="outline"
className="w-full bg-danger-50 dark:bg-danger-900/20 text-danger-700 dark:text-danger-300 border-danger-300 dark:border-danger-700 hover:bg-danger-100 dark:hover:bg-danger-900/30"
> >
{isDeleting ? ( {isDeleting ? (
<> <>
<svg <Loader2 className="w-3 h-3 animate-spin" />
className="w-3 h-3 animate-spin"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Deleting... Deleting...
</> </>
) : ( ) : (
@ -80,8 +67,9 @@ export function PatternSummaryCard() {
Delete Pattern Delete Pattern
</> </>
)} )}
</button> </Button>
)} )}
</div> </CardContent>
</Card>
); );
} }

View file

@ -1,3 +1,6 @@
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
interface SkeletonLoaderProps { interface SkeletonLoaderProps {
className?: string; className?: string;
variant?: "text" | "rect" | "circle"; variant?: "text" | "rect" | "circle";
@ -7,9 +10,6 @@ export function SkeletonLoader({
className = "", className = "",
variant = "rect", variant = "rect",
}: SkeletonLoaderProps) { }: SkeletonLoaderProps) {
const baseClasses =
"animate-pulse bg-gradient-to-r from-gray-200 via-gray-300 to-gray-200 dark:from-gray-700 dark:via-gray-600 dark:to-gray-700 bg-[length:200%_100%]";
const variantClasses = { const variantClasses = {
text: "h-4 rounded", text: "h-4 rounded",
rect: "rounded-lg", rect: "rounded-lg",
@ -17,7 +17,7 @@ export function SkeletonLoader({
}; };
return ( return (
<div className={`${baseClasses} ${variantClasses[variant]} ${className}`} /> <Skeleton className={cn(variantClasses[variant], className)} />
); );
} }