kreativortex/components/Common/AppDataView.tsx
Jessica Rekcah 4253483f44 jalan
2025-12-02 00:22:34 +07:00

131 lines
4.2 KiB
TypeScript

/**
* File: AppDataView.tsx
* Created by: AI Assistant
* Date: 2025-11-29
* Purpose: Data view component for kreatiVortex platform
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
*/
'use client';
import React from 'react';
import { cn } from '@/lib/utils';
interface Column<T> {
key: keyof T;
label: string;
render?: (value: T[keyof T], row: T) => React.ReactNode;
className?: string;
}
interface AppDataViewProps<T> {
data: T[];
columns: Column<T>[];
loading?: boolean;
className?: string;
emptyMessage?: string;
}
function AppDataView<T extends Record<string, unknown>>({
data,
columns,
loading = false,
className,
emptyMessage = 'Tidak ada data tersedia'
}: AppDataViewProps<T>) {
if (loading) {
return (
<div className="space-y-4">
{/* Skeleton rows */}
{Array.from({ length: 5 }).map((_, index) => (
<div key={index} className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
<div className="space-y-4">
<div className="h-4 bg-gray-600/20 rounded animate-pulse"></div>
<div className="h-4 bg-gray-600/20 rounded animate-pulse"></div>
<div className="h-4 bg-gray-600/20 rounded animate-pulse w-3/4"></div>
</div>
</div>
))}
</div>
);
}
if (data.length === 0) {
return (
<div className={cn(
'bg-white/10 backdrop-blur-sm rounded-xl p-12 border border-white/20 text-center',
className
)}>
<div className="w-16 h-16 bg-gray-600/20 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
<h3 className="text-lg font-medium text-gray-300 mb-2">{emptyMessage}</h3>
<p className="text-gray-400">Coba ubah filter atau tambah data baru</p>
</div>
);
}
return (
<div className={cn(
'bg-white/10 backdrop-blur-sm rounded-xl border border-white/20 overflow-hidden',
className
)}>
{/* Table for desktop */}
<div className="hidden lg:block overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-white/10">
{columns.map((column) => (
<th
key={column.key as string}
className={cn(
'px-6 py-4 text-left text-xs font-medium text-gray-300 uppercase tracking-wider',
column.className
)}
>
{column.label}
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-white/10">
{data.map((row, index) => (
<tr key={index} className="hover:bg-white/5 transition-colors">
{columns.map((column) => (
<td
key={column.key as string}
className="px-6 py-4 whitespace-nowrap text-sm text-gray-300"
>
{column.render ? column.render(row[column.key], row) : String(row[column.key] || '')}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
{/* Cards for mobile */}
<div className="lg:hidden space-y-4 p-4">
{data.map((row, index) => (
<div key={index} className="bg-white/5 rounded-lg p-4 border border-white/10">
{columns.map((column) => (
<div key={column.key as string} className="mb-3">
<div className="text-xs font-medium text-gray-400 uppercase tracking-wider mb-1">
{column.label}
</div>
<div className="text-sm text-gray-300">
{column.render ? column.render(row[column.key], row) : String(row[column.key] || '')}
</div>
</div>
))}
</div>
))}
</div>
</div>
);
}
export default AppDataView;