feat: enhance JiraDashboardPage with new components and improved UI

- Integrated `PeriodSelector`, `SkeletonGrid`, and `MetricsGrid` for better data visualization and user interaction.
- Replaced legacy period selection and error display with new components for a cleaner UI.
- Updated `UIShowcaseClient` to demonstrate new Jira dashboard components, enhancing showcase functionality.
This commit is contained in:
Julien Froidefond
2025-09-29 16:47:35 +02:00
parent 6c0c353a4e
commit c1a14f9196
6 changed files with 304 additions and 101 deletions

View File

@@ -8,7 +8,7 @@ import { Alert as ShadcnAlert, AlertTitle, AlertDescription } from '@/components
import { Input } from '@/components/ui/Input';
import { StyledCard } from '@/components/ui/StyledCard';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import { StatCard, ProgressBar, ActionCard, TaskCard, MetricCard, ToggleButton, SearchInput, ControlPanel, ControlSection, ControlGroup, FilterSummary, FilterChip, ColumnHeader, EmptyState, DropZone, Tabs, PriorityBadge, AchievementCard, ChallengeCard } from '@/components/ui';
import { StatCard, ProgressBar, ActionCard, TaskCard, MetricCard, ToggleButton, SearchInput, ControlPanel, ControlSection, ControlGroup, FilterSummary, FilterChip, ColumnHeader, EmptyState, DropZone, Tabs, PriorityBadge, AchievementCard, ChallengeCard, PeriodSelector, SkeletonCard, SkeletonGrid, MetricsGrid } from '@/components/ui';
import { CheckboxItem, CheckboxItemData } from '@/components/ui/CheckboxItem';
import { Calendar } from '@/components/ui/Calendar';
import { DailyAddForm } from '@/components/ui/DailyAddForm';
@@ -22,6 +22,7 @@ import { ChallengeData } from '@/components/ui/ChallengeCard';
export function UIShowcaseClient() {
const [inputValue, setInputValue] = useState('');
const [selectedDate, setSelectedDate] = useState(new Date());
const [selectedPeriod, setSelectedPeriod] = useState('7d');
const [checkboxItems, setCheckboxItems] = useState<CheckboxItemData[]>([
{ id: '1', text: 'Tâche complétée', isChecked: true, type: 'task' },
{ id: '2', text: 'Réunion importante', isChecked: false, type: 'meeting' },
@@ -1081,6 +1082,93 @@ export function UIShowcaseClient() {
</div>
</section>
{/* Jira Dashboard Components Section */}
<section className="space-y-8">
<h2 className="text-2xl font-mono font-semibold text-[var(--foreground)] border-b border-[var(--border)] pb-3">
Jira Dashboard Components
</h2>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* PeriodSelector */}
<div className="space-y-6">
<h3 className="text-lg font-medium text-[var(--foreground)]">PeriodSelector</h3>
<div className="space-y-4">
<div className="space-y-2">
<div className="text-xs font-mono text-[var(--muted-foreground)] bg-[var(--card)] px-2 py-1 rounded">
PeriodSelector - Sélecteur de période
</div>
<PeriodSelector
options={[
{ value: '7d', label: '7j' },
{ value: '30d', label: '30j' },
{ value: '3m', label: '3m' },
{ value: 'current', label: 'Sprint' }
]}
selectedValue={selectedPeriod}
onValueChange={setSelectedPeriod}
/>
<div className="text-xs text-[var(--muted-foreground)]">
Période sélectionnée: {selectedPeriod}
</div>
</div>
</div>
</div>
{/* MetricsGrid */}
<div className="space-y-6">
<h3 className="text-lg font-medium text-[var(--foreground)]">MetricsGrid</h3>
<div className="space-y-4">
<div className="space-y-2">
<div className="text-xs font-mono text-[var(--muted-foreground)] bg-[var(--card)] px-2 py-1 rounded">
MetricsGrid - Grille de métriques
</div>
<Card>
<CardContent className="p-4">
<MetricsGrid
metrics={[
{ title: 'Tickets', value: 42, color: 'primary' },
{ title: 'Équipe', value: 8, color: 'default' },
{ title: 'Actifs', value: 6, color: 'success' },
{ title: 'Points', value: 156, color: 'warning' }
]}
/>
</CardContent>
</Card>
</div>
</div>
</div>
{/* SkeletonCard */}
<div className="space-y-6">
<h3 className="text-lg font-medium text-[var(--foreground)]">SkeletonCard</h3>
<div className="space-y-4">
<div className="space-y-2">
<div className="text-xs font-mono text-[var(--muted-foreground)] bg-[var(--card)] px-2 py-1 rounded">
SkeletonCard - Carte de chargement
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<SkeletonCard lines={3} />
<SkeletonCard lines={4} />
</div>
</div>
</div>
</div>
{/* SkeletonGrid */}
<div className="space-y-6">
<h3 className="text-lg font-medium text-[var(--foreground)]">SkeletonGrid</h3>
<div className="space-y-4">
<div className="space-y-2">
<div className="text-xs font-mono text-[var(--muted-foreground)] bg-[var(--card)] px-2 py-1 rounded">
SkeletonGrid - Grille de chargement
</div>
<SkeletonGrid count={4} lines={3} />
</div>
</div>
</div>
</div>
</section>
{/* Daily Components Section */}
<section className="space-y-8">
<h2 className="text-2xl font-mono font-semibold text-[var(--foreground)] border-b border-[var(--border)] pb-3">

View File

@@ -0,0 +1,57 @@
import { ReactNode } from 'react';
import { cn } from '@/lib/utils';
interface MetricsGridProps {
metrics: Array<{
title: string;
value: string | number;
subtitle?: string;
icon?: ReactNode;
color?: 'default' | 'primary' | 'success' | 'warning' | 'destructive';
}>;
className?: string;
columns?: 2 | 3 | 4;
}
export function MetricsGrid({
metrics,
className,
columns = 4
}: MetricsGridProps) {
const gridCols = {
2: 'grid-cols-2',
3: 'grid-cols-3',
4: 'grid-cols-2 lg:grid-cols-4'
};
return (
<div className={cn(
"grid gap-4",
gridCols[columns],
className
)}>
{metrics.map((metric, index) => (
<div key={index} className="text-center">
<div className={cn(
"text-xl font-bold",
metric.color === 'primary' && 'text-[var(--primary)]',
metric.color === 'success' && 'text-[var(--success)]',
metric.color === 'warning' && 'text-[var(--accent)]',
metric.color === 'destructive' && 'text-[var(--destructive)]',
!metric.color && 'text-[var(--foreground)]'
)}>
{metric.value}
</div>
<div className="text-xs text-[var(--muted-foreground)]">
{metric.title}
</div>
{metric.subtitle && (
<div className="text-xs text-[var(--muted-foreground)] mt-1">
{metric.subtitle}
</div>
)}
</div>
))}
</div>
);
}

View File

@@ -0,0 +1,45 @@
import { ReactNode } from 'react';
import { cn } from '@/lib/utils';
export interface PeriodOption {
value: string;
label: string;
icon?: ReactNode;
}
interface PeriodSelectorProps {
options: PeriodOption[];
selectedValue: string;
onValueChange: (value: string) => void;
className?: string;
}
export function PeriodSelector({
options,
selectedValue,
onValueChange,
className
}: PeriodSelectorProps) {
return (
<div className={cn(
"flex bg-[var(--card)] border border-[var(--border)] rounded-lg p-1",
className
)}>
{options.map((option) => (
<button
key={option.value}
onClick={() => onValueChange(option.value)}
className={cn(
"px-3 py-1 text-sm rounded transition-all flex items-center gap-1",
selectedValue === option.value
? 'bg-[var(--primary)] text-[var(--primary-foreground)]'
: 'text-[var(--muted-foreground)] hover:text-[var(--foreground)]'
)}
>
{option.icon && <span>{option.icon}</span>}
<span>{option.label}</span>
</button>
))}
</div>
);
}

View File

@@ -0,0 +1,50 @@
import { Card, CardContent } from './Card';
import { cn } from '@/lib/utils';
interface SkeletonCardProps {
className?: string;
lines?: number;
}
export function SkeletonCard({ className, lines = 3 }: SkeletonCardProps) {
return (
<Card className={cn("animate-pulse", className)}>
<CardContent className="p-6">
<div className="space-y-4">
{/* Titre */}
<div className="h-4 bg-[var(--muted)] rounded mb-4"></div>
{/* Valeur principale */}
<div className="h-8 bg-[var(--muted)] rounded mb-2"></div>
{/* Lignes supplémentaires */}
{Array.from({ length: lines - 2 }).map((_, i) => (
<div
key={i}
className={cn(
"h-4 bg-[var(--muted)] rounded",
i === lines - 3 ? "w-2/3" : "w-full"
)}
></div>
))}
</div>
</CardContent>
</Card>
);
}
interface SkeletonGridProps {
count?: number;
className?: string;
lines?: number;
}
export function SkeletonGrid({ count = 6, className, lines = 3 }: SkeletonGridProps) {
return (
<div className={cn("grid grid-cols-1 lg:grid-cols-3 gap-6", className)}>
{Array.from({ length: count }).map((_, i) => (
<SkeletonCard key={i} lines={lines} />
))}
</div>
);
}

View File

@@ -35,6 +35,11 @@ export { DailyAddForm } from './DailyAddForm';
export { AlertBanner } from './AlertBanner';
export { CollapsibleSection } from './CollapsibleSection';
// Composants Jira Dashboard
export { PeriodSelector } from './PeriodSelector';
export { SkeletonCard, SkeletonGrid } from './SkeletonCard';
export { MetricsGrid } from './MetricsGrid';
// Composants existants
export { Card, CardHeader, CardTitle, CardContent, CardFooter } from './Card';
export { FontSizeToggle } from './FontSizeToggle';