import { useEffect, useState, useCallback } from 'react'; import { useParams, Link } from 'react-router-dom'; import { useRoleStore, type Role, type CreateRoleData, type UpdateRoleData } from '../stores/role.ts'; // Permission bitflags const PERMS = { VIEW_CHANNEL: 1, SEND_MESSAGES: 2, MANAGE_MESSAGES: 4, KICK_MEMBERS: 8, BAN_MEMBERS: 16, MANAGE_SERVER: 32, MANAGE_CHANNELS: 64, ADMINISTRATOR: 128, CONNECT_VOICE: 256, SPEAK_VOICE: 512, SHARE_SCREEN: 1024, MUTE_MEMBERS: 2048, CREATE_INSTANT_INVITE: 4096, CHANGE_NICKNAME: 8192, MANAGE_NICKNAMES: 16384, MANAGE_ROLES: 32768, MANAGE_WEBHOOKS: 65536, EMBED_LINKS: 131072, ATTACH_FILES: 262144, ADD_REACTIONS: 524288, USE_EXTERNAL_EMOJIS: 1048576, MENTION_EVERYONE: 2097152, } as const; const PERM_CATEGORIES = [ { name: 'General', perms: [ { key: 'VIEW_CHANNEL', label: 'View Channel', flag: PERMS.VIEW_CHANNEL }, { key: 'SEND_MESSAGES', label: 'Send Messages', flag: PERMS.SEND_MESSAGES }, { key: 'MANAGE_MESSAGES', label: 'Manage Messages', flag: PERMS.MANAGE_MESSAGES }, { key: 'ADD_REACTIONS', label: 'Add Reactions', flag: PERMS.ADD_REACTIONS }, { key: 'EMBED_LINKS', label: 'Embed Links', flag: PERMS.EMBED_LINKS }, { key: 'ATTACH_FILES', label: 'Attach Files', flag: PERMS.ATTACH_FILES }, { key: 'MENTION_EVERYONE', label: 'Mention @everyone', flag: PERMS.MENTION_EVERYONE }, { key: 'USE_EXTERNAL_EMOJIS', label: 'Use External Emojis', flag: PERMS.USE_EXTERNAL_EMOJIS }, ], }, { name: 'Moderation', perms: [ { key: 'KICK_MEMBERS', label: 'Kick Members', flag: PERMS.KICK_MEMBERS }, { key: 'BAN_MEMBERS', label: 'Ban Members', flag: PERMS.BAN_MEMBERS }, { key: 'MUTE_MEMBERS', label: 'Mute Members', flag: PERMS.MUTE_MEMBERS }, { key: 'MANAGE_NICKNAMES', label: 'Manage Nicknames', flag: PERMS.MANAGE_NICKNAMES }, { key: 'CHANGE_NICKNAME', label: 'Change Nickname', flag: PERMS.CHANGE_NICKNAME }, ], }, { name: 'Server', perms: [ { key: 'MANAGE_SERVER', label: 'Manage Server', flag: PERMS.MANAGE_SERVER }, { key: 'MANAGE_CHANNELS', label: 'Manage Channels', flag: PERMS.MANAGE_CHANNELS }, { key: 'MANAGE_ROLES', label: 'Manage Roles', flag: PERMS.MANAGE_ROLES }, { key: 'MANAGE_WEBHOOKS', label: 'Manage Webhooks', flag: PERMS.MANAGE_WEBHOOKS }, { key: 'CREATE_INSTANT_INVITE', label: 'Create Instant Invite', flag: PERMS.CREATE_INSTANT_INVITE }, { key: 'ADMINISTRATOR', label: 'Administrator', flag: PERMS.ADMINISTRATOR }, ], }, { name: 'Voice', perms: [ { key: 'CONNECT_VOICE', label: 'Connect Voice', flag: PERMS.CONNECT_VOICE }, { key: 'SPEAK_VOICE', label: 'Speak Voice', flag: PERMS.SPEAK_VOICE }, { key: 'SHARE_SCREEN', label: 'Share Screen', flag: PERMS.SHARE_SCREEN }, ], }, ]; const GRUVBOX_COLORS = [ '#fb4934', '#b8bb26', '#fabd2f', '#83a598', '#d3869b', '#8ec07c', '#fe8019', '#ebdbb2', '#a89984', '#928374', '#282828', ]; function hasPerm(permissions: number, flag: number): boolean { return (permissions & flag) !== 0; } function permSummary(permissions: number): string { if (hasPerm(permissions, PERMS.ADMINISTRATOR)) return 'ADMINISTRATOR'; const active: string[] = []; if (hasPerm(permissions, PERMS.MANAGE_SERVER)) active.push('MGR_SERVER'); if (hasPerm(permissions, PERMS.MANAGE_CHANNELS)) active.push('MGR_CHAN'); if (hasPerm(permissions, PERMS.MANAGE_ROLES)) active.push('MGR_ROLES'); if (hasPerm(permissions, PERMS.MANAGE_MESSAGES)) active.push('MGR_MSG'); if (hasPerm(permissions, PERMS.KICK_MEMBERS)) active.push('KICK'); if (hasPerm(permissions, PERMS.BAN_MEMBERS)) active.push('BAN'); if (hasPerm(permissions, PERMS.MUTE_MEMBERS)) active.push('MUTE'); return active.length > 0 ? active.join('+') : 'BASIC'; } interface RoleFormProps { serverId: string; role?: Role; onClose: () => void; } function RoleForm({ serverId, role, onClose }: RoleFormProps) { const createRole = useRoleStore((s) => s.createRole); const updateRole = useRoleStore((s) => s.updateRole); const loading = useRoleStore((s) => s.loading); const [name, setName] = useState(role?.name ?? ''); const [color, setColor] = useState(role?.color ?? '#ebdbb2'); const [position, setPosition] = useState(role?.position ?? 0); const [permissions, setPermissions] = useState(role?.permissions ?? 0); const togglePerm = useCallback((flag: number) => { setPermissions((prev) => prev ^ flag); }, []); const handleAdminToggle = useCallback(() => { setPermissions((prev) => { if (hasPerm(prev, PERMS.ADMINISTRATOR)) return 0; return 0xFFFFFFFF; // grant all }); }, []); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!name.trim()) return; try { if (role) { const data: UpdateRoleData = { name: name.trim(), color, permissions, position }; await updateRole(serverId, role.id, data); } else { const data: CreateRoleData = { name: name.trim(), color, permissions, position }; await createRole(serverId, data); } onClose(); } catch { // error in store } }; return (
{'>'} {role ? 'EDIT ROLE' : 'CREATE ROLE'}
ERR: no server selected
{'┌──────────────────────────────────┐\n'}
{'│ === ROLE MANAGER === │\n'}
{'└──────────────────────────────────┘'}
{error && (
ERR: {error}
)}[loading roles...]
)} {!loading && roles.length === 0 && ([no roles configured]
)}perms: {permSummary(role.permissions)}