refactor: split handlers.go into 5 files, redesign export month picker, pad calendar rows, improve comments
- Split 1571-line handlers.go into: handlers.go (core), clock.go, settings.go, calendar.go, report.go - Redesigned export month picker: year navigation + 12-month grid instead of prev/next month - Fixed calendar last row: pad remaining cells with empty buttons to ensure 7 columns - Added Go doc comments across all files (dateutil.go, totals.go, store.go, main.go, webhook.go)
This commit is contained in:
111
internal/bot/clock.go
Normal file
111
internal/bot/clock.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// clockIn records a clock-in event for today.
|
||||
// Returns the formatted time or an error if already clocked in today.
|
||||
func (h *Handler) clockIn(chatID int64) (string, error) {
|
||||
user, day, err := h.getUserToday(chatID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
last, err := h.DB.GetLastEvent(user.ID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if last != nil && last.EventType == "in" {
|
||||
loc := loadLocation(user.Timezone)
|
||||
now := time.Now().In(loc)
|
||||
lastTime := time.Unix(last.OccurredAt, 0).In(loc)
|
||||
if lastTime.Format("2006-01-02") == now.Format("2006-01-02") {
|
||||
return "", errors.New("already clocked in")
|
||||
}
|
||||
}
|
||||
|
||||
loc := loadLocation(user.Timezone)
|
||||
now := time.Now().In(loc)
|
||||
wt := day.CurrentWorkTypeID
|
||||
if err := h.DB.CreateEvent(user.ID, day.ID, "in", &wt, now.Unix(), ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("clocked in at %s", now.Format("15:04")), nil
|
||||
}
|
||||
|
||||
// clockOut records a clock-out event for today.
|
||||
// Returns the formatted time or an error if not clocked in.
|
||||
func (h *Handler) clockOut(chatID int64) (string, error) {
|
||||
user, day, err := h.getUserToday(chatID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
last, err := h.DB.GetLastEvent(user.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if last == nil {
|
||||
return "", errors.New("no clock-in found — start with /clockin first")
|
||||
}
|
||||
if last.EventType == "out" {
|
||||
return "", errors.New("already clocked out")
|
||||
}
|
||||
loc := loadLocation(user.Timezone)
|
||||
now := time.Now().In(loc)
|
||||
if err := h.DB.CreateEvent(user.ID, day.ID, "out", nil, now.Unix(), ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("clocked out at %s", now.Format("15:04")), nil
|
||||
}
|
||||
|
||||
// dayOff toggles today as a day off.
|
||||
func (h *Handler) dayOff(chatID int64) (string, error) {
|
||||
user, day, err := h.getUserToday(chatID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if day.IsDayOff {
|
||||
if err := h.DB.RemoveDayOff(user.ID, day.Date); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "day off removed", nil
|
||||
}
|
||||
if err := h.DB.SetDayOff(user.ID, day.Date, ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "today marked as day off", nil
|
||||
}
|
||||
|
||||
// report returns today's work summary via buildDailyReport.
|
||||
func (h *Handler) report(chatID int64) (string, error) {
|
||||
user, day, err := h.getUserToday(chatID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
events, err := h.DB.EventsForDayByDayID(day.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h.buildDailyReport(user, day, events), nil
|
||||
}
|
||||
|
||||
// toggleReport enables or disables the automatic daily report.
|
||||
func (h *Handler) toggleReport(chatID int64) (string, error) {
|
||||
user, err := h.getOrCreateUser(chatID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
user.ReportEnabled = !user.ReportEnabled
|
||||
if err := h.DB.UpdateUser(user); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if user.ReportEnabled {
|
||||
return "daily report enabled", nil
|
||||
}
|
||||
return "daily report disabled", nil
|
||||
}
|
||||
Reference in New Issue
Block a user