Improved UI for clients and ensured function of all current APIs

This commit is contained in:
Logan Cusano
2025-05-26 01:21:37 -04:00
parent 30707fc0d5
commit 7e55c120b1
2 changed files with 135 additions and 11 deletions

View File

@@ -113,7 +113,7 @@ const BotsManagement: React.FC<BotsManagementProps> = ({ token }) => {
guild_ids: newIdData.guild_ids.split(',').map(id => id.trim()).filter(id => id),
};
const method = editingId ? 'PUT' : 'POST';
const url = editingId ? `${API_BASE_URL}/discord_ids/${editingId._id}` : `${API_BASE_URL}/discord_ids/`;
const url = editingId ? `${API_BASE_URL}/bots/token/${editingId._id}` : `${API_BASE_URL}/bots/token`;
const response = await fetch(url, {
method,
@@ -147,7 +147,7 @@ const BotsManagement: React.FC<BotsManagementProps> = ({ token }) => {
if (!window.confirm('Are you sure you want to delete this Discord ID?')) return;
setError('');
try {
const response = await fetch(`${API_BASE_URL}/discord_ids/${id}`, {
const response = await fetch(`${API_BASE_URL}/bots/token/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
@@ -274,7 +274,7 @@ const BotsManagement: React.FC<BotsManagementProps> = ({ token }) => {
<TableCell>{dId.discord_id}</TableCell>
<TableCell className="truncate max-w-xs">{dId.token ? dId.token.substring(0, 8) + '...' : 'N/A'}</TableCell>
<TableCell>{dId.active ? 'Yes' : 'No'}</TableCell>
<TableCell>{dId.guild_ids.join(', ')}</TableCell>
<TableCell>{dId.guild_ids?.join(', ')}</TableCell>
<TableCell>
<Button variant="outline" size="sm" className="mr-2"
onClick={() => {
@@ -284,7 +284,7 @@ const BotsManagement: React.FC<BotsManagementProps> = ({ token }) => {
name: dId.name,
token: dId.token,
active: dId.active,
guild_ids: dId.guild_ids.join(', '),
guild_ids: dId.guild_ids?.join(', '),
});
setIsAddIdDialogOpen(true);
}}>

View File

@@ -8,9 +8,10 @@ import { ErrorResponse, NodeStatusResponse, System } from '@/types';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
interface IndividualClientPageProps {
clientId: string; // Now received as a prop
clientId: string;
token: string;
}
@@ -26,6 +27,16 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
const [isSetConfigDialogOpen, setIsSetConfigDialogOpen] = useState<boolean>(false);
const [availableSystems, setAvailableSystems] = useState<System[]>([]);
// Join Discord Popup States
const [isJoinDiscordDialogOpen, setIsJoinDiscordDialogOpen] = useState<boolean>(false);
const [discordServerId, setDiscordServerId] = useState<string>('');
const [discordChannelId, setDiscordChannelId] = useState<string>('');
// Leave Discord Popup States
const [isLeaveDiscordDialogOpen, setIsLeaveDiscordDialogOpen] = useState<boolean>(false);
const [leaveGuildId, setLeaveGuildId] = useState<string>('');
const fetchClientStatus = async () => {
try {
setLoading(true);
@@ -80,7 +91,11 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
fetchSystems(); // Fetch systems when the component mounts or clientId/token changes
}, [clientId, token]);
const handleAction = async (action: 'join' | 'leave' | 'op25_start' | 'op25_stop' | 'op25_set'): Promise<void> => {
const handleAction = async (
action: 'join' | 'leave' | 'op25_start' | 'op25_stop' | 'op25_set',
// Optional parameters for 'join' and 'leave' actions
payloadData?: { server_id?: string; channel_id?: string; guild_id?: string }
): Promise<void> => {
setMessage('');
setError('');
setLoading(true);
@@ -103,6 +118,25 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
httpOptions.body = JSON.stringify({
"system_id": selectedSystem
});
} else if (action === "join") {
if (!payloadData?.server_id || !payloadData?.channel_id) {
setError("Please provide both Discord Server ID and Channel ID.");
setLoading(false);
return;
}
httpOptions.body = JSON.stringify({
"guild_id": payloadData.server_id,
"channel_id": payloadData.channel_id
});
} else if (action === "leave") {
if (!payloadData?.guild_id) {
setError("Please provide the Guild ID to leave.");
setLoading(false);
return;
}
httpOptions.body = JSON.stringify({
"guild_id": payloadData.guild_id
});
}
const response = await fetch(`${API_BASE_URL}/nodes/${clientId}/${action}`, httpOptions);
@@ -112,7 +146,9 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
if (response.ok) {
setMessage(`Client "${clientId}" successfully ${action === 'op25_set' ? 'set config for system ' + selectedSystem : action + 'ed'}.`);
fetchClientStatus();
setIsSetConfigDialogOpen(false); // Close dialog on success
setIsSetConfigDialogOpen(false); // Close set config dialog on success
setIsJoinDiscordDialogOpen(false); // Close join discord dialog on success
setIsLeaveDiscordDialogOpen(false); // Close leave discord dialog on success
} else {
const errorData = data as ErrorResponse;
setError(`Failed to ${action} client "${clientId}": ${errorData.message || (typeof errorData.detail === 'string' ? errorData.detail : JSON.stringify(errorData.detail)) || response.statusText}`);
@@ -125,6 +161,26 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
}
};
const handleJoinClick = () => {
setDiscordServerId(''); // Clear previous values
setDiscordChannelId(''); // Clear previous values
setIsJoinDiscordDialogOpen(true);
};
const handleConfirmJoin = () => {
handleAction('join', { server_id: discordServerId, channel_id: discordChannelId });
};
const handleLeaveClick = () => {
setLeaveGuildId(''); // Clear previous value
setIsLeaveDiscordDialogOpen(true);
};
const handleConfirmLeave = () => {
handleAction('leave', { guild_id: leaveGuildId });
};
return (
<Card className="w-full max-w-md mx-auto mt-8">
<CardHeader>
@@ -134,19 +190,19 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
<p>Current Discord Status: {currentClientDiscordStatus}</p>
<div className="flex space-x-2">
<Button
onClick={() => handleAction('join')}
onClick={handleJoinClick} // Open dialog for join
disabled={loading}
className="flex-1"
>
{loading && message.includes('join') ? 'Joining...' : 'Join Client'}
Join Client
</Button>
<Button
onClick={() => handleAction('leave')}
onClick={handleLeaveClick} // Open dialog for leave
disabled={loading}
variant="outline"
className="flex-1"
>
{loading && message.includes('leave') ? 'Leaving...' : 'Leave Client'}
Leave Client
</Button>
</div>
<p>Current OP25 Status: {currentClientOp25Status}</p>
@@ -214,6 +270,74 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
</DialogFooter>
</DialogContent>
</Dialog>
{/* Join Discord Dialog */}
<Dialog open={isJoinDiscordDialogOpen} onOpenChange={setIsJoinDiscordDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Join Discord Server</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="discord-server-id" className="text-right">Server ID</Label>
<Input
id="discord-server-id"
value={discordServerId}
onChange={(e) => setDiscordServerId(e.target.value)}
className="col-span-3"
placeholder="Enter Discord Server ID"
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="discord-channel-id" className="text-right">Channel ID</Label>
<Input
id="discord-channel-id"
value={discordChannelId}
onChange={(e) => setDiscordChannelId(e.target.value)}
className="col-span-3"
placeholder="Enter Discord Channel ID"
/>
</div>
</div>
<DialogFooter>
<Button onClick={handleConfirmJoin} disabled={!discordServerId || !discordChannelId || loading}>
{loading ? 'Joining...' : 'Confirm Join'}
</Button>
<Button variant="outline" onClick={() => setIsJoinDiscordDialogOpen(false)}>
Cancel
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Leave Discord Dialog */}
<Dialog open={isLeaveDiscordDialogOpen} onOpenChange={setIsLeaveDiscordDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Leave Discord Server</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="leave-guild-id" className="text-right">Guild ID</Label>
<Input
id="leave-guild-id"
value={leaveGuildId}
onChange={(e) => setLeaveGuildId(e.target.value)}
className="col-span-3"
placeholder="Enter Discord Guild ID to Leave"
/>
</div>
</div>
<DialogFooter>
<Button onClick={handleConfirmLeave} disabled={!leaveGuildId || loading}>
{loading ? 'Leaving...' : 'Confirm Leave'}
</Button>
<Button variant="outline" onClick={() => setIsLeaveDiscordDialogOpen(false)}>
Cancel
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</Card>
);
};