feat(workspace): add WorkspaceEdit and WorkspaceCreate pages

This commit is contained in:
2026-03-13 01:56:56 +08:00
parent 228de3c180
commit b291689b1c
11 changed files with 1287 additions and 54 deletions

View File

@@ -0,0 +1,99 @@
import { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { ArrowLeft, ArrowRight, Edit2, Trash2 } from 'lucide-react';
import { useTranslation } from '../hooks/useTranslation';
import { getWorkspace, deleteWorkspace, type Workspace } from '../api/workspaces';
export default function WorkspaceDetail() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const { t, lang } = useTranslation();
const [workspace, setWorkspace] = useState<Workspace | null>(null);
const [isLoading, setIsLoading] = useState(true);
const isRtl = lang === 'fa';
const BackIcon = isRtl ? ArrowRight : ArrowLeft;
useEffect(() => {
if (id) loadWorkspace();
}, [id]);
const loadWorkspace = async () => {
try {
const data = await getWorkspace(id!);
setWorkspace(data);
} catch (error) {
console.error(error);
navigate('/workspaces');
} finally {
setIsLoading(false);
}
};
const handleDelete = async () => {
if (!window.confirm(t.workspace?.confirmDelete) || !id) return;
try {
await deleteWorkspace(id);
navigate('/workspaces');
} catch (error) {
console.error(error);
}
};
if (isLoading || !workspace) {
return <div className="p-8 text-center">{t.workspace?.loading}</div>;
}
const canEdit = workspace.my_role === 'owner' || workspace.my_role === 'admin';
return (
<div className="max-w-4xl mx-auto p-6">
<button
onClick={() => navigate('/workspaces')}
className="flex items-center gap-2 text-slate-500 hover:text-slate-900 dark:hover:text-white mb-6 transition-colors"
>
<BackIcon className="h-5 w-5" />
<span>{t.workspace?.back}</span>
</button>
<div className="bg-white dark:bg-slate-900 rounded-xl p-8 border border-slate-200 dark:border-slate-800 shadow-sm relative">
<div className="flex justify-between items-start mb-6">
<div>
<h1 className="text-3xl font-bold text-slate-900 dark:text-white mb-2">
{workspace.name}
</h1>
<span className="inline-block px-3 py-1 bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 text-sm rounded-full font-medium">
{workspace.my_role ? t.workspace?.roles[workspace.my_role] : "-"}
</span>
</div>
{canEdit && (
<div className="flex gap-2">
<button
onClick={() => navigate(`/workspaces/${id}/edit`)}
className="p-2 text-slate-500 hover:text-emerald-600 bg-slate-50 dark:bg-slate-800 rounded-lg"
>
<Edit2 className="h-5 w-5" />
</button>
{workspace.my_role === 'owner' && (
<button
onClick={handleDelete}
className="p-2 text-slate-500 hover:text-red-600 bg-slate-50 dark:bg-slate-800 rounded-lg"
>
<Trash2 className="h-5 w-5" />
</button>
)}
</div>
)}
</div>
<div className="prose dark:prose-invert max-w-none">
<h3 className="text-lg font-semibold mb-2">{t.workspace?.descriptionLabel}</h3>
<p className="text-slate-600 dark:text-slate-400 whitespace-pre-wrap">
{workspace.description || t.workspace?.noDescription}
</p>
</div>
</div>
</div>
);
}