GodSpeed / frontend /src /components /admin /AdminChannelManagement.tsx
AdithyaVardan's picture
fix: type errors, merge OAuth branch, upgrade BM25Store
e2766a5
import { useState } from 'react'
import { useQuery, useMutation } from '@tanstack/react-query'
import { apiFetch } from '@/lib/http'
import { Channel, CreateChannelInput, ChannelSensitivity } from '@/types/settings'
import { LoadingSkeleton } from '../common/LoadingSkeleton'
const SENSITIVITY_COLORS: Record<ChannelSensitivity, string> = {
public: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
internal: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200',
confidential: 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200',
restricted: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
}
const SENSITIVITY_ICONS: Record<ChannelSensitivity, string> = {
public: '🌐',
internal: '🔒',
confidential: '🔐',
restricted: '🚫',
}
export function AdminChannelManagement() {
const [showAddForm, setShowAddForm] = useState(false)
const [formData, setFormData] = useState<CreateChannelInput>({
name: '',
sensitivity: 'internal',
})
// Fetch channels
const { data: channels, isLoading, refetch } = useQuery({
queryKey: ['workspace-channels'],
queryFn: async () => {
// TODO: GET /api/workspace/channels
return [] as Channel[]
},
})
// Create channel
const { mutate: createChannel, isPending: isCreating } = useMutation({
mutationFn: async (input: CreateChannelInput) => {
// TODO: POST /api/workspace/channels
console.log('Create channel:', input)
},
onSuccess: () => {
setFormData({ name: '', sensitivity: 'internal' })
setShowAddForm(false)
refetch()
},
})
const handleCreateChannel = () => {
if (!formData.name.trim()) return
createChannel(formData)
}
return (
<div className="space-y-6 p-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-semibold">Channels</h3>
<p className="mt-1 text-xs text-stone-500">{channels?.length || 0} channels total</p>
</div>
<button
onClick={() => setShowAddForm(!showAddForm)}
className="rounded bg-brand px-3 py-1.5 text-sm font-medium text-white hover:bg-brand-dark"
>
+ Add Channel
</button>
</div>
{/* Create Channel Form */}
{showAddForm && (
<div className="space-y-4 rounded-lg border border-blue-200 bg-blue-50 p-4 dark:border-blue-900 dark:bg-stone-900">
<div>
<label className="block text-sm font-medium text-stone-700 dark:text-stone-300">
Channel Name
</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
placeholder="e.g., Engineering, HR, Finance"
className="mt-1 block w-full rounded border border-stone-300 bg-white px-3 py-2 text-stone-900 placeholder-stone-400 focus:border-brand focus:outline-none focus:ring-1 focus:ring-brand dark:border-stone-600 dark:bg-stone-800 dark:text-white"
/>
</div>
<div>
<label className="block text-sm font-medium text-stone-700 dark:text-stone-300">
Data Source Type (Optional)
</label>
<select
value={formData.source_type || ''}
onChange={(e) => setFormData({ ...formData, source_type: e.target.value || undefined })}
className="mt-1 block w-full rounded border border-stone-300 bg-white px-3 py-2 text-stone-900 focus:border-brand focus:outline-none focus:ring-1 focus:ring-brand dark:border-stone-600 dark:bg-stone-800 dark:text-white"
>
<option value="">General</option>
<option value="github">GitHub</option>
<option value="confluence">Confluence</option>
<option value="jira">Jira</option>
<option value="notion">Notion</option>
<option value="slack">Slack</option>
<option value="file">File Upload</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-stone-700 dark:text-stone-300">
Sensitivity Level
</label>
<div className="mt-2 grid grid-cols-2 gap-2">
{(Object.keys(SENSITIVITY_COLORS) as ChannelSensitivity[]).map((level) => (
<button
key={level}
onClick={() => setFormData({ ...formData, sensitivity: level })}
className={`rounded border-2 px-3 py-2 text-left text-sm font-medium transition-colors ${
formData.sensitivity === level
? `border-brand ${SENSITIVITY_COLORS[level]}`
: 'border-stone-300 text-stone-700 hover:border-stone-400 dark:border-stone-600 dark:text-stone-300'
}`}
>
<span className="mr-2">{SENSITIVITY_ICONS[level]}</span>
{level.charAt(0).toUpperCase() + level.slice(1)}
</button>
))}
</div>
<p className="mt-2 text-xs text-stone-500">
• Public: visible to all • Internal: team members only • Confidential: executives only • Restricted: need approval
</p>
</div>
<div className="flex gap-2">
<button
onClick={handleCreateChannel}
disabled={isCreating || !formData.name.trim()}
className="rounded bg-brand px-3 py-1.5 text-sm font-medium text-white hover:bg-brand-dark disabled:opacity-50"
>
{isCreating ? 'Creating...' : 'Create Channel'}
</button>
<button
onClick={() => setShowAddForm(false)}
className="rounded border border-stone-300 px-3 py-1.5 text-sm font-medium text-stone-700 hover:bg-stone-100 dark:border-stone-600 dark:text-stone-300 dark:hover:bg-stone-700"
>
Cancel
</button>
</div>
</div>
)}
{/* Channels List */}
<div>
{isLoading ? (
<LoadingSkeleton rows={4} className="h-16" />
) : channels && channels.length > 0 ? (
<div className="grid gap-3">
{channels.map((channel) => (
<div
key={channel.id}
className="flex items-start justify-between rounded-lg border border-stone-200 p-4 dark:border-stone-700"
>
<div className="flex-1">
<div className="flex items-center gap-2">
<h4 className="font-medium text-stone-900 dark:text-white">{channel.name}</h4>
<span
className={`rounded-full px-2 py-0.5 text-xs font-medium ${
SENSITIVITY_COLORS[channel.sensitivity]
}`}
>
{SENSITIVITY_ICONS[channel.sensitivity]} {channel.sensitivity}
</span>
</div>
<div className="mt-2 flex gap-3 text-xs text-stone-600 dark:text-stone-400">
{channel.source_type && <span>📁 {channel.source_type}</span>}
<span>Created {new Date(channel.created_at).toLocaleDateString()}</span>
</div>
</div>
<div className="flex gap-2">
<button className="text-xs font-medium text-stone-500 hover:text-brand">
Edit
</button>
<button className="text-xs font-medium text-stone-500 hover:text-red-600">
Delete
</button>
</div>
</div>
))}
</div>
) : (
<div className="rounded-lg border-2 border-dashed border-stone-300 p-8 text-center dark:border-stone-600">
<p className="text-sm text-stone-500">No channels yet</p>
<p className="mt-1 text-xs text-stone-400">
Create your first channel to organize data by team or department
</p>
</div>
)}
</div>
</div>
)
}