81 Commits

Author SHA1 Message Date
bddb920c82 fix: add missing confirmations to command handlers for consistency
All checks were successful
CI / build (push) Successful in 50s
All /setrate, /setcurrency, /settimezone, /setbreak, /setusername,
/setreporttime now send a confirmation message, matching the UI flow.
Also fixes rate confirmation to show the actual currency symbol.
2026-06-29 10:09:09 +03:30
2c1c149b13 fix: show actual currency symbol in set rate confirmation
processRateInput was passing empty string to CurrencySymbol, causing
it to default to '$' instead of the user's configured currency.
2026-06-29 10:07:12 +03:30
6f7f5878bf fix: correct daily work/break calculations and league back button
All checks were successful
CI / build (push) Successful in 38s
- ComputeDailyTotals: only assume working from dayStart when first event
  is 'out' (cross-midnight session); otherwise start idle. Fixes inflated
  work/break counts in reports, status, export, burnout, salary, and RPG.
- leagueToggleCallback: add settings back keyboard when RPG is disabled.
- webhook: add graceful HTTP server shutdown on context cancellation.
2026-06-27 23:15:12 +03:30
7dbd9fda12 feat: add /setcalendar command, store tests, and improve error handling
All checks were successful
CI / build (push) Successful in 48s
- Add /setcalendar command for calendar type parity (Settings UI)
- Add 17 store tests covering all repository methods
- Add handler tests for formatDuration, fmtHHMM, fmtDDHHMM
- Add domain tests for BurnoutLevel.String, NavMonth, FormatSalary
- Fix silent error discarding in save* helpers (log write failures)
- Fix computeAndAwardXP error handling instead of discarding
- Wrap critical store.go SQL errors with fmt.Errorf context
- Update README commands table with /setcalendar
2026-06-25 15:56:00 +03:30
d9e13cb51d chore: gitignore and untrack opencode.json
All checks were successful
CI / build (push) Successful in 33s
2026-06-25 15:10:04 +03:30
59c35305c7 docs: add Gitea Actions CI badge to README
All checks were successful
CI / build (push) Successful in 33s
2026-06-25 12:33:58 +03:30
5670b0455d feat: add delete account button in settings, implement all remaining achievements
All checks were successful
CI / build (push) Successful in 37s
- Add delete all data button with red danger style and confirmation prompt
- Add DeleteUserData repo method that wipes user from all tables in a transaction
- Add helper methods deleteAccountPrompt and deleteAccountConfirm
- Implement consistency_master, perfect_week, remote_veteran, onsite_master achievements
- Add GetDailyXPInRange and SumWorkSecondsByWorkType repo methods
- Update Dockerfile to alpine3.23, docker-compose to use env vars for proxy/paths
2026-06-25 12:27:03 +03:30
0926323d18 fix: removed an stray space character in text of main menu league
All checks were successful
CI / build (push) Successful in 1m6s
2026-06-25 09:58:54 +03:30
a96419f324 feat: add /settings command and document /history for UI-command parity
- Add /settings command handler with inline settings keyboard
- Add /history to README commands table
- Update /help command list
- Remove unused Leagu.go old reference
2026-06-25 09:55:53 +03:30
d15ed46066 fix: resolve go vet error and dayStart logic in ComputeDailyTotals
- Fix 'string(d)' int-to-rune conversion in rpg_test.go by using fmt.Sprintf
- Fix ComputeDailyTotals dayStart logic: initial state should be StateWorking
  when dayStart > 0, correctly counting work from dayStart to first out event
- Update README project structure to reflect new domain/handler/repo layout
- Implement missing achievements: rpg_pioneer (on RPG enable) and
  salary_setter (on first rate set)
- Add tryUnlockAchievement helper to Handler
2026-06-25 09:54:22 +03:30
3835b002a7 fix: corrected the layout of buttons in settings and text in main menu 2026-06-25 09:30:41 +03:30
3cd692be0f fix: separate RPG and League into their own rows in settings menu 2026-06-25 09:19:32 +03:30
b822068f0a feat: enforce RPG dependency for League — cannot enable League without RPG
- handleSelectCallback: disabling RPG also disables League; enabling
  League when RPG is off shows toast error via answerCbWithText
