HR Dashboard — technical guide
This page documents the legacy manager/HR dashboard reached through Analytique → Tableaux de bord. It covers the shell, aggregation, population resolution, partial-response contracts and configuration dependencies. It is intended for implementers and support engineers; use the functional guide for user tasks and the configuration guide for activation.
Unless marked otherwise, implementation statements are Verified in code in the inspected local repositories. Source paths are relative to the named repository; they identify implementation evidence without exposing tenant data or deployment addresses.
Architecture and ownership
| Component | Responsibility |
|---|---|
Company/ManagerDashboardBundle/Controller/DefaultController.php | Page/tab entry points; hierarchy/session state; eligibility queries; chart payloads and Twig rendering. |
Services/Helper.php | Current user/company context; feature checks; legacy access fallback; admin classification; labels, scale and palette. |
Chart/TeamProficiency.php | Job mastery, gaps, motivation and job representation calculations. |
Chart/SkillsMapCampaign.php | Self-declaration/skills-campaign statistics. |
Controller/OneToOneController.php | Interview progress detail within the last resolved perimeter. |
Controller/PerimeterController.php | Server-side searchable/paginated employee listing for the scope modal. |
Resources/views/Default, Tabs, Partials | Page skeleton, tab content and drill-down tables. |
Resources/public/js/{dashboard,perimeter}.js | Tab loading, scope submission, chart/table initialization, drill-downs and downloads. |
The page loads jQuery/Bootstrap integrations, Select2, DataTables and Highcharts, including chart-export modules. Doctrine reads the legacy relational model. No AI service call was identified in these aggregation paths.
Request and rendering flow
indexActiongets the authenticated user'sSchooland constructs enabled tabs and the organization tree.- The Twig page renders tab endpoints and the scope form. The browser restores a tab from the URL fragment where possible.
- A tab change submits the checked
.filterinputs to that tab's endpoint by POST. jQuery supplies the XMLHttpRequest header expected by the controller. - The handler validates access and a nonempty
level[]orrelation[], resolves manager IDs, stores session filter markers and computes the perimeter. - An empty perimeter returns a rendered empty-state partial with the perimeter attribute. A nonempty perimeter runs the enabled aggregations.
- The browser inserts
html, updatesattributes.perimeterSize, initializes returned chart definitions and tables, and supports additional drill-down requests.
The long-request message appears after 15 seconds in the browser. This is a presentation timer, not an API timeout or freshness guarantee. Tab requests destroy existing charts before rendering the response.
HTTP contracts
All paths below use the legacy /company/dashboard-manager prefix from config/routes.yaml. These are session-authenticated application endpoints, not a public JSON reporting API. Examples omit hostnames, cookies and employee identifiers.
| Method / suffix | Handler / response |
|---|---|
GET / | indexAction; full Twig page. |
POST /team-activity | teamActivityAction; completion/activity HTML, charts and perimeter attribute. |
POST /team-proficiency | teamProficiencyAction; job-based HTML/charts; supported job drill-downs. |
POST /skill-analysis | skillAnalysisAction; skill HTML/charts or selected-skill partials. |
POST /skills-map-campaign | campaignAction; skills-campaign panels. |
POST /1to1 | oneToOneAction; eligible interview campaign selector. |
GET /1to1/progress/{id} | OneToOneController::progressAction; selected interview progress; numeric ID required. |
POST /perimeter | PerimeterController::indexAction; DataTables server-side response using session perimeter. |
Illustrative form request, Verified in code; not replayed against the service:
POST /company/dashboard-manager/skill-analysis
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded
level%5B%5D=1
level[]=entire_company selects the company-tree branch; numeric level values select hierarchy levels. relation[] uses configured hierarchical relationship IDs when enabled. UI choices and server authorization must be considered separately.
The normal tab response has this structural shape; placeholders are explanatory, not real response data:
{
"html": "<rendered tab markup>",
"highcharts": {},
"attributes": {"perimeterSize": "<rendered population label and count>"}
}
Not every branch returns every key. Empty-perimeter responses return html and attributes; selected-skill partials can return html and a tableId. Skill queries use parameters such as skill, motivation, vw, and jid on supported branches. For example, vw=top/flop selects sum-based ranking detail while vw=topMastered/flopMastered selects average-based ranking detail. Job filtering is conditional on dashboard_filter_by_job.
The perimeter endpoint consumes DataTables draw, start, length, search[value] and order[0] values. It returns draw, recordsTotal, recordsFiltered and data; the current implementation sets both record-count fields from the same counted query. It depends on an established session perimeter and should not be treated as an independent employee search API.
Permissions and population boundaries
indexAction accepts any of the five dashboard tab feature switches or Helper::isManagerDashboardActive(). That fallback is true for the legacy permission behavior implemented by the helper, including a manager_dashboard role or its empty-permission condition. Each displayed tab still requires its feature switch. Tab handlers generally accept their own switch or the permission fallback; graph builders have additional element-switch checks.
PerimeterController uses an all-switch test plus dashboard-permission/admin alternatives, which differs from the page's any-switch test. Interview progress detail requires dashboard_team_1to1 directly and returns 404 for an unknown interview. A successful page load does not establish that every detail endpoint is accessible.
getManagerIds selects the normal hierarchy tree or the company tree. extractLevel also includes level 0 when level 1 is selected. For subordinate accounts with a permission profile lacking all_collaborators, it calls applyVisibilityRules, which delegates to VisibilityFilter. Relationship users can be unioned with hierarchy-based membership in individual queries.
Helper::isAdmin() is an implementation-specific dashboard scope classification using the School hierarchy, visibility and all_managers_filter right. It is not equivalent to company-settings administration. Observed in application: Jim had the wider-scope control while company settings returned an authorization error.
Feature evaluation is delegated through FeatureSwitchVoter/BaseVoter to company switch/tokenizer access. FeatureSwitchService reads persisted values keyed by company Setting and also accepts tokenizer-based access. The YAML defaults seed configuration; they are not automatically loaded as a fallback for every missing runtime value.
Data model and state lifecycle
| Data | Use |
|---|---|
User, Customer, School, Setting | Enabled employee identity, profile link, organizational membership, company context and skill scale. |
Job, Jobskill | Assigned job and required skill levels. |
Userskill | Employee mastery (note), motivation and source eligibility. |
| Skill/job translations | Labels for the active locale with fallback to source titles on inspected paths. |
CompanyHierarchicalRelationship | Optional relationship types and related users for scope selection. |
Interview, InterviewUser, assessment/signature state | Campaign eligibility and interview-progress monitoring. |
The dashboard aggregates existing records; it does not maintain a separate rating lifecycle. No dashboard-specific background aggregation job was found on the inspected request paths. Setup commands create feature catalogue/configuration entries; they are not evidence of a scheduled refresh process.
Session state includes filters, perimeter (managerIds, hierarchicalUsers, total) and treeStructure. The hierarchy cache stores the tree, nesting level, authenticated user ID and an expiry of time() + 600, with separate normal/company keys. The controller replaces the treeStructure value when caching a branch; do not assume both branches are always cached together. Services/Memcache.php exists, but the inspected tree path explicitly uses the session cache rather than that wrapper.
Observed in application: changing tabs retained the applied company-wide scope, while leaving and reopening the page returned to N - 1. Stored filter markers do not guarantee durable user preferences.
Calculations and data eligibility
Population and completion
getPerimeterSize counts distinct enabled employees with a Customer, within selected organizational IDs or relationship membership, excluding the signed-in user. It stores the result for the perimeter modal. It does not apply the skill queries' optional login filter.
Completion is recomputed via Profile::getActionCompletenessByUsers, rather than relying on the stored User.completeness field. The headline is the number of employees with completion at least 60%, divided by the perimeter, rounded to one decimal. Helper builds percentage bands from the profile service's completion-step count. The French UI says “more than 60%”, so the boundary wording differs from the implementation.
Job proficiency and gaps
getJobOrUserSkills builds job and employee skill maps. The employee query requires Userskill.source IS NULL; both maps optionally require logincounter > 0. It can restrict the selected job with jid and merges configured relationship users into scope.
TeamProficiency::jobMasteryRatio calculates, for skill IDs present in both maps:
percentage = 100 × sum(min(employee rating, required rating))
/ sum(required rating)
It returns zero when the summed required level is zero. Required levels for job skills absent from the employee map are not included in this denominator. Consequently, 100% on the matched subset is not proof of complete job-skill coverage. This is the inspected implementation, which is narrower than the explanatory text displayed in the page.
Per-user gap calculations iterate required job skills and use employee rating − required rating; a missing employee skill contributes 0 − required rating. The per-user mean divides by the number of required skills. Negative means indicate shortfalls. Job-level aggregates have their own implementation; do not assume every gap panel shares identical denominators or reconcile reports solely by their labels.
Skill analysis
getSkillMotivationAndMastery reads enabled users other than the signed-in user, null-source skill records and optional login eligibility. It skips a record when both calculated mastery and motivation are zero. It retains raw and job-capped mastery separately, and derives skill totals/averages for display. Not all drill-down values use the same capped/raw field.
The bubble chart uses mastery on X, motivation on Y and employee count for size. Its payload also supplies zero-sized default points from 0 through 5; accessibility-tree references to those points are not real employee measurements. Ranking paths separately order by totals or averages. dashboard_graph_unmastered_skills is present in seed configuration, but its TeamProficiency call is commented out.
Observed in application: a perimeter of 62 produced populated completion results but empty job/skill tables. Login filtering, source eligibility and missing job/skill records are code-supported explanations to investigate; the actual cause in PS Testing was Not verified.
Campaign monitoring
getActiveCampaings selects published interviews whose start date is in the past, scoped to eligible schools/participants. Admin classification and interview_all affect the query branch. It groups ongoing and ended campaigns, rather than filtering exclusively to active campaigns.
OneToOneController obtains the last session perimeter, filters enabled campaign participants to it and builds preparation, interview and signature indicators. Absent employees are separated. Indicators can overlap; they should not automatically be summed as mutually exclusive stages. The separate SkillsMapCampaign builder is for skills-declaration campaigns, not interview progress.
Exports, failure modes and observability
Dashboard.exportAsCsv reads DataTables rows with {filter: 'applied'}, strips markup, removes embedded semicolons from cell text and writes a UTF-8 BOM plus semicolon-separated content into a browser Blob. Download naming comes from the panel's text. This path exports the loaded filtered table, not a new server query across all employees. Chart export uses the separately loaded Highcharts modules.
| Outcome | Implementation / diagnostic |
|---|---|
| Non-XHR tab/detail/perimeter request | HTTP 400 Bad Request. Use the actual browser journey for reproduction. |
| No selected level or relationship | Tab handler returns HTTP 400 with a translated filter message. |
| Feature/access rejected | Tab handlers return 403; page/detail controllers can throw access-denied exceptions. |
| Unknown interview | Progress detail returns 404. |
| Valid empty perimeter | JSON with an empty-state partial and count; not necessarily an HTTP error. |
| Nonempty perimeter, empty chart | Inspect that chart's joins, login filter, source conditions, ratings and job requirements. |
| Recent hierarchy edit appears stale | Inspect the user's session-tree expiry and selected branch before diagnosing aggregation. |
Use application request/error logs and browser request status, tab and filter context to diagnose failures. No dedicated dashboard metrics, correlation scheme, automatic retry policy or monitoring job was established by this inspection. Avoid including employee names, contact information or raw export data in shared diagnostics.
Legacy, redesign and runtime dependencies
Observed in application: the current navigation hosted the legacy route. Verified in code: the feature's runtime logic is owned by smartmobility-legacy; targeted searches of smartmobility-nextgen-front/src and smartmobility-nextgen-back/src did not establish equivalent HR Dashboard routes or controllers. Nextgen People Review dashboard components implement another workflow and are not evidence of replacement.
aihub/src/templates/config_file.json did not identify a dashboard integration, and the traced legacy requests do not call AIHub. No AIHub scoring or AI matching dependency is established for these charts. This targeted check is not a claim that no other product surface can display AI data.
Runtime dependencies include an authenticated legacy Symfony session, Doctrine/database access, a valid user/company attachment, translation services and the front-end chart/table libraries loaded by the Twig page. The hierarchy needs session continuity; perimeter and interview details depend on prior population resolution. Deployment topology and production library versions were not verified from the live environment.
Security, privacy and extension points
Employee-level tables, profile links and exports contain personnel data. Keep authorization, tenant context and scope resolution on the server; hiding a tab/control is not sufficient evidence of endpoint enforcement. Validate both permitted and excluded populations when changing scope handling. Do not expose session identifiers, real employee records or credentials in API examples.
To extend this dashboard, follow the existing route → handler → population/eligibility → chart builder → Twig → browser initializer flow. Add the feature catalogue/default and access checks at the relevant layers. Preserve the distinction between tab permission, chart activation, hierarchy visibility and detail access. Keep calculation and export semantics explicit when adding a chart so similarly named panels do not silently acquire incompatible denominators. This documentation update changes none of those implementations.
Evidence and limitations
- Observed in application: 23 October 2026, Chrome, PS Testing 2026 as Jim Carter; manager dashboard's four tabs, empty and company-wide scopes, completion table, empty job/skill results, empty interview selector and denied company settings. No account switch, source-data edit or configuration save occurred.
- Verified in code: legacy
config/routes.yaml,config/configuration/{feature_module,default_permission}.yml;src/Company/ManagerDashboardBundle/{Controller,Chart,Services,Resources/views,Resources/public/js};src/Security/{FeatureSwitchVoter.php,Voter/BaseVoter.php};src/Service/Configuration/FeatureSwitchService.php; module editor template undersrc/Company/AccountBundle/Resources/views/Setting/module.html.twig. - Verified in code — targeted coexistence review: nextgen front
src/router/src/modules, nextgen backsrc/config, and AIHubsrc/templates/config_file.json. No application source repository was modified. - Inferred: the diagnostic ordering and extension guidance above follow the inspected flow; they are not deployed monitoring behavior.
- Not verified: deployed revision parity with local code, production topology, populated drill-downs/campaign statistics, actual download contents, all-account authorization coverage and the cause of the test tenant's empty skill datasets.
Functional-guide evidence
These implementation and product references support the functional guide. Local source behavior does not establish which revision or optional features are deployed. Application observations remain distinct from code and documentary evidence.
- Observed in application: 23 October 2026, Chrome, PS Testing 2026, Jim Carter, manager workspace; four tabs, N - 1 and company-wide filters, populated profile-completion table, empty skill/job datasets, empty interview campaign selector and denied company configuration. Six full-window screenshots support the three guides; the open filter menu and active tab styling identify the relevant controls.
- Verified in code:
smartmobility-legacy/src/Company/ManagerDashboardBundle/controllers,Chart/TeamProficiency.php,Services/Helper.php, templates andResources/public/js/dashboard.js; feature defaults inconfig/configuration/feature_module.yml. See the technical guide's source map. - Inferred: using these results to plan development conversations is recommended usage, not an automated platform decision.
- Not verified: the cause of the test tenant's empty skill results, populated drill-downs, downloads, privileged activation, campaign statistics and other accounts' effective access. No records or settings were changed; only dashboard view filters were applied.
Last updated on 3 November 2026