Added a link from each opportunity to its latest Find a Tender notice
Opportunities from Find a Tender have a link icon before their title. Clicking it fetches the opportunity with its releases (the list leaves them out) and opens its newest release's notice, www.find-tender.service.gov.uk/notice/<release_id>, in a new tab. The tab is opened on the click and pointed at the notice when the request returns, so it isn't blocked as a popup; if the request fails, or there are no releases, it's closed and a toast says why. Opportunities from other sources have no link for now, but keep its space so the titles line up. The icon is lucide-solid's, added for it at 1.52.0.
This commit is contained in:
1 parent
a6630f112f
commit
e98f18b790
8 files changed
+186
-10
No files matched your search
@@ -1,6 +1,12 @@
|
||||
import { expect, test } from 'vitest';
|
||||
|
||||
import { mergeOpportunities } from './opportunities';
|
||||
import type { OpportunityRelease } from '@/types/Opportunities';
|
||||
import {
|
||||
findATenderNoticeUrl,
|
||||
isFromFindATender,
|
||||
latestRelease,
|
||||
mergeOpportunities,
|
||||
} from './opportunities';
|
||||
import { testOpportunity } from './testOpportunity';
|
||||
|
||||
test('replaces opportunities in place and adds new ones at the end', () => {
|
||||
@@ -16,3 +22,38 @@ test('replaces opportunities in place and adds new ones at the end', () => {
|
||||
]);
|
||||
expect(list[0]?.title).toBe('Opportunity a');
|
||||
});
|
||||
|
||||
const release = (releaseId: string, releaseDate: string): OpportunityRelease => ({
|
||||
uuid: 'a',
|
||||
release_id: releaseId,
|
||||
release_date: releaseDate,
|
||||
release_tags: null,
|
||||
data: '{}',
|
||||
});
|
||||
|
||||
test('only Find a Tender opportunities are from Find a Tender', () => {
|
||||
expect(isFromFindATender(testOpportunity('a'))).toBe(true);
|
||||
expect(isFromFindATender(testOpportunity('a', { source: 'FTSMock' }))).toBe(false);
|
||||
});
|
||||
|
||||
test('the latest release is the newest, wherever it is in the list', () => {
|
||||
const opportunity = testOpportunity('a', {
|
||||
releases: [
|
||||
release('001-2026', '2026-10-01T09:00:00Z'),
|
||||
release('003-2026', '2026-10-09T19:21:17+01:00'),
|
||||
release('002-2026', '2026-10-05T12:00:00Z'),
|
||||
],
|
||||
});
|
||||
expect(latestRelease(opportunity)?.release_id).toBe('003-2026');
|
||||
});
|
||||
|
||||
test('an opportunity without releases has no latest release', () => {
|
||||
expect(latestRelease(testOpportunity('a', { releases: null }))).toBeUndefined();
|
||||
expect(latestRelease(testOpportunity('a', { releases: [] }))).toBeUndefined();
|
||||
});
|
||||
|
||||
test('a notice’s URL is its release ID on Find a Tender', () => {
|
||||
expect(findATenderNoticeUrl('095868-2026')).toBe(
|
||||
'https://www.find-tender.service.gov.uk/notice/095868-2026',
|
||||
);
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Opportunity } from '@/types/Opportunities';
|
||||
import type { Opportunity, OpportunityRelease } from '@/types/Opportunities';
|
||||
|
||||
/**
|
||||
* The opportunities with changed merged in: each replaces the opportunity
|
||||
@@ -12,3 +12,27 @@ export function mergeOpportunities(
|
||||
for (const opportunity of changed) byUuid.set(opportunity.uuid, opportunity);
|
||||
return [...byUuid.values()];
|
||||
}
|
||||
|
||||
/** The source the backend gives opportunities fetched from Find a Tender. */
|
||||
const FIND_A_TENDER_SOURCE = 'FindATender';
|
||||
|
||||
/** Whether the opportunity was fetched from Find a Tender, so its releases are notices there. */
|
||||
export function isFromFindATender(opportunity: Opportunity): boolean {
|
||||
return opportunity.source === FIND_A_TENDER_SOURCE;
|
||||
}
|
||||
|
||||
/** The opportunity's newest release, or undefined if it has none. */
|
||||
export function latestRelease(opportunity: Opportunity): OpportunityRelease | undefined {
|
||||
let latest: OpportunityRelease | undefined;
|
||||
for (const release of opportunity.releases ?? []) {
|
||||
if (!latest || Date.parse(release.release_date) > Date.parse(latest.release_date)) {
|
||||
latest = release;
|
||||
}
|
||||
}
|
||||
return latest;
|
||||
}
|
||||
|
||||
/** The page of a Find a Tender notice; a Find a Tender release's ID is its notice's. */
|
||||
export function findATenderNoticeUrl(releaseId: string): string {
|
||||
return `https://www.find-tender.service.gov.uk/notice/${encodeURIComponent(releaseId)}`;
|
||||
}
|
||||
Reference in new issue
Block a user