Skip to main content
Glama
UserRequests.tsx7.07 kB
import React, { useEffect, useState } from 'react'; import '../App.css'; import { useAuth } from '../contexts/AuthContext'; import { fetchWithAuth } from '../utils/api'; import { useApiError } from '../utils/errorHandling'; import useLoading from '../hooks/useLoading'; import LoadingSpinner from '../components/LoadingSpinner'; // Interface para o formato de solicitação de acesso a grupo interface AccessRequest { request_id: string; username: string; grupo: string; status: 'pending' | 'approved' | 'rejected'; justificativa: string; created_at: string; updated_at: string | null; reviewed_by: string | null; review_comment: string | null; } // Interface para o formato do formulário de solicitação interface RequestFormData { grupo: string; justificativa: string; } const UserRequests: React.FC = () => { const { token, user } = useAuth(); const { isLoading, error, execute } = useLoading(); const { processApiResponse, handleApiError } = useApiError(); const [requests, setRequests] = useState<AccessRequest[]>([]); const [availableGroups, setAvailableGroups] = useState<string[]>([]); const [formData, setFormData] = useState<RequestFormData>({ grupo: '', justificativa: '', }); const [showForm, setShowForm] = useState(false); // Busca as solicitações do usuário atual const fetchUserRequests = async () => { if (!token) return; try { const result = await execute(fetchWithAuth('/tools/requests/me', token)); if (!result) return; const hasError = await processApiResponse(result); if (hasError) return; const data: AccessRequest[] = await result.json(); setRequests(data); } catch (err: any) { handleApiError(err); } }; // Busca grupos disponíveis para solicitar acesso const fetchAvailableGroups = async () => { if (!token) return; try { const result = await execute(fetchWithAuth('/tools/grupos/disponivel', token)); if (!result) return; const hasError = await processApiResponse(result); if (hasError) return; const data = await result.json(); setAvailableGroups(data.grupos || []); } catch (err: any) { handleApiError(err); } }; // Enviar solicitação de acesso const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!token) return; try { const result = await execute(fetchWithAuth('/tools/requests', token, { method: 'POST', body: JSON.stringify(formData), headers: { 'Content-Type': 'application/json' } })); if (!result) return; const hasError = await processApiResponse(result); if (hasError) return; // Limpar formulário e recarregar solicitações setFormData({ grupo: '', justificativa: '' }); setShowForm(false); fetchUserRequests(); } catch (err: any) { handleApiError(err); } }; // Handle input changes const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; useEffect(() => { if (token && user) { fetchUserRequests(); fetchAvailableGroups(); } }, [token, user]); // Renderiza status com cores const renderStatus = (status: string) => { switch (status) { case 'approved': return <span className="status approved">Aprovado</span>; case 'rejected': return <span className="status rejected">Rejeitado</span>; default: return <span className="status pending">Pendente</span>; } }; if (isLoading) { return <LoadingSpinner message="Carregando solicitações..." />; } return ( <div className="card user-requests-page"> <h2>Minhas Solicitações de Acesso</h2> {error && <div className="error-message">{error}</div>} <div className="action-bar"> <button className="primary-button" onClick={() => setShowForm(!showForm)} > {showForm ? 'Cancelar' : 'Nova Solicitação'} </button> </div> {showForm && ( <div className="request-form card"> <h3>Solicitar Acesso a Grupo</h3> <form onSubmit={handleSubmit}> <div className="form-group"> <label htmlFor="grupo">Grupo</label> <select id="grupo" name="grupo" value={formData.grupo} onChange={handleInputChange} required > <option value="">Selecione um grupo</option> {availableGroups.map(group => ( <option key={group} value={group}>{group}</option> ))} </select> </div> <div className="form-group"> <label htmlFor="justificativa">Justificativa</label> <textarea id="justificativa" name="justificativa" value={formData.justificativa} onChange={handleInputChange} placeholder="Explique por que você precisa de acesso a este grupo" required rows={4} minLength={5} maxLength={500} /> </div> <div className="button-group"> <button type="submit" className="primary-button">Enviar Solicitação</button> </div> </form> </div> )} {requests.length === 0 ? ( <p className="info-message">Você não possui solicitações de acesso pendentes ou realizadas.</p> ) : ( <div className="request-list"> <h3>Suas Solicitações</h3> {requests.map((req) => ( <div key={req.request_id} className="request-item card"> <div className="request-header"> <h4>Grupo: {req.grupo}</h4> {renderStatus(req.status)} </div> <div className="request-details"> <p><strong>Solicitado em:</strong> {new Date(req.created_at).toLocaleString()}</p> <p><strong>Justificativa:</strong> {req.justificativa}</p> {req.status !== 'pending' && ( <> <p><strong>Revisado por:</strong> {req.reviewed_by || 'N/A'}</p> <p><strong>Revisado em:</strong> {req.updated_at ? new Date(req.updated_at).toLocaleString() : 'N/A'}</p> {req.review_comment && ( <p><strong>Comentário:</strong> {req.review_comment}</p> )} </> )} </div> </div> ))} </div> )} </div> ); }; export default UserRequests;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jowpereira/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server