Skip to content

Commit fda9002

Browse files
Merge pull request #2416 from zetkin/undocumented/join-form-smart-search
Join form smart search
2 parents e5e6a76 + 04c4d35 commit fda9002

File tree

9 files changed

+238
-7
lines changed

9 files changed

+238
-7
lines changed

src/features/smartSearch/components/SmartSearchDialog/FilterEditor.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import CampaignParticipation from '../filters/CampaignParticipation';
44
import EmailBlacklist from '../filters/EmailBlacklist';
55
import EmailClick from '../filters/EmailClick';
66
import EmailHistory from '../filters/EmailHistory';
7+
import JoinFormFilter from '../filters/JoinForm';
78
import Journey from '../filters/Journey';
89
import MostActive from '../filters/MostActive';
910
import PersonData from '../filters/PersonData';
@@ -166,6 +167,13 @@ const FilterEditor = ({
166167
onSubmit={onSubmitFilter}
167168
/>
168169
)}
170+
{filter.type === FILTER_TYPE.JOINFORM && (
171+
<JoinFormFilter
172+
filter={filter}
173+
onCancel={onCancelSubmitFilter}
174+
onSubmit={onSubmitFilter}
175+
/>
176+
)}
169177
{filter.type === FILTER_TYPE.JOURNEY && (
170178
<Journey
171179
filter={filter}

src/features/smartSearch/components/SmartSearchDialog/FilterGallery/filterGalleryPattern.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,10 @@ export default function filterGalleryPattern(
123123
pattern = PATTERN_TEMPLATES.pattern15;
124124
}
125125

126-
pattern.background = pattern.background.replaceAll(
127-
'$strongColor',
128-
colors.strong
129-
);
130-
pattern.background = pattern.background.replaceAll('$paleColor', colors.pale);
131-
return pattern;
126+
return {
127+
...pattern,
128+
background: pattern.background
129+
.replaceAll('$strongColor', colors.strong)
130+
.replaceAll('$paleColor', colors.pale),
131+
};
132132
}

src/features/smartSearch/components/SmartSearchDialog/FilterGallery/groupedFilters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,6 @@ export const GROUPED_FILTERS: {
8282
pale: filterCategoryColors.red.pale,
8383
strong: filterCategoryColors.red.strong,
8484
},
85-
filters: [FILTER_TYPE.RANDOM, FILTER_TYPE.USER],
85+
filters: [FILTER_TYPE.JOINFORM, FILTER_TYPE.RANDOM, FILTER_TYPE.USER],
8686
},
8787
};

