Skip to content

Commit c7793d9

Browse files
author
Philip
committed
feat: add satellite activity events section and table
Add a satellite activity events view with filtering and download support. Wire it into the satellite accordion and add supporting translations. Made-with: Cursor
1 parent 26db99b commit c7793d9

7 files changed

Lines changed: 227 additions & 1 deletion
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use server';
2+
3+
import Api from '@/libs/Api';
4+
5+
export async function getActivityEventsByNoradId(noradId: string) {
6+
const { data } = await Api.getActivityEventsByNoradIdNoradId(noradId);
7+
return data;
8+
}

src/components/satellite/SatelliteAccordion.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getSession } from '@/actions/getSession';
66
import Accordion from '@/ui/accordion/accordion';
77
import { isAgencyApprover, isSatteliteOperator } from '@/utils/Roles';
88

9+
import { SatelliteActivityEvents } from './SatelliteActivityEvents';
910
import { SatelliteAdditionalInformations } from './SatelliteAdditionalInformation';
1011
import { SatelliteConjunctionEvents } from './SatelliteConjunctionEvents';
1112
import { SatelliteEphemerisData } from './SatelliteEphemerisData';
@@ -65,6 +66,7 @@ const SatelliteAccordion = async ({
6566
<h2 data-anchor="conjunction_events" className="govuk-heading-l">{t('conjunction_events')}</h2>
6667
<Accordion
6768
id="conjunction-events"
69+
addAnchor={false}
6870
initialItems={[
6971
{
7072
id: 'all_conjunction_events',
@@ -73,6 +75,18 @@ const SatelliteAccordion = async ({
7375
},
7476
]}
7577
/>
78+
<h2 data-anchor="potential-impact" className="govuk-heading-l">{t('activity_information')}</h2>
79+
<Accordion
80+
id="activity-events"
81+
addAnchor={false}
82+
initialItems={[
83+
{
84+
id: 'all_activity_events',
85+
heading: t('all_activity_events'),
86+
content: <SatelliteActivityEvents commonName={object.common_name} noradId={noradId} />,
87+
},
88+
]}
89+
/>
7690
</>
7791

7892
);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { getTranslations } from 'next-intl/server';
2+
3+
import { getActivityEventsByNoradId } from '@/actions/getActivityEventsNoradId';
4+
import Details from '@/ui/details/details';
5+
6+
import { DownloadData } from '../DownloadData';
7+
import { SatelliteActivitiesDataTable } from './data-table/SatelliteActivitiesDataTable';
8+
9+
type SatelliteActivityEventsProps = {
10+
noradId: string;
11+
commonName: string;
12+
};
13+
14+
const SatelliteActivityEvents = async ({ noradId, commonName }: SatelliteActivityEventsProps) => {
15+
const t = await getTranslations('Tables');
16+
const tActivities = await getTranslations('Satellite.Activity_events');
17+
const data = await getActivityEventsByNoradId(noradId);
18+
19+
const downloadData = async () => {
20+
'use server';
21+
return await getActivityEventsByNoradId(noradId);
22+
};
23+
24+
return (
25+
<div className="mb-12">
26+
<SatelliteActivitiesDataTable initialData={data} />
27+
<Details summary={tActivities.rich('help.title')}>
28+
{tActivities.rich('help.content', {
29+
commonName,
30+
})}
31+
</Details>
32+
<DownloadData type={t('Download.types.activities')} params={{ norad_id: noradId }} downloadAction={downloadData} ariaLabel="Activities" />
33+
</div>
34+
);
35+
};
36+
37+
export { SatelliteActivityEvents };
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use client';
2+
3+
import { useMemo, useState } from 'react';
4+
5+
import type { TypeActivityEvent } from '@/__generated__/data-contracts';
6+
import { DataTable } from '@/components/DataTable';
7+
8+
import { satteliteActivityEventsColumns } from './SatelliteActivitiesDataTableColumns';
9+
import { SatelliteActivityDataTableFilters } from './SatelliteActivityDataTableFilters';
10+
11+
type SatelliteActivitiesDataTableProps = {
12+
initialData: TypeActivityEvent[];
13+
};
14+
15+
const SatelliteActivitiesDataTable = ({ initialData }: SatelliteActivitiesDataTableProps) => {
16+
const [value, setValue] = useState<string>('All flags');
17+
18+
const filtredData = useMemo(() => {
19+
return value === 'All flags' ? initialData : initialData.filter(item => item.reason_for_flag ?? value === 'Missing data');
20+
}, [initialData, value]);
21+
22+
return (
23+
<div className="mb-12">
24+
<SatelliteActivityDataTableFilters value={value} onChange={setValue} />
25+
<DataTable<TypeActivityEvent>
26+
data={filtredData}
27+
columns={satteliteActivityEventsColumns}
28+
/>
29+
</div>
30+
);
31+
};
32+
33+
export { SatelliteActivitiesDataTable };
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
'use client';
2+
3+
import Link from 'next/link';
4+
5+
import type { TypeActivityEvent, TypeActivityReasonForFlag } from '@/__generated__/data-contracts';
6+
import { dayjs, FORMAT_DATE_FULL_MONTH } from '@/libs/Dayjs';
7+
import type { TranslatedColumnDef } from '@/types';
8+
import { renderReasonForFlagTag } from '@/utils/Tags';
9+
10+
export const satteliteActivityEventsColumns: TranslatedColumnDef<TypeActivityEvent>[] = [
11+
{
12+
header: 'Activities.event_information',
13+
columns: [
14+
{
15+
accessorKey: 'reason_for_flag',
16+
header: 'Activities.reason_for_flag',
17+
enableSorting: false,
18+
cell: ({ getValue }) => {
19+
const value = getValue<TypeActivityReasonForFlag>();
20+
return renderReasonForFlagTag(value);
21+
},
22+
},
23+
{
24+
accessorKey: 'short_id',
25+
header: 'Activities.short_id',
26+
enableSorting: false,
27+
cell: ({ getValue }) => {
28+
const value = getValue<string>();
29+
return (
30+
<Link
31+
href={`/activity/${value}`}
32+
className="govuk-link"
33+
>
34+
{value}
35+
</Link>
36+
);
37+
},
38+
},
39+
{
40+
accessorKey: 'flag_date',
41+
header: 'Activities.flag_date',
42+
enableSorting: false,
43+
cell: ({ getValue }) => {
44+
const value = getValue<string>();
45+
return value ? dayjs(value).format(FORMAT_DATE_FULL_MONTH) : '-';
46+
},
47+
},
48+
],
49+
},
50+
{
51+
header: 'Activities.position_change',
52+
columns: [
53+
{
54+
accessorKey: 'inclination',
55+
header: 'Activities.inclination',
56+
enableSorting: false,
57+
},
58+
{
59+
accessorKey: 'apogee',
60+
header: 'Activities.apogee',
61+
enableSorting: false,
62+
},
63+
{
64+
accessorKey: 'perigee',
65+
header: 'Activities.perigee',
66+
enableSorting: false,
67+
},
68+
{
69+
accessorKey: 'longitude',
70+
header: 'Activities.longitude',
71+
enableSorting: false,
72+
},
73+
],
74+
},
75+
];
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use client';
2+
3+
import { useTranslations } from 'next-intl';
4+
import { useCallback } from 'react';
5+
6+
import Select from '@/ui/select/select';
7+
8+
const FILTER_OPTIONS = [
9+
'All flags',
10+
'Position change',
11+
'Manoeuvre (unplanned)',
12+
'Manoeuvre (planned)',
13+
'Missing data',
14+
];
15+
16+
type SatelliteActivityDataTableFiltersProps = {
17+
value: string;
18+
onChange: (value: string) => void;
19+
};
20+
21+
const SatelliteActivityDataTableFilters = ({ value, onChange }: SatelliteActivityDataTableFiltersProps) => {
22+
const t = useTranslations('Tables.Activities.details');
23+
24+
const handleChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
25+
onChange(e.target.value);
26+
}, [onChange]);
27+
28+
return (
29+
<div>
30+
<h4 className="govuk-heading-s mb-4">{t.rich('summary')}</h4>
31+
<div className="flex flex-col md:flex-row gap-4 justify-between md:items-center border-b border-midGrey pb-4 mb-4">
32+
<Select
33+
name="epoch"
34+
id="epoch"
35+
label={t('select_label')}
36+
value={value}
37+
options={FILTER_OPTIONS.map(option => ({
38+
children: option,
39+
value: option,
40+
}))}
41+
onChange={handleChange}
42+
/>
43+
</div>
44+
</div>
45+
);
46+
};
47+
48+
export { SatelliteActivityDataTableFilters };

src/locales/en.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,8 @@
351351
"ephemeris_data": "Ephemeris data",
352352
"conjunction_events": "Conjunction events",
353353
"all_conjunction_events": "All conjunction events",
354-
"activity_information": "Activity information"
354+
"activity_information": "Activity information",
355+
"all_activity_events": "All activity events"
355356
},
356357
"Ephemeris_data": {
357358
"title": "Ephemeris data",
@@ -371,6 +372,12 @@
371372
"future": "Upcoming",
372373
"past": "Previous"
373374
}
375+
},
376+
"Activity_events": {
377+
"help": {
378+
"title": "Help with this table<screenReaderOnly> - Activity events</screenReaderOnly>",
379+
"content": "<p>This table shows all ongoing and closed activity flags for {commonName}.</p><p>Select the event ID to view more information</p>"
380+
}
374381
}
375382
},
376383
"Satellite_ephemeris_upload": {
@@ -1718,6 +1725,10 @@
17181725
"orbit_type": "Orbit",
17191726
"altitude": "Altitude (km)",
17201727
"longitude": "Longitude (°)",
1728+
"inclination": "Inclination (°)",
1729+
"apogee": "Apogee (km)",
1730+
"perigee": "Perigee (km)",
1731+
"position_change": "Position after change and size of change (if applicable)",
17211732
"activity_events_as_of": "Activity events as of "
17221733
}
17231734
},

0 commit comments

Comments
 (0)