Files
Content-Hunter_FE/src/components/content/ContentTable.tsx
Harun CAN 823d11f88d
Some checks failed
UI Deploy (Next-Auth Support) 🎨 / build-and-deploy (push) Has been cancelled
main
2026-03-22 23:39:41 +03:00

191 lines
7.8 KiB
TypeScript

"use client";
import { Box, Table, Badge, HStack, IconButton, Button } from "@chakra-ui/react";
import { useState, useEffect, useCallback } from "react";
import { useSession } from "next-auth/react";
import { LuEye, LuPencil, LuTrash2, LuRefreshCw } from "react-icons/lu";
import { toaster } from "@/components/ui/feedback/toaster";
import { ContentPreviewDialog } from "./ContentPreviewDialog";
const getStatusColor = (status: string) => {
switch (status?.toLowerCase()) {
case "published": return "green";
case "draft": return "gray";
case "scheduled": return "blue";
case "review": return "orange";
default: return "gray";
}
};
export function ContentTable() {
const { data: session } = useSession();
const [selectedItem, setSelectedItem] = useState<any>(null);
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
const [contentList, setContentList] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(false);
const fetchContent = useCallback(async () => {
setIsLoading(true);
try {
const headers: HeadersInit = {};
if (session?.accessToken) {
headers['Authorization'] = `Bearer ${session.accessToken}`;
}
const res = await fetch('/api/backend/content', {
headers,
});
if (res.ok) {
const responseData = await res.json();
// Handle wrapped response from global interceptor: { success, data, message }
const items = responseData?.data || responseData;
setContentList(Array.isArray(items) ? items : []);
}
} catch (error) {
console.error("Failed to fetch content:", error);
} finally {
setIsLoading(false);
}
}, [session]);
useEffect(() => {
fetchContent();
}, [fetchContent]);
const handleDelete = async (item: any) => {
if (!session?.accessToken) return;
try {
const res = await fetch(`/api/backend/content/${item.id}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${session.accessToken}`
}
});
if (res.ok) {
toaster.create({ title: "Deleted", description: `"${item.title || 'Content'}" removed.`, type: "success" });
fetchContent();
} else {
toaster.create({ title: "Delete failed", type: "error" });
}
} catch {
toaster.create({ title: "Delete failed", type: "error" });
}
};
const handleAction = (action: string, item: any) => {
if (action === 'View') {
setSelectedItem(item);
setIsPreviewOpen(true);
return;
}
if (action === 'Edit') {
// Open the preview in edit mode
setSelectedItem(item);
setIsPreviewOpen(true);
return;
}
if (action === 'Delete') {
handleDelete(item);
return;
}
};
return (
<>
<HStack justify="flex-end" mb={3}>
<Button
size="sm"
variant="outline"
onClick={fetchContent}
loading={isLoading}
>
<LuRefreshCw /> Refresh
</Button>
</HStack>
<Box borderWidth="1px" borderRadius="lg" overflow="hidden" bg="bg.panel">
<Table.Root striped>
<Table.Header>
<Table.Row>
<Table.ColumnHeader>Title</Table.ColumnHeader>
<Table.ColumnHeader>Platform</Table.ColumnHeader>
<Table.ColumnHeader>Status</Table.ColumnHeader>
<Table.ColumnHeader>Date</Table.ColumnHeader>
<Table.ColumnHeader textAlign="right">Actions</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{isLoading ? (
<Table.Row>
<Table.Cell colSpan={5} textAlign="center" py={10}>
<HStack justify="center" gap={2}>
<LuRefreshCw className="animate-spin" />
<Box>Loading content...</Box>
</HStack>
</Table.Cell>
</Table.Row>
) : contentList.length === 0 ? (
<Table.Row>
<Table.Cell colSpan={5} textAlign="center" py={10}>
No content found. Start by generating some!
</Table.Cell>
</Table.Row>
) : contentList.map((item) => (
<Table.Row key={item.id}>
<Table.Cell fontWeight="medium">
{item.title || item.body?.substring(0, 60) + '...' || 'Untitled'}
</Table.Cell>
<Table.Cell>{item.type || item.platform}</Table.Cell>
<Table.Cell>
<Badge colorPalette={getStatusColor(item.status)} variant="solid">
{item.status}
</Badge>
</Table.Cell>
<Table.Cell>{new Date(item.createdAt || item.date).toLocaleDateString()}</Table.Cell>
<Table.Cell textAlign="right">
<HStack justify="flex-end" gap={2}>
<IconButton
variant="ghost"
size="sm"
aria-label="View"
onClick={() => handleAction('View', item)}
>
<LuEye />
</IconButton>
<IconButton
variant="ghost"
size="sm"
aria-label="Edit"
onClick={() => handleAction('Edit', item)}
>
<LuPencil />
</IconButton>
<IconButton
variant="ghost"
size="sm"
colorPalette="red"
aria-label="Delete"
onClick={() => handleAction('Delete', item)}
>
<LuTrash2 />
</IconButton>
</HStack>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
</Box>
<ContentPreviewDialog
item={selectedItem}
open={isPreviewOpen}
onOpenChange={(e) => setIsPreviewOpen(e.open)}
onContentUpdated={fetchContent}
/>
</>
);
}