diff --git a/README.md b/README.md
index f2fedcec..f67a6f6e 100644
--- a/README.md
+++ b/README.md
@@ -104,11 +104,11 @@ exit
## Contribuição
-Certifique-se de ler o [Guia de Contribuição](https://github.com/fga-eps-mds/2023-1-CAPJu-Front/blob/main/.github/CONTRIBUTING.md) antes de realizar qualquer atividade no projeto!
+Certifique-se de ler o [Guia de Contribuição](https://github.com/fga-eps-mds/2023-2-CAPJu-Front/blob/main/.github/CONTRIBUTING.md) antes de realizar qualquer atividade no projeto!
## Licença
-O CAPJu está sob as regras aplicadas na licença [MIT](https://github.com/fga-eps-mds/2023-1-CAPJu-Front/blob/main/LICENSE)
+O CAPJu está sob as regras aplicadas na licença [MIT](https://github.com/fga-eps-mds/2023-2-CAPJu-Front/blob/main/LICENSE)
## Contribuidores
diff --git a/package.json b/package.json
index 463269d3..f3157b46 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
"react-input-mask": "^2.0.4",
"react-paginate": "^8.2.0",
"react-query": "^3.39.3",
- "react-router-dom": "^6.10.0",
+ "react-router-dom": "^6.17.0",
"react-select-event": "^5.5.1",
"react-table": "^7.8.0",
"reactflow": "^11.7.2",
@@ -78,7 +78,7 @@
"jsdom": "^22.0.0",
"lint-staged": "^11.1.2",
"prettier": "^2.4.0",
- "typescript": "^4.4.2",
+ "typescript": "^4.9.5",
"vitest": "^0.31.1"
}
}
diff --git a/src/components/CustomAccordion/index.tsx b/src/components/CustomAccordion/index.tsx
new file mode 100644
index 00000000..2e9d7723
--- /dev/null
+++ b/src/components/CustomAccordion/index.tsx
@@ -0,0 +1,52 @@
+import {
+ Accordion,
+ AccordionButton,
+ AccordionIcon,
+ AccordionItem,
+ AccordionPanel,
+ Box,
+} from "@chakra-ui/react";
+
+interface CustomAccordionProps {
+ title: String;
+ children: JSX.Element;
+ marginBottom: number;
+}
+export default function CustomAccordion({
+ title,
+ children,
+ marginBottom,
+}: CustomAccordionProps) {
+ return (
+
+
+
+
+
+
+ {title}
+
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/src/pages/Statistics/StepDeadlineReports/index.tsx b/src/pages/Statistics/StepDeadlineReports/index.tsx
new file mode 100644
index 00000000..6014940a
--- /dev/null
+++ b/src/pages/Statistics/StepDeadlineReports/index.tsx
@@ -0,0 +1,292 @@
+import { useEffect, useState, useMemo } from "react";
+import { useToast, Box, Flex, Button, Text, Input } from "@chakra-ui/react";
+import CustomAccordion from "components/CustomAccordion";
+import { DataTable } from "components/DataTable";
+import { getFlows } from "services/processManagement/flows";
+import { createColumnHelper } from "@tanstack/react-table";
+import { useQuery } from "react-query";
+import { useAuth } from "hooks/useAuth";
+import { isActionAllowedToUser } from "utils/permissions";
+import { ViewIcon } from "@chakra-ui/icons";
+import { Pagination } from "components/Pagination";
+import { getProcessesByDueDate } from "services/processManagement/statistics";
+import { useLocation } from "react-router-dom";
+
+export default function StepDeadlineReports() {
+ const toast = useToast();
+ const { getUserData } = useAuth();
+ const { data: userData, isFetched: isUserFetched } = useQuery({
+ queryKey: ["user-data"],
+ queryFn: getUserData,
+ });
+
+ const [flows, setFlows] = useState([] as Flow[]);
+ const { state } = useLocation();
+ const [tableVisible, setTableVisible] = useState(false);
+ const [minDate, setMinDate] = useState("");
+ const [isFetching, setIsFetching] = useState(true);
+ const [maxDate, setMaxDate] = useState("");
+ const [processData, setProcessData] = useState([]);
+ const [processDueTotalPages, setProcessDueTotalPages] = useState<
+ number | undefined
+ >();
+ const [loading, setLoading] = useState(false);
+ const [currentPage, setCurrentPage] = useState(0);
+
+ useEffect(() => {
+ const handlePageChange = async () => {
+ setLoading(true);
+ await handleProcessByDueDate();
+ setLoading(false);
+ };
+
+ if (minDate && maxDate) handlePageChange();
+ }, [currentPage]);
+
+ const getDataFlows = async () => {
+ const dataFlows = await getFlows();
+ if (dataFlows.value) setFlows(dataFlows.value);
+ };
+
+ const handleProcessByDueDate = async () => {
+ const res = await getProcessesByDueDate(minDate, maxDate, {
+ offset: currentPage * 5,
+ limit: 5,
+ });
+
+ if (res.type === "error") throw new Error(res.error.message);
+
+ setProcessData(res.value);
+ setProcessDueTotalPages(res.totalPages);
+ };
+
+ useEffect(() => {
+ if (flows.length === 0) getDataFlows();
+ }, []);
+
+ const tableColumnHelper = createColumnHelper>();
+ const tableActions = useMemo(
+ () => [
+ {
+ label: "Visualizar Processo",
+ icon: ,
+ isNavigate: true,
+ actionName: "see-process",
+ disabled: !isActionAllowedToUser(
+ userData?.value?.allowedActions || [],
+ "see-process"
+ ),
+ },
+ ],
+ [isUserFetched, userData]
+ );
+
+ const tableColumns = [
+ tableColumnHelper.accessor("record", {
+ cell: (info) => info.getValue(),
+ header: "Registro",
+ meta: {
+ isSortable: true,
+ },
+ }),
+ tableColumnHelper.accessor("nickname", {
+ cell: (info) => info.getValue(),
+ header: "Apelido",
+ meta: {
+ isSortable: true,
+ },
+ }),
+ tableColumnHelper.accessor("nameFlow", {
+ cell: (info) => info.getValue(),
+ header: "Fluxo",
+ meta: {
+ isSortable: true,
+ },
+ }),
+ tableColumnHelper.accessor("nameStage", {
+ cell: (info) => info.getValue(),
+ header: "Etapa Atual",
+ meta: {
+ isSortable: true,
+ },
+ }),
+ tableColumnHelper.accessor("tableActions", {
+ cell: (info) => info.getValue(),
+ header: "Ações",
+ meta: {
+ isTableActions: true,
+ isSortable: false,
+ },
+ }),
+ ];
+
+ const filteredStepDeadlineReports = useMemo[]>(() => {
+ if (processData.length <= 0) return [];
+
+ return (
+ (processData.reduce(
+ (
+ acc: TableRow[] | Process[],
+ curr: TableRow | Process
+ ) => {
+ return [
+ ...acc,
+ {
+ ...curr,
+ tableActions,
+ actionsProps: {
+ process: curr,
+ pathname: `/processos/${curr.record}`,
+ state: {
+ process: curr,
+ ...(state || {}),
+ },
+ },
+ record: curr.record,
+ },
+ ];
+ },
+ []
+ ) as TableRow[]) || []
+ );
+ }, [processData, tableActions]);
+
+ const handleConfirmClick = async () => {
+ const minDateValue = Date.parse(minDate);
+ const maxDateValue = Date.parse(maxDate);
+
+ if (
+ Number.isNaN(minDateValue) ||
+ Number.isNaN(maxDateValue) ||
+ minDateValue > maxDateValue
+ ) {
+ toast({
+ id: "date-validation-error",
+ title: "Erro",
+ description:
+ "Por favor, insira datas válidas e data da direita menor que a da esquerda",
+ status: "error",
+ isClosable: true,
+ });
+ } else {
+ setTableVisible(true);
+ try {
+ setIsFetching(true);
+ await handleProcessByDueDate();
+ setIsFetching(false);
+ } catch (error) {
+ toast({
+ id: "processes-error",
+ title: "Erro ao carregar processos",
+ description: "Insira o período de validade.",
+ status: "error",
+ isClosable: true,
+ });
+ }
+ }
+ };
+
+ return (
+
+
+
+ Estatísticas
+
+
+
+
+
+ <>
+
+
+
+ {
+ const novoValor = event.target.value;
+ setMinDate(novoValor);
+ }}
+ />
+ até
+ {
+ const novoValor = event.target.value;
+ setMaxDate(novoValor);
+ }}
+ />
+
+
+
+
+
+ {tableVisible && (
+ <>
+
+
+ >
+ )}
+
+
+
+
+ {tableVisible && (
+
+ )}
+
+
+ {processDueTotalPages !== undefined ? (
+
+ setCurrentPage(selectedPage.selected)
+ }
+ />
+ ) : null}
+
+ >
+
+
+
+
+ );
+}
diff --git a/src/pages/Statistics/index.tsx b/src/pages/Statistics/index.tsx
new file mode 100644
index 00000000..2b8987af
--- /dev/null
+++ b/src/pages/Statistics/index.tsx
@@ -0,0 +1,10 @@
+import { PrivateLayout } from "layouts/Private";
+import StepDeadlineReports from "./StepDeadlineReports";
+
+export default function Statistics() {
+ return (
+
+
+
+ );
+}
diff --git a/src/pages/ViewProcess/index.tsx b/src/pages/ViewProcess/index.tsx
index 02ab822f..fbfcd9ff 100644
--- a/src/pages/ViewProcess/index.tsx
+++ b/src/pages/ViewProcess/index.tsx
@@ -105,7 +105,9 @@ function ViewProcess() {
"flow",
typeof process?.idFlow === "number"
? process?.idFlow
- : process?.idFlow[0],
+ : Array.isArray(process?.idFlow)
+ ? process.idFlow[0]
+ : process.idFlow,
],
queryFn: async () => {
const res = process.idFlow
@@ -288,8 +290,8 @@ function ViewProcess() {
colorScheme="blue"
onClick={() => navigate(-1)}
>
- Voltar aos
- Processos{flow ? ` do Fluxo ${flow?.name}` : ""}
+ Voltar{" "}
+ {flow ? ` do Fluxo ${flow?.name}` : ""}
import("pages/Processes"));
const ViewProcess = lazy(() => import("pages/ViewProcess"));
const About = lazy(() => import("pages/About"));
const ActionsManager = lazy(() => import("pages/ProfilesActionsManager"));
+const Statistics = lazy(() => import("pages/Statistics"));
export const PrivateRoutes: MenuItem[] = [
{
@@ -45,6 +46,12 @@ export const PrivateRoutes: MenuItem[] = [
actionName: "see-process",
element: ,
},
+ {
+ path: "estatisticas",
+ name: "Estatisticas",
+ actionName: "see-process",
+ element: ,
+ },
{
path: "acessos",
name: "ProfilesManager",
diff --git a/src/services/processManagement/statistics/index.ts b/src/services/processManagement/statistics/index.ts
new file mode 100644
index 00000000..00bb4fbc
--- /dev/null
+++ b/src/services/processManagement/statistics/index.ts
@@ -0,0 +1,34 @@
+import { api } from "services/api";
+
+export const getProcessesByDueDate = async (
+ minDate: string,
+ maxDate: string,
+ pagination?: Pagination
+): Promise> => {
+ try {
+ const res = await api.processManagement.get<{
+ processInDue: Process[];
+ totalPages: number;
+ }>(`/statistics/${minDate}/${maxDate}`, {
+ params: {
+ offset: pagination?.offset ?? 0,
+ limit: pagination?.limit,
+ },
+ });
+
+ return {
+ type: "success",
+ value: res.data.processInDue,
+ totalPages: res.data.totalPages,
+ };
+ } catch (error) {
+ if (error instanceof Error)
+ return { type: "error", error, value: undefined };
+
+ return {
+ type: "error",
+ error: new Error("Erro desconhecido"),
+ value: undefined,
+ };
+ }
+};
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
index 0152bd9e..84046863 100644
--- a/src/types/global.d.ts
+++ b/src/types/global.d.ts
@@ -74,6 +74,8 @@ declare global {
status: string;
progress?: Progress[];
isNextSage?: boolean;
+ nameStage?: string;
+ nameFlow?: string;
};
type Note = {
diff --git a/src/utils/tabs.ts b/src/utils/tabs.ts
index 622f55ae..ef038759 100644
--- a/src/utils/tabs.ts
+++ b/src/utils/tabs.ts
@@ -23,6 +23,12 @@ export const tabs = [
path: "/processos",
action: "see-process",
},
+ {
+ label: "Estatisticas",
+ pathIndex: "/estatisticas",
+ path: "/estatisticas",
+ action: "see-process",
+ },
{
label: "Cadastro",
pathIndex: "/acessos",