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,61 +30,46 @@ 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">
<div className="flex items-start gap-3 mb-3"> <CardContent className="p-4">
<DocumentTextIcon className="w-6 h-6 text-primary-600 dark:text-primary-400 flex-shrink-0 mt-0.5" /> <div className="flex items-start gap-3 mb-3">
<div className="flex-1 min-w-0"> <DocumentTextIcon className="w-6 h-6 text-primary-600 dark:text-primary-400 flex-shrink-0 mt-0.5" />
<h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-1"> <div className="flex-1 min-w-0">
Active Pattern <h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-1">
</h3> Active Pattern
<p </h3>
className="text-xs text-gray-600 dark:text-gray-400 truncate" <p
title={currentFileName} className="text-xs text-gray-600 dark:text-gray-400 truncate"
> title={currentFileName}
{currentFileName} >
</p> {currentFileName}
</p>
</div>
</div> </div>
</div>
<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 <>
className="w-3 h-3 animate-spin" <Loader2 className="w-3 h-3 animate-spin" />
fill="none" Deleting...
viewBox="0 0 24 24" </>
> ) : (
<circle <>
className="opacity-25" <TrashIcon className="w-3 h-3" />
cx="12" Delete Pattern
cy="12" </>
r="10" )}
stroke="currentColor" </Button>
strokeWidth="4" )}
></circle> </CardContent>
<path </Card>
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...
</>
) : (
<>
<TrashIcon className="w-3 h-3" />
Delete Pattern
</>
)}
</button>
)}
</div>
); );
} }

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)} />
); );
} }