feat: add IN/OUT event creation from day view in history

- Empty day view now shows Add IN / Add OUT buttons
- Day view with events also shows Add IN / Add OUT below existing events
- Hour picker → minute picker (15-min intervals) → event creation flow
- Back button returns to the day view
This commit is contained in:
2026-06-24 15:02:08 +03:30
parent 49c0e82bac
commit 75da4648aa

View File

@@ -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)
}