70 lines
2.0 KiB
TypeScript
70 lines
2.0 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import type { AppCardProps } from '@/types/component-props';
|
|
|
|
export default function AppCard({
|
|
app,
|
|
showActions = true,
|
|
onClick,
|
|
onDelete,
|
|
}: AppCardProps) {
|
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
|
|
const handleDelete = async (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
if (!confirm('Delete this app?')) return;
|
|
|
|
setIsDeleting(true);
|
|
onDelete?.(app.id ?? '');
|
|
};
|
|
|
|
const statusColors = {
|
|
generating: 'bg-yellow-100 text-yellow-800',
|
|
completed: 'bg-green-100 text-green-800',
|
|
failed: 'bg-red-100 text-red-800',
|
|
};
|
|
|
|
return (
|
|
<div
|
|
onClick={() => onClick?.(app.id ?? '')}
|
|
className="bg-white border rounded-lg p-4 hover:shadow-md transition-shadow cursor-pointer"
|
|
>
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<h3 className="font-semibold text-gray-900">{app.title}</h3>
|
|
<span className={`text-xs px-2 py-1 rounded ${statusColors[app.status]}`}>
|
|
{app.status}
|
|
</span>
|
|
</div>
|
|
{app.appType && (
|
|
<span className="inline-block text-xs bg-gray-100 text-gray-600 px-2 py-1 rounded mb-2">
|
|
{app.appType}
|
|
</span>
|
|
)}
|
|
{app.description && (
|
|
<p className="text-sm text-gray-600 line-clamp-2 mb-2">
|
|
{app.description}
|
|
</p>
|
|
)}
|
|
{app.createdAt && (
|
|
<p className="text-xs text-gray-500">
|
|
Created: {new Date(app.createdAt).toLocaleDateString()}
|
|
</p>
|
|
)}
|
|
</div>
|
|
{showActions && (
|
|
<button
|
|
onClick={handleDelete}
|
|
disabled={isDeleting}
|
|
className="ml-4 text-red-500 hover:text-red-700 disabled:opacity-50"
|
|
>
|
|
{isDeleting ? '⏳' : '🗑️'}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|