feat: channel settings modal now has name editing

Added [OVERVIEW] tab to ChannelSettingsModal with an editable
channel name field. The ⚙ button on each channel opens the modal.
Users can rename channels without needing to know about the
right-click context menu.
This commit is contained in:
2026-07-02 09:10:33 -04:00
parent 7f2a58599d
commit af961d805e
+52 -7
View File
@@ -1,7 +1,9 @@
import { useEffect, useState } from 'react';
import { usePermissionStore, PERMISSION_LABELS, PERMS, type ChannelOverride, hasPermission } from '../stores/permissions.ts';
import { useRoleStore } from '../stores/role.ts';
import { useMemberStore } from '../stores/member.ts';
import { useEffect, useState } from "react";
import { usePermissionStore, PERMISSION_LABELS, PERMS, type ChannelOverride, hasPermission } from "../stores/permissions.ts";
import { useRoleStore } from "../stores/role.ts";
import { useMemberStore } from "../stores/member.ts";
import { useChannelStore } from "../stores/channel.ts";
import { api } from "../lib/api.ts";
interface ChannelSettingsModalProps {
serverId: string;
@@ -10,7 +12,7 @@ interface ChannelSettingsModalProps {
onClose: () => void;
}
type Tab = 'permissions';
type Tab = 'overview' | 'permissions';
type TargetMode = 'role' | 'user';
interface OverrideFormData {
@@ -76,10 +78,13 @@ function OverrideMatrix({ allow, deny, onChange }: { allow: number; deny: number
}
export function ChannelSettingsModal({ serverId, channelId, channelName, onClose }: ChannelSettingsModalProps) {
const [tab] = useState<Tab>('permissions');
const [tab, setTab] = useState<Tab>('overview');
const [form, setForm] = useState<OverrideFormData>({ targetType: 'role', targetId: '', allow: 0, deny: 0 });
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
const [editName, setEditName] = useState(channelName);
const [savingName, setSavingName] = useState(false);
const updateChannel = useChannelStore((s) => s.updateChannel);
const overrides = usePermissionStore((s) => s.overridesByChannel[channelId] || []);
const fetchOverrides = usePermissionStore((s) => s.fetchOverrides);
@@ -122,6 +127,24 @@ export function ChannelSettingsModal({ serverId, channelId, channelName, onClose
}
};
const handleSaveName = async () => {
const name = editName.trim();
if (!name || name === channelName) return;
setSavingName(true);
setError(null);
try {
const updated = await api.patch<{ id: string; server_id: string; name: string; type: string; category: string | null; position: number; group_id?: string | null }>(
`/servers/${serverId}/channels/${channelId}`,
{ name }
);
updateChannel(updated as any);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to rename channel');
} finally {
setSavingName(false);
}
};
const targets = form.targetType === 'role'
? roles.filter((r) => !r.is_default).map((r) => ({ id: r.id, label: r.name }))
: members.map((m) => ({ id: m.id, label: m.username }));
@@ -134,9 +157,31 @@ export function ChannelSettingsModal({ serverId, channelId, channelName, onClose
<button onClick={onClose} className="text-xs text-gb-red">[x]</button>
</div>
<div className="flex border-b border-gb-bg-t">
<button className="px-4 py-2 text-xs text-gb-orange bg-gb-bg-s">[PERMISSIONS]</button>
<button onClick={() => setTab('overview')} className={`px-4 py-2 text-xs ${tab === 'overview' ? 'text-gb-orange bg-gb-bg-s' : 'text-gb-fg-s hover:text-gb-fg'}`}>[OVERVIEW]</button>
<button onClick={() => setTab('permissions')} className={`px-4 py-2 text-xs ${tab === 'permissions' ? 'text-gb-orange bg-gb-bg-s' : 'text-gb-fg-s hover:text-gb-fg'}`}>[PERMISSIONS]</button>
</div>
<div className="flex-1 overflow-y-auto p-4">
{tab === 'overview' && (
<div className="space-y-3">
<div className="text-xs text-gb-fg-f">Channel name</div>
<div className="flex gap-2">
<input
value={editName}
onChange={(e) => setEditName(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') handleSaveName(); }}
className="terminal-input text-sm flex-1"
/>
<button
onClick={handleSaveName}
disabled={savingName || !editName.trim() || editName.trim() === channelName}
className="px-3 py-1 bg-gb-orange text-gb-bg text-xs disabled:opacity-50"
>
{savingName ? 'SAVING...' : '[SAVE]'}
</button>
</div>
{error && <div className="text-xs text-gb-red">ERR: {error}</div>}
</div>
)}
{tab === 'permissions' && (
<div className="space-y-3">
<div className="text-xs text-gb-fg-f">Existing overrides</div>