From 06935287d3b09dd76b91533cbb7947305612e487 Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:21:02 +0100 Subject: [PATCH 01/24] move all button actions to the right, move toolbar, add background --- .../components/MetricCard.tsx | 2 +- .../[canvassAssId]/outcomes.tsx | 306 +++++++++--------- 2 files changed, 162 insertions(+), 146 deletions(-) diff --git a/src/features/canvassAssignments/components/MetricCard.tsx b/src/features/canvassAssignments/components/MetricCard.tsx index 83b5d711b..8da78d3cd 100644 --- a/src/features/canvassAssignments/components/MetricCard.tsx +++ b/src/features/canvassAssignments/components/MetricCard.tsx @@ -93,7 +93,7 @@ const MetricCard: FC = ({ )} - + {isEditing && !isOnlyQuestion && ( - - + {metricBeingEdited && ( )} - - {assignment.metrics.length > 0 ? 'Your list of questions:' : ''} - {assignment.metrics.map((metric) => ( - - - - - {metric.definesDone && ( - - This question defines if the mission was successful + + + + Here you can configure the questions for your canvass + assignment + + + + + + + + {assignment.metrics.length > 0 ? 'Your list of questions:' : ''} + {assignment.metrics.map((metric) => ( + + + + + {metric.definesDone && ( + + This question defines if the mission was + successful + + )} + + {metric.question || 'Untitled question'} - )} - - {metric.question || 'Untitled question'} - - - {metric.description || 'No description'} - - - - - {metric.kind == 'boolean' ? 'Yes/no' : 'Scale'} - + + {metric.description || 'No description'} + + + + + {metric.kind == 'boolean' ? 'Yes/no' : 'Scale'} + + - - - - - - {assignment.metrics.length > 1 && ( - - )} - - - ))} - - setAnchorEl(null)} open={!!anchorEl}> - - - {`Delete "${ - assignment.metrics.find( - (metric) => metric.id == idOfMetricBeingDeleted - )?.question - }"`} - { - setIdOfQuestionBeingDeleted(null); - setAnchorEl(null); - }} - > - - - - - {`If you want to delete "${ - assignment.metrics.find( - (metric) => metric.id == idOfMetricBeingDeleted - )?.question - }" you need to pick another - yes/no-question to be the question that defines if the msision - was successful`} - - - Yes/no questions - {assignment.metrics - .filter( - (metric) => - metric.kind == 'boolean' && - metric.id != idOfMetricBeingDeleted - ) - .map((metric) => ( - - {metric.question} + + {assignment.metrics.length > 1 && ( - - ))} - + )} + + + ))} - + setAnchorEl(null)} open={!!anchorEl}> + + + {`Delete "${ + assignment.metrics.find( + (metric) => metric.id == idOfMetricBeingDeleted + )?.question + }"`} + { + setIdOfQuestionBeingDeleted(null); + setAnchorEl(null); + }} + > + + + + + {`If you want to delete "${ + assignment.metrics.find( + (metric) => metric.id == idOfMetricBeingDeleted + )?.question + }" you need to pick another + yes/no-question to be the question that defines if the msision + was successful`} + + + Yes/no questions + {assignment.metrics + .filter( + (metric) => + metric.kind == 'boolean' && + metric.id != idOfMetricBeingDeleted + ) + .map((metric) => ( + + {metric.question} + + + ))} + + + + )} From a39420ebe733e499a4ba3ddc7288326a56fdc1da Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:09:24 +0100 Subject: [PATCH 02/24] remove update label, give minwidth to metricCard, add Modal when editing, fix typo --- .../components/MetricCard.tsx | 4 +- .../[canvassAssId]/outcomes.tsx | 52 +++++++++++++++---- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/features/canvassAssignments/components/MetricCard.tsx b/src/features/canvassAssignments/components/MetricCard.tsx index 8da78d3cd..ef175feb6 100644 --- a/src/features/canvassAssignments/components/MetricCard.tsx +++ b/src/features/canvassAssignments/components/MetricCard.tsx @@ -50,7 +50,7 @@ const MetricCard: FC = ({ metric.kind == 'boolean' && (metric.definesDone || !hasDefinedDone); return ( - + @@ -115,7 +115,7 @@ const MetricCard: FC = ({ }} variant="outlined" > - {isEditing ? 'Update' : 'Save'} + Save diff --git a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx index d8f1426cc..f297c7979 100644 --- a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx +++ b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx @@ -10,6 +10,7 @@ import { Dialog, IconButton, MenuItem, + Modal, Select, Typography, } from '@mui/material'; @@ -58,6 +59,8 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< canvassAssId ); + const [metricBeingCreated, setMetricBeingCreated] = + useState(null); const [metricBeingEdited, setMetricBeingEdited] = useState(null); const [anchorEl, setAnchorEl] = useState(null); @@ -74,6 +77,7 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< }); } setMetricBeingEdited(null); + setMetricBeingCreated(null); }; const handleDeleteMetric = async (id: string) => { @@ -88,7 +92,7 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< }; const handleAddNewMetric = (kind: 'boolean' | 'scale5') => { - setMetricBeingEdited({ + setMetricBeingCreated({ definesDone: false, description: '', id: '', @@ -121,27 +125,55 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< Place (more privacy) - - {metricBeingEdited && ( + {metricBeingCreated && ( metric.definesDone )} isOnlyQuestion={assignment.metrics.length == 1} - metric={metricBeingEdited} - onClose={() => setMetricBeingEdited(null)} + metric={metricBeingCreated} + onClose={() => setMetricBeingCreated(null)} onDelete={(target: EventTarget & HTMLButtonElement) => { - if (metricBeingEdited.definesDone) { - setIdOfQuestionBeingDeleted(metricBeingEdited.id); + if (metricBeingCreated.definesDone) { + setIdOfQuestionBeingDeleted(metricBeingCreated.id); setAnchorEl(target); - setMetricBeingEdited(null); + setMetricBeingCreated(null); } else { - handleDeleteMetric(metricBeingEdited.id); + handleDeleteMetric(metricBeingCreated.id); } }} onSave={handleSaveMetric} /> )} + {metricBeingEdited && ( + + metric.definesDone + )} + isOnlyQuestion={assignment.metrics.length == 1} + metric={metricBeingEdited} + onClose={() => setMetricBeingEdited(null)} + onDelete={(target: EventTarget & HTMLButtonElement) => { + if (metricBeingEdited.definesDone) { + setIdOfQuestionBeingDeleted(metricBeingEdited.id); + setAnchorEl(target); + setMetricBeingEdited(null); + } else { + handleDeleteMetric(metricBeingEdited.id); + } + }} + onSave={handleSaveMetric} + /> + + )} metric.id == idOfMetricBeingDeleted )?.question }" you need to pick another - yes/no-question to be the question that defines if the msision + yes/no-question to be the question that defines if the mision was successful`} From ed0ecd977aa77308988cf26ffb1158a0505dca7e Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:13:38 +0100 Subject: [PATCH 03/24] show delete modal for all delete cases --- .../[canvassAssId]/outcomes.tsx | 184 ++++++++++-------- 1 file changed, 101 insertions(+), 83 deletions(-) diff --git a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx index f297c7979..56355b1cf 100644 --- a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx +++ b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx @@ -64,9 +64,8 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< const [metricBeingEdited, setMetricBeingEdited] = useState(null); const [anchorEl, setAnchorEl] = useState(null); - const [idOfMetricBeingDeleted, setIdOfQuestionBeingDeleted] = useState< - string | null - >(null); + const [metricBeingDeleted, setMetricBeingDeleted] = + useState(null); const handleSaveMetric = async (metric: ZetkinMetric) => { if (canvassAssignmentFuture.data) { @@ -134,13 +133,9 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< metric={metricBeingCreated} onClose={() => setMetricBeingCreated(null)} onDelete={(target: EventTarget & HTMLButtonElement) => { - if (metricBeingCreated.definesDone) { - setIdOfQuestionBeingDeleted(metricBeingCreated.id); - setAnchorEl(target); - setMetricBeingCreated(null); - } else { - handleDeleteMetric(metricBeingCreated.id); - } + setMetricBeingDeleted(metricBeingCreated); + setAnchorEl(target); + setMetricBeingCreated(null); }} onSave={handleSaveMetric} /> @@ -149,9 +144,9 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< setMetricBeingEdited(null)} onDelete={(target: EventTarget & HTMLButtonElement) => { - if (metricBeingEdited.definesDone) { - setIdOfQuestionBeingDeleted(metricBeingEdited.id); - setAnchorEl(target); - setMetricBeingEdited(null); - } else { - handleDeleteMetric(metricBeingEdited.id); - } + setMetricBeingDeleted(metricBeingEdited); + setAnchorEl(target); + setMetricBeingEdited(null); }} onSave={handleSaveMetric} /> @@ -243,12 +234,8 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< {assignment.metrics.length > 1 && ( - - ))} - + {metric.question} + + + ))} + + ) : ( + + + + + )} From b3bb8010c2bf448ecc12c369003a62137223a7c2 Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:45:01 +0100 Subject: [PATCH 04/24] implement design for reporting level --- .../components/MetricCard.tsx | 2 +- .../[canvassAssId]/outcomes.tsx | 170 +++++++++++++----- 2 files changed, 123 insertions(+), 49 deletions(-) diff --git a/src/features/canvassAssignments/components/MetricCard.tsx b/src/features/canvassAssignments/components/MetricCard.tsx index ef175feb6..41182be15 100644 --- a/src/features/canvassAssignments/components/MetricCard.tsx +++ b/src/features/canvassAssignments/components/MetricCard.tsx @@ -50,7 +50,7 @@ const MetricCard: FC = ({ metric.kind == 'boolean' && (metric.definesDone || !hasDefinedDone); return ( - + diff --git a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx index 56355b1cf..e3d6c1f32 100644 --- a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx +++ b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx @@ -8,10 +8,14 @@ import { CardActions, CardContent, Dialog, + Divider, + FormControl, + FormControlLabel, + Grid, IconButton, - MenuItem, Modal, - Select, + Radio, + RadioGroup, Typography, } from '@mui/material'; @@ -22,10 +26,7 @@ import { scaffold } from 'utils/next'; import { PageWithLayout } from 'utils/types'; import useCanvassAssignmentMutations from 'features/canvassAssignments/hooks/useCanvassAssignmentMutations'; import useCanvassAssignment from 'features/canvassAssignments/hooks/useCanvassAssignment'; -import { - ZetkinCanvassAssignment, - ZetkinMetric, -} from 'features/canvassAssignments/types'; +import { ZetkinMetric } from 'features/canvassAssignments/types'; import CanvassAssignmentLayout from 'features/canvassAssignments/layouts/CanvassAssignmentLayout'; import ZUICard from 'zui/ZUICard'; import theme from 'theme'; @@ -101,44 +102,27 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< }; return ( - - - {(assignment) => ( - <> - - - Decide what level of precision should be used for statistics. - - - + + {(assignment) => ( + + {metricBeingCreated && ( - metric.definesDone - )} - isOnlyQuestion={assignment.metrics.length == 1} - metric={metricBeingCreated} - onClose={() => setMetricBeingCreated(null)} - onDelete={(target: EventTarget & HTMLButtonElement) => { - setMetricBeingDeleted(metricBeingCreated); - setAnchorEl(target); - setMetricBeingCreated(null); - }} - onSave={handleSaveMetric} - /> + + metric.definesDone + )} + isOnlyQuestion={assignment.metrics.length == 1} + metric={metricBeingCreated} + onClose={() => setMetricBeingCreated(null)} + onDelete={(target: EventTarget & HTMLButtonElement) => { + setMetricBeingDeleted(metricBeingCreated); + setAnchorEl(target); + setMetricBeingCreated(null); + }} + onSave={handleSaveMetric} + /> + )} {metricBeingEdited && ( @@ -360,10 +343,101 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< - - )} - - + + + + + Decide what level of precision should be used for statistics. + + + + { + const value = ev.target.value; + if (value === 'household' || value === 'place') { + updateCanvassAssignment({ + reporting_level: value, + }); + } + }} + value={assignment.reporting_level} + > + + + + + updateCanvassAssignment({ + reporting_level: 'household', + }) + } + sx={{ + border: + assignment.reporting_level === 'household' + ? `1px solid ${theme.palette.primary.main}` + : `1px solid ${theme.palette.grey[300]}`, + cursor: 'pointer', + height: '100%', + }} + > + + } + label={ + Household + } + sx={{ pointerEvents: 'none' }} + value="household" + /> + + + + Collect the most precise data. + + + + + + updateCanvassAssignment({ + reporting_level: 'place', + }) + } + sx={{ + border: + assignment.reporting_level === 'place' + ? `1px solid ${theme.palette.primary.main}` + : `1px solid ${theme.palette.grey[300]}`, + cursor: 'pointer', + height: '100%', + }} + > + + } + label={ + Place + } + sx={{ pointerEvents: 'none' }} + value="place" + /> + + + + Collect data only on places, preserving some + privacy. + + + + + + + + + + + )} + ); }; From a03e712f24647f4dae1e262c9bcf08482fd7ea7c Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:59:31 +0100 Subject: [PATCH 05/24] remove delete action from edit modal, place questions toolbar in the bottom of list --- .../components/MetricCard.tsx | 15 +-- .../[canvassAssId]/outcomes.tsx | 112 ++++++++---------- 2 files changed, 52 insertions(+), 75 deletions(-) diff --git a/src/features/canvassAssignments/components/MetricCard.tsx b/src/features/canvassAssignments/components/MetricCard.tsx index 41182be15..1c0db855b 100644 --- a/src/features/canvassAssignments/components/MetricCard.tsx +++ b/src/features/canvassAssignments/components/MetricCard.tsx @@ -18,7 +18,6 @@ type MetricCardProps = { isOnlyQuestion: boolean; metric: ZetkinMetric; onClose: () => void; - onDelete: (target: EventTarget & HTMLButtonElement) => void; onSave: (metric: ZetkinMetric) => void; }; @@ -27,7 +26,6 @@ const MetricCard: FC = ({ isOnlyQuestion, metric, onClose, - onDelete, onSave, }) => { const [question, setQuestion] = useState(metric.question || ''); @@ -38,8 +36,6 @@ const MetricCard: FC = ({ metric.definesDone || false ); - const isEditing = !!metric?.id; - useEffect(() => { setQuestion(metric.question || ''); setDescription(metric.description || ''); @@ -50,7 +46,7 @@ const MetricCard: FC = ({ metric.kind == 'boolean' && (metric.definesDone || !hasDefinedDone); return ( - + @@ -94,15 +90,6 @@ const MetricCard: FC = ({ )} - {isEditing && !isOnlyQuestion && ( - - )} + + {assignment.metrics.length > 1 && ( + + )} + + + ))} + {metricBeingCreated && ( setMetricBeingCreated(null)} - onDelete={(target: EventTarget & HTMLButtonElement) => { - setMetricBeingDeleted(metricBeingCreated); - setAnchorEl(target); - setMetricBeingCreated(null); - }} onSave={handleSaveMetric} /> @@ -140,11 +185,6 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< isOnlyQuestion={assignment.metrics.length == 1} metric={metricBeingEdited} onClose={() => setMetricBeingEdited(null)} - onDelete={(target: EventTarget & HTMLButtonElement) => { - setMetricBeingDeleted(metricBeingEdited); - setAnchorEl(target); - setMetricBeingEdited(null); - }} onSave={handleSaveMetric} /> @@ -154,6 +194,7 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< sx={{ backgroundColor: theme.palette.grey[200], border: 'none', + marginTop: 2, padding: 2, }} > @@ -177,57 +218,6 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< - - {assignment.metrics.length > 0 ? 'Your list of questions:' : ''} - {assignment.metrics.map((metric) => ( - - - - - {metric.definesDone && ( - - This question defines if the mission was - successful - - )} - - {metric.question || 'Untitled question'} - - - {metric.description || 'No description'} - - - - - {metric.kind == 'boolean' ? 'Yes/no' : 'Scale'} - - - - - - - - {assignment.metrics.length > 1 && ( - - )} - - - ))} - setAnchorEl(null)} open={!!anchorEl}> Date: Thu, 9 Jan 2025 10:10:07 +0100 Subject: [PATCH 06/24] update mui-icons dependency to latest --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2f939b89c..1c30f3596 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@hapi/iron": "^7.0.1", "@messageformat/parser": "^5.1.0", "@mui/base": "^5.0.0-alpha.99", - "@mui/icons-material": "^5.10.6", + "@mui/icons-material": "^6.3.1", "@mui/lab": "^5.0.0-alpha.100", "@mui/material": "^5.10.7", "@mui/material-nextjs": "^5.15.11", From db8f37036f03cfde8ac22dd88e6510ebc794f249 Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:13:10 +0100 Subject: [PATCH 07/24] remove unnecessary / from import to prevent it of breaking import --- src/zui/ZUIOrganizeSidebar/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zui/ZUIOrganizeSidebar/index.tsx b/src/zui/ZUIOrganizeSidebar/index.tsx index 0b3be4551..0eb01e110 100644 --- a/src/zui/ZUIOrganizeSidebar/index.tsx +++ b/src/zui/ZUIOrganizeSidebar/index.tsx @@ -17,7 +17,7 @@ import { Map, Search, Settings, -} from '@mui/icons-material/'; +} from '@mui/icons-material'; import { Avatar, Box, From 7006dfdb341f119c6facd5aebd600a0903929959 Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:17:34 +0100 Subject: [PATCH 08/24] add type icons, polish styles --- .../components/MetricCard.tsx | 19 +++- .../[canvassAssId]/outcomes.tsx | 106 ++++++++++++------ 2 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/features/canvassAssignments/components/MetricCard.tsx b/src/features/canvassAssignments/components/MetricCard.tsx index 1c0db855b..8379ab8ec 100644 --- a/src/features/canvassAssignments/components/MetricCard.tsx +++ b/src/features/canvassAssignments/components/MetricCard.tsx @@ -1,4 +1,4 @@ -import { Close } from '@mui/icons-material'; +import { Close, LinearScale, SwitchLeft } from '@mui/icons-material'; import React, { FC, useEffect, useState } from 'react'; import { Card, @@ -49,16 +49,25 @@ const MetricCard: FC = ({ - - {metric.kind === 'boolean' ? 'Yes/No Question' : 'Scale Question'} - + {metric.kind === 'boolean' ? ( + + Choice question{' '} + + + ) : ( + + Scale question{' '} + + + )} + {metric.kind == 'scale5' && ( - + The canvasser will respond by giving a rating from 1 to 5 )} diff --git a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx index 8746ca13a..6bc8b6155 100644 --- a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx +++ b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx @@ -1,11 +1,18 @@ -import { Close } from '@mui/icons-material'; +import AdsClickIcon from '@mui/icons-material/AdsClick'; +import { + Close, + Delete, + Edit, + LinearScale, + SwitchLeft, +} from '@mui/icons-material'; import { GetServerSideProps } from 'next'; import { useState } from 'react'; import { + alpha, Box, Button, Card, - CardActions, CardContent, Dialog, Divider, @@ -19,7 +26,6 @@ import { Typography, } from '@mui/material'; -import ZUIFuture from 'zui/ZUIFuture'; import MetricCard from 'features/canvassAssignments/components/MetricCard'; import { AREAS } from 'utils/featureFlags'; import { scaffold } from 'utils/next'; @@ -30,6 +36,7 @@ import { ZetkinMetric } from 'features/canvassAssignments/types'; import CanvassAssignmentLayout from 'features/canvassAssignments/layouts/CanvassAssignmentLayout'; import ZUICard from 'zui/ZUICard'; import theme from 'theme'; +import ZUIFuture from 'zui/ZUIFuture'; const scaffoldOptions = { authLevelRequired: 2, @@ -118,41 +125,65 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< flexGrow={1} gap={1} > - {metric.definesDone && ( - - This question defines if the mission was successful - - )} - - {metric.question || 'Untitled question'} - - - {metric.description || 'No description'} - - - + + + + {metric.question || 'Untitled question'} + + + {metric.kind == 'boolean' ? ( + + ) : ( + + )} + + + + {metric.definesDone ? ( + + + Defines success + + ) : ( + + )} + + + + {assignment.metrics.length > 1 && ( + + )} + + - {metric.kind == 'boolean' ? 'Yes/no' : 'Scale'} + {metric.description || 'No description'} - - - - {assignment.metrics.length > 1 && ( - - )} - ))} @@ -199,22 +230,23 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< }} > - Here you can configure the questions for your canvass - assignment + Add questions for your canvass assignment. From c5214d4933ad8b260c1ef85f374300cea14ef28f Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Thu, 9 Jan 2025 13:31:41 +0100 Subject: [PATCH 09/24] add alert component, remove checkbox logic from MetricCard and add it in output page, fix styles --- .../components/MetricCard.tsx | 33 +------- .../[canvassAssId]/outcomes.tsx | 79 ++++++++++++++----- 2 files changed, 63 insertions(+), 49 deletions(-) diff --git a/src/features/canvassAssignments/components/MetricCard.tsx b/src/features/canvassAssignments/components/MetricCard.tsx index 8379ab8ec..8780f65c8 100644 --- a/src/features/canvassAssignments/components/MetricCard.tsx +++ b/src/features/canvassAssignments/components/MetricCard.tsx @@ -6,7 +6,6 @@ import { Typography, Box, TextField, - Checkbox, Button, IconButton, } from '@mui/material'; @@ -14,20 +13,12 @@ import { import { ZetkinMetric } from '../types'; type MetricCardProps = { - hasDefinedDone: boolean; - isOnlyQuestion: boolean; metric: ZetkinMetric; onClose: () => void; onSave: (metric: ZetkinMetric) => void; }; -const MetricCard: FC = ({ - hasDefinedDone, - isOnlyQuestion, - metric, - onClose, - onSave, -}) => { +const MetricCard: FC = ({ metric, onClose, onSave }) => { const [question, setQuestion] = useState(metric.question || ''); const [description, setDescription] = useState( metric.description || '' @@ -42,20 +33,17 @@ const MetricCard: FC = ({ setDefinesDone(metric.definesDone || false); }, [metric]); - const showDefinesDoneCheckbox = - metric.kind == 'boolean' && (metric.definesDone || !hasDefinedDone); - return ( {metric.kind === 'boolean' ? ( - + Choice question{' '} ) : ( - + Scale question{' '} @@ -85,19 +73,6 @@ const MetricCard: FC = ({ value={description} variant="outlined" /> - {showDefinesDoneCheckbox && ( - - setDefinesDone(ev.target.checked)} - /> - - The answer to this question defines if the mission was - successful - - - )} diff --git a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx index 6bc8b6155..ce04d2249 100644 --- a/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx +++ b/src/pages/organize/[orgId]/projects/[campId]/canvassassignments/[canvassAssId]/outcomes.tsx @@ -9,6 +9,8 @@ import { import { GetServerSideProps } from 'next'; import { useState } from 'react'; import { + Alert, + AlertTitle, alpha, Box, Button, @@ -113,7 +115,17 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< {(assignment) => ( - + , + }} + severity="info" + > + Define successful visit + Decide what metric to use for a visit to count as successful by + clicking this symbol. + + {assignment.metrics.length > 0 ? 'Your list of questions:' : ''} {assignment.metrics.map((metric) => ( @@ -126,24 +138,31 @@ const CanvassAssignmentOutcomesPage: PageWithLayout< gap={1} > - - {metric.question || 'Untitled question'} - - + {metric.kind == 'boolean' ? ( - + + + ) : ( - + + + )} + {metric.question || 'Untitled question'} - {metric.definesDone ? ( + {metric.definesDone && metric.kind === 'boolean' && ( Defines success - ) : ( - + )} + {!metric.definesDone && metric.kind === 'boolean' && ( + )} - )} - - - {assignment.metrics.length > 1 && ( - - )} - - - - {metric.description || 'No description'} - - - - - - ))} - - {metricBeingCreated && ( - - setMetricBeingCreated(null)} - onSave={handleSaveMetric} - /> - - )} - {metricBeingEdited && ( - setMetricBeingEdited(null)} - open={metricBeingEdited ? true : false} - sx={{ - alignItems: 'center', - display: 'flex', - justifyContent: 'center', - }} - > - setMetricBeingEdited(null)} - onSave={handleSaveMetric} - /> - - )} - - - - Add questions for your canvass assignment. - - - - - - - setAnchorEl(null)} open={!!anchorEl}> - - - - {`Delete ${ - assignment.metrics.find( - (metric) => metric.id === metricBeingDeleted?.id - )?.question || 'Untitled question' - }`} - - { - setMetricBeingDeleted(null); - setAnchorEl(null); - }} - > - - - - - {metricBeingDeleted?.definesDone ? ( - - {`If you want to delete "${metricBeingDeleted.question}" you need to pick another - choice question to be the question that defines if the mision - was successful`} - - ) : ( - - Are you sure you want to delete this question? This action - is permanent and it cannot be undone. - - )} - - {metricBeingDeleted?.definesDone ? ( - - {assignment.metrics - .filter( - (metric) => - metric.kind == 'boolean' && - metric.id != metricBeingDeleted?.id - ) - .map((metric) => ( - - {metric.question || 'Untitled question'} - - - ))} - - ) : ( - - - - - )} - - - - - - - - - Defines success - - - - - - - { - const value = ev.target.value; - if (value === 'household' || value === 'location') { - updateAreaAssignment({ - reporting_level: value, - }); - } - }} - value={assignment.reporting_level} - > - Collect data.. - } - label="per household (most precise)" - sx={{ ml: 1 }} - value="household" - /> - } - label="per location (less precise, more privacy)" - sx={{ ml: 1 }} - value="location" - /> - - - - - - )} - - ); -}; - -AreaAssignmentLoggingPage.getLayout = function getLayout(page) { - return {page}; -}; - -export default AreaAssignmentLoggingPage; diff --git a/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx b/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx index a51c24cd5..7d1748f61 100644 --- a/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx +++ b/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx @@ -1,33 +1,46 @@ -import { Close } from '@mui/icons-material'; +import { + Close, + Delete, + Edit, + LinearScale, + SwitchLeft, +} from '@mui/icons-material'; import { GetServerSideProps } from 'next'; import { useState } from 'react'; import { + alpha, Box, Button, Card, - CardActions, CardContent, Dialog, + Divider, + FormControl, + FormControlLabel, IconButton, + InputLabel, MenuItem, + Radio, + RadioGroup, Select, + SelectChangeEvent, Typography, } from '@mui/material'; -import Head from 'next/head'; -import ZUIFuture from 'zui/ZUIFuture'; -import MetricCard from 'features/areaAssignments/components/MetricCard'; import { AREAS } from 'utils/featureFlags'; -import { scaffold } from 'utils/next'; +import AreaAssignmentLayout from 'features/areaAssignments/layouts/AreaAssignmentLayout'; +import MetricCard from 'features/areaAssignments/components/MetricCard'; import { PageWithLayout } from 'utils/types'; -import useAreaAssignmentMutations from 'features/areaAssignments/hooks/useAreaAssignmentMutations'; +import { scaffold } from 'utils/next'; +import theme from 'theme'; import useAreaAssignment from 'features/areaAssignments/hooks/useAreaAssignment'; +import useAreaAssignmentMutations from 'features/areaAssignments/hooks/useAreaAssignmentMutations'; +import ZUICard from 'zui/ZUICard'; +import ZUIFuture from 'zui/ZUIFuture'; import { ZetkinAreaAssignment, ZetkinMetric, } from 'features/areaAssignments/types'; -import AreaAssignmentLayout from 'features/areaAssignments/layouts/AreaAssignmentLayout'; -import ZUICard from 'zui/ZUICard'; const scaffoldOptions = { authLevelRequired: 2, @@ -41,12 +54,12 @@ export const getServerSideProps: GetServerSideProps = scaffold(async (ctx) => { }; }, scaffoldOptions); -interface AreaAssignmentReportProps { +interface AreaAssignmentLoggingProps { orgId: string; areaAssId: string; } -const AreaAssignmentReportPage: PageWithLayout = ({ +const AreaAssignmentLoggingPage: PageWithLayout = ({ orgId, areaAssId, }) => { @@ -56,35 +69,39 @@ const AreaAssignmentReportPage: PageWithLayout = ({ ); const areaAssignmentFuture = useAreaAssignment(parseInt(orgId), areaAssId); + const [metricBeingCreated, setMetricBeingCreated] = + useState(null); const [metricBeingEdited, setMetricBeingEdited] = useState(null); const [anchorEl, setAnchorEl] = useState(null); - const [idOfMetricBeingDeleted, setIdOfQuestionBeingDeleted] = useState< - string | null - >(null); + const [metricBeingDeleted, setMetricBeingDeleted] = + useState(null); const handleSaveMetric = async (metric: ZetkinMetric) => { if (areaAssignmentFuture.data) { await updateAreaAssignment({ metrics: areaAssignmentFuture.data.metrics - .map((m) => (m.id === metric.id ? metric : m)) + .map((m: ZetkinMetric) => (m.id === metric.id ? metric : m)) .concat(metric.id ? [] : [metric]), }); } setMetricBeingEdited(null); + setMetricBeingCreated(null); }; const handleDeleteMetric = async (id: string) => { if (areaAssignmentFuture.data) { await updateAreaAssignment({ - metrics: areaAssignmentFuture.data.metrics.filter((m) => m.id !== id), + metrics: areaAssignmentFuture.data.metrics.filter( + (m: ZetkinMetric) => m.id !== id + ), }); } setMetricBeingEdited(null); }; const handleAddNewMetric = (kind: 'boolean' | 'scale5') => { - setMetricBeingEdited({ + setMetricBeingCreated({ definesDone: false, description: '', id: '', @@ -94,115 +111,143 @@ const AreaAssignmentReportPage: PageWithLayout = ({ }; return ( - <> - - {areaAssignmentFuture.data?.title} - - - - {(assignment) => ( - <> - - - Decide what level of precision should be used for statistics. - - - - - Here you can configure the questions for your area assignment - - - - - - {metricBeingEdited && ( - setMetricBeingEdited(null)} - onSave={handleSaveMetric} - /> - )} - - {assignment.metrics.length > 0 ? 'Your list of questions:' : ''} - {assignment.metrics.map((metric) => ( - - - + + {(assignment: ZetkinAreaAssignment) => ( + + + + {assignment.metrics.map((metric) => ( + + + + - {metric.definesDone && ( - - This question defines if the mission was - successful + + + {metric.kind == 'boolean' ? ( + + + + ) : ( + + + + )} + {metric.question || 'Untitled question'} - )} - - {metric.question || 'Untitled question'} - - - {metric.description || 'No description'} - - - - - {metric.kind == 'boolean' ? 'Yes/no' : 'Scale'} - + + + {metric.definesDone && metric.kind === 'boolean' && ( + + Defines success + + )} + + + {assignment.metrics.length > 1 && ( + + )} + + + {metric.description || 'No description'} + - - - - - {assignment.metrics.length > 1 && ( - - )} - - - ))} + + + + ))} + + {metricBeingCreated && ( + + setMetricBeingCreated(null)} + onSave={handleSaveMetric} + /> + )} + {metricBeingEdited && ( + setMetricBeingEdited(null)} + open={metricBeingEdited ? true : false} + sx={{ + alignItems: 'center', + display: 'flex', + justifyContent: 'center', + }} + > + setMetricBeingEdited(null)} + onSave={handleSaveMetric} + /> + + )} + + + + Add questions for your canvass assignment. + + + + + + setAnchorEl(null)} open={!!anchorEl}> = ({ display="flex" justifyContent="space-between" > - {`Delete "${ - assignment.metrics.find( - (metric) => metric.id == idOfMetricBeingDeleted - )?.question - }"`} + + {`Delete ${ + assignment.metrics.find( + (metric) => metric.id === metricBeingDeleted?.id + )?.question || 'Untitled question' + }`} + { - setIdOfQuestionBeingDeleted(null); + setMetricBeingDeleted(null); setAnchorEl(null); }} > - - {`If you want to delete "${ - assignment.metrics.find( - (metric) => metric.id == idOfMetricBeingDeleted - )?.question - }" you need to pick another - yes/no-question to be the question that defines if the msision + + {metricBeingDeleted?.definesDone ? ( + + {`If you want to delete "${metricBeingDeleted.question}" you need to pick another + choice question to be the question that defines if the mision was successful`} - - - Yes/no questions - {assignment.metrics - .filter( - (metric) => - metric.kind == 'boolean' && - metric.id != idOfMetricBeingDeleted - ) - .map((metric) => ( - - {metric.question} - - - ))} - + {metric.question || 'Untitled question'} + + + ))} + + ) : ( + + + + + )} - - )} - - - + + + + + + + Defines success + + + + + + + { + const value = ev.target.value; + if (value === 'household' || value === 'location') { + updateAreaAssignment({ + reporting_level: value, + }); + } + }} + value={assignment.reporting_level} + > + Collect data.. + } + label="per household (most precise)" + sx={{ ml: 1 }} + value="household" + /> + } + label="per location (less precise, more privacy)" + sx={{ ml: 1 }} + value="location" + /> + + + + + + )} + ); }; -AreaAssignmentReportPage.getLayout = function getLayout(page) { +AreaAssignmentLoggingPage.getLayout = function getLayout(page) { return {page}; }; -export default AreaAssignmentReportPage; +export default AreaAssignmentLoggingPage; From 38babe91f57e763bacb403bb2f584b5a03c00de5 Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:43:27 +0100 Subject: [PATCH 22/24] rename page and props variables to report --- .../[campId]/areaassignments/[areaAssId]/report.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx b/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx index 7d1748f61..87315227e 100644 --- a/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx +++ b/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx @@ -54,12 +54,12 @@ export const getServerSideProps: GetServerSideProps = scaffold(async (ctx) => { }; }, scaffoldOptions); -interface AreaAssignmentLoggingProps { +interface AreaAssignmentReportProps { orgId: string; areaAssId: string; } -const AreaAssignmentLoggingPage: PageWithLayout = ({ +const AreaAssignmentReportPage: PageWithLayout = ({ orgId, areaAssId, }) => { @@ -460,8 +460,8 @@ const AreaAssignmentLoggingPage: PageWithLayout = ({ ); }; -AreaAssignmentLoggingPage.getLayout = function getLayout(page) { +AreaAssignmentReportPage.getLayout = function getLayout(page) { return {page}; }; -export default AreaAssignmentLoggingPage; +export default AreaAssignmentReportPage; From 8b906c038355614eacefb1d4f444ddda3ac61fa7 Mon Sep 17 00:00:00 2001 From: Rebe R <36491300+rebecarubio@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:02:48 +0100 Subject: [PATCH 23/24] add internationalization, change long ternary expression for two conditions --- .../areaAssignments/components/MetricCard.tsx | 10 +- .../areaAssignments/l10n/messageIds.ts | 44 +++++++ .../areaassignments/[areaAssId]/report.tsx | 121 ++++++++++-------- 3 files changed, 115 insertions(+), 60 deletions(-) diff --git a/src/features/areaAssignments/components/MetricCard.tsx b/src/features/areaAssignments/components/MetricCard.tsx index 4e8a05f83..99a376ff6 100644 --- a/src/features/areaAssignments/components/MetricCard.tsx +++ b/src/features/areaAssignments/components/MetricCard.tsx @@ -10,6 +10,8 @@ import { IconButton, } from '@mui/material'; +import messageIds from '../l10n/messageIds'; +import { Msg } from 'core/i18n'; import { ZetkinMetric } from '../types'; type MetricCardProps = { @@ -40,12 +42,12 @@ const MetricCard: FC = ({ metric, onClose, onSave }) => { {metric.kind === 'boolean' ? ( - Choice question + ) : ( - Scale question + )} @@ -56,7 +58,7 @@ const MetricCard: FC = ({ metric, onClose, onSave }) => { {metric.kind == 'scale5' && ( - The areaAssignee will respond by giving a rating from 1 to 5 + )} = ({ metric, onClose, onSave }) => { }} variant="contained" > - Save + diff --git a/src/features/areaAssignments/l10n/messageIds.ts b/src/features/areaAssignments/l10n/messageIds.ts index f31b5988b..e32ebb491 100644 --- a/src/features/areaAssignments/l10n/messageIds.ts +++ b/src/features/areaAssignments/l10n/messageIds.ts @@ -136,4 +136,48 @@ export default makeMessages('feat.areaAssignments', { }, }, }, + report: { + card: { + definesSuccess: m('Defines success'), + description: m('No description'), + question: m('Untitled question'), + }, + dataCard: { + header: m('Data precision & privacy'), + household: m('per household (most precise)'), + info: m('Collect data...'), + location: m('per location (less precise, more privacy)'), + subheader: m( + 'Configuring where to store the data is a matter of striking a balance between precision and privacy that is right for your cause' + ), + }, + delete: { + cancel: m('Cancel'), + confirm: m('Confirm'), + deleteWarningText: m<{ title: string }>( + 'If you want to delete {title} you need to pick another choice question to be the question that defines if the mision was successful' + ), + dialog: m( + 'Are you sure you want to delete this question? This action is permanent and it cannot be undone.' + ), + select: m('Select'), + }, + metricCard: { + choice: m('Choice question'), + ratingDescription: m( + ' The assignee will respond by giving a rating from 1 to 5' + ), + save: m('Save'), + scale: m('Scale question'), + }, + successCard: { + header: m('Successful visit'), + subheader: m( + 'Pick a question to use when counting successful visits. Answering yes to this question will count the visit as successful' + ), + }, + toolBar: { + title: m('Add questions for your canvass assignment.'), + }, + }, }); diff --git a/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx b/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx index 87315227e..3d4b652c3 100644 --- a/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx +++ b/src/pages/organize/[orgId]/projects/[campId]/areaassignments/[areaAssId]/report.tsx @@ -29,12 +29,14 @@ import { import { AREAS } from 'utils/featureFlags'; import AreaAssignmentLayout from 'features/areaAssignments/layouts/AreaAssignmentLayout'; +import messagesIds from 'features/areaAssignments/l10n/messageIds'; import MetricCard from 'features/areaAssignments/components/MetricCard'; import { PageWithLayout } from 'utils/types'; import { scaffold } from 'utils/next'; import theme from 'theme'; import useAreaAssignment from 'features/areaAssignments/hooks/useAreaAssignment'; import useAreaAssignmentMutations from 'features/areaAssignments/hooks/useAreaAssignmentMutations'; +import { Msg, useMessages } from 'core/i18n'; import ZUICard from 'zui/ZUICard'; import ZUIFuture from 'zui/ZUIFuture'; import { @@ -68,6 +70,7 @@ const AreaAssignmentReportPage: PageWithLayout = ({ areaAssId ); const areaAssignmentFuture = useAreaAssignment(parseInt(orgId), areaAssId); + const messages = useMessages(messagesIds); const [metricBeingCreated, setMetricBeingCreated] = useState(null); @@ -147,7 +150,8 @@ const AreaAssignmentReportPage: PageWithLayout = ({ )} - {metric.question || 'Untitled question'} + {metric.question || + messages.report.card.question()} @@ -162,7 +166,11 @@ const AreaAssignmentReportPage: PageWithLayout = ({ mr={1} p={0.5} > - Defines success + + + )} @@ -259,7 +268,7 @@ const AreaAssignmentReportPage: PageWithLayout = ({ {`Delete ${ assignment.metrics.find( (metric) => metric.id === metricBeingDeleted?.id - )?.question || 'Untitled question' + )?.question || messages.report.card.question() }`} = ({ - {metricBeingDeleted?.definesDone ? ( - - {`If you want to delete "${metricBeingDeleted.question}" you need to pick another - choice question to be the question that defines if the mision - was successful`} - - ) : ( - - Are you sure you want to delete this question? This action - is permanent and it cannot be undone. - - )} - - {metricBeingDeleted?.definesDone ? ( + {metricBeingDeleted?.definesDone && ( + + + {assignment.metrics .filter( (metric) => @@ -302,7 +304,7 @@ const AreaAssignmentReportPage: PageWithLayout = ({ justifyContent="space-between" width="100%" > - {metric.question || 'Untitled question'} + {metric.question || messages.report.card.question()} ))} - ) : ( - - - + )} + + {!metricBeingDeleted?.definesDone && ( + + + + + + + + )} @@ -366,20 +375,20 @@ const AreaAssignmentReportPage: PageWithLayout = ({ - Defines success + {messages.report.card.definesSuccess()}