"use client"; // ============================================================================ // Workspace Settings Page // ============================================================================ import { useState, useEffect } from "react"; import { useParams, useRouter } from "next/navigation"; import { Loader2, Settings, Pencil, Trash2, Users, Shield, ChevronDown, UserPlus, Mail, Clock } from "lucide-react"; import { DashboardHeader } from "@/components/layout/dashboard-header"; import { useWorkspace } from "@/components/providers/workspace-provider"; import { workspacesApi, membersApi, invitesApi } from "@/lib/api"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { DropdownMenu, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import type { WorkspaceMember, WorkspaceInvite, PermissionKey } from "@/lib/types/api"; // ============================================================================ // Constants // ============================================================================ const PERMISSION_LABELS: Record = { admin_full: "Полный доступ", projects_read: "Просмотр проектов", projects_write: "Редактирование проектов", placements_read: "Просмотр размещений", placements_write: "Редактирование размещений", analytics_read: "Просмотр аналитики", }; const ALL_PERMISSIONS: PermissionKey[] = [ "admin_full", "projects_read", "projects_write", "placements_read", "placements_write", "analytics_read", ]; // ============================================================================ // Component // ============================================================================ export default function SettingsPage() { const params = useParams(); const router = useRouter(); const workspaceId = params?.workspaceId as string; const { currentWorkspace, refreshWorkspaces } = useWorkspace(); const [name, setName] = useState(currentWorkspace?.name || ""); const [isUpdating, setIsUpdating] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [deleteConfirm, setDeleteConfirm] = useState(""); const [showDeleteDialog, setShowDeleteDialog] = useState(false); // Members and invites const [members, setMembers] = useState([]); const [invites, setInvites] = useState([]); const [loadingMembers, setLoadingMembers] = useState(true); const [updating, setUpdating] = useState(null); const [showInviteDialog, setShowInviteDialog] = useState(false); const [username, setUsername] = useState(""); const [isCreating, setIsCreating] = useState(false); const [error, setError] = useState(null); useEffect(() => { if (currentWorkspace) { setName(currentWorkspace.name); } }, [currentWorkspace]); useEffect(() => { loadMembers(); }, [workspaceId]); const loadMembers = async () => { try { setLoadingMembers(true); const [membersResponse, invitesResponse] = await Promise.all([ membersApi.list(workspaceId, { size: 100 }), invitesApi.list(workspaceId, { size: 100 }), ]); setMembers(membersResponse.items); setInvites(invitesResponse.items); } catch (err) { console.error("Failed to load data:", err); } finally { setLoadingMembers(false); } }; const handleUpdate = async () => { if (!name.trim() || name === currentWorkspace?.name) return; try { setIsUpdating(true); await workspacesApi.update(workspaceId, { name: name.trim() }); await refreshWorkspaces(); } catch (err) { console.error("Failed to update workspace:", err); } finally { setIsUpdating(false); } }; const handleCreateInvite = async () => { if (!username.trim()) return; try { setIsCreating(true); setError(null); const cleanUsername = username.trim().replace(/^@/, ""); const invite = await invitesApi.create(workspaceId, { username: cleanUsername, }); setInvites((prev) => [invite, ...prev]); setShowInviteDialog(false); setUsername(""); } catch (err: any) { setError(err?.error?.message || "Не удалось отправить приглашение"); } finally { setIsCreating(false); } }; const handlePermissionChange = async ( memberId: string, userId: string, permission: PermissionKey, enabled: boolean ) => { try { setUpdating(memberId); const member = members.find((m) => m.id === memberId); if (!member) return; let newPermissions = [...member.permissions]; if (enabled) { if (!newPermissions.some((p) => p.key === permission)) { newPermissions.push({ key: permission }); } } else { newPermissions = newPermissions.filter((p) => p.key !== permission); } await membersApi.updatePermissions(workspaceId, userId, { permissions: newPermissions, }); setMembers((prev) => prev.map((m) => m.id === memberId ? { ...m, permissions: newPermissions } : m ) ); } catch (err) { console.error("Failed to update permissions:", err); } finally { setUpdating(null); } }; const handleDelete = async () => { if (deleteConfirm !== `Удалить ${currentWorkspace?.name}`) return; try { setIsDeleting(true); await workspacesApi.delete(workspaceId); router.replace("/"); } catch (err) { console.error("Failed to delete workspace:", err); } finally { setIsDeleting(false); setShowDeleteDialog(false); setDeleteConfirm(""); } }; const hasPermission = (member: WorkspaceMember, key: PermissionKey) => { return member.permissions.some((p) => p.key === key); }; // Combine members and pending invites const pendingInvites = invites.filter((inv) => inv.status === "pending"); const allPeople = [ ...members.map((m) => ({ type: "member" as const, data: m })), ...pendingInvites.map((inv) => ({ type: "invite" as const, data: inv })), ]; if (!currentWorkspace) { return ( <>
); } return ( <>

Настройки воркспейса

Управление настройками воркспейса

{/* General Settings */} Основные настройки Название и основные параметры воркспейса
setName(e.target.value)} placeholder="Название воркспейса" />
{/* Team Management */}
Участники Управление участниками воркспейса
Пригласить участника Введите username Telegram пользователя для отправки приглашения через бота
setUsername(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { handleCreateInvite(); } }} />
{error && (
{error}
)}
{loadingMembers ? (
) : allPeople.length === 0 ? (

Нет участников

Пригласите участников в воркспейс

) : ( Пользователь Статус Права доступа {allPeople.map((item) => { if (item.type === "invite") { const invite = item.data; return (
@{invite.username}
Ожидает принятия
); } else { const member = item.data; return (
{[member.user.first_name, member.user.last_name] .filter(Boolean) .join(" ") || member.user.username || `User ${member.user.telegram_id}`}
{member.user.username && (
@{member.user.username}
)}
{member.status === "active" ? "Активен" : "Приглашён"}
{hasPermission(member, "admin_full") ? ( Админ ) : ( member.permissions.map((p) => ( {PERMISSION_LABELS[p.key]} )) )} {member.permissions.length === 0 && ( Нет прав )}
Права доступа {ALL_PERMISSIONS.map((perm) => ( handlePermissionChange( member.id, member.user.id, perm, checked ) } > {PERMISSION_LABELS[perm]} ))}
); } })}
)}
{/* Danger Zone */} Опасная зона Необратимые действия с воркспейсом Удалить воркспейс? Это действие необратимо. Все данные воркспейса будут удалены, включая проекты, креативы, размещения и аналитику.
setDeleteConfirm(e.target.value)} placeholder={`Удалить ${currentWorkspace.name}`} className="font-mono" />
{ setShowDeleteDialog(false); setDeleteConfirm(""); }}> Отмена {isDeleting ? ( <> Удаление... ) : ( "Удалить" )}
); }