diff --git a/app/spa/src/components/Projects/ProjectChartsTable.tsx b/app/spa/src/components/Projects/ProjectChartsTable.tsx index 08d4c975a..42e4eac53 100644 --- a/app/spa/src/components/Projects/ProjectChartsTable.tsx +++ b/app/spa/src/components/Projects/ProjectChartsTable.tsx @@ -1,6 +1,8 @@ import { useState } from "react" import { + Backdrop, Box, + CircularProgress, Grid, Link, Paper, @@ -15,7 +17,6 @@ import { import { gql, useQuery } from "@apollo/client" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { Backdrop, CircularProgress } from "@mui/material" import { formatDate, secondsToDays } from "../../lib/date" import { DemandsList } from "../../modules/demand/demand.types" import { Project } from "../../modules/project/project.types" @@ -192,7 +193,7 @@ const ProjectChartsTable = () => { {t("details.start")} {formatDate({ - date: project.startDate, + date: project.startDate || "", format: "dd/MM/yyyy", })} @@ -202,7 +203,10 @@ const ProjectChartsTable = () => { {t("details.end")} - {formatDate({ date: project.endDate, format: "dd/MM/yyyy" })} + {formatDate({ + date: project.endDate || "", + format: "dd/MM/yyyy", + })} @@ -279,7 +283,7 @@ const ProjectChartsTable = () => { })} sx={{ color: "info.dark", textDecoration: "none" }} > - {project.upstreamDemands.length} + {project.upstreamDemands?.length} @@ -313,7 +317,7 @@ const ProjectChartsTable = () => { })} sx={{ color: "info.dark", textDecoration: "none" }} > - {project.discardedDemands.length} + {project.discardedDemands?.length} @@ -328,7 +332,7 @@ const ProjectChartsTable = () => { })} sx={{ color: "info.dark", textDecoration: "none" }} > - {project.unscoredDemands.length} + {project.unscoredDemands?.length} @@ -339,18 +343,18 @@ const ProjectChartsTable = () => { href={`/companies/${companySlug}/demand_blocks`} sx={{ color: "info.dark", textDecoration: "none" }} > - {project.demandBlocks.length} + {project.demandBlocks?.length} {t("details.flowPressure")} - {project.flowPressure.toFixed(2)} + {project.flowPressure?.toFixed(2)} {tDemands("list.demandsTable.averageSpeed", { - numberOfDemandsPerDay: project.averageSpeed.toFixed(2), + numberOfDemandsPerDay: project.averageSpeed?.toFixed(2), })} diff --git a/app/spa/src/components/Projects/ProjectDemandsCharts.tsx b/app/spa/src/components/Projects/ProjectDemandsCharts.tsx index 460208ddb..35258bb74 100644 --- a/app/spa/src/components/Projects/ProjectDemandsCharts.tsx +++ b/app/spa/src/components/Projects/ProjectDemandsCharts.tsx @@ -4,38 +4,32 @@ import { useTranslation } from "react-i18next" import { ChartGridItem } from "../charts/ChartGridItem" import { BarChart } from "../charts/BarChart" -import { LineChart } from "../charts/LineChart" -import { normalizeCfdData } from "../charts/LineChart" -import { ScatterChart } from "../charts/ScatterChart" +import { LineChart, normalizeCfdData } from "../charts/LineChart" import LineChartTooltip from "./../charts/tooltips/LineChartTooltip" import { secondsToDays } from "../../lib/date" import { Project } from "../../modules/project/project.types" import { Grid } from "@mui/material" -import { useContext } from "react" -import { MeContext } from "../../contexts/MeContext" import { ChartAxisData } from "../../modules/charts/charts.types" import { cfdChartData } from "../../lib/charts" -import { useNavigate } from "react-router-dom" import { buildBurnupData } from "../../utils/charts" import ProjectBurnup from "../../pages/Projects/Charts/ProjectBurnup" +import ProjectLeadTime from "../../pages/Projects/Charts/ProjectLeadTime" +import ProjectLeadTimeControlChart from "../../pages/Projects/Charts/ProjectLeadTimeControlChart" type ProjectDemandsChartsProps = { project: Project hoursPerCoordinationStageChartData?: ChartAxisData } +// eslint-disable-next-line complexity const ProjectDemandsCharts = ({ project, hoursPerCoordinationStageChartData, }: ProjectDemandsChartsProps) => { const { t } = useTranslation(["projectChart"]) - const { me } = useContext(MeContext) - const navigate = useNavigate() const projectConsolidationsWeekly = project.projectConsolidationsWeekly const projectConsolidationsLastMonth = project.projectConsolidationsLastMonth - const demandsFinishedWithLeadtime = project.demandsFinishedWithLeadtime - const lastProjectConsolidationsWeekly = - project.lastProjectConsolidationsWeekly + const demandsFlowChartData = project.demandsFlowChartData const leadTimeHistogramData = project.leadTimeHistogramData const leadTimeBreakdownData = project.leadTimeBreakdown @@ -45,26 +39,28 @@ const ProjectDemandsCharts = ({ const operationalRiskChartData = [ { id: t("chartsTab.projectCharts.operational_math_risk_evolution_chart"), - data: projectConsolidationsWeekly.map( - ({ consolidationDate, operationalRisk }) => { - return { - x: consolidationDate, - y: operationalRisk * 100, + data: + projectConsolidationsWeekly?.map( + ({ consolidationDate, operationalRisk }) => { + return { + x: consolidationDate || "", + y: (operationalRisk || 0) * 100, + } } - } - ), + ) || [], }, ] - const projectBugsChartData: BarDatum[] = projectConsolidationsWeekly.map( - ({ bugsOpened, bugsClosed, consolidationDate }) => { - return { - index: consolidationDate, - [t("chartsTab.projectCharts.bugs_openned")]: bugsOpened, - [t("chartsTab.projectCharts.bugs_closed")]: bugsClosed, + const projectBugsChartData: BarDatum[] = + projectConsolidationsWeekly?.map( + ({ bugsOpened, bugsClosed, consolidationDate }) => { + return { + index: consolidationDate, + [t("chartsTab.projectCharts.bugs_opened")]: bugsOpened, + [t("chartsTab.projectCharts.bugs_closed")]: bugsClosed, + } } - } - ) + ) || [] const cfdXaxis = cumulativeFlowChartData?.xAxis || [] const cfdYaxis = cumulativeFlowChartData?.yAxis.reverse() || [] @@ -124,177 +120,123 @@ const ProjectDemandsCharts = ({ project.hoursBurnup ) - const leadTimeP80ChartData = [ - { - id: project.name, - data: projectConsolidationsWeekly.map( - ({ leadTimeP80, consolidationDate }) => { - const leadTimep80InDays = secondsToDays(leadTimeP80) - - return { - x: consolidationDate, - y: leadTimep80InDays, - } - } - ), - }, - ] - const projectQualityChartData = [ { - id: project.name, - data: projectConsolidationsWeekly.map( - ({ consolidationDate, projectQuality }) => ({ - x: consolidationDate, - y: (1 - projectQuality) * 100, - }) - ), - }, - ] - - const leadTimeControlChartData = [ - { - id: t("chartsTab.projectCharts.lead_time_control_label"), + id: project.name || "", data: - demandsFinishedWithLeadtime?.map(({ externalId, leadtime }) => { - const leadTimeInDays = secondsToDays(leadtime) - - return { - x: externalId, - y: leadTimeInDays, - } - }) || [], + projectConsolidationsWeekly?.map( + ({ consolidationDate, projectQuality }) => ({ + x: consolidationDate, + y: (1 - projectQuality) * 100, + }) + ) || [], }, ] - const leadTimeP65InDays = secondsToDays( - lastProjectConsolidationsWeekly?.leadTimeP65 - ) - const leadTimeP80InDays = secondsToDays( - lastProjectConsolidationsWeekly?.leadTimeP80 - ) - const leadTimeP95InDays = secondsToDays( - lastProjectConsolidationsWeekly?.leadTimeP95 - ) - - const leadTimeControlP65Marker = { - value: leadTimeP65InDays, - legend: t("chartsTab.projectCharts.lead_time_control_marker_p65", { - leadTime: leadTimeP65InDays, - }), - } - - const leadTimeControlP80Marker = { - value: leadTimeP80InDays, - legend: t("chartsTab.projectCharts.lead_time_control_marker_p80", { - leadTime: leadTimeP80InDays, - }), - } - - const leadTimeControlP95Marker = { - value: leadTimeP95InDays, - legend: t("chartsTab.projectCharts.lead_time_control_marker_p95", { - leadTime: leadTimeP95InDays, - }), - } - const projectQualityForCodingChartData = [ { id: project.name || "", - data: projectConsolidationsWeekly.map( - ({ consolidationDate, codeNeededBlocksCount }) => ({ - x: consolidationDate, - y: codeNeededBlocksCount, - }) - ), + data: + projectConsolidationsWeekly?.map( + ({ consolidationDate, codeNeededBlocksCount }) => ({ + x: consolidationDate, + y: codeNeededBlocksCount, + }) + ) || [], }, ] const projectQualityForCodingPerDemand = [ { id: project.name || "", - data: projectConsolidationsWeekly.map( - ({ consolidationDate, codeNeededBlocksPerDemand }) => ({ - x: consolidationDate, - y: codeNeededBlocksPerDemand, - }) - ), + data: + projectConsolidationsWeekly?.map( + ({ consolidationDate, codeNeededBlocksPerDemand }) => ({ + x: consolidationDate, + y: codeNeededBlocksPerDemand, + }) + ) || [], }, ] const flowEfficiencyChartData = [ { id: project.name || "", - data: projectConsolidationsWeekly.map( - ({ consolidationDate, flowEfficiency }) => { - return { - x: consolidationDate, - y: flowEfficiency, + data: + projectConsolidationsWeekly?.map( + ({ consolidationDate, flowEfficiency }) => { + return { + x: consolidationDate, + y: flowEfficiency, + } } - } - ), + ) || [], }, ] const hoursPerDemandChartData = [ { id: project.name || "", - data: projectConsolidationsWeekly.map( - ({ consolidationDate, hoursPerDemand }) => { - return { - x: consolidationDate, - y: hoursPerDemand.toFixed(2), + data: + projectConsolidationsWeekly?.map( + ({ consolidationDate, hoursPerDemand }) => { + return { + x: consolidationDate, + y: hoursPerDemand.toFixed(2), + } } - } - ), + ) || [], }, ] - const projectHoursConsummed = projectConsolidationsWeekly.map( - ({ - consolidationDate, - projectThroughputHours, - projectThroughputHoursAdditional, - projectThroughputHoursUpstream, - projectThroughputHoursDownstream, - }) => { - return { - [t("chartsTab.projectCharts.hours_consumed_x_label")]: - consolidationDate, - [t("chartsTab.projectCharts.hours_consumed_upstream")]: - projectThroughputHoursUpstream.toFixed(2), - [t("chartsTab.projectCharts.hours_consumed_downstream")]: - projectThroughputHoursDownstream.toFixed(2), - [t("chartsTab.projectCharts.additional_hours_consumed")]: - projectThroughputHoursAdditional?.toFixed(2) || 0, - [t("chartsTab.projectCharts.hours_consumed_total_throughput")]: - projectThroughputHours.toFixed(2), + const projectHoursConsummed = + projectConsolidationsWeekly?.map( + ({ + consolidationDate, + projectThroughputHours, + projectThroughputHoursAdditional, + projectThroughputHoursUpstream, + projectThroughputHoursDownstream, + }) => { + return { + [t("chartsTab.projectCharts.hours_consumed_x_label")]: + consolidationDate, + [t("chartsTab.projectCharts.hours_consumed_upstream")]: + projectThroughputHoursUpstream.toFixed(2), + [t("chartsTab.projectCharts.hours_consumed_downstream")]: + projectThroughputHoursDownstream.toFixed(2), + [t("chartsTab.projectCharts.additional_hours_consumed")]: + projectThroughputHoursAdditional?.toFixed(2) || 0, + [t("chartsTab.projectCharts.hours_consumed_total_throughput")]: + projectThroughputHours.toFixed(2), + } } - } - ) + ) || [] - const projectConsumedHoursByRoleChartData = projectConsolidationsWeekly.map( - ({ - consolidationDate, - projectThroughputHours, - projectThroughputHoursManagement, - projectThroughputHoursDevelopment, - projectThroughputHoursDesign, - }) => { - return { - period: consolidationDate, - [t("chartsTab.projectCharts.consumed_hours_by_role_design_effort")]: - projectThroughputHoursDesign.toFixed(2), - [t( - "chartsTab.projectCharts.consumed_hours_by_role_development_effort" - )]: projectThroughputHoursDevelopment.toFixed(2), - [t("chartsTab.projectCharts.consumed_hours_by_role_management_effort")]: - projectThroughputHoursManagement.toFixed(2), - [t("chartsTab.projectCharts.consumed_hours_by_role_total_effort")]: - projectThroughputHours.toFixed(2), + const projectConsumedHoursByRoleChartData = + projectConsolidationsWeekly?.map( + ({ + consolidationDate, + projectThroughputHours, + projectThroughputHoursManagement, + projectThroughputHoursDevelopment, + projectThroughputHoursDesign, + }) => { + return { + period: consolidationDate, + [t("chartsTab.projectCharts.consumed_hours_by_role_design_effort")]: + projectThroughputHoursDesign.toFixed(2), + [t( + "chartsTab.projectCharts.consumed_hours_by_role_development_effort" + )]: projectThroughputHoursDevelopment.toFixed(2), + [t( + "chartsTab.projectCharts.consumed_hours_by_role_management_effort" + )]: projectThroughputHoursManagement.toFixed(2), + [t("chartsTab.projectCharts.consumed_hours_by_role_total_effort")]: + projectThroughputHours.toFixed(2), + } } - } - ) + ) || [] const projectConsumedHoursByRoleInMonthChartData = projectConsolidationsLastMonth?.map( @@ -430,43 +372,11 @@ const ProjectDemandsCharts = ({ }} /> - - ( - - ), - }} - /> - - - { - navigate( - `/companies/${me?.currentCompany?.slug}/demands/${props.data.x}` - ) - }} - /> - + + + + diff --git a/app/spa/src/components/ReplenishingProjectsInfo.tsx b/app/spa/src/components/ReplenishingProjectsInfo.tsx index e8d2b5fb6..4d58b8676 100644 --- a/app/spa/src/components/ReplenishingProjectsInfo.tsx +++ b/app/spa/src/components/ReplenishingProjectsInfo.tsx @@ -1,13 +1,13 @@ import { + Collapse, + IconButton, + Link, Table, TableBody, TableCell, TableContainer, TableHead, TableRow as MaterialTableRow, - IconButton, - Collapse, - Link, } from "@mui/material" import { Box } from "@mui/system" import { Fragment, useState } from "react" @@ -63,31 +63,31 @@ const TableRow = ({ project, companySlug }: TableRowProps) => { height: "24px", borderRadius: "50%", backgroundColor: getCustomerHappinessColor( - project.customerHappiness + project.customerHappiness || 0 ), }} /> {project.remainingBacklog} demandas - {project.flowPressurePercentage.toFixed(2)}% - {secondsToReadbleDate(project.leadTimeP80)} + {project.flowPressurePercentage?.toFixed(2)}% + {secondsToReadbleDate(project.leadTimeP80 || 0)} {project.qtyInProgress} demandas {project.startDate} {project.endDate} - {project.monteCarloP80.toFixed(2)} + {project.monteCarloP80?.toFixed(2)} - + - {project.customers.map(({ name, id }) => ( + {project.customers?.map(({ name, id }) => ( {name} ))} - {project.customerHappiness.toFixed(2)} + {project.customerHappiness?.toFixed(2)} - {project.flowPressure.toFixed(2)} + {project.flowPressure?.toFixed(2)} Limite de WiP: {project.maxWorkInProgress} Idade: {project.aging} dias @@ -95,9 +95,9 @@ const TableRow = ({ project, companySlug }: TableRowProps) => { @@ -106,8 +106,8 @@ const TableRow = ({ project, companySlug }: TableRowProps) => { - - {project.products.map(({ name, id }) => ( + + {project.products?.map(({ name, id }) => ( {name} @@ -119,38 +119,38 @@ const TableRow = ({ project, companySlug }: TableRowProps) => { Dados do time: - + Mín: {project.teamMonteCarloWeeksMin} - + Monte Carlo 80%: {project.teamMonteCarloP80} - - Throughputs: ({project.weeklyThroughputs.join(", ")}) + + Throughputs: ({project.weeklyThroughputs?.join(", ")}) - + Máx: {project.teamMonteCarloWeeksMax} - + Desvio padrão:{" "} - {project.teamMonteCarloWeeksStdDev.toFixed(2)} + {project.teamMonteCarloWeeksStdDev?.toFixed(2)} - + Selecionadas: {project.qtySelected} - + Chances da data:{" "} - {(project.teamBasedOddsToDeadline * 100).toFixed(2)}% + {((project.teamBasedOddsToDeadline || 0) * 100).toFixed(2)}% diff --git a/app/spa/src/components/ReplenishingTeamInfo.tsx b/app/spa/src/components/ReplenishingTeamInfo.tsx index 1b47a6dd1..1088b2d88 100644 --- a/app/spa/src/components/ReplenishingTeamInfo.tsx +++ b/app/spa/src/components/ReplenishingTeamInfo.tsx @@ -1,4 +1,4 @@ -import { Typography, Grid, Divider } from "@mui/material" +import { Divider, Grid, Typography } from "@mui/material" import { Box } from "@mui/system" import { Fragment } from "react" import { Project } from "../modules/project/project.types" @@ -22,7 +22,7 @@ type ReplenishmentTeamInfoProps = { } export const getWipLimits = (projects: Project[]): number[] => - projects.map(({ maxWorkInProgress }) => maxWorkInProgress) + projects.map(({ maxWorkInProgress }) => maxWorkInProgress || 0) export const isTeamWipLimitSurpassed = ( projects: Project[], diff --git a/app/spa/src/components/TeamMemberDashboardTables.tsx b/app/spa/src/components/TeamMemberDashboardTables.tsx index 879fe0abf..336a19636 100644 --- a/app/spa/src/components/TeamMemberDashboardTables.tsx +++ b/app/spa/src/components/TeamMemberDashboardTables.tsx @@ -158,8 +158,8 @@ const TeamMemberDashboardTables = ({ > {project.name} , - , - , + , + , `${((project.currentRiskToDeadline || 0) * 100).toFixed(2)}%`, `${((project.quality || 0) * 100).toFixed(2)}%`, `${secondsToDays(project.leadTimeP80)} ${t("dashboard.days")}`, diff --git a/app/spa/src/components/charts/ScatterChart.tsx b/app/spa/src/components/charts/ScatterChart.tsx index fc022e00a..8b571e3f3 100644 --- a/app/spa/src/components/charts/ScatterChart.tsx +++ b/app/spa/src/components/charts/ScatterChart.tsx @@ -52,7 +52,7 @@ export const ScatterChart = ({ const chartData = Array.isArray(data) ? data : axisDataToScatter(data, "Demands") - const bottomAxisTicks = chartData[0].data.map((item) => item.x) + const bottomAxisTicks = chartData[0]?.data.map((item) => item.x) const chartMarkers: NivoMarker[] = markers?.map(({ value, legend }) => ({ axis: "y", @@ -122,9 +122,9 @@ export const ScatterChart = ({ markers={chartMarkers} tooltip={({ node }) => ( - {node.data.x} + {node?.data.x}
- Lead time: {node.data.y} {t("days")} + Lead time: {node?.data.y} {t("days")}
)} enableGridX={false} diff --git a/app/spa/src/locales/en/projectChart.json b/app/spa/src/locales/en/projectChart.json index e6e2125f7..6023598f7 100644 --- a/app/spa/src/locales/en/projectChart.json +++ b/app/spa/src/locales/en/projectChart.json @@ -24,7 +24,7 @@ "operationalRisk": "Operational Risk (%)", "bugs_chart": "Bugs", "bugs_y_label": "Bugs", - "bugs_openned": "Bugs Openned", + "bugs_opened": "Bugs Openned", "bugs_closed": "Bugs Closed", "demandsBurnupChart": "Demands Burnup for {{projectName}}", "demandsBurnupYLabel": "Demands", diff --git a/app/spa/src/locales/pt/projectChart.json b/app/spa/src/locales/pt/projectChart.json index d92cc5d1e..f7068d362 100644 --- a/app/spa/src/locales/pt/projectChart.json +++ b/app/spa/src/locales/pt/projectChart.json @@ -24,7 +24,7 @@ "operationalRisk": "Risco Operacional (%)", "bugs_chart": "Bugs", "bugs_y_label": "Bugs", - "bugs_openned": "Bugs Abertos", + "bugs_opened": "Bugs Abertos", "bugs_closed": "Bugs Fechados", "demandsBurnupChart": "Burn Up de Demandas para {{projectName}}", "demandsBurnupYLabel": "Demandas", diff --git a/app/spa/src/modules/project/components/ProjectsTable.tsx b/app/spa/src/modules/project/components/ProjectsTable.tsx index dab0408fb..a9c3ea5cf 100644 --- a/app/spa/src/modules/project/components/ProjectsTable.tsx +++ b/app/spa/src/modules/project/components/ProjectsTable.tsx @@ -2,12 +2,12 @@ import { gql, useQuery } from "@apollo/client" import { useTranslation } from "react-i18next" import { Backdrop, - CircularProgress, Box, + CircularProgress, LinearProgress, - styled, linearProgressClasses, Link, + styled, Typography, } from "@mui/material" @@ -124,8 +124,8 @@ const ProjectsTable = ({ projectsFilters }: ProjectsTableProps) => { {project.team?.name} , project.status, - , - , + , + , `${project.numberOfDemands} ${t("projectsTable.row_demands")}`, `${project.remainingDays} ${t("projectsTable.row_days")}`, `${project.numberOfDemandsDelivered} ${t( @@ -145,14 +145,14 @@ const ProjectsTable = ({ projectsFilters }: ProjectsTableProps) => { > - {`${project.consumedHours.toFixed(2)}h ${t( + {`${project.consumedHours?.toFixed(2)}h ${t( "projectsTable.row_consumed" )}`} @@ -168,7 +168,7 @@ const ProjectsTable = ({ projectsFilters }: ProjectsTableProps) => { ], collapseBody: [ project.customersNames, - formatCurrency(project.value), + formatCurrency(project.value || 0), project.maxWorkInProgress, ], }, diff --git a/app/spa/src/modules/project/project.types.ts b/app/spa/src/modules/project/project.types.ts index aa3f5639d..2424173e9 100644 --- a/app/spa/src/modules/project/project.types.ts +++ b/app/spa/src/modules/project/project.types.ts @@ -13,87 +13,86 @@ import { import { Team } from "../team/team.types" export type Project = { - company?: Company - customers: Customer[] - products: Product[] - latestDeliveries: Demand[] - id: string - name: string - customersNames: string + company?: Company + customers?: Customer[] + products?: Product[] + latestDeliveries?: Demand[] + name?: string + customersNames?: string quality?: number - team: Team - status: string - qtyHours: number - consumedHours: number - percentageHoursDelivered: number - remainingWeeks: number - remainingBacklog: number - backlogCountFor: number - flowPressure: number - flowPressurePercentage: number - pastWeeks: number - remainingWork: number - leadTimeP65: number - leadTimeP80: number - leadTimeP95: number - qtySelected: number - qtyInProgress: number - monteCarloP80: number - maxWorkInProgress: number - lastWeekThroughput: number - weeklyThroughputs: number[] - modeWeeklyTroughputs: number - stdDevWeeklyTroughputs: number - currentMonteCarloWeeksMin: number - currentMonteCarloWeeksMax: number - currentMonteCarloWeeksStdDev: number - currentWeeksByLittleLaw: number - teamMonteCarloP80: number - teamMonteCarloWeeksMin: number - teamMonteCarloWeeksMax: number - teamMonteCarloWeeksStdDev: number - teamBasedOddsToDeadline: number - customerHappiness: number - startDate: string - endDate: string - aging: number - firstDeadline: string - daysDifferenceBetweenFirstAndLastDeadlines: number - deadlinesChangeCount: number - currentCost: number - totalHoursConsumed: number - averageSpeed: number - averageDemandAging: number - numberOfDemands: number - numberOfDemandsDelivered: number - numberOfRemainingBacklog: number - totalThroughput: number - failureLoad: number - discoveredScope: number - scope: number - initialScope: number - projectConsolidations: ProjectConsolidation[] - projectConsolidationsWeekly: ProjectConsolidation[] - projectConsolidationsLastMonth: ProjectConsolidation[] + team?: Team + status?: string + qtyHours?: number + consumedHours?: number + percentageHoursDelivered?: number + remainingWeeks?: number + remainingBacklog?: number + backlogCountFor?: number + flowPressure?: number + flowPressurePercentage?: number + pastWeeks?: number + remainingWork?: number + leadTimeP65?: number + leadTimeP80?: number + leadTimeP95?: number + qtySelected?: number + qtyInProgress?: number + monteCarloP80?: number + maxWorkInProgress?: number + lastWeekThroughput?: number + weeklyThroughputs?: number[] + modeWeeklyTroughputs?: number + stdDevWeeklyTroughputs?: number + currentMonteCarloWeeksMin?: number + currentMonteCarloWeeksMax?: number + currentMonteCarloWeeksStdDev?: number + currentWeeksByLittleLaw?: number + teamMonteCarloP80?: number + teamMonteCarloWeeksMin?: number + teamMonteCarloWeeksMax?: number + teamMonteCarloWeeksStdDev?: number + teamBasedOddsToDeadline?: number + customerHappiness?: number + startDate?: string + endDate?: string + aging?: number + firstDeadline?: string + daysDifferenceBetweenFirstAndLastDeadlines?: number + deadlinesChangeCount?: number + currentCost?: number + totalHoursConsumed?: number + averageSpeed?: number + averageDemandAging?: number + numberOfDemands?: number + numberOfDemandsDelivered?: number + numberOfRemainingBacklog?: number + totalThroughput?: number + failureLoad?: number + discoveredScope?: number + scope?: number + initialScope?: number + projectConsolidations?: ProjectConsolidation[] + projectConsolidationsWeekly?: ProjectConsolidation[] + projectConsolidationsLastMonth?: ProjectConsolidation[] lastProjectConsolidationsWeekly?: ProjectConsolidation currentRiskToDeadline?: number - remainingDays: number - currentTeamBasedRisk: number - running: boolean - upstreamDemands: Demand[] - discardedDemands: Demand[] - unscoredDemands: Demand[] - demandBlocks: Demand[] - demandsFinishedWithLeadtime: Demand[] - numberOfDownstreamDemands: number - averageQueueTime: number - averageTouchTime: number - value: number + remainingDays?: number + currentTeamBasedRisk?: number + running?: boolean + upstreamDemands?: Demand[] + discardedDemands?: Demand[] + unscoredDemands?: Demand[] + demandBlocks?: Demand[] + demandsFinishedWithLeadtime?: Demand[] + numberOfDownstreamDemands?: number + averageQueueTime?: number + averageTouchTime?: number + value?: number projectMembers: { - demandsCount: number - memberName: string + demandsCount?: number + memberName?: string }[] hoursPerStageChartData: ChartAxisData leadTimeBreakdown: NumberChartData @@ -112,13 +111,13 @@ export type Project = { } export type Burnup = { - idealBurn: number[] - scope: number[] - currentBurn: number[] + idealBurn?: number[] + scope?: number[] + currentBurn?: number[] xAxis: string[] } export type ProjectsList = { - totalCount: number + totalCount?: number projects: Project[] } diff --git a/app/spa/src/pages/Projects/Charts/ProjectLeadTime.tsx b/app/spa/src/pages/Projects/Charts/ProjectLeadTime.tsx new file mode 100644 index 000000000..fb41f8fa3 --- /dev/null +++ b/app/spa/src/pages/Projects/Charts/ProjectLeadTime.tsx @@ -0,0 +1,51 @@ +import { LineChart } from "../../../components/charts/LineChart" +import { SliceTooltipProps } from "@nivo/line" +import LineChartTooltip from "../../../components/charts/tooltips/LineChartTooltip" +import { ChartGridItem } from "../../../components/charts/ChartGridItem" +import { secondsToDays } from "../../../lib/date" +import { Project } from "../../../modules/project/project.types" +import { useTranslation } from "react-i18next" + +const ProjectLeadTime = ({ project }: ProjectLeadTimeProps) => { + const { t } = useTranslation(["projectChart"]) + + const projectConsolidationsWeekly = project.projectConsolidationsWeekly + const leadTimeP80ChartData = [ + { + id: project.name || "", + data: + projectConsolidationsWeekly?.map( + ({ leadTimeP80, consolidationDate }) => { + const leadTimeP80InDays = secondsToDays(leadTimeP80) + + return { + x: consolidationDate, + y: leadTimeP80InDays, + } + } + ) || [], + }, + ] + + return ( + + ( + + ), + }} + /> + + ) +} + +type ProjectLeadTimeProps = { + project: Project +} + +export default ProjectLeadTime diff --git a/app/spa/src/pages/Projects/Charts/ProjectLeadTimeControlChart.tsx b/app/spa/src/pages/Projects/Charts/ProjectLeadTimeControlChart.tsx new file mode 100644 index 000000000..03fddc4a4 --- /dev/null +++ b/app/spa/src/pages/Projects/Charts/ProjectLeadTimeControlChart.tsx @@ -0,0 +1,93 @@ +import { ScatterChart } from "../../../components/charts/ScatterChart" +import { ChartGridItem } from "../../../components/charts/ChartGridItem" +import { secondsToDays } from "../../../lib/date" +import { useTranslation } from "react-i18next" +import { useNavigate } from "react-router-dom" +import { useContext } from "react" +import { MeContext } from "../../../contexts/MeContext" +import { Project } from "../../../modules/project/project.types" + +const ProjectLeadTimeControlChart = ({ + project, +}: ProjectLeadTimeControlChartProps) => { + const { t } = useTranslation(["projectChart"]) + const navigate = useNavigate() + const { me } = useContext(MeContext) + + const demandsFinishedWithLeadtime = project.demandsFinishedWithLeadtime + const leadTimeControlChartData = [ + { + id: t("chartsTab.projectCharts.lead_time_control_label"), + data: + demandsFinishedWithLeadtime?.map(({ externalId, leadtime }) => { + const leadTimeInDays = secondsToDays(leadtime) + + return { + x: externalId, + y: leadTimeInDays, + } + }) || [], + }, + ] + + const lastProjectConsolidationsWeekly = + project.lastProjectConsolidationsWeekly + const leadTimeP65InDays = secondsToDays( + lastProjectConsolidationsWeekly?.leadTimeP65 + ) + const leadTimeP80InDays = secondsToDays( + lastProjectConsolidationsWeekly?.leadTimeP80 + ) + const leadTimeP95InDays = secondsToDays( + lastProjectConsolidationsWeekly?.leadTimeP95 + ) + + const leadTimeControlP65Marker = { + value: leadTimeP65InDays, + legend: t("chartsTab.projectCharts.lead_time_control_marker_p65", { + leadTime: leadTimeP65InDays, + }), + } + + const leadTimeControlP80Marker = { + value: leadTimeP80InDays, + legend: t("chartsTab.projectCharts.lead_time_control_marker_p80", { + leadTime: leadTimeP80InDays, + }), + } + + const leadTimeControlP95Marker = { + value: leadTimeP95InDays, + legend: t("chartsTab.projectCharts.lead_time_control_marker_p95", { + leadTime: leadTimeP95InDays, + }), + } + + return ( + + { + navigate( + `/companies/${me?.currentCompany?.slug}/demands/${props.data.x}` + ) + }} + /> + + ) +} + +type ProjectLeadTimeControlChartProps = { + project: Project +} + +export default ProjectLeadTimeControlChart diff --git a/app/spa/src/pages/Projects/DemandsCharts.tsx b/app/spa/src/pages/Projects/DemandsCharts.tsx index 6aa22a43b..b96b2ea72 100644 --- a/app/spa/src/pages/Projects/DemandsCharts.tsx +++ b/app/spa/src/pages/Projects/DemandsCharts.tsx @@ -142,8 +142,8 @@ const PROJECT_CHART_QUERY = gql` ` type ProjectChartResult = { - project: Project - hoursPerCoordinationStageChartData: Pick + project?: Project + hoursPerCoordinationStageChartData?: Pick } type ProjectChartDTO = ProjectChartResult | undefined @@ -166,7 +166,7 @@ const DemandsCharts = () => { const project = data?.project const hoursPerCoordinationStageChartData = - data?.hoursPerCoordinationStageChartData.hoursPerStageChartData + data?.hoursPerCoordinationStageChartData?.hoursPerStageChartData return project ? ( { } ) - const lastProjectConsolidation = data?.project.projectConsolidations.pop() + const lastProjectConsolidation = data?.project.projectConsolidations?.pop() const currentLeadTime = [ { diff --git a/app/spa/src/pages/Projects/RiskDrill.tsx b/app/spa/src/pages/Projects/RiskDrill.tsx index b914b6c53..3bbb9d476 100644 --- a/app/spa/src/pages/Projects/RiskDrill.tsx +++ b/app/spa/src/pages/Projects/RiskDrill.tsx @@ -1,8 +1,8 @@ import { gql, useQuery } from "@apollo/client" import { useParams } from "react-router-dom" import { - ProjectPage, PROJECT_STANDARD_FRAGMENT, + ProjectPage, } from "../../components/Projects/ProjectPage" import TicketGroup from "../../components/TicketGroup" import { Project } from "../../modules/project/project.types" @@ -107,12 +107,12 @@ export const RiskDrill = () => { }, { title: "Percentil 80", - value: data?.project.monteCarloP80.toFixed(2), + value: data?.project.monteCarloP80?.toFixed(2), unity: "semanas", }, { title: "Desvio padrão", - value: data?.project.currentMonteCarloWeeksStdDev.toFixed(2), + value: data?.project.currentMonteCarloWeeksStdDev?.toFixed(2), unity: "semanas", }, ] @@ -130,12 +130,12 @@ export const RiskDrill = () => { }, { title: "Percentil 80", - value: data?.project.teamMonteCarloP80.toFixed(2), + value: data?.project.teamMonteCarloP80?.toFixed(2), unity: "semanas", }, { title: "Desvio padrão", - value: data?.project.teamMonteCarloWeeksStdDev.toFixed(2), + value: data?.project.teamMonteCarloWeeksStdDev?.toFixed(2), unity: "semanas", }, ] diff --git a/app/spa/src/pages/Users/ManagerDashboard.tsx b/app/spa/src/pages/Users/ManagerDashboard.tsx index 61e33af83..18e0901fd 100644 --- a/app/spa/src/pages/Users/ManagerDashboard.tsx +++ b/app/spa/src/pages/Users/ManagerDashboard.tsx @@ -20,6 +20,8 @@ import ActiveContractsHoursTicket from "../../modules/contracts/ActiveContractsH import ProjectBurnup from "../Projects/Charts/ProjectBurnup" import { PROJECT_STANDARD_FRAGMENT } from "../../components/Projects/ProjectPage" import ProjectHoursBurnup from "../Projects/Charts/ProjectHoursBurnup" +import ProjectLeadTime from "../Projects/Charts/ProjectLeadTime" +import ProjectLeadTimeControlChart from "../Projects/Charts/ProjectLeadTimeControlChart" const ManagerDashboard = () => { const { me } = useContext(MeContext) @@ -81,14 +83,12 @@ const ManagerDashboard = () => { - - - - - - - - + + + + + + ) : ( @@ -109,44 +109,64 @@ const MANAGER_DASHBOARD_QUERY = gql` me { projects(name: $name) { ...ProjectStandardFragment - + ...ProjectChartsFragment totalActiveContractsHours consumedActiveContractsHours remainingActiveContractsHours - - demandsBurnup { - scope - xAxis - idealBurn - currentBurn - } - - hoursBurnup { - scope - xAxis - idealBurn - currentBurn - } } projectsActive { ...ProjectStandardFragment + ...ProjectChartsFragment + } + } + } - demandsBurnup { - scope - xAxis - idealBurn - currentBurn - } + fragment ProjectChartsFragment on Project { + demandsBurnup { + scope + xAxis + idealBurn + currentBurn + } - hoursBurnup { - scope - xAxis - idealBurn - currentBurn - } - } + hoursBurnup { + scope + xAxis + idealBurn + currentBurn + } + demandsFinishedWithLeadtime { + id + leadtime + externalId + } + lastProjectConsolidationsWeekly { + leadTimeP65 + leadTimeP80 + leadTimeP95 + } + projectConsolidationsWeekly { + leadTimeP80 + projectQuality + consolidationDate + operationalRisk + codeNeededBlocksCount + codeNeededBlocksPerDemand + flowEfficiency + hoursPerDemand + projectThroughput + projectThroughputHours + projectThroughputHoursAdditional + bugsOpened + bugsClosed + projectThroughputHoursManagement + projectThroughputHoursDevelopment + projectThroughputHoursDesign + projectThroughputHoursUpstream + projectThroughputHoursDownstream } } + ${PROJECT_STANDARD_FRAGMENT} ` diff --git a/app/spa/src/utils/charts.ts b/app/spa/src/utils/charts.ts index f253e38fe..11cc8c034 100644 --- a/app/spa/src/utils/charts.ts +++ b/app/spa/src/utils/charts.ts @@ -9,7 +9,7 @@ export const buildBurnupData = ( { id: scopeLabel, data: - data?.scope.map((scope, index) => ({ + data?.scope?.map((scope, index) => ({ x: data.xAxis?.[index], y: scope, })) || [], @@ -17,7 +17,7 @@ export const buildBurnupData = ( { id: idealLabel, data: - data?.idealBurn.map((idealScope, index) => ({ + data?.idealBurn?.map((idealScope, index) => ({ x: data.xAxis?.[index], y: idealScope.toFixed(2), })) || [], @@ -25,7 +25,7 @@ export const buildBurnupData = ( { id: deliveredLabel, data: - data?.currentBurn.map((projectThroughput, index) => ({ + data?.currentBurn?.map((projectThroughput, index) => ({ x: data.xAxis?.[index], y: projectThroughput, })) || [],