+
+ Tap RFID on Scanner to Redeem
+
ID: {id}
From b18f86aee73d818521fbdf8fb542d5233280d454 Mon Sep 17 00:00:00 2001
From: Hugo Liu <98724522+hugoliu-code@users.noreply.github.com>
Date: Mon, 19 Jan 2026 15:56:46 -0500
Subject: [PATCH 13/16] Documentation
---
apps/api/internal/api/handlers/redeemables.go | 61 ++++++++++++++++++-
.../api/internal/db/repository/redeemables.go | 7 +--
2 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/apps/api/internal/api/handlers/redeemables.go b/apps/api/internal/api/handlers/redeemables.go
index df37806f..8c14c82b 100644
--- a/apps/api/internal/api/handlers/redeemables.go
+++ b/apps/api/internal/api/handlers/redeemables.go
@@ -30,6 +30,18 @@ func NewRedeemablesHandler(
}
}
+// GetRedeemables
+//
+// @Summary Get all redeemables for an event
+// @Description Retrieve a list of all redeemable items associated with a specific event ID.
+// @Tags Redeemables
+// @Accept json
+// @Produce json
+// @Param eventId path string true "Event ID (UUID)"
+// @Success 200 {array} sqlc.Redeemable
+// @Failure 400 {object} response.ErrorResponse "Missing or invalid Event ID"
+// @Failure 500 {object} response.ErrorResponse "Internal Server Error"
+// @Router /events/{eventId}/redeemables [get]
func (h *RedeemablesHandler) GetRedeemables(w http.ResponseWriter, r *http.Request) {
eventIdStr := chi.URLParam(r, "eventId")
if eventIdStr == "" {
@@ -56,6 +68,19 @@ type CreateRedeemableRequest struct {
MaxUserAmount int `json:"max_user_amount"`
}
+// CreateRedeemable
+//
+// @Summary Create a new redeemable
+// @Description Create a new redeemable item for a specific event.
+// @Tags Redeemables
+// @Accept json
+// @Produce json
+// @Param eventId path string true "Event ID (UUID)"
+// @Param request body CreateRedeemableRequest true "Redeemable creation data"
+// @Success 201 {object} sqlc.Redeemable
+// @Failure 400 {object} response.ErrorResponse "Invalid request body or ID"
+// @Failure 500 {object} response.ErrorResponse "Internal Server Error"
+// @Router /events/{eventId}/redeemables [post]
func (h *RedeemablesHandler) CreateRedeemable(w http.ResponseWriter, r *http.Request) {
eventIdStr := chi.URLParam(r, "eventId")
if eventIdStr == "" {
@@ -91,6 +116,19 @@ type UpdateRedeemableRequest struct {
MaxUserAmount *int `json:"max_user_amount,omitempty"`
}
+// UpdateRedeemable
+//
+// @Summary Update an existing redeemable
+// @Description Update specific fields (name, stock, max per user) of a redeemable.
+// @Tags Redeemables
+// @Accept json
+// @Produce json
+// @Param redeemableId path string true "Redeemable ID (UUID)"
+// @Param request body UpdateRedeemableRequest true "Redeemable update data (partial fields allowed)"
+// @Success 201 {object} sqlc.Redeemable
+// @Failure 400 {object} response.ErrorResponse "Invalid ID or request body"
+// @Failure 500 {object} response.ErrorResponse "Internal Server Error"
+// @Router /redeemables/{redeemableId} [patch]
func (h *RedeemablesHandler) UpdateRedeemable(w http.ResponseWriter, r *http.Request) {
redeemableIdStr := chi.URLParam(r, "redeemableId")
if redeemableIdStr == "" {
@@ -119,9 +157,18 @@ func (h *RedeemablesHandler) UpdateRedeemable(w http.ResponseWriter, r *http.Req
res.Send(w, http.StatusCreated, redeemable)
return
- // decode request body
}
+// DeleteRedeemable
+//
+// @Summary Delete a redeemable
+// @Description Permanently delete a redeemable item by ID.
+// @Tags Redeemables
+// @Param redeemableId path string true "Redeemable ID (UUID)"
+// @Success 204 "No Content"
+// @Failure 400 {object} response.ErrorResponse "Invalid Redeemable ID"
+// @Failure 500 {object} response.ErrorResponse "Internal Server Error"
+// @Router /redeemables/{redeemableId} [delete]
func (h *RedeemablesHandler) DeleteRedeemable(w http.ResponseWriter, r *http.Request) {
redeemableIdStr := chi.URLParam(r, "redeemableId")
if redeemableIdStr == "" {
@@ -141,6 +188,17 @@ func (h *RedeemablesHandler) DeleteRedeemable(w http.ResponseWriter, r *http.Req
res.Send(w, http.StatusNoContent, nil)
}
+// RedeemRedeemable
+//
+// @Summary Redeem an item for a user
+// @Description Create a redemption record linking a specific user to a redeemable item.
+// @Tags Redeemables
+// @Param redeemableId path string true "Redeemable ID (UUID)"
+// @Param userId path string true "User ID (UUID)"
+// @Success 204 "No Content"
+// @Failure 400 {object} response.ErrorResponse "Invalid IDs"
+// @Failure 500 {object} response.ErrorResponse "Internal Server Error"
+// @Router /redeemables/{redeemableId}/users/{userId} [post]
func (h *RedeemablesHandler) RedeemRedeemable(w http.ResponseWriter, r *http.Request) {
// user id, redeemable id
userIdStr := chi.URLParam(r, "userId")
@@ -171,6 +229,7 @@ func (h *RedeemablesHandler) RedeemRedeemable(w http.ResponseWriter, r *http.Req
res.Send(w, http.StatusNoContent, nil)
}
+// UNUSED FOR NOW
func (h *RedeemablesHandler) UpdateRedemption(w http.ResponseWriter, r *http.Request) {
// user id, redeemable id
userIdStr := chi.URLParam(r, "userId")
diff --git a/apps/api/internal/db/repository/redeemables.go b/apps/api/internal/db/repository/redeemables.go
index 69191970..9d2f460e 100644
--- a/apps/api/internal/db/repository/redeemables.go
+++ b/apps/api/internal/db/repository/redeemables.go
@@ -21,7 +21,7 @@ func NewRedeemablesRepository(db *db.DB) *RedeemablesRepository {
func (r *RedeemablesRepository) GetRedeemablesByEventID(ctx context.Context, eventID uuid.UUID) (*[]sqlc.GetRedeemablesByEventIDRow, error) {
redeemables, err := r.db.Query.GetRedeemablesByEventID(ctx, eventID)
if err != nil {
- return nil, err // TODO: more robust error handling
+ return nil, err
}
return &redeemables, nil
}
@@ -35,10 +35,9 @@ func (r *RedeemablesRepository) CreateRedeemable(ctx context.Context, eventID uu
}
redeemable, err := r.db.Query.CreateRedeemable(ctx, params)
if err != nil {
- return nil, err // TODO: more robust error handling
+ return nil, err
}
return &redeemable, nil
- // TODO: make sure this is used properly
}
func (r *RedeemablesRepository) DeleteRedeemable(ctx context.Context, redeemableID uuid.UUID) error {
err := r.db.Query.DeleteRedeemable(ctx, redeemableID)
@@ -69,7 +68,6 @@ func (r *RedeemablesRepository) UpdateRedeemable(ctx context.Context, redeemable
return nil, err
}
return &redeemable, nil
- // TODO: make sure the this is used properly
}
func (r *RedeemablesRepository) RedeemRedeemable(ctx context.Context, redeemableID uuid.UUID, userID uuid.UUID) (*sqlc.UserRedemption, error) {
@@ -82,7 +80,6 @@ func (r *RedeemablesRepository) RedeemRedeemable(ctx context.Context, redeemable
return nil, err
}
return &redemption, nil
- // TODO: make sure this is used properly
}
func (r *RedeemablesRepository) UpdateRedemption(ctx context.Context, redeemableID uuid.UUID, userID uuid.UUID) error {
From 5c24a0ea5eee073ed9dd7d181cb0261820ab1fa4 Mon Sep 17 00:00:00 2001
From: Hugo Liu <98724522+hugoliu-code@users.noreply.github.com>
Date: Mon, 19 Jan 2026 16:01:52 -0500
Subject: [PATCH 14/16] UpdateRedemption missing body fix
---
apps/api/internal/api/handlers/redeemables.go | 15 +++++++++++++--
apps/api/internal/db/repository/redeemables.go | 3 ++-
apps/api/internal/services/redeemables.go | 4 ++--
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/apps/api/internal/api/handlers/redeemables.go b/apps/api/internal/api/handlers/redeemables.go
index 8c14c82b..5e39b54d 100644
--- a/apps/api/internal/api/handlers/redeemables.go
+++ b/apps/api/internal/api/handlers/redeemables.go
@@ -229,7 +229,10 @@ func (h *RedeemablesHandler) RedeemRedeemable(w http.ResponseWriter, r *http.Req
res.Send(w, http.StatusNoContent, nil)
}
-// UNUSED FOR NOW
+type UpdateRedemptionRequest struct {
+ amount int `json:"new_amount,omitempty"`
+}
+
func (h *RedeemablesHandler) UpdateRedemption(w http.ResponseWriter, r *http.Request) {
// user id, redeemable id
userIdStr := chi.URLParam(r, "userId")
@@ -252,7 +255,15 @@ func (h *RedeemablesHandler) UpdateRedemption(w http.ResponseWriter, r *http.Req
res.SendError(w, http.StatusBadRequest, res.NewError("invalid_redeemable_id", "The redeemable ID is not a valid UUID"))
return
}
- err = h.redeemablesService.UpdateRedemption(r.Context(), redeemableId, userId)
+ var req UpdateRedemptionRequest
+ decoder := json.NewDecoder(r.Body)
+ decoder.DisallowUnknownFields()
+ if err := decoder.Decode(&req); err != nil {
+ res.SendError(w, http.StatusBadRequest, res.NewError("invalid_request_body", "The request body is invalid: "+err.Error()))
+ return
+ }
+
+ err = h.redeemablesService.UpdateRedemption(r.Context(), redeemableId, userId, req.amount)
if err != nil {
res.SendError(w, http.StatusInternalServerError, res.NewError("internal_server_error", "internal service error, failed to update redemption"))
return
diff --git a/apps/api/internal/db/repository/redeemables.go b/apps/api/internal/db/repository/redeemables.go
index 9d2f460e..4270d0cf 100644
--- a/apps/api/internal/db/repository/redeemables.go
+++ b/apps/api/internal/db/repository/redeemables.go
@@ -82,10 +82,11 @@ func (r *RedeemablesRepository) RedeemRedeemable(ctx context.Context, redeemable
return &redemption, nil
}
-func (r *RedeemablesRepository) UpdateRedemption(ctx context.Context, redeemableID uuid.UUID, userID uuid.UUID) error {
+func (r *RedeemablesRepository) UpdateRedemption(ctx context.Context, redeemableID uuid.UUID, userID uuid.UUID, amount int) error {
err := r.db.Query.UpdateRedemption(ctx, sqlc.UpdateRedemptionParams{
RedeemableID: redeemableID,
UserID: userID,
+ Amount: int32(amount),
})
if err != nil {
return err
diff --git a/apps/api/internal/services/redeemables.go b/apps/api/internal/services/redeemables.go
index 8c5e27ff..bf02076e 100644
--- a/apps/api/internal/services/redeemables.go
+++ b/apps/api/internal/services/redeemables.go
@@ -69,8 +69,8 @@ func (s *RedeemablesService) RedeemRedeemable(ctx context.Context, redeemableID
return nil
}
-func (s *RedeemablesService) UpdateRedemption(ctx context.Context, redeemableID uuid.UUID, userID uuid.UUID) error {
- err := s.redeemablesRepo.UpdateRedemption(ctx, redeemableID, userID)
+func (s *RedeemablesService) UpdateRedemption(ctx context.Context, redeemableID uuid.UUID, userID uuid.UUID, amount int) error {
+ err := s.redeemablesRepo.UpdateRedemption(ctx, redeemableID, userID, amount)
if err != nil {
s.logger.Error().Err(err).Msg("failed to update redemption")
return err
From e6562a74baf1b69235db3398e096a9c5cc150171 Mon Sep 17 00:00:00 2001
From: Hugo Liu <98724522+hugoliu-code@users.noreply.github.com>
Date: Mon, 19 Jan 2026 16:02:45 -0500
Subject: [PATCH 15/16] 1 line bug fix
---
apps/api/internal/api/handlers/redeemables.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/api/internal/api/handlers/redeemables.go b/apps/api/internal/api/handlers/redeemables.go
index 5e39b54d..96f8fe58 100644
--- a/apps/api/internal/api/handlers/redeemables.go
+++ b/apps/api/internal/api/handlers/redeemables.go
@@ -230,7 +230,7 @@ func (h *RedeemablesHandler) RedeemRedeemable(w http.ResponseWriter, r *http.Req
}
type UpdateRedemptionRequest struct {
- amount int `json:"new_amount,omitempty"`
+ Amount int `json:"new_amount,omitempty"`
}
func (h *RedeemablesHandler) UpdateRedemption(w http.ResponseWriter, r *http.Request) {
@@ -263,7 +263,7 @@ func (h *RedeemablesHandler) UpdateRedemption(w http.ResponseWriter, r *http.Req
return
}
- err = h.redeemablesService.UpdateRedemption(r.Context(), redeemableId, userId, req.amount)
+ err = h.redeemablesService.UpdateRedemption(r.Context(), redeemableId, userId, req.Amount)
if err != nil {
res.SendError(w, http.StatusInternalServerError, res.NewError("internal_server_error", "internal service error, failed to update redemption"))
return
From 4b19a8560781c8fbe6555786420376d17c1dbeac Mon Sep 17 00:00:00 2001
From: Hugo Liu <98724522+hugoliu-code@users.noreply.github.com>
Date: Mon, 19 Jan 2026 18:08:54 -0500
Subject: [PATCH 16/16] Cleaner handlers uuid parsing + qr throttling
---
apps/api/internal/api/handlers/redeemables.go | 103 ++++++++----------
.../components/RedeemableDetailsModal.tsx | 61 +++++++----
2 files changed, 82 insertions(+), 82 deletions(-)
diff --git a/apps/api/internal/api/handlers/redeemables.go b/apps/api/internal/api/handlers/redeemables.go
index 96f8fe58..5178b416 100644
--- a/apps/api/internal/api/handlers/redeemables.go
+++ b/apps/api/internal/api/handlers/redeemables.go
@@ -4,12 +4,11 @@ import (
"encoding/json"
"net/http"
- "github.com/go-chi/chi/v5"
- "github.com/google/uuid"
"github.com/rs/zerolog"
res "github.com/swamphacks/core/apps/api/internal/api/response"
"github.com/swamphacks/core/apps/api/internal/config"
"github.com/swamphacks/core/apps/api/internal/services"
+ "github.com/swamphacks/core/apps/api/internal/web"
)
type RedeemablesHandler struct {
@@ -43,16 +42,14 @@ func NewRedeemablesHandler(
// @Failure 500 {object} response.ErrorResponse "Internal Server Error"
// @Router /events/{eventId}/redeemables [get]
func (h *RedeemablesHandler) GetRedeemables(w http.ResponseWriter, r *http.Request) {
- eventIdStr := chi.URLParam(r, "eventId")
- if eventIdStr == "" {
- res.SendError(w, http.StatusBadRequest, res.NewError("missing_event_id", "The event ID is missing from the URL!"))
- return
- }
- eventId, err := uuid.Parse(eventIdStr)
+ eventId, err := web.PathParamToUUID(r, "eventId")
if err != nil {
- res.SendError(w, http.StatusBadRequest, res.NewError("invalid_event_id", "The event ID is not a valid UUID"))
+ res.SendError(w, http.StatusBadRequest,
+ res.NewError("invalid_request", "Invalid or missing event_id."),
+ )
return
}
+
redeemables, err := h.redeemablesService.GetRedeemablesByEventID(r.Context(), eventId)
if err != nil {
res.SendError(w, http.StatusInternalServerError, res.NewError("internal_server_error", "internal service error, failed to get redeemables by ID"))
@@ -82,14 +79,11 @@ type CreateRedeemableRequest struct {
// @Failure 500 {object} response.ErrorResponse "Internal Server Error"
// @Router /events/{eventId}/redeemables [post]
func (h *RedeemablesHandler) CreateRedeemable(w http.ResponseWriter, r *http.Request) {
- eventIdStr := chi.URLParam(r, "eventId")
- if eventIdStr == "" {
- res.SendError(w, http.StatusBadRequest, res.NewError("missing_event_id", "The event ID is missing from the URL!"))
- return
- }
- eventId, err := uuid.Parse(eventIdStr)
+ eventId, err := web.PathParamToUUID(r, "eventId")
if err != nil {
- res.SendError(w, http.StatusBadRequest, res.NewError("invalid_event_id", "The event ID is not a valid UUID"))
+ res.SendError(w, http.StatusBadRequest,
+ res.NewError("invalid_request", "Invalid or missing event_id."),
+ )
return
}
@@ -101,6 +95,11 @@ func (h *RedeemablesHandler) CreateRedeemable(w http.ResponseWriter, r *http.Req
return
}
+ if req.Name == "" || req.Amount <= 0 {
+ res.SendError(w, http.StatusBadRequest, res.NewError("invalid_params", "name and amount are required"))
+ return
+ }
+
redeemable, err := h.redeemablesService.CreateRedeemable(r.Context(), eventId, req.Name, req.Amount, req.MaxUserAmount)
if err != nil {
res.SendError(w, http.StatusInternalServerError, res.NewError("internal_server_error", "internal service error, failed to create redeemable"))
@@ -125,19 +124,16 @@ type UpdateRedeemableRequest struct {
// @Produce json
// @Param redeemableId path string true "Redeemable ID (UUID)"
// @Param request body UpdateRedeemableRequest true "Redeemable update data (partial fields allowed)"
-// @Success 201 {object} sqlc.Redeemable
+// @Success 200 {object} sqlc.Redeemable
// @Failure 400 {object} response.ErrorResponse "Invalid ID or request body"
// @Failure 500 {object} response.ErrorResponse "Internal Server Error"
// @Router /redeemables/{redeemableId} [patch]
func (h *RedeemablesHandler) UpdateRedeemable(w http.ResponseWriter, r *http.Request) {
- redeemableIdStr := chi.URLParam(r, "redeemableId")
- if redeemableIdStr == "" {
- res.SendError(w, http.StatusBadRequest, res.NewError("missing_redeemable_id", "The redeemable ID is missing from the URL!"))
- return
- }
- redeemableId, err := uuid.Parse(redeemableIdStr)
+ redeemableId, err := web.PathParamToUUID(r, "redeemableId")
if err != nil {
- res.SendError(w, http.StatusBadRequest, res.NewError("invalid_redeemable_id", "The redeemable ID is not a valid UUID"))
+ res.SendError(w, http.StatusBadRequest,
+ res.NewError("invalid_request", "Invalid or missing redeemable_id."),
+ )
return
}
@@ -170,16 +166,14 @@ func (h *RedeemablesHandler) UpdateRedeemable(w http.ResponseWriter, r *http.Req
// @Failure 500 {object} response.ErrorResponse "Internal Server Error"
// @Router /redeemables/{redeemableId} [delete]
func (h *RedeemablesHandler) DeleteRedeemable(w http.ResponseWriter, r *http.Request) {
- redeemableIdStr := chi.URLParam(r, "redeemableId")
- if redeemableIdStr == "" {
- res.SendError(w, http.StatusBadRequest, res.NewError("missing_redeemable_id", "The redeemable ID is missing from the URL!"))
- return
- }
- redeemableId, err := uuid.Parse(redeemableIdStr)
+ redeemableId, err := web.PathParamToUUID(r, "redeemableId")
if err != nil {
- res.SendError(w, http.StatusBadRequest, res.NewError("invalid_redeemable_id", "The redeemable ID is not a valid UUID"))
+ res.SendError(w, http.StatusBadRequest,
+ res.NewError("invalid_request", "Invalid or missing redeemable_id."),
+ )
return
}
+
err = h.redeemablesService.DeleteRedeemable(r.Context(), redeemableId)
if err != nil {
res.SendError(w, http.StatusInternalServerError, res.NewError("internal_server_error", "internal service error, failed to delete redeemable"))
@@ -201,26 +195,22 @@ func (h *RedeemablesHandler) DeleteRedeemable(w http.ResponseWriter, r *http.Req
// @Router /redeemables/{redeemableId}/users/{userId} [post]
func (h *RedeemablesHandler) RedeemRedeemable(w http.ResponseWriter, r *http.Request) {
// user id, redeemable id
- userIdStr := chi.URLParam(r, "userId")
- if userIdStr == "" {
- res.SendError(w, http.StatusBadRequest, res.NewError("missing_user_id", "The user ID is missing from the URL!"))
- return
- }
- userId, err := uuid.Parse(userIdStr)
+ userId, err := web.PathParamToUUID(r, "userId")
if err != nil {
- res.SendError(w, http.StatusBadRequest, res.NewError("invalid_user_id", "The user ID is not a valid UUID"))
+ res.SendError(w, http.StatusBadRequest,
+ res.NewError("invalid_request", "Invalid or missing user_id."),
+ )
return
}
- redeemableIdStr := chi.URLParam(r, "redeemableId")
- if redeemableIdStr == "" {
- res.SendError(w, http.StatusBadRequest, res.NewError("missing_redeemable_id", "The redeemable ID is missing from the URL!"))
- return
- }
- redeemableId, err := uuid.Parse(redeemableIdStr)
+
+ redeemableId, err := web.PathParamToUUID(r, "redeemableId")
if err != nil {
- res.SendError(w, http.StatusBadRequest, res.NewError("invalid_redeemable_id", "The redeemable ID is not a valid UUID"))
+ res.SendError(w, http.StatusBadRequest,
+ res.NewError("invalid_request", "Invalid or missing redeemable_id."),
+ )
return
}
+
err = h.redeemablesService.RedeemRedeemable(r.Context(), redeemableId, userId)
if err != nil {
res.SendError(w, http.StatusInternalServerError, res.NewError("internal_server_error", "internal service error, failed to redeem redeemable"))
@@ -235,24 +225,19 @@ type UpdateRedemptionRequest struct {
func (h *RedeemablesHandler) UpdateRedemption(w http.ResponseWriter, r *http.Request) {
// user id, redeemable id
- userIdStr := chi.URLParam(r, "userId")
- if userIdStr == "" {
- res.SendError(w, http.StatusBadRequest, res.NewError("missing_user_id", "The user ID is missing from the URL!"))
- return
- }
- userId, err := uuid.Parse(userIdStr)
+ userId, err := web.PathParamToUUID(r, "userId")
if err != nil {
- res.SendError(w, http.StatusBadRequest, res.NewError("invalid_user_id", "The user ID is not a valid UUID"))
+ res.SendError(w, http.StatusBadRequest,
+ res.NewError("invalid_request", "Invalid or missing user_id."),
+ )
return
}
- redeemableIdStr := chi.URLParam(r, "redeemableId")
- if redeemableIdStr == "" {
- res.SendError(w, http.StatusBadRequest, res.NewError("missing_redeemable_id", "The redeemable ID is missing from the URL!"))
- return
- }
- redeemableId, err := uuid.Parse(redeemableIdStr)
+
+ redeemableId, err := web.PathParamToUUID(r, "redeemableId")
if err != nil {
- res.SendError(w, http.StatusBadRequest, res.NewError("invalid_redeemable_id", "The redeemable ID is not a valid UUID"))
+ res.SendError(w, http.StatusBadRequest,
+ res.NewError("invalid_request", "Invalid or missing redeemable_id."),
+ )
return
}
var req UpdateRedemptionRequest
diff --git a/apps/web/src/features/Redeemables/components/RedeemableDetailsModal.tsx b/apps/web/src/features/Redeemables/components/RedeemableDetailsModal.tsx
index d7cabd30..5986c132 100644
--- a/apps/web/src/features/Redeemables/components/RedeemableDetailsModal.tsx
+++ b/apps/web/src/features/Redeemables/components/RedeemableDetailsModal.tsx
@@ -5,7 +5,7 @@ import {
} from "react-aria-components";
import { Button } from "@/components/ui/Button";
import TablerTrash from "~icons/tabler/trash";
-import { useContext, useRef, useEffect, useState } from "react";
+import { useContext, useRef, useEffect, useState, useCallback } from "react";
import { Text } from "react-aria-components";
import {
useRedeemRedeemable,
@@ -46,6 +46,7 @@ export function RedeemableDetailsModal({
const [rfidInput, setRfidInput] = useState("");
const [mode, setMode] = useState("details");
const inputRef = useRef(null);
+ const [isScanning, setIsScanning] = useState(false);
const { mutateAsync: redeemRedeemable, isPending } = useRedeemRedeemable(
eventId,
id,
@@ -111,29 +112,42 @@ export function RedeemableDetailsModal({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [rfidInput]);
- const onScan = async (scannedData: IDetectedBarcode[]) => {
- const res = parseQrIntent(scannedData[0].rawValue);
- if (res.ok && res.value.intent === Intent.IDENT) {
- try {
- await redeemRedeemable(res.value.user_id);
-
- showToast({
- title: "Success",
- message: "Redeemable redeemed successfully!",
- type: "success",
- });
- } catch (error) {
- showToast({
- title: "Error",
- message:
- "Failed to redeem with QR. User not found or already redeemed.",
- type: "error",
- });
- console.log("Redeem error:", error);
- }
- }
- };
+ const onScan = useCallback(
+ async (scannedData: IDetectedBarcode[]) => {
+ const res = parseQrIntent(scannedData[0].rawValue);
+
+ if (res.ok && res.value.intent === Intent.IDENT) {
+ setIsScanning(true);
+ try {
+ await redeemRedeemable(res.value.user_id);
+
+ showToast({
+ title: "Success",
+ message: "Redeemable redeemed successfully!",
+ type: "success",
+ });
+
+ // 4. Unlock after your desired delay
+ setTimeout(() => {
+ setIsScanning(false);
+ }, 5000);
+ } catch (error) {
+ showToast({
+ title: "Error",
+ message:
+ "Failed to redeem with QR. User not found or already redeemed.",
+ type: "error",
+ });
+ console.log("QR Scan error:", error);
+ setTimeout(() => {
+ setIsScanning(false);
+ }, 2000);
+ }
+ }
+ },
+ [redeemRedeemable],
+ );
const handleRedeem = async () => {
if (rfidInput.length !== 10) {
showToast({
@@ -301,6 +315,7 @@ export function RedeemableDetailsModal({
sound={false}
formats={["qr_code"]}
onScan={onScan}
+ paused={isScanning}
scanDelay={500}
onError={(err) => console.log(err)}
/>