TQPro Admin Interface -- UX Improvement Implementation Plan¶
Overview¶
This implementation plan addresses the 137 UX issues identified in the UX Improvement Recommendations report. The plan is organized into 4 phases, progressing from quick critical fixes to strategic platform-level improvements.
Total estimated effort: 12-16 weeks Quick wins (Phase 1): ~4 hours, fixing 6 critical bugs immediately
Phase 1: Critical Bug Fixes (Quick Wins)¶
Duration: ~4 hours Resolves: 6 critical bugs, 5 high-priority issues, 4 polish items Prerequisite: None
These are single-file fixes with minimal risk and immediate impact.
1.1 Hotel Management -- Date Cap Fix¶
File: tqweb-adm/hotelmgmt.html
Change: Replace max="2025" with max="2030" (or remove the cap) on all date input fields in the calendar dialog.
Impact: Unblocks entire Hotel Management module for 2026+ dates.
1.2 Offer Management -- Modal Save Button Fix¶
File: tqweb-adm/offerman.html
Lines: 223, 262, 367, 486, 591, 634
Change: Remove data-bs-dismiss="modal" attribute from all save buttons. Close modals programmatically in JavaScript only after successful validation and API save.
Impact: Stops form data loss across all Offer Management dialogs.
1.3 Hotel Management -- Async/Await Fix¶
File: tqweb-adm/js/modules/hotelmgmt.js
Change: In saveQuickRates(), replace forEach loop with for...of loop to properly await async operations.
Impact: Prevents silent save failures when saving multiple hotel rates.
1.4 Trip Maker -- Generate PDF Button¶
File: tqweb-adm/tripmaker-workspace.html
Change: Add a "Generate PDF" button element that calls the existing generatePDF() function.
Impact: Unlocks the quotation workflow -- the Trip Maker's primary output.
1.5 Trip Maker -- Parameter Swap Fix¶
File: tqweb-adm/js/modules/tripmaker-workspace.js
Change: Fix updateComponentMargin() call to pass componentId and componentType in the correct parameter order.
Impact: Fixes margin update data corruption.
1.6 Trip Maker -- Auto-Save Fix¶
File: tqweb-adm/js/modules/tripmaker-workspace.js
Change: Either implement a real API call in the auto-save timer, or remove the deceptive "Saving..." / "Saved" indicator text entirely.
Impact: Eliminates false sense of data safety.
1.7 Login Page -- Spinner Fix¶
File: tqweb-adm/adm/login.html
Change: Fix onclick="toggleLoader" to onclick="toggleLoader()" (add parentheses for function invocation).
Impact: Fixes broken login loading spinner.
1.8 Navigation -- Dead Link Fixes¶
File: tqweb-adm/header_bootstrap.html
Changes:
- Fix current-offers.html link to point to offerman.html
- Fix login.html path to adm/login.html
Impact: Removes broken navigation links.
1.9 Cruise Management -- Bulk Cabin Fix¶
File: tqweb-adm/js/modules/cruisemgmt.js
Change: Modify bulk cabin assignment to send per-cabin maxPax values instead of only the first cabin's value.
Impact: Prevents cabin capacity data corruption.
1.10 Visa Management -- Badge Color Fix¶
File: tqweb-adm/visamgmt.html (CSS section)
Change: Differentiate Cancelled (grey/bg-secondary) from Rejected (red/bg-danger) badge colors.
Impact: Removes visual ambiguity between two semantically different terminal states.
1.11 Trip Maker -- Replace window.prompt()¶
File: tqweb-adm/js/modules/tripmaker-workspace.js
Change: Replace window.prompt() call in createNewItinerary() with a Bootstrap modal dialog.
Impact: Eliminates jarring native dialog.
1.12 Trip Maker -- Placeholder Cleanup¶
File: tqweb-adm/tripmaker-workspace.html
Change: Remove developer-facing text "Activity calendar will be rendered here".
Impact: Removes internal text from user-facing UI.
1.13 Outbound Groups -- Lookup Hint¶
Files: tqweb-adm/outbound-groups-*.html
Change: Add placeholder text "Type at least 3 characters" to partner lookup input fields.
Impact: Reveals hidden interaction requirement.
1.14 Required Field Asterisks¶
Files: All module HTML files with forms
Change: Add asterisks (*) to all mandatory field labels. Add * Required legend text.
Impact: Instantly improves form clarity across the entire application.
Phase 2: Shared Component Library¶
Duration: 2-3 weeks Resolves: Cross-module patterns 1-5 (validation, loading, unsaved changes, native dialogs, pagination) Prerequisite: Phase 1 complete
2.1 Create js/modules/tqpro-components.js¶
New shared module providing reusable UI components:
2.1.1 Unified Validation (TQForm)¶
TQForm.validate(formElement, rules) -> { valid: boolean, errors: [] }
TQForm.markRequired(formElement) -> adds asterisks to required fields
TQForm.clearErrors(formElement) -> removes all error indicators
Implementation steps:
1. In pageutil.js, replace all references to is-invalid-input with Bootstrap 5's is-invalid
2. Create validateForm() function that checks rules, applies is-invalid class, and inserts .invalid-feedback divs
3. Add CSS class .required-field::after { content: " *"; color: var(--bs-danger); } to tqadmin.css
4. Apply to all forms across all modules
2.1.2 Notification System (TQNotify)¶
TQNotify.success(message)
TQNotify.warning(message)
TQNotify.error(message)
TQNotify.confirm(title, message, onConfirm, options)
Implementation steps:
1. Create Bootstrap 5 toast container in a shared template
2. Implement success/warning/error using Bootstrap toasts
3. Implement confirm as a reusable Bootstrap modal (replacing all confirm() calls)
4. Replace all alert() calls across the application
5. Replace all $.notify() calls (Hotel, Cruise, Offer Management)
6. Replace window.prompt() in Trip Maker
7. Remove vendor/notify.js dependency
2.1.3 Loading State Management (TQLoading)¶
TQLoading.start(buttonElement) -> disables button, shows spinner
TQLoading.stop(buttonElement) -> re-enables button, removes spinner
TQLoading.showOverlay() -> full-page loading overlay
TQLoading.hideOverlay() -> removes overlay
Implementation steps:
1. Standardize on showWaitDialog()/hideWaitDialog() for full-page operations
2. Create button-level spinner using Bootstrap's spinner-border-sm
3. Add debounce/disable to prevent double-submit on all save buttons
4. Optionally wire into tlinq() function for automatic loading state
2.1.4 Unsaved Changes Detection (TQFormGuard)¶
TQFormGuard.watch(formElement) -> start tracking dirty state
TQFormGuard.isDirty() -> check if form has changes
TQFormGuard.reset() -> mark form as clean
Implementation steps:
1. Create input change listeners for all form fields
2. Intercept beforeunload event
3. Intercept sidebar/header navigation clicks
4. Show Bootstrap confirmation modal on dirty state
5. Apply to: Hotel Management, Cruise Management, Outbound Groups, Trip Maker, Visa Management, Offer Management
2.1.5 Pagination Component (TQPagination)¶
Implementation steps: 1. Create Bootstrap 5 pagination renderer 2. Remove cosmetic pagination HTML from Visa Management and Trip Maker 3. Wire up with actual data paging logic 4. Apply to all list views exceeding ~20 items
2.2 CSS Consolidation¶
File: tqweb-adm/css/tqadmin.css
- Move all
:rootCSS variables fromindex.htmlandvisamgmt.htmlinline styles intotqadmin.css - Remove duplicate inline
<style>blocks - Ensure all admin pages load
tqadmin.css - Define standard button hierarchy:
- Primary action:
btn-primary - Secondary action:
btn-outline-secondary - Destructive action:
btn-danger - Define standard status badge palette:
- Draft/Pending:
bg-info(blue) - In Progress/Active:
bg-primary - Awaiting/Quoted:
bg-warning text-dark - Confirmed/Approved:
bg-success - Rejected:
bg-danger - Cancelled:
bg-secondary(grey, distinct from Rejected) - Completed:
bg-dark - Fix
p { text-align: center; }intqapp.css-- scope or remove
2.3 Integration per Module¶
Apply tqpro-components.js to each module:
| Module | Validation | Notifications | Loading | FormGuard | Pagination |
|---|---|---|---|---|---|
| Hotel Management | Yes | Replace $.notify() |
Yes | Yes | If needed |
| Cruise Management | Yes | Replace $.notify() |
Yes | Yes | Yes |
| Outbound Groups | Enhance existing | Replace confirm() |
Yes | Yes | Yes |
| Trip Maker | Yes | Replace prompt() |
Yes | Yes | Fix existing |
| Visa Management | Yes | Replace alert() |
Yes | Yes | Fix existing |
| Offer Management | Enhance existing | Replace $.notify(), confirm() |
Yes | Yes | Already works |
| Dashboard | N/A | Replace alert() |
Yes | N/A | N/A |
Phase 3: Navigation and Layout¶
Duration: 1 week Resolves: Navigation misalignment, dead links, information architecture issues Prerequisite: Phase 2 complete (shared components available)
3.1 Header Navigation Restructuring¶
File: tqweb-adm/header_bootstrap.html
Restructure into dropdown groups: - Dashboard (direct link) - Groups (dropdown): Inbound Groups, Outbound Groups - Products (dropdown): Hotels, Flights, Cruises, Activities - Tools (dropdown): Trip Maker, Offer Manager, Marketing Plan - System (dropdown): Settings (if implemented)
Remove or mark all dead href="#" links with "Coming Soon" badge and disabled class.
3.2 Sidebar Navigation Restructuring¶
File: tqweb-adm/index.html
- Add Outbound Groups entry
- Add Marketing Planning entry
- Move Offer Manager out of "System" section into "Tools" or "Marketing"
- Mirror the header's grouping structure
- Mark non-functional items with "Coming Soon"
3.3 Flight Search Integration¶
File: tqweb-adm/flights.html
Wrap the React flight search widget in the standard page layout shell that includes header_bootstrap.html. If the React widget cannot coexist with the Bootstrap navbar, create a minimal wrapper page with header/footer and embed via iframe.
3.4 Outbound Groups Inter-Page Navigation¶
Files: tqweb-adm/outbound-groups-*.html
Add "Previous Step" / "Next Step" buttons to sub-pages alongside "Back to Summary", enabling sequential workflow without hub-and-spoke round-trips.
3.5 Breadcrumb System¶
Add breadcrumbs to all modules with multi-level navigation: - Dashboard > Module > Page > Action - Consistent format across all modules
Phase 4: Module Feature Completion¶
Duration: 6-8 weeks Resolves: Feature gaps, missing workflows, backend stubs Prerequisite: Phase 2-3 complete
4.1 Dashboard Transformation (1-2 weeks)¶
Files: tqweb-adm/index.html, tqweb-adm/js/modules/dashboard.js, new API endpoints
- Create API endpoints for dashboard statistics:
- Group counts by status
- Visa application counts by status
- Hotel/cruise/trip maker counts
- Connect
loadDashboardStats()to live APIs - Make KPI cards clickable (navigate to filtered module lists)
- Add KPI sections for Hotels, Cruises, Trip Maker
- Add "Quick Actions" bar: New Group, New Visa Application, Search Flights
- Add "Recent Items" section (last 10 items across modules)
- Remove "Add More Dashboard Widgets" placeholder
4.2 Trip Maker Completion (3-4 weeks)¶
Files: tqweb-adm/tripmaker-workspace.*, tqweb-adm/js/modules/tripmaker-workspace.js
Priority implementation order:
1. Activity Date and Time Period fields + calendar renderer
2. Activity edit and remove functionality
3. PDF generation button + quotation workflow (button added in Phase 1)
4. Real auto-save implementation (replace fake auto-save from Phase 1)
5. Project status transitions (Draft -> Active -> Quoted -> Closed)
6. Project deletion from dashboard
7. Global margin application
8. Price override modal (implement showOverridePriceModal)
9. Pre-fill flight origin/destination from project data
10. Functional pagination on dashboard (using Phase 2 component)
11. Create lead and save customer info in Odoo (use existing Customer API)
12. On accepting customer contact email, check if email exists in Odoo and if not, create it (use existing Customer API)
13. Quote creation in Odoo (use existing Document API)
4.3 Visa Management Completion (3-4 weeks)¶
Files: tqweb-adm/visamgmt.html, tqweb-adm/js/modules/visamgmt.js, backend API implementations
Priority implementation order: 1. Populate supplier dropdown from API 2. Implement Invoice & Payment step (wizard step 4) 3. Implement backend APIs for invoice and delivery (currently stubs) 4. Build delivery management UI 5. Make drag-and-drop upload functional 6. Add file type/size validation on uploads 7. Resolve dual editing paradigm (wizard vs. modal) -- unify into single approach 8. Add document preview/download/delete 9. Implement Alerts & Reports page 10. Implement Settings page
4.4 Cruise Management Pricing (1-2 weeks)¶
Files: tqweb-adm/js/modules/cruisemgmt.js, backend configuration
- Replace hardcoded exchange rate (
1.0) with configurable system - Implement exchange rate lookup from database or API
- Add proper cruise status lifecycle: Available, Cancelled, Sold-out
- Add itinerary status options: Suspended, Archived (in addition to Draft/Active)
- Fix bulk cabin maxPax handling (if not done in Phase 1)
- Add company delete capability
4.5 Outbound Groups Enhancements (1 week) -- COMPLETE¶
Status: Implemented in TQ-51 (2026-02-19)
Files: tqweb-adm/outbound-groups-*.html, tqweb-adm/js/modules/outbound-groups-*.js
Implemented: 1. Workflow progress indicator on Summary page showing step completion 2. Prerequisite checks for Rooming (needs Passengers + Accommodation) and Invoicing (needs Passengers + Pricing) 3. Activity price fields (previously only cost fields existed) 4. Mixed currency summing fix (conversion before aggregation) 5. Trip cloning capability 6. Empty-state guidance with action buttons for prerequisite pages
4.6 Inbound Groups — Schedule & UX Overhaul -- COMPLETE¶
Status: Implemented in TQ-55 (2026-02-22)
Files: tqweb-adm/groups-list.html, tqweb-adm/js/modules/groups-list.js, tqweb-adm/css/groups.css, tqweb-adm/header_bootstrap.html, tqapp/.../group/ScheduleExcelService.java, tqapp/.../group/GroupManagerFacade.java, tqapi/.../api/GroupApi.java, config/tourlinq-config.xml, config/api-roles.properties
Implemented:
1. Dark plum (#2a2149) navbar with dynamic page title ("Incoming Groups")
2. Inline filter panel replaced with filter modal dialog
3. Action bar with [New Group], [Filter], [Print Schedule], [Download Schedule] buttons
4. Responsive card grid replaced with horizontal scrollable compact card strip (220px cards, scroll arrows)
5. Schedule table below card strip: date-grouped events across all groups with color-coded readiness status pills
6. Expanded data loading via Promise.all() for all visible groups
7. Alert builders for hotels (confirmation), transports (driver, phone, booking), activities (tickets, transfer)
8. Configurable schedule range (2 weeks / 1 month / 2 months / 3 months) via schedule.default-range property
9. Portrait-orientation print support for schedule table (hidden modals to prevent blank 2nd page)
10. Server-side schedule Excel export (POST /groups/exportSchedule) using Apache POI with date-grouped rows, conditional status styling
11. Frontend binary download via fetch() with Bearer auth (bypasses JSON-only tlinq())
12. Calendar button removed (feature postponed — schedule table provides equivalent functionality)
4.7 Native OIDC Authentication (1-2 weeks) -- COMPLETE (Steps 1-2)¶
Status: Steps 1-2 implemented in TQ-51 (2026-02-19). Steps 3-5 pending production validation.
Replace oauth2-proxy-based authentication with a native OIDC client using Keycloak for the admin site (tqweb-adm), while preserving form-based authentication for the public site (tqweb-pub).
Full implementation details: OIDC Migration Implementation Plan
4.7.1 Backend — JWT Validation (tqapi module) -- COMPLETE¶
Files: tqapi/build.gradle.kts, new com.perun.tlinq.oidc package, AuthenticationFilter.java, TQProApiServer.java
Implemented:
1. Added Nimbus JWT dependencies to tqapi/build.gradle.kts (nimbus-jose-jwt:9.37.3, oauth2-oidc-sdk:11.10.1)
2. Created com.perun.tlinq.oidc package with 6 classes:
- AuthMode.java — enum: OAUTH2_PROXY, NATIVE_OIDC, HYBRID
- OIDCConfig.java — configuration holder (issuer, clientId, jwksUri, rolesClaim, etc.)
- ValidatedToken.java — validated JWT claims DTO
- TokenValidationException.java — validation failure with reason enum
- JWKSManager.java — fetches/caches Keycloak public keys (RS256)
- JWTValidator.java — validates JWT signature, issuer, audience, expiration; extracts roles from realm_access.roles
3. Modified AuthenticationFilter.java — three-tier auth pipeline: (1) Bearer JWT → (2) X-User/X-Roles headers → (3) dev-mode/guest fallback
4. Modified TQProApiServer.java — loads OIDC config from properties, initializes validator
5. Logout handler supports both native OIDC (direct Keycloak redirect) and oauth2-proxy mode
4.7.2 Frontend — OIDC Client (tqweb-adm only) -- COMPLETE¶
Files: new auth-service.js, new oidc-config.js, new callback.html, new silent-renew.html, globals.js, 24 admin pages
Implemented:
1. Added oidc-client-ts library
2. Created tqweb-adm/js/modules/auth-service.js (229 lines) — AuthService class wrapping UserManager with:
- login() / logout() / getAccessToken() / isAuthenticated()
- PKCE-based Authorization Code flow
- Automatic silent token renewal
- Backward-compatible sessionStorage population (ss_user_uid, ss_access_token, ss_loggedin, reguser)
3. Created tqweb-adm/js/modules/oidc-config.js — OIDC client configuration
4. Created tqweb-adm/callback.html — OIDC redirect callback handler
5. Created tqweb-adm/silent-renew.html — iframe-based silent token refresh
6. Modified tqweb-adm/js/modules/globals.js:
- tlinq() is now async, adds Authorization: Bearer <token> header to all API calls
- globalLogIn() triggers OIDC redirect instead of form-based login
- globalLogOut() uses OIDC end-session endpoint
- setGlobalHandlers() checks OIDC auth state on page load
- 401 responses trigger OIDC re-authentication
7. Updated 24 admin pages: setGlobalHandlers() → setGlobalHandlers({ requireAuth: true })
8. Updated header_bootstrap.html — logout via JS handler instead of direct API link
9. Updated logout.html — OIDC-aware logout flow
4.7.3 Configuration -- COMPLETE¶
- Added OIDC properties to
config/tlinqapi.properties:auth-mode,oidc-issuer,oidc-client-id, JWKS cache settings - Keycloak client configuration documented in OIDC Migration Implementation Plan
- Realm role assignment (
admin,agent) documented
4.7.4 Migration Rollout -- Steps 1-2 COMPLETE¶
- COMPLETE — Backend deployed with
auth-mode=hybrid(both JWT and oauth2-proxy work) - COMPLETE — Frontend deployed with OIDC client (new logins use OIDC, existing sessions continue)
- PENDING — Monitor and validate both auth paths in production
- PENDING — Switch to
auth-mode=native-oidc, remove oauth2-proxy - PENDING — Cleanup legacy auth code
4.7.5 Public Site (tqweb-pub) — No Changes¶
- Form-based login via
/user/authenticatecontinues unchanged - Guest access for anonymous browsing continues unchanged
sessionparameter in request bodies handled at facade layer (independent of OIDC)
Verification Plan¶
Phase 1 Verification¶
- [ ] Hotel calendar dialog accepts dates in 2026+
- [ ] Offer Management modals stay open when validation fails
- [ ] Hotel
saveQuickRatessaves all rates without silent failures - [ ] Trip Maker shows PDF generation button
- [ ] Trip Maker margin updates save correct values
- [ ] Trip Maker auto-save either works or shows no indicator
- [ ] Login page spinner activates on click
- [ ] All header navigation links reach valid pages
- [ ] Cruise bulk cabin assignment preserves per-cabin maxPax
- [ ] Visa Cancelled and Rejected badges have distinct colors
Phase 2 Verification¶
- [ ] Form validation shows inline errors on all modules
- [ ] All notifications use Bootstrap toasts (no native dialogs)
- [ ] All API calls show loading indicators
- [ ] Navigating away from dirty forms shows confirmation dialog
- [ ] Pagination works on all list views with 20+ items
Phase 3 Verification¶
- [ ] All navigation links in header and sidebar reach valid pages
- [ ] Header and sidebar show the same feature set
- [ ] Flight search page includes standard header
- [ ] Navigation between Outbound Groups sub-pages works sequentially
- [ ] Breadcrumbs appear on all multi-level pages
Phase 4 Verification¶
- [ ] Dashboard shows live KPI data
- [ ] KPI cards navigate to filtered module lists
- [ ] Trip Maker: full workflow from project creation to PDF generation
- [ ] Visa Management: full workflow from application to delivery
- [ ] Cruise pricing works with multiple currencies
- [ ] Backend starts with
auth-mode=hybridwithout errors - [ ] API accepts both Bearer JWT tokens and X-User/X-Roles headers
- [ ] API defaults to guest when no auth provided (tqweb-pub browsing works)
- [ ] OIDC login redirects to Keycloak and returns with valid token
- [ ]
tlinq()includes Authorization header in API calls - [ ] Silent token renewal works before expiration
- [ ] Logout clears session and redirects through Keycloak end-session
- [ ] tqweb-pub form-based login continues working unchanged
- [ ]
./gradlew buildpasses with new Nimbus dependencies - [ ] Cross-browser testing: Chrome, Firefox minimum
Dependencies and Risks¶
| Risk | Mitigation |
|---|---|
| Phase 2 changes affect all modules simultaneously | Roll out per-module; test each module before proceeding |
| Backend API stubs (Visa, TripMaker) require server-side work | Coordinate with backend team; frontend can implement UI with mock data first |
| Flight search React widget may not integrate with Bootstrap header | Fallback: iframe-based integration |
| Large scope may cause regressions | Phase 1 quick wins provide immediate value; each phase is independently deployable |
| OIDC migration affects authentication for all admin users | Hybrid mode allows gradual rollout; oauth2-proxy remains as fallback until native OIDC is validated |
| tqweb-pub must continue using form-based auth | Auth paths are independent; session parameter handled at facade layer, OIDC changes only affect tqweb-adm |
Plan generated: 2026-02-17 Based on: UX Improvement Recommendations report (137 issues across 9 modules)