Improved UI for clients and ensured function of all current APIs
This commit is contained in:
@@ -113,7 +113,7 @@ const BotsManagement: React.FC<BotsManagementProps> = ({ token }) => {
|
|||||||
guild_ids: newIdData.guild_ids.split(',').map(id => id.trim()).filter(id => id),
|
guild_ids: newIdData.guild_ids.split(',').map(id => id.trim()).filter(id => id),
|
||||||
};
|
};
|
||||||
const method = editingId ? 'PUT' : 'POST';
|
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, {
|
const response = await fetch(url, {
|
||||||
method,
|
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;
|
if (!window.confirm('Are you sure you want to delete this Discord ID?')) return;
|
||||||
setError('');
|
setError('');
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE_URL}/discord_ids/${id}`, {
|
const response = await fetch(`${API_BASE_URL}/bots/token/${id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
@@ -274,7 +274,7 @@ const BotsManagement: React.FC<BotsManagementProps> = ({ token }) => {
|
|||||||
<TableCell>{dId.discord_id}</TableCell>
|
<TableCell>{dId.discord_id}</TableCell>
|
||||||
<TableCell className="truncate max-w-xs">{dId.token ? dId.token.substring(0, 8) + '...' : 'N/A'}</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.active ? 'Yes' : 'No'}</TableCell>
|
||||||
<TableCell>{dId.guild_ids.join(', ')}</TableCell>
|
<TableCell>{dId.guild_ids?.join(', ')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Button variant="outline" size="sm" className="mr-2"
|
<Button variant="outline" size="sm" className="mr-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -284,7 +284,7 @@ const BotsManagement: React.FC<BotsManagementProps> = ({ token }) => {
|
|||||||
name: dId.name,
|
name: dId.name,
|
||||||
token: dId.token,
|
token: dId.token,
|
||||||
active: dId.active,
|
active: dId.active,
|
||||||
guild_ids: dId.guild_ids.join(', '),
|
guild_ids: dId.guild_ids?.join(', '),
|
||||||
});
|
});
|
||||||
setIsAddIdDialogOpen(true);
|
setIsAddIdDialogOpen(true);
|
||||||
}}>
|
}}>
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ import { ErrorResponse, NodeStatusResponse, System } from '@/types';
|
|||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
|
||||||
interface IndividualClientPageProps {
|
interface IndividualClientPageProps {
|
||||||
clientId: string; // Now received as a prop
|
clientId: string;
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +27,16 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
|
|||||||
const [isSetConfigDialogOpen, setIsSetConfigDialogOpen] = useState<boolean>(false);
|
const [isSetConfigDialogOpen, setIsSetConfigDialogOpen] = useState<boolean>(false);
|
||||||
const [availableSystems, setAvailableSystems] = useState<System[]>([]);
|
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 () => {
|
const fetchClientStatus = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -80,7 +91,11 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
|
|||||||
fetchSystems(); // Fetch systems when the component mounts or clientId/token changes
|
fetchSystems(); // Fetch systems when the component mounts or clientId/token changes
|
||||||
}, [clientId, token]);
|
}, [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('');
|
setMessage('');
|
||||||
setError('');
|
setError('');
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -103,6 +118,25 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
|
|||||||
httpOptions.body = JSON.stringify({
|
httpOptions.body = JSON.stringify({
|
||||||
"system_id": selectedSystem
|
"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);
|
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) {
|
if (response.ok) {
|
||||||
setMessage(`Client "${clientId}" successfully ${action === 'op25_set' ? 'set config for system ' + selectedSystem : action + 'ed'}.`);
|
setMessage(`Client "${clientId}" successfully ${action === 'op25_set' ? 'set config for system ' + selectedSystem : action + 'ed'}.`);
|
||||||
fetchClientStatus();
|
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 {
|
} else {
|
||||||
const errorData = data as ErrorResponse;
|
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}`);
|
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 (
|
return (
|
||||||
<Card className="w-full max-w-md mx-auto mt-8">
|
<Card className="w-full max-w-md mx-auto mt-8">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
@@ -134,19 +190,19 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
|
|||||||
<p>Current Discord Status: {currentClientDiscordStatus}</p>
|
<p>Current Discord Status: {currentClientDiscordStatus}</p>
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleAction('join')}
|
onClick={handleJoinClick} // Open dialog for join
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
>
|
>
|
||||||
{loading && message.includes('join') ? 'Joining...' : 'Join Client'}
|
Join Client
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleAction('leave')}
|
onClick={handleLeaveClick} // Open dialog for leave
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
>
|
>
|
||||||
{loading && message.includes('leave') ? 'Leaving...' : 'Leave Client'}
|
Leave Client
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p>Current OP25 Status: {currentClientOp25Status}</p>
|
<p>Current OP25 Status: {currentClientOp25Status}</p>
|
||||||
@@ -214,6 +270,74 @@ const IndividualClientPage: React.FC<IndividualClientPageProps> = ({ clientId, t
|
|||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</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>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user