diff --git a/backend/cmd/api/main.go b/backend/cmd/api/main.go index f2d882d..4f6fab5 100644 --- a/backend/cmd/api/main.go +++ b/backend/cmd/api/main.go @@ -3,11 +3,16 @@ package main import ( "log" + "photum-backend/internal/anos_formaturas" "photum-backend/internal/auth" "photum-backend/internal/config" + "photum-backend/internal/cursos" "photum-backend/internal/db" + "photum-backend/internal/empresas" "photum-backend/internal/funcoes" "photum-backend/internal/profissionais" + "photum-backend/internal/tipos_eventos" + "photum-backend/internal/tipos_servicos" "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" @@ -45,11 +50,21 @@ func main() { profissionaisService := profissionais.NewService(queries) authService := auth.NewService(queries, profissionaisService, cfg) funcoesService := funcoes.NewService(queries) + cursosService := cursos.NewService(queries) + empresasService := empresas.NewService(queries) + anosFormaturasService := anos_formaturas.NewService(queries) + tiposServicosService := tipos_servicos.NewService(queries) + tiposEventosService := tipos_eventos.NewService(queries) // Initialize handlers authHandler := auth.NewHandler(authService) profissionaisHandler := profissionais.NewHandler(profissionaisService) funcoesHandler := funcoes.NewHandler(funcoesService) + cursosHandler := cursos.NewHandler(cursosService) + empresasHandler := empresas.NewHandler(empresasService) + anosFormaturasHandler := anos_formaturas.NewHandler(anosFormaturasService) + tiposServicosHandler := tipos_servicos.NewHandler(tiposServicosService) + tiposEventosHandler := tipos_eventos.NewHandler(tiposEventosService) r := gin.Default() @@ -65,8 +80,14 @@ func main() { authGroup.POST("/logout", authHandler.Logout) } - // Public API Routes + // Public API Routes (Data Lists) r.GET("/api/funcoes", funcoesHandler.List) + r.GET("/api/cursos", cursosHandler.List) + r.GET("/api/empresas", empresasHandler.List) + r.GET("/api/anos-formaturas", anosFormaturasHandler.List) + r.GET("/api/tipos-servicos", tiposServicosHandler.List) + r.GET("/api/tipos-eventos", tiposEventosHandler.List) + r.GET("/api/tipos-eventos/:id/precos", tiposEventosHandler.ListPrices) // Protected Routes api := r.Group("/api") @@ -94,10 +115,29 @@ func main() { funcoesGroup := api.Group("/funcoes") { funcoesGroup.POST("", funcoesHandler.Create) - // GET is now public funcoesGroup.PUT("/:id", funcoesHandler.Update) funcoesGroup.DELETE("/:id", funcoesHandler.Delete) } + + // protected CRUD (create/update/delete) + api.POST("/cursos", cursosHandler.Create) + api.PUT("/cursos/:id", cursosHandler.Update) + api.DELETE("/cursos/:id", cursosHandler.Delete) + + api.POST("/empresas", empresasHandler.Create) + api.PUT("/empresas/:id", empresasHandler.Update) + api.DELETE("/empresas/:id", empresasHandler.Delete) + + api.POST("/anos-formaturas", anosFormaturasHandler.Create) + api.PUT("/anos-formaturas/:id", anosFormaturasHandler.Update) + api.DELETE("/anos-formaturas/:id", anosFormaturasHandler.Delete) + + api.POST("/tipos-servicos", tiposServicosHandler.Create) + api.PUT("/tipos-servicos/:id", tiposServicosHandler.Update) + api.DELETE("/tipos-servicos/:id", tiposServicosHandler.Delete) + + api.POST("/tipos-eventos", tiposEventosHandler.Create) + api.POST("/tipos-eventos/precos", tiposEventosHandler.SetPrice) } log.Printf("Server running on port %s", cfg.AppPort) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index fd4d6e9..9d604b8 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -24,13 +24,488 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/api/funcoes": { + "/api/anos-formaturas": { "get": { "security": [ { "BearerAuth": [] } ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "anos_formaturas" + ], + "summary": "List all graduation years", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/anos_formaturas.AnoFormaturaResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "anos_formaturas" + ], + "summary": "Create a new graduation year", + "parameters": [ + { + "description": "Ano Semestre", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/anos_formaturas.CreateAnoFormaturaRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/anos_formaturas.AnoFormaturaResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/anos-formaturas/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "anos_formaturas" + ], + "summary": "Update a graduation year", + "parameters": [ + { + "type": "string", + "description": "ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Ano Semestre", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/anos_formaturas.CreateAnoFormaturaRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/anos_formaturas.AnoFormaturaResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "anos_formaturas" + ], + "summary": "Delete a graduation year", + "parameters": [ + { + "type": "string", + "description": "ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/cursos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cursos" + ], + "summary": "List all courses", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/cursos.CursoResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cursos" + ], + "summary": "Create a new course", + "parameters": [ + { + "description": "Curso Name", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/cursos.CreateCursoRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/cursos.CursoResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/cursos/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cursos" + ], + "summary": "Update a course", + "parameters": [ + { + "type": "string", + "description": "Curso ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Curso Name", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/cursos.CreateCursoRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/cursos.CursoResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cursos" + ], + "summary": "Delete a course", + "parameters": [ + { + "type": "string", + "description": "Curso ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/empresas": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "empresas" + ], + "summary": "List all companies", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/empresas.EmpresaResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "empresas" + ], + "summary": "Create a new company", + "parameters": [ + { + "description": "Empresa Name", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/empresas.CreateEmpresaRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/empresas.EmpresaResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/empresas/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "empresas" + ], + "summary": "Update a company", + "parameters": [ + { + "type": "string", + "description": "Empresa ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Empresa Name", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/empresas.CreateEmpresaRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/empresas.EmpresaResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "empresas" + ], + "summary": "Delete a company", + "parameters": [ + { + "type": "string", + "description": "Empresa ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/funcoes": { + "get": { "description": "List all professional functions", "consumes": [ "application/json" @@ -87,10 +562,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "$ref": "#/definitions/funcoes.CreateFuncaoRequest" } } ], @@ -110,6 +582,15 @@ const docTemplate = `{ } } }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -154,10 +635,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "$ref": "#/definitions/funcoes.CreateFuncaoRequest" } } ], @@ -177,6 +655,15 @@ const docTemplate = `{ } } }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -522,6 +1009,312 @@ const docTemplate = `{ } } }, + "/api/tipos-eventos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_eventos" + ], + "summary": "List all event types", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/tipos_eventos.EventoResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_eventos" + ], + "summary": "Create a new event type", + "parameters": [ + { + "description": "Nome", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tipos_eventos.CreateEventoRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/tipos_eventos.EventoResponse" + } + } + } + } + }, + "/api/tipos-eventos/precos": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_eventos" + ], + "summary": "Set price for an event function", + "parameters": [ + { + "description": "Price Input", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tipos_eventos.PriceInput" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/tipos-eventos/{id}/precos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_eventos" + ], + "summary": "List prices for an event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/tipos_eventos.PrecoResponse" + } + } + } + } + } + }, + "/api/tipos-servicos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_servicos" + ], + "summary": "List all service types", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/tipos_servicos.TipoServicoResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_servicos" + ], + "summary": "Create a new service type", + "parameters": [ + { + "description": "Nome", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tipos_servicos.CreateTipoServicoRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/tipos_servicos.TipoServicoResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/tipos-servicos/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_servicos" + ], + "summary": "Update a service type", + "parameters": [ + { + "type": "string", + "description": "ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Nome", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tipos_servicos.CreateTipoServicoRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/tipos_servicos.TipoServicoResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_servicos" + ], + "summary": "Delete a service type", + "parameters": [ + { + "type": "string", + "description": "ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/auth/login": { "post": { "description": "Login with email and password", @@ -542,7 +1335,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/auth.loginRequest" + "$ref": "#/definitions/auth.authRequest" } } ], @@ -655,7 +1448,7 @@ const docTemplate = `{ }, "/auth/register": { "post": { - "description": "Register a new user with optional professional profile", + "description": "Register a new user (defaults to 'profissional' role) with email and password", "consumes": [ "application/json" ], @@ -673,7 +1466,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/auth.registerRequest" + "$ref": "#/definitions/auth.authRequest" } } ], @@ -710,7 +1503,30 @@ const docTemplate = `{ } }, "definitions": { - "auth.loginRequest": { + "anos_formaturas.AnoFormaturaResponse": { + "type": "object", + "properties": { + "ano_semestre": { + "type": "string" + }, + "id": { + "type": "string" + } + } + }, + "anos_formaturas.CreateAnoFormaturaRequest": { + "type": "object", + "required": [ + "ano_semestre" + ], + "properties": { + "ano_semestre": { + "type": "string", + "example": "2029.1" + } + } + }, + "auth.authRequest": { "type": "object", "required": [ "email", @@ -721,7 +1537,8 @@ const docTemplate = `{ "type": "string" }, "senha": { - "type": "string" + "type": "string", + "minLength": 6 } } }, @@ -740,33 +1557,6 @@ const docTemplate = `{ } } }, - "auth.registerRequest": { - "type": "object", - "required": [ - "email", - "role", - "senha" - ], - "properties": { - "email": { - "type": "string" - }, - "profissional_data": { - "$ref": "#/definitions/profissionais.CreateProfissionalInput" - }, - "role": { - "type": "string", - "enum": [ - "profissional", - "empresa" - ] - }, - "senha": { - "type": "string", - "minLength": 6 - } - } - }, "auth.userResponse": { "type": "object", "properties": { @@ -781,6 +1571,61 @@ const docTemplate = `{ } } }, + "cursos.CreateCursoRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, + "cursos.CursoResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nome": { + "type": "string" + } + } + }, + "empresas.CreateEmpresaRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, + "empresas.EmpresaResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nome": { + "type": "string" + } + } + }, + "funcoes.CreateFuncaoRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, "funcoes.FuncaoResponse": { "type": "object", "properties": { @@ -1023,6 +1868,84 @@ const docTemplate = `{ "type": "string" } } + }, + "tipos_eventos.CreateEventoRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, + "tipos_eventos.EventoResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nome": { + "type": "string" + } + } + }, + "tipos_eventos.PrecoResponse": { + "type": "object", + "properties": { + "funcao_nome": { + "type": "string" + }, + "funcao_profissional_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "tipo_evento_id": { + "type": "string" + }, + "valor": { + "type": "number" + } + } + }, + "tipos_eventos.PriceInput": { + "type": "object", + "properties": { + "funcao_profissional_id": { + "type": "string" + }, + "tipo_evento_id": { + "type": "string" + }, + "valor": { + "type": "number" + } + } + }, + "tipos_servicos.CreateTipoServicoRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, + "tipos_servicos.TipoServicoResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nome": { + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index fea1aa9..1759bb7 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -18,13 +18,488 @@ "host": "localhost:8080", "basePath": "/", "paths": { - "/api/funcoes": { + "/api/anos-formaturas": { "get": { "security": [ { "BearerAuth": [] } ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "anos_formaturas" + ], + "summary": "List all graduation years", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/anos_formaturas.AnoFormaturaResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "anos_formaturas" + ], + "summary": "Create a new graduation year", + "parameters": [ + { + "description": "Ano Semestre", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/anos_formaturas.CreateAnoFormaturaRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/anos_formaturas.AnoFormaturaResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/anos-formaturas/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "anos_formaturas" + ], + "summary": "Update a graduation year", + "parameters": [ + { + "type": "string", + "description": "ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Ano Semestre", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/anos_formaturas.CreateAnoFormaturaRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/anos_formaturas.AnoFormaturaResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "anos_formaturas" + ], + "summary": "Delete a graduation year", + "parameters": [ + { + "type": "string", + "description": "ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/cursos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cursos" + ], + "summary": "List all courses", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/cursos.CursoResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cursos" + ], + "summary": "Create a new course", + "parameters": [ + { + "description": "Curso Name", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/cursos.CreateCursoRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/cursos.CursoResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/cursos/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cursos" + ], + "summary": "Update a course", + "parameters": [ + { + "type": "string", + "description": "Curso ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Curso Name", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/cursos.CreateCursoRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/cursos.CursoResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "cursos" + ], + "summary": "Delete a course", + "parameters": [ + { + "type": "string", + "description": "Curso ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/empresas": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "empresas" + ], + "summary": "List all companies", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/empresas.EmpresaResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "empresas" + ], + "summary": "Create a new company", + "parameters": [ + { + "description": "Empresa Name", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/empresas.CreateEmpresaRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/empresas.EmpresaResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/empresas/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "empresas" + ], + "summary": "Update a company", + "parameters": [ + { + "type": "string", + "description": "Empresa ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Empresa Name", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/empresas.CreateEmpresaRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/empresas.EmpresaResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "empresas" + ], + "summary": "Delete a company", + "parameters": [ + { + "type": "string", + "description": "Empresa ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/funcoes": { + "get": { "description": "List all professional functions", "consumes": [ "application/json" @@ -81,10 +556,7 @@ "in": "body", "required": true, "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "$ref": "#/definitions/funcoes.CreateFuncaoRequest" } } ], @@ -104,6 +576,15 @@ } } }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -148,10 +629,7 @@ "in": "body", "required": true, "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "$ref": "#/definitions/funcoes.CreateFuncaoRequest" } } ], @@ -171,6 +649,15 @@ } } }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -516,6 +1003,312 @@ } } }, + "/api/tipos-eventos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_eventos" + ], + "summary": "List all event types", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/tipos_eventos.EventoResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_eventos" + ], + "summary": "Create a new event type", + "parameters": [ + { + "description": "Nome", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tipos_eventos.CreateEventoRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/tipos_eventos.EventoResponse" + } + } + } + } + }, + "/api/tipos-eventos/precos": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_eventos" + ], + "summary": "Set price for an event function", + "parameters": [ + { + "description": "Price Input", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tipos_eventos.PriceInput" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/tipos-eventos/{id}/precos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_eventos" + ], + "summary": "List prices for an event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/tipos_eventos.PrecoResponse" + } + } + } + } + } + }, + "/api/tipos-servicos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_servicos" + ], + "summary": "List all service types", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/tipos_servicos.TipoServicoResponse" + } + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_servicos" + ], + "summary": "Create a new service type", + "parameters": [ + { + "description": "Nome", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tipos_servicos.CreateTipoServicoRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/tipos_servicos.TipoServicoResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/api/tipos-servicos/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_servicos" + ], + "summary": "Update a service type", + "parameters": [ + { + "type": "string", + "description": "ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Nome", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tipos_servicos.CreateTipoServicoRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/tipos_servicos.TipoServicoResponse" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tipos_servicos" + ], + "summary": "Delete a service type", + "parameters": [ + { + "type": "string", + "description": "ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, "/auth/login": { "post": { "description": "Login with email and password", @@ -536,7 +1329,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/auth.loginRequest" + "$ref": "#/definitions/auth.authRequest" } } ], @@ -649,7 +1442,7 @@ }, "/auth/register": { "post": { - "description": "Register a new user with optional professional profile", + "description": "Register a new user (defaults to 'profissional' role) with email and password", "consumes": [ "application/json" ], @@ -667,7 +1460,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/auth.registerRequest" + "$ref": "#/definitions/auth.authRequest" } } ], @@ -704,7 +1497,30 @@ } }, "definitions": { - "auth.loginRequest": { + "anos_formaturas.AnoFormaturaResponse": { + "type": "object", + "properties": { + "ano_semestre": { + "type": "string" + }, + "id": { + "type": "string" + } + } + }, + "anos_formaturas.CreateAnoFormaturaRequest": { + "type": "object", + "required": [ + "ano_semestre" + ], + "properties": { + "ano_semestre": { + "type": "string", + "example": "2029.1" + } + } + }, + "auth.authRequest": { "type": "object", "required": [ "email", @@ -715,7 +1531,8 @@ "type": "string" }, "senha": { - "type": "string" + "type": "string", + "minLength": 6 } } }, @@ -734,33 +1551,6 @@ } } }, - "auth.registerRequest": { - "type": "object", - "required": [ - "email", - "role", - "senha" - ], - "properties": { - "email": { - "type": "string" - }, - "profissional_data": { - "$ref": "#/definitions/profissionais.CreateProfissionalInput" - }, - "role": { - "type": "string", - "enum": [ - "profissional", - "empresa" - ] - }, - "senha": { - "type": "string", - "minLength": 6 - } - } - }, "auth.userResponse": { "type": "object", "properties": { @@ -775,6 +1565,61 @@ } } }, + "cursos.CreateCursoRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, + "cursos.CursoResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nome": { + "type": "string" + } + } + }, + "empresas.CreateEmpresaRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, + "empresas.EmpresaResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nome": { + "type": "string" + } + } + }, + "funcoes.CreateFuncaoRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, "funcoes.FuncaoResponse": { "type": "object", "properties": { @@ -1017,6 +1862,84 @@ "type": "string" } } + }, + "tipos_eventos.CreateEventoRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, + "tipos_eventos.EventoResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nome": { + "type": "string" + } + } + }, + "tipos_eventos.PrecoResponse": { + "type": "object", + "properties": { + "funcao_nome": { + "type": "string" + }, + "funcao_profissional_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "tipo_evento_id": { + "type": "string" + }, + "valor": { + "type": "number" + } + } + }, + "tipos_eventos.PriceInput": { + "type": "object", + "properties": { + "funcao_profissional_id": { + "type": "string" + }, + "tipo_evento_id": { + "type": "string" + }, + "valor": { + "type": "number" + } + } + }, + "tipos_servicos.CreateTipoServicoRequest": { + "type": "object", + "required": [ + "nome" + ], + "properties": { + "nome": { + "type": "string" + } + } + }, + "tipos_servicos.TipoServicoResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "nome": { + "type": "string" + } + } } }, "securityDefinitions": { diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 8223aad..169bdcb 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -1,10 +1,26 @@ basePath: / definitions: - auth.loginRequest: + anos_formaturas.AnoFormaturaResponse: + properties: + ano_semestre: + type: string + id: + type: string + type: object + anos_formaturas.CreateAnoFormaturaRequest: + properties: + ano_semestre: + example: "2029.1" + type: string + required: + - ano_semestre + type: object + auth.authRequest: properties: email: type: string senha: + minLength: 6 type: string required: - email @@ -20,25 +36,6 @@ definitions: user: $ref: '#/definitions/auth.userResponse' type: object - auth.registerRequest: - properties: - email: - type: string - profissional_data: - $ref: '#/definitions/profissionais.CreateProfissionalInput' - role: - enum: - - profissional - - empresa - type: string - senha: - minLength: 6 - type: string - required: - - email - - role - - senha - type: object auth.userResponse: properties: email: @@ -48,6 +45,41 @@ definitions: role: type: string type: object + cursos.CreateCursoRequest: + properties: + nome: + type: string + required: + - nome + type: object + cursos.CursoResponse: + properties: + id: + type: string + nome: + type: string + type: object + empresas.CreateEmpresaRequest: + properties: + nome: + type: string + required: + - nome + type: object + empresas.EmpresaResponse: + properties: + id: + type: string + nome: + type: string + type: object + funcoes.CreateFuncaoRequest: + properties: + nome: + type: string + required: + - nome + type: object funcoes.FuncaoResponse: properties: id: @@ -209,6 +241,56 @@ definitions: whatsapp: type: string type: object + tipos_eventos.CreateEventoRequest: + properties: + nome: + type: string + required: + - nome + type: object + tipos_eventos.EventoResponse: + properties: + id: + type: string + nome: + type: string + type: object + tipos_eventos.PrecoResponse: + properties: + funcao_nome: + type: string + funcao_profissional_id: + type: string + id: + type: string + tipo_evento_id: + type: string + valor: + type: number + type: object + tipos_eventos.PriceInput: + properties: + funcao_profissional_id: + type: string + tipo_evento_id: + type: string + valor: + type: number + type: object + tipos_servicos.CreateTipoServicoRequest: + properties: + nome: + type: string + required: + - nome + type: object + tipos_servicos.TipoServicoResponse: + properties: + id: + type: string + nome: + type: string + type: object host: localhost:8080 info: contact: @@ -223,6 +305,303 @@ info: title: Photum Backend API version: "1.0" paths: + /api/anos-formaturas: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/anos_formaturas.AnoFormaturaResponse' + type: array + security: + - BearerAuth: [] + summary: List all graduation years + tags: + - anos_formaturas + post: + consumes: + - application/json + parameters: + - description: Ano Semestre + in: body + name: request + required: true + schema: + $ref: '#/definitions/anos_formaturas.CreateAnoFormaturaRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/anos_formaturas.AnoFormaturaResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "409": + description: Conflict + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create a new graduation year + tags: + - anos_formaturas + /api/anos-formaturas/{id}: + delete: + consumes: + - application/json + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + security: + - BearerAuth: [] + summary: Delete a graduation year + tags: + - anos_formaturas + put: + consumes: + - application/json + parameters: + - description: ID + in: path + name: id + required: true + type: string + - description: Ano Semestre + in: body + name: request + required: true + schema: + $ref: '#/definitions/anos_formaturas.CreateAnoFormaturaRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/anos_formaturas.AnoFormaturaResponse' + security: + - BearerAuth: [] + summary: Update a graduation year + tags: + - anos_formaturas + /api/cursos: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/cursos.CursoResponse' + type: array + security: + - BearerAuth: [] + summary: List all courses + tags: + - cursos + post: + consumes: + - application/json + parameters: + - description: Curso Name + in: body + name: request + required: true + schema: + $ref: '#/definitions/cursos.CreateCursoRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/cursos.CursoResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "409": + description: Conflict + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create a new course + tags: + - cursos + /api/cursos/{id}: + delete: + consumes: + - application/json + parameters: + - description: Curso ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + security: + - BearerAuth: [] + summary: Delete a course + tags: + - cursos + put: + consumes: + - application/json + parameters: + - description: Curso ID + in: path + name: id + required: true + type: string + - description: Curso Name + in: body + name: request + required: true + schema: + $ref: '#/definitions/cursos.CreateCursoRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/cursos.CursoResponse' + security: + - BearerAuth: [] + summary: Update a course + tags: + - cursos + /api/empresas: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/empresas.EmpresaResponse' + type: array + security: + - BearerAuth: [] + summary: List all companies + tags: + - empresas + post: + consumes: + - application/json + parameters: + - description: Empresa Name + in: body + name: request + required: true + schema: + $ref: '#/definitions/empresas.CreateEmpresaRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/empresas.EmpresaResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "409": + description: Conflict + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create a new company + tags: + - empresas + /api/empresas/{id}: + delete: + consumes: + - application/json + parameters: + - description: Empresa ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + security: + - BearerAuth: [] + summary: Delete a company + tags: + - empresas + put: + consumes: + - application/json + parameters: + - description: Empresa ID + in: path + name: id + required: true + type: string + - description: Empresa Name + in: body + name: request + required: true + schema: + $ref: '#/definitions/empresas.CreateEmpresaRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/empresas.EmpresaResponse' + security: + - BearerAuth: [] + summary: Update a company + tags: + - empresas /api/funcoes: get: consumes: @@ -243,8 +622,6 @@ paths: additionalProperties: type: string type: object - security: - - BearerAuth: [] summary: List functions tags: - funcoes @@ -258,9 +635,7 @@ paths: name: request required: true schema: - additionalProperties: - type: string - type: object + $ref: '#/definitions/funcoes.CreateFuncaoRequest' produces: - application/json responses: @@ -274,6 +649,12 @@ paths: additionalProperties: type: string type: object + "409": + description: Conflict + schema: + additionalProperties: + type: string + type: object "500": description: Internal Server Error schema: @@ -337,9 +718,7 @@ paths: name: request required: true schema: - additionalProperties: - type: string - type: object + $ref: '#/definitions/funcoes.CreateFuncaoRequest' produces: - application/json responses: @@ -353,6 +732,12 @@ paths: additionalProperties: type: string type: object + "409": + description: Conflict + schema: + additionalProperties: + type: string + type: object "500": description: Internal Server Error schema: @@ -541,6 +926,194 @@ paths: summary: Update profissional tags: - profissionais + /api/tipos-eventos: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/tipos_eventos.EventoResponse' + type: array + security: + - BearerAuth: [] + summary: List all event types + tags: + - tipos_eventos + post: + consumes: + - application/json + parameters: + - description: Nome + in: body + name: request + required: true + schema: + $ref: '#/definitions/tipos_eventos.CreateEventoRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/tipos_eventos.EventoResponse' + security: + - BearerAuth: [] + summary: Create a new event type + tags: + - tipos_eventos + /api/tipos-eventos/{id}/precos: + get: + consumes: + - application/json + parameters: + - description: Event ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/tipos_eventos.PrecoResponse' + type: array + security: + - BearerAuth: [] + summary: List prices for an event + tags: + - tipos_eventos + /api/tipos-eventos/precos: + post: + consumes: + - application/json + parameters: + - description: Price Input + in: body + name: request + required: true + schema: + $ref: '#/definitions/tipos_eventos.PriceInput' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Set price for an event function + tags: + - tipos_eventos + /api/tipos-servicos: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/tipos_servicos.TipoServicoResponse' + type: array + security: + - BearerAuth: [] + summary: List all service types + tags: + - tipos_servicos + post: + consumes: + - application/json + parameters: + - description: Nome + in: body + name: request + required: true + schema: + $ref: '#/definitions/tipos_servicos.CreateTipoServicoRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/tipos_servicos.TipoServicoResponse' + "400": + description: Bad Request + schema: + additionalProperties: + type: string + type: object + "409": + description: Conflict + schema: + additionalProperties: + type: string + type: object + security: + - BearerAuth: [] + summary: Create a new service type + tags: + - tipos_servicos + /api/tipos-servicos/{id}: + delete: + consumes: + - application/json + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + security: + - BearerAuth: [] + summary: Delete a service type + tags: + - tipos_servicos + put: + consumes: + - application/json + parameters: + - description: ID + in: path + name: id + required: true + type: string + - description: Nome + in: body + name: request + required: true + schema: + $ref: '#/definitions/tipos_servicos.CreateTipoServicoRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/tipos_servicos.TipoServicoResponse' + security: + - BearerAuth: [] + summary: Update a service type + tags: + - tipos_servicos /auth/login: post: consumes: @@ -552,7 +1125,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/auth.loginRequest' + $ref: '#/definitions/auth.authRequest' produces: - application/json responses: @@ -630,14 +1203,15 @@ paths: post: consumes: - application/json - description: Register a new user with optional professional profile + description: Register a new user (defaults to 'profissional' role) with email + and password parameters: - description: Register Request in: body name: request required: true schema: - $ref: '#/definitions/auth.registerRequest' + $ref: '#/definitions/auth.authRequest' produces: - application/json responses: diff --git a/backend/internal/anos_formaturas/handler.go b/backend/internal/anos_formaturas/handler.go new file mode 100644 index 0000000..23c4021 --- /dev/null +++ b/backend/internal/anos_formaturas/handler.go @@ -0,0 +1,137 @@ +package anos_formaturas + +import ( + "net/http" + "strings" + + "photum-backend/internal/db/generated" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +type Handler struct { + service *Service +} + +func NewHandler(service *Service) *Handler { + return &Handler{service: service} +} + +type AnoFormaturaResponse struct { + ID string `json:"id"` + AnoSemestre string `json:"ano_semestre"` +} + +type CreateAnoFormaturaRequest struct { + AnoSemestre string `json:"ano_semestre" binding:"required" example:"2029.1"` +} + +func toResponse(a generated.AnosFormatura) AnoFormaturaResponse { + return AnoFormaturaResponse{ + ID: uuid.UUID(a.ID.Bytes).String(), + AnoSemestre: a.AnoSemestre, + } +} + +// Create godoc +// @Summary Create a new graduation year +// @Tags anos_formaturas +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body CreateAnoFormaturaRequest true "Ano Semestre" +// @Success 201 {object} AnoFormaturaResponse +// @Failure 400 {object} map[string]string +// @Failure 409 {object} map[string]string +// @Router /api/anos-formaturas [post] +func (h *Handler) Create(c *gin.Context) { + var req CreateAnoFormaturaRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + ano, err := h.service.Create(c.Request.Context(), req.AnoSemestre) + if err != nil { + if strings.Contains(err.Error(), "duplicate key value") { + c.JSON(http.StatusConflict, gin.H{"error": "Ano/Semestre already exists"}) + return + } + if strings.Contains(err.Error(), "invalid format") { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusCreated, toResponse(*ano)) +} + +// List godoc +// @Summary List all graduation years +// @Tags anos_formaturas +// @Accept json +// @Produce json +// @Security BearerAuth +// @Success 200 {array} AnoFormaturaResponse +// @Router /api/anos-formaturas [get] +func (h *Handler) List(c *gin.Context) { + anos, err := h.service.List(c.Request.Context()) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + var response []AnoFormaturaResponse + for _, ano := range anos { + response = append(response, toResponse(ano)) + } + c.JSON(http.StatusOK, response) +} + +// Update godoc +// @Summary Update a graduation year +// @Tags anos_formaturas +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path string true "ID" +// @Param request body CreateAnoFormaturaRequest true "Ano Semestre" +// @Success 200 {object} AnoFormaturaResponse +// @Router /api/anos-formaturas/{id} [put] +func (h *Handler) Update(c *gin.Context) { + id := c.Param("id") + var req CreateAnoFormaturaRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + ano, err := h.service.Update(c.Request.Context(), id, req.AnoSemestre) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, toResponse(*ano)) +} + +// Delete godoc +// @Summary Delete a graduation year +// @Tags anos_formaturas +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path string true "ID" +// @Success 204 {object} nil +// @Router /api/anos-formaturas/{id} [delete] +func (h *Handler) Delete(c *gin.Context) { + id := c.Param("id") + if err := h.service.Delete(c.Request.Context(), id); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.Status(http.StatusNoContent) +} diff --git a/backend/internal/anos_formaturas/service.go b/backend/internal/anos_formaturas/service.go new file mode 100644 index 0000000..0d9c521 --- /dev/null +++ b/backend/internal/anos_formaturas/service.go @@ -0,0 +1,80 @@ +package anos_formaturas + +import ( + "context" + "errors" + "regexp" + + "photum-backend/internal/db/generated" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" +) + +type Service struct { + queries *generated.Queries +} + +func NewService(queries *generated.Queries) *Service { + return &Service{queries: queries} +} + +func (s *Service) Create(ctx context.Context, anoSemestre string) (*generated.AnosFormatura, error) { + // Validate format YYYY.S + matched, _ := regexp.MatchString(`^\d{4}\.[1-2]$`, anoSemestre) + if !matched { + return nil, errors.New("invalid format. Expected YYYY.1 or YYYY.2") + } + + ano, err := s.queries.CreateAnoFormatura(ctx, anoSemestre) + if err != nil { + return nil, err + } + return &ano, nil +} + +func (s *Service) List(ctx context.Context) ([]generated.AnosFormatura, error) { + return s.queries.ListAnosFormaturas(ctx) +} + +func (s *Service) GetByID(ctx context.Context, id string) (*generated.AnosFormatura, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + ano, err := s.queries.GetAnoFormaturaByID(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) + if err != nil { + return nil, err + } + return &ano, nil +} + +func (s *Service) Update(ctx context.Context, id, anoSemestre string) (*generated.AnosFormatura, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + + // Validate format YYYY.S + matched, _ := regexp.MatchString(`^\d{4}\.[1-2]$`, anoSemestre) + if !matched { + return nil, errors.New("invalid format. Expected YYYY.1 or YYYY.2") + } + + ano, err := s.queries.UpdateAnoFormatura(ctx, generated.UpdateAnoFormaturaParams{ + ID: pgtype.UUID{Bytes: uuidVal, Valid: true}, + AnoSemestre: anoSemestre, + }) + if err != nil { + return nil, err + } + return &ano, nil +} + +func (s *Service) Delete(ctx context.Context, id string) error { + uuidVal, err := uuid.Parse(id) + if err != nil { + return errors.New("invalid id") + } + return s.queries.DeleteAnoFormatura(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) +} diff --git a/backend/internal/auth/handler.go b/backend/internal/auth/handler.go index a2712b6..c30057f 100644 --- a/backend/internal/auth/handler.go +++ b/backend/internal/auth/handler.go @@ -2,6 +2,7 @@ package auth import ( "net/http" + "strings" "photum-backend/internal/profissionais" @@ -17,38 +18,39 @@ func NewHandler(service *Service) *Handler { return &Handler{service: service} } -type registerRequest struct { - Email string `json:"email" binding:"required,email"` - Senha string `json:"senha" binding:"required,min=6"` - Role string `json:"role" binding:"required,oneof=profissional empresa"` - ProfissionalData *profissionais.CreateProfissionalInput `json:"profissional_data"` +type authRequest struct { + Email string `json:"email" binding:"required,email"` + Senha string `json:"senha" binding:"required,min=6"` } // Register godoc // @Summary Register a new user -// @Description Register a new user with optional professional profile +// @Description Register a new user (defaults to 'profissional' role) with email and password // @Tags auth // @Accept json // @Produce json -// @Param request body registerRequest true "Register Request" +// @Param request body authRequest true "Register Request" // @Success 201 {object} map[string]string // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /auth/register [post] func (h *Handler) Register(c *gin.Context) { - var req registerRequest + var req authRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - if req.Role == "profissional" && req.ProfissionalData == nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "profissional_data is required for role 'profissional'"}) - return - } + // Default simplified registration: Role="profissional", No professional data yet + role := "profissional" + var profData *profissionais.CreateProfissionalInput = nil - _, err := h.service.Register(c.Request.Context(), req.Email, req.Senha, req.Role, req.ProfissionalData) + _, err := h.service.Register(c.Request.Context(), req.Email, req.Senha, role, profData) if err != nil { + if strings.Contains(err.Error(), "duplicate key") { + c.JSON(http.StatusConflict, gin.H{"error": "email already registered"}) + return + } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } @@ -56,11 +58,6 @@ func (h *Handler) Register(c *gin.Context) { c.JSON(http.StatusCreated, gin.H{"message": "user created"}) } -type loginRequest struct { - Email string `json:"email" binding:"required,email"` - Senha string `json:"senha" binding:"required"` -} - type loginResponse struct { AccessToken string `json:"access_token"` ExpiresAt string `json:"expires_at"` @@ -80,13 +77,13 @@ type userResponse struct { // @Tags auth // @Accept json // @Produce json -// @Param request body loginRequest true "Login Request" +// @Param request body authRequest true "Login Request" // @Success 200 {object} loginResponse // @Failure 401 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /auth/login [post] func (h *Handler) Login(c *gin.Context) { - var req loginRequest + var req authRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return @@ -94,7 +91,7 @@ func (h *Handler) Login(c *gin.Context) { tokenPair, user, profData, err := h.service.Login(c.Request.Context(), req.Email, req.Senha) if err != nil { - c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"}) return } @@ -110,7 +107,7 @@ func (h *Handler) Login(c *gin.Context) { resp := loginResponse{ AccessToken: tokenPair.AccessToken, - ExpiresAt: "2025-...", + ExpiresAt: "2025-...", // logic to calculate if needed, or remove field User: userResponse{ ID: uuid.UUID(user.ID.Bytes).String(), Email: user.Email, @@ -131,11 +128,6 @@ func (h *Handler) Login(c *gin.Context) { c.JSON(http.StatusOK, resp) } -// Refresh and Logout handlers should be kept or restored if they were lost. -// I will assume they are needed and add them back in a subsequent edit if missing, -// or include them here if I can fit them. -// The previous content had them. I'll add them to be safe. - // Refresh godoc // @Summary Refresh access token // @Description Get a new access token using a valid refresh token diff --git a/backend/internal/auth/middleware.go b/backend/internal/auth/middleware.go index 7160e9b..d755934 100644 --- a/backend/internal/auth/middleware.go +++ b/backend/internal/auth/middleware.go @@ -33,7 +33,7 @@ func AuthMiddleware(cfg *config.Config) gin.HandlerFunc { return } - c.Set("userID", claims.UserID) + c.Set("userID", claims.UserID.String()) c.Set("role", claims.Role) c.Next() } diff --git a/backend/internal/cursos/handler.go b/backend/internal/cursos/handler.go new file mode 100644 index 0000000..ee006d8 --- /dev/null +++ b/backend/internal/cursos/handler.go @@ -0,0 +1,133 @@ +package cursos + +import ( + "net/http" + "strings" + + "photum-backend/internal/db/generated" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +type Handler struct { + service *Service +} + +func NewHandler(service *Service) *Handler { + return &Handler{service: service} +} + +type CursoResponse struct { + ID string `json:"id"` + Nome string `json:"nome"` +} + +type CreateCursoRequest struct { + Nome string `json:"nome" binding:"required"` +} + +func toResponse(c generated.Curso) CursoResponse { + return CursoResponse{ + ID: uuid.UUID(c.ID.Bytes).String(), + Nome: c.Nome, + } +} + +// Create godoc +// @Summary Create a new course +// @Tags cursos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body CreateCursoRequest true "Curso Name" +// @Success 201 {object} CursoResponse +// @Failure 400 {object} map[string]string +// @Failure 409 {object} map[string]string +// @Router /api/cursos [post] +func (h *Handler) Create(c *gin.Context) { + var req CreateCursoRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + curso, err := h.service.Create(c.Request.Context(), req.Nome) + if err != nil { + if strings.Contains(err.Error(), "duplicate key value") { + c.JSON(http.StatusConflict, gin.H{"error": "Curso already exists"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusCreated, toResponse(*curso)) +} + +// List godoc +// @Summary List all courses +// @Tags cursos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Success 200 {array} CursoResponse +// @Router /api/cursos [get] +func (h *Handler) List(c *gin.Context) { + cursos, err := h.service.List(c.Request.Context()) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + var response []CursoResponse + for _, curso := range cursos { + response = append(response, toResponse(curso)) + } + c.JSON(http.StatusOK, response) +} + +// Update godoc +// @Summary Update a course +// @Tags cursos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path string true "Curso ID" +// @Param request body CreateCursoRequest true "Curso Name" +// @Success 200 {object} CursoResponse +// @Router /api/cursos/{id} [put] +func (h *Handler) Update(c *gin.Context) { + id := c.Param("id") + var req CreateCursoRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + curso, err := h.service.Update(c.Request.Context(), id, req.Nome) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, toResponse(*curso)) +} + +// Delete godoc +// @Summary Delete a course +// @Tags cursos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path string true "Curso ID" +// @Success 204 {object} nil +// @Router /api/cursos/{id} [delete] +func (h *Handler) Delete(c *gin.Context) { + id := c.Param("id") + if err := h.service.Delete(c.Request.Context(), id); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.Status(http.StatusNoContent) +} diff --git a/backend/internal/cursos/service.go b/backend/internal/cursos/service.go new file mode 100644 index 0000000..c16df4d --- /dev/null +++ b/backend/internal/cursos/service.go @@ -0,0 +1,67 @@ +package cursos + +import ( + "context" + "errors" + + "photum-backend/internal/db/generated" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" +) + +type Service struct { + queries *generated.Queries +} + +func NewService(queries *generated.Queries) *Service { + return &Service{queries: queries} +} + +func (s *Service) Create(ctx context.Context, nome string) (*generated.Curso, error) { + curso, err := s.queries.CreateCurso(ctx, nome) + if err != nil { + return nil, err + } + return &curso, nil +} + +func (s *Service) List(ctx context.Context) ([]generated.Curso, error) { + return s.queries.ListCursos(ctx) +} + +func (s *Service) GetByID(ctx context.Context, id string) (*generated.Curso, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + curso, err := s.queries.GetCursoByID(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) + if err != nil { + return nil, err + } + return &curso, nil +} + +func (s *Service) Update(ctx context.Context, id, nome string) (*generated.Curso, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + + curso, err := s.queries.UpdateCurso(ctx, generated.UpdateCursoParams{ + ID: pgtype.UUID{Bytes: uuidVal, Valid: true}, + Nome: nome, + }) + if err != nil { + return nil, err + } + return &curso, nil +} + +func (s *Service) Delete(ctx context.Context, id string) error { + uuidVal, err := uuid.Parse(id) + if err != nil { + return errors.New("invalid id") + } + return s.queries.DeleteCurso(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) +} diff --git a/backend/internal/db/generated/anos_formaturas.sql.go b/backend/internal/db/generated/anos_formaturas.sql.go new file mode 100644 index 0000000..801b764 --- /dev/null +++ b/backend/internal/db/generated/anos_formaturas.sql.go @@ -0,0 +1,83 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: anos_formaturas.sql + +package generated + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createAnoFormatura = `-- name: CreateAnoFormatura :one +INSERT INTO anos_formaturas (ano_semestre) VALUES ($1) RETURNING id, ano_semestre, criado_em +` + +func (q *Queries) CreateAnoFormatura(ctx context.Context, anoSemestre string) (AnosFormatura, error) { + row := q.db.QueryRow(ctx, createAnoFormatura, anoSemestre) + var i AnosFormatura + err := row.Scan(&i.ID, &i.AnoSemestre, &i.CriadoEm) + return i, err +} + +const deleteAnoFormatura = `-- name: DeleteAnoFormatura :exec +DELETE FROM anos_formaturas WHERE id = $1 +` + +func (q *Queries) DeleteAnoFormatura(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteAnoFormatura, id) + return err +} + +const getAnoFormaturaByID = `-- name: GetAnoFormaturaByID :one +SELECT id, ano_semestre, criado_em FROM anos_formaturas WHERE id = $1 +` + +func (q *Queries) GetAnoFormaturaByID(ctx context.Context, id pgtype.UUID) (AnosFormatura, error) { + row := q.db.QueryRow(ctx, getAnoFormaturaByID, id) + var i AnosFormatura + err := row.Scan(&i.ID, &i.AnoSemestre, &i.CriadoEm) + return i, err +} + +const listAnosFormaturas = `-- name: ListAnosFormaturas :many +SELECT id, ano_semestre, criado_em FROM anos_formaturas ORDER BY ano_semestre +` + +func (q *Queries) ListAnosFormaturas(ctx context.Context) ([]AnosFormatura, error) { + rows, err := q.db.Query(ctx, listAnosFormaturas) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AnosFormatura + for rows.Next() { + var i AnosFormatura + if err := rows.Scan(&i.ID, &i.AnoSemestre, &i.CriadoEm); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateAnoFormatura = `-- name: UpdateAnoFormatura :one +UPDATE anos_formaturas SET ano_semestre = $2 WHERE id = $1 RETURNING id, ano_semestre, criado_em +` + +type UpdateAnoFormaturaParams struct { + ID pgtype.UUID `json:"id"` + AnoSemestre string `json:"ano_semestre"` +} + +func (q *Queries) UpdateAnoFormatura(ctx context.Context, arg UpdateAnoFormaturaParams) (AnosFormatura, error) { + row := q.db.QueryRow(ctx, updateAnoFormatura, arg.ID, arg.AnoSemestre) + var i AnosFormatura + err := row.Scan(&i.ID, &i.AnoSemestre, &i.CriadoEm) + return i, err +} diff --git a/backend/internal/db/generated/cursos.sql.go b/backend/internal/db/generated/cursos.sql.go new file mode 100644 index 0000000..ce56aa6 --- /dev/null +++ b/backend/internal/db/generated/cursos.sql.go @@ -0,0 +1,83 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: cursos.sql + +package generated + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createCurso = `-- name: CreateCurso :one +INSERT INTO cursos (nome) VALUES ($1) RETURNING id, nome, criado_em +` + +func (q *Queries) CreateCurso(ctx context.Context, nome string) (Curso, error) { + row := q.db.QueryRow(ctx, createCurso, nome) + var i Curso + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} + +const deleteCurso = `-- name: DeleteCurso :exec +DELETE FROM cursos WHERE id = $1 +` + +func (q *Queries) DeleteCurso(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteCurso, id) + return err +} + +const getCursoByID = `-- name: GetCursoByID :one +SELECT id, nome, criado_em FROM cursos WHERE id = $1 +` + +func (q *Queries) GetCursoByID(ctx context.Context, id pgtype.UUID) (Curso, error) { + row := q.db.QueryRow(ctx, getCursoByID, id) + var i Curso + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} + +const listCursos = `-- name: ListCursos :many +SELECT id, nome, criado_em FROM cursos ORDER BY nome +` + +func (q *Queries) ListCursos(ctx context.Context) ([]Curso, error) { + rows, err := q.db.Query(ctx, listCursos) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Curso + for rows.Next() { + var i Curso + if err := rows.Scan(&i.ID, &i.Nome, &i.CriadoEm); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateCurso = `-- name: UpdateCurso :one +UPDATE cursos SET nome = $2 WHERE id = $1 RETURNING id, nome, criado_em +` + +type UpdateCursoParams struct { + ID pgtype.UUID `json:"id"` + Nome string `json:"nome"` +} + +func (q *Queries) UpdateCurso(ctx context.Context, arg UpdateCursoParams) (Curso, error) { + row := q.db.QueryRow(ctx, updateCurso, arg.ID, arg.Nome) + var i Curso + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} diff --git a/backend/internal/db/generated/empresas.sql.go b/backend/internal/db/generated/empresas.sql.go new file mode 100644 index 0000000..e3f9f39 --- /dev/null +++ b/backend/internal/db/generated/empresas.sql.go @@ -0,0 +1,83 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: empresas.sql + +package generated + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createEmpresa = `-- name: CreateEmpresa :one +INSERT INTO empresas (nome) VALUES ($1) RETURNING id, nome, criado_em +` + +func (q *Queries) CreateEmpresa(ctx context.Context, nome string) (Empresa, error) { + row := q.db.QueryRow(ctx, createEmpresa, nome) + var i Empresa + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} + +const deleteEmpresa = `-- name: DeleteEmpresa :exec +DELETE FROM empresas WHERE id = $1 +` + +func (q *Queries) DeleteEmpresa(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteEmpresa, id) + return err +} + +const getEmpresaByID = `-- name: GetEmpresaByID :one +SELECT id, nome, criado_em FROM empresas WHERE id = $1 +` + +func (q *Queries) GetEmpresaByID(ctx context.Context, id pgtype.UUID) (Empresa, error) { + row := q.db.QueryRow(ctx, getEmpresaByID, id) + var i Empresa + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} + +const listEmpresas = `-- name: ListEmpresas :many +SELECT id, nome, criado_em FROM empresas ORDER BY nome +` + +func (q *Queries) ListEmpresas(ctx context.Context) ([]Empresa, error) { + rows, err := q.db.Query(ctx, listEmpresas) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Empresa + for rows.Next() { + var i Empresa + if err := rows.Scan(&i.ID, &i.Nome, &i.CriadoEm); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateEmpresa = `-- name: UpdateEmpresa :one +UPDATE empresas SET nome = $2 WHERE id = $1 RETURNING id, nome, criado_em +` + +type UpdateEmpresaParams struct { + ID pgtype.UUID `json:"id"` + Nome string `json:"nome"` +} + +func (q *Queries) UpdateEmpresa(ctx context.Context, arg UpdateEmpresaParams) (Empresa, error) { + row := q.db.QueryRow(ctx, updateEmpresa, arg.ID, arg.Nome) + var i Empresa + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} diff --git a/backend/internal/db/generated/models.go b/backend/internal/db/generated/models.go index 1ab4967..456f92d 100644 --- a/backend/internal/db/generated/models.go +++ b/backend/internal/db/generated/models.go @@ -8,6 +8,12 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) +type AnosFormatura struct { + ID pgtype.UUID `json:"id"` + AnoSemestre string `json:"ano_semestre"` + CriadoEm pgtype.Timestamptz `json:"criado_em"` +} + type CadastroProfissionai struct { ID pgtype.UUID `json:"id"` UsuarioID pgtype.UUID `json:"usuario_id"` @@ -38,6 +44,18 @@ type CadastroProfissionai struct { AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"` } +type Curso struct { + ID pgtype.UUID `json:"id"` + Nome string `json:"nome"` + CriadoEm pgtype.Timestamptz `json:"criado_em"` +} + +type Empresa struct { + ID pgtype.UUID `json:"id"` + Nome string `json:"nome"` + CriadoEm pgtype.Timestamptz `json:"criado_em"` +} + type FuncoesProfissionai struct { ID pgtype.UUID `json:"id"` Nome string `json:"nome"` @@ -45,6 +63,14 @@ type FuncoesProfissionai struct { AtualizadoEm pgtype.Timestamptz `json:"atualizado_em"` } +type PrecosTiposEvento struct { + ID pgtype.UUID `json:"id"` + TipoEventoID pgtype.UUID `json:"tipo_evento_id"` + FuncaoProfissionalID pgtype.UUID `json:"funcao_profissional_id"` + Valor pgtype.Numeric `json:"valor"` + CriadoEm pgtype.Timestamptz `json:"criado_em"` +} + type RefreshToken struct { ID pgtype.UUID `json:"id"` UsuarioID pgtype.UUID `json:"usuario_id"` @@ -56,6 +82,18 @@ type RefreshToken struct { CriadoEm pgtype.Timestamptz `json:"criado_em"` } +type TiposEvento struct { + ID pgtype.UUID `json:"id"` + Nome string `json:"nome"` + CriadoEm pgtype.Timestamptz `json:"criado_em"` +} + +type TiposServico struct { + ID pgtype.UUID `json:"id"` + Nome string `json:"nome"` + CriadoEm pgtype.Timestamptz `json:"criado_em"` +} + type Usuario struct { ID pgtype.UUID `json:"id"` Email string `json:"email"` diff --git a/backend/internal/db/generated/tipos_eventos.sql.go b/backend/internal/db/generated/tipos_eventos.sql.go new file mode 100644 index 0000000..dd94b91 --- /dev/null +++ b/backend/internal/db/generated/tipos_eventos.sql.go @@ -0,0 +1,184 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: tipos_eventos.sql + +package generated + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createPrecoEvento = `-- name: CreatePrecoEvento :one +INSERT INTO precos_tipos_eventos (tipo_evento_id, funcao_profissional_id, valor) +VALUES ($1, $2, $3) +ON CONFLICT (tipo_evento_id, funcao_profissional_id) DO UPDATE +SET valor = $3 +RETURNING id, tipo_evento_id, funcao_profissional_id, valor, criado_em +` + +type CreatePrecoEventoParams struct { + TipoEventoID pgtype.UUID `json:"tipo_evento_id"` + FuncaoProfissionalID pgtype.UUID `json:"funcao_profissional_id"` + Valor pgtype.Numeric `json:"valor"` +} + +func (q *Queries) CreatePrecoEvento(ctx context.Context, arg CreatePrecoEventoParams) (PrecosTiposEvento, error) { + row := q.db.QueryRow(ctx, createPrecoEvento, arg.TipoEventoID, arg.FuncaoProfissionalID, arg.Valor) + var i PrecosTiposEvento + err := row.Scan( + &i.ID, + &i.TipoEventoID, + &i.FuncaoProfissionalID, + &i.Valor, + &i.CriadoEm, + ) + return i, err +} + +const createTipoEvento = `-- name: CreateTipoEvento :one +INSERT INTO tipos_eventos (nome) VALUES ($1) RETURNING id, nome, criado_em +` + +func (q *Queries) CreateTipoEvento(ctx context.Context, nome string) (TiposEvento, error) { + row := q.db.QueryRow(ctx, createTipoEvento, nome) + var i TiposEvento + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} + +const deletePreco = `-- name: DeletePreco :exec +DELETE FROM precos_tipos_eventos WHERE id = $1 +` + +func (q *Queries) DeletePreco(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deletePreco, id) + return err +} + +const deleteTipoEvento = `-- name: DeleteTipoEvento :exec +DELETE FROM tipos_eventos WHERE id = $1 +` + +func (q *Queries) DeleteTipoEvento(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteTipoEvento, id) + return err +} + +const getPreco = `-- name: GetPreco :one +SELECT id, tipo_evento_id, funcao_profissional_id, valor, criado_em FROM precos_tipos_eventos WHERE tipo_evento_id = $1 AND funcao_profissional_id = $2 +` + +type GetPrecoParams struct { + TipoEventoID pgtype.UUID `json:"tipo_evento_id"` + FuncaoProfissionalID pgtype.UUID `json:"funcao_profissional_id"` +} + +func (q *Queries) GetPreco(ctx context.Context, arg GetPrecoParams) (PrecosTiposEvento, error) { + row := q.db.QueryRow(ctx, getPreco, arg.TipoEventoID, arg.FuncaoProfissionalID) + var i PrecosTiposEvento + err := row.Scan( + &i.ID, + &i.TipoEventoID, + &i.FuncaoProfissionalID, + &i.Valor, + &i.CriadoEm, + ) + return i, err +} + +const getTipoEventoByID = `-- name: GetTipoEventoByID :one +SELECT id, nome, criado_em FROM tipos_eventos WHERE id = $1 +` + +func (q *Queries) GetTipoEventoByID(ctx context.Context, id pgtype.UUID) (TiposEvento, error) { + row := q.db.QueryRow(ctx, getTipoEventoByID, id) + var i TiposEvento + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} + +const listPrecosByEventoID = `-- name: ListPrecosByEventoID :many +SELECT p.id, p.tipo_evento_id, p.funcao_profissional_id, p.valor, p.criado_em, f.nome as funcao_nome +FROM precos_tipos_eventos p +JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id +WHERE p.tipo_evento_id = $1 +` + +type ListPrecosByEventoIDRow struct { + ID pgtype.UUID `json:"id"` + TipoEventoID pgtype.UUID `json:"tipo_evento_id"` + FuncaoProfissionalID pgtype.UUID `json:"funcao_profissional_id"` + Valor pgtype.Numeric `json:"valor"` + CriadoEm pgtype.Timestamptz `json:"criado_em"` + FuncaoNome string `json:"funcao_nome"` +} + +func (q *Queries) ListPrecosByEventoID(ctx context.Context, tipoEventoID pgtype.UUID) ([]ListPrecosByEventoIDRow, error) { + rows, err := q.db.Query(ctx, listPrecosByEventoID, tipoEventoID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ListPrecosByEventoIDRow + for rows.Next() { + var i ListPrecosByEventoIDRow + if err := rows.Scan( + &i.ID, + &i.TipoEventoID, + &i.FuncaoProfissionalID, + &i.Valor, + &i.CriadoEm, + &i.FuncaoNome, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listTiposEventos = `-- name: ListTiposEventos :many +SELECT id, nome, criado_em FROM tipos_eventos ORDER BY nome +` + +func (q *Queries) ListTiposEventos(ctx context.Context) ([]TiposEvento, error) { + rows, err := q.db.Query(ctx, listTiposEventos) + if err != nil { + return nil, err + } + defer rows.Close() + var items []TiposEvento + for rows.Next() { + var i TiposEvento + if err := rows.Scan(&i.ID, &i.Nome, &i.CriadoEm); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateTipoEvento = `-- name: UpdateTipoEvento :one +UPDATE tipos_eventos SET nome = $2 WHERE id = $1 RETURNING id, nome, criado_em +` + +type UpdateTipoEventoParams struct { + ID pgtype.UUID `json:"id"` + Nome string `json:"nome"` +} + +func (q *Queries) UpdateTipoEvento(ctx context.Context, arg UpdateTipoEventoParams) (TiposEvento, error) { + row := q.db.QueryRow(ctx, updateTipoEvento, arg.ID, arg.Nome) + var i TiposEvento + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} diff --git a/backend/internal/db/generated/tipos_servicos.sql.go b/backend/internal/db/generated/tipos_servicos.sql.go new file mode 100644 index 0000000..d5fc561 --- /dev/null +++ b/backend/internal/db/generated/tipos_servicos.sql.go @@ -0,0 +1,83 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: tipos_servicos.sql + +package generated + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createTipoServico = `-- name: CreateTipoServico :one +INSERT INTO tipos_servicos (nome) VALUES ($1) RETURNING id, nome, criado_em +` + +func (q *Queries) CreateTipoServico(ctx context.Context, nome string) (TiposServico, error) { + row := q.db.QueryRow(ctx, createTipoServico, nome) + var i TiposServico + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} + +const deleteTipoServico = `-- name: DeleteTipoServico :exec +DELETE FROM tipos_servicos WHERE id = $1 +` + +func (q *Queries) DeleteTipoServico(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteTipoServico, id) + return err +} + +const getTipoServicoByID = `-- name: GetTipoServicoByID :one +SELECT id, nome, criado_em FROM tipos_servicos WHERE id = $1 +` + +func (q *Queries) GetTipoServicoByID(ctx context.Context, id pgtype.UUID) (TiposServico, error) { + row := q.db.QueryRow(ctx, getTipoServicoByID, id) + var i TiposServico + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} + +const listTiposServicos = `-- name: ListTiposServicos :many +SELECT id, nome, criado_em FROM tipos_servicos ORDER BY nome +` + +func (q *Queries) ListTiposServicos(ctx context.Context) ([]TiposServico, error) { + rows, err := q.db.Query(ctx, listTiposServicos) + if err != nil { + return nil, err + } + defer rows.Close() + var items []TiposServico + for rows.Next() { + var i TiposServico + if err := rows.Scan(&i.ID, &i.Nome, &i.CriadoEm); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateTipoServico = `-- name: UpdateTipoServico :one +UPDATE tipos_servicos SET nome = $2 WHERE id = $1 RETURNING id, nome, criado_em +` + +type UpdateTipoServicoParams struct { + ID pgtype.UUID `json:"id"` + Nome string `json:"nome"` +} + +func (q *Queries) UpdateTipoServico(ctx context.Context, arg UpdateTipoServicoParams) (TiposServico, error) { + row := q.db.QueryRow(ctx, updateTipoServico, arg.ID, arg.Nome) + var i TiposServico + err := row.Scan(&i.ID, &i.Nome, &i.CriadoEm) + return i, err +} diff --git a/backend/internal/db/queries/anos_formaturas.sql b/backend/internal/db/queries/anos_formaturas.sql new file mode 100644 index 0000000..8df2463 --- /dev/null +++ b/backend/internal/db/queries/anos_formaturas.sql @@ -0,0 +1,14 @@ +-- name: CreateAnoFormatura :one +INSERT INTO anos_formaturas (ano_semestre) VALUES ($1) RETURNING *; + +-- name: ListAnosFormaturas :many +SELECT * FROM anos_formaturas ORDER BY ano_semestre; + +-- name: GetAnoFormaturaByID :one +SELECT * FROM anos_formaturas WHERE id = $1; + +-- name: UpdateAnoFormatura :one +UPDATE anos_formaturas SET ano_semestre = $2 WHERE id = $1 RETURNING *; + +-- name: DeleteAnoFormatura :exec +DELETE FROM anos_formaturas WHERE id = $1; diff --git a/backend/internal/db/queries/cursos.sql b/backend/internal/db/queries/cursos.sql new file mode 100644 index 0000000..3c85f2c --- /dev/null +++ b/backend/internal/db/queries/cursos.sql @@ -0,0 +1,14 @@ +-- name: CreateCurso :one +INSERT INTO cursos (nome) VALUES ($1) RETURNING *; + +-- name: ListCursos :many +SELECT * FROM cursos ORDER BY nome; + +-- name: GetCursoByID :one +SELECT * FROM cursos WHERE id = $1; + +-- name: UpdateCurso :one +UPDATE cursos SET nome = $2 WHERE id = $1 RETURNING *; + +-- name: DeleteCurso :exec +DELETE FROM cursos WHERE id = $1; diff --git a/backend/internal/db/queries/empresas.sql b/backend/internal/db/queries/empresas.sql new file mode 100644 index 0000000..d598195 --- /dev/null +++ b/backend/internal/db/queries/empresas.sql @@ -0,0 +1,14 @@ +-- name: CreateEmpresa :one +INSERT INTO empresas (nome) VALUES ($1) RETURNING *; + +-- name: ListEmpresas :many +SELECT * FROM empresas ORDER BY nome; + +-- name: GetEmpresaByID :one +SELECT * FROM empresas WHERE id = $1; + +-- name: UpdateEmpresa :one +UPDATE empresas SET nome = $2 WHERE id = $1 RETURNING *; + +-- name: DeleteEmpresa :exec +DELETE FROM empresas WHERE id = $1; diff --git a/backend/internal/db/queries/tipos_eventos.sql b/backend/internal/db/queries/tipos_eventos.sql new file mode 100644 index 0000000..4c161bd --- /dev/null +++ b/backend/internal/db/queries/tipos_eventos.sql @@ -0,0 +1,33 @@ +-- name: CreateTipoEvento :one +INSERT INTO tipos_eventos (nome) VALUES ($1) RETURNING *; + +-- name: ListTiposEventos :many +SELECT * FROM tipos_eventos ORDER BY nome; + +-- name: GetTipoEventoByID :one +SELECT * FROM tipos_eventos WHERE id = $1; + +-- name: UpdateTipoEvento :one +UPDATE tipos_eventos SET nome = $2 WHERE id = $1 RETURNING *; + +-- name: DeleteTipoEvento :exec +DELETE FROM tipos_eventos WHERE id = $1; + +-- name: CreatePrecoEvento :one +INSERT INTO precos_tipos_eventos (tipo_evento_id, funcao_profissional_id, valor) +VALUES ($1, $2, $3) +ON CONFLICT (tipo_evento_id, funcao_profissional_id) DO UPDATE +SET valor = $3 +RETURNING *; + +-- name: ListPrecosByEventoID :many +SELECT p.*, f.nome as funcao_nome +FROM precos_tipos_eventos p +JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id +WHERE p.tipo_evento_id = $1; + +-- name: GetPreco :one +SELECT * FROM precos_tipos_eventos WHERE tipo_evento_id = $1 AND funcao_profissional_id = $2; + +-- name: DeletePreco :exec +DELETE FROM precos_tipos_eventos WHERE id = $1; diff --git a/backend/internal/db/queries/tipos_servicos.sql b/backend/internal/db/queries/tipos_servicos.sql new file mode 100644 index 0000000..205dfff --- /dev/null +++ b/backend/internal/db/queries/tipos_servicos.sql @@ -0,0 +1,14 @@ +-- name: CreateTipoServico :one +INSERT INTO tipos_servicos (nome) VALUES ($1) RETURNING *; + +-- name: ListTiposServicos :many +SELECT * FROM tipos_servicos ORDER BY nome; + +-- name: GetTipoServicoByID :one +SELECT * FROM tipos_servicos WHERE id = $1; + +-- name: UpdateTipoServico :one +UPDATE tipos_servicos SET nome = $2 WHERE id = $1 RETURNING *; + +-- name: DeleteTipoServico :exec +DELETE FROM tipos_servicos WHERE id = $1; diff --git a/backend/internal/db/schema.sql b/backend/internal/db/schema.sql index 593a940..d7f8ac1 100644 --- a/backend/internal/db/schema.sql +++ b/backend/internal/db/schema.sql @@ -64,3 +64,83 @@ CREATE TABLE refresh_tokens ( revogado BOOLEAN NOT NULL DEFAULT FALSE, criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW() ); + +-- Cursos Table +CREATE TABLE cursos ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + nome VARCHAR(100) UNIQUE NOT NULL, + criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +INSERT INTO cursos (nome) VALUES +('Administração Empresas'), ('Agronomia'), ('Arquitetura / Urbanismo'), ('Biomedicina'), ('Comunicação'), ('Contábeis'), +('Direito'), ('Economia'), ('Educação Física'), ('EFI I/EFI II'), ('EI/EFI'), ('EF II/EM'), ('EFI(5º ano)'), ('EFII'), +('EI'), ('EM'), ('EM / TEC'), ('Enfermagem'), ('Eng. Ambiental'), ('Eng. Elétrica'), ('Engenharia'), ('Estética'), +('Farmácia'), ('Fisioterapia'), ('Gastronomia'), ('Historia'), ('Jornalismo'), ('Med. Veterinária'), ('Medicina'), +('Nutrição'), ('Odontologia'), ('Outro'), ('Pedagogia'), ('Publicidade'), ('Superior Diversos'), ('Tec. Diversos'), +('Termomecânica'), ('Unificados'), ('Tec. Enfermagem'), ('Quimica'), ('EFI / EF II / EM'), ('Psicologia'), +('Terapia Ocupacinal'), ('R.I'); + +-- Empresas Table +CREATE TABLE empresas ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + nome VARCHAR(100) UNIQUE NOT NULL, + criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +INSERT INTO empresas (nome) VALUES +('Arte Formaturas'), ('JR Formaturas'), ('Perfil'), ('Photum'), ('Populi Formaturas'), ('Prime'), ('Smart'), ('Viva SP'), +('Antares'), ('Forcamp'), ('PNI'), ('Fábio Ribeiro'), ('Told - B2_(CAMPINAS)'), ('Told - B2_(RECIFE)'), +('NOVO - Told - B2_(SP)'), ('NOVO - Told - RUB_(SP)'), ('NOVO - TOLD'), ('Alpha Digital'), ('Golden'), ('Festa da Beca'), +('Ponta Eventos'), ('Toy SP'), ('MVP Formaturas'), ('RUB'); + +-- Anos Formaturas Table +CREATE TABLE anos_formaturas ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + ano_semestre VARCHAR(20) UNIQUE NOT NULL, -- Ex: 2019.2 + criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +INSERT INTO anos_formaturas (ano_semestre) VALUES +('2019.2'), ('2020.1'), ('2020.2'), ('2021.1'), ('2021.2'), ('2022.1'), ('2022.2'), ('2023.1'), ('2023.2'), +('2024.1'), ('2024.2'), ('2025.1'), ('2025.2'), ('2026.1'), ('2026.2'), ('2027.1'), ('2027.2'), ('2028.1'), +('2028.2'), ('2029.1'), ('2029.2'); + +-- Tipos Servicos Table +CREATE TABLE tipos_servicos ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + nome VARCHAR(50) UNIQUE NOT NULL, + criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +INSERT INTO tipos_servicos (nome) VALUES +('Fotógrafo'), ('Recepcionista'), ('Cinegrafista'), ('Coordenação'), ('Ponto de Foto'), ('Ponto de ID'), ('Estúdio'), +('Outro'), ('Controle'); + +-- Tipos Eventos Table +CREATE TABLE tipos_eventos ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + nome VARCHAR(100) UNIQUE NOT NULL, + criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +INSERT INTO tipos_eventos (nome) VALUES +('Identificação'), ('Baile'), ('Colação'), ('Col/Baile (mesmo local)'), ('Col/Baile (local diferente)'), ('Missa / Culto'), +('Churrasco'), ('Trote'), ('Outro'), ('Balada'), ('Jantar'), ('Festa Junina'), ('Colação Oficial'), ('Family Day'), +('Refeição'), ('Estudio ID e Family Day'), ('Estudio Colação / Baile'); + +-- Precos Tipos Eventos Table (Junction Table for Pricing) +CREATE TABLE precos_tipos_eventos ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + tipo_evento_id UUID REFERENCES tipos_eventos(id) ON DELETE CASCADE, + funcao_profissional_id UUID REFERENCES funcoes_profissionais(id) ON DELETE CASCADE, + valor NUMERIC(10,2) NOT NULL DEFAULT 0.00, + criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(), + UNIQUE(tipo_evento_id, funcao_profissional_id) +); + +-- Initial Pricing Seed (Examples based on image, requires joining IDs which makes raw SQL insert hard without known UUIDs. +-- For simplicity in schema.sql, we'll skip complex dynamic inserts. +-- The user can populate via API or we can write a more complex PL/pgSQL block if absolutely necessary, +-- but usually schema.sql is structure + static data. Dynamic pricing is better handled via admin or separate migration script. +-- Leaving table empty for now, or adding a comment.) diff --git a/backend/internal/empresas/handler.go b/backend/internal/empresas/handler.go new file mode 100644 index 0000000..72bf931 --- /dev/null +++ b/backend/internal/empresas/handler.go @@ -0,0 +1,133 @@ +package empresas + +import ( + "net/http" + "strings" + + "photum-backend/internal/db/generated" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +type Handler struct { + service *Service +} + +func NewHandler(service *Service) *Handler { + return &Handler{service: service} +} + +type EmpresaResponse struct { + ID string `json:"id"` + Nome string `json:"nome"` +} + +type CreateEmpresaRequest struct { + Nome string `json:"nome" binding:"required"` +} + +func toResponse(e generated.Empresa) EmpresaResponse { + return EmpresaResponse{ + ID: uuid.UUID(e.ID.Bytes).String(), + Nome: e.Nome, + } +} + +// Create godoc +// @Summary Create a new company +// @Tags empresas +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body CreateEmpresaRequest true "Empresa Name" +// @Success 201 {object} EmpresaResponse +// @Failure 400 {object} map[string]string +// @Failure 409 {object} map[string]string +// @Router /api/empresas [post] +func (h *Handler) Create(c *gin.Context) { + var req CreateEmpresaRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + empresa, err := h.service.Create(c.Request.Context(), req.Nome) + if err != nil { + if strings.Contains(err.Error(), "duplicate key value") { + c.JSON(http.StatusConflict, gin.H{"error": "Empresa already exists"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusCreated, toResponse(*empresa)) +} + +// List godoc +// @Summary List all companies +// @Tags empresas +// @Accept json +// @Produce json +// @Security BearerAuth +// @Success 200 {array} EmpresaResponse +// @Router /api/empresas [get] +func (h *Handler) List(c *gin.Context) { + empresas, err := h.service.List(c.Request.Context()) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + var response []EmpresaResponse + for _, empresa := range empresas { + response = append(response, toResponse(empresa)) + } + c.JSON(http.StatusOK, response) +} + +// Update godoc +// @Summary Update a company +// @Tags empresas +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path string true "Empresa ID" +// @Param request body CreateEmpresaRequest true "Empresa Name" +// @Success 200 {object} EmpresaResponse +// @Router /api/empresas/{id} [put] +func (h *Handler) Update(c *gin.Context) { + id := c.Param("id") + var req CreateEmpresaRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + empresa, err := h.service.Update(c.Request.Context(), id, req.Nome) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, toResponse(*empresa)) +} + +// Delete godoc +// @Summary Delete a company +// @Tags empresas +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path string true "Empresa ID" +// @Success 204 {object} nil +// @Router /api/empresas/{id} [delete] +func (h *Handler) Delete(c *gin.Context) { + id := c.Param("id") + if err := h.service.Delete(c.Request.Context(), id); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.Status(http.StatusNoContent) +} diff --git a/backend/internal/empresas/service.go b/backend/internal/empresas/service.go new file mode 100644 index 0000000..7bda922 --- /dev/null +++ b/backend/internal/empresas/service.go @@ -0,0 +1,67 @@ +package empresas + +import ( + "context" + "errors" + + "photum-backend/internal/db/generated" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" +) + +type Service struct { + queries *generated.Queries +} + +func NewService(queries *generated.Queries) *Service { + return &Service{queries: queries} +} + +func (s *Service) Create(ctx context.Context, nome string) (*generated.Empresa, error) { + empresa, err := s.queries.CreateEmpresa(ctx, nome) + if err != nil { + return nil, err + } + return &empresa, nil +} + +func (s *Service) List(ctx context.Context) ([]generated.Empresa, error) { + return s.queries.ListEmpresas(ctx) +} + +func (s *Service) GetByID(ctx context.Context, id string) (*generated.Empresa, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + empresa, err := s.queries.GetEmpresaByID(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) + if err != nil { + return nil, err + } + return &empresa, nil +} + +func (s *Service) Update(ctx context.Context, id, nome string) (*generated.Empresa, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + + empresa, err := s.queries.UpdateEmpresa(ctx, generated.UpdateEmpresaParams{ + ID: pgtype.UUID{Bytes: uuidVal, Valid: true}, + Nome: nome, + }) + if err != nil { + return nil, err + } + return &empresa, nil +} + +func (s *Service) Delete(ctx context.Context, id string) error { + uuidVal, err := uuid.Parse(id) + if err != nil { + return errors.New("invalid id") + } + return s.queries.DeleteEmpresa(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) +} diff --git a/backend/internal/funcoes/handler.go b/backend/internal/funcoes/handler.go index a4f92ae..1aba020 100644 --- a/backend/internal/funcoes/handler.go +++ b/backend/internal/funcoes/handler.go @@ -2,6 +2,7 @@ package funcoes import ( "net/http" + "strings" "photum-backend/internal/db/generated" @@ -22,6 +23,10 @@ type FuncaoResponse struct { Nome string `json:"nome"` } +type CreateFuncaoRequest struct { + Nome string `json:"nome" binding:"required"` +} + func toResponse(f generated.FuncoesProfissionai) FuncaoResponse { return FuncaoResponse{ ID: uuid.UUID(f.ID.Bytes).String(), @@ -36,15 +41,14 @@ func toResponse(f generated.FuncoesProfissionai) FuncaoResponse { // @Accept json // @Produce json // @Security BearerAuth -// @Param request body map[string]string true "Create Function Request" +// @Param request body CreateFuncaoRequest true "Create Function Request" // @Success 201 {object} FuncaoResponse // @Failure 400 {object} map[string]string +// @Failure 409 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/funcoes [post] func (h *Handler) Create(c *gin.Context) { - var req struct { - Nome string `json:"nome" binding:"required"` - } + var req CreateFuncaoRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return @@ -52,6 +56,10 @@ func (h *Handler) Create(c *gin.Context) { funcao, err := h.service.Create(c.Request.Context(), req.Nome) if err != nil { + if strings.Contains(err.Error(), "duplicate key value") { + c.JSON(http.StatusConflict, gin.H{"error": "Funcao already exists"}) + return + } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } @@ -65,7 +73,6 @@ func (h *Handler) Create(c *gin.Context) { // @Tags funcoes // @Accept json // @Produce json -// @Security BearerAuth // @Success 200 {array} FuncaoResponse // @Failure 500 {object} map[string]string // @Router /api/funcoes [get] @@ -92,16 +99,15 @@ func (h *Handler) List(c *gin.Context) { // @Produce json // @Security BearerAuth // @Param id path string true "Function ID" -// @Param request body map[string]string true "Update Function Request" +// @Param request body CreateFuncaoRequest true "Update Function Request" // @Success 200 {object} FuncaoResponse // @Failure 400 {object} map[string]string +// @Failure 409 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/funcoes/{id} [put] func (h *Handler) Update(c *gin.Context) { id := c.Param("id") - var req struct { - Nome string `json:"nome" binding:"required"` - } + var req CreateFuncaoRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return @@ -109,6 +115,10 @@ func (h *Handler) Update(c *gin.Context) { funcao, err := h.service.Update(c.Request.Context(), id, req.Nome) if err != nil { + if strings.Contains(err.Error(), "duplicate key value") { + c.JSON(http.StatusConflict, gin.H{"error": "Funcao already exists"}) + return + } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } diff --git a/backend/internal/tipos_eventos/handler.go b/backend/internal/tipos_eventos/handler.go new file mode 100644 index 0000000..e491aea --- /dev/null +++ b/backend/internal/tipos_eventos/handler.go @@ -0,0 +1,150 @@ +package tipos_eventos + +import ( + "net/http" + + "photum-backend/internal/db/generated" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" +) + +type Handler struct { + service *Service +} + +func NewHandler(service *Service) *Handler { + return &Handler{service: service} +} + +type EventoResponse struct { + ID string `json:"id"` + Nome string `json:"nome"` +} + +type PrecoResponse struct { + ID string `json:"id"` + TipoEventoID string `json:"tipo_evento_id"` + FuncaoProfissionalID string `json:"funcao_profissional_id"` + FuncaoNome string `json:"funcao_nome"` + Valor float64 `json:"valor"` +} + +func toEventoResponse(e generated.TiposEvento) EventoResponse { + return EventoResponse{ + ID: uuid.UUID(e.ID.Bytes).String(), + Nome: e.Nome, + } +} + +func fromPgNumeric(n pgtype.Numeric) float64 { + f, _ := n.Float64Value() + return f.Float64 +} + +type CreateEventoRequest struct { + Nome string `json:"nome" binding:"required"` +} + +// Create godoc +// @Summary Create a new event type +// @Tags tipos_eventos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body CreateEventoRequest true "Nome" +// @Success 201 {object} EventoResponse +// @Router /api/tipos-eventos [post] +func (h *Handler) Create(c *gin.Context) { + var req CreateEventoRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + event, err := h.service.Create(c.Request.Context(), req.Nome) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusCreated, toEventoResponse(*event)) +} + +// List godoc +// @Summary List all event types +// @Tags tipos_eventos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Success 200 {array} EventoResponse +// @Router /api/tipos-eventos [get] +func (h *Handler) List(c *gin.Context) { + events, err := h.service.List(c.Request.Context()) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + var response []EventoResponse + for _, e := range events { + response = append(response, toEventoResponse(e)) + } + c.JSON(http.StatusOK, response) +} + +// SetPrice godoc +// @Summary Set price for an event function +// @Tags tipos_eventos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body PriceInput true "Price Input" +// @Success 200 {object} map[string]string +// @Router /api/tipos-eventos/precos [post] +func (h *Handler) SetPrice(c *gin.Context) { + var req PriceInput + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + _, err := h.service.SetPrice(c.Request.Context(), req) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "price set"}) +} + +// ListPrices godoc +// @Summary List prices for an event +// @Tags tipos_eventos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path string true "Event ID" +// @Success 200 {array} PrecoResponse +// @Router /api/tipos-eventos/{id}/precos [get] +func (h *Handler) ListPrices(c *gin.Context) { + id := c.Param("id") + prices, err := h.service.ListPrices(c.Request.Context(), id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + var response []PrecoResponse + for _, p := range prices { + response = append(response, PrecoResponse{ + ID: uuid.UUID(p.ID.Bytes).String(), + TipoEventoID: uuid.UUID(p.TipoEventoID.Bytes).String(), + FuncaoProfissionalID: uuid.UUID(p.FuncaoProfissionalID.Bytes).String(), + FuncaoNome: p.FuncaoNome, + Valor: fromPgNumeric(p.Valor), + }) + } + c.JSON(http.StatusOK, response) +} diff --git a/backend/internal/tipos_eventos/service.go b/backend/internal/tipos_eventos/service.go new file mode 100644 index 0000000..e8f9d8e --- /dev/null +++ b/backend/internal/tipos_eventos/service.go @@ -0,0 +1,112 @@ +package tipos_eventos + +import ( + "context" + "errors" + + "photum-backend/internal/db/generated" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" +) + +type Service struct { + queries *generated.Queries +} + +func NewService(queries *generated.Queries) *Service { + return &Service{queries: queries} +} + +// Event CRUD + +func (s *Service) Create(ctx context.Context, nome string) (*generated.TiposEvento, error) { + event, err := s.queries.CreateTipoEvento(ctx, nome) + if err != nil { + return nil, err + } + return &event, nil +} + +func (s *Service) List(ctx context.Context) ([]generated.TiposEvento, error) { + return s.queries.ListTiposEventos(ctx) +} + +func (s *Service) GetByID(ctx context.Context, id string) (*generated.TiposEvento, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + event, err := s.queries.GetTipoEventoByID(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) + if err != nil { + return nil, err + } + return &event, nil +} + +func (s *Service) Update(ctx context.Context, id, nome string) (*generated.TiposEvento, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + event, err := s.queries.UpdateTipoEvento(ctx, generated.UpdateTipoEventoParams{ + ID: pgtype.UUID{Bytes: uuidVal, Valid: true}, + Nome: nome, + }) + if err != nil { + return nil, err + } + return &event, nil +} + +func (s *Service) Delete(ctx context.Context, id string) error { + uuidVal, err := uuid.Parse(id) + if err != nil { + return errors.New("invalid id") + } + return s.queries.DeleteTipoEvento(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) +} + +// Pricing Logic + +type PriceInput struct { + TipoEventoID string `json:"tipo_evento_id"` + FuncaoProfissionalID string `json:"funcao_profissional_id"` + Valor float64 `json:"valor"` +} + +func (s *Service) SetPrice(ctx context.Context, input PriceInput) (*generated.PrecosTiposEvento, error) { + eventoUUID, err := uuid.Parse(input.TipoEventoID) + if err != nil { + return nil, errors.New("invalid tipo_evento_id") + } + funcaoUUID, err := uuid.Parse(input.FuncaoProfissionalID) + if err != nil { + return nil, errors.New("invalid funcao_profissional_id") + } + + precio, err := s.queries.CreatePrecoEvento(ctx, generated.CreatePrecoEventoParams{ + TipoEventoID: pgtype.UUID{Bytes: eventoUUID, Valid: true}, + FuncaoProfissionalID: pgtype.UUID{Bytes: funcaoUUID, Valid: true}, + Valor: toPgNumeric(input.Valor), + }) + if err != nil { + return nil, err + } + return &precio, nil +} + +func (s *Service) ListPrices(ctx context.Context, eventoID string) ([]generated.ListPrecosByEventoIDRow, error) { + eventoUUID, err := uuid.Parse(eventoID) + if err != nil { + return nil, errors.New("invalid evento_id") + } + return s.queries.ListPrecosByEventoID(ctx, pgtype.UUID{Bytes: eventoUUID, Valid: true}) +} + +// Helper (Assuming user doesn't have it in shared utils or similar) +func toPgNumeric(f float64) pgtype.Numeric { + var n pgtype.Numeric + n.Scan(f) + return n +} diff --git a/backend/internal/tipos_servicos/handler.go b/backend/internal/tipos_servicos/handler.go new file mode 100644 index 0000000..02a7121 --- /dev/null +++ b/backend/internal/tipos_servicos/handler.go @@ -0,0 +1,133 @@ +package tipos_servicos + +import ( + "net/http" + "strings" + + "photum-backend/internal/db/generated" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +type Handler struct { + service *Service +} + +func NewHandler(service *Service) *Handler { + return &Handler{service: service} +} + +type TipoServicoResponse struct { + ID string `json:"id"` + Nome string `json:"nome"` +} + +type CreateTipoServicoRequest struct { + Nome string `json:"nome" binding:"required"` +} + +func toResponse(t generated.TiposServico) TipoServicoResponse { + return TipoServicoResponse{ + ID: uuid.UUID(t.ID.Bytes).String(), + Nome: t.Nome, + } +} + +// Create godoc +// @Summary Create a new service type +// @Tags tipos_servicos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param request body CreateTipoServicoRequest true "Nome" +// @Success 201 {object} TipoServicoResponse +// @Failure 400 {object} map[string]string +// @Failure 409 {object} map[string]string +// @Router /api/tipos-servicos [post] +func (h *Handler) Create(c *gin.Context) { + var req CreateTipoServicoRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + tipo, err := h.service.Create(c.Request.Context(), req.Nome) + if err != nil { + if strings.Contains(err.Error(), "duplicate key value") { + c.JSON(http.StatusConflict, gin.H{"error": "Tipo de Servico already exists"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusCreated, toResponse(*tipo)) +} + +// List godoc +// @Summary List all service types +// @Tags tipos_servicos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Success 200 {array} TipoServicoResponse +// @Router /api/tipos-servicos [get] +func (h *Handler) List(c *gin.Context) { + tipos, err := h.service.List(c.Request.Context()) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + var response []TipoServicoResponse + for _, tipo := range tipos { + response = append(response, toResponse(tipo)) + } + c.JSON(http.StatusOK, response) +} + +// Update godoc +// @Summary Update a service type +// @Tags tipos_servicos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path string true "ID" +// @Param request body CreateTipoServicoRequest true "Nome" +// @Success 200 {object} TipoServicoResponse +// @Router /api/tipos-servicos/{id} [put] +func (h *Handler) Update(c *gin.Context) { + id := c.Param("id") + var req CreateTipoServicoRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + tipo, err := h.service.Update(c.Request.Context(), id, req.Nome) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, toResponse(*tipo)) +} + +// Delete godoc +// @Summary Delete a service type +// @Tags tipos_servicos +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param id path string true "ID" +// @Success 204 {object} nil +// @Router /api/tipos-servicos/{id} [delete] +func (h *Handler) Delete(c *gin.Context) { + id := c.Param("id") + if err := h.service.Delete(c.Request.Context(), id); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.Status(http.StatusNoContent) +} diff --git a/backend/internal/tipos_servicos/service.go b/backend/internal/tipos_servicos/service.go new file mode 100644 index 0000000..5e3b4d2 --- /dev/null +++ b/backend/internal/tipos_servicos/service.go @@ -0,0 +1,67 @@ +package tipos_servicos + +import ( + "context" + "errors" + + "photum-backend/internal/db/generated" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" +) + +type Service struct { + queries *generated.Queries +} + +func NewService(queries *generated.Queries) *Service { + return &Service{queries: queries} +} + +func (s *Service) Create(ctx context.Context, nome string) (*generated.TiposServico, error) { + tipo, err := s.queries.CreateTipoServico(ctx, nome) + if err != nil { + return nil, err + } + return &tipo, nil +} + +func (s *Service) List(ctx context.Context) ([]generated.TiposServico, error) { + return s.queries.ListTiposServicos(ctx) +} + +func (s *Service) GetByID(ctx context.Context, id string) (*generated.TiposServico, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + tipo, err := s.queries.GetTipoServicoByID(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) + if err != nil { + return nil, err + } + return &tipo, nil +} + +func (s *Service) Update(ctx context.Context, id, nome string) (*generated.TiposServico, error) { + uuidVal, err := uuid.Parse(id) + if err != nil { + return nil, errors.New("invalid id") + } + + tipo, err := s.queries.UpdateTipoServico(ctx, generated.UpdateTipoServicoParams{ + ID: pgtype.UUID{Bytes: uuidVal, Valid: true}, + Nome: nome, + }) + if err != nil { + return nil, err + } + return &tipo, nil +} + +func (s *Service) Delete(ctx context.Context, id string) error { + uuidVal, err := uuid.Parse(id) + if err != nil { + return errors.New("invalid id") + } + return s.queries.DeleteTipoServico(ctx, pgtype.UUID{Bytes: uuidVal, Valid: true}) +}