From ab46fb9de403d1ab701e420fd7968c9b9591111f Mon Sep 17 00:00:00 2001 From: ahmedyasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Thu, 30 May 2024 01:13:00 +0300 Subject: [PATCH 1/9] Added Server Pagination and sorting implementation to datatable --- .../Endpoints/RequestVariables/index.tsx | 59 ++++++++---- GUI/src/components/DataTable/index.tsx | 28 ++++-- GUI/src/pages/ConnectionRequestsPage.tsx | 27 ++++-- GUI/src/pages/FaultyServicesPage.tsx | 95 ++++++++++--------- .../ConnectServiceToIntentModel.tsx | 23 ++--- 5 files changed, 141 insertions(+), 91 deletions(-) diff --git a/GUI/src/components/ApiEndpointCard/Endpoints/RequestVariables/index.tsx b/GUI/src/components/ApiEndpointCard/Endpoints/RequestVariables/index.tsx index 8461c440..1c5548b5 100644 --- a/GUI/src/components/ApiEndpointCard/Endpoints/RequestVariables/index.tsx +++ b/GUI/src/components/ApiEndpointCard/Endpoints/RequestVariables/index.tsx @@ -18,6 +18,7 @@ import { } from "../../../../types/request-variables"; import useServiceStore from "store/new-services.store"; import { getColumns } from "./columns"; +import { PaginationState, SortingState } from "@tanstack/react-table"; type RequestVariablesProps = { disableRawData?: boolean; @@ -45,6 +46,12 @@ const RequestVariables: React.FC = ({ const [jsonError, setJsonError] = useState(); const [key, setKey] = useState(0); const { setEndpoints, updateEndpointRawData, updateEndpointData } = useServiceStore(); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + + const [sorting, setSorting] = useState([]); const constructRow = (id: number, data: EndpointVariableData, nestedLevel: number): RequestVariablesRowData => { const value = isLive ? data.value : data.testValue; @@ -178,21 +185,21 @@ const RequestVariables: React.FC = ({ const deleteVariable = (rowData: RequestVariablesRowData) => { setEndpoints((prevEndpoints: EndpointData[]) => { - const newEndpoints : EndpointData[] = []; + const newEndpoints: EndpointData[] = []; for (const prevEndpoint of prevEndpoints) { - const defEndpoint = prevEndpoint.definedEndpoints.find(x => x.id === endpointData.id); + const defEndpoint = prevEndpoint.definedEndpoints.find((x) => x.id === endpointData.id); const endpoint = defEndpoint?.[requestTab.tab]; - if(defEndpoint && endpoint) { + if (defEndpoint && endpoint) { if (rowData.endpointVariableId && endpoint.variables.map((v) => v.id).includes(rowData.endpointVariableId)) { endpoint.variables = endpoint.variables.filter((v) => v.id !== rowData.endpointVariableId); } else { endpoint.variables - .filter(variable => ["schema", "array"].includes(variable.type)) - .forEach(variable => checkNestedVariables(rowData.endpointVariableId!, variable)); + .filter((variable) => ["schema", "array"].includes(variable.type)) + .forEach((variable) => checkNestedVariables(rowData.endpointVariableId!, variable)); } } - + newEndpoints.push(prevEndpoint); } return newEndpoints; @@ -228,19 +235,23 @@ const RequestVariables: React.FC = ({ onParametersChange(parameters); } }; - - const columns = useMemo(() => getColumns({ - rowsData, - updateParams, - requestTab, - deleteVariable, - setRowsData, - updateRowVariable, - requestValues, - isLive, - updateRowValue, - getTabsRowsData, - }), []); + + const columns = useMemo( + () => + getColumns({ + rowsData, + updateParams, + requestTab, + deleteVariable, + setRowsData, + updateRowVariable, + requestValues, + isLive, + updateRowValue, + getTabsRowsData, + }), + [] + ); const buildRawDataView = (): JSX.Element => { return ( @@ -328,7 +339,15 @@ const RequestVariables: React.FC = ({ buildRawDataView() ) : ( <> - +
)} diff --git a/GUI/src/components/DataTable/index.tsx b/GUI/src/components/DataTable/index.tsx index 11cfede5..3582a331 100644 --- a/GUI/src/components/DataTable/index.tsx +++ b/GUI/src/components/DataTable/index.tsx @@ -30,15 +30,19 @@ type DataTableProps = { data: any; columns: ColumnDef[]; tableBodyPrefix?: ReactNode; + isClientSide?: boolean; sortable?: boolean; filterable?: boolean; pagination?: PaginationState; - setPagination?: React.Dispatch>; + sorting?: SortingState; + setPagination?: (state: PaginationState) => void; + setSorting?: (state: SortingState) => void; globalFilter?: string; setGlobalFilter?: React.Dispatch>; columnVisibility?: VisibilityState; setColumnVisibility?: React.Dispatch>; disableHead?: boolean; + pagesCount?: number; meta?: TableMeta; }; @@ -80,18 +84,21 @@ const DataTable: FC = ({ tableBodyPrefix, sortable, filterable, + isClientSide = true, pagination, + sorting, setPagination, + setSorting, globalFilter, setGlobalFilter, columnVisibility, setColumnVisibility, disableHead, + pagesCount, meta, }) => { const id = useId(); const { t } = useTranslation(); - const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = React.useState([]); const tablePagination = pagination ?? { pageIndex: 0, @@ -116,12 +123,21 @@ const DataTable: FC = ({ onGlobalFilterChange: setGlobalFilter, onColumnVisibilityChange: setColumnVisibility, globalFilterFn: fuzzyFilter, - onSortingChange: setSorting, - onPaginationChange: setPagination, + onSortingChange: (updater) => { + if (typeof updater !== "function") return; + setSorting?.(updater(table.getState().sorting)); + }, + onPaginationChange: (updater) => { + if (typeof updater !== "function") return; + setPagination?.(updater(table.getState().pagination)); + }, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), ...(pagination && { getPaginationRowModel: getPaginationRowModel() }), ...(sortable && { getSortedRowModel: getSortedRowModel() }), + manualPagination: isClientSide ? undefined : true, + manualSorting: isClientSide ? undefined : true, + pageCount: isClientSide ? undefined : pagesCount, }); return ( @@ -146,9 +162,7 @@ const DataTable: FC = ({ )} {flexRender(header.column.columnDef.header, header.getContext())} - {filterable && header.column.getCanFilter() && ( - - )} + {filterable && header.column.getCanFilter() && } )} diff --git a/GUI/src/pages/ConnectionRequestsPage.tsx b/GUI/src/pages/ConnectionRequestsPage.tsx index 920502be..2acb341b 100644 --- a/GUI/src/pages/ConnectionRequestsPage.tsx +++ b/GUI/src/pages/ConnectionRequestsPage.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import { createColumnHelper } from "@tanstack/react-table"; +import { PaginationState, SortingState, createColumnHelper } from "@tanstack/react-table"; import { format } from "date-fns"; import { AiFillCheckCircle, AiFillCloseCircle } from "react-icons/ai"; import { Card, DataTable, Icon } from "components"; @@ -11,6 +11,12 @@ import withAuthorization, { ROLES } from "hoc/with-authorization"; const ConnectionRequestsPage: React.FC = () => { const { t } = useTranslation(); const [triggers, setTriggers] = useState(undefined); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + + const [sorting, setSorting] = useState([]); const loadConnectionRequests = () => { useServiceStore @@ -42,7 +48,15 @@ const ConnectionRequestsPage: React.FC = () => { <>

{t("connectionRequests.title")}

- + ); @@ -109,10 +123,7 @@ const getColumns = (respondToConnectionRequest: (result: boolean, tigger: Trigge size: "1%", }, }), - ] -} + ]; +}; -export default withAuthorization(ConnectionRequestsPage, [ - ROLES.ROLE_ADMINISTRATOR, - ROLES.ROLE_SERVICE_MANAGER, -]); +export default withAuthorization(ConnectionRequestsPage, [ROLES.ROLE_ADMINISTRATOR, ROLES.ROLE_SERVICE_MANAGER]); diff --git a/GUI/src/pages/FaultyServicesPage.tsx b/GUI/src/pages/FaultyServicesPage.tsx index f1ad1f37..0293830b 100644 --- a/GUI/src/pages/FaultyServicesPage.tsx +++ b/GUI/src/pages/FaultyServicesPage.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Button, Card, DataTable, Icon, Track } from "../components"; -import { Row, createColumnHelper } from "@tanstack/react-table"; +import { PaginationState, Row, SortingState, createColumnHelper } from "@tanstack/react-table"; import { MdOutlineRemoveRedEye } from "react-icons/md"; import Popup from "../components/Popup"; import axios from "axios"; @@ -27,8 +27,12 @@ const FaultyServicesPage: React.FC = () => { const { t } = useTranslation(); const [viewFaultyServiceLog, setViewFaultyServiceLog] = useState(null); const [data, setData] = useState([]); - const columns = useMemo(() => getColumns(setViewFaultyServiceLog), []); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + const [sorting, setSorting] = useState([]); useEffect(() => { axios.get(getFaultyServices()).then((res) => setData(res.data)); @@ -124,6 +128,10 @@ const FaultyServicesPage: React.FC = () => { filterable data={data} columns={columns} + sorting={sorting} + pagination={pagination} + setSorting={setSorting} + setPagination={setPagination} /> @@ -134,47 +142,44 @@ const FaultyServicesPage: React.FC = () => { const getColumns = (setViewFaultyServiceLog: (data: FaultyService) => void) => { const columnHelper = createColumnHelper(); - return [ - columnHelper.accessor("service", { - header: i18n.t("logs.service") ?? "", - cell: (props) => {props.getValue().split("/").pop()}, - }), - columnHelper.accessor("serviceMethod", { - header: i18n.t("logs.method") ?? "", - }), - columnHelper.accessor("errorCode", { - header: i18n.t("logs.errorCode") ?? "", - }), - columnHelper.accessor("stepName", { - header: i18n.t("logs.failedStep") ?? "", - }), - columnHelper.accessor("timestamp", { - header: i18n.t("logs.failedTime") ?? "", - cell: (props) => {format(new Date(parseInt(props.getValue() ?? "0")), "dd-MM-yyyy HH:mm:ss")}, - filterFn: (row: Row, _, filterValue) => { - return format(new Date(parseInt(row.original.timestamp ?? "0")), "dd-MM-yyyy HH:mm:ss") - .toLowerCase() - .includes(filterValue.toLowerCase()); - }, - }), - columnHelper.display({ - id: "view", - meta: { - size: 90, - }, - cell: (props) => ( - - - - ), - }), - ]; -} + return [ + columnHelper.accessor("service", { + header: i18n.t("logs.service") ?? "", + cell: (props) => {props.getValue().split("/").pop()}, + }), + columnHelper.accessor("serviceMethod", { + header: i18n.t("logs.method") ?? "", + }), + columnHelper.accessor("errorCode", { + header: i18n.t("logs.errorCode") ?? "", + }), + columnHelper.accessor("stepName", { + header: i18n.t("logs.failedStep") ?? "", + }), + columnHelper.accessor("timestamp", { + header: i18n.t("logs.failedTime") ?? "", + cell: (props) => {format(new Date(parseInt(props.getValue() ?? "0")), "dd-MM-yyyy HH:mm:ss")}, + filterFn: (row: Row, _, filterValue) => { + return format(new Date(parseInt(row.original.timestamp ?? "0")), "dd-MM-yyyy HH:mm:ss") + .toLowerCase() + .includes(filterValue.toLowerCase()); + }, + }), + columnHelper.display({ + id: "view", + meta: { + size: 90, + }, + cell: (props) => ( + + + + ), + }), + ]; +}; -export default withAuthorization(FaultyServicesPage, [ - ROLES.ROLE_ADMINISTRATOR, - ROLES.ROLE_SERVICE_MANAGER, -]); +export default withAuthorization(FaultyServicesPage, [ROLES.ROLE_ADMINISTRATOR, ROLES.ROLE_SERVICE_MANAGER]); diff --git a/GUI/src/pages/Integration/ConnectServiceToIntentModel.tsx b/GUI/src/pages/Integration/ConnectServiceToIntentModel.tsx index 09981634..f8d0c2a5 100644 --- a/GUI/src/pages/Integration/ConnectServiceToIntentModel.tsx +++ b/GUI/src/pages/Integration/ConnectServiceToIntentModel.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useMemo, useState } from "react"; -import { createColumnHelper, PaginationState } from "@tanstack/react-table"; +import { createColumnHelper, PaginationState, SortingState } from "@tanstack/react-table"; import { useTranslation } from "react-i18next"; import { MdOutlineArrowForward } from "react-icons/md"; import useServiceStore from "store/services.store"; @@ -19,6 +19,7 @@ const ConnectServiceToIntentModel: FC = ({ onM pageIndex: 0, pageSize: 8, }); + const [sorting, setSorting] = useState([]); const [intents, setIntents] = useState(undefined); const [selectedIntent, setSelectedIntent] = useState(); const [showConfirmationModal, setShowConfirmationModal] = useState(false); @@ -37,10 +38,11 @@ const ConnectServiceToIntentModel: FC = ({ onM }, []); const intentColumns = useMemo( - () => getColumns((intent) => { - setSelectedIntent(intent); - setShowConfirmationModal(true); - }), + () => + getColumns((intent) => { + setSelectedIntent(intent); + setShowConfirmationModal(true); + }), [] ); @@ -80,8 +82,10 @@ const ConnectServiceToIntentModel: FC = ({ onM globalFilter={filter} setGlobalFilter={setFilter} sortable + sorting={sorting} pagination={pagination} setPagination={setPagination} + setSorting={setSorting} /> )} {showConfirmationModal && ( @@ -114,10 +118,7 @@ const getColumns = (onClick: (intent: Intent) => void) => { columnHelper.display({ id: "connect", cell: (props) => ( - @@ -126,7 +127,7 @@ const getColumns = (onClick: (intent: Intent) => void) => { size: "1%", }, }), - ] -} + ]; +}; export default ConnectServiceToIntentModel; From 6a394cf4929f54cc1d9e8263a5ec2efb08ece4d4 Mon Sep 17 00:00:00 2001 From: ahmedyasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Fri, 31 May 2024 02:47:32 +0300 Subject: [PATCH 2/9] Added Pagination to overview --- .../services/get-common-services-list.sql | 34 ++++++++ DSL/Resql/services/get-services-list.sql | 2 +- DSL/Ruuter/GET/common-services.yml | 33 +++++++ GUI/src/components/ServicesTable/index.tsx | 86 ++++++++++++------- GUI/src/pages/OverviewPage.tsx | 12 +-- GUI/src/resources/api-constants.ts | 1 + GUI/src/store/services.store.ts | 33 ++++++- 7 files changed, 154 insertions(+), 47 deletions(-) create mode 100644 DSL/Resql/services/get-common-services-list.sql create mode 100644 DSL/Ruuter/GET/common-services.yml diff --git a/DSL/Resql/services/get-common-services-list.sql b/DSL/Resql/services/get-common-services-list.sql new file mode 100644 index 00000000..8e5b79d6 --- /dev/null +++ b/DSL/Resql/services/get-common-services-list.sql @@ -0,0 +1,34 @@ +WITH MaxServices AS ( + SELECT MAX(id) AS maxId + FROM services + GROUP BY service_id +) +SELECT + id, + name, + description, + current_state AS state, + ruuter_type AS type, + is_common AS isCommon, + structure::json, + subquery.endpoints::json AS endpoints, + service_id +FROM services +JOIN MaxServices ON id = maxId +JOIN ( + SELECT jsonb_agg(endpoint) AS endpoints + FROM ( + SELECT DISTINCT endpoint + FROM ( + SELECT endpoint::jsonb + FROM services, json_array_elements(endpoints) AS endpoint + WHERE (endpoint->>'isCommon')::boolean = true + UNION + SELECT endpoint::jsonb + FROM services, json_array_elements(endpoints) AS endpoint, MaxServices + WHERE id = maxId + ) AS combined_endpoints + ) subquery +) subquery ON true +WHERE NOT deleted AND is_common +ORDER BY id ASC; diff --git a/DSL/Resql/services/get-services-list.sql b/DSL/Resql/services/get-services-list.sql index c44ed0f2..c849a6f8 100644 --- a/DSL/Resql/services/get-services-list.sql +++ b/DSL/Resql/services/get-services-list.sql @@ -30,5 +30,5 @@ JOIN ( ) AS combined_endpoints ) subquery ) subquery ON true -WHERE NOT deleted +WHERE NOT deleted AND NOT is_common ORDER BY id ASC; diff --git a/DSL/Ruuter/GET/common-services.yml b/DSL/Ruuter/GET/common-services.yml new file mode 100644 index 00000000..f5d04ddf --- /dev/null +++ b/DSL/Ruuter/GET/common-services.yml @@ -0,0 +1,33 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SERVICES'" + method: get + accepts: json + returns: json + namespace: service + +get_services_list: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-common-services-list" + result: services_res + +assign_services_result: + assign: + services: ${services_res.response.body} + +get_approved_triggers: + call: http.post + args: + url: "[#SERVICE_TRAINING_RESQL]/get-approved-tiggers" + result: triggers_res + +assign_training_result: + assign: + triggers: ${triggers_res.response.body} + +return_ok: + status: 200 + return: ${[services, triggers]} + next: end diff --git a/GUI/src/components/ServicesTable/index.tsx b/GUI/src/components/ServicesTable/index.tsx index a28e61b1..e9fbc7be 100644 --- a/GUI/src/components/ServicesTable/index.tsx +++ b/GUI/src/components/ServicesTable/index.tsx @@ -1,4 +1,4 @@ -import { FC, useMemo, useState } from "react"; +import { FC, useEffect, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { Button, Card, Modal, Track } from ".."; @@ -11,6 +11,7 @@ import { Trigger } from "types/Trigger"; import { getColumns } from "./columns"; import "../../styles/main.scss"; import "./ServicesTable.scss"; +import { PaginationState, SortingState } from "@tanstack/react-table"; type ServicesTableProps = { isCommon?: boolean; @@ -25,9 +26,22 @@ const ServicesTable: FC = ({ isCommon = false }) => { const [popupText, setPopupText] = useState(""); const [readyPopupText, setReadyPopupText] = useState(""); const [isReadyStatusChecking, setIsReadyStatusChecking] = useState(false); - const services = useServiceListStore((state) => state.services.filter((x) => x.isCommon === isCommon)); + const services = useServiceListStore((state) => (isCommon ? state.commonServices : state.notCommonServices)); const navigate = useNavigate(); const [selectedConnectionTrigger, setSelectedConnectionTrigger] = useState(); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 8, + }); + const [sorting, setSorting] = useState([]); + + useEffect(() => { + if (!isCommon) { + useServiceListStore.getState().loadServicesList(); + } else { + useServiceListStore.getState().loadCommonServicesList(); + } + }, []); const checkIntentConnection = () => { useServiceListStore.getState().checkServiceIntentConnection( @@ -46,21 +60,25 @@ const ServicesTable: FC = ({ isCommon = false }) => { } ); }; - - const columns = useMemo(() => getColumns({ - isCommon, - navigate, - checkIntentConnection, - hideDeletePopup: () => setIsDeletePopupVisible(true), - showStatePopup: (text: string) => { - setPopupText(text); - setIsStatePopupVisible(true); - }, - showReadyPopup: () => { - setIsReadyStatusChecking(true); - setIsReadyPopupVisible(true); - } - }), []); + + const columns = useMemo( + () => + getColumns({ + isCommon, + navigate, + checkIntentConnection, + hideDeletePopup: () => setIsDeletePopupVisible(true), + showStatePopup: (text: string) => { + setPopupText(text); + setIsStatePopupVisible(true); + }, + showReadyPopup: () => { + setIsReadyStatusChecking(true); + setIsReadyPopupVisible(true); + }, + }), + [] + ); const changeServiceState = (activate: boolean = false, draft: boolean = false) => { useServiceListStore.getState().changeServiceState( @@ -110,19 +128,17 @@ const ServicesTable: FC = ({ isCommon = false }) => { }; const getChangeServiceStateButtonTitle = () => { - if(popupText === t("overview.popup.setInactive")) - return t("overview.popup.deactivate"); - if(popupText === t("overview.popup.setReady")) - return t("overview.popup.setState"); + if (popupText === t("overview.popup.setInactive")) return t("overview.popup.deactivate"); + if (popupText === t("overview.popup.setReady")) return t("overview.popup.setState"); return t("overview.popup.activate"); - } + }; const getActiveAndConnectionButton = () => { - if(readyPopupText === t("overview.popup.setActive")) { - return + if (readyPopupText === t("overview.popup.setActive")) { + return ; } - if(readyPopupText === t("overview.popup.connectionPending")) { - return + if (readyPopupText === t("overview.popup.connectionPending")) { + return ; } return ( - ) - } + ); + }; return ( @@ -159,9 +175,7 @@ const ServicesTable: FC = ({ isCommon = false }) => { {popupText === t("overview.popup.setInactive") && ( )} - + )} @@ -191,7 +205,15 @@ const ServicesTable: FC = ({ isCommon = false }) => { onConnect={(intent: Intent) => requestServiceIntentConnection(intent.intent)} /> )} - + ); }; diff --git a/GUI/src/pages/OverviewPage.tsx b/GUI/src/pages/OverviewPage.tsx index bdc34e80..cc0729e4 100644 --- a/GUI/src/pages/OverviewPage.tsx +++ b/GUI/src/pages/OverviewPage.tsx @@ -1,21 +1,16 @@ -import React, { useEffect } from "react"; +import React from "react"; import { useTranslation } from "react-i18next"; import { Button, Track } from "../components"; import { trainingModuleTraining } from "../resources/api-constants"; import ServicesTable from "../components/ServicesTable"; import { useNavigate } from "react-router-dom"; import { ROUTES } from "../resources/routes-constants"; -import useServiceListStore from "store/services.store"; import withAuthorization, { ROLES } from "hoc/with-authorization"; const OverviewPage: React.FC = () => { const { t } = useTranslation(); const navigate = useNavigate(); - useEffect(() => { - useServiceListStore.getState().loadServicesList(); - }, []); - return ( <> @@ -35,7 +30,4 @@ const OverviewPage: React.FC = () => { ); }; -export default withAuthorization(OverviewPage, [ - ROLES.ROLE_ADMINISTRATOR, - ROLES.ROLE_SERVICE_MANAGER, -]); +export default withAuthorization(OverviewPage, [ROLES.ROLE_ADMINISTRATOR, ROLES.ROLE_SERVICE_MANAGER]); diff --git a/GUI/src/resources/api-constants.ts b/GUI/src/resources/api-constants.ts index 949e78bc..f756ec0c 100644 --- a/GUI/src/resources/api-constants.ts +++ b/GUI/src/resources/api-constants.ts @@ -22,6 +22,7 @@ export const testService = (state: ServiceState, serviceName: string): string => `${baseUrl}/services/${state.toLowerCase()}/${serviceName}`; export const editService = (id: string): string => `${baseUrl}/services/edit?id=${id}`; export const getServicesList = (): string => `${baseUrl}/services`; +export const getCommonServicesList = (): string => `${baseUrl}/common-services`; export const getConnectionRequests = (): string => `${baseUrl}/services/connection-requests`; export const getAvailableIntents = (): string => `${baseUrl}/services/available-intents`; export const jsonToYml = (): string => `${baseUrl}/saveJsonToYml`; diff --git a/GUI/src/store/services.store.ts b/GUI/src/store/services.store.ts index 0fae2d63..a37baf50 100644 --- a/GUI/src/store/services.store.ts +++ b/GUI/src/store/services.store.ts @@ -5,6 +5,7 @@ import { changeServiceStatus, deleteService as deleteServiceApi, getAvailableIntents, + getCommonServicesList, getConnectionRequests, getServicesList, requestServiceIntentConnection, @@ -20,6 +21,7 @@ interface ServiceStoreState { commonServices: Service[]; notCommonServices: Service[]; loadServicesList: () => Promise; + loadCommonServicesList: () => Promise; deleteService: (id: string) => Promise; selectedService: Service | undefined; setSelectedService: (service: Service) => void; @@ -79,10 +81,32 @@ const useServiceListStore = create((set, get, store) => ({ ) || []; set({ - services, - commonServices: services.filter((e: Service) => e.isCommon === true), - notCommonServices: services.filter((e: Service) => e.isCommon === false), + notCommonServices: services, + }); + }, + loadCommonServicesList: async () => { + const result = await axios.get(getCommonServicesList()); + const triggers = result.data.response[1]; + const services = + result.data.response[0].map?.( + (item: any) => + ({ + id: item.id, + name: item.name, + description: item.description, + state: item.state, + type: item.type, + isCommon: item.iscommon, + serviceId: item.serviceId, + usedCount: 0, + linkedIntent: triggers.find((e: Trigger) => e.service === item.serviceId)?.intent || "", + } as Service) + ) || []; + + set({ + commonServices: services, }); + console.log(get().notCommonServices); }, deleteService: async (id) => { const services = get().services.filter((e: Service) => e.serviceId !== id); @@ -100,7 +124,6 @@ const useServiceListStore = create((set, get, store) => ({ }, changeServiceState: async (onEnd, successMessage, errorMessage, activate, draft) => { const selectedService = get().selectedService; - console.log(selectedService); if (!selectedService) return; try { @@ -122,6 +145,7 @@ const useServiceListStore = create((set, get, store) => ({ }); useToastStore.getState().success({ title: successMessage }); await useServiceListStore.getState().loadServicesList(); + await useServiceListStore.getState().loadCommonServicesList(); } catch (_) { useToastStore.getState().error({ title: errorMessage }); } @@ -178,6 +202,7 @@ const useServiceListStore = create((set, get, store) => ({ }); useToastStore.getState().success({ title: successMessage }); await useServiceListStore.getState().loadServicesList(); + await useServiceListStore.getState().loadCommonServicesList(); } catch (_) { useToastStore.getState().error({ title: errorMessage }); } From 3fc91f72a4def9e9b3d33b312d25b4ea05c7ab09 Mon Sep 17 00:00:00 2001 From: ahmedyasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Fri, 31 May 2024 04:16:25 +0300 Subject: [PATCH 3/9] Added Server Side Pagination & Sorting to overview --- .../services/get-common-services-list.sql | 11 ++++- DSL/Resql/services/get-services-list.sql | 11 ++++- DSL/Ruuter/{GET => POST}/common-services.yml | 6 ++- DSL/Ruuter/{GET => POST}/services.yml | 8 +++- GUI/src/components/ServicesTable/index.tsx | 35 +++++++++++--- GUI/src/store/services.store.ts | 47 ++++++++++++------- GUI/src/types/service.ts | 1 + 7 files changed, 89 insertions(+), 30 deletions(-) rename DSL/Ruuter/{GET => POST}/common-services.yml (75%) rename DSL/Ruuter/{GET => POST}/services.yml (80%) diff --git a/DSL/Resql/services/get-common-services-list.sql b/DSL/Resql/services/get-common-services-list.sql index 8e5b79d6..c331aeb9 100644 --- a/DSL/Resql/services/get-common-services-list.sql +++ b/DSL/Resql/services/get-common-services-list.sql @@ -12,7 +12,8 @@ SELECT is_common AS isCommon, structure::json, subquery.endpoints::json AS endpoints, - service_id + service_id, + CEIL(COUNT(*) OVER() / :page_size::DECIMAL) AS total_pages FROM services JOIN MaxServices ON id = maxId JOIN ( @@ -31,4 +32,10 @@ JOIN ( ) subquery ) subquery ON true WHERE NOT deleted AND is_common -ORDER BY id ASC; +ORDER BY + CASE WHEN :sorting = 'id asc' THEN id END ASC, + CASE WHEN :sorting = 'name asc' THEN name END ASC, + CASE WHEN :sorting = 'name desc' THEN name END DESC, + CASE WHEN :sorting = 'state asc' THEN current_state END ASC, + CASE WHEN :sorting = 'state desc' THEN current_state END DESC +OFFSET ((GREATEST(:page, 1) - 1) * :page_size) LIMIT :page_size; diff --git a/DSL/Resql/services/get-services-list.sql b/DSL/Resql/services/get-services-list.sql index c849a6f8..51ca1eec 100644 --- a/DSL/Resql/services/get-services-list.sql +++ b/DSL/Resql/services/get-services-list.sql @@ -12,7 +12,8 @@ SELECT is_common AS isCommon, structure::json, subquery.endpoints::json AS endpoints, - service_id + service_id, + CEIL(COUNT(*) OVER() / :page_size::DECIMAL) AS total_pages FROM services JOIN MaxServices ON id = maxId JOIN ( @@ -31,4 +32,10 @@ JOIN ( ) subquery ) subquery ON true WHERE NOT deleted AND NOT is_common -ORDER BY id ASC; +ORDER BY + CASE WHEN :sorting = 'id asc' THEN id END ASC, + CASE WHEN :sorting = 'name asc' THEN name END ASC, + CASE WHEN :sorting = 'name desc' THEN name END DESC, + CASE WHEN :sorting = 'state asc' THEN current_state END ASC, + CASE WHEN :sorting = 'state desc' THEN current_state END DESC +OFFSET ((GREATEST(:page, 1) - 1) * :page_size) LIMIT :page_size; diff --git a/DSL/Ruuter/GET/common-services.yml b/DSL/Ruuter/POST/common-services.yml similarity index 75% rename from DSL/Ruuter/GET/common-services.yml rename to DSL/Ruuter/POST/common-services.yml index f5d04ddf..2f7e780e 100644 --- a/DSL/Ruuter/GET/common-services.yml +++ b/DSL/Ruuter/POST/common-services.yml @@ -1,7 +1,7 @@ declaration: call: declare version: 0.1 - description: "Decription placeholder for 'SERVICES'" + description: "Decription placeholder for 'COMMON SERVICES'" method: get accepts: json returns: json @@ -11,6 +11,10 @@ get_services_list: call: http.post args: url: "[#SERVICE_RESQL]/get-common-services-list" + body: + page: ${incoming.body.page} + page_size: ${incoming.body.page_size} + sorting: ${incoming.body.sorting} result: services_res assign_services_result: diff --git a/DSL/Ruuter/GET/services.yml b/DSL/Ruuter/POST/services.yml similarity index 80% rename from DSL/Ruuter/GET/services.yml rename to DSL/Ruuter/POST/services.yml index 50222e44..8ef3d0f1 100644 --- a/DSL/Ruuter/GET/services.yml +++ b/DSL/Ruuter/POST/services.yml @@ -11,10 +11,14 @@ get_services_list: call: http.post args: url: "[#SERVICE_RESQL]/get-services-list" + body: + page: ${incoming.body.page} + page_size: ${incoming.body.page_size} + sorting: ${incoming.body.sorting} result: services_res assign_services_result: - assign: + assign: services: ${services_res.response.body} get_approved_triggers: @@ -24,7 +28,7 @@ get_approved_triggers: result: triggers_res assign_training_result: - assign: + assign: triggers: ${triggers_res.response.body} return_ok: diff --git a/GUI/src/components/ServicesTable/index.tsx b/GUI/src/components/ServicesTable/index.tsx index e9fbc7be..8fe2bc6f 100644 --- a/GUI/src/components/ServicesTable/index.tsx +++ b/GUI/src/components/ServicesTable/index.tsx @@ -31,15 +31,15 @@ const ServicesTable: FC = ({ isCommon = false }) => { const [selectedConnectionTrigger, setSelectedConnectionTrigger] = useState(); const [pagination, setPagination] = useState({ pageIndex: 0, - pageSize: 8, + pageSize: 10, }); const [sorting, setSorting] = useState([]); useEffect(() => { if (!isCommon) { - useServiceListStore.getState().loadServicesList(); + useServiceListStore.getState().loadServicesList(pagination, sorting); } else { - useServiceListStore.getState().loadCommonServicesList(); + useServiceListStore.getState().loadCommonServicesList(pagination, sorting); } }, []); @@ -89,7 +89,9 @@ const ServicesTable: FC = ({ isCommon = false }) => { t("overview.service.toast.updated"), t("overview.service.toast.failed.state"), activate, - draft + draft, + pagination, + sorting ); }; @@ -110,7 +112,9 @@ const ServicesTable: FC = ({ isCommon = false }) => { () => setIsIntentConnectionPopupVisible(false), t("overview.service.toast.connectedToIntentSuccessfully"), t("overview.service.toast.failed.failedToConnectToIntent"), - intent + intent, + pagination, + sorting ); }; @@ -211,8 +215,25 @@ const ServicesTable: FC = ({ isCommon = false }) => { columns={columns} pagination={pagination} sorting={sorting} - setPagination={setPagination} - setSorting={setSorting} + setPagination={(state: PaginationState) => { + if (state.pageIndex === pagination.pageIndex && state.pageSize === pagination.pageSize) return; + setPagination(state); + if (!isCommon) { + useServiceListStore.getState().loadServicesList(state, sorting); + } else { + useServiceListStore.getState().loadCommonServicesList(state, sorting); + } + }} + setSorting={(state: SortingState) => { + setSorting(state); + if (!isCommon) { + useServiceListStore.getState().loadServicesList(pagination, state); + } else { + useServiceListStore.getState().loadCommonServicesList(pagination, state); + } + }} + isClientSide={false} + pagesCount={services[services.length - 1]?.totalPages ?? 1} /> ); diff --git a/GUI/src/store/services.store.ts b/GUI/src/store/services.store.ts index a37baf50..e1815de4 100644 --- a/GUI/src/store/services.store.ts +++ b/GUI/src/store/services.store.ts @@ -15,13 +15,14 @@ import { Service, ServiceState } from "types"; import useToastStore from "./toasts.store"; import { Trigger } from "types/Trigger"; import { Intent } from "types/Intent"; +import { PaginationState, SortingState } from "@tanstack/react-table"; interface ServiceStoreState { services: Service[]; commonServices: Service[]; notCommonServices: Service[]; - loadServicesList: () => Promise; - loadCommonServicesList: () => Promise; + loadServicesList: (pagination: PaginationState, sorting: SortingState) => Promise; + loadCommonServicesList: (pagination: PaginationState, sorting: SortingState) => Promise; deleteService: (id: string) => Promise; selectedService: Service | undefined; setSelectedService: (service: Service) => void; @@ -30,7 +31,9 @@ interface ServiceStoreState { successMessage: string, errorMessage: string, activate: boolean, - draft: boolean + draft: boolean, + pagination: PaginationState, + sorting: SortingState ) => Promise; checkServiceIntentConnection: (onConnected: (response: Trigger) => void, onNotConnected: () => void) => Promise; deleteSelectedService: (onEnd: () => void, successMessage: string, errorMessage: string) => Promise; @@ -38,7 +41,9 @@ interface ServiceStoreState { onEnd: () => void, successMessage: string, errorMessage: string, - intent: string + intent: string, + pagination: PaginationState, + sorting: SortingState ) => Promise; loadRequestsList: (onEnd: (requests: Trigger[]) => void, errorMessage: string) => Promise; loadAvailableIntentsList: (onEnd: (requests: Intent[]) => void, errorMessage: string) => Promise; @@ -61,8 +66,13 @@ const useServiceListStore = create((set, get, store) => ({ services: [], commonServices: [], notCommonServices: [], - loadServicesList: async () => { - const result = await axios.get(getServicesList()); + loadServicesList: async (pagination, sorting) => { + const sort = sorting.length === 0 ? "id asc" : sorting[0].id + " " + (sorting[0].desc ? "desc" : "asc"); + const result = await axios.post(getServicesList(), { + page: pagination.pageIndex + 1, + page_size: pagination.pageSize, + sorting: sort, + }); const triggers = result.data.response[1]; const services = result.data.response[0].map?.( @@ -76,6 +86,7 @@ const useServiceListStore = create((set, get, store) => ({ isCommon: item.iscommon, serviceId: item.serviceId, usedCount: 0, + totalPages: item.totalPages, linkedIntent: triggers.find((e: Trigger) => e.service === item.serviceId)?.intent || "", } as Service) ) || []; @@ -84,8 +95,13 @@ const useServiceListStore = create((set, get, store) => ({ notCommonServices: services, }); }, - loadCommonServicesList: async () => { - const result = await axios.get(getCommonServicesList()); + loadCommonServicesList: async (pagination, sorting) => { + const sort = sorting.length === 0 ? "id asc" : sorting[0].id + " " + (sorting[0].desc ? "desc" : "asc"); + const result = await axios.post(getCommonServicesList(), { + page: pagination.pageIndex + 1, + page_size: pagination.pageSize, + sorting: sort, + }); const triggers = result.data.response[1]; const services = result.data.response[0].map?.( @@ -98,6 +114,7 @@ const useServiceListStore = create((set, get, store) => ({ type: item.type, isCommon: item.iscommon, serviceId: item.serviceId, + totalPages: item.totalPages, usedCount: 0, linkedIntent: triggers.find((e: Trigger) => e.service === item.serviceId)?.intent || "", } as Service) @@ -106,12 +123,10 @@ const useServiceListStore = create((set, get, store) => ({ set({ commonServices: services, }); - console.log(get().notCommonServices); }, deleteService: async (id) => { const services = get().services.filter((e: Service) => e.serviceId !== id); set({ - services, commonServices: services.filter((e: Service) => e.isCommon === true), notCommonServices: services.filter((e: Service) => e.isCommon === false), }); @@ -122,7 +137,7 @@ const useServiceListStore = create((set, get, store) => ({ selectedService: service, }); }, - changeServiceState: async (onEnd, successMessage, errorMessage, activate, draft) => { + changeServiceState: async (onEnd, successMessage, errorMessage, activate, draft, pagination, sorting) => { const selectedService = get().selectedService; if (!selectedService) return; @@ -144,8 +159,8 @@ const useServiceListStore = create((set, get, store) => ({ type: selectedService.type, }); useToastStore.getState().success({ title: successMessage }); - await useServiceListStore.getState().loadServicesList(); - await useServiceListStore.getState().loadCommonServicesList(); + await useServiceListStore.getState().loadServicesList(pagination, sorting); + await useServiceListStore.getState().loadCommonServicesList(pagination, sorting); } catch (_) { useToastStore.getState().error({ title: errorMessage }); } @@ -190,7 +205,7 @@ const useServiceListStore = create((set, get, store) => ({ }); onEnd(); }, - requestServiceIntentConnection: async (onEnd, successMessage, errorMessage, intent) => { + requestServiceIntentConnection: async (onEnd, successMessage, errorMessage, intent, pagination, sorting) => { const selectedService = get().selectedService; if (!selectedService) return; @@ -201,8 +216,8 @@ const useServiceListStore = create((set, get, store) => ({ intent: intent, }); useToastStore.getState().success({ title: successMessage }); - await useServiceListStore.getState().loadServicesList(); - await useServiceListStore.getState().loadCommonServicesList(); + await useServiceListStore.getState().loadServicesList(pagination, sorting); + await useServiceListStore.getState().loadCommonServicesList(pagination, sorting); } catch (_) { useToastStore.getState().error({ title: errorMessage }); } diff --git a/GUI/src/types/service.ts b/GUI/src/types/service.ts index 76e954ec..e5e8f72a 100644 --- a/GUI/src/types/service.ts +++ b/GUI/src/types/service.ts @@ -12,4 +12,5 @@ export interface Service { readonly endpoints: any; readonly serviceId: string; readonly linkedIntent: string; + readonly totalPages: number; } From 37bc731166b77d318adfb0d7e123dac935b6a279 Mon Sep 17 00:00:00 2001 From: ahmedyasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Fri, 31 May 2024 04:29:31 +0300 Subject: [PATCH 4/9] Added Pagination & Sorting to available intents --- DSL/Resql/training/get-available-intents.sql | 7 +++++-- .../services/available-intents.yml | 4 ++++ .../ConnectServiceToIntentModel.tsx | 21 ++++++++++++++----- GUI/src/store/services.store.ts | 16 +++++++++++--- GUI/src/types/Intent.ts | 1 + 5 files changed, 39 insertions(+), 10 deletions(-) rename DSL/Ruuter/{GET => POST}/services/available-intents.yml (72%) diff --git a/DSL/Resql/training/get-available-intents.sql b/DSL/Resql/training/get-available-intents.sql index 0ca35ba8..691498ca 100644 --- a/DSL/Resql/training/get-available-intents.sql +++ b/DSL/Resql/training/get-available-intents.sql @@ -19,9 +19,12 @@ WITH connected_intents AS service_name) AND status in ('pending', 'approved')) -SELECT intent +SELECT intent, CEIL(COUNT(*) OVER() / :page_size::DECIMAL) AS total_pages FROM intent WHERE intent NOT IN (SELECT intent FROM connected_intents) -ORDER BY intent ASC +ORDER BY + CASE WHEN :sorting = 'intent asc' THEN intent END ASC, + CASE WHEN :sorting = 'intent desc' THEN intent END DESC +OFFSET ((GREATEST(:page, 1) - 1) * :page_size) LIMIT :page_size; diff --git a/DSL/Ruuter/GET/services/available-intents.yml b/DSL/Ruuter/POST/services/available-intents.yml similarity index 72% rename from DSL/Ruuter/GET/services/available-intents.yml rename to DSL/Ruuter/POST/services/available-intents.yml index 5e768c82..1bc77e27 100644 --- a/DSL/Ruuter/GET/services/available-intents.yml +++ b/DSL/Ruuter/POST/services/available-intents.yml @@ -11,6 +11,10 @@ get_available_intents: call: http.post args: url: "[#SERVICE_TRAINING_RESQL]/get-available-intents" + body: + page: ${incoming.body.page} + page_size: ${incoming.body.page_size} + sorting: ${incoming.body.sorting} result: res return_result: diff --git a/GUI/src/pages/Integration/ConnectServiceToIntentModel.tsx b/GUI/src/pages/Integration/ConnectServiceToIntentModel.tsx index f8d0c2a5..8c54f7c9 100644 --- a/GUI/src/pages/Integration/ConnectServiceToIntentModel.tsx +++ b/GUI/src/pages/Integration/ConnectServiceToIntentModel.tsx @@ -24,17 +24,19 @@ const ConnectServiceToIntentModel: FC = ({ onM const [selectedIntent, setSelectedIntent] = useState(); const [showConfirmationModal, setShowConfirmationModal] = useState(false); - const loadAvailableIntents = () => { + const loadAvailableIntents = (pagination: PaginationState, sorting: SortingState) => { useServiceStore .getState() .loadAvailableIntentsList( (requests: Intent[]) => setIntents(requests), - t("overview.toast.failed.availableIntents") + t("overview.toast.failed.availableIntents"), + pagination, + sorting ); }; useEffect(() => { - loadAvailableIntents(); + loadAvailableIntents(pagination, sorting); }, []); const intentColumns = useMemo( @@ -84,8 +86,17 @@ const ConnectServiceToIntentModel: FC = ({ onM sortable sorting={sorting} pagination={pagination} - setPagination={setPagination} - setSorting={setSorting} + setPagination={(state: PaginationState) => { + if (state.pageIndex === pagination.pageIndex && state.pageSize === pagination.pageSize) return; + setPagination(state); + loadAvailableIntents(state, sorting); + }} + setSorting={(state: SortingState) => { + setSorting(state); + loadAvailableIntents(pagination, state); + }} + isClientSide={false} + pagesCount={intents[intents.length - 1]?.totalPages ?? 1} /> )} {showConfirmationModal && ( diff --git a/GUI/src/store/services.store.ts b/GUI/src/store/services.store.ts index e1815de4..796c93e8 100644 --- a/GUI/src/store/services.store.ts +++ b/GUI/src/store/services.store.ts @@ -46,7 +46,12 @@ interface ServiceStoreState { sorting: SortingState ) => Promise; loadRequestsList: (onEnd: (requests: Trigger[]) => void, errorMessage: string) => Promise; - loadAvailableIntentsList: (onEnd: (requests: Intent[]) => void, errorMessage: string) => Promise; + loadAvailableIntentsList: ( + onEnd: (requests: Intent[]) => void, + errorMessage: string, + pagination: PaginationState, + sorting: SortingState + ) => Promise; respondToConnectionRequest: ( onEnd: () => void, successMessage: string, @@ -232,9 +237,14 @@ const useServiceListStore = create((set, get, store) => ({ useToastStore.getState().error({ title: errorMessage }); } }, - loadAvailableIntentsList: async (onEnd, errorMessage) => { + loadAvailableIntentsList: async (onEnd, errorMessage, pagination, sorting) => { try { - const requests = await axios.get(getAvailableIntents()); + const sort = sorting.length === 0 ? "intent asc" : sorting[0].id + " " + (sorting[0].desc ? "desc" : "asc"); + const requests = await axios.post(getAvailableIntents(), { + page: pagination.pageIndex + 1, + page_size: pagination.pageSize, + sorting: sort, + }); onEnd(requests.data.response); } catch (_) { onEnd([]); diff --git a/GUI/src/types/Intent.ts b/GUI/src/types/Intent.ts index db9fd7b4..dde5e2c6 100644 --- a/GUI/src/types/Intent.ts +++ b/GUI/src/types/Intent.ts @@ -1,4 +1,5 @@ export interface Intent { readonly id: number; intent: string; + readonly totalPages: number; } From a18b3759c40cc282614443cbee6f8881201ed8af Mon Sep 17 00:00:00 2001 From: ahmedyasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Fri, 31 May 2024 04:53:38 +0300 Subject: [PATCH 5/9] Added Pagination & Sorting to connection requests --- .../get-requested-service-triggers.sql | 15 ++++++++--- .../services/connection-requests.yml | 4 +++ GUI/src/pages/ConnectionRequestsPage.tsx | 27 ++++++++++++++----- GUI/src/store/services.store.ts | 16 ++++++++--- GUI/src/types/Trigger.ts | 1 + 5 files changed, 50 insertions(+), 13 deletions(-) rename DSL/Ruuter/{GET => POST}/services/connection-requests.yml (73%) diff --git a/DSL/Resql/training/get-requested-service-triggers.sql b/DSL/Resql/training/get-requested-service-triggers.sql index 974ffde7..3510b0bf 100644 --- a/DSL/Resql/training/get-requested-service-triggers.sql +++ b/DSL/Resql/training/get-requested-service-triggers.sql @@ -2,9 +2,18 @@ SELECT intent, service, MAX(service_name) AS service_name, MAX(created) AS requested_at, - MAX(author_role) as author_role + MAX(author_role) as author_role, + CEIL(COUNT(*) OVER() / :page_size::DECIMAL) AS total_pages FROM service_trigger GROUP BY intent, - service + service HAVING MAX(status) = 'pending' -AND MAX("author_role") != 'service_manager'; +AND MAX("author_role") != 'service_manager' +ORDER BY + CASE WHEN :sorting = 'intent asc' THEN intent END ASC, + CASE WHEN :sorting = 'intent desc' THEN intent END DESC, + CASE WHEN :sorting = 'serviceName asc' THEN MAX(service_name) END ASC, + CASE WHEN :sorting = 'serviceName desc' THEN MAX(service_name) END DESC, + CASE WHEN :sorting = 'requestedAt asc' THEN MAX(created) END ASC, + CASE WHEN :sorting = 'requestedAt desc' THEN MAX(created) END DESC +OFFSET ((GREATEST(:page, 1) - 1) * :page_size) LIMIT :page_size; diff --git a/DSL/Ruuter/GET/services/connection-requests.yml b/DSL/Ruuter/POST/services/connection-requests.yml similarity index 73% rename from DSL/Ruuter/GET/services/connection-requests.yml rename to DSL/Ruuter/POST/services/connection-requests.yml index fc0e7a04..f51c9252 100644 --- a/DSL/Ruuter/GET/services/connection-requests.yml +++ b/DSL/Ruuter/POST/services/connection-requests.yml @@ -11,6 +11,10 @@ get_connection_requests: call: http.post args: url: "[#SERVICE_TRAINING_RESQL]/get-requested-service-triggers" + body: + page: ${incoming.body.page} + page_size: ${incoming.body.page_size} + sorting: ${incoming.body.sorting} result: res return_result: diff --git a/GUI/src/pages/ConnectionRequestsPage.tsx b/GUI/src/pages/ConnectionRequestsPage.tsx index 2acb341b..57ffbce8 100644 --- a/GUI/src/pages/ConnectionRequestsPage.tsx +++ b/GUI/src/pages/ConnectionRequestsPage.tsx @@ -15,24 +15,28 @@ const ConnectionRequestsPage: React.FC = () => { pageIndex: 0, pageSize: 10, }); - const [sorting, setSorting] = useState([]); - const loadConnectionRequests = () => { + const loadConnectionRequests = (pagination: PaginationState, sorting: SortingState) => { useServiceStore .getState() - .loadRequestsList((requests: Trigger[]) => setTriggers(requests), t("connectionRequests.toast.failed.requests")); + .loadRequestsList( + (requests: Trigger[]) => setTriggers(requests), + t("connectionRequests.toast.failed.requests"), + pagination, + sorting + ); }; useEffect(() => { - loadConnectionRequests(); + loadConnectionRequests(pagination, sorting); }, []); const respondToConnectionRequest = (status: boolean, request: Trigger) => { useServiceStore .getState() .respondToConnectionRequest( - () => loadConnectionRequests(), + () => loadConnectionRequests(pagination, sorting), t("connectionRequests.approvedConnection"), t("connectionRequests.declinedConnection"), status, @@ -54,8 +58,17 @@ const ConnectionRequestsPage: React.FC = () => { sortable sorting={sorting} pagination={pagination} - setPagination={setPagination} - setSorting={setSorting} + setPagination={(state: PaginationState) => { + if (state.pageIndex === pagination.pageIndex && state.pageSize === pagination.pageSize) return; + setPagination(state); + loadConnectionRequests(state, sorting); + }} + setSorting={(state: SortingState) => { + setSorting(state); + loadConnectionRequests(pagination, state); + }} + isClientSide={false} + pagesCount={triggers[triggers.length - 1]?.totalPages ?? 1} /> diff --git a/GUI/src/store/services.store.ts b/GUI/src/store/services.store.ts index 796c93e8..14125298 100644 --- a/GUI/src/store/services.store.ts +++ b/GUI/src/store/services.store.ts @@ -45,7 +45,12 @@ interface ServiceStoreState { pagination: PaginationState, sorting: SortingState ) => Promise; - loadRequestsList: (onEnd: (requests: Trigger[]) => void, errorMessage: string) => Promise; + loadRequestsList: ( + onEnd: (requests: Trigger[]) => void, + errorMessage: string, + pagination: PaginationState, + sorting: SortingState + ) => Promise; loadAvailableIntentsList: ( onEnd: (requests: Intent[]) => void, errorMessage: string, @@ -228,9 +233,14 @@ const useServiceListStore = create((set, get, store) => ({ } onEnd(); }, - loadRequestsList: async (onEnd, errorMessage) => { + loadRequestsList: async (onEnd, errorMessage, pagination, sorting) => { try { - const requests = await axios.get(getConnectionRequests()); + const sort = sorting.length === 0 ? "requestedAt desc" : sorting[0].id + " " + (sorting[0].desc ? "desc" : "asc"); + const requests = await axios.post(getConnectionRequests(), { + page: pagination.pageIndex + 1, + page_size: pagination.pageSize, + sorting: sort, + }); onEnd(requests.data.response); } catch (_) { onEnd([]); diff --git a/GUI/src/types/Trigger.ts b/GUI/src/types/Trigger.ts index 1865be94..d6b81173 100644 --- a/GUI/src/types/Trigger.ts +++ b/GUI/src/types/Trigger.ts @@ -6,4 +6,5 @@ export interface Trigger { authorRole: string; requestedAt: string; status: string; + readonly totalPages: number; } From ac96a4adef376289d293f8274abb058a9709678e Mon Sep 17 00:00:00 2001 From: ahmedyasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Fri, 31 May 2024 06:50:06 +0300 Subject: [PATCH 6/9] Added Pagination & Sorting to Faulty Services --- .../GET/services/services-detailed/nok.yml | 14 +++--- GUI/src/components/DataTable/DataTable.scss | 35 ++++++++++--- GUI/src/components/DataTable/index.tsx | 2 +- GUI/src/pages/FaultyServicesPage.tsx | 49 +++++++++++++++++-- GUI/src/resources/api-constants.ts | 3 +- 5 files changed, 84 insertions(+), 19 deletions(-) diff --git a/DSL/Ruuter/GET/services/services-detailed/nok.yml b/DSL/Ruuter/GET/services/services-detailed/nok.yml index adbe95f5..00d84f94 100644 --- a/DSL/Ruuter/GET/services/services-detailed/nok.yml +++ b/DSL/Ruuter/GET/services/services-detailed/nok.yml @@ -12,13 +12,15 @@ getFaults: args: url: "[#SERVICE_OPENSEARCH]/ruuterlog/_search" query: - size: 20 + from: ${(incoming.params.page - 1) * incoming.params.page_size} + size: ${incoming.params.page_size} _source_excludes: "stackTrace,statusCode" body: + sort: [{ "timestamp": { "order": "${incoming.params.order}" } }] query: match_phrase_prefix: dslName: - query: "services/active" + query: "services/active" result: getFaultsResult mapFaultsData: @@ -26,13 +28,11 @@ mapFaultsData: args: url: "[#SERVICE_DMAPPER]/hbs/services/get-faults" headers: - type: 'json' + type: "json" body: - data: { - "hits": "${getFaultsResult.response.body.hits.hits}" - } + data: { "hits": "${getFaultsResult.response.body.hits.hits}" } result: faultsData returnSuccess: wrapper: false - return: ${faultsData.response.body} + return: ${[faultsData.response.body, getFaultsResult.response.body.hits.total.value]} diff --git a/GUI/src/components/DataTable/DataTable.scss b/GUI/src/components/DataTable/DataTable.scss index bc027a2c..1e02fa28 100644 --- a/GUI/src/components/DataTable/DataTable.scss +++ b/GUI/src/components/DataTable/DataTable.scss @@ -1,11 +1,25 @@ -@import 'src/styles/tools/spacing'; -@import 'src/styles/tools/color'; -@import 'src/styles/settings/variables/typography'; +@import "src/styles/tools/spacing"; +@import "src/styles/tools/color"; +@import "src/styles/settings/variables/typography"; .data-table { width: 100%; color: get-color(black-coral-20); text-align: left; + margin-bottom: 0; + display: table; + + &__scrollWrapper { + height: 100%; + overflow-x: auto; + white-space: nowrap; + display: block; + } + + thead, + tbody { + width: 100%; + } th { padding: 12px 14.5px; @@ -20,6 +34,11 @@ padding: 12px 24px 12px 16px; border-bottom: 1px solid get-color(black-coral-2); vertical-align: middle; + max-width: fit-content; + + p { + white-space: break-spaces; + } .entity { display: inline-flex; @@ -121,7 +140,8 @@ li { display: block; - a, span { + a, + span { display: flex; align-items: center; justify-content: center; @@ -135,7 +155,8 @@ } &.active { - a, span { + a, + span { color: get-color(white); background-color: get-color(sapphire-blue-10); } @@ -160,10 +181,10 @@ height: 30px; min-width: 50px; padding: 6px 10px; - border: 1px solid #8F91A8; + border: 1px solid #8f91a8; border-radius: 2px; background-color: get-color(white); - background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iNiIgdmlld0JveD0iMCAwIDEwIDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNS4zMTMwNiA1LjgwODIyQzUuMTU2ODUgNS45NjQ0MyA0LjkwMzU4IDUuOTY0NDMgNC43NDczNyA1LjgwODIyTDAuMjgyNzMgMS4zNDM1OEMwLjEyNjUyIDEuMTg3MzcgMC4xMjY1MiAwLjkzNDEwMiAwLjI4MjczIDAuNzc3ODkzTDAuNzc3NzA0IDAuMjgyOTE4QzAuOTMzOTE0IDAuMTI2NzA4IDEuMTg3MTggMC4xMjY3MDggMS4zNDMzOSAwLjI4MjkxN0w1LjAzMDIyIDMuOTY5NzRMOC43MTcwNCAwLjI4MjkxN0M4Ljg3MzI1IDAuMTI2NzA4IDkuMTI2NTIgMC4xMjY3MDggOS4yODI3MyAwLjI4MjkxN0w5Ljc3NzcgMC43Nzc4OTJDOS45MzM5MSAwLjkzNDEwMiA5LjkzMzkxIDEuMTg3MzcgOS43Nzc3IDEuMzQzNThMNS4zMTMwNiA1LjgwODIyWiIgZmlsbD0iIzU1NTg2NyIvPgo8L3N2Zz4K'); + background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAiIGhlaWdodD0iNiIgdmlld0JveD0iMCAwIDEwIDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNS4zMTMwNiA1LjgwODIyQzUuMTU2ODUgNS45NjQ0MyA0LjkwMzU4IDUuOTY0NDMgNC43NDczNyA1LjgwODIyTDAuMjgyNzMgMS4zNDM1OEMwLjEyNjUyIDEuMTg3MzcgMC4xMjY1MiAwLjkzNDEwMiAwLjI4MjczIDAuNzc3ODkzTDAuNzc3NzA0IDAuMjgyOTE4QzAuOTMzOTE0IDAuMTI2NzA4IDEuMTg3MTggMC4xMjY3MDggMS4zNDMzOSAwLjI4MjkxN0w1LjAzMDIyIDMuOTY5NzRMOC43MTcwNCAwLjI4MjkxN0M4Ljg3MzI1IDAuMTI2NzA4IDkuMTI2NTIgMC4xMjY3MDggOS4yODI3MyAwLjI4MjkxN0w5Ljc3NzcgMC43Nzc4OTJDOS45MzM5MSAwLjkzNDEwMiA5LjkzMzkxIDEuMTg3MzcgOS43Nzc3IDEuMzQzNThMNS4zMTMwNiA1LjgwODIyWiIgZmlsbD0iIzU1NTg2NyIvPgo8L3N2Zz4K"); background-repeat: no-repeat; background-position: top 11px right 10px; } diff --git a/GUI/src/components/DataTable/index.tsx b/GUI/src/components/DataTable/index.tsx index 3582a331..eb12faf0 100644 --- a/GUI/src/components/DataTable/index.tsx +++ b/GUI/src/components/DataTable/index.tsx @@ -141,7 +141,7 @@ const DataTable: FC = ({ }); return ( -
+
{!disableHead && ( diff --git a/GUI/src/pages/FaultyServicesPage.tsx b/GUI/src/pages/FaultyServicesPage.tsx index 0293830b..82143210 100644 --- a/GUI/src/pages/FaultyServicesPage.tsx +++ b/GUI/src/pages/FaultyServicesPage.tsx @@ -27,6 +27,7 @@ const FaultyServicesPage: React.FC = () => { const { t } = useTranslation(); const [viewFaultyServiceLog, setViewFaultyServiceLog] = useState(null); const [data, setData] = useState([]); + const [pageCount, setPageCount] = useState(0); const columns = useMemo(() => getColumns(setViewFaultyServiceLog), []); const [pagination, setPagination] = useState({ pageIndex: 0, @@ -34,8 +35,41 @@ const FaultyServicesPage: React.FC = () => { }); const [sorting, setSorting] = useState([]); + const loadFaultyServices = (pagination: PaginationState, sorting: SortingState) => { + let sort = "dslName.keyword"; + let order = "desc"; + + if (sorting.length > 0) { + switch (sorting[0].id) { + case "serviceMethod": + sort = "dslMethod.keyword"; + break; + case "errorCode": + sort = "errorCode"; + break; + case "stepName": + sort = "stepName.keyword"; + break; + case "timestamp": + sort = "timestamp"; + break; + default: + sort = "dslName.keyword"; + break; + } + order = sorting[0].desc ? "desc" : "asc"; + } + axios + .get(getFaultyServices(pagination.pageIndex + 1, pagination.pageSize, sort, order)) + .then((res) => { + setData(res.data[0]); + setPageCount(Math.ceil(res.data[1] / pagination.pageSize)); + }) + .catch((err) => console.log(err)); + }; + useEffect(() => { - axios.get(getFaultyServices()).then((res) => setData(res.data)); + loadFaultyServices(pagination, sorting); }, []); return ( @@ -130,8 +164,17 @@ const FaultyServicesPage: React.FC = () => { columns={columns} sorting={sorting} pagination={pagination} - setSorting={setSorting} - setPagination={setPagination} + setPagination={(state: PaginationState) => { + if (state.pageIndex === pagination.pageIndex && state.pageSize === pagination.pageSize) return; + setPagination(state); + loadFaultyServices(state, sorting); + }} + setSorting={(state: SortingState) => { + setSorting(state); + loadFaultyServices(pagination, state); + }} + isClientSide={false} + pagesCount={pageCount} /> diff --git a/GUI/src/resources/api-constants.ts b/GUI/src/resources/api-constants.ts index f756ec0c..7aa6398b 100644 --- a/GUI/src/resources/api-constants.ts +++ b/GUI/src/resources/api-constants.ts @@ -26,7 +26,8 @@ export const getCommonServicesList = (): string => `${baseUrl}/common-services`; export const getConnectionRequests = (): string => `${baseUrl}/services/connection-requests`; export const getAvailableIntents = (): string => `${baseUrl}/services/available-intents`; export const jsonToYml = (): string => `${baseUrl}/saveJsonToYml`; -export const getFaultyServices = (): string => `${baseUrl}/services/services-detailed/nok`; +export const getFaultyServices = (page: number, pageSize: number, sort: string, order: string): string => + `${baseUrl}/services/services-detailed/nok?page=${page}&page_size=${pageSize}&sort=${sort}&order=${order}`; export const trainingModuleTraining = (): string => `${trainingModuleBaseUrl}/treening/treeni-uus-mudel`; export const getServiceById = (id: string): string => `${baseUrl}/service-by-id?id=${id}`; export const updateServiceEndpoints = (id: string): string => `${baseUrl}/services/update-service-endpoints?id=${id}`; From 12d3e17b8a3f04ea1f3e0c7fa071e87548e0ae53 Mon Sep 17 00:00:00 2001 From: ahmedyasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Fri, 31 May 2024 08:21:25 +0300 Subject: [PATCH 7/9] Removed unused import --- GUI/src/components/DataTable/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUI/src/components/DataTable/index.tsx b/GUI/src/components/DataTable/index.tsx index eb12faf0..171bf629 100644 --- a/GUI/src/components/DataTable/index.tsx +++ b/GUI/src/components/DataTable/index.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties, FC, ReactNode, useId, useState } from "react"; +import React, { CSSProperties, FC, ReactNode, useId } from "react"; import { ColumnDef, useReactTable, From e02074e5a35f915e45e84d4206e78a873b34d66f Mon Sep 17 00:00:00 2001 From: ahmedyasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Wed, 5 Jun 2024 11:29:49 +0300 Subject: [PATCH 8/9] Addressed PR Comments --- DSL/Ruuter/GET/services/services-detailed/nok.yml | 11 +++++++++++ DSL/Ruuter/POST/common-services.yml | 13 ++++++++++++- DSL/Ruuter/POST/services.yml | 13 ++++++++++++- DSL/Ruuter/POST/services/available-intents.yml | 13 ++++++++++++- DSL/Ruuter/POST/services/connection-requests.yml | 13 ++++++++++++- 5 files changed, 59 insertions(+), 4 deletions(-) diff --git a/DSL/Ruuter/GET/services/services-detailed/nok.yml b/DSL/Ruuter/GET/services/services-detailed/nok.yml index 00d84f94..9c903abc 100644 --- a/DSL/Ruuter/GET/services/services-detailed/nok.yml +++ b/DSL/Ruuter/GET/services/services-detailed/nok.yml @@ -6,6 +6,17 @@ declaration: accepts: json returns: json namespace: service + allowlist: + params: + - field: page + type: number + description: "Parameter 'page'" + - field: page_size + type: number + description: "Parameter 'page_size'" + - field: sorting + type: string + description: "Parameter 'sorting'" getFaults: call: http.post diff --git a/DSL/Ruuter/POST/common-services.yml b/DSL/Ruuter/POST/common-services.yml index 2f7e780e..b9c5feb9 100644 --- a/DSL/Ruuter/POST/common-services.yml +++ b/DSL/Ruuter/POST/common-services.yml @@ -2,10 +2,21 @@ declaration: call: declare version: 0.1 description: "Decription placeholder for 'COMMON SERVICES'" - method: get + method: post accepts: json returns: json namespace: service + allowlist: + body: + - field: page + type: number + description: "Body field 'page'" + - field: page_size + type: number + description: "Body field 'page_size'" + - field: sorting + type: string + description: "Body field 'sorting'" get_services_list: call: http.post diff --git a/DSL/Ruuter/POST/services.yml b/DSL/Ruuter/POST/services.yml index 8ef3d0f1..2cb1e653 100644 --- a/DSL/Ruuter/POST/services.yml +++ b/DSL/Ruuter/POST/services.yml @@ -2,10 +2,21 @@ declaration: call: declare version: 0.1 description: "Decription placeholder for 'SERVICES'" - method: get + method: post accepts: json returns: json namespace: service + allowlist: + body: + - field: page + type: number + description: "Body field 'page'" + - field: page_size + type: number + description: "Body field 'page_size'" + - field: sorting + type: string + description: "Body field 'sorting'" get_services_list: call: http.post diff --git a/DSL/Ruuter/POST/services/available-intents.yml b/DSL/Ruuter/POST/services/available-intents.yml index 1bc77e27..32ca0a70 100644 --- a/DSL/Ruuter/POST/services/available-intents.yml +++ b/DSL/Ruuter/POST/services/available-intents.yml @@ -2,10 +2,21 @@ declaration: call: declare version: 0.1 description: "Decription placeholder for 'AVAILABLE-INTENTS'" - method: get + method: post accepts: json returns: json namespace: service + allowlist: + body: + - field: page + type: number + description: "Body field 'page'" + - field: page_size + type: number + description: "Body field 'page_size'" + - field: sorting + type: string + description: "Body field 'sorting'" get_available_intents: call: http.post diff --git a/DSL/Ruuter/POST/services/connection-requests.yml b/DSL/Ruuter/POST/services/connection-requests.yml index f51c9252..117c028a 100644 --- a/DSL/Ruuter/POST/services/connection-requests.yml +++ b/DSL/Ruuter/POST/services/connection-requests.yml @@ -2,10 +2,21 @@ declaration: call: declare version: 0.1 description: "Decription placeholder for 'CONNECTION-REQUESTS'" - method: get + method: post accepts: json returns: json namespace: service + allowlist: + body: + - field: page + type: number + description: "Body field 'page'" + - field: page_size + type: number + description: "Body field 'page_size'" + - field: sorting + type: string + description: "Body field 'sorting'" get_connection_requests: call: http.post From 9ffb10c5adb44123ae44bc23ec298b5989e9ee81 Mon Sep 17 00:00:00 2001 From: ahmedyasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Wed, 5 Jun 2024 14:14:22 +0300 Subject: [PATCH 9/9] Solved Sonar Issues --- GUI/src/pages/FaultyServicesPage.tsx | 3 --- GUI/src/store/services.store.ts | 12 ++++++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/GUI/src/pages/FaultyServicesPage.tsx b/GUI/src/pages/FaultyServicesPage.tsx index 82143210..93584356 100644 --- a/GUI/src/pages/FaultyServicesPage.tsx +++ b/GUI/src/pages/FaultyServicesPage.tsx @@ -53,9 +53,6 @@ const FaultyServicesPage: React.FC = () => { case "timestamp": sort = "timestamp"; break; - default: - sort = "dslName.keyword"; - break; } order = sorting[0].desc ? "desc" : "asc"; } diff --git a/GUI/src/store/services.store.ts b/GUI/src/store/services.store.ts index 14125298..548fed45 100644 --- a/GUI/src/store/services.store.ts +++ b/GUI/src/store/services.store.ts @@ -77,7 +77,8 @@ const useServiceListStore = create((set, get, store) => ({ commonServices: [], notCommonServices: [], loadServicesList: async (pagination, sorting) => { - const sort = sorting.length === 0 ? "id asc" : sorting[0].id + " " + (sorting[0].desc ? "desc" : "asc"); + const order = sorting[0].desc ? "desc" : "asc"; + const sort = sorting.length === 0 ? "id asc" : sorting[0].id + " " + order; const result = await axios.post(getServicesList(), { page: pagination.pageIndex + 1, page_size: pagination.pageSize, @@ -106,7 +107,8 @@ const useServiceListStore = create((set, get, store) => ({ }); }, loadCommonServicesList: async (pagination, sorting) => { - const sort = sorting.length === 0 ? "id asc" : sorting[0].id + " " + (sorting[0].desc ? "desc" : "asc"); + const order = sorting[0].desc ? "desc" : "asc"; + const sort = sorting.length === 0 ? "id asc" : sorting[0].id + " " + order; const result = await axios.post(getCommonServicesList(), { page: pagination.pageIndex + 1, page_size: pagination.pageSize, @@ -235,7 +237,8 @@ const useServiceListStore = create((set, get, store) => ({ }, loadRequestsList: async (onEnd, errorMessage, pagination, sorting) => { try { - const sort = sorting.length === 0 ? "requestedAt desc" : sorting[0].id + " " + (sorting[0].desc ? "desc" : "asc"); + const order = sorting[0].desc ? "desc" : "asc"; + const sort = sorting.length === 0 ? "requestedAt desc" : sorting[0].id + " " + order; const requests = await axios.post(getConnectionRequests(), { page: pagination.pageIndex + 1, page_size: pagination.pageSize, @@ -249,7 +252,8 @@ const useServiceListStore = create((set, get, store) => ({ }, loadAvailableIntentsList: async (onEnd, errorMessage, pagination, sorting) => { try { - const sort = sorting.length === 0 ? "intent asc" : sorting[0].id + " " + (sorting[0].desc ? "desc" : "asc"); + const order = sorting[0].desc ? "desc" : "asc"; + const sort = sorting.length === 0 ? "intent asc" : sorting[0].id + " " + order; const requests = await axios.post(getAvailableIntents(), { page: pagination.pageIndex + 1, page_size: pagination.pageSize,