@@ -27,7 +27,7 @@ interface Props {
2727export const FormSubmissions : React . FC < Props > = memo ( ( props ) => {
2828 const [ summary , setSummary ] = useState < any > ( [ ] ) ;
2929 const [ summaryCsv , setSummaryCsv ] = useState < any > ( [ ] ) ;
30- const yesNoMap : any = { True : Locale . label ( "common.yes" ) , False : Locale . label ( "common.no" ) } ;
30+ const yesNoMap = useMemo ( ( ) => ( { True : Locale . label ( "common.yes" ) , False : Locale . label ( "common.no" ) } ) , [ ] ) ;
3131 const yesNoDefault = useMemo (
3232 ( ) => [
3333 { value : "Yes" , text : Locale . label ( "common.yes" ) } ,
@@ -38,6 +38,63 @@ export const FormSubmissions: React.FC<Props> = memo((props) => {
3838 const contentRef : any = useRef < HTMLDivElement > ( null ) ;
3939 const handleSummaryPrint = useReactToPrint ( { content : ( ) => contentRef . current } ) ;
4040
41+ const getPerson = useCallback ( ( people : PersonInterface [ ] , formSubmission : any ) => {
42+ let result = people . find ( ( person : PersonInterface ) => person . id === formSubmission . submittedBy ) ;
43+ if ( formSubmission . contentType === "person" ) result = people . find ( ( person : PersonInterface ) => person . id === formSubmission . contentId ) ;
44+ return result ;
45+ } , [ ] ) ;
46+
47+ const setSummaryResultDefault = useCallback (
48+ ( question : QuestionInterface , answer : AnswerInterface ) => {
49+ const choices : any = [ ] ;
50+ const questionChoices = question . choices || yesNoDefault ;
51+ questionChoices . forEach ( ( choice : any ) => {
52+ const choiceCount = { [ choice . value ] : 0 , text : choice . text } ;
53+ if ( question . fieldType === "Checkbox" ) {
54+ if ( answer && answer ?. value ) {
55+ const splitAnswer = answer . value ?. split ( "," ) ;
56+ if ( splitAnswer . indexOf ( choice . value ) > - 1 ) choiceCount [ choice . value ] = 1 ;
57+ }
58+ } else {
59+ if ( answer && answer ?. value && choice . value === answer . value ) choiceCount [ choice . value ] = 1 ;
60+ }
61+ choices . push ( choiceCount ) ;
62+ } ) ;
63+ return { title : question . title , values : choices } ;
64+ } ,
65+ [ yesNoDefault ]
66+ ) ;
67+
68+ const setSummaryResultData = useCallback ( ( summaryData : any , question : QuestionInterface , answer : AnswerInterface ) => {
69+ const match = summaryData . find ( ( result : any ) => result . title === question . title ) ;
70+ if ( match ) {
71+ match . values . forEach ( ( resultValue : any ) => {
72+ const key : string = Object . keys ( resultValue ) [ 0 ] ;
73+ if ( question . fieldType === "Checkbox" ) {
74+ const splitAnswer = answer ?. value ?. split ( "," ) ;
75+ if ( splitAnswer ?. indexOf ( key ) > - 1 ) resultValue [ key ] = resultValue [ key ] + 1 ;
76+ } else {
77+ if ( key === answer ?. value ) resultValue [ key ] = resultValue [ key ] + 1 ;
78+ }
79+ } ) ;
80+ } else summaryData . push ( setSummaryResultDefault ( question , answer ) ) ;
81+ } , [ setSummaryResultDefault ] ) ;
82+
83+ const setFormSubmissionData = useCallback (
84+ ( people : PersonInterface [ ] , formSubmission : any ) => {
85+ const submittedBy = getPerson ( people , formSubmission ) ;
86+
87+ formSubmission . person = { name : submittedBy ?. name ?. display || Locale . label ( "forms.formSubmissions.anon" ) , id : submittedBy ?. id || null } ;
88+ formSubmission . mappedQA = [ ] ;
89+ formSubmission . csvData = [ ] ;
90+ if ( formSubmission . questions ) {
91+ formSubmission . questions = formSubmission . questions . sort ( ( a : QuestionInterface , b : QuestionInterface ) => ( a . title > b . title ? 1 : - 1 ) ) ;
92+ }
93+ return formSubmission ;
94+ } ,
95+ [ getPerson ]
96+ ) ;
97+
4198 const people = useQuery < PersonInterface [ ] > ( {
4299 queryKey : [ "/people" , "MembershipApi" ] ,
43100 placeholderData : [ ] ,
@@ -74,62 +131,7 @@ export const FormSubmissions: React.FC<Props> = memo((props) => {
74131 setSummary ( summaryData ) ;
75132 setSummaryCsv ( csv ) ;
76133 }
77- } , [ people . data , formSubmissions . data , yesNoMap ] ) ;
78-
79- const setSummaryResultData = useCallback ( ( summaryData : any , question : QuestionInterface , answer : AnswerInterface ) => {
80- const match = summaryData . find ( ( result : any ) => result . title === question . title ) ;
81- if ( match ) {
82- match . values . forEach ( ( resultValue : any ) => {
83- const key : string = Object . keys ( resultValue ) [ 0 ] ;
84- if ( question . fieldType === "Checkbox" ) {
85- const splitAnswer = answer ?. value ?. split ( "," ) ;
86- if ( splitAnswer ?. indexOf ( key ) > - 1 ) resultValue [ key ] = resultValue [ key ] + 1 ;
87- } else {
88- if ( key === answer ?. value ) resultValue [ key ] = resultValue [ key ] + 1 ;
89- }
90- } ) ;
91- } else summaryData . push ( setSummaryResultDefault ( question , answer ) ) ;
92- } , [ ] ) ;
93-
94- const getPerson = useCallback ( ( people : PersonInterface [ ] , formSubmission : any ) => {
95- let result = people . find ( ( person : PersonInterface ) => person . id === formSubmission . submittedBy ) ;
96- if ( formSubmission . contentType === "person" ) result = people . find ( ( person : PersonInterface ) => person . id === formSubmission . contentId ) ;
97- return result ;
98- } , [ ] ) ;
99-
100- const setFormSubmissionData = useCallback (
101- ( people : PersonInterface [ ] , formSubmission : any ) => {
102- const submittedBy = getPerson ( people , formSubmission ) ;
103-
104- formSubmission . person = { name : submittedBy ?. name ?. display || Locale . label ( "forms.formSubmissions.anon" ) , id : submittedBy ?. id || null } ;
105- formSubmission . mappedQA = [ ] ;
106- formSubmission . csvData = [ ] ;
107- formSubmission . questions = formSubmission . questions . sort ( ( a : QuestionInterface , b : QuestionInterface ) => ( a . title > b . title ? 1 : - 1 ) ) ;
108- return formSubmission ;
109- } ,
110- [ getPerson ]
111- ) ;
112-
113- const setSummaryResultDefault = useCallback (
114- ( question : QuestionInterface , answer : AnswerInterface ) => {
115- const choices : any = [ ] ;
116- const questionChoices = question . choices || yesNoDefault ;
117- questionChoices . forEach ( ( choice : any ) => {
118- const choiceCount = { [ choice . value ] : 0 , text : choice . text } ;
119- if ( question . fieldType === "Checkbox" ) {
120- if ( answer && answer ?. value ) {
121- const splitAnswer = answer . value ?. split ( "," ) ;
122- if ( splitAnswer . indexOf ( choice . value ) > - 1 ) choiceCount [ choice . value ] = 1 ;
123- }
124- } else {
125- if ( answer && answer ?. value && choice . value === answer . value ) choiceCount [ choice . value ] = 1 ;
126- }
127- choices . push ( choiceCount ) ;
128- } ) ;
129- return { title : question . title , values : choices } ;
130- } ,
131- [ yesNoDefault ]
132- ) ;
134+ } , [ people . data , formSubmissions . data , yesNoMap , getPerson , setFormSubmissionData , setSummaryResultData , setSummaryResultDefault ] ) ;
133135
134136 const getResultCount = useCallback ( ( summaryValues : any [ ] ) => {
135137 const results : JSX . Element [ ] = [ ] ;
@@ -184,8 +186,9 @@ export const FormSubmissions: React.FC<Props> = memo((props) => {
184186
185187 const getAnswers = useCallback ( ( formSubmission : FormSubmissionInterface ) => {
186188 const rows : JSX . Element [ ] = [ ] ;
187- formSubmission . questions . forEach ( ( question : QuestionInterface ) => {
188- const answer = formSubmission . answers . find ( ( answer : AnswerInterface ) => answer . questionId === question . id ) ;
189+ formSubmission . questions ?. forEach ( ( question : QuestionInterface ) => {
190+ if ( ! question ?. id ) return ;
191+ const answer = formSubmission . answers ?. find ( ( answer : AnswerInterface ) => answer . questionId === question . id ) ;
189192 rows . push (
190193 < TableCell key = { question . id } >
191194 < Typography variant = "body2" > { answer ?. value || "-" } </ Typography >
@@ -215,6 +218,13 @@ export const FormSubmissions: React.FC<Props> = memo((props) => {
215218 }
216219
217220 formSubmissions . data ?. forEach ( ( submission : any , i : number ) => {
221+ // Process the submission data inline
222+ const processedSubmission = people . data ? setFormSubmissionData ( people . data , submission ) : submission ;
223+
224+ // Allow submissions even without a person ID (anonymous submissions)
225+ const personName = processedSubmission . person ?. name || Locale . label ( "forms.formSubmissions.anon" ) ;
226+ const personId = processedSubmission . person ?. id ;
227+
218228 rows . push (
219229 < TableRow
220230 key = { i }
@@ -223,25 +233,31 @@ export const FormSubmissions: React.FC<Props> = memo((props) => {
223233 transition : "background-color 0.2s ease" ,
224234 } } >
225235 < TableCell key = "personName" >
226- < a
227- href = { "/people/" + submission . person . id }
228- style = { {
229- textDecoration : "none" ,
230- color : "var(--c1l2)" ,
231- fontWeight : 500 ,
232- } } >
233- { submission . person . name }
234- </ a >
236+ { personId ? (
237+ < a
238+ href = { "/people/" + personId }
239+ style = { {
240+ textDecoration : "none" ,
241+ color : "var(--c1l2)" ,
242+ fontWeight : 500 ,
243+ } } >
244+ { personName }
245+ </ a >
246+ ) : (
247+ < Typography variant = "body2" sx = { { fontWeight : 500 } } >
248+ { personName }
249+ </ Typography >
250+ ) }
235251 </ TableCell >
236252 < TableCell key = "subDate" >
237- < Typography variant = "body2" > { DateHelper . prettyDate ( new Date ( submission . submissionDate ) ) } </ Typography >
253+ < Typography variant = "body2" > { DateHelper . prettyDate ( new Date ( processedSubmission . submissionDate ) ) } </ Typography >
238254 </ TableCell >
239- { getAnswers ( submission ) }
255+ { getAnswers ( processedSubmission ) }
240256 </ TableRow >
241257 ) ;
242258 } ) ;
243259 return rows ;
244- } , [ formSubmissions . data , getAnswers ] ) ;
260+ } , [ formSubmissions . data , people . data , getAnswers , setFormSubmissionData ] ) ;
245261
246262 const editLinks = useMemo ( ( ) => {
247263 const formName = formSubmissions . data ?. length ? formSubmissions . data [ 0 ] . form ?. name + ".csv" : "form_submissions.csv" ;
0 commit comments