Skip to content

Commit 50ff8f8

Browse files
Merge pull request #2389 from zetkin/release-241127
241127 Release
2 parents 601edb5 + 516088d commit 50ff8f8

File tree

24 files changed

+314
-59
lines changed

24 files changed

+314
-59
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
"xlsx-js-style": "^1.2.0",
101101
"yaml": "^1.10.0",
102102
"zetkin": "~1.3.1",
103-
"zod": "^3.20.2"
103+
"zod": "^3.23.8"
104104
},
105105
"devDependencies": {
106106
"@babel/core": "^7.17.9",

src/app/o/[orgId]/embedjoinform/[formData]/page.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,33 @@ export default async function Page({ params, searchParams }: PageProps) {
3131
{searchParams.stylesheet && (
3232
<style>{`@import url(${searchParams.stylesheet})`}</style>
3333
)}
34+
{!searchParams.stylesheet && (
35+
<style>{`
36+
body {
37+
padding: 0.5rem;
38+
}
39+
40+
.zetkin-joinform__field {
41+
margin-bottom: 1rem;
42+
}
43+
44+
.zetkin-joinform__field input {
45+
width: 100%;
46+
max-width: 600px;
47+
padding: 0.3rem;
48+
font-size: 1.5rem;
49+
}
50+
51+
.zetkin-joinform__submit-button {
52+
border-width: 0;
53+
font-size: 1.5rem;
54+
background-color: black;
55+
color: white;
56+
padding: 0.5rem 1rem;
57+
border-radius: 0.2rem;
58+
}
59+
`}</style>
60+
)}
3461
</div>
3562
);
3663
} catch (err) {

src/core/i18n/globalMessageIds.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ export default makeMessages('glob', {
66
edit: m('Editing'),
77
readonly: m('Read-only'),
88
},
9+
genderOptions: {
10+
f: m('Female'),
11+
m: m('Male'),
12+
o: m('Other'),
13+
unspecified: m('Unspecified'),
14+
},
915
personFields: {
1016
alt_phone: m('Alternate Phone Number'),
1117
city: m('City'),

src/features/canvassAssignments/components/PlaceDialog/pages/CreateHouseholdsPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ const CreateHouseholdsPage: FC<Props> = ({
4343
width: widthPerHousehold * 5,
4444
};
4545

46-
const maxHeight = rect.width / widthPerHousehold - 2;
47-
const maxWidth = rect.height / heightPerFloor - 1;
46+
const maxHeight = rect.height / (heightPerFloor - 1);
47+
const maxWidth = rect.width / (widthPerHousehold - 1);
4848
const scaleX = Math.min(1.0, maxWidth / newNumAptsPerFloor);
4949
const scaleY = Math.min(1.0, maxHeight / newNumFloors);
5050
const newScale = Math.min(scaleX, scaleY);

src/features/canvassAssignments/components/PlaceDialog/pages/EditHousehold.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ type Props = {
1313

1414
const EditHousehold: FC<Props> = ({ onClose, onBack, onSave, household }) => {
1515
const [title, setTitle] = useState(household.title || '');
16-
const [floor, setFloor] = useState(household.floor || NaN);
16+
const [floor, setFloor] = useState(household.floor ?? NaN);
1717

1818
useEffect(() => {
1919
setTitle(household.title || '');
20+
setFloor(household.floor ?? NaN);
2021
}, [household]);
2122

2223
const nothingHasBeenEdited =

src/features/events/components/EventTypeAutocomplete.tsx

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import Fuse from 'fuse.js';
22
import { lighten } from '@mui/material/styles';
33
import makeStyles from '@mui/styles/makeStyles';
4-
import { Add, Clear } from '@mui/icons-material';
4+
import { Add, DeleteOutline } from '@mui/icons-material';
55
import { Autocomplete, Box, TextField, Theme, Tooltip } from '@mui/material';
6-
import { FC, useEffect, useRef, useState } from 'react';
6+
import { FC, useContext, useEffect, useRef, useState } from 'react';
77

88
import messageIds from '../l10n/messageIds';
9+
import theme from 'theme';
910
import useCreateType from '../hooks/useCreateType';
1011
import { useMessages } from 'core/i18n';
12+
import useDeleteType from '../hooks/useDeleteType';
1113
import { ZetkinActivity, ZetkinEvent } from 'utils/types/zetkin';
14+
import { ZUIConfirmDialogContext } from 'zui/ZUIConfirmDialogProvider';
1215

1316
interface StyleProps {
1417
showBorder: boolean | undefined;
@@ -74,6 +77,7 @@ const EventTypeAutocomplete: FC<EventTypeAutocompleteProps> = ({
7477
value,
7578
}) => {
7679
const createType = useCreateType(orgId);
80+
const deleteType = useDeleteType(orgId);
7781
const messages = useMessages(messageIds);
7882
const uncategorizedMsg = messages.type.uncategorized();
7983
const [createdType, setCreatedType] = useState<string>('');
@@ -83,14 +87,18 @@ const EventTypeAutocomplete: FC<EventTypeAutocompleteProps> = ({
8387
const spanRef = useRef<HTMLSpanElement>(null);
8488
const classes = useStyles({ showBorder });
8589

90+
const { showConfirmDialog } = useContext(ZUIConfirmDialogContext);
91+
8692
useEffect(() => {
8793
//When a user creates a new type, it is missing an event ID.
8894
//In here, when the length of the type changes,
8995
//it searches for the created event and updates event with an ID.
9096
if (createdType !== '') {
9197
const newEventType = types.find((item) => item.title === createdType);
92-
setText(newEventType!.title);
93-
onChangeNewOption(newEventType!.id);
98+
setText(newEventType ? newEventType!.title : uncategorizedMsg);
99+
if (newEventType) {
100+
onChangeNewOption(newEventType!.id);
101+
}
94102
}
95103
}, [types.length]);
96104

@@ -224,17 +232,44 @@ const EventTypeAutocomplete: FC<EventTypeAutocompleteProps> = ({
224232
return (
225233
<Box key={option.id}>
226234
{option.id != 'CREATE' && option.id != 'UNCATEGORIZED' && (
227-
<li {...props}>{option.title}</li>
235+
<li {...props} style={{ justifyContent: 'space-between' }}>
236+
{option.title}
237+
<DeleteOutline
238+
onClick={(ev) => {
239+
ev.stopPropagation();
240+
showConfirmDialog({
241+
onSubmit: () => {
242+
if (typeof option.id === 'number') {
243+
deleteType(option.id);
244+
//If the current event has the deleted event type,
245+
//set the current event's type to null
246+
if (value && option.id == value.id) {
247+
onChange(null);
248+
}
249+
}
250+
},
251+
warningText: messages.type.deleteMessage({
252+
eventType: option.title,
253+
}),
254+
});
255+
}}
256+
sx={{
257+
'&:hover': {
258+
color: theme.palette.secondary.main,
259+
},
260+
color: theme.palette.secondary.light,
261+
cursor: 'pointer',
262+
transition: 'color 0.3s ease',
263+
}}
264+
/>
265+
</li>
228266
)}
229267
{option.id == 'UNCATEGORIZED' && (
230-
<li {...props}>
231-
<Clear />
232-
{uncategorizedMsg}
233-
</li>
268+
<li {...props}>{uncategorizedMsg}</li>
234269
)}
235270
{option.id == 'CREATE' && (
236271
<li {...props}>
237-
<Add />
272+
<Add sx={{ marginRight: 1 }} />
238273
{messages.type.createType({ type: option.title! })}
239274
</li>
240275
)}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { typeDeleted } from '../store';
2+
import { useApiClient, useAppDispatch } from 'core/hooks';
3+
4+
export default function useDeleteType(orgId: number) {
5+
const apiClient = useApiClient();
6+
const dispatch = useAppDispatch();
7+
8+
return async (typeId: number) => {
9+
await apiClient.delete(`/api/orgs/${orgId}/activities/${typeId}`);
10+
dispatch(typeDeleted(typeId));
11+
};
12+
}

src/features/events/l10n/messageIds.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ export default makeMessages('feat.events', {
282282
tooltipContent: m('Untitled events will display type as title'),
283283
type: {
284284
createType: m<{ type: string }>('Create "{type}"'),
285+
deleteMessage: m<{ eventType: string }>(
286+
'Are you sure you want to delete the "{eventType}" event type for the whole organization?'
287+
),
285288
tooltip: m('Click to change type'),
286289
uncategorized: m('Uncategorized'),
287290
},

src/features/events/store.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,20 @@ const eventsSlice = createSlice({
541541
remoteItem(data.id, { data: data, isLoading: false }),
542542
]);
543543
},
544+
typeDeleted: (state, action: PayloadAction<number>) => {
545+
const typeId = action.payload;
546+
const typeListItem = state.typeList.items.find(
547+
(item) => item.id === typeId
548+
);
549+
550+
if (typeListItem) {
551+
typeListItem.deleted = true;
552+
}
553+
554+
state.typeList.items = state.typeList.items.filter(
555+
(type) => type.id !== typeId
556+
);
557+
},
544558
typeLoad: (state, action: PayloadAction<number>) => {
545559
const id = action.payload;
546560
const item = state.typeList.items.find((item) => item.id == id);
@@ -728,6 +742,7 @@ export const {
728742
statsLoaded,
729743
typeAdd,
730744
typeAdded,
745+
typeDeleted,
731746
typeLoad,
732747
typeLoaded,
733748
typesLoad,

src/features/files/components/FileLibraryDialog/FilePreview.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FC } from 'react';
1+
import { FC, useState } from 'react';
22
import Image from 'next/image';
33
import { Box, Button, Typography } from '@mui/material';
44

@@ -14,31 +14,48 @@ type Props = {
1414
};
1515

1616
const FilePreview: FC<Props> = ({ file, onBack, onSelect }) => {
17+
const [dimensions, setDimensions] = useState({ height: 0, width: 0 });
18+
1719
return (
1820
<Box display="flex" flexDirection="column" height="100%">
1921
<Box height="100%" my={2} overflow="auto">
2022
<TransparentGridBackground
2123
interactive={false}
2224
sx={{
23-
height: 'calc(100% - 2em)',
25+
height: 'calc(100% - 4em)',
2426
position: 'relative',
2527
}}
2628
>
2729
<Image
2830
alt={file.original_name}
2931
height="800"
32+
onLoad={(e) => {
33+
if (e.target instanceof HTMLImageElement) {
34+
setDimensions({
35+
height: e.target.naturalHeight,
36+
width: e.target.naturalWidth,
37+
});
38+
}
39+
}}
3040
src={file.url}
3141
style={{
3242
height: '100%',
3343
objectFit: 'contain',
3444
width: '100%',
3545
}}
46+
unoptimized
3647
width="800"
3748
/>
3849
</TransparentGridBackground>
3950
<Typography color="secondary" mt={1} textAlign="center" variant="body2">
4051
{file.original_name}
4152
</Typography>
53+
<Typography color="secondary" mt={1} textAlign="center" variant="body2">
54+
<Msg
55+
id={messageIds.image.dimensions}
56+
values={{ height: dimensions.height, width: dimensions.width }}
57+
/>
58+
</Typography>
4259
</Box>
4360
<Box display="flex" gap={1} justifyContent="flex-end">
4461
<Button onClick={() => onBack()} variant="outlined">

src/features/files/components/LibraryImage.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,27 @@ import { ZetkinFile } from 'utils/types/zetkin';
66

77
interface LibraryImageProps {
88
imageFile: ZetkinFile;
9-
onLoad?: () => void;
10-
onLoadingComplete?: () => void;
9+
onLoad?: ({ height, width }: { height: number; width: number }) => void;
1110
}
1211

13-
const LibraryImage: FC<LibraryImageProps> = ({
14-
imageFile,
15-
onLoad,
16-
onLoadingComplete,
17-
}) => {
12+
const LibraryImage: FC<LibraryImageProps> = ({ imageFile, onLoad }) => {
1813
return (
1914
<TransparentGridBackground>
2015
<Image
2116
alt={imageFile.original_name}
2217
height="400"
23-
onLoad={() => onLoad && onLoad()}
24-
onLoadingComplete={() => onLoadingComplete && onLoadingComplete()}
18+
onLoad={(e) => {
19+
if (onLoad) {
20+
if (e.target instanceof HTMLImageElement) {
21+
onLoad({
22+
height: e.target.naturalHeight,
23+
width: e.target.naturalWidth,
24+
});
25+
} else {
26+
onLoad({ height: 0, width: 0 });
27+
}
28+
}
29+
}}
2530
src={imageFile.url}
2631
style={{
2732
aspectRatio: '1 / 1',
@@ -30,6 +35,7 @@ const LibraryImage: FC<LibraryImageProps> = ({
3035
objectFit: 'contain',
3136
width: '100%',
3237
}}
38+
unoptimized
3339
width="400"
3440
/>
3541
</TransparentGridBackground>

src/features/files/components/LibraryImageCard.tsx

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { FC, useState } from 'react';
33

44
import LibraryImage from './LibraryImage';
55
import { ZetkinFile } from 'utils/types/zetkin';
6+
import messageIds from 'features/files/l10n/messageIds';
7+
import { Msg } from 'core/i18n';
68

79
interface LibraryImageCardProps {
810
imageFile: ZetkinFile;
@@ -14,6 +16,7 @@ const LibraryImageCard: FC<LibraryImageCardProps> = ({
1416
onSelectImage,
1517
}) => {
1618
const [loading, setLoading] = useState(true);
19+
const [dimensions, setDimensions] = useState({ height: 0, width: 0 });
1720

1821
return (
1922
<Box
@@ -24,10 +27,18 @@ const LibraryImageCard: FC<LibraryImageCardProps> = ({
2427
>
2528
<LibraryImage
2629
imageFile={imageFile}
27-
onLoad={() => setLoading(true)}
28-
onLoadingComplete={() => setLoading(false)}
30+
onLoad={(dimensions) => {
31+
setLoading(false);
32+
setDimensions(dimensions);
33+
}}
2934
/>
30-
<Box alignItems="center" display="flex" justifyContent="center" mt={1}>
35+
<Box
36+
alignItems="center"
37+
display="flex"
38+
flexDirection="column"
39+
justifyContent="center"
40+
mt={1}
41+
>
3142
{loading && (
3243
<CircularProgress color="primary" size="1em" sx={{ mr: 1 }} />
3344
)}
@@ -43,6 +54,21 @@ const LibraryImageCard: FC<LibraryImageCardProps> = ({
4354
>
4455
{imageFile.original_name}
4556
</Typography>
57+
<Typography
58+
alignSelf="center"
59+
color="secondary"
60+
component="span"
61+
maxWidth="80%"
62+
noWrap
63+
overflow="hidden"
64+
textOverflow="ellipsis"
65+
variant="body2"
66+
>
67+
<Msg
68+
id={messageIds.image.dimensions}
69+
values={{ height: dimensions.height, width: dimensions.width }}
70+
/>
71+
</Typography>
4672
</Box>
4773
</Box>
4874
);

0 commit comments

Comments
 (0)