antd#TableColumnsType TypeScript Examples

The following examples show how to use antd#TableColumnsType. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: TicketListView.tsx    From condo with MIT License 5 votes vote down vote up
TicketListView: React.FC<ITicketAnalyticsPageListViewProps> = (props) => {
    const { loading = false, data, viewMode, mapperInstance, filters } = props

    const intl = useIntl()
    const DateTitle = intl.formatMessage({ id: 'Date' })
    const AddressTitle = intl.formatMessage({ id: 'field.Address' })
    const ExecutorTitle = intl.formatMessage({ id: 'field.Executor' })
    const AssigneeTitle = intl.formatMessage({ id: 'field.Responsible' })
    const AllAddressTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.tableColumns.AllAddresses' })
    const CategoryClassifierTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.tableColumns.Classifier' })
    const AllCategoryClassifiersTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.tableColumns.AllClassifiers' })
    const AllExecutorsTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.tableColumns.AllExecutors' })
    const AllAssigneesTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.tableColumns.AllAssignees' })
    const { isSmall } = useLayoutContext()

    if (data === null || filters === null || loading) {
        return <Skeleton loading={loading} active paragraph={{ rows: 10 }} />
    }

    const restOptions = {
        translations: {
            date: DateTitle,
            address: AddressTitle,
            categoryClassifier: CategoryClassifierTitle,
            executor: ExecutorTitle,
            assignee: AssigneeTitle,
            allCategoryClassifiers: AllCategoryClassifiersTitle,
            allAddresses: AllAddressTitle,
            allExecutors: AllExecutorsTitle,
            allAssignees: AllAssigneesTitle,
        },
        filters: {
            address: filters.addressList.map(({ value }) => value),
            categoryClassifier: filters.classifierList.map(({ value }) => value),
            executor: filters.executorList.map(({ value }) => value),
            assignee: filters.responsibleList.map(({ value }) => value),
        },
    }

    const { tableColumns, dataSource } = mapperInstance.getTableConfig(viewMode, data, restOptions)

    return (
        <Table
            bordered
            tableLayout={'fixed'}
            scroll={getScrollConfig(isSmall)}
            dataSource={dataSource}
            columns={tableColumns as TableColumnsType}
            pagination={false}
        />
    )
}
Example #2
Source File: index.tsx    From datart with Apache License 2.0 5 votes vote down vote up
ScheduleErrorLog: FC<ScheduleErrorLogProps> = ({ scheduleId }) => {
  const dispatch = useDispatch();
  const logs = useSelector(selectScheduleLogs),
    loading = useSelector(selectScheduleLogsLoading);
  const { actions } = useScheduleSlice();
  const t = useI18NPrefix(
    'main.pages.schedulePage.sidebar.editorPage.scheduleErrorLog.index',
  );
  useEffect(() => {
    if (scheduleId) {
      dispatch(getScheduleErrorLogs({ scheduleId, count: 100 }));
    }
    return () => {
      dispatch(actions.clearLogs);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [scheduleId, dispatch]);
  const columns: TableColumnsType<ErrorLog> = useMemo(() => {
    return [
      { title: t('startTime'), dataIndex: 'start', key: 'start' },
      { title: t('endTime'), dataIndex: 'end', key: 'end' },
      {
        title: t('logPhase'),
        dataIndex: 'status',
        key: 'status',
        render(status: LogStatus) {
          return LOG_STATUS_TEXT[status];
        },
      },
      {
        title: t('executionInformation'),
        dataIndex: 'message',
        key: 'message',
        render(text, record) {
          const isSuccess = record?.status === LogStatus.S15;
          return isSuccess ? t('success') : text;
        },
      },
    ];
  }, []);
  if (logs?.length > 0) {
    return (
      <FormCard title={t('log')}>
        <FormWrapper>
          <Table
            rowKey="id"
            loading={loading}
            columns={columns}
            dataSource={logs || []}
            size="small"
            scroll={{ y: 400 }}
            pagination={false}
          />
        </FormWrapper>
      </FormCard>
    );
  } else {
    return <></>;
  }
}
Example #3
Source File: index.tsx    From condo with MIT License 4 votes vote down vote up
TicketAnalyticsPage: ITicketAnalyticsPage = () => {
    const intl = useIntl()
    const PageTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.PageTitle' })
    const HeaderButtonTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.HeaderButtonTitle' })
    const ViewModeTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.ViewModeTitle' })
    const StatusFilterLabel = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.groupByFilter.Status' })
    const PropertyFilterLabel = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.groupByFilter.Property' })
    const CategoryFilterLabel = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.groupByFilter.Category' })
    const UserFilterLabel = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.groupByFilter.User' })
    const ResponsibleFilterLabel = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.groupByFilter.Responsible' })
    const AllAddresses = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.AllAddresses' })
    const ManyAddresses = intl.formatMessage({ id:'pages.condo.analytics.TicketAnalyticsPage.ManyAddresses' })
    const AllAddressTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.tableColumns.AllAddresses' })
    const SingleAddress = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.SingleAddress' })
    const AllCategories = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.AllCategories' })
    const AllCategoryClassifiersTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.tableColumns.AllClassifiers' })
    const AllExecutorsTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.tableColumns.AllExecutors' })
    const AllAssigneesTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.tableColumns.AllAssignees' })
    const EmptyCategoryClassifierTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.NullReplaces.CategoryClassifier' })
    const EmptyExecutorTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.NullReplaces.Executor' })
    const EmptyAssigneeTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.NullReplaces.Assignee' })

    const TableTitle = intl.formatMessage({ id: 'Table' })
    const NotImplementedYetMessage = intl.formatMessage({ id: 'NotImplementedYet' })
    const PrintTitle = intl.formatMessage({ id: 'Print' })
    const ExcelTitle = intl.formatMessage({ id: 'Excel' })

    const router = useRouter()
    const userOrganization = useOrganization()
    const userOrganizationId = get(userOrganization, ['organization', 'id'])
    const { isSmall } = useLayoutContext()
    const filtersRef = useRef<null | ticketAnalyticsPageFilters>(null)
    const mapperInstanceRef = useRef(null)
    const ticketLabelsRef = useRef<TicketLabel[]>([])
    const [groupTicketsBy, setGroupTicketsBy] = useState<GroupTicketsByTypes>('status')
    const [viewMode, setViewMode] = useState<ViewModeTypes>('line')
    const [analyticsData, setAnalyticsData] = useState<null | TicketGroupedCounter[]>(null)
    const [excelDownloadLink, setExcelDownloadLink] = useState<null | string>(null)
    const [ticketType, setTicketType] = useState<TicketSelectTypes>('all')
    const [dateFrom, dateTo] = filtersRef.current !== null ? filtersRef.current.range : []
    const selectedPeriod = filtersRef.current !== null ? filtersRef.current.range.map(e => e.format(DATE_DISPLAY_FORMAT)).join(' - ') : ''
    const selectedAddresses = filtersRef.current !== null ? filtersRef.current.addressList : []
    const ticketTypeRef = useRef<TicketSelectTypes>('all')

    const { TicketWarningModal, setIsVisible } = useTicketWarningModal(groupTicketsBy)
    const nullReplaces = {
        categoryClassifier: EmptyCategoryClassifierTitle,
        executor: EmptyExecutorTitle,
        assignee: EmptyAssigneeTitle,
    }

    const [loadTicketAnalytics, { loading }] = useLazyQuery(TICKET_ANALYTICS_REPORT_QUERY, {
        onError: error => {
            console.log(error)
            notification.error(error)
        },
        fetchPolicy: 'network-only',
        onCompleted: response => {
            const { result: { groups, ticketLabels } } = response
            ticketLabelsRef.current = ticketLabels
            setAnalyticsData(groups)
        },
    })
    const [exportTicketAnalyticsToExcel, { loading: isXSLXLoading }] = useLazyQuery(EXPORT_TICKET_ANALYTICS_TO_EXCEL, {
        onError: error => {
            console.log(error)
            notification.error(error)
        },
        fetchPolicy: 'network-only',
        onCompleted: response => {
            const { result: { link } } = response
            setExcelDownloadLink(link)
        },
    })

    const getAnalyticsData = useCallback(() => {
        if (filtersRef.current !== null) {
            mapperInstanceRef.current = new TicketChart({
                line: {
                    chart: (viewMode, ticketGroupedCounter) => {
                        const { groupBy } = filterToQuery(
                            { filter: filtersRef.current, viewMode, ticketType, mainGroup: groupTicketsBy }
                        )
                        const data = getAggregatedData(ticketGroupedCounter, groupBy)
                        const axisLabels = Array.from(new Set(Object.values(data).flatMap(e => Object.keys(e))))
                        const legend = Object.keys(data)
                        const series = []
                        Object.entries(data).map(([groupBy, dataObj]) => {
                            series.push({
                                name: groupBy,
                                type: viewMode,
                                symbol: 'none',
                                stack: groupBy,
                                data: Object.values(dataObj),
                                emphasis: {
                                    focus: 'none',
                                    blurScope: 'none',
                                },
                            })
                        })
                        const axisData = { yAxis: { type: 'value', data: null }, xAxis: { type: 'category', data: axisLabels } }
                        const tooltip = { trigger: 'axis', axisPointer: { type: 'line' } }
                        const result = { series, legend, axisData, tooltip }
                        if (groupBy[0] === 'status') {
                            result['color'] = ticketLabelsRef.current.map(({ color }) => color)
                        }
                        return result
                    },
                    table: (viewMode, ticketGroupedCounter, restOptions) => {
                        const { groupBy } = filterToQuery(
                            { filter: filtersRef.current, viewMode, ticketType, mainGroup: groupTicketsBy }
                        )
                        const data = getAggregatedData(ticketGroupedCounter, groupBy)
                        const dataSource = []
                        const { translations, filters } = restOptions
                        const tableColumns: TableColumnsType = [
                            { title: translations['address'], dataIndex: 'address', key: 'address', sorter: (a, b) => a['address'] - b['address'] },
                            {
                                title: translations['date'],
                                dataIndex: 'date',
                                key: 'date',
                                defaultSortOrder: 'descend',
                                sorter: (a, b) => dayjs(a['date'], DATE_DISPLAY_FORMAT).unix() - dayjs(b['date'], DATE_DISPLAY_FORMAT).unix(),
                            },
                            ...Object.entries(data).map(([key]) => ({ title: key, dataIndex: key, key, sorter: (a, b) =>a[key] - b[key] })),
                        ]
                        const uniqueDates = Array.from(new Set(Object.values(data).flatMap(e => Object.keys(e))))
                        uniqueDates.forEach((date, key) => {
                            const restTableColumns = {}
                            Object.keys(data).forEach(ticketType => (restTableColumns[ticketType] = data[ticketType][date]))
                            let address = translations['allAddresses']
                            const addressList = get(filters, 'address')
                            if (addressList && addressList.length) {
                                address = addressList.join(', ')
                            }
                            dataSource.push({ key, address, date, ...restTableColumns })
                        })
                        return { dataSource, tableColumns }
                    },
                },
                bar: {
                    chart: (viewMode, ticketGroupedCounter) => {
                        const { groupBy } = filterToQuery(
                            { filter: filtersRef.current, viewMode, ticketType, mainGroup: groupTicketsBy }
                        )
                        const data = getAggregatedData(ticketGroupedCounter, groupBy, true)
                        const series = []
                        const axisLabels = Object.keys(data.summary)
                            .sort((firstLabel, secondLabel) => data.summary[firstLabel] - data.summary[secondLabel])
                        const legend = Object.keys(data)
                        Object.entries(data).map(([name, dataObj]) => {
                            const seriesData = []
                            axisLabels.forEach(axisLabel => {
                                seriesData.push(dataObj[axisLabel])
                            })
                            series.push({
                                name,
                                type: viewMode,
                                symbol: 'none',
                                stack: 'total',
                                sampling: 'sum',
                                large: true,
                                largeThreshold: 200,
                                data: seriesData,
                                emphasis: {
                                    focus: 'self',
                                    blurScope: 'self',
                                },
                            })
                        })
                        const axisData = { yAxis: { type: 'category', data: axisLabels }, xAxis: { type: 'value', data: null } }
                        const tooltip = { trigger: 'item', axisPointer: { type: 'line' }, borderColor: '#fff' }
                        const result = { series, legend, axisData, tooltip }
                        if (groupBy[0] === 'status') {
                            result['color'] = ticketLabelsRef.current.map(({ color }) => color)
                        }
                        return result
                    },
                    table: (viewMode, ticketGroupedCounter, restOptions) => {
                        const { groupBy } = filterToQuery(
                            { filter: filtersRef.current, viewMode, ticketType, mainGroup: groupTicketsBy }
                        )
                        const data = getAggregatedData(ticketGroupedCounter, groupTicketsBy === 'status' ? groupBy.reverse() : groupBy)
                        const { translations, filters } = restOptions
                        const dataSource = []
                        const tableColumns: TableColumnsType = [
                            { title: translations['address'], dataIndex: 'address', key: 'address', sorter: (a, b) => a['address'] - b['address'] },
                            ...Object.entries(data).map(([key]) => ({ title: key, dataIndex: key, key, sorter: (a, b) => a[key] - b[key] })),
                        ]

                        if (TICKET_REPORT_TABLE_MAIN_GROUP.includes(groupBy[1])) {
                            tableColumns.unshift({
                                title: translations[groupBy[1]],
                                dataIndex: groupBy[1],
                                key: groupBy[1],
                                sorter: (a, b) => a[groupBy[1]] - b[groupBy[1]],
                            })
                        }

                        const restTableColumns = {}
                        const addressList = get(filters, 'address', [])
                        const aggregateSummary = [addressList, get(filters, groupBy[1], [])]
                            .every(filterList => filterList.length === 0)
                        if (aggregateSummary) {
                            Object.entries(data).forEach((rowEntry) => {
                                const [ticketType, dataObj] = rowEntry
                                const counts = Object.values(dataObj) as number[]
                                restTableColumns[ticketType] = sum(counts)
                            })
                            dataSource.push({
                                key: 0,
                                address: translations['allAddresses'],
                                categoryClassifier: translations['allCategoryClassifiers'],
                                executor: translations['allExecutors'],
                                assignee: translations['allAssignees'],
                                ...restTableColumns,
                            })
                        } else {
                            const mainAggregation = TICKET_REPORT_TABLE_MAIN_GROUP.includes(groupBy[1]) ? get(filters, groupBy[1], []) : null
                            // TODO(sitozzz): find clean solution for aggregation by 2 id_in fields
                            if (mainAggregation === null) {
                                addressList.forEach((address, key) => {
                                    const tableRow = { key, address }
                                    Object.entries(data).forEach(rowEntry => {
                                        const [ticketType, dataObj] = rowEntry
                                        const counts = Object.entries(dataObj)
                                            .filter(obj => obj[0] === address).map(e => e[1]) as number[]
                                        tableRow[ticketType] = sum(counts)
                                    })
                                    dataSource.push(tableRow)
                                })
                            } else {
                                mainAggregation.forEach((aggregateField, key) => {
                                    const tableRow = { key, [groupBy[1]]: aggregateField }
                                    tableRow['address'] = addressList.length
                                        ? addressList.join(', ')
                                        : translations['allAddresses']
                                    Object.entries(data).forEach(rowEntry => {
                                        const [ticketType, dataObj] = rowEntry
                                        const counts = Object.entries(dataObj)
                                            .filter(obj => obj[0] === aggregateField).map(e => e[1]) as number[]
                                        tableRow[ticketType] = sum(counts)
                                    })
                                    dataSource.push(tableRow)
                                })
                            }
                        }
                        return { dataSource, tableColumns }
                    },
                },
                pie: {
                    chart: (viewMode, ticketGroupedCounter) => {
                        const { groupBy } = filterToQuery(
                            { filter: filtersRef.current, viewMode, ticketType, mainGroup: groupTicketsBy }
                        )
                        const data = getAggregatedData(ticketGroupedCounter, groupBy)
                        const series = []

                        const legend = [...new Set(Object.values(data).flatMap(e => Object.keys(e)))]
                        Object.entries(data).forEach(([label, groupObject]) => {
                            const chartData = Object.entries(groupObject)
                                .map(([name, value]) => ({ name, value }))
                            if (chartData.map(({ value }) => value).some(value => value > 0)) {
                                series.push({
                                    name: label,
                                    data: chartData,
                                    selectedMode: false,
                                    type: viewMode,
                                    radius: [60, 120],
                                    center: ['25%', '50%'],
                                    symbol: 'none',
                                    emphasis: {
                                        focus: 'self',
                                        blurScope: 'self',
                                    },
                                    labelLine: { show: false },
                                    label: {
                                        show: true,
                                        fontSize: fontSizes.content,
                                        overflow: 'none',
                                        formatter: [
                                            '{value|{b}} {percent|{d} %}',
                                        ].join('\n'),
                                        rich: {
                                            value: {
                                                fontSize: fontSizes.content,
                                                align: 'left',
                                                width: 100,
                                            },
                                            percent: {
                                                align: 'left',
                                                fontWeight: 700,
                                                fontSize: fontSizes.content,
                                                width: 40,
                                            },
                                        },
                                    },
                                    labelLayout: (chart) =>  {
                                        const { dataIndex, seriesIndex } = chart
                                        const elementYOffset = 25 * dataIndex
                                        const yOffset = 75 + 250 * Math.floor(seriesIndex / 2) + 10 + elementYOffset
                                        return {
                                            x: 340,
                                            y: yOffset,
                                            align: 'left',
                                            verticalAlign: 'top',
                                        }
                                    },
                                })
                            }
                        })
                        return { series, legend, color: ticketLabelsRef.current.map(({ color }) => color) }
                    },
                    table: (viewMode, ticketGroupedCounter, restOptions) => {
                        const { groupBy } = filterToQuery(
                            { filter: filtersRef.current, viewMode, ticketType, mainGroup: groupTicketsBy }
                        )
                        const data = getAggregatedData(ticketGroupedCounter, groupBy.reverse())
                        const { translations, filters } = restOptions
                        const dataSource = []
                        const tableColumns: TableColumnsType = [
                            { title: translations['address'], dataIndex: 'address', key: 'address', sorter: (a, b) => a['address'] - b['address'] },
                            ...Object.entries(data).map(([key]) => ({ title: key, dataIndex: key, key, sorter: (a, b) => a[key] - b[key] })),
                        ]

                        if (TICKET_REPORT_TABLE_MAIN_GROUP.includes(groupBy[1])) {
                            tableColumns.unshift({
                                title: translations[groupBy[1]],
                                dataIndex: groupBy[1],
                                key: groupBy[1],
                                sorter: (a, b) => a[groupBy[1]] - b[groupBy[1]],
                            })
                        }

                        const restTableColumns = {}
                        const addressList = get(filters, 'address', [])
                        const aggregateSummary = [addressList, get(filters, groupBy[1], [])]
                            .every(filterList => filterList.length === 0)
                        if (aggregateSummary) {
                            const totalCount = Object.values(data)
                                .reduce((prev, curr) => prev + sum(Object.values(curr)), 0)

                            Object.entries(data).forEach((rowEntry) => {
                                const [ticketType, dataObj] = rowEntry
                                const counts = Object.values(dataObj) as number[]
                                restTableColumns[ticketType] = totalCount > 0
                                    ? ((sum(counts) / totalCount) * 100).toFixed(2) + ' %'
                                    : totalCount
                            })
                            dataSource.push({
                                key: 0,
                                address: translations['allAddresses'],
                                categoryClassifier: translations['allCategoryClassifiers'],
                                executor: translations['allExecutors'],
                                assignee: translations['allAssignees'],
                                ...restTableColumns,
                            })
                        } else {
                            const totalCounts = {}
                            Object.values(data).forEach((dataObj) => {
                                Object.entries(dataObj).forEach(([aggregationField, count]) => {
                                    if (get(totalCounts, aggregationField, false)) {
                                        totalCounts[aggregationField] += count
                                    } else {
                                        totalCounts[aggregationField] = count
                                    }
                                })
                            })
                            const mainAggregation = TICKET_REPORT_TABLE_MAIN_GROUP.includes(groupBy[1]) ? get(filters, groupBy[1], []) : null
                            // TODO(sitozzz): find clean solution for aggregation by 2 id_in fields
                            if (mainAggregation === null) {
                                addressList.forEach((address, key) => {
                                    const tableRow = { key, address }
                                    Object.entries(data).forEach(rowEntry => {
                                        const [ticketType, dataObj] = rowEntry
                                        const counts = Object.entries(dataObj)
                                            .filter(obj => obj[0] === address).map(e => e[1]) as number[]
                                        const totalPropertyCount = sum(counts)
                                        tableRow[ticketType] = totalCounts[address] > 0
                                            ? (totalPropertyCount / totalCounts[address] * 100).toFixed(2) + ' %'
                                            : totalCounts[address]
                                    })
                                    dataSource.push(tableRow)
                                })
                            } else {
                                mainAggregation.forEach((aggregateField, key) => {
                                    const tableRow = { key, [groupBy[1]]: aggregateField }
                                    tableRow['address'] = addressList.length
                                        ? addressList.join(', ')
                                        : translations['allAddresses']
                                    Object.entries(data).forEach(rowEntry => {
                                        const [ticketType, dataObj] = rowEntry
                                        const counts = Object.entries(dataObj)
                                            .filter(obj => obj[0] === aggregateField).map(e => e[1]) as number[]
                                        const totalPropertyCount = sum(counts)
                                        tableRow[ticketType] = totalCounts[aggregateField] > 0
                                            ? (totalPropertyCount / totalCounts[aggregateField] * 100).toFixed(2) + ' %'
                                            : totalCounts[aggregateField]
                                    })
                                    dataSource.push(tableRow)
                                })
                            }
                        }
                        return { dataSource, tableColumns }
                    },
                },
            })
            const { AND, groupBy } = filterToQuery(
                { filter: filtersRef.current, viewMode, ticketType: ticketTypeRef.current, mainGroup: groupTicketsBy }
            )

            const where = { organization: { id: userOrganizationId }, AND }
            loadTicketAnalytics({ variables: { data: { groupBy, where, nullReplaces } } })
        }
    }, [userOrganizationId, viewMode, groupTicketsBy])

    useEffect(() => {
        const queryParams = getQueryParams()
        setGroupTicketsBy(get(queryParams, 'groupTicketsBy', 'status'))
        setViewMode(get(queryParams, 'viewMode', 'line'))
    }, [])

    useEffect(() => {
        ticketTypeRef.current = ticketType
        setAnalyticsData(null)
        getAnalyticsData()
    }, [groupTicketsBy, userOrganizationId, ticketType, viewMode])

    // Download excel file when file link was created
    useEffect(() => {
        if (excelDownloadLink !== null && !isXSLXLoading) {
            const link = document.createElement('a')
            link.href = excelDownloadLink
            link.target = '_blank'
            link.hidden = true
            document.body.appendChild(link)
            link.click()
            link.parentNode.removeChild(link)
            setExcelDownloadLink(null)
        }
    }, [excelDownloadLink, isXSLXLoading])

    const printPdf = useCallback(
        () => {
            let currentFilter
            switch (groupTicketsBy) {
                case 'property':
                    currentFilter = filtersRef.current.addressList
                    break
                case 'categoryClassifier':
                    currentFilter = filtersRef.current.classifierList
                    break
                case 'executor':
                    currentFilter = filtersRef.current.executorList
                    break
                case 'assignee':
                    currentFilter = filtersRef.current.responsibleList
                    break
                default:
                    currentFilter = null
            }

            const uniqueDataSets = Array.from(new Set(analyticsData.map(ticketCounter => ticketCounter[groupTicketsBy])))
            const isPdfAvailable = currentFilter === null
                || uniqueDataSets.length < MAX_FILTERED_ELEMENTS
                || (currentFilter.length !== 0 && currentFilter.length < MAX_FILTERED_ELEMENTS)
            if (isPdfAvailable) {
                router.push(router.route + '/pdf?' + qs.stringify({
                    dateFrom: dateFrom.toISOString(),
                    dateTo: dateTo.toISOString(),
                    groupBy: groupTicketsBy,
                    ticketType,
                    viewMode,
                    addressList: JSON.stringify(filtersRef.current.addressList),
                    executorList: JSON.stringify(filtersRef.current.executorList),
                    assigneeList: JSON.stringify(filtersRef.current.responsibleList),
                    categoryClassifierList: JSON.stringify(filtersRef.current.classifierList),
                    specification: filtersRef.current.specification,
                }))
            } else {
                setIsVisible(true)
            }
        },
        [ticketType, viewMode, dateFrom, dateTo, groupTicketsBy, userOrganizationId, analyticsData],
    )

    const downloadExcel = useCallback(
        () => {
            const { AND, groupBy } = filterToQuery({ filter: filtersRef.current, viewMode, ticketType, mainGroup: groupTicketsBy })
            const where = { organization: { id: userOrganizationId }, AND }
            const filters = filtersRef.current
            const translates: ExportTicketAnalyticsToExcelTranslates = {
                property: filters.addressList.length
                    ? filters.addressList.map(({ value }) => value).join('@')
                    : AllAddressTitle,
                categoryClassifier: filters.classifierList.length
                    ? filters.classifierList.map(({ value }) => value).join('@')
                    : AllCategoryClassifiersTitle,
                executor: filters.executorList.length
                    ? filters.executorList.map(({ value }) => value).join('@')
                    : AllExecutorsTitle,
                assignee: filters.responsibleList.length
                    ? filters.responsibleList.map(({ value }) => value).join('@')
                    : AllAssigneesTitle,
            }

            exportTicketAnalyticsToExcel({ variables: { data: { groupBy, where, translates, nullReplaces } } })
        },
        [ticketType, viewMode, dateFrom, dateTo, groupTicketsBy, userOrganizationId],
    )
    const onFilterChange: ITicketAnalyticsPageFilterProps['onChange'] = useCallback((filters) => {
        setAnalyticsData(null)
        filtersRef.current = filters
        getAnalyticsData()
    }, [viewMode, userOrganizationId, groupTicketsBy, dateFrom, dateTo])

    let addressFilterTitle = selectedAddresses.length === 0 ? AllAddresses : `${SingleAddress} «${selectedAddresses[0].value}»`
    if (selectedAddresses.length > 1) {
        addressFilterTitle = ManyAddresses
    }

    const onTabChange = useCallback((key: GroupTicketsByTypes) => {
        setGroupTicketsBy((prevState) => {
            if (prevState !== key) {
                setAnalyticsData(null)
                return key
            } else { 
                return prevState
            }
        })
        if (key === 'status') {
            setViewMode('line')
        } else {
            setViewMode('bar')
        }
    }, [viewMode, groupTicketsBy])

    const isControlsDisabled = loading || isXSLXLoading || filtersRef.current === null

    return <>
        <Head>
            <title>{PageTitle}</title>
        </Head>
        <PageWrapper>
            <PageContent>
                <Row gutter={[0, 40]}>
                    <Col xs={24} sm={18}>
                        <PageHeader title={<Typography.Title>{PageTitle}</Typography.Title>} />
                    </Col>
                    <Col span={6} hidden={isSmall}>
                        <Tooltip title={NotImplementedYetMessage}>
                            <Button icon={<PlusCircleFilled />} type='sberPrimary' secondary>{HeaderButtonTitle}</Button>
                        </Tooltip>
                    </Col>
                </Row>
                <Row gutter={[0, 24]} align={'top'} justify={'space-between'}>
                    <Col span={24}>
                        <Tabs
                            css={tabsCss}
                            defaultActiveKey='status'
                            activeKey={groupTicketsBy}
                            onChange={onTabChange}
                        >
                            <Tabs.TabPane key='status' tab={StatusFilterLabel} />
                            <Tabs.TabPane key='property' tab={PropertyFilterLabel} />
                            <Tabs.TabPane key='categoryClassifier' tab={CategoryFilterLabel} />
                            <Tabs.TabPane key='executor' tab={UserFilterLabel} />
                            <Tabs.TabPane key='assignee' tab={ResponsibleFilterLabel} />
                        </Tabs>
                    </Col>
                    <Col span={24}>
                        <Row justify={'space-between'} gutter={[0, 20]}>
                            <Col span={24}>
                                <TicketAnalyticsPageFilter
                                    onChange={onFilterChange}
                                    viewMode={viewMode}
                                    groupTicketsBy={groupTicketsBy}
                                />
                            </Col>
                            <Col span={24}>
                                <Divider/>
                            </Col>
                            <Col xs={24} lg={16}>
                                <Typography.Title level={3}>
                                    {ViewModeTitle} {selectedPeriod} {addressFilterTitle} {AllCategories}
                                </Typography.Title>
                            </Col>
                            <Col xs={12} lg={3}>
                                <RadioGroupWithIcon
                                    value={viewMode}
                                    size='small'
                                    buttonStyle='outline'
                                    onChange={(e) => setViewMode(e.target.value)}
                                >
                                    {groupTicketsBy === 'status' && (
                                        <Radio.Button value='line'>
                                            <LinearChartIcon height={32} width={24} />
                                        </Radio.Button>
                                    )}
                                    <Radio.Button value='bar'>
                                        <BarChartIcon height={32} width={24} />
                                    </Radio.Button>
                                    {groupTicketsBy !== 'status' && (
                                        <Radio.Button value='pie'>
                                            <PieChartIcon height={32} width={24} />
                                        </Radio.Button>
                                    )}
                                </RadioGroupWithIcon>
                            </Col>
                            <Col
                                xs={8}
                                hidden={!isSmall}
                            >
                                <TicketTypeSelect
                                    ticketType={ticketType}
                                    setTicketType={setTicketType}
                                    loading={loading}
                                />
                            </Col>
                        </Row>
                    </Col>
                    <Col span={24}>
                        {useMemo(() => (
                            <TicketChartView
                                data={analyticsData}
                                loading={loading}
                                viewMode={viewMode}
                                mainGroup={groupTicketsBy}
                                chartConfig={{
                                    animationEnabled: true,
                                    chartOptions: { renderer: 'svg', height: viewMode === 'line' ? 440 : 'auto' },
                                }}
                                mapperInstance={mapperInstanceRef.current}
                            >
                                <Col
                                    style={{ position: 'absolute', top: 0, right: 0, minWidth: '132px' }}
                                    hidden={isSmall}
                                >
                                    <TicketTypeSelect
                                        ticketType={ticketType}
                                        setTicketType={setTicketType}
                                        loading={loading}
                                    />
                                </Col>
                            </TicketChartView>
                        ), [analyticsData, loading, viewMode, ticketType, userOrganizationId, groupTicketsBy, isSmall])}
                    </Col>
                    <Col span={24}>
                        <Row gutter={[0, 20]}>
                            <Col span={24}>
                                <Typography.Title level={4}>{TableTitle}</Typography.Title>
                            </Col>
                            <Col span={24}>
                                {useMemo(() => (
                                    <TicketListView
                                        data={analyticsData}
                                        loading={loading}
                                        viewMode={viewMode}
                                        filters={filtersRef.current}
                                        mapperInstance={mapperInstanceRef.current}
                                    />
                                ), [analyticsData, loading, viewMode, ticketType, userOrganizationId, groupTicketsBy])}
                            </Col>
                        </Row>
                    </Col>
                    <ActionBar hidden={isSmall}>
                        <Button disabled={isControlsDisabled || isEmpty(analyticsData)} onClick={printPdf} icon={<FilePdfFilled />} type='sberPrimary' secondary>
                            {PrintTitle}
                        </Button>
                        <Button disabled={isControlsDisabled || isEmpty(analyticsData)} onClick={downloadExcel} loading={isXSLXLoading} icon={<EditFilled />} type='sberPrimary' secondary>
                            {ExcelTitle}
                        </Button>
                    </ActionBar>
                </Row>
                <TicketWarningModal />
            </PageContent>
        </PageWrapper>
    </>
}
Example #4
Source File: pdf.tsx    From condo with MIT License 4 votes vote down vote up
PdfView = () => {
    const intl = useIntl()
    const PageTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.PageTitle' })
    const AllAddresses = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.AllAddresses' })
    const SingleAddress = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.SingleAddress' })
    const ManyAddresses = intl.formatMessage({ id:'pages.condo.analytics.TicketAnalyticsPage.ManyAddresses' })
    const AllCategories = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.AllCategories' })
    const DefaultTickets = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.DefaultTickets' })
    const PaidTickets = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.PaidTickets' })
    const EmergencyTickets = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.EmergencyTickets' })
    const LoadingTip = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.PDF.LoadingTip' })
    const EmptyCategoryClassifierTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.NullReplaces.CategoryClassifier' })
    const EmptyExecutorTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.NullReplaces.Executor' })
    const EmptyAssigneeTitle = intl.formatMessage({ id: 'pages.condo.analytics.TicketAnalyticsPage.NullReplaces.Assignee' })

    const containerRef = useRef<null | HTMLDivElement>(null)
    const queryParamsRef = useRef(null)
    const groupByRef = useRef<null | TicketAnalyticsGroupBy[]>(null)
    const mapperInstanceRef = useRef(null)
    const ticketLabelsRef = useRef<TicketLabel[]>([])

    const [data, setData] = useState<null | TicketGroupedCounter[]>(null)
    // 2 different loading states by reason of it is 2 step page loading - 1st is data fetch, 2nd is wait for all charts to be rendered
    const [loading, setLoading] = useState(true)
    const [chartLoading, setChartLoading] = useState(true)
    const userOrganization = useOrganization()
    const userOrganizationId = get(userOrganization, ['organization', 'id'])

    const nullReplaces = {
        categoryClassifier: EmptyCategoryClassifierTitle,
        executor: EmptyExecutorTitle,
        assignee: EmptyAssigneeTitle,
    }

    const [loadTicketAnalyticsData] = useLazyQuery(TICKET_ANALYTICS_REPORT_QUERY, {
        onError: error => {
            console.log(error)
            notification.error({
                message: intl.formatMessage(({ id: 'errors.PdfGenerationError' })),
                description: error,
            })
            setLoading(false)
        },
        fetchPolicy: 'network-only',
        onCompleted: response => {
            setLoading(false)
            const { result: { groups, ticketLabels } } = response
            ticketLabelsRef.current = ticketLabels
            setData(groups)
        },
    })
    useEffect(() => {
        const queryParams = getQueryParams()
        queryParamsRef.current = queryParams
        const dateFrom = get(queryParams, 'dateFrom', dayjs().subtract(1, 'week'))
        const dateTo = get(queryParams, 'dateTo', dayjs())
        const addressList = JSON.parse(get(queryParams, 'addressList', '[]'))
        const classifierList = JSON.parse(get(queryParams, 'categoryClassifierList', '[]'))
        const executorList = JSON.parse(get(queryParams, 'executorList', '[]'))
        const assigneeList = JSON.parse(get(queryParams, 'assigneeList', '[]'))
        const mainGroup = get(queryParams, 'groupBy', 'status')
        const specification = get(queryParams, 'specification', 'day')
        const viewMode = get(queryParams, 'viewMode', 'line')
        const ticketType = get(queryParams, 'ticketType', 'all')
        const { AND, groupBy } = filterToQuery({
            viewMode,
            ticketType,
            filter: {
                range: [dayjs(dateFrom), dayjs(dateTo)],
                addressList,
                specification,
                classifierList,
                executorList,
                responsibleList: assigneeList,
            },
            mainGroup,
        })
        groupByRef.current = groupBy
        const where = { organization: { id: userOrganizationId }, AND }

        loadTicketAnalyticsData({ variables: { data: { groupBy, where, nullReplaces } } })

    }, [userOrganizationId])

    useEffect(() => {
        if (mapperInstanceRef.current === null && groupByRef.current !== null) {
            const mainGroup = get(queryParamsRef.current, 'groupBy', 'status')
            mapperInstanceRef.current = new TicketChart({
                line: {
                    chart: (viewMode, ticketGroupedCounter) => {
                        const data = getAggregatedData(ticketGroupedCounter, groupByRef.current)
                        const axisLabels = Array.from(new Set(Object.values(data).flatMap(e => Object.keys(e))))
                        const legend = Object.keys(data)
                        const series = []
                        Object.entries(data).map(([groupBy, dataObj]) => {
                            series.push({
                                name: groupBy,
                                type: viewMode,
                                symbol: 'none',
                                stack: groupBy,
                                data: Object.values(dataObj),
                                emphasis: {
                                    focus: 'none',
                                    blurScope: 'none',
                                },
                            })
                        })
                        const axisData = { yAxis: { type: 'value', data: null }, xAxis: { type: 'category', data: axisLabels } }
                        const tooltip = { trigger: 'axis', axisPointer: { type: 'line' } }
                        const result = { series, legend, axisData, tooltip }
                        if (groupByRef.current[0] === 'status') {
                            result['color'] = ticketLabelsRef.current.map(({ color }) => color)
                        }
                        return result
                    },
                    table: (viewMode, ticketGroupedCounter, restOptions) => {
                        const data = getAggregatedData(ticketGroupedCounter, groupByRef.current)
                        const dataSource = []
                        const { translations, filters } = restOptions
                        const tableColumns: TableColumnsType = [
                            { title: translations['address'], dataIndex: 'address', key: 'address', sorter: (a, b) => a['address'] - b['address'] },
                            {
                                title: translations['date'],
                                dataIndex: 'date',
                                key: 'date',
                                defaultSortOrder: 'descend',
                                sorter: (a, b) => dayjs(a['date'], DATE_DISPLAY_FORMAT).unix() - dayjs(b['date'], DATE_DISPLAY_FORMAT).unix(),
                            },
                            ...Object.entries(data).map(([key]) => ({ title: key, dataIndex: key, key, sorter: (a, b) =>a[key] - b[key] })),
                        ]
                        const uniqueDates = Array.from(new Set(Object.values(data).flatMap(e => Object.keys(e))))
                        uniqueDates.forEach((date, key) => {
                            const restTableColumns = {}
                            Object.keys(data).forEach(ticketType => (restTableColumns[ticketType] = data[ticketType][date]))
                            let address = translations['allAddresses']
                            const addressList = get(filters, 'address')
                            if (addressList && addressList.length) {
                                address = addressList.join(', ')
                            }
                            dataSource.push({ key, address, date, ...restTableColumns })
                        })
                        return { dataSource, tableColumns }
                    },
                },
                bar: {
                    chart: (viewMode, ticketGroupedCounter) => {
                        const data = getAggregatedData(ticketGroupedCounter, groupByRef.current, true)
                        const series = []
                        const axisLabels = Object.keys(data.summary)
                            .sort((firstLabel, secondLabel) => data.summary[firstLabel] - data.summary[secondLabel])
                        const legend = Object.keys(data)
                        Object.entries(data).map(([groupBy, dataObj]) => {
                            const seriesData = []
                            axisLabels.forEach(axisLabel => {
                                seriesData.push(dataObj[axisLabel])
                            })
                            series.push({
                                name: groupBy,
                                type: viewMode,
                                symbol: 'none',
                                stack: 'total',
                                data: seriesData,
                                emphasis: {
                                    focus: 'self',
                                    blurScope: 'self',
                                },
                            })
                        })
                        const axisData = { yAxis: { type: 'category', data: axisLabels }, xAxis: { type: 'value', data: null } }
                        const tooltip = { trigger: 'item', axisPointer: { type: 'line' }, show: false }
                        const result = { series, legend, axisData, tooltip }
                        if (groupByRef.current[0] === 'status') {
                            result['color'] = ticketLabelsRef.current.map(({ color }) => color)
                        }
                        return result

                    },
                    table: (viewMode, ticketGroupedCounter, restOptions) => {
                        const groupByCopy = [...groupByRef.current]
                        const data = getAggregatedData(ticketGroupedCounter, mainGroup === 'status' ? groupByCopy.reverse() : groupByCopy)
                        const { translations, filters } = restOptions
                        const dataSource = []
                        const tableColumns: TableColumnsType = [
                            { title: translations['address'], dataIndex: 'address', key: 'address', sorter: (a, b) => a['address'] - b['address'] },
                            ...Object.entries(data).map(([key]) => ({ title: key, dataIndex: key, key, sorter: (a, b) => a[key] - b[key] })),
                        ]

                        if (TICKET_REPORT_TABLE_MAIN_GROUP.includes(groupByRef.current[1])) {
                            tableColumns.unshift({
                                title: translations[groupByRef.current[1]],
                                dataIndex: groupByRef.current[1],
                                key: groupByRef.current[1],
                                sorter: (a, b) => a[groupByRef.current[1]] - b[groupByRef.current[1]],
                            })
                        }

                        const restTableColumns = {}
                        const addressList = get(filters, 'address', [])
                        const aggregateSummary = [addressList, get(filters, groupByRef.current[1], [])]
                            .every(filterList => filterList.length === 0)
                        if (aggregateSummary) {
                            Object.entries(data).forEach((rowEntry) => {
                                const [ticketType, dataObj] = rowEntry
                                const counts = Object.values(dataObj) as number[]
                                restTableColumns[ticketType] = sum(counts)
                            })
                            dataSource.push({
                                key: 0,
                                address: translations['allAddresses'],
                                categoryClassifier: translations['allCategoryClassifiers'],
                                executor: translations['allExecutors'],
                                assignee: translations['allAssignees'],
                                ...restTableColumns,
                            })
                        } else {
                            const mainAggregation = TICKET_REPORT_TABLE_MAIN_GROUP.includes(groupByRef.current[1]) ? get(filters, groupByRef.current[1], []) : null
                            // TODO(sitozzz): find clean solution for aggregation by 2 id_in fields
                            if (mainAggregation === null) {
                                addressList.forEach((address, key) => {
                                    const tableRow = { key, address }
                                    Object.entries(data).forEach(rowEntry => {
                                        const [ticketType, dataObj] = rowEntry
                                        const counts = Object.entries(dataObj)
                                            .filter(obj => obj[0] === address).map(e => e[1]) as number[]
                                        tableRow[ticketType] = sum(counts)
                                    })
                                    dataSource.push(tableRow)
                                })
                            } else {
                                mainAggregation.forEach((aggregateField, key) => {
                                    const tableRow = { key, [groupByRef.current[1]]: aggregateField }
                                    tableRow['address'] = addressList.length
                                        ? addressList.join(', ')
                                        : translations['allAddresses']
                                    Object.entries(data).forEach(rowEntry => {
                                        const [ticketType, dataObj] = rowEntry
                                        const counts = Object.entries(dataObj)
                                            .filter(obj => obj[0] === aggregateField).map(e => e[1]) as number[]
                                        tableRow[ticketType] = sum(counts)
                                    })
                                    dataSource.push(tableRow)
                                })
                            }
                        }
                        return { dataSource, tableColumns }
                    },

                },
                pie: {
                    chart: (viewMode, ticketGroupedCounter) => {
                        const queryParams = getQueryParams()
                        const dateFrom = get(queryParams, 'dateFrom', dayjs().subtract(1, 'week'))
                        const dateTo = get(queryParams, 'dateTo', dayjs())
                        const addressList = JSON.parse(get(queryParams, 'addressList', '[]'))
                        const classifierList = JSON.parse(get(queryParams, 'categoryClassifierList', '[]'))
                        const executorList = JSON.parse(get(queryParams, 'executorList', '[]'))
                        const assigneeList = JSON.parse(get(queryParams, 'assigneeList', '[]'))
                        const mainGroup = get(queryParams, 'groupBy', 'status')
                        const specification = get(queryParams, 'specification', 'day')
                        const ticketType = get(queryParams, 'ticketType', 'all')
                        const { groupBy } = filterToQuery({
                            viewMode,
                            ticketType,
                            filter: {
                                range: [dayjs(dateFrom), dayjs(dateTo)],
                                addressList,
                                specification,
                                executorList: executorList,
                                classifierList,
                                responsibleList: assigneeList,
                            },
                            mainGroup,
                        })
                        const data = getAggregatedData(ticketGroupedCounter, groupBy)
                        const series = []

                        const legend = [...new Set(Object.values(data).flatMap(e => Object.keys(e)))]
                        Object.entries(data).forEach(([label, groupObject]) => {
                            const chartData = Object.entries(groupObject)
                                .map(([name, value]) => ({ name, value }))
                            if (chartData.map(({ value }) => value).some(value => value > 0)) {
                                series.push({
                                    name: label,
                                    data: chartData,
                                    selectedMode: false,
                                    type: viewMode,
                                    radius: [60, 120],
                                    center: ['30%', '50%'],
                                    symbol: 'none',
                                    emphasis: {
                                        focus: 'self',
                                        blurScope: 'self',
                                    },
                                    labelLine: { show: false },
                                    label: {
                                        show: true,
                                        fontSize: 14,
                                        overflow: 'none',
                                        formatter: [
                                            '{value|{b}} {percent|{d} %}',
                                        ].join('\n'),
                                        rich: {
                                            value: {
                                                fontSize: 14,
                                                align: 'left',
                                                width: 100,
                                            },
                                            percent: {
                                                align: 'left',
                                                fontWeight: 700,
                                                fontSize: 14,
                                                width: 40,
                                            },
                                        },
                                    },
                                    labelLayout: (chart) =>  {
                                        const { dataIndex, seriesIndex } = chart
                                        const elementYOffset = 25 * dataIndex
                                        const yOffset = 75 + 250 * Math.floor(seriesIndex / 2) + 10 + elementYOffset
                                        return {
                                            x: 340,
                                            y: yOffset,
                                            align: 'left',
                                            verticalAlign: 'top',
                                        }
                                    },
                                })
                            }
                        })
                        return { series, legend, color: ticketLabelsRef.current.map(({ color }) => color) }
                    },
                    table: (viewMode, ticketGroupedCounter, restOptions) => {
                        const queryParams = getQueryParams()
                        const dateFrom = get(queryParams, 'dateFrom', dayjs().subtract(1, 'week'))
                        const dateTo = get(queryParams, 'dateTo', dayjs())
                        const mainGroup = get(queryParams, 'groupBy', 'status')
                        const specification = get(queryParams, 'specification', 'day')
                        const ticketType = get(queryParams, 'ticketType', 'all')
                        const { groupBy } = filterToQuery({
                            viewMode,
                            ticketType,
                            filter: {
                                range: [dayjs(dateFrom), dayjs(dateTo)],
                                addressList: JSON.parse(get(queryParams, 'addressList', '[]')),
                                specification,
                                classifierList: JSON.parse(get(queryParams, 'categoryClassifierList', '[]')),
                                executorList: JSON.parse(get(queryParams, 'executorList', '[]')),
                                responsibleList: JSON.parse(get(queryParams, 'assigneeList', '[]')),
                            },
                            mainGroup,
                        })
                        const data = getAggregatedData(ticketGroupedCounter, groupBy.reverse())
                        const { translations, filters } = restOptions
                        const dataSource = []
                        const tableColumns: TableColumnsType = [
                            { title: translations['address'], dataIndex: 'address', key: 'address', sorter: (a, b) => a['address'] - b['address'] },
                            ...Object.entries(data).map(([key]) => ({ title: key, dataIndex: key, key, sorter: (a, b) => a[key] - b[key] })),
                        ]

                        if (TICKET_REPORT_TABLE_MAIN_GROUP.includes(groupBy[1])) {
                            tableColumns.unshift({
                                title: translations[groupBy[1]],
                                dataIndex: groupBy[1],
                                key: groupBy[1],
                                sorter: (a, b) => a[groupBy[1]] - b[groupBy[1]],
                            })
                        }

                        const restTableColumns = {}
                        const addressList = get(filters, 'address', [])
                        const aggregateSummary = [addressList, get(filters, groupBy[1], [])]
                            .every(filterList => filterList.length === 0)
                        if (aggregateSummary) {
                            const totalCount = Object.values(data)
                                .reduce((prev, curr) => prev + sum(Object.values(curr)), 0)

                            Object.entries(data).forEach((rowEntry) => {
                                const [ticketType, dataObj] = rowEntry
                                const counts = Object.values(dataObj) as number[]
                                restTableColumns[ticketType] = totalCount > 0
                                    ? ((sum(counts) / totalCount) * 100).toFixed(2) + ' %'
                                    : totalCount
                            })
                            dataSource.push({
                                key: 0,
                                address: translations['allAddresses'],
                                categoryClassifier: translations['allCategoryClassifiers'],
                                executor: translations['allExecutors'],
                                assignee: translations['allAssignees'],
                                ...restTableColumns,
                            })
                        } else {
                            const totalCounts = {}
                            Object.values(data).forEach((dataObj) => {
                                Object.entries(dataObj).forEach(([aggregationField, count]) => {
                                    if (get(totalCounts, aggregationField, false)) {
                                        totalCounts[aggregationField] += count
                                    } else {
                                        totalCounts[aggregationField] = count
                                    }
                                })
                            })
                            const mainAggregation = TICKET_REPORT_TABLE_MAIN_GROUP.includes(groupBy[1]) ? get(filters, groupBy[1], []) : null

                            if (mainAggregation === null) {
                                addressList.forEach((address, key) => {
                                    const tableRow = { key, address }
                                    Object.entries(data).forEach(rowEntry => {
                                        const [ticketType, dataObj] = rowEntry
                                        const counts = Object.entries(dataObj)
                                            .filter(obj => obj[0] === address).map(e => e[1]) as number[]
                                        const totalPropertyCount = sum(counts)
                                        tableRow[ticketType] = totalCounts[address] > 0
                                            ? (totalPropertyCount / totalCounts[address] * 100).toFixed(2) + ' %'
                                            : totalCounts[address]
                                    })
                                    dataSource.push(tableRow)
                                })
                            } else {
                                mainAggregation.forEach((aggregateField, key) => {
                                    const tableRow = { key, [groupBy[1]]: aggregateField }
                                    tableRow['address'] = addressList.length
                                        ? addressList.join(', ')
                                        : translations['allAddresses']
                                    Object.entries(data).forEach(rowEntry => {
                                        const [ticketType, dataObj] = rowEntry
                                        const counts = Object.entries(dataObj)
                                            .filter(obj => obj[0] === aggregateField).map(e => e[1]) as number[]
                                        const totalPropertyCount = sum(counts)
                                        tableRow[ticketType] = totalCounts[aggregateField] > 0
                                            ? (totalPropertyCount / totalCounts[aggregateField] * 100).toFixed(2) + ' %'
                                            : totalCounts[aggregateField]
                                    })
                                    dataSource.push(tableRow)
                                })
                            }
                        }
                        return { dataSource, tableColumns }
                    },
                },
            })
        }
        if (!loading && !chartLoading && data !== null) {
            createPdfWithPageBreaks({ element: containerRef.current, fileName: 'analytics_result.pdf' })
                .catch((e) => {
                    notification.error({
                        message: intl.formatMessage(({ id: 'errors.PdfGenerationError' })),
                        description: e.message,
                    })
                })
        }
    }, [loading, data, chartLoading])

    if (queryParamsRef.current === null ) {
        return null
    }
    let ticketTypeTitle = DefaultTickets
    const {
        dateFrom, dateTo, viewMode, ticketType, addressList, specification, executorList, assigneeList, categoryClassifierList,
    } = queryParamsRef.current
    ticketType === 'paid' && (ticketTypeTitle = PaidTickets)
    ticketType === 'emergency' && (ticketTypeTitle = EmergencyTickets)
    const addressListParsed = JSON.parse(addressList)
    let addressFilterTitle = addressListParsed.length ? `${SingleAddress} «${addressListParsed[0].value}»` : AllAddresses
    if (addressListParsed.length > 1) {
        addressFilterTitle = ManyAddresses
    }
    return <>
        {loading && <Loader fill spinning tip={LoadingTip} /> }
        <Row
            ref={containerRef}
            gutter={[0, 40]}
            style={{ width: PDF_REPORT_WIDTH, paddingLeft: 80, paddingRight: 120, pointerEvents: 'none' }}
        >
            <Col flex={1} style={{ visibility: loading ? 'hidden' : 'visible', position: 'relative' }}>
                <Typography.Paragraph style={{ position: 'absolute', top: 0, right: 0 }}>
                    <Logo onClick={undefined} fillColor={colors.lightGrey[6]} />
                </Typography.Paragraph>
                {chartLoading &&
                    <Typography.Paragraph>
                        <Loader fill spinning tip={LoadingTip} />
                    </Typography.Paragraph>
                }
                <Typography.Title level={3}>{PageTitle}</Typography.Title>
                <Typography.Title level={4}>
                    {ticketTypeTitle} {dayjs(dateFrom).format('DD.MM.YYYY')} - {dayjs(dateTo).format('DD.MM.YYYY')} {addressFilterTitle} {AllCategories}
                </Typography.Title>
                <TicketChartView
                    data={data}
                    viewMode={viewMode}
                    onChartReady={() => setChartLoading(false)}
                    mapperInstance={mapperInstanceRef.current}
                    chartConfig={{
                        animationEnabled: false,
                        chartOptions: { renderer: 'svg', height: viewMode === 'line' ? 400 : 'auto' },
                    }}
                />
            </Col>
            <Col flex={1} >
                <TicketListView
                    mapperInstance={mapperInstanceRef.current}
                    data={data}
                    viewMode={viewMode}
                    filters={{
                        range: [dateFrom, dateTo],
                        addressList: addressListParsed,
                        specification: specification,
                        classifierList: JSON.parse(categoryClassifierList),
                        executorList: JSON.parse(executorList),
                        responsibleList: JSON.parse(assigneeList),
                    }}
                />
            </Col>
        </Row>
    </>
}