@@ -8,34 +8,28 @@ import activityLogService from '../services/activityLogService';
88import ReportGeneratorButton from '../components/ReportGeneratorButton' ;
99import ContextSelect from '../components/ContextSelect' ;
1010import { Volunteer } from '../types/Volunteer' ;
11+ import { Period } from '../types/Period' ;
1112import { ActivityLog } from '../types/ActivityLog' ;
1213import { ReportType } from '../types/Report' ;
13- import { ALERT_NO_CONTEXT_SELECTED } from '../constants/messages' ;
14+ import { ALERT_NOT_LOGGED_IN } from '../constants/messages' ;
1415
1516
1617const ActivityLogPage : React . FC = ( ) => {
17- const { selectedPeriod } = usePeriod ( ) ;
18- const { volunteers } = useVolunteers ( ) ;
1918 const { user, token } = useContext ( AuthContext ) ;
20- const [ activityLogs , setActivityLogs ] = useState < ActivityLog [ ] > ( [ ] ) ;
19+ const { volunteers, loading : volunteersLoading } = useVolunteers ( ) ;
20+ const { periods, loading : periodsLoading } = usePeriod ( ) ;
2121 const [ loading , setLoading ] = useState ( false ) ;
22+ const [ activityLogs , setActivityLogs ] = useState < ActivityLog [ ] > ( [ ] ) ;
23+ const [ selectedPeriod , setSelectedPeriod ] = useState < Period | null > ( null ) ;
2224 const [ selectedVolunteer , setSelectedVolunteer ] = useState < Volunteer | null > ( null ) ;
2325
2426 useEffect ( ( ) => {
25- if ( ! selectedPeriod ) return ;
2627 setLoading ( true ) ;
2728 const fetchLogs = async ( ) => {
2829 try {
2930 const token = localStorage . getItem ( 'token' ) || '' ;
3031 let logs : ActivityLog [ ] = [ ] ;
31- if ( selectedVolunteer ) {
32- logs = await activityLogService . listByVolunteer ( selectedVolunteer . volunteerId , token ) ;
33- // Filter to current period
34- logs = logs . filter ( l => l . periodId === selectedPeriod . periodId ) ;
35- } else {
36- logs = await activityLogService . list ( token ) ;
37- logs = logs . filter ( l => l . periodId === selectedPeriod . periodId ) ;
38- }
32+ logs = await activityLogService . list ( token ) ;
3933 logs . sort ( ( a , b ) => new Date ( a . timestamp || '' ) . getTime ( ) - new Date ( b . timestamp || '' ) . getTime ( ) ) ;
4034 setActivityLogs ( logs ) ;
4135 } catch ( e ) {
@@ -47,9 +41,22 @@ const ActivityLogPage: React.FC = () => {
4741 fetchLogs ( ) ;
4842 } , [ selectedPeriod , selectedVolunteer ] ) ;
4943
50- if ( ! selectedPeriod ) {
51- return < Alert variant = "warning" > { ALERT_NO_CONTEXT_SELECTED } </ Alert > ;
52- }
44+ if ( ! token ) return < Alert variant = "warning" > { ALERT_NOT_LOGGED_IN } </ Alert > ;
45+
46+ // Filter and sort activity logs before rendering
47+ const filteredActivityLogs = ( activityLogs || [ ] )
48+ . filter ( l => {
49+ // Filter by selected period if set
50+ if ( selectedPeriod && l . periodId !== selectedPeriod . periodId ) return false ;
51+ // Filter by selected volunteer if set
52+ if ( selectedVolunteer && l . volunteerId !== selectedVolunteer . volunteerId ) return false ;
53+ return true ;
54+ } )
55+ . sort ( ( a , b ) => {
56+ const aTimestamp = new Date ( a . timestamp || '' ) . getTime ( ) ;
57+ const bTimestamp = new Date ( b . timestamp || '' ) . getTime ( ) ;
58+ return aTimestamp - bTimestamp ;
59+ } ) ;
5360
5461 return (
5562 < Container >
@@ -58,26 +65,35 @@ const ActivityLogPage: React.FC = () => {
5865 < Row className = "align-items-center" >
5966 < Col > < strong > Activity Log</ strong > </ Col >
6067 < Col md = "auto" className = "d-flex align-items-center gap-2" >
68+ < ContextSelect
69+ label = "Period"
70+ options = { periods }
71+ value = { selectedPeriod ? selectedPeriod . periodId : null }
72+ onSelect = { id => setSelectedPeriod ( id ? periods . find ( p => p . periodId === id ) ?? null : null ) }
73+ loading = { periodsLoading }
74+ getOptionLabel = { p => p . description }
75+ getOptionValue = { p => p . periodId }
76+ />
6177 < ContextSelect
6278 label = "Volunteer"
6379 options = { volunteers }
6480 value = { selectedVolunteer ? selectedVolunteer . volunteerId : null }
6581 onSelect = { id => setSelectedVolunteer ( id ? volunteers . find ( v => v . volunteerId === id ) ?? null : null ) }
66- loading = { loading }
67- getOptionLabel = { v => v . name }
82+ loading = { volunteersLoading }
83+ getOptionLabel = { v => v . callsign ? ` ${ v . name } ( ${ v . callsign } )` : v . name }
6884 getOptionValue = { v => v . volunteerId }
6985 />
7086 < ReportGeneratorButton
7187 requiredReportType = { ReportType . ICS214 }
72- token = { token || '' }
88+ token = { token }
7389 user = { user }
7490 buttonText = "Generate ICS-214"
75- disabled = { ! selectedPeriod || activityLogs . length === 0 }
91+ disabled = { filteredActivityLogs . length === 0 }
7692 buildReportData = { ( { positionTitle } ) => {
7793 // ICS-214 report payload builder
7894 // Find referenced volunteers and count log entries per volunteer
7995 const logCounts : Record < string , number > = { } ;
80- for ( const log of activityLogs ) {
96+ for ( const log of filteredActivityLogs ) {
8197 if ( log . volunteerId ) {
8298 logCounts [ log . volunteerId ] = ( logCounts [ log . volunteerId ] || 0 ) + 1 ;
8399 }
@@ -89,7 +105,7 @@ const ActivityLogPage: React.FC = () => {
89105 const referencedVolunteers = volunteers . filter ( v => topVolunteerIds . includes ( v . volunteerId ) ) ;
90106 return {
91107 period : selectedPeriod ,
92- activityLogs,
108+ activityLogs : filteredActivityLogs ,
93109 volunteers : referencedVolunteers ,
94110 preparedBy : user ,
95111 positionTitle,
@@ -115,10 +131,10 @@ const ActivityLogPage: React.FC = () => {
115131 < td > < Placeholder animation = "glow" > < Placeholder xs = { 10 } /> </ Placeholder > </ td >
116132 </ tr >
117133 ) )
118- ) : activityLogs . length === 0 ? (
134+ ) : filteredActivityLogs . length === 0 ? (
119135 < tr > < td colSpan = { 2 } className = "text-center" > No activity log entries found.</ td > </ tr >
120136 ) : (
121- activityLogs . map ( entry => (
137+ filteredActivityLogs . map ( entry => (
122138 < tr key = { entry . logId } >
123139 < td > { entry . timestamp ? formatLocalDateTime ( entry . timestamp ) : '' } </ td >
124140 < td > { entry . details ? `${ entry . details } ` : '' } </ td >
0 commit comments