Skip to content

Commit 8139a98

Browse files
authored
Merge pull request #899 from AppQuality/insights-page-collection
Insights page collection
2 parents 449221a + 9cd7deb commit 8139a98

File tree

3 files changed

+238
-17
lines changed

3 files changed

+238
-17
lines changed

src/pages/Insights/Collection/components/AccordionSection.tsx

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import {
22
Accordion,
3-
Checkbox,
43
getColor,
5-
Label,
64
LG,
5+
Tag,
76
} from '@appquality/unguess-design-system';
87
import styled from 'styled-components';
98
import { Grape as GrapeType } from 'src/features/api';
109
import { ReactComponent as TitleIcon } from '@zendeskgarden/svg-icons/src/12/copy-fill.svg';
10+
import { ReactComponent as UserIcon } from '@zendeskgarden/svg-icons/src/12/user-group-fill.svg';
11+
import { ReactComponent as ObservationIcon } from '@zendeskgarden/svg-icons/src/12/tag-stroke.svg';
1112
import { useMemo } from 'react';
1213
import { appTheme } from 'src/app/theme';
14+
import { ArrayHelpers, FieldArray } from 'formik';
1315
import { ObservationCard } from '../ObservationCard';
1416
import { CardGrid } from './CardGrid';
17+
import { GrapeCheckbox } from './GrapeCheckbox';
1518

