@@ -11,7 +11,9 @@ import {
1111 TextField ,
1212 Typography ,
1313 Snackbar ,
14- Alert
14+ Alert ,
15+ Button ,
16+ useTheme
1517} from '@mui/material' ;
1618import { Box , Stack } from '@mui/system' ;
1719import { Control , Controller , FieldErrors , UseFormHandleSubmit , UseFormSetValue , UseFormWatch } from 'react-hook-form' ;
@@ -37,6 +39,7 @@ import { routes } from '../../../utils/routes';
3739import { wbsNumComparator } from 'shared/src/validate-wbs' ;
3840import { codeAndRefundSourceName , expenseTypePipe } from '../../../utils/pipes' ;
3941import NERAutocomplete from '../../../components/NERAutocomplete' ;
42+ import FileUploadIcon from '@mui/icons-material/FileUpload' ;
4043
4144interface ReimbursementRequestFormViewProps {
4245 allVendors : Vendor [ ] ;
@@ -48,7 +51,7 @@ interface ReimbursementRequestFormViewProps {
4851 } [ ] ;
4952 control : Control < ReimbursementRequestFormInput , any > ;
5053 reimbursementProducts : ReimbursementProductFormArgs [ ] ;
51- receiptAppend : ( args : ReimbursementReceiptUploadArgs ) => void ;
54+ receiptPrepend : ( args : ReimbursementReceiptUploadArgs ) => void ;
5255 receiptRemove : ( index : number ) => void ;
5356 reimbursementProductAppend : ( args : ReimbursementProductFormArgs ) => void ;
5457 reimbursementProductRemove : ( index : number ) => void ;
@@ -69,7 +72,7 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
6972 receiptFiles,
7073 reimbursementProducts,
7174 control,
72- receiptAppend ,
75+ receiptPrepend ,
7376 receiptRemove,
7477 reimbursementProductAppend,
7578 reimbursementProductRemove,
@@ -84,6 +87,7 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
8487} ) => {
8588 const [ datePickerOpen , setDatePickerOpen ] = useState ( false ) ;
8689 const toast = useToast ( ) ;
90+ const theme = useTheme ( ) ;
8791 const products = watch ( `reimbursementProducts` ) ;
8892 const expenseTypeId = watch ( 'expenseTypeId' ) ;
8993 const selectedExpenseType = allExpenseTypes . find ( ( expenseType ) => expenseType . expenseTypeId === expenseTypeId ) ;
@@ -100,7 +104,6 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
100104
101105 const ReceiptFileInput = ( ) => (
102106 < FormControl >
103- < FormLabel > Receipts</ FormLabel >
104107 < ul >
105108 { receiptFiles . map ( ( receiptFile , index ) => (
106109 < li key = { index } >
@@ -126,6 +129,7 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
126129 e . stopPropagation ( ) ;
127130 handleSubmit ( onSubmit ) ( e ) ;
128131 } }
132+ style = { { minHeight : 'calc(100vh - 161px)' , display : 'flex' , flexDirection : 'column' , justifyContent : 'space-between' } }
129133 >
130134 { ! hasSecureSettingsSet && (
131135 < Snackbar anchorOrigin = { { vertical : 'top' , horizontal : 'center' } } open = { true } >
@@ -140,7 +144,7 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
140144 </ Snackbar >
141145 ) }
142146 < Grid container spacing = { 2 } >
143- < Grid item container maxHeight = { 375 } spacing = { 2 } md = { 6 } xs = { 12 } >
147+ < Grid item container spacing = { 2 } md = { 6 } xs = { 12 } sx = { { '&.MuiGrid-item' : { height : 'fit-content' } } } >
144148 < Grid item xs = { 12 } >
145149 < FormControl fullWidth >
146150 < FormLabel > Purchased From</ FormLabel >
@@ -250,35 +254,48 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
250254 </ Grid >
251255 < Grid item xs = { 6 } >
252256 < FormControl fullWidth >
253- < ReceiptFileInput />
254- < input
255- onChange = { ( e ) => {
256- if ( e . target . files ) {
257- const file = e . target . files [ 0 ] ;
258- if ( file . size < 1000000 ) {
259- receiptAppend ( {
260- file : e . target . files [ 0 ] ,
261- name : e . target . files [ 0 ] . name ,
262- googleFileId : ''
257+ < FormLabel > Receipts</ FormLabel >
258+ < Button
259+ variant = "contained"
260+ color = "success"
261+ component = "label"
262+ startIcon = { < FileUploadIcon /> }
263+ sx = { {
264+ width : 'fit-content' ,
265+ textTransform : 'none' ,
266+ mt : '9.75px'
267+ } }
268+ >
269+ Upload
270+ < input
271+ onChange = { ( e ) => {
272+ if ( e . target . files ) {
273+ [ ...e . target . files ] . forEach ( ( file ) => {
274+ if ( file . size < 1000000 ) {
275+ receiptPrepend ( {
276+ file : file ,
277+ name : file . name ,
278+ googleFileId : ''
279+ } ) ;
280+ } else {
281+ toast . error ( `Error uploading ${ file . name } ; file must be less than 1 MB` , 5000 ) ;
282+ document . getElementById ( 'receipt-image' ) ! . innerHTML = '' ;
283+ }
263284 } ) ;
264- } else {
265- toast . error ( 'File must be less than 1 MB' , 5000 ) ;
266- document . getElementById ( 'receipt-image' ) ! . innerHTML = '' ;
267285 }
268- }
269- } }
270- type = "file"
271- id = "receipt-image"
272- accept = "image/png, image/jpeg, .pdf"
273- name = "receiptFiles"
274- />
286+ } }
287+ type = "file"
288+ id = "receipt-image"
289+ accept = "image/png, image/jpeg, .pdf"
290+ name = "receiptFiles"
291+ multiple
292+ hidden
293+ />
294+ </ Button >
295+ < ReceiptFileInput />
275296 < FormHelperText error > { errors . receiptFiles ?. message } </ FormHelperText >
276297 </ FormControl >
277298 </ Grid >
278- < Grid item xs = { 12 } >
279- < FormLabel > Total Cost</ FormLabel >
280- < Typography variant = "h6" > ${ calculatedTotalCost } </ Typography >
281- </ Grid >
282299 </ Grid >
283300 < Grid item md = { 6 } xs = { 12 } sx = { { '&.MuiGrid-item' : { paddingTop : '4px' } } } >
284301 < FormControl fullWidth >
@@ -295,13 +312,30 @@ const ReimbursementRequestFormView: React.FC<ReimbursementRequestFormViewProps>
295312 </ FormControl >
296313 </ Grid >
297314 </ Grid >
298- < Box sx = { { display : 'flex' , justifyContent : 'flex-end' , mt : 2 } } >
299- < NERFailButton variant = "contained" href = { previousPage } sx = { { mx : 1 } } >
300- Cancel
301- </ NERFailButton >
302- < NERSuccessButton variant = "contained" type = "submit" disabled = { ! hasSecureSettingsSet } >
303- { submitText }
304- </ NERSuccessButton >
315+ < Box
316+ sx = { {
317+ position : 'sticky' ,
318+ bottom : 0 ,
319+ background : theme . palette . background . default ,
320+ p : 1 ,
321+ borderTop : `solid 1px ${ theme . palette . divider } ` ,
322+ zIndex : 1 ,
323+ display : 'flex' ,
324+ justifyContent : 'space-between'
325+ } }
326+ >
327+ < Box >
328+ < FormLabel > Total Cost</ FormLabel >
329+ < Typography variant = "h6" > ${ calculatedTotalCost } </ Typography >
330+ </ Box >
331+ < Box sx = { { display : 'flex' , alignSelf : 'center' } } >
332+ < NERFailButton variant = "contained" href = { previousPage } sx = { { mx : 1 } } >
333+ Cancel
334+ </ NERFailButton >
335+ < NERSuccessButton variant = "contained" type = "submit" disabled = { ! hasSecureSettingsSet } >
336+ { submitText }
337+ </ NERSuccessButton >
338+ </ Box >
305339 </ Box >
306340 </ form >
307341 ) ;
0 commit comments