Skip to content

Commit

Permalink
feat(form-admin-app): support data value search
Browse files Browse the repository at this point in the history
Also include criteria in export request.
  • Loading branch information
tzuge committed Jan 6, 2025
1 parent e9e3f19 commit 7688a7d
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import styled from 'styled-components';

export const SearchFormItemsContainer = styled.div`
display: flex;
flex-direction: row;
`;
88 changes: 57 additions & 31 deletions apps/form-admin-app/src/app/containers/FormSubmissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
GoADropdown,
GoADropdownItem,
GoAFormItem,
GoAInput,
GoATable,
} from '@abgov/react-components-new';
import { useDispatch, useSelector } from 'react-redux';
Expand All @@ -27,6 +28,7 @@ import { SearchLayout } from '../components/SearchLayout';
import { DataValueCell } from '../components/DataValueCell';
import { Digest } from '../components/Digest';
import { ExportModal } from '../components/ExportModal';
import { SearchFormItemsContainer } from '../components/SearchFormItemsContainer';

interface FormSubmissionsProps {
definitionId: string;
Expand All @@ -41,7 +43,7 @@ export const FormSubmissions: FunctionComponent<FormSubmissionsProps> = ({ defin
const isFormAdmin = useSelector(isFormAdminSelector);
const busy = useSelector(busySelector);
const submissions = useSelector(submissionsSelector);
const columns = useSelector(selectedDataValuesSelector);
const dataValues = useSelector(selectedDataValuesSelector);
const criteria = useSelector(submissionCriteriaSelector);
const { submissions: next } = useSelector(nextSelector);
const submissionsExport = useSelector(submissionsExportSelector);
Expand All @@ -57,32 +59,56 @@ export const FormSubmissions: FunctionComponent<FormSubmissionsProps> = ({ defin
<SearchLayout
searchForm={
<form>
<GoAFormItem label="Disposition">
<GoADropdown
relative={true}
name="submission-disposition"
value={
typeof criteria['dispositioned'] !== 'boolean'
? 'all'
: criteria['dispositioned'] === true
? 'dispositioned'
: 'not dispositioned'
}
onChange={(_, values) =>
dispatch(
formActions.setSubmissionCriteria({
...criteria,
dispositioned: values === 'all' ? undefined : values === 'dispositioned',
})
)
}
>
<GoADropdownItem value="not dispositioned" label="Not dispositioned" />
<GoADropdownItem value="dispositioned" label="Dispositioned" />
<GoADropdownItem value="all" label="All" />
</GoADropdown>
</GoAFormItem>
<GoAButtonGroup alignment="end">
<SearchFormItemsContainer>
<GoAFormItem label="Disposition">
<GoADropdown
relative={true}
name="submission-disposition"
value={
typeof criteria['dispositioned'] !== 'boolean'
? 'all'
: criteria['dispositioned'] === true
? 'dispositioned'
: 'not dispositioned'
}
onChange={(_, values) =>
dispatch(
formActions.setSubmissionCriteria({
...criteria,
dispositioned: values === 'all' ? undefined : values === 'dispositioned',
})
)
}
>
<GoADropdownItem value="not dispositioned" label="Not dispositioned" />
<GoADropdownItem value="dispositioned" label="Dispositioned" />
<GoADropdownItem value="all" label="All" />
</GoADropdown>
</GoAFormItem>
{dataValues
.filter(({ type }) => type === 'string')
.map(({ name, path }) => (
<GoAFormItem label={name} key={path} ml="m">
<GoAInput
type="text"
onChange={(_, value: string) =>
dispatch(
formActions.setSubmissionCriteria({
...criteria,
dataCriteria: {
...criteria?.dataCriteria,
[path]: value,
},
})
)
}
value={criteria?.dataCriteria?.[path]?.toString() || ''}
name={name}
/>
</GoAFormItem>
))}
</SearchFormItemsContainer>
<GoAButtonGroup alignment="end" mt="l">
{isFormAdmin && (
<GoAButton type="tertiary" mr="xl" onClick={() => setShowExport(true)}>
Export to file
Expand Down Expand Up @@ -113,7 +139,7 @@ export const FormSubmissions: FunctionComponent<FormSubmissionsProps> = ({ defin
<th>Submitted on</th>
<th>Digest</th>
<th>Disposition</th>
{columns.map(({ name }) => (
{dataValues.map(({ name }) => (
<th>{name}</th>
))}
<th>Actions</th>
Expand All @@ -127,7 +153,7 @@ export const FormSubmissions: FunctionComponent<FormSubmissionsProps> = ({ defin
<Digest value={submission.hash} />
</td>
<td>{submission.disposition?.status}</td>
{columns.map(({ path }) => (
{dataValues.map(({ path }) => (
<DataValueCell key={path}>{submission.values[path]}</DataValueCell>
))}
<td>
Expand All @@ -140,7 +166,7 @@ export const FormSubmissions: FunctionComponent<FormSubmissionsProps> = ({ defin
</tr>
))}
{next && (
<td colSpan={4 + columns.length}>
<td colSpan={4 + dataValues.length}>
<GoAButtonGroup alignment="center">
<GoAButton
type="tertiary"
Expand All @@ -160,7 +186,7 @@ export const FormSubmissions: FunctionComponent<FormSubmissionsProps> = ({ defin
heading="Export submissions to file"
state={submissionsExport}
onClose={() => setShowExport(false)}
onStartExport={() => dispatch(exportSubmissions(definitionId))}
onStartExport={() => dispatch(exportSubmissions({ definitionId, criteria }))}
/>
</SearchLayout>
);
Expand Down
76 changes: 51 additions & 25 deletions apps/form-admin-app/src/app/containers/Forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
GoADropdown,
GoADropdownItem,
GoAFormItem,
GoAInput,
GoATable,
} from '@abgov/react-components-new';
import { useNavigate } from 'react-router-dom';
Expand All @@ -26,6 +27,7 @@ import { SearchLayout } from '../components/SearchLayout';
import { ContentContainer } from '../components/ContentContainer';
import { DataValueCell } from '../components/DataValueCell';
import { ExportModal } from '../components/ExportModal';
import { SearchFormItemsContainer } from '../components/SearchFormItemsContainer';

interface FormsProps {
definitionId: string;
Expand All @@ -40,7 +42,7 @@ export const Forms: FunctionComponent<FormsProps> = ({ definitionId }) => {
const isFormAdmin = useSelector(isFormAdminSelector);
const busy = useSelector(busySelector);
const forms = useSelector(formsSelector);
const columns = useSelector(selectedDataValuesSelector);
const dataValues = useSelector(selectedDataValuesSelector);
const criteria = useSelector(formCriteriaSelector);
const { forms: next } = useSelector(nextSelector);
const formsExport = useSelector(formsExportSelector);
Expand All @@ -56,26 +58,50 @@ export const Forms: FunctionComponent<FormsProps> = ({ definitionId }) => {
<SearchLayout
searchForm={
<form>
<GoAFormItem label="Status">
<GoADropdown
relative={true}
name="form-status"
value={criteria.statusEquals}
onChange={(_, value: string) =>
dispatch(
formActions.setFormCriteria({
...criteria,
statusEquals: value,
})
)
}
>
<GoADropdownItem value="submitted" label="Submitted" />
<GoADropdownItem value="draft" label="Draft" />
<GoADropdownItem value={null} label="All" />
</GoADropdown>
</GoAFormItem>
<GoAButtonGroup alignment="end">
<SearchFormItemsContainer>
<GoAFormItem label="Status">
<GoADropdown
relative={true}
name="form-status"
value={criteria.statusEquals}
onChange={(_, value: string) =>
dispatch(
formActions.setFormCriteria({
...criteria,
statusEquals: value,
})
)
}
>
<GoADropdownItem value="submitted" label="Submitted" />
<GoADropdownItem value="draft" label="Draft" />
<GoADropdownItem value={null} label="All" />
</GoADropdown>
</GoAFormItem>
{dataValues
.filter(({ type }) => type === 'string')
.map(({ name, path }) => (
<GoAFormItem label={name} key={path} ml="m">
<GoAInput
type="text"
onChange={(_, value: string) =>
dispatch(
formActions.setFormCriteria({
...criteria,
dataCriteria: {
...criteria?.dataCriteria,
[path]: value,
},
})
)
}
value={criteria?.dataCriteria?.[path]?.toString() || ''}
name={name}
/>
</GoAFormItem>
))}
</SearchFormItemsContainer>
<GoAButtonGroup alignment="end" mt="l">
{isFormAdmin && (
<GoAButton type="tertiary" mr="xl" onClick={() => setShowExport(true)}>
Export to file
Expand Down Expand Up @@ -105,7 +131,7 @@ export const Forms: FunctionComponent<FormsProps> = ({ definitionId }) => {
<tr>
<th>Created on</th>
<th>Status</th>
{columns.map(({ name }) => (
{dataValues.map(({ name }) => (
<th>{name}</th>
))}
<th>Actions</th>
Expand All @@ -116,7 +142,7 @@ export const Forms: FunctionComponent<FormsProps> = ({ definitionId }) => {
<tr key={form.urn}>
<td>{form.created.toFormat('LLL dd, yyyy')}</td>
<td>{form.status}</td>
{columns.map(({ path }) => (
{dataValues.map(({ path }) => (
<DataValueCell key={path}>{form.values[path]}</DataValueCell>
))}
<td>
Expand All @@ -129,7 +155,7 @@ export const Forms: FunctionComponent<FormsProps> = ({ definitionId }) => {
</tr>
))}
{next && (
<td colSpan={3 + columns.length}>
<td colSpan={3 + dataValues.length}>
<GoAButtonGroup alignment="center">
<GoAButton
type="tertiary"
Expand All @@ -149,7 +175,7 @@ export const Forms: FunctionComponent<FormsProps> = ({ definitionId }) => {
heading="Export forms to file"
state={formsExport}
onClose={() => setShowExport(false)}
onStartExport={() => dispatch(exportForms(definitionId))}
onStartExport={() => dispatch(exportForms({ definitionId, criteria }))}
/>
</SearchLayout>
);
Expand Down
19 changes: 13 additions & 6 deletions apps/form-admin-app/src/app/state/form.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ interface FormSubmissionCriteria {
dispositioned?: boolean;
createdAfter?: string;
createdBefore?: string;
dataCriteria?: Record<string, unknown>;
}

interface FormCriteria {
statusEquals: string;
createdAfter?: string;
createdBefore?: string;
dataCriteria?: Record<string, unknown>;
}

interface DataValue {
Expand Down Expand Up @@ -514,7 +516,10 @@ export const getExportJobStatus = createAsyncThunk(

export const exportForms = createAsyncThunk(
'form/export-forms',
async (definitionId: string, { dispatch, getState, rejectWithValue }) => {
async (
{ definitionId, criteria }: { definitionId: string; criteria: FormCriteria },
{ dispatch, getState, rejectWithValue }
) => {
try {
const { config } = getState() as AppState;
const exportServiceUrl = config.directory[EXPORT_SERVICE_ID];
Expand All @@ -529,6 +534,7 @@ export const exportForms = createAsyncThunk(
params: {
includeData: true,
criteria: JSON.stringify({
...criteria,
definitionIdEquals: definitionId,
}),
},
Expand Down Expand Up @@ -556,7 +562,7 @@ export const exportForms = createAsyncThunk(

export const exportSubmissions = createAsyncThunk(
'form/export-submissions',
async (definitionId: string, { dispatch, getState, rejectWithValue }) => {
async ({ definitionId, criteria }: { definitionId: string; criteria: FormSubmissionCriteria }, { dispatch, getState, rejectWithValue }) => {
try {
const { config } = getState() as AppState;
const exportServiceUrl = config.directory[EXPORT_SERVICE_ID];
Expand All @@ -570,6 +576,7 @@ export const exportSubmissions = createAsyncThunk(
fileType: 'form-export',
params: {
criteria: JSON.stringify({
...criteria,
definitionIdEquals: definitionId,
}),
},
Expand Down Expand Up @@ -733,10 +740,10 @@ export const formSlice = createSlice({
})
.addCase(exportForms.pending, (state, { meta }) => {
state.busy.exporting = true;
state.export.forms = { definitionId: meta.arg };
state.export.forms = { definitionId: meta.arg.definitionId };
})
.addCase(exportForms.fulfilled, (state, { payload, meta }) => {
if (state.export.forms.definitionId === meta.arg && !state.export.forms.jobId) {
if (state.export.forms.definitionId === meta.arg.definitionId && !state.export.forms.jobId) {
state.export.forms.jobId = payload.id;
}
})
Expand All @@ -745,10 +752,10 @@ export const formSlice = createSlice({
})
.addCase(exportSubmissions.pending, (state, { meta }) => {
state.busy.exporting = true;
state.export.submissions = { definitionId: meta.arg };
state.export.submissions = { definitionId: meta.arg.definitionId };
})
.addCase(exportSubmissions.fulfilled, (state, { payload, meta }) => {
if (state.export.submissions.definitionId === meta.arg && !state.export.submissions.jobId) {
if (state.export.submissions.definitionId === meta.arg.definitionId && !state.export.submissions.jobId) {
state.export.submissions.jobId = payload.id;
}
})
Expand Down
4 changes: 3 additions & 1 deletion apps/form-service/src/mongo/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ export class MongoFormRepository implements FormRepository {
}

if (criteria?.dataCriteria) {
query.data = criteria?.dataCriteria;
Object.entries(criteria.dataCriteria).forEach((property, value) => {
query[`data.${property}`] = value;
});
}

return new Promise<FormEntity[]>((resolve, reject) => {
Expand Down
4 changes: 3 additions & 1 deletion apps/form-service/src/mongo/formSubmission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ export class MongoFormSubmissionRepository implements FormSubmissionRepository {
}

if (criteria?.dataCriteria) {
query.formData = criteria?.dataCriteria;
Object.entries(criteria.dataCriteria).forEach((property, value) => {
query[`formData.${property}`] = value;
});
}

return new Promise<FormSubmissionEntity[]>((resolve, reject) => {
Expand Down

0 comments on commit 7688a7d

Please sign in to comment.