@@ -17,6 +17,7 @@ import {
1717import { resolve } from '../../api/api.js' ;
1818import DatePickerField from '../date-picker-field/DatePickerField' ;
1919import ConfirmationDialog from '../dialogs/ConfirmationDialog' ;
20+ import OrganizationDialog from '../dialogs/OrganizationDialog' ; // Include OrganizationDialog
2021import { AppContext } from '../../context/AppContext' ;
2122import { selectCsrfToken , selectCurrentUser , selectProfileMap } from '../../context/selectors' ;
2223import { formatDate } from '../../helpers/datetime' ;
@@ -30,11 +31,13 @@ const propTypes = { forceUpdate: PropTypes.func, onlyMe: PropTypes.bool };
3031const VolunteerEvents = ( { forceUpdate = ( ) => { } , onlyMe = false } ) => {
3132 const [ confirmDeleteOpen , setConfirmDeleteOpen ] = useState ( false ) ;
3233 const [ eventDialogOpen , setEventDialogOpen ] = useState ( false ) ;
34+ const [ organizationDialogOpen , setOrganizationDialogOpen ] = useState ( false ) ; // Organization dialog state
3335 const [ events , setEvents ] = useState ( [ ] ) ;
3436 const [ organizationMap , setOrganizationMap ] = useState ( { } ) ;
3537 const [ relationshipMap , setRelationshipMap ] = useState ( { } ) ;
3638 const [ relationships , setRelationships ] = useState ( [ ] ) ;
3739 const [ selectedEvent , setSelectedEvent ] = useState ( null ) ;
40+ const [ newOrganization , setNewOrganization ] = useState ( { name : '' , description : '' , website : '' } ) ;
3841 const [ sortAscending , setSortAscending ] = useState ( true ) ;
3942 const [ sortColumn , setSortColumn ] = useState ( 'Relationship' ) ;
4043
@@ -63,7 +66,6 @@ const VolunteerEvents = ({ forceUpdate = () => {}, onlyMe = false }) => {
6366
6467 let events = res . payload . data ;
6568 if ( onlyMe ) {
66- // Only keep the events for my relationships.
6769 events = events . filter ( e => Boolean ( relationshipMap [ e . relationshipId ] ) ) ;
6870 }
6971 events . sort ( ( event1 , event2 ) => event1 . eventDate . localeCompare ( event2 . eventDate ) ) ;
@@ -168,40 +170,113 @@ const VolunteerEvents = ({ forceUpdate = () => {}, onlyMe = false }) => {
168170 [ relationshipMap ]
169171 ) ;
170172
173+ const openCreateOrganizationDialog = useCallback ( ( ) => {
174+ setNewOrganization ( { name : '' , description : '' , website : '' } ) ;
175+ setOrganizationDialogOpen ( true ) ;
176+ } , [ ] ) ;
177+
178+ const saveOrganizationAndRelationship = useCallback ( async ( ) => {
179+ const { name, description, website } = newOrganization ;
180+ if ( ! name || ! description ) {
181+ console . error ( 'Missing organization name or description' ) ;
182+ return ;
183+ }
184+
185+ try {
186+ // Step 1: Create the organization
187+ const res = await resolve ( {
188+ method : 'POST' ,
189+ url : organizationBaseUrl ,
190+ headers : {
191+ 'X-CSRF-Header' : csrf ,
192+ Accept : 'application/json' ,
193+ 'Content-Type' : 'application/json;charset=UTF-8'
194+ } ,
195+ data : { name, description, website }
196+ } ) ;
197+
198+ if ( res . error ) {
199+ console . error ( 'Error creating organization' , res . error ) ;
200+ return ;
201+ }
202+
203+ const createdOrg = res . payload . data ;
204+
205+ // Step 2: Create a relationship between the current user and the newly created organization
206+ const relationshipRes = await resolve ( {
207+ method : 'POST' ,
208+ url : relationshipBaseUrl , // Ensure the correct URL is used
209+ headers : {
210+ 'X-CSRF-Header' : csrf ,
211+ Accept : 'application/json' ,
212+ 'Content-Type' : 'application/json;charset=UTF-8'
213+ } ,
214+ data : {
215+ memberId : currentUser . id ,
216+ organizationId : createdOrg . id ,
217+ startDate : formatDate ( new Date ( ) ) , // Set the start date as the current date
218+ endDate : null // Leave endDate as null for an active relationship
219+ }
220+ } ) ;
221+
222+ if ( relationshipRes . error ) {
223+ console . error ( 'Error creating relationship' , relationshipRes . error ) ;
224+ return ;
225+ }
226+
227+ const createdRelationship = relationshipRes . payload . data ;
228+
229+ // Step 3: Update the organization and relationship maps
230+ setOrganizationMap ( prev => ( { ...prev , [ createdOrg . id ] : createdOrg } ) ) ;
231+ setRelationshipMap ( prev => ( { ...prev , [ createdRelationship . id ] : createdRelationship } ) ) ;
232+
233+ // Step 4: Update selectedEvent with the new relationship
234+ setSelectedEvent ( {
235+ ...selectedEvent ,
236+ relationshipId : createdRelationship . id // Set the new relationship ID
237+ } ) ;
238+
239+ // Step 5: Close organization dialog and open event dialog
240+ setOrganizationDialogOpen ( false ) ;
241+ setEventDialogOpen ( true ) ;
242+
243+ } catch ( error ) {
244+ console . error ( 'Failed to create organization and relationship' , error ) ;
245+ }
246+ } , [ newOrganization , csrf , currentUser . id , selectedEvent ] ) ;
247+
171248 const eventDialog = useCallback (
172249 ( ) => (
173250 < Dialog classes = { { root : 'volunteer-dialog' } } open = { eventDialogOpen } onClose = { cancelEvent } >
174251 < DialogTitle > { selectedEvent ?. id ? 'Edit' : 'Add' } Event</ DialogTitle >
175252 < DialogContent >
176- < Autocomplete
177- disableClearable
178- getOptionLabel = { relationshipName }
179- isOptionEqualToValue = { ( option , value ) => option . id === value . id }
180- onChange = { ( event , relationship ) => {
253+ < Autocomplete
254+ disableClearable
255+ getOptionLabel = { ( option ) =>
256+ option === 'new' ? 'Create a New Organization' : ( relationshipMap [ option ] ?. organizationId && organizationMap [ relationshipMap [ option ] . organizationId ] ?. name ) || 'Unknown'
257+ }
258+ options = { [ 'new' , ...relationships . filter ( ( rel ) => ! rel . endDate ) . map ( ( rel ) => rel . id ) ] } // Use relationship IDs
259+ onChange = { ( event , value ) => {
260+ if ( value === 'new' ) {
261+ openCreateOrganizationDialog ( ) ; // Open the organization creation dialog
262+ } else {
181263 setSelectedEvent ( {
182264 ...selectedEvent ,
183- relationshipId : relationship . id
265+ relationshipId : value // Set relationshipId correctly
184266 } ) ;
185- } }
186- options = { relationships }
187- renderInput = { params => (
188- < TextField { ...params } className = "fullWidth" label = { onlyMe ? 'Organization' : 'Volunteer Relationship' } />
189- ) }
190- value = { selectedEvent ?. relationshipId ? relationshipMap [ selectedEvent . relationshipId ] : null }
191- />
267+ }
268+ } }
269+ renderInput = { ( params ) => < TextField { ...params } className = "fullWidth" label = "Organization" /> }
270+ value = { selectedEvent ?. relationshipId || '' } // Bind to the correct relationship ID
271+ />
192272 < DatePickerField
193273 date = { getDate ( selectedEvent ?. eventDate ) }
194274 label = "Date"
195- setDate = { date => {
196- setSelectedEvent ( {
197- ...selectedEvent ,
198- eventDate : formatDate ( date )
199- } ) ;
200- } }
275+ setDate = { ( date ) => setSelectedEvent ( { ...selectedEvent , eventDate : formatDate ( date ) } ) }
201276 />
202277 < TextField
203- label = "Hours"
204- onChange = { e => {
278+ label = "Hours You Volunteered "
279+ onChange = { ( e ) => {
205280 const hours = Number ( e . target . value ) ;
206281 if ( hours >= 0 ) setSelectedEvent ( { ...selectedEvent , hours } ) ;
207282 } }
@@ -210,29 +285,39 @@ const VolunteerEvents = ({ forceUpdate = () => {}, onlyMe = false }) => {
210285 />
211286 < TextField
212287 label = "Notes"
213- onChange = { e => setSelectedEvent ( { ...selectedEvent , notes : e . target . value } ) }
288+ onChange = { ( e ) => setSelectedEvent ( { ...selectedEvent , notes : e . target . value } ) }
214289 value = { selectedEvent ?. notes ?? '' }
215290 />
216291 </ DialogContent >
217292 < DialogActions >
218293 < Button onClick = { cancelEvent } > Cancel</ Button >
219- < Button disabled = { ! validEvent ( ) } onClick = { saveEvent } >
220- Save
221- </ Button >
294+ < Button disabled = { ! validEvent ( ) } onClick = { saveEvent } > Save</ Button >
222295 </ DialogActions >
223296 </ Dialog >
224297 ) ,
225- [ eventDialogOpen , selectedEvent ]
298+ [ eventDialogOpen , selectedEvent , relationships , relationshipMap ]
226299 ) ;
227300
228301 const eventRow = useCallback (
229- event => {
302+ ( event ) => {
230303 const relationship = relationshipMap [ event . relationshipId ] ;
231- const member = profileMap [ relationship . memberId ] ;
232- const org = organizationMap [ relationship . organizationId ] ;
304+
305+ if ( ! relationship ) {
306+ console . error ( `Relationship ${ event . relationshipId } not found in relationshipMap.` ) ;
307+ return null ;
308+ }
309+
310+ const member = profileMap [ relationship ?. memberId ] ;
311+ const org = organizationMap [ relationship ?. organizationId ] ;
312+
313+ if ( ! member || ! org ) {
314+ console . error ( `Member or Organization not found for relationship ${ event . relationshipId } ` ) ;
315+ return null ;
316+ }
317+
233318 return (
234319 < tr key = { event . id } >
235- < td > { onlyMe ? org . name : member . name + ' - ' + org . name } </ td >
320+ < td > { onlyMe ? org . name : ` ${ member . name } - ${ org . name } ` } </ td >
236321 < td > { event . eventDate } </ td >
237322 < td > { event . hours } </ td >
238323 < td > { event . notes } </ td >
@@ -317,33 +402,71 @@ const VolunteerEvents = ({ forceUpdate = () => {}, onlyMe = false }) => {
317402 } ;
318403
319404 const saveEvent = useCallback ( async ( ) => {
320- const { id } = selectedEvent ;
321- const res = await resolve ( {
322- method : id ? 'PUT' : 'POST' ,
323- url : id ? `${ eventBaseUrl } /${ id } ` : eventBaseUrl ,
324- headers : {
325- 'X-CSRF-Header' : csrf ,
326- Accept : 'application/json' ,
327- 'Content-Type' : 'application/json;charset=UTF-8'
328- } ,
329- data : selectedEvent
330- } ) ;
331- if ( res . error ) return ;
332-
333- const newRel = res . payload . data ;
334-
335- if ( id ) {
336- const index = events . findIndex ( rel => rel . id === id ) ;
337- events [ index ] = newRel ;
338- } else {
339- events . push ( newRel ) ;
405+ const { id, relationshipId, eventDate, hours, notes } = selectedEvent ;
406+
407+ // Check if relationshipId is valid
408+ if ( ! relationshipId ) {
409+ console . error ( 'No relationship selected for the event.' ) ;
410+ return ;
340411 }
341- sortEvents ( events ) ;
342- setEvents ( events ) ;
343-
344- setSelectedEvent ( null ) ;
345- setEventDialogOpen ( false ) ;
346- } , [ relationshipMap , selectedEvent ] ) ;
412+
413+ // Check that all required fields are filled in
414+ if ( ! eventDate || hours <= 0 ) {
415+ console . error ( "Missing required fields: date or hours." ) ;
416+ return ;
417+ }
418+
419+ try {
420+ // Ensure the relationship exists
421+ const relationshipCheck = await resolve ( {
422+ method : 'GET' ,
423+ url : `${ relationshipBaseUrl } /${ relationshipId } ` ,
424+ headers : {
425+ 'X-CSRF-Header' : csrf ,
426+ Accept : 'application/json' ,
427+ 'Content-Type' : 'application/json;charset=UTF-8'
428+ }
429+ } ) ;
430+
431+ if ( relationshipCheck . error || ! relationshipCheck . payload ) {
432+ console . error ( `Relationship ${ relationshipId } doesn't exist.` ) ;
433+ return ;
434+ }
435+
436+ // Proceed with saving the event
437+ const res = await resolve ( {
438+ method : id ? 'PUT' : 'POST' ,
439+ url : id ? `${ eventBaseUrl } /${ id } ` : eventBaseUrl ,
440+ headers : {
441+ 'X-CSRF-Header' : csrf ,
442+ Accept : 'application/json' ,
443+ 'Content-Type' : 'application/json;charset=UTF-8'
444+ } ,
445+ data : { relationshipId, eventDate, hours, notes }
446+ } ) ;
447+
448+ if ( res . error ) {
449+ console . error ( 'Error saving event:' , res . error ) ;
450+ return ;
451+ }
452+
453+ // Update event list and close dialog
454+ const newEvent = res . payload . data ;
455+ if ( id ) {
456+ const index = events . findIndex ( e => e . id === id ) ;
457+ events [ index ] = newEvent ;
458+ } else {
459+ events . push ( newEvent ) ;
460+ }
461+
462+ sortEvents ( events ) ;
463+ setEvents ( events ) ;
464+ setSelectedEvent ( null ) ;
465+ setEventDialogOpen ( false ) ; // Close dialog after save
466+ } catch ( error ) {
467+ console . error ( 'Failed to save event:' , error ) ;
468+ }
469+ } , [ selectedEvent , events , csrf ] ) ;
347470
348471 const sortEvents = useCallback (
349472 events => {
@@ -391,6 +514,15 @@ const VolunteerEvents = ({ forceUpdate = () => {}, onlyMe = false }) => {
391514
392515 { eventDialog ( ) }
393516
517+ { /* Dialog for creating a new organization */ }
518+ < OrganizationDialog
519+ open = { organizationDialogOpen }
520+ onClose = { ( ) => setOrganizationDialogOpen ( false ) }
521+ onSave = { saveOrganizationAndRelationship }
522+ organization = { newOrganization }
523+ setOrganization = { setNewOrganization }
524+ />
525+
394526 < ConfirmationDialog
395527 open = { confirmDeleteOpen }
396528 onYes = { ( ) => deleteEvent ( selectedEvent ) }
0 commit comments