Skip to content

Commit 8905394

Browse files
authored
Merge pull request #1462 from AppQuality/UN-1999
Un 1999
2 parents dc7d7fd + ecd78b3 commit 8905394

File tree

6 files changed

+163
-1
lines changed

6 files changed

+163
-1
lines changed

src/features/api/apiTags.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,9 @@ unguessApi.enhanceEndpoints({
336336
postPlansByPidWatchers: {
337337
invalidatesTags: ['PlanWatchers'],
338338
},
339+
putPlansByPidWatchers: {
340+
invalidatesTags: ['PlanWatchers'],
341+
},
339342
deletePlansByPidWatchersAndProfileId: {
340343
invalidatesTags: ['PlanWatchers'],
341344
},

src/features/api/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,16 @@ const injectedRtkApi = api.injectEndpoints({
802802
body: queryArg.body,
803803
}),
804804
}),
805+
putPlansByPidWatchers: build.mutation<
806+
PutPlansByPidWatchersApiResponse,
807+
PutPlansByPidWatchersApiArg
808+
>({
809+
query: (queryArg) => ({
810+
url: `/plans/${queryArg.pid}/watchers`,
811+
method: 'PUT',
812+
body: queryArg.body,
813+
}),
814+
}),
805815
getWorkspacesByWidTemplates: build.query<
806816
GetWorkspacesByWidTemplatesApiResponse,
807817
GetWorkspacesByWidTemplatesApiArg
@@ -2055,6 +2065,15 @@ export type PostPlansByPidWatchersApiArg = {
20552065
}[];
20562066
};
20572067
};
2068+
export type PutPlansByPidWatchersApiResponse = /** status 200 OK */ void;
2069+
export type PutPlansByPidWatchersApiArg = {
2070+
pid: string;
2071+
body: {
2072+
users: {
2073+
id: number;
2074+
}[];
2075+
};
2076+
};
20582077
export type GetWorkspacesByWidTemplatesApiResponse = /** status 200 OK */ {
20592078
items: CpReqTemplate[];
20602079
} & PaginationData;
@@ -3110,6 +3129,7 @@ export const {
31103129
useGetWorkspacesByWidProjectsAndPidCampaignsQuery,
31113130
useGetPlansByPidWatchersQuery,
31123131
usePostPlansByPidWatchersMutation,
3132+
usePutPlansByPidWatchersMutation,
31133133
useGetWorkspacesByWidTemplatesQuery,
31143134
usePostWorkspacesByWidTemplatesMutation,
31153135
useDeleteWorkspacesByWidTemplatesAndTidMutation,

src/locales/en/translation.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,11 @@
849849
"__PLAN_PAGE_MODAL_SEND_REQUEST_TITLE_LABEL": "Activity title",
850850
"__PLAN_PAGE_MODAL_SEND_REQUEST_TOAST_ERROR": "Something went wrong while requesting the activity",
851851
"__PLAN_PAGE_MODAL_SEND_REQUEST_WAIT": "<XL>Setting up your request</XL><LG>We're preparing everything for our experts to review. This will take just a few seconds.</LG>",
852+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_DESCRIPTION": "They’ll receive email updates at every stage",
853+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_ERROR": "Please add at least one team member to continue",
854+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_HINT": "You can add only workspace members in this phase",
855+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_LABEL": "Involve your team in this activity",
856+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_PLACEHOLDER": "Search for team members...",
852857
"__PLAN_PAGE_MODUL_GENERAL_REMOVE_MODAL_CANCEL": "Cancel",
853858
"__PLAN_PAGE_MODUL_GENERAL_REMOVE_MODAL_CONFIRM": "Confirm",
854859
"__PLAN_PAGE_MODUL_GENERAL_REMOVE_MODAL_DESCRIPTION": "If you delete the item, the entered information will no longer be recoverable.",

src/locales/it/translation.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,11 @@
882882
"__PLAN_PAGE_MODAL_SEND_REQUEST_TITLE_LABEL": "",
883883
"__PLAN_PAGE_MODAL_SEND_REQUEST_TOAST_ERROR": "",
884884
"__PLAN_PAGE_MODAL_SEND_REQUEST_WAIT": "",
885+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_DESCRIPTION": "",
886+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_ERROR": "",
887+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_HINT": "",
888+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_LABEL": "",
889+
"__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_PLACEHOLDER": "",
885890
"__PLAN_PAGE_MODUL_GENERAL_REMOVE_MODAL_CANCEL": "",
886891
"__PLAN_PAGE_MODUL_GENERAL_REMOVE_MODAL_CONFIRM": "",
887892
"__PLAN_PAGE_MODUL_GENERAL_REMOVE_MODAL_DESCRIPTION": "",

src/pages/Plan/modals/SendRequestModal.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,19 @@ import {
1313
useToast,
1414
XL,
1515
} from '@appquality/unguess-design-system';
16+
import { useState } from 'react';
1617
import { Trans, useTranslation } from 'react-i18next';
1718
import { useParams } from 'react-router-dom';
1819
import { appTheme } from 'src/app/theme';
19-
import { useGetPlansByPidRulesEvaluationQuery } from 'src/features/api';
20+
import {
21+
useGetPlansByPidRulesEvaluationQuery,
22+
usePutPlansByPidWatchersMutation,
23+
} from 'src/features/api';
2024
import { useRequestQuotation } from 'src/features/modules/useRequestQuotation';
2125
import { useValidateForm } from 'src/features/planModules';
2226
import { getModuleBySlug } from '../modules/Factory';
2327
import { PurchasablePlanRulesGuide } from './PurchasablePlanRules';
28+
import { Watchers } from './Watchers';
2429

2530
const SendRequestModal = ({
2631
onQuit,
@@ -31,10 +36,12 @@ const SendRequestModal = ({
3136
}) => {
3237
const { planId } = useParams();
3338
const { t } = useTranslation();
39+
const [updateWatchers] = usePutPlansByPidWatchersMutation();
3440
const { isRequestingQuote, handleQuoteRequest } = useRequestQuotation();
3541
const { data, isLoading } = useGetPlansByPidRulesEvaluationQuery({
3642
pid: planId || '',
3743
});
44+
const [watchers, setWatchers] = useState<number[]>([]);
3845

3946
const isFailed = isPurchasable && data && data.failed.length > 0;
4047
const { addToast } = useToast();
@@ -43,8 +50,14 @@ const SendRequestModal = ({
4350

4451
const { validateForm } = useValidateForm();
4552

53+
if (!planId) return null;
54+
4655
const handleConfirm = async () => {
4756
try {
57+
await updateWatchers({
58+
pid: planId,
59+
body: { users: watchers.map((id) => ({ id })) },
60+
}).unwrap();
4861
await validateForm();
4962
await handleQuoteRequest();
5063
} catch (e) {
@@ -154,6 +167,18 @@ const SendRequestModal = ({
154167
{t('__PLAN_PAGE_MODAL_SEND_REQUEST_DATES_HINT')}
155168
</Message>
156169
</div>
170+
<div style={{ padding: `${appTheme.space.md} 0` }}>
171+
<Label style={{ marginBottom: appTheme.space.xxs }}>
172+
{t('__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_LABEL')}
173+
</Label>
174+
<SM style={{ marginBottom: appTheme.space.sm }}>
175+
{t('__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_DESCRIPTION')}
176+
</SM>
177+
<Watchers onChange={setWatchers} planId={planId} />
178+
<Message style={{ marginTop: appTheme.space.sm }}>
179+
{t('__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_HINT')}
180+
</Message>
181+
</div>
157182
</>
158183
)}
159184
</Modal.Body>
@@ -171,6 +196,7 @@ const SendRequestModal = ({
171196
</FooterItem>
172197
<FooterItem>
173198
<Button
199+
disabled={watchers.length === 0}
174200
isAccent
175201
isPrimary
176202
onClick={handleConfirm}

src/pages/Plan/modals/Watchers.tsx

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import {
2+
Message,
3+
MultiSelect,
4+
Skeleton,
5+
} from '@appquality/unguess-design-system';
6+
import { useEffect, useState } from 'react';
7+
import { useTranslation } from 'react-i18next';
8+
import {
9+
useGetPlansByPidWatchersQuery,
10+
useGetWorkspacesByWidUsersQuery,
11+
} from 'src/features/api';
12+
import { useActiveWorkspace } from 'src/hooks/useActiveWorkspace';
13+
import { useTheme } from 'styled-components';
14+
15+
const useOptions = (planId: string) => {
16+
const [watchers, setWatchers] = useState<
17+
{
18+
id: number;
19+
label: string;
20+
selected: boolean;
21+
}[]
22+
>([]);
23+
const { activeWorkspace, isLoading: isActiveWorkspaceLoading } =
24+
useActiveWorkspace();
25+
const { data: users, isLoading: isLoadingUsers } =
26+
useGetWorkspacesByWidUsersQuery(
27+
{
28+
wid: (activeWorkspace?.id || '0').toString(),
29+
},
30+
{
31+
skip: !activeWorkspace?.id,
32+
}
33+
);
34+
const { data, isLoading } = useGetPlansByPidWatchersQuery({ pid: planId });
35+
useEffect(() => {
36+
if (users) {
37+
const watchersIds = (data?.items || []).map((watcher) => watcher.id);
38+
const options = (users?.items || []).map((user) => ({
39+
id: user.profile_id,
40+
label: `${user.name}`,
41+
selected: watchersIds.includes(user.profile_id),
42+
}));
43+
setWatchers(options);
44+
}
45+
}, [data, users]);
46+
47+
if (isLoading || isLoadingUsers || isActiveWorkspaceLoading)
48+
return { options: [], select: () => {}, isLoading: true };
49+
50+
return {
51+
options: watchers,
52+
select: (selected: number[]) =>
53+
setWatchers(
54+
watchers.map((w) => ({ ...w, selected: selected.includes(w.id) }))
55+
),
56+
isLoading: false,
57+
};
58+
};
59+
60+
const Watchers = ({
61+
onChange,
62+
planId,
63+
}: {
64+
onChange: (selectedIds: number[]) => void;
65+
planId: string;
66+
}) => {
67+
const { t } = useTranslation();
68+
const appTheme = useTheme();
69+
const { options, select, isLoading } = useOptions(planId);
70+
71+
useEffect(() => {
72+
const selected = options.filter((item) => item.selected);
73+
onChange(selected.map((item) => Number(item.id)));
74+
}, [options]);
75+
76+
if (isLoading) return <Skeleton width="100%" height="40px" />;
77+
78+
return (
79+
<div>
80+
<MultiSelect
81+
listboxAppendToNode={document.body}
82+
options={options}
83+
size="small"
84+
maxItems={10}
85+
i18n={{
86+
placeholder: t('__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_PLACEHOLDER'),
87+
}}
88+
onChange={async (selectedItems) => {
89+
const selected = selectedItems.filter((item) => item.selected);
90+
select(selected.map((item) => Number(item.id)));
91+
onChange(selected.map((item) => Number(item.id)));
92+
}}
93+
/>
94+
{options.filter((item) => item.selected).length === 0 && (
95+
<Message style={{ marginTop: appTheme.space.xs }} validation="error">
96+
{t('__PLAN_PAGE_MODAL_SEND_REQUEST_WATCHERS_ERROR')}
97+
</Message>
98+
)}
99+
</div>
100+
);
101+
};
102+
103+
export { Watchers };

0 commit comments

Comments
 (0)