Skip to content

Commit c97b40a

Browse files
Merge pull request #2360 from zetkin/release-241118
241118 Release
2 parents 93641b6 + 5ab5734 commit c97b40a

File tree

28 files changed

+988
-284
lines changed

28 files changed

+988
-284
lines changed

src/app/beta/orgs/[orgId]/canvassassignments/[canvassAssId]/areasgraph/route.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export async function GET(request: NextRequest, { params }: RouteMeta) {
3939

4040
const assignmentModel = await CanvassAssignmentModel.findOne({
4141
_id: params.canvassAssId,
42-
});
42+
}).lean();
4343

4444
if (!assignmentModel) {
4545
return new NextResponse(null, { status: 404 });
@@ -51,7 +51,9 @@ export async function GET(request: NextRequest, { params }: RouteMeta) {
5151
const person = await apiClient.get<ZetkinPerson>(
5252
`/api/orgs/${orgId}/people/${sessionData.personId}`
5353
);
54-
const areaModel = await AreaModel.findOne({ _id: sessionData.areaId });
54+
const areaModel = await AreaModel.findOne({
55+
_id: sessionData.areaId,
56+
}).lean();
5557

5658
if (areaModel && person) {
5759
sessions.push({
@@ -88,7 +90,7 @@ export async function GET(request: NextRequest, { params }: RouteMeta) {
8890
...new Map(areas.map((area) => [area.id, area])).values(),
8991
];
9092

91-
const allPlaceModels = await PlaceModel.find({ orgId });
93+
const allPlaceModels = await PlaceModel.find({ orgId }).lean();
9294
const allPlaces: ZetkinPlace[] = allPlaceModels.map((model) => ({
9395
description: model.description,
9496
households: model.households,
@@ -195,7 +197,7 @@ export async function GET(request: NextRequest, { params }: RouteMeta) {
195197
}
196198
});
197199

198-
//rogue visits logic
200+
//Visits outside assigned areas logic
199201
const idsOfPlacesInAreas = new Set(
200202
placesInAreas.map((place) => place.id)
201203
);
@@ -220,12 +222,15 @@ export async function GET(request: NextRequest, { params }: RouteMeta) {
220222
firstVisit,
221223
metricThatDefinesDone || ''
222224
);
223-
const noAreaData = (areaData['noArea'] = {
224-
area: { id: 'noArea', title: 'noArea' },
225-
data: visitsData,
226-
});
227225

228-
areasDataList.push(noAreaData);
226+
if (!areaData['noArea']) {
227+
const noAreaData = (areaData['noArea'] = {
228+
area: { id: 'noArea', title: 'noArea' },
229+
data: visitsData,
230+
});
231+
232+
areasDataList.push(noAreaData);
233+
}
229234
}
230235

231236
const areasDataArray: AreaCardData[] = Object.values(areasDataList);

src/app/beta/orgs/[orgId]/canvassassignments/[canvassAssId]/sessions/route.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,14 @@ export async function GET(request: NextRequest, { params }: RouteMeta) {
3535
const sessions: ZetkinCanvassSession[] = [];
3636

3737
for await (const sessionData of model.sessions) {
38-
const person = await apiClient.get<ZetkinPerson>(
39-
`/api/orgs/${orgId}/people/${sessionData.personId}`
40-
);
38+
let person: ZetkinPerson | null;
39+
try {
40+
person = await apiClient.get<ZetkinPerson>(
41+
`/api/orgs/${orgId}/people/${sessionData.personId}`
42+
);
43+
} catch (err) {
44+
person = null;
45+
}
4146
const area = await AreaModel.findOne({
4247
_id: sessionData.areaId,
4348
});

src/app/beta/orgs/[orgId]/places/[placeId]/households/[householdId]/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ export async function PATCH(request: NextRequest, { params }: RouteMeta) {
2626
const model = await PlaceModel.findOneAndUpdate(
2727
{ _id: params.placeId, orgId },
2828
{
29-
$set: { 'households.$[elem].title': payload.title },
29+
$set: {
30+
'households.$[elem].floor': payload.floor,
31+
'households.$[elem].title': payload.title,
32+
},
3033
},
3134
{
3235
arrayFilters: [{ 'elem.id': { $eq: params.householdId } }],

src/core/rpc/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { getEmailInsightsDef } from 'features/emails/rpc/getEmailInsights';
2020
import { renderEmailDef } from 'features/emails/rpc/renderEmail/server';
2121
import { createCallAssignmentDef } from 'features/callAssignments/rpc/createCallAssignment';
2222
import { getJoinFormEmbedDataDef } from 'features/joinForms/rpc/getJoinFormEmbedData';
23+
import { createHouseholdsDef } from 'features/canvassAssignments/rpc/createHouseholds/server';
2324

2425
export function createRPCRouter() {
2526
const rpcRouter = new RPCRouter();
@@ -45,6 +46,7 @@ export function createRPCRouter() {
4546
rpcRouter.register(renderEmailDef);
4647
rpcRouter.register(createCallAssignmentDef);
4748
rpcRouter.register(getJoinFormEmbedDataDef);
49+
rpcRouter.register(createHouseholdsDef);
4850

4951
return rpcRouter;
5052
}

src/features/areas/components/AreaOverlay/index.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ const AreaOverlay: FC<Props> = ({
106106
<TextField
107107
fullWidth
108108
inputProps={props}
109+
onBlur={() => {
110+
if (fieldEditing === 'title') {
111+
setFieldEditing(null);
112+
updateArea({ title });
113+
}
114+
}}
109115
onChange={(ev) => setTitle(ev.target.value)}
110116
sx={{ marginBottom: 2 }}
111117
value={title}
@@ -146,6 +152,12 @@ const AreaOverlay: FC<Props> = ({
146152
inputRef={handleDescriptionTextAreaRef}
147153
maxRows={4}
148154
multiline
155+
onBlur={() => {
156+
if (fieldEditing === 'description') {
157+
setFieldEditing(null);
158+
updateArea({ description });
159+
}
160+
}}
149161
onChange={(ev) => setDescription(ev.target.value)}
150162
sx={{ marginTop: 2 }}
151163
value={description}

src/features/canvassAssignments/components/AreaCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const AreaCard: FC<AreaCardProps> = ({
5656
x: point.hour !== '0' ? `${point.date} ${point.hour}` : point.date,
5757
y: point.householdVisits,
5858
})),
59-
id: `Household Visits`,
59+
id: `Households Visited`,
6060
};
6161

6262
const successfulVisitsSeries: NivoSeries = {
@@ -139,7 +139,7 @@ const AreaCard: FC<AreaCardProps> = ({
139139
<MapIcon />
140140
</IconButton>
141141
) : (
142-
<Tooltip title="This graph gather the visits made outside the assigned areas">
142+
<Tooltip title="This graph gathers the visits made outside the assigned areas">
143143
<InfoOutlined
144144
sx={{
145145
color: theme.palette.secondary.main,

src/features/canvassAssignments/components/NumberCard.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { FC } from 'react';
2-
import { Card, CardContent, Typography, Box } from '@mui/material';
2+
import { Typography, Box } from '@mui/material';
33

44
import theme from 'theme';
55

@@ -15,14 +15,17 @@ const NumberCard: FC<NumberCardProps> = ({
1515
secondNumber,
1616
}) => {
1717
return (
18-
<Card
18+
<Box
1919
sx={{
20+
display: 'flex',
2021
flex: 1,
21-
textAlign: 'center',
22+
flexDirection: 'row',
23+
margin: 1,
24+
textAlign: 'left',
2225
}}
2326
>
24-
<CardContent>
25-
<Box alignItems="baseline" display="flex" justifyContent="center">
27+
<Box sx={{ display: 'flex', flexDirection: 'column', marginRight: 2 }}>
28+
<Box alignItems="baseline" display="flex" justifyContent="left">
2629
<Typography
2730
sx={{
2831
color: theme.palette.primary.main,
@@ -37,9 +40,11 @@ const NumberCard: FC<NumberCardProps> = ({
3740
/{secondNumber}
3841
</Typography>
3942
</Box>
40-
<Typography sx={{ fontSize: 14 }}>{message}</Typography>
41-
</CardContent>
42-
</Card>
43+
<Box>
44+
<Typography sx={{ fontSize: 14 }}>{message}</Typography>
45+
</Box>
46+
</Box>
47+
</Box>
4348
);
4449
};
4550

src/features/canvassAssignments/components/OrganizerMapRenderer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ const OrganizerMapRenderer: FC<OrganizerMapRendererProps> = ({
479479
>
480480
<ZUIAvatar
481481
size={zoom >= 16 ? 'sm' : 'xs'}
482-
url={`/api/orgs/1/people/${person.id}/avatar`}
482+
url={`/api/orgs/${assignment.organization.id}/people/${person.id}/avatar`}
483483
/>
484484
</Box>
485485
);

src/features/canvassAssignments/components/PlaceDialog/index.tsx

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FC, useState } from 'react';
1+
import { FC, useCallback, useEffect, useRef, useState } from 'react';
22
import { Box } from '@mui/material';
33

44
import VisitWizard from './pages/VisitWizard';
@@ -12,6 +12,7 @@ import {
1212
import usePlaceMutations from 'features/canvassAssignments/hooks/usePlaceMutations';
1313
import ZUINavStack from 'zui/ZUINavStack';
1414
import EditHousehold from './pages/EditHousehold';
15+
import CreateHouseholdsPage from './pages/CreateHouseholdsPage';
1516

1617
type PlaceDialogProps = {
1718
assignment: ZetkinCanvassAssignment;
@@ -23,6 +24,7 @@ type PlaceDialogProps = {
2324
type PlaceDialogStep =
2425
| 'place'
2526
| 'edit'
27+
| 'createHouseholds'
2628
| 'household'
2729
| 'editHousehold'
2830
| 'wizard';
@@ -39,6 +41,45 @@ const PlaceDialog: FC<PlaceDialogProps> = ({
3941
place.id
4042
);
4143

44+
const pushedRef = useRef(false);
45+
46+
const goto = useCallback(
47+
(step: PlaceDialogStep) => {
48+
setDialogStep(step);
49+
history.pushState({ step: step }, '', `?step=${step}`);
50+
},
51+
[setDialogStep]
52+
);
53+
54+
const back = useCallback(() => {
55+
history.back();
56+
}, []);
57+
58+
useEffect(() => {
59+
function handlePopState(event: PopStateEvent) {
60+
if (event.state.step) {
61+
setDialogStep(event.state.step);
62+
} else {
63+
onClose();
64+
}
65+
}
66+
67+
if (typeof window !== 'undefined') {
68+
window.addEventListener('popstate', handlePopState);
69+
70+
return () => {
71+
window.removeEventListener('popstate', handlePopState);
72+
};
73+
}
74+
}, []);
75+
76+
useEffect(() => {
77+
if (!pushedRef.current) {
78+
pushedRef.current = true;
79+
goto('place');
80+
}
81+
}, []);
82+
4283
const [selectedHouseholdId, setSelectedHouseholdId] = useState<string | null>(
4384
null
4485
);
@@ -53,54 +94,63 @@ const PlaceDialog: FC<PlaceDialogProps> = ({
5394
<Place
5495
key="place"
5596
assignment={assignment}
97+
onBulk={() => goto('createHouseholds')}
5698
onClose={onClose}
5799
onCreateHousehold={(household) => {
58100
setSelectedHouseholdId(household.id);
59-
setDialogStep('household');
101+
goto('household');
60102
}}
61-
onEdit={() => setDialogStep('edit')}
103+
onEdit={() => goto('edit')}
62104
onSelectHousehold={(householdId: string) => {
63105
setSelectedHouseholdId(householdId);
64-
setDialogStep('household');
106+
goto('household');
65107
}}
66108
orgId={orgId}
67109
place={place}
68110
/>
69111
<EditPlace
70112
key="edit"
71-
onBack={() => setDialogStep('place')}
113+
onBack={() => back()}
72114
onClose={onClose}
73115
onSave={async (title, description) => {
74116
await updatePlace({ description, title });
75-
setDialogStep('place');
117+
back();
76118
}}
77119
place={place}
78120
/>
79121
<Box key="household" height="100%">
80122
{selectedHousehold && (
81123
<Household
82124
household={selectedHousehold}
83-
onBack={() => setDialogStep('place')}
125+
onBack={() => back()}
84126
onClose={onClose}
85-
onEdit={() => setDialogStep('editHousehold')}
127+
onEdit={() => goto('editHousehold')}
86128
onWizardStart={() => {
87-
setDialogStep('wizard');
129+
goto('wizard');
88130
}}
89131
visitedInThisAssignment={selectedHousehold.visits.some(
90132
(visit) => visit.canvassAssId == assignment.id
91133
)}
92134
/>
93135
)}
94136
</Box>
137+
<Box key="createHouseholds" height="100%">
138+
<CreateHouseholdsPage
139+
onBack={() => back()}
140+
onClose={onClose}
141+
orgId={orgId}
142+
placeId={place.id}
143+
/>
144+
</Box>
95145
<Box key="editHousehold" height="100%">
96146
{selectedHousehold && (
97147
<EditHousehold
98148
household={selectedHousehold}
99-
onBack={() => setDialogStep('household')}
149+
onBack={() => back()}
100150
onClose={onClose}
101-
onSave={async (title) => {
102-
await updateHousehold(selectedHousehold.id, { title });
103-
setDialogStep('household');
151+
onSave={async (title, floor) => {
152+
await updateHousehold(selectedHousehold.id, { floor, title });
153+
back();
104154
}}
105155
/>
106156
)}
@@ -110,15 +160,15 @@ const PlaceDialog: FC<PlaceDialogProps> = ({
110160
<VisitWizard
111161
household={selectedHousehold}
112162
metrics={assignment.metrics}
113-
onBack={() => setDialogStep('household')}
163+
onBack={() => back()}
114164
onLogVisit={(responses, noteToOfficial) => {
115165
addVisit(selectedHousehold.id, {
116166
canvassAssId: assignment.id,
117167
noteToOfficial,
118168
responses,
119169
timestamp: new Date().toISOString(),
120170
});
121-
setDialogStep('place');
171+
goto('place');
122172
}}
123173
/>
124174
)}

0 commit comments

Comments
 (0)