feat: add manual interval creation and inline entry editing
- Service: CreateInterval() validates same-day, end>start, day not closed
- Service: Update() now rejects edits on closed-day entries (ErrDayAlreadyClosed)
- Handler: POST /api/entries creates a completed interval with explicit times
- API client: entries.createInterval(startMs, endMs, note)
- Utils: parseTimeInput / toTimeInput helpers for HH:MM <-> unix ms
- Today page: '+ Add interval' form (time pickers + optional note)
- Today page: pencil button on each entry opens inline edit row (start/end/note)
- Tests: TestCreateInterval, TestCreateIntervalEndBeforeStart,
TestCreateIntervalCrossesMidnight, TestUpdateRejectsClosedDay
This commit is contained in:
@@ -19,12 +19,47 @@ func NewEntryHandler(svc *service.EntryService) *EntryHandler {
|
||||
|
||||
func (h *EntryHandler) Routes(r chi.Router) {
|
||||
r.Post("/entries/start", h.Start)
|
||||
r.Post("/entries", h.CreateInterval)
|
||||
r.Post("/entries/{id}/stop", h.StopByID)
|
||||
r.Get("/entries", h.List)
|
||||
r.Put("/entries/{id}", h.Update)
|
||||
r.Delete("/entries/{id}", h.Delete)
|
||||
}
|
||||
|
||||
// CreateInterval POST /api/entries — create a completed interval with explicit times
|
||||
func (h *EntryHandler) CreateInterval(w http.ResponseWriter, r *http.Request) {
|
||||
var body struct {
|
||||
StartTime int64 `json:"start_time"`
|
||||
EndTime int64 `json:"end_time"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
if err := decodeJSON(r, &body); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid JSON")
|
||||
return
|
||||
}
|
||||
if body.StartTime == 0 || body.EndTime == 0 {
|
||||
writeError(w, http.StatusBadRequest, "start_time and end_time are required")
|
||||
return
|
||||
}
|
||||
entry, err := h.svc.CreateInterval(r.Context(), service.CreateIntervalInput{
|
||||
StartTime: body.StartTime,
|
||||
EndTime: body.EndTime,
|
||||
Note: body.Note,
|
||||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, service.ErrCrossesMidnight):
|
||||
writeError(w, http.StatusUnprocessableEntity, err.Error())
|
||||
case errors.Is(err, service.ErrDayAlreadyClosed):
|
||||
writeError(w, http.StatusConflict, err.Error())
|
||||
default:
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusCreated, entry)
|
||||
}
|
||||
|
||||
// Start POST /api/entries/start
|
||||
func (h *EntryHandler) Start(w http.ResponseWriter, r *http.Request) {
|
||||
var body struct {
|
||||
|
||||
Reference in New Issue
Block a user