From 02ded4765986a28fb92c4c0eb3807b14207e9c56 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Fri, 19 May 2023 11:29:55 +0200 Subject: [PATCH 01/23] more button in dropdown init --- src/modules/auth/roles/RolesResourcesCard.js | 67 +++++++++++++------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/modules/auth/roles/RolesResourcesCard.js b/src/modules/auth/roles/RolesResourcesCard.js index e9574796..cca70d11 100644 --- a/src/modules/auth/roles/RolesResourcesCard.js +++ b/src/modules/auth/roles/RolesResourcesCard.js @@ -15,6 +15,8 @@ const RolesResourcesCard = (props) => { const [unassignedResources, setUnassignedResources] = useState([]); const [editMode, setEditMode] = useState(false); const [dropdownOpen, setDropdown] = useState(false); + const [limit, setLimit] = useState(10); + const [count, setCount] = useState(0); const { role_name, tenant_id } = props.params; const roleId = props.role._id ? props.role._id : role_name; @@ -22,31 +24,37 @@ const RolesResourcesCard = (props) => { const { t } = useTranslation(); + useEffect(() => console.log(limit), [limit]); useEffect(() => fetchAssignedResources(), []); - useEffect(() => fetchUnassignedResources(), [assignedResources]); - - const fetchAssignedResources = () => { - SeaCatAuthAPI.get(`/role/${tenant_id}/${role_name}`) - .then(res => { - const assignedResources = res.data.resources; - setAssignedResources(assignedResources); - }) - .catch((e) => props.app.addAlert("warning", `${t("RolesResourcesCard|Fetch of assigned resources failed")}. ${e?.response?.data?.message}`, 30)); + useEffect(() => fetchUnassignedResources(), [assignedResources, limit]); + + const fetchAssignedResources = async () => { + try { + let response = await SeaCatAuthAPI.get(`/role/${tenant_id}/${role_name}`); + setAssignedResources(response.data.resources); + } catch(e) { + console.error(e); + props.app.addAlert("warning", `${t("RolesResourcesCard|Fetch of assigned resources failed")}. ${e?.response?.data?.message}`, 30); + setEditMode(false); + } if (editMode) setEditMode(false); } - const fetchUnassignedResources = () => { - SeaCatAuthAPI.get(`/resource`) - .then(res => { - const allResources = res.data.data.map(resource => resource._id); - const unassignedResources = allResources.filter(resource => assignedResources.indexOf(resource) < 0); - // Remove authz:superuser from unassigned resources on every role, which is not global - if (roleId.indexOf('*/') == -1){ - unassignedResources.splice(unassignedResources.indexOf('authz:superuser'), 1) - } - setUnassignedResources(unassignedResources); - }) - .catch((e) => props.app.addAlert("warning", `${t("RolesResourcesCard|Fetch of all resources failed")}. ${e?.response?.data?.message}`, 30)); + const fetchUnassignedResources = async () => { + try { + let response = await SeaCatAuthAPI.get(`/resource`, {params: { i: limit }}); + const allResources = response.data.data.map(resource => resource._id); + const unassignedResources = allResources.filter(resource => assignedResources.indexOf(resource) < 0); + // Remove authz:superuser from unassigned resources on every role, which is not global + if (roleId.indexOf('*/') == -1){ + unassignedResources.splice(unassignedResources.indexOf('authz:superuser'), 1); + } + setUnassignedResources(unassignedResources); + setCount(response.data.count); + } catch(e) { + console.error(e); + props.app.addAlert("warning", `${t("RolesResourcesCard|Fetch of all resources failed")}. ${e?.response?.data?.message}`, 30); + } } const assignResource = (resource) => { @@ -61,7 +69,7 @@ const RolesResourcesCard = (props) => { const onSave = async () => { try { - let response = await SeaCatAuthAPI.put(`/role/${tenant_id}/${role_name}`, { 'set': assignedResources }); + await SeaCatAuthAPI.put(`/role/${tenant_id}/${role_name}`, { 'set': assignedResources }); props.app.addAlert("success", t("RolesResourcesCard|Role has been updated successfully")); setEditMode(false); } catch(e) { @@ -134,6 +142,21 @@ const RolesResourcesCard = (props) => { {resource} ))} + {count > limit ? + <> + + { + setLimit(limit + 5); + // toggleAddRole(); + }} + > + {t("RolesResourcesCard|More")} + + + : + null + } From 78ba7ad0e693041dce6f06bed4b9b21d418d8988 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Mon, 22 May 2023 12:04:43 +0200 Subject: [PATCH 02/23] refactoring client detail container to css grid --- .../auth/clients/ClientDetailContainer.js | 339 +++++++++--------- src/modules/auth/clients/clients.scss | 49 +-- 2 files changed, 194 insertions(+), 194 deletions(-) diff --git a/src/modules/auth/clients/ClientDetailContainer.js b/src/modules/auth/clients/ClientDetailContainer.js index b88b7aa2..5df3f564 100644 --- a/src/modules/auth/clients/ClientDetailContainer.js +++ b/src/modules/auth/clients/ClientDetailContainer.js @@ -78,181 +78,176 @@ const ClientDetailContainer = (props) => { }; return ( - -
-
- - -
- - {t("ClientDetailContainer|Client")} -
-
- - - {t("ClientDetailContainer|Client name")} - {client?.client_name ? client.client_name : "N/A"} - - - {t("ClientDetailContainer|Client ID")} - {client?.client_id ? client.client_id : "N/A"} - - - {t("Created at")} - - - - {t("Modified at")} - - - - {t("ClientDetailContainer|Redirect URIs")} - - {client?.redirect_uris.map((item, idx) => ( -
{item}
)) - } - -
- - {t("ClientDetailContainer|Redirect URI validation method")} - {client?.redirect_uri_validation_method ? client.redirect_uri_validation_method : "N/A"} - - - {t("ClientDetailContainer|Client URI")} - {client?.client_uri ? client.client_uri : "N/A"} - - - {t("ClientDetailContainer|Application type")} - {client?.application_type} - -
- - - props.history.push(`/auth/clients/${client_id}/edit`)} - > - {t("Edit")} - - removeClientConfirm()} - resource={resource} - resources={resources} - > - {t("ClientDetailContainer|Remove client")} - - - -
-
-
- - -
- - {t("ClientDetailContainer|Multidomain")} -
-
- - - {t("ClientDetailContainer|Login URI")} - {client?.login_uri ? client.login_uri : "N/A"} - - - {t("ClientDetailContainer|Cookie domain")} - {client?.cookie_domain ? client.cookie_domain : "N/A"} - - - {t("ClientDetailContainer|Authorize URI")} - {client?.authorize_uri ? client.authorize_uri : "N/A"} - - -
+ + + +
+ + {t("ClientDetailContainer|Client")} +
+
+ + + {t("ClientDetailContainer|Client name")} + {client?.client_name ? client.client_name : "N/A"} + + + {t("ClientDetailContainer|Client ID")} + {client?.client_id ? client.client_id : "N/A"} + + + {t("Created at")} + + + + {t("Modified at")} + + + + {t("ClientDetailContainer|Redirect URIs")} + + {client?.redirect_uris.map((item, idx) => ( +
{item}
)) + } + +
+ + {t("ClientDetailContainer|Redirect URI validation method")} + {client?.redirect_uri_validation_method ? client.redirect_uri_validation_method : "N/A"} + + + {t("ClientDetailContainer|Client URI")} + {client?.client_uri ? client.client_uri : "N/A"} + + + {t("ClientDetailContainer|Application type")} + {client?.application_type} + +
+ + + props.history.push(`/auth/clients/${client_id}/edit`)} + > + {t("Edit")} + + removeClientConfirm()} + resource={resource} + resources={resources} + > + {t("ClientDetailContainer|Remove client")} + + + +
- - -
- - {t("ClientDetailContainer|Authorization")} -
-
- - - {t("ClientDetailContainer|Code challenge method (PKCE)")} - {client?.code_challenge_method ? client.code_challenge_method : "N/A"} - - - {t("ClientDetailContainer|Response types")} - - {client?.response_types?.length > 0 && - client?.response_types.map((item, idx) => ( -
{item}
- )) - } - -
- - {t("ClientDetailContainer|Grant types")} - - {client?.grant_types?.length > 0 && - client?.grant_types.map((item, idx) => ( -
{item}
- )) - } - -
- - {t("ClientDetailContainer|Token endpoint auth. method")} - {client?.token_endpoint_auth_method} - - {client?.client_secret && - - {t("ClientDetailContainer|Client secret")} - - {client?.client_secret ? client.client_secret : "N/A"} - - - + + +
+ + {t("ClientDetailContainer|Multidomain")} +
+
+ + + {t("ClientDetailContainer|Login URI")} + {client?.login_uri ? client.login_uri : "N/A"} + + + {t("ClientDetailContainer|Cookie domain")} + {client?.cookie_domain ? client.cookie_domain : "N/A"} + + + {t("ClientDetailContainer|Authorize URI")} + {client?.authorize_uri ? client.authorize_uri : "N/A"} + + +
+ + + +
+ + {t("ClientDetailContainer|Authorization")} +
+
+ + + {t("ClientDetailContainer|Code challenge method (PKCE)")} + {client?.code_challenge_method ? client.code_challenge_method : "N/A"} + + + {t("ClientDetailContainer|Response types")} + + {client?.response_types?.length > 0 && + client?.response_types.map((item, idx) => ( +
{item}
+ )) } -
-
+ + + + {t("ClientDetailContainer|Grant types")} + + {client?.grant_types?.length > 0 && + client?.grant_types.map((item, idx) => ( +
{item}
+ )) + } + +
+ + {t("ClientDetailContainer|Token endpoint auth. method")} + {client?.token_endpoint_auth_method} + + {client?.client_secret && + + {t("ClientDetailContainer|Client secret")} + + {client?.client_secret ? client.client_secret : "N/A"} + + + + } +
+
- - -
- - {t("ClientDetailContainer|Access control")} -
-
- - - {' '} - {t('ClientDetailContainer|Authorize anonymous users')} - - -
-
-
+ + +
+ + {t("ClientDetailContainer|Access control")} +
+
+ + + {' '} + {t('ClientDetailContainer|Authorize anonymous users')} + + +
{advmode && diff --git a/src/modules/auth/clients/clients.scss b/src/modules/auth/clients/clients.scss index 7b2cb7b2..1c94b700 100644 --- a/src/modules/auth/clients/clients.scss +++ b/src/modules/auth/clients/clients.scss @@ -14,26 +14,29 @@ $svg-arrow: var(--svg-arrow); .client-wrapper { display: grid; - gap: 1rem; + gap: 0.25rem; grid-template-areas: - "a" - "b"; - grid-template-columns: 1fr; + "a b" + "a c" + "a d" + "e e"; + grid-template-columns: 1fr 1fr; - & .client-cards-wrapper { - display: grid; + & .client-main-info-card { grid-area: a; - grid-template-areas: - "i q"; - gap: 1rem; - grid-template-columns: 1fr 1fr; - .client-main-info { - grid-area: i; - } - .client-right-cards{ - grid-area: q; + align-self: start; + } - } + & .client-multidomain-card { + grid-area: b; + } + + & .client-authorization-card { + grid-area: c; + } + + & .client-access-control-card { + grid-area: d; } @@ -43,7 +46,7 @@ $svg-arrow: var(--svg-arrow); margin-top: 8px; } .adv-card { - grid-area: b; + grid-area: e; } } @@ -97,11 +100,13 @@ $svg-arrow: var(--svg-arrow); @media (max-width: 994px) { .client-wrapper { - & .client-cards-wrapper { - grid-template-areas: - "i" - "q"; - } + grid-template-areas: + "a" + "b" + "c" + "d" + "e"; + grid-template-columns: 1fr; } } From 3414bde835aea35c96e4c4773d44538475f45732 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Mon, 22 May 2023 12:05:06 +0200 Subject: [PATCH 03/23] locales update --- public/locales/cs/translation.json | 4 +++- public/locales/en/translation.json | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/public/locales/cs/translation.json b/public/locales/cs/translation.json index 74f6ad45..2d350ce9 100644 --- a/public/locales/cs/translation.json +++ b/public/locales/cs/translation.json @@ -347,7 +347,9 @@ "Save": "Uložit", "Cancel": "Zrušit", "Edit": "Upravit", - "No data": "Žádná data" + "No data": "Žádná data", + "More": "Více...", + "Search": "Hledat" }, "ResourcesCreateContainer": { "Resource created": "Zdroj byl úspěšně vytvořen", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index a1b358b3..4a41ef4a 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -347,7 +347,9 @@ "Save": "Save", "Cancel": "Cancel", "Edit": "Edit", - "No data": "No data" + "No data": "No data", + "More": "More...", + "Search": "Search" }, "ResourcesCreateContainer": { "Resource created": "Resource has been created", From 174acc0860404a4ba1c9528ea4509096452a43f8 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Mon, 22 May 2023 12:05:44 +0200 Subject: [PATCH 04/23] changing container to fluid --- src/modules/auth/roles/RolesDetailContainer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/auth/roles/RolesDetailContainer.js b/src/modules/auth/roles/RolesDetailContainer.js index 91c2ffb4..cb71e605 100644 --- a/src/modules/auth/roles/RolesDetailContainer.js +++ b/src/modules/auth/roles/RolesDetailContainer.js @@ -281,7 +281,7 @@ const RolesDetailContainer = (props) => { if (!role) return null; return ( - +
From 5e7739e4b19b90104e14b612b3a2ed2042aff917 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Mon, 22 May 2023 12:06:36 +0200 Subject: [PATCH 05/23] filter search for resource assignment --- src/modules/auth/roles/RolesResourcesCard.js | 35 ++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/modules/auth/roles/RolesResourcesCard.js b/src/modules/auth/roles/RolesResourcesCard.js index cca70d11..8ed7c870 100644 --- a/src/modules/auth/roles/RolesResourcesCard.js +++ b/src/modules/auth/roles/RolesResourcesCard.js @@ -1,10 +1,12 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { Row, Col, Card, CardHeader, CardFooter, CardBody, Button, - Dropdown, DropdownToggle, DropdownMenu, DropdownItem, ButtonGroup, Label + Dropdown, DropdownToggle, DropdownMenu, + DropdownItem, ButtonGroup, Label, + Input } from 'reactstrap'; import { ButtonWithAuthz } from 'asab-webui'; @@ -17,6 +19,7 @@ const RolesResourcesCard = (props) => { const [dropdownOpen, setDropdown] = useState(false); const [limit, setLimit] = useState(10); const [count, setCount] = useState(0); + const [filter, setFilter] = useState(''); const { role_name, tenant_id } = props.params; const roleId = props.role._id ? props.role._id : role_name; @@ -24,10 +27,22 @@ const RolesResourcesCard = (props) => { const { t } = useTranslation(); - useEffect(() => console.log(limit), [limit]); + const timeoutRef = useRef(null); + useEffect(() => fetchAssignedResources(), []); useEffect(() => fetchUnassignedResources(), [assignedResources, limit]); + //sets 0.5s delay before triggering the search call when filtering through tennants + useEffect(() => { + if (timeoutRef.current !== null) { + clearTimeout(timeoutRef.current); + } + timeoutRef.current = setTimeout(() => { + timeoutRef.current = null; + fetchUnassignedResources() + }, 500); + }, [filter]); + const fetchAssignedResources = async () => { try { let response = await SeaCatAuthAPI.get(`/role/${tenant_id}/${role_name}`); @@ -42,7 +57,7 @@ const RolesResourcesCard = (props) => { const fetchUnassignedResources = async () => { try { - let response = await SeaCatAuthAPI.get(`/resource`, {params: { i: limit }}); + let response = await SeaCatAuthAPI.get(`/resource`, {params: { f: filter, i: limit }}); const allResources = response.data.data.map(resource => resource._id); const unassignedResources = allResources.filter(resource => assignedResources.indexOf(resource) < 0); // Remove authz:superuser from unassigned resources on every role, which is not global @@ -136,7 +151,15 @@ const RolesResourcesCard = (props) => { {t("RolesResourcesCard|Add resource")} - {t("RolesResourcesCard|Select resource")} + + setFilter(e.target.value)} + value={filter} + /> + + {/* {t("RolesResourcesCard|Select resource")} */} {unassignedResources.map((resource, idx) => ( assignResource(resource)}> {resource} @@ -148,7 +171,7 @@ const RolesResourcesCard = (props) => { { setLimit(limit + 5); - // toggleAddRole(); + setDropdown(prev => !prev); }} > {t("RolesResourcesCard|More")} From a221e23a1e9302dc03d0cb24c7f0909103698271 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Mon, 22 May 2023 12:07:01 +0200 Subject: [PATCH 06/23] changing container to fluid --- src/modules/auth/tenant/TenantDetailContainer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/auth/tenant/TenantDetailContainer.js b/src/modules/auth/tenant/TenantDetailContainer.js index 30a0d091..72e8efed 100644 --- a/src/modules/auth/tenant/TenantDetailContainer.js +++ b/src/modules/auth/tenant/TenantDetailContainer.js @@ -298,7 +298,7 @@ function TenantDetailContainer(props) { ); return ( - +
From c18915c9b011ba202877e32b801bd96774e7225e Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Wed, 24 May 2023 12:24:40 +0200 Subject: [PATCH 07/23] reducing number of get requests --- src/modules/auth/roles/RolesResourcesCard.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/modules/auth/roles/RolesResourcesCard.js b/src/modules/auth/roles/RolesResourcesCard.js index 8ed7c870..2f8258f7 100644 --- a/src/modules/auth/roles/RolesResourcesCard.js +++ b/src/modules/auth/roles/RolesResourcesCard.js @@ -30,7 +30,7 @@ const RolesResourcesCard = (props) => { const timeoutRef = useRef(null); useEffect(() => fetchAssignedResources(), []); - useEffect(() => fetchUnassignedResources(), [assignedResources, limit]); + useEffect(() => fetchUnassignedResources(), [limit]); //sets 0.5s delay before triggering the search call when filtering through tennants useEffect(() => { @@ -93,6 +93,11 @@ const RolesResourcesCard = (props) => { } } + const onCancel = () => { + fetchAssignedResources(); + fetchUnassignedResources(); + } + return ( @@ -140,7 +145,7 @@ const RolesResourcesCard = (props) => { @@ -159,7 +164,6 @@ const RolesResourcesCard = (props) => { value={filter} /> - {/* {t("RolesResourcesCard|Select resource")} */} {unassignedResources.map((resource, idx) => ( assignResource(resource)}> {resource} From 2ff41fb746ed07bc44a9596802646aa686d16f85 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Wed, 24 May 2023 13:06:11 +0200 Subject: [PATCH 08/23] ClientCreateContainer refactored to css grid --- .../auth/clients/ClientCreateContainer.js | 310 +++++++++--------- 1 file changed, 153 insertions(+), 157 deletions(-) diff --git a/src/modules/auth/clients/ClientCreateContainer.js b/src/modules/auth/clients/ClientCreateContainer.js index 0a6c16bd..1e4eef0b 100644 --- a/src/modules/auth/clients/ClientCreateContainer.js +++ b/src/modules/auth/clients/ClientCreateContainer.js @@ -195,177 +195,173 @@ const ClientCreateContainer = (props) => { } return ( - -
-
- - -
- - {((client != undefined) && (editClient == true)) ? - t("ClientCreateContainer|Edit client") - : - t("ClientCreateContainer|Create new client") - } -
-
- - - + + + +
+ + {((client != undefined) && (editClient == true)) ? + t("ClientCreateContainer|Edit client") + : + t("ClientCreateContainer|Create new client") + } +
+
+ + + + {(metaData != undefined) && metaData["properties"] && metaData["properties"]["redirect_uri_validation_method"] && + - } + {(client == undefined) && + - {(metaData != undefined) && metaData["properties"] && metaData["properties"]["redirect_uri_validation_method"] && - } - {(client == undefined) && - - } - - - - - {((client != undefined) && (editClient == true)) ? - <> - - {t("ClientListContainer|Save")} - - props.history.push(`/auth/clients/${client_id}`)} - > - {t("Cancel")} - - - : + } + +
+ + + {((client != undefined) && (editClient == true)) ? + <> - {t("ClientCreateContainer|Create")} + {t("ClientListContainer|Save")} - } - - -
-
-
- - -
- - {t("ClientCreateContainer|Multidomain")} -
-
- - - - - -
- - - -
- - {t("ClientCreateContainer|Authorization")} -
-
- - {(metaData != undefined) && metaData["properties"] && metaData["properties"]["code_challenge_method"] && - } - -
- - - -
- - {t("ClientCreateContainer|Access control")} -
-
- - props.history.push(`/auth/clients/${client_id}`)} + > + {t("Cancel")} + + + : + + {t("ClientCreateContainer|Create")} + + } + + +
+ + + +
+ + {t("ClientCreateContainer|Multidomain")} +
+
+ + + + + +
+ + + +
+ + {t("ClientCreateContainer|Authorization")} +
+
+ + {(metaData != undefined) && metaData["properties"] && metaData["properties"]["code_challenge_method"] && + - -
-
+ labelName={t('ClientCreateContainer|Code challenge method (PKCE)')} + editing={(client != undefined)} + />} + + + + + +
+ + {t("ClientCreateContainer|Access control")} +
+
+ + + +
{/*Display the card when the client is being edited*/} From 035c70ebadd7a2f0bd22a571eca293514962de36 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Thu, 25 May 2023 17:34:47 +0200 Subject: [PATCH 09/23] syntax update --- src/modules/auth/roles/RolesResourcesCard.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/auth/roles/RolesResourcesCard.js b/src/modules/auth/roles/RolesResourcesCard.js index 2f8258f7..5a13f060 100644 --- a/src/modules/auth/roles/RolesResourcesCard.js +++ b/src/modules/auth/roles/RolesResourcesCard.js @@ -182,8 +182,7 @@ const RolesResourcesCard = (props) => {
: - null - } + null} From fd1ce9724bb936e66275dc0fd5f98824537ec727 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Thu, 25 May 2023 17:35:22 +0200 Subject: [PATCH 10/23] more button in dropdown --- .../auth/tenant/TenantDetailContainer.js | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/modules/auth/tenant/TenantDetailContainer.js b/src/modules/auth/tenant/TenantDetailContainer.js index 72e8efed..05625e9d 100644 --- a/src/modules/auth/tenant/TenantDetailContainer.js +++ b/src/modules/auth/tenant/TenantDetailContainer.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, useMemo } from 'react'; import { useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; import { Link } from "react-router-dom"; @@ -34,9 +34,11 @@ function TenantDetailContainer(props) { const currentTenant = useSelector(state => state.tenant?.current); const [count, setCount] = useState(0); + const [dropdownCount, setDropdownCount] = useState(0); const timeoutRef = useRef(null); const [page, setPage] = useState(1); const [filter, setFilter] = useState(""); + const [dropdownLimit, setDropdownLimit] = useState(10); const limit = 10; const [loading, setLoading] = useState(true); const [loadingCustomData, setLoadingCustomData] = useState(true); @@ -99,6 +101,10 @@ function TenantDetailContainer(props) { const suspendRow = { condition: (row) => (row.suspended === true), className: "bg-light" }; + useEffect(() => { + retrieveData(); + }, []); + useEffect(() => { if (timeoutRef.current !== null) { clearTimeout(timeoutRef.current); @@ -118,9 +124,9 @@ function TenantDetailContainer(props) { retrieveAssignedCredentials(); }, [page]); - useEffect(() => { - retrieveData(); - }, []); + useEffect(() => { + retrieveCredentialsForDropdown(); + }, [dropdownLimit]); const retrieveData = async () => { try { @@ -182,11 +188,12 @@ function TenantDetailContainer(props) { const retrieveCredentialsForDropdown = async () => { let response; try { - response = await SeaCatAuthAPI.get("/credentials", {params: {p:page, i: limit, f: filter}}); + response = await SeaCatAuthAPI.get("/credentials", {params: {i: dropdownLimit, f: filter}}); if (response.data.result !== "OK") { throw new Error(t("TenantDetailContainer|Something went wrong, failed to fetch data")); } setAssignedCredentialsDropdown(response.data.data); + setDropdownCount(response.data.count); setLoading(false); } catch(e) { console.error(e); @@ -290,8 +297,21 @@ function TenantDetailContainer(props) { ) } else { return null } - })) - } + }))} + {dropdownCount > dropdownLimit ? + <> + + { + setDropdownLimit(dropdownLimit + 5); + toggleDropdown(); + }} + > + {t("RolesResourcesCard|More")} + + + : + null} {assignedCredentialsDropdown.length === 0 && {t("TenantDetailContainer|No match")}} From fa02fb5a28e09ffd9b8527744709f023a41de8e0 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Thu, 25 May 2023 18:29:48 +0200 Subject: [PATCH 11/23] locales update --- public/locales/cs/translation.json | 6 ++++-- public/locales/en/translation.json | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/public/locales/cs/translation.json b/public/locales/cs/translation.json index ab693c66..21b4b995 100644 --- a/public/locales/cs/translation.json +++ b/public/locales/cs/translation.json @@ -323,7 +323,8 @@ "Credentials suspended": "Uživatel suspendován", "Something went wrong, failed to fetch assigned credentials": "Něco je špatně, nepodařilo se získat přiřazené uživatelské údaje", "Assign credentials": "Přiřadit uživatelské údaje", - "Assigned credentials": "Přiřazené uživatelské údaje" + "Assigned credentials": "Přiřazené uživatelské údaje", + "More": "Více ..." }, "RolesCreateContainer": { "Global role": "Globální role", @@ -465,7 +466,8 @@ "Failed to remove the tenant": "Nepodařilo se odstranit tenanta", "Tenant removed successfully, you will be logged out in a while": "Tenant úspěšně odstraněn, za moment budete odhlášeni", "Tenant removed successfully": "Tenant úspěšně odstraněn", - "Tenant data updated successfully": "Tenant úspěšně aktualizován" + "Tenant data updated successfully": "Tenant úspěšně aktualizován", + "More": "Více..." }, "TenantCreateContainer": { "Tenant has been created": "Tenant byl vytvořen", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 2d39aa62..d1e1c233 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -323,7 +323,8 @@ "Credentials suspended": "Credentials suspended", "Something went wrong, failed to fetch assigned credentials": "Something went wrong, failed to fetch assigned credentials", "Assign credentials": "Assign credentials", - "Assigned credentials": "Assigned credentials" + "Assigned credentials": "Assigned credentials", + "More": "More..." }, "RolesCreateContainer": { "Global role": "Global role", @@ -465,7 +466,8 @@ "Failed to remove the tenant": "Failed to remove the tenant", "Tenant removed successfully, you will be logged out in a while": "Tenant removed successfully, you will be logged out in a while", "Tenant removed successfully": "Tenant removed successfully", - "Tenant data updated successfully": "Tenant data updated successfully" + "Tenant data updated successfully": "Tenant data updated successfully", + "More": "More..." }, "TenantCreateContainer": { "Tenant has been created": "Tenant has been created", From c051a27db80a56d196a71983bf2b0cde116aceb3 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Thu, 25 May 2023 18:30:11 +0200 Subject: [PATCH 12/23] more button in dropdowns --- .../auth/roles/RolesDetailContainer.js | 26 +++++++++++++++++-- .../auth/tenant/TenantDetailContainer.js | 7 ++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/modules/auth/roles/RolesDetailContainer.js b/src/modules/auth/roles/RolesDetailContainer.js index cb71e605..8b6e267c 100644 --- a/src/modules/auth/roles/RolesDetailContainer.js +++ b/src/modules/auth/roles/RolesDetailContainer.js @@ -36,6 +36,8 @@ const RolesDetailContainer = (props) => { const [loading, setLoading] = useState(true); const [show, setShow] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false); + const [dropdownCount, setDropdownCount] = useState(0); + const [dropdownLimit, setDropdownLimit] = useState(10); const toggleDropdown = () => setDropdownOpen(prevState => !prevState); const limit = 10; @@ -118,6 +120,10 @@ const RolesDetailContainer = (props) => { getRoleDetail(); }, []); + useEffect(() => { + retrieveCredentialsForDropdown(); + }, [dropdownLimit]); + const getRoleDetail = async () => { try { @@ -170,11 +176,12 @@ const RolesDetailContainer = (props) => { const retrieveCredentialsForDropdown = async () => { let response; try { - response = await SeaCatAuthAPI.get("/credentials", {params: {p:page, i: limit, f: filter}}); + response = await SeaCatAuthAPI.get("/credentials", {params: {i: dropdownLimit, f: filter}}); if (response.data.result !== "OK") { throw new Error(t("RolesDetailContainer|Something went wrong, failed to fetch data")); } setAssignedCredentialsDropdown(response.data.data); + setDropdownCount(response.data.count); setLoading(false); } catch(e) { console.error(e); @@ -230,7 +237,7 @@ const RolesDetailContainer = (props) => { } const assignNewCredentials = ( - retrieveCredentialsForDropdown()}> + { } else { return null } })) } + {dropdownCount > dropdownLimit ? + <> + + { + setDropdownLimit(dropdownLimit + 5); + toggleDropdown(); + }} + > + {t("RolesDetailContainer|More")} + + + : + null + } {assignedCredentialsDropdown.length === 0 && {t("RolesDetailContainer|No match")}} diff --git a/src/modules/auth/tenant/TenantDetailContainer.js b/src/modules/auth/tenant/TenantDetailContainer.js index 05625e9d..1a964112 100644 --- a/src/modules/auth/tenant/TenantDetailContainer.js +++ b/src/modules/auth/tenant/TenantDetailContainer.js @@ -256,7 +256,7 @@ function TenantDetailContainer(props) { } const assignNewCredentials = ( - retrieveCredentialsForDropdown()}> + - {t("RolesResourcesCard|More")} + {t("TenantDetailContainer|More")} : - null} + null + } {assignedCredentialsDropdown.length === 0 && {t("TenantDetailContainer|No match")}} From a863279357ee402d4917c8d7d22f89c2179e1900 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Thu, 25 May 2023 18:41:39 +0200 Subject: [PATCH 13/23] whitespace removal --- src/modules/auth/roles/RolesDetailContainer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/auth/roles/RolesDetailContainer.js b/src/modules/auth/roles/RolesDetailContainer.js index 8b6e267c..bbd85b42 100644 --- a/src/modules/auth/roles/RolesDetailContainer.js +++ b/src/modules/auth/roles/RolesDetailContainer.js @@ -115,7 +115,6 @@ const RolesDetailContainer = (props) => { } }, [role, page]); - useEffect(() => { getRoleDetail(); }, []); @@ -124,7 +123,6 @@ const RolesDetailContainer = (props) => { retrieveCredentialsForDropdown(); }, [dropdownLimit]); - const getRoleDetail = async () => { try { let response = await SeaCatAuthAPI.get(`role/${tenant_id}/${role_name}`); From 54749eb1bff3609a0ee8d68225a4f1c2f9a936ef Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Thu, 25 May 2023 18:44:00 +0200 Subject: [PATCH 14/23] CHANGELOG.md update --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20c00b27..fd6650e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Changing logo based on app's theme (INDIGO Sprint 230428, [!12](https://github.com/TeskaLabs/seacat-admin-webui/pull/12)) +- Pagination via `More..` button in dropdowns. Clients refactored to easily maintainable css grid. (INDIGO 230512, [!35](https://github.com/TeskaLabs/seacat-admin-webui/pull/35)) + ### Refactoring - Vertical spacing unification across seacat admin screens app. (INDIGO Sprint 230414, [!26](https://github.com/TeskaLabs/seacat-admin-webui/pull/26)) From c71729e410f1e422bf6b33a8337df0e9b70d7e3b Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Fri, 26 May 2023 10:12:40 +0200 Subject: [PATCH 15/23] translations update --- src/modules/auth/clients/ClientCreateContainer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/auth/clients/ClientCreateContainer.js b/src/modules/auth/clients/ClientCreateContainer.js index 1e4eef0b..1fc1fc5e 100644 --- a/src/modules/auth/clients/ClientCreateContainer.js +++ b/src/modules/auth/clients/ClientCreateContainer.js @@ -255,14 +255,14 @@ const ClientCreateContainer = (props) => { {((client != undefined) && (editClient == true)) ? <> - {t("ClientListContainer|Save")} + {t("Save")} Date: Mon, 29 May 2023 13:20:31 +0200 Subject: [PATCH 16/23] unassigned resources filter removal --- src/modules/auth/roles/RolesResourcesCard.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/modules/auth/roles/RolesResourcesCard.js b/src/modules/auth/roles/RolesResourcesCard.js index 5a13f060..9474454a 100644 --- a/src/modules/auth/roles/RolesResourcesCard.js +++ b/src/modules/auth/roles/RolesResourcesCard.js @@ -59,12 +59,7 @@ const RolesResourcesCard = (props) => { try { let response = await SeaCatAuthAPI.get(`/resource`, {params: { f: filter, i: limit }}); const allResources = response.data.data.map(resource => resource._id); - const unassignedResources = allResources.filter(resource => assignedResources.indexOf(resource) < 0); - // Remove authz:superuser from unassigned resources on every role, which is not global - if (roleId.indexOf('*/') == -1){ - unassignedResources.splice(unassignedResources.indexOf('authz:superuser'), 1); - } - setUnassignedResources(unassignedResources); + setUnassignedResources(allResources); setCount(response.data.count); } catch(e) { console.error(e); @@ -73,13 +68,16 @@ const RolesResourcesCard = (props) => { } const assignResource = (resource) => { - setAssignedResources([...assignedResources, resource]); - setUnassignedResources(unassignedResources.filter(currentResource => currentResource !== resource)); + // checks if the resource is already assigned to prevent selecting the same resource twice + if (assignedResources.indexOf(resource) === -1 ) { + setAssignedResources([...assignedResources, resource]); + } else { + setDropdown(prev => !prev); + } } const unassignResource = (resource) => { setAssignedResources(assignedResources.filter(currentResource => currentResource !== resource)); - setUnassignedResources([...unassignedResources, resource]); } const onSave = async () => { @@ -95,7 +93,7 @@ const RolesResourcesCard = (props) => { const onCancel = () => { fetchAssignedResources(); - fetchUnassignedResources(); + // fetchUnassignedResources(); } return ( From bffa55cfa2714c5fcd12622a7297e0499891004a Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Mon, 29 May 2023 14:07:06 +0200 Subject: [PATCH 17/23] obsolete code removal --- src/modules/auth/roles/RolesResourcesCard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/auth/roles/RolesResourcesCard.js b/src/modules/auth/roles/RolesResourcesCard.js index 9474454a..691abdbf 100644 --- a/src/modules/auth/roles/RolesResourcesCard.js +++ b/src/modules/auth/roles/RolesResourcesCard.js @@ -73,6 +73,7 @@ const RolesResourcesCard = (props) => { setAssignedResources([...assignedResources, resource]); } else { setDropdown(prev => !prev); + props.app.addAlert("warning", `${t("RolesResourcesCard|Resource")} ${resource} ${t("RolesResourcesCard|already selected")}`, 5); } } @@ -93,7 +94,6 @@ const RolesResourcesCard = (props) => { const onCancel = () => { fetchAssignedResources(); - // fetchUnassignedResources(); } return ( From 02574ef6606b9b9de1e5cccf04144102dfc1badb Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Mon, 29 May 2023 14:07:35 +0200 Subject: [PATCH 18/23] filtering removed from dropdown --- .../auth/tenant/TenantDetailContainer.js | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/modules/auth/tenant/TenantDetailContainer.js b/src/modules/auth/tenant/TenantDetailContainer.js index 1a964112..5aacfd58 100644 --- a/src/modules/auth/tenant/TenantDetailContainer.js +++ b/src/modules/auth/tenant/TenantDetailContainer.js @@ -280,23 +280,19 @@ function TenantDetailContainer(props) { {t("TenantDetailContainer|Loading...")} : (assignedCredentialsDropdown && Object.keys(assignedCredentialsDropdown).map((item, i) => { - let checkCredentialsAvailability = credentialsList.findIndex(elem => elem._id === assignedCredentialsDropdown[item]._id); - if (checkCredentialsAvailability === -1) { - // Display only if the credentials is not already assigned - return ( - assignCredentials(assignedCredentialsDropdown[item]._id)}> - {assignedCredentialsDropdown[item].username ? - {assignedCredentialsDropdown[item].username} - : - - } - - ) - } else { return null } + return ( + assignCredentials(assignedCredentialsDropdown[item]._id)}> + {assignedCredentialsDropdown[item].username ? + {assignedCredentialsDropdown[item].username} + : + + } + + ) }))} {dropdownCount > dropdownLimit ? <> From 1a477a8f6a03fc7dae8ce1fda9dbb568bdb2c650 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Mon, 29 May 2023 14:07:54 +0200 Subject: [PATCH 19/23] locales udpate --- public/locales/cs/translation.json | 4 +++- public/locales/en/translation.json | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/public/locales/cs/translation.json b/public/locales/cs/translation.json index 1efb9f1b..2708b4ba 100644 --- a/public/locales/cs/translation.json +++ b/public/locales/cs/translation.json @@ -355,7 +355,9 @@ "Edit": "Upravit", "No data": "Žádná data", "More": "Více...", - "Search": "Hledat" + "Search": "Hledat", + "Resource": "Zdroj", + "already selected": "byl již zvolen" }, "ResourcesCreateContainer": { "Resource created": "Zdroj byl úspěšně vytvořen", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index a5dd85a6..c4eeb336 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -355,7 +355,9 @@ "Edit": "Edit", "No data": "No data", "More": "More...", - "Search": "Search" + "Search": "Search", + "Resource": "Resource", + "already selected": "already selected" }, "ResourcesCreateContainer": { "Resource created": "Resource has been created", From 53bf78daa5b535df1a59263298407e40959467db Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Mon, 29 May 2023 14:08:14 +0200 Subject: [PATCH 20/23] obsolete code removal --- .../auth/roles/RolesDetailContainer.js | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/modules/auth/roles/RolesDetailContainer.js b/src/modules/auth/roles/RolesDetailContainer.js index bbd85b42..fd7589be 100644 --- a/src/modules/auth/roles/RolesDetailContainer.js +++ b/src/modules/auth/roles/RolesDetailContainer.js @@ -259,23 +259,19 @@ const RolesDetailContainer = (props) => { {t("RolesDetailContainer|Loading...")} : (assignedCredentialsDropdown && Object.keys(assignedCredentialsDropdown).map((item, i) => { - let checkCredentialsAvailability = credentialsList.findIndex(elem => elem._id === assignedCredentialsDropdown[item]._id); - if (checkCredentialsAvailability === -1) { - // Display only if the credentials is not already assigned - return ( - assignCredentials(assignedCredentialsDropdown[item]._id)}> - {assignedCredentialsDropdown[item].username ? - {assignedCredentialsDropdown[item].username} - : - - } - - ) - } else { return null } + return ( + assignCredentials(assignedCredentialsDropdown[item]._id)}> + {assignedCredentialsDropdown[item].username ? + {assignedCredentialsDropdown[item].username} + : + + } + + ) })) } {dropdownCount > dropdownLimit ? From 1d2cdbfd906453e66e0fb6ba84de1bf82cfdcbcb Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Tue, 30 May 2023 11:42:48 +0200 Subject: [PATCH 21/23] locales update --- public/locales/cs/translation.json | 8 ++++++-- public/locales/en/translation.json | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/public/locales/cs/translation.json b/public/locales/cs/translation.json index 2708b4ba..daf2403c 100644 --- a/public/locales/cs/translation.json +++ b/public/locales/cs/translation.json @@ -221,16 +221,19 @@ }, "CredentialsRolesCard": { "More": "Více ...", + "Role": "Role", "Roles": "Role", "Assign a new role": "Přidat novou roli", "Select role to assign ...": "Vyberte roli k přiřazení ...", "Roles updated successfully": "Role úspěšně aktualizované", "Failed to update roles": "Role se nepodařilo aktualizovat", "Something went wrong, failed to fetch roles": "Něco je špatně, nepodařilo se získat role", - "Something went wrong, failed to fetch assigned roles": "Něco je špatně, nepodařilo se získat přiřazené role" + "Something went wrong, failed to fetch assigned roles": "Něco je špatně, nepodařilo se získat přiřazené role", + "already selected": "byl již zvolen" }, "CredentialsTenantsCard": { "More": "Více ...", + "Tenant": "Tenant", "Tenants": "Tenanti", "Assign a tenant": "Přiřadit tenanta", "Search": "Vyhledat", @@ -238,7 +241,8 @@ "Failed to update tenants": "Tenanty se nepodařilo aktualizovat", "Something went wrong, failed to fetch tenants": "Něco je špatně, nepodařilo se získat tenanty", "Something went wrong, failed to fetch assigned tenants": "Něco je špatně, nepodařilo se získat přiřazené tenanty", - "No match": "Shoda nenalezena" + "No match": "Shoda nenalezena", + "already selected": "byl již zvolen" }, "CredentialsSessionCard": { "Sessions": "Relace", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index c4eeb336..3aedac6b 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -221,16 +221,19 @@ }, "CredentialsRolesCard": { "More": "More...", + "Role": "Role", "Roles": "Roles", "Assign a new role": "Assign a new role", "Select role to assign ...": "Select role to assign ...", "Roles updated successfully": "Roles updated successfully", "Failed to update roles": "Failed to update roles", "Something went wrong, failed to fetch roles": "Something went wrong, failed to fetch roles", - "Something went wrong, failed to fetch assigned roles": "Something went wrong, failed to fetch assigned roles" + "Something went wrong, failed to fetch assigned roles": "Something went wrong, failed to fetch assigned roles", + "already selected": "already selected" }, "CredentialsTenantsCard": { "More": "More...", + "Tenant": "Tenant", "Tenants": "Tenants", "Assign a tenant": "Assign a tenant", "Search": "Search", @@ -238,7 +241,8 @@ "Failed to update tenants": "Failed to update tenants", "Something went wrong, failed to fetch tenants": "Something went wrong, failed to fetch tenants", "Something went wrong, failed to fetch assigned tenants": "Something went wrong, failed to fetch assigned tenants", - "No match": "No match" + "No match": "No match", + "already selected": "already selected" }, "CredentialsSessionCard": { "Sessions": "Sessions", From d0e3e62149064344059a8a51ae639dd6b775baa9 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Tue, 30 May 2023 11:43:56 +0200 Subject: [PATCH 22/23] filtering removed from dropdown --- src/modules/auth/credentials/CredentialsRolesCard.js | 6 ++---- src/modules/auth/credentials/CredentialsTenantsCard.js | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/modules/auth/credentials/CredentialsRolesCard.js b/src/modules/auth/credentials/CredentialsRolesCard.js index f4503afe..7388a144 100644 --- a/src/modules/auth/credentials/CredentialsRolesCard.js +++ b/src/modules/auth/credentials/CredentialsRolesCard.js @@ -69,6 +69,8 @@ function CredentialsRolesCard(props) { let x = assignedRoles.find((existing) => { return existing == role }); if (x == null) { setAssignedRoles(assignedRoles.concat(role)) + } else { + props.app.addAlert("warning", `${t("CredentialsRolesCard|Role")} ${role} ${t("CredentialsRolesCard|already selected")}`, 5); } } @@ -142,15 +144,11 @@ function CredentialsRolesCard(props) { {t("CredentialsRolesCard|Select role to assign ...")} {Object.values(rolesLookup).map((role_id, i) => { - let x = assignedRoles.find((existing) => { return existing == role_id._id }); - if (x == null) { - // Add only if the role is not already assigned return ( assignRole(role_id._id)}> ) - } else { return null } })} {count > limit ? <> diff --git a/src/modules/auth/credentials/CredentialsTenantsCard.js b/src/modules/auth/credentials/CredentialsTenantsCard.js index 00fd9b9b..c5ac391a 100644 --- a/src/modules/auth/credentials/CredentialsTenantsCard.js +++ b/src/modules/auth/credentials/CredentialsTenantsCard.js @@ -68,6 +68,8 @@ function CredentialsTenantsCard(props) { let x = assignedTenants.find((existing) => { return existing == tenant_id }); if (x == null) { setAssignedTenants(assignedTenants.concat(tenant_id)) + } else { + props.app.addAlert("warning", `${t("CredentialsTenantsCard|Tenant")} ${tenant_id} ${t("CredentialsTenantsCard|already selected")}`, 5); } } @@ -185,9 +187,6 @@ function CredentialsTenantsCard(props) { /> {(allTenants.length > 0) && allTenants.map((tenant, i) => { - let aleadyAssignedTenant = assignedTenants.find((existing) => { return existing == tenant._id }); - if (aleadyAssignedTenant == null) { - // Display only if the tenant is not already assigned return ( ) - } else { return null } })} {(count > limit) ? <> From 89dd566658f446d151e9d69cb9f2cead76a368b3 Mon Sep 17 00:00:00 2001 From: petrKavulok Date: Tue, 30 May 2023 11:59:06 +0200 Subject: [PATCH 23/23] CHANGELOG.md update --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9df6b522..49fa3e3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ - Changing logo based on app's theme (INDIGO Sprint 230428, [!12](https://github.com/TeskaLabs/seacat-admin-webui/pull/12)) -- Pagination via `More..` button in dropdowns. Clients refactored to easily maintainable css grid. (INDIGO 230512, [!35](https://github.com/TeskaLabs/seacat-admin-webui/pull/35)) +- Pagination via `More..` button in dropdowns. Clients refactored to easily maintainable css grid. (INDIGO 230526, [!35](https://github.com/TeskaLabs/seacat-admin-webui/pull/35)) ### Refactoring