diff --git a/internal/bot/calendar.go b/internal/bot/calendar.go index 4f4a6ba..fd097d6 100644 --- a/internal/bot/calendar.go +++ b/internal/bot/calendar.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "strconv" "strings" "time" @@ -125,6 +126,21 @@ func (h *Handler) handleHistoryCallback(ctx context.Context, chatID int64, msgID h.editEventTimeMin(ctx, chatID, msgID, user, eid, hh, loc) return } + // Add IN event — show hour picker + if strings.HasPrefix(data, "addin_") { + h.addEventTime(ctx, chatID, msgID, user, data[6:], "in", loc) + return + } + // Add OUT event — show hour picker + if strings.HasPrefix(data, "addout_") { + h.addEventTime(ctx, chatID, msgID, user, data[7:], "out", loc) + return + } + // Add event hour/minute callbacks + if strings.HasPrefix(data, "addtm_") { + h.handleAddEventTime(ctx, chatID, msgID, user, data[6:], loc) + return + } } // sendCalendar sends a calendar view as a new message. @@ -245,6 +261,8 @@ func (h *Handler) sendDayView(ctx context.Context, chatID int64, msgID int, user text += "\nNo events for this day." kb := models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{ + {{Text: "Add IN", CallbackData: "addin_" + date}}, + {{Text: "Add OUT", CallbackData: "addout_" + date}}, {{Text: "Back to Calendar", CallbackData: "history"}}, }, } @@ -279,6 +297,11 @@ func (h *Handler) sendDayView(ctx context.Context, chatID int64, msgID int, user }) } + kbRows = append(kbRows, []models.InlineKeyboardButton{ + {Text: "Add IN", CallbackData: "addin_" + date}, + {Text: "Add OUT", CallbackData: "addout_" + date}, + }) + kbRows = append(kbRows, []models.InlineKeyboardButton{ {Text: "Back to Calendar", CallbackData: "history"}, }) @@ -490,3 +513,99 @@ func (h *Handler) getEventByID(userID, eventID int64) (*db.Event, error) { } return &e, nil } + +// addEventTime shows an hour picker for adding a new event to a day. +func (h *Handler) addEventTime(ctx context.Context, chatID int64, msgID int, user *db.User, date, eventType string, loc *time.Location) { + text := fmt.Sprintf("Select hour for %s event:", eventType) + kbRows := [][]models.InlineKeyboardButton{} + hourRow := []models.InlineKeyboardButton{} + for hh := 0; hh < 24; hh++ { + hourRow = append(hourRow, models.InlineKeyboardButton{ + Text: fmt.Sprintf("%02d", hh), + CallbackData: fmt.Sprintf("addtm_%s_%s_%02d", eventType, date, hh), + }) + if len(hourRow) == 6 || hh == 23 { + kbRows = append(kbRows, hourRow) + hourRow = nil + } + } + kbRows = append(kbRows, []models.InlineKeyboardButton{ + {Text: "Back", CallbackData: "cal_day_" + date}, + }) + kb := models.InlineKeyboardMarkup{InlineKeyboard: kbRows} + h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ + ChatID: chatID, MessageID: msgID, Text: text, ReplyMarkup: &kb, + }) +} + +// addEventTimeMin shows a minute picker (15-min intervals) after hour selection for adding an event. +func (h *Handler) addEventTimeMin(ctx context.Context, chatID int64, msgID int, user *db.User, date, eventType string, hh int, loc *time.Location) { + text := fmt.Sprintf("Select minute for hour %02d:", hh) + kbRows := [][]models.InlineKeyboardButton{} + minRow := []models.InlineKeyboardButton{} + for _, mm := range []int{0, 15, 30, 45} { + minRow = append(minRow, models.InlineKeyboardButton{ + Text: fmt.Sprintf("%02d", mm), + CallbackData: fmt.Sprintf("addtm_%s_%s_%02d_%02d", eventType, date, hh, mm), + }) + } + kbRows = append(kbRows, minRow) + kbRows = append(kbRows, []models.InlineKeyboardButton{ + {Text: "Back", CallbackData: "add" + eventType + "_" + date}, + }) + kb := models.InlineKeyboardMarkup{InlineKeyboard: kbRows} + h.Bot.EditMessageText(ctx, &bot.EditMessageTextParams{ + ChatID: chatID, MessageID: msgID, Text: text, ReplyMarkup: &kb, + }) +} + +// addEventDo creates a new event at the specified time and returns to the day view. +func (h *Handler) addEventDo(ctx context.Context, chatID int64, msgID int, user *db.User, date, eventType string, hh, mm int, loc *time.Location) { + t, err := time.ParseInLocation("2006-01-02 15:04", date+" "+fmt.Sprintf("%02d:%02d", hh, mm), loc) + if err != nil { + return + } + + day, err := h.DB.GetOrCreateDay(user.ID, date) + if err != nil { + return + } + + var wt *int64 + if eventType == "in" { + wt = &day.CurrentWorkTypeID + if *wt == 0 { + wt = nil + } + } + + if err := h.DB.CreateEvent(user.ID, day.ID, eventType, wt, t.Unix(), ""); err != nil { + return + } + + h.sendDayView(ctx, chatID, msgID, user, date, loc, false) +} + +// handleAddEventTime parses the addtm_ callback and dispatches to the minute picker or event creation. +func (h *Handler) handleAddEventTime(ctx context.Context, chatID int64, msgID int, user *db.User, payload string, loc *time.Location) { + parts := strings.SplitN(payload, "_", 4) + if len(parts) < 3 { + return + } + eventType := parts[0] + addDate := parts[1] + hh, err := strconv.Atoi(parts[2]) + if err != nil { + return + } + + if len(parts) == 3 { + h.addEventTimeMin(ctx, chatID, msgID, user, addDate, eventType, hh, loc) + return + } + mm, err := strconv.Atoi(parts[3]) + if err != nil { + return + } + h.addEventDo(ctx, chatID, msgID, user, addDate, eventType, hh, mm, loc) +}