IO-1911 Timetickets Scoreboard.

This commit is contained in:
Patrick Fic
2022-06-06 09:54:02 -07:00
parent 3feb1a3887
commit 674c06665c
12 changed files with 674 additions and 132 deletions

View File

@@ -40827,6 +40827,69 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobs</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>lastmonth</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>lastweek</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>monthlytarget</name>
<definition_loaded>false</definition_loaded>
@@ -40848,6 +40911,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>productivestatistics</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>productivetimeticketsoverdate</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>targets</name>
<definition_loaded>false</definition_loaded>
@@ -40869,6 +40974,69 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>thismonth</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>thisweek</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>timetickets</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>todateactual</name>
<definition_loaded>false</definition_loaded>
@@ -40890,6 +41058,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>totaloverperiod</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>weeklyactual</name>
<definition_loaded>false</definition_loaded>

View File

@@ -49,11 +49,6 @@ export const ListOfDaysInCurrentMonth = () => {
};
export const ListDaysBetween = ({ start, end }) => {
console.log(
"🚀 ~ file: scoreboard-targets-table.util.js ~ line 52 ~ start, end",
start,
end
);
const days = [];
const dateStart = moment(start);
const dateEnd = moment(end);

View File

@@ -1,14 +1,12 @@
import { Card } from "antd";
import moment from "moment";
import React, { useMemo } from "react";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import {
Area,
Bar,
CartesianGrid,
ComposedChart,
Legend,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
@@ -16,9 +14,7 @@ import {
} from "recharts";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
import _ from "lodash";
import TimeTicketsDatesSelector from "../ticket-tickets-dates-selector/time-tickets-dates-selector.component";
const graphProps = {
strokeWidth: 3,
};
@@ -35,86 +31,16 @@ export default connect(
mapDispatchToProps
)(ScoreboardTicketsBar);
export function ScoreboardTicketsBar({ start, end, timetickets, bodyshop }) {
console.log(
"🚀 ~ file: scoreboard-timetickets.bar.component.jsx ~ line 39 ~ start, end,",
start,
end
);
const listOfBusDays = Utils.ListOfDaysInCurrentMonth();
const data = useMemo(() => {
const ticketsGroupedByDate = _.groupBy(timetickets, "date");
const listOfDays = Utils.ListDaysBetween({ start, end });
console.log(
"🚀 ~ file: scoreboard-timetickets.bar.component.jsx ~ line 45 ~ listOfDays",
listOfDays
);
console.log(
"🚀 ~ file: scoreboard-timetickets.bar.component.jsx ~ line 43 ~ groupedByDate",
ticketsGroupedByDate
);
const ret = [];
listOfDays.forEach((day) => {
const r = {
date: day,
total: 0,
};
});
}, [timetickets]);
// const data = listOfBusDays.reduce((acc, val) => {
// //Sum up the current day.
// const groupedbyDate = _.groupBy(data, "date");
// let dayhrs;
// if (!!sbEntriesByDate[val]) {
// dayhrs = sbEntriesByDate[val].reduce(
// (dayAcc, dayVal) => {
// return {
// bodyhrs: dayAcc.bodyhrs + dayVal.bodyhrs,
// painthrs: dayAcc.painthrs + dayVal.painthrs,
// };
// },
// { bodyhrs: 0, painthrs: 0 }
// );
// } else {
// dayhrs = {
// bodyhrs: 0,
// painthrs: 0,
// };
// }
// const theValue = {
// date: moment(val).format("D ddd"),
// paintHrs: _.round(dayhrs.painthrs, 1),
// bodyHrs: _.round(dayhrs.bodyhrs, 1),
// accTargetHrs: _.round(
// Utils.AsOfDateTargetHours(
// bodyshop.scoreboard_target.dailyBodyTarget +
// bodyshop.scoreboard_target.dailyPaintTarget,
// val
// ),
// 1
// ),
// accHrs: _.round(
// acc.length > 0
// ? acc[acc.length - 1].accHrs + dayhrs.painthrs + dayhrs.bodyhrs
// : dayhrs.painthrs + dayhrs.bodyhrs,
// 1
// ),
// };
// return [...acc, theValue];
// }, []);
export function ScoreboardTicketsBar({ data, bodyshop }) {
const { t } = useTranslation();
return (
<Card>
<Card
title={t("scoreboard.labels.productivetimeticketsoverdate")}
extra={<TimeTicketsDatesSelector />}
>
<ResponsiveContainer width="100%" height={475}>
<ComposedChart
data={data}
data={data.chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
@@ -122,34 +48,32 @@ export function ScoreboardTicketsBar({ start, end, timetickets, bodyshop }) {
<YAxis strokeWidth={graphProps.strokeWidth} />
<Tooltip />
<Legend />
<Area
{/* <Area
type="monotone"
name="Accumulated Hours"
dataKey="accHrs"
fill="lightgreen"
stroke="green"
/>
<Bar
name="Body Hours"
dataKey="bodyHrs"
stackId="day"
barSize={20}
fill="darkblue"
/>
<Bar
name="Paint Hours"
dataKey="paintHrs"
stackId="day"
barSize={20}
fill="darkred"
/>
<Line
/> */}
{data &&
data.employees.map((e, idx) => (
<Bar
key={`${e}productive`}
name={e}
dataKey={`employees.${e}.productive`}
stackId="productive"
// barSize={20}
fill={data.colors[idx]}
/>
))}
{/* <Line
name="Target Hours"
type="monotone"
dataKey="accTargetHrs"
stroke="#ff7300"
strokeWidth={graphProps.strokeWidth}
/>
/> */}
</ComposedChart>
</ResponsiveContainer>
</Card>

View File

@@ -1,12 +1,16 @@
import React from "react";
import { useLocation } from "react-router-dom";
import queryString from "query-string";
import moment from "moment";
import { useQuery } from "@apollo/client";
import { Col, Row } from "antd";
import _ from "lodash";
import moment from "moment";
import queryString from "query-string";
import React, { useMemo } from "react";
import { useLocation } from "react-router-dom";
import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries";
import AlertComponent from "../alert/alert.component";
import TimeTicketsDatesSelector from "../../components/ticket-tickets-dates-selector/time-tickets-dates-selector.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
import ScoreboardTicketsBar from "./scoreboard-timetickets.bar.component";
import ScoreboardTicketsStats from "./scoreboard-timetickets.stats.component";
export default function ScoreboardTimeTickets() {
const searchParams = queryString.parse(useLocation().search);
@@ -15,25 +19,230 @@ export default function ScoreboardTimeTickets() {
? moment(start)
: moment().startOf("week").subtract(7, "days");
const endDate = end ? moment(end) : moment().endOf("week");
const fixedPeriods = useMemo(() => {
const endOfThisMonth = moment().endOf("month");
const startofthisMonth = moment().startOf("month");
const endOfLastmonth = moment().subtract(1, "month").endOf("month");
const startOfLastmonth = moment().subtract(1, "month").startOf("month");
const endOfThisWeek = moment().endOf("week");
const startOfThisWeek = moment().startOf("week");
const endOfLastWeek = moment().subtract(1, "week").endOf("week");
const startOfLastWeek = moment().subtract(1, "week").startOf("week");
const allDates = [
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
];
const start = moment.min(allDates);
const end = moment.max(allDates);
return {
start,
end,
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
};
}, []);
const { loading, error, data } = useQuery(QUERY_TIME_TICKETS_IN_RANGE, {
variables: {
start: startDate,
end: endDate,
start: startDate.format("YYYY-MM-DD"),
end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
pollInterval: 60000,
skip: !fixedPeriods,
});
if (error) return <AlertComponent message={error.message} type="error" />;
const calculatedData = useMemo(() => {
if (!data) return [];
const ret = {
totalThisWeek: 0,
totalLastWeek: 0,
totalThisMonth: 0,
totalLastMonth: 0,
totalOverPeriod: 0,
employees: {},
};
data.fixedperiod.forEach((ticket) => {
const ticketDate = moment(ticket.date);
if (!ret.employees[ticket.employee.employee_number]) {
ret.employees[ticket.employee.employee_number] = {
totalThisWeek: 0,
totalLastWeek: 0,
totalThisMonth: 0,
totalLastMonth: 0,
totalOverPeriod: 0,
};
}
if (
ticketDate.isBetween(
fixedPeriods.startOfThisWeek,
fixedPeriods.endOfThisWeek,
undefined,
"[]"
)
) {
ret.totalThisWeek = ret.totalThisWeek + ticket.productivehrs;
ret.employees[ticket.employee.employee_number].totalThisWeek =
ret.employees[ticket.employee.employee_number].totalThisWeek +
ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastWeek,
fixedPeriods.endOfLastWeek,
undefined,
"[]"
)
) {
ret.totalLastWeek = ret.totalLastWeek + ticket.productivehrs;
ret.employees[ticket.employee.employee_number].totalLastWeek =
ret.employees[ticket.employee.employee_number].totalLastWeek +
ticket.productivehrs;
}
if (
ticketDate.isBetween(
fixedPeriods.startofthisMonth,
fixedPeriods.endOfThisMonth,
undefined,
"[]"
)
) {
ret.totalThisMonth = ret.totalThisMonth + ticket.productivehrs;
ret.employees[ticket.employee.employee_number].totalThisMonth =
ret.employees[ticket.employee.employee_number].totalThisMonth +
ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastmonth,
fixedPeriods.endOfLastmonth,
undefined,
"[]"
)
) {
ret.totalLastMonth = ret.totalLastMonth + ticket.productivehrs;
ret.employees[ticket.employee.employee_number].totalLastMonth =
ret.employees[ticket.employee.employee_number].totalLastMonth +
ticket.productivehrs;
}
});
const ticketsGroupedByDate = _.groupBy(data.timetickets, "date");
const listOfDays = Utils.ListDaysBetween({
start: startDate,
end: endDate,
});
const employees = [];
const ret2 = [];
let totals = {
totalproductive: 0,
totalactual: 0,
employees: {},
};
listOfDays.forEach((day) => {
const r = {
date: moment(day).format("MM/DD"),
actualtotal: 0,
productivetotal: 0,
employees: {},
};
if (ticketsGroupedByDate[day]) {
ticketsGroupedByDate[day].forEach((ticket) => {
r.actualtotal = r.actualtotal + ticket.actualhrs;
r.productivetotal = r.productivetotal + ticket.productivehrs;
totals.totalactual = totals.totalactual + ticket.actualhrs;
totals.totalproductive =
totals.totalproductive + ticket.productivehrs;
employees.push(ticket.employee.employee_number);
//Add to table data.
ret.employees[ticket.employee.employee_number].totalOverPeriod =
ret.employees[ticket.employee.employee_number].totalOverPeriod +
ticket.productivehrs;
if (!totals.employees[ticket.employee.employee_number])
totals.employees[ticket.employee.employee_number] = {
totalactual: 0,
totalproductive: 0,
};
if (!r.employees[ticket.employee.employee_number])
r.employees[ticket.employee.employee_number] = {
actual: 0,
productive: 0,
};
//Add to totals.
totals.employees[ticket.employee.employee_number].totalproductive =
totals.employees[ticket.employee.employee_number].totalproductive +
ticket.productivehrs;
totals.employees[ticket.employee.employee_number].totalactual =
totals.employees[ticket.employee.employee_number].totalactual +
ticket.actualhrs;
//Add to dailys.
r.employees[ticket.employee.employee_number].productive =
r.employees[ticket.employee.employee_number].productive +
ticket.productivehrs;
r.employees[ticket.employee.employee_number].actual =
r.employees[ticket.employee.employee_number].actual +
ticket.actualhrs;
});
}
ret2.push(r);
});
return {
fixed: ret,
timeperiod: {
totals,
chartData: ret2,
employees: _.uniq(employees),
colors: getColorArray(employees.length),
},
};
}, [fixedPeriods, data, startDate, endDate]);
if (error) return <AlertComponent message={error.message} type="error" />;
if (loading) return <LoadingSpinner />;
return (
<div>
<TimeTicketsDatesSelector />
<ScoreboardTicketsBar
start={startDate}
end={endDate}
timetickets={data ? data.timetickets : []}
/>
</div>
<Row gutter={[16, 16]}>
<Col span={24}>
<ScoreboardTicketsStats data={calculatedData.fixed} />
</Col>
<Col span={24}>
<ScoreboardTicketsBar
start={startDate}
end={endDate}
data={calculatedData.timeperiod}
/>
</Col>
</Row>
);
}
@@ -43,3 +252,50 @@ export default function ScoreboardTimeTickets() {
//Hours produced in last 7 days
//Hours produced for time period by day
//Hours produced by employee by day for time period.
function getColorArray(num) {
return [
"#3366cc",
"#dc3912",
"#ff9900",
"#109618",
"#990099",
"#0099c6",
"#dd4477",
"#66aa00",
"#b82e2e",
"#316395",
"#3366cc",
"#994499",
"#22aa99",
"#aaaa11",
"#6633cc",
"#e67300",
"#8b0707",
"#651067",
"#329262",
"#5574a6",
"#3b3eac",
"#b77322",
"#16d620",
"#b91383",
"#f4359e",
"#9c5935",
"#a9c413",
"#2a778d",
"#668d1c",
"#bea413",
"#0c5922",
"#743411",
];
// var result = [];
// for (var i = 0; i < num; i += 1) {
// var letters = "0123456789ABCDEF".split("");
// var color = "#";
// for (var j = 0; j < 6; j += 1) {
// color += letters[Math.floor(Math.random() * 16)];
// }
// result.push(color);
// }
// return result;
}

View File

@@ -0,0 +1,101 @@
import { Card, Col, Row, Space, Statistic, Table } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ScoreboardTicketsStats);
export function ScoreboardTicketsStats({ data, bodyshop }) {
const { t } = useTranslation();
const columns = [
{
title: t("employees.fields.employee_number"),
dataIndex: "employee_number",
key: "employee_number",
sorter: (a, b) => a.employee_number - b.employee_number,
},
{
title: t("scoreboard.labels.thisweek"),
dataIndex: "totalThisWeek",
key: "totalThisWeek",
sorter: (a, b) => a.totalThisWeek - b.totalThisWeek,
},
{
title: t("scoreboard.labels.lastweek"),
dataIndex: "totalLastWeek",
key: "totalLastWeek",
sorter: (a, b) => a.totalLastWeek - b.totalLastWeek,
},
{
title: t("scoreboard.labels.thismonth"),
dataIndex: "totalThisMonth",
key: "totalThisMonth",
sorter: (a, b) => a.totalThisMonth - b.totalThisMonth,
},
{
title: t("scoreboard.labels.lastmonth"),
dataIndex: "totalLastMonth",
key: "totalLastMonth",
sorter: (a, b) => a.totalLastMonth - b.totalLastMonth,
},
{
title: t("scoreboard.labels.totaloverperiod"),
dataIndex: "totalOverPeriod",
key: "totalOverPeriod",
sorter: (a, b) => a.totalOverPeriod - b.totalOverPeriod,
},
];
const tableData = data
? Object.keys(data.employees).map((key) => {
return { employee_number: key, ...data.employees[key] };
})
: [];
return (
<Card title={t("scoreboard.labels.productivestatistics")}>
<Row gutter={[16, 16]}>
<Col md={24} lg={6}>
<Space wrap>
<Statistic
title={t("scoreboard.labels.lastweek")}
value={data.totalLastWeek.toFixed(1)}
/>
<Statistic
title={t("scoreboard.labels.thisweek")}
value={data.totalThisWeek.toFixed(1)}
/>
<Statistic
title={t("scoreboard.labels.lastmonth")}
value={data.totalLastMonth.toFixed(1)}
/>
<Statistic
title={t("scoreboard.labels.thismonth")}
value={data.totalThisMonth.toFixed(1)}
/>
</Space>
</Col>
<Col md={24} lg={18}>
<Table
columns={columns}
dataSource={tableData}
id="employee_number"
/>
</Col>
</Row>
</Card>
);
}

View File

@@ -17,6 +17,7 @@ export default function TimeTicketsDatesSelector() {
if (!!start && !!end) {
history.push({
search: queryString.stringify({
...searchParams,
start: start.format("YYYY-MM-DD"),
end: end.format("YYYY-MM-DD"),
}),
@@ -25,6 +26,7 @@ export default function TimeTicketsDatesSelector() {
} else {
history.push({
search: queryString.stringify({
...searchParams,
start: null,
end: null,
}),

View File

@@ -61,6 +61,7 @@ export const GET_LINE_TICKET_BY_PK = gql`
flat_rate
clockon
clockoff
rate
employee {
id
first_name

View File

@@ -26,7 +26,12 @@ export const QUERY_TICKETS_BY_JOBID = gql`
`;
export const QUERY_TIME_TICKETS_IN_RANGE = gql`
query QUERY_TIME_TICKETS_IN_RANGE($start: date!, $end: date!) {
query QUERY_TIME_TICKETS_IN_RANGE(
$start: date!
$end: date!
$fixedStart: date!
$fixedEnd: date!
) {
timetickets(
where: { date: { _gte: $start, _lte: $end } }
order_by: { date: desc_nulls_first }
@@ -56,6 +61,35 @@ export const QUERY_TIME_TICKETS_IN_RANGE = gql`
last_name
}
}
fixedperiod: timetickets(
where: { date: { _gte: $fixedStart, _lte: $fixedEnd } }
order_by: { date: desc_nulls_first }
) {
actualhrs
ciecacode
clockoff
clockon
cost_center
created_at
date
id
rate
productivehrs
memo
jobid
flat_rate
job {
id
ro_number
}
employeeid
employee {
id
employee_number
first_name
last_name
}
}
}
`;

View File

@@ -1,19 +1,21 @@
import Icon from "@ant-design/icons";
import Icon, { BarsOutlined } from "@ant-design/icons";
import { Tabs } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { FaShieldAlt } from "react-icons/fa";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
import ScoreboardTimeTickets from "../../components/scoreboard-timetickets/scoreboard-timetickets.component";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { FaHardHat, FaRegStickyNote, FaShieldAlt } from "react-icons/fa";
import ScoreboardTimeTickets from "../../components/scoreboard-timetickets/scoreboard-timetickets.component";
import queryString from "query-string";
import { useHistory, useLocation } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -26,7 +28,9 @@ const mapDispatchToProps = (dispatch) => ({
export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
const searchParams = queryString.parse(useLocation().search);
const { tab } = searchParams;
const history = useHistory();
useEffect(() => {
document.title = t("titles.scoreboard");
setSelectedHeader("scoreboard");
@@ -41,12 +45,21 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
return (
<FeatureWrapper featureName="scoreboard">
<RbacWrapper action="scoreboard:view">
<Tabs>
<Tabs
activeKey={tab || "sb"}
destroyInactiveTabPane
onChange={(key) => {
searchParams.tab = key;
history.push({
search: queryString.stringify(searchParams),
});
}}
>
<Tabs.TabPane
tab={
<span>
<Icon component={FaShieldAlt} />
{t("menus.jobsdetail.general")}
{t("scoreboard.labels.jobs")}
</span>
}
destroyInactiveTabPane
@@ -57,8 +70,8 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
<Tabs.TabPane
tab={
<span>
<Icon component={FaShieldAlt} />
{t("menus.jobsdetail.general")}
<BarsOutlined />
{t("scoreboard.labels.timetickets")}
</span>
}
destroyInactiveTabPane

View File

@@ -2426,9 +2426,18 @@
"asoftodaytarget": "As of Today",
"dailyactual": "Actual (D)",
"dailytarget": "Daily",
"jobs": "Jobs",
"lastmonth": "Last Month",
"lastweek": "Last Week",
"monthlytarget": "Monthly",
"productivestatistics": "Productive Hours Statistics",
"productivetimeticketsoverdate": "Productive Hours over Selected Dates",
"targets": "Targets",
"thismonth": "This Month",
"thisweek": "This Week",
"timetickets": "Timetickets",
"todateactual": "Actual (MTD)",
"totaloverperiod": "Total Period",
"weeklyactual": "Actual (W)",
"weeklytarget": "Weekly",
"workingdays": "Working Days / Month"

View File

@@ -2426,9 +2426,18 @@
"asoftodaytarget": "",
"dailyactual": "",
"dailytarget": "",
"jobs": "",
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
"productivestatistics": "",
"productivetimeticketsoverdate": "",
"targets": "",
"thismonth": "",
"thisweek": "",
"timetickets": "",
"todateactual": "",
"totaloverperiod": "",
"weeklyactual": "",
"weeklytarget": "",
"workingdays": ""

View File

@@ -2426,9 +2426,18 @@
"asoftodaytarget": "",
"dailyactual": "",
"dailytarget": "",
"jobs": "",
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
"productivestatistics": "",
"productivetimeticketsoverdate": "",
"targets": "",
"thismonth": "",
"thisweek": "",
"timetickets": "",
"todateactual": "",
"totaloverperiod": "",
"weeklyactual": "",
"weeklytarget": "",
"workingdays": ""