CreateChannelDialog.tsx raw

   1  import { useChat } from '@/providers/ChatProvider'
   2  import { TAccessMode } from '@/services/chat.service'
   3  import { Globe, Loader2, Lock, LockOpen } from 'lucide-react'
   4  import { useState } from 'react'
   5  import { Button } from '../ui/button'
   6  
   7  const accessModes: { mode: TAccessMode; label: string; icon: React.ReactNode; desc: string }[] = [
   8    { mode: 'open', label: 'Open', icon: <Globe className="size-3" />, desc: 'Anyone authenticated can read and write' },
   9    { mode: 'whitelist', label: 'Whitelist', icon: <Lock className="size-3" />, desc: 'Only listed members can access' },
  10    { mode: 'blacklist', label: 'Blacklist', icon: <LockOpen className="size-3" />, desc: 'Everyone except excluded users can access' }
  11  ]
  12  
  13  export default function CreateChannelDialog({
  14    open,
  15    onOpenChange
  16  }: {
  17    open: boolean
  18    onOpenChange: (open: boolean) => void
  19  }) {
  20    const { createChannel } = useChat()
  21    const [name, setName] = useState('')
  22    const [about, setAbout] = useState('')
  23    const [accessMode, setAccessMode] = useState<TAccessMode>('open')
  24    const [isCreating, setIsCreating] = useState(false)
  25  
  26    if (!open) return null
  27  
  28    const handleCreate = async () => {
  29      if (!name.trim()) return
  30      setIsCreating(true)
  31      try {
  32        await createChannel(name.trim(), about.trim(), accessMode)
  33        setName('')
  34        setAbout('')
  35        setAccessMode('open')
  36        onOpenChange(false)
  37      } finally {
  38        setIsCreating(false)
  39      }
  40    }
  41  
  42    const current = accessModes.find((m) => m.mode === accessMode)!
  43  
  44    return (
  45      <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onClick={() => onOpenChange(false)}>
  46        <div
  47          className="bg-background border rounded-lg p-4 w-80 space-y-3"
  48          onClick={(e) => e.stopPropagation()}
  49        >
  50          <h3 className="font-semibold">Create Channel</h3>
  51          <input
  52            type="text"
  53            placeholder="Channel name"
  54            value={name}
  55            onChange={(e) => setName(e.target.value)}
  56            className="w-full px-3 py-2 text-sm border rounded-md bg-background"
  57            autoFocus
  58            onKeyDown={(e) => e.key === 'Enter' && handleCreate()}
  59          />
  60          <input
  61            type="text"
  62            placeholder="Description (optional)"
  63            value={about}
  64            onChange={(e) => setAbout(e.target.value)}
  65            className="w-full px-3 py-2 text-sm border rounded-md bg-background"
  66            onKeyDown={(e) => e.key === 'Enter' && handleCreate()}
  67          />
  68          <div className="space-y-1.5">
  69            <span className="text-xs text-muted-foreground">Access mode</span>
  70            <div className="flex gap-1">
  71              {accessModes.map(({ mode, label, icon }) => (
  72                <button
  73                  key={mode}
  74                  className={`text-xs px-3 py-1.5 rounded border flex items-center gap-1.5 ${accessMode === mode ? 'bg-primary text-primary-foreground border-primary' : 'border-border'}`}
  75                  onClick={() => setAccessMode(mode)}
  76                >
  77                  {icon} {label}
  78                </button>
  79              ))}
  80            </div>
  81            <div className="text-[10px] text-muted-foreground">{current.desc}</div>
  82          </div>
  83          <div className="flex justify-end gap-2">
  84            <Button variant="ghost" size="sm" onClick={() => onOpenChange(false)}>
  85              Cancel
  86            </Button>
  87            <Button size="sm" onClick={handleCreate} disabled={!name.trim() || isCreating}>
  88              {isCreating ? <Loader2 className="size-4 animate-spin" /> : 'Create'}
  89            </Button>
  90          </div>
  91        </div>
  92      </div>
  93    )
  94  }
  95