diff --git a/docker-compose.yml b/docker-compose.yml
index faeadff7..ab4be392 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,6 +3,7 @@ x-frontend: &frontend
VITE_ZONE: DEV
VITE_USER_POOLS_ID: ca-central-1_t2HSZBHur
VITE_USER_POOLS_WEB_CLIENT_ID: 3g6n2ha1loi4kp1jhaq359vrvb
+ VITE_BACKEND_URL: http://localhost:8080
healthcheck:
test: curl http://localhost:3000"
interval: 15s
diff --git a/frontend/src/components/ActionsTable/index.jsx b/frontend/src/components/ActionsTable/index.jsx
deleted file mode 100644
index e4c37c60..00000000
--- a/frontend/src/components/ActionsTable/index.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import React from 'react';
-import { Table, TableHead, TableRow, TableHeader, TableBody, TableCell } from "@carbon/react";
-import StatusTag from "../StatusTag";
-
-import ActivityTag from "../ActivityTag";
-const ActionsTable = ({rows,headers}) => {
- return (
-
-
-
- {headers.map(header => )}
-
-
-
- {rows.map((row, idx) =>
- {Object.keys(row).filter(key => key !== 'id').map(key => {
- return (
-
- {key === "status" ? (
-
- ):
- key === "activityType" && !row["fileFormat"] ? (
-
- ):
- key === "activityType" && row["fileFormat"] ? (
-
- ):
- row[key]}
-
- );
- })}
- )}
-
-
- );
-};
-
-ActionsTable.defaultProps = {
- rows:[],
- headers:[]
-}
-
-export default ActionsTable;
diff --git a/frontend/src/components/ActionsTable/index.tsx b/frontend/src/components/ActionsTable/index.tsx
new file mode 100644
index 00000000..70b09335
--- /dev/null
+++ b/frontend/src/components/ActionsTable/index.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import { Table, TableHead, TableRow, TableHeader, TableBody, TableCell } from "@carbon/react";
+import StatusTag from "../StatusTag";
+import ActivityTag from "../ActivityTag";
+
+interface IActionsTable {
+ rows: any[];
+ headers: any[];
+}
+
+const ActionsTable: React.FC = ({rows, headers}) => (
+
+
+
+ {headers.map(header => )}
+
+
+
+ {rows.map((row, idx) =>
+ {Object.keys(row).filter(key => key !== 'id').map(key => {
+ return (
+
+ {key === "status" ? (
+
+ ):
+ key === "activityType" && !row["fileFormat"] ? (
+
+ ):
+ key === "activityType" && row["fileFormat"] ? (
+
+ ):
+ row[key]}
+
+ );
+ })}
+ )}
+
+
+);
+
+export default ActionsTable;
diff --git a/frontend/src/components/ActionsTable/testData.js b/frontend/src/components/ActionsTable/testData.ts
similarity index 100%
rename from frontend/src/components/ActionsTable/testData.js
rename to frontend/src/components/ActionsTable/testData.ts
diff --git a/frontend/src/components/BarChartGrouped/index.jsx b/frontend/src/components/BarChartGrouped/index.tsx
similarity index 56%
rename from frontend/src/components/BarChartGrouped/index.jsx
rename to frontend/src/components/BarChartGrouped/index.tsx
index 8219976a..d3828147 100644
--- a/frontend/src/components/BarChartGrouped/index.jsx
+++ b/frontend/src/components/BarChartGrouped/index.tsx
@@ -1,57 +1,66 @@
import React, { useState, useEffect } from "react";
-import { GroupedBarChart } from "@carbon/charts-react";
+import { GroupedBarChart, ScaleTypes } from "@carbon/charts-react";
import { Dropdown, DatePicker, DatePickerInput } from "@carbon/react";
import "@carbon/charts/styles.css";
import "./BarChartGrouped.scss";
import { fetchOpeningsPerYear } from "../../services/OpeningService";
+interface IDropdownItem {
+ value: string,
+ text: string
+}
+
const BarChartGrouped = () => {
- const [windowWidth, setWindowWidth] = useState(window.innerWidth);
- const [chartData, setChartData] = useState([]);
- const [isLoading, setIsLoading] = useState(true);
- const [orgUnitCode, setOrgUnitCode] = useState(null);
- const [statusCode, setStatusCode] = useState(null);
- const [startDate, setStartDate] = useState(null);
- const [endDate, setEndDate] = useState(null);
+ const [windowWidth, setWindowWidth] = useState(window.innerWidth);
+ const [chartData, setChartData] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [orgUnitCode, setOrgUnitCode] = useState('');
+ const [statusCode, setStatusCode] = useState('');
+ const [startDate, setStartDate] = useState(null);
+ const [endDate, setEndDate] = useState(null);
const handleResize = () => {
setWindowWidth(window.innerWidth);
};
useEffect(() => {
+ const fetchChartData = async () => {
+ try {
+ setIsLoading(true);
+ let formattedStartDate: string | null = null;
+ let formattedEndDate: string | null = null;
+
+ if (startDate) {
+ formattedStartDate = formatDateToString(startDate);
+ }
+ if (endDate) {
+ formattedEndDate = formatDateToString(endDate);
+ }
+
+ const data = await fetchOpeningsPerYear({
+ orgUnitCode,
+ statusCode,
+ entryDateStart: formattedStartDate,
+ entryDateEnd: formattedEndDate
+ });
+ setChartData(data);
+ setIsLoading(false);
+ } catch (error) {
+ console.error("Error fetching chart data:", error);
+ setIsLoading(false);
+ }
+ };
+
fetchChartData();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, [orgUnitCode, statusCode, startDate, endDate]);
- const fetchChartData = async () => {
- try {
- setIsLoading(true);
- let formattedStartDate = startDate;
- let formattedEndDate = endDate;
-
- if (startDate) {
- formattedStartDate = formatDateToString(startDate);
- }
- if (endDate) {
- formattedEndDate = formatDateToString(endDate);
- }
-
- const data = await fetchOpeningsPerYear(orgUnitCode, statusCode, formattedStartDate, formattedEndDate);
- setChartData(data);
- setIsLoading(false);
- } catch (error) {
- console.error("Error fetching chart data:", error);
- setIsLoading(false);
- }
- };
-
-
- const formatDateToString = (dates) => {
- const date = dates[0]
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, "0");
- const day = String(date.getDate()).padStart(2, "0");
+ const formatDateToString = (dateToFormat: Date | null) => {
+ if (!dateToFormat) return null;
+ const year = dateToFormat.getFullYear();
+ const month = String(dateToFormat.getMonth() + 1).padStart(2, "0");
+ const day = String(dateToFormat.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
@@ -65,7 +74,7 @@ const BarChartGrouped = () => {
mapsTo: "value",
},
bottom: {
- scaleType: "labels",
+ scaleType: ScaleTypes.LABELS,
mapsTo: "key",
},
},
@@ -111,6 +120,14 @@ const BarChartGrouped = () => {
// Add more options as needed
];
+ const setOrgUnitCodeSelected = ({selectedItem}:{selectedItem: IDropdownItem}) => {
+ setOrgUnitCode(selectedItem.value);
+ };
+
+ const setStatusCodeSelected = ({selectedItem}:{selectedItem: IDropdownItem}) => {
+ setStatusCode(selectedItem.value);
+ };
+
return (
@@ -118,24 +135,26 @@ const BarChartGrouped = () => {
item ? item.text : ''}
- onChange={({ selectedItem }) => setOrgUnitCode(selectedItem.value)}
+ itemToString={(item: IDropdownItem) => item ? item.text : ''}
+ onChange={setOrgUnitCodeSelected}
/>
item ? item.text : ''}
- onChange={({ selectedItem }) => setStatusCode(selectedItem.value)}
+ itemToString={(item: IDropdownItem) => item ? item.text : ''}
+ onChange={setStatusCodeSelected}
/>
setStartDate(date)}
+ onChange={(date: Date) => setStartDate(date)}
>
{
setEndDate(date)}
+ onChange={(date: Date) => setEndDate(date)}
>
{
+interface IDonutChart {
+ group: any;
+ value: any;
+}
+
+interface IDropdownItem {
+ value: string,
+ text: string
+}
+
+const DonutChartView: React.FC = () => {
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
- const [chartData, setChartData] = useState([]);
- const [isLoading, setIsLoading] = useState(true);
- const [orgUnitCode, setOrgUnitCode] = useState("");
- const [clientNumber, setClientNumber] = useState("");
- const [startDate, setStartDate] = useState(null);
- const [endDate, setEndDate] = useState(null);
+ const [chartData, setChartData] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [orgUnitCode, setOrgUnitCode] = useState("");
+ const [clientNumber, setClientNumber] = useState("");
+ const [startDate, setStartDate] = useState(null);
+ const [endDate, setEndDate] = useState(null);
const handleResize = () => {
setWindowWidth(window.innerWidth);
@@ -28,7 +38,12 @@ const DonutChartView = () => {
setIsLoading(true);
const formattedStartDate = formatDateToString(startDate);
const formattedEndDate = formatDateToString(endDate);
- const data = await fetchFreeGrowingMilestones(orgUnitCode, clientNumber, formattedStartDate, formattedEndDate);
+ const data = await fetchFreeGrowingMilestones({
+ orgUnitCode,
+ clientNumber,
+ entryDateStart: formattedStartDate,
+ entryDateEnd: formattedEndDate
+ });
setChartData(data);
setIsLoading(false);
} catch (error) {
@@ -37,12 +52,11 @@ const DonutChartView = () => {
}
};
- const formatDateToString = (dates) => {
- if (!dates) return null;
- const date = dates[0];
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, "0");
- const day = String(date.getDate()).padStart(2, "0");
+ const formatDateToString = (dateToFormat: Date | null) => {
+ if (!dateToFormat) return null;
+ const year = dateToFormat.getFullYear();
+ const month = String(dateToFormat.getMonth() + 1).padStart(2, "0");
+ const day = String(dateToFormat.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
@@ -61,27 +75,42 @@ const DonutChartView = () => {
};
// Sample data for dropdowns
- const items = [
+ const items: IDropdownItem[] = [
{ value: 'DCR', text: 'DCR' },
- { value: 2, text: 'Option 2' },
- { value: 3, text: 'Option 3' },
- { value: 4, text: 'Option 4' },
+ { value: '2', text: 'Option 2' },
+ { value: '3', text: 'Option 3' },
+ { value: '4', text: 'Option 4' },
// Add more options as needed
];
+ const setOrgUnitCodeSelected = ({selectedItem}:{selectedItem: IDropdownItem}) => {
+ setOrgUnitCode(selectedItem.value);
+ };
+
return (
- item ? item.text : ''} onChange={option => setOrgUnitCode(option.selectedItem.value)} />
+ item ? item.text : ''}
+ onChange={setOrgUnitCodeSelected}
+ />
- setClientNumber(event.target.value)} />
+ ) => setClientNumber(event.target.value)}
+ />
setStartDate(date)}
+ onChange={(date: Date) => setStartDate(date)}
>
{
setEndDate(date)}
+ onChange={(date: Date) => setEndDate(date)}
>
{
- const iconName = props;
- const Base = Carbon[iconName]
-
- return
-}
-
-export default Icon;
diff --git a/frontend/src/components/Icon/index.tsx b/frontend/src/components/Icon/index.tsx
new file mode 100644
index 00000000..ed8c490e
--- /dev/null
+++ b/frontend/src/components/Icon/index.tsx
@@ -0,0 +1,9 @@
+import * as Carbon from '@carbon/icons-react';
+
+const Icon = (props: string) => {
+ const iconName = props;
+ const Base = Carbon[iconName]
+ return ;
+}
+
+export default Icon;
diff --git a/frontend/src/components/MyRecentActions/filesData.js b/frontend/src/components/MyRecentActions/filesData.ts
similarity index 100%
rename from frontend/src/components/MyRecentActions/filesData.js
rename to frontend/src/components/MyRecentActions/filesData.ts
diff --git a/frontend/src/components/MyRecentActions/index.jsx b/frontend/src/components/MyRecentActions/index.tsx
similarity index 93%
rename from frontend/src/components/MyRecentActions/index.jsx
rename to frontend/src/components/MyRecentActions/index.tsx
index 0c74d092..7021ec50 100644
--- a/frontend/src/components/MyRecentActions/index.jsx
+++ b/frontend/src/components/MyRecentActions/index.tsx
@@ -4,8 +4,8 @@ import ActionsTable from "../ActionsTable";
import { fetchRecentActions } from '../../services/OpeningService';
import { rows as fileRows, headers as fileHeaders } from "./filesData";
-const MyRecentActions = () => {
- const [recentActions, setRecentActions] = useState([]);
+const MyRecentActions: React.FC = () => {
+ const [recentActions, setRecentActions] = useState([]);
const headers = [
{
diff --git a/frontend/src/components/MyRecentActions/testData.js b/frontend/src/components/MyRecentActions/testData.ts
similarity index 100%
rename from frontend/src/components/MyRecentActions/testData.js
rename to frontend/src/components/MyRecentActions/testData.ts
diff --git a/frontend/src/components/OpeningMetricsTab/index.tsx b/frontend/src/components/OpeningMetricsTab/index.tsx
index 00365c6e..f2dadc0c 100644
--- a/frontend/src/components/OpeningMetricsTab/index.tsx
+++ b/frontend/src/components/OpeningMetricsTab/index.tsx
@@ -19,17 +19,15 @@ const OpeningMetricsTab: React.FC = () => {
setShowSpatial(!showSpatial)
}
- const userDetails = useSelector((state:any) => state.userDetails)
- const goToActivity = () => {
- console.log("clicked a row")
- }
- const { user } = userDetails
- return (
- <>
+ return (
+ <>
-
+
@@ -53,8 +51,8 @@ const OpeningMetricsTab: React.FC = () => {
- >
- );
- };
+ >
+ );
+};
export default OpeningMetricsTab;
diff --git a/frontend/src/components/OpeningScreenDataTable/index.jsx b/frontend/src/components/OpeningScreenDataTable/index.tsx
similarity index 77%
rename from frontend/src/components/OpeningScreenDataTable/index.jsx
rename to frontend/src/components/OpeningScreenDataTable/index.tsx
index 0c9cbaa9..7db4555f 100644
--- a/frontend/src/components/OpeningScreenDataTable/index.jsx
+++ b/frontend/src/components/OpeningScreenDataTable/index.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { useCallback, useContext, useState } from 'react';
import {
DataTable,
TableBatchAction,
@@ -25,60 +25,31 @@ import * as Icons from '@carbon/icons-react'
import StatusTag from '../StatusTag'; // Import the StatusTag component
import './styles.scss'
import EmptySection from '../EmptySection';
+import PaginationContext from '../../contexts/PaginationContext';
-export const batchActionClick = (selectedRows) => () => {
- console.log('Batch action clicked with selected rows:', selectedRows);
- // Add your logic to handle batch actions here
-};
-
-// A custom hook to handle pagination logic
-const usePagination = (data, initialItemsPerPage) => {
- const [currentPage, setCurrentPage] = useState(1);
- const [itemsPerPage, setItemsPerPage] = useState(initialItemsPerPage);
-
- // Update the total number of pages when itemsPerPage changes
- const totalPages = Math.ceil(data.length / itemsPerPage);
-
- // Get the current page data
- const currentData = () => {
- const start = (currentPage - 1) * itemsPerPage;
- const end = start + itemsPerPage;
- return data.slice(start, end);
- };
-
- // Update the current page when the user changes the page
- const handlePageChange = ({ page }) => {
- setCurrentPage(page);
- };
-
- // Update the items per page when the user changes the value
- const handleItemsPerPageChange = (event) => {
- setCurrentPage(event.page);
- setItemsPerPage(event.pageSize);
- };
-
- return {
- currentData,
- currentPage,
- totalPages,
- handlePageChange,
- handleItemsPerPageChange,
- itemsPerPage, // Expose the current itemsPerPage value
- };
-};
+interface IOpeningScreenDataTable {
+ rows: any[],
+ headers: any[],
+ setOpeningId: Function,
+}
-export default function OpeningScreenDataTable({ rows, headers, error, setOpeningId }) {
- const [filteredRows, setFilteredRows] = useState(rows);
+const OpeningScreenDataTable: React.FC = ({ rows, headers, setOpeningId }) => {
+ const [filteredRows, setFilteredRows] = useState(rows);
const {
- currentData,
+ getCurrentData,
currentPage,
totalPages,
handlePageChange,
handleItemsPerPageChange,
itemsPerPage, // Use itemsPerPage from the hook
- } = usePagination(filteredRows, 5);
+ setPageData,
+ setInitialItemsPerPage
+ } = useContext(PaginationContext);
- const handleSearchChange = (searchTerm) => {
+ setPageData(filteredRows);
+ setInitialItemsPerPage(5);
+
+ const handleSearchChange = (searchTerm: string) => {
const filtered = rows.filter((item) =>
Object.values(item)
.join(' ')
@@ -88,20 +59,25 @@ export default function OpeningScreenDataTable({ rows, headers, error, setOpenin
setFilteredRows(filtered);
};
- const clickViewAction = useCallback((id) => {
+ const clickViewAction = useCallback((id: string) => {
console.log(`Clicked view on id ${id}`);
}, []);
- const selectRowEvent = useCallback((openingId, selected) => {
+ const selectRowEvent = useCallback((openingId: string, selected: boolean) => {
if (!selected) {
console.log(`Selected row id=${openingId} selected=${JSON.stringify(!selected)}`);
setOpeningId(openingId);
}
}, []);
+ const batchActionClick = (selectedRows: any[]) => () => {
+ console.log('Batch action clicked with selected rows:', selectedRows);
+ // Add your logic to handle batch actions here
+ };
+
return (
-
+
{({
rows,
headers,
@@ -115,6 +91,19 @@ export default function OpeningScreenDataTable({ rows, headers, error, setOpenin
getTableProps,
getTableContainerProps,
selectRow,
+ }:{
+ rows: any[],
+ headers: any[],
+ getHeaderProps: Function,
+ getRowProps: Function,
+ getSelectionProps: Function,
+ getToolbarProps: Function,
+ getBatchActionProps: Function,
+ onInputChange: Function,
+ selectedRows: any[],
+ getTableProps: Function,
+ getTableContainerProps: Function,
+ selectRow: any
}) => {
const batchActionProps = getBatchActionProps();
@@ -126,7 +115,7 @@ export default function OpeningScreenDataTable({ rows, headers, error, setOpenin
handleSearchChange(e.target.value)}
+ onChange={(e: React.ChangeEvent) => handleSearchChange(e.target.value)}
placeholder="Filter by opening ID, File ID, timber mark, cut block, status..."
persistent
/>
@@ -190,10 +179,10 @@ export default function OpeningScreenDataTable({ rows, headers, error, setOpenin
selectRowEvent(row.id, row.isSelected)
+ onClick: (e: Event) => selectRowEvent(row.id, row.isSelected)
})
} />
- {row.cells.map((cell, j) => (
+ {row.cells.map((cell: any, j: number) => (
{cell.info.header === "status" ? (
@@ -253,12 +242,14 @@ export default function OpeningScreenDataTable({ rows, headers, error, setOpenin
pageSize={itemsPerPage}
pageSizes={[5, 20, 50]}
itemsPerPageText="Items per page"
- onChange={({ page, pageSize }) => {
- handlePageChange({ page, pageSize });
- handleItemsPerPageChange({ page,pageSize });
+ onChange={({ page, pageSize }:{ page: number, pageSize: number}) => {
+ handlePageChange( page );
+ handleItemsPerPageChange(page, pageSize);
}}
/>
) : null}
);
}
+
+export default OpeningScreenDataTable;
diff --git a/frontend/src/components/OpeningScreenDataTable/testData.js b/frontend/src/components/OpeningScreenDataTable/testData.ts
similarity index 99%
rename from frontend/src/components/OpeningScreenDataTable/testData.js
rename to frontend/src/components/OpeningScreenDataTable/testData.ts
index e69a2106..16751c64 100644
--- a/frontend/src/components/OpeningScreenDataTable/testData.js
+++ b/frontend/src/components/OpeningScreenDataTable/testData.ts
@@ -261,4 +261,4 @@ export const headers = [
key: 'actions',
header: 'Actions',
},
-];
\ No newline at end of file
+];
diff --git a/frontend/src/components/OpeningsTab/index.tsx b/frontend/src/components/OpeningsTab/index.tsx
index 2f8e0728..1928940e 100644
--- a/frontend/src/components/OpeningsTab/index.tsx
+++ b/frontend/src/components/OpeningsTab/index.tsx
@@ -93,7 +93,6 @@ const OpeningsTab: React.FC = ({showSpatial, setShowSpatial}) => {
)}
diff --git a/frontend/src/components/TableSkeleton/index.jsx b/frontend/src/components/TableSkeleton/index.jsx
deleted file mode 100644
index 277e5a8e..00000000
--- a/frontend/src/components/TableSkeleton/index.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import React, { useState } from 'react';
-import {
- DataTableSkeleton
-} from '@carbon/react';
-import './styles.scss'
-
-export default function TableSkeleton({ headers }) {
- return (
-
-
-
- );
-}
diff --git a/frontend/src/components/TableSkeleton/index.tsx b/frontend/src/components/TableSkeleton/index.tsx
new file mode 100644
index 00000000..33781367
--- /dev/null
+++ b/frontend/src/components/TableSkeleton/index.tsx
@@ -0,0 +1,23 @@
+import React, { useState } from 'react';
+import {
+ DataTableSkeleton
+} from '@carbon/react';
+import './styles.scss'
+
+interface Props {
+ headers: {
+ key: string;
+ header: string;
+ }[];
+}
+
+const TableSkeleton: React.FC = ({headers}) => (
+
+
+
+);
+
+export default TableSkeleton;
diff --git a/frontend/src/contexts/PaginationContext.ts b/frontend/src/contexts/PaginationContext.ts
new file mode 100644
index 00000000..d1d949aa
--- /dev/null
+++ b/frontend/src/contexts/PaginationContext.ts
@@ -0,0 +1,16 @@
+import { createContext } from "react";
+
+export interface PaginationContextData {
+ getCurrentData(): void;
+ currentPage: number;
+ totalPages: number;
+ handlePageChange(page: number): void;
+ handleItemsPerPageChange(page: number, pageSize: number): void;
+ itemsPerPage: number;
+ setPageData(data: any[]): void;
+ setInitialItemsPerPage(items: number): void;
+}
+
+const PaginationContext = createContext({} as PaginationContextData);
+
+export default PaginationContext;
diff --git a/frontend/src/contexts/PaginationProvider.tsx b/frontend/src/contexts/PaginationProvider.tsx
new file mode 100644
index 00000000..dd3e3644
--- /dev/null
+++ b/frontend/src/contexts/PaginationProvider.tsx
@@ -0,0 +1,62 @@
+import { useMemo, useState } from "react";
+import PaginationContext, { PaginationContextData } from "./PaginationContext";
+
+const PaginationProvider: React.FC<{children: React.ReactNode}> = ({ children }) => {
+ const [data, setData] = useState([]);
+ const [initialItemsPerPage, setInitialItemsPerPage] = useState(0);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [itemsPerPage, setItemsPerPage] = useState(5);
+
+ // Update the total number of pages when itemsPerPage changes
+ const totalPages = Math.ceil(data.length / itemsPerPage);
+
+ // Get the current page data
+ const getCurrentData = () => {
+ const start = (currentPage - 1) * itemsPerPage;
+ const end = start + itemsPerPage;
+ return data.slice(start, end);
+ };
+
+ // Update the current page when the user changes the page
+ const handlePageChange = (page: number) => {
+ setCurrentPage(page);
+ };
+
+ // Update the items per page when the user changes the value
+ const handleItemsPerPageChange = (page: number, pageSize: number) => {
+ setCurrentPage(page);
+ setItemsPerPage(pageSize);
+ };
+
+ const setPageData = (data: any[]): void => {
+ setData(data);
+ };
+
+ const contextValue: PaginationContextData = useMemo(() => ({
+ getCurrentData,
+ currentPage,
+ totalPages,
+ handlePageChange,
+ handleItemsPerPageChange,
+ itemsPerPage,
+ setPageData,
+ setInitialItemsPerPage,
+ }), [
+ getCurrentData,
+ currentPage,
+ totalPages,
+ handlePageChange,
+ handleItemsPerPageChange,
+ itemsPerPage,
+ setPageData,
+ setInitialItemsPerPage,
+ ]);
+
+ return (
+
+ { children }
+
+ )
+};
+
+export default PaginationProvider;
diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx
index 55b8e106..e14d5164 100644
--- a/frontend/src/index.tsx
+++ b/frontend/src/index.tsx
@@ -8,6 +8,7 @@ import store from './store'
import App from './App';
import { ThemePreference } from './utils/ThemePreference';
import { createRoot } from 'react-dom/client';
+import PaginationProvider from './contexts/PaginationProvider';
const container:any = document.getElementById('root');
const root = createRoot(container);
@@ -16,7 +17,9 @@ root.render(
-
+
+
+
diff --git a/frontend/src/services/OpeningService.js b/frontend/src/services/OpeningService.js
deleted file mode 100644
index 9e4b8568..00000000
--- a/frontend/src/services/OpeningService.js
+++ /dev/null
@@ -1,175 +0,0 @@
-import axios from 'axios';
-import { getAuthIdToken } from './AuthService';
-import { env } from '../env';
-
-const backendUrl = env.VITE_BACKEND_URL;
-
-export async function fetchRecentOpenings() {
- let authToken = getAuthIdToken();
- try {
- const response = await axios.get(backendUrl.concat("/api/openings/recent-openings?page=0&perPage=100"), {
- headers: {
- Authorization: `Bearer ${authToken}`
- }
- });
-
- const { data } = response;
-
- if (data && data.data) {
- // Extracting row information from the fetched data
- const rows = data.data.map(opening => ({
- id: opening.openingId.toString(),
- openingId: opening.openingId.toString(),
- fileId: opening.fileId ? opening.fileId : '-',
- cuttingPermit: opening.cuttingPermit ? opening.cuttingPermit : '-',
- timberMark: opening.timberMark ? opening.timberMark : '-',
- cutBlock: opening.cutBlock ? opening.cutBlock : '-',
- grossAreaHa: opening.grossAreaHa ? opening.grossAreaHa.toString() : '-',
- status: opening.status ? opening.status.description : '-',
- category: opening.category ? opening.category.code : '-',
- disturbanceStart: opening.disturbanceStart ? opening.disturbanceStart : '-',
- createdAt: opening.entryTimestamp ? opening.entryTimestamp.split('T')[0] : '-',
- lastViewed: opening.updateTimestamp ? opening.updateTimestamp.split('T')[0] : '-'
- }));
- // Returning the rows
- return rows;
- } else {
- console.log('No data found in the response.');
- return [];
- }
- } catch (error) {
- console.error('Error fetching recent openings:', error);
- throw error;
- }
-}
-
-export async function fetchOpeningsPerYear(orgUnitCode, statusCode, entryDateStart, entryDateEnd) {
- let authToken = await getAuthIdToken();
- try {
- // Construct URL with optional parameters
- let url = backendUrl.concat("/api/dashboard-metrics/submission-trends");
- if (orgUnitCode || statusCode || entryDateStart || entryDateEnd) {
- url += '?';
- if (orgUnitCode) url += `orgUnitCode=${orgUnitCode}&`;
- if (statusCode) url += `statusCode=${statusCode}&`;
- if (entryDateStart) url += `entryDateStart=${entryDateStart}&`;
- if (entryDateEnd) url += `entryDateEnd=${entryDateEnd}&`;
- // Remove trailing '&' if present
- url = url.replace(/&$/, '');
- }
-
- const response = await axios.get(url, {
- headers: {
- Authorization: `Bearer ${authToken}`
- }
- });
-
- const { data } = response;
-
- if (data && Array.isArray(data)) {
- // Format data for BarChartGrouped component
- const formattedData = data.map(item => ({
- group: "Openings",
- key: item.monthName,
- value: item.amount
- }));
-
- return formattedData;
- } else {
- console.log('No data found in the response.');
- return [];
- }
- } catch (error) {
- console.error('Error fetching openings per year:', error);
- throw error;
- }
-}
-
-export async function fetchFreeGrowingMilestones(orgUnitCode, clientNumber, entryDateStart, entryDateEnd) {
- let authToken = await getAuthIdToken();
- let url = backendUrl.concat("/api/dashboard-metrics/free-growing-milestones");
-
- // Construct URL with optional parameters
- if (orgUnitCode || clientNumber || entryDateStart || entryDateEnd) {
- url += '?';
- if (orgUnitCode) url += `orgUnitCode=${orgUnitCode}&`;
- if (clientNumber) url += `clientNumber=${clientNumber}&`;
- if (entryDateStart) url += `entryDateStart=${entryDateStart}&`;
- if (entryDateEnd) url += `entryDateEnd=${entryDateEnd}&`;
- // Remove trailing '&' if present
- url = url.replace(/&$/, '');
- }
- console.log("the url being called:")
- console.log(url)
-
- try {
- const response = await axios.get(url, {
- headers: {
- Authorization: `Bearer ${authToken}`
- }
- });
-
- const { data } = response;
-
- if (data && Array.isArray(data)) {
- // Transform data for DonutChartView component
- const transformedData = data.map(item => ({
- group: item.label,
- value: item.amount
- }));
-
- return transformedData;
- } else {
- console.log('No data found in the response.');
- return [];
- }
- } catch (error) {
- console.error('Error fetching free growing milestones:', error);
- throw error;
- }
-}
-
-export async function fetchRecentActions() {
- let authToken = await getAuthIdToken();
- try {
- // Comment out the actual API call for now
- // const response = await axios.get(backendUrl.concat("/api/dashboard-metrics/my-recent-actions/requests"));
- // headers: {
- // Authorization: `Bearer ${authToken}`
- // }
- // });
-
- // Temporarily use the sample data for testing
- // const { data } = response;
- const data = [
- {
- "activityType": "Update",
- "openingId": 1541297,
- "statusCode": "APP",
- "statusDescription": "Approved",
- "lastUpdatedLabel": "1 minute ago",
- "lastUpdated": "2024-05-16T19:59:21.635Z"
- },
- // Add more sample objects here if needed
- ];
-
- if (Array.isArray(data)) {
- // Transforming response data into a format consumable by the component
- const rows = data.map(action => ({
- activityType: action.activityType,
- openingID: action.openingId.toString(), // Convert openingId to string if needed
- status: action.statusDescription,
- lastUpdated: action.lastUpdatedLabel // Use lastUpdatedLabel from API
- }));
-
- // Returning the transformed data
- return rows;
- } else {
- console.log('No data found in the response.');
- return [];
- }
- } catch (error) {
- console.error('Error fetching recent actions:', error);
- throw error;
- }
-}
diff --git a/frontend/src/services/OpeningService.ts b/frontend/src/services/OpeningService.ts
new file mode 100644
index 00000000..4e1a3352
--- /dev/null
+++ b/frontend/src/services/OpeningService.ts
@@ -0,0 +1,196 @@
+import axios from 'axios';
+import { getAuthIdToken } from './AuthService';
+import { env } from '../env';
+
+const backendUrl = env.VITE_BACKEND_URL;
+
+interface IOpening {
+ openingId: number;
+ fileId: string;
+ cuttingPermit: string | null;
+ timberMark: string | null;
+ cutBlock: string | null;
+ grossAreaHa: number | null;
+ status: {code: string, description: string} | null;
+ category: {code: string, description: string} | null;
+ disturbanceStart: string | null;
+ entryTimestamp: string | null;
+ updateTimestamp: string | null;
+}
+
+export async function fetchRecentOpenings() {
+ let authToken = getAuthIdToken();
+ try {
+ const response = await axios.get(backendUrl.concat("/api/openings/recent-openings?page=0&perPage=100"), {
+ headers: {
+ Authorization: `Bearer ${authToken}`
+ }
+ });
+
+ if (response.status >= 200 && response.status < 300) {
+ const { data } = response;
+
+ if (data.data) {
+ // Extracting row information from the fetched data
+ const rows: any[] = data.data.map((opening: IOpening) => ({
+ id: opening.openingId.toString(),
+ openingId: opening.openingId.toString(),
+ fileId: opening.fileId ? opening.fileId : '-',
+ cuttingPermit: opening.cuttingPermit ? opening.cuttingPermit : '-',
+ timberMark: opening.timberMark ? opening.timberMark : '-',
+ cutBlock: opening.cutBlock ? opening.cutBlock : '-',
+ grossAreaHa: opening.grossAreaHa ? opening.grossAreaHa.toString() : '-',
+ status: opening.status ? opening.status.description : '-',
+ category: opening.category ? opening.category.code : '-',
+ disturbanceStart: opening.disturbanceStart ? opening.disturbanceStart : '-',
+ createdAt: opening.entryTimestamp ? opening.entryTimestamp.split('T')[0] : '-',
+ lastViewed: opening.updateTimestamp ? opening.updateTimestamp.split('T')[0] : '-'
+ }));
+
+ return rows;
+ }
+ }
+ return [];
+ } catch (error) {
+ console.error('Error fetching recent openings:', error);
+ throw error;
+ }
+}
+
+interface IOpeningPerYear {
+ orgUnitCode: string;
+ statusCode: string;
+ entryDateStart: string | null;
+ entryDateEnd: string | null;
+}
+
+export async function fetchOpeningsPerYear(props: IOpeningPerYear) {
+ let authToken = getAuthIdToken();
+ try {
+ // Construct URL with optional parameters
+ let url = backendUrl.concat("/api/dashboard-metrics/submission-trends");
+ if (props.orgUnitCode || props.statusCode || props.entryDateStart || props.entryDateEnd) {
+ url += '?';
+ if (props.orgUnitCode) url += `orgUnitCode=${props.orgUnitCode}&`;
+ if (props.statusCode) url += `statusCode=${props.statusCode}&`;
+ if (props.entryDateStart) url += `entryDateStart=${props.entryDateStart}&`;
+ if (props.entryDateEnd) url += `entryDateEnd=${props.entryDateEnd}&`;
+ // Remove trailing '&' if present
+ url = url.replace(/&$/, '');
+ }
+
+ const response = await axios.get(url, {
+ headers: {
+ Authorization: `Bearer ${authToken}`
+ }
+ });
+
+ const { data } = response;
+ if (data && Array.isArray(data)) {
+ // Format data for BarChartGrouped component
+ const formattedData = data.map(item => ({
+ group: "Openings",
+ key: item.monthName,
+ value: item.amount
+ }));
+
+ return formattedData;
+ } else {
+ return [];
+ }
+ } catch (error) {
+ console.error('Error fetching openings per year:', error);
+ throw error;
+ }
+}
+
+interface IFreeGrowingProps {
+ orgUnitCode: string;
+ clientNumber: string;
+ entryDateStart: string | null;
+ entryDateEnd: string | null;
+}
+
+export async function fetchFreeGrowingMilestones(props: IFreeGrowingProps) {
+ let authToken = getAuthIdToken();
+ let url = backendUrl.concat("/api/dashboard-metrics/free-growing-milestones");
+
+ // Construct URL with optional parameters
+ if (props.orgUnitCode || props.clientNumber || props.entryDateStart || props.entryDateEnd) {
+ url += '?';
+ if (props.orgUnitCode) url += `orgUnitCode=${props.orgUnitCode}&`;
+ if (props.clientNumber) url += `clientNumber=${props.clientNumber}&`;
+ if (props.entryDateStart) url += `entryDateStart=${props.entryDateStart}&`;
+ if (props.entryDateEnd) url += `entryDateEnd=${props.entryDateEnd}&`;
+ // Remove trailing '&' if present
+ url = url.replace(/&$/, '');
+ }
+
+ try {
+ const response = await axios.get(url, {
+ headers: {
+ Authorization: `Bearer ${authToken}`
+ }
+ });
+
+ const { data } = response;
+ if (data && Array.isArray(data)) {
+ // Transform data for DonutChartView component
+ const transformedData = data.map(item => ({
+ group: item.label,
+ value: item.amount
+ }));
+
+ return transformedData;
+ } else {
+ return [];
+ }
+ } catch (error) {
+ console.error('Error fetching free growing milestones:', error);
+ throw error;
+ }
+}
+
+export async function fetchRecentActions() {
+ let authToken = getAuthIdToken();
+ try {
+ // Comment out the actual API call for now
+ // const response = await axios.get(backendUrl.concat("/api/dashboard-metrics/my-recent-actions/requests"));
+ // headers: {
+ // Authorization: `Bearer ${authToken}`
+ // }
+ // });
+
+ // Temporarily use the sample data for testing
+ // const { data } = response;
+ const data = [
+ {
+ "activityType": "Update",
+ "openingId": 1541297,
+ "statusCode": "APP",
+ "statusDescription": "Approved",
+ "lastUpdatedLabel": "1 minute ago",
+ "lastUpdated": "2024-05-16T19:59:21.635Z"
+ },
+ // Add more sample objects here if needed
+ ];
+
+ if (Array.isArray(data)) {
+ // Transforming response data into a format consumable by the component
+ const rows = data.map(action => ({
+ activityType: action.activityType,
+ openingID: action.openingId.toString(), // Convert openingId to string if needed
+ status: action.statusDescription,
+ lastUpdated: action.lastUpdatedLabel // Use lastUpdatedLabel from API
+ }));
+
+ // Returning the transformed data
+ return rows;
+ } else {
+ return [];
+ }
+ } catch (error) {
+ console.error('Error fetching recent actions:', error);
+ throw error;
+ }
+}