'use client' import { useState, useMemo } from 'react' import { useRouter } from 'next/navigation' import { Plus, Search, Lock, Pin, Eye, EyeOff, Pencil, Trash2, ChevronDown, Users2, FileText } from 'lucide-react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { createKbEntry, updateKbEntry, deleteKbEntry } from '@/lib/actions' import { revealSecret, deleteSecret } from '@/lib/actions/secrets' import { OpsAclEditor } from '@/components/ops/ops-acl-editor' import { createClient } from '@/lib/supabase/client' import { toast } from 'sonner' import type { Tables, TablesInsert } from '@/types/database' import { PageTitle } from '@/components/ui/page-title' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { useConfirm } from '@/components/ui/confirm' import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyContent } from '@/components/ui/empty' type KbEntry = Tables<'knowledge_base'> type AreaLead = Tables<'area_leads'> type Secret = Tables<'secrets'> interface Props { member: Tables<'space_members'> spaceId: string kbEntries: KbEntry[] areaLeads: AreaLead[] secrets: Secret[] canSeeSecrets: boolean canManageAcl?: boolean aclRoleOptions?: { value: string; label: string }[] aclByEntity?: Record } type Tab = 'kb' | 'processes' | 'secrets' | 'area-leads' const TABS: { id: Tab; label: string }[] = [ { id: 'kb', label: 'Knowledge Base' }, { id: 'processes', label: 'Processes' }, { id: 'secrets', label: 'Secrets & Credentials' }, { id: 'area-leads', label: 'Area Leads' }, ] const VISIBILITY_LABELS: Record = { all_members: 'All Members', board: 'Board', admin_only: 'Admin Only', } const VISIBILITY_COLORS: Record = { admin_only: 'text-red-600 bg-red-50 border-red-200', board: 'text-amber-600 bg-amber-50 border-amber-200', all_members: 'text-primary bg-primary/5 border-primary/20', } // ─── KB Entry Modal ──────────────────────────────────────────────────────────── function KbModal({ entry, onClose, onSaved, }: { entry: KbEntry | null onClose: () => void onSaved: (entry: KbEntry) => void }) { const isEdit = !!entry const [title, setTitle] = useState(entry?.title ?? '') const [content, setContent] = useState(entry?.content ?? '') const [area, setArea] = useState(entry?.area ?? '') const [visibility, setVisibility] = useState(entry?.visibility ?? 'all_members') const [isPinned, setIsPinned] = useState(entry?.is_pinned ?? false) const [isProcess, setIsProcess] = useState(entry?.tags?.includes('process') ?? false) const [saving, setSaving] = useState(false) async function handleSave(e: React.FormEvent) { e.preventDefault() if (!title.trim() || !content.trim()) return setSaving(true) const tags = isProcess ? ['process'] : [] let result if (isEdit) { result = await updateKbEntry(entry.id, { title, content, area, visibility, is_pinned: isPinned, tags }) } else { result = await createKbEntry({ title, content, area, visibility, is_pinned: isPinned, tags }) } setSaving(false) if ('error' in result && result.error) { toast.error(result.error) return } toast.success(isEdit ? 'Entry updated' : 'Entry created') if (!isEdit && 'data' in result && result.data) onSaved(result.data as KbEntry) else if (isEdit) onSaved({ ...entry, title, content, area, visibility, is_pinned: isPinned, tags }) onClose() } return ( { if (!o) onClose() }}> {isEdit ? 'Edit Entry' : 'New KB Entry'}
setTitle(e.target.value)} className="w-full bg-background border border-border rounded px-3 py-2 font-sans text-sm text-foreground focus:outline-none focus:border-primary transition" placeholder="e.g. How to open the space" required />