From ee95811cd1a258e984c8a8cf6a47a309ffbc1b8a Mon Sep 17 00:00:00 2001 From: yaroslav Date: Fri, 2 Feb 2024 14:36:15 +0200 Subject: [PATCH] Completed development of statistics v1.0 --- public/assets/processing_time.svg | 4 +- public/assets/tickets_count.svg | 4 +- public/assets/users_count.svg | 4 +- public/locales/en/translation.json | 9 +- public/locales/ua/translation.json | 9 +- src/assets/processing_time.svg | 4 +- src/assets/tickets_count.svg | 4 +- src/assets/users_count.svg | 4 +- .../StatisticCard/StatisticCard.tsx | 2 +- src/index.css | 18 +++ .../GeneralActions/GeneralActions.tsx | 8 +- src/pages/Statistic/Statistic.tsx | 51 +++++--- .../ActivitySummary/ActivitySummary.tsx | 29 ++--- .../components/ActivityTile/ActivityTile.tsx | 9 +- .../FacultiesStatistic/FacultiesStatistic.tsx | 119 +++++++++++------- .../components/FacultyTile/FacultyTile.tsx | 86 ------------- .../components/FacultyTile/index.ts | 1 - .../ScopesStatistic/ScopesStatistic.tsx | 7 ++ .../components/ScopeTile/ScopeTile.tsx | 17 ++- .../StatusesStatistic/StatusesStatistic.tsx | 12 +- src/store/api/statistic.api.ts | 14 +-- 21 files changed, 199 insertions(+), 216 deletions(-) delete mode 100644 src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/FacultyTile.tsx delete mode 100644 src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/index.ts diff --git a/public/assets/processing_time.svg b/public/assets/processing_time.svg index 4f813c5..4a36be2 100644 --- a/public/assets/processing_time.svg +++ b/public/assets/processing_time.svg @@ -1,6 +1,6 @@ - - + + diff --git a/public/assets/tickets_count.svg b/public/assets/tickets_count.svg index 6af8851..74edd67 100644 --- a/public/assets/tickets_count.svg +++ b/public/assets/tickets_count.svg @@ -1,4 +1,4 @@ - - + + diff --git a/public/assets/users_count.svg b/public/assets/users_count.svg index cadd6b6..93795a7 100644 --- a/public/assets/users_count.svg +++ b/public/assets/users_count.svg @@ -1,4 +1,4 @@ - - + + diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index fbdbdda..e0e8229 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -14,6 +14,7 @@ "12": "Dec." }, "common": { + "tickets": "Tickets", "q/a": "Q/A", "reports": "Reports", "suggestion": "Suggestions", @@ -133,7 +134,7 @@ "scopes": { "reportsTitle": "Reports", "q/aTitle": "Questions", - "suggestionsTitle": "Suggestions" + "suggestionTitle": "Suggestions" }, "showAll": "Show all" }, @@ -254,7 +255,11 @@ "statistic": { "heading": "Statistic", "activitySummary": { - "heading": "Activity summary" + "heading": "Activity summary", + "processingTime": "Processing time", + "users": "Users", + "hours": "h", + "days": "d" }, "scopeStatistic": { "heading": "Tickets processed" diff --git a/public/locales/ua/translation.json b/public/locales/ua/translation.json index 45c3f11..38988b0 100644 --- a/public/locales/ua/translation.json +++ b/public/locales/ua/translation.json @@ -14,6 +14,7 @@ "12": "Груд." }, "common": { + "tickets": "Звернень", "q/a": "Питання", "reports": "Скарги", "suggestion": "Пропозиції", @@ -133,7 +134,7 @@ "scopes": { "reportsTitle": "Скарги", "q/aTitle": "Питання", - "suggestionsTitle": "Пропозиції" + "suggestionTitle": "Пропозиції" }, "showAll": "Показати всі" }, @@ -254,7 +255,11 @@ "statistic": { "heading": "Статистика", "activitySummary": { - "heading": "Підсумок діяльності" + "heading": "Підсумкова діяльність", + "processingTime": "Час обробки", + "users": "Користувачів", + "hours": "г", + "days": "д" }, "scopeStatistic": { "heading": "Звернень оброблено" diff --git a/src/assets/processing_time.svg b/src/assets/processing_time.svg index 4f813c5..4a36be2 100644 --- a/src/assets/processing_time.svg +++ b/src/assets/processing_time.svg @@ -1,6 +1,6 @@ - - + + diff --git a/src/assets/tickets_count.svg b/src/assets/tickets_count.svg index 6af8851..74edd67 100644 --- a/src/assets/tickets_count.svg +++ b/src/assets/tickets_count.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/assets/users_count.svg b/src/assets/users_count.svg index cadd6b6..93795a7 100644 --- a/src/assets/users_count.svg +++ b/src/assets/users_count.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/components/StatisticCard/StatisticCard.tsx b/src/components/StatisticCard/StatisticCard.tsx index cdca352..ac09d53 100644 --- a/src/components/StatisticCard/StatisticCard.tsx +++ b/src/components/StatisticCard/StatisticCard.tsx @@ -31,7 +31,7 @@ const StatisticCard: FC = ({ flexDirection: "column", gap: "20px", p: "15px 20px", - width: `${width}px`, + minWidth: `${width}px`, height: `fit-content`, borderRadius: 1, bgcolor: palette.grey.card, diff --git a/src/index.css b/src/index.css index 4390feb..40991f8 100644 --- a/src/index.css +++ b/src/index.css @@ -54,6 +54,24 @@ select { } } +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.1); +} + +::-webkit-scrollbar-thumb { + background: #888; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: #555; +} + .scroll::-webkit-scrollbar { width: 8px; height: 8px; diff --git a/src/layouts/MainLayout/components/Sidebar/components/SidebarActions/components/GeneralActions/GeneralActions.tsx b/src/layouts/MainLayout/components/Sidebar/components/SidebarActions/components/GeneralActions/GeneralActions.tsx index 0e17793..6e9cdde 100644 --- a/src/layouts/MainLayout/components/Sidebar/components/SidebarActions/components/GeneralActions/GeneralActions.tsx +++ b/src/layouts/MainLayout/components/Sidebar/components/SidebarActions/components/GeneralActions/GeneralActions.tsx @@ -19,8 +19,8 @@ import GridViewIcon from "@mui/icons-material/GridView"; import GridViewSharpIcon from "@mui/icons-material/GridViewSharp"; import ExpandLess from "@mui/icons-material/ExpandLess"; import ExpandMore from "@mui/icons-material/ExpandMore"; -// import InsertChartIcon from "@mui/icons-material/InsertChart"; -// import InsertChartOutlinedIcon from "@mui/icons-material/InsertChartOutlined"; +import InsertChartIcon from "@mui/icons-material/InsertChart"; +import InsertChartOutlinedIcon from "@mui/icons-material/InsertChartOutlined"; import { NavbarListItem } from "./components/NavbarListItem"; @@ -115,7 +115,7 @@ const GeneralActions: FC = ({ activeIcon={} disableIcon={} /> - {/* = ({ handleListItemClick={handleListItemClick} activeIcon={} disableIcon={} - /> */} + /> )} { const { t } = useTranslation(); + const { palette }: IPalette = useTheme(); const { data: summaryActivity } = useGetSummaryActivityQuery({}); - const { data: facultiesStatistic } = useGetFacultyStatisticQuery({}); - const [getStatistics, { data: generalStatistic }] = - useGetGeneralStatisticMutation(); - - useEffect(() => { - getStatistics(JSON.stringify({})); - }, []); + const { data: generalStatistic } = useGetPeriodStatisticQuery({}); return ( @@ -44,27 +40,44 @@ const Statistic: FC = () => { sx={{ display: "grid", gap: "20px", - gridTemplateAreas: `"statuses activities faculties" - "statuses scopes faculties" - "statuses none none"`, + gridTemplateAreas: `"statuses faculties faculties" + "statuses scopes activities"`, pt: "100px !important", + overflowX: "auto", + pb: 2, + "&::-webkit-scrollbar": { + height: "8px", + }, + "&::-webkit-scrollbar-thumb": { + background: palette.grey.divider, + borderRadius: "4px", + }, + "&::-webkit-scrollbar-thumb:hover": { + background: "#555", + }, + "&": { + scrollbarWidth: "thin", + scrollbarColor: "#555 #212125", + }, + "&:hover": { + scrollbarColor: "#555 #212125", + }, }} > - {generalStatistic && ( + {generalStatistic?.statuses && ( )} - {generalStatistic && ( + {generalStatistic?.scopes && ( )} - {facultiesStatistic && ( + {generalStatistic?.faculty_scopes && ( )} {summaryActivity && ( )} -
); diff --git a/src/pages/Statistic/components/ActivitySummary/ActivitySummary.tsx b/src/pages/Statistic/components/ActivitySummary/ActivitySummary.tsx index 0ca96b7..3e60154 100644 --- a/src/pages/Statistic/components/ActivitySummary/ActivitySummary.tsx +++ b/src/pages/Statistic/components/ActivitySummary/ActivitySummary.tsx @@ -1,4 +1,5 @@ import { FC } from "react"; +import { useTranslation } from "react-i18next"; import Box from "@mui/material/Box"; import useTheme from "@mui/material/styles/useTheme"; @@ -21,6 +22,7 @@ interface ActivitySummaryProps { } const ActivitySummary: FC = ({ summaryActivity }) => { + const { t } = useTranslation(); const { palette }: IPalette = useTheme(); const { average_process_time, tickets_processed, users_registered } = @@ -29,31 +31,25 @@ const ActivitySummary: FC = ({ summaryActivity }) => { const activityList = [ { icon: processingTime, - stat: `${average_process_time} d`, - title: "Processing time", - percent: "+10% from yesterday", - color: "#B59469", + stat: `${average_process_time} ${t("statistic.activitySummary.days")}`, + title: t("statistic.activitySummary.processingTime"), }, { icon: ticketsCount, stat: tickets_processed, - title: "Tickets", - percent: "+8% from yesterday", - color: "#A9DFD8", + title: t("common.tickets"), }, { icon: usersCount, stat: users_registered, - title: "Users", - percent: "+3% from yesterday", - color: "#20AEF3", + title: t("statistic.activitySummary.users"), }, ]; return ( = ({ summaryActivity }) => { }} > {activityList.map((activity, index) => { - const { icon, stat, title, percent, color } = activity; + const { icon, stat, title } = activity; return ( - + ); })} diff --git a/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/ActivityTile.tsx b/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/ActivityTile.tsx index ba0544e..28a66b4 100644 --- a/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/ActivityTile.tsx +++ b/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/ActivityTile.tsx @@ -1,16 +1,19 @@ import { FC } from "react"; import Box from "@mui/material/Box"; +import useTheme from "@mui/material/styles/useTheme"; + +import IPalette from "theme/IPalette.interface"; interface ActivityTileProps { icon: any; stat: number | string; title: string; - percent: string; - color: string; } const ActivityTile: FC = ({ icon, stat, title }) => { + const { palette }: IPalette = useTheme(); + return ( = ({ icon, stat, title }) => { gap: 5, }} > -
{stat}
+
{stat}
{title}
diff --git a/src/pages/Statistic/components/FacultiesStatistic/FacultiesStatistic.tsx b/src/pages/Statistic/components/FacultiesStatistic/FacultiesStatistic.tsx index a3ec380..dd40a29 100644 --- a/src/pages/Statistic/components/FacultiesStatistic/FacultiesStatistic.tsx +++ b/src/pages/Statistic/components/FacultiesStatistic/FacultiesStatistic.tsx @@ -1,18 +1,27 @@ import { FC } from "react"; +import { useTranslation } from "react-i18next"; -import Box from "@mui/material/Box"; -import useTheme from "@mui/material/styles/useTheme"; - +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + BarElement, + Tooltip, +} from "chart.js"; import { StatisticCard } from "components/StatisticCard"; -import { FacultyTile } from "./components/FacultyTile"; -import IPalette from "theme/IPalette.interface"; +import { Bar } from "react-chartjs-2"; + +import { scopes } from "constants/scopes"; + +ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip); interface IFacultyStat { faculty_id: number; name: string; - registered_users: number; - created_tickets_percent: number; + qa_count: number; + reports_count: number; + suggestion: number; } interface FacultiesStatisticProps { @@ -22,51 +31,67 @@ interface FacultiesStatisticProps { const FacultiesStatistic: FC = ({ facultiesStatistic, }) => { - const { palette }: IPalette = useTheme(); + const { t } = useTranslation(); + + const colors = { + [scopes.QA]: "#12DB87", + [scopes.REPORTS]: "#D94B44", + [scopes.SUGGESTION]: "#03A2E8", + }; + + const data = { + labels: facultiesStatistic.map(facultyStat => facultyStat.name), + datasets: [ + { + label: t(`common.${scopes.SUGGESTION.toLowerCase()}`), + data: facultiesStatistic.map(facultyStat => facultyStat.suggestion), + backgroundColor: colors[scopes.SUGGESTION], + }, + { + label: t(`common.${scopes.QA.toLowerCase()}`), + data: facultiesStatistic.map(facultyStat => facultyStat.qa_count), + backgroundColor: colors[scopes.QA], + }, + { + label: t(`common.${scopes.REPORTS.toLowerCase()}`), + data: facultiesStatistic.map(facultyStat => facultyStat.reports_count), + backgroundColor: colors[scopes.REPORTS], + }, + ], + }; + + const options = { + type: "bar", + responsive: true, + scales: { + x: { + stacked: true, + grid: { + color: "transparent", + }, + }, + y: { + stacked: true, + grid: { + color: "rgba(255, 255, 255, 0.1)", + }, + border: { + width: 0, + }, + ticks: { + padding: 10, + }, + }, + }, + }; + return ( - - {facultiesStatistic.map(facultyStat => { - const { - faculty_id, - name, - registered_users, - created_tickets_percent, - } = facultyStat; - return ( - - ); - })} - + ); }; diff --git a/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/FacultyTile.tsx b/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/FacultyTile.tsx deleted file mode 100644 index 5af46bb..0000000 --- a/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/FacultyTile.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { FC } from "react"; - -import { Chart as ChartJS, ArcElement, Tooltip } from "chart.js"; -import { Doughnut } from "react-chartjs-2"; - -import useTheme from "@mui/material/styles/useTheme"; - -import IPalette from "theme/IPalette.interface"; - -interface FacultyTileProps { - title: string; - usersCount: number; - ticketsPercent: number; -} - -ChartJS.register(ArcElement, Tooltip); - -const FacultyTile: FC = ({ - title, - usersCount, - ticketsPercent, -}) => { - const { palette }: IPalette = useTheme(); - - const data = { - labels: ["Created at this faculty", "Created at other faculties"], - datasets: [ - { - data: [ticketsPercent, 100 - ticketsPercent], - backgroundColor: [palette.semantic.info, palette.common.white], - borderWidth: 0, - cutout: "80%", - }, - ], - }; - - const options = { - plugins: { - tooltip: { - enabled: false, - }, - }, - }; - - const doughnutLabel = { - id: "doughnutLabel", - beforeDatasetsDraw(chart) { - const { ctx } = chart; - - ctx.save(); - const xCoord = chart.getDatasetMeta(0).data[0].x; - const yCoord = chart.getDatasetMeta(0).data[0].y; - ctx.font = "600 18px san-serif"; - ctx.fillStyle = "#fff"; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillText(`${ticketsPercent}%`, xCoord, yCoord); - }, - }; - - return ( -
-
-
{title}
-
- {usersCount} Registered -
-
-
- -
-
- ); -}; - -export { FacultyTile }; diff --git a/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/index.ts b/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/index.ts deleted file mode 100644 index 521c66d..0000000 --- a/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { FacultyTile } from "./FacultyTile"; diff --git a/src/pages/Statistic/components/ScopesStatistic/ScopesStatistic.tsx b/src/pages/Statistic/components/ScopesStatistic/ScopesStatistic.tsx index 7c0d59b..f210268 100644 --- a/src/pages/Statistic/components/ScopesStatistic/ScopesStatistic.tsx +++ b/src/pages/Statistic/components/ScopesStatistic/ScopesStatistic.tsx @@ -28,6 +28,12 @@ const ScopesStatistic: FC = ({ calendarStatistic }) => { [scopes.SUGGESTION]: , }; + const colors = { + [scopes.QA]: "#12DB87", + [scopes.REPORTS]: "#D94B44", + [scopes.SUGGESTION]: "#03A2E8", + }; + return ( = ({ calendarStatistic }) => { icon={icons[scope]} title={scope} ticketsCount={tickets_count} + color={colors[scope]} key={scope} /> ); diff --git a/src/pages/Statistic/components/ScopesStatistic/components/ScopeTile/ScopeTile.tsx b/src/pages/Statistic/components/ScopesStatistic/components/ScopeTile/ScopeTile.tsx index 5c4f186..48b78de 100644 --- a/src/pages/Statistic/components/ScopesStatistic/components/ScopeTile/ScopeTile.tsx +++ b/src/pages/Statistic/components/ScopesStatistic/components/ScopeTile/ScopeTile.tsx @@ -1,4 +1,5 @@ import { FC } from "react"; +import { useTranslation } from "react-i18next"; import Box from "@mui/material/Box"; import useTheme from "@mui/material/styles/useTheme"; @@ -9,9 +10,16 @@ interface ScopeTileProps { icon: JSX.Element; title: string; ticketsCount: number; + color: string; } -const ScopeTile: FC = ({ icon, title, ticketsCount }) => { +const ScopeTile: FC = ({ + icon, + title, + ticketsCount, + color, +}) => { + const { t } = useTranslation(); const { palette }: IPalette = useTheme(); return ( @@ -31,19 +39,20 @@ const ScopeTile: FC = ({ icon, title, ticketsCount }) => { >
{icon}
-
{title}
+
{t(`common.${title.toLowerCase()}`)}
{ticketsCount}{" "} - tickets + {t("common.tickets").toLowerCase()}
diff --git a/src/pages/Statistic/components/StatusesStatistic/StatusesStatistic.tsx b/src/pages/Statistic/components/StatusesStatistic/StatusesStatistic.tsx index 7fa6bf7..121d498 100644 --- a/src/pages/Statistic/components/StatusesStatistic/StatusesStatistic.tsx +++ b/src/pages/Statistic/components/StatusesStatistic/StatusesStatistic.tsx @@ -38,13 +38,13 @@ const StatusesStatistic: FC = ({ const data = { labels: statusesStatistic.map(status => - t(`statusesFilter.${status.status_name.toLowerCase()}`) + t(`statusesFilter.${status.name.toLowerCase()}`) ), datasets: [ { data: statusesStatistic.map(status => status.tickets_count), backgroundColor: statusesStatistic.map( - status => `rgba(${colors[status.status_name.toLowerCase()]}, 1)` + status => `rgba(${colors[status.name.toLowerCase()]}, 1)` ), borderWidth: 0, cutout: "70%", @@ -68,7 +68,7 @@ const StatusesStatistic: FC = ({ ctx.font = "500 20px Inter"; ctx.fillStyle = "rgba(255, 255, 255, 0.80)"; - ctx.fillText("Tickets", xCoord, yCoord + 30); + ctx.fillText(t("common.tickets"), xCoord, yCoord + 30); }, }; @@ -84,13 +84,13 @@ const StatusesStatistic: FC = ({ {statusesStatistic.map(status => { - const { tickets_count, status_name, status_id } = status; + const { tickets_count, name, status_id } = status; return ( ); diff --git a/src/store/api/statistic.api.ts b/src/store/api/statistic.api.ts index dc36d29..c0cda7e 100644 --- a/src/store/api/statistic.api.ts +++ b/src/store/api/statistic.api.ts @@ -5,21 +5,17 @@ export const statisticApi = api.injectEndpoints({ getSummaryActivity: builder.query({ query: () => "/statistic/activity_summary", }), - getFacultyStatistic: builder.query({ + getFacultiesStatistic: builder.query({ query: () => "/statistic/faculties", }), - getGeneralStatistic: builder.mutation({ - query: ({ body }) => ({ - url: "/statistic/period", - method: "POST", - body, - }), + getPeriodStatistic: builder.query({ + query: () => "/statistic/period", }), }), }); export const { useGetSummaryActivityQuery, - useGetFacultyStatisticQuery, - useGetGeneralStatisticMutation, + useGetFacultiesStatisticQuery, + useGetPeriodStatisticQuery, } = statisticApi;