diff --git a/package.json b/package.json index ed8b05a..ba2cebc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tres", "private": true, - "version": "1.4.0", + "version": "1.5.0", "type": "module", "scripts": { "dev": "vite", diff --git a/public/assets/processing_time.svg b/public/assets/processing_time.svg new file mode 100644 index 0000000..4f813c5 --- /dev/null +++ b/public/assets/processing_time.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/tickets_count.svg b/public/assets/tickets_count.svg new file mode 100644 index 0000000..6af8851 --- /dev/null +++ b/public/assets/tickets_count.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/users_count.svg b/public/assets/users_count.svg new file mode 100644 index 0000000..cadd6b6 --- /dev/null +++ b/public/assets/users_count.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index b59b51b..17224db 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -124,6 +124,7 @@ "deleted": "Deleted" }, "notifications": "Notifications", + "statistic": "Statistic", "generalTickets": "General Tickets", "settings": "Settings" }, @@ -251,7 +252,19 @@ "close": "Close" }, "statistic": { - "heading": "Statistic" + "heading": "Statistic", + "activitySummary": { + "heading": "Activity summary" + }, + "calendarStatistic": { + "heading": "Calendar" + }, + "mentionedFaculties": { + "heading": "Mentioned faculties" + }, + "systemStatus": { + "heading": "Current system status" + } }, "privacyPolicy": { "postscript": { diff --git a/public/locales/ua/translation.json b/public/locales/ua/translation.json index 64b53de..be67186 100644 --- a/public/locales/ua/translation.json +++ b/public/locales/ua/translation.json @@ -124,6 +124,7 @@ "deleted": "Видалені" }, "notifications": "Сповіщення", + "statistic": "Статистика", "generalTickets": "Поточні звернення", "settings": "Налаштування" }, @@ -251,7 +252,19 @@ "close": "Вирішений" }, "statistic": { - "heading": "Статистика" + "heading": "Статистика", + "activitySummary": { + "heading": "Підсумок діяльності" + }, + "calendarStatistic": { + "heading": "Календар" + }, + "mentionedFaculties": { + "heading": "Згадані факультети" + }, + "systemStatus": { + "heading": "Поточний стан системи" + } }, "privacyPolicy": { "postscript": { diff --git a/src/assets/processing_time.svg b/src/assets/processing_time.svg new file mode 100644 index 0000000..4f813c5 --- /dev/null +++ b/src/assets/processing_time.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/tickets_count.svg b/src/assets/tickets_count.svg new file mode 100644 index 0000000..6af8851 --- /dev/null +++ b/src/assets/tickets_count.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/users_count.svg b/src/assets/users_count.svg new file mode 100644 index 0000000..cadd6b6 --- /dev/null +++ b/src/assets/users_count.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/StatisticCard/StatisticCard.tsx b/src/components/StatisticCard/StatisticCard.tsx new file mode 100644 index 0000000..cdca352 --- /dev/null +++ b/src/components/StatisticCard/StatisticCard.tsx @@ -0,0 +1,49 @@ +import { CSSProperties, FC } from "react"; +import { useTranslation } from "react-i18next"; + +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import useTheme from "@mui/material/styles/useTheme"; + +import IPalette from "theme/IPalette.interface"; + +interface StatisticCardProps { + title: string; + width: number; + children: JSX.Element; + styles?: CSSProperties; +} + +const StatisticCard: FC = ({ + title, + width, + children, + styles, +}) => { + const { t } = useTranslation(); + const { palette }: IPalette = useTheme(); + + return ( + + + {t(`statistic.${title}.heading`)} + + {children} + + ); +}; + +export { StatisticCard }; diff --git a/src/components/StatisticCard/index.ts b/src/components/StatisticCard/index.ts new file mode 100644 index 0000000..a8d3755 --- /dev/null +++ b/src/components/StatisticCard/index.ts @@ -0,0 +1 @@ +export { StatisticCard } from "./StatisticCard"; diff --git a/src/constants/scopes.ts b/src/constants/scopes.ts index 681462f..cf2ebe3 100644 --- a/src/constants/scopes.ts +++ b/src/constants/scopes.ts @@ -1,7 +1,7 @@ const scopes = { REPORTS: "Reports", QA: "Q/A", - SUGGESTIONS: "Suggestions", + SUGGESTION: "Suggestion", }; export { scopes }; 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 a2330d6..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,6 +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 { NavbarListItem } from "./components/NavbarListItem"; @@ -103,15 +105,26 @@ const GeneralActions: FC = ({ handleListItemClick={handleListItemClick} /> {isAdmin && ( - } - disableIcon={} - /> + <> + } + disableIcon={} + /> + } + disableIcon={} + /> + > )} { const { t } = useTranslation(); - const [getStatistics] = useGetStatisticsMutation(); - const { data: statuses, isSuccess } = useGetStatusesQuery({}); - - const [global, setGlobal] = useState([]); + const { data: summaryActivity } = useGetSummaryActivityQuery({}); + const { data: facultiesStatistic } = useGetFacultyStatisticQuery({}); + const [getStatistics, { data: generalStatistic }] = + useGetGeneralStatisticMutation(); useEffect(() => { - getStatistics(JSON.stringify({})).then((res: ApiResponse) => { - if (res?.data) { - const statistics = JSON.parse(res.data as any); - - setGlobal(statistics.global.statuses); - } - }); + getStatistics(JSON.stringify({})); }, []); return ( - + - - {t("statistic.heading")} - + {t("statistic.heading")} - - {global.length > 0 && isSuccess && ( - + {generalStatistic && ( + + )} + {generalStatistic && ( + + )} + {facultiesStatistic && ( + )} + {summaryActivity && ( + + )} ); diff --git a/src/pages/Statistic/components/ActivitySummary/ActivitySummary.tsx b/src/pages/Statistic/components/ActivitySummary/ActivitySummary.tsx new file mode 100644 index 0000000..b839ad4 --- /dev/null +++ b/src/pages/Statistic/components/ActivitySummary/ActivitySummary.tsx @@ -0,0 +1,90 @@ +import { FC } from "react"; + +import Box from "@mui/material/Box"; +import useTheme from "@mui/material/styles/useTheme"; + +import processingTime from "../../../../assets/processing_time.svg"; +import ticketsCount from "../../../../assets//tickets_count.svg"; +import usersCount from "../../../../assets/users_count.svg"; + +import { StatisticCard } from "components/StatisticCard"; + +import IPalette from "theme/IPalette.interface"; +import { ActivityTile } from "./components/ActivityTile"; + +interface ActivitySummaryProps { + summaryActivity: { + average_process_time: number; + tickets_processed: number; + users_registered: number; + }; +} + +const ActivitySummary: FC = ({ summaryActivity }) => { + const { palette }: IPalette = useTheme(); + + const { average_process_time, tickets_processed, users_registered } = + summaryActivity; + + const activityList = [ + { + icon: processingTime, + stat: `${average_process_time} d`, + title: "Processing time", + percent: "+10% from yesterday", + color: "#B59469", + }, + { + icon: ticketsCount, + stat: tickets_processed, + title: "Tickets", + percent: "+8% from yesterday", + color: "#A9DFD8", + }, + { + icon: usersCount, + stat: users_registered, + title: "Users", + percent: "+3% from yesterday", + color: "#20AEF3", + }, + ]; + + return ( + + + {activityList.map((activity, index) => { + const { icon, stat, title, percent, color } = activity; + + return ( + + ); + })} + + + ); +}; + +export { ActivitySummary }; diff --git a/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/ActivityTile.tsx b/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/ActivityTile.tsx new file mode 100644 index 0000000..c53bd13 --- /dev/null +++ b/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/ActivityTile.tsx @@ -0,0 +1,32 @@ +import { FC } from "react"; + +import Box from "@mui/material/Box"; + +interface ActivityTileProps { + icon: any; + stat: number | string; + title: string; + percent: string; + color: string; +} + +const ActivityTile: FC = ({ + icon, + stat, + title, + percent, + color, +}) => { + return ( + + + + {stat} + {title} + {percent} + + + ); +}; + +export { ActivityTile }; diff --git a/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/index.ts b/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/index.ts new file mode 100644 index 0000000..2b2957a --- /dev/null +++ b/src/pages/Statistic/components/ActivitySummary/components/ActivityTile/index.ts @@ -0,0 +1 @@ +export { ActivityTile } from "./ActivityTile"; diff --git a/src/pages/Statistic/components/ActivitySummary/index.ts b/src/pages/Statistic/components/ActivitySummary/index.ts new file mode 100644 index 0000000..3842f0e --- /dev/null +++ b/src/pages/Statistic/components/ActivitySummary/index.ts @@ -0,0 +1 @@ +export { ActivitySummary } from "./ActivitySummary"; diff --git a/src/pages/Statistic/components/CalendarStatistic/CalendarStatistic.tsx b/src/pages/Statistic/components/CalendarStatistic/CalendarStatistic.tsx new file mode 100644 index 0000000..7098962 --- /dev/null +++ b/src/pages/Statistic/components/CalendarStatistic/CalendarStatistic.tsx @@ -0,0 +1,36 @@ +import { FC } from "react"; + +import { StatisticCard } from "components/StatisticCard"; +import { TicketsCount } from "./components/TicketsCount"; +import { ScopesStatistic } from "./components/ScopesStatistic"; + +export interface IScope { + date: string; + scope: string; + tickets_count: number; +} + +interface CalendarStatisticProps { + calendarStatistic: IScope[]; +} + +const CalendarStatistic: FC = ({ + calendarStatistic, +}) => { + const ticketsCount = 25; + + return ( + + <> + + + > + + ); +}; + +export { CalendarStatistic }; diff --git a/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/ScopesStatistic.tsx b/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/ScopesStatistic.tsx new file mode 100644 index 0000000..6c3256b --- /dev/null +++ b/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/ScopesStatistic.tsx @@ -0,0 +1,79 @@ +import { FC } from "react"; + +import Box from "@mui/material/Box"; +import useTheme from "@mui/material/styles/useTheme"; +import { Button, Divider, Typography } from "@mui/material"; + +import HelpOutlineIcon from "@mui/icons-material/HelpOutline"; +import FlagOutlinedIcon from "@mui/icons-material/FlagOutlined"; +import HandshakeOutlinedIcon from "@mui/icons-material/HandshakeOutlined"; + +import IPalette from "theme/IPalette.interface"; +import { IScope } from "../../CalendarStatistic"; +import { scopes } from "constants/scopes"; +import { ScopeTile } from "./components/ScopeTile"; + +interface ScopesStatisticProps { + calendarStatistic: IScope[]; +} + +const ScopesStatistic: FC = ({ calendarStatistic }) => { + const { palette }: IPalette = useTheme(); + + const icons = { + [scopes.QA]: , + [scopes.REPORTS]: , + [scopes.SUGGESTION]: , + }; + + return ( + + + + + Tickets processed + + + View all + + + + {calendarStatistic.map(scopeStat => { + const { scope, tickets_count } = scopeStat; + + return ( + + ); + })} + + + ); +}; + +export { ScopesStatistic }; diff --git a/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/components/ScopeTile/ScopeTile.tsx b/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/components/ScopeTile/ScopeTile.tsx new file mode 100644 index 0000000..d99b9c1 --- /dev/null +++ b/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/components/ScopeTile/ScopeTile.tsx @@ -0,0 +1,64 @@ +import { FC } from "react"; + +import Box from "@mui/material/Box"; +import IconButton from "@mui/material/IconButton"; +import useTheme from "@mui/material/styles/useTheme"; + +import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; + +import IPalette from "theme/IPalette.interface"; + +interface ScopeTileProps { + icon: JSX.Element; + title: string; + ticketsCount: number; +} + +const ScopeTile: FC = ({ icon, title, ticketsCount }) => { + const { palette }: IPalette = useTheme(); + + return ( + + + + {icon} + + + {title} + + {ticketsCount}{" "} + + tickets + + + + + + + + + ); +}; + +export { ScopeTile }; diff --git a/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/components/ScopeTile/index.ts b/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/components/ScopeTile/index.ts new file mode 100644 index 0000000..8250247 --- /dev/null +++ b/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/components/ScopeTile/index.ts @@ -0,0 +1 @@ +export { ScopeTile } from "./ScopeTile"; diff --git a/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/index.ts b/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/index.ts new file mode 100644 index 0000000..2757900 --- /dev/null +++ b/src/pages/Statistic/components/CalendarStatistic/components/ScopesStatistic/index.ts @@ -0,0 +1 @@ +export { ScopesStatistic } from "./ScopesStatistic"; diff --git a/src/pages/Statistic/components/CalendarStatistic/components/TicketsCount/TicketsCount.tsx b/src/pages/Statistic/components/CalendarStatistic/components/TicketsCount/TicketsCount.tsx new file mode 100644 index 0000000..cca77be --- /dev/null +++ b/src/pages/Statistic/components/CalendarStatistic/components/TicketsCount/TicketsCount.tsx @@ -0,0 +1,50 @@ +import { FC } from "react"; + +import Box from "@mui/material/Box"; +import useTheme from "@mui/material/styles/useTheme"; + +import IPalette from "theme/IPalette.interface"; + +interface TicketsCountProps { + ticketsCount: number; +} + +const TicketsCount: FC = ({ ticketsCount }) => { + const { palette }: IPalette = useTheme(); + + return ( + + + {ticketsCount} + + + Tickets + + + ); +}; + +export { TicketsCount }; diff --git a/src/pages/Statistic/components/CalendarStatistic/components/TicketsCount/index.ts b/src/pages/Statistic/components/CalendarStatistic/components/TicketsCount/index.ts new file mode 100644 index 0000000..7e2b8bb --- /dev/null +++ b/src/pages/Statistic/components/CalendarStatistic/components/TicketsCount/index.ts @@ -0,0 +1 @@ +export { TicketsCount } from "./TicketsCount"; diff --git a/src/pages/Statistic/components/CalendarStatistic/index.ts b/src/pages/Statistic/components/CalendarStatistic/index.ts new file mode 100644 index 0000000..3224720 --- /dev/null +++ b/src/pages/Statistic/components/CalendarStatistic/index.ts @@ -0,0 +1 @@ +export { CalendarStatistic } from "./CalendarStatistic"; diff --git a/src/pages/Statistic/components/FacultiesStatistic/FacultiesStatistic.tsx b/src/pages/Statistic/components/FacultiesStatistic/FacultiesStatistic.tsx new file mode 100644 index 0000000..a3ec380 --- /dev/null +++ b/src/pages/Statistic/components/FacultiesStatistic/FacultiesStatistic.tsx @@ -0,0 +1,74 @@ +import { FC } from "react"; + +import Box from "@mui/material/Box"; +import useTheme from "@mui/material/styles/useTheme"; + +import { StatisticCard } from "components/StatisticCard"; +import { FacultyTile } from "./components/FacultyTile"; + +import IPalette from "theme/IPalette.interface"; + +interface IFacultyStat { + faculty_id: number; + name: string; + registered_users: number; + created_tickets_percent: number; +} + +interface FacultiesStatisticProps { + facultiesStatistic: IFacultyStat[]; +} + +const FacultiesStatistic: FC = ({ + facultiesStatistic, +}) => { + const { palette }: IPalette = useTheme(); + return ( + + + {facultiesStatistic.map(facultyStat => { + const { + faculty_id, + name, + registered_users, + created_tickets_percent, + } = facultyStat; + return ( + + ); + })} + + + ); +}; + +export { FacultiesStatistic }; diff --git a/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/FacultyTile.tsx b/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/FacultyTile.tsx new file mode 100644 index 0000000..5af46bb --- /dev/null +++ b/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/FacultyTile.tsx @@ -0,0 +1,86 @@ +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 new file mode 100644 index 0000000..521c66d --- /dev/null +++ b/src/pages/Statistic/components/FacultiesStatistic/components/FacultyTile/index.ts @@ -0,0 +1 @@ +export { FacultyTile } from "./FacultyTile"; diff --git a/src/pages/Statistic/components/FacultiesStatistic/index.ts b/src/pages/Statistic/components/FacultiesStatistic/index.ts new file mode 100644 index 0000000..d8f29bf --- /dev/null +++ b/src/pages/Statistic/components/FacultiesStatistic/index.ts @@ -0,0 +1 @@ +export { FacultiesStatistic } from "./FacultiesStatistic"; diff --git a/src/pages/Statistic/components/GlobalStatistic/GlobalStatistic.tsx b/src/pages/Statistic/components/GlobalStatistic/GlobalStatistic.tsx deleted file mode 100644 index cf6f149..0000000 --- a/src/pages/Statistic/components/GlobalStatistic/GlobalStatistic.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { FC } from "react"; -import { useTranslation } from "react-i18next"; - -import { - Chart as ChartJS, - CategoryScale, - LinearScale, - BarElement, -} from "chart.js"; -import { Bar } from "react-chartjs-2"; - -import { useTheme } from "@mui/material"; - -import { IGlobalStatus, IStatus } from "../../Statistic"; -import IPalette from "theme/IPalette.interface"; - -ChartJS.register(BarElement, CategoryScale, LinearScale); - -interface GlobalStatisticProps { - globalStatistic: IGlobalStatus[]; - statuses: IStatus[]; -} - -const GlobalStatistic: FC = ({ - globalStatistic, - statuses, -}) => { - const { t } = useTranslation(); - const { palette }: IPalette = useTheme(); - - const statistic = { - labels: statuses.map(status => - t(`statusesFilter.${status.name.toLowerCase()}`) - ), - datasets: [ - { - label: "Global statistic", - backgroundColor: [ - palette.common.white, - palette.semantic.warning, - palette.semantic.info, - palette.semantic.waiting, - palette.semantic.success, - palette.semantic.error, - ], - data: globalStatistic.map(status => status.count), - }, - ], - }; - - const options = { - scales: { - y: { - ticks: { - stepSize: 1, - }, - }, - }, - }; - - return ( - - - - ); -}; - -export { GlobalStatistic }; diff --git a/src/pages/Statistic/components/GlobalStatistic/index.ts b/src/pages/Statistic/components/GlobalStatistic/index.ts deleted file mode 100644 index ffbfb77..0000000 --- a/src/pages/Statistic/components/GlobalStatistic/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { GlobalStatistic } from "./GlobalStatistic"; diff --git a/src/pages/Statistic/components/StatusesStatistic/StatusesStatistic.tsx b/src/pages/Statistic/components/StatusesStatistic/StatusesStatistic.tsx new file mode 100644 index 0000000..979d6e6 --- /dev/null +++ b/src/pages/Statistic/components/StatusesStatistic/StatusesStatistic.tsx @@ -0,0 +1,104 @@ +import { FC } from "react"; +import { useTranslation } from "react-i18next"; + +import { Chart as ChartJS, ArcElement, Tooltip } from "chart.js"; +import { Doughnut } from "react-chartjs-2"; + +import Box from "@mui/material/Box"; + +import { StatisticCard } from "components/StatisticCard"; +import { StatusTile } from "./components/StatusTile"; + +import { IPeriodStatus } from "../../Statistic"; +import { statuses } from "constants/statuses"; + +ChartJS.register(ArcElement, Tooltip); + +interface StatusesStatisticProps { + statusesStatistic: IPeriodStatus[]; +} + +const StatusesStatistic: FC = ({ + statusesStatistic, +}) => { + const { t } = useTranslation(); + + const ticketsCount = statusesStatistic + .map(status => status.tickets_count) + .reduce((partialSum, a) => partialSum + a, 0); + + const colors = { + [statuses.ACCEPTED]: "224, 156, 54", + [statuses.CLOSE]: "104, 182, 81", + [statuses.NEW]: "255, 255, 255", + [statuses.OPEN]: "41, 130, 211", + [statuses.REJECTED]: "217, 75, 68", + [statuses.WAITING]: "158, 61, 255", + }; + + const data = { + labels: statusesStatistic.map(status => + t(`statusesFilter.${status.status_name.toLowerCase()}`) + ), + datasets: [ + { + data: statusesStatistic.map(status => status.tickets_count), + backgroundColor: statusesStatistic.map( + status => `rgba(${colors[status.status_name.toLowerCase()]}, 1)` + ), + borderWidth: 0, + cutout: "70%", + }, + ], + }; + + 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 56px san-serif"; + ctx.fillStyle = "rgba(255, 255, 255, 0.48)"; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText(ticketsCount, xCoord, yCoord - 10); + + ctx.font = "500 20px san-serif"; + ctx.fillStyle = "rgba(255, 255, 255, 0.80)"; + ctx.fillText("Tickets", xCoord, yCoord + 30); + }, + }; + + return ( + + <> + + + + + {statusesStatistic.map(status => { + const { tickets_count, status_name, status_id } = status; + + return ( + + ); + })} + + > + + ); +}; + +export { StatusesStatistic }; diff --git a/src/pages/Statistic/components/StatusesStatistic/components/StatusTile/StatusTile.tsx b/src/pages/Statistic/components/StatusesStatistic/components/StatusTile/StatusTile.tsx new file mode 100644 index 0000000..4a76665 --- /dev/null +++ b/src/pages/Statistic/components/StatusesStatistic/components/StatusTile/StatusTile.tsx @@ -0,0 +1,57 @@ +import { FC } from "react"; +import { useTranslation } from "react-i18next"; + +interface StatusTileProps { + count: number; + title: string; + color: string; +} + +const StatusTile: FC = ({ count, title, color }) => { + const { t } = useTranslation(); + + return ( + + + + {t(`statusesFilter.${title.toLowerCase()}`)} + + + {count} + + + ); +}; + +export { StatusTile }; diff --git a/src/pages/Statistic/components/StatusesStatistic/components/StatusTile/index.ts b/src/pages/Statistic/components/StatusesStatistic/components/StatusTile/index.ts new file mode 100644 index 0000000..2f39146 --- /dev/null +++ b/src/pages/Statistic/components/StatusesStatistic/components/StatusTile/index.ts @@ -0,0 +1 @@ +export { StatusTile } from "./StatusTile"; diff --git a/src/pages/Statistic/components/StatusesStatistic/index.ts b/src/pages/Statistic/components/StatusesStatistic/index.ts new file mode 100644 index 0000000..b257427 --- /dev/null +++ b/src/pages/Statistic/components/StatusesStatistic/index.ts @@ -0,0 +1 @@ +export { StatusesStatistic } from "./StatusesStatistic"; diff --git a/src/store/api/statistic.api.ts b/src/store/api/statistic.api.ts new file mode 100644 index 0000000..dc36d29 --- /dev/null +++ b/src/store/api/statistic.api.ts @@ -0,0 +1,25 @@ +import { api } from "./api"; + +export const statisticApi = api.injectEndpoints({ + endpoints: builder => ({ + getSummaryActivity: builder.query({ + query: () => "/statistic/activity_summary", + }), + getFacultyStatistic: builder.query({ + query: () => "/statistic/faculties", + }), + getGeneralStatistic: builder.mutation({ + query: ({ body }) => ({ + url: "/statistic/period", + method: "POST", + body, + }), + }), + }), +}); + +export const { + useGetSummaryActivityQuery, + useGetFacultyStatisticQuery, + useGetGeneralStatisticMutation, +} = statisticApi; diff --git a/src/store/api/statistics.api.ts b/src/store/api/statistics.api.ts deleted file mode 100644 index b570407..0000000 --- a/src/store/api/statistics.api.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { api } from "./api"; - -export const statisticsApi = api.injectEndpoints({ - endpoints: builder => ({ - getStatistics: builder.mutation({ - query: ({ body }) => ({ - url: "/statistic/general", - method: "POST", - body, - }), - }), - }), -}); - -export const { useGetStatisticsMutation } = statisticsApi;