package controllers import ( "net/http" "strconv" "github.com/gofiber/fiber/v2" "nearle/models" "nearle/services" ) type DeliverySlotController struct { service services.DeliverySlotService } func NewDeliverySlotController(service services.DeliverySlotService) *DeliverySlotController { return &DeliverySlotController{service: service} } /* GET /v1/web/deliveryslots?tenantid&locationid Everything a branch has configured, active or not, for the console's editor. A branch that has set nothing returns an empty list — see the note on Available about why that is never an error. */ func (ctl *DeliverySlotController) ListDeliverySlots(c *fiber.Ctx) error { tenantID, _ := strconv.Atoi(c.Query("tenantid")) locationID, _ := strconv.Atoi(c.Query("locationid")) if tenantID <= 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "code": http.StatusBadRequest, "message": "tenantid is required", "status": false, }) } slots, err := ctl.service.ListForBranch(tenantID, locationID) if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "code": http.StatusInternalServerError, "message": err.Error(), "status": false, }) } return c.Status(http.StatusOK).JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": slots, }) } /* PUT /v1/web/deliveryslots The branch's windows, all three together rather than one at a time: they are edited as a set on one screen, and sending them together is what lets the service reject the whole edit when one row is wrong instead of applying part of it. A business objection — an unreadable time, a window ending before it starts, the same key twice — is 409 and not 500. It is an answer about the request, and the message is written to be shown to the person who typed it. */ func (ctl *DeliverySlotController) SaveDeliverySlots(c *fiber.Ctx) error { var req struct { Tenantid int `json:"tenantid"` Locationid int `json:"locationid"` Slots []models.DeliverySlots `json:"slots"` } if err := c.BodyParser(&req); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "code": http.StatusBadRequest, "message": "Invalid request body", "status": false, }) } if req.Tenantid <= 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "code": http.StatusBadRequest, "message": "tenantid is required", "status": false, }) } if err := ctl.service.Save(req.Tenantid, req.Locationid, req.Slots); err != nil { return c.Status(http.StatusConflict).JSON(fiber.Map{ "code": http.StatusConflict, "message": err.Error(), "status": false, }) } return c.Status(http.StatusOK).JSON(fiber.Map{ "code": http.StatusOK, "message": "Successfully Updated", "status": true, }) } /* GET /v1/mob/deliveryslots/available?tenantid&locationid What the shopper may pick, already filtered and dated. The app renders this list and does no time arithmetic of its own — see the service for why one clock has to be authoritative. An empty list is 200 with `details: []`, NOT an error. It means this branch has set no windows, which is the state every shop is in today, and the app is required to fall back to ordering without one. Returning 404 here would turn an ordinary shop into a broken one. */ func (ctl *DeliverySlotController) AvailableDeliverySlots(c *fiber.Ctx) error { tenantID, _ := strconv.Atoi(c.Query("tenantid")) locationID, _ := strconv.Atoi(c.Query("locationid")) if tenantID <= 0 { return c.Status(http.StatusBadRequest).JSON(fiber.Map{ "code": http.StatusBadRequest, "message": "tenantid is required", "status": false, }) } slots, err := ctl.service.Available(tenantID, locationID) if err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ "code": http.StatusInternalServerError, "message": err.Error(), "status": false, }) } return c.Status(http.StatusOK).JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", "status": true, "details": slots, }) }