Portfolio summary implementation - #79
Conversation
…ry-merge # Conflicts: # cspell.json # package.json # pnpm-lock.yaml # src/app/modules/organizations/settings/salesforce-building-integration/salesforce-building-integration.component.ts
- Resolve merge conflicts with main (cspell.json, package.json,
pnpm-lock.yaml, salesforce-building-integration component) by keeping
main's newer Salesforce Building Integration implementation (BB
Salesforce support, barrel imports) under the PR's renamed path.
- Convert new API/component imports to the @seed/api and MaterialImports
barrels per project convention; register goal and salesforce-portfolio
in the @seed/api barrel index.
- Fix missing salesforce-login/salesforce-portfolio-integration .scss
files referenced by styleUrl (never committed) by dropping the
styleUrl instead.
- Add full Transloco translation coverage for all new UI strings across
the portfolio summary, configure-goals, add-cycle,
salesforce-portfolio-integration and salesforce-login templates.
- Fix several crashes found via live testing against a real backend:
- ConfigureGoalsDialogComponent.save() threw when Salesforce
integration was not enabled / no partner was selected.
- AddCycleDialogComponent.submit() threw when no annual report was
selected (Salesforce not enabled).
- PortfolioSummaryComponent.createChart() threw when a goal had no
weighted EUI data yet.
- PortfolioSummaryComponent.openAddCycle() threw because cycle_goals
was never initialized/fetched.
- Removed a stray {{ chart }} interpolation inside the results canvas
that rendered "[object Object]".
- Add GoalService.getCycleGoals() and call it when selecting/updating a
goal so previously created cycle goals load after a refresh (the
Goal API response never embeds cycle_goals).
- Remove leftover console.log debug statements.
Verified with pnpm lint / pnpm build, and by standing up a throwaway
Django backend (docker) with seeded data and exercising the goal
create/edit, add-cycle, and Salesforce settings flows via Playwright.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ry-merge # Conflicts: # public/i18n/en_US.json
- Remove an erroneous p-80 (320px) padding class on the CycleGoal side summary table, which was causing huge, uneven whitespace around that card. Drop the redundant duplicate p-10 alongside p-4 on the goal summary card for the same reason. - Add px-3 py-1 cell padding to the summary table so label/value columns aren't flush against each other. - Wrap the page content in the same content mx-5 my-2 container used by sibling insights pages (Program Overview, Property Insights, Default Reports) instead of leaving it unpadded/flush with the page edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Implements the Insights “Portfolio Summary” feature area, including goal configuration workflows, Salesforce portfolio sync UI/service plumbing, and related inventory-grid/action enhancements.
Changes:
- Added a full Portfolio Summary page (goal selection, cycle comparison, goal notes editing, exports) plus supporting dialogs (configure goals, add/edit cycle, bulk edit notes, sync-to-Salesforce review).
- Introduced new API clients/types for Goals and Salesforce Portfolio, and extended existing services (labels, inventory, audit-template, bb-salesforce config handling).
- Updated org settings routing/navigation for the Salesforce building integration page; added an OAuth callback route/component for Salesforce login.
Reviewed changes
Copilot reviewed 59 out of 64 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/app/modules/salesforce-login/salesforce-login.component.ts | New Salesforce OAuth callback component to exchange code for token and redirect. |
| src/app/modules/salesforce-login/salesforce-login.component.spec.ts | Adds a basic unit test for the Salesforce login callback component. |
| src/app/modules/salesforce-login/salesforce-login.component.html | Adds a translated “Logging in…” loading message. |
| src/app/modules/organizations/settings/settings.routes.ts | Renames Salesforce settings route to salesforce-building-integration and switches component. |
| src/app/modules/organizations/settings/settings.component.ts | Updates settings nav link to new Salesforce settings route. |
| src/app/modules/organizations/settings/salesforce-building-integration/salesforce-building-integration.component.ts | Renames/refactors Salesforce settings component and adjusts bb-salesforce config save behavior. |
| src/app/modules/organizations/settings/salesforce-building-integration/salesforce-building-integration.component.html | Minor formatting update to seed-page config binding. |
| src/app/modules/organizations/settings/salesforce-building-integration/modal/index.ts | Barrel export for Salesforce mapping modals. |
| src/app/modules/organizations/settings/salesforce-building-integration/modal/form-modal.component.ts | Adds create/edit Salesforce field mapping modal. |
| src/app/modules/organizations/settings/salesforce-building-integration/modal/form-modal.component.html | Template for Salesforce mapping create/edit modal. |
| src/app/modules/organizations/settings/salesforce-building-integration/modal/delete-modal.component.ts | Adds delete-confirm modal for Salesforce field mappings. |
| src/app/modules/organizations/settings/salesforce-building-integration/modal/delete-modal.component.html | Template for Salesforce mapping delete confirmation modal. |
| src/app/modules/inventory-list/summary/summary.component.ts | Formatting-only refactors in requestColumns and filter matcher logic. |
| src/app/modules/inventory-list/list/inventory.component.html | Passes cycles into grid actions component. |
| src/app/modules/inventory-list/list/grid/label-header.component.ts | New ag-grid header component for toggling label column expansion. |
| src/app/modules/inventory-list/list/grid/grid.component.ts | Adds label column expand/collapse behavior and new compact label rendering. |
| src/app/modules/inventory-list/list/grid/grid.component.html | Provides ag-grid context to support header-driven label toggling. |
| src/app/modules/inventory-list/list/grid/actions.component.ts | Adds audit-template export/import modals and Salesforce update action wiring. |
| src/app/modules/inventory-list/list/grid/actions.component.html | Enables Audit Template and Salesforce menu items conditionally and wires actions. |
| src/app/modules/inventory-list/list/actions/index.ts | Exports newly added audit-template action modals. |
| src/app/modules/inventory-list/list/actions/audit-template-import-modal.component.ts | New modal to import audit-template submissions into inventory with progress tracking. |
| src/app/modules/inventory-list/list/actions/audit-template-import-modal.component.html | Template for audit-template import modal. |
| src/app/modules/inventory-list/list/actions/audit-template-export-modal.component.ts | New modal to export selected properties to Audit Template with progress tracking. |
| src/app/modules/inventory-list/list/actions/audit-template-export-modal.component.html | Template for audit-template export modal. |
| src/app/modules/insights/portfolio-summary/sync-salesforce-dialog/sync-salesforce-dialog.component.ts | New dialog to compare SEED vs Salesforce and sync current/historical reports. |
| src/app/modules/insights/portfolio-summary/sync-salesforce-dialog/sync-salesforce-dialog.component.spec.ts | Comprehensive unit tests for sync-salesforce dialog logic. |
| src/app/modules/insights/portfolio-summary/sync-salesforce-dialog/sync-salesforce-dialog.component.html | Sync-to-Salesforce dialog UI (loading/error states, comparison tables, actions). |
| src/app/modules/insights/portfolio-summary/sync-salesforce-dialog/index.ts | Barrel export for sync-salesforce dialog. |
| src/app/modules/insights/portfolio-summary/portfolio-summary.types.ts | Adds Portfolio Summary dialog/context types. |
| src/app/modules/insights/portfolio-summary/portfolio-summary.component.spec.ts | Adds extensive unit test coverage for Portfolio Summary component logic. |
| src/app/modules/insights/portfolio-summary/portfolio-summary.component.html | Implements the full Portfolio Summary page UI and interactions. |
| src/app/modules/insights/portfolio-summary/portfolio-summary-label-header.component.ts | New label header for Portfolio Summary grids (expand/collapse per column). |
| src/app/modules/insights/portfolio-summary/portfolio-summary-header-menu.component.ts | New ag-grid header menu component using CDK Overlay for sort/pin/hide. |
| src/app/modules/insights/portfolio-summary/portfolio-summary-header-menu.component.html | Template for Portfolio Summary header menu overlay. |
| src/app/modules/insights/portfolio-summary/index.ts | Re-exports Portfolio Summary types in addition to component. |
| src/app/modules/insights/portfolio-summary/configure-goals-dialog/index.ts | Barrel export for configure-goals dialog. |
| src/app/modules/insights/portfolio-summary/configure-goals-dialog/configure-goals-dialog.component.ts | Adds goal creation/editing/deletion dialog (incl. Salesforce partner/goal association). |
| src/app/modules/insights/portfolio-summary/configure-goals-dialog/configure-goals-dialog.component.html | Template for goal configuration dialog. |
| src/app/modules/insights/portfolio-summary/bulk-edit-goal-notes-modal.component.ts | Adds bulk edit modal for goal notes fields across selected properties. |
| src/app/modules/insights/portfolio-summary/bulk-edit-goal-notes-modal.component.html | Template for bulk edit goal notes modal. |
| src/app/modules/insights/portfolio-summary/add-cycle-dialog/index.ts | Barrel export for add-cycle dialog. |
| src/app/modules/insights/portfolio-summary/add-cycle-dialog/add-cycle-dialog.component.ts | Adds add/edit cycle dialog (incl. optional Salesforce annual report association). |
| src/app/modules/insights/portfolio-summary/add-cycle-dialog/add-cycle-dialog.component.html | Template for add/edit cycle dialog. |
| src/app/modules/insights/facilities-plan/facilities-plan.component.spec.ts | Formatting-only changes in tests. |
| src/app/modules/datasets/pairing/pairing.component.ts | Formatting-only change to drag column definition. |
| src/app/app.routes.ts | Adds /salesforce-login route for Salesforce OAuth callback. |
| src/@seed/api/salesforce/salesforce.types.ts | Changes bb_salesforce_configs response shape type. |
| src/@seed/api/salesforce/bb-salesforce.service.ts | Updates bb-salesforce config parsing and update signature to match new API behavior. |
| src/@seed/api/salesforce-portfolio/salesforce-portfolio.types.ts | New types for Salesforce portfolio config/token/partners/annual reports endpoints. |
| src/@seed/api/salesforce-portfolio/salesforce-portfolio.service.ts | New service for Salesforce portfolio endpoints (login/token/partners/annual reports). |
| src/@seed/api/salesforce-portfolio/index.ts | Barrel export for Salesforce portfolio API package. |
| src/@seed/api/label/label.types.ts | Adds PropertyViewLabel type. |
| src/@seed/api/label/label.service.ts | Adds listByCycleGoal call for property view labels. |
| src/@seed/api/inventory/inventory.service.ts | Adds updateSalesforce endpoint wrapper for inventory updates. |
| src/@seed/api/index.ts | Exports new goal and salesforce-portfolio API packages. |
| src/@seed/api/goal/index.ts | Barrel export for new goal API package. |
| src/@seed/api/goal/goal.types.ts | Adds goal/cycle-goal/portfolio-summary/notes/salesforce-summary type definitions. |
| src/@seed/api/goal/goal.service.ts | Adds goal CRUD + portfolio summary + property list + Salesforce sync service methods. |
| src/@seed/api/audit-template/audit-template.service.ts | Adds batch export/import endpoints returning progress_key. |
| public/i18n/fr_CA.json | Adds translations for new Portfolio Summary and related UI strings. |
| public/i18n/es.json | Adds translations for new Portfolio Summary and related UI strings. |
| public/i18n/en_US.json | Adds new keys for Portfolio Summary UI; removes some existing keys. |
| .spelling.dic | Adds domain terms (e.g., kbtu, euis) to spelling dictionary. |
Suppressed comments (5)
src/app/modules/salesforce-login/salesforce-login.component.ts:26
- ngOnInit unconditionally calls getToken with params.code cast to string. If the callback is hit without a code (or with an error param), this will call the API with an invalid value. Guard against missing code and prefer org_id when available.
src/@seed/api/salesforce-portfolio/salesforce-portfolio.service.ts:75 - getAnnualReports is typed as getPartnersResponse (SalesforcePartner[]), but the endpoint is for Annual Reports. This will cause type errors at call sites (and can mask real response-shape issues). Use getAnnualReportsResponse for both the return type and HttpClient generic (and consider a more specific error message).
getAnnualReports(goalId: number): Observable<getPartnersResponse> {
const url = `/api/v3/bb_salesforce/annual_report/?organization_id=${this.orgId}&goal_id=${goalId}`
return this._httpClient.get<getPartnersResponse>(url).pipe(
map((response) => response),
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, `Error fetching summary: ${error.message}`)
}),
)
src/@seed/api/goal/goal.service.ts:80
- editedGoal is implicitly typed as any here. With noImplicitAny enabled this will fail to compile; even without it, it defeats type safety for payload construction. Type it as a Partial (or a narrower payload type) to keep API calls typed.
editGoal(goalId: number, editedGoal, orgId: number): Observable<Goal> {
src/@seed/api/goal/goal.service.ts:89
- newGoal is implicitly typed as any here. With noImplicitAny enabled this will fail to compile; type it as Partial (or a dedicated create payload type) to keep this call type-safe.
createGoal(newGoal, orgId: number): Observable<Goal> {
public/i18n/en_US.json:74
- Several i18n keys were removed from en_US.json but are still referenced in code (e.g., inventory-create.component.ts translates 'Access Level Instance is required', 'Cycle is required', and 'Duplicate columns are not allowed'). Removing these keys will cause missing-translation behavior in the UI. Please restore the removed keys (or update the callers to use the new keys).
"Access Level": "Access Level",
"Access Level Instance": "Access Level Instance",
"Access Level Instance Information": "Access Level Instance Information",
"Access Level Instance Tree": "Access Level Instance Tree",
"Access Level Instance:": "Access Level Instance:",
"Access Level Instances": "Access Level Instances",
"Access Level Instances Errors": "Access Level Instances Errors",
"Access Level Tree": "Access Level Tree",
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: kflemin <2205659+kflemin@users.noreply.github.com>
…spose overlay and unsubscribe Co-authored-by: kflemin <2205659+kflemin@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 59 out of 64 changed files in this pull request and generated 1 comment.
Suppressed comments (13)
src/app/modules/salesforce-login/salesforce-login.component.ts:28
Router.navigateis using a relative navigation array (no leading/), which will append to the currentsalesforce-loginroute. Alsoparams.code as stringwill passundefinedwhen the query param is missing. Guard against a missingcodeand navigate to an absolute path (this also makes use of the currently-unusedEMPTYimport).
src/app/modules/inventory-list/list/grid/grid.component.ts:280- The collapsed label “color bar” uses hard-coded hex colors via
LABEL_COLOR_MAP. The codebase already defines label colors via the global.labeltheme classes (src/styles/styles.scss:129-161), so hard-coding hex values here risks theme drift. You can build the mini-bars by applying the existing.labelcolor classes and overriding padding/margins inline.
src/app/modules/inventory-list/list/grid/grid.component.ts:20 LABEL_COLOR_MAPhard-codes hex colors (and will be unused if the renderer uses.labelclasses). Since label coloring is already centralized insrc/styles/styles.scssunder.label, removing this constant avoids theme drift and prevents an unused-const lint failure.
src/@seed/api/salesforce-portfolio/salesforce-portfolio.service.ts:73getAnnualReportshas the wrong response type (getPartnersResponse). This makes the method unsafe to consume and currently leaves the importedgetAnnualReportsResponseunused. The endpoint returns{ status, results: AnnualReport[] }, so the method should usegetAnnualReportsResponsefor both the Observable and the HttpClient generic.
getAnnualReports(goalId: number): Observable<getPartnersResponse> {
const url = `/api/v3/bb_salesforce/annual_report/?organization_id=${this.orgId}&goal_id=${goalId}`
return this._httpClient.get<getPartnersResponse>(url).pipe(
map((response) => response),
catchError((error: HttpErrorResponse) => {
src/app/modules/inventory-list/list/actions/audit-template-import-modal.component.ts:41
- The progress polling subscription created in
onSubmit()is not tied to the dialog lifecycle. If the user closes the modal while polling is in progress, the polling can continue in the background. AddtakeUntil(this._unsubscribeAll$)to cancel it on destroy.
src/app/modules/inventory-list/list/actions/audit-template-export-modal.component.ts:34 - The export progress polling subscription is not cancelled on dialog destroy, so it can continue running after the modal is closed. Add
takeUntil(this._unsubscribeAll$)to the polling chain.
src/app/modules/inventory-list/list/grid/actions.component.ts:56 _auditTemplateServiceis injected but never used, which will trigger an unused private member lint error.
src/app/modules/inventory-list/summary/summary.component.ts:223- The assignment formatting here (
const requestColumnswith=on the next line) is inconsistent with the surrounding code and is likely to fail Prettier formatting checks. Reformat the ternary to the standard multi-line style used elsewhere in this file.
src/app/modules/inventory-list/summary/summary.component.ts:1018 - This assignment formatting (
const valueTextwith=on the next line) is inconsistent with the file’s existing style and likely to be rewritten by Prettier. Reformat to the usual multi-line ternary layout for readability and to avoid formatting churn.
src/@seed/api/goal/goal.service.ts:96 editedGoalandnewGoalare implicitlyany, which will fail under the repo’s TypeScript strictness/linting. Type these payload parameters (e.g.,Partial<Goal>), since callers pass partial update/create payloads.
editGoal(goalId: number, editedGoal, orgId: number): Observable<Goal> {
const url = `/api/v3/goals/${goalId}/?organization_id=${orgId}`
return this._httpClient.put<Goal>(url, editedGoal).pipe(
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, `Error fetching summary: ${error.message}`)
src/app/modules/inventory-list/list/actions/audit-template-import-modal.component.ts:6
_unsubscribeAll$is defined and completed inngOnDestroy, but the observable chain inonSubmit()doesn’t use it. ImporttakeUntilso the long-running progress polling can be cancelled if the dialog is closed/destroyed.
This issue also appears on line 37 of the same file.
src/app/modules/inventory-list/list/actions/audit-template-export-modal.component.ts:5
- Like the import modal, this dialog defines
_unsubscribeAll$but doesn’t use it in the polling observable chain. ImporttakeUntilso the export progress polling can be cancelled when the dialog is destroyed.
This issue also appears on line 30 of the same file.
src/app/modules/inventory-list/list/grid/actions.component.ts:8
AuditTemplateServiceis imported but not used in this component (and_auditTemplateServiceis never referenced). This will fail linting under unused-import/member rules.
This issue also appears on line 56 of the same file.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
No description provided.