- handleLeagueToggleCmd (/leaguetoggle): rejects with message if RPG off
- leagueToggleCallback (inline): rejects with message if RPG off
- buildSettingsKeyboard: League status shows as 'off' when RPG is disabled
  (even if the DB still has LeagueOptIn=true, since RPG disable forces it false)
2026-06-25 09:19:03 +03:30
80de661f5b refactor: eliminate store bypass and fix GetLastEvent style
- Add GetDistinctEventDates, UpdateEventWorkType, UpdateEventTime,
  DeleteEvent, GetEventByID store methods to calendar.go
- Replace all 5 raw h.DB.DB().Exec/Query calls with proper store methods
- Remove unused database/sql import from calendar.go
- Rewrite GetLastEvent to use QueryRow instead of Query+manual iteration
  (fixing row-vs-rows naming issue; removing rows.Close boilerplate)
- Add errors import to store.go for errors.Is
2026-06-25 02:29:30 +03:30
af23f80fbb fix: address audit findings — nil pointers, timezone bug, dead code, tests, README
- Fix nil pointer dereference in clock.go (guard GetOrCreateSettings and
  GetOrCreateRPGStats errors before accessing fields)
- Fix timezone bug in handleSetBreakMsg (was using system time.Now()
  instead of user's configured timezone)
- Remove dead code: sendExportMonthPicker (unused wrapper)
- Simplify xpForWorkSeconds (xpPerSecond=1, inline to just return sec)
- Extract computeStreakFromDates as pure testable function from
  recalcAggregates; add 6 tests covering empty, current, gaps,
  no-today, long streak, longest-not-current edge cases
- Update README with full feature documentation: RPG, League, Salary,
  Burnout, Work Types, Inline History Editing, all missing commands
2026-06-25 02:25:13 +03:30
5cd811e057 refactor: restore stored aggregates with incremental update on edit
- Restore TotalWorkSeconds, CurrentStreak, LongestStreak writes in
  computeAndAwardXP and updateStreak (incremental on clock out)
- Add recalcDayWorkSeconds and recalcAggregates for event edit path
  (recompute day + total + streak from scratch when events change)
- Call recalcAggregates after editEventTimeSet, deleteEventConfirm,
  addEventDo in calendar.go
- Remove runtime computeStreaks and totalWorkSeconds (N+1 scan)
  from RPG display, burnout, and clockOut
- Add SetDailyWorkSeconds, SumDailyWorkSeconds store methods
- Clean up unused dead functions and dead code in rpg.go
2026-06-25 02:19:40 +03:30
4fcb694418 fix: compute work hours from events at runtime; add Achievements button to RPG view 2026-06-25 02:08:33 +03:30
85587d563d fix: computeTrend division by zero, add tests for trend and currency parsing, add username to settings 2026-06-25 01:58:27 +03:30
88ccc6aa68 feat: show Anonymous in league when name empty, add username to settings, show salary hint in summary 2026-06-25 01:54:37 +03:30
605c41ab88 fix: currency validation, league mobile layout, settings 2-col, burnout ratio, add setusername
- Require both symbol+code in /setcurrency; add preset picker ($ USD,
  T Toman, IRR Rial, EUR, GBP) in settings inline menu
- League: compact 2-line per entry for mobile; sort buttons in 2x2 grid
- Settings: 2-column layout with grouped buttons (timezone+calendar,
  report+report time, break+accent, rpg+league, currency+rate)
- Burnout: cap break ratio at 100% to prevent nonsense values
- Add /setusername command (uses existing SanitizeDisplayName)
- Update /help with new/changed commands
2026-06-25 01:46:07 +03:30
344c615666 refactor: break up large functions, add RPG/league/salary/burnout, fix bugs, add tests
- Refactor GenerateMonthlyReport (198→70), HandleCallback (128→85),
  handleHistoryCallback (131→38), handleExportCallback (100→25),
  checkAchievements (86→25) into extracted helper functions
- Add RPG system (XP, levels, streak, achievements, burnout)
- Add WorkTime League leaderboard
- Add salary estimation with configurable currency/rate
- Add input sanitization (SanitizeNote, SanitizeDisplayName)
- Add settings select-style pickers for toggles (report, RPG, league)
- Add break threshold inline picker UI
- Add event notes with pending state and /note command
- Add multi-calendar support (Jalali, Hijri)
- Add Excel export with theme picker
- Fix: getTodayBreakThreshold uses user's timezone (was UTC)
- Fix: acknowledgeCallback passes real callback ID
- Fix: currency symbol safety with currencySymbol() helper
- Fix: count work hours in summary on day-off days
- Fix: unsilence all error returns from SQL Exec/Bot API/migrations
- Remove stale db/schema.sql and unreferenced computeWeekTotals
- Extract constants: DateLayout, TimeLayout, secondsPerHour, etc.
- Add 12 new tests (XP, salary, sanitize, currency, dateutil)
- Remove duplicate package doc comment in totals.go
2026-06-25 01:02:16 +03:30
0d942c51db refactor: simplify break threshold picker values and labels 2026-06-24 21:06:23 +03:30
5f12e3838b feat: add break threshold picker UI to settings with preset values
Replaces the text-only instructions with a grid of common break thresholds (0/5/10/15/30 min, 1/2/4/8h) matching the accent picker pattern.
2026-06-24 20:59:49 +03:30
e399c3ee88 feat: add break threshold to settings UI with inline button and callback 2026-06-24 20:58:18 +03:30
c9dd076235 fix: count work hours in summary even on day-off days
buildMonthlySummary now includes hours for days marked as day-off, matching buildStatusText behavior. Shows "Xh worked (Day Off)" when a day-off day has events.
2026-06-24 20:53:15 +03:30
e539350030 refactor: simplify calendar nav buttons by removing redundant year/step numbers 2026-06-24 20:49:07 +03:30
834c318271 feat: add /summary, /setbreak, weekly totals, and fix command dispatch for arg-bearing commands
- Add /summary [YYYY-MM] command for monthly work/break/day-off totals

- Add weekly totals row to main menu status

- Add /setbreak <minutes> for configurable daily break threshold

- Fix HandleMessage routing: extract command name instead of exact match (fixes /export YYYY-MM, /edit, /note, /summary, /setbreak)

- Update README with full command table and configurable break docs
2026-06-24 20:39:51 +03:30
323c1bc010 test: add dateutil and totals tests for calendar conversions and ComputeDailyTotals 2026-06-24 20:07:38 +03:30
e7e4dc7bfe fix: unsilence all error returns from SQL Exec, Bot API calls, and migrations 2026-06-24 20:03:24 +03:30
4165839477 feat: add event notes with pending state, 5-min TTL, cancel, and /note command 2026-06-24 19:49:32 +03:30
68c8518522 add year navigation: year row at top of calendar, clickable year in export, shared buildYearPicker with 12-year grid 2026-06-24 19:47:03 +03:30
8d919decb0 export: tint day-off rows with theme totalFill background 2026-06-24 19:23:16 +03:30
4edff71bfd replace manual SQL migration with goose (v3); embed migrations in binary; remove schema.sql / SCHEMA_PATH 2026-06-24 19:20:48 +03:30
c959cb7a87 export: include all days, show Day Off in type column; menu: don't hide status on day off 2026-06-24 19:06:54 +03:30
af7a438174 feat: extended the build minute keyboard to have each 10 minutes from 0 to 50 2026-06-24 15:06:08 +03:30
353056349f DRY hour/minute picker: extract shared buildHourPickerKeyboard/buildMinutePickerKeyboard 2026-06-24 15:04:22 +03:30
75da4648aa 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
2026-06-24 15:02:08 +03:30
49c0e82bac refactor: migrate from go-telegram-bot-api/v5 to go-telegram/bot
- Replaced tgbotapi with go-telegram/bot (zero-dependency, context-aware, Bot API 10.0)
- All handler methods now accept context.Context; threading ctx through all sends/edits
- Changed from function-based (NewMessage/NewEditMessageText/NewInlineKeyboardMarkup) to struct-param API (SendMessageParams/EditMessageTextParams/InlineKeyboardMarkup)
- Added colored buttons: DEL buttons in calendar use Style: 'danger' (red)
- Both polling and webhook modes preserved with new library patterns
- Context-based shutdown (signal.NotifyContext) replaces stop channel
2026-06-24 14:56:50 +03:30
fb2d0ef7d1 chore: changed the rate limit down to 1 second instead of 2 2026-06-24 11:39:47 +03:30
a8b849f8fd 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)
2026-06-24 11:33:17 +03:30
d8599657f4 refactor: export full Gregorian range, add timezone fallback, cleanup rate limit, configurable schema path
- GenerateMonthlyReport now takes explicit Gregorian start/end range,
  computed via monthGregorianRange, so Jalali/Hijri months spanning
  two Gregorian months are fully covered in exports.
- Added loadLocation helper (UTC fallback on error) and replaced all
  11 ignored-error call sites in handlers + export.
- Added periodicCleanup goroutine in NewHandler to prevent unbounded
  growth of the rateLimit map.
- schemaPath is now read from SCHEMA_PATH env var (init), defaulting
  to /app/db/schema.sql.
- Migrated sendDayView to use sendOrEdit helper, removing duplicated
  send/edit branching.
- Removed dead code: userYearMonthToGregorian (unused), the old
  startOffset function (replaced).
- Updated README with calendar docs, new env vars, full project tree.
- Updated .env.example and docker-compose.yml with SCHEMA_PATH.
- Simplified Makefile: added fmt, tidy, check, run-dev targets;
  removed broken Make-glob prerequisite pattern.
2026-06-24 11:21:54 +03:30
f89f5607aa fix: calendar day-of-week offset off by one, nav double-jump, race conditions
- Fix Jalali/Gregorian/Hijri day-of-week startOffset formulas
  (dow returns 0=Sat, not 0=Sun as the old comment claimed)
- Fix calendar and export month picker navigation jumping 2 months
  (navMonth buttons already have pre-computed values; handlers
  were decrementing/incrementing them again)
- Fix GetOrCreateUser/GetOrCreateDay race condition with INSERT OR IGNORE
- Add month/day bounds validation in handleEditMsg to prevent panic
2026-06-24 11:07:53 +03:30
577aee91fb refactor: remove dead code, DRY calendar navigation and helpers
- Fix Jalali calendar weekday offset in buildCalendarMonth
- Remove unused gregorianDateKey function and PairCount field
- Replace inline export month title with formatMonthTitle
- Extract navMonth helper for prev/next month navigation
- Extract sendOrEdit helper for send-vs-edit message pattern
- Extract monthGregorianRange helper to simplify editCalendar 3-way switch
- Replace export displayDate conversion with formatDateForCalendar
2026-06-24 10:54:06 +03:30
67eae5ffa3 fix export timezone and calendar month title
- Export now uses time.Now().In(loc) for year/month so users in
  positive UTC offsets get the correct month
- Export month title for Jalali/Hijri now correctly converts the
  first day of the Gregorian month to the target calendar instead
  of using the Gregorian month index to look up target month names
2026-06-24 10:32:17 +03:30
8fe43bff4d add settimezone command and timezone display in settings
- New /settimezone <IANA name> command to change user timezone
- Shows current timezone in settings menu
- timezone callback shows current value and instructions
- Updated /help with the new command
2026-06-24 10:17:09 +03:30
069bbb614e convert calendar day view header to user's calendar type 2026-06-24 10:14:09 +03:30
cafa139198 add /help command 2026-06-24 10:11:18 +03:30
69c5e20d8e display date in user's selected calendar on main menu and reports
buildStatusText and buildDailyReport now use formatDateForCalendar
to convert the stored Gregorian date to the user's calendar type
(jalali/hijri/gregorian).
2026-06-24 10:06:39 +03:30
07306cb0f0 remove reply keyboard; keep Clock In/Out as inline buttons only
- Removed smartKeyboard, defaultKeyboard, updateReplyKeyboard
- Removed statusMsg/keyboardMsg tracking from Handler
- sendText now sends plain messages (no reply keyboard)
- handleActionMsg sends result with mainKeyboard() inline keyboard
- handleActionCallback edits message with backKeyboard (unchanged)
- handleStart sends welcome + menu with mainKeyboard
- SendDailyReport sends plain text report
- Removed text-trigger 'Clock In'/'Clock Out' message handlers
  (only /clockin and /clockout commands remain)
2026-06-24 10:00:27 +03:30
9c83ed98c6 fix: keep keyboard update message alive instead of send-delete pattern
Previous approach (send \u200C then immediately delete it) lost the
reply keyboard because Telegram may not persist the keyboard from a
deleted message. Now we delete the OLD keyboard message and keep the
new one, ensuring the reply keyboard is always present and correct.
2026-06-24 09:52:28 +03:30