Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f416f65
initial optimization pass
kmandryk Apr 16, 2025
e681ff9
refactor: add loading icon for user page
kmandryk Apr 17, 2025
817830d
refactor: table virtualization
kmandryk Apr 23, 2025
a68f357
Merge branch 'development' into refactor/optimize-page-loading
alex-struk Jul 21, 2025
1487d80
fix: restored same columns as in original code
alex-struk Jul 21, 2025
6b9d64a
feat: added search feature
alex-struk Jul 21, 2025
c0c2530
refactor: moved VirtualizedTable to separate file, aligned with front…
alex-struk Jul 21, 2025
d7c3c4a
refactor: standardized new component, fixed scrolling after search
alex-struk Jul 21, 2025
e088e22
fix: sonarqube fixes
alex-struk Jul 23, 2025
1262b53
fix: development branch merge conflict fixes
alex-struk Jul 23, 2025
222373f
fix: import fixes
alex-struk Jul 23, 2025
1adca67
refactor: created a re-usable virtualized-table component
alex-struk Jul 24, 2025
787b91e
refactor: eliminated the use of a separate table object for the purpo…
alex-struk Jul 24, 2025
81d2538
refactor: fixed magic numbers, removed tooltips, fixed scroll reset, …
alex-struk Jul 24, 2025
19a34d1
refactor: clean up to align with orignal code
alex-struk Jul 24, 2025
5787767
refactor: clean up
alex-struk Jul 24, 2025
0d425c7
refactor: minor reversion
alex-struk Jul 24, 2025
bf9e200
refactor: function rename
alex-struk Jul 24, 2025
dffb4a9
refactor: cleanup
alex-struk Jul 24, 2025
f472d36
refactor: removed td tooltip logic
alex-struk Jul 24, 2025
d6b9c7e
refactor: minor alignment to framework
alex-struk Jul 24, 2025
990b62d
refactor: removed null from ADT definitions
alex-struk Jul 28, 2025
cdd436a
refactor: removed resetScroll event and replaced logic with initializ…
alex-struk Jul 28, 2025
7475bf2
refactor: removed unnecessary argument from connection.raw calls
alex-struk Jul 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 29 additions & 20 deletions src/back-end/lib/db/opportunity/code-with-us.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,28 +308,37 @@ export function generateCWUOpportunityQuery(
const query: Knex.QueryBuilder = connection<RawCWUOpportunity>(
"cwuOpportunities as opp"
)
/**
* Performance optimization: Replaced correlated subqueries with DISTINCT ON window functions.
*
* The previous approach used max(createdAt) subqueries that executed once per opportunity record,
* resulting in O(n²) complexity. This new approach uses PostgreSQL's DISTINCT ON to scan each
* table once, sort by (opportunity, createdAt DESC), and select the first (most recent) record
* per opportunity group. This reduces complexity to O(n log n) and typically provides 10-100x
* performance improvement on large datasets by enabling better index utilization and join optimization.
*/

// Join on latest CWU status
.join<RawCWUOpportunity>("cwuOpportunityStatuses as stat", function () {
this.on("opp.id", "=", "stat.opportunity").andOn(
"stat.createdAt",
"=",
connection.raw(
'(select max("createdAt") from "cwuOpportunityStatuses" as stat2 where \
stat2.opportunity = opp.id and stat2.status is not null)'
)
);
})
.join(
connection.raw(
`(SELECT DISTINCT ON (opportunity) * FROM "cwuOpportunityStatuses"
WHERE status IS NOT NULL
ORDER BY opportunity, "createdAt" DESC) as stat`,
[]
),
"opp.id",
"stat.opportunity"
)
// Join on latest CWU version
.join<RawCWUOpportunity>("cwuOpportunityVersions as version", function () {
this.on("opp.id", "=", "version.opportunity").andOn(
"version.createdAt",
"=",
connection.raw(
'(select max("createdAt") from "cwuOpportunityVersions" as version2 where \
version2.opportunity = opp.id)'
)
);
})
.join(
connection.raw(
`(SELECT DISTINCT ON (opportunity) * FROM "cwuOpportunityVersions"
ORDER BY opportunity, "createdAt" DESC) as version`,
[]
),
"opp.id",
"version.opportunity"
)
.select<RawCWUOpportunity[]>(
"opp.id",
"opp.createdAt",
Expand Down
39 changes: 19 additions & 20 deletions src/back-end/lib/db/opportunity/sprint-with-us.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,27 +462,26 @@ export function generateSWUOpportunityQuery(
"swuOpportunities as opportunities"
)
// Join on latest SWU status
.join("swuOpportunityStatuses as statuses", function () {
this.on("opportunities.id", "=", "statuses.opportunity").andOn(
"statuses.createdAt",
"=",
connection.raw(
'(select max("createdAt") from "swuOpportunityStatuses" as statuses2 where \
statuses2.opportunity = opportunities.id and statuses2.status is not null)'
)
);
})
.join(
connection.raw(
`(SELECT DISTINCT ON (opportunity) * FROM "swuOpportunityStatuses"
WHERE status IS NOT NULL
ORDER BY opportunity, "createdAt" DESC) as statuses`,
[]
),
"opportunities.id",
"statuses.opportunity"
)
// Join on latest SWU version
.join("swuOpportunityVersions as versions", function () {
this.on("opportunities.id", "=", "versions.opportunity").andOn(
"versions.createdAt",
"=",
connection.raw(
'(select max("createdAt") from "swuOpportunityVersions" as versions2 where \
versions2.opportunity = opportunities.id)'
)
);
})
.join(
connection.raw(
`(SELECT DISTINCT ON (opportunity) * FROM "swuOpportunityVersions"
ORDER BY opportunity, "createdAt" DESC) as versions`,
[]
),
"opportunities.id",
"versions.opportunity"
)
.select<RawSWUOpportunitySlim[]>(
"opportunities.id",
"opportunities.createdAt",
Expand Down
39 changes: 19 additions & 20 deletions src/back-end/lib/db/opportunity/team-with-us.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,27 +514,26 @@ export function generateTWUOpportunityQuery(
"twuOpportunities as opportunities"
)
// Join on latest TWU status
.join("twuOpportunityStatuses as statuses", function () {
this.on("opportunities.id", "=", "statuses.opportunity").andOn(
"statuses.createdAt",
"=",
connection.raw(
'(select max("createdAt") from "twuOpportunityStatuses" as statuses2 where \
statuses2.opportunity = opportunities.id and statuses2.status is not null)'
)
);
})
.join(
connection.raw(
`(SELECT DISTINCT ON (opportunity) * FROM "twuOpportunityStatuses"
WHERE status IS NOT NULL
ORDER BY opportunity, "createdAt" DESC) as statuses`,
[]
),
"opportunities.id",
"statuses.opportunity"
)
// Join on latest TWU version
.join("twuOpportunityVersions as versions", function () {
this.on("opportunities.id", "=", "versions.opportunity").andOn(
"versions.createdAt",
"=",
connection.raw(
'(select max("createdAt") from "twuOpportunityVersions" as versions2 where \
versions2.opportunity = opportunities.id)'
)
);
})
.join(
connection.raw(
`(SELECT DISTINCT ON (opportunity) * FROM "twuOpportunityVersions"
ORDER BY opportunity, "createdAt" DESC) as versions`,
[]
),
"opportunities.id",
"versions.opportunity"
)
.select<RawTWUOpportunitySlim[]>(
"opportunities.id",
"opportunities.createdAt",
Expand Down
67 changes: 67 additions & 0 deletions src/front-end/sass/components/_virtualized-table.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* Wrapper for the fixed header table */
.virtualized-table-header-wrapper {
// Headers should not scroll
overflow: hidden;
border: 1px solid #dee2e6; // Match body container border
border-bottom: none; // Avoid double border
border-radius: 4px 4px 0 0; // Match body container corners
}

/* Header table specific styles */
.virtualized-table-header-fixed {
margin-bottom: 0 !important; // Remove default table margin
width: 100%;
table-layout: fixed;
background-color: #f8f9fa; // Header background

thead th {
// No longer sticky, relies on table-header-wrapper not scrolling
background: inherit; // Inherit from thead if set, or set here
border-bottom: 2px solid #dee2e6;
vertical-align: middle;
padding: 0.75rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: left;

// Apply width from colgroup in body for consistency (via fixed layout)
}
}

/* Scrollable container for the table body */
.virtualized-body-container {
position: relative; // Needed for absolute positioning inside
height: 550px; // Fixed height for body (600px total - header height approx)
overflow-y: auto; // Enable vertical scroll
overflow-x: hidden; // Prevent horizontal scroll if columns fit
border: 1px solid #dee2e6;
border-top: none; // Connects with header wrapper border
border-radius: 0 0 4px 4px; // Match header wrapper corners
}

/* Body table specific styles (rendered inside positioned div) */
.virtualized-body-table {
margin-bottom: 0;
width: 100%;
table-layout: fixed;
background-color: white;

tbody {
tr {
// Height is set inline on <tr> for virtualization
&:hover {
background-color: rgba(0, 0, 0, 0.075);
}
}

td {
vertical-align: middle;
padding: 0.75rem;
border-top: 1px solid #dee2e6;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
1 change: 1 addition & 0 deletions src/front-end/sass/index.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Imports

@import "./font";
@import "./components/virtualized-table";

// Custom Bootstrap variable overrides.

Expand Down
Loading
Loading