generated from Code-4-Community/frontend-scaffold
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added component for filter * Add skeleton component, params, api route to client, basic outline * Fix compilation errors * Fix site id * Add list view of image * Cannot read properties of undefined map * Start selection * Tested filter site images * Fix null * Cleanup * Add frontend implementation of approving and rejecting images * Address review comments * fix translation changes * Add loading functionality and clear filters functionality * working: translations + styling + tests * working: need to finalize design * tests + cleanup * tests * disable submit when no reason * initial value * remove usecallback + switch to dropdown * Fix so that the table works with no unapproved images * correct reject route * Added reason --------- Co-authored-by: SurabhiKeesara <surabhi.keesara@gmail> Co-authored-by: Avery Huang <[email protected]>
- Loading branch information
1 parent
9f879f5
commit d9182fd
Showing
9 changed files
with
614 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
import moment from 'moment'; | ||
import { EmailerFilters } from '../../containers/email/types'; | ||
import { | ||
Collapse, | ||
Slider, | ||
DatePicker, | ||
Select, | ||
message, | ||
SelectProps, | ||
} from 'antd'; | ||
import { SliderMarks } from 'antd/lib/slider'; | ||
import { Neighborhoods } from '../../assets/content'; | ||
import apiClient from '../../api/apiClient'; | ||
import { formatActivityCountRange } from '../../utils/stringFormat'; | ||
import styled from 'styled-components'; | ||
import { ReviewImageFilters } from '../../containers/reviewImages/types'; | ||
import { DefaultOptionType } from 'antd/es/select'; | ||
|
||
const AutoCompleteSelect = styled((props: SelectProps) => ( | ||
<Select {...props} /> | ||
))` | ||
min-width: 200px; | ||
max-width: 500px; | ||
`; | ||
|
||
const MAX_ACTIVITY_COUNT = 10; | ||
|
||
// convert emailer filter values to the slider's internal values | ||
function activityCountRange(filters: EmailerFilters): [number, number] { | ||
return [ | ||
filters.activityCountMin, | ||
filters.activityCountMax ?? MAX_ACTIVITY_COUNT + 1, | ||
]; | ||
} | ||
|
||
function formatDates( | ||
start: string | null, | ||
end: string | null, | ||
): [moment.Moment | null, moment.Moment | null] { | ||
return [start ? moment(start) : null, end ? moment(end) : null]; | ||
} | ||
|
||
function disabledDate(current: moment.Moment): boolean { | ||
// Can not select future days | ||
return current > moment().endOf('day'); | ||
} | ||
|
||
const neighborhoodOptions = Object.values(Neighborhoods) | ||
.sort() | ||
.map((value) => { | ||
return { label: value, value }; | ||
}); | ||
|
||
interface UnapprovedFilterImageFilterControlsProps { | ||
filters: ReviewImageFilters; | ||
setFilters: React.Dispatch<React.SetStateAction<ReviewImageFilters>>; | ||
} | ||
|
||
const UnapprovedFilterImageControls: React.FC< | ||
UnapprovedFilterImageFilterControlsProps | ||
> = ({ filters, setFilters }) => { | ||
const [siteIdOptions, setSiteIdOptions] = useState< | ||
{ label: string; value: string }[] | ||
>([]); | ||
|
||
const [siteIdInvalidOrNone, setSiteIdError] = useState<{ | ||
status: '' | 'warning' | 'error' | undefined; | ||
value: string; | ||
}>({ status: '', value: 'Enter a site id' }); | ||
|
||
// TODO: verify what Tree ID means | ||
return ( | ||
<Collapse ghost> | ||
<Collapse.Panel header="Date Submitted" key="submittedDate"> | ||
<DatePicker.RangePicker | ||
allowEmpty={[true, true]} | ||
value={formatDates(filters.submittedStart, filters.submittedEnd)} | ||
onChange={(_, dateStrings) => | ||
setFilters({ | ||
...filters, | ||
submittedStart: dateStrings[0] || null, | ||
submittedEnd: dateStrings[1] || null, | ||
}) | ||
} | ||
disabledDate={disabledDate} | ||
/> | ||
</Collapse.Panel> | ||
<Collapse.Panel header="Neighborhood" key="neighborhood"> | ||
<AutoCompleteSelect | ||
value={filters.neighborhoods} | ||
mode="multiple" | ||
allowClear | ||
placeholder="Enter a neighborhood" | ||
onChange={(value: Neighborhoods[]) => | ||
setFilters({ ...filters, neighborhoods: value }) | ||
} | ||
options={neighborhoodOptions} | ||
/> | ||
</Collapse.Panel> | ||
<Collapse.Panel header="Tree ID" key="siteId"> | ||
<AutoCompleteSelect | ||
value={filters.siteIds} | ||
mode="multiple" | ||
allowClear | ||
placeholder="Enter a site" | ||
onChange={(value: number[]) => { | ||
let valueInt = value.filter((v) => !isNaN(Number(v))); | ||
valueInt = valueInt.map((v) => Number(v)); | ||
setFilters({ ...filters, siteIds: valueInt }); | ||
}} | ||
status={siteIdInvalidOrNone.status} | ||
onSearch={(e) => { | ||
const numVal = Number(e); | ||
if (numVal || e.length === 0) { | ||
setSiteIdError({ | ||
status: '', | ||
value: '', | ||
}); | ||
if (e.length > 0) setSiteIdOptions([{ label: e, value: e }]); | ||
} else if (e === '') { | ||
setSiteIdError({ | ||
status: '', | ||
value: 'Enter a site id', | ||
}); | ||
} else { | ||
setSiteIdError({ | ||
status: 'error', | ||
value: 'No data. Site IDs must be strings', | ||
}); | ||
} | ||
}} | ||
options={siteIdOptions} | ||
notFoundContent={siteIdInvalidOrNone.value} | ||
/> | ||
</Collapse.Panel> | ||
</Collapse> | ||
); | ||
}; | ||
|
||
export default UnapprovedFilterImageControls; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import React, { SetStateAction, useMemo, useState } from 'react'; | ||
import { Table } from 'antd'; | ||
import type { ColumnsType } from 'antd/es/table'; | ||
import { EmailerTableData, FilteredSite } from '../../containers/email/types'; | ||
import { NEIGHBORHOOD_IDS } from '../../assets/content'; | ||
import { | ||
FilteredSiteImage, | ||
FilterImageTableData, | ||
} from '../../containers/reviewImages/types'; | ||
|
||
interface UnapprovedImagesTable { | ||
readonly fetchData: FilteredSiteImage[]; | ||
readonly setSelectedImageIds: React.Dispatch<SetStateAction<number[]>>; | ||
} | ||
|
||
const columns: ColumnsType<FilterImageTableData> = [ | ||
{ | ||
title: 'Preview', | ||
dataIndex: 'preview_url', | ||
key: 'preview_url', | ||
render: (dataIndexValue, record) => { | ||
return <img style={{ height: 60, width: 60 }} src={record.preview}></img>; | ||
}, | ||
}, | ||
{ | ||
title: 'Site ID', | ||
dataIndex: 'siteId', | ||
key: 'siteId', | ||
}, | ||
{ | ||
title: 'Species', | ||
dataIndex: 'species', | ||
key: 'species', | ||
}, | ||
{ | ||
title: 'Neighborhood', | ||
dataIndex: 'neighborhood', | ||
key: 'neighborhood', | ||
}, | ||
{ | ||
title: 'Date Submitted', | ||
dataIndex: 'dateSubmitted', | ||
key: 'dateSubmitted', | ||
}, | ||
]; | ||
|
||
function responseToTableData( | ||
data: FilteredSiteImage, | ||
index: number, | ||
): FilterImageTableData { | ||
return { | ||
key: data.imageId, | ||
preview: data.imageUrl, | ||
siteId: data.siteId, | ||
dateSubmitted: data.dateSubmitted, | ||
species: data.commonName, | ||
neighborhood: NEIGHBORHOOD_IDS[data.neighborhoodId], | ||
}; | ||
} | ||
|
||
const UnapprovedImagesTable: React.FC<UnapprovedImagesTable> = ({ | ||
fetchData, | ||
setSelectedImageIds, | ||
}) => { | ||
const tableData = useMemo( | ||
() => (fetchData ? fetchData.map(responseToTableData) : []), | ||
[fetchData], | ||
); | ||
|
||
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]); | ||
|
||
return ( | ||
<Table | ||
columns={columns} | ||
dataSource={tableData} | ||
size="middle" | ||
rowSelection={{ | ||
selectedRowKeys, | ||
onChange: (_, selectedRows) => { | ||
setSelectedImageIds(selectedRows.map((row) => row.key)); | ||
setSelectedRowKeys(selectedRows.map((row) => row.key)); | ||
}, | ||
}} | ||
/> | ||
); | ||
}; | ||
|
||
export default UnapprovedImagesTable; |
Oops, something went wrong.