Advertisement
minafaw3

InsightsFilterDrawer

Dec 10th, 2024
20
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.73 KB | None | 0 0
  1. /* eslint-disable react/prop-types */
  2. /* eslint-disable max-len */
  3.  
  4. import React, { useCallback, useMemo } from 'react';
  5. import { useTranslation } from 'react-i18next';
  6. import {
  7. Box, FormControlLabelProps, Grid, RadioProps, Stack, Typography,
  8. } from '@mui/material';
  9. import { Field, Form, Formik } from 'formik';
  10. import { useDispatch, useSelector } from 'react-redux';
  11. import useInsightsDashboard from 'scenes/Insights/hooks/useInsightsDashboard';
  12. import Button from 'components/Controls/Button/Button';
  13. import { disableDrawer } from 'ducks/Drawer/actions';
  14. import { selectors as globalSelectors } from 'ducks/global';
  15. import { getDateRangeInMilSecs, renderRangeDate } from 'utils/dateUtils';
  16. import useApig from 'shared/hooks/useApig';
  17. import { trackEvent } from '@sbd-ctg/user-behavior-tracking';
  18. import { InsightsAssignment, InsightsDashboardFilter } from '../../domain/insightsDashboard';
  19. import FilterDrawer from './FilterDrawer';
  20. import FilterDrawerToggleButton from './FilterDrawerToggleButton';
  21. import {
  22. ContentWrapper,
  23. StyledLabelWeeks,
  24. StyledRadioGroup,
  25. StyledRadioWeeks,
  26. } from './InsightsFilterDrawer.style';
  27. import AssignmentIdDropdown from '../Fields/AssignmentIdDropdown';
  28. import ToolClassDropdown from '../Fields/ToolClassDropdown';
  29. import ModelNumberDropdown from '../Fields/ModelNumberDropdown';
  30. import { GET_JOBSITE_POWERSHIFT_LIST_SUCCESS } from '../../../../ducks/Insights/types';
  31. import { InsightsFilterDrawerSchema } from 'schemas/validation';
  32.  
  33. type RadioButtonProps = Omit<FormControlLabelProps, 'control'> & RadioProps & { testid: string };
  34.  
  35. const WeeksRadioButton: React.FC<RadioButtonProps> = ({
  36. label, testid = '', ...props
  37. }) => (
  38. <StyledLabelWeeks
  39. control={(
  40. <StyledRadioWeeks
  41. {...props}
  42. icon={<span>{label}</span>}
  43. checkedIcon={<span>{label}</span>}
  44. data-testid={testid}
  45. />
  46. )}
  47. label=""
  48. />
  49. );
  50.  
  51. const InsightsFilterDrawer = () => {
  52. const { t } = useTranslation();
  53. const dispatch = useDispatch();
  54. const companyId = useSelector((state: any) => globalSelectors.getSelectedCompanyId(state));
  55. const status = useApig(GET_JOBSITE_POWERSHIFT_LIST_SUCCESS);
  56.  
  57. const {
  58. filters,
  59. fetchData,
  60. fetchCompareData,
  61. setDashboardFilter,
  62. isComparingAssets,
  63. assetClassesState,
  64. getInsightsUrl,
  65. insightsUnavailable,
  66. } = useInsightsDashboard();
  67.  
  68. const initialValues: InsightsDashboardFilter = { ...filters };
  69.  
  70. const dateRanges = [
  71. { value: 1, label: t('Insights.FilterDrawer.DateRangeLastWeek'), testid: 'insights-filters-weeks-1' },
  72. { value: 2, label: t('Insights.FilterDrawer.DateRangeLast2Weeks'), testid: 'insights-filters-weeks-2' },
  73. { value: 4, label: t('Insights.FilterDrawer.DateRangeLast4Weeks'), testid: 'insights-filters-weeks-4' },
  74. { value: 12, label: t('Insights.FilterDrawer.DateRangeLast12Weeks'), testid: 'insights-filters-weeks-12' },
  75. ];
  76.  
  77. const renderDateRangeSelected = useCallback((weeks) => {
  78. const { start, end } = getDateRangeInMilSecs(weeks);
  79. return (
  80. weeks
  81. ? (
  82. <Typography variant="caption" color="text.secondary" marginTop="10px" component="div">
  83. {renderRangeDate(start, end)}
  84. </Typography>
  85. )
  86. : null
  87. );
  88. }, []);
  89.  
  90. const handleAssetClassChange = useCallback((assetClass: string, { setFieldValue }: any) => {
  91. setFieldValue('assetClass', assetClass);
  92. setFieldValue('modelNumber', null);
  93. setFieldValue('assignmentId', null);
  94. setFieldValue('assignmentId2', null);
  95. }, []);
  96.  
  97. const handleModelNumberChange = useCallback((modelNumber: string, { setFieldValue }: any) => {
  98. setFieldValue('modelNumber', modelNumber);
  99. setFieldValue('assignmentId', null);
  100. setFieldValue('assignmentId2', null);
  101. }, []);
  102.  
  103. const handleDateRangeChange = (weeks: string, setFieldValue: (...params: any) => void) => {
  104. const intWeek = +weeks;
  105. const { start, end } = getDateRangeInMilSecs(intWeek);
  106. const defineWindow = intWeek === 4 || intWeek === 12 ? 'Weeks' : 'Days';
  107.  
  108. setFieldValue('start', start);
  109. setFieldValue('end', end);
  110. setFieldValue('window', defineWindow);
  111. setFieldValue('weeks', intWeek);
  112. };
  113.  
  114. const getAssignments = useCallback((values: InsightsDashboardFilter): InsightsAssignment[] => {
  115. const { assetClass, modelNumber } = values;
  116.  
  117. if (assetClass && modelNumber) {
  118. const assetClassData = assetClassesState.data[assetClass]; // Safe indexing since `data` is always an object
  119. return assetClassData?.assignmentsByModelNumber?.[modelNumber] || [];
  120. }
  121.  
  122. return [];
  123. }, [assetClassesState]);
  124.  
  125. const handleSubmit = useCallback((values: InsightsDashboardFilter) => {
  126. const checkDirty: Array<keyof InsightsDashboardFilter> = ['assetClass', 'modelNumber', 'weeks', 'assignmentId', 'assignmentId2'];
  127. const changes = checkDirty.reduce((acc: Record<string, boolean>, field) => {
  128. acc[field] = values[field] !== filters[field];
  129. // if (valueChanged) {
  130. // console.debug(`${field} changed from ${filters[field]} to ${values[field]}`);
  131. // }
  132. return acc;
  133. }, {});
  134.  
  135. const fetchLeft = changes.assetClass || changes.modelNumber || changes.assignmentId || changes.weeks;
  136. const fetchRight = isComparingAssets && (changes.assetClass || changes.modelNumber || changes.assignmentId2 || changes.weeks);
  137. const trackingValues = {
  138. assetClass: values.assetClass,
  139. modelNumber: values.modelNumber,
  140. window: values.window,
  141. dateRange: renderRangeDate(values.start, values.end),
  142. assignment1: values.assignmentId,
  143. ...(isComparingAssets && { assignment2: values.assignmentId2 }),
  144. };
  145.  
  146. trackEvent('apply_filter', { category: 'Insights', filters: trackingValues });
  147.  
  148. if (fetchLeft || fetchRight) {
  149. const newValues = isComparingAssets ? { ...values } : {
  150. ...values, assignmentId2: '',
  151. };
  152. setDashboardFilter(newValues);
  153.  
  154. if (fetchLeft) {
  155. fetchData(newValues);
  156. }
  157. if (fetchRight) {
  158. fetchCompareData(newValues);
  159. }
  160. }
  161. dispatch(disableDrawer());
  162. }, [filters, isComparingAssets, getAssignments]);
  163.  
  164. const handleCopyFilters = (event: React.MouseEvent<HTMLButtonElement>, values: InsightsDashboardFilter) => {
  165. if (event.ctrlKey || event.metaKey) {
  166. event.preventDefault(); // Prevent the form from submitting
  167. const url = getInsightsUrl(values);
  168. navigator.clipboard.writeText(url)
  169. .then(() => {
  170. alert('Text copied to clipboard!');
  171. })
  172. .catch((err) => {
  173. console.error('Failed to copy text: ', err);
  174. });
  175. }
  176. };
  177.  
  178. return (
  179. <FilterDrawer
  180. key={`UtilizationFilter-${companyId}`}
  181. drawerName="DASHBOARD"
  182. title={t('Common.Filters')}
  183. content={(
  184. <Formik initialValues={initialValues} onSubmit={handleSubmit} validationSchema={InsightsFilterDrawerSchema(isComparingAssets)}>
  185. {({ values, setFieldValue, isValid }) => {
  186. const memoizedAssignments = useMemo(() => getAssignments(values), [getAssignments, values]);
  187. return (
  188. <Form style={{ height: '100%' }}>
  189. <ContentWrapper direction="column">
  190. <Stack direction="column" spacing={2} className="fields-controllers">
  191. <Stack direction="column" spacing={2}>
  192. <Typography variant="body1">
  193. {t('Insights.FilterDrawer.AssetHeading')}
  194. </Typography>
  195. <Field name="assetClass">
  196. {({ form } : any) => (
  197. <ToolClassDropdown
  198. defaultValue={filters.assetClass}
  199. disabled={isComparingAssets || insightsUnavailable}
  200. onChange={e => handleAssetClassChange(e.target.value as string, form)}
  201. />
  202. )}
  203. </Field>
  204. <Field name="modelNumber" disabled={isComparingAssets}>
  205. {({ form }: any) => (
  206. <ModelNumberDropdown
  207. key={`ModelNumber-${values.assetClass}`}
  208. value={values.modelNumber}
  209. assetClass={values.assetClass}
  210. disabled={isComparingAssets}
  211. onChange={e => handleModelNumberChange(e.target.value as string, form)}
  212. />
  213. )}
  214. </Field>
  215. <Field name="assignmentId">
  216. {() => (
  217. <AssignmentIdDropdown
  218. key={`AssignmentId-${values.assetClass}-${values.modelNumber}`}
  219. label={isComparingAssets ? t('Insights.FilterDrawer.Assignment1') : t('Insights.FilterDrawer.Assignment')}
  220. value={values.assignmentId}
  221. assignments={memoizedAssignments}
  222. loading={status === 'loading'}
  223. onChange={e => setFieldValue('assignmentId', e.target.value)}
  224. disabled={!values.modelNumber}
  225. />
  226. )}
  227. </Field>
  228. {isComparingAssets && (
  229. <Field name="assignmentId2">
  230. {() => (
  231. <AssignmentIdDropdown
  232. key={`AssignmentId2-${values.assetClass}-${values.modelNumber}`}
  233. label={t('Insights.FilterDrawer.Assignment2')}
  234. value={values.assignmentId2}
  235. assignments={memoizedAssignments}
  236. loading={status === 'loading'}
  237. onChange={e => setFieldValue('assignmentId2', e.target.value)}
  238. disabled={!values.modelNumber}
  239. />
  240. )}
  241. </Field>
  242. )}
  243. </Stack>
  244. <Box marginTop={2}>
  245. <Typography variant="body1" margin="20px 0 10px 0">
  246. {t('Insights.FilterDrawer.DateHeading')}
  247. </Typography>
  248. <StyledRadioGroup
  249. name="weeks"
  250. value={values.weeks}
  251. onChange={({ target }: any) => {
  252. handleDateRangeChange(target.value, setFieldValue);
  253. }}
  254. >
  255. {dateRanges.map(({ label, value, testid }) => (
  256. <WeeksRadioButton
  257. name="weeks"
  258. value={value}
  259. defaultValue={value}
  260. label={label}
  261. key={label}
  262. testid={testid}
  263. disabled={insightsUnavailable}
  264. required
  265. />
  266. ))}
  267. </StyledRadioGroup>
  268. {renderDateRangeSelected(values.weeks)}
  269. </Box>
  270. </Stack>
  271.  
  272. <Grid spacing={2} paddingTop={2} textTransform="capitalize" container>
  273. <Grid item xs={6}>
  274. <FilterDrawerToggleButton
  275. size="medium"
  276. variant="outlined"
  277. color="secondary"
  278. drawerName="DASHBOARD"
  279. disabled={insightsUnavailable}
  280. fullWidth
  281. sx={{ height: '42px' }}
  282. testid="insights-filters-cancel"
  283. >
  284. {t('Common.Cancel')}
  285. </FilterDrawerToggleButton>
  286. </Grid>
  287. <Grid item xs={6}>
  288. <Button
  289. size="medium"
  290. variant="contained"
  291. color="primary"
  292. type="submit"
  293. fullWidth
  294. disabled={!isValid}
  295. sx={{ height: '42px' }}
  296. data-testid="insights-filters-apply"
  297. onClick={e => handleCopyFilters(e, values)}
  298. >
  299. {t('RateTableSettingsModal.Buttons.Apply')}
  300. </Button>
  301. </Grid>
  302. </Grid>
  303. </ContentWrapper>
  304. </Form>
  305. );
  306. }}
  307. </Formik>
  308. )}
  309. />
  310. );
  311. };
  312.  
  313. export default InsightsFilterDrawer;
  314.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement