Files
panel/src/components/ui/States.js
Chop a1261b2169 Refactor Project Tasks and Task Templates pages with new UI components
- Introduced PageContainer and PageHeader components for consistent layout.
- Added SearchBar and FilterBar components for improved task filtering and searching.
- Implemented LoadingState component for better loading indication.
- Updated ProjectTasksPage to utilize new components and enhance user experience.
- Refactored TaskTemplatesPage to use PageContainer and PageHeader for better structure.
- Created FilterBar component to manage filter options dynamically.
- Added SearchBar component for searching tasks with clear functionality.
- Introduced States component for loading and error states.
2025-06-02 23:41:49 +02:00

114 lines
2.8 KiB
JavaScript

"use client";
import { Card, CardContent } from "./Card";
import Button from "./Button";
const LoadingSpinner = ({ size = "md" }) => {
const sizeClasses = {
sm: "w-4 h-4",
md: "w-8 h-8",
lg: "w-12 h-12",
};
return (
<div className="flex items-center justify-center">
<div
className={`${sizeClasses[size]} animate-spin rounded-full border-2 border-gray-300 border-t-blue-600`}
></div>
</div>
);
};
const LoadingState = ({ message = "Loading...", className = "" }) => {
return (
<div className={`flex items-center justify-center py-12 ${className}`}>
<div className="text-center">
<LoadingSpinner size="lg" />
<p className="text-gray-600 mt-4">{message}</p>
</div>
</div>
);
};
const EmptyState = ({
icon,
title,
description,
actionLabel,
actionHref,
onAction,
className = "",
}) => {
return (
<Card className={className}>
<CardContent className="text-center py-12">
<div className="text-gray-400 mb-4">
{icon || (
<svg
className="w-16 h-16 mx-auto"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zm0 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V8zm0 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1v-2z"
clipRule="evenodd"
/>
</svg>
)}
</div>
<h3 className="text-lg font-medium text-gray-900 mb-2">{title}</h3>
{description && <p className="text-gray-500 mb-6">{description}</p>}
{actionLabel && (actionHref || onAction) && (
<Button
variant="primary"
onClick={onAction}
as={actionHref ? "a" : "button"}
href={actionHref}
>
{actionLabel}
</Button>
)}
</CardContent>
</Card>
);
};
const ErrorState = ({
title = "Something went wrong",
description = "We encountered an error. Please try again.",
onRetry,
className = "",
}) => {
return (
<Card className={className}>
<CardContent className="text-center py-12">
<div className="text-red-400 mb-4">
<svg
className="w-16 h-16 mx-auto"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
/>
</svg>
</div>
<h3 className="text-lg font-medium text-gray-900 mb-2">{title}</h3>
<p className="text-gray-500 mb-6">{description}</p>
{onRetry && (
<Button variant="primary" onClick={onRetry}>
Try Again
</Button>
)}
</CardContent>
</Card>
);
};
export { LoadingSpinner, LoadingState, EmptyState, ErrorState };