feat(backend): implementa módulo Agenda com lógica automática de status

- Cria tabela 'agenda' com FKs normalizadas e colunas específicas de evento
- Gera operações CRUD via SQLC para Agenda
- Implementa Service e Handler da Agenda
- Adiciona cálculo automático de 'status_profissionais' (OK/FALTA/ERRO) baseado na contagem de faltantes
- Registra rotas da Agenda em /api/agenda
- Atualiza documentação Swagger
- Corrige caminhos de importação e tipagem UUID no novo serviço
This commit is contained in:
NANDO9322 2025-12-15 18:49:59 -03:00
parent d84d6ff022
commit a1a4d0a5d1
11 changed files with 1764 additions and 0 deletions

View file

@ -5,6 +5,7 @@ import (
"log" "log"
"photum-backend/docs" "photum-backend/docs"
"photum-backend/internal/agenda"
"photum-backend/internal/anos_formaturas" "photum-backend/internal/anos_formaturas"
"photum-backend/internal/auth" "photum-backend/internal/auth"
"photum-backend/internal/cadastro_fot" "photum-backend/internal/cadastro_fot"
@ -68,6 +69,7 @@ func main() {
tiposServicosService := tipos_servicos.NewService(queries) tiposServicosService := tipos_servicos.NewService(queries)
tiposEventosService := tipos_eventos.NewService(queries) tiposEventosService := tipos_eventos.NewService(queries)
cadastroFotService := cadastro_fot.NewService(queries) cadastroFotService := cadastro_fot.NewService(queries)
agendaService := agenda.NewService(queries)
// Seed Demo Users // Seed Demo Users
if err := authService.EnsureDemoUsers(context.Background()); err != nil { if err := authService.EnsureDemoUsers(context.Background()); err != nil {
@ -84,6 +86,7 @@ func main() {
tiposServicosHandler := tipos_servicos.NewHandler(tiposServicosService) tiposServicosHandler := tipos_servicos.NewHandler(tiposServicosService)
tiposEventosHandler := tipos_eventos.NewHandler(tiposEventosService) tiposEventosHandler := tipos_eventos.NewHandler(tiposEventosService)
cadastroFotHandler := cadastro_fot.NewHandler(cadastroFotService) cadastroFotHandler := cadastro_fot.NewHandler(cadastroFotService)
agendaHandler := agenda.NewHandler(agendaService)
r := gin.Default() r := gin.Default()
@ -182,6 +185,12 @@ func main() {
api.PUT("/cadastro-fot/:id", cadastroFotHandler.Update) api.PUT("/cadastro-fot/:id", cadastroFotHandler.Update)
api.DELETE("/cadastro-fot/:id", cadastroFotHandler.Delete) api.DELETE("/cadastro-fot/:id", cadastroFotHandler.Delete)
api.GET("/agenda", agendaHandler.List)
api.POST("/agenda", agendaHandler.Create)
api.GET("/agenda/:id", agendaHandler.Get)
api.PUT("/agenda/:id", agendaHandler.Update)
api.DELETE("/agenda/:id", agendaHandler.Delete)
admin := api.Group("/admin") admin := api.Group("/admin")
{ {
admin.GET("/users", authHandler.ListUsers) admin.GET("/users", authHandler.ListUsers)

View file

@ -410,6 +410,247 @@ const docTemplate = `{
} }
} }
}, },
"/api/agenda": {
"get": {
"description": "List all agenda events with details",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "List all agenda events",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
},
"post": {
"description": "Create a new agenda event",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "Create a new agenda event",
"parameters": [
{
"description": "Create Agenda Request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/agenda.CreateAgendaRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/api/agenda/{id}": {
"get": {
"description": "Get agenda event details by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "Get agenda event by ID",
"parameters": [
{
"type": "string",
"description": "Agenda ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
},
"put": {
"description": "Update agenda event by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "Update agenda event",
"parameters": [
{
"type": "string",
"description": "Agenda ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Update Agenda Request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/agenda.CreateAgendaRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
},
"delete": {
"description": "Delete agenda event by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "Delete agenda event",
"parameters": [
{
"type": "string",
"description": "Agenda ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/api/anos-formaturas": { "/api/anos-formaturas": {
"get": { "get": {
"security": [ "security": [
@ -2164,6 +2405,80 @@ const docTemplate = `{
} }
}, },
"definitions": { "definitions": {
"agenda.CreateAgendaRequest": {
"type": "object",
"properties": {
"cine_faltante": {
"type": "integer"
},
"data_evento": {
"type": "string"
},
"endereco": {
"type": "string"
},
"fot_id": {
"type": "string"
},
"foto_faltante": {
"type": "integer"
},
"horario": {
"type": "string"
},
"local_evento": {
"type": "string"
},
"logistica_observacoes": {
"type": "string"
},
"observacoes_evento": {
"type": "string"
},
"pre_venda": {
"type": "boolean"
},
"qtd_cinegrafistas": {
"type": "integer"
},
"qtd_estudios": {
"type": "integer"
},
"qtd_formandos": {
"type": "integer"
},
"qtd_fotografos": {
"type": "integer"
},
"qtd_plataforma_360": {
"type": "integer"
},
"qtd_ponto_decorado": {
"type": "integer"
},
"qtd_ponto_foto": {
"type": "integer"
},
"qtd_ponto_id": {
"type": "integer"
},
"qtd_pontos_led": {
"type": "integer"
},
"qtd_recepcionistas": {
"type": "integer"
},
"recep_faltante": {
"type": "integer"
},
"status_profissionais": {
"type": "string"
},
"tipo_evento_id": {
"type": "string"
}
}
},
"anos_formaturas.AnoFormaturaResponse": { "anos_formaturas.AnoFormaturaResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View file

@ -404,6 +404,247 @@
} }
} }
}, },
"/api/agenda": {
"get": {
"description": "List all agenda events with details",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "List all agenda events",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
},
"post": {
"description": "Create a new agenda event",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "Create a new agenda event",
"parameters": [
{
"description": "Create Agenda Request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/agenda.CreateAgendaRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/api/agenda/{id}": {
"get": {
"description": "Get agenda event details by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "Get agenda event by ID",
"parameters": [
{
"type": "string",
"description": "Agenda ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
},
"put": {
"description": "Update agenda event by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "Update agenda event",
"parameters": [
{
"type": "string",
"description": "Agenda ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Update Agenda Request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/agenda.CreateAgendaRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
},
"delete": {
"description": "Delete agenda event by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"agenda"
],
"summary": "Delete agenda event",
"parameters": [
{
"type": "string",
"description": "Agenda ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/api/anos-formaturas": { "/api/anos-formaturas": {
"get": { "get": {
"security": [ "security": [
@ -2158,6 +2399,80 @@
} }
}, },
"definitions": { "definitions": {
"agenda.CreateAgendaRequest": {
"type": "object",
"properties": {
"cine_faltante": {
"type": "integer"
},
"data_evento": {
"type": "string"
},
"endereco": {
"type": "string"
},
"fot_id": {
"type": "string"
},
"foto_faltante": {
"type": "integer"
},
"horario": {
"type": "string"
},
"local_evento": {
"type": "string"
},
"logistica_observacoes": {
"type": "string"
},
"observacoes_evento": {
"type": "string"
},
"pre_venda": {
"type": "boolean"
},
"qtd_cinegrafistas": {
"type": "integer"
},
"qtd_estudios": {
"type": "integer"
},
"qtd_formandos": {
"type": "integer"
},
"qtd_fotografos": {
"type": "integer"
},
"qtd_plataforma_360": {
"type": "integer"
},
"qtd_ponto_decorado": {
"type": "integer"
},
"qtd_ponto_foto": {
"type": "integer"
},
"qtd_ponto_id": {
"type": "integer"
},
"qtd_pontos_led": {
"type": "integer"
},
"qtd_recepcionistas": {
"type": "integer"
},
"recep_faltante": {
"type": "integer"
},
"status_profissionais": {
"type": "string"
},
"tipo_evento_id": {
"type": "string"
}
}
},
"anos_formaturas.AnoFormaturaResponse": { "anos_formaturas.AnoFormaturaResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View file

@ -1,5 +1,54 @@
basePath: / basePath: /
definitions: definitions:
agenda.CreateAgendaRequest:
properties:
cine_faltante:
type: integer
data_evento:
type: string
endereco:
type: string
fot_id:
type: string
foto_faltante:
type: integer
horario:
type: string
local_evento:
type: string
logistica_observacoes:
type: string
observacoes_evento:
type: string
pre_venda:
type: boolean
qtd_cinegrafistas:
type: integer
qtd_estudios:
type: integer
qtd_formandos:
type: integer
qtd_fotografos:
type: integer
qtd_plataforma_360:
type: integer
qtd_ponto_decorado:
type: integer
qtd_ponto_foto:
type: integer
qtd_ponto_id:
type: integer
qtd_pontos_led:
type: integer
qtd_recepcionistas:
type: integer
recep_faltante:
type: integer
status_profissionais:
type: string
tipo_evento_id:
type: string
type: object
anos_formaturas.AnoFormaturaResponse: anos_formaturas.AnoFormaturaResponse:
properties: properties:
ano_semestre: ano_semestre:
@ -650,6 +699,167 @@ paths:
summary: List pending users summary: List pending users
tags: tags:
- admin - admin
/api/agenda:
get:
consumes:
- application/json
description: List all agenda events with details
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
additionalProperties: true
type: object
type: array
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
summary: List all agenda events
tags:
- agenda
post:
consumes:
- application/json
description: Create a new agenda event
parameters:
- description: Create Agenda Request
in: body
name: request
required: true
schema:
$ref: '#/definitions/agenda.CreateAgendaRequest'
produces:
- application/json
responses:
"201":
description: Created
schema:
additionalProperties: true
type: object
"400":
description: Bad Request
schema:
additionalProperties:
type: string
type: object
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
summary: Create a new agenda event
tags:
- agenda
/api/agenda/{id}:
delete:
consumes:
- application/json
description: Delete agenda event by ID
parameters:
- description: Agenda ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"204":
description: No Content
"400":
description: Bad Request
schema:
additionalProperties:
type: string
type: object
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
summary: Delete agenda event
tags:
- agenda
get:
consumes:
- application/json
description: Get agenda event details by ID
parameters:
- description: Agenda ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
"400":
description: Bad Request
schema:
additionalProperties:
type: string
type: object
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
summary: Get agenda event by ID
tags:
- agenda
put:
consumes:
- application/json
description: Update agenda event by ID
parameters:
- description: Agenda ID
in: path
name: id
required: true
type: string
- description: Update Agenda Request
in: body
name: request
required: true
schema:
$ref: '#/definitions/agenda.CreateAgendaRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
"400":
description: Bad Request
schema:
additionalProperties:
type: string
type: object
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
summary: Update agenda event
tags:
- agenda
/api/anos-formaturas: /api/anos-formaturas:
get: get:
consumes: consumes:

View file

@ -0,0 +1,152 @@
package agenda
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type Handler struct {
service *Service
}
func NewHandler(service *Service) *Handler {
return &Handler{service: service}
}
// Create godoc
// @Summary Create a new agenda event
// @Description Create a new agenda event
// @Tags agenda
// @Accept json
// @Produce json
// @Param request body CreateAgendaRequest true "Create Agenda Request"
// @Success 201 {object} map[string]interface{}
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/agenda [post]
func (h *Handler) Create(c *gin.Context) {
var req CreateAgendaRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Dados inválidos: " + err.Error()})
return
}
agenda, err := h.service.Create(c.Request.Context(), req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao criar agenda: " + err.Error()})
return
}
c.JSON(http.StatusCreated, agenda)
}
// List godoc
// @Summary List all agenda events
// @Description List all agenda events with details
// @Tags agenda
// @Accept json
// @Produce json
// @Success 200 {array} map[string]interface{}
// @Failure 500 {object} map[string]string
// @Router /api/agenda [get]
func (h *Handler) List(c *gin.Context) {
agendas, err := h.service.List(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao listar agendas: " + err.Error()})
return
}
c.JSON(http.StatusOK, agendas)
}
// Get godoc
// @Summary Get agenda event by ID
// @Description Get agenda event details by ID
// @Tags agenda
// @Accept json
// @Produce json
// @Param id path string true "Agenda ID"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/agenda/{id} [get]
func (h *Handler) Get(c *gin.Context) {
idParam := c.Param("id")
id, err := uuid.Parse(idParam)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID inválido: " + err.Error()})
return
}
agenda, err := h.service.Get(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao buscar agenda: " + err.Error()})
return
}
c.JSON(http.StatusOK, agenda)
}
// Update godoc
// @Summary Update agenda event
// @Description Update agenda event by ID
// @Tags agenda
// @Accept json
// @Produce json
// @Param id path string true "Agenda ID"
// @Param request body CreateAgendaRequest true "Update Agenda Request"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/agenda/{id} [put]
func (h *Handler) Update(c *gin.Context) {
idParam := c.Param("id")
id, err := uuid.Parse(idParam)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID inválido: " + err.Error()})
return
}
var req CreateAgendaRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Dados inválidos: " + err.Error()})
return
}
agenda, err := h.service.Update(c.Request.Context(), id, req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao atualizar agenda: " + err.Error()})
return
}
c.JSON(http.StatusOK, agenda)
}
// Delete godoc
// @Summary Delete agenda event
// @Description Delete agenda event by ID
// @Tags agenda
// @Accept json
// @Produce json
// @Param id path string true "Agenda ID"
// @Success 204 {object} nil
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/agenda/{id} [delete]
func (h *Handler) Delete(c *gin.Context) {
idParam := c.Param("id")
id, err := uuid.Parse(idParam)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID inválido: " + err.Error()})
return
}
if err := h.service.Delete(c.Request.Context(), id); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erro ao deletar agenda: " + err.Error()})
return
}
c.Status(http.StatusNoContent)
}

View file

@ -0,0 +1,136 @@
package agenda
import (
"context"
"time"
"photum-backend/internal/db/generated"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
)
type Service struct {
queries *generated.Queries
}
func NewService(db *generated.Queries) *Service {
return &Service{queries: db}
}
type CreateAgendaRequest struct {
FotID uuid.UUID `json:"fot_id"`
DataEvento time.Time `json:"data_evento"`
TipoEventoID uuid.UUID `json:"tipo_evento_id"`
ObservacoesEvento string `json:"observacoes_evento"`
LocalEvento string `json:"local_evento"`
Endereco string `json:"endereco"`
Horario string `json:"horario"`
QtdFormandos int32 `json:"qtd_formandos"`
QtdFotografos int32 `json:"qtd_fotografos"`
QtdRecepcionistas int32 `json:"qtd_recepcionistas"`
QtdCinegrafistas int32 `json:"qtd_cinegrafistas"`
QtdEstudios int32 `json:"qtd_estudios"`
QtdPontoFoto int32 `json:"qtd_ponto_foto"`
QtdPontoID int32 `json:"qtd_ponto_id"`
QtdPontoDecorado int32 `json:"qtd_ponto_decorado"`
QtdPontosLed int32 `json:"qtd_pontos_led"`
QtdPlataforma360 int32 `json:"qtd_plataforma_360"`
StatusProfissionais string `json:"status_profissionais"`
FotoFaltante int32 `json:"foto_faltante"`
RecepFaltante int32 `json:"recep_faltante"`
CineFaltante int32 `json:"cine_faltante"`
LogisticaObservacoes string `json:"logistica_observacoes"`
PreVenda bool `json:"pre_venda"`
}
func (s *Service) CalculateStatus(fotoFaltante, recepFaltante, cineFaltante int32) string {
if fotoFaltante < 0 || recepFaltante < 0 || cineFaltante < 0 {
return "ERRO"
}
sum := fotoFaltante + recepFaltante + cineFaltante
if sum == 0 {
return "OK"
} else if sum > 0 {
return "FALTA"
}
return "ERRO"
}
func (s *Service) Create(ctx context.Context, req CreateAgendaRequest) (generated.Agenda, error) {
status := s.CalculateStatus(req.FotoFaltante, req.RecepFaltante, req.CineFaltante)
params := generated.CreateAgendaParams{
FotID: pgtype.UUID{Bytes: req.FotID, Valid: true},
DataEvento: pgtype.Date{Time: req.DataEvento, Valid: true},
TipoEventoID: pgtype.UUID{Bytes: req.TipoEventoID, Valid: true},
ObservacoesEvento: pgtype.Text{String: req.ObservacoesEvento, Valid: req.ObservacoesEvento != ""},
LocalEvento: pgtype.Text{String: req.LocalEvento, Valid: req.LocalEvento != ""},
Endereco: pgtype.Text{String: req.Endereco, Valid: req.Endereco != ""},
Horario: pgtype.Text{String: req.Horario, Valid: req.Horario != ""},
QtdFormandos: pgtype.Int4{Int32: req.QtdFormandos, Valid: true},
QtdFotografos: pgtype.Int4{Int32: req.QtdFotografos, Valid: true},
QtdRecepcionistas: pgtype.Int4{Int32: req.QtdRecepcionistas, Valid: true},
QtdCinegrafistas: pgtype.Int4{Int32: req.QtdCinegrafistas, Valid: true},
QtdEstudios: pgtype.Int4{Int32: req.QtdEstudios, Valid: true},
QtdPontoFoto: pgtype.Int4{Int32: req.QtdPontoFoto, Valid: true},
QtdPontoID: pgtype.Int4{Int32: req.QtdPontoID, Valid: true},
QtdPontoDecorado: pgtype.Int4{Int32: req.QtdPontoDecorado, Valid: true},
QtdPontosLed: pgtype.Int4{Int32: req.QtdPontosLed, Valid: true},
QtdPlataforma360: pgtype.Int4{Int32: req.QtdPlataforma360, Valid: true},
StatusProfissionais: pgtype.Text{String: status, Valid: true},
FotoFaltante: pgtype.Int4{Int32: req.FotoFaltante, Valid: true},
RecepFaltante: pgtype.Int4{Int32: req.RecepFaltante, Valid: true},
CineFaltante: pgtype.Int4{Int32: req.CineFaltante, Valid: true},
LogisticaObservacoes: pgtype.Text{String: req.LogisticaObservacoes, Valid: req.LogisticaObservacoes != ""},
PreVenda: pgtype.Bool{Bool: req.PreVenda, Valid: true},
}
return s.queries.CreateAgenda(ctx, params)
}
func (s *Service) List(ctx context.Context) ([]generated.ListAgendasRow, error) {
return s.queries.ListAgendas(ctx)
}
func (s *Service) Get(ctx context.Context, id uuid.UUID) (generated.Agenda, error) {
return s.queries.GetAgenda(ctx, pgtype.UUID{Bytes: id, Valid: true})
}
func (s *Service) Update(ctx context.Context, id uuid.UUID, req CreateAgendaRequest) (generated.Agenda, error) {
status := s.CalculateStatus(req.FotoFaltante, req.RecepFaltante, req.CineFaltante)
params := generated.UpdateAgendaParams{
ID: pgtype.UUID{Bytes: id, Valid: true},
FotID: pgtype.UUID{Bytes: req.FotID, Valid: true},
DataEvento: pgtype.Date{Time: req.DataEvento, Valid: true},
TipoEventoID: pgtype.UUID{Bytes: req.TipoEventoID, Valid: true},
ObservacoesEvento: pgtype.Text{String: req.ObservacoesEvento, Valid: req.ObservacoesEvento != ""},
LocalEvento: pgtype.Text{String: req.LocalEvento, Valid: req.LocalEvento != ""},
Endereco: pgtype.Text{String: req.Endereco, Valid: req.Endereco != ""},
Horario: pgtype.Text{String: req.Horario, Valid: req.Horario != ""},
QtdFormandos: pgtype.Int4{Int32: req.QtdFormandos, Valid: true},
QtdFotografos: pgtype.Int4{Int32: req.QtdFotografos, Valid: true},
QtdRecepcionistas: pgtype.Int4{Int32: req.QtdRecepcionistas, Valid: true},
QtdCinegrafistas: pgtype.Int4{Int32: req.QtdCinegrafistas, Valid: true},
QtdEstudios: pgtype.Int4{Int32: req.QtdEstudios, Valid: true},
QtdPontoFoto: pgtype.Int4{Int32: req.QtdPontoFoto, Valid: true},
QtdPontoID: pgtype.Int4{Int32: req.QtdPontoID, Valid: true},
QtdPontoDecorado: pgtype.Int4{Int32: req.QtdPontoDecorado, Valid: true},
QtdPontosLed: pgtype.Int4{Int32: req.QtdPontosLed, Valid: true},
QtdPlataforma360: pgtype.Int4{Int32: req.QtdPlataforma360, Valid: true},
StatusProfissionais: pgtype.Text{String: status, Valid: true},
FotoFaltante: pgtype.Int4{Int32: req.FotoFaltante, Valid: true},
RecepFaltante: pgtype.Int4{Int32: req.RecepFaltante, Valid: true},
CineFaltante: pgtype.Int4{Int32: req.CineFaltante, Valid: true},
LogisticaObservacoes: pgtype.Text{String: req.LogisticaObservacoes, Valid: req.LogisticaObservacoes != ""},
PreVenda: pgtype.Bool{Bool: req.PreVenda, Valid: true},
}
return s.queries.UpdateAgenda(ctx, params)
}
func (s *Service) Delete(ctx context.Context, id uuid.UUID) error {
return s.queries.DeleteAgenda(ctx, pgtype.UUID{Bytes: id, Valid: true})
}

View file

@ -0,0 +1,401 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: agenda.sql
package generated
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const createAgenda = `-- name: CreateAgenda :one
INSERT INTO agenda (
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
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23
) RETURNING 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
`
type CreateAgendaParams struct {
FotID pgtype.UUID `json:"fot_id"`
DataEvento pgtype.Date `json:"data_evento"`
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
LocalEvento pgtype.Text `json:"local_evento"`
Endereco pgtype.Text `json:"endereco"`
Horario pgtype.Text `json:"horario"`
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
StatusProfissionais pgtype.Text `json:"status_profissionais"`
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
CineFaltante pgtype.Int4 `json:"cine_faltante"`
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
PreVenda pgtype.Bool `json:"pre_venda"`
}
func (q *Queries) CreateAgenda(ctx context.Context, arg CreateAgendaParams) (Agenda, error) {
row := q.db.QueryRow(ctx, createAgenda,
arg.FotID,
arg.DataEvento,
arg.TipoEventoID,
arg.ObservacoesEvento,
arg.LocalEvento,
arg.Endereco,
arg.Horario,
arg.QtdFormandos,
arg.QtdFotografos,
arg.QtdRecepcionistas,
arg.QtdCinegrafistas,
arg.QtdEstudios,
arg.QtdPontoFoto,
arg.QtdPontoID,
arg.QtdPontoDecorado,
arg.QtdPontosLed,
arg.QtdPlataforma360,
arg.StatusProfissionais,
arg.FotoFaltante,
arg.RecepFaltante,
arg.CineFaltante,
arg.LogisticaObservacoes,
arg.PreVenda,
)
var i Agenda
err := row.Scan(
&i.ID,
&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,
)
return i, err
}
const deleteAgenda = `-- name: DeleteAgenda :exec
DELETE FROM agenda
WHERE id = $1
`
func (q *Queries) DeleteAgenda(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, deleteAgenda, id)
return err
}
const getAgenda = `-- name: GetAgenda :one
SELECT 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
WHERE id = $1 LIMIT 1
`
func (q *Queries) GetAgenda(ctx context.Context, id pgtype.UUID) (Agenda, error) {
row := q.db.QueryRow(ctx, getAgenda, id)
var i Agenda
err := row.Scan(
&i.ID,
&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,
)
return i, err
}
const listAgendas = `-- name: ListAgendas :many
SELECT
a.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,
cf.fot as fot_numero,
cf.instituicao,
c.nome as curso_nome,
e.nome as empresa_nome,
af.ano_semestre,
cf.observacoes as observacoes_fot,
te.nome as tipo_evento_nome
FROM agenda a
JOIN cadastro_fot cf ON a.fot_id = cf.id
JOIN cursos c ON cf.curso_id = c.id
JOIN empresas e ON cf.empresa_id = e.id
JOIN anos_formaturas af ON cf.ano_formatura_id = af.id
JOIN tipos_eventos te ON a.tipo_evento_id = te.id
ORDER BY a.data_evento
`
type ListAgendasRow struct {
ID pgtype.UUID `json:"id"`
FotID pgtype.UUID `json:"fot_id"`
DataEvento pgtype.Date `json:"data_evento"`
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
LocalEvento pgtype.Text `json:"local_evento"`
Endereco pgtype.Text `json:"endereco"`
Horario pgtype.Text `json:"horario"`
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
StatusProfissionais pgtype.Text `json:"status_profissionais"`
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
CineFaltante pgtype.Int4 `json:"cine_faltante"`
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
PreVenda pgtype.Bool `json:"pre_venda"`
CriadoEm pgtype.Timestamptz `json:"criado_em"`
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
FotNumero int32 `json:"fot_numero"`
Instituicao pgtype.Text `json:"instituicao"`
CursoNome string `json:"curso_nome"`
EmpresaNome string `json:"empresa_nome"`
AnoSemestre string `json:"ano_semestre"`
ObservacoesFot pgtype.Text `json:"observacoes_fot"`
TipoEventoNome string `json:"tipo_evento_nome"`
}
func (q *Queries) ListAgendas(ctx context.Context) ([]ListAgendasRow, error) {
rows, err := q.db.Query(ctx, listAgendas)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ListAgendasRow
for rows.Next() {
var i ListAgendasRow
if err := rows.Scan(
&i.ID,
&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.FotNumero,
&i.Instituicao,
&i.CursoNome,
&i.EmpresaNome,
&i.AnoSemestre,
&i.ObservacoesFot,
&i.TipoEventoNome,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateAgenda = `-- name: UpdateAgenda :one
UPDATE agenda
SET
fot_id = $2,
data_evento = $3,
tipo_evento_id = $4,
observacoes_evento = $5,
local_evento = $6,
endereco = $7,
horario = $8,
qtd_formandos = $9,
qtd_fotografos = $10,
qtd_recepcionistas = $11,
qtd_cinegrafistas = $12,
qtd_estudios = $13,
qtd_ponto_foto = $14,
qtd_ponto_id = $15,
qtd_ponto_decorado = $16,
qtd_pontos_led = $17,
qtd_plataforma_360 = $18,
status_profissionais = $19,
foto_faltante = $20,
recep_faltante = $21,
cine_faltante = $22,
logistica_observacoes = $23,
pre_venda = $24,
atualizado_em = NOW()
WHERE id = $1
RETURNING 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
`
type UpdateAgendaParams struct {
ID pgtype.UUID `json:"id"`
FotID pgtype.UUID `json:"fot_id"`
DataEvento pgtype.Date `json:"data_evento"`
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
LocalEvento pgtype.Text `json:"local_evento"`
Endereco pgtype.Text `json:"endereco"`
Horario pgtype.Text `json:"horario"`
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
StatusProfissionais pgtype.Text `json:"status_profissionais"`
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
CineFaltante pgtype.Int4 `json:"cine_faltante"`
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
PreVenda pgtype.Bool `json:"pre_venda"`
}
func (q *Queries) UpdateAgenda(ctx context.Context, arg UpdateAgendaParams) (Agenda, error) {
row := q.db.QueryRow(ctx, updateAgenda,
arg.ID,
arg.FotID,
arg.DataEvento,
arg.TipoEventoID,
arg.ObservacoesEvento,
arg.LocalEvento,
arg.Endereco,
arg.Horario,
arg.QtdFormandos,
arg.QtdFotografos,
arg.QtdRecepcionistas,
arg.QtdCinegrafistas,
arg.QtdEstudios,
arg.QtdPontoFoto,
arg.QtdPontoID,
arg.QtdPontoDecorado,
arg.QtdPontosLed,
arg.QtdPlataforma360,
arg.StatusProfissionais,
arg.FotoFaltante,
arg.RecepFaltante,
arg.CineFaltante,
arg.LogisticaObservacoes,
arg.PreVenda,
)
var i Agenda
err := row.Scan(
&i.ID,
&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,
)
return i, err
}

View file

@ -8,6 +8,35 @@ import (
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
type Agenda struct {
ID pgtype.UUID `json:"id"`
FotID pgtype.UUID `json:"fot_id"`
DataEvento pgtype.Date `json:"data_evento"`
TipoEventoID pgtype.UUID `json:"tipo_evento_id"`
ObservacoesEvento pgtype.Text `json:"observacoes_evento"`
LocalEvento pgtype.Text `json:"local_evento"`
Endereco pgtype.Text `json:"endereco"`
Horario pgtype.Text `json:"horario"`
QtdFormandos pgtype.Int4 `json:"qtd_formandos"`
QtdFotografos pgtype.Int4 `json:"qtd_fotografos"`
QtdRecepcionistas pgtype.Int4 `json:"qtd_recepcionistas"`
QtdCinegrafistas pgtype.Int4 `json:"qtd_cinegrafistas"`
QtdEstudios pgtype.Int4 `json:"qtd_estudios"`
QtdPontoFoto pgtype.Int4 `json:"qtd_ponto_foto"`
QtdPontoID pgtype.Int4 `json:"qtd_ponto_id"`
QtdPontoDecorado pgtype.Int4 `json:"qtd_ponto_decorado"`
QtdPontosLed pgtype.Int4 `json:"qtd_pontos_led"`
QtdPlataforma360 pgtype.Int4 `json:"qtd_plataforma_360"`
StatusProfissionais pgtype.Text `json:"status_profissionais"`
FotoFaltante pgtype.Int4 `json:"foto_faltante"`
RecepFaltante pgtype.Int4 `json:"recep_faltante"`
CineFaltante pgtype.Int4 `json:"cine_faltante"`
LogisticaObservacoes pgtype.Text `json:"logistica_observacoes"`
PreVenda pgtype.Bool `json:"pre_venda"`
CriadoEm pgtype.Timestamptz `json:"criado_em"`
AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"`
}
type AnosFormatura struct { type AnosFormatura struct {
ID pgtype.UUID `json:"id"` ID pgtype.UUID `json:"id"`
AnoSemestre string `json:"ano_semestre"` AnoSemestre string `json:"ano_semestre"`

View file

@ -0,0 +1,84 @@
-- name: CreateAgenda :one
INSERT INTO agenda (
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
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23
) RETURNING *;
-- name: GetAgenda :one
SELECT * FROM agenda
WHERE id = $1 LIMIT 1;
-- name: ListAgendas :many
SELECT
a.*,
cf.fot as fot_numero,
cf.instituicao,
c.nome as curso_nome,
e.nome as empresa_nome,
af.ano_semestre,
cf.observacoes as observacoes_fot,
te.nome as tipo_evento_nome
FROM agenda a
JOIN cadastro_fot cf ON a.fot_id = cf.id
JOIN cursos c ON cf.curso_id = c.id
JOIN empresas e ON cf.empresa_id = e.id
JOIN anos_formaturas af ON cf.ano_formatura_id = af.id
JOIN tipos_eventos te ON a.tipo_evento_id = te.id
ORDER BY a.data_evento;
-- name: UpdateAgenda :one
UPDATE agenda
SET
fot_id = $2,
data_evento = $3,
tipo_evento_id = $4,
observacoes_evento = $5,
local_evento = $6,
endereco = $7,
horario = $8,
qtd_formandos = $9,
qtd_fotografos = $10,
qtd_recepcionistas = $11,
qtd_cinegrafistas = $12,
qtd_estudios = $13,
qtd_ponto_foto = $14,
qtd_ponto_id = $15,
qtd_ponto_decorado = $16,
qtd_pontos_led = $17,
qtd_plataforma_360 = $18,
status_profissionais = $19,
foto_faltante = $20,
recep_faltante = $21,
cine_faltante = $22,
logistica_observacoes = $23,
pre_venda = $24,
atualizado_em = NOW()
WHERE id = $1
RETURNING *;
-- name: DeleteAgenda :exec
DELETE FROM agenda
WHERE id = $1;

View file

@ -313,3 +313,32 @@ CREATE TABLE IF NOT EXISTS cadastro_clientes (
atualizado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(), atualizado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(usuario_id) UNIQUE(usuario_id)
); );
-- Agenda Table
CREATE TABLE IF NOT EXISTS agenda (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
fot_id UUID NOT NULL REFERENCES cadastro_fot(id) ON DELETE CASCADE,
data_evento DATE NOT NULL,
tipo_evento_id UUID NOT NULL REFERENCES tipos_eventos(id),
observacoes_evento TEXT,
local_evento VARCHAR(255),
endereco VARCHAR(255),
horario VARCHAR(20),
qtd_formandos INTEGER DEFAULT 0,
qtd_fotografos INTEGER DEFAULT 0,
qtd_recepcionistas INTEGER DEFAULT 0,
qtd_cinegrafistas INTEGER DEFAULT 0,
qtd_estudios INTEGER DEFAULT 0,
qtd_ponto_foto INTEGER DEFAULT 0,
qtd_ponto_id INTEGER DEFAULT 0,
qtd_ponto_decorado INTEGER DEFAULT 0,
qtd_pontos_led INTEGER DEFAULT 0,
qtd_plataforma_360 INTEGER DEFAULT 0,
status_profissionais VARCHAR(20) DEFAULT 'OK', -- OK, FALTA, ERRO
foto_faltante INTEGER DEFAULT 0,
recep_faltante INTEGER DEFAULT 0,
cine_faltante INTEGER DEFAULT 0,
logistica_observacoes TEXT,
pre_venda BOOLEAN DEFAULT FALSE,
criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(),
atualizado_em TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

84
project_documentation.md Normal file
View file

@ -0,0 +1,84 @@
# Documentação do Projeto Photum - Sessão de Desenvolvimento
Esta documentação resume todo o trabalho realizado durante a sessão atual de desenvolvimento, focada na implementação do Backend e ajustes no Frontend para o sistema Photum.
## 1. Visão Geral
O objetivo principal foi fortalecer o backend em Go, implementar autenticação segura, e criar as estruturas para gerenciamento de FOT (Formandos, Organização, Turma) e Agenda de Eventos.
## 2. Implementações no Backend (Go + PostgreSQL)
### 2.1. Sistema de Autenticação e Usuários
- **JWT & Refresh Tokens**: Implementação de autenticação baseada em tokens JWT curta duração e Refresh Tokens seguros.
- **Hierarquia de Permissões (RBAC)**: Definição de roles padronizadas: ROLES Backend (Strings no Banco de Dados):
admin ⮕ Mapeado para SUPERADMIN
empresa ⮕ Mapeado para BUSINESS_OWNER
cliente ⮕ Mapeado para EVENT_OWNER
profissional ⮕ Mapeado para PHOTOGRAPHER
admin@photum.com -> SUPERADMIN
empresa@photum.com -> BUSINESS_OWNER
foto@photum.com -> PHOTOGRAPHER
- **Fluxo de Aprovação**:
- Usuários se registram com status `ativo=false`.
- Admin lista usuários pendentes (`/admin/users/pending`).
- Admin aprova e define a Role (`/admin/users/:id/approve` e `/admin/users/:id/role`).
- Bloqueio de login para usuários inativos.
- **Dados do Usuário**: Adição de campos `Nome`, `Telefone` e `Empresa` no registro e listagens.
### 2.2. Tabelas Auxiliares (CRUDs)
Criadas tabelas, Services e Handlers para as entidades básicas do sistema:
- `cursos`
- `empresas`
- `anos_formaturas`
- `tipos_eventos` (incluindo tabela de preços por função)
- `tipos_servicos`
- `funcoes_profissionais`
### 2.3. Módulo Cadastro FOT
Responsável por vincular Turmas a Empresas e Instituições.
- **Tabela**: `cadastro_fot` (Campos: `fot`, `empresa_id`, `curso_id`, `ano_formatura_id`, `instituicao`, `observacoes`, etc.).
- **Backend**:
- Endpoint `POST /api/cadastro-fot`: Criação com verificação de FOT duplicado.
- Endpoint `GET /api/cadastro-fot`: Listagem com _joins_ para trazer nomes de Empresa, Curso, etc.
- **Integração**: Conectado ao `CourseManagement.tsx` no frontend.
### 2.4. Módulo Agenda
Responsável pelo agendamento de eventos vinculados a um FOT.
- **Tabela**: `agenda` criada com uma estrutura normalizada, mas com campos específicos de evento.
- Vinculada a `cadastro_fot` via `fot_id`.
- Campos de dados do evento: `data_evento`, `local_evento`, `horario`, etc.
- Campos de quantitativos: `qtd_formandos`, `qtd_fotografos`, `qtd_recepcionistas`, etc.
- Campos de controle: `status_profissionais`, `foto_faltante`, `pre_venda`, etc.
- **Backend**:
- Arquivos gerados via SQLC (`internal/db/generated/agenda.sql.go`).
- Service (`internal/agenda/service.go`) e Handler (`internal/agenda/handler.go`) implementados.
- Rotas registradas em `cmd/api/main.go` sob `/api/agenda`.
### 2.5. Decisões Técnicas Backend
- **Framework**: Gin Gonic.
- **Database**: PostgreSQL com driver `pgx/v5`.
- **Geração de Código**: Utilização do `sqlc` para gerar código Go type-safe a partir de queries SQL puras.
- **Estrutura**: Padrão `internal/domain` (Service/Handler/Repository isolados).
## 3. Implementações no Frontend (React/Next.js)
### 3.1. Autenticação e Sessão
- **AuthContext**: Melhoria na persistência de sessão. O frontend valida o token ao carregar e mantêm o usuário logado.
- **Logout**: Implementada função de logout que chama o backend e limpa o estado local.
- **UserApproval**: Interface para aprovação de usuários pendentes, exibindo Nome, E-mail e Empresa.
### 3.2. Gerenciamento de FOT (`CourseManagement.tsx`)
- **Listagem**: Atualizada para consumir `/api/cadastro-fot` em vez de dados mockados.
- **Formulário de Criação (`FotForm.tsx`)**:
- Modal para criar novo FOT.
- Dropdowns (Selects) conectados dinamicamente às APIs `/api/empresas`, `/api/cursos`, `/api/anos-formaturas`.
- Validação e envio dos dados para o backend.
### 3.3. API Service
- **`apiService.ts`**: Centralização das chamadas HTTP. Adicionadas funções para buscar cursos, anos de formatura, criar FOT, etc.
## 4. Próximos Passos Sugeridos
1. **Frontend Agenda**: Criar a interface para visualizar e editar a Agenda (`Calendar` ou `List View`), consumindo os endpoints `/api/agenda` já criados.
2. **Validações Avançadas**: Implementar regras de negócio mais complexas no backend (ex: conflito de horários).
3. **Relatórios**: Gerar PDFs ou Excels baseados nos dados de FOT e Agenda.