Merge pull request #39 from rede5/Front-back-integracao-task16

fix: ajustado fluxo e geracao de codigos de acesso
This commit is contained in:
Andre F. Rodrigues 2025-12-29 17:23:11 -03:00 committed by GitHub
commit 991897dcc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 175 additions and 9 deletions

View file

@ -146,6 +146,7 @@ func main() {
r.GET("/api/tipos-servicos", tiposServicosHandler.List) r.GET("/api/tipos-servicos", tiposServicosHandler.List)
r.GET("/api/tipos-eventos", tiposEventosHandler.List) r.GET("/api/tipos-eventos", tiposEventosHandler.List)
r.GET("/api/tipos-eventos/:id/precos", tiposEventosHandler.ListPrices) r.GET("/api/tipos-eventos/:id/precos", tiposEventosHandler.ListPrices)
r.GET("/api/public/codigos-acesso/verificar", codigosHandler.Verify)
// Protected Routes // Protected Routes
api := r.Group("/api") api := r.Group("/api")

View file

@ -2135,6 +2135,43 @@ const docTemplate = `{
} }
} }
}, },
"/api/public/codigos-acesso/verificar": {
"get": {
"tags": [
"codigos"
],
"summary": "Verify Access Code",
"parameters": [
{
"type": "string",
"description": "Code",
"name": "code",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": {
"type": "boolean"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/api/tipos-eventos": { "/api/tipos-eventos": {
"get": { "get": {
"security": [ "security": [

View file

@ -2129,6 +2129,43 @@
} }
} }
}, },
"/api/public/codigos-acesso/verificar": {
"get": {
"tags": [
"codigos"
],
"summary": "Verify Access Code",
"parameters": [
{
"type": "string",
"description": "Code",
"name": "code",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": {
"type": "boolean"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/api/tipos-eventos": { "/api/tipos-eventos": {
"get": { "get": {
"security": [ "security": [

View file

@ -1874,6 +1874,30 @@ paths:
summary: Update profissional summary: Update profissional
tags: tags:
- profissionais - profissionais
/api/public/codigos-acesso/verificar:
get:
parameters:
- description: Code
in: query
name: code
required: true
type: string
responses:
"200":
description: OK
schema:
additionalProperties:
type: boolean
type: object
"400":
description: Bad Request
schema:
additionalProperties:
type: string
type: object
summary: Verify Access Code
tags:
- codigos
/api/tipos-eventos: /api/tipos-eventos:
get: get:
consumes: consumes:

View file

@ -88,3 +88,34 @@ func (h *Handler) Delete(c *gin.Context) {
} }
c.JSON(http.StatusOK, gin.H{"message": "deleted"}) c.JSON(http.StatusOK, gin.H{"message": "deleted"})
} }
// Verify godoc
// @Summary Verify Access Code
// @Tags codigos
// @Param code query string true "Code"
// @Success 200 {object} map[string]bool
// @Failure 400 {object} map[string]string
// @Router /api/public/codigos-acesso/verificar [get]
func (h *Handler) Verify(c *gin.Context) {
code := c.Query("code")
if code == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "code required"})
return
}
err := h.service.Verify(c.Request.Context(), code)
if err != nil {
// Distinguish validation error from DB error strictly?
// For security, just say invalid.
// But service returns specific AppError.
if _, ok := err.(*AppError); ok {
c.JSON(http.StatusBadRequest, gin.H{"valid": false, "error": err.Error()})
return
}
// If check failed (not found etc), return 404 or 400
c.JSON(http.StatusBadRequest, gin.H{"valid": false, "error": "Código inválido"})
return
}
c.JSON(http.StatusOK, gin.H{"valid": true})
}

View file

@ -77,3 +77,29 @@ func (s *Service) IncrementUse(ctx context.Context, id uuid.UUID) error {
pgUUID.Valid = true pgUUID.Valid = true
return s.q.IncrementCodigoAcessoUso(ctx, pgUUID) return s.q.IncrementCodigoAcessoUso(ctx, pgUUID)
} }
// Custom error for validation
type AppError struct {
Message string
}
func (e *AppError) Error() string {
return e.Message
}
func (s *Service) Verify(ctx context.Context, code string) error {
c, err := s.q.GetCodigoAcesso(ctx, code)
if err != nil {
return err // Not found or DB error
}
if !c.Ativo {
return &AppError{Message: "Código inativo"}
}
if time.Now().After(c.ExpiraEm.Time) {
return &AppError{Message: "Código expirado"}
}
return nil
}

View file

@ -1,6 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Button } from "../components/Button"; import { Button } from "../components/Button";
import { X } from "lucide-react"; import { X } from "lucide-react";
import { verifyAccessCode } from "../services/apiService";
interface HomeProps { interface HomeProps {
onEnter: () => void; onEnter: () => void;
@ -12,26 +13,28 @@ export const Home: React.FC<HomeProps> = ({ onEnter }) => {
const [showProfessionalPrompt, setShowProfessionalPrompt] = useState(false); const [showProfessionalPrompt, setShowProfessionalPrompt] = useState(false);
const [codeError, setCodeError] = useState(""); const [codeError, setCodeError] = useState("");
// Código mockado - em produção, verificar com o backend
const MOCK_ACCESS_CODE = "PHOTUM2025";
const handleRegisterClick = () => { const handleRegisterClick = () => {
setShowProfessionalPrompt(true); setShowProfessionalPrompt(true);
setAccessCode(""); setAccessCode("");
setCodeError(""); setCodeError("");
}; };
const handleVerifyCode = () => { const handleVerifyCode = async () => {
if (accessCode.trim() === "") { if (accessCode.trim() === "") {
setCodeError("Por favor, digite o código de acesso"); setCodeError("Por favor, digite o código de acesso");
return; return;
} }
if (accessCode.toUpperCase() === MOCK_ACCESS_CODE) { try {
setShowAccessCodeModal(false); const res = await verifyAccessCode(accessCode.toUpperCase());
window.location.href = "/cadastro"; if (res.data && res.data.valid) {
} else { setShowAccessCodeModal(false);
setCodeError("Código de acesso inválido ou expirado"); window.location.href = "/cadastro";
} else {
setCodeError(res.data?.error || "Código de acesso inválido ou expirado");
}
} catch (e) {
setCodeError("Erro ao verificar código");
} }
}; };

View file

@ -1059,3 +1059,10 @@ export async function listPassengers(carroId: string, token: string) {
return { data, error: null }; return { data, error: null };
} catch (err: any) { return { data: null, error: err.message }; } } catch (err: any) { return { data: null, error: err.message }; }
} }
/**
* Verifica se um código de acesso é válido
*/
export async function verifyAccessCode(code: string): Promise<ApiResponse<{ valid: boolean; error?: string }>> {
return fetchFromBackend<{ valid: boolean; error?: string }>(`/api/public/codigos-acesso/verificar?code=${encodeURIComponent(code)}`);
}