1619
interface GrapeProps {
1720
grape: GrapeType;
@@ -56,11 +59,13 @@ const AccordionSection = styled(Accordion.Section)<{ severity: string }>`
5659
${({ severity, theme }) => getDropShadowColor(severity, theme)};
5760
`;
5861

62+
const AccordionLabel = styled(Accordion.Label)`
63+
display: grid;
64+
grid-template-columns: 1fr auto;
65+
gap: ${({ theme }) => theme.space.sm};
66+
`;
67+
5968
export const Grape = ({ grape }: GrapeProps) => {
60-
const handleCheckboxChange = (value: any) => {
61-
// eslint-disable-next-line no-alert
62-
alert(`secelt all obs ${value}`);
63-
};
6469
const memoizedGrape = useMemo(() => {
6570
const grapeSeverity = grape.severity.replace(' ', '-').toLowerCase();
6671
const observations = grape.observations.map((obs) => {
@@ -96,17 +101,42 @@ export const Grape = ({ grape }: GrapeProps) => {
96101
return (
97102
<AccordionSection severity={memoizedGrape.severity}>
98103
<Accordion.Header>
99-
<Accordion.Label>
100-
<Checkbox checked={false} onChange={handleCheckboxChange}>
101-
<Label>
102-
<TitleIcon
103-
color={getSeverityColor(memoizedGrape.severity, appTheme)}
104-
/>
105-
<LG isBold>{memoizedGrape.title}</LG>
106-
{memoizedGrape.observations.length} observations
107-
</Label>
108-
</Checkbox>
109-
</Accordion.Label>
104+
<AccordionLabel>
105+
<div
106+
style={{
107+
display: 'flex',
108+
alignItems: 'center',
109+
gap: appTheme.space.sm,
110+
}}
111+
>
112+
<FieldArray name="observations">
113+
{({ push, remove }: ArrayHelpers) => (
114+
<GrapeCheckbox
115+
push={push}
116+
remove={remove}
117+
grapeObservations={memoizedGrape.observations}
118+
/>
119+
)}
120+
</FieldArray>
121+
<TitleIcon
122+
color={getSeverityColor(memoizedGrape.severity, appTheme)}
123+
/>
124+
<LG isBold>{memoizedGrape.title}</LG>
125+
</div>
126+
<div>
127+
<Tag
128+
isPill
129+
hue={getDropShadowColor(memoizedGrape.severity, appTheme)}
130+
>
131+
<ObservationIcon />
132+
{memoizedGrape.observations.length}
133+
</Tag>
134+
<Tag isPill>
135+
<UserIcon color={getColor(appTheme.colors.accentHue, 600)} />
136+
{memoizedGrape.usersNumber}
137+
</Tag>
138+
</div>
139+
</AccordionLabel>
110140
</Accordion.Header>
111141
<Accordion.Panel>
112142
<CardGrid>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { Field } from '@zendeskgarden/react-forms';
2+
import { ArrayHelpers, useFormikContext } from 'formik';
3+
import { ChangeEvent, useCallback, useMemo } from 'react';
4+
import { Grape as GrapeType } from 'src/features/api';
5+
import { Checkbox, Label } from '@appquality/unguess-design-system';
6+
import { InsightFormValues } from '../../FormProvider';
7+
8+
interface Props {
9+
push: ArrayHelpers['push'];
10+
remove: ArrayHelpers['remove'];
11+
grapeObservations: GrapeType['observations'];
12+
}
13+
export const GrapeCheckbox = ({ push, remove, grapeObservations }: Props) => {
14+
const { values } = useFormikContext<InsightFormValues>();
15+
16+
const selectedObservations = useMemo(() => {
17+
const observationIds = grapeObservations.map((obs) => obs.id);
18+
return values.observations.filter((obs) => observationIds.includes(obs.id));
19+
}, [values.observations, grapeObservations]);
20+
21+
const checkboxState = useMemo(() => {
22+
if (selectedObservations.length === grapeObservations.length) {
23+
return {
24+
checked: true,
25+
indeterminate: false,
26+
};
27+
}
28+
if (selectedObservations.length > 0) {
29+
return {
30+
checked: false,
31+
indeterminate: true,
32+
};
33+
}
34+
return {
35+
checked: false,
36+
indeterminate: false,
37+
};
38+
}, [grapeObservations, values.observations]);
39+
40+
const handleCheckboxChange = useCallback(
41+
(e: ChangeEvent<HTMLInputElement>) => {
42+
e.preventDefault();
43+
e.stopPropagation();
44+
45+
if (e.target.checked) {
46+
grapeObservations
47+
// filter out already selected observations looking at their id
48+
.filter(
49+
(obs) => !selectedObservations.map((sel) => sel.id).includes(obs.id)
50+
)
51+
.forEach((obs) => {
52+
push(obs);
53+
});
54+
} else {
55+
selectedObservations.forEach((obs, i) => {
56+
// use i to update the index of the removed obs
57+
// because we are removing elements but values are not updated
58+
remove(values.observations.indexOf(obs) - i);
59+
});
60+
}
61+
},
62+
[selectedObservations, values.observations, grapeObservations, push, remove]
63+
);
64+
return (
65+
<Field>
66+
<Checkbox
67+
onChange={handleCheckboxChange}
68+
// use key to force re-render
69+
key={values.observations.length}
70+
{...checkboxState}
71+
>
72+
<Label hidden>
73+
select or deselect all observations in this cluster
74+
</Label>
75+
</Checkbox>
76+
);
77+
</Field>
78+
);
79+
};

src/pages/Insights/NewInsight.tsx

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import {
2+
Button,
3+
Checkbox,
4+
Input,
5+
Label,
6+
Message,
7+
} from '@appquality/unguess-design-system';
8+
import { Field, FieldProps, useFormikContext } from 'formik';
9+
import { Field as ZendeskField } from '@zendeskgarden/react-forms';
10+
import { useTranslation } from 'react-i18next';
11+
import { appTheme } from 'src/app/theme';
12+
import { InsightFormValues } from './FormProvider';
13+
14+
const NewInsightForm = () => {
15+
const { t } = useTranslation();
16+
const {
17+
values: { observations },
18+
isSubmitting,
19+
} = useFormikContext<InsightFormValues>();
20+
21+
return (
22+
<>
23+
<Label>{t('__INSIGHTS_PAGE_INSIGHT_FORM_FIELD_TITLE_LABEL')}</Label>
24+
<Field name="title">
25+
{({ field, form, meta }: FieldProps) => (
26+
<>
27+
<Input
28+
{...field}
29+
placeholder={t(
30+
'__INSIGHTS_PAGE_INSIGHT_FORM_FIELD_TITLE_PLACEHOLDER'
31+
)}
32+
onChange={(e) => {
33+
form.setFieldValue('title', e.target.value);
34+
}}
35+
/>
36+
{meta.touched && meta.error && (
37+
<Message
38+
validation="error"
39+
style={{ marginTop: appTheme.space.sm }}
40+
>
41+
{meta.error}
42+
</Message>
43+
)}
44+
</>
45+
)}
46+
</Field>
47+
<div style={{ margin: `${appTheme.space.md} 0` }}>
48+
<Label>
49+
{t('__INSIGHTS_PAGE_INSIGHT_FORM_FIELD_OBSERVATIONS_LABEL')}
50+
</Label>
51+
<div style={{ marginTop: appTheme.space.sm }}>
52+
{observations.map(
53+
(observation: InsightFormValues['observations'][number]) => {
54+
const title =
55+
observation.tags.find((tag) => tag.group.name === 'title')?.tag
56+
.name || observation.title;
57+
58+
return (
59+
<Field
60+
key={observation.id}
61+
name={`observations.${observation.id}`}
62+
>
63+
{({ form }: FieldProps) => (
64+
<ZendeskField>
65+
<Checkbox
66+
checked={
67+
!!observations.find((o) => o.id === observation.id)
68+
}
69+
onChange={(e) => {
70+
if (e.target.checked) {
71+
form.setFieldValue('observations', [
72+
...observations,
73+
observation,
74+
]);
75+
} else {
76+
form.setFieldValue(
77+
'observations',
78+
observations.filter(
79+
(o) => o.id !== observation.id
80+
)
81+
);
82+
}
83+
}}
84+
>
85+
<Label isRegular>{title}</Label>
86+
</Checkbox>
87+
</ZendeskField>
88+
)}
89+
</Field>
90+
);
91+
}
92+
)}
93+
</div>
94+
</div>
95+
<Button
96+
type="reset"
97+
isBasic
98+
disabled={isSubmitting}
99+
style={{
100+
marginRight: appTheme.space.sm,
101+
}}
102+
onClick={() => {}}
103+
>
104+
{t('__INSIGHTS_PAGE_INSIGHT_FORM_BUTTON_UNDO')}
105+
</Button>
106+
<Button isPrimary type="submit">
107+
{t('__INSIGHTS_PAGE_INSIGHT_FORM_BUTTON_SAVE')}
108+
</Button>
109+
</>
110+
);
111+
};
112+
export { NewInsightForm };

0 commit comments

Comments
 (0)