191 lines
7.1 KiB
TypeScript
191 lines
7.1 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { WorkItem, WorkItemFormData, WorkItemStatus } from '@/lib/types';
|
|
import { workItemApi } from '@/lib/api-client';
|
|
import WorkItemForm from '@/components/WorkItemForm';
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
import { faCog, faEdit, faTrash } from '@fortawesome/free-solid-svg-icons';
|
|
|
|
export default function ManagePage() {
|
|
const [workItems, setWorkItems] = useState<WorkItem[]>([]);
|
|
const [editingItem, setEditingItem] = useState<WorkItem | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
loadWorkItems();
|
|
}, []);
|
|
|
|
const loadWorkItems = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const items = await workItemApi.getWorkItems();
|
|
setWorkItems(items);
|
|
setError(null);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to load work items');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleCreate = async (data: WorkItemFormData) => {
|
|
try {
|
|
const newItem = await workItemApi.createWorkItem(data);
|
|
setWorkItems([...workItems, newItem]);
|
|
} catch (err) {
|
|
alert('Failed to create work item. Please try again.');
|
|
console.error('Error creating item:', err);
|
|
}
|
|
};
|
|
|
|
const handleEdit = (item: WorkItem) => {
|
|
setEditingItem(item);
|
|
};
|
|
|
|
const handleUpdate = async (data: WorkItemFormData) => {
|
|
if (editingItem) {
|
|
try {
|
|
const updatedItem = { ...editingItem, title: data.title, description: data.description };
|
|
await workItemApi.updateWorkItem(editingItem.id, updatedItem);
|
|
await loadWorkItems();
|
|
setEditingItem(null);
|
|
} catch (err) {
|
|
alert('Failed to update work item. Please try again.');
|
|
console.error('Error updating item:', err);
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleDelete = async (id: number) => {
|
|
if (confirm('Are you sure you want to delete this item?')) {
|
|
try {
|
|
await workItemApi.deleteWorkItem(id);
|
|
await loadWorkItems();
|
|
} catch (err) {
|
|
alert('Failed to delete work item. Please try again.');
|
|
console.error('Error deleting item:', err);
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleCancelEdit = () => {
|
|
setEditingItem(null);
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="px-4 sm:px-0">
|
|
<div className="max-w-2xl mx-auto">
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
|
<div className="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
|
<h1 className="text-lg font-semibold text-gray-900 flex items-center">
|
|
<FontAwesomeIcon icon={faCog} className="mr-2 h-5 w-5" />
|
|
Admin Panel
|
|
</h1>
|
|
</div>
|
|
<div className="p-6 text-center text-gray-500">
|
|
Loading work items...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="px-4 sm:px-0">
|
|
<div className="max-w-2xl mx-auto">
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
|
<div className="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
|
<h1 className="text-lg font-semibold text-gray-900 flex items-center">
|
|
<FontAwesomeIcon icon={faCog} className="mr-2 h-5 w-5" />
|
|
Admin Panel
|
|
</h1>
|
|
</div>
|
|
<div className="p-6 text-center text-red-600">
|
|
Error: {error}
|
|
<button
|
|
onClick={loadWorkItems}
|
|
className="ml-2 px-3 py-1 bg-blue-600 text-white rounded text-sm hover:bg-blue-700"
|
|
>
|
|
Retry
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="px-4 sm:px-0">
|
|
<div className="max-w-2xl mx-auto">
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
|
<div className="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
|
<h1 className="text-lg font-semibold text-gray-900 flex items-center">
|
|
<FontAwesomeIcon icon={faCog} className="mr-2 h-5 w-5" />
|
|
Admin Panel
|
|
</h1>
|
|
</div>
|
|
|
|
<div className="p-6">
|
|
<div className="mb-8">
|
|
<h2 className="text-md font-medium text-gray-900 mb-4">
|
|
{editingItem ? 'Edit Work Item' : 'Create New Work Item'}
|
|
</h2>
|
|
<WorkItemForm
|
|
initialData={editingItem ? { title: editingItem.title, description: editingItem.description } : undefined}
|
|
onSubmit={editingItem ? handleUpdate : handleCreate}
|
|
submitText={editingItem ? 'Update' : 'Save'}
|
|
/>
|
|
{editingItem && (
|
|
<button
|
|
onClick={handleCancelEdit}
|
|
className="mt-2 inline-flex items-center px-3 py-1 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
>
|
|
Cancel
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="text-md font-medium text-gray-900 mb-4">Existing Work Items</h3>
|
|
<div className="space-y-3">
|
|
{workItems.map((item) => (
|
|
<div
|
|
key={item.id}
|
|
className="flex items-center justify-between p-4 bg-gray-50 rounded-lg border border-gray-200"
|
|
>
|
|
<div className="flex-1">
|
|
<h4 className="text-sm font-medium text-gray-900">{item.title}</h4>
|
|
<p className="text-sm text-gray-600 mt-1 truncate">{item.description}</p>
|
|
</div>
|
|
<div className="flex items-center space-x-2 ml-4">
|
|
<button
|
|
onClick={() => handleEdit(item)}
|
|
className="inline-flex items-center px-2 py-1 border border-gray-300 text-xs font-medium rounded text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
>
|
|
<FontAwesomeIcon icon={faEdit} className="h-3 w-3 mr-1" />
|
|
Edit
|
|
</button>
|
|
<button
|
|
onClick={() => handleDelete(item.id)}
|
|
className="inline-flex items-center px-2 py-1 border border-red-300 text-xs font-medium rounded text-red-700 bg-white hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
|
|
>
|
|
<FontAwesomeIcon icon={faTrash} className="h-3 w-3 mr-1" />
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |