Skip to content

Commit ec74fc5

Browse files
committed
Updated "Edit Trip" dialog - reduced date picker, allowed to manually update dates, etc.
1 parent a51df1b commit ec74fc5

4 files changed

Lines changed: 221 additions & 4 deletions

File tree

frontend/src/style.css

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,71 @@ body {
641641
opacity: 0.4 !important;
642642
}
643643

644+
/* Month/Year Views */
645+
.p-dark .p-datepicker-month-view,
646+
.p-dark .p-datepicker-year-view {
647+
background: var(--gp-surface-dark) !important;
648+
}
649+
650+
.p-dark .p-datepicker-month,
651+
.p-dark .p-datepicker-year {
652+
color: var(--gp-text-primary) !important;
653+
border-radius: var(--gp-radius-small) !important;
654+
transition: all 0.2s ease !important;
655+
}
656+
657+
.p-dark .p-datepicker-month:hover,
658+
.p-dark .p-datepicker-year:hover {
659+
background: var(--gp-surface-light) !important;
660+
color: var(--gp-text-primary) !important;
661+
}
662+
663+
.p-dark .p-datepicker-month-selected,
664+
.p-dark .p-datepicker-year-selected {
665+
background: var(--gp-primary) !important;
666+
color: white !important;
667+
font-weight: 600 !important;
668+
}
669+
670+
.p-dark .p-datepicker-month.p-disabled,
671+
.p-dark .p-datepicker-year.p-disabled {
672+
color: var(--gp-text-muted) !important;
673+
opacity: 0.4 !important;
674+
}
675+
676+
/* DatePicker Footer Buttons (Today/Clear) */
677+
.p-dark .p-datepicker-buttonbar {
678+
background: var(--gp-surface-dark) !important;
679+
border-top: 1px solid var(--gp-border-dark) !important;
680+
gap: var(--gp-spacing-xs) !important;
681+
}
682+
683+
.p-dark .p-datepicker-today-button,
684+
.p-dark .p-datepicker-clear-button {
685+
color: var(--gp-text-primary) !important;
686+
border: 1px solid var(--gp-border-dark) !important;
687+
background: var(--gp-surface-light) !important;
688+
border-radius: var(--gp-radius-small) !important;
689+
}
690+
691+
.p-dark .p-datepicker-today-button .p-button-label,
692+
.p-dark .p-datepicker-clear-button .p-button-label {
693+
color: var(--gp-text-primary) !important;
694+
}
695+
696+
.p-dark .p-datepicker-today-button:hover,
697+
.p-dark .p-datepicker-clear-button:hover {
698+
background: color-mix(in srgb, var(--gp-primary) 20%, var(--gp-surface-light)) !important;
699+
border-color: var(--gp-primary-light) !important;
700+
color: var(--gp-text-primary) !important;
701+
}
702+
703+
.p-dark .p-datepicker-today-button:focus-visible,
704+
.p-dark .p-datepicker-clear-button:focus-visible {
705+
outline: 2px solid var(--gp-primary-light) !important;
706+
outline-offset: 1px !important;
707+
}
708+
644709
.p-dark .p-datepicker-time-picker {
645710
background: var(--gp-surface-dark) !important;
646711
border-color: var(--gp-border-dark) !important;
@@ -1441,3 +1506,37 @@ html body .p-dark .p-tooltip .p-tooltip-arrow::before {
14411506
box-shadow: 0 0 0 4px color-mix(in srgb, var(--gp-primary) 14%, transparent);
14421507
transition: box-shadow 0.2s ease, outline-color 0.2s ease;
14431508
}
1509+
1510+
.trip-date-range-panel {
1511+
--p-datepicker-date-width: 2.2rem;
1512+
--p-datepicker-date-height: 2.2rem;
1513+
width: min(92vw, 22rem) !important;
1514+
min-width: min(92vw, 22rem) !important;
1515+
max-width: min(92vw, 22rem) !important;
1516+
}
1517+
1518+
.trip-date-range-panel .p-datepicker-calendar-container {
1519+
width: 100% !important;
1520+
}
1521+
1522+
.trip-date-range-panel .p-datepicker-header {
1523+
padding: var(--gp-spacing-sm) !important;
1524+
}
1525+
1526+
.trip-date-range-panel .p-datepicker-day,
1527+
.trip-date-range-panel .p-datepicker-weekday {
1528+
font-size: 0.95rem !important;
1529+
}
1530+
1531+
.trip-date-range-panel .p-datepicker-month,
1532+
.trip-date-range-panel .p-datepicker-year {
1533+
padding: 0.35rem 0.45rem !important;
1534+
}
1535+
1536+
@media (max-width: 640px) {
1537+
.trip-date-range-panel {
1538+
width: min(92vw, 19rem) !important;
1539+
min-width: min(92vw, 19rem) !important;
1540+
max-width: min(92vw, 19rem) !important;
1541+
}
1542+
}

frontend/src/views/app/TripsManagementPage.vue

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@
177177
v-model:visible="showTripDialog"
178178
modal
179179
:header="isEditMode ? 'Edit Trip Plan' : 'Create Trip Plan'"
180-
class="gp-dialog-md"
180+
class="gp-dialog-lg"
181181
@hide="resetTripForm"
182182
>
183183
<div class="grid">
@@ -200,12 +200,21 @@
200200
v-model="tripDateRange"
201201
selectionMode="range"
202202
class="w-full"
203-
:manualInput="false"
203+
:manualInput="true"
204+
iconDisplay="input"
204205
:dateFormat="timezone.getPrimeVueDatePickerFormat()"
206+
:placeholder="tripDateRangePlaceholder"
207+
append-to="body"
208+
panel-class="trip-date-range-panel"
205209
:class="{ 'p-invalid': formErrors.dateRange }"
206210
/>
207-
<small v-if="formErrors.dateRange" class="p-error">{{ formErrors.dateRange }}</small>
208-
<small v-else class="field-hint">{{ tripDateRangeHint }}</small>
211+
<div class="date-range-help">
212+
<small v-if="formErrors.dateRange" class="p-error">{{ formErrors.dateRange }}</small>
213+
<template v-else>
214+
<small class="field-hint">{{ tripDateRangeHint }}</small>
215+
<small class="field-hint field-hint-strong">Tip: click month or year in the calendar header to jump faster.</small>
216+
</template>
217+
</div>
209218
</div>
210219

211220
<div class="col-12">
@@ -430,6 +439,15 @@ const tripDateRangeHint = computed(() => {
430439
if (editingTripWasUnplanned.value) return 'Add both dates to schedule this trip.'
431440
return 'Date range is required for scheduled trips.'
432441
})
442+
const tripDateRangePlaceholder = computed(() => {
443+
const dateTokenByFormat = {
444+
MDY: 'MM/DD/YYYY',
445+
DMY: 'DD/MM/YYYY',
446+
YMD: 'YYYY-MM-DD'
447+
}
448+
const token = dateTokenByFormat[timezone.getDateFormat()] || 'MM/DD/YYYY'
449+
return `${token} - ${token}`
450+
})
433451
434452
const filteredTrips = computed(() => {
435453
let items = Array.isArray(trips.value) ? [...trips.value] : []
@@ -973,6 +991,24 @@ onMounted(async () => {
973991
color: var(--gp-text-secondary);
974992
}
975993
994+
.field-hint-strong {
995+
color: var(--gp-text-primary);
996+
}
997+
998+
.date-range-help {
999+
margin-top: var(--gp-spacing-xs);
1000+
margin-bottom: var(--gp-spacing-sm);
1001+
display: flex;
1002+
flex-direction: column;
1003+
gap: 0.2rem;
1004+
}
1005+
1006+
.date-range-help .field-hint,
1007+
.date-range-help .p-error {
1008+
margin-top: 0;
1009+
line-height: 1.35;
1010+
}
1011+
9761012
.color-row {
9771013
display: flex;
9781014
align-items: center;

tests/e2e/trips-management.spec.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,33 @@ test.describe('Trip Plans Management Page', () => {
2323
expect(linkedTag.source).toBe('trip');
2424
});
2525

26+
test('allows editable date input and fast year jump in create dialog', async ({ page, isolatedUsers, dbManager }) => {
27+
const { tripsPage } = await TestSetupHelper.loginAndNavigateToTripsPage(
28+
page,
29+
dbManager,
30+
createManagedUser(isolatedUsers)
31+
);
32+
33+
const tripName = `Year Jump ${Date.now()}`;
34+
const targetYear = new Date().getFullYear() - 2;
35+
36+
await page.click(tripsPage.selectors.createButton);
37+
await page.waitForSelector('.p-dialog:visible:has-text("Create Trip Plan")', { timeout: 5000 });
38+
39+
await page.fill(tripsPage.selectors.tripNameInput, tripName);
40+
await tripsPage.expectDateRangeInputEditable();
41+
await tripsPage.selectDateRangeByYearAndDay({
42+
targetYear,
43+
monthIndex: 5,
44+
startDay: 10,
45+
endDay: 15
46+
});
47+
48+
await page.locator('.p-dialog:visible button:has-text("Create Trip")').click();
49+
await page.waitForSelector('.p-dialog:has-text("Create Trip Plan")', { state: 'hidden', timeout: 10000 });
50+
await expect(tripsPage.rowByTripName(tripName)).toBeVisible({ timeout: 10000 });
51+
});
52+
2653
test('edits trip and synchronizes linked label fields', async ({ page, isolatedUsers, dbManager }) => {
2754
const { tripsPage, user } = await TestSetupHelper.loginAndNavigateToTripsPage(
2855
page,

tests/pages/TripsManagementPage.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,61 @@ export class TripsManagementPage {
6666
await this.closeDatePickerPanelIfOpen();
6767
}
6868

69+
async expectDateRangeInputEditable() {
70+
const input = this.page.locator(this.selectors.dateRangeInput).first();
71+
const isReadOnly = await input.evaluate((element) => element.readOnly);
72+
expect(isReadOnly).toBe(false);
73+
}
74+
75+
async selectDateRangeByYearAndDay({ targetYear, monthIndex = 0, startDay = 10, endDay = 15 }) {
76+
await this.page.locator(this.selectors.dateRangeInput).first().click({ force: true });
77+
const pickerPanel = this.page.locator('.p-datepicker-panel:visible').first();
78+
await pickerPanel.waitFor({ state: 'visible', timeout: 5000 });
79+
80+
const yearButton = pickerPanel.locator('.p-datepicker-select-year').first();
81+
await yearButton.click();
82+
83+
while (true) {
84+
const yearCell = pickerPanel
85+
.locator('.p-datepicker-year-view .p-datepicker-year', { hasText: String(targetYear) })
86+
.first();
87+
if (await yearCell.isVisible().catch(() => false)) {
88+
await yearCell.click();
89+
break;
90+
}
91+
92+
const decadeLabel = (await pickerPanel.locator('.p-datepicker-decade').first().innerText()).trim();
93+
const match = decadeLabel.match(/(\d{4})\s*-\s*(\d{4})/);
94+
if (!match) throw new Error(`Unexpected decade label in DatePicker: ${decadeLabel}`);
95+
96+
const start = Number(match[1]);
97+
const end = Number(match[2]);
98+
if (targetYear < start) {
99+
await pickerPanel.locator('.p-datepicker-prev-button').first().click();
100+
} else if (targetYear > end) {
101+
await pickerPanel.locator('.p-datepicker-next-button').first().click();
102+
} else {
103+
throw new Error(`Year ${targetYear} should be visible but was not found.`);
104+
}
105+
}
106+
107+
const monthCell = pickerPanel
108+
.locator('.p-datepicker-month-view .p-datepicker-month:not(.p-disabled)')
109+
.nth(monthIndex);
110+
await monthCell.click();
111+
112+
const startButton = pickerPanel
113+
.locator('.p-datepicker-day-view td .p-datepicker-day[aria-disabled="false"]', { hasText: String(startDay) })
114+
.first();
115+
const endButton = pickerPanel
116+
.locator('.p-datepicker-day-view td .p-datepicker-day[aria-disabled="false"]', { hasText: String(endDay) })
117+
.first();
118+
119+
await startButton.click();
120+
await endButton.click();
121+
await this.closeDatePickerPanelIfOpen();
122+
}
123+
69124
async closeDatePickerPanelIfOpen() {
70125
const panel = this.page.locator('.p-datepicker-panel:visible').first();
71126
const isVisible = await panel.isVisible().catch(() => false);

0 commit comments

Comments
 (0)