Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 1722/sub org survey read only #1941

Merged
merged 35 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
36d617c
pass campId to surveyLayout prop
kaulfield23 Apr 25, 2024
754239e
set title readonly when campId is shared
kaulfield23 Apr 25, 2024
050cd18
disable buttons, remove tooltip when campId is shared
kaulfield23 Apr 25, 2024
1784817
add readOnly prop to zuiDateRangePicker and disable it
kaulfield23 Apr 25, 2024
1aaa5ce
readOnly to surveyEditor, return null when question is hidden
kaulfield23 Apr 25, 2024
e5c2113
hide hidden elements when survey is shared
kaulfield23 Apr 26, 2024
79c4e65
hide submission sidebar when shared
kaulfield23 Apr 26, 2024
cadba07
add alert message to tabbedLayout when survey is shared
kaulfield23 Apr 26, 2024
1265490
show alert message when there is orgTitle
kaulfield23 Apr 26, 2024
c0914ae
try to get role from useOrganizations but it needs some change to store
kaulfield23 Apr 29, 2024
3f5a318
change tsx to ts and rename userOrgList to userMembershipList
kaulfield23 Apr 30, 2024
e47853f
change useOrganizations to useMemberships
kaulfield23 Apr 30, 2024
7e64617
use useSubOrgs hook instead of useMemberships, create ZetkinMembershi…
kaulfield23 Apr 30, 2024
cfa4138
navigate to original survey when clicking button in alert
kaulfield23 Apr 30, 2024
1d841a3
use existing useMemberships hook instead
kaulfield23 May 2, 2024
d5fe13a
move useMemberships hook to organizations dir, remove membershipLoad …
kaulfield23 May 2, 2024
9a32f33
hide questions on submission pane if hidden is true and hide edit war…
kaulfield23 May 2, 2024
8530c09
hide text block on submission pane if elem is hidden
kaulfield23 May 2, 2024
3cae507
create SharedCard, pass shared surveys data and add messages for the …
kaulfield23 May 2, 2024
1cc2803
navigates to shared with us project and create sharedActivitiesLayout
kaulfield23 May 3, 2024
988d14b
show hidden question and texts to suborgs
kaulfield23 May 3, 2024
db88b57
display only shared surveys on acitivities overview card
kaulfield23 May 3, 2024
8012e9a
add message 'shared' to breadcrumb messageIds
kaulfield23 May 3, 2024
cabf0c3
creates archive and activities page
kaulfield23 May 3, 2024
09e7956
shared card visible when there are active shared surveys, rename shar…
kaulfield23 May 6, 2024
1075182
filter active sub orgs only
kaulfield23 May 6, 2024
1b73f48
fix urls on shared pages, cleaning codes
kaulfield23 May 6, 2024
66ed4b6
fix styles and messages based on reviews
kaulfield23 May 7, 2024
665642b
change isReadOnly to readonly
kaulfield23 May 10, 2024
04527e8
fix logic to get shared surveys, add alert message on top of the shar…
kaulfield23 May 13, 2024
4d0efd5
remove redundant localescope
kaulfield23 May 14, 2024
7a071ed
set message when there are no activities, hide subtitle
kaulfield23 May 14, 2024
a49bdf0
move shared card to campaigns dir
kaulfield23 May 15, 2024
9f15e59
change messages based on review
kaulfield23 May 27, 2024
8958bad
Merge branch 'main' into issue-1722/sub-org-survey-read-only
kaulfield23 May 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/features/breadcrumbs/l10n/messageIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default makeMessages('feat.breadcrumbs', {
projects: m('Projects'),
questions: m('Questions'),
settings: m('Settings'),
shared: m('Shared with us'),
submissions: m('Submissions'),
surveys: m('Surveys'),
tasks: m('Tasks'),
Expand Down
25 changes: 23 additions & 2 deletions src/features/campaigns/components/ActivitiesOverview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import messageIds from 'features/campaigns/l10n/messageIds';
import useActivitiyOverview from 'features/campaigns/hooks/useActivityOverview';
import ZUIEmptyState from 'zui/ZUIEmptyState';
import ZUIFuture from 'zui/ZUIFuture';
import { ActivityOverview, CampaignActivity } from 'features/campaigns/types';
import { Msg, useMessages } from 'core/i18n';

type ActivitiesOverviewProps = {
campaignId?: number;
isShared?: boolean;
orgId: number;
};

const ActivitiesOverview: FC<ActivitiesOverviewProps> = ({
campaignId,
isShared,
orgId,
}) => {
const messages = useMessages(messageIds);
Expand All @@ -25,6 +28,15 @@ const ActivitiesOverview: FC<ActivitiesOverviewProps> = ({
const tomorrowDate = new Date();
tomorrowDate.setDate(tomorrowDate.getDate() + 1);

const filterSharedSurveys = (items: CampaignActivity[]) => {
return items.filter(
(item) =>
item.kind === 'survey' &&
item.data.org_access === 'suborgs' &&
item.data.organization.id != orgId
);
};

return (
<>
<Box
Expand All @@ -41,7 +53,7 @@ const ActivitiesOverview: FC<ActivitiesOverviewProps> = ({
<Box>
<NextLink
href={`/organize/${orgId}/projects${
campaignId ? `/${campaignId}` : ''
campaignId ? `/${campaignId}` : isShared ? '/shared' : ''
}/activities`}
legacyBehavior
passHref
Expand All @@ -53,7 +65,16 @@ const ActivitiesOverview: FC<ActivitiesOverviewProps> = ({
</Box>
</Box>
<ZUIFuture future={activityOverview}>
{(data) => {
{(activities) => {
//It only filters shared surveys for now, but there will be more shared activities in the future.
const data: ActivityOverview = isShared
? {
alsoThisWeek: filterSharedSurveys(activities.alsoThisWeek),
today: filterSharedSurveys(activities.today),
tomorrow: filterSharedSurveys(activities.tomorrow),
}
: activities;

const totalLength =
data.today.length + data.tomorrow.length + data.alsoThisWeek.length;

Expand Down
57 changes: 57 additions & 0 deletions src/features/campaigns/components/SharedCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { GroupWork } from '@mui/icons-material';
import NextLink from 'next/link';
import {
Box,
Card,
CardActions,
CardContent,
Link,
Typography,
} from '@mui/material';

import messageIds from 'features/campaigns/l10n/messageIds';
import { Msg } from 'core/i18n';
import theme from 'theme';
import { useNumericRouteParams } from 'core/hooks';

const SharedCard = (): JSX.Element => {
const { orgId } = useNumericRouteParams();

return (
<Card
data-testid="campaign-card"
sx={{ border: `2px solid ${theme.palette.primary.main}` }}
>
<CardContent>
<Box display="flex" justifyContent="space-between">
<Typography gutterBottom noWrap variant="h6">
<Msg id={messageIds.shared.title} />
</Typography>
<Box sx={{ display: 'flex', mr: '0.3rem', position: 'relative' }}>
<GroupWork
color="secondary"
sx={{ backgroundColor: 'white', borderRadius: '50%', zIndex: 1 }}
/>
<GroupWork
color="secondary"
sx={{ left: 8, position: 'absolute' }}
/>
kaulfield23 marked this conversation as resolved.
Show resolved Hide resolved
</Box>
</Box>
</CardContent>
<CardActions sx={{ paddingBottom: 2, paddingLeft: 2 }}>
<NextLink
href={`/organize/${orgId}/projects/shared`}
legacyBehavior
passHref
>
<Link underline="hover" variant="button">
<Msg id={messageIds.shared.cta} />
</Link>
</NextLink>
</CardActions>
</Card>
);
};

export default SharedCard;
24 changes: 24 additions & 0 deletions src/features/campaigns/l10n/messageIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,30 @@ export default makeMessages('feat.campaigns', {
heading: m('Mobilization and outreach (none configured)'),
},
noManager: m('No Project Manager'),
shared: {
cta: m('Go to project'),
noActivities: m(
'All ongoing activities shared with your organization will appear here.'
),
noArchives: m(
'All expired activities that have been shared with your organization will appear here.'
),
title: m('Shared with us'),
},
sharedLayout: {
alertMsg: m(
'This project contains only activities shared from other organizations and you can view it but not change it.'
),
subtitle: m<{ numOfActivities: number }>(
'{numOfActivities, plural, =1 {1 activity} other {# activities}}'
),
tabs: {
activities: m('Activities'),
archive: m('Archive'),
overview: m('Overview'),
},
title: m('Shared with us'),
},
singleProject: {
filterActivities: m('Type to filter'),
noActivities: m('There are no activities in this project yet.'),
Expand Down
74 changes: 74 additions & 0 deletions src/features/campaigns/layout/SharedActivitiesLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { AutoAwesomeMotionOutlined } from '@mui/icons-material';
import { Box } from '@mui/material';
import dayjs from 'dayjs';

import messageIds from '../l10n/messageIds';
import TabbedLayout from 'utils/layout/TabbedLayout';
import useSurveys from 'features/surveys/hooks/useSurveys';
import ZUIIconLabelRow from 'zui/ZUIIconLabelRow';
import { Msg, useMessages } from 'core/i18n';

interface SharedActivitiesLayout {
children: React.ReactNode;
orgId: string;
}

const SharedActivitiesLayout: React.FC<SharedActivitiesLayout> = ({
children,
orgId,
}) => {
const messages = useMessages(messageIds);
const parsedOrgId = parseInt(orgId);
const surveys = useSurveys(parsedOrgId).data ?? [];

const sharedSurveys = surveys.filter(
(survey) =>
survey.org_access === 'suborgs' &&
survey.organization.id != parsedOrgId &&
!dayjs(survey.expires).isBefore(dayjs(), 'day')
);

return (
<TabbedLayout
alertMsg={<Msg id={messageIds.sharedLayout.alertMsg} />}
baseHref={`/organize/${orgId}/projects/shared`}
defaultTab="/"
subtitle={
<Box alignItems="center" display="flex">
<Box display="flex" marginX={1}>
{sharedSurveys.length > 0 && (
<ZUIIconLabelRow
iconLabels={[
{
icon: <AutoAwesomeMotionOutlined />,
label: (
<Msg
id={messageIds.sharedLayout.subtitle}
values={{ numOfActivities: sharedSurveys.length }}
/>
),
},
]}
/>
)}
</Box>
</Box>
}
tabs={[
{ href: '/', label: messages.sharedLayout.tabs.overview() },
{
href: '/activities',
label: messages.sharedLayout.tabs.activities(),
},
{
href: '/archive',
label: messages.sharedLayout.tabs.archive(),
},
]}
title={messages.sharedLayout.title()}
>
{children}
</TabbedLayout>
);
};
export default SharedActivitiesLayout;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import AddedTags from './AddedTags';
import ChangedFields from './ChangedFields';
import CreatedAndUpdated from './CreatedAndUpdated';
import { PersonImportSummary } from 'features/import/utils/types';
import useOrganizations from 'features/organizations/hooks/useOrganizations';
import useSubOrganizations from 'features/organizations/hooks/useSubOrganizations';
import useTags from 'features/tags/hooks/useTags';

type Props = {
Expand All @@ -17,16 +17,17 @@ type Props = {

const ImpactSummary: FC<Props> = ({ orgId, summary, tense }) => {
const tags = useTags(orgId).data ?? [];
const organizations = useOrganizations().data ?? [];
const subOrgs = useSubOrganizations(orgId).data ?? [];

const { addedToOrg, tagged } = summary;

const addedTags = tags.filter((tag) =>
Object.keys(tagged.byTag).includes(tag.id.toString())
);

const orgsWithNewPeople = organizations.filter((org) =>
Object.keys(addedToOrg.byOrg).includes(org.id.toString())
const orgsWithNewPeople = subOrgs.filter(
(org) =>
Object.keys(addedToOrg.byOrg).includes(org.id.toString()) && org.is_active
);

return (
Expand Down
5 changes: 2 additions & 3 deletions src/features/import/hooks/usePersonPreview.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import createPreviewData from '../utils/createPreviewData';
import { Sheet } from '../utils/types';
import useOrganizations from 'features/organizations/hooks/useOrganizations';
import useSubOrganizations from 'features/organizations/hooks/useSubOrganizations';
import useTags from 'features/tags/hooks/useTags';
import { ZetkinTag } from 'utils/types/zetkin';

Expand All @@ -10,8 +10,7 @@ export default function usePersonPreview(
orgId: number
) {
const allTags = useTags(orgId).data ?? [];
const organizations = useOrganizations().data ?? [];

const organizations = useSubOrganizations(orgId).data ?? [];
const previewRow = createPreviewData(sheet, personIndex);

const fields = previewRow?.data;
Expand Down
24 changes: 13 additions & 11 deletions src/features/organizations/components/OrganizationsList.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import messageIds from 'features/organizations/l10n/messageIds';
import NextLink from 'next/link';
import { useMessages } from 'core/i18n';
import useOrganizations from '../hooks/useOrganizations';
import { ZetkinMembership } from 'utils/types/zetkin';
import { Avatar, Box, Link, List, ListItem, Typography } from '@mui/material';

import messageIds from 'features/organizations/l10n/messageIds';
import useMemberships from '../hooks/useMemberships';
import { useMessages } from 'core/i18n';
import ZUIFuture from 'zui/ZUIFuture';
import { Avatar, Box, Link, List, ListItem, Typography } from '@mui/material';

const OrganizationsList = () => {
const messages = useMessages(messageIds);
const organizations = useOrganizations();
const organizations = useMemberships();

return (
<ZUIFuture future={organizations}>
Expand All @@ -18,19 +17,22 @@ const OrganizationsList = () => {
<Box style={{ margin: '30px' }}>
<Typography variant="h3">{messages.page.title()}</Typography>
<List>
{data?.map((org: ZetkinMembership['organization']) => {
{data?.map((membership) => {
const orgId = membership.organization.id;
return (
<ListItem key={org.id}>
<ListItem key={orgId}>
<Avatar
src={`/api/orgs/${org.id}/avatar`}
src={`/api/orgs/${orgId}/avatar`}
style={{ margin: '15px' }}
/>
<NextLink
href={`/organize/${org.id}`}
href={`/organize/${orgId}`}
legacyBehavior
passHref
>
<Link underline="hover">{org.title}</Link>
<Link underline="hover">
{membership.organization.title}
</Link>
</NextLink>
</ListItem>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { IFuture } from 'core/caching/futures';
import { loadListIfNecessary } from 'core/caching/cacheUtils';
import { ZetkinMembership } from 'utils/types/zetkin';
import { membershipsLoad, membershipsLoaded } from 'features/user/store';
import { useApiClient, useAppDispatch, useAppSelector } from 'core/hooks';
import { userMembershipsLoad, userMembershipsLoaded } from '../store';

export default function useMemberships(): IFuture<ZetkinMembership[]> {
const apiClient = useApiClient();
const dispatch = useAppDispatch();
const membershipList = useAppSelector((state) => state.user.membershipList);
const membershipList = useAppSelector(
(state) => state.organizations.userMembershipList
);

return loadListIfNecessary(membershipList, dispatch, {
actionOnLoad: () => dispatch(membershipsLoad()),
actionOnSuccess: (data) => dispatch(membershipsLoaded(data)),
actionOnLoad: () => dispatch(userMembershipsLoad()),
actionOnSuccess: (data) => dispatch(userMembershipsLoaded(data)),
loader: () =>
apiClient.get<ZetkinMembership[]>(`/api/users/me/memberships`),
apiClient
.get<ZetkinMembership[]>(`/api/users/me/memberships`)
.then((response) => response.filter((m) => m.role != null)),
});
}
33 changes: 0 additions & 33 deletions src/features/organizations/hooks/useOrganizations.tsx

This file was deleted.

Loading
Loading