src/features/smartSearch/components/SmartSearchDialog/QueryOverview/getFilterComponents.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
Block,
77
Call,
88
CheckBoxOutlined,
9+
DoorFrontOutlined,
910
DraftsOutlined,
1011
Event,
1112
ExploreOutlined,
@@ -50,6 +51,7 @@ import {
5051
EmailClickFilterConfig,
5152
EmailHistoryFilterConfig,
5253
FILTER_TYPE,
54+
JoinFormFilterConfig,
5355
JourneyFilterConfig,
5456
MostActiveFilterConfig,
5557
OPERATION,
@@ -66,6 +68,7 @@ import {
6668
TaskFilterConfig,
6769
UserFilterConfig,
6870
} from 'features/smartSearch/components/types';
71+
import DisplayJoinForm from '../../filters/JoinForm/DisplayJoinForm';
6972

7073
export default function getFilterComponents(
7174
filter: SmartSearchFilterWithId<AnyFilterConfig>
@@ -219,6 +222,13 @@ export default function getFilterComponents(
219222
filterTypeIcon = (
220223
<AccountCircleOutlined color="secondary" fontSize="small" />
221224
);
225+
} else if (filter.type == FILTER_TYPE.JOINFORM) {
226+
displayFilter = (
227+
<DisplayJoinForm
228+
filter={filter as SmartSearchFilterWithId<JoinFormFilterConfig>}
229+
/>
230+
);
231+
filterTypeIcon = <DoorFrontOutlined color="secondary" fontSize="small" />;
222232
}
223233

224234
return {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { FC } from 'react';
2+
3+
import {
4+
JoinFormFilterConfig,
5+
OPERATION,
6+
SmartSearchFilterWithId,
7+
} from '../../types';
8+
import { Msg } from 'core/i18n';
9+
import messageIds from 'features/smartSearch/l10n/messageIds';
10+
import UnderlinedMsg from '../../UnderlinedMsg';
11+
import { useNumericRouteParams } from 'core/hooks';
12+
import DisplayJoinFormTitle from './DisplayJoinFormTitle';
13+
import DisplayTimeFrame from '../DisplayTimeFrame';
14+
import { getTimeFrameWithConfig } from '../../utils';
15+
16+
type Props = {
17+
filter: SmartSearchFilterWithId<JoinFormFilterConfig>;
18+
};
19+
20+
const localMessageIds = messageIds.filters.joinForm;
21+
22+
const DisplayJoinForm: FC<Props> = ({ filter }) => {
23+
const { orgId } = useNumericRouteParams();
24+
const op = filter.op || OPERATION.ADD;
25+
const timeFrame = getTimeFrameWithConfig({
26+
after: filter.config.submitted?.after,
27+
before: filter.config.submitted?.before,
28+
});
29+
30+
return (
31+
<Msg
32+
id={localMessageIds.inputString}
33+
values={{
34+
addRemoveSelect: <UnderlinedMsg id={messageIds.operators[op]} />,
35+
formSelect: filter.config.form ? (
36+
<DisplayJoinFormTitle formId={filter.config.form} orgId={orgId} />
37+
) : (
38+
<UnderlinedMsg id={localMessageIds.anyForm} />
39+
),
40+
timeFrame: <DisplayTimeFrame config={timeFrame} />,
41+
}}
42+
/>
43+
);
44+
};
45+
46+
export default DisplayJoinForm;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { FC } from 'react';
2+
3+
import messageIds from 'features/smartSearch/l10n/messageIds';
4+
import useJoinForm from 'features/joinForms/hooks/useJoinForm';
5+
import UnderlinedMsg from '../../UnderlinedMsg';
6+
7+
type Props = {
8+
formId: number;
9+
orgId: number;
10+
};
11+
12+
const localMessageIds = messageIds.filters.joinForm;
13+
14+
const DisplayJoinFormTitle: FC<Props> = ({ formId, orgId }) => {
15+
const { data } = useJoinForm(orgId, formId);
16+
17+
return (
18+
<UnderlinedMsg
19+
id={localMessageIds.form}
20+
values={{ title: data?.title || '' }}
21+
/>
22+
);
23+
};
24+
25+
export default DisplayJoinFormTitle;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { FC, FormEvent } from 'react';
2+
import { MenuItem } from '@mui/material';
3+
4+
import {
5+
JoinFormFilterConfig,
6+
NewSmartSearchFilter,
7+
OPERATION,
8+
SmartSearchFilterWithId,
9+
TIME_FRAME,
10+
ZetkinSmartSearchFilter,
11+
} from '../../types';
12+
import FilterForm from '../../FilterForm';
13+
import { Msg } from 'core/i18n';
14+
import messageIds from 'features/smartSearch/l10n/messageIds';
15+
import StyledSelect from '../../inputs/StyledSelect';
16+
import useSmartSearchFilter from 'features/smartSearch/hooks/useSmartSearchFilter';
17+
import TimeFrame from '../TimeFrame';
18+
import useJoinForms from 'features/joinForms/hooks/useJoinForms';
19+
import { useNumericRouteParams } from 'core/hooks';
20+
21+
type Props = {
22+
filter: SmartSearchFilterWithId<JoinFormFilterConfig> | NewSmartSearchFilter;
23+
onCancel: () => void;
24+
onSubmit: (
25+
filter:
26+
| SmartSearchFilterWithId<JoinFormFilterConfig>
27+
| ZetkinSmartSearchFilter<JoinFormFilterConfig>
28+
) => void;
29+
};
30+
31+
const localMessageIds = messageIds.filters.joinForm;
32+
33+
const JoinFormFilter: FC<Props> = ({
34+
filter: initialFilter,
35+
onSubmit,
36+
onCancel,
37+
}) => {
38+
const { orgId } = useNumericRouteParams();
39+
const forms = useJoinForms(orgId).data || [];
40+
const { filter, setConfig, setOp } =
41+
useSmartSearchFilter<JoinFormFilterConfig>(initialFilter, {});
42+
43+
const handleSubmit = (e: FormEvent) => {
44+
e.preventDefault();
45+
onSubmit(filter);
46+
};
47+
48+
return (
49+
<FilterForm
50+
onCancel={onCancel}
51+
onSubmit={handleSubmit}
52+
renderSentence={() => (
53+
<Msg
54+
id={localMessageIds.inputString}
55+
values={{
56+
addRemoveSelect: (
57+
<StyledSelect
58+
onChange={(e) => setOp(e.target.value as OPERATION)}
59+
value={filter.op}
60+
>
61+
{Object.values(OPERATION).map((o) => (
62+
<MenuItem key={o} value={o}>
63+
<Msg id={messageIds.operators[o]} />
64+
</MenuItem>
65+
))}
66+
</StyledSelect>
67+
),
68+
formSelect: (
69+
<StyledSelect
70+
onChange={(e) => {
71+
const formId = parseInt(e.target.value);
72+
const config = { ...filter.config };
73+
if (formId) {
74+
config.form = formId;
75+
} else {
76+
delete config.form;
77+
}
78+
79+
setConfig(config);
80+
}}
81+
value={filter.config.form || 'any'}
82+
>
83+
<MenuItem value="any">
84+
<Msg id={localMessageIds.anyForm} />
85+
</MenuItem>
86+
{forms.map((form) => (
87+
<MenuItem key={form.id} value={form.id}>
88+
{form.title}
89+
</MenuItem>
90+
))}
91+
</StyledSelect>
92+
),
93+
timeFrame: (
94+
<TimeFrame
95+
filterConfig={filter.config.submitted || {}}
96+
onChange={(range) =>
97+
setConfig({ ...filter.config, submitted: range })
98+
}
99+
options={[
100+
TIME_FRAME.AFTER_DATE,
101+
TIME_FRAME.BEFORE_DATE,
102+
TIME_FRAME.BEFORE_TODAY,
103+
TIME_FRAME.BETWEEN,
104+
TIME_FRAME.EVER,
105+
TIME_FRAME.LAST_FEW_DAYS,
106+
]}
107+
/>
108+
),
109+
}}
110+
/>
111+
)}
112+
/>
113+
);
114+
};
115+
116+
export default JoinFormFilter;

src/features/smartSearch/components/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export enum FILTER_TYPE {
2020
EMAIL_BLACKLIST = 'email_blacklist',
2121
EMAIL_CLICK = 'email_click',
2222
EMAIL_HISTORY = 'email_history',
23+
JOINFORM = 'joinform',
2324
JOURNEY = 'journey_subjects',
2425
MOST_ACTIVE = 'most_active',
2526
PERSON_DATA = 'person_data',
@@ -160,6 +161,15 @@ export interface EmailHistoryFilterConfig {
160161
email?: number;
161162
operator: 'sent' | 'not_sent' | 'opened' | 'not_opened';
162163
}
164+
165+
export interface JoinFormFilterConfig {
166+
form?: number;
167+
submitted?: {
168+
after?: string;
169+
before?: string;
170+
};
171+
}
172+
163173
export interface MostActiveFilterConfig {
164174
after?: string;
165175
before?: string;
@@ -325,6 +335,7 @@ export type AnyFilterConfig =
325335
| CampaignParticipationConfig
326336
| DefaultFilterConfig
327337
| EmailBlacklistFilterConfig
338+
| JoinFormFilterConfig
328339
| MostActiveFilterConfig
329340
| PersonDataFilterConfig
330341
| PersonFieldFilterConfig

src/features/smartSearch/l10n/messageIds.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ export default makeMessages('feat.smartSearch', {
7575
description: m('Who was sent what, when?'),
7676
title: m('Based on their email history'),
7777
},
78+
joinform: {
79+
description: m('Find people who came in through a join form.'),
80+
title: m('Based on join form source'),
81+
},
7882
journey_subjects: {
7983
description: m(
8084
'Find people who are on a journey or finished it already'
@@ -301,6 +305,17 @@ export default makeMessages('feat.smartSearch', {
301305
sent: m('been sent'),
302306
},
303307
},
308+
joinForm: {
309+
anyForm: m('any join form'),
310+
form: m<{ title: string }>('"{title}"'),
311+
inputString: m<{
312+
addRemoveSelect: ReactElement;
313+
formSelect: ReactElement;
314+
timeFrame: ReactElement;
315+
}>(
316+
'{addRemoveSelect} people who came in through {formSelect} {timeFrame}'
317+
),
318+
},
304319
journey: {
305320
condition: {
306321
conditionSelect: {

0 commit comments

Comments
 (0)