feat(org-lens): wire org projects page to live data - #1065
Conversation
There was a problem hiding this comment.
Pull request overview
Wires the Org Lens → Projects page from demo fixtures to live BFF endpoints backed by Snowflake + member-service workspace APIs, and updates the UI to handle real loading/error/empty states with improved add-project UX.
Changes:
- Introduces new org-projects BFF routes (projects list, project search, workspace CRUD, workspace membership updates) with Snowflake + member-service integrations and Valkey caching.
- Updates the Org Projects Angular page to call live APIs, support workspace management, and add richer empty/error states and trend UI.
- Removes demo-data fixtures, extends shared contracts/constants, and expands Playwright coverage for stubbed API flows.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/shared/src/interfaces/org-lens-projects.interface.ts | Extends shared contracts for live project/search/workspace APIs and UI view-model fields. |
| packages/shared/src/constants/org-lens-projects.constants.ts | Adds new Org Projects constants (workspace defaults, search limits, Insights/warehouse-related constants). |
| apps/lfx-one/src/styles.scss | Adds a dedicated tooltip style for the trend tooltip. |
| apps/lfx-one/src/server/services/org-lens-projects.service.ts | New server-side service for Snowflake project rows, CDP/Insights enrichment, and member-service workspace operations. |
| apps/lfx-one/src/server/routes/orgs.route.ts | Registers new /lens/projects and /lens/workspaces routes. |
| apps/lfx-one/src/server/controllers/org-lens-projects.controller.ts | New controller implementing org projects/workspaces HTTP endpoints. |
| apps/lfx-one/src/app/shared/services/org-lens-projects.service.ts | Replaces demo-data service with HttpClient-backed calls to the new BFF routes. |
| apps/lfx-one/src/app/shared/services/org-lens-projects.demo-data.ts | Removes demo fixtures now that live APIs are wired. |
| apps/lfx-one/src/app/shared/components/multi-select/multi-select.component.ts | Extends multi-select wrapper with filterBy/panelStyleClass/scrollHeight and emits filter events. |
| apps/lfx-one/src/app/shared/components/multi-select/multi-select.component.scss | Adds panel styling hook for the Org Projects add-projects multi-select. |
| apps/lfx-one/src/app/shared/components/multi-select/multi-select.component.html | Wires new inputs/outputs through to PrimeNG MultiSelect. |
| apps/lfx-one/src/app/modules/dashboards/org/org-projects/org-projects.component.ts | Updates Org Projects page logic for live data, workspace CRUD, async search, and richer row view-models. |
| apps/lfx-one/src/app/modules/dashboards/org/org-projects/org-projects.component.html | Updates UI for loading/no-access/error/empty states, workspace dialogs, and add-project flow. |
| apps/lfx-one/e2e/org-projects.spec.ts | Expands Playwright tests for API-stubbed flows and new UI states. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Replace client-side demo data with BFF routes that hydrate project rows from platinum Snowflake models and workspace CRUD from member-service. - Add org-lens-projects controller and service with workspace/search endpoints - Update org-projects UI for loading, no-access, and error states - Extend multi-select for async search race-safety and dialog error surfaces - Remove org-lens-projects.demo-data.ts; expand Playwright coverage Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
- Correct double-negative bulk-add failure message - Seed default e2e workspace with project slugs so /lens/projects is called - Document trendTooltipHtml XSS constraint on the shared interface Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
fd954af to
d7d2a79
Compare
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughOrg Projects moves from static demo data to a fully backend-driven implementation. New shared contracts and constants support project search, workspace CRUD, and CDP health enrichment. A server controller/routes/service integrate Snowflake and member-service. The client service, component, and template are reworked around async signals and dialog states. Multi-select and e2e tests are extended accordingly. ChangesOrg Projects Workspace Backend Integration
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant OrgProjectsComponent
participant OrgLensProjectsService as ClientService
participant OrgLensProjectsController
participant OrgLensProjectsService_S as ServerService
participant MemberService
OrgProjectsComponent->>ClientService: getWorkspaces(orgUid)
ClientService->>OrgLensProjectsController: GET /lens/workspaces
OrgLensProjectsController->>ServerService: getWorkspaces(req, accountId)
ServerService->>MemberService: fetch/bootstrap workspace metadata
MemberService-->>ServerService: workspace resources
ServerService-->>OrgLensProjectsController: OrgProjectsWorkspacesResponse
OrgLensProjectsController-->>ClientService: workspaces JSON
ClientService-->>OrgProjectsComponent: workspaces signal updated
OrgProjectsComponent->>ClientService: confirmAddProjects(slugs)
ClientService->>OrgLensProjectsController: POST /lens/workspaces/:id/projects
OrgLensProjectsController->>ServerService: addProjectsToWorkspace(workspaceId, slugs)
ServerService->>MemberService: bulk add project slugs
MemberService-->>ServerService: succeeded/failed slugs
ServerService-->>OrgLensProjectsController: updated OrgProjectsWorkspace
OrgLensProjectsController-->>ClientService: 200 workspace JSON
ClientService-->>OrgProjectsComponent: merge workspace, trigger reload
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
apps/lfx-one/src/app/shared/services/org-lens-projects.service.ts (1)
15-21: 🚀 Performance & Scalability | 🔵 Trivial
slugsquery param may grow unbounded for large workspaces.
getProjectsserializes the fullworkspace.projectSlugslist into a comma-joined query string. For workspaces with many projects this can approach browser/proxy URL-length limits (~2–8 KB) and silently truncate or 414. Since the backendgetProjectsalready validates the array server-side, consider moving the slug list into a request body (e.g., POST/lens/projectswith{ slugs }) if workspaces are expected to hold large project counts.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/lfx-one/src/app/shared/services/org-lens-projects.service.ts` around lines 15 - 21, The current getProjects method in OrgLensProjectsService sends every slug through the query string, which can hit URL length limits for large workspaces. Update this API call to avoid serializing workspace.projectSlugs into HttpParams, and instead move the slug list into a request body (for example, switch the call in getProjects to POST while keeping orgUid/orgName context) so large slug sets can be handled safely.apps/lfx-one/src/server/services/org-lens-projects.service.ts (2)
547-562: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winFixed 1s retry delay can add multi-second latency to write-triggering requests.
fetchWorkspaceProjectSlugsWithRetryblocks for up to(attempts - 1) * 1000ms on the request thread when the query-service index hasn't caught up yet (used bygetWorkspaces,renameWorkspace,addProjectsToWorkspace). Consider a shorter initial delay with backoff, or making this async/non-blocking to the caller where the eventual value isn't immediately needed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/lfx-one/src/server/services/org-lens-projects.service.ts` around lines 547 - 562, The retry loop in fetchWorkspaceProjectSlugsWithRetry currently sleeps a fixed 1s between attempts, which can add unnecessary latency to callers like getWorkspaces, renameWorkspace, and addProjectsToWorkspace. Update this helper to use a shorter initial delay with backoff or otherwise avoid blocking the request path when the slugs are not immediately required, while keeping the retry behavior in fetchWorkspaceProjectSlugs and fetchWorkspaceProjectSlugsWithRetry intact.
182-275: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated error-construction logic across four branches.
The
WORKSPACE_PROJECTS_ADD_PARTIAL/WORKSPACE_PROJECTS_ADD_FAILEDMicroserviceErrorblocks are nearly identical and repeated 4 times (inside the loop's catch, inside the loop'schunkMissingcheck, after the loop'smissingRequestedcheck, and the finalresponseSlugscheck). Note the post-loopmissingRequestedcheck (Line 243) also appears unreachable, since anychunkMissingalready throws inside the loop before it can be reached.♻️ Extract a shared error-throwing helper
+ private throwWorkspaceAddError(accountId: string, workspaceId: string, latestResponse: unknown, partialSlugs: string[]): never { + if (partialSlugs.length > 0) { + throw new MicroserviceError('Some of the selected projects could not be added to this workspace.', 400, 'WORKSPACE_PROJECTS_ADD_PARTIAL', { + operation: 'add_org_lens_workspace_projects', + service: 'LFX_V2_MEMBER_SERVICE', + path: `/b2b_orgs/${accountId}/workspaces/${workspaceId}/projects/bulk`, + errorBody: { response: latestResponse, partialSlugs }, + }); + } + throw new MicroserviceError('None of the selected projects could be added to this workspace.', 400, 'WORKSPACE_PROJECTS_ADD_FAILED', { + operation: 'add_org_lens_workspace_projects', + service: 'LFX_V2_MEMBER_SERVICE', + path: `/b2b_orgs/${accountId}/workspaces/${workspaceId}/projects/bulk`, + errorBody: latestResponse, + }); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/lfx-one/src/server/services/org-lens-projects.service.ts` around lines 182 - 275, The addProjectsToWorkspace flow repeats the same WORKSPACE_PROJECTS_ADD_PARTIAL/WORKSPACE_PROJECTS_ADD_FAILED MicroserviceError construction in multiple branches, making the logic hard to maintain. Extract a shared helper in OrgLensProjectsService (or a private method near addProjectsToWorkspace) that accepts the current context and throws the appropriate partial/failed MicroserviceError, then replace the duplicate blocks in the chunk catch, chunkMissing handling, missingRequested handling, and final responseSlugs check with calls to that helper. While doing so, review the missingRequested branch in addProjectsToWorkspace because it appears unreachable after the per-chunk chunkMissing throw and should be removed or consolidated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/lfx-one/src/server/services/org-lens-projects.service.ts`:
- Around line 365-397: The CDP fetch in fetchCdpProjects currently calls the
project API without auth, causing it to fail silently and return empty results.
Update the fetch request to include the same bearer Authorization header pattern
used elsewhere in org-lens-projects.service.ts, or otherwise propagate/log the
failure instead of swallowing it in the catch block. Keep the existing batch
logic and response parsing, but ensure the request is authenticated and failures
are visible.
- Around line 103-135: The default-workspace bootstrap path in getWorkspaces and
bootstrapDefaultWorkspace is still race-prone, so concurrent first loads can
create multiple default workspaces and deduplicateDefaultWorkspaces only hides
the duplicates after the fact. Make the bootstrap idempotent by using an atomic
create-or-return flow or a per-account lock in bootstrapDefaultWorkspace, and
ensure getWorkspaces reuses the same canonical workspace rather than relying on
query order; if duplicates already exist, reconcile them explicitly instead of
just filtering them out.
- Around line 590-645: The issue is that ensureDefaultWorkspaceProjects uses
fetchMemberServiceWorkspaceSlugs as if it were a read-only probe, but that
helper POSTs to /projects/bulk and can mutate workspace membership. Update the
flow in ensureDefaultWorkspaceProjects to either use a truly read-only
membership lookup for the probe path, or explicitly rename/document
fetchMemberServiceWorkspaceSlugs (and its call sites) to make the write side
effect clear; keep the existing probeSlug logic aligned with the intended
non-mutating vs mutating behavior.
In `@packages/shared/src/interfaces/org-lens-projects.interface.ts`:
- Around line 204-271: Move the server-only mapping types out of the shared
interfaces file and into org-lens-projects.service.ts, since OrgLensProjectRow,
OrgLensProjectPersonRow, OrgProjectsWorkspaceResource,
OrgProjectsWorkspaceProjectResource, OrgProjectsMemberServiceWorkspaceProject,
OrgProjectsCdpProject, OrgProjectsCdpHealthScore, and
OrgProjectsCdpProjectListResponse are raw/internal inputs used only by the
service. Keep the shared package limited to true BFF/client wire contracts, and
update org-lens-projects.service.ts to define or import these types locally so
the service remains self-contained.
---
Nitpick comments:
In `@apps/lfx-one/src/app/shared/services/org-lens-projects.service.ts`:
- Around line 15-21: The current getProjects method in OrgLensProjectsService
sends every slug through the query string, which can hit URL length limits for
large workspaces. Update this API call to avoid serializing
workspace.projectSlugs into HttpParams, and instead move the slug list into a
request body (for example, switch the call in getProjects to POST while keeping
orgUid/orgName context) so large slug sets can be handled safely.
In `@apps/lfx-one/src/server/services/org-lens-projects.service.ts`:
- Around line 547-562: The retry loop in fetchWorkspaceProjectSlugsWithRetry
currently sleeps a fixed 1s between attempts, which can add unnecessary latency
to callers like getWorkspaces, renameWorkspace, and addProjectsToWorkspace.
Update this helper to use a shorter initial delay with backoff or otherwise
avoid blocking the request path when the slugs are not immediately required,
while keeping the retry behavior in fetchWorkspaceProjectSlugs and
fetchWorkspaceProjectSlugsWithRetry intact.
- Around line 182-275: The addProjectsToWorkspace flow repeats the same
WORKSPACE_PROJECTS_ADD_PARTIAL/WORKSPACE_PROJECTS_ADD_FAILED MicroserviceError
construction in multiple branches, making the logic hard to maintain. Extract a
shared helper in OrgLensProjectsService (or a private method near
addProjectsToWorkspace) that accepts the current context and throws the
appropriate partial/failed MicroserviceError, then replace the duplicate blocks
in the chunk catch, chunkMissing handling, missingRequested handling, and final
responseSlugs check with calls to that helper. While doing so, review the
missingRequested branch in addProjectsToWorkspace because it appears unreachable
after the per-chunk chunkMissing throw and should be removed or consolidated.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2bd017c2-b3d4-4cb5-9c7b-fb6496741e10
📒 Files selected for processing (14)
apps/lfx-one/e2e/org-projects.spec.tsapps/lfx-one/src/app/modules/dashboards/org/org-projects/org-projects.component.htmlapps/lfx-one/src/app/modules/dashboards/org/org-projects/org-projects.component.tsapps/lfx-one/src/app/shared/components/multi-select/multi-select.component.htmlapps/lfx-one/src/app/shared/components/multi-select/multi-select.component.scssapps/lfx-one/src/app/shared/components/multi-select/multi-select.component.tsapps/lfx-one/src/app/shared/services/org-lens-projects.demo-data.tsapps/lfx-one/src/app/shared/services/org-lens-projects.service.tsapps/lfx-one/src/server/controllers/org-lens-projects.controller.tsapps/lfx-one/src/server/routes/orgs.route.tsapps/lfx-one/src/server/services/org-lens-projects.service.tsapps/lfx-one/src/styles.scsspackages/shared/src/constants/org-lens-projects.constants.tspackages/shared/src/interfaces/org-lens-projects.interface.ts
💤 Files with no reviewable changes (1)
- apps/lfx-one/src/app/shared/services/org-lens-projects.demo-data.ts
- Sort normalized slug lists in parseSlugList for stable cache signatures - Use ORG_PROJECTS_SEARCH_MIN_LENGTH in add-project search gating and copy Reviewer feedback: PR #1065, Copilot round 2 (3 threads). Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
Rename fetchMemberServiceWorkspaceSlugs to probeWorkspaceMembershipViaBulkUpsert and document the intentional write used when query-service reads lag. Reviewer feedback: PR #1065, CodeRabbit thread on membership probe. Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/lfx-one/src/app/modules/dashboards/org/org-projects/org-projects.component.ts (1)
531-556: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winInvalidate in-flight searches as soon as the input changes.
Line 555 increments the request id only when the debounced request starts. If an older request resolves during the 300ms debounce window for a newer query, it can still publish stale options for the current input.
Proposed fix
- protected searchAddableProjects(query: string): void { + protected searchAddableProjects(query: string): void { + const requestId = ++this.addableProjectsSearchRequestId; this.addProjectsSearchQuery.set(query); if (this.addableProjectsSearchDebounceTimer) { clearTimeout(this.addableProjectsSearchDebounceTimer); } this.addableProjectsSearchDebounceTimer = setTimeout(() => { - void this.runAddableProjectsSearch(query); + void this.runAddableProjectsSearch(query, requestId); }, 300); } - private async runAddableProjectsSearch(query: string): Promise<void> { + private async runAddableProjectsSearch(query: string, requestId: number): Promise<void> { const account = this.accountContext.selectedAccount(); const trimmed = query.trim(); - const requestId = ++this.addableProjectsSearchRequestId;Also applies to: 568-580
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/lfx-one/src/app/modules/dashboards/org/org-projects/org-projects.component.ts` around lines 531 - 556, Invalidate stale addable-project searches immediately when the query changes, not only when runAddableProjectsSearch starts. Update searchAddableProjects and the request-id guard in runAddableProjectsSearch so each new input bumps the in-flight request token before the 300ms debounce delay, preventing older results from publishing over newer queries. Use the existing addableProjectsSearchRequestId and addableProjectsSearchDebounceTimer symbols to locate the change, and keep the final request-id check around the async response handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In
`@apps/lfx-one/src/app/modules/dashboards/org/org-projects/org-projects.component.ts`:
- Around line 531-556: Invalidate stale addable-project searches immediately
when the query changes, not only when runAddableProjectsSearch starts. Update
searchAddableProjects and the request-id guard in runAddableProjectsSearch so
each new input bumps the in-flight request token before the 300ms debounce
delay, preventing older results from publishing over newer queries. Use the
existing addableProjectsSearchRequestId and addableProjectsSearchDebounceTimer
symbols to locate the change, and keep the final request-id check around the
async response handling.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4dc61b71-19bc-43c4-aed3-f9d2eac345f5
📒 Files selected for processing (2)
apps/lfx-one/src/app/modules/dashboards/org/org-projects/org-projects.component.tsapps/lfx-one/src/server/controllers/org-lens-projects.controller.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/lfx-one/src/server/controllers/org-lens-projects.controller.ts
Do not persist degraded health payloads when the Insights project API errors; retry on the next request instead of caching for the full hour. Reviewer feedback: PR #1065, Copilot round 3 (CDP cache thread). Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
audigregorie
left a comment
There was a problem hiding this comment.
Code Review Summary
Solid integration pass: three-file BFF wiring, thoughtful workspace bootstrap/retry logic, loading/error/empty states, and expanded Playwright coverage. Remaining findings are mostly convention gaps and one UX guard on the default workspace.
Several Copilot/CodeRabbit threads from earlier commits are already addressed (cache signature sort, search min-length constant, CDP cache skip, probe JSDoc).
Major — outside the diff
- Inline
<p-dialog>(checklist #3): Both workspace and add-project dialogs still use template<p-dialog>(pre-existing markup, unchanged hunks). PreferDialogService.open()with extracted dialog components, matchingadd-access-user-modal/key-contactspatterns. - Default workspace delete: The settings dialog still exposes Delete workspace for the canonical All Projects with Activities preset (
isCanonicalDefaultWorkspaceis only used for empty-state copy). Hide/disable delete for that workspace or block indeleteWorkspace()before calling the API. - Frontend GET error logging: Component-level
catchError/catchpaths flip signals but never log (checklist 14.6).
What's done well
- Workspace/project fetch pipeline with separate workspace vs. project error surfaces and targeted retry.
- Async add-project search uses request IDs + debounce to avoid race conditions.
- Backend skips Valkey caching when CDP enrichment fails so degraded health scores are not frozen for an hour.
- Replace raw search input with lfx-input-text bound to addProjectsForm - Move org-projects multi-select panel styles out of shared component - Log catchError and CDP enrichment failures before graceful fallbacks - Remove dead defaultOrgProjectsWorkspace static; stable @for track keys Reviewer feedback: PR #1065, audigregorie review (7 threads). Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
Reviewer feedback: PR #1065, Copilot — removeProjectFromWorkspace DELETE now passes X-Sync: true like the other member-service write paths. Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
Serve health scores and descriptions from ANALYTICS.PLATINUM_LFX_ONE ORG_LENS_PROJECTS instead of the Insights CDP API. Removes the CDP batch enrichment path and cache-skip-on-enrichment-failure logic now that lf-dbt ships these columns (PR #2588). Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
MRashad26
left a comment
There was a problem hiding this comment.
Code Review — PR #1065 feat(org-lens): wire org projects page to live data
Overview
Large integration PR (2588+/995−, 14 files). Key changes:
- New BFF controller + service:
org-lens-projects.{controller,service}.ts— 8 REST endpoints (projects list, search, workspace CRUD + add/remove project) - Angular
OrgProjectsComponentwired to live data: replaces demo-data fixture with realOrgLensProjectsServicecalls; adds access-guard states (no-org-access, no-company, loading skeleton), async project-search dialog with race-safety, and workspace save/delete with full error handling lfx-multi-selectextended withfilterBy,panelStyleClass,scrollHeightinputs andfilterChangeoutput for async searchOrgLensProjectsService(Angular) gainsgetWorkspaces,createWorkspace,renameWorkspace,deleteWorkspace,addProjectsToWorkspace,removeProjectFromWorkspace,searchProjects- Demo-data file deleted; Playwright specs updated for API-stubbed flows
Secrets / critical-constants check ✅
No credentials, tokens, or secrets. DEFAULT_LFX_ONE_PLATINUM_SCHEMA = 'ANALYTICS.PLATINUM_LFX_ONE' is a Snowflake schema name used server-side only; schema names are not credentials (actual Snowflake auth is environment-variable gated). Consistent with existing BFF constants pattern (e.g. VALKEY_CACHE).
Code-standards audit
| # | Severity | Finding |
|---|---|---|
| 1 | 🔵 Info | tableEmptyState() read 5× in consecutive property bindings — @let would compute once |
Angular 20 patterns audit
- No function calls in templates: all bindings use signal reads (
sortIconMap(),loading(), etc.) or property accesses on the table-row view model.@forloops track oniandbar.x— no method calls. ✅ - No
effect(): reactive state wired viatoSignal(toObservable(...).pipe(...)),subscribe()in constructor withtakeUntilDestroyed(). ✅ - No
@ViewChild/@ViewChildren: not in imports or class. ✅ initX()pattern: privateinitSortField/SortIconMap/AriaSortMap/PageSize/PageFirst/SelectedWorkspaceId/TableEmptyState/WorkspaceDialogErrorMessageall called exclusively insidecomputed()— not in the template. ✅- Type and constant placement: new types (
OrgProjectsAriaSort,OrgProjectsEmptyState,AddableProjectOption, Snowflake row shapes, member-service shapes) inorg-lens-projects.interface.ts; new runtime constants (INFLUENCE_TREND_TEXT_CLASS,INFLUENCE_TREND_ARROW_BADGE_CLASS,INFLUENCE_TREND_ARROW_ICON,ORG_PROJECTS_SEARCH_MIN_LENGTH, etc.) inorg-lens-projects.constants.tswith noexport type. ✅ HealthScore: extended with'unavailable';HEALTH_SCORE_LABELS+HEALTH_SCORE_SEVERITYrecords updated to match — no missed cases. ✅
BFF security notes
assertOrgUid()called on all endpoints before service invocation. ✅readRequiredStringBody/readStringArrayBodytype-check all body fields before use. ✅parseSlugListlowercases, deduplicates, and sorts the slug list — normalises user-supplied input before it reaches SQL/service. ✅- All mutations return
Cache-Control: no-store. ✅ removeProjectFromWorkspacevalidates the:slugpath param explicitly. ✅
Code quality notes
- Race-safe project search via
addableProjectsSearchRequestIdcounter — stale responses from superseded requests are silently discarded before updating state. ✅ orgUid$observable withdistinctUntilChanged()+skip(1)closes dialogs on org-context switch without re-triggering on the initial emission. ✅isStillSelectedAccount(accountUid)guard in all async actions prevents stale mutations from updating state if the org changed mid-flight. ✅concat(of(null), serviceCall$)insideinitResponseemitsnullfirst (clears stale rows) then the real response — avoids briefly showing the previous workspace's rows for the new workspace. ✅trendTooltipHtmlrendered with[escape]="false"and annotated as component-authored only — the interface-level comment was strengthened to emphasize it must never be sourced from the wire. ✅
Verdict: PASS ✅ (1 info)
One @let cleanup suggested inline; no critical or warning findings.
Bind the org-projects empty-state block to a single @let-hoisted tableEmptyState() read instead of evaluating the computed signal in each of the six bindings. Reviewer feedback: PR #1065, MRashad26 thread on repeated computed reads. Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
The influence trend text and badge color classes live in
INFLUENCE_TREND_TEXT_CLASS / INFLUENCE_TREND_ARROW_BADGE_CLASS in
@lfx-one/shared, which is outside the Tailwind content scan
(./src/**/*.{html,ts}). Safelist text-emerald-600, text-red-600,
bg-emerald-100, bg-red-100, and bg-gray-100 so they survive JIT
purge, matching the existing fill-* signal-bar safelist convention.
Reviewer feedback: PR #1065, Copilot thread on unsafelisted shared trend classes.
Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org>
…ration Signed-off-by: ahmedomosanya <aopeyemi@contractor.linuxfoundation.org> # Conflicts: # apps/lfx-one/src/server/routes/orgs.route.ts
eac58ab
Summary
org-lens-projectsBFF controller and service: project rows from Snowflake, workspace CRUD and project search from member-service/CDPlfx-multi-selectfor async search race-safety and inline dialog errorsorg-lens-projects.demo-data.ts; expand Playwright coverage for API stubbed flows