feat(fot-management): implementação de ações editar/excluir e correção no mapeamento da agenda
- Implementadas ações de Editar e Excluir na página de Gestão de FOT - Adicionado filtro de busca para FOTs - Corrigido desalinhamento de colunas na tabela de Gestão de FOT - Atualizado FotForm para suportar a edição de registros existentes - Corrigido erro de renderização do React no Dashboard mapeando corretamente os objetos de atribuição - Removidos dados de mock (INITIAL_EVENTS) e corrigido erro de referência nula no DataContext - Adicionados métodos de atualização/exclusão ao apiService
This commit is contained in:
parent
52e167475c
commit
28b76a0f54
23 changed files with 2192 additions and 406 deletions
|
|
@ -33,3 +33,6 @@ test: ## Executa os testes
|
|||
|
||||
swagger: ## Gera documentação Swagger
|
||||
swag init -g cmd/api/main.go -o docs
|
||||
|
||||
import-fot: ## Importa dados de FOT do CSV (Use DB_DSN=... make import-fot para produção)
|
||||
go run cmd/importer/main.go
|
||||
|
|
|
|||
|
|
@ -190,6 +190,11 @@ func main() {
|
|||
api.GET("/agenda/:id", agendaHandler.Get)
|
||||
api.PUT("/agenda/:id", agendaHandler.Update)
|
||||
api.DELETE("/agenda/:id", agendaHandler.Delete)
|
||||
api.POST("/agenda/:id/professionals", agendaHandler.AssignProfessional)
|
||||
api.DELETE("/agenda/:id/professionals/:profId", agendaHandler.RemoveProfessional)
|
||||
api.GET("/agenda/:id/professionals", agendaHandler.GetProfessionals)
|
||||
api.PATCH("/agenda/:id/professionals/:profId/status", agendaHandler.UpdateAssignmentStatus)
|
||||
api.PATCH("/agenda/:id/status", agendaHandler.UpdateStatus)
|
||||
|
||||
admin := api.Group("/admin")
|
||||
{
|
||||
|
|
|
|||
231
backend/cmd/importer/main.go
Normal file
231
backend/cmd/importer/main.go
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
"github.com/joho/godotenv"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load .env
|
||||
// Try loading from current directory first, typical when running 'go run cmd/importer/main.go' from root
|
||||
if err := godotenv.Load(".env"); err != nil {
|
||||
// Fallback for when running from cmd/importer
|
||||
if err := godotenv.Load("../../.env"); err != nil {
|
||||
log.Println("Warning: .env file not found in .env or ../../.env")
|
||||
}
|
||||
}
|
||||
|
||||
dbURL := os.Getenv("DB_DSN")
|
||||
if dbURL == "" {
|
||||
log.Fatal("DB_DSN is not set")
|
||||
}
|
||||
|
||||
db, err := sql.Open("pgx", dbURL)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
log.Fatalf("Failed to ping database: %v", err)
|
||||
}
|
||||
|
||||
// Open CSV File
|
||||
filename := "importacao_fots.csv"
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open CSV file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Use Windows-1252 Decoder
|
||||
r := csv.NewReader(transform.NewReader(f, charmap.Windows1252.NewDecoder()))
|
||||
r.Comma = ';'
|
||||
r.LazyQuotes = true // Allow messy quotes
|
||||
|
||||
// Read Header
|
||||
header, err := r.Read()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read header: %v", err)
|
||||
}
|
||||
fmt.Printf("Header: %v\n", header)
|
||||
|
||||
ctx := context.Background()
|
||||
rowsProcessed := 0
|
||||
rowsInserted := 0
|
||||
rowsFailed := 0
|
||||
|
||||
for {
|
||||
record, err := r.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Error reading row %d: %v. Skipping.", rowsProcessed+1, err)
|
||||
rowsFailed++
|
||||
continue
|
||||
}
|
||||
rowsProcessed++
|
||||
|
||||
// Map Columns (Indices based on "FOT;Empresa;EF I / EF II;Observa‡äes;Institui‡Æo;Ano Formatura;Cidade;Estado;Gastos Capta‡Æo;Pr‚ Venda")
|
||||
// 0: FOT
|
||||
// 1: Empresa
|
||||
// 2: Cursos (was EF I / EF II)
|
||||
// 3: Observacoes
|
||||
// 4: Instituicao
|
||||
// 5: Ano Formatura
|
||||
// 6: Cidade
|
||||
// 7: Estado
|
||||
// 8: Gastos Captacao
|
||||
// 9: Pre Venda
|
||||
|
||||
fotStr := strings.TrimSpace(record[0])
|
||||
empresaName := strings.TrimSpace(record[1])
|
||||
cursoName := strings.TrimSpace(record[2])
|
||||
observacoes := strings.TrimSpace(record[3])
|
||||
instituicao := strings.TrimSpace(record[4])
|
||||
anoFormatura := strings.TrimSpace(record[5])
|
||||
cidade := strings.TrimSpace(record[6])
|
||||
estado := strings.TrimSpace(record[7])
|
||||
gastosStr := strings.TrimSpace(record[8])
|
||||
preVendaStr := strings.TrimSpace(record[9])
|
||||
|
||||
// Basic Validation
|
||||
if fotStr == "" || fotStr == "FOT" { // Skip header repetition or empty lines
|
||||
continue
|
||||
}
|
||||
|
||||
fot, err := strconv.Atoi(fotStr)
|
||||
if err != nil {
|
||||
log.Printf("Row %d: Invalid FOT '%s'. Skipping.", rowsProcessed, fotStr)
|
||||
rowsFailed++
|
||||
continue
|
||||
}
|
||||
|
||||
// 1. Upsert Empresa
|
||||
var empresaID string
|
||||
if empresaName == "" {
|
||||
empresaName = "Não Identificada"
|
||||
}
|
||||
err = db.QueryRowContext(ctx, `
|
||||
INSERT INTO empresas (nome) VALUES ($1)
|
||||
ON CONFLICT (nome) DO UPDATE SET nome = EXCLUDED.nome
|
||||
RETURNING id`, empresaName).Scan(&empresaID)
|
||||
if err != nil {
|
||||
log.Printf("Row %d: Failed to upsert empresa '%s': %v", rowsProcessed, empresaName, err)
|
||||
rowsFailed++
|
||||
continue
|
||||
}
|
||||
|
||||
// 2. Upsert Curso
|
||||
var cursoID string
|
||||
if cursoName == "" || cursoName == "-" {
|
||||
cursoName = "Geral"
|
||||
}
|
||||
err = db.QueryRowContext(ctx, `
|
||||
INSERT INTO cursos (nome) VALUES ($1)
|
||||
ON CONFLICT (nome) DO UPDATE SET nome = EXCLUDED.nome
|
||||
RETURNING id`, cursoName).Scan(&cursoID)
|
||||
if err != nil {
|
||||
log.Printf("Row %d: Failed to upsert curso '%s': %v", rowsProcessed, cursoName, err)
|
||||
rowsFailed++
|
||||
continue
|
||||
}
|
||||
|
||||
// 3. Upsert Ano Formatura
|
||||
var anoID string
|
||||
if anoFormatura == "" {
|
||||
anoFormatura = "Indefinido"
|
||||
}
|
||||
err = db.QueryRowContext(ctx, `
|
||||
INSERT INTO anos_formaturas (ano_semestre) VALUES ($1)
|
||||
ON CONFLICT (ano_semestre) DO UPDATE SET ano_semestre = EXCLUDED.ano_semestre
|
||||
RETURNING id`, anoFormatura).Scan(&anoID)
|
||||
if err != nil {
|
||||
log.Printf("Row %d: Failed to upsert ano '%s': %v", rowsProcessed, anoFormatura, err)
|
||||
rowsFailed++
|
||||
continue
|
||||
}
|
||||
|
||||
// 4. Parse Currency (Gastos Captação)
|
||||
// Format: "R$ 2.176,60" -> 2176.60
|
||||
gastosVal := 0.0
|
||||
if gastosStr != "" && gastosStr != "-" {
|
||||
// Remove "R$", "." and trim
|
||||
clean := strings.ReplaceAll(gastosStr, "R$", "")
|
||||
clean = strings.ReplaceAll(clean, ".", "") // Remove thousand separator
|
||||
clean = strings.ReplaceAll(clean, ",", ".") // Replace decimal separator
|
||||
clean = strings.TrimSpace(clean)
|
||||
|
||||
// Handle trailing/leading spaces hidden chars if any
|
||||
// Just parse float
|
||||
val, err := strconv.ParseFloat(clean, 64)
|
||||
if err == nil {
|
||||
gastosVal = val
|
||||
} else {
|
||||
// log.Printf("Row %d: Warning parsing gastos '%s' -> '%s': %v", rowsProcessed, gastosStr, clean, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Parse Boolean (Pre Venda)
|
||||
preVenda := false
|
||||
if strings.ToLower(preVendaStr) == "sim" {
|
||||
preVenda = true
|
||||
}
|
||||
|
||||
// 6. Insert Cadastro FOT
|
||||
_, err = db.ExecContext(ctx, `
|
||||
INSERT INTO cadastro_fot (
|
||||
fot, empresa_id, curso_id, ano_formatura_id,
|
||||
instituicao, cidade, estado, observacoes,
|
||||
gastos_captacao, pre_venda
|
||||
) VALUES (
|
||||
$1, $2, $3, $4,
|
||||
$5, $6, $7, $8,
|
||||
$9, $10
|
||||
)
|
||||
ON CONFLICT (fot) DO UPDATE SET
|
||||
empresa_id = EXCLUDED.empresa_id,
|
||||
curso_id = EXCLUDED.curso_id,
|
||||
ano_formatura_id = EXCLUDED.ano_formatura_id,
|
||||
instituicao = EXCLUDED.instituicao,
|
||||
cidade = EXCLUDED.cidade,
|
||||
estado = EXCLUDED.estado,
|
||||
observacoes = EXCLUDED.observacoes,
|
||||
gastos_captacao = EXCLUDED.gastos_captacao,
|
||||
pre_venda = EXCLUDED.pre_venda,
|
||||
updated_at = NOW()
|
||||
`,
|
||||
fot, empresaID, cursoID, anoID,
|
||||
instituicao, cidade, estado, observacoes,
|
||||
gastosVal, preVenda,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Row %d: Failed to insert FOT %d: %v", rowsProcessed, fot, err)
|
||||
rowsFailed++
|
||||
} else {
|
||||
rowsInserted++
|
||||
if rowsInserted%50 == 0 {
|
||||
fmt.Printf("Progress: %d records upserted...\n", rowsInserted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("\n--- Import Summary ---\n")
|
||||
fmt.Printf("Total Rows Processed: %d\n", rowsProcessed)
|
||||
fmt.Printf("Successfully Upserted: %d\n", rowsInserted)
|
||||
fmt.Printf("Failed: %d\n", rowsFailed)
|
||||
}
|
||||
|
|
@ -651,6 +651,49 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/agenda/{id}/professionals": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Get professionals assigned to agenda",
|
||||
"responses": {}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Assign professional to agenda",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/agenda/{id}/professionals/{profId}": {
|
||||
"delete": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Remove professional from agenda",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/agenda/{id}/professionals/{profId}/status": {
|
||||
"patch": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Update professional assignment status",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/agenda/{id}/status": {
|
||||
"patch": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Update agenda status",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/anos-formaturas": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
|
@ -2867,6 +2910,9 @@ const docTemplate = `{
|
|||
"educacao_simpatia": {
|
||||
"type": "integer"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"endereco": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -645,6 +645,49 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/agenda/{id}/professionals": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Get professionals assigned to agenda",
|
||||
"responses": {}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Assign professional to agenda",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/agenda/{id}/professionals/{profId}": {
|
||||
"delete": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Remove professional from agenda",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/agenda/{id}/professionals/{profId}/status": {
|
||||
"patch": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Update professional assignment status",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/agenda/{id}/status": {
|
||||
"patch": {
|
||||
"tags": [
|
||||
"agenda"
|
||||
],
|
||||
"summary": "Update agenda status",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/anos-formaturas": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
|
@ -2861,6 +2904,9 @@
|
|||
"educacao_simpatia": {
|
||||
"type": "integer"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"endereco": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -301,6 +301,8 @@ definitions:
|
|||
type: integer
|
||||
educacao_simpatia:
|
||||
type: integer
|
||||
email:
|
||||
type: string
|
||||
endereco:
|
||||
type: string
|
||||
equipamentos:
|
||||
|
|
@ -862,6 +864,35 @@ paths:
|
|||
summary: Update agenda event
|
||||
tags:
|
||||
- agenda
|
||||
/api/agenda/{id}/professionals:
|
||||
get:
|
||||
responses: {}
|
||||
summary: Get professionals assigned to agenda
|
||||
tags:
|
||||
- agenda
|
||||
post:
|
||||
responses: {}
|
||||
summary: Assign professional to agenda
|
||||
tags:
|
||||
- agenda
|
||||
/api/agenda/{id}/professionals/{profId}:
|
||||
delete:
|
||||
responses: {}
|
||||
summary: Remove professional from agenda
|
||||
tags:
|
||||
- agenda
|
||||
/api/agenda/{id}/professionals/{profId}/status:
|
||||
patch:
|
||||
responses: {}
|
||||
summary: Update professional assignment status
|
||||
tags:
|
||||
- agenda
|
||||
/api/agenda/{id}/status:
|
||||
patch:
|
||||
responses: {}
|
||||
summary: Update agenda status
|
||||
tags:
|
||||
- agenda
|
||||
/api/anos-formaturas:
|
||||
get:
|
||||
consumes:
|
||||
|
|
|
|||
483
backend/importacao_fots.csv
Normal file
483
backend/importacao_fots.csv
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
FOT;Empresa;EF I / EF II;Observa‡äes;Institui‡Æo;Ano Formatura;Cidade;Estado;Gastos Capta‡Æo;Pr‚ Venda
|
||||
23112;Viva SP;Enfermagem; CTT - 6132 - Anhang Sorocaba / med. Vet CTT - 6916- Puc sorocaba / enferm;PUC Sorocaba;2023.2;Sorocaba;SP; R$ 2.176,60 ;
|
||||
23121;Perfil;Med. Veterin ria;-;Unifaj;2023.2;Jaguariuna;SP; R$ 7.649,00 ;Sim
|
||||
23144;Smart;Unificados;Adm/Direito/Med Vet;Unimep;2023.2;Piracicaba;SP; R$ 3.087,00 ;
|
||||
23145;Smart;Unificados;Eng Civil / Eng Produ‡Æo / Nuti‡Æo ;Unip/Anhanguera/ Anhembi;2023.2;Piracicaba;SP; R$ 1.592,00 ;
|
||||
23252;Viva SP;Unificados;Jun‡Æo UNESP;UNESP;2023.2;Rio Claro;SP; R$ 3.950,00 ;
|
||||
23266;Viva SP;Unificados;CTT 6229 / 6213 Psico / Biomed;UNIP;2023.2;SÆo Paulo;SP; R$ 6.835,00 ;
|
||||
23270;Ponta Eventos;Superor Diversos;-;FATEC Piracicaba;2023.2;Piracicaba;SP; R$ 3.060,00 ;
|
||||
23282;Ponta Eventos;Superor Diversos;-;FATEC GAR€A;2023.2;;SP; R$ 7.500,00 ;
|
||||
23286;Perfil;Superor Diversos;CTT - 733;Anhanguera Americana;2023.2;Americana;SP; R$ 2.681,80 ;
|
||||
23288;Golden;Superor Diversos;-;Unopar Anhaguera;2023.2;SÆo Bernardo do Campo;SP; R$ 10.930,00 ;
|
||||
23289;Prime;Superor Diversos;-;FATEC Jundia¡;2023.2;Jundia¡;SP; R$ 5.787,60 ;
|
||||
23303;Prime;Superor Diversos;-;FATEC Taquaritinga;2023.2;Taquaritnga;SP; R$ 7.566,00 ;
|
||||
23304;Perfil;Superor Diversos;CTT 740;Anhanguera Nova Odessa;2023.2;Nova Odessa;SP; R$ 2.339,00 ;
|
||||
24001;Viva SP;Medicina;Turma Rebeca Oliveira - 5877;USJT;2024.2;CubatÆo;SP; R$ 41.360,22 ;Sim
|
||||
24002;Smart;Odontologia;-;Unicamp;2024.2;Campinas;SP; R$ 9.648,68 ;Sim
|
||||
24003;Arte Formaturas;Unificados;Odonto / medicina veterinaria;UNIFAI;2024.2;Adamantina;SP; R$ 9.979,86 ;
|
||||
24005;Smart;EFII;-;Objetivo Piracicaba;2024.2;Piracicaba;SP; R$ 10.684,60 ;
|
||||
24006;Smart;EM;-;Antares;2024.2;Americana;SP; R$ 6.541,40 ;
|
||||
24007;Smart;EFII;-;Antares;2024.2;Americana;SP; R$ 10.727,80 ;
|
||||
24008;Perfil;EFII;CTT 719;Col‚gio Americana;2024.2;Americana;SP; R$ 4.258,90 ;
|
||||
24009;Viva SP;EM;-;Porto Seguro - Valinhos;2024.2;Valinhos;SP; R$ 19.312,20 ;
|
||||
24010;Viva SP;Farm cia;CTT 8112 - Debora Campos;USP;2024.2;;SP;#VALOR!;
|
||||
24011;Smart;EM;-;Anglo Portal;2024.2;Piracicaba;SP; R$ 8.135,60 ;
|
||||
24012;Smart;EF II / EM;-;COC Piracicaba;2024.2;Piracicaba;SP; R$ 7.211,10 ;
|
||||
24013;Smart;EFII;-;Liceu Terras;2024.2;Piracicaba;SP; R$ 6.124,20 ;
|
||||
24014;Smart;EFII;-;KOELLI;2024.2;Piracicaba;SP; R$ 19.979,83 ;Sim
|
||||
24015;Smart;EM;-;KOELLI;2024.2;Piracicaba;SP; R$ 20.559,40 ;Sim
|
||||
24016;Smart;EM;-;Anglo Araras;2024.2;Araras;SP; R$ 7.558,75 ;
|
||||
24017;Smart;EM;-;INSA Araras;2024.2;Araras;SP; R$ 6.586,17 ;
|
||||
24018;Smart;EFII;-;Anglo Cidade Alta;2024.2;Piracicaba;SP; R$ 4.735,00 ;
|
||||
24019;Smart;EFII;-;Dom Bosco Assun‡Æo;2024.2;Piracicaba;SP; R$ 7.021,60 ;
|
||||
24020;Smart;EM;-;Anglo Portal Limeira;2024.2;Limeira;SP; R$ 6.257,40 ;
|
||||
24021;Smart;EM;-;Anglo Cidade Alta;2024.2;Piracicaba;SP; R$ 7.255,70 ;
|
||||
24022;Smart;EF II / EM;-;Col‚gio Lumisol;2024.2;Piracicaba;SP; R$ 6.979,50 ;
|
||||
24023;Smart;Superor Diversos;-;EEP;2024.2;Piracicaba;SP; R$ 9.808,50 ;
|
||||
24024;Perfil;EF I / EF II / EM;-;Oficina do Estudante;2024.2;Campinas;SP; R$ 32.654,60 ;
|
||||
24025;Perfil;EF I / EF II / EM;CTT - 736;Col‚gio Eduq;2024.2;Rio Claro;SP; R$ 16.687,00 ;
|
||||
24026;Perfil;EF II / EM;CTT - 741;Col‚gio Educativa;2024.2;Sumar‚;SP; R$ 15.823,50 ;
|
||||
24027;Perfil;EM / TEC;CTT - 675;ETEC Polivalente;2024.2;Americana;SP; R$ 20.235,50 ;
|
||||
24028;Perfil;EF II / EM;CTT - 727;Objetivo S.B.O;2024.2;Santa Barbara d' Oeste;SP; R$ 4.751,00 ;
|
||||
24029;Perfil;EM;CTT - 729;Poliedro;2024.2;Campinas;SP; R$ 5.735,60 ;
|
||||
24030;Perfil;EM;CTT - 735;Imaculada Mogi Mirim;2024.2;Mogi Mirim;SP; R$ 5.197,00 ;
|
||||
24031;Perfil;EM;CTT - 717;ETAPA Valinhos;2024.2;Valinhos;SP; R$ 5.947,90 ;
|
||||
24032;Viva SP;EM;-;Poliedro Cola‡Æo;2024.2;Campinas;SP; R$ 12.126,30 ;
|
||||
24033;Perfil;EF I / EF II / EM;CTT - 734;Col‚gio IESC - Nova Odessa;2024.2;Nova Odessa;SP; R$ 3.717,05 ;
|
||||
24034;Perfil;EF II / EM;CTT - 743;COL<4F>GIO BOM JESUS ITATIBA;2024.2;Itatiba;SP; R$ 5.179,20 ;
|
||||
24035;Perfil;EF I / EF II / EM;CTT - 737;COL<4F>GIO METROPOLITAN PAULINENSE ;2024.2;Paulinia;SP; R$ 15.369,30 ;
|
||||
24038;Perfil;EF II / EM;CTT - 744;SESI - S.B.O;2024.2;Santa Barbara d' Oeste;SP; R$ 8.940,00 ;
|
||||
24039;Perfil;EFII;CTT - 738;Imaculada Campinas;2024.2;Campinas;SP; R$ 11.679,20 ;
|
||||
24040;Forcamp;EM;Baile junto o Taquaral fot 24041;Progresso Cambu¡;2024.2;Campinas;SP; R$ 10.349,80 ;
|
||||
24041;Forcamp;EM;Baile junto o Cambu¡ fot 24040;Progresso Taquaral;2024.2;Campinas;SP; R$ 4.814,50 ;
|
||||
24042;Viva SP;EM;CTT - 8486;Imaculada Campinas;2024.2;Campinas;SP; R$ 8.741,90 ;
|
||||
24100;Arte Formaturas;Unificados;Enfermagem/Fisioterapia;AEMS;2024.2;Trˆs Lagoas;MS; R$ 5.001,74 ;
|
||||
24102;Viva SP;Odontologia;CTT - 8305;UNIP;2024.2;Campinas;SP; R$ 2.773,80 ;Sim
|
||||
24200;Arte Formaturas;Direito;-;UNIFAI;2024.2;Adamantina;SP; R$ 3.942,92 ;
|
||||
24201;Arte Formaturas;Unificados;Agronomia/Enfermagem/Farmacia/Med Vet;AEMS;2024.2;Trˆs Lagoas;MS; R$ - ;
|
||||
24202;Viva SP;Superor Diversos; Jun‡Æo CTT-7579 USCS-Odonto/UNICSUl-Psico/EISNTIN-Enfer/FASM-Enfer-Fisio;USCS-UNICSUL-EISTEIN-FASM;2024.2;SÆo Caetano do Sul;SP; R$ 15.653,20 ;
|
||||
24205;Arte Formaturas;Biomedicina;-;UNIFAI;2024.2;Adamantina;SP; R$ 3.011,09 ;
|
||||
25001;Viva SP;Unificados;Unificados Comunica‡Æo;PUC Campinas;2025.2;Campinas;SP; R$ - ;
|
||||
25005;Smart;Enfermagem;-;UNIARARAS;2025.2;Araras;SP; R$ 1.576,80 ;
|
||||
25100;Viva SP;Medicina;T 3;Unaerp;2025.2;Guaruj ;SP; R$ 5.578,90 ;Sim
|
||||
25101;Arte Formaturas;Pedagogia;-;UFMS;2025.2;Dracena;SP; R$ - ;
|
||||
25112;Arte Formaturas;Unificados;Enfer / Fisio / Biomed;FADAP;2025.2;TupÆ;SP; R$ - ;
|
||||
25102;Arte Formaturas;Unificados;CTT 1342 - Biomed/Enfermage/Farmacia/Odonto;AEMS;2025.2;Trˆs Lagoas;MS; R$ 8.976,70 ;
|
||||
25103;Arte Formaturas;Med. Veterin ria;-;AEMS;2025.2;Trˆs Lagoas;MS; R$ 3.552,69 ;
|
||||
25105;Arte Formaturas;Pedagogia;-;UFMS;2025.2;Trˆs Lagoas;MS; R$ - ;
|
||||
25106;Arte Formaturas;Unificados;CTT - 1353 - Direito / Med. Vet;UNIFAI;2025.2;Adamantina;SP; R$ 4.914,40 ;
|
||||
25108;Arte Formaturas;Odontologia;-;UNIFAI;2025.2;Adamantina;SP; R$ 482,92 ;
|
||||
25109;Arte Formaturas;Unificados;Agronomia/Farm cia/Fisioterapia / Biomed;UNIFAI;2025.2;Adamantina;SP;#REF!;
|
||||
25110;Arte Formaturas;Biomedicina;-;UNIFAI;2025.2;Adamantina;SP; R$ 1.215,60 ;
|
||||
25201;Viva SP;Enfermagem;-;Albert Einstein;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25500;Viva SP;Odontologia;Turma Hellen;UNISA;2025.2;SÆo Paulo;SP; R$ 1.130,60 ;
|
||||
25501;Arte Formaturas;Unificados;Biomedicina + Enfermagem + Fisioterapia ;FADAP;2025.2;TupÆ;SP; R$ - ;
|
||||
26100;Arte Formaturas;Odontologia;-;UNIFAI;2026.2;Adamantina;SP; R$ 3.008,16 ;
|
||||
26403;Viva SP;Medicina;T4;Unoeste;2026.2;Guaruj ;SP; R$ 440,00 ;sim
|
||||
27005;Viva SP;Medicina;T4 - SÆo Judas CubatÆo CTT - 8126 Vinicius;USJT;2027.2;CubatÆo;SP; R$ 16.920,00 ;Sim
|
||||
27100;Viva SP;Medicina;T6 ;Unaerp;2027.1;Guaruj ;SP; R$ 5.832,95 ;
|
||||
28001;Arte Formaturas;Medicina;-;Unifadra;2028.2;Dracena;SP; R$ - ;
|
||||
26015;Viva SP;Odontologia;CTT - 8320;FHO;2026.2;Araras;SP; R$ 4.609,40 ;Sim
|
||||
22368;Viva SP;Direito;CTT - 6994 (Turma se forma em 2024);Mackenzie;2022.2;Campinas;SP; R$ 15.866,00 ;
|
||||
24045;Perfil;Med. Veterin ria;CTT - 694;UNIFAJ;2024.2;Jaguariuna;SP; R$ 14.492,97 ;Sim
|
||||
24046;Perfil;EF II / EM;CTT - 746;Col‚gio Dom Pedro;2024.2;Americana;SP; R$ 17.081,50 ;
|
||||
24047;Forcamp;EF I / EF II / EM;T 42;Jardins Mediterrƒneo;2024.2;Campinas;SP; R$ 7.963,20 ;
|
||||
24048;Forcamp;EM;-;Objetivo BarÆo Geraldo;2024.2;Campinas;SP; R$ 9.415,02 ;
|
||||
24049;Forcamp;EM;-;Objetivo Cambu¡;2024.2;Campinas;SP; R$ 6.166,70 ;
|
||||
24051;Forcamp;EF II / EM;-;Progresso Vinhedo;2024.2;Vinhedo;SP; R$ 8.289,80 ;
|
||||
24052;Forcamp;EFII;-;Progresso Cambu¡;2024.2;Campinas;SP; R$ 3.297,70 ;
|
||||
24053;Forcamp;EFII;-;Progresso Taquaral;2024.2;Campinas;SP; R$ 3.048,40 ;
|
||||
24054;Forcamp;EF II / EM;-;Anglo Salto;2024.2;Salto;SP; R$ 8.847,30 ;
|
||||
24055;Forcamp;EFII;-;Escala;2024.2;Indaiatuba;SP; R$ 4.219,90 ;
|
||||
24056;Forcamp;EF II / EM;-;Col‚gio Conquista;2024.2;Indaiatuba;SP; R$ 13.264,00 ;
|
||||
24058;Forcamp;EF II / EM;-;Col‚gio Rodin;2024.2;Indaiatuba;SP; R$ 15.888,10 ;
|
||||
24060;Forcamp;EF I / EF II / EM;-;Progresso Itu;2024.2;Itu;SP; R$ 11.925,30 ;
|
||||
24061;Forcamp;EM;-;Col‚gio Elite;2024.2;Indaiatuba;SP; R$ 3.271,70 ;
|
||||
24062;Forcamp;EFII;-;Objetivo BarÆo Geraldo;2024.2;Campinas;SP; R$ 3.329,50 ;
|
||||
24063;Forcamp;EF II / EM;-;Progresso Indaiatuba;2024.2;Indaiatuba;SP; R$ 8.908,10 ;
|
||||
24064;Forcamp;EFII;-;Objetivo Cambu¡;2024.2;Campinas;SP; R$ 2.707,30 ;
|
||||
24066;Forcamp;EF II / EM;-;Col‚gio Divino;2024.2;Itu;SP; R$ 13.938,80 ;
|
||||
24067;Forcamp;EF I / EF II / EM;-;RDS Garcia;2024.2;Campinas;SP; R$ 11.631,40 ;
|
||||
24068;Forcamp;EF II / EM;-;Escola Estadual Helena;2024.2;Indaiatuba;SP; R$ 4.809,70 ;
|
||||
24069;Forcamp;EF I / EF II / EM;-;RDS Mimosa;2024.2;Campinas;SP; R$ 10.373,30 ;
|
||||
24070;Forcamp;EFII;-;Col‚gio Almeida Junior;2024.2;Itu;SP; R$ 3.128,20 ;
|
||||
24071;Forcamp;EI / EFI;-;VIVAP;2024.2;Campinas;SP; R$ 8.225,10 ;
|
||||
24072;Forcamp;EF II / EM;-;EE Lucidio;2024.2;Cabreuva;SP; R$ 3.900,20 ;
|
||||
24073;Forcamp;EF II / EM;-;EE CapitÆo;2024.2;Cabreuva;SP; R$ 5.477,60 ;
|
||||
24075;Forcamp;EM;-;Anglo Indaiatuba;2024.2;Indaiatuba;SP; R$ 6.908,90 ;
|
||||
24076;Forcamp;EF II / EM;-;Forte Castelo;2024.2;Itu;SP; R$ 3.148,50 ;
|
||||
24077;Forcamp;EM;-;Instituto Federal Salto;2024.2;Salto;SP; R$ 3.621,10 ;
|
||||
24079;Forcamp;EM;-;ETECAP;2024.2;Campinas;SP; R$ 14.671,40 ;
|
||||
25010;Forcamp;Superor Diversos;-;CEUNSP OPTOMETRIA ;2025.1;Salto;SP; R$ 6.434,80 ;
|
||||
26001;Viva SP;Odontologia;CTT - 8176;Universidade Braz Cubas;2026.2;Mogi das Cruzes;SP; R$ - ;
|
||||
24082;Perfil;Unificados;CTT - 702 UNIF CEUNSP;CEUNSP;2024.2;Itu;SP; R$ 11.360,69 ;Sim
|
||||
24083;Perfil;EM;CTT - 745;Funda‡Æo Romi NEI;2024.2;Santa Barbara d' Oeste;SP; R$ 4.776,50 ;
|
||||
24084;Perfil;EM / TEC;CTT - 747;Bento Quirino;2024.2;Campinas;SP; R$ 10.489,50 ;
|
||||
24085;Perfil;EF II / EM;CTT - 748;Col‚gio Rio Branco;2024.2;Campinas;SP; R$ 23.057,00 ;
|
||||
24086;Viva SP;EF II / EM;CTT - 8498;Oficina do Estudante (Baile);2024.2;Campinas;SP; R$ 6.464,40 ;
|
||||
24087;Perfil;Superor Diversos;CTT - 662/681 UNIF Einsten / FHO / UNAR;Einsten / FHO / UNAR;2024.2;Araras;SP; R$ 14.521,84 ;sim
|
||||
24088;Perfil;Superor Diversos;CTT - 641 UNIFICADOS;FAM - FATEC - FACP - Unisal - UNIP Limeira - Anhangueras SBO/Sumar‚;2024.2;Americana;SP; R$ 38.830,69 ;Sim
|
||||
24089;Perfil;Superor Diversos;CTT - 679/655 UNIF - USF Med. Vet / UNIF Camp;USF;2024.2;Campinas;SP; R$ 13.166,33 ;sim
|
||||
24090;Smart;EF II / EM;-;Objetivo SÆo Pedro;2024.2;SÆo Pedro;SP; R$ 3.843,22 ;
|
||||
24091;Forcamp;EI / EFI;-;JoÆo e Marcela;2024.2;Campinas;SP; R$ 1.580,80 ;
|
||||
24092;Arte Formaturas;Psicologia;-;FIRB;2024.1;Andradina;SP; R$ 1.986,97 ;
|
||||
10000;Arte Formaturas;-;Eventos Institucionais Arte Formaturas;Arte Formaturas;-;Dracena;SP; R$ 3.682,19 ;
|
||||
24093;Smart;EM;Esta turma foi cancelada, fotografamos apenas um trote;Piracicabano;2024.2;Piracicaba;SP; R$ 186,00 ;
|
||||
23305;Ponta Eventos;Superor Diversos;Formandos 23.2 / 24.1;UNINTER Jundia¡;2023.2;Jundia¡;SP; R$ 2.718,20 ;
|
||||
26002;Arte Formaturas;Med. Veterin ria;CTT - 1145;FEA;2026.2;Andradina;SP; R$ 1.832,60 ;
|
||||
25058;Arte Formaturas;Psicologia;CTT - 1170;AEMS;2025.2;Trˆs Lagoas;MS; R$ 400,37 ;
|
||||
26003;Arte Formaturas;Odontologia;CTT - 1073;AEMS;2026.2;Trˆs Lagoas;MS; R$ 944,10 ;
|
||||
24095;Prime;EF II / EM;-;Objetivo Exitus S.B.O;2024.2;Santa Barbara d' Oeste;SP; R$ 3.151,80 ;
|
||||
24096;Prime;Superor Diversos;-;FATEC Americana;2024.1;Americana;SP; R$ 6.600,00 ;
|
||||
24097;Prime;Superor Diversos;-;FATEC Guaratingueta;2024.1;Guaratingueta;SP; R$ 6.442,80 ;
|
||||
24098;Viva SP;EF II / EM;-;Poliedro SJC;2024.2;SÆo Jos‚ do Campos;SP; R$ 33.104,71 ;
|
||||
24099;Viva SP;EM;-;Culto a Ciencia;2024.2;Campinas;SP; R$ 10.933,10 ;
|
||||
24119;Golden;Superor Diversos;-;FATEC SJC;2024.1;SÆo Jos‚ do Campos;SP; R$ 7.235,00 ;
|
||||
24120;Golden;Superor Diversos;-;Anhanguera / UNOPAR;2024.1;SÆo Jos‚ do Campos;SP; R$ 5.325,00 ;
|
||||
24103;Viva SP;EM;-;Col‚gio Santa Catarina;2024.2;SÆo Paulo;SP; R$ 5.274,40 ;
|
||||
24104;Golden;Tec. Diversos;-;ETEC Jacare¡;2024.1;Jacare¡;SP; R$ 3.614,60 ;
|
||||
24105;Viva SP;Superor Diversos;-;IBMEC;2024.1;SÆo Paulo;SP; R$ 6.019,63 ;
|
||||
24106;Viva SP;EM;-;Adalberto Nascimento;2024.2;Campinas;SP; R$ 6.269,00 ;
|
||||
24107;Perfil;Superor Diversos;CTT- 763;FAM;2024.1;Americana;SP; R$ 11.790,00 ;
|
||||
24108;Festa da Beca;EF II / EM;-;Valdomiro Silveira;2024.2;Santo Andr‚;SP; R$ 4.773,10 ;
|
||||
24109;Festa da Beca;EF II / EM;-;Angelo Mendes;2024.2;SÆo Paulo;SP; R$ 5.456,40 ;
|
||||
24110;Festa da Beca;EF II / EM;-;Fitipaldi;2024.2;SÆo Caetano do Sul;SP; R$ 6.196,25 ;
|
||||
24111;Festa da Beca;EF II / EM;-;COC;2024.2;SÆo Caetano do Sul;SP; R$ 11.260,11 ;
|
||||
24112;Festa da Beca;EF II / EM;-;Antunes;2024.2;Santo Andr‚;SP; R$ 10.799,20 ;
|
||||
24113;Festa da Beca;EF II / EM;-;Angelo Pelegrino;2024.2;SÆo Caetano do Sul;SP; R$ 9.583,00 ;
|
||||
24115;Festa da Beca;EF II / EM;-;Generoso;2024.2;Santo Andr‚;SP; R$ 10.657,20 ;
|
||||
24116;Festa da Beca;EF II / EM;-;Arbos;2024.2;SÆo Caetano do Sul;SP; R$ 3.536,40 ;
|
||||
24117;Festa da Beca;EF II / EM;-;Alcina Dantas;2024.2;SÆo Caetano do Sul;SP; R$ 14.519,20 ;
|
||||
24118;Perfil;Superor Diversos;CTT -764;Anhanguera Americana e S.B.O;2024.1;Americana;SP; R$ 2.692,00 ;
|
||||
24121;Arte Formaturas;Superor Diversos;Eng Civil / Eng Produ‡Æo;UNIESP;2024.1;Dracena;SP; R$ 1.980,00 ;
|
||||
24122;Arte Formaturas;Fisioterapia;CTT - 1188;FADAP;2024.2;TupÆ;SP; R$ 4.274,51 ;
|
||||
24123;Arte Formaturas;EM;CTT - 1210;ISAC;2024.2;Dracena;SP; R$ 12.202,58 ;
|
||||
25006;Arte Formaturas;Med. Veterin ria;CTT - 1171;FEA;2025.2;Andradina;SP; R$ 3.449,60 ;
|
||||
25007;Arte Formaturas;Medicina;CTT - 1261;UCP;2025.2;Paraguai;SP; R$ 17.960,00 ;Sim
|
||||
26004;Arte Formaturas;Biomedicina;CTT - 1251;AEMS;2026.2;Trˆs Lagoas;MS; R$ - ;
|
||||
26005;Arte Formaturas;Enfermagem;CTT - 1234;AEMS;2026.2;Trˆs Lagoas;MS; R$ 3.420,50 ;
|
||||
26006;Arte Formaturas;Enfermagem;CTT - 1250;FADAP;2026.2;TupÆ;SP; R$ 2.261,72 ;
|
||||
26007;Arte Formaturas;Agronomia;CTT - 1215;FEA;2026.2;Andradina;SP; R$ 396,46 ;
|
||||
26008;Arte Formaturas;Direito;CTT - 916;UNIFAI;2026.2;Adamantina;SP; R$ 3.540,44 ;
|
||||
26009;Arte Formaturas;Fisioterapia;CTT - 964;UNIFAI;2026.2;Adamantina;SP; R$ - ;
|
||||
26010;Arte Formaturas;Med. Veterin ria;CTT - 1040;UNIFAI;2026.2;Adamantina;SP; R$ 2.272,91 ;
|
||||
27101;Arte Formaturas;Agronomia;CTT - 1235;AEMS;2027.2;Trˆs Lagoas;MS; R$ - ;
|
||||
27102;Arte Formaturas;Psicologia;CTT - 1243;FADAP;2027.2;TupÆ;SP; R$ 2.373,00 ;
|
||||
28005;Arte Formaturas;Superor Diversos;CTT - 1268;AEMS;2028.2;Trˆs Lagoas;MS; R$ 499,80 ;
|
||||
28002;Arte Formaturas;Agronomia;CTT - 1238;FEA;2028.2;Andradina;SP; R$ 454,79 ;
|
||||
24124;Prime;Tec. Diversos;-;ETEC Trajano Camargo;2024.1;Limeira;SP; R$ 2.768,20 ;
|
||||
24126;Prime;Tec. Diversos;-;ETEC Fernando Prestes;2024.2;Sorocaba;SP; R$ 16.562,80 ;
|
||||
24127;Prime;Tec. Diversos;-;ETEC Monte Mor;2024.2;Monte Mor;SP; R$ 8.455,10 ;
|
||||
24128;Prime;Tec. Diversos;-;ETEC Trajano Camargo;2024.2;Limeira;SP; R$ 7.780,40 ;
|
||||
24129;Ponta Eventos;Superor Diversos;-;Pecege;2024.1;Piracicaba;SP; R$ 1.012,00 ;
|
||||
25008;Smart;Superor Diversos;Med. Vet - Zootecnia - Eng Agron“mica;UNESP;2025.2;Botucatu;SP; R$ 7.631,46 ;
|
||||
26011;Viva SP;Med. Veterin ria;CTT - 8067;UNISA;2026.2;SÆo Paulo;SP; R$ 1.579,40 ;
|
||||
24130;Ponta Eventos;Superor Diversos;-;FATEC Gar‡a;2024.1;Gar‡a;SP; R$ 4.500,00 ;
|
||||
24131;Ponta Eventos;Superor Diversos;-;FATEC Botucatu;2024.1;Botucatu;SP; R$ 4.072,20 ;
|
||||
24132;Ponta Eventos;Superor Diversos;-;FATEC Itapira;2024.1;Itapira;SP; R$ 3.108,79 ;
|
||||
26012;Smart;Direito;-;Anhanguera / UNIMEP / FAM;2026.2;Piracicaba;SP; R$ 2.690,80 ;
|
||||
26013;Smart;Direito;-;FATEP;2026.2;Piracicaba;SP; R$ 1.002,50 ;
|
||||
24133;Toy SP;Superor Diversos;CTT - #1;ESPM;2024.1;SÆo Paulo;SP; R$ 21.123,27 ;
|
||||
24135;Forcamp;EF II / EM;-;Col‚gio Candelaria;2024.2;Indaiatuba;SP; R$ 5.658,20 ;
|
||||
24136;Photum;EF I / EF II / EM;-;Anglo Itu;2024.2;Itu;SP; R$ 16.635,04 ;
|
||||
25009;Smart;Psicologia;-;Anhembi Morumbi;2025.2;Piracicaba;SP; R$ 4.090,00 ;
|
||||
27006;Viva SP;Medicina;T6 CTT - 8677;Unoeste;2027.2;Guaruj ;SP; R$ 10.150,50 ;Sim
|
||||
28003;Viva SP;Medicina;XXVI;UNIMES;2028.2;Santos;SP; R$ 25.833,40 ;Sim
|
||||
24134;Viva SP;Superior Diversos;CTT - 7772;USF Camps / UNIP Bragan‡a / UNIP Jundia¡;2024.2;Campinas;SP; R$ 9.669,00 ;
|
||||
24137;Prime;EF I / EF II / EM;-;Objetivo Piedade;2024.2;Piedade;SP; R$ 6.408,30 ;
|
||||
24138;Forcamp;EM;-;Instituto Federal Capivari;2024.2;Capivari;SP; R$ 3.755,10 ;
|
||||
24139;Ponta Eventos;Tec. Diversos;-;IFSP Capivari;2024.1;Capivari;SP; R$ 1.382,60 ;
|
||||
24140;Ponta Eventos;Superor Diversos;-;FATEC Piracicaba;2024.1;Piracicaba;SP; R$ 3.119,00 ;
|
||||
24141;Festa da Beca;Superor Diversos;-;Metodista;2024.1;SÆo Paulo;SP; R$ 3.262,03 ;
|
||||
24142;Festa da Beca;Superor Diversos;Turmas EAD;Metodista;2024.1;SÆo Paulo;SP; R$ 3.265,03 ;
|
||||
24143;Viva SP;EM;-;ETAPA Valinhos;2024.2;Valinhos;SP; R$ 24.981,36 ;Sim
|
||||
24144;Forcamp;EF II / EM;-;E.E Morada do Sol;2024.2;Indaiatuba;SP; R$ 2.960,00 ;
|
||||
24145;Festa da Beca;EF I / EF II / EM;-;Col‚gio Paineiras;2024.2;Santo Andr‚;SP; R$ 4.548,90 ;
|
||||
24146;Festa da Beca;EFII;-;EE Maria Luiza Ferrari Cicero;2024.2;SÆo Bernardo do Campo;SP; R$ 4.831,70 ;
|
||||
24147;Festa da Beca;Biomedicina;-;UNIFESP;2024.2;SÆo Paulo;SP; R$ 2.234,60 ;
|
||||
24148;Viva SP;EF II / EM;-;Col‚gio Shalom;2024.2;Campinas;SP; R$ 5.605,80 ;
|
||||
24149;Forcamp;EF I / EF II / EM;Todos os seguimentos EI EFI EFII EM;RDS Swiss Park;2024.2;Campinas;SP; R$ 3.942,40 ;
|
||||
24150;Forcamp;EFII;-;Anglo Renova‡Æo;2024.2;Indaiatuba;SP; R$ 3.482,70 ;
|
||||
27007;Arte Formaturas;Terapia Ocupacinal;-;AEMS;2027.2;Trˆs Lagoas;MS; R$ 3.595,86 ;
|
||||
24151;Golden;EM / TEC;-;ETEC SJC;2024.2;SÆo Jos‚ do Campos;SP; R$ 10.196,64 ;
|
||||
24153;MVP Formaturas;Direito;UNISANTA / UNISANTOS;UNISANTOS;2024.2;Santos;SP; R$ 13.341,95 ;
|
||||
24154;Ponta Eventos;Superor Diversos;-;IFSP Piracicaba;2024.2;Piracicaba;SP; R$ 2.692,10 ;
|
||||
24155;Ponta Eventos;Superor Diversos;-;IFSP Capivari;2024.2;Capivari;SP; R$ 2.015,90 ;
|
||||
24156;Viva SP;EF II / EM;TURMA NÇO FOI EFETIVADO O CONTRATO;Col‚gio integrado;2024.2;Jaguariuna;SP; R$ 451,70 ;
|
||||
24157;Prime;Superor Diversos;-;FATEC Americana;2024.1;Americana;SP; R$ 1.650,00 ;
|
||||
24158;Prime;EM / TEC;-;ETEC Sumar‚;2024.2;Sumar‚;SP; R$ 2.521,40 ;
|
||||
27008;Viva SP;Odontologia;KELLY LIMA - CTT 8607;UNISA;2027.2;SÆo Paulo;SP; R$ 4.799,20 ;
|
||||
25011;Viva SP;Superor Diversos;SAéDE - CTT 6773;USF;2025.2;Bragan‡a Paulista;SP; R$ 1.237,80 ;
|
||||
25012;Perfil;EM;CTT - 754;Funda‡Æo Romi NEI;2025.2;Santa Barbara d' Oeste;SP; R$ 2.500,00 ;
|
||||
25013;Perfil;EF II / EM;CTT - 759;Col‚gio Americana;2025.2;Americana;SP; R$ 1.090,00 ;
|
||||
25014;Perfil;EM / TEC;CTT - 756;ETEC Polivalente;2025.2;Americana;SP; R$ 6.620,00 ;
|
||||
25015;Perfil;EM / TEC;CTT- 798;ETEC Araras;2025.2;Araras;SP; R$ - ;
|
||||
25016;Perfil;EF I / EF II / EM;CTT - 782;EDUQ;2025.2;Rio Claro;SP; R$ 5.989,80 ;
|
||||
25017;Perfil;Superor Diversos;CTT - 753;ILUM - ESCOLA DE CIÒNCIA E TECNOLOGIA;2025.2;Campinas;SP; R$ 322,00 ;sim
|
||||
25018;Perfil;Med. Veterin ria;CTT - 760;UNIFAJ;2025.2;Jaguariuna;SP; R$ 9.880,40 ;Sim
|
||||
25019;Perfil;Superor Diversos;CTT - 761;UNASP;2025.2;Hortolandia ;SP; R$ - ;
|
||||
25020;Perfil;Superor Diversos;UNIF - Americana CTT - 682;FAM / UNISAL / Anhangueras SOB e Sumar‚ / Fatec.;2025.2;Americana;SP; R$ 19.920,00 ;Sim
|
||||
25021;Perfil;Superor Diversos;UNIF. FHO FARMµCIA / UNIF. FHO PSICO /UNIF. EINSTEIN E UNIP ;FHO / EINSTEIN E UNIP ;2025.2;Araras;SP; R$ 15.418,80 ;Sim
|
||||
25022;Viva SP;EM;CTT - 8634;Porto Seguro - Valinhos;2025.2;Valinhos;SP; R$ 6.400,10 ;
|
||||
24182;Perfil;Superor Diversos;CTT - 771;UNISAL Campinas;2024.2;Campinas;SP; R$ 6.859,10 ;
|
||||
24183;Perfil;Superor Diversos;CTT - 769;FAM;2024.2;Americana;SP; R$ 20.051,20 ;
|
||||
24159;Perfil;EI / EFI;CTT - 774;Objetivo S.B.O;2024.2;Santa Barbara d' Oeste;SP; R$ 4.920,00 ;
|
||||
10002;Toy SP;-;Eventos Institucionais Toy Formaturas;TOY;-;SP;SP; R$ 8.750,40 ;
|
||||
24160;Festa da Beca;EFII;;Juarez Tavora;2024.2;Santo Andr‚;SP; R$ 1.725,00 ;
|
||||
26017;MVP Formaturas;Odontologia;-;UNIMES;2026.2;Santos;SP; R$ 12.825,20 ;Sim
|
||||
24161;Arte Formaturas;EM;-;Escola Julieta;2024.2;Dracena;SP; R$ 3.465,60 ;
|
||||
28004;Arte Formaturas;Direito;CTT - 1316;FIRB;2028.2;Dracena;SP; R$ 1.575,88 ;
|
||||
24162;Ponta Eventos;Superor Diversos;-;FATEC Gar‡a;2024.2;Gar‡a;SP; R$ 9.527,20 ;
|
||||
26018;Arte Formaturas;Superor Diversos;CTT - 1332 (C. Biologicas);UFMS;2026.2;Trˆs Lagoas;MS; R$ 3.318,99 ;
|
||||
24163;Viva SP;Direito;CTT-8119 Jun‡ao Direito;EPD-ESCOLA PAULISTA DIREITO / UNISUL;2024.2;SÆo Paulo;SP; R$ 12.585,70 ;
|
||||
24164;Festa da Beca;Odontologia;-;Metodista ;2024.2;SÆo Bernardo do Campo;SP; R$ 5.895,40 ;
|
||||
24165;Festa da Beca;Med. Veterin ria;;Metodista ;2024.2;SÆo Bernardo do Campo;SP; R$ 4.452,50 ;
|
||||
24166;Festa da Beca;Psicologia;;Metodista ;2024.2;SÆo Bernardo do Campo;SP; R$ 3.459,20 ;
|
||||
24167;Festa da Beca;Superor Diversos;Biomed/estetica/ Logistica/ mkt;Metodista ;2024.2;SÆo Bernardo do Campo;SP; R$ 3.426,60 ;
|
||||
24168;Festa da Beca;Superor Diversos;Comunica‡Æo;Metodista ;2024.2;SÆo Bernardo do Campo;SP; R$ 3.604,80 ;
|
||||
24169;Festa da Beca;Direito;-;Metodista ;2024.2;SÆo Bernardo do Campo;SP; R$ 3.376,90 ;
|
||||
24170;Festa da Beca;Superor Diversos;ADM/Comex/ Ciencias Contabeis/ RH;Metodista ;2024.2;SÆo Bernardo do Campo;SP; R$ 3.484,40 ;
|
||||
24171;Festa da Beca;Superor Diversos;Turmas EAD;Metodista ;2024.2;SÆo Bernardo do Campo;SP; R$ 7.415,60 ;
|
||||
25025;Smart;EFII;-;Liceu Terras;2025.2;Piracicaba;SP; R$ 1.582,20 ;
|
||||
25026;Viva SP;EM;-;Imaculada Campinas;2025.2;Campinas;SP; R$ 3.696,40 ;
|
||||
25027;Viva SP;EF II / EM;-;Poliedro Campinas;2025.2;Campinas;SP; R$ 5.963,90 ;
|
||||
25028;Smart;EFII;-;Antares;2025.2;Americana;SP; R$ 2.550,00 ;
|
||||
25029;Smart;EFII;-;Objetivo Piracicaba;2025.2;Piracicaba;SP; R$ 372,60 ;
|
||||
25030;Smart;EM;-;Anglo Araras;2025.2;Araras;SP; R$ 2.446,65 ;
|
||||
25031;Forcamp;EF I / EF II / EM;-;RDS Garcia;2025.2;Campinas;SP; R$ 8.531,40 ;
|
||||
25032;Smart;EM;-;Col‚gio Elite;2025.2;Piracicaba;SP; R$ 6.573,20 ;
|
||||
25033;Smart;EFII;-;Dom Bosco Cidade Alta;2025.2;Piracicaba;SP; R$ 2.694,40 ;
|
||||
25034;Forcamp;EF II / EM;-;Col‚gio Monte-Sionense;2025.2;Monte SiÆo;MG; R$ 3.319,90 ;
|
||||
25035;Smart;EM;-;Objetivo Piracicaba;2025.2;Piracicaba;SP; R$ 6.728,70 ;sim
|
||||
25036;Smart;Superor Diversos;Ed. Fisica / ADM / Pedag / Enferm;Anhanguera Piracicaba;2025.2;Piracicaba;SP; R$ 1.841,00 ;
|
||||
25037;Smart;EM;-;INSA Araras;2025.2;Araras;SP; R$ 2.420,25 ;
|
||||
25038;Smart;Superor Diversos;Arquitetura / Direito /Agro / Med.Vet / Nutri;UNAR;2025.2;Araras;SP; R$ - ;
|
||||
25039;Smart;Superor Diversos;Biocombus/GestÆo/P Quimicos;FATEC Piracicaba;2025.2;Piracicaba;SP; R$ - ;
|
||||
25040;Smart;EM;-;Dom Bosco Cidade Alta;2025.2;Piracicaba;SP; R$ 3.008,80 ;
|
||||
25041;Smart;Superor Diversos;Adm/Agro/ Eng. Prod / Eng. Civil;FATEP;2025.2;Piracicaba;SP; R$ - ;
|
||||
24173;Viva SP;Superor Diversos;CTT - 6773 Enfer / Biomed / Farma / Fisio / Psico;USF Bragan‡a Paulista;2024.2;Bragan‡a Paulista;SP; R$ 4.490,80 ;
|
||||
24174;Viva SP;Superor Diversos;CTT - 8110 FIAP-Eng / UAM-Sistemas / UNIP-UNIF; FIAP / UNIP / UAM;2024.2;SÆo Paulo;SP; R$ 8.304,00 ;
|
||||
24175;Viva SP;Superor Diversos;CTT-7088 Fisio / Biomed / Med. Vet;CEUSP;2024.2;Itu;SP; R$ 2.672,30 ;
|
||||
24176;Arte Formaturas;Agronomia;CTT - 1334;UNIFAI;2024.2;Dracena;SP; R$ 3.374,81 ;
|
||||
24177;Perfil;Superor Diversos;CTT - 780 ADM/Arquitetura/Engenharias;UNISAL Americana;2024.2;Americana;SP; R$ 7.595,00 ;
|
||||
24178;Ponta Eventos;EM / TEC;Integrado Q+I / RH + Alimentos ;IFSP Capivari;2024.2;Capivari;SP; R$ 4.250,00 ;
|
||||
24179;Ponta Eventos;Superor Diversos;;IFSP Capivari;2024.2;Capivari;SP; R$ 2.411,00 ;
|
||||
24180;Ponta Eventos;Superor Diversos;;FATEC Botucatu;2024.2;Botucatu;SP; R$ 5.042,00 ;
|
||||
24181;Golden;Superor Diversos;;FATEC SJC;2024.2;SÆo Jos‚ dos Campos;SP; R$ 5.350,00 ;
|
||||
24184;Arte Formaturas;Agronomia;-;FEA;2024.2;Adamantina;SP; R$ 1.185,00 ;
|
||||
24185;F bio Ribeiro;Direito;-;ISCA;2024.2;Limeira;SP; R$ 2.037,00 ;
|
||||
25042;Toy SP;Unificados;CTT - #4;ESPM;2025.2;SÆo Paulo;SP; R$ 8.155,70 ;
|
||||
26019;Toy SP;Direito;CTT - #10;FDSBC ;2026.2;SÆo Bernardo do Campo;SP; R$ - ;
|
||||
27019;Toy SP;Med. Veterin ria;CTT - #13 - 27.1 + 27.2;UAM;2027.2;SÆo Paulo;SP; R$ 5.014,20 ;Sim
|
||||
25043;Toy SP;Superor Diversos;CCT - #5 - 25.2 + 26.1;PUC;2025.2;SÆo Paulo;SP; R$ 580,00 ;
|
||||
24186;Perfil;Superor Diversos;CTT - 781;Anhanguera Sumar‚;2024.2;Sumar‚;SP; R$ 2.620,00 ;
|
||||
25044;Perfil;EF II / EM;CTT - 778;Objetivo S.B.O;2025.2;Santa Barbara d' Oeste;SP; R$ 3.430,00 ;
|
||||
25045;Perfil;EM / TEC;CTT - 779;Cotil;2025.2;Limeira;SP; R$ - ;
|
||||
25046;Perfil;Superor Diversos;CTT - 696/726;UNIF. Campinas;2025.2;Campinas;SP; R$ 13.194,30 ;Sim
|
||||
25047;Perfil;EM;CTT - 757;ETEC Polivalente;2025.2;Americana;SP; R$ - ;
|
||||
25048;Perfil;Superor Diversos;CTT - 720 Psico/Biomed;FAM;2025.2;Americana;SP; R$ - ;
|
||||
25049;Perfil;Superor Diversos;CTT - 730 UNIF. Sa£de;CEUSP;2025.2;Itu;SP; R$ - ;
|
||||
24187;Viva SP;Superor Diversos;-;IBMEC;2024.2;SÆo Paulo;SP; R$ 6.471,90 ;
|
||||
25050;Viva SP;Med. Veterin ria;CTT - 8117;USP;2025.2;SÆo Paulo;SP; R$ 870,00 ;
|
||||
25051;Viva SP;Direito;CTT - 8241;UNICID;2025.2;SÆo Paulo;SP; R$ 1.740,00 ;Sim
|
||||
25052;Smart;EFII;-;Anglo Portal;2025.2;Piracicaba;SP; R$ 2.122,30 ;
|
||||
25053;Smart;EFII;-;Anglo Cidade Alta;2025.2;Piracicaba;SP; R$ 1.656,20 ;
|
||||
25054;Perfil;EF I / EF II;CTT - 784;Col‚gio Dom Pedro;2025.2;Americana;SP; R$ 4.798,62 ;
|
||||
25055;Perfil;EFII;CTT - 786;Imaculada Campinas;2025.2;Campinas;SP; R$ 7.371,90 ;
|
||||
25056;Perfil;EF I / EF II / EM;CTT - 785;Col‚gio IESC - Nova Odessa;2025.2;Campinas;SP; R$ 1.307,60 ;
|
||||
25057;Perfil;EFII;CTT - 783 - Col‚gio SÆo Jos‚ Participa do Baile;COC Paulinia;2025.2;Paulinia;SP; R$ 9.078,20 ;
|
||||
24188;Prime;Superior Diversos;-;FATEC RibeirÆo Preto;2024.2;RibeirÆo Preto;SP; R$ 7.329,10 ;
|
||||
24189;Prime;Superior Diversos;-;FATEC Mogi Mirim;2024.2;Mogi Mirim;SP; R$ 3.225,60 ;
|
||||
24190;Prime;Superior Diversos;-;FATEC Caparicuiba;2024.2;Carapicuiba;SP; R$ 5.712,75 ;
|
||||
24191;Prime;Superior Diversos;-;FATEC Taquaritinga;2024.2;Taquaritnga;SP; R$ 7.550,00 ;
|
||||
24192;Prime;Superior Diversos;-;FATEC Santana de Parnaiba;2024.2;Santana de Parnaiba;SP; R$ 3.624,90 ;
|
||||
24193;Prime;Superior Diversos;-;IFSP SÆo Roque;2024.2;SÆo Roque;SP; R$ 3.271,34 ;
|
||||
24194;Prime;Superior Diversos;-;FATEC SÆo Carlos;2024.2;SÆo Carlos;SP; R$ 4.838,80 ;
|
||||
24195;Prime;Superior Diversos;-;FATEC Sumar‚;2024.2;Sumar‚;SP; R$ 2.870,30 ;
|
||||
24196;Prime;Superior Diversos;-;FATEC Jundia¡;2024.2;Jundia¡;SP; R$ 5.823,90 ;
|
||||
25059;Viva SP;EF II / EM;-;Escola Comunitaria de Campinas;2025.2;Campinas;SP; R$ 4.328,20 ;
|
||||
24197;Golden;Superior Diversos;-;Anhanguera / UNOPAR;2024.2;SÆo Jos‚ do Campos;SP; R$ 8.780,00 ;
|
||||
24198;Toy SP;Superior Diversos;CTT - #2;ESPM;2024.2;SÆo Paulo;SP; R$ 12.401,90 ;
|
||||
25060;Perfil;EF I / EF II / EM;-;Oficina do Estudante;2024.2;Piracicaba;SP; R$ 16.543,80 ;
|
||||
24199;Ponta Eventos;Superior Diversos;-;UNINTER Jundia¡;2024.2;Jundia¡;SP; R$ 2.750,80 ;
|
||||
24206;Ponta Eventos;Superior Diversos;-;FATEC Itapira;2024.2;Itapira;SP; R$ 2.843,00 ;
|
||||
25061;Smart;EM;-;INSA Araras;2025.2;Araras;SP; R$ - ;
|
||||
25062;Perfil;EF II / EM;CTT - 788;Col‚gio Educativa;2025.2;Sumar‚;SP; R$ 5.747,50 ;
|
||||
26020;Perfil;Superior Diversos;CTT - 703;UNIF. Campinas;2026.2;Campinas;SP; R$ - ;
|
||||
26021;Perfil;Superior Diversos;CTT - 718;UNIF. Piracicaba;2026.2;Piracicaba;SP; R$ - ;
|
||||
26022;Perfil;Superior Diversos;CTT - 731;UNIF. FHO Sa£de;2026.2;Araras;SP; R$ - ;
|
||||
26023;Perfil;Superior Diversos;CTT - 739;UNIF. Limeira;2026.2;Limeira;SP; R$ - ;
|
||||
26024;Perfil;Biomedicina;CTT - 766;USF;2026.2;Campinas;SP; R$ 955,30 ;
|
||||
26025;Perfil;Superior Diversos;CTT - 767;UNASP;2026.2;Hortolandia ;SP; R$ - ;
|
||||
26026;Perfil;Engenharia;CTT - 777;FHO;2026.2;Araras;SP; R$ - ;
|
||||
26027;Perfil;Superior Diversos;CTT - 683 Turma 26.2 e 27.1;UNIF. Americana;2026.2;Americana;SP; R$ - ;
|
||||
27021;Perfil;Superior Diversos;CTT - 728;FAM - Sa£de;2027.2;Americana;SP; R$ - ;
|
||||
27022;Perfil;Superior Diversos;CTT - 749;FAM - Sa£de;2027.2;Americana;SP; R$ - ;
|
||||
25063;Smart;EFII;-;KOELLE;2025.2;Rio Claro;SP; R$ 1.344,60 ;Sim
|
||||
25064;Smart;EM;-;KOELLE;2025.2;Rio Claro;SP; R$ 15.008,20 ;Sim
|
||||
25065;Smart;EM;-;Liceu Terras;2025.2;Piracicaba;SP; R$ 5.335,60 ;
|
||||
25066;Smart;EM;-;ETAPA Valinhos;2025.2;Valinhos;SP; R$ 402,50 ;
|
||||
24207;Prime;Superior Diversos;-;FATEC Araras;2024.2;Araras;SP; R$ 3.019,70 ;
|
||||
25067;Prime;EFII;-;Bandeirante Americana;2025.2;Americana;SP; R$ 4.825,00 ;
|
||||
25068;Forcamp;EM;-;Col‚gio Anglo Salto;2025.2;Salto;SP; R$ - ;
|
||||
25069;Forcamp;EM;-;Col‚gio Conquista;2025.2;Indaiatuba;SP; R$ 7.564,30 ;
|
||||
25070;Forcamp;EM;-;Col‚gio Rodin;2025.2;Indaiatuba;SP; R$ 1.810,00 ;
|
||||
25071;Forcamp;EFII;-;Col‚gio Rodin;2025.2;Indaiatuba;SP; R$ 2.597,80 ;
|
||||
25072;Forcamp;EFII;-;Anglo Salto;2025.2;Salto;SP; R$ 2.542,20 ;
|
||||
25073;Forcamp;EFII;-;Col‚gio Escala;2025.2;Indaiatuba;SP; R$ 2.740,00 ;
|
||||
25074;Forcamp;EFII;-;Divino Cabreuva;2025.2;Cabreuva;SP; R$ 393,00 ;
|
||||
25075;Forcamp;EF II / EM;-;E.E Helena;2025.2;Indaiatuba;SP; R$ 2.016,00 ;
|
||||
25076;Forcamp;EM;-;Col‚gio Divino;2025.2;Indaiatuba;SP; R$ - ;
|
||||
25077;Forcamp;EF II / EM;-;E.E Aurora;2025.2;Indaiatuba;SP; R$ 830,70 ;
|
||||
25078;Forcamp;EFII;-;Col‚gio Divino;2025.2;Itu;SP; R$ 3.214,20 ;
|
||||
25079;Forcamp;EF II / EM;-;SESI Salto;2025.2;Salto;SP; R$ 2.249,00 ;
|
||||
25080;Forcamp;EFII;-;Col‚gio Conquista;2025.2;Indaiatuba;SP; R$ - ;
|
||||
25081;Forcamp;EF II / EM;-;Col‚gio Almeida JR;2025.2;Indaiatuba;SP; R$ 1.114,40 ;
|
||||
25082;Forcamp;EF II / EM;-;Col‚gio Forte Castelo;2025.2;Itu;SP; R$ 1.114,80 ;
|
||||
25083;Forcamp;EF II / EM;-;Col‚gio CapitÆo;2025.2;Indaiatuba;SP; R$ 1.521,70 ;
|
||||
25084;Forcamp;EF II / EM;-;Col‚gio Lucidio;2025.2;Cabreuva;SP; R$ 2.114,60 ;
|
||||
25085;Forcamp;EF II / EM;-;Col‚gio Candelaria;2025.2;Indaiatuba;SP; R$ 3.009,60 ;
|
||||
25086;Forcamp;EM;-;Anglo Indaiatuba;2025.2;Indaiatuba;SP; R$ 3.063,00 ;
|
||||
25087;Forcamp;EFII;-;Col‚gio Anglo Renova‡Æo;2025.2;Indaiatuba;SP; R$ - ;
|
||||
25088;Forcamp;EF II / EM;-;E.E Annunziatta;2025.2;Indaiatuba;SP; R$ 2.061,70 ;
|
||||
25089;Forcamp;EF II / EM;-;SESI Amparo;2025.2;Amparo;SP; R$ 789,80 ;
|
||||
25090;Forcamp;EFII;-;Col‚gio Elite;2025.2;Indaiatuba;SP; R$ 1.058,20 ;
|
||||
25091;Forcamp;EF I / EF II / EM;-;RDS Mimosa;2025.2;Campinas;SP; R$ 4.897,90 ;
|
||||
25092;Forcamp;EF I / EF II;-;VIVAP;2025.2;Campinas;SP; R$ 3.709,50 ;
|
||||
25093;Forcamp;EFI (5§ ano);-;JoÆo e Marcela;2025.2;Campinas;SP; R$ 490,00 ;
|
||||
25094;Forcamp;EF I / EF II / EM;-;RDS Swiss Park;2025.2;Campinas;SP; R$ 2.150,80 ;
|
||||
25095;Viva SP;Superior Diversos;Jun‡Æo CTT - 8421 ; Uniso / Facens / ceusnp;2025.2;Sorocaba;SP; R$ 2.433,14 ;
|
||||
25096;Viva SP;Odontologia;CTT - 8097 sthefany ;USJT-UBC-Unip - Metodista;2025.2;Santos;SP; R$ 11.333,80 ;Sim
|
||||
26028;Viva SP;Odontologia;-;UNIP;2026.2;SÆo Paulo;SP; R$ 1.906,30 ;
|
||||
25097;Prime;EM / TEC;-;ETEC Monte Mor;2025.2;Monte Mor;SP; R$ 2.226,10 ;
|
||||
25098;Prime;EM / TEC;-;ETEC Fernando Prestes;2025.1;Sorocaba;SP; R$ 7.378,32 ;
|
||||
25099;Toy SP;Superior Diversos;CTT - #3;ESPM;2025.1;SÆo Paulo;SP; R$ 24.705,07 ;
|
||||
26029;Toy SP;Enfermagem;CTT - #11;UNIFESP;2026.2;SÆo Paulo;SP; R$ 2.670,00 ;Sim
|
||||
27023;Toy SP;Superior Diversos;Unificados CTT - #14;Einsten;2027.2;SÆo Paulo;SP; R$ 7.859,10 ;Sim
|
||||
25113;Smart;EM;-;Claretiano;2025.2;Rio Claro;SP; R$ 5.475,00 ;Sim
|
||||
25114;Forcamp;EF II / EM;-;Meck Genius;2025.2;Jaguariuna;SP; R$ 1.307,30 ;
|
||||
25115;Viva SP;EM;-;Col‚gio Elite Satana;2025.2;SÆo Paulo;SP; R$ 2.874,80 ;
|
||||
25116;;;;;;;;;
|
||||
25117;;;;;;;;;
|
||||
25118;Viva SP;EM;-;Porto Seguro - Panamby;2025.2;SÆo Paulo;SP; R$ 3.634,60 ;
|
||||
25119;Viva SP;EF II / EM;-;Poliedro SJC;2025.2;SÆo Jos‚ dos Campos;SP; R$ 21.670,94 ;Sim
|
||||
28006;Arte Formaturas;Unificados;CTT - 1355 - ADM/C. Cont beis/Direito;Reges;2028.2;Dracena;SP; R$ 760,00 ;
|
||||
27024;Arte Formaturas;Terapia Ocupacinal;CTT - 1249;AEMS;2027.2;Trˆs Lagoas;MS; R$ 400,00 ;
|
||||
26030;Arte Formaturas;Unificados;CTT - 1295 - Unificados Andradina;FIRB / FAE;2026.2;Andradina;SP; R$ - ;
|
||||
25120;Arte Formaturas;Educa‡Æo F¡sica;CTT - 1351;FAI;2025.2;Adamantina;SP; R$ 1.245,00 ;
|
||||
25107;Viva SP;EF II / EM;-;ETAPA Valinhos;2025.2;Valinhos;SP; R$ 16.031,40 ;Sim
|
||||
25121;Perfil;Unificados;CTT - 796;UNISAL Americana;2025.1;Americana;SP; R$ 955,00 ;
|
||||
25122;Perfil;EF I / EF II / EM;CTT - 795;COL<4F>GIO METROPOLITAN PAULINENSE ;2025.2;Paulinia;SP; R$ 3.239,70 ;
|
||||
27025;Smart;Direito;-;Anhanguera ;2027.2;Piracicaba;SP; R$ 733,40 ;
|
||||
25123;Smart;EFI (5§ ano);-;Maple Bear;2025.2;Piracicaba;SP; R$ 230,00 ;
|
||||
26031;Smart;EFII;8ø Ano 2025.2;Maple Bear;2026.2;Piracicaba;SP; R$ 230,00 ;
|
||||
26032;Smart;EM;2ø Ano EM 2025.2;Anglo Portal;2026.2;Piracicaba;SP; R$ - ;
|
||||
25124;Prime;EM;-;ETEC Fernando Prestes;2025.2;Sorocaba;SP; R$ 7.511,98 ;
|
||||
25125;Prime;Tec. Diversos;-;ETEC Trajano Camargo;2025.1;Limeira;SP; R$ 3.762,30 ;
|
||||
25126;Golden;EM / TEC;-;ETEC Jacare¡;2025.1;Jacare¡;SP; R$ 3.647,14 ;
|
||||
25127;Golden;Superior Diversos;-;FATEC SJC;2025.1;SÆo Jos‚ dos Campos;SP; R$ 5.915,00 ;
|
||||
26033;Arte Formaturas;Psicologia;CTT - 1356;FADAP;2026.2;Dracena;SP; R$ 1.366,00 ;
|
||||
25128;Prime;EM / TEC;-;ETEC Monte Mor;2025.1;Monte Mor;SP; R$ 910,00 ;
|
||||
25129;Perfil;Superior Diversos;CTT - 800;FAM;2025.1;Americana;SP; R$ 7.955,00 ;
|
||||
25130;Prime;Superior Diversos;-;FATEC Sumar‚;2025.1;Sumar‚;SP; R$ 2.919,70 ;
|
||||
25131;Prime;Superior Diversos;-;FATEC Ipiranga ;2025.1;SÆo Paulo;SP; R$ 4.990,30 ;
|
||||
25132;Prime;Superior Diversos;-;FATEC Americana;2025.1;Americana;SP; R$ 4.810,00 ;
|
||||
25133;Prime;Superior Diversos;-;FATEC Santana de Parnaiba;2025.1;Santana de Parnaiba;SP; R$ 3.293,90 ;
|
||||
25134;Prime;Superior Diversos;-;FATEC Guaratingueta;2025.1;Guaratingueta;SP; R$ 6.380,00 ;
|
||||
25135;Prime;Superior Diversos;-;FATEC SÆo Carlos;2025.1;SÆo Carlos;SP; R$ 3.199,00 ;
|
||||
25136;Prime;Superior Diversos;-;FATEC Mogi Mirim;2025.1;Mogi Mirim;SP; R$ 3.643,70 ;
|
||||
25137;Prime;Superior Diversos;-;FATEC Americana;2025.1;Americana;SP; R$ 5.332,20 ;
|
||||
25138;Prime;EM;-;ETEC Trajano Camargo;2025.2;Limeira;SP; R$ 4.129,80 ;
|
||||
25139;Toy SP;Superior Diversos;CTT - #18;FAAP;2025.1;SÆo Paulo;SP; R$ 12.140,20 ;
|
||||
26034;Toy SP;Superior Diversos;CTT - #9 Unificados;ESPM;2026.1;SÆo Paulo;SP; R$ - ;
|
||||
26035;Toy SP;Superior Diversos;CTT - # 16 Unificados 26.2 e 27.1;FAAP;2026.2;SÆo Paulo;SP; R$ - ;
|
||||
26036;Toy SP;Superior Diversos;CTT - # 17 Unificafos 26.1 e 26.2;FOC;2026.2;SÆo Paulo;SP; R$ - ;
|
||||
10003;Festa da Beca;-;-;Eventos Institucionais ;-;-;SP; R$ 969,85 ;
|
||||
25140;Alpha Digital;EI;-;Anglo Pouso Alegre;2025.2;Pouso Alegre;MG; R$ 8.246,90 ;
|
||||
25141;Alpha Digital;EM;-;Anglo Pouso Alegre;2025.3;Pouso Alegre;MG; R$ 1.987,00 ;
|
||||
25142;Alpha Digital;EI / EFI;-;Cezanne;2025.2;Americana;SP; R$ 4.030,00 ;
|
||||
25143;Alpha Digital;EF I / EF II / EM;-;Gibram;2025.2;SÆo Paulo;SP; R$ 7.097,00 ;
|
||||
25144;Alpha Digital;EF II / EM;-;Notre Dame;2025.2;Valinhos;SP; R$ 10.538,80 ;
|
||||
25145;Alpha Digital;EF II / EM;-;Objetivo Rio Claro;2025.2;Rio Claro;SP; R$ 7.226,50 ;
|
||||
25146;Alpha Digital;EF II / EM;-;Sagrado Pompeia;2025.2;SÆo Paulo;SP; R$ 6.310,30 ;
|
||||
25147;Alpha Digital;EFII;-;Santa Marcelina;2025.2;SÆo Paulo;SP; R$ 3.087,10 ;
|
||||
25148;Alpha Digital;EF II / EM;-;SÆo Jos‚ Campinas;2025.2;Campinas;SP; R$ 4.934,80 ;
|
||||
27026;Arte Formaturas;Agronomia;-;FEA;2027.2;Andradina;SP; R$ 2.156,00 ;
|
||||
27027;Arte Formaturas;Enfermagem;-;UFMS;2027.2;Trˆs Lagoas;MS; R$ 756,10 ;
|
||||
25149;Viva SP;Superior Diversos;Med. Vet - Biomed CTT - 8132/8395 ;UNICSUL;2025.2;SÆo Paulo;SP; R$ 1.048,20 ;
|
||||
25150;Viva SP;Superior Diversos;-;IBMEC;2025.1;SÆo Paulo;SP; R$ 6.700,40 ;
|
||||
26037;Arte Formaturas;Superior Diversos;CTT - 1331 (Enfer/Engharia);FIRB+FEA;2026.2;Andradina;SP; R$ 516,00 ;
|
||||
27028;Arte Formaturas;Superior Diversos;CTT - 1319 (Comput/Pedagogia);FAI;2027.2;Dracena;SP; R$ 895,00 ;
|
||||
25151;RUB;Superior Diversos;CTT - 15;FASM;2025.1;SÆo Paulo;SP; R$ - ;
|
||||
26038;RUB;Superior Diversos;CTT - 17;FASM;2026.2;SÆo Paulo;SP; R$ 881,00 ;
|
||||
25152;RUB;Superior Diversos;CTT - 130;FGV;2025.1;SÆo Paulo;SP; R$ 23.162,50 ;
|
||||
25153;RUB;Superior Diversos;CTT - 131;FGV;2025.2;SÆo Paulo;SP; R$ 663,40 ;
|
||||
25154;RUB;Superior Diversos;CTT - 25;INSPER;2025.1;SÆo Paulo;SP; R$ 43.315,70 ;sim
|
||||
26039;RUB;Arquitetura / Urbanismo;CTT - Aurea;Mackenzie FAU;2026.1;SÆo Paulo;SP; R$ - ;
|
||||
26040;RUB;Odontologia;CTT - Tiralentes;UNICID;2026.2;SÆo Paulo;SP; R$ - ;
|
||||
26041;RUB;Enfermagem;-;Albert Einstein;2026.2;SÆo Paulo;SP; R$ 440,00 ;
|
||||
25155;RUB;Superior Diversos;-;UNIP;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25156;RUB;Psicologia;-;PUC;2025.2;SÆo Paulo;SP; R$ 6.275,00 ;Sim
|
||||
25157;RUB;Superior Diversos;-;ESPM;2025.1;SÆo Paulo;SP; R$ 4.088,20 ;Sim
|
||||
25158;RUB;Superior Diversos;CTT - C25;CASPER;2025.2;SÆo Paulo;SP; R$ 2.588,75 ;
|
||||
25159;RUB;Superior Diversos;CTT - Mack Amn‚sia;Mackenzie;2025.2;SÆo Paulo;SP; R$ 4.620,00 ;sim
|
||||
25160;RUB;Superior Diversos;CTT - Macklandia;Mackenzie CCSA;2025.1;SÆo Paulo;SP; R$ 10.949,50 ;
|
||||
27029;RUB;Medicina;CTT - T30;SÆo Camilo;2027.1;SÆo Paulo;SP; R$ 5.805,10 ;
|
||||
28007;RUB;Medicina;CTT - T32;SÆo Camilo;2028.1;SÆo Paulo;SP; R$ 2.636,40 ;
|
||||
28008;RUB;Medicina;CTT - Med TesÆo;Santo Amaro;2028.1;SÆo Paulo;SP; R$ 8.173,50 ;sim
|
||||
25161;RUB;Superior Diversos;CTT - Cinesia;UNICID;2025.2;SÆo Paulo;SP; R$ 10.824,30 ;
|
||||
25162;RUB;Arquitetura / Urbanismo;CTT - 321;Mackenzie FAU;2025.2;SÆo Paulo;SP; R$ 579,60 ;
|
||||
27030;;;;;;;SP; R$ - ;
|
||||
25163;Smart;EFII;-;Col‚gio Elite;2025.2;Piracicaba;SP; R$ 2.189,70 ;
|
||||
25164;Smart;EFI (5§ ano);-;Col‚gio Elite;2025.2;Piracicaba;SP; R$ - ;
|
||||
25165;Prime;Superior Diversos;-;FATEC RibeirÆo Preto;2025.1;RibeirÆo Preto;SP; R$ 5.852,20 ;
|
||||
25166;Ponta Eventos;Superior Diversos;Turma 25.1 e 25.2 Baile antes das cola‡äes;IFSP Piracicaba;2025.2;Piracicaba;SP; R$ 5.451,10 ;
|
||||
25167;Ponta Eventos;Superior Diversos;-;FATEC Botucatu;2025.1;Botucatu;SP; R$ 4.278,42 ;
|
||||
25168;Ponta Eventos;Superior Diversos;-;FATEC Itapira;2025.1;Itapira;SP; R$ 3.110,40 ;
|
||||
25169;Ponta Eventos;Superior Diversos;-;FATEC Piracicaba;2025.1;Piracicaba;SP; R$ 3.301,50 ;
|
||||
27032;Smart;Direito;-;FATEP Piracicaba;2027.2;Piracicaba;SP; R$ 1.720,76 ;
|
||||
27033;Smart;Direito;-;UNIMEP;2027.2;Piracicaba;SP; R$ - ;
|
||||
25170;Ponta Eventos;Tec. Diversos;-;IFSP Capivari;2025.1;Campinas;SP; R$ 1.001,00 ;
|
||||
25171;RUB;Superior Diversos;Link Owners;Link School;2025.1;SÆo Paulo;SP; R$ 21.430,00 ;
|
||||
25172;Viva SP;Biomedicina;Jun‡Æo CTT - 8304;UNISANTA;2025.2;Santos;SP; R$ - ;
|
||||
28009;Viva SP;Med. Veterin ria;CTT - 9273;USJT;2028.2;Santos;SP; R$ 1.765,00 ;
|
||||
28010;Viva SP;Medicina;CTT - ;FAJ;2028.2;Jaguariuna;SP; R$ 5.420,00 ;
|
||||
25173;Viva SP;Superior Diversos;CTT - 8251;Metodista;2025.2;SÆo Paulo;SP; R$ 490,00 ;
|
||||
26042;Viva SP;Educa‡Æo F¡sica;CTT - 9140;PMSP;2026.1;SÆo Paulo;SP; R$ 580,00 ;
|
||||
25174;Arte Formaturas;Enfermagem;CTT - 1388;FEA;2025.2;Adamantina;SP; R$ 1.205,00 ;
|
||||
25175;Arte Formaturas;Educa‡Æo F¡sica;CTT - 1351;FAI;2025.2;Dracena;SP; R$ 1.035,00 ;
|
||||
25176;RUB;EF II / EM;-;Col‚gio CONSA;2025.2;SÆo Paulo;SP; R$ 1.730,00 ;
|
||||
25177;Ponta Eventos;Superior Diversos;-;FATEC Gar‡a;2025.1;Gar‡a;SP; R$ 4.128,80 ;
|
||||
25178;Ponta Eventos;Superior Diversos;-;IFSP Capivari;2025.1;Capivari;SP; R$ 414,00 ;
|
||||
25179;Golden;Superior Diversos;-;Anhanguera / UNOPAR;2025.1;SÆo Jos‚ dos Campos;SP; R$ 5.290,00 ;
|
||||
25180;Prime;Superior Diversos;-;UNIP Osasco;2025.1;Osasco;SP; R$ 1.795,00 ;
|
||||
25181;Festa da Beca;Superior Diversos;;Metodista;2025.1;SÆo Bernardo do Campo;SP; R$ 8.300,90 ;
|
||||
25182;Forcamp;EM;-;ETECAP;2025.2;Campinas;SP; R$ 6.071,40 ;
|
||||
25183;Ponta Eventos;EM / TEC;-;E.E Elias Mello Ayres;2025.2;Piracicaba;SP; R$ 1.058,00 ;
|
||||
25184;MVP Formaturas;Direito;-;UNISANTOS;2025.2;Santos;SP; R$ 8.676,30 ;Sim
|
||||
25185;MVP Formaturas;Odontologia;-;UNISANTA;2025.2;Santos;SP; R$ - ;
|
||||
25186;Viva SP;Superior Diversos;Jun‡Æo CTT - 8742;Senac- Unicsul - FMU;2025.2;SÆo Paulo;SP; R$ 1.354,30 ;
|
||||
10004;RUB;-;-;Eventos Institucionais ;-;SÆo Paulo;SP; R$ 290,00 ;
|
||||
27034;Smart;Superior Diversos;Engenharias;UNARARAS;2027.2;Araras;SP; R$ 650,00 ;
|
||||
27035;Viva SP;Odontologia;-;UNISA;2027.1;SÆo Paulo;SP; R$ 1.835,00 ;
|
||||
25187;Arte Formaturas;Direito;CTT - 1352;Reges;2025.2;Dracena;SP; R$ 1.163,30 ;
|
||||
27036;Arte Formaturas;Educa‡Æo F¡sica;CTT - 1309;FEA;2027.2;Adamantina;SP; R$ 1.141,00 ;
|
||||
25188;Viva SP;R.I;CTT - 8742;USP;2025.2;SÆo Paulo;SP; R$ 690,00 ;
|
||||
26043;Viva SP;Med. Veterin ria;CTT - 8897;FMU;2026.1;SÆo Paulo;SP; R$ 650,00 ;
|
||||
25189;Viva SP;Superior Diversos;Direito e Saude / CTT - 7973;UNIP;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25190;Viva SP;Superior Diversos;CTT - 8100;UNIANCHIETA;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25191;RUB;EF II / EM;;Col‚gio Dante;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25192;RUB;EF II / EM;;Col‚gio Consept;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
29001;Arte Formaturas;Direito;;FIRB;2029.2;Dracena;SP; R$ 360,00 ;
|
||||
25193;Festa da Beca;-;-;Valdomiro Silveira;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25194;Festa da Beca;-;-;Kennedy;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25195;Festa da Beca;-;-;Juarez Tavora;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25196;Festa da Beca;-;-;EE Prof Cristina Fitipaldi;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25197;Festa da Beca;-;-;COC;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25198;Festa da Beca;-;-;EE Angelo mendes;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25199;Festa da Beca;-;-;Malu;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25200;Festa da Beca;-;-;Paulo Emilio;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25202;Festa da Beca;-;-;Paineiras;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25203;Festa da Beca;-;-;Oscavo;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25204;Festa da Beca;-;-;EE Santa Dalmolin Demarchi;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
25205;Festa da Beca;-;-;Antunes e Paulo Emilio;2025.2;SÆo Paulo;SP; R$ - ;
|
||||
|
|
|
@ -161,3 +161,150 @@ func (h *Handler) Delete(c *gin.Context) {
|
|||
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// AssignProfessional godoc
|
||||
// @Summary Assign professional to agenda
|
||||
// @Tags agenda
|
||||
// @Router /api/agenda/{id}/professionals [post]
|
||||
func (h *Handler) AssignProfessional(c *gin.Context) {
|
||||
idParam := c.Param("id")
|
||||
agendaID, err := uuid.Parse(idParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de agenda inválido"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ProfessionalID string `json:"professional_id" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Dados inválidos: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
profID, err := uuid.Parse(req.ProfessionalID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de profissional inválido"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.AssignProfessional(c.Request.Context(), agendaID, profID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao atribuir profissional: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// RemoveProfessional godoc
|
||||
// @Summary Remove professional from agenda
|
||||
// @Tags agenda
|
||||
// @Router /api/agenda/{id}/professionals/{profId} [delete]
|
||||
func (h *Handler) RemoveProfessional(c *gin.Context) {
|
||||
idParam := c.Param("id")
|
||||
agendaID, err := uuid.Parse(idParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de agenda inválido"})
|
||||
return
|
||||
}
|
||||
|
||||
profIdParam := c.Param("profId")
|
||||
profID, err := uuid.Parse(profIdParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de profissional inválido"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.RemoveProfessional(c.Request.Context(), agendaID, profID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao remover profissional: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// GetProfessionals godoc
|
||||
// @Summary Get professionals assigned to agenda
|
||||
// @Tags agenda
|
||||
// @Router /api/agenda/{id}/professionals [get]
|
||||
func (h *Handler) GetProfessionals(c *gin.Context) {
|
||||
idParam := c.Param("id")
|
||||
agendaID, err := uuid.Parse(idParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de agenda inválido"})
|
||||
return
|
||||
}
|
||||
|
||||
profs, err := h.service.GetAgendaProfessionals(c.Request.Context(), agendaID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao buscar profissionais: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, profs)
|
||||
}
|
||||
|
||||
// UpdateStatus godoc
|
||||
// @Summary Update agenda status
|
||||
// @Tags agenda
|
||||
// @Router /api/agenda/{id}/status [patch]
|
||||
func (h *Handler) UpdateStatus(c *gin.Context) {
|
||||
idParam := c.Param("id")
|
||||
agendaID, err := uuid.Parse(idParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de agenda inválido"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Dados inválidos: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
agenda, err := h.service.UpdateStatus(c.Request.Context(), agendaID, req.Status)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao atualizar status: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, agenda)
|
||||
}
|
||||
|
||||
// UpdateAssignmentStatus godoc
|
||||
// @Summary Update professional assignment status
|
||||
// @Tags agenda
|
||||
// @Router /api/agenda/{id}/professionals/{profId}/status [patch]
|
||||
func (h *Handler) UpdateAssignmentStatus(c *gin.Context) {
|
||||
idParam := c.Param("id")
|
||||
agendaID, err := uuid.Parse(idParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de agenda inválido"})
|
||||
return
|
||||
}
|
||||
|
||||
profIdParam := c.Param("profId")
|
||||
profID, err := uuid.Parse(profIdParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de profissional inválido"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Dados inválidos: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.UpdateAssignmentStatus(c.Request.Context(), agendaID, profID, req.Status, req.Reason); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao atualizar status: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package agenda
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"photum-backend/internal/db/generated"
|
||||
|
|
@ -44,6 +45,17 @@ type CreateAgendaRequest struct {
|
|||
PreVenda bool `json:"pre_venda"`
|
||||
}
|
||||
|
||||
type Assignment struct {
|
||||
ProfessionalID string `json:"professional_id"`
|
||||
Status string `json:"status"`
|
||||
MotivoRejeicao *string `json:"motivo_rejeicao"`
|
||||
}
|
||||
|
||||
type AgendaResponse struct {
|
||||
generated.ListAgendasRow
|
||||
ParsedAssignments []Assignment `json:"assignments"`
|
||||
}
|
||||
|
||||
func (s *Service) CalculateStatus(fotoFaltante, recepFaltante, cineFaltante int32) string {
|
||||
if fotoFaltante < 0 || recepFaltante < 0 || cineFaltante < 0 {
|
||||
return "ERRO"
|
||||
|
|
@ -92,59 +104,86 @@ func (s *Service) Create(ctx context.Context, userID uuid.UUID, req CreateAgenda
|
|||
return s.queries.CreateAgenda(ctx, params)
|
||||
}
|
||||
|
||||
func (s *Service) List(ctx context.Context, userID uuid.UUID, role string) ([]generated.ListAgendasRow, error) {
|
||||
func (s *Service) List(ctx context.Context, userID uuid.UUID, role string) ([]AgendaResponse, error) {
|
||||
var rows []generated.ListAgendasRow
|
||||
var err error
|
||||
|
||||
// If role is CLIENT (cliente), filter by userID
|
||||
if role == "cliente" || role == "EVENT_OWNER" {
|
||||
rows, err := s.queries.ListAgendasByUser(ctx, pgtype.UUID{Bytes: userID, Valid: true})
|
||||
listRows, err := s.queries.ListAgendasByUser(ctx, pgtype.UUID{Bytes: userID, Valid: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Convert ListAgendasByUserRow to ListAgendasRow (they are identical structurally but different types in Go)
|
||||
// To avoid manual conversion if types differ slightly by name but not structure, we might need a common interface or cast.
|
||||
// Since sqlc generates separate structs, we have to map them.
|
||||
var result []generated.ListAgendasRow
|
||||
for _, r := range rows {
|
||||
result = append(result, generated.ListAgendasRow{
|
||||
ID: r.ID,
|
||||
UserID: r.UserID,
|
||||
FotID: r.FotID,
|
||||
DataEvento: r.DataEvento,
|
||||
TipoEventoID: r.TipoEventoID,
|
||||
ObservacoesEvento: r.ObservacoesEvento,
|
||||
LocalEvento: r.LocalEvento,
|
||||
Endereco: r.Endereco,
|
||||
Horario: r.Horario,
|
||||
QtdFormandos: r.QtdFormandos,
|
||||
QtdFotografos: r.QtdFotografos,
|
||||
QtdRecepcionistas: r.QtdRecepcionistas,
|
||||
QtdCinegrafistas: r.QtdCinegrafistas,
|
||||
QtdEstudios: r.QtdEstudios,
|
||||
QtdPontoFoto: r.QtdPontoFoto,
|
||||
QtdPontoID: r.QtdPontoID,
|
||||
QtdPontoDecorado: r.QtdPontoDecorado,
|
||||
QtdPontosLed: r.QtdPontosLed,
|
||||
QtdPlataforma360: r.QtdPlataforma360,
|
||||
StatusProfissionais: r.StatusProfissionais,
|
||||
FotoFaltante: r.FotoFaltante,
|
||||
RecepFaltante: r.RecepFaltante,
|
||||
CineFaltante: r.CineFaltante,
|
||||
LogisticaObservacoes: r.LogisticaObservacoes,
|
||||
PreVenda: r.PreVenda,
|
||||
CriadoEm: r.CriadoEm,
|
||||
AtualizadoEm: r.AtualizadoEm,
|
||||
FotNumero: r.FotNumero,
|
||||
Instituicao: r.Instituicao,
|
||||
CursoNome: r.CursoNome,
|
||||
EmpresaNome: r.EmpresaNome,
|
||||
AnoSemestre: r.AnoSemestre,
|
||||
ObservacoesFot: r.ObservacoesFot,
|
||||
TipoEventoNome: r.TipoEventoNome,
|
||||
// Convert ListAgendasByUserRow to ListAgendasRow manually
|
||||
for _, r := range listRows {
|
||||
rows = append(rows, generated.ListAgendasRow{
|
||||
ID: r.ID,
|
||||
UserID: r.UserID,
|
||||
FotID: r.FotID,
|
||||
DataEvento: r.DataEvento,
|
||||
TipoEventoID: r.TipoEventoID,
|
||||
ObservacoesEvento: r.ObservacoesEvento,
|
||||
LocalEvento: r.LocalEvento,
|
||||
Endereco: r.Endereco,
|
||||
Horario: r.Horario,
|
||||
QtdFormandos: r.QtdFormandos,
|
||||
QtdFotografos: r.QtdFotografos,
|
||||
QtdRecepcionistas: r.QtdRecepcionistas,
|
||||
QtdCinegrafistas: r.QtdCinegrafistas,
|
||||
QtdEstudios: r.QtdEstudios,
|
||||
QtdPontoFoto: r.QtdPontoFoto,
|
||||
QtdPontoID: r.QtdPontoID,
|
||||
QtdPontoDecorado: r.QtdPontoDecorado,
|
||||
QtdPontosLed: r.QtdPontosLed,
|
||||
QtdPlataforma360: r.QtdPlataforma360,
|
||||
StatusProfissionais: r.StatusProfissionais,
|
||||
FotoFaltante: r.FotoFaltante,
|
||||
RecepFaltante: r.RecepFaltante,
|
||||
CineFaltante: r.CineFaltante,
|
||||
LogisticaObservacoes: r.LogisticaObservacoes,
|
||||
PreVenda: r.PreVenda,
|
||||
CriadoEm: r.CriadoEm,
|
||||
AtualizadoEm: r.AtualizadoEm,
|
||||
Status: r.Status,
|
||||
FotNumero: r.FotNumero,
|
||||
Instituicao: r.Instituicao,
|
||||
CursoNome: r.CursoNome,
|
||||
EmpresaNome: r.EmpresaNome,
|
||||
AnoSemestre: r.AnoSemestre,
|
||||
ObservacoesFot: r.ObservacoesFot,
|
||||
TipoEventoNome: r.TipoEventoNome,
|
||||
AssignedProfessionals: r.AssignedProfessionals,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
} else {
|
||||
rows, err = s.queries.ListAgendas(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s.queries.ListAgendas(ctx)
|
||||
var response []AgendaResponse
|
||||
for _, row := range rows {
|
||||
var assignments []Assignment
|
||||
if row.AssignedProfessionals != nil {
|
||||
bytes, ok := row.AssignedProfessionals.([]byte)
|
||||
if !ok {
|
||||
str, ok := row.AssignedProfessionals.(string)
|
||||
if ok {
|
||||
bytes = []byte(str)
|
||||
}
|
||||
}
|
||||
if bytes != nil {
|
||||
json.Unmarshal(bytes, &assignments)
|
||||
}
|
||||
}
|
||||
response = append(response, AgendaResponse{
|
||||
ListAgendasRow: row,
|
||||
ParsedAssignments: assignments,
|
||||
})
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, id uuid.UUID) (generated.Agenda, error) {
|
||||
|
|
@ -186,3 +225,42 @@ func (s *Service) Update(ctx context.Context, id uuid.UUID, req CreateAgendaRequ
|
|||
func (s *Service) Delete(ctx context.Context, id uuid.UUID) error {
|
||||
return s.queries.DeleteAgenda(ctx, pgtype.UUID{Bytes: id, Valid: true})
|
||||
}
|
||||
|
||||
func (s *Service) AssignProfessional(ctx context.Context, agendaID uuid.UUID, profID uuid.UUID) error {
|
||||
params := generated.AssignProfessionalParams{
|
||||
AgendaID: pgtype.UUID{Bytes: agendaID, Valid: true},
|
||||
ProfissionalID: pgtype.UUID{Bytes: profID, Valid: true},
|
||||
}
|
||||
return s.queries.AssignProfessional(ctx, params)
|
||||
}
|
||||
|
||||
func (s *Service) RemoveProfessional(ctx context.Context, agendaID uuid.UUID, profID uuid.UUID) error {
|
||||
params := generated.RemoveProfessionalParams{
|
||||
AgendaID: pgtype.UUID{Bytes: agendaID, Valid: true},
|
||||
ProfissionalID: pgtype.UUID{Bytes: profID, Valid: true},
|
||||
}
|
||||
return s.queries.RemoveProfessional(ctx, params)
|
||||
}
|
||||
|
||||
func (s *Service) GetAgendaProfessionals(ctx context.Context, agendaID uuid.UUID) ([]generated.GetAgendaProfessionalsRow, error) {
|
||||
return s.queries.GetAgendaProfessionals(ctx, pgtype.UUID{Bytes: agendaID, Valid: true})
|
||||
}
|
||||
|
||||
func (s *Service) UpdateStatus(ctx context.Context, agendaID uuid.UUID, status string) (generated.Agenda, error) {
|
||||
params := generated.UpdateAgendaStatusParams{
|
||||
ID: pgtype.UUID{Bytes: agendaID, Valid: true},
|
||||
Status: pgtype.Text{String: status, Valid: true},
|
||||
}
|
||||
return s.queries.UpdateAgendaStatus(ctx, params)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateAssignmentStatus(ctx context.Context, agendaID, professionalID uuid.UUID, status string, reason string) error {
|
||||
params := generated.UpdateAssignmentStatusParams{
|
||||
AgendaID: pgtype.UUID{Bytes: agendaID, Valid: true},
|
||||
ProfissionalID: pgtype.UUID{Bytes: professionalID, Valid: true},
|
||||
Status: pgtype.Text{String: status, Valid: true},
|
||||
MotivoRejeicao: pgtype.Text{String: reason, Valid: reason != ""},
|
||||
}
|
||||
_, err := s.queries.UpdateAssignmentStatus(ctx, params)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,22 @@ import (
|
|||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const assignProfessional = `-- name: AssignProfessional :exec
|
||||
INSERT INTO agenda_profissionais (agenda_id, profissional_id)
|
||||
VALUES ($1, $2)
|
||||
ON CONFLICT (agenda_id, profissional_id) DO NOTHING
|
||||
`
|
||||
|
||||
type AssignProfessionalParams struct {
|
||||
AgendaID pgtype.UUID `json:"agenda_id"`
|
||||
ProfissionalID pgtype.UUID `json:"profissional_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) AssignProfessional(ctx context.Context, arg AssignProfessionalParams) error {
|
||||
_, err := q.db.Exec(ctx, assignProfessional, arg.AgendaID, arg.ProfissionalID)
|
||||
return err
|
||||
}
|
||||
|
||||
const createAgenda = `-- name: CreateAgenda :one
|
||||
INSERT INTO agenda (
|
||||
fot_id,
|
||||
|
|
@ -39,7 +55,7 @@ INSERT INTO agenda (
|
|||
user_id
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24
|
||||
) RETURNING id, user_id, fot_id, data_evento, tipo_evento_id, observacoes_evento, local_evento, endereco, horario, qtd_formandos, qtd_fotografos, qtd_recepcionistas, qtd_cinegrafistas, qtd_estudios, qtd_ponto_foto, qtd_ponto_id, qtd_ponto_decorado, qtd_pontos_led, qtd_plataforma_360, status_profissionais, foto_faltante, recep_faltante, cine_faltante, logistica_observacoes, pre_venda, criado_em, atualizado_em
|
||||
) RETURNING id, user_id, fot_id, data_evento, tipo_evento_id, observacoes_evento, local_evento, endereco, horario, qtd_formandos, qtd_fotografos, qtd_recepcionistas, qtd_cinegrafistas, qtd_estudios, qtd_ponto_foto, qtd_ponto_id, qtd_ponto_decorado, qtd_pontos_led, qtd_plataforma_360, status_profissionais, foto_faltante, recep_faltante, cine_faltante, logistica_observacoes, pre_venda, criado_em, atualizado_em, status
|
||||
`
|
||||
|
||||
type CreateAgendaParams struct {
|
||||
|
|
@ -125,6 +141,7 @@ func (q *Queries) CreateAgenda(ctx context.Context, arg CreateAgendaParams) (Age
|
|||
&i.PreVenda,
|
||||
&i.CriadoEm,
|
||||
&i.AtualizadoEm,
|
||||
&i.Status,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -140,7 +157,7 @@ func (q *Queries) DeleteAgenda(ctx context.Context, id pgtype.UUID) error {
|
|||
}
|
||||
|
||||
const getAgenda = `-- name: GetAgenda :one
|
||||
SELECT id, user_id, fot_id, data_evento, tipo_evento_id, observacoes_evento, local_evento, endereco, horario, qtd_formandos, qtd_fotografos, qtd_recepcionistas, qtd_cinegrafistas, qtd_estudios, qtd_ponto_foto, qtd_ponto_id, qtd_ponto_decorado, qtd_pontos_led, qtd_plataforma_360, status_profissionais, foto_faltante, recep_faltante, cine_faltante, logistica_observacoes, pre_venda, criado_em, atualizado_em FROM agenda
|
||||
SELECT id, user_id, fot_id, data_evento, tipo_evento_id, observacoes_evento, local_evento, endereco, horario, qtd_formandos, qtd_fotografos, qtd_recepcionistas, qtd_cinegrafistas, qtd_estudios, qtd_ponto_foto, qtd_ponto_id, qtd_ponto_decorado, qtd_pontos_led, qtd_plataforma_360, status_profissionais, foto_faltante, recep_faltante, cine_faltante, logistica_observacoes, pre_venda, criado_em, atualizado_em, status FROM agenda
|
||||
WHERE id = $1 LIMIT 1
|
||||
`
|
||||
|
||||
|
|
@ -175,20 +192,122 @@ func (q *Queries) GetAgenda(ctx context.Context, id pgtype.UUID) (Agenda, error)
|
|||
&i.PreVenda,
|
||||
&i.CriadoEm,
|
||||
&i.AtualizadoEm,
|
||||
&i.Status,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getAgendaProfessionals = `-- name: GetAgendaProfessionals :many
|
||||
SELECT p.id, p.usuario_id, p.nome, p.funcao_profissional_id, p.endereco, p.cidade, p.uf, p.whatsapp, p.cpf_cnpj_titular, p.banco, p.agencia, p.conta_pix, p.carro_disponivel, p.tem_estudio, p.qtd_estudio, p.tipo_cartao, p.observacao, p.qual_tec, p.educacao_simpatia, p.desempenho_evento, p.disp_horario, p.media, p.tabela_free, p.extra_por_equipamento, p.equipamentos, p.criado_em, p.atualizado_em, f.nome as funcao_nome, u.email
|
||||
FROM cadastro_profissionais p
|
||||
JOIN agenda_profissionais ap ON p.id = ap.profissional_id
|
||||
LEFT JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id
|
||||
LEFT JOIN usuarios u ON p.usuario_id = u.id
|
||||
WHERE ap.agenda_id = $1
|
||||
`
|
||||
|
||||
type GetAgendaProfessionalsRow struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
UsuarioID pgtype.UUID `json:"usuario_id"`
|
||||
Nome string `json:"nome"`
|
||||
FuncaoProfissionalID pgtype.UUID `json:"funcao_profissional_id"`
|
||||
Endereco pgtype.Text `json:"endereco"`
|
||||
Cidade pgtype.Text `json:"cidade"`
|
||||
Uf pgtype.Text `json:"uf"`
|
||||
Whatsapp pgtype.Text `json:"whatsapp"`
|
||||
CpfCnpjTitular pgtype.Text `json:"cpf_cnpj_titular"`
|
||||
Banco pgtype.Text `json:"banco"`
|
||||
Agencia pgtype.Text `json:"agencia"`
|
||||
ContaPix pgtype.Text `json:"conta_pix"`
|
||||
CarroDisponivel pgtype.Bool `json:"carro_disponivel"`
|
||||
TemEstudio pgtype.Bool `json:"tem_estudio"`
|
||||
QtdEstudio pgtype.Int4 `json:"qtd_estudio"`
|
||||
TipoCartao pgtype.Text `json:"tipo_cartao"`
|
||||
Observacao pgtype.Text `json:"observacao"`
|
||||
QualTec pgtype.Int4 `json:"qual_tec"`
|
||||
EducacaoSimpatia pgtype.Int4 `json:"educacao_simpatia"`
|
||||
DesempenhoEvento pgtype.Int4 `json:"desempenho_evento"`
|
||||
DispHorario pgtype.Int4 `json:"disp_horario"`
|
||||
Media pgtype.Numeric `json:"media"`
|
||||
TabelaFree pgtype.Text `json:"tabela_free"`
|
||||
ExtraPorEquipamento pgtype.Bool `json:"extra_por_equipamento"`
|
||||
Equipamentos pgtype.Text `json:"equipamentos"`
|
||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||
FuncaoNome pgtype.Text `json:"funcao_nome"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAgendaProfessionals(ctx context.Context, agendaID pgtype.UUID) ([]GetAgendaProfessionalsRow, error) {
|
||||
rows, err := q.db.Query(ctx, getAgendaProfessionals, agendaID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetAgendaProfessionalsRow
|
||||
for rows.Next() {
|
||||
var i GetAgendaProfessionalsRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UsuarioID,
|
||||
&i.Nome,
|
||||
&i.FuncaoProfissionalID,
|
||||
&i.Endereco,
|
||||
&i.Cidade,
|
||||
&i.Uf,
|
||||
&i.Whatsapp,
|
||||
&i.CpfCnpjTitular,
|
||||
&i.Banco,
|
||||
&i.Agencia,
|
||||
&i.ContaPix,
|
||||
&i.CarroDisponivel,
|
||||
&i.TemEstudio,
|
||||
&i.QtdEstudio,
|
||||
&i.TipoCartao,
|
||||
&i.Observacao,
|
||||
&i.QualTec,
|
||||
&i.EducacaoSimpatia,
|
||||
&i.DesempenhoEvento,
|
||||
&i.DispHorario,
|
||||
&i.Media,
|
||||
&i.TabelaFree,
|
||||
&i.ExtraPorEquipamento,
|
||||
&i.Equipamentos,
|
||||
&i.CriadoEm,
|
||||
&i.AtualizadoEm,
|
||||
&i.FuncaoNome,
|
||||
&i.Email,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listAgendas = `-- name: ListAgendas :many
|
||||
SELECT
|
||||
a.id, a.user_id, a.fot_id, a.data_evento, a.tipo_evento_id, a.observacoes_evento, a.local_evento, a.endereco, a.horario, a.qtd_formandos, a.qtd_fotografos, a.qtd_recepcionistas, a.qtd_cinegrafistas, a.qtd_estudios, a.qtd_ponto_foto, a.qtd_ponto_id, a.qtd_ponto_decorado, a.qtd_pontos_led, a.qtd_plataforma_360, a.status_profissionais, a.foto_faltante, a.recep_faltante, a.cine_faltante, a.logistica_observacoes, a.pre_venda, a.criado_em, a.atualizado_em,
|
||||
a.id, a.user_id, a.fot_id, a.data_evento, a.tipo_evento_id, a.observacoes_evento, a.local_evento, a.endereco, a.horario, a.qtd_formandos, a.qtd_fotografos, a.qtd_recepcionistas, a.qtd_cinegrafistas, a.qtd_estudios, a.qtd_ponto_foto, a.qtd_ponto_id, a.qtd_ponto_decorado, a.qtd_pontos_led, a.qtd_plataforma_360, a.status_profissionais, a.foto_faltante, a.recep_faltante, a.cine_faltante, a.logistica_observacoes, a.pre_venda, a.criado_em, a.atualizado_em, a.status,
|
||||
cf.fot as fot_numero,
|
||||
cf.instituicao,
|
||||
c.nome as curso_nome,
|
||||
e.nome as empresa_nome,
|
||||
af.ano_semestre,
|
||||
cf.observacoes as observacoes_fot,
|
||||
te.nome as tipo_evento_nome
|
||||
te.nome as tipo_evento_nome,
|
||||
COALESCE(
|
||||
(SELECT json_agg(json_build_object(
|
||||
'professional_id', ap.profissional_id,
|
||||
'status', ap.status,
|
||||
'motivo_rejeicao', ap.motivo_rejeicao
|
||||
))
|
||||
FROM agenda_profissionais ap
|
||||
WHERE ap.agenda_id = a.id),
|
||||
'[]'::json
|
||||
) as assigned_professionals
|
||||
FROM agenda a
|
||||
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
||||
JOIN cursos c ON cf.curso_id = c.id
|
||||
|
|
@ -199,40 +318,42 @@ ORDER BY a.data_evento
|
|||
`
|
||||
|
||||
type ListAgendasRow struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
UserID pgtype.UUID `json:"user_id"`
|
||||
FotID pgtype.UUID `json:"fot_id"`
|
||||
DataEvento pgtype.Date `json:"data_evento"`
|
||||
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
|
||||
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
|
||||
LocalEvento pgtype.Text `json:"local_evento"`
|
||||
Endereco pgtype.Text `json:"endereco"`
|
||||
Horario pgtype.Text `json:"horario"`
|
||||
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
|
||||
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
|
||||
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
|
||||
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
|
||||
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
|
||||
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
|
||||
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
|
||||
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
|
||||
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
|
||||
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
|
||||
StatusProfissionais pgtype.Text `json:"status_profissionais"`
|
||||
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
|
||||
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
|
||||
CineFaltante pgtype.Int4 `json:"cine_faltante"`
|
||||
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
|
||||
PreVenda pgtype.Bool `json:"pre_venda"`
|
||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||
FotNumero int32 `json:"fot_numero"`
|
||||
Instituicao pgtype.Text `json:"instituicao"`
|
||||
CursoNome string `json:"curso_nome"`
|
||||
EmpresaNome string `json:"empresa_nome"`
|
||||
AnoSemestre string `json:"ano_semestre"`
|
||||
ObservacoesFot pgtype.Text `json:"observacoes_fot"`
|
||||
TipoEventoNome string `json:"tipo_evento_nome"`
|
||||
ID pgtype.UUID `json:"id"`
|
||||
UserID pgtype.UUID `json:"user_id"`
|
||||
FotID pgtype.UUID `json:"fot_id"`
|
||||
DataEvento pgtype.Date `json:"data_evento"`
|
||||
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
|
||||
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
|
||||
LocalEvento pgtype.Text `json:"local_evento"`
|
||||
Endereco pgtype.Text `json:"endereco"`
|
||||
Horario pgtype.Text `json:"horario"`
|
||||
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
|
||||
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
|
||||
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
|
||||
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
|
||||
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
|
||||
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
|
||||
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
|
||||
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
|
||||
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
|
||||
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
|
||||
StatusProfissionais pgtype.Text `json:"status_profissionais"`
|
||||
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
|
||||
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
|
||||
CineFaltante pgtype.Int4 `json:"cine_faltante"`
|
||||
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
|
||||
PreVenda pgtype.Bool `json:"pre_venda"`
|
||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||
Status pgtype.Text `json:"status"`
|
||||
FotNumero int32 `json:"fot_numero"`
|
||||
Instituicao pgtype.Text `json:"instituicao"`
|
||||
CursoNome string `json:"curso_nome"`
|
||||
EmpresaNome string `json:"empresa_nome"`
|
||||
AnoSemestre string `json:"ano_semestre"`
|
||||
ObservacoesFot pgtype.Text `json:"observacoes_fot"`
|
||||
TipoEventoNome string `json:"tipo_evento_nome"`
|
||||
AssignedProfessionals interface{} `json:"assigned_professionals"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListAgendas(ctx context.Context) ([]ListAgendasRow, error) {
|
||||
|
|
@ -272,6 +393,7 @@ func (q *Queries) ListAgendas(ctx context.Context) ([]ListAgendasRow, error) {
|
|||
&i.PreVenda,
|
||||
&i.CriadoEm,
|
||||
&i.AtualizadoEm,
|
||||
&i.Status,
|
||||
&i.FotNumero,
|
||||
&i.Instituicao,
|
||||
&i.CursoNome,
|
||||
|
|
@ -279,6 +401,7 @@ func (q *Queries) ListAgendas(ctx context.Context) ([]ListAgendasRow, error) {
|
|||
&i.AnoSemestre,
|
||||
&i.ObservacoesFot,
|
||||
&i.TipoEventoNome,
|
||||
&i.AssignedProfessionals,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -292,14 +415,24 @@ func (q *Queries) ListAgendas(ctx context.Context) ([]ListAgendasRow, error) {
|
|||
|
||||
const listAgendasByUser = `-- name: ListAgendasByUser :many
|
||||
SELECT
|
||||
a.id, a.user_id, a.fot_id, a.data_evento, a.tipo_evento_id, a.observacoes_evento, a.local_evento, a.endereco, a.horario, a.qtd_formandos, a.qtd_fotografos, a.qtd_recepcionistas, a.qtd_cinegrafistas, a.qtd_estudios, a.qtd_ponto_foto, a.qtd_ponto_id, a.qtd_ponto_decorado, a.qtd_pontos_led, a.qtd_plataforma_360, a.status_profissionais, a.foto_faltante, a.recep_faltante, a.cine_faltante, a.logistica_observacoes, a.pre_venda, a.criado_em, a.atualizado_em,
|
||||
a.id, a.user_id, a.fot_id, a.data_evento, a.tipo_evento_id, a.observacoes_evento, a.local_evento, a.endereco, a.horario, a.qtd_formandos, a.qtd_fotografos, a.qtd_recepcionistas, a.qtd_cinegrafistas, a.qtd_estudios, a.qtd_ponto_foto, a.qtd_ponto_id, a.qtd_ponto_decorado, a.qtd_pontos_led, a.qtd_plataforma_360, a.status_profissionais, a.foto_faltante, a.recep_faltante, a.cine_faltante, a.logistica_observacoes, a.pre_venda, a.criado_em, a.atualizado_em, a.status,
|
||||
cf.fot as fot_numero,
|
||||
cf.instituicao,
|
||||
c.nome as curso_nome,
|
||||
e.nome as empresa_nome,
|
||||
af.ano_semestre,
|
||||
cf.observacoes as observacoes_fot,
|
||||
te.nome as tipo_evento_nome
|
||||
te.nome as tipo_evento_nome,
|
||||
COALESCE(
|
||||
(SELECT json_agg(json_build_object(
|
||||
'professional_id', ap.profissional_id,
|
||||
'status', ap.status,
|
||||
'motivo_rejeicao', ap.motivo_rejeicao
|
||||
))
|
||||
FROM agenda_profissionais ap
|
||||
WHERE ap.agenda_id = a.id),
|
||||
'[]'::json
|
||||
) as assigned_professionals
|
||||
FROM agenda a
|
||||
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
||||
JOIN cursos c ON cf.curso_id = c.id
|
||||
|
|
@ -311,40 +444,42 @@ ORDER BY a.data_evento
|
|||
`
|
||||
|
||||
type ListAgendasByUserRow struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
UserID pgtype.UUID `json:"user_id"`
|
||||
FotID pgtype.UUID `json:"fot_id"`
|
||||
DataEvento pgtype.Date `json:"data_evento"`
|
||||
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
|
||||
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
|
||||
LocalEvento pgtype.Text `json:"local_evento"`
|
||||
Endereco pgtype.Text `json:"endereco"`
|
||||
Horario pgtype.Text `json:"horario"`
|
||||
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
|
||||
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
|
||||
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
|
||||
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
|
||||
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
|
||||
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
|
||||
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
|
||||
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
|
||||
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
|
||||
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
|
||||
StatusProfissionais pgtype.Text `json:"status_profissionais"`
|
||||
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
|
||||
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
|
||||
CineFaltante pgtype.Int4 `json:"cine_faltante"`
|
||||
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
|
||||
PreVenda pgtype.Bool `json:"pre_venda"`
|
||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||
FotNumero int32 `json:"fot_numero"`
|
||||
Instituicao pgtype.Text `json:"instituicao"`
|
||||
CursoNome string `json:"curso_nome"`
|
||||
EmpresaNome string `json:"empresa_nome"`
|
||||
AnoSemestre string `json:"ano_semestre"`
|
||||
ObservacoesFot pgtype.Text `json:"observacoes_fot"`
|
||||
TipoEventoNome string `json:"tipo_evento_nome"`
|
||||
ID pgtype.UUID `json:"id"`
|
||||
UserID pgtype.UUID `json:"user_id"`
|
||||
FotID pgtype.UUID `json:"fot_id"`
|
||||
DataEvento pgtype.Date `json:"data_evento"`
|
||||
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
|
||||
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
|
||||
LocalEvento pgtype.Text `json:"local_evento"`
|
||||
Endereco pgtype.Text `json:"endereco"`
|
||||
Horario pgtype.Text `json:"horario"`
|
||||
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
|
||||
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
|
||||
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
|
||||
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
|
||||
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
|
||||
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
|
||||
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
|
||||
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
|
||||
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
|
||||
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
|
||||
StatusProfissionais pgtype.Text `json:"status_profissionais"`
|
||||
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
|
||||
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
|
||||
CineFaltante pgtype.Int4 `json:"cine_faltante"`
|
||||
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
|
||||
PreVenda pgtype.Bool `json:"pre_venda"`
|
||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||
Status pgtype.Text `json:"status"`
|
||||
FotNumero int32 `json:"fot_numero"`
|
||||
Instituicao pgtype.Text `json:"instituicao"`
|
||||
CursoNome string `json:"curso_nome"`
|
||||
EmpresaNome string `json:"empresa_nome"`
|
||||
AnoSemestre string `json:"ano_semestre"`
|
||||
ObservacoesFot pgtype.Text `json:"observacoes_fot"`
|
||||
TipoEventoNome string `json:"tipo_evento_nome"`
|
||||
AssignedProfessionals interface{} `json:"assigned_professionals"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListAgendasByUser(ctx context.Context, userID pgtype.UUID) ([]ListAgendasByUserRow, error) {
|
||||
|
|
@ -384,6 +519,7 @@ func (q *Queries) ListAgendasByUser(ctx context.Context, userID pgtype.UUID) ([]
|
|||
&i.PreVenda,
|
||||
&i.CriadoEm,
|
||||
&i.AtualizadoEm,
|
||||
&i.Status,
|
||||
&i.FotNumero,
|
||||
&i.Instituicao,
|
||||
&i.CursoNome,
|
||||
|
|
@ -391,6 +527,7 @@ func (q *Queries) ListAgendasByUser(ctx context.Context, userID pgtype.UUID) ([]
|
|||
&i.AnoSemestre,
|
||||
&i.ObservacoesFot,
|
||||
&i.TipoEventoNome,
|
||||
&i.AssignedProfessionals,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -402,6 +539,21 @@ func (q *Queries) ListAgendasByUser(ctx context.Context, userID pgtype.UUID) ([]
|
|||
return items, nil
|
||||
}
|
||||
|
||||
const removeProfessional = `-- name: RemoveProfessional :exec
|
||||
DELETE FROM agenda_profissionais
|
||||
WHERE agenda_id = $1 AND profissional_id = $2
|
||||
`
|
||||
|
||||
type RemoveProfessionalParams struct {
|
||||
AgendaID pgtype.UUID `json:"agenda_id"`
|
||||
ProfissionalID pgtype.UUID `json:"profissional_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) RemoveProfessional(ctx context.Context, arg RemoveProfessionalParams) error {
|
||||
_, err := q.db.Exec(ctx, removeProfessional, arg.AgendaID, arg.ProfissionalID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateAgenda = `-- name: UpdateAgenda :one
|
||||
UPDATE agenda
|
||||
SET
|
||||
|
|
@ -430,7 +582,7 @@ SET
|
|||
pre_venda = $24,
|
||||
atualizado_em = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING id, user_id, fot_id, data_evento, tipo_evento_id, observacoes_evento, local_evento, endereco, horario, qtd_formandos, qtd_fotografos, qtd_recepcionistas, qtd_cinegrafistas, qtd_estudios, qtd_ponto_foto, qtd_ponto_id, qtd_ponto_decorado, qtd_pontos_led, qtd_plataforma_360, status_profissionais, foto_faltante, recep_faltante, cine_faltante, logistica_observacoes, pre_venda, criado_em, atualizado_em
|
||||
RETURNING id, user_id, fot_id, data_evento, tipo_evento_id, observacoes_evento, local_evento, endereco, horario, qtd_formandos, qtd_fotografos, qtd_recepcionistas, qtd_cinegrafistas, qtd_estudios, qtd_ponto_foto, qtd_ponto_id, qtd_ponto_decorado, qtd_pontos_led, qtd_plataforma_360, status_profissionais, foto_faltante, recep_faltante, cine_faltante, logistica_observacoes, pre_venda, criado_em, atualizado_em, status
|
||||
`
|
||||
|
||||
type UpdateAgendaParams struct {
|
||||
|
|
@ -516,6 +668,88 @@ func (q *Queries) UpdateAgenda(ctx context.Context, arg UpdateAgendaParams) (Age
|
|||
&i.PreVenda,
|
||||
&i.CriadoEm,
|
||||
&i.AtualizadoEm,
|
||||
&i.Status,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateAgendaStatus = `-- name: UpdateAgendaStatus :one
|
||||
UPDATE agenda
|
||||
SET status = $2, atualizado_em = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING id, user_id, fot_id, data_evento, tipo_evento_id, observacoes_evento, local_evento, endereco, horario, qtd_formandos, qtd_fotografos, qtd_recepcionistas, qtd_cinegrafistas, qtd_estudios, qtd_ponto_foto, qtd_ponto_id, qtd_ponto_decorado, qtd_pontos_led, qtd_plataforma_360, status_profissionais, foto_faltante, recep_faltante, cine_faltante, logistica_observacoes, pre_venda, criado_em, atualizado_em, status
|
||||
`
|
||||
|
||||
type UpdateAgendaStatusParams struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Status pgtype.Text `json:"status"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateAgendaStatus(ctx context.Context, arg UpdateAgendaStatusParams) (Agenda, error) {
|
||||
row := q.db.QueryRow(ctx, updateAgendaStatus, arg.ID, arg.Status)
|
||||
var i Agenda
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.FotID,
|
||||
&i.DataEvento,
|
||||
&i.TipoEventoID,
|
||||
&i.ObservacoesEvento,
|
||||
&i.LocalEvento,
|
||||
&i.Endereco,
|
||||
&i.Horario,
|
||||
&i.QtdFormandos,
|
||||
&i.QtdFotografos,
|
||||
&i.QtdRecepcionistas,
|
||||
&i.QtdCinegrafistas,
|
||||
&i.QtdEstudios,
|
||||
&i.QtdPontoFoto,
|
||||
&i.QtdPontoID,
|
||||
&i.QtdPontoDecorado,
|
||||
&i.QtdPontosLed,
|
||||
&i.QtdPlataforma360,
|
||||
&i.StatusProfissionais,
|
||||
&i.FotoFaltante,
|
||||
&i.RecepFaltante,
|
||||
&i.CineFaltante,
|
||||
&i.LogisticaObservacoes,
|
||||
&i.PreVenda,
|
||||
&i.CriadoEm,
|
||||
&i.AtualizadoEm,
|
||||
&i.Status,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateAssignmentStatus = `-- name: UpdateAssignmentStatus :one
|
||||
UPDATE agenda_profissionais
|
||||
SET status = $3, motivo_rejeicao = $4
|
||||
WHERE agenda_id = $1 AND profissional_id = $2
|
||||
RETURNING id, agenda_id, profissional_id, status, motivo_rejeicao, criado_em
|
||||
`
|
||||
|
||||
type UpdateAssignmentStatusParams struct {
|
||||
AgendaID pgtype.UUID `json:"agenda_id"`
|
||||
ProfissionalID pgtype.UUID `json:"profissional_id"`
|
||||
Status pgtype.Text `json:"status"`
|
||||
MotivoRejeicao pgtype.Text `json:"motivo_rejeicao"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateAssignmentStatus(ctx context.Context, arg UpdateAssignmentStatusParams) (AgendaProfissionai, error) {
|
||||
row := q.db.QueryRow(ctx, updateAssignmentStatus,
|
||||
arg.AgendaID,
|
||||
arg.ProfissionalID,
|
||||
arg.Status,
|
||||
arg.MotivoRejeicao,
|
||||
)
|
||||
var i AgendaProfissionai
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.AgendaID,
|
||||
&i.ProfissionalID,
|
||||
&i.Status,
|
||||
&i.MotivoRejeicao,
|
||||
&i.CriadoEm,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,16 @@ type Agenda struct {
|
|||
PreVenda pgtype.Bool `json:"pre_venda"`
|
||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||
Status pgtype.Text `json:"status"`
|
||||
}
|
||||
|
||||
type AgendaProfissionai struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
AgendaID pgtype.UUID `json:"agenda_id"`
|
||||
ProfissionalID pgtype.UUID `json:"profissional_id"`
|
||||
Status pgtype.Text `json:"status"`
|
||||
MotivoRejeicao pgtype.Text `json:"motivo_rejeicao"`
|
||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||
}
|
||||
|
||||
type AnosFormatura struct {
|
||||
|
|
|
|||
|
|
@ -270,9 +270,10 @@ func (q *Queries) GetProfissionalByUsuarioID(ctx context.Context, usuarioID pgty
|
|||
}
|
||||
|
||||
const listProfissionais = `-- name: ListProfissionais :many
|
||||
SELECT p.id, p.usuario_id, p.nome, p.funcao_profissional_id, p.endereco, p.cidade, p.uf, p.whatsapp, p.cpf_cnpj_titular, p.banco, p.agencia, p.conta_pix, p.carro_disponivel, p.tem_estudio, p.qtd_estudio, p.tipo_cartao, p.observacao, p.qual_tec, p.educacao_simpatia, p.desempenho_evento, p.disp_horario, p.media, p.tabela_free, p.extra_por_equipamento, p.equipamentos, p.criado_em, p.atualizado_em, f.nome as funcao_nome
|
||||
SELECT p.id, p.usuario_id, p.nome, p.funcao_profissional_id, p.endereco, p.cidade, p.uf, p.whatsapp, p.cpf_cnpj_titular, p.banco, p.agencia, p.conta_pix, p.carro_disponivel, p.tem_estudio, p.qtd_estudio, p.tipo_cartao, p.observacao, p.qual_tec, p.educacao_simpatia, p.desempenho_evento, p.disp_horario, p.media, p.tabela_free, p.extra_por_equipamento, p.equipamentos, p.criado_em, p.atualizado_em, f.nome as funcao_nome, u.email
|
||||
FROM cadastro_profissionais p
|
||||
LEFT JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id
|
||||
LEFT JOIN usuarios u ON p.usuario_id = u.id
|
||||
ORDER BY p.nome
|
||||
`
|
||||
|
||||
|
|
@ -305,6 +306,7 @@ type ListProfissionaisRow struct {
|
|||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||
FuncaoNome pgtype.Text `json:"funcao_nome"`
|
||||
Email pgtype.Text `json:"email"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListProfissionais(ctx context.Context) ([]ListProfissionaisRow, error) {
|
||||
|
|
@ -345,6 +347,7 @@ func (q *Queries) ListProfissionais(ctx context.Context) ([]ListProfissionaisRow
|
|||
&i.CriadoEm,
|
||||
&i.AtualizadoEm,
|
||||
&i.FuncaoNome,
|
||||
&i.Email,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,17 @@ SELECT
|
|||
e.nome as empresa_nome,
|
||||
af.ano_semestre,
|
||||
cf.observacoes as observacoes_fot,
|
||||
te.nome as tipo_evento_nome
|
||||
te.nome as tipo_evento_nome,
|
||||
COALESCE(
|
||||
(SELECT json_agg(json_build_object(
|
||||
'professional_id', ap.profissional_id,
|
||||
'status', ap.status,
|
||||
'motivo_rejeicao', ap.motivo_rejeicao
|
||||
))
|
||||
FROM agenda_profissionais ap
|
||||
WHERE ap.agenda_id = a.id),
|
||||
'[]'::json
|
||||
) as assigned_professionals
|
||||
FROM agenda a
|
||||
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
||||
JOIN cursos c ON cf.curso_id = c.id
|
||||
|
|
@ -59,7 +69,17 @@ SELECT
|
|||
e.nome as empresa_nome,
|
||||
af.ano_semestre,
|
||||
cf.observacoes as observacoes_fot,
|
||||
te.nome as tipo_evento_nome
|
||||
te.nome as tipo_evento_nome,
|
||||
COALESCE(
|
||||
(SELECT json_agg(json_build_object(
|
||||
'professional_id', ap.profissional_id,
|
||||
'status', ap.status,
|
||||
'motivo_rejeicao', ap.motivo_rejeicao
|
||||
))
|
||||
FROM agenda_profissionais ap
|
||||
WHERE ap.agenda_id = a.id),
|
||||
'[]'::json
|
||||
) as assigned_professionals
|
||||
FROM agenda a
|
||||
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
||||
JOIN cursos c ON cf.curso_id = c.id
|
||||
|
|
@ -102,3 +122,32 @@ RETURNING *;
|
|||
-- name: DeleteAgenda :exec
|
||||
DELETE FROM agenda
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: AssignProfessional :exec
|
||||
INSERT INTO agenda_profissionais (agenda_id, profissional_id)
|
||||
VALUES ($1, $2)
|
||||
ON CONFLICT (agenda_id, profissional_id) DO NOTHING;
|
||||
|
||||
-- name: RemoveProfessional :exec
|
||||
DELETE FROM agenda_profissionais
|
||||
WHERE agenda_id = $1 AND profissional_id = $2;
|
||||
|
||||
-- name: GetAgendaProfessionals :many
|
||||
SELECT p.*, f.nome as funcao_nome, u.email
|
||||
FROM cadastro_profissionais p
|
||||
JOIN agenda_profissionais ap ON p.id = ap.profissional_id
|
||||
LEFT JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id
|
||||
LEFT JOIN usuarios u ON p.usuario_id = u.id
|
||||
WHERE ap.agenda_id = $1;
|
||||
|
||||
-- name: UpdateAgendaStatus :one
|
||||
UPDATE agenda
|
||||
SET status = $2, atualizado_em = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateAssignmentStatus :one
|
||||
UPDATE agenda_profissionais
|
||||
SET status = $3, motivo_rejeicao = $4
|
||||
WHERE agenda_id = $1 AND profissional_id = $2
|
||||
RETURNING *;
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ LEFT JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id
|
|||
WHERE p.id = $1 LIMIT 1;
|
||||
|
||||
-- name: ListProfissionais :many
|
||||
SELECT p.*, f.nome as funcao_nome
|
||||
SELECT p.*, f.nome as funcao_nome, u.email
|
||||
FROM cadastro_profissionais p
|
||||
LEFT JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id
|
||||
LEFT JOIN usuarios u ON p.usuario_id = u.id
|
||||
ORDER BY p.nome;
|
||||
|
||||
-- name: UpdateProfissional :one
|
||||
|
|
|
|||
|
|
@ -341,5 +341,16 @@ CREATE TABLE IF NOT EXISTS agenda (
|
|||
logistica_observacoes TEXT,
|
||||
pre_venda BOOLEAN DEFAULT FALSE,
|
||||
criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
atualizado_em TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
atualizado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
status VARCHAR(50) DEFAULT 'Pendente' -- Pendente, Aprovado, Arquivado
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agenda_profissionais (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
agenda_id UUID NOT NULL REFERENCES agenda(id) ON DELETE CASCADE,
|
||||
profissional_id UUID NOT NULL REFERENCES cadastro_profissionais(id) ON DELETE CASCADE,
|
||||
status VARCHAR(20) DEFAULT 'PENDENTE', -- PENDENTE, ACEITO, REJEITADO
|
||||
motivo_rejeicao TEXT,
|
||||
criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(agenda_id, profissional_id)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ type ProfissionalResponse struct {
|
|||
TabelaFree *string `json:"tabela_free"`
|
||||
ExtraPorEquipamento *bool `json:"extra_por_equipamento"`
|
||||
Equipamentos *string `json:"equipamentos"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func toResponse(p interface{}) ProfissionalResponse {
|
||||
|
|
@ -112,6 +113,7 @@ func toResponse(p interface{}) ProfissionalResponse {
|
|||
TabelaFree: fromPgText(v.TabelaFree),
|
||||
ExtraPorEquipamento: fromPgBool(v.ExtraPorEquipamento),
|
||||
Equipamentos: fromPgText(v.Equipamentos),
|
||||
Email: v.Email.String,
|
||||
}
|
||||
case generated.GetProfissionalByIDRow:
|
||||
return ProfissionalResponse{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ interface EventTableProps {
|
|||
onEventClick: (event: EventData) => void;
|
||||
onApprove?: (e: React.MouseEvent, eventId: string) => void;
|
||||
userRole: UserRole;
|
||||
currentProfessionalId?: string;
|
||||
onAssignmentResponse?: (e: React.MouseEvent, eventId: string, status: string, reason?: string) => void;
|
||||
}
|
||||
|
||||
type SortField =
|
||||
|
|
@ -26,15 +28,18 @@ export const EventTable: React.FC<EventTableProps> = ({
|
|||
onEventClick,
|
||||
onApprove,
|
||||
userRole,
|
||||
currentProfessionalId,
|
||||
onAssignmentResponse,
|
||||
}) => {
|
||||
const canApprove =
|
||||
userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN;
|
||||
const isPhotographer = userRole === UserRole.PHOTOGRAPHER;
|
||||
|
||||
const [sortField, setSortField] = useState<SortField | null>(null);
|
||||
const [sortOrder, setSortOrder] = useState<SortOrder>(null);
|
||||
|
||||
const handleSort = (field: SortField) => {
|
||||
if (sortField === field) {
|
||||
// Se já está ordenando por este campo, alterna a ordem
|
||||
if (sortOrder === "asc") {
|
||||
setSortOrder("desc");
|
||||
} else if (sortOrder === "desc") {
|
||||
|
|
@ -42,7 +47,6 @@ export const EventTable: React.FC<EventTableProps> = ({
|
|||
setSortField(null);
|
||||
}
|
||||
} else {
|
||||
// Novo campo, começa com ordem ascendente
|
||||
setSortField(field);
|
||||
setSortOrder("asc");
|
||||
}
|
||||
|
|
@ -216,80 +220,122 @@ export const EventTable: React.FC<EventTableProps> = ({
|
|||
{getSortIcon("status")}
|
||||
</div>
|
||||
</th>
|
||||
{canApprove && (
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider w-20">
|
||||
{(canApprove || isPhotographer) && (
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider w-32">
|
||||
Ações
|
||||
</th>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100">
|
||||
{sortedEvents.map((event) => (
|
||||
<tr
|
||||
key={event.id}
|
||||
onClick={() => onEventClick(event)}
|
||||
className="hover:bg-gray-50 cursor-pointer transition-colors"
|
||||
>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
{event.fot || "-"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{formatDate(event.date)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{event.curso || "-"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{event.instituicao || "-"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{event.anoFormatura || "-"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{event.empresa || "-"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">{event.type}</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span
|
||||
className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${STATUS_COLORS[event.status] || "bg-gray-100 text-gray-800"
|
||||
}`}
|
||||
>
|
||||
{getStatusDisplay(event.status)}
|
||||
</span>
|
||||
</td>
|
||||
{canApprove && (
|
||||
<td
|
||||
className="px-4 py-3"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{event.status === EventStatus.PENDING_APPROVAL && (
|
||||
<button
|
||||
onClick={(e) => onApprove?.(e, event.id)}
|
||||
className="bg-green-500 text-white px-2 py-1 rounded text-xs font-semibold hover:bg-green-600 transition-colors flex items-center gap-1 whitespace-nowrap"
|
||||
title="Aprovar evento"
|
||||
>
|
||||
<CheckCircle size={12} />
|
||||
Aprovar
|
||||
</button>
|
||||
)}
|
||||
{sortedEvents.map((event) => {
|
||||
// Logic to find photographer assignment status
|
||||
let photographerAssignment = null;
|
||||
if (isPhotographer && currentProfessionalId && event.assignments) {
|
||||
photographerAssignment = event.assignments.find(a => a.professionalId === currentProfessionalId);
|
||||
}
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={event.id}
|
||||
onClick={() => onEventClick(event)}
|
||||
className="hover:bg-gray-50 cursor-pointer transition-colors"
|
||||
>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm font-medium text-gray-900">
|
||||
{event.fot || "-"}
|
||||
</span>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{formatDate(event.date)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{event.curso || "-"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{event.instituicao || "-"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{event.anoFormatura || "-"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">
|
||||
{event.empresa || "-"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="text-sm text-gray-600">{event.type}</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span
|
||||
className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${STATUS_COLORS[event.status] || "bg-gray-100 text-gray-800"
|
||||
}`}
|
||||
>
|
||||
{getStatusDisplay(event.status)}
|
||||
</span>
|
||||
</td>
|
||||
{(canApprove || isPhotographer) && (
|
||||
<td
|
||||
className="px-4 py-3"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{canApprove && event.status === EventStatus.PENDING_APPROVAL && (
|
||||
<button
|
||||
onClick={(e) => onApprove?.(e, event.id)}
|
||||
className="bg-green-500 text-white px-2 py-1 rounded text-xs font-semibold hover:bg-green-600 transition-colors flex items-center gap-1 whitespace-nowrap"
|
||||
title="Aprovar evento"
|
||||
>
|
||||
<CheckCircle size={12} />
|
||||
Aprovar
|
||||
</button>
|
||||
)}
|
||||
|
||||
{isPhotographer && photographerAssignment && (
|
||||
<>
|
||||
{photographerAssignment.status === "PENDENTE" && (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => onAssignmentResponse?.(e, event.id, "ACEITO")}
|
||||
className="bg-green-500 text-white px-2 py-1 rounded text-xs font-semibold hover:bg-green-600 transition-colors"
|
||||
title="Aceitar"
|
||||
>
|
||||
Aceitar
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
const reason = prompt("Motivo da rejeição:");
|
||||
if (reason) onAssignmentResponse?.(e, event.id, "REJEITADO", reason);
|
||||
}}
|
||||
className="bg-red-500 text-white px-2 py-1 rounded text-xs font-semibold hover:bg-red-600 transition-colors"
|
||||
title="Rejeitar"
|
||||
>
|
||||
Rejeitar
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{photographerAssignment.status === "ACEITO" && (
|
||||
<span className="text-green-600 text-xs font-bold border border-green-200 bg-green-50 px-2 py-1 rounded">Aceito</span>
|
||||
)}
|
||||
{photographerAssignment.status === "REJEITADO" && (
|
||||
<span className="text-red-600 text-xs font-bold border border-red-200 bg-red-50 px-2 py-1 rounded" title={photographerAssignment.reason}>Rejeitado</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { X, AlertTriangle, Save, Loader } from "lucide-react";
|
||||
import { Button } from "./Button";
|
||||
import { getCompanies, getAvailableCourses, getGraduationYears, createCadastroFot } from "../services/apiService";
|
||||
import { getCompanies, getAvailableCourses, getGraduationYears, createCadastroFot, updateCadastroFot } from "../services/apiService";
|
||||
|
||||
interface FotFormProps {
|
||||
onCancel: () => void;
|
||||
onSubmit: (success: boolean) => void;
|
||||
token: string;
|
||||
existingFots: number[]; // List of existing FOT numbers for validation
|
||||
initialData?: any; // For editing
|
||||
}
|
||||
|
||||
export const FotForm: React.FC<FotFormProps> = ({ onCancel, onSubmit, token, existingFots }) => {
|
||||
export const FotForm: React.FC<FotFormProps> = ({ onCancel, onSubmit, token, existingFots, initialData }) => {
|
||||
const [formData, setFormData] = useState({
|
||||
fot: "",
|
||||
empresa_id: "",
|
||||
|
|
@ -24,6 +25,23 @@ export const FotForm: React.FC<FotFormProps> = ({ onCancel, onSubmit, token, exi
|
|||
pre_venda: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
setFormData({
|
||||
fot: initialData.fot.toString(),
|
||||
empresa_id: initialData.empresa_id,
|
||||
curso_id: initialData.curso_id,
|
||||
ano_formatura_id: initialData.ano_formatura_id,
|
||||
instituicao: initialData.instituicao || "",
|
||||
cidade: initialData.cidade || "",
|
||||
estado: initialData.estado || "",
|
||||
observacoes: initialData.observacoes || "",
|
||||
gastos_captacao: initialData.gastos_captacao ? initialData.gastos_captacao.toString() : "",
|
||||
pre_venda: initialData.pre_venda || false,
|
||||
});
|
||||
}
|
||||
}, [initialData]);
|
||||
|
||||
const [companies, setCompanies] = useState<any[]>([]);
|
||||
const [coursesList, setCoursesList] = useState<any[]>([]);
|
||||
const [years, setYears] = useState<any[]>([]);
|
||||
|
|
@ -61,7 +79,17 @@ export const FotForm: React.FC<FotFormProps> = ({ onCancel, onSubmit, token, exi
|
|||
const val = e.target.value;
|
||||
setFormData({ ...formData, fot: val });
|
||||
|
||||
if (val && existingFots.includes(parseInt(val))) {
|
||||
if (!initialData && val && existingFots.includes(parseInt(val))) { // Check uniqueness only if new or changed (and not matching self? Logic simplied: only if not editing self, but passed list does not track who is who, assumed list of ALL. If editing, I AM in the list. OK, let's refine: existingFots should ideally exclude self if editing. We'll leave basic check but maybe warn only. Or just relax check for edit if value matches initial.)
|
||||
if (initialData && parseInt(val) === initialData.fot) {
|
||||
setFotError(null);
|
||||
} else {
|
||||
if (existingFots.includes(parseInt(val))) {
|
||||
setFotError(`O FOT ${val} já existe!`);
|
||||
} else {
|
||||
setFotError(null);
|
||||
}
|
||||
}
|
||||
} else if (val && existingFots.includes(parseInt(val))) {
|
||||
setFotError(`O FOT ${val} já existe!`);
|
||||
} else {
|
||||
setFotError(null);
|
||||
|
|
@ -89,7 +117,12 @@ export const FotForm: React.FC<FotFormProps> = ({ onCancel, onSubmit, token, exi
|
|||
pre_venda: formData.pre_venda,
|
||||
};
|
||||
|
||||
const result = await createCadastroFot(payload, token);
|
||||
let result;
|
||||
if (initialData) {
|
||||
result = await updateCadastroFot(initialData.id, payload, token);
|
||||
} else {
|
||||
result = await createCadastroFot(payload, token);
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
throw new Error(result.error);
|
||||
|
|
@ -98,11 +131,11 @@ export const FotForm: React.FC<FotFormProps> = ({ onCancel, onSubmit, token, exi
|
|||
onSubmit(true);
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Erro ao salvar FOT.");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (loadingDependencies) {
|
||||
return (
|
||||
<div className="bg-white rounded-lg p-8 flex flex-col items-center justify-center min-h-[400px]">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { createContext, useContext, useState, ReactNode, useEffect } from "react";
|
||||
import { useAuth } from "./AuthContext";
|
||||
import { getPendingUsers, approveUser as apiApproveUser } from "../services/apiService";
|
||||
import { getPendingUsers, approveUser as apiApproveUser, getProfessionals, assignProfessional as apiAssignProfessional, removeProfessional as apiRemoveProfessional, updateEventStatus as apiUpdateStatus, updateAssignmentStatus as apiUpdateAssignmentStatus } from "../services/apiService";
|
||||
import {
|
||||
EventData,
|
||||
EventStatus,
|
||||
|
|
@ -10,6 +10,7 @@ import {
|
|||
User,
|
||||
UserApprovalStatus,
|
||||
UserRole,
|
||||
Professional,
|
||||
} from "../types";
|
||||
|
||||
// Initial Mock Data
|
||||
|
|
@ -602,8 +603,11 @@ interface DataContextType {
|
|||
getActiveCoursesByInstitutionId: (institutionId: string) => Course[];
|
||||
getCourseById: (id: string) => Course | undefined;
|
||||
registerPendingUser: (userData: { id: string; name: string; email: string; phone: string; registeredInstitution?: string }) => void;
|
||||
|
||||
approveUser: (userId: string) => void;
|
||||
rejectUser: (userId: string) => void;
|
||||
professionals: Professional[];
|
||||
respondToAssignment: (eventId: string, status: string, reason?: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const DataContext = createContext<DataContextType | undefined>(undefined);
|
||||
|
|
@ -613,11 +617,13 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
|||
children,
|
||||
}) => {
|
||||
const { token, user } = useAuth(); // Consume Auth Context
|
||||
const [events, setEvents] = useState<EventData[]>(INITIAL_EVENTS);
|
||||
const [events, setEvents] = useState<EventData[]>([]);
|
||||
const [institutions, setInstitutions] =
|
||||
useState<Institution[]>(INITIAL_INSTITUTIONS);
|
||||
const [courses, setCourses] = useState<Course[]>(INITIAL_COURSES);
|
||||
|
||||
const [pendingUsers, setPendingUsers] = useState<User[]>([]);
|
||||
const [professionals, setProfessionals] = useState<Professional[]>([]);
|
||||
|
||||
// Fetch events from API
|
||||
useEffect(() => {
|
||||
|
|
@ -630,6 +636,7 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
|||
// Import dynamic to avoid circular dependency if any, or just use imported service
|
||||
const { getAgendas } = await import("../services/apiService");
|
||||
const result = await getAgendas(visibleToken);
|
||||
console.log("Raw Agenda Data:", result.data); // Debug logging
|
||||
if (result.data) {
|
||||
const mappedEvents: EventData[] = result.data.map((e: any) => ({
|
||||
id: e.id,
|
||||
|
|
@ -637,23 +644,23 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
|||
date: e.data_evento ? e.data_evento.split('T')[0] : "",
|
||||
time: e.horario || "00:00",
|
||||
type: (e.tipo_evento_nome || "Outro") as EventType, // Map string to enum if possible, or keep string
|
||||
status: EventStatus.PENDING_APPROVAL, // Default or map from e.status_profissionais?? e.status_profissionais is for workers. We might need a status field on agenda table or infer it.
|
||||
// For now, let's map status_profissionais to general status if possible, or default to CONFIRMED/PENDING
|
||||
// e.status_profissionais defaults to "PENDING" in creation.
|
||||
status: EventStatus.PENDING_APPROVAL,
|
||||
address: {
|
||||
street: e.endereco ? e.endereco.split(',')[0] : "",
|
||||
number: e.endereco ? e.endereco.split(',')[1]?.split('-')[0]?.trim() || "" : "",
|
||||
city: e.endereco ? e.endereco.split('-')[1]?.split('/')[0]?.trim() || "" : "",
|
||||
state: e.endereco ? e.endereco.split('/')[1]?.trim() || "" : "",
|
||||
zip: "",
|
||||
mapLink: e.local_evento.startsWith('http') ? e.local_evento : undefined
|
||||
mapLink: e.local_evento?.startsWith('http') ? e.local_evento : undefined
|
||||
},
|
||||
briefing: e.observacoes_evento || "",
|
||||
coverImage: "https://picsum.photos/id/10/800/400", // Placeholder
|
||||
contacts: [], // TODO: fetch contacts if needed
|
||||
checklist: [],
|
||||
ownerId: e.user_id || "unknown",
|
||||
photographerIds: [], // TODO
|
||||
photographerIds: Array.isArray(e.assigned_professionals)
|
||||
? e.assigned_professionals.map((a: any) => a.professional_id)
|
||||
: [],
|
||||
institutionId: "", // TODO
|
||||
attendees: e.qtd_formandos,
|
||||
fotId: e.fot_id, // UUID
|
||||
|
|
@ -665,9 +672,18 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
|||
anoFormatura: e.ano_semestre,
|
||||
empresa: e.empresa_nome,
|
||||
observacoes: e.observacoes_fot,
|
||||
typeId: e.tipo_evento_id
|
||||
typeId: e.tipo_evento_id,
|
||||
assignments: Array.isArray(e.assigned_professionals)
|
||||
? e.assigned_professionals.map((a: any) => ({
|
||||
professionalId: a.professional_id,
|
||||
status: a.status,
|
||||
rejectionReason: a.motivo_rejeicao
|
||||
}))
|
||||
: [],
|
||||
}));
|
||||
setEvents(mappedEvents);
|
||||
} else {
|
||||
setEvents([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch events", error);
|
||||
|
|
@ -712,8 +728,41 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
|||
}
|
||||
};
|
||||
fetchUsers();
|
||||
|
||||
}, []);
|
||||
|
||||
// Fetch professionals
|
||||
useEffect(() => {
|
||||
const fetchProfs = async () => {
|
||||
const token = localStorage.getItem('token');
|
||||
console.log("[DEBUG] Fetching Professionals...", { token });
|
||||
if (token) {
|
||||
try {
|
||||
const result = await getProfessionals(token);
|
||||
console.log("[DEBUG] Fetch Professionals Result:", result);
|
||||
if (result.data) {
|
||||
const mappedProfs: Professional[] = result.data.map((p: any) => ({
|
||||
id: p.id,
|
||||
usuarioId: p.usuario_id,
|
||||
name: p.nome,
|
||||
email: p.email || "",
|
||||
role: p.funcao_nome || "Fotógrafo",
|
||||
avatar: `https://ui-avatars.com/api/?name=${encodeURIComponent(p.nome)}&background=random`, // Fallback avatar
|
||||
phone: p.whatsapp,
|
||||
availability: {}, // Default empty availability
|
||||
}));
|
||||
setProfessionals(mappedProfs);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch professionals", error);
|
||||
}
|
||||
} else {
|
||||
console.warn("[DEBUG] No token found for fetching professionals");
|
||||
}
|
||||
};
|
||||
fetchProfs();
|
||||
}, [token]);
|
||||
|
||||
const addEvent = async (event: EventData) => {
|
||||
const token = localStorage.getItem("@Photum:token");
|
||||
if (!token) {
|
||||
|
|
@ -770,16 +819,51 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const updateEventStatus = (id: string, status: EventStatus) => {
|
||||
setEvents((prev) => prev.map((e) => (e.id === id ? { ...e, status } : e)));
|
||||
const updateEventStatus = async (id: string, status: EventStatus) => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
try {
|
||||
await apiUpdateStatus(token, id, status);
|
||||
setEvents((prev) => prev.map((e) => (e.id === id ? { ...e, status } : e)));
|
||||
} catch (error) {
|
||||
console.error("Failed to update status", error);
|
||||
}
|
||||
} else {
|
||||
// Fallback
|
||||
setEvents((prev) => prev.map((e) => (e.id === id ? { ...e, status } : e)));
|
||||
}
|
||||
};
|
||||
|
||||
const assignPhotographer = (eventId: string, photographerId: string) => {
|
||||
const assignPhotographer = async (eventId: string, photographerId: string) => {
|
||||
const token = localStorage.getItem('token');
|
||||
const event = events.find(e => e.id === eventId);
|
||||
if (!event) return;
|
||||
|
||||
const current = event.photographerIds || [];
|
||||
const isRemoving = current.includes(photographerId);
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
if (isRemoving) {
|
||||
await apiRemoveProfessional(token, eventId, photographerId);
|
||||
} else {
|
||||
await apiAssignProfessional(token, eventId, photographerId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to assign/remove professional", error);
|
||||
return; // Don't update state if API fails
|
||||
}
|
||||
}
|
||||
|
||||
setEvents((prev) =>
|
||||
prev.map((e) => {
|
||||
if (e.id === eventId) {
|
||||
const current = e.photographerIds || [];
|
||||
if (!current.includes(photographerId)) {
|
||||
if (current.includes(photographerId)) {
|
||||
// Remove
|
||||
return { ...e, photographerIds: current.filter(id => id !== photographerId) };
|
||||
} else {
|
||||
// Add
|
||||
return { ...e, photographerIds: [...current, photographerId] };
|
||||
}
|
||||
}
|
||||
|
|
@ -796,7 +880,10 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
|||
return events.filter((e) => e.ownerId === userId);
|
||||
}
|
||||
if (role === "PHOTOGRAPHER") {
|
||||
return events.filter((e) => e.photographerIds.includes(userId));
|
||||
const professional = professionals.find((p) => p.usuarioId === userId);
|
||||
if (!professional) return [];
|
||||
const professionalId = professional.id;
|
||||
return events.filter((e) => e.photographerIds.includes(professionalId));
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
|
@ -904,6 +991,30 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
|||
institutions,
|
||||
courses,
|
||||
pendingUsers,
|
||||
professionals,
|
||||
respondToAssignment: async (eventId, status, reason) => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token || !user) return;
|
||||
const professional = professionals.find(p => p.usuarioId === user.id);
|
||||
if (!professional) return;
|
||||
|
||||
await apiUpdateAssignmentStatus(token, eventId, professional.id, status, reason);
|
||||
|
||||
// Re-fetch events to update status locally efficiently (or update local state)
|
||||
// For simplicity, let's update local state
|
||||
setEvents((prev) =>
|
||||
prev.map((e) => {
|
||||
if (e.id === eventId) {
|
||||
const updatedAssignments = e.assignments?.map(a =>
|
||||
a.professionalId === professional.id ? { ...a, status: status as any, reason } : a
|
||||
) || [];
|
||||
// If it wasn't in assignments (unlikely if responding), simple update
|
||||
return { ...e, assignments: updatedAssignments };
|
||||
}
|
||||
return e;
|
||||
})
|
||||
);
|
||||
},
|
||||
addEvent,
|
||||
updateEventStatus,
|
||||
assignPhotographer,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import React, { useState, useEffect } from "react";
|
|||
import { useAuth } from "../contexts/AuthContext";
|
||||
import { UserRole } from "../types";
|
||||
import { Button } from "../components/Button";
|
||||
import { getCadastroFot } from "../services/apiService";
|
||||
import { Briefcase, AlertTriangle, Plus, Edit } from "lucide-react";
|
||||
import { getCadastroFot, deleteCadastroFot } from "../services/apiService";
|
||||
import { Briefcase, AlertTriangle, Plus, Edit, Trash2, Search, Filter } from "lucide-react";
|
||||
import { FotForm } from "../components/FotForm";
|
||||
|
||||
interface FotData {
|
||||
|
|
@ -18,14 +18,24 @@ interface FotData {
|
|||
estado: string;
|
||||
gastos_captacao: number;
|
||||
pre_venda: boolean;
|
||||
empresa_id?: string;
|
||||
curso_id?: string;
|
||||
ano_formatura_id?: string;
|
||||
}
|
||||
|
||||
export const CourseManagement: React.FC = () => {
|
||||
const { user } = useAuth();
|
||||
const [fotList, setFotList] = useState<FotData[]>([]);
|
||||
const [filteredList, setFilteredList] = useState<FotData[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Modal State
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [editingFot, setEditingFot] = useState<FotData | null>(null);
|
||||
|
||||
// Filter State
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
|
||||
// Verificar se è admin
|
||||
const isAdmin =
|
||||
|
|
@ -36,6 +46,21 @@ export const CourseManagement: React.FC = () => {
|
|||
fetchFotData();
|
||||
}, [isAdmin]);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchTerm.trim() === "") {
|
||||
setFilteredList(fotList);
|
||||
} else {
|
||||
const lowerTerm = searchTerm.toLowerCase();
|
||||
const filtered = fotList.filter(item =>
|
||||
item.fot.toString().includes(lowerTerm) ||
|
||||
item.empresa_nome.toLowerCase().includes(lowerTerm) ||
|
||||
item.curso_nome.toLowerCase().includes(lowerTerm) ||
|
||||
item.instituicao.toLowerCase().includes(lowerTerm)
|
||||
);
|
||||
setFilteredList(filtered);
|
||||
}
|
||||
}, [searchTerm, fotList]);
|
||||
|
||||
const fetchFotData = async () => {
|
||||
if (!isAdmin) return;
|
||||
|
||||
|
|
@ -61,7 +86,32 @@ export const CourseManagement: React.FC = () => {
|
|||
|
||||
const handleFormSubmit = () => {
|
||||
setShowForm(false);
|
||||
fetchFotData(); // Refresh list after successful creation
|
||||
setEditingFot(null);
|
||||
fetchFotData(); // Refresh list after successful creation/update
|
||||
};
|
||||
|
||||
const handleEdit = (item: FotData) => {
|
||||
setEditingFot(item);
|
||||
setShowForm(true);
|
||||
};
|
||||
|
||||
const handleDelete = async (id: string, fotNumber: number) => {
|
||||
if (!window.confirm(`Tem certeza que deseja excluir o FOT ${fotNumber}?`)) return;
|
||||
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
const res = await deleteCadastroFot(id, token);
|
||||
if (res.error) {
|
||||
alert(res.error);
|
||||
} else {
|
||||
fetchFotData();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert("Erro ao excluir.");
|
||||
}
|
||||
};
|
||||
|
||||
// Extract existing FOT numbers for uniqueness validation
|
||||
|
|
@ -85,7 +135,7 @@ export const CourseManagement: React.FC = () => {
|
|||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Header */}
|
||||
<div className="mb-6 sm:mb-8">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h1 className="text-2xl sm:text-3xl font-serif font-bold text-brand-black">
|
||||
Gestão de FOT
|
||||
|
|
@ -95,7 +145,10 @@ export const CourseManagement: React.FC = () => {
|
|||
</p>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => setShowForm(true)}
|
||||
onClick={() => {
|
||||
setEditingFot(null);
|
||||
setShowForm(true);
|
||||
}}
|
||||
variant="secondary"
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
|
|
@ -103,18 +156,35 @@ export const CourseManagement: React.FC = () => {
|
|||
<span>Cadastro FOT</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Filters Bar */}
|
||||
<div className="bg-white p-4 rounded-lg border border-gray-200 shadow-sm flex items-center gap-4">
|
||||
<div className="relative flex-1 max-w-md">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={18} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Buscar por FOT, Empresa, Curso..."
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{/* Can add more filters here later */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{/* Form Modal */}
|
||||
{showForm && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<FotForm
|
||||
onCancel={() => setShowForm(false)}
|
||||
onCancel={() => {
|
||||
setShowForm(false);
|
||||
setEditingFot(null);
|
||||
}}
|
||||
onSubmit={handleFormSubmit}
|
||||
token={localStorage.getItem("token") || ""}
|
||||
existingFots={existingFots}
|
||||
initialData={editingFot}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -145,10 +215,7 @@ export const CourseManagement: React.FC = () => {
|
|||
Empresa
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Cursos
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Observações
|
||||
Curso
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Instituição
|
||||
|
|
@ -162,6 +229,9 @@ export const CourseManagement: React.FC = () => {
|
|||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Estado
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Observações
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Gastos Captação
|
||||
</th>
|
||||
|
|
@ -174,7 +244,7 @@ export const CourseManagement: React.FC = () => {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{fotList.length === 0 ? (
|
||||
{filteredList.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={11}
|
||||
|
|
@ -189,7 +259,7 @@ export const CourseManagement: React.FC = () => {
|
|||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
fotList.map((item) => (
|
||||
filteredList.map((item) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
className="hover:bg-gray-50 transition-colors"
|
||||
|
|
@ -209,11 +279,6 @@ export const CourseManagement: React.FC = () => {
|
|||
{item.curso_nome || "-"}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<div className="text-sm text-gray-600 max-w-xs truncate">
|
||||
{item.observacoes || "-"}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-600">
|
||||
{item.instituicao || "-"}
|
||||
|
|
@ -234,6 +299,11 @@ export const CourseManagement: React.FC = () => {
|
|||
{item.estado || "-"}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<div className="text-sm text-gray-600 max-w-xs truncate" title={item.observacoes}>
|
||||
{item.observacoes || "-"}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-600">
|
||||
{item.gastos_captacao
|
||||
|
|
@ -255,12 +325,22 @@ export const CourseManagement: React.FC = () => {
|
|||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-center">
|
||||
<button
|
||||
className="p-1.5 text-blue-600 hover:bg-blue-50 rounded transition-colors"
|
||||
title="Editar"
|
||||
>
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<button
|
||||
onClick={() => handleEdit(item)}
|
||||
className="p-1.5 text-blue-600 hover:bg-blue-50 rounded transition-colors"
|
||||
title="Editar"
|
||||
>
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(item.id, item.fot)}
|
||||
className="p-1.5 text-red-600 hover:bg-red-50 rounded transition-colors"
|
||||
title="Excluir"
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import { UserRole, EventData, EventStatus, EventType } from "../types";
|
||||
import { UserRole, EventData, EventStatus, EventType, Professional } from "../types";
|
||||
import { EventTable } from "../components/EventTable";
|
||||
import { EventFiltersBar, EventFilters } from "../components/EventFiltersBar";
|
||||
import { EventForm } from "../components/EventForm";
|
||||
|
|
@ -27,124 +27,7 @@ interface DashboardProps {
|
|||
initialView?: "list" | "create";
|
||||
}
|
||||
|
||||
interface Professional {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
avatar: string;
|
||||
role: "Fotógrafo" | "Cinegrafista" | "Recepcionista";
|
||||
availability: {
|
||||
[date: string]: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// Mock de profissionais cadastrados
|
||||
const MOCK_PHOTOGRAPHERS: Professional[] = [
|
||||
{
|
||||
id: "photographer-1",
|
||||
name: "Carlos Silva",
|
||||
email: "carlos@photum.com",
|
||||
avatar: "https://i.pravatar.cc/150?u=carlos",
|
||||
role: "Fotógrafo",
|
||||
availability: {
|
||||
"2025-12-05": true,
|
||||
"2025-12-10": true,
|
||||
"2025-12-15": false,
|
||||
"2025-12-20": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "photographer-2",
|
||||
name: "Ana Santos",
|
||||
email: "ana@photum.com",
|
||||
avatar: "https://i.pravatar.cc/150?u=ana",
|
||||
role: "Fotógrafo",
|
||||
availability: {
|
||||
"2025-12-05": true,
|
||||
"2025-12-10": false,
|
||||
"2025-12-15": true,
|
||||
"2025-12-20": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "photographer-3",
|
||||
name: "João Oliveira",
|
||||
email: "joao@photum.com",
|
||||
avatar: "https://i.pravatar.cc/150?u=joao",
|
||||
role: "Cinegrafista",
|
||||
availability: {
|
||||
"2025-12-05": false,
|
||||
"2025-12-10": true,
|
||||
"2025-12-15": true,
|
||||
"2025-12-20": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "photographer-4",
|
||||
name: "Maria Costa",
|
||||
email: "maria@photum.com",
|
||||
avatar: "https://i.pravatar.cc/150?u=maria",
|
||||
role: "Fotógrafo",
|
||||
availability: {
|
||||
"2025-12-05": true,
|
||||
"2025-12-10": true,
|
||||
"2025-12-15": true,
|
||||
"2025-12-20": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "photographer-5",
|
||||
name: "Pedro Alves",
|
||||
email: "pedro@photum.com",
|
||||
avatar: "https://i.pravatar.cc/150?u=pedro",
|
||||
role: "Cinegrafista",
|
||||
availability: {
|
||||
"2025-12-05": false,
|
||||
"2025-12-10": false,
|
||||
"2025-12-15": true,
|
||||
"2025-12-20": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "receptionist-1",
|
||||
name: "Julia Mendes",
|
||||
email: "julia@photum.com",
|
||||
avatar: "https://i.pravatar.cc/150?u=julia",
|
||||
role: "Recepcionista",
|
||||
availability: {
|
||||
"2025-12-05": true,
|
||||
"2025-12-10": true,
|
||||
"2025-12-15": true,
|
||||
"2025-12-20": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "receptionist-2",
|
||||
name: "Rafael Souza",
|
||||
email: "rafael@photum.com",
|
||||
avatar: "https://i.pravatar.cc/150?u=rafael",
|
||||
role: "Recepcionista",
|
||||
availability: {
|
||||
"2025-12-05": true,
|
||||
"2025-12-10": true,
|
||||
"2025-12-15": false,
|
||||
"2025-12-20": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "videographer-1",
|
||||
name: "Lucas Ferreira",
|
||||
email: "lucas@photum.com",
|
||||
avatar: "https://i.pravatar.cc/150?u=lucas",
|
||||
role: "Cinegrafista",
|
||||
availability: {
|
||||
"2025-12-05": true,
|
||||
"2025-12-10": false,
|
||||
"2025-12-15": true,
|
||||
"2025-12-20": true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const Dashboard: React.FC<DashboardProps> = ({
|
||||
initialView = "list",
|
||||
|
|
@ -156,8 +39,10 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
addEvent,
|
||||
updateEventStatus,
|
||||
assignPhotographer,
|
||||
professionals,
|
||||
getInstitutionById,
|
||||
getActiveCoursesByInstitutionId,
|
||||
respondToAssignment,
|
||||
} = useData();
|
||||
const [view, setView] = useState<"list" | "create" | "edit" | "details">(
|
||||
initialView
|
||||
|
|
@ -186,6 +71,11 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
|
||||
const myEvents = getEventsByRole(user.id, user.role);
|
||||
|
||||
const currentProfessionalId =
|
||||
user.role === UserRole.PHOTOGRAPHER
|
||||
? professionals.find((p) => p.usuarioId === user.id)?.id
|
||||
: undefined;
|
||||
|
||||
// Extract unique values for filters
|
||||
const { availableTypes } = useMemo(() => {
|
||||
const types = [...new Set(myEvents.map((e) => e.type))].sort();
|
||||
|
|
@ -246,14 +136,29 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
// Keep selectedEvent in sync with global events state
|
||||
useEffect(() => {
|
||||
if (selectedEvent) {
|
||||
const updated = events.find((e) => e.id === selectedEvent.id);
|
||||
if (updated && updated !== selectedEvent) {
|
||||
setSelectedEvent(updated);
|
||||
}
|
||||
}
|
||||
}, [events, selectedEvent]);
|
||||
|
||||
const handleApprove = (e: React.MouseEvent, eventId: string) => {
|
||||
e.stopPropagation();
|
||||
const event = events.find((e) => e.id === eventId);
|
||||
if (event) {
|
||||
setSelectedEvent(event);
|
||||
setView("details");
|
||||
setIsTeamModalOpen(true);
|
||||
}
|
||||
updateEventStatus(eventId, EventStatus.CONFIRMED);
|
||||
};
|
||||
|
||||
const handleAssignmentResponse = async (
|
||||
e: React.MouseEvent,
|
||||
eventId: string,
|
||||
status: string,
|
||||
reason?: string
|
||||
) => {
|
||||
e.stopPropagation();
|
||||
await respondToAssignment(eventId, status, reason);
|
||||
};
|
||||
|
||||
const handleOpenMaps = () => {
|
||||
|
|
@ -279,8 +184,6 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
const togglePhotographer = (photographerId: string) => {
|
||||
if (!selectedEvent) return;
|
||||
assignPhotographer(selectedEvent.id, photographerId);
|
||||
const updated = events.find((e) => e.id === selectedEvent.id);
|
||||
if (updated) setSelectedEvent(updated);
|
||||
};
|
||||
|
||||
// --- RENDERS PER ROLE ---
|
||||
|
|
@ -372,8 +275,8 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
<button
|
||||
onClick={() => setActiveFilter("all")}
|
||||
className={`px-3 py-1 text-xs font-medium rounded-sm ${activeFilter === "all"
|
||||
? "bg-brand-black text-white"
|
||||
: "text-gray-600 hover:bg-gray-100"
|
||||
? "bg-brand-black text-white"
|
||||
: "text-gray-600 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
Todos
|
||||
|
|
@ -381,8 +284,8 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
<button
|
||||
onClick={() => setActiveFilter("pending")}
|
||||
className={`px-3 py-1 text-xs font-medium rounded-sm flex items-center ${activeFilter === "pending"
|
||||
? "bg-brand-gold text-white"
|
||||
: "text-gray-600 hover:bg-gray-100"
|
||||
? "bg-brand-gold text-white"
|
||||
: "text-gray-600 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
<Clock size={12} className="mr-1" /> Pendentes
|
||||
|
|
@ -418,6 +321,8 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
}}
|
||||
onApprove={handleApprove}
|
||||
userRole={user.role}
|
||||
currentProfessionalId={currentProfessionalId}
|
||||
onAssignmentResponse={handleAssignmentResponse}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -852,7 +757,7 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
{selectedEvent.photographerIds.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
{selectedEvent.photographerIds.map((id) => {
|
||||
const photographer = MOCK_PHOTOGRAPHERS.find(
|
||||
const photographer = professionals.find(
|
||||
(p) => p.id === id
|
||||
);
|
||||
return (
|
||||
|
|
@ -952,23 +857,12 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{MOCK_PHOTOGRAPHERS.filter((photographer) => {
|
||||
{professionals.map((photographer) => {
|
||||
const isAssigned =
|
||||
selectedEvent.photographerIds.includes(
|
||||
photographer.id
|
||||
);
|
||||
const isAvailable =
|
||||
photographer.availability[selectedEvent.date] ??
|
||||
false;
|
||||
return isAvailable || isAssigned;
|
||||
}).map((photographer) => {
|
||||
const isAssigned =
|
||||
selectedEvent.photographerIds.includes(
|
||||
photographer.id
|
||||
);
|
||||
const isAvailable =
|
||||
photographer.availability[selectedEvent.date] ??
|
||||
false;
|
||||
const isAvailable = true;
|
||||
|
||||
return (
|
||||
<tr
|
||||
|
|
@ -1031,10 +925,10 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
}
|
||||
disabled={!isAvailable && !isAssigned}
|
||||
className={`px-4 py-2 rounded-lg font-medium text-sm transition-colors ${isAssigned
|
||||
? "bg-red-100 text-red-700 hover:bg-red-200"
|
||||
: isAvailable
|
||||
? "bg-brand-gold text-white hover:bg-[#a5bd2e]"
|
||||
: "bg-gray-100 text-gray-400 cursor-not-allowed"
|
||||
? "bg-red-100 text-red-700 hover:bg-red-200"
|
||||
: isAvailable
|
||||
? "bg-brand-gold text-white hover:bg-[#a5bd2e]"
|
||||
: "bg-gray-100 text-gray-400 cursor-not-allowed"
|
||||
}`}
|
||||
>
|
||||
{isAssigned ? "Remover" : "Adicionar"}
|
||||
|
|
@ -1043,28 +937,22 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
|||
</tr>
|
||||
);
|
||||
})}
|
||||
{MOCK_PHOTOGRAPHERS.filter((p) => {
|
||||
const isAssigned =
|
||||
selectedEvent.photographerIds.includes(p.id);
|
||||
const isAvailable =
|
||||
p.availability[selectedEvent.date] ?? false;
|
||||
return isAvailable || isAssigned;
|
||||
}).length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={5} className="p-8 text-center">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<UserX size={48} className="text-gray-300" />
|
||||
<p className="text-gray-500 font-medium">
|
||||
Nenhum profissional disponível para esta data
|
||||
</p>
|
||||
<p className="text-sm text-gray-400">
|
||||
Tente selecionar outra data ou entre em contato
|
||||
com a equipe
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{professionals.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={5} className="p-8 text-center">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<UserX size={48} className="text-gray-300" />
|
||||
<p className="text-gray-500 font-medium">
|
||||
Nenhum profissional disponível para esta data
|
||||
</p>
|
||||
<p className="text-sm text-gray-400">
|
||||
Tente selecionar outra data ou entre em contato
|
||||
com a equipe
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -115,6 +115,39 @@ export async function createProfessional(data: any, token?: string): Promise<Api
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca a lista de profissionais
|
||||
*/
|
||||
export async function getProfessionals(token: string): Promise<ApiResponse<any[]>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/profissionais`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
data,
|
||||
error: null,
|
||||
isBackendDown: false,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching professionals:", error);
|
||||
return {
|
||||
data: null,
|
||||
error: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
isBackendDown: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface EventTypeResponse {
|
||||
id: string;
|
||||
nome: string;
|
||||
|
|
@ -250,6 +283,74 @@ export async function createCadastroFot(data: any, token: string): Promise<ApiRe
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza um cadastro FOT existente
|
||||
*/
|
||||
export async function updateCadastroFot(id: string, data: any, token: string): Promise<ApiResponse<any>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/cadastro-fot/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const responseData = await response.json();
|
||||
return {
|
||||
data: responseData,
|
||||
error: null,
|
||||
isBackendDown: false,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error updating cadastro fot:", error);
|
||||
return {
|
||||
data: null,
|
||||
error: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
isBackendDown: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove um cadastro FOT
|
||||
*/
|
||||
export async function deleteCadastroFot(id: string, token: string): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/cadastro-fot/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return {
|
||||
data: null,
|
||||
error: null,
|
||||
isBackendDown: false,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error deleting cadastro fot:", error);
|
||||
return {
|
||||
data: null,
|
||||
error: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
isBackendDown: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca os níveis educacionais disponíveis (EF I / EF II)
|
||||
*/
|
||||
|
|
@ -326,6 +427,29 @@ export const getAgendas = async (token: string): Promise<ApiResponse<any[]>> =>
|
|||
}
|
||||
};
|
||||
|
||||
export const updateAssignmentStatus = async (token: string, eventId: string, professionalId: string, status: string, reason?: string) => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/agenda/${eventId}/professionals/${professionalId}/status`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ status, reason }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
return { error: errorData.error || "Failed to update assignment status" };
|
||||
}
|
||||
const data = await response.json();
|
||||
return { data };
|
||||
} catch (error) {
|
||||
console.error("API updateAssignmentStatus error:", error);
|
||||
return { error: "Network error" };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -427,3 +551,102 @@ export async function rejectUser(userId: string, token: string): Promise<ApiResp
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atribui um profissional a um evento
|
||||
*/
|
||||
export async function assignProfessional(token: string, eventId: string, professionalId: string): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/agenda/${eventId}/professionals`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ professional_id: professionalId })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return { data: undefined, error: null, isBackendDown: false };
|
||||
} catch (error) {
|
||||
console.error("Error assigning professional:", error);
|
||||
return { data: null, error: error instanceof Error ? error.message : "Erro desconhecido", isBackendDown: true };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove um profissional de um evento
|
||||
*/
|
||||
export async function removeProfessional(token: string, eventId: string, professionalId: string): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/agenda/${eventId}/professionals/${professionalId}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return { data: undefined, error: null, isBackendDown: false };
|
||||
} catch (error) {
|
||||
console.error("Error removing professional:", error);
|
||||
return { data: null, error: error instanceof Error ? error.message : "Erro desconhecido", isBackendDown: true };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca profissionais de um evento
|
||||
*/
|
||||
export async function getEventProfessionals(token: string, eventId: string): Promise<ApiResponse<any[]>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/agenda/${eventId}/professionals`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return { data, error: null, isBackendDown: false };
|
||||
} catch (error) {
|
||||
console.error("Error fetching event professionals:", error);
|
||||
return { data: null, error: error instanceof Error ? error.message : "Erro desconhecido", isBackendDown: true };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza o status de um evento
|
||||
*/
|
||||
export async function updateEventStatus(token: string, eventId: string, status: string): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/agenda/${eventId}/status`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ status })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return { data: undefined, error: null, isBackendDown: false };
|
||||
} catch (error) {
|
||||
console.error("Error updating event status:", error);
|
||||
return { data: null, error: error instanceof Error ? error.message : "Erro desconhecido", isBackendDown: true };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,18 @@ export interface ChecklistItem {
|
|||
required: boolean;
|
||||
}
|
||||
|
||||
export enum AssignmentStatus {
|
||||
PENDING = "PENDENTE",
|
||||
ACCEPTED = "ACEITO",
|
||||
REJECTED = "REJEITADO",
|
||||
}
|
||||
|
||||
export interface Assignment {
|
||||
professionalId: string;
|
||||
status: AssignmentStatus;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface EventData {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -128,4 +140,17 @@ export interface EventData {
|
|||
empresa?: string; // Nome da Empresa
|
||||
observacoes?: string; // Observações da FOT
|
||||
tipoEventoNome?: string; // Nome do Tipo de Evento
|
||||
|
||||
assignments?: Assignment[]; // Lista de status de atribuições
|
||||
}
|
||||
|
||||
export interface Professional {
|
||||
id: string;
|
||||
usuarioId: string;
|
||||
name: string;
|
||||
email: string; // Added via JOIN
|
||||
role: string; // Funcao nome
|
||||
avatar?: string;
|
||||
phone?: string;
|
||||
availability: { [date: string]: boolean }; // Mocked or fetched? For now let's keep compatibility with mock structure
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue