feat(event-form): habilita seleção de empresa para business owners
- Atualiza o EventForm para buscar a lista de empresas via /api/empresas para usuários BUSINESS_OWNER e SUPERADMIN. - Adiciona campo de seleção de 'Empresa' antes do carregamento das FOTs (Turmas). - Implementa lógica em cascata: Seleção de Empresa -> Carrega Turmas -> Filtra Cursos -> Filtra Instituições. - Adiciona mensagem de aviso quando a empresa selecionada não possui turmas cadastradas. - Refatora o gerenciamento de estado para utilizar o ID da empresa ao invés do nome, garantindo maior integridade.
This commit is contained in:
parent
8515796ae9
commit
0e3e74eb59
1 changed files with 108 additions and 46 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useMemo } from "react";
|
||||||
import { EventType, EventStatus, Address } from "../types";
|
import { EventType, EventStatus, Address } from "../types";
|
||||||
import { Input, Select } from "./Input";
|
import { Input, Select } from "./Input";
|
||||||
import { Button } from "./Button";
|
import { Button } from "./Button";
|
||||||
|
|
@ -26,7 +26,7 @@ import { useData } from "../contexts/DataContext";
|
||||||
import { UserRole } from "../types";
|
import { UserRole } from "../types";
|
||||||
import { InstitutionForm } from "./InstitutionForm";
|
import { InstitutionForm } from "./InstitutionForm";
|
||||||
import { MapboxMap } from "./MapboxMap";
|
import { MapboxMap } from "./MapboxMap";
|
||||||
import { getEventTypes, EventTypeResponse, getCadastroFot, createAgenda } from "../services/apiService";
|
import { getEventTypes, EventTypeResponse, getCadastroFot, createAgenda, getCompanies } from "../services/apiService";
|
||||||
|
|
||||||
interface EventFormProps {
|
interface EventFormProps {
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
|
@ -121,48 +121,30 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
fetchEventTypes();
|
fetchEventTypes();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Fetch FOTs filtered by user company
|
|
||||||
const [availableFots, setAvailableFots] = useState<any[]>([]);
|
|
||||||
const [loadingFots, setLoadingFots] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const loadFots = async () => {
|
|
||||||
// Allow FOT loading for Business Owners, Event Owners (Clients), and Superadmins
|
|
||||||
if (user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.EVENT_OWNER || user?.role === UserRole.SUPERADMIN) {
|
|
||||||
// If user is not superadmin (admin generally has no empresaId but sees all or selects one, here we assume superadmin logic is separate or allowed)
|
|
||||||
// Check if regular user has empresaId
|
|
||||||
if (user?.role !== UserRole.SUPERADMIN && !user?.empresaId) {
|
|
||||||
// If no company linked, do not load FOTs
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoadingFots(true);
|
|
||||||
const token = localStorage.getItem("token") || "";
|
|
||||||
// Use empresaId from user context if available
|
|
||||||
const empresaId = user.empresaId;
|
|
||||||
const response = await getCadastroFot(token, empresaId);
|
|
||||||
|
|
||||||
if (response.data) {
|
|
||||||
// If we didn't filter by API (e.g. no empresaId), filter client side as fallback
|
|
||||||
const myFots = (empresaId || user.companyName)
|
|
||||||
? response.data.filter(f =>
|
|
||||||
(empresaId && f.empresa_id === empresaId) ||
|
|
||||||
(user.companyName && f.empresa_nome === user.companyName)
|
|
||||||
)
|
|
||||||
: response.data;
|
|
||||||
|
|
||||||
setAvailableFots(myFots);
|
|
||||||
}
|
|
||||||
setLoadingFots(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
loadFots();
|
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
// Derived state for dropdowns
|
// Derived state for dropdowns
|
||||||
|
const [companies, setCompanies] = useState<any[]>([]); // New state for companies list
|
||||||
|
const [selectedCompanyId, setSelectedCompanyId] = useState(""); // Changed from Name to ID
|
||||||
|
|
||||||
const [selectedCourseName, setSelectedCourseName] = useState("");
|
const [selectedCourseName, setSelectedCourseName] = useState("");
|
||||||
const [selectedInstitutionName, setSelectedInstitutionName] = useState("");
|
const [selectedInstitutionName, setSelectedInstitutionName] = useState("");
|
||||||
|
|
||||||
|
// Load Companies for Business Owner / Superadmin
|
||||||
|
useEffect(() => {
|
||||||
|
if (user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.SUPERADMIN) {
|
||||||
|
const fetchCompanies = async () => {
|
||||||
|
try {
|
||||||
|
const response = await getCompanies();
|
||||||
|
if (response.data) {
|
||||||
|
setCompanies(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load companies", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchCompanies();
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
// Populate form with initialData
|
// Populate form with initialData
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
|
|
@ -171,11 +153,17 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
...prev,
|
...prev,
|
||||||
...initialData,
|
...initialData,
|
||||||
startTime: initialData.time || "00:00",
|
startTime: initialData.time || "00:00",
|
||||||
locationName: (initialData as any).local_evento || (initialData as any).address?.mapLink?.includes('http') ? "" : (initialData as any).address?.mapLink || "", // Try to recover location name or clear if it's a link
|
locationName: (initialData as any).local_evento || (initialData as any).address?.mapLink?.includes('http') ? "" : (initialData as any).address?.mapLink || "",
|
||||||
fotId: initialData.fotId || "",
|
fotId: initialData.fotId || "",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 2. Populate derived dropdowns if data exists
|
// 2. Populate derived dropdowns if data exists
|
||||||
|
// Check for empresa_id or empresaId in initialData
|
||||||
|
const initEmpresaId = (initialData as any).empresa_id || (initialData as any).empresaId;
|
||||||
|
if (initEmpresaId && (user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.SUPERADMIN)) {
|
||||||
|
setSelectedCompanyId(initEmpresaId);
|
||||||
|
}
|
||||||
|
|
||||||
if (initialData.curso) {
|
if (initialData.curso) {
|
||||||
setSelectedCourseName(initialData.curso);
|
setSelectedCourseName(initialData.curso);
|
||||||
}
|
}
|
||||||
|
|
@ -195,9 +183,47 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [initialData]);
|
}, [initialData, user?.role]);
|
||||||
|
|
||||||
// Unique Courses
|
// Fetch FOTs filtered by user company OR selected company
|
||||||
|
const [availableFots, setAvailableFots] = useState<any[]>([]);
|
||||||
|
const [loadingFots, setLoadingFots] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadFots = async () => {
|
||||||
|
// Determine which company ID to use
|
||||||
|
let targetEmpresaId = user?.empresaId;
|
||||||
|
|
||||||
|
if (user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.SUPERADMIN) {
|
||||||
|
// Must select a company first
|
||||||
|
if (!selectedCompanyId) {
|
||||||
|
setAvailableFots([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
targetEmpresaId = selectedCompanyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a target company (or user is linked), fetch FOTs
|
||||||
|
if (targetEmpresaId || user?.role === UserRole.EVENT_OWNER) { // EventOwner might be linked differently or via getCadastroFot logic
|
||||||
|
// Verify logic: EventOwner (client) usually has strict link.
|
||||||
|
// If targetEmpresaId is still empty and user is NOT BusinessOwner/Superadmin, we might skip or let backend decide (if user has implicit link).
|
||||||
|
// But let's assume valid flow.
|
||||||
|
|
||||||
|
setLoadingFots(true);
|
||||||
|
const token = localStorage.getItem("token") || "";
|
||||||
|
const response = await getCadastroFot(token, targetEmpresaId);
|
||||||
|
|
||||||
|
if (response.data) {
|
||||||
|
setAvailableFots(response.data);
|
||||||
|
}
|
||||||
|
setLoadingFots(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadFots();
|
||||||
|
}, [user, selectedCompanyId]);
|
||||||
|
|
||||||
|
|
||||||
|
// Unique Courses (from availableFots - which are already specific to the company)
|
||||||
const uniqueCourses = Array.from(new Set(availableFots.map(f => f.curso_nome))).sort();
|
const uniqueCourses = Array.from(new Set(availableFots.map(f => f.curso_nome))).sort();
|
||||||
|
|
||||||
// Filtered Institutions based on Course
|
// Filtered Institutions based on Course
|
||||||
|
|
@ -611,7 +637,7 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
<div className="bg-gray-50 p-4 rounded-md border border-gray-200">
|
<div className="bg-gray-50 p-4 rounded-md border border-gray-200">
|
||||||
<h3 className="text-sm font-medium text-gray-700 mb-4 uppercase tracking-wider">Seleção da Turma</h3>
|
<h3 className="text-sm font-medium text-gray-700 mb-4 uppercase tracking-wider">Seleção da Turma</h3>
|
||||||
|
|
||||||
{!user?.empresaId && user?.role !== UserRole.SUPERADMIN ? (
|
{!user?.empresaId && user?.role !== UserRole.SUPERADMIN && user?.role !== UserRole.BUSINESS_OWNER ? (
|
||||||
<div className="bg-red-50 border-l-4 border-red-400 p-4 mb-4">
|
<div className="bg-red-50 border-l-4 border-red-400 p-4 mb-4">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
|
|
@ -626,6 +652,42 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
{/* 0. Empresa (Only for Business Owner / Superadmin) */}
|
||||||
|
{(user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.SUPERADMIN) && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block text-sm text-gray-600 mb-1">Empresa</label>
|
||||||
|
<select
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-brand-gold focus:border-brand-gold"
|
||||||
|
value={selectedCompanyId}
|
||||||
|
onChange={e => {
|
||||||
|
setSelectedCompanyId(e.target.value);
|
||||||
|
setSelectedCourseName("");
|
||||||
|
setSelectedInstitutionName("");
|
||||||
|
setFormData({ ...formData, fotId: "" });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="">Selecione a Empresa</option>
|
||||||
|
{companies.map(c => <option key={c.id} value={c.id}>{c.nome}</option>)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Warning if Company Selected but No FOTs */}
|
||||||
|
{selectedCompanyId && !loadingFots && availableFots.length === 0 && (user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.SUPERADMIN) && (
|
||||||
|
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-4">
|
||||||
|
<div className="flex">
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<AlertTriangle className="h-5 w-5 text-yellow-400" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
<div className="ml-3">
|
||||||
|
<p className="text-sm text-yellow-700">
|
||||||
|
Esta empresa selecionada ainda não possui turmas, cursos ou FOTs cadastrados.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 1. Curso */}
|
{/* 1. Curso */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label className="block text-sm text-gray-600 mb-1">Curso</label>
|
<label className="block text-sm text-gray-600 mb-1">Curso</label>
|
||||||
|
|
@ -637,7 +699,7 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
setSelectedInstitutionName("");
|
setSelectedInstitutionName("");
|
||||||
setFormData({ ...formData, fotId: "" });
|
setFormData({ ...formData, fotId: "" });
|
||||||
}}
|
}}
|
||||||
disabled={loadingFots}
|
disabled={loadingFots || ((user?.role === UserRole.BUSINESS_OWNER || user?.role === UserRole.SUPERADMIN) && !selectedCompanyId)}
|
||||||
>
|
>
|
||||||
<option value="">Selecione o Curso</option>
|
<option value="">Selecione o Curso</option>
|
||||||
{uniqueCourses.map(c => <option key={c} value={c}>{c}</option>)}
|
{uniqueCourses.map(c => <option key={c} value={c}>{c}</option>)}
|
||||||
|
|
@ -685,7 +747,7 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setActiveTab("location")}
|
onClick={() => setActiveTab("location")}
|
||||||
className="w-full sm:w-auto"
|
className="w-full sm:w-auto"
|
||||||
disabled={(!user?.empresaId && user?.role !== UserRole.SUPERADMIN)}
|
disabled={(!user?.empresaId && user?.role !== UserRole.SUPERADMIN && user?.role !== UserRole.BUSINESS_OWNER)}
|
||||||
>
|
>
|
||||||
Próximo: Localização
|
Próximo: Localização
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue