From 7cc94fdea3b9448fa767de844171b333b8b4c0d4 Mon Sep 17 00:00:00 2001 From: "anders.dahlin@knowit.no" Date: Mon, 11 Mar 2024 15:31:54 +0100 Subject: [PATCH 1/2] Visning av kundeansvalrlig og bugfixes --- .../routers/customer/customerAggregation.ts | 96 ++++---- .../server/routers/customer/customerRouter.ts | 9 +- apps/server/routers/customer/customerTypes.ts | 5 +- .../routers/employees/employeesAggregation.ts | 1 - .../src/api/data/customer/customerApiTypes.ts | 5 +- .../src/api/data/customer/customerQueries.ts | 25 +- .../{usePerWeekFilter.ts => useChartData.ts} | 221 +++++++++--------- apps/web/src/pages/customer/CustomerPage.tsx | 1 - .../src/pages/customer/cards/CustomerCard.tsx | 115 +++++---- .../cards/HoursBilledPerWeekChart.tsx | 51 ++-- .../components/CustomerSiteContent.tsx | 32 ++- .../CustomerSpesificHoursBilledGraph.tsx | 57 ----- .../customer-overview/CustomerCardList.tsx | 5 +- .../customer-overview/CustomerCardSort.tsx | 7 +- .../CustomerHoursPerWeekSection.tsx | 26 +-- .../customer-overview/CustomerOverview.tsx | 27 --- .../CustomerOverviewFilter.tsx | 38 ++- .../customer/util/sort-customer-cards.tsx | 10 +- apps/web/src/pages/login/LoginPage.tsx | 1 - .../OrganizationStructurePage.tsx | 1 + 20 files changed, 351 insertions(+), 382 deletions(-) rename apps/web/src/components/charts/chartFilters/{usePerWeekFilter.ts => useChartData.ts} (65%) delete mode 100644 apps/web/src/pages/customer/components/CustomerSpesificHoursBilledGraph.tsx diff --git a/apps/server/routers/customer/customerAggregation.ts b/apps/server/routers/customer/customerAggregation.ts index d8a44a20..61013b3a 100644 --- a/apps/server/routers/customer/customerAggregation.ts +++ b/apps/server/routers/customer/customerAggregation.ts @@ -51,56 +51,64 @@ async function createEmployeeForCustomerList( } } +const addBy = + ( + key: T + ): ((acc: number, cp: { [key in T]: number }) => number) => + (acc, cp) => + acc + cp[key] +const unique = (list: T[]): T[] => [...new Set(list)] +const getPeriods = (ec: EmployeeCustomers) => + ec.reg_periods + .toString() + .split(';') + .map((period) => Number(period)) export function createCustomerCardData( projects: BilledCustomerHours[], - employeesCustomer: EmployeeCustomers[] + employeesCustomer: EmployeeCustomers[], + accountManagerTable: { customer: string; account_manager: string }[] ): CustomerCardsData[] { - const results = {} - - const last_reg_periods = [ - ...new Set( - projects.map(function (o) { - return o.reg_period - }) - ), - ] + const accountManagerMap = accountManagerTable.reduce( + (acc, row) => ({ + ...acc, + [row.customer]: row.account_manager, + }), + {} + ) + const last_reg_periods = unique(projects.map((o) => o.reg_period)) .sort() - .slice(-3) + .slice(-4) - projects.forEach((elem) => { - const curr_el = results[elem.customer] - if (!curr_el) { - results[elem.customer] = { - customer: elem.customer, - billedLastPeriod: 0, - billedTotal: 0, - } - } - if (elem.reg_period == last_reg_periods[last_reg_periods.length - 1]) { - results[elem.customer]['billedLastPeriod'] = - results[elem.customer]['billedLastPeriod'] + elem.hours - } - results[elem.customer]['billedTotal'] = - results[elem.customer]['billedTotal'] + elem.hours - }) + const result = unique(employeesCustomer.map((ec) => ec.customer)).map( + (customer) => { + const customerProjects = projects.filter((p) => p.customer === customer) + const customerEmployees = employeesCustomer.filter( + (ec) => ec.customer === customer + ) + const employeesLastPeriod = customerEmployees + .filter((ce) => getPeriods(ce).includes(last_reg_periods.at(-1))) + .map((ce) => ce.email) + const employeesLastLongPeriod = customerEmployees + .filter((ce) => + getPeriods(ce).some((period) => last_reg_periods.includes(period)) + ) + .map((ce) => ce.email) - Object.keys(results).forEach((customer) => { - const consultants = new Set() - employeesCustomer.forEach((employeeCustomer) => { - if (employeeCustomer.customer == customer) { - const reg_periods = - employeeCustomer?.reg_periods - .toString() - .split(';') - .map((period) => Number(period)) || [] - if (last_reg_periods.some((p) => reg_periods.includes(p))) { - consultants.add(employeeCustomer.user_id) - } + return { + customer, + accountManager: accountManagerMap[customer], + billedLastPeriod: customerProjects + .filter((cp) => cp.reg_period === last_reg_periods.at(-1)) + .reduce(addBy('hours'), 0), + billedLastLongPeriod: customerProjects + .filter((cp) => last_reg_periods.includes(cp.reg_period)) + .reduce(addBy('hours'), 0), + billedTotal: customerProjects.reduce(addBy('hours'), 0), + consultantsLastPeriod: unique(employeesLastPeriod).length, + consultantsLastLongPeriod: unique(employeesLastLongPeriod).length, } - }) - if (consultants.size === 0) delete results[customer] - else results[customer]['consultants'] = consultants.size - }) + } + ) - return Object.values(results) + return result } diff --git a/apps/server/routers/customer/customerRouter.ts b/apps/server/routers/customer/customerRouter.ts index ccce24ef..b40500a6 100644 --- a/apps/server/routers/customer/customerRouter.ts +++ b/apps/server/routers/customer/customerRouter.ts @@ -37,9 +37,16 @@ router.get('/customerCards', async (req, res, next) => { try { const perProject = await getFileFromS3('perProject') const employeeCustomers = await getFileFromS3('employeeCustomers') + const accountManager = await getFileFromS3('accountManager') const project_data = JSON.parse(perProject) const customer_data = JSON.parse(employeeCustomers) - const aggregatedData = createCustomerCardData(project_data, customer_data) + const accountManagerTable = JSON.parse(accountManager) + + const aggregatedData = createCustomerCardData( + project_data, + customer_data, + accountManagerTable + ) res.send(aggregatedData) } catch (error) { next(error) diff --git a/apps/server/routers/customer/customerTypes.ts b/apps/server/routers/customer/customerTypes.ts index 7f45159f..e4121a83 100644 --- a/apps/server/routers/customer/customerTypes.ts +++ b/apps/server/routers/customer/customerTypes.ts @@ -37,8 +37,11 @@ export type EmployeeForCustomerListRowData = [ export type CustomerCardsData = { customer: string - consultants: number + accountManager: string | undefined + consultantsLastPeriod: number + consultantsLastLongPeriod: number billedLastPeriod: number + billedLastLongPeriod: number billedTotal: number } diff --git a/apps/server/routers/employees/employeesAggregation.ts b/apps/server/routers/employees/employeesAggregation.ts index e781528e..bf4104d8 100644 --- a/apps/server/routers/employees/employeesAggregation.ts +++ b/apps/server/routers/employees/employeesAggregation.ts @@ -24,7 +24,6 @@ import { WorkExperience, } from './employeesTypes' import { EmployeeCustomers } from '../customer/customerTypes' -import { groupBy } from '../../repository/util' export const aggregateEmployeeTable = async ( basicEmployeeInformation: BasicEmployeeInformation[], diff --git a/apps/web/src/api/data/customer/customerApiTypes.ts b/apps/web/src/api/data/customer/customerApiTypes.ts index 6c72b615..98532824 100644 --- a/apps/web/src/api/data/customer/customerApiTypes.ts +++ b/apps/web/src/api/data/customer/customerApiTypes.ts @@ -4,8 +4,11 @@ import { TableRow } from '../tableResponses' // customerCards export interface CustomerCardData { customer: string - consultants: number + accountManager: string | undefined + consultantsLastPeriod: number + consultantsLastLongPeriod: number billedLastPeriod: number + billedLastLongPeriod: number billedTotal: number } diff --git a/apps/web/src/api/data/customer/customerQueries.ts b/apps/web/src/api/data/customer/customerQueries.ts index bb2208ed..f6e22693 100644 --- a/apps/web/src/api/data/customer/customerQueries.ts +++ b/apps/web/src/api/data/customer/customerQueries.ts @@ -5,15 +5,12 @@ import { getHoursBilledPerCustomerCharts, getHoursBilledPerWeekCharts, } from './customerApi' -import { CustomerData } from '../../../pages/customer/cards/CustomerCard' +import { CustomerCardData } from './customerApiTypes' -export const useCustomerCardsQuery = () => - useSWR('/customerCards', getCustomerCards, { +export const useCustomerCards = () => { + const { data } = useSWR('/customerCards', getCustomerCards, { revalidateOnFocus: false, }) - -export const useCustomerCards = () => { - const { data } = useCustomerCardsQuery() return data || [] } @@ -27,9 +24,21 @@ export const useHoursBilledPerCustomerCharts = () => revalidateOnFocus: false, }) -export const useAllCustomerData = (): CustomerData[] => { +export const useAllCustomerData = (): CustomerCardData[] => { const { data: hoursBilledPerCustomer } = useHoursBilledPerCustomerCharts() - return hoursBilledPerCustomer ? hoursBilledPerCustomer?.data : [] + return ( + hoursBilledPerCustomer?.data?.map( + (cd): CustomerCardData => ({ + customer: cd.customer, + accountManager: undefined, + consultantsLastPeriod: 0, + consultantsLastLongPeriod: 0, + billedLastPeriod: 0, + billedLastLongPeriod: 0, + billedTotal: 0, + }) + ) || [] + ) } export const useHoursBilledPerWeekCharts = () => diff --git a/apps/web/src/components/charts/chartFilters/usePerWeekFilter.ts b/apps/web/src/components/charts/chartFilters/useChartData.ts similarity index 65% rename from apps/web/src/components/charts/chartFilters/usePerWeekFilter.ts rename to apps/web/src/components/charts/chartFilters/useChartData.ts index 615adff6..faeb0a82 100644 --- a/apps/web/src/components/charts/chartFilters/usePerWeekFilter.ts +++ b/apps/web/src/components/charts/chartFilters/useChartData.ts @@ -1,115 +1,106 @@ -import { SingularChartData } from '../../../../../../packages/folk-common/types/chartTypes' -import { Dispatch, SetStateAction, useMemo, useState } from 'react' - -export type PerWeekFilterOptions = 'Uke' | 'Måned' - -type DateDisplay = { - month: string - week: string - date: Date -} - -/** Returns props of x scale displays and date based on regPeriod to compare sort */ -function getDateDisplay(regPeriod: string): DateDisplay { - const y = Number(regPeriod.slice(0, 4)) - const w = Number(regPeriod.slice(-2)) - const date = new Date(y, 0, 1 + (w - 1) * 7) - date.setDate(date.getDate() + (1 - date.getDay())) - - const month = date.toLocaleString('no-NO', { month: 'short' }) - - return { - month: `${y} - ${month}`, - week: `${y} - Uke ${w}`, - date: date, - } -} - -export type PerWeekFilteredData = { - filterOptions: PerWeekFilterOptions[] - selectedFilter: PerWeekFilterOptions - setSelectedFilter: Dispatch> - weeklyData: SingularChartData - monthlyData: SingularChartData -} - -const usePerWeekFilter = (data: SingularChartData): PerWeekFilteredData => { - const filterOptions: PerWeekFilterOptions[] = ['Uke', 'Måned'] - - const [selectedFilter, setSelectedFilter] = useState(filterOptions[0]) - - const groupDataByTimePeriod = ( - data: SingularChartData, - period: PerWeekFilterOptions - ): SingularChartData => { - if (data && data.type === 'LineChart') { - const filteredData = data.data.map((customer) => { - let monthValue = 0 - let month = null - const filteredData = customer.data - .map((v) => { - const date = getDateDisplay(v.x) - const value = { ...v, date: date.date } - if (!month || date.month != month) { - month = date.month - monthValue = 0 - } - monthValue += value.y - switch (period) { - case 'Uke': - return { ...value, x: date.week } - case 'Måned': { - const monthDate = new Date(date.date) - monthDate.setDate(1) - return { date: monthDate, x: date.month, y: monthValue } - } - default: - return value - } - }) - .reduce((list, value) => { - const sum = list - .filter((o) => o.x === value) - .reduce((v, o) => o.y + v, 0) - - return [ - ...list.filter((o) => o.x !== value.x), - { ...value, y: sum + value.y }, - ] - }, []) - .sort((a, b) => a.date - b.date) - - // Filter out empty data - return filteredData.length - ? { - ...customer, - data: filteredData, - } - : null - }) - - return { - ...data, - data: filteredData, - } - } else { - return data - } - } - - const weeklyData = useMemo(() => groupDataByTimePeriod(data, 'Uke'), [data]) - const monthlyData = useMemo( - () => groupDataByTimePeriod(data, 'Måned'), - [data] - ) - - return { - filterOptions, - selectedFilter, - setSelectedFilter, - weeklyData, - monthlyData, - } -} - -export default usePerWeekFilter +import { SingularChartData } from '@folk/common/types/chartTypes' +import { useMemo } from 'react' + +export enum ChartPeriod { + WEEK = 'WEEK', + MONTH = 'MONTH', +} + +type DateDisplay = { + month: string + week: string + date: Date +} + +/** Returns props of x scale displays and date based on regPeriod to compare sort */ +function getDateDisplay(regPeriod: string): DateDisplay { + const y = Number(regPeriod.slice(0, 4)) + const w = Number(regPeriod.slice(-2)) + const date = new Date(y, 0, 1 + (w - 1) * 7) + date.setDate(date.getDate() + (1 - date.getDay())) + + const month = date.toLocaleString('no-NO', { month: 'short' }) + + return { + month: `${y} - ${month}`, + week: `${y} - Uke ${w}`, + date: date, + } +} + +const useChartData = ( + data: SingularChartData, + chartPeriod: ChartPeriod +): SingularChartData => { + const groupDataByTimePeriod = ( + data: SingularChartData, + period: ChartPeriod + ): SingularChartData => { + if (data && data.type === 'LineChart') { + const filteredData = data.data.map((customer) => { + let monthValue = 0 + let month = null + const filteredData = customer.data + .map((v) => { + const date = getDateDisplay(v.x) + const value = { ...v, date: date.date } + if (!month || date.month != month) { + month = date.month + monthValue = 0 + } + monthValue += value.y + switch (period) { + case ChartPeriod.WEEK: + return { ...value, x: date.week } + case ChartPeriod.MONTH: { + const monthDate = new Date(date.date) + monthDate.setDate(1) + return { date: monthDate, x: date.month, y: monthValue } + } + default: + return value + } + }) + .reduce((list, value) => { + const sum = list + .filter((o) => o.x === value) + .reduce((v, o) => o.y + v, 0) + + return [ + ...list.filter((o) => o.x !== value.x), + { ...value, y: sum + value.y }, + ] + }, []) + .sort((a, b) => a.date - b.date) + + // Filter out empty data + return filteredData.length + ? { + ...customer, + data: filteredData, + } + : null + }) + + return { + ...data, + data: filteredData, + } + } else { + return data + } + } + + const weeklyData = useMemo( + () => groupDataByTimePeriod(data, ChartPeriod.WEEK), + [data] + ) + const monthlyData = useMemo( + () => groupDataByTimePeriod(data, ChartPeriod.MONTH), + [data] + ) + + return chartPeriod === ChartPeriod.WEEK ? weeklyData : monthlyData +} + +export default useChartData diff --git a/apps/web/src/pages/customer/CustomerPage.tsx b/apps/web/src/pages/customer/CustomerPage.tsx index 4633a8e7..2cc4ce7d 100644 --- a/apps/web/src/pages/customer/CustomerPage.tsx +++ b/apps/web/src/pages/customer/CustomerPage.tsx @@ -1,4 +1,3 @@ -import React from 'react' import NavTab from '../../components/header/NavTab' import CustomerList from './customer-list/CustomerList' import { CustomerOverview } from './customer-overview/CustomerOverview' diff --git a/apps/web/src/pages/customer/cards/CustomerCard.tsx b/apps/web/src/pages/customer/cards/CustomerCard.tsx index 4fdb4bc1..25b00b9f 100644 --- a/apps/web/src/pages/customer/cards/CustomerCard.tsx +++ b/apps/web/src/pages/customer/cards/CustomerCard.tsx @@ -7,16 +7,11 @@ import { GridItemContent } from '../../../components/gridItem/GridItemContent' import { GridItemHeader } from '../../../components/gridItem/GridItemHeader' import { styled } from '@mui/material/styles' import { Checkbox } from '@mui/material' - -export type CustomerData = { - customer: string - consultants: number - billedLastPeriod: number - billedTotal: number -} +import { CustomerCardData } from '../../../api/data/customer/customerApiTypes' +import { ChartPeriod } from '../../../components/charts/chartFilters/useChartData' interface CustomerCardProps { - data: CustomerData + data: CustomerCardData selectedCustomerIds: string[] handleCheckboxChange?: ( event: React.ChangeEvent, @@ -24,11 +19,11 @@ interface CustomerCardProps { ) => void customerSpecificCard?: boolean vertical?: boolean + selectedChartPeriod?: ChartPeriod } interface CustomerCardContent { - consultants: number - billedLastPeriod: number - billedTotal: number | string + customer: CustomerCardData + selectedChartPeriod: ChartPeriod vertical?: boolean } @@ -50,36 +45,18 @@ const LinkStyled = styled(Link)(() => ({ const CustomerCardContent: React.FC = ({ vertical = false, - consultants, - billedLastPeriod, - billedTotal, + customer, + selectedChartPeriod, }) => { - const ComponentRoot = styled(Grid, { - shouldForwardProp: (prop) => prop !== 'vertical', - })<{ vertical?: boolean }>(({ vertical }) => ({ - display: 'flex', - flexDirection: vertical ? 'column' : 'row', - justifyContent: vertical ? 'center' : 'space-between', - textAlign: 'center', - })) - - const GridStyled = styled(Grid)(() => ({ - display: 'flex', - justifyContent: 'end', - flexDirection: 'column', - })) - - const GridHeadline = styled('p')(() => ({ - margin: 5, - })) - - const GridValue = styled('p', { - shouldForwardProp: (prop) => prop !== 'vertical', - })<{ vertical?: boolean }>(({ vertical }) => ({ - fontSize: 32, - fontWeight: 700, - margin: vertical ? 10 : 0, - })) + const billedTotalFixedNumber = Number(customer.billedTotal).toFixed(2) + const consultants = + selectedChartPeriod === ChartPeriod.WEEK + ? customer.consultantsLastPeriod + : customer.consultantsLastLongPeriod + const billedLastPeriod = + selectedChartPeriod === ChartPeriod.WEEK + ? customer.billedLastPeriod + : customer.billedLastLongPeriod if (vertical) { return ( @@ -94,8 +71,14 @@ const CustomerCardContent: React.FC = ({ Totalt fakturerte timer - {billedTotal} + {billedTotalFixedNumber} + {customer.accountManager && ( + + Kundeansvarlig: + {customer.accountManager} + + )} ) } else { @@ -120,9 +103,18 @@ const CustomerCardContent: React.FC = ({ {billedLastPeriod} - {billedTotal} + {customer.billedTotal} + {customer.accountManager && ( + + + + Kundeansvarlig: {customer.accountManager} + + + + )} ) } @@ -134,12 +126,9 @@ const CustomerCard: React.FC = ({ selectedCustomerIds, customerSpecificCard, vertical = false, + selectedChartPeriod = ChartPeriod.WEEK, }) => { - const { customer, consultants, billedLastPeriod, billedTotal } = data - - const billedTotalFixedNumber = Number.isInteger(billedTotal) - ? billedTotal - : billedTotal?.toFixed(2) + const { customer } = data return ( @@ -165,9 +154,8 @@ const CustomerCard: React.FC = ({ @@ -175,3 +163,30 @@ const CustomerCard: React.FC = ({ } export default CustomerCard + +const ComponentRoot = styled(Grid, { + shouldForwardProp: (prop) => prop !== 'vertical', +})<{ vertical?: boolean }>(({ vertical }) => ({ + display: 'flex', + flexDirection: vertical ? 'column' : 'row', + justifyContent: vertical ? 'center' : 'space-between', + textAlign: 'center', +})) + +const GridValue = styled('p', { + shouldForwardProp: (prop) => prop !== 'vertical', +})<{ vertical?: boolean }>(({ vertical }) => ({ + fontSize: 32, + fontWeight: 700, + margin: vertical ? 10 : 0, +})) + +const GridStyled = styled(Grid)(() => ({ + display: 'flex', + justifyContent: 'end', + flexDirection: 'column', +})) + +const GridHeadline = styled('p')(() => ({ + margin: 5, +})) diff --git a/apps/web/src/pages/customer/cards/HoursBilledPerWeekChart.tsx b/apps/web/src/pages/customer/cards/HoursBilledPerWeekChart.tsx index 8963d8b1..1a7a8baf 100644 --- a/apps/web/src/pages/customer/cards/HoursBilledPerWeekChart.tsx +++ b/apps/web/src/pages/customer/cards/HoursBilledPerWeekChart.tsx @@ -1,7 +1,7 @@ import { useMatomo } from '@jonkoops/matomo-tracker-react' import { useHoursBilledPerWeekCharts } from '../../../api/data/customer/customerQueries' import ChartCard from '../../../components/charts/ChartCard' -import usePerWeekFilter from '../../../components/charts/chartFilters/usePerWeekFilter' +import { ChartPeriod } from '../../../components/charts/chartFilters/useChartData' import { Box, FormControl, @@ -10,40 +10,43 @@ import { Radio, RadioGroup, } from '@mui/material' +import storageTokens from '../util/local-storage-tokens' import { DateRangePickerButton } from '../../../components/dateranges/DateRangePickerButton' import HoursBilledPerWeekTooltip from '../components/HoursBilledPerWeekTooltip' import { POSSIBLE_OLD_DATA_WARNING } from './messages' +import { Dispatch, SetStateAction, useEffect, useState } from 'react' +import useChartData from '../../../components/charts/chartFilters/useChartData' interface Props { - handleDateRangeChange: ( - selectedPeriodStartDate?: Date, - selectedPeriodEndDate?: Date - ) => void - startDate: Date - endDate: Date customersWithConsultants: string[] selectedCustomerIds: string[] showCustomerHistory?: boolean specificCustomer?: boolean + selectedChartPeriod: ChartPeriod + setSelectedChartPeriod: Dispatch> } const HoursBilledPerWeekChart = ({ - handleDateRangeChange, - startDate, - endDate, customersWithConsultants, selectedCustomerIds, showCustomerHistory, specificCustomer, + selectedChartPeriod, + setSelectedChartPeriod, }: Props) => { + const [startDate, setStartDate] = useState(storageTokens.getPeriodStartDate()) + const [endDate, setEndDate] = useState(storageTokens.getPeriodEndDate()) + + useEffect(() => { + storageTokens.setPeriodStartDate(startDate) + }, [startDate]) + + useEffect(() => { + storageTokens.setPeriodEndDate(endDate) + }, [endDate]) + const { data, error } = useHoursBilledPerWeekCharts() - const { - filterOptions, - selectedFilter, - setSelectedFilter, - weeklyData, - monthlyData, - } = usePerWeekFilter(data) + const chartData = useChartData(data, selectedChartPeriod) const { trackEvent } = useMatomo() const selectedCustomers = showCustomerHistory @@ -52,10 +55,10 @@ const HoursBilledPerWeekChart = ({ const setDateRange = (startDate, endDate) => { trackEvent({ category: 'filter-dato', action: 'click-event' }) - handleDateRangeChange(startDate, endDate) + setStartDate(startDate) + setEndDate(endDate) } - const chartData = selectedFilter === 'Uke' ? weeklyData : monthlyData const filteredData = data === undefined ? undefined @@ -134,24 +137,24 @@ const HoursBilledPerWeekChart = ({ { - const option = filterOptions.find( + const option = Object.keys(ChartPeriod).find( (option) => option === event.target.value ) trackEvent({ category: `${eventNavn}-${option.toLowerCase()}`, action: 'click-event', }) - setSelectedFilter(option) + setSelectedChartPeriod(ChartPeriod[option]) }} > - {filterOptions.map((option) => ( + {Object.keys(ChartPeriod).map((option: ChartPeriod) => ( } - label={option} + label={option === ChartPeriod.WEEK ? 'Uker' : 'Måneder'} style={{ flexGrow: 1 }} /> ))} diff --git a/apps/web/src/pages/customer/components/CustomerSiteContent.tsx b/apps/web/src/pages/customer/components/CustomerSiteContent.tsx index 51ca6efa..dbc4b5ec 100644 --- a/apps/web/src/pages/customer/components/CustomerSiteContent.tsx +++ b/apps/web/src/pages/customer/components/CustomerSiteContent.tsx @@ -1,13 +1,16 @@ import { makeStyles } from '@mui/styles' -import * as React from 'react' import { useCustomerCards, useHoursBilledPerCustomerCharts, } from '../../../api/data/customer/customerQueries' -import { CustomerSpecificHoursBilledGraph } from './CustomerSpesificHoursBilledGraph' import CustomerCard from '../cards/CustomerCard' import { EmployeeTable } from '../../employee/table/EmployeeTable' import { CustomerNotFound } from './CustomerNotFound' +import { ChartPeriod } from '../../../components/charts/chartFilters/useChartData' +import { useState } from 'react' +import { Grid, styled } from '@mui/material' +import { GridItem } from '../../../components/gridItem/GridItem' +import HoursBilledPerWeekChart from '../cards/HoursBilledPerWeekChart' const useStyles = makeStyles({ root: { @@ -51,7 +54,9 @@ interface Props { export function CustomerSiteContent({ customerId }: Props) { const classes = useStyles() - + const [selectedChartPeriod, setSelectedChartPeriod] = useState( + ChartPeriod.WEEK + ) const customerCards = useCustomerCards() const { data: hoursBilledData } = useHoursBilledPerCustomerCharts() const isLoading = !hoursBilledData @@ -85,7 +90,19 @@ export function CustomerSiteContent({ customerId }: Props) {
- + + + + + + +
{cardData || historicalCustomer ? ( @@ -95,6 +112,7 @@ export function CustomerSiteContent({ customerId }: Props) { selectedCustomerIds={[customerId]} customerSpecificCard={true} vertical={true} + selectedChartPeriod={selectedChartPeriod} /> ) : null}
@@ -109,3 +127,9 @@ export function CustomerSiteContent({ customerId }: Props) { ) } + +const GridContainer = styled('div')({ + display: 'grid', + gridTemplateColumns: '3fr', + gridGap: '1rem', +}) diff --git a/apps/web/src/pages/customer/components/CustomerSpesificHoursBilledGraph.tsx b/apps/web/src/pages/customer/components/CustomerSpesificHoursBilledGraph.tsx deleted file mode 100644 index ae36fa68..00000000 --- a/apps/web/src/pages/customer/components/CustomerSpesificHoursBilledGraph.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { useEffect, useState } from 'react' -import { Grid } from '@mui/material' -import { styled } from '@mui/material/styles' -import storageTokens from '../util/local-storage-tokens' -import HoursBilledPerWeekChart from '../cards/HoursBilledPerWeekChart' -import { GridItem } from '../../../components/gridItem/GridItem' - -const GridContainer = styled('div')({ - display: 'grid', - gridTemplateColumns: '3fr', - gridGap: '1rem', -}) - -export const CustomerSpecificHoursBilledGraph = ({ customerId }) => { - const [selectedPeriodStartDate, setPeriodStartDate] = useState(null) - const [selectedPeriodEndDate, setPeriodEndDate] = useState(null) - - useEffect(() => { - const startDate = storageTokens.getPeriodStartDate() - const endDate = storageTokens.getPeriodEndDate() - - setPeriodStartDate(startDate) - setPeriodEndDate(endDate) - localStorage.setItem('selectedCustomerIds', JSON.stringify([customerId])) - }, [customerId]) - - useEffect(() => { - storageTokens.setPeriodStartDate(selectedPeriodStartDate) - }, [selectedPeriodStartDate]) - - useEffect(() => { - storageTokens.setPeriodEndDate(selectedPeriodEndDate) - }, [selectedPeriodEndDate]) - - return ( - - - - - - - - ) -} diff --git a/apps/web/src/pages/customer/customer-overview/CustomerCardList.tsx b/apps/web/src/pages/customer/customer-overview/CustomerCardList.tsx index 9dc422bd..3c7945f3 100644 --- a/apps/web/src/pages/customer/customer-overview/CustomerCardList.tsx +++ b/apps/web/src/pages/customer/customer-overview/CustomerCardList.tsx @@ -1,11 +1,12 @@ import React, { useEffect, useState } from 'react' import { BaseSkeleton } from '../../../components/skeletons/BaseSkeleton' import { useCustomerCards } from '../../../api/data/customer/customerQueries' -import CustomerCard, { CustomerData } from '../cards/CustomerCard' +import CustomerCard from '../cards/CustomerCard' import { GridItem } from 'web/src/components/gridItem/GridItem' import { Grid } from '@mui/material' import CustomerCardSort from './CustomerCardSort' import { styled } from '@mui/material/styles' +import { CustomerCardData } from '../../../api/data/customer/customerApiTypes' const Title = styled('h2')({ marginBottom: '2px', @@ -14,7 +15,7 @@ const Title = styled('h2')({ interface Props { selectedCustomerIds: string[] showHistoricalData: boolean - historicalCustomers: CustomerData[] + historicalCustomers: CustomerCardData[] handleCheckboxChange: ( event: React.ChangeEvent, customerId: string diff --git a/apps/web/src/pages/customer/customer-overview/CustomerCardSort.tsx b/apps/web/src/pages/customer/customer-overview/CustomerCardSort.tsx index 831d5077..e9b9294a 100644 --- a/apps/web/src/pages/customer/customer-overview/CustomerCardSort.tsx +++ b/apps/web/src/pages/customer/customer-overview/CustomerCardSort.tsx @@ -3,12 +3,13 @@ import { GridItemHeader } from '../../../components/gridItem/GridItemHeader' import SearchInput from '../../../components/SearchInput' import SortButton from '../cards/SortButton' import { Grid, styled } from '@mui/material' -import CustomerCard, { CustomerData } from '../cards/CustomerCard' +import CustomerCard from '../cards/CustomerCard' import { SortCustomerCards } from '../util/sort-customer-cards' import { useMatomo } from '@jonkoops/matomo-tracker-react' +import { CustomerCardData } from '../../../api/data/customer/customerApiTypes' interface Props { - data: CustomerData[] + data: CustomerCardData[] handleCheckboxChange: ( event: React.ChangeEvent, customerId: string @@ -29,7 +30,7 @@ const CustomerCardSort = ({ const [searchTerm, setSearchTerm] = useState('') const [activeSortButton, setActiveSortBotton] = useState('Alfabetisk') const [sortOrder, setSortOrder] = useState('ASC') - const [sortedData, setSortedData] = useState([]) + const [sortedData, setSortedData] = useState([]) const { trackEvent } = useMatomo() const buttons = ['Alfabetisk', 'Antall konsulenter', 'Antall timer'] diff --git a/apps/web/src/pages/customer/customer-overview/CustomerHoursPerWeekSection.tsx b/apps/web/src/pages/customer/customer-overview/CustomerHoursPerWeekSection.tsx index dc9f3b5e..1e19d03a 100644 --- a/apps/web/src/pages/customer/customer-overview/CustomerHoursPerWeekSection.tsx +++ b/apps/web/src/pages/customer/customer-overview/CustomerHoursPerWeekSection.tsx @@ -5,24 +5,19 @@ import { import { GridItem } from '../../../components/gridItem/GridItem' import { GridItemHeader } from '../../../components/gridItem/GridItemHeader' import { styled } from '@mui/material/styles' -import usePerWeekFilter from '../../../components/charts/chartFilters/usePerWeekFilter' +import { ChartPeriod } from '../../../components/charts/chartFilters/useChartData' import { BaseSkeleton } from '../../../components/skeletons/BaseSkeleton' import CustomerGraphFilter from './CustomerGraphFilter' import HoursBilledPerWeekChart from '../cards/HoursBilledPerWeekChart' import CustomerOverviewFilter, { SortMethod } from './CustomerOverviewFilter' import { FormControlLabel, Radio, RadioGroup } from '@mui/material' -import { ChangeEvent, useState } from 'react' +import { useState } from 'react' import { Translation } from '../../../utils/translation' +import useChartData from '../../../components/charts/chartFilters/useChartData' interface HoursBilledPerWeekCardProps { selectedCustomerIds: string[] setSelectedCustomerIds: (ids: string[]) => void - selectedPeriodStartDate: Date - selectedPeriodEndDate: Date - handleDateRangeChange: ( - selectedPeriodStartDate?: Date, - selectedPeriodEndDate?: Date - ) => void handleCheckboxChange: ( event: React.ChangeEvent, customerId: string @@ -34,9 +29,6 @@ interface HoursBilledPerWeekCardProps { const CustomerHoursPerWeekSection = ({ selectedCustomerIds, setSelectedCustomerIds, - selectedPeriodStartDate: startDate, - selectedPeriodEndDate: endDate, - handleDateRangeChange, handleCheckboxChange, showCustomerHistory, setShowCustomerHistory, @@ -46,7 +38,10 @@ const CustomerHoursPerWeekSection = ({ (customerCard) => customerCard.customer ) const { data } = useHoursBilledPerWeekCharts() - const { selectedFilter, weeklyData, monthlyData } = usePerWeekFilter(data) + const [selectedChartPeriod, setSelectedChartPeriod] = useState( + ChartPeriod.WEEK + ) + const chartData = useChartData(data, selectedChartPeriod) const customerIdsUnfiltered = data === undefined ? [] : data?.data?.map((item) => item.id as string) @@ -70,7 +65,6 @@ const CustomerHoursPerWeekSection = ({ setSelectedCustomerIds([]) } - const chartData = selectedFilter === 'Uke' ? weeklyData : monthlyData const filteredData = data === undefined ? undefined @@ -114,12 +108,11 @@ const CustomerHoursPerWeekSection = ({ ) : ( @@ -152,6 +145,7 @@ const CustomerHoursPerWeekSection = ({ handleCheckboxChange={handleCheckboxChange} selectedSortMethod={selectedSortMethod} showCustomerHistory={showCustomerHistory} + selectedChartPeriod={selectedChartPeriod} /> diff --git a/apps/web/src/pages/customer/customer-overview/CustomerOverview.tsx b/apps/web/src/pages/customer/customer-overview/CustomerOverview.tsx index 80906b04..c2541327 100644 --- a/apps/web/src/pages/customer/customer-overview/CustomerOverview.tsx +++ b/apps/web/src/pages/customer/customer-overview/CustomerOverview.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useState } from 'react' import CustomerCardListOverview from './CustomerCardListOverview' import { Grid } from '@mui/material' -import storageTokens from '../util/local-storage-tokens' import CustomerHoursPerWeekSection from './CustomerHoursPerWeekSection' import { useSelectedCustomerIds } from '../util/local-storage-hooks' @@ -9,16 +8,7 @@ export const CustomerOverview = () => { const [showHistoricCustomers, setShowHistoricCustomers] = useState(false) const { selectedCustomerIds, setSelectedCustomerIds } = useSelectedCustomerIds() - const [selectedPeriodStartDate, setPeriodStartDate] = useState(null) - const [selectedPeriodEndDate, setPeriodEndDate] = useState(null) - useEffect(() => { - const startDate = storageTokens.getPeriodStartDate() - const endDate = storageTokens.getPeriodEndDate() - - setPeriodStartDate(startDate) - setPeriodEndDate(endDate) - }, []) useEffect(() => { if (selectedCustomerIds !== null) { localStorage.setItem( @@ -28,14 +18,6 @@ export const CustomerOverview = () => { } }, [selectedCustomerIds]) - useEffect(() => { - storageTokens.setPeriodStartDate(selectedPeriodStartDate) - }, [selectedPeriodStartDate]) - - useEffect(() => { - storageTokens.setPeriodEndDate(selectedPeriodEndDate) - }, [selectedPeriodEndDate]) - const handleCheckboxChange = ( event: React.ChangeEvent, customerId: string @@ -54,15 +36,6 @@ export const CustomerOverview = () => { - a.customer.localeCompare(b.customer), - [SortMethod.konsulenter]: (a: CustomerCardData, b: CustomerCardData) => - b.consultants - a.consultants, -} - interface Props { handleCheckboxChange: ( event: React.ChangeEvent, @@ -29,6 +22,7 @@ interface Props { selectedCustomerIds: string[] selectedSortMethod: SortMethod showCustomerHistory: boolean + selectedChartPeriod: ChartPeriod } const CustomerOverviewFilter = ({ @@ -36,16 +30,10 @@ const CustomerOverviewFilter = ({ selectedCustomerIds, selectedSortMethod, showCustomerHistory, + selectedChartPeriod, }: Props) => { const customerCards = useCustomerCards() - const customerData = useAllCustomerData().map( - (cd): CustomerData => ({ - customer: cd.customer, - consultants: 0, - billedLastPeriod: 0, - billedTotal: 0, - }) - ) + const customerData = useAllCustomerData() const historicalCustomerData = customerData.filter( (cd) => !customerCards.find((cc) => cc.customer === cd.customer) ) @@ -53,7 +41,17 @@ const CustomerOverviewFilter = ({ ...customerCards, ...(showCustomerHistory ? historicalCustomerData : []), ] - console.log(populatedCustomerCards) + const getConsultants = (customer: CustomerCardData) => + selectedChartPeriod === ChartPeriod.WEEK + ? customer.consultantsLastPeriod + : customer.consultantsLastLongPeriod + + const sortMethod = { + [SortMethod.abc]: (a: CustomerCardData, b: CustomerCardData) => + a.customer.localeCompare(b.customer), + [SortMethod.konsulenter]: (a: CustomerCardData, b: CustomerCardData) => + getConsultants(b) - getConsultants(a), + } const sortedSelectedCustomers = populatedCustomerCards .filter((cc) => selectedCustomerIds.includes(cc.customer)) .sort(sortMethod[selectedSortMethod]) @@ -61,8 +59,6 @@ const CustomerOverviewFilter = ({ .filter((cc) => !selectedCustomerIds.includes(cc.customer)) .sort(sortMethod[selectedSortMethod]) - console.log(sortedSelectedCustomers.length, sortedUnselectedCustomers.length) - return ( @@ -85,7 +81,7 @@ const CustomerOverviewFilter = ({ name={customer.customer} /> } - label={`${customer.customer} (${customer.consultants})`} + label={`${customer.customer} (${getConsultants(customer)})`} labelPlacement="start" key={customer.customer} /> @@ -108,7 +104,7 @@ const CustomerOverviewFilter = ({ name={customer.customer} /> } - label={`${customer.customer} (${customer.consultants})`} + label={`${customer.customer} (${getConsultants(customer)})`} labelPlacement="start" key={customer.customer} /> diff --git a/apps/web/src/pages/customer/util/sort-customer-cards.tsx b/apps/web/src/pages/customer/util/sort-customer-cards.tsx index 074a17cf..3621c41b 100644 --- a/apps/web/src/pages/customer/util/sort-customer-cards.tsx +++ b/apps/web/src/pages/customer/util/sort-customer-cards.tsx @@ -1,24 +1,24 @@ -import { CustomerData } from '../cards/CustomerCard' +import { CustomerCardData } from '../../../api/data/customer/customerApiTypes' export function SortCustomerCards( - data: CustomerData[], + data: CustomerCardData[], currentSort: string, sortOrder: string ) { if (!currentSort) return data - const getCellValue = (row: CustomerData) => { + const getCellValue = (row: CustomerCardData) => { switch (currentSort) { case 'Alfabetisk': return row.customer case 'Antall konsulenter': - return row.consultants + return row.consultantsLastPeriod case 'Antall timer': return row.billedTotal } } - const compare = (a: CustomerData, b: CustomerData) => { + const compare = (a: CustomerCardData, b: CustomerCardData) => { const aValue = getCellValue(a) const bValue = getCellValue(b) if (currentSort === 'Alfabetisk') { diff --git a/apps/web/src/pages/login/LoginPage.tsx b/apps/web/src/pages/login/LoginPage.tsx index a1e7eec5..69e013b6 100644 --- a/apps/web/src/pages/login/LoginPage.tsx +++ b/apps/web/src/pages/login/LoginPage.tsx @@ -1,5 +1,4 @@ import { Button } from '@mui/material' -import React from 'react' import { pageTitle } from '../../utils/pagetitle' const LoginPage = () => { diff --git a/apps/web/src/pages/organizationStructure/OrganizationStructurePage.tsx b/apps/web/src/pages/organizationStructure/OrganizationStructurePage.tsx index 60c138e4..dfcc5d2d 100644 --- a/apps/web/src/pages/organizationStructure/OrganizationStructurePage.tsx +++ b/apps/web/src/pages/organizationStructure/OrganizationStructurePage.tsx @@ -43,4 +43,5 @@ export default function OrganizationStructurePage() { const RelativePositionedBox = styled('div')({ position: 'relative', + height: '1140px', }) From 7a4e45f1a41b913be3c0cd555303d3b07d592bc5 Mon Sep 17 00:00:00 2001 From: "anders.dahlin@knowit.no" Date: Tue, 12 Mar 2024 14:26:31 +0100 Subject: [PATCH 2/2] Sortering og henting av data i kortlista --- .../routers/customer/customerAggregation.ts | 52 +++++++++---------- .../src/pages/customer/cards/CustomerCard.tsx | 49 ++++++++--------- .../cards/HoursBilledPerWeekChart.tsx | 2 +- .../src/pages/customer/cards/SortButton.tsx | 2 +- .../customer-overview/CustomerCardList.tsx | 19 +++---- .../CustomerCardListOverview.tsx | 38 -------------- .../customer-overview/CustomerCardSort.tsx | 45 ++++++++-------- .../CustomerHoursPerWeekSection.tsx | 10 ++-- .../customer-overview/CustomerOverview.tsx | 11 +++- .../CustomerOverviewFilter.tsx | 20 +++---- .../customer/util/sort-customer-cards.tsx | 10 ++-- 11 files changed, 110 insertions(+), 148 deletions(-) delete mode 100644 apps/web/src/pages/customer/customer-overview/CustomerCardListOverview.tsx diff --git a/apps/server/routers/customer/customerAggregation.ts b/apps/server/routers/customer/customerAggregation.ts index 61013b3a..06f6f23e 100644 --- a/apps/server/routers/customer/customerAggregation.ts +++ b/apps/server/routers/customer/customerAggregation.ts @@ -79,36 +79,34 @@ export function createCustomerCardData( .sort() .slice(-4) - const result = unique(employeesCustomer.map((ec) => ec.customer)).map( - (customer) => { - const customerProjects = projects.filter((p) => p.customer === customer) - const customerEmployees = employeesCustomer.filter( - (ec) => ec.customer === customer + const result = unique(projects.map((ec) => ec.customer)).map((customer) => { + const customerProjects = projects.filter((p) => p.customer === customer) + const customerEmployees = employeesCustomer.filter( + (ec) => ec.customer === customer + ) + const employeesLastPeriod = customerEmployees + .filter((ce) => getPeriods(ce).includes(last_reg_periods.at(-1))) + .map((ce) => ce.email) + const employeesLastLongPeriod = customerEmployees + .filter((ce) => + getPeriods(ce).some((period) => last_reg_periods.includes(period)) ) - const employeesLastPeriod = customerEmployees - .filter((ce) => getPeriods(ce).includes(last_reg_periods.at(-1))) - .map((ce) => ce.email) - const employeesLastLongPeriod = customerEmployees - .filter((ce) => - getPeriods(ce).some((period) => last_reg_periods.includes(period)) - ) - .map((ce) => ce.email) + .map((ce) => ce.email) - return { - customer, - accountManager: accountManagerMap[customer], - billedLastPeriod: customerProjects - .filter((cp) => cp.reg_period === last_reg_periods.at(-1)) - .reduce(addBy('hours'), 0), - billedLastLongPeriod: customerProjects - .filter((cp) => last_reg_periods.includes(cp.reg_period)) - .reduce(addBy('hours'), 0), - billedTotal: customerProjects.reduce(addBy('hours'), 0), - consultantsLastPeriod: unique(employeesLastPeriod).length, - consultantsLastLongPeriod: unique(employeesLastLongPeriod).length, - } + return { + customer, + accountManager: accountManagerMap[customer], + billedLastPeriod: customerProjects + .filter((cp) => cp.reg_period === last_reg_periods.at(-1)) + .reduce(addBy('hours'), 0), + billedLastLongPeriod: customerProjects + .filter((cp) => last_reg_periods.includes(cp.reg_period)) + .reduce(addBy('hours'), 0), + billedTotal: customerProjects.reduce(addBy('hours'), 0), + consultantsLastPeriod: unique(employeesLastPeriod).length, + consultantsLastLongPeriod: unique(employeesLastLongPeriod).length, } - ) + }) return result } diff --git a/apps/web/src/pages/customer/cards/CustomerCard.tsx b/apps/web/src/pages/customer/cards/CustomerCard.tsx index 25b00b9f..b7f1104c 100644 --- a/apps/web/src/pages/customer/cards/CustomerCard.tsx +++ b/apps/web/src/pages/customer/cards/CustomerCard.tsx @@ -48,15 +48,16 @@ const CustomerCardContent: React.FC = ({ customer, selectedChartPeriod, }) => { - const billedTotalFixedNumber = Number(customer.billedTotal).toFixed(2) + const billedTotalFixedNumber = Number(customer.billedTotal).toFixed(0) const consultants = selectedChartPeriod === ChartPeriod.WEEK ? customer.consultantsLastPeriod : customer.consultantsLastLongPeriod - const billedLastPeriod = + const billedLastPeriod = Number( selectedChartPeriod === ChartPeriod.WEEK ? customer.billedLastPeriod : customer.billedLastLongPeriod + ).toFixed(1) if (vertical) { return ( @@ -73,48 +74,44 @@ const CustomerCardContent: React.FC = ({ Totalt fakturerte timer {billedTotalFixedNumber} - {customer.accountManager && ( - - Kundeansvarlig: - {customer.accountManager} - - )} + + Kundeansvarlig: + {customer.accountManager || 'Ukjent'} + ) } else { return ( <> - - Antall konsulenter + + Antall konsulenter siste periode - + Fakturerte timer siste periode - + Totalt fakturerte timer - + {consultants} - + {billedLastPeriod} - - {customer.billedTotal} + + {billedTotalFixedNumber} + + + + + + Kundeansvarlig: {customer.accountManager || 'Ukjent'} + - {customer.accountManager && ( - - - - Kundeansvarlig: {customer.accountManager} - - - - )} ) } @@ -176,7 +173,7 @@ const ComponentRoot = styled(Grid, { const GridValue = styled('p', { shouldForwardProp: (prop) => prop !== 'vertical', })<{ vertical?: boolean }>(({ vertical }) => ({ - fontSize: 32, + fontSize: 26, fontWeight: 700, margin: vertical ? 10 : 0, })) diff --git a/apps/web/src/pages/customer/cards/HoursBilledPerWeekChart.tsx b/apps/web/src/pages/customer/cards/HoursBilledPerWeekChart.tsx index 1a7a8baf..ed67dbcb 100644 --- a/apps/web/src/pages/customer/cards/HoursBilledPerWeekChart.tsx +++ b/apps/web/src/pages/customer/cards/HoursBilledPerWeekChart.tsx @@ -132,7 +132,7 @@ const HoursBilledPerWeekChart = ({ sliceTooltip={HoursBilledPerWeekTooltip} extraHeaderContent={ - Grupper etter + Grupper etter periode { }} active={active} > - {title} {title === 'Alfabetisk' && sortIcon} + {title} {active && sortIcon} ) } diff --git a/apps/web/src/pages/customer/customer-overview/CustomerCardList.tsx b/apps/web/src/pages/customer/customer-overview/CustomerCardList.tsx index 3c7945f3..314eae84 100644 --- a/apps/web/src/pages/customer/customer-overview/CustomerCardList.tsx +++ b/apps/web/src/pages/customer/customer-overview/CustomerCardList.tsx @@ -6,7 +6,7 @@ import { GridItem } from 'web/src/components/gridItem/GridItem' import { Grid } from '@mui/material' import CustomerCardSort from './CustomerCardSort' import { styled } from '@mui/material/styles' -import { CustomerCardData } from '../../../api/data/customer/customerApiTypes' +import { ChartPeriod } from '../../../components/charts/chartFilters/useChartData' const Title = styled('h2')({ marginBottom: '2px', @@ -15,18 +15,18 @@ const Title = styled('h2')({ interface Props { selectedCustomerIds: string[] showHistoricalData: boolean - historicalCustomers: CustomerCardData[] handleCheckboxChange: ( event: React.ChangeEvent, customerId: string ) => void + selectedChartPeriod: ChartPeriod } const CustomerCardList = ({ selectedCustomerIds, showHistoricalData, - historicalCustomers, handleCheckboxChange, + selectedChartPeriod, }: Props) => { const customerCards = useCustomerCards() const [customersInGraph, setCustomersInGraph] = useState([]) @@ -34,8 +34,8 @@ const CustomerCardList = ({ useEffect(() => { if (customerCards) { - const all_customer = customerCards.concat( - showHistoricalData ? historicalCustomers : [] + const all_customer = customerCards.filter( + (cc) => showHistoricalData || cc.consultantsLastPeriod > 0 ) const customers_in_graph = @@ -56,12 +56,7 @@ const CustomerCardList = ({ setCustomersInGraph(customers_in_graph) setOtherCustomers(other_customers) } - }, [ - customerCards, - historicalCustomers, - selectedCustomerIds, - showHistoricalData, - ]) + }, [customerCards, selectedCustomerIds, showHistoricalData]) if (!customerCards) { return ( @@ -89,6 +84,7 @@ const CustomerCardList = ({ data={customer} handleCheckboxChange={handleCheckboxChange} selectedCustomerIds={selectedCustomerIds} + selectedChartPeriod={selectedChartPeriod} /> ))} @@ -98,6 +94,7 @@ const CustomerCardList = ({ data={otherCustomers} handleCheckboxChange={handleCheckboxChange} selectedCustomerIds={selectedCustomerIds} + selectedChartPeriod={selectedChartPeriod} /> diff --git a/apps/web/src/pages/customer/customer-overview/CustomerCardListOverview.tsx b/apps/web/src/pages/customer/customer-overview/CustomerCardListOverview.tsx deleted file mode 100644 index 0230d627..00000000 --- a/apps/web/src/pages/customer/customer-overview/CustomerCardListOverview.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import CustomerCardList from './CustomerCardList' -import { - useAllCustomerData, - useCustomerCards, -} from '../../../api/data/customer/customerQueries' - -interface Props { - selectedCustomerIds: string[] - showHistoricalData: boolean - handleCheckboxChange: ( - event: React.ChangeEvent, - customerId: string - ) => void -} - -const CustomerCardListOverview = ({ - selectedCustomerIds, - showHistoricalData, - handleCheckboxChange, -}: Props) => { - const customersWithConsultants = useCustomerCards().map( - (customerCard) => customerCard.customer - ) - const historicalCustomers = useAllCustomerData().filter( - (customerData) => !customersWithConsultants.includes(customerData.customer) - ) - - return ( - - ) -} - -export default CustomerCardListOverview diff --git a/apps/web/src/pages/customer/customer-overview/CustomerCardSort.tsx b/apps/web/src/pages/customer/customer-overview/CustomerCardSort.tsx index e9b9294a..39d84854 100644 --- a/apps/web/src/pages/customer/customer-overview/CustomerCardSort.tsx +++ b/apps/web/src/pages/customer/customer-overview/CustomerCardSort.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import { GridItemHeader } from '../../../components/gridItem/GridItemHeader' import SearchInput from '../../../components/SearchInput' import SortButton from '../cards/SortButton' @@ -7,6 +7,7 @@ import CustomerCard from '../cards/CustomerCard' import { SortCustomerCards } from '../util/sort-customer-cards' import { useMatomo } from '@jonkoops/matomo-tracker-react' import { CustomerCardData } from '../../../api/data/customer/customerApiTypes' +import { ChartPeriod } from '../../../components/charts/chartFilters/useChartData' interface Props { data: CustomerCardData[] @@ -15,6 +16,7 @@ interface Props { customerId: string ) => void selectedCustomerIds: string[] + selectedChartPeriod: ChartPeriod } const ButtonWrapper = styled('div')({ @@ -26,13 +28,23 @@ const CustomerCardSort = ({ data, handleCheckboxChange, selectedCustomerIds, + selectedChartPeriod, }: Props) => { const [searchTerm, setSearchTerm] = useState('') const [activeSortButton, setActiveSortBotton] = useState('Alfabetisk') const [sortOrder, setSortOrder] = useState('ASC') - const [sortedData, setSortedData] = useState([]) const { trackEvent } = useMatomo() + const filtredRows = data.filter((term) => { + return term.customer.toLowerCase().includes(searchTerm.toLowerCase()) + }) + const sortedData = SortCustomerCards( + filtredRows, + activeSortButton, + sortOrder, + selectedChartPeriod + ) + const buttons = ['Alfabetisk', 'Antall konsulenter', 'Antall timer'] const showHeader = selectedCustomerIds !== null && selectedCustomerIds.length > 0 @@ -42,29 +54,17 @@ const CustomerCardSort = ({ category: `sortering-kunder-${type.replace(/\s/g, '').toLowerCase()}`, action: 'click-event', }) - const order = - type === activeSortButton && activeSortButton === 'Alfabetisk' - ? sortOrder === 'ASC' - ? 'DESC' - : 'ASC' - : 'ASC' + if (activeSortButton === type) { + const newOrder = sortOrder == 'ASC' ? 'DESC' : 'ASC' + setSortOrder(newOrder) + } else { + const order = type === 'Alfabetisk' ? 'ASC' : 'DESC' - setSortOrder(order) - setActiveSortBotton(type) - setSortedData(SortCustomerCards(data, type, order)) + setSortOrder(order) + setActiveSortBotton(type) + } } - useEffect(() => { - const filtredRows = data.filter((term) => { - return term.customer.toLowerCase().includes(searchTerm) - }) - setSortedData(filtredRows) - }, [searchTerm, data]) - - useEffect(() => { - setSortedData(SortCustomerCards(data, activeSortButton, sortOrder)) - }, [data, activeSortButton, sortOrder]) - return ( <> @@ -98,6 +98,7 @@ const CustomerCardSort = ({ data={customer} handleCheckboxChange={handleCheckboxChange} selectedCustomerIds={selectedCustomerIds} + selectedChartPeriod={selectedChartPeriod} /> ))} diff --git a/apps/web/src/pages/customer/customer-overview/CustomerHoursPerWeekSection.tsx b/apps/web/src/pages/customer/customer-overview/CustomerHoursPerWeekSection.tsx index 1e19d03a..62e81709 100644 --- a/apps/web/src/pages/customer/customer-overview/CustomerHoursPerWeekSection.tsx +++ b/apps/web/src/pages/customer/customer-overview/CustomerHoursPerWeekSection.tsx @@ -11,7 +11,7 @@ import CustomerGraphFilter from './CustomerGraphFilter' import HoursBilledPerWeekChart from '../cards/HoursBilledPerWeekChart' import CustomerOverviewFilter, { SortMethod } from './CustomerOverviewFilter' import { FormControlLabel, Radio, RadioGroup } from '@mui/material' -import { useState } from 'react' +import { Dispatch, SetStateAction, useState } from 'react' import { Translation } from '../../../utils/translation' import useChartData from '../../../components/charts/chartFilters/useChartData' @@ -24,6 +24,8 @@ interface HoursBilledPerWeekCardProps { ) => void showCustomerHistory: boolean setShowCustomerHistory: (v: boolean) => void + selectedChartPeriod: ChartPeriod + setSelectedChartPeriod: Dispatch> } const CustomerHoursPerWeekSection = ({ @@ -32,15 +34,15 @@ const CustomerHoursPerWeekSection = ({ handleCheckboxChange, showCustomerHistory, setShowCustomerHistory, + selectedChartPeriod, + setSelectedChartPeriod, }: HoursBilledPerWeekCardProps) => { const [selectedSortMethod, setSelectedSortMethod] = useState(SortMethod.abc) const customersWithConsultants = useCustomerCards().map( (customerCard) => customerCard.customer ) const { data } = useHoursBilledPerWeekCharts() - const [selectedChartPeriod, setSelectedChartPeriod] = useState( - ChartPeriod.WEEK - ) + const chartData = useChartData(data, selectedChartPeriod) const customerIdsUnfiltered = diff --git a/apps/web/src/pages/customer/customer-overview/CustomerOverview.tsx b/apps/web/src/pages/customer/customer-overview/CustomerOverview.tsx index c2541327..d74855c8 100644 --- a/apps/web/src/pages/customer/customer-overview/CustomerOverview.tsx +++ b/apps/web/src/pages/customer/customer-overview/CustomerOverview.tsx @@ -1,11 +1,15 @@ import React, { useEffect, useState } from 'react' -import CustomerCardListOverview from './CustomerCardListOverview' import { Grid } from '@mui/material' import CustomerHoursPerWeekSection from './CustomerHoursPerWeekSection' import { useSelectedCustomerIds } from '../util/local-storage-hooks' +import CustomerCardList from './CustomerCardList' +import { ChartPeriod } from '../../../components/charts/chartFilters/useChartData' export const CustomerOverview = () => { const [showHistoricCustomers, setShowHistoricCustomers] = useState(false) + const [selectedChartPeriod, setSelectedChartPeriod] = useState( + ChartPeriod.WEEK + ) const { selectedCustomerIds, setSelectedCustomerIds } = useSelectedCustomerIds() @@ -39,11 +43,14 @@ export const CustomerOverview = () => { setSelectedCustomerIds={setSelectedCustomerIds} showCustomerHistory={showHistoricCustomers} setShowCustomerHistory={setShowHistoricCustomers} + selectedChartPeriod={selectedChartPeriod} + setSelectedChartPeriod={setSelectedChartPeriod} /> - ) diff --git a/apps/web/src/pages/customer/customer-overview/CustomerOverviewFilter.tsx b/apps/web/src/pages/customer/customer-overview/CustomerOverviewFilter.tsx index ca3ce266..ac7eb89c 100644 --- a/apps/web/src/pages/customer/customer-overview/CustomerOverviewFilter.tsx +++ b/apps/web/src/pages/customer/customer-overview/CustomerOverviewFilter.tsx @@ -2,10 +2,7 @@ import { styled } from '@mui/material/styles' import { GridItemContent } from '../../../components/gridItem/GridItemContent' import { LayoutGroup, motion } from 'framer-motion' import { Checkbox, FormControlLabel } from '@mui/material' -import { - useAllCustomerData, - useCustomerCards, -} from '../../../api/data/customer/customerQueries' +import { useCustomerCards } from '../../../api/data/customer/customerQueries' import { CustomerCardData } from '../../../api/data/customer/customerApiTypes' import { ChartPeriod } from '../../../components/charts/chartFilters/useChartData' @@ -33,14 +30,11 @@ const CustomerOverviewFilter = ({ selectedChartPeriod, }: Props) => { const customerCards = useCustomerCards() - const customerData = useAllCustomerData() - const historicalCustomerData = customerData.filter( - (cd) => !customerCards.find((cc) => cc.customer === cd.customer) + + const filteredCustomerCards = customerCards.filter( + (cc) => showCustomerHistory || cc.consultantsLastPeriod > 0 ) - const populatedCustomerCards = [ - ...customerCards, - ...(showCustomerHistory ? historicalCustomerData : []), - ] + const getConsultants = (customer: CustomerCardData) => selectedChartPeriod === ChartPeriod.WEEK ? customer.consultantsLastPeriod @@ -52,10 +46,10 @@ const CustomerOverviewFilter = ({ [SortMethod.konsulenter]: (a: CustomerCardData, b: CustomerCardData) => getConsultants(b) - getConsultants(a), } - const sortedSelectedCustomers = populatedCustomerCards + const sortedSelectedCustomers = filteredCustomerCards .filter((cc) => selectedCustomerIds.includes(cc.customer)) .sort(sortMethod[selectedSortMethod]) - const sortedUnselectedCustomers = populatedCustomerCards + const sortedUnselectedCustomers = filteredCustomerCards .filter((cc) => !selectedCustomerIds.includes(cc.customer)) .sort(sortMethod[selectedSortMethod]) diff --git a/apps/web/src/pages/customer/util/sort-customer-cards.tsx b/apps/web/src/pages/customer/util/sort-customer-cards.tsx index 3621c41b..f8ae3661 100644 --- a/apps/web/src/pages/customer/util/sort-customer-cards.tsx +++ b/apps/web/src/pages/customer/util/sort-customer-cards.tsx @@ -1,9 +1,11 @@ import { CustomerCardData } from '../../../api/data/customer/customerApiTypes' +import { ChartPeriod } from '../../../components/charts/chartFilters/useChartData' export function SortCustomerCards( data: CustomerCardData[], currentSort: string, - sortOrder: string + sortOrder: string, + selectedChartPeriod: ChartPeriod ) { if (!currentSort) return data @@ -12,7 +14,9 @@ export function SortCustomerCards( case 'Alfabetisk': return row.customer case 'Antall konsulenter': - return row.consultantsLastPeriod + return selectedChartPeriod === ChartPeriod.WEEK + ? row.consultantsLastPeriod + : row.consultantsLastLongPeriod case 'Antall timer': return row.billedTotal } @@ -30,7 +34,7 @@ export function SortCustomerCards( } } - if (currentSort === 'Alfabetisk' && sortOrder === 'ASC') { + if (sortOrder === 'ASC') { return data.sort((a, b) => compare(a, b)) } else { return data.sort((a, b) => compare(b, a))