turash/bugulma/frontend/components/ui/DataCard.tsx
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- Remove nested git repository from bugulma/frontend/.git
- Add all frontend files to main repository tracking
- Convert from separate frontend/backend repos to unified monorepo
- Preserve all frontend code and development history as tracked files
- Eliminate nested repository complexity for simpler development workflow

This creates a proper monorepo structure with frontend and backend
coexisting in the same repository for easier development and deployment.
2025-11-25 06:02:57 +01:00

86 lines
2.5 KiB
TypeScript

import React from 'react';
import { useCommonTranslations } from '@/hooks/useCommonTranslations';
import { getIconClasses } from '@/lib/icons';
import { spacing } from '@/lib/spacing';
import Badge from '@/components/ui/Badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import Spinner from '@/components/ui/Spinner';
interface DataCardProps<T> {
title: string;
data?: T[] | null;
count?: number;
isLoading?: boolean;
error?: Error | null;
emptyMessage?: string;
loadingMessage?: string;
errorMessage?: string;
countBadge?: boolean;
children?: (data: T[]) => React.ReactNode;
renderItem?: (item: T, index: number) => React.ReactNode;
}
function DataCard<T>({
title,
data,
count,
isLoading = false,
error,
emptyMessage,
loadingMessage,
errorMessage,
countBadge = true,
children,
renderItem,
}: DataCardProps<T>) {
const { common } = useCommonTranslations();
const defaultEmptyMessage = common.noData;
const defaultLoadingMessage = common.loading;
const defaultErrorMessage = common.error;
return (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>{title}</CardTitle>
{countBadge && count !== undefined && !isLoading && !error && (
<Badge variant="outline">{count}</Badge>
)}
</div>
</CardHeader>
<CardContent>
{isLoading ? (
<div className="flex items-center justify-center py-8">
<Spinner className={`${getIconClasses('lg')} mr-3`} />
<span className="text-muted-foreground">
{loadingMessage || defaultLoadingMessage}
</span>
</div>
) : error ? (
<div className="text-center py-8 text-destructive">
<p className="font-medium">
{errorMessage || defaultErrorMessage}
</p>
<p className="text-sm mt-1">{error.message}</p>
</div>
) : !data || data.length === 0 ? (
<div className="text-center py-8 text-muted-foreground">
<p>{emptyMessage || defaultEmptyMessage}</p>
</div>
) : children ? (
children(data)
) : renderItem ? (
<div className={spacing.content}>
{data.map((item, index) => renderItem(item, index))}
</div>
) : null}
</CardContent>
</Card>
);
}
export default React.memo(DataCard) as <T>(
props: DataCardProps<T>
) => React.JSX.Element;