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
|
swagger: ## Gera documentação Swagger
|
||||||
swag init -g cmd/api/main.go -o docs
|
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.GET("/agenda/:id", agendaHandler.Get)
|
||||||
api.PUT("/agenda/:id", agendaHandler.Update)
|
api.PUT("/agenda/:id", agendaHandler.Update)
|
||||||
api.DELETE("/agenda/:id", agendaHandler.Delete)
|
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")
|
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": {
|
"/api/anos-formaturas": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
|
@ -2867,6 +2910,9 @@ const docTemplate = `{
|
||||||
"educacao_simpatia": {
|
"educacao_simpatia": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"endereco": {
|
"endereco": {
|
||||||
"type": "string"
|
"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": {
|
"/api/anos-formaturas": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
|
@ -2861,6 +2904,9 @@
|
||||||
"educacao_simpatia": {
|
"educacao_simpatia": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"endereco": {
|
"endereco": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -301,6 +301,8 @@ definitions:
|
||||||
type: integer
|
type: integer
|
||||||
educacao_simpatia:
|
educacao_simpatia:
|
||||||
type: integer
|
type: integer
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
endereco:
|
endereco:
|
||||||
type: string
|
type: string
|
||||||
equipamentos:
|
equipamentos:
|
||||||
|
|
@ -862,6 +864,35 @@ paths:
|
||||||
summary: Update agenda event
|
summary: Update agenda event
|
||||||
tags:
|
tags:
|
||||||
- agenda
|
- 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:
|
/api/anos-formaturas:
|
||||||
get:
|
get:
|
||||||
consumes:
|
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)
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"photum-backend/internal/db/generated"
|
"photum-backend/internal/db/generated"
|
||||||
|
|
@ -44,6 +45,17 @@ type CreateAgendaRequest struct {
|
||||||
PreVenda bool `json:"pre_venda"`
|
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 {
|
func (s *Service) CalculateStatus(fotoFaltante, recepFaltante, cineFaltante int32) string {
|
||||||
if fotoFaltante < 0 || recepFaltante < 0 || cineFaltante < 0 {
|
if fotoFaltante < 0 || recepFaltante < 0 || cineFaltante < 0 {
|
||||||
return "ERRO"
|
return "ERRO"
|
||||||
|
|
@ -92,59 +104,86 @@ func (s *Service) Create(ctx context.Context, userID uuid.UUID, req CreateAgenda
|
||||||
return s.queries.CreateAgenda(ctx, params)
|
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 is CLIENT (cliente), filter by userID
|
||||||
if role == "cliente" || role == "EVENT_OWNER" {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Convert ListAgendasByUserRow to ListAgendasRow (they are identical structurally but different types in Go)
|
// Convert ListAgendasByUserRow to ListAgendasRow manually
|
||||||
// To avoid manual conversion if types differ slightly by name but not structure, we might need a common interface or cast.
|
for _, r := range listRows {
|
||||||
// Since sqlc generates separate structs, we have to map them.
|
rows = append(rows, generated.ListAgendasRow{
|
||||||
var result []generated.ListAgendasRow
|
ID: r.ID,
|
||||||
for _, r := range rows {
|
UserID: r.UserID,
|
||||||
result = append(result, generated.ListAgendasRow{
|
FotID: r.FotID,
|
||||||
ID: r.ID,
|
DataEvento: r.DataEvento,
|
||||||
UserID: r.UserID,
|
TipoEventoID: r.TipoEventoID,
|
||||||
FotID: r.FotID,
|
ObservacoesEvento: r.ObservacoesEvento,
|
||||||
DataEvento: r.DataEvento,
|
LocalEvento: r.LocalEvento,
|
||||||
TipoEventoID: r.TipoEventoID,
|
Endereco: r.Endereco,
|
||||||
ObservacoesEvento: r.ObservacoesEvento,
|
Horario: r.Horario,
|
||||||
LocalEvento: r.LocalEvento,
|
QtdFormandos: r.QtdFormandos,
|
||||||
Endereco: r.Endereco,
|
QtdFotografos: r.QtdFotografos,
|
||||||
Horario: r.Horario,
|
QtdRecepcionistas: r.QtdRecepcionistas,
|
||||||
QtdFormandos: r.QtdFormandos,
|
QtdCinegrafistas: r.QtdCinegrafistas,
|
||||||
QtdFotografos: r.QtdFotografos,
|
QtdEstudios: r.QtdEstudios,
|
||||||
QtdRecepcionistas: r.QtdRecepcionistas,
|
QtdPontoFoto: r.QtdPontoFoto,
|
||||||
QtdCinegrafistas: r.QtdCinegrafistas,
|
QtdPontoID: r.QtdPontoID,
|
||||||
QtdEstudios: r.QtdEstudios,
|
QtdPontoDecorado: r.QtdPontoDecorado,
|
||||||
QtdPontoFoto: r.QtdPontoFoto,
|
QtdPontosLed: r.QtdPontosLed,
|
||||||
QtdPontoID: r.QtdPontoID,
|
QtdPlataforma360: r.QtdPlataforma360,
|
||||||
QtdPontoDecorado: r.QtdPontoDecorado,
|
StatusProfissionais: r.StatusProfissionais,
|
||||||
QtdPontosLed: r.QtdPontosLed,
|
FotoFaltante: r.FotoFaltante,
|
||||||
QtdPlataforma360: r.QtdPlataforma360,
|
RecepFaltante: r.RecepFaltante,
|
||||||
StatusProfissionais: r.StatusProfissionais,
|
CineFaltante: r.CineFaltante,
|
||||||
FotoFaltante: r.FotoFaltante,
|
LogisticaObservacoes: r.LogisticaObservacoes,
|
||||||
RecepFaltante: r.RecepFaltante,
|
PreVenda: r.PreVenda,
|
||||||
CineFaltante: r.CineFaltante,
|
CriadoEm: r.CriadoEm,
|
||||||
LogisticaObservacoes: r.LogisticaObservacoes,
|
AtualizadoEm: r.AtualizadoEm,
|
||||||
PreVenda: r.PreVenda,
|
Status: r.Status,
|
||||||
CriadoEm: r.CriadoEm,
|
FotNumero: r.FotNumero,
|
||||||
AtualizadoEm: r.AtualizadoEm,
|
Instituicao: r.Instituicao,
|
||||||
FotNumero: r.FotNumero,
|
CursoNome: r.CursoNome,
|
||||||
Instituicao: r.Instituicao,
|
EmpresaNome: r.EmpresaNome,
|
||||||
CursoNome: r.CursoNome,
|
AnoSemestre: r.AnoSemestre,
|
||||||
EmpresaNome: r.EmpresaNome,
|
ObservacoesFot: r.ObservacoesFot,
|
||||||
AnoSemestre: r.AnoSemestre,
|
TipoEventoNome: r.TipoEventoNome,
|
||||||
ObservacoesFot: r.ObservacoesFot,
|
AssignedProfessionals: r.AssignedProfessionals,
|
||||||
TipoEventoNome: r.TipoEventoNome,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
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) {
|
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 {
|
func (s *Service) Delete(ctx context.Context, id uuid.UUID) error {
|
||||||
return s.queries.DeleteAgenda(ctx, pgtype.UUID{Bytes: id, Valid: true})
|
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"
|
"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
|
const createAgenda = `-- name: CreateAgenda :one
|
||||||
INSERT INTO agenda (
|
INSERT INTO agenda (
|
||||||
fot_id,
|
fot_id,
|
||||||
|
|
@ -39,7 +55,7 @@ INSERT INTO agenda (
|
||||||
user_id
|
user_id
|
||||||
) VALUES (
|
) 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
|
$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 {
|
type CreateAgendaParams struct {
|
||||||
|
|
@ -125,6 +141,7 @@ func (q *Queries) CreateAgenda(ctx context.Context, arg CreateAgendaParams) (Age
|
||||||
&i.PreVenda,
|
&i.PreVenda,
|
||||||
&i.CriadoEm,
|
&i.CriadoEm,
|
||||||
&i.AtualizadoEm,
|
&i.AtualizadoEm,
|
||||||
|
&i.Status,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +157,7 @@ func (q *Queries) DeleteAgenda(ctx context.Context, id pgtype.UUID) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAgenda = `-- name: GetAgenda :one
|
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
|
WHERE id = $1 LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -175,20 +192,122 @@ func (q *Queries) GetAgenda(ctx context.Context, id pgtype.UUID) (Agenda, error)
|
||||||
&i.PreVenda,
|
&i.PreVenda,
|
||||||
&i.CriadoEm,
|
&i.CriadoEm,
|
||||||
&i.AtualizadoEm,
|
&i.AtualizadoEm,
|
||||||
|
&i.Status,
|
||||||
)
|
)
|
||||||
return i, err
|
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
|
const listAgendas = `-- name: ListAgendas :many
|
||||||
SELECT
|
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.fot as fot_numero,
|
||||||
cf.instituicao,
|
cf.instituicao,
|
||||||
c.nome as curso_nome,
|
c.nome as curso_nome,
|
||||||
e.nome as empresa_nome,
|
e.nome as empresa_nome,
|
||||||
af.ano_semestre,
|
af.ano_semestre,
|
||||||
cf.observacoes as observacoes_fot,
|
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
|
FROM agenda a
|
||||||
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
||||||
JOIN cursos c ON cf.curso_id = c.id
|
JOIN cursos c ON cf.curso_id = c.id
|
||||||
|
|
@ -199,40 +318,42 @@ ORDER BY a.data_evento
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListAgendasRow struct {
|
type ListAgendasRow struct {
|
||||||
ID pgtype.UUID `json:"id"`
|
ID pgtype.UUID `json:"id"`
|
||||||
UserID pgtype.UUID `json:"user_id"`
|
UserID pgtype.UUID `json:"user_id"`
|
||||||
FotID pgtype.UUID `json:"fot_id"`
|
FotID pgtype.UUID `json:"fot_id"`
|
||||||
DataEvento pgtype.Date `json:"data_evento"`
|
DataEvento pgtype.Date `json:"data_evento"`
|
||||||
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
|
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
|
||||||
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
|
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
|
||||||
LocalEvento pgtype.Text `json:"local_evento"`
|
LocalEvento pgtype.Text `json:"local_evento"`
|
||||||
Endereco pgtype.Text `json:"endereco"`
|
Endereco pgtype.Text `json:"endereco"`
|
||||||
Horario pgtype.Text `json:"horario"`
|
Horario pgtype.Text `json:"horario"`
|
||||||
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
|
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
|
||||||
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
|
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
|
||||||
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
|
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
|
||||||
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
|
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
|
||||||
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
|
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
|
||||||
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
|
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
|
||||||
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
|
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
|
||||||
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
|
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
|
||||||
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
|
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
|
||||||
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
|
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
|
||||||
StatusProfissionais pgtype.Text `json:"status_profissionais"`
|
StatusProfissionais pgtype.Text `json:"status_profissionais"`
|
||||||
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
|
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
|
||||||
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
|
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
|
||||||
CineFaltante pgtype.Int4 `json:"cine_faltante"`
|
CineFaltante pgtype.Int4 `json:"cine_faltante"`
|
||||||
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
|
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
|
||||||
PreVenda pgtype.Bool `json:"pre_venda"`
|
PreVenda pgtype.Bool `json:"pre_venda"`
|
||||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||||
FotNumero int32 `json:"fot_numero"`
|
Status pgtype.Text `json:"status"`
|
||||||
Instituicao pgtype.Text `json:"instituicao"`
|
FotNumero int32 `json:"fot_numero"`
|
||||||
CursoNome string `json:"curso_nome"`
|
Instituicao pgtype.Text `json:"instituicao"`
|
||||||
EmpresaNome string `json:"empresa_nome"`
|
CursoNome string `json:"curso_nome"`
|
||||||
AnoSemestre string `json:"ano_semestre"`
|
EmpresaNome string `json:"empresa_nome"`
|
||||||
ObservacoesFot pgtype.Text `json:"observacoes_fot"`
|
AnoSemestre string `json:"ano_semestre"`
|
||||||
TipoEventoNome string `json:"tipo_evento_nome"`
|
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) {
|
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.PreVenda,
|
||||||
&i.CriadoEm,
|
&i.CriadoEm,
|
||||||
&i.AtualizadoEm,
|
&i.AtualizadoEm,
|
||||||
|
&i.Status,
|
||||||
&i.FotNumero,
|
&i.FotNumero,
|
||||||
&i.Instituicao,
|
&i.Instituicao,
|
||||||
&i.CursoNome,
|
&i.CursoNome,
|
||||||
|
|
@ -279,6 +401,7 @@ func (q *Queries) ListAgendas(ctx context.Context) ([]ListAgendasRow, error) {
|
||||||
&i.AnoSemestre,
|
&i.AnoSemestre,
|
||||||
&i.ObservacoesFot,
|
&i.ObservacoesFot,
|
||||||
&i.TipoEventoNome,
|
&i.TipoEventoNome,
|
||||||
|
&i.AssignedProfessionals,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -292,14 +415,24 @@ func (q *Queries) ListAgendas(ctx context.Context) ([]ListAgendasRow, error) {
|
||||||
|
|
||||||
const listAgendasByUser = `-- name: ListAgendasByUser :many
|
const listAgendasByUser = `-- name: ListAgendasByUser :many
|
||||||
SELECT
|
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.fot as fot_numero,
|
||||||
cf.instituicao,
|
cf.instituicao,
|
||||||
c.nome as curso_nome,
|
c.nome as curso_nome,
|
||||||
e.nome as empresa_nome,
|
e.nome as empresa_nome,
|
||||||
af.ano_semestre,
|
af.ano_semestre,
|
||||||
cf.observacoes as observacoes_fot,
|
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
|
FROM agenda a
|
||||||
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
||||||
JOIN cursos c ON cf.curso_id = c.id
|
JOIN cursos c ON cf.curso_id = c.id
|
||||||
|
|
@ -311,40 +444,42 @@ ORDER BY a.data_evento
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListAgendasByUserRow struct {
|
type ListAgendasByUserRow struct {
|
||||||
ID pgtype.UUID `json:"id"`
|
ID pgtype.UUID `json:"id"`
|
||||||
UserID pgtype.UUID `json:"user_id"`
|
UserID pgtype.UUID `json:"user_id"`
|
||||||
FotID pgtype.UUID `json:"fot_id"`
|
FotID pgtype.UUID `json:"fot_id"`
|
||||||
DataEvento pgtype.Date `json:"data_evento"`
|
DataEvento pgtype.Date `json:"data_evento"`
|
||||||
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
|
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
|
||||||
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
|
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
|
||||||
LocalEvento pgtype.Text `json:"local_evento"`
|
LocalEvento pgtype.Text `json:"local_evento"`
|
||||||
Endereco pgtype.Text `json:"endereco"`
|
Endereco pgtype.Text `json:"endereco"`
|
||||||
Horario pgtype.Text `json:"horario"`
|
Horario pgtype.Text `json:"horario"`
|
||||||
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
|
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
|
||||||
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
|
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
|
||||||
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
|
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
|
||||||
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
|
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
|
||||||
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
|
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
|
||||||
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
|
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
|
||||||
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
|
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
|
||||||
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
|
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
|
||||||
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
|
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
|
||||||
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
|
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
|
||||||
StatusProfissionais pgtype.Text `json:"status_profissionais"`
|
StatusProfissionais pgtype.Text `json:"status_profissionais"`
|
||||||
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
|
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
|
||||||
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
|
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
|
||||||
CineFaltante pgtype.Int4 `json:"cine_faltante"`
|
CineFaltante pgtype.Int4 `json:"cine_faltante"`
|
||||||
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
|
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
|
||||||
PreVenda pgtype.Bool `json:"pre_venda"`
|
PreVenda pgtype.Bool `json:"pre_venda"`
|
||||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||||
FotNumero int32 `json:"fot_numero"`
|
Status pgtype.Text `json:"status"`
|
||||||
Instituicao pgtype.Text `json:"instituicao"`
|
FotNumero int32 `json:"fot_numero"`
|
||||||
CursoNome string `json:"curso_nome"`
|
Instituicao pgtype.Text `json:"instituicao"`
|
||||||
EmpresaNome string `json:"empresa_nome"`
|
CursoNome string `json:"curso_nome"`
|
||||||
AnoSemestre string `json:"ano_semestre"`
|
EmpresaNome string `json:"empresa_nome"`
|
||||||
ObservacoesFot pgtype.Text `json:"observacoes_fot"`
|
AnoSemestre string `json:"ano_semestre"`
|
||||||
TipoEventoNome string `json:"tipo_evento_nome"`
|
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) {
|
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.PreVenda,
|
||||||
&i.CriadoEm,
|
&i.CriadoEm,
|
||||||
&i.AtualizadoEm,
|
&i.AtualizadoEm,
|
||||||
|
&i.Status,
|
||||||
&i.FotNumero,
|
&i.FotNumero,
|
||||||
&i.Instituicao,
|
&i.Instituicao,
|
||||||
&i.CursoNome,
|
&i.CursoNome,
|
||||||
|
|
@ -391,6 +527,7 @@ func (q *Queries) ListAgendasByUser(ctx context.Context, userID pgtype.UUID) ([]
|
||||||
&i.AnoSemestre,
|
&i.AnoSemestre,
|
||||||
&i.ObservacoesFot,
|
&i.ObservacoesFot,
|
||||||
&i.TipoEventoNome,
|
&i.TipoEventoNome,
|
||||||
|
&i.AssignedProfessionals,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -402,6 +539,21 @@ func (q *Queries) ListAgendasByUser(ctx context.Context, userID pgtype.UUID) ([]
|
||||||
return items, nil
|
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
|
const updateAgenda = `-- name: UpdateAgenda :one
|
||||||
UPDATE agenda
|
UPDATE agenda
|
||||||
SET
|
SET
|
||||||
|
|
@ -430,7 +582,7 @@ SET
|
||||||
pre_venda = $24,
|
pre_venda = $24,
|
||||||
atualizado_em = NOW()
|
atualizado_em = NOW()
|
||||||
WHERE id = $1
|
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 {
|
type UpdateAgendaParams struct {
|
||||||
|
|
@ -516,6 +668,88 @@ func (q *Queries) UpdateAgenda(ctx context.Context, arg UpdateAgendaParams) (Age
|
||||||
&i.PreVenda,
|
&i.PreVenda,
|
||||||
&i.CriadoEm,
|
&i.CriadoEm,
|
||||||
&i.AtualizadoEm,
|
&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
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,16 @@ type Agenda struct {
|
||||||
PreVenda pgtype.Bool `json:"pre_venda"`
|
PreVenda pgtype.Bool `json:"pre_venda"`
|
||||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_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 {
|
type AnosFormatura struct {
|
||||||
|
|
|
||||||
|
|
@ -270,9 +270,10 @@ func (q *Queries) GetProfissionalByUsuarioID(ctx context.Context, usuarioID pgty
|
||||||
}
|
}
|
||||||
|
|
||||||
const listProfissionais = `-- name: ListProfissionais :many
|
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
|
FROM cadastro_profissionais p
|
||||||
LEFT JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id
|
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
|
ORDER BY p.nome
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -305,6 +306,7 @@ type ListProfissionaisRow struct {
|
||||||
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
CriadoEm pgtype.Timestamptz `json:"criado_em"`
|
||||||
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
|
||||||
FuncaoNome pgtype.Text `json:"funcao_nome"`
|
FuncaoNome pgtype.Text `json:"funcao_nome"`
|
||||||
|
Email pgtype.Text `json:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) ListProfissionais(ctx context.Context) ([]ListProfissionaisRow, error) {
|
func (q *Queries) ListProfissionais(ctx context.Context) ([]ListProfissionaisRow, error) {
|
||||||
|
|
@ -345,6 +347,7 @@ func (q *Queries) ListProfissionais(ctx context.Context) ([]ListProfissionaisRow
|
||||||
&i.CriadoEm,
|
&i.CriadoEm,
|
||||||
&i.AtualizadoEm,
|
&i.AtualizadoEm,
|
||||||
&i.FuncaoNome,
|
&i.FuncaoNome,
|
||||||
|
&i.Email,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,17 @@ SELECT
|
||||||
e.nome as empresa_nome,
|
e.nome as empresa_nome,
|
||||||
af.ano_semestre,
|
af.ano_semestre,
|
||||||
cf.observacoes as observacoes_fot,
|
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
|
FROM agenda a
|
||||||
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
||||||
JOIN cursos c ON cf.curso_id = c.id
|
JOIN cursos c ON cf.curso_id = c.id
|
||||||
|
|
@ -59,7 +69,17 @@ SELECT
|
||||||
e.nome as empresa_nome,
|
e.nome as empresa_nome,
|
||||||
af.ano_semestre,
|
af.ano_semestre,
|
||||||
cf.observacoes as observacoes_fot,
|
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
|
FROM agenda a
|
||||||
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
JOIN cadastro_fot cf ON a.fot_id = cf.id
|
||||||
JOIN cursos c ON cf.curso_id = c.id
|
JOIN cursos c ON cf.curso_id = c.id
|
||||||
|
|
@ -102,3 +122,32 @@ RETURNING *;
|
||||||
-- name: DeleteAgenda :exec
|
-- name: DeleteAgenda :exec
|
||||||
DELETE FROM agenda
|
DELETE FROM agenda
|
||||||
WHERE id = $1;
|
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;
|
WHERE p.id = $1 LIMIT 1;
|
||||||
|
|
||||||
-- name: ListProfissionais :many
|
-- name: ListProfissionais :many
|
||||||
SELECT p.*, f.nome as funcao_nome
|
SELECT p.*, f.nome as funcao_nome, u.email
|
||||||
FROM cadastro_profissionais p
|
FROM cadastro_profissionais p
|
||||||
LEFT JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id
|
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;
|
ORDER BY p.nome;
|
||||||
|
|
||||||
-- name: UpdateProfissional :one
|
-- name: UpdateProfissional :one
|
||||||
|
|
|
||||||
|
|
@ -341,5 +341,16 @@ CREATE TABLE IF NOT EXISTS agenda (
|
||||||
logistica_observacoes TEXT,
|
logistica_observacoes TEXT,
|
||||||
pre_venda BOOLEAN DEFAULT FALSE,
|
pre_venda BOOLEAN DEFAULT FALSE,
|
||||||
criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
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"`
|
TabelaFree *string `json:"tabela_free"`
|
||||||
ExtraPorEquipamento *bool `json:"extra_por_equipamento"`
|
ExtraPorEquipamento *bool `json:"extra_por_equipamento"`
|
||||||
Equipamentos *string `json:"equipamentos"`
|
Equipamentos *string `json:"equipamentos"`
|
||||||
|
Email string `json:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func toResponse(p interface{}) ProfissionalResponse {
|
func toResponse(p interface{}) ProfissionalResponse {
|
||||||
|
|
@ -112,6 +113,7 @@ func toResponse(p interface{}) ProfissionalResponse {
|
||||||
TabelaFree: fromPgText(v.TabelaFree),
|
TabelaFree: fromPgText(v.TabelaFree),
|
||||||
ExtraPorEquipamento: fromPgBool(v.ExtraPorEquipamento),
|
ExtraPorEquipamento: fromPgBool(v.ExtraPorEquipamento),
|
||||||
Equipamentos: fromPgText(v.Equipamentos),
|
Equipamentos: fromPgText(v.Equipamentos),
|
||||||
|
Email: v.Email.String,
|
||||||
}
|
}
|
||||||
case generated.GetProfissionalByIDRow:
|
case generated.GetProfissionalByIDRow:
|
||||||
return ProfissionalResponse{
|
return ProfissionalResponse{
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ interface EventTableProps {
|
||||||
onEventClick: (event: EventData) => void;
|
onEventClick: (event: EventData) => void;
|
||||||
onApprove?: (e: React.MouseEvent, eventId: string) => void;
|
onApprove?: (e: React.MouseEvent, eventId: string) => void;
|
||||||
userRole: UserRole;
|
userRole: UserRole;
|
||||||
|
currentProfessionalId?: string;
|
||||||
|
onAssignmentResponse?: (e: React.MouseEvent, eventId: string, status: string, reason?: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SortField =
|
type SortField =
|
||||||
|
|
@ -26,15 +28,18 @@ export const EventTable: React.FC<EventTableProps> = ({
|
||||||
onEventClick,
|
onEventClick,
|
||||||
onApprove,
|
onApprove,
|
||||||
userRole,
|
userRole,
|
||||||
|
currentProfessionalId,
|
||||||
|
onAssignmentResponse,
|
||||||
}) => {
|
}) => {
|
||||||
const canApprove =
|
const canApprove =
|
||||||
userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN;
|
userRole === UserRole.BUSINESS_OWNER || userRole === UserRole.SUPERADMIN;
|
||||||
|
const isPhotographer = userRole === UserRole.PHOTOGRAPHER;
|
||||||
|
|
||||||
const [sortField, setSortField] = useState<SortField | null>(null);
|
const [sortField, setSortField] = useState<SortField | null>(null);
|
||||||
const [sortOrder, setSortOrder] = useState<SortOrder>(null);
|
const [sortOrder, setSortOrder] = useState<SortOrder>(null);
|
||||||
|
|
||||||
const handleSort = (field: SortField) => {
|
const handleSort = (field: SortField) => {
|
||||||
if (sortField === field) {
|
if (sortField === field) {
|
||||||
// Se já está ordenando por este campo, alterna a ordem
|
|
||||||
if (sortOrder === "asc") {
|
if (sortOrder === "asc") {
|
||||||
setSortOrder("desc");
|
setSortOrder("desc");
|
||||||
} else if (sortOrder === "desc") {
|
} else if (sortOrder === "desc") {
|
||||||
|
|
@ -42,7 +47,6 @@ export const EventTable: React.FC<EventTableProps> = ({
|
||||||
setSortField(null);
|
setSortField(null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Novo campo, começa com ordem ascendente
|
|
||||||
setSortField(field);
|
setSortField(field);
|
||||||
setSortOrder("asc");
|
setSortOrder("asc");
|
||||||
}
|
}
|
||||||
|
|
@ -216,80 +220,122 @@ export const EventTable: React.FC<EventTableProps> = ({
|
||||||
{getSortIcon("status")}
|
{getSortIcon("status")}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
{canApprove && (
|
{(canApprove || isPhotographer) && (
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider w-20">
|
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider w-32">
|
||||||
Ações
|
Ações
|
||||||
</th>
|
</th>
|
||||||
)}
|
)}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-100">
|
<tbody className="divide-y divide-gray-100">
|
||||||
{sortedEvents.map((event) => (
|
{sortedEvents.map((event) => {
|
||||||
<tr
|
// Logic to find photographer assignment status
|
||||||
key={event.id}
|
let photographerAssignment = null;
|
||||||
onClick={() => onEventClick(event)}
|
if (isPhotographer && currentProfessionalId && event.assignments) {
|
||||||
className="hover:bg-gray-50 cursor-pointer transition-colors"
|
photographerAssignment = event.assignments.find(a => a.professionalId === currentProfessionalId);
|
||||||
>
|
}
|
||||||
<td className="px-4 py-3">
|
|
||||||
<span className="text-sm font-medium text-gray-900">
|
return (
|
||||||
{event.fot || "-"}
|
<tr
|
||||||
</span>
|
key={event.id}
|
||||||
</td>
|
onClick={() => onEventClick(event)}
|
||||||
<td className="px-4 py-3">
|
className="hover:bg-gray-50 cursor-pointer transition-colors"
|
||||||
<span className="text-sm text-gray-600">
|
>
|
||||||
{formatDate(event.date)}
|
<td className="px-4 py-3">
|
||||||
</span>
|
<span className="text-sm font-medium text-gray-900">
|
||||||
</td>
|
{event.fot || "-"}
|
||||||
<td className="px-4 py-3">
|
</span>
|
||||||
<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>
|
|
||||||
)}
|
|
||||||
</td>
|
</td>
|
||||||
)}
|
<td className="px-4 py-3">
|
||||||
</tr>
|
<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>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { X, AlertTriangle, Save, Loader } from "lucide-react";
|
import { X, AlertTriangle, Save, Loader } from "lucide-react";
|
||||||
import { Button } from "./Button";
|
import { Button } from "./Button";
|
||||||
import { getCompanies, getAvailableCourses, getGraduationYears, createCadastroFot } from "../services/apiService";
|
import { getCompanies, getAvailableCourses, getGraduationYears, createCadastroFot, updateCadastroFot } from "../services/apiService";
|
||||||
|
|
||||||
interface FotFormProps {
|
interface FotFormProps {
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onSubmit: (success: boolean) => void;
|
onSubmit: (success: boolean) => void;
|
||||||
token: string;
|
token: string;
|
||||||
existingFots: number[]; // List of existing FOT numbers for validation
|
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({
|
const [formData, setFormData] = useState({
|
||||||
fot: "",
|
fot: "",
|
||||||
empresa_id: "",
|
empresa_id: "",
|
||||||
|
|
@ -24,6 +25,23 @@ export const FotForm: React.FC<FotFormProps> = ({ onCancel, onSubmit, token, exi
|
||||||
pre_venda: false,
|
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 [companies, setCompanies] = useState<any[]>([]);
|
||||||
const [coursesList, setCoursesList] = useState<any[]>([]);
|
const [coursesList, setCoursesList] = useState<any[]>([]);
|
||||||
const [years, setYears] = 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;
|
const val = e.target.value;
|
||||||
setFormData({ ...formData, fot: val });
|
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!`);
|
setFotError(`O FOT ${val} já existe!`);
|
||||||
} else {
|
} else {
|
||||||
setFotError(null);
|
setFotError(null);
|
||||||
|
|
@ -89,7 +117,12 @@ export const FotForm: React.FC<FotFormProps> = ({ onCancel, onSubmit, token, exi
|
||||||
pre_venda: formData.pre_venda,
|
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) {
|
if (result.error) {
|
||||||
throw new Error(result.error);
|
throw new Error(result.error);
|
||||||
|
|
@ -98,11 +131,11 @@ export const FotForm: React.FC<FotFormProps> = ({ onCancel, onSubmit, token, exi
|
||||||
onSubmit(true);
|
onSubmit(true);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.message || "Erro ao salvar FOT.");
|
setError(err.message || "Erro ao salvar FOT.");
|
||||||
} finally {
|
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if (loadingDependencies) {
|
if (loadingDependencies) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg p-8 flex flex-col items-center justify-center min-h-[400px]">
|
<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 React, { createContext, useContext, useState, ReactNode, useEffect } from "react";
|
||||||
import { useAuth } from "./AuthContext";
|
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 {
|
import {
|
||||||
EventData,
|
EventData,
|
||||||
EventStatus,
|
EventStatus,
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
User,
|
User,
|
||||||
UserApprovalStatus,
|
UserApprovalStatus,
|
||||||
UserRole,
|
UserRole,
|
||||||
|
Professional,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
// Initial Mock Data
|
// Initial Mock Data
|
||||||
|
|
@ -602,8 +603,11 @@ interface DataContextType {
|
||||||
getActiveCoursesByInstitutionId: (institutionId: string) => Course[];
|
getActiveCoursesByInstitutionId: (institutionId: string) => Course[];
|
||||||
getCourseById: (id: string) => Course | undefined;
|
getCourseById: (id: string) => Course | undefined;
|
||||||
registerPendingUser: (userData: { id: string; name: string; email: string; phone: string; registeredInstitution?: string }) => void;
|
registerPendingUser: (userData: { id: string; name: string; email: string; phone: string; registeredInstitution?: string }) => void;
|
||||||
|
|
||||||
approveUser: (userId: string) => void;
|
approveUser: (userId: string) => void;
|
||||||
rejectUser: (userId: string) => void;
|
rejectUser: (userId: string) => void;
|
||||||
|
professionals: Professional[];
|
||||||
|
respondToAssignment: (eventId: string, status: string, reason?: string) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DataContext = createContext<DataContextType | undefined>(undefined);
|
const DataContext = createContext<DataContextType | undefined>(undefined);
|
||||||
|
|
@ -613,11 +617,13 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const { token, user } = useAuth(); // Consume Auth Context
|
const { token, user } = useAuth(); // Consume Auth Context
|
||||||
const [events, setEvents] = useState<EventData[]>(INITIAL_EVENTS);
|
const [events, setEvents] = useState<EventData[]>([]);
|
||||||
const [institutions, setInstitutions] =
|
const [institutions, setInstitutions] =
|
||||||
useState<Institution[]>(INITIAL_INSTITUTIONS);
|
useState<Institution[]>(INITIAL_INSTITUTIONS);
|
||||||
const [courses, setCourses] = useState<Course[]>(INITIAL_COURSES);
|
const [courses, setCourses] = useState<Course[]>(INITIAL_COURSES);
|
||||||
|
|
||||||
const [pendingUsers, setPendingUsers] = useState<User[]>([]);
|
const [pendingUsers, setPendingUsers] = useState<User[]>([]);
|
||||||
|
const [professionals, setProfessionals] = useState<Professional[]>([]);
|
||||||
|
|
||||||
// Fetch events from API
|
// Fetch events from API
|
||||||
useEffect(() => {
|
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
|
// Import dynamic to avoid circular dependency if any, or just use imported service
|
||||||
const { getAgendas } = await import("../services/apiService");
|
const { getAgendas } = await import("../services/apiService");
|
||||||
const result = await getAgendas(visibleToken);
|
const result = await getAgendas(visibleToken);
|
||||||
|
console.log("Raw Agenda Data:", result.data); // Debug logging
|
||||||
if (result.data) {
|
if (result.data) {
|
||||||
const mappedEvents: EventData[] = result.data.map((e: any) => ({
|
const mappedEvents: EventData[] = result.data.map((e: any) => ({
|
||||||
id: e.id,
|
id: e.id,
|
||||||
|
|
@ -637,23 +644,23 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
||||||
date: e.data_evento ? e.data_evento.split('T')[0] : "",
|
date: e.data_evento ? e.data_evento.split('T')[0] : "",
|
||||||
time: e.horario || "00:00",
|
time: e.horario || "00:00",
|
||||||
type: (e.tipo_evento_nome || "Outro") as EventType, // Map string to enum if possible, or keep string
|
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.
|
status: EventStatus.PENDING_APPROVAL,
|
||||||
// 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.
|
|
||||||
address: {
|
address: {
|
||||||
street: e.endereco ? e.endereco.split(',')[0] : "",
|
street: e.endereco ? e.endereco.split(',')[0] : "",
|
||||||
number: e.endereco ? e.endereco.split(',')[1]?.split('-')[0]?.trim() || "" : "",
|
number: e.endereco ? e.endereco.split(',')[1]?.split('-')[0]?.trim() || "" : "",
|
||||||
city: 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() || "" : "",
|
state: e.endereco ? e.endereco.split('/')[1]?.trim() || "" : "",
|
||||||
zip: "",
|
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 || "",
|
briefing: e.observacoes_evento || "",
|
||||||
coverImage: "https://picsum.photos/id/10/800/400", // Placeholder
|
coverImage: "https://picsum.photos/id/10/800/400", // Placeholder
|
||||||
contacts: [], // TODO: fetch contacts if needed
|
contacts: [], // TODO: fetch contacts if needed
|
||||||
checklist: [],
|
checklist: [],
|
||||||
ownerId: e.user_id || "unknown",
|
ownerId: e.user_id || "unknown",
|
||||||
photographerIds: [], // TODO
|
photographerIds: Array.isArray(e.assigned_professionals)
|
||||||
|
? e.assigned_professionals.map((a: any) => a.professional_id)
|
||||||
|
: [],
|
||||||
institutionId: "", // TODO
|
institutionId: "", // TODO
|
||||||
attendees: e.qtd_formandos,
|
attendees: e.qtd_formandos,
|
||||||
fotId: e.fot_id, // UUID
|
fotId: e.fot_id, // UUID
|
||||||
|
|
@ -665,9 +672,18 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
||||||
anoFormatura: e.ano_semestre,
|
anoFormatura: e.ano_semestre,
|
||||||
empresa: e.empresa_nome,
|
empresa: e.empresa_nome,
|
||||||
observacoes: e.observacoes_fot,
|
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);
|
setEvents(mappedEvents);
|
||||||
|
} else {
|
||||||
|
setEvents([]);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch events", error);
|
console.error("Failed to fetch events", error);
|
||||||
|
|
@ -712,8 +728,41 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchUsers();
|
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 addEvent = async (event: EventData) => {
|
||||||
const token = localStorage.getItem("@Photum:token");
|
const token = localStorage.getItem("@Photum:token");
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
|
@ -770,16 +819,51 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateEventStatus = (id: string, status: EventStatus) => {
|
const updateEventStatus = async (id: string, status: EventStatus) => {
|
||||||
setEvents((prev) => prev.map((e) => (e.id === id ? { ...e, status } : e)));
|
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) =>
|
setEvents((prev) =>
|
||||||
prev.map((e) => {
|
prev.map((e) => {
|
||||||
if (e.id === eventId) {
|
if (e.id === eventId) {
|
||||||
const current = e.photographerIds || [];
|
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] };
|
return { ...e, photographerIds: [...current, photographerId] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -796,7 +880,10 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
||||||
return events.filter((e) => e.ownerId === userId);
|
return events.filter((e) => e.ownerId === userId);
|
||||||
}
|
}
|
||||||
if (role === "PHOTOGRAPHER") {
|
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 [];
|
return [];
|
||||||
};
|
};
|
||||||
|
|
@ -904,6 +991,30 @@ export const DataProvider: React.FC<{ children: ReactNode }> = ({
|
||||||
institutions,
|
institutions,
|
||||||
courses,
|
courses,
|
||||||
pendingUsers,
|
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,
|
addEvent,
|
||||||
updateEventStatus,
|
updateEventStatus,
|
||||||
assignPhotographer,
|
assignPhotographer,
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import React, { useState, useEffect } from "react";
|
||||||
import { useAuth } from "../contexts/AuthContext";
|
import { useAuth } from "../contexts/AuthContext";
|
||||||
import { UserRole } from "../types";
|
import { UserRole } from "../types";
|
||||||
import { Button } from "../components/Button";
|
import { Button } from "../components/Button";
|
||||||
import { getCadastroFot } from "../services/apiService";
|
import { getCadastroFot, deleteCadastroFot } from "../services/apiService";
|
||||||
import { Briefcase, AlertTriangle, Plus, Edit } from "lucide-react";
|
import { Briefcase, AlertTriangle, Plus, Edit, Trash2, Search, Filter } from "lucide-react";
|
||||||
import { FotForm } from "../components/FotForm";
|
import { FotForm } from "../components/FotForm";
|
||||||
|
|
||||||
interface FotData {
|
interface FotData {
|
||||||
|
|
@ -18,14 +18,24 @@ interface FotData {
|
||||||
estado: string;
|
estado: string;
|
||||||
gastos_captacao: number;
|
gastos_captacao: number;
|
||||||
pre_venda: boolean;
|
pre_venda: boolean;
|
||||||
|
empresa_id?: string;
|
||||||
|
curso_id?: string;
|
||||||
|
ano_formatura_id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CourseManagement: React.FC = () => {
|
export const CourseManagement: React.FC = () => {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [fotList, setFotList] = useState<FotData[]>([]);
|
const [fotList, setFotList] = useState<FotData[]>([]);
|
||||||
|
const [filteredList, setFilteredList] = useState<FotData[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Modal State
|
||||||
const [showForm, setShowForm] = useState(false);
|
const [showForm, setShowForm] = useState(false);
|
||||||
|
const [editingFot, setEditingFot] = useState<FotData | null>(null);
|
||||||
|
|
||||||
|
// Filter State
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
|
||||||
// Verificar se è admin
|
// Verificar se è admin
|
||||||
const isAdmin =
|
const isAdmin =
|
||||||
|
|
@ -36,6 +46,21 @@ export const CourseManagement: React.FC = () => {
|
||||||
fetchFotData();
|
fetchFotData();
|
||||||
}, [isAdmin]);
|
}, [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 () => {
|
const fetchFotData = async () => {
|
||||||
if (!isAdmin) return;
|
if (!isAdmin) return;
|
||||||
|
|
||||||
|
|
@ -61,7 +86,32 @@ export const CourseManagement: React.FC = () => {
|
||||||
|
|
||||||
const handleFormSubmit = () => {
|
const handleFormSubmit = () => {
|
||||||
setShowForm(false);
|
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
|
// 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">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-6 sm:mb-8">
|
<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>
|
<div>
|
||||||
<h1 className="text-2xl sm:text-3xl font-serif font-bold text-brand-black">
|
<h1 className="text-2xl sm:text-3xl font-serif font-bold text-brand-black">
|
||||||
Gestão de FOT
|
Gestão de FOT
|
||||||
|
|
@ -95,7 +145,10 @@ export const CourseManagement: React.FC = () => {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setShowForm(true)}
|
onClick={() => {
|
||||||
|
setEditingFot(null);
|
||||||
|
setShowForm(true);
|
||||||
|
}}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="flex items-center space-x-2"
|
className="flex items-center space-x-2"
|
||||||
>
|
>
|
||||||
|
|
@ -103,18 +156,35 @@ export const CourseManagement: React.FC = () => {
|
||||||
<span>Cadastro FOT</span>
|
<span>Cadastro FOT</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Form Modal */}
|
{/* Form Modal */}
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||||
<FotForm
|
<FotForm
|
||||||
onCancel={() => setShowForm(false)}
|
onCancel={() => {
|
||||||
|
setShowForm(false);
|
||||||
|
setEditingFot(null);
|
||||||
|
}}
|
||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
token={localStorage.getItem("token") || ""}
|
token={localStorage.getItem("token") || ""}
|
||||||
existingFots={existingFots}
|
existingFots={existingFots}
|
||||||
|
initialData={editingFot}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -145,10 +215,7 @@ export const CourseManagement: React.FC = () => {
|
||||||
Empresa
|
Empresa
|
||||||
</th>
|
</th>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
Cursos
|
Curso
|
||||||
</th>
|
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Observações
|
|
||||||
</th>
|
</th>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
Instituição
|
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">
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
Estado
|
Estado
|
||||||
</th>
|
</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">
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
Gastos Captação
|
Gastos Captação
|
||||||
</th>
|
</th>
|
||||||
|
|
@ -174,7 +244,7 @@ export const CourseManagement: React.FC = () => {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
{fotList.length === 0 ? (
|
{filteredList.length === 0 ? (
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
colSpan={11}
|
colSpan={11}
|
||||||
|
|
@ -189,7 +259,7 @@ export const CourseManagement: React.FC = () => {
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
) : (
|
) : (
|
||||||
fotList.map((item) => (
|
filteredList.map((item) => (
|
||||||
<tr
|
<tr
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className="hover:bg-gray-50 transition-colors"
|
className="hover:bg-gray-50 transition-colors"
|
||||||
|
|
@ -209,11 +279,6 @@ export const CourseManagement: React.FC = () => {
|
||||||
{item.curso_nome || "-"}
|
{item.curso_nome || "-"}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</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">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-sm text-gray-600">
|
||||||
{item.instituicao || "-"}
|
{item.instituicao || "-"}
|
||||||
|
|
@ -234,6 +299,11 @@ export const CourseManagement: React.FC = () => {
|
||||||
{item.estado || "-"}
|
{item.estado || "-"}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</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">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-sm text-gray-600">
|
||||||
{item.gastos_captacao
|
{item.gastos_captacao
|
||||||
|
|
@ -255,12 +325,22 @@ export const CourseManagement: React.FC = () => {
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-center">
|
<td className="px-6 py-4 whitespace-nowrap text-center">
|
||||||
<button
|
<div className="flex items-center justify-center gap-2">
|
||||||
className="p-1.5 text-blue-600 hover:bg-blue-50 rounded transition-colors"
|
<button
|
||||||
title="Editar"
|
onClick={() => handleEdit(item)}
|
||||||
>
|
className="p-1.5 text-blue-600 hover:bg-blue-50 rounded transition-colors"
|
||||||
<Edit size={16} />
|
title="Editar"
|
||||||
</button>
|
>
|
||||||
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useEffect, useMemo } from "react";
|
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 { EventTable } from "../components/EventTable";
|
||||||
import { EventFiltersBar, EventFilters } from "../components/EventFiltersBar";
|
import { EventFiltersBar, EventFilters } from "../components/EventFiltersBar";
|
||||||
import { EventForm } from "../components/EventForm";
|
import { EventForm } from "../components/EventForm";
|
||||||
|
|
@ -27,124 +27,7 @@ interface DashboardProps {
|
||||||
initialView?: "list" | "create";
|
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> = ({
|
export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
initialView = "list",
|
initialView = "list",
|
||||||
|
|
@ -156,8 +39,10 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
addEvent,
|
addEvent,
|
||||||
updateEventStatus,
|
updateEventStatus,
|
||||||
assignPhotographer,
|
assignPhotographer,
|
||||||
|
professionals,
|
||||||
getInstitutionById,
|
getInstitutionById,
|
||||||
getActiveCoursesByInstitutionId,
|
getActiveCoursesByInstitutionId,
|
||||||
|
respondToAssignment,
|
||||||
} = useData();
|
} = useData();
|
||||||
const [view, setView] = useState<"list" | "create" | "edit" | "details">(
|
const [view, setView] = useState<"list" | "create" | "edit" | "details">(
|
||||||
initialView
|
initialView
|
||||||
|
|
@ -186,6 +71,11 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
|
|
||||||
const myEvents = getEventsByRole(user.id, user.role);
|
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
|
// Extract unique values for filters
|
||||||
const { availableTypes } = useMemo(() => {
|
const { availableTypes } = useMemo(() => {
|
||||||
const types = [...new Set(myEvents.map((e) => e.type))].sort();
|
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) => {
|
const handleApprove = (e: React.MouseEvent, eventId: string) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const event = events.find((e) => e.id === eventId);
|
updateEventStatus(eventId, EventStatus.CONFIRMED);
|
||||||
if (event) {
|
};
|
||||||
setSelectedEvent(event);
|
|
||||||
setView("details");
|
const handleAssignmentResponse = async (
|
||||||
setIsTeamModalOpen(true);
|
e: React.MouseEvent,
|
||||||
}
|
eventId: string,
|
||||||
|
status: string,
|
||||||
|
reason?: string
|
||||||
|
) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
await respondToAssignment(eventId, status, reason);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOpenMaps = () => {
|
const handleOpenMaps = () => {
|
||||||
|
|
@ -279,8 +184,6 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
const togglePhotographer = (photographerId: string) => {
|
const togglePhotographer = (photographerId: string) => {
|
||||||
if (!selectedEvent) return;
|
if (!selectedEvent) return;
|
||||||
assignPhotographer(selectedEvent.id, photographerId);
|
assignPhotographer(selectedEvent.id, photographerId);
|
||||||
const updated = events.find((e) => e.id === selectedEvent.id);
|
|
||||||
if (updated) setSelectedEvent(updated);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- RENDERS PER ROLE ---
|
// --- RENDERS PER ROLE ---
|
||||||
|
|
@ -372,8 +275,8 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveFilter("all")}
|
onClick={() => setActiveFilter("all")}
|
||||||
className={`px-3 py-1 text-xs font-medium rounded-sm ${activeFilter === "all"
|
className={`px-3 py-1 text-xs font-medium rounded-sm ${activeFilter === "all"
|
||||||
? "bg-brand-black text-white"
|
? "bg-brand-black text-white"
|
||||||
: "text-gray-600 hover:bg-gray-100"
|
: "text-gray-600 hover:bg-gray-100"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Todos
|
Todos
|
||||||
|
|
@ -381,8 +284,8 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveFilter("pending")}
|
onClick={() => setActiveFilter("pending")}
|
||||||
className={`px-3 py-1 text-xs font-medium rounded-sm flex items-center ${activeFilter === "pending"
|
className={`px-3 py-1 text-xs font-medium rounded-sm flex items-center ${activeFilter === "pending"
|
||||||
? "bg-brand-gold text-white"
|
? "bg-brand-gold text-white"
|
||||||
: "text-gray-600 hover:bg-gray-100"
|
: "text-gray-600 hover:bg-gray-100"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Clock size={12} className="mr-1" /> Pendentes
|
<Clock size={12} className="mr-1" /> Pendentes
|
||||||
|
|
@ -418,6 +321,8 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
}}
|
}}
|
||||||
onApprove={handleApprove}
|
onApprove={handleApprove}
|
||||||
userRole={user.role}
|
userRole={user.role}
|
||||||
|
currentProfessionalId={currentProfessionalId}
|
||||||
|
onAssignmentResponse={handleAssignmentResponse}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -852,7 +757,7 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
{selectedEvent.photographerIds.length > 0 ? (
|
{selectedEvent.photographerIds.length > 0 ? (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{selectedEvent.photographerIds.map((id) => {
|
{selectedEvent.photographerIds.map((id) => {
|
||||||
const photographer = MOCK_PHOTOGRAPHERS.find(
|
const photographer = professionals.find(
|
||||||
(p) => p.id === id
|
(p) => p.id === id
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
|
|
@ -952,23 +857,12 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{MOCK_PHOTOGRAPHERS.filter((photographer) => {
|
{professionals.map((photographer) => {
|
||||||
const isAssigned =
|
const isAssigned =
|
||||||
selectedEvent.photographerIds.includes(
|
selectedEvent.photographerIds.includes(
|
||||||
photographer.id
|
photographer.id
|
||||||
);
|
);
|
||||||
const isAvailable =
|
const isAvailable = true;
|
||||||
photographer.availability[selectedEvent.date] ??
|
|
||||||
false;
|
|
||||||
return isAvailable || isAssigned;
|
|
||||||
}).map((photographer) => {
|
|
||||||
const isAssigned =
|
|
||||||
selectedEvent.photographerIds.includes(
|
|
||||||
photographer.id
|
|
||||||
);
|
|
||||||
const isAvailable =
|
|
||||||
photographer.availability[selectedEvent.date] ??
|
|
||||||
false;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr
|
<tr
|
||||||
|
|
@ -1031,10 +925,10 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
}
|
}
|
||||||
disabled={!isAvailable && !isAssigned}
|
disabled={!isAvailable && !isAssigned}
|
||||||
className={`px-4 py-2 rounded-lg font-medium text-sm transition-colors ${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"
|
? "bg-red-100 text-red-700 hover:bg-red-200"
|
||||||
: isAvailable
|
: isAvailable
|
||||||
? "bg-brand-gold text-white hover:bg-[#a5bd2e]"
|
? "bg-brand-gold text-white hover:bg-[#a5bd2e]"
|
||||||
: "bg-gray-100 text-gray-400 cursor-not-allowed"
|
: "bg-gray-100 text-gray-400 cursor-not-allowed"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{isAssigned ? "Remover" : "Adicionar"}
|
{isAssigned ? "Remover" : "Adicionar"}
|
||||||
|
|
@ -1043,28 +937,22 @@ export const Dashboard: React.FC<DashboardProps> = ({
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{MOCK_PHOTOGRAPHERS.filter((p) => {
|
{professionals.length === 0 && (
|
||||||
const isAssigned =
|
<tr>
|
||||||
selectedEvent.photographerIds.includes(p.id);
|
<td colSpan={5} className="p-8 text-center">
|
||||||
const isAvailable =
|
<div className="flex flex-col items-center gap-3">
|
||||||
p.availability[selectedEvent.date] ?? false;
|
<UserX size={48} className="text-gray-300" />
|
||||||
return isAvailable || isAssigned;
|
<p className="text-gray-500 font-medium">
|
||||||
}).length === 0 && (
|
Nenhum profissional disponível para esta data
|
||||||
<tr>
|
</p>
|
||||||
<td colSpan={5} className="p-8 text-center">
|
<p className="text-sm text-gray-400">
|
||||||
<div className="flex flex-col items-center gap-3">
|
Tente selecionar outra data ou entre em contato
|
||||||
<UserX size={48} className="text-gray-300" />
|
com a equipe
|
||||||
<p className="text-gray-500 font-medium">
|
</p>
|
||||||
Nenhum profissional disponível para esta data
|
</div>
|
||||||
</p>
|
</td>
|
||||||
<p className="text-sm text-gray-400">
|
</tr>
|
||||||
Tente selecionar outra data ou entre em contato
|
)}
|
||||||
com a equipe
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</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 {
|
export interface EventTypeResponse {
|
||||||
id: string;
|
id: string;
|
||||||
nome: 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)
|
* 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;
|
required: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AssignmentStatus {
|
||||||
|
PENDING = "PENDENTE",
|
||||||
|
ACCEPTED = "ACEITO",
|
||||||
|
REJECTED = "REJEITADO",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Assignment {
|
||||||
|
professionalId: string;
|
||||||
|
status: AssignmentStatus;
|
||||||
|
reason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface EventData {
|
export interface EventData {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -128,4 +140,17 @@ export interface EventData {
|
||||||
empresa?: string; // Nome da Empresa
|
empresa?: string; // Nome da Empresa
|
||||||
observacoes?: string; // Observações da FOT
|
observacoes?: string; // Observações da FOT
|
||||||
tipoEventoNome?: string; // Nome do Tipo de Evento
|
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