Files
mattermost/server
53aa05d8c6 Interactive Dialog - DateTime manual entry and timezone support (#34932)
* Respect user display preferences for date and time formatting

User Preference Support:
- Read isUseMilitaryTime preference from user settings
- Apply 24-hour format when enabled (14:00 instead of 2:00 PM)
- Apply 12-hour format when disabled (2:00 PM instead of 14:00)
- Pass useTime prop to Timestamp component with correct hourCycle/hour12

Date Formatting Consistency:
- Create formatDateForDisplay() utility in date_utils.ts
- Centralize date formatting logic (month: 'short', day/year: 'numeric')
- Use consistent "Jan 15, 2025" format across all date/datetime fields
- Replace DateTime.fromJSDate().toLocaleString() which varies by browser

Components Updated:
- DateTimeInput: Use isMilitaryTime for dropdown and selected time display
- DateTimeInput: Use formatDateForDisplay for date display
- AppsFormDateField: Use formatDateForDisplay instead of inline Intl code

Tests Added:
- 4 tests for user preference handling (military time, locale)
- 5 tests for formatDateForDisplay utility
- Updated snapshot for Timestamp changes

Benefits:
- Single source of truth for date formatting
- Easy to change format globally by updating one function
- Respects user preferences consistently
- Fixes inconsistency where datetime showed "1/1/2026" vs date showing "Jan 1, 2026"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* Add timezone and manual time entry support for datetime fields

Object Model:
- Create DialogDateTimeConfig with TimeInterval, LocationTimezone, AllowManualTimeEntry
- Add DateTimeConfig field to DialogElement
- Keep legacy MinDate, MaxDate, TimeInterval for fallback

Timezone Support (location_timezone):
- Display datetime in specific IANA timezone (e.g., "Europe/London", "Asia/Tokyo")
- Show timezone indicator: "🌍 Times in GMT"
- Preserve timezone through all operations
- Fix momentToString to clone before converting to UTC (prevents mutation)
- Use moment.tz array syntax for timezone-safe moment creation
- Generate time intervals starting at midnight in display timezone

Manual Time Entry (allow_manual_time_entry):
- Add parseTimeString() function supporting multiple formats:
  - 12-hour: 12a, 12:30p, 3:45pm
  - 24-hour: 14:30, 9:15
- Add TimeInputManual component with text input
- Conditional rendering: manual input OR dropdown
- No rounding for manual entry (exact minutes preserved)
- No auto-advance (validation only, show error for invalid format)
- Respects user's 12h/24h preference for placeholder

Critical Bug Fixes:
- Fix getTimeInIntervals to return Moment[] instead of Date[] (preserves timezone)
- Fix momentToString mutation: use .clone() before .utc()
- Use .clone() when calling .startOf('day') to preserve timezone
- Use moment.tz([...], timezone) array syntax instead of .tz().hour() mutation
- Display selected time using .format() instead of Timestamp component
- Fix null handling: optional fields start empty, show '--:--'
- Manual entry gets exact current time, dropdown gets rounded time

Component Updates:
- DateTimeInput: Add TimeInputManual component, parseTimeString, timezone handling
- AppsFormDateTimeField: Extract config, timezone indicator, pass timezone to child
- Modal components: Handle Moment | null signatures
- CSS: Add manual entry input styles with error states

Features:
- Timezone-aware time generation (dropdown starts at midnight in display TZ)
- Manual entry works with timezones (creates moments in correct TZ)
- Optional fields start empty (null value, no display default)
- Required datetime fields get rounded default from apps_form_component

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* Fix momentToString mutation - clone before converting to UTC

Prevents .utc() from mutating the original moment object.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* Add E2E test for 12h/24h time preference support

Test MM-T2530H verifies that datetime fields respect user's display preference:
- Sets preference to 24-hour format
- Verifies dropdown shows times as 14:00, 15:00, etc.
- Verifies selected time displays in 24-hour format
- Changes preference to 12-hour format
- Verifies dropdown shows times as 2:00 PM, 3:00 PM, etc.

Uses cy.apiSaveClockDisplayModeTo24HourPreference() to set user preference.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* Auto-round time to interval boundaries in DateTimeInput

Automatically rounds displayed time to timePickerInterval to ensure
consistent behavior across all callers.

Problem:
- DND modal and Custom Status modal showed unrounded times (e.g., 13:47)
- Should show rounded times (e.g., 14:00) to match dropdown intervals
- Some callers pre-rounded, others didn't (inconsistent)

Solution:
- Add useEffect in DateTimeInput that auto-rounds on mount
- Only calls handleChange if time needs rounding
- Uses timePickerInterval prop or 30-minute default
- Harmless for callers that already pre-round (no change triggered)

Behavior:
- DND modal: Now shows 14:00 instead of 13:47
- Custom Status: Still works (already pre-rounded, so no-op)
- Post Reminder: Still works (already pre-rounded, so no-op)
- Interactive Dialog: Still works (uses custom intervals)

Added 3 unit tests for auto-rounding behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* lint fix

* Add deferred login cleanup to post_test.go 'not logged in' test

Ensures the test helper is logged back in after the logout test completes, preventing test state issues for subsequent tests.

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* Add unit tests for parseTimeString and timezone handling

parseTimeString tests (9 test cases):
- 12-hour format with AM/PM (12a, 3:30pm, etc.)
- 24-hour format (14:30, 23:59, etc.)
- Time without minutes (defaults to :00)
- Invalid hours, minutes, and formats
- Edge cases (midnight 12:00am, noon 12:00pm)

Timezone handling tests (3 test cases):
- Preserve timezone in getTimeInIntervals
- Generate intervals starting at midnight in timezone
- Timezone conversion pattern verification

Total: 12 new tests added (32 total in file)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* Add E2E tests and webhook support for timezone/manual entry

E2E Tests Added (MM-T2530O through MM-T2530S):
- MM-T2530O: Manual time entry basic functionality
- MM-T2530P: Manual time entry multiple formats (12a, 14:30, 9pm)
- MM-T2530Q: Manual time entry invalid format handling
- MM-T2530R: Timezone support dropdown (London GMT)
- MM-T2530S: Timezone support manual entry (London GMT)

Webhook Server Support:
- Added getTimezoneManualDialog() to webhook_utils.js
- Added 'timezone-manual' case to webhook_serve.js
- Dialog with 3 fields: local manual, London dropdown, London manual

Bug Fixes:
- Skip auto-rounding for allowManualTimeEntry fields (preserve exact minutes)
- Generate dropdown options even when displayTime is null (use currentTime fallback)
- Scope Cypress selectors with .within() to avoid duplicate ID issues

All tests passing (13 total datetime tests).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* Fix ESLint no-multi-spaces in apps.ts

Remove extra spacing before comments to comply with ESLint rules.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* Fix gofmt formatting in integration_action.go

Align Options, MultiSelect, and Refresh field spacing to match Go formatting standards.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* more lint fixes

* css lint fix

* i18n-extract

* lint fixes

* update snapshot

* Fix modal scroll containment for datetime fields

The .modal-overflow class was applying overflow: visible to .modal-body,
which broke scroll containment when datetime fields were present. This
caused the entire form to scroll instead of scrolling within the modal-body
viewport.

Changes:
- Remove .modal-body overflow override from .modal-overflow class to
  preserve scroll containment while still allowing date/time popups to
  display correctly via z-index
- Remove italic styling from timezone indicator for cleaner appearance
- Remove redundant "Time" label from manual time entry input (aria-label
  is sufficient for accessibility)
- Add CSS rule to ensure "(optional)" label text is not bold

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

* fixes for cypress tests

* fix for using timezone crossing dates

* fix dateonly strings parse failures

* regex fix

* linter fix

---------

Co-authored-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-02-16 13:32:31 -07:00
..
…
2026-02-13 17:39:39 +01:00
…