This document outlines the implementation plan for enhancing the existing iTop Dashboard widget in the Nextcloud integration. The widget currently displays only assigned tickets but needs to be expanded to show a comprehensive view of user tickets and configuration items.
- Location:
lib/Dashboard/ItopWidget.php - Current Features:
- Shows assigned tickets only (via
getAssignedTickets()) - Limited to 7 tickets
- Basic ticket information (title, status, caller, creation date)
- Priority-based overlay icons
- Links to iTop ticket details
- Shows assigned tickets only (via
- Registration: Widget is registered in
Application.phpduring bootstrap - Authentication: Checks for token presence (legacy model - needs update for dual-token)
- Authentication Model: Uses deprecated
tokenfield instead of dual-token architecture (application_token + person_id) - Limited Scope: Only shows assigned tickets, not user-created tickets
- No CI Display: Configuration Items are not shown despite being core feature in Phase 3-5
- Missing Method: Calls
getAssignedTickets()which doesn't exist inItopAPIService - No Ticket Type Support: Doesn't distinguish between UserRequest and Incident
- No Status Filtering: Shows all tickets without status-based filtering
- Ticket Summary View: Display user's created tickets (UserRequest + Incident)
- Status-Based Sections: Group tickets by status (open, escalated, pending, etc.)
- Priority Indicators: Visual priority badges/icons
- Ticket Counts: Show counts by status and priority
- Quick Actions: Links to iTop ticket details and create new ticket
- Configuration Item Highlights: Show recently accessed/relevant CIs
- Dual-Token Authentication: Migrate to person_id based authentication
- Profile-Aware Display: Portal users see only their tickets, power users see assigned + created
- Ticket creation directly from dashboard
- Bulk ticket operations
- Advanced filtering/sorting controls
- Ticket status updates/comments
- CI relationship management
- Custom dashboard layouts
Add new methods to support dashboard requirements:
/**
* Get tickets created by user grouped by status
* @param string $userId
* @return array ['open' => [...], 'pending' => [...], 'escalated' => [...], 'resolved' => [...]]
*/
public function getUserTicketsByStatus(string $userId): array
/**
* Get recently accessed or relevant CIs for user
* @param string $userId
* @param int $limit
* @return array
*/
public function getUserRecentCIs(string $userId, int $limit = 5): arrayCurrent Issues to Fix:
- Replace
tokencheck withperson_idcheck (dual-token model) - Remove
getAssignedTickets()call (method doesn't exist) - Use
getUserCreatedTickets()or newgetUserTicketsByStatus()method - Add CI display using existing CI preview infrastructure
New Structure:
public function getItems(string $userId): array {
// Check person_id instead of token
if (!$this->hasPersonId($userId)) {
return [];
}
// Get ticket summary
$ticketStats = $this->getUserTicketsByStatus($userId);
// Get recent CIs (if enabled)
$recentCIs = $this->getUserRecentCIs($userId, 3);
// Format dashboard items
return $this->formatDashboardItems($ticketStats, $recentCIs);
}Current State: File needs to be created or updated Purpose: Handle dashboard widget interactivity and data display
Features to Implement:
- Fetch ticket summary data via API endpoint
- Display ticket counts by status with color coding
- Show priority distribution
- Render CI preview cards
- Implement refresh mechanism
- Handle error states gracefully
New File: Create dedicated styles for dashboard widget
Components:
- Ticket status badges (color-coded)
- Priority indicators (high/medium/low)
- CI preview cards (compact version of rich preview)
- Empty state messages
- Loading spinners
- Responsive layout for mobile
[
'name' => 'ItopAPI#getDashboardData',
'url' => '/dashboard',
'verb' => 'GET'
],
[
'name' => 'ItopAPI#getRecentCIs',
'url' => '/recent-cis',
'verb' => 'GET'
]/**
* Get dashboard data for current user
* @NoAdminRequired
*/
public function getDashboardData(): DataResponse {
// Get ticket statistics
$ticketStats = $this->itopAPIService->getUserTicketsByStatus($this->userId);
// Get ticket counts
$counts = $this->itopAPIService->getUserCreatedTicketsCount($this->userId);
// Get recent CIs if enabled
$recentCIs = [];
if ($this->isCIBrowsingEnabled()) {
$recentCIs = $this->itopAPIService->getUserRecentCIs($this->userId, 5);
}
return new DataResponse([
'stats' => $ticketStats,
'counts' => $counts,
'recent_cis' => $recentCIs
]);
}Priority: Critical - Widget currently non-functional
Tasks:
- Analyze existing
ItopAPIServicemethodsgetUserCreatedTickets()exists βgetUserCreatedTicketsCount()exists β- No need for
getAssignedTickets()- method doesn't exist
- Update
ItopWidget.phpauthentication check- Replace
tokenwithperson_idcheck - Update
load()method to use person_id - Update
getItems()to use person_id
- Replace
- Fix
getItems()method- Replace
getAssignedTickets()withgetUserCreatedTickets() - Add error handling for missing person_id
- Update ticket formatting for both UserRequest and Incident
- Replace
- Add dashboard data endpoint
- Create
getDashboardData()in ItopAPIController - Return ticket counts and status breakdown
- Create
- Test with real iTop instance
- Verify ticket display works
- Test with portal-only and power users
Files to Modify:
lib/Dashboard/ItopWidget.php- Fix authentication and data fetchinglib/Controller/ItopAPIController.php- Add dashboard endpointappinfo/routes.php- Register new routes
Expected Outcome: Dashboard widget displays user's created tickets correctly
Status Update (2025-10-29): Implemented person_id-based checks and switched to getUserCreatedTickets(); added /dashboard endpoint and route returning counts and by-status breakdown.
Priority: High - Improve user experience
Tasks:
- Implement status-based grouping
- Add
getUserTicketsByStatus()method in ItopAPIService - Group tickets: open, escalated, pending, resolved, closed
- Add ticket type distinction (UserRequest vs Incident)
- Add
- Create dashboard JavaScript bundle
- Setup Vite config for dashboard entry point
- Fetch data from new API endpoint
- Implement interactive ticket list (top 4 recent tickets)
- Add status filter toggles (My Open, class filters, recently updated)
- Design dashboard UI
- Status cards with counts (compact badges)
- Color coding and badges
- SVG icons per ticket state (new, escalated, deadline, closed)
- Quick action buttons (Refresh, New Ticket)
- Responsive layout (breakpoints for cards and statuses)
- Add dashboard styles
- Create inline dashboard styles in DashboardWidget.vue
- Status badge colors matching iTop
- Priority emoji indicators (π΄π π‘π’)
- Status emoji indicators (ππ₯β³
β οΈ β βοΈβ) - Mobile-responsive grid
- Enhanced UX features
- Tooltips on status emoji (shows status label)
- Tooltips on priority emoji (shows priority level)
- Tooltips on ticket title (shows full ticket details with sanitized description)
- Tooltips on relative time (shows full timestamp with "Last updated:" prefix)
- Click-to-open ticket functionality (removed hover effects)
- Inline metadata display (status β’ priority β’ time)
Files Created/Modified:
src/dashboard.js- Main dashboard Vue app [created]src/views/DashboardWidget.vue- Dashboard component with full feature set [created]vite.config.ts- Add dashboard build entry [updated]lib/Controller/ItopAPIController.php- Dashboard data endpoint [updated]
Expected Outcome: Rich, interactive dashboard with status-based ticket organization Status Update (2025-11-01): β FULLY IMPLEMENTED AND WORKING
- β Dual-widget design
- β Compact status badges showing ticket counts
- β most recent tickets displayed with type icons and status indicators
- β Agent widget: My Work, Team Queue, SLA tracking
- β Change management section
- β One Change displayed with time window
- β Refresh and action buttons
- β Priority indicators (red/orange/green) on tickets
- β Status badges (Ongoing, Resolved) with relative timestamps
- β Responsive layout
Key Implementation Highlights:
- Agent widget displays comprehensive metrics: My Work, Team Queue, SLA Warnings/Breaches
- Change widget integration showing emergency change with start/end times
- Clean, professional UI
Priority: Medium Deferred - Widget space constraints
Status: SKIPPED - No space available in current widget layout
Reason for Skipping: Due to space constraints in the dashboard widget (limited to 4 tickets + action buttons), there is insufficient room to add CI integration while maintaining a clean, usable interface. The widget is optimized for ticket viewing, and adding CI cards would create information overload.
Alternative Solutions:
- Agent Widget: CI browsing could be added to the proposed Agent Dashboard widget (Phase 5)
- Dedicated CI Widget: Create a separate "iTop Configuration Items" dashboard widget
- Smart Picker Integration: CIs are already accessible via the existing Smart Picker feature
Original Tasks (deferred):
- Implement recent CI tracking via
getUserRecentCIs()method - Create CI dashboard cards (compact version of rich preview)
- Add "Recently Viewed CIs" section to dashboard
- Test CI display with different CI classes
Status Update (2025-10-31): Skipped to maintain clean, focused dashboard UI
Priority: Low - Optimization and refinement
Tasks:
- Implement dashboard caching
- Cache dashboard data with 60s TTL
- Add refresh button for manual reload
- Use existing CacheService infrastructure
- Add loading states
- Skeleton loaders for tickets and CIs
- Smooth transitions
- Error state handling
- Optimize API calls
- Batch ticket and CI requests
- Minimize redundant queries
- Use existing query cache
- Accessibility improvements
- Keyboard navigation
- Screen reader support
- High contrast mode
- Add dashboard configuration
- Personal settings toggle for dashboard
- Choose what to display (tickets only, tickets + CIs, etc.)
- Set refresh interval
Files to Modify:
lib/Service/CacheService.php- Add dashboard cache TTLsrc/views/DashboardWidget.vue- Loading states and a11ylib/Settings/Personal.php- Dashboard preferencestemplates/personalSettings.php- Dashboard settings UI
Expected Outcome: Fast, accessible, configurable dashboard experience
Priority: Medium - Enhanced agent experience Status: β FULLY IMPLEMENTED
Completed Tasks:
- Fully read and understand ## PROPOSED: Dual Dashboard Widgets (Portal + Agent)
- Create
ItopAgentWidget.phpimplementingIConditionalWidget - Add team membership query methods to
ItopAPIService - Add agent-specific ticket query methods
- Create
/agent-dashboardAPI endpoint - Create
AgentDashboardWidget.vuecomponent - Add agent dashboard build entry to vite.config
- Register both widgets in
Application.php - Test conditional visibility (portal vs agent users)
- Test agent-specific data fetching
- Add translations for agent widget strings - β COMPLETED
- Update README with dual-widget documentation - β COMPLETED
Translation Status (Task 11 - β COMPLETED):
- β Added 35+ translation strings to all 4 language files (en, de, de_DE, fr)
- β Fixed all hardcoded strings in DashboardWidget.vue (status labels, priorities, tooltips, relative time)
- β Fixed all hardcoded strings in AgentDashboardWidget.vue (change status labels, template text)
- β Fixed template to use getStatusLabel() for dynamic status display
- β Added Vue.prototype.t/n/OC in dashboard.js and agentDashboard.js
- β Registered IL10N service in Application.php for proper translation injection
- β Built successfully with npm run build
- β Translations working in browser (verified with French/German locales)
- β
Additional improvements:
- Shortened French translations to prevent text overflow ("CrΓ©er" instead of "Nouveau ticket")
- Optimized German translations ("SLA-Warnung" singular form)
- Updated app icons (app.svg, app-dark.svg) to match Nextcloud design system
Files to Create:
lib/Dashboard/ItopPortalWidget.php(rename from ItopWidget.php)lib/Dashboard/ItopAgentWidget.php(new)src/views/AgentDashboardWidget.vue(new)src/agentDashboard.js(new)
Files to Modify:
lib/Service/ItopAPIService.php- Add agent query methodslib/Controller/ItopAPIController.php- Add getAgentDashboardData()appinfo/routes.php- Add /agent-dashboard routelib/AppInfo/Application.php- Register both widgetsvite.config.ts- Add agent dashboard build entryl10n/en.json- Add agent widget strings
Expected Outcome:
- Portal users see "iTop - My Tickets" widget only
- Agent users see both "iTop - My Tickets" and "iTop - Agent Dashboard" widgets
- Agent widget shows assigned tickets, team queue, escalations, and upcoming changes
- Conditional visibility works correctly based on user profile
Tasks:
- Write PHPUnit tests - β
COMPLETED
- Created
tests/Unit/Service/ItopAPIServiceDashboardTest.php(13 tests)- Test
getUserCreatedTicketsCount()method - Test
getUserTicketsByStatus()method - Test
getMyAssignedTickets()method - Test
getTeamAssignedTickets()method - Test
getUpcomingChanges()method - Test
getSLAWarningCounts()method - Test
getSLABreachCounts()method - Test
getUserTeams()method - Test error handling scenarios
- Test
- Created
tests/Unit/Controller/ItopAPIControllerDashboardTest.php(11 tests)- Test
/dashboardendpoint success/error cases - Test
/agent-dashboardendpoint success/error cases - Test authentication failures
- Test empty queues and tickets
- Test mixed ticket types (Incident/UserRequest)
- Test data structure consistency
- Test
- Note: Tests require Nextcloud test environment to execute
- Created
- Manual testing - β
COMPLETED
- Test with portal-only user (verified widget visibility)
- Test with power user/agent (verified both widgets)
- Test with no tickets/empty queues
- Test error scenarios (API failures handled gracefully)
- Update documentation - β
COMPLETED
- Add dashboard section to README (dual-widget architecture fully documented)
- Document dashboard API endpoints (API_DASHBOARD.md created)
- Update PLAN_CI_BROWSING.md with dashboard status
- Add dashboard screenshots to docs/images/ (dashboard1.png created)
- Update translations - β
COMPLETED
- Add dashboard strings to l10n/en.json (35+ strings added)
- Translate to German (de.json, de_DE.json)
- Translate to French (fr.json)
- Refactor all hardcoded strings to use t() function
- Add Vue.prototype.t/n/OC to dashboard entry points
- Register IL10N service in Application.php for proper injection
- Translations working correctly in all languages
- Text optimization (shortened French/German strings for better UI fit)
Files to Create/Modify:
tests/unit/Service/ItopAPIServiceDashboardTest.php- New test filetests/unit/Controller/ItopAPIControllerTest.php- Update existingdocs/dashboard.md- New documentationl10n/*.json- Translation updatesREADME.md- Feature documentation
Expected Outcome: Fully tested, documented dashboard feature
{
"stats": {
"open": 5,
"escalated": 2,
"pending": 3,
"resolved": 10,
"closed": 45
},
"counts": {
"incidents": 8,
"requests": 12,
"total": 20
},
"tickets": {
"open": [
{
"type": "UserRequest",
"id": 123,
"ref": "R-000123",
"title": "Cannot access email",
"status": "new",
"priority": "high",
"created": "2025-01-15T10:30:00Z"
}
],
"escalated": [...],
"pending": [...]
},
"recent_cis": [
{
"class": "PC",
"id": 45,
"name": "LAPTOP-001",
"status": "production",
"org_name": "IT Department",
"url": "http://itop/pages/UI.php?operation=details&class=PC&id=45"
}
]
}ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ποΈ <DisplayName> β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 5 Tickets ( 3 Open ) ( 1 Pending ) ( 1 Resolved ) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β π¬ R-000007: Test Request β
β Ongoing β’ π’ β’ π 11 hours ago β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β π΄ I-000004: Incident by Bo... [NEW] β
β Ongoing β’ π΄ β’ π 3 days ago β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β οΈ I-000003: Incident opene... β
β Ongoing β’ π β’ π 2 weeks ago β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
R-000002: Test Request... β
β Resolved β’ π’ β’ π 2 weeks ago β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β [ π Refresh ] [ β New Ticket ] β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key Features Implemented:
- Compact Header: Shows total ticket count with status badge breakdown
- Status Badges: Pill-style badges for Open, Pending, Resolved counts
- Ticket Cards: 4 most recent tickets with:
- Type-specific icons (π¬ UserRequest, π΄/
β οΈ Incident) - Reference and truncated title
- Status emoji indicators (ππ₯β³
β οΈ β ) - Priority emoji indicators (π΄π π‘π’)
- Relative timestamps with tooltips
- "NEW" badge for recently created tickets
- Type-specific icons (π¬ UserRequest, π΄/
- Action Buttons: Refresh and New Ticket at the bottom
- Responsive Design: Mobile-friendly layout with proper breakpoints
- No CI Section: Space constraints prevent CI integration in this widget
- Enable/disable dashboard widget globally
- Set default ticket limit for dashboard
- Configure dashboard refresh interval
- Toggle CI display on/off
- Choose dashboard sections to display
- Set personal ticket limit
- Configure refresh behavior
- Select which ticket statuses to show
- Dashboard Load Time: <500ms (cached)
- API Response Time: <300ms for dashboard data
- Refresh Interval: 60s default (configurable)
- Cache TTL: 60s for dashboard data, 5 minutes for CI list
- Memory Usage: <2MB additional for dashboard widget
{
"Your Tickets": "Your Tickets",
"Open Tickets": "Open Tickets",
"Escalated": "Escalated",
"Pending": "Pending",
"Resolved": "Resolved",
"Recent Tickets": "Recent Tickets",
"Recently Viewed CIs": "Recently Viewed CIs",
"Create New Ticket": "Create New Ticket",
"View All in iTop": "View All in iTop",
"No tickets found": "No tickets found",
"No CIs accessed recently": "No CIs accessed recently",
"Dashboard refresh failed": "Dashboard refresh failed",
"Loading dashboard...": "Loading dashboard..."
}- Authentication: Use dual-token model (person_id based)
- Data Isolation: All queries filtered by person_id
- Profile-Aware: Portal users see only their created tickets
- No Write Operations: Dashboard is read-only
- Rate Limiting: Dashboard refresh limited to 1 request per 30 seconds
- Cache Security: Dashboard data cached per-user, not shared
- User Engagement: >60% of configured users view dashboard within 7 days
- Performance: 95th percentile load time <1s
- Reliability: <0.5% error rate for dashboard data fetching
- User Satisfaction: Positive feedback on ticket overview functionality
- Phase 3-5 CI browsing infrastructure (already implemented)
- Dual-token authentication model (already implemented)
- Existing ItopAPIService methods:
getUserCreatedTickets()βgetUserCreatedTicketsCount()βgetCurrentUser()β
- None - uses existing Nextcloud Dashboard API
| Risk | Impact | Mitigation |
|---|---|---|
| Dashboard widget conflicts with existing dashboards | Low | Test thoroughly with default Nextcloud widgets |
| Performance issues with many tickets | Medium | Implement pagination and limit default display to 10 tickets |
| API rate limiting with frequent refreshes | Low | Default refresh to 60s, cache aggressively |
| CI data stale in dashboard | Low | Show "last updated" timestamp, add manual refresh |
-
ItopAPIService::getUserCreatedTickets()- Exists -
ItopAPIService::getUserCreatedTicketsCount()- Exists -
ItopAPIService::getUserTicketsByStatus()- To be implemented -
ItopAPIService::getUserRecentCIs()- To be implemented -
ItopAPIController::getDashboardData()- To be implemented
- Dashboard widget loads correctly
- Ticket data fetches and displays
- CI preview cards render
- Portal-only user sees filtered data
- Power user sees full data
- Error states handled gracefully
- Dashboard displays on Nextcloud home
- Ticket counts match reality
- Status badges correct colors
- Links to iTop work
- CI cards display properly
- Refresh button works
- Empty states display correctly
- Mobile responsive layout
- Dark mode compatible
- Phase 1: β COMPLETED - Backend fixes deployed (fixes broken widget)
- Phase 2: β COMPLETED - Enhanced ticket display fully implemented
- Phase 3: βοΈ SKIPPED - CI integration deferred due to space constraints
- Phase 4: π IN PROGRESS - Performance optimization, caching, accessibility improvements
- Phase 5: π IN PROGRESS - Dual widget architecture implemented (Portal + Agent widgets working)
- Phase 6: π NEXT - Testing, optimization, and documentation
Current Status (2025-11-01):
- β Core dashboard functionality complete and working
- β Dual widget architecture live (Portal + Agent widgets)
- π Translation integration in progress (blocking issue with Vue i18n)
- π Ready for testing phase once translations resolved
- π Documentation updates pending (README dual-widget section)
- Create tickets directly from dashboard
- Quick status updates (mark as resolved, etc.)
- Ticket assignment changes
- Add/remove watchers
- Inline comment threads
- Custom dashboard layouts
- Widget size options (small/medium/large)
- Multiple widget instances with filters
Based on investigation, it is fully feasible to deliver two separate dashboard widgets following the pattern used by the Nextcloud Deck app.
Nextcloud allows multiple dashboard widgets from a single app. The Deck app demonstrates this pattern:
Each widget is registered separately in Application.php:130-132:
$context->registerDashboardWidget(DeckWidgetUpcoming::class);
$context->registerDashboardWidget(DeckWidgetToday::class);
$context->registerDashboardWidget(DeckWidgetTomorrow::class);The IConditionalWidget interface allows widgets to control their visibility per-user via an isEnabled() method. This is perfect for showing the Agent widget only to non-portal users.
interface IConditionalWidget extends IWidget {
public function isEnabled(): bool;
}Target Audience: All users (portal-only and agents)
Widget ID: integration_itop_portal
Title: {DisplayName}
Features:
- User's created tickets (UserRequest + Incident)
- Status breakdown (Open, Pending, Escalated, Resolved)
- Recent 4 tickets with quick links
- Recently viewed CIs (optional)
- "Create New Ticket" button
- Matches current Phase 2 implementation
Visibility: Always enabled for users with person_id configured
Target Audience: iTop Agents only (users with is_portal_only = false)
Widget ID: integration_itop_agent
Title: {DisplayName} - Agent
Features:
-
My Open Tickets
- Tickets assigned to me (
agent_id = person_id) - Count and list view
- Priority breakdown
- Tickets assigned to me (
-
Team Tickets
- Open tickets assigned to groups/teams I belong to
- Requires querying
Teammemberships via iTop API - Group-level assignment tracking
-
Escalated Tickets
- Escalated tickets in my teams
- Filter by
status = escalatedwithin team scope - Priority indicators
-
Upcoming Changes
- Change tickets with upcoming implementation dates
- Query
Changeclass wherestart_dateis near-future - Filter by team membership or CI ownership
-
Additional Suggestions:
- Recently Closed by Me: Tickets I recently resolved (last 7 days)
- Pending Customer Response: Tickets waiting on caller reply
- SLA Breaches: Tickets approaching or exceeding SLA deadlines
- Recently Viewed CIs: CIs I've accessed (reuse from portal widget)
Visibility: Only enabled if ProfileService->isPortalOnly($userId) === false
How widgets appear for different user types:
| User Type | person_id | is_portal_only | iTop Widget | iTop - Agent Widget |
|---|---|---|---|---|
| Unconfigured | β No | N/A | β Hidden | β Hidden |
| Portal User | β Yes | β true | β Visible | β Hidden |
| Agent User | β Yes | β false | β Visible | β Visible |
Logic Details:
-
ItopWidget (portal widget):
- Checks
person_idviaisEnabled()method - Hidden if user hasn't configured iTop connection
- Visible once
person_idis set (regardless of portal/agent status)
- Checks
-
ItopAgentWidget (agent widget):
- Checks both
person_idANDProfileService->isPortalOnly()viaisEnabled()method - Hidden if no
person_idconfigured - Hidden if user is portal-only (
is_portal_only = true) - Visible only for agents (
is_portal_only = false)
- Checks both
Implementation:
Both widgets implement IConditionalWidget interface with isEnabled(): bool method that controls visibility automatically.
lib/Dashboard/
βββ ItopWidget.php (existing, implements IConditionalWidget look for person_id)
βββ ItopAgentWidget.php (agent-only widget, implements IConditionalWidget)
// Agent-specific ticket queries
public function getMyAssignedTickets(string $userId): array
public function getTeamAssignedTickets(string $userId): array
public function getEscalatedTicketsForMyTeams(string $userId): array
public function getUpcomingChanges(string $userId): array
// Team membership queries
public function getUserTeams(string $userId): array
public function getTeamTickets(string $userId, array $teamIds, string $status = ''): array
// SLA and metrics
public function getTicketsNearingSLA(string $userId, array $teamIds): array
public function getPendingCustomerTickets(string $userId): array// lib/Controller/ItopAPIController.php
public function getAgentDashboardData(): DataResponse {
// Returns:
// - myTickets: assigned to me
// - teamTickets: assigned to my teams
// - escalated: escalated in my teams
// - upcomingChanges: relevant changes
// - counts: ticket counts by category
}// appinfo/routes.php
['name' => 'ItopAPI#getAgentDashboardData', 'url' => '/agent-dashboard', 'verb' => 'GET'],src/views/
βββ DashboardWidget.vue (existing - portal widget)
βββ AgentDashboardWidget.vue (new - agent widget)
// vite.config.ts
build: {
rollupOptions: {
input: {
dashboard: './src/dashboard.js', // existing
agentDashboard: './src/agentDashboard.js', // new
}
}
}src/
βββ dashboard.js (existing - portal widget)
βββ agentDashboard.js (new - agent widget)
// lib/AppInfo/Application.php
public function register(IRegistrationContext $context): void {
// Register both widgets
$context->registerDashboardWidget(ItopPortalWidget::class);
$context->registerDashboardWidget(ItopAgentWidget::class);
// ... existing registrations
}namespace OCA\Itop\Dashboard;
use OCA\Itop\Service\ProfileService;
use OCP\Dashboard\IConditionalWidget;
use OCP\Dashboard\IWidget;
class ItopAgentWidget implements IWidget, IConditionalWidget {
public function __construct(
private ProfileService $profileService,
private ?string $userId,
// ... other dependencies
) {}
public function getId(): string {
return 'integration_itop_agent';
}
public function getTitle(): string {
return $this->l10n->t('%s - Agent Dashboard', [$displayName]);
}
public function isEnabled(): bool {
if ($this->userId === null) {
return false;
}
// Only show to non-portal users (agents)
try {
return !$this->profileService->isPortalOnly($this->userId);
} catch (\Exception $e) {
return false;
}
}
public function load(): void {
if ($this->userId !== null && $this->isEnabled()) {
Util::addScript(Application::APP_ID, 'integration_itop-agent-dashboard');
}
}
}To get teams a user belongs to:
$params = [
'operation' => 'core/get',
'class' => 'Team',
'key' => "SELECT Team AS t JOIN lnkPersonToTeam AS l ON l.team_id = t.id WHERE l.person_id = {$personId}"
];To get tickets assigned to specific teams:
// For UserRequest
$params = [
'operation' => 'core/get',
'class' => 'UserRequest',
'key' => "SELECT UserRequest WHERE team_id IN (" . implode(',', $teamIds) . ") AND status != 'closed'"
];
// For Incident
$params = [
'operation' => 'core/get',
'class' => 'Incident',
'key' => "SELECT Incident WHERE team_id IN (" . implode(',', $teamIds) . ") AND status != 'closed'"
];$params = [
'operation' => 'core/get',
'class' => 'Change',
'key' => "SELECT Change WHERE start_date > NOW() AND start_date < DATE_ADD(NOW(), INTERVAL 7 DAY) AND status IN ('approved', 'planned')",
'output_fields' => 'id,ref,title,description,start_date,status,impact,priority'
];ββββββββββββββββββββββββββββββββββββββββββββββββββ
β <DisplayName> - Agent β
ββββββββββββββββββββββββββββββββββββββββββββββββββ€
β My Work Team Queue Escalated β
β π€ 8 tickets π₯ 16 tickets β οΈ 1 tickets β
β β
β π€ My Work π₯ Team Queue. β
β ββββββββββββββββββββββββ ββββββββββββββββββββ β
β β 5 Incidents β β 12 Incidents β β
β β 3 User Request β β 4 User Request β β
β ββββββββββββββββββββββββ ββββββββββββββββββββ β
β β οΈ Escalated π¨ SLA Breaches β
β ββββββββββββββββββββββββ ββββββββββββββββββββ β
β β 1 Incidents β β 2 Incidents β β
β β 0 User Request β β 0 User Request β β
β ββββββββββββββββββββββββ ββββββββββββββββββββ β
β Upcoming Changes (3) β
β ββββββββββββββββββββββββββββββββββββββββββββββ β
β β C-000023: Firewall update β β
β β π
Tomorrow 2:00 AM β β
β ββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β [ Refresh ] [ View All Tickets ] β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
{
"Agent Dashboard": "Agent Dashboard",
"%s - Agent Dashboard": "%s - Agent Dashboard",
"My Assigned Tickets": "My Assigned Tickets",
"Team Queue": "Team Queue",
"Escalated Tickets": "Escalated Tickets",
"Upcoming Changes": "Upcoming Changes",
"My Work": "My Work",
"Recently Closed by Me": "Recently Closed by Me",
"Pending Customer Response": "Pending Customer Response",
"SLA Breaches": "SLA Breaches",
"unassigned": "unassigned",
"assigned %s ago": "assigned %s ago",
"Team %s": "Team %s",
"No assigned tickets": "No assigned tickets",
"No team tickets": "No team tickets",
"No upcoming changes": "No upcoming changes",
"View All Tickets": "View All Tickets"
}Priority: Medium - Enhanced agent experience Depends on: Phase 1-2 completion
Tasks:
- Rename existing
ItopWidget.phptoItopPortalWidget.php - Create
ItopAgentWidget.phpimplementingIConditionalWidget - Add team membership query methods to
ItopAPIService - Add agent-specific ticket query methods
- Create
/agent-dashboardAPI endpoint - Create
AgentDashboardWidget.vuecomponent - Add agent dashboard build entry to vite.config
- Register both widgets in
Application.php - Test conditional visibility (portal vs agent users)
- Test agent-specific data fetching
- Add translations for agent widget strings
- Update README with dual-widget documentation
Files to Create:
lib/Dashboard/ItopPortalWidget.php(rename from ItopWidget.php)lib/Dashboard/ItopAgentWidget.php(new)src/views/AgentDashboardWidget.vue(new)src/agentDashboard.js(new)
Files to Modify:
lib/Service/ItopAPIService.php- Add agent query methodslib/Controller/ItopAPIController.php- Add getAgentDashboardData()appinfo/routes.php- Add /agent-dashboard routelib/AppInfo/Application.php- Register both widgetsvite.config.ts- Add agent dashboard build entryl10n/en.json- Add agent widget strings
Expected Outcome:
- Portal users see "iTop - My Tickets" widget only
- Agent users see both "iTop - My Tickets" and "iTop - Agent Dashboard" widgets
- Agent widget shows assigned tickets, team queue, escalations, and upcoming changes
- Conditional visibility works correctly based on user profile
-
Clear Separation of Concerns
- Portal widget: End-user ticket tracking
- Agent widget: Operational agent workflows
-
User Choice
- Users can enable/disable widgets independently
- Agents can choose to show one or both widgets
- Follows Nextcloud's user-centric design philosophy
-
Performance
- Agent widget only loads when needed
- Portal users don't pay performance cost for unused agent features
- Separate API endpoints prevent over-fetching
-
Maintainability
- Clear code separation
- Easier to extend agent features without affecting portal widget
- Independent testing and debugging
-
Scalability
- Can add more specialized widgets in future (e.g., "iTop - Manager Dashboard")
- Each widget can have its own update cadence
- Widget-specific caching strategies
| Risk | Impact | Mitigation |
|---|---|---|
| Performance degradation with team queries | Medium | Implement aggressive caching for team memberships (1hr TTL), use query optimization |
| iTop API doesn't support team membership queries | High | Validate API capabilities before implementation; fallback to agent_id only if needed |
| Complex permission logic | Medium | Reuse existing ProfileService; add comprehensive unit tests |
| User confusion with two widgets | Low | Clear naming and descriptions; documentation with screenshots |
-
ProfileService::isPortalOnly()validation -
ItopAPIService::getMyAssignedTickets() -
ItopAPIService::getTeamAssignedTickets() -
ItopAPIService::getUserTeams() -
ItopAgentWidget::isEnabled()conditional logic
- Portal user sees only portal widget
- Agent sees both widgets
- Agent widget disabled when ProfileService indicates portal-only
- Team ticket queries return correct results
- Upcoming changes query works
- Test with real iTop instance (portal user)
- Test with real iTop instance (agent user)
- Test with user having multiple teams
- Test with user having no teams (fallback to agent_id only)
- Test widget visibility toggling in dashboard settings
Status: Planning phase complete, ready for implementation Next Step: Begin Phase 1 backend fixes Owner: To be assigned Target Completion: TBD based on priority
Dual Widget Status: Feasibility confirmed, awaiting Phase 1-2 completion before implementation