Files
Tomas Dvorak 3cb40adb23 first commit
2026-04-10 12:04:09 +02:00

132 lines
3.9 KiB
Go

package httpapi
import (
"net/http"
"github.com/gin-gonic/gin"
"productier/apps/backend/internal/store"
)
func (s *Server) registerProductivityRoutes(group *gin.RouterGroup) {
// Inbox
group.GET("/inbox", func(c *gin.Context) {
workspaceSlug := c.Query("workspaceSlug")
if _, ok := s.requireWorkspaceMember(c, workspaceSlug); !ok {
return
}
c.JSON(http.StatusOK, gin.H{"data": s.store.ListInboxItems(workspaceSlug)})
})
group.POST("/inbox", func(c *gin.Context) {
var input store.CreateInboxItemInput
if err := c.ShouldBindJSON(&input); err != nil {
s.writeStatusError(c, http.StatusBadRequest, err.Error())
return
}
if _, ok := s.requireWorkspaceMember(c, input.WorkspaceSlug); !ok {
return
}
item := s.store.CreateInboxItem(input)
c.JSON(http.StatusCreated, gin.H{"data": item})
})
group.POST("/inbox/:itemId/process", func(c *gin.Context) {
var input struct {
EntityType string `json:"entityType" binding:"required"`
EntityID string `json:"entityId" binding:"required"`
}
if err := c.ShouldBindJSON(&input); err != nil {
s.writeStatusError(c, http.StatusBadRequest, err.Error())
return
}
if err := s.store.ProcessInboxItem(c.Param("itemId"), input.EntityType, input.EntityID); err != nil {
s.writeStatusError(c, http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
})
group.DELETE("/inbox/:itemId", func(c *gin.Context) {
if err := s.store.DeleteInboxItem(c.Param("itemId")); err != nil {
s.writeStatusError(c, http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
})
// Time entries
group.GET("/time-entries", func(c *gin.Context) {
workspaceSlug := c.Query("workspaceSlug")
if _, ok := s.requireWorkspaceMember(c, workspaceSlug); !ok {
return
}
c.JSON(http.StatusOK, gin.H{"data": s.store.ListTimeEntries(workspaceSlug)})
})
group.POST("/time-entries", func(c *gin.Context) {
var input store.CreateTimeEntryInput
if err := c.ShouldBindJSON(&input); err != nil {
s.writeStatusError(c, http.StatusBadRequest, err.Error())
return
}
if _, ok := s.requireWorkspaceMember(c, input.WorkspaceSlug); !ok {
return
}
entry := s.store.CreateTimeEntry(input)
c.JSON(http.StatusCreated, gin.H{"data": entry})
})
group.PATCH("/time-entries/:entryId", func(c *gin.Context) {
var input store.UpdateTimeEntryInput
if err := c.ShouldBindJSON(&input); err != nil {
s.writeStatusError(c, http.StatusBadRequest, err.Error())
return
}
updated, err := s.store.UpdateTimeEntry(c.Param("entryId"), input)
if err != nil {
s.writeStatusError(c, http.StatusNotFound, err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"data": updated})
})
group.DELETE("/time-entries/:entryId", func(c *gin.Context) {
if err := s.store.DeleteTimeEntry(c.Param("entryId")); err != nil {
s.writeStatusError(c, http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
})
// Saved views
group.GET("/saved-views", func(c *gin.Context) {
workspaceSlug := c.Query("workspaceSlug")
entityType := c.Query("entityType")
if _, ok := s.requireWorkspaceMember(c, workspaceSlug); !ok {
return
}
c.JSON(http.StatusOK, gin.H{"data": s.store.ListSavedViews(workspaceSlug, entityType)})
})
group.POST("/saved-views", func(c *gin.Context) {
var input store.CreateSavedViewInput
if err := c.ShouldBindJSON(&input); err != nil {
s.writeStatusError(c, http.StatusBadRequest, err.Error())
return
}
if _, ok := s.requireWorkspaceMember(c, input.WorkspaceSlug); !ok {
return
}
view := s.store.CreateSavedView(input)
c.JSON(http.StatusCreated, gin.H{"data": view})
})
group.DELETE("/saved-views/:viewId", func(c *gin.Context) {
if err := s.store.DeleteSavedView(c.Param("viewId")); err != nil {
s.writeStatusError(c, http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
})
}