From a5abcc6e607dc61437fce6ddc8ed9e870fb7ae91 Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Tue, 28 Jul 2026 18:22:33 -0300 Subject: [PATCH 1/4] chore: core migration - WIP --- src/actions/audit-log-actions.js | 26 +- src/components/audit-logs/index.js | 337 +++++++++----------------- src/pages/audit-log/audit-log-page.js | 23 +- 3 files changed, 123 insertions(+), 263 deletions(-) diff --git a/src/actions/audit-log-actions.js b/src/actions/audit-log-actions.js index 0b9cb3d9a..1c56c90d0 100644 --- a/src/actions/audit-log-actions.js +++ b/src/actions/audit-log-actions.js @@ -19,11 +19,7 @@ import { authErrorHandler, escapeFilterValue } from "openstack-uicore-foundation/lib/utils/actions"; -import { - getAccessTokenSafely, - isNumericString, - parseDateRangeFilter -} from "../utils/methods"; +import { getAccessTokenSafely, isNumericString } from "../utils/methods"; import { DEFAULT_CURRENT_PAGE, DEFAULT_ORDER_DIR } from "../utils/constants"; export const CLEAR_LOG_PARAMS = "CLEAR_LOG_PARAMS"; @@ -32,22 +28,8 @@ export const RECEIVE_LOG = "RECEIVE_LOG"; const DEFAULT_PER_PAGE_AUDIT_LOG = 100; -const parseFilters = (filters, term = null) => { - const filter = []; - - if (filters.created_date_filter) { - parseDateRangeFilter(filter, filters.created_date_filter, "created"); - } - - if ( - filters.hasOwnProperty("user_id_filter") && - Array.isArray(filters.user_id_filter) && - filters.user_id_filter.length > 0 - ) { - filter.push( - `user_id==${filters.user_id_filter.map((t) => t.id).join("||")}` - ); - } +const parseFilters = (filters = [], term = null) => { + const filter = Array.isArray(filters) ? [...filters] : []; if (term) { const escapedTerm = escapeFilterValue(term); @@ -73,7 +55,7 @@ export const getAuditLog = perPage = DEFAULT_PER_PAGE_AUDIT_LOG, order = null, orderDir = DEFAULT_ORDER_DIR, - filters = {} + filters = [] ) => async (dispatch, getState) => { const { currentSummitState } = getState(); diff --git a/src/components/audit-logs/index.js b/src/components/audit-logs/index.js index 81cb2a9d6..beddd2280 100644 --- a/src/components/audit-logs/index.js +++ b/src/components/audit-logs/index.js @@ -1,105 +1,110 @@ import React, { useEffect, useState } from "react"; -import FreeTextSearch from "openstack-uicore-foundation/lib/components/free-text-search" -import Table from "openstack-uicore-foundation/lib/components/table" -import Dropdown from "openstack-uicore-foundation/lib/components/inputs/dropdown" -import MemberInput from "openstack-uicore-foundation/lib/components/inputs/member-input" -import DateTimePicker from "openstack-uicore-foundation/lib/components/inputs/datetimepicker"; +import { Grid2 } from "@mui/material"; +import SearchInput from "openstack-uicore-foundation/lib/components/mui/search-input"; +import MuiTable from "openstack-uicore-foundation/lib/components/mui/table"; +import { + GridFilter, + useGridFilter, + OPERATORS +} from "openstack-uicore-foundation/lib/components/mui/grid-filter"; +import { queryMembers } from "openstack-uicore-foundation/lib/utils/query-actions"; import T from "i18n-react"; -import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods"; -import { Pagination } from "react-bootstrap"; import { connect } from "react-redux"; import { clearAuditLogParams as clearAuditLogParamsAction, getAuditLog as getAuditLogAction } from "../../actions/audit-log-actions"; -import { - DATE_FILTER_ARRAY_SIZE, - DEFAULT_CURRENT_PAGE -} from "../../utils/constants"; +import { DEFAULT_CURRENT_PAGE } from "../../utils/constants"; + +const FILTER_ID = "audit_log_list"; + +const getCriterias = () => [ + { + key: "user_id", + label: T.translate("audit_log.placeholders.user_id"), + operators: [OPERATORS.IS], + values: { + type: "asyncSelect", + props: { + queryFunction: queryMembers, + formatOption: (m) => ({ + value: m.id, + label: `${m.first_name} ${m.last_name} (${m.email})` + }), + multiple: true + } + }, + customParser: (f) => [`user_id==${f.value.map((s) => s.value).join("||")}`] + }, + { + key: "created", + label: T.translate("audit_log.date"), + operators: [OPERATORS.BEFORE, OPERATORS.AFTER], + values: { + type: "datetime", + props: { + mode: "datetime" + } + } + } +]; const AuditLogs = ({ entityFilter = [], - currentSummit, term, logEntries, perPage, - lastPage, currentPage, + totalLogEntries, order, orderDir, columns, getAuditLog, - clearAuditLogParams, - filters + clearAuditLogParams }) => { - const [page, setPage] = useState(currentPage); const [searchTerm, setSearchTerm] = useState(term); - const defaultFilters = { - user_id_filter: [], - created_date_filter: Array(DATE_FILTER_ARRAY_SIZE).fill(null) - }; - - const [enabledFilters, setEnabledFilters] = useState( - Object.keys(filters).filter((e) => - Array.isArray(filters[e]) - ? filters[e]?.some((a) => a !== null) - : filters[e]?.length > 0 - ) - ); - const [auditLogFilters, setAuditLogFilters] = useState({ - ...defaultFilters, - ...filters - }); - - const filtersDdl = [ - { label: "Created", value: "created_date_filter" }, - { label: "Member", value: "user_id_filter" } - ]; - - const auditLogTableOptions = { - sortCol: order, - sortDir: orderDir, - actions: {} - }; + const { parsedFilter, resetFilters } = useGridFilter(FILTER_ID); const auditLogColumns = [ { columnKey: "created", - value: T.translate("audit_log.date"), + header: T.translate("audit_log.date"), sortable: true }, { columnKey: "action_description", - value: T.translate("audit_log.action"), + header: T.translate("audit_log.action"), sortable: false }, { columnKey: "event_id", - value: T.translate("audit_log.event"), + header: T.translate("audit_log.event"), sortable: true }, - { columnKey: "user", value: T.translate("audit_log.user"), sortable: false } + { + columnKey: "user", + header: T.translate("audit_log.user"), + sortable: false + } ]; const showColumns = columns ? auditLogColumns.filter((c) => columns.includes(c.columnKey)) : auditLogColumns; - const handleSort = (_index, key, dir) => { - setPage(1); + const handleSort = (key, dir) => { getAuditLog( entityFilter, searchTerm, - 1, + DEFAULT_CURRENT_PAGE, perPage, key, dir, - auditLogFilters + parsedFilter ); }; const handlePageChange = (newPage) => { - setPage(newPage); getAuditLog( entityFilter, searchTerm, @@ -107,87 +112,35 @@ const AuditLogs = ({ perPage, order, orderDir, - auditLogFilters + parsedFilter ); }; - const handleSearch = (newTerm) => { - setSearchTerm(newTerm); - setPage(1); + const handlePerPageChange = (newPerPage) => { getAuditLog( entityFilter, - newTerm, - 1, - perPage, + searchTerm, + DEFAULT_CURRENT_PAGE, + newPerPage, order, orderDir, - auditLogFilters + parsedFilter ); }; - const handleDDLSortByLabel = (ddlArray) => - ddlArray.sort((a, b) => a.label.localeCompare(b.label)); - - const handleFiltersChange = (ev) => { - const { value } = ev.target; - if (value.length < enabledFilters.length) { - if (value.length === 0) { - setEnabledFilters(value); - setAuditLogFilters(defaultFilters); - } else { - const removedFilter = enabledFilters.filter( - (e) => !value.includes(e) - )[0]; - const defaultValue = Array.isArray(auditLogFilters[removedFilter]) - ? [] - : ""; - const newEventFilters = { - ...auditLogFilters, - [removedFilter]: defaultValue - }; - setEnabledFilters(value); - setAuditLogFilters(newEventFilters); - } - } else { - setEnabledFilters(value); - } - }; - - const handleChangeDateFilter = (ev, lastDate) => { - const { value, id } = ev.target; - const newDateFilter = auditLogFilters[id]; - - setAuditLogFilters({ - ...auditLogFilters, - [id]: lastDate - ? [newDateFilter[0], value.unix()] - : [value.unix(), newDateFilter[1]] - }); - }; - - const handleAuditLogFilterChange = (ev) => { - const { value, id } = ev.target; - setAuditLogFilters({ ...auditLogFilters, [id]: value }); - }; - - const handleApplyAuditLogFilters = () => { - setPage(1); + const handleSearch = (newTerm) => { + setSearchTerm(newTerm); getAuditLog( entityFilter, - searchTerm, - 1, + newTerm, + DEFAULT_CURRENT_PAGE, perPage, order, orderDir, - auditLogFilters + parsedFilter ); }; - const getUserFieldValue = (member) => - `${member.first_name ?? ""} ${member.last_name ?? ""} (${ - member.email ?? member.id - })`; - useEffect(() => { getAuditLog( entityFilter, @@ -196,132 +149,62 @@ const AuditLogs = ({ perPage, order, orderDir, - filters + parsedFilter ); - - return () => { + }, [parsedFilter.join(",")]); + + // AuditLogs is reused in different contexts (the standalone audit log + // page, an event's edit form, a ticket's edit page) sharing one Redux- + // backed FILTER_ID, so a filter applied in one context must not leak into + // another mount — reset it along with the log params on unmount. + useEffect( + () => () => { clearAuditLogParams(); - }; - }, []); + resetFilters(); + }, + [] + ); + + const tableOptions = { + sortCol: order, + sortDir: orderDir + }; return ( <> -
-
- + + -
-
- -
- -
-
- -
-
- -
-
-
- {enabledFilters.includes("user_id_filter") && ( -
- -
- )} - {enabledFilters.includes("created_date_filter") && ( - <> -
- handleChangeDateFilter(ev, false)} - timezone={currentSummit.time_zone_id} - value={epochToMomentTimeZone( - auditLogFilters.created_date_filter[0], - currentSummit.time_zone_id - )} - className="event-list-date-picker" - /> -
-
- handleChangeDateFilter(ev, true)} - timezone={currentSummit.time_zone_id} - value={epochToMomentTimeZone( - auditLogFilters.created_date_filter[1], - currentSummit.time_zone_id - )} - className="event-list-date-picker" - /> -
- - )} -
+ + + + + {logEntries.length === 0 && (
{T.translate("audit_log.no_log_entries")}
)} {logEntries.length > 0 && ( - <> - - - + )} ); diff --git a/src/pages/audit-log/audit-log-page.js b/src/pages/audit-log/audit-log-page.js index 4012421f1..ebcf36aca 100644 --- a/src/pages/audit-log/audit-log-page.js +++ b/src/pages/audit-log/audit-log-page.js @@ -9,26 +9,21 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - **/ + * */ import React from "react"; import { connect } from "react-redux"; import T from "i18n-react/dist/i18n-react"; import AuditLogs from "../../components/audit-logs"; -import { Breadcrumb } from "react-breadcrumbs"; -const AuditLogPage = ({ totalLogEntries, match }) => { - return ( -
- -

- {" "} - {T.translate("audit_log.log_entries")} ({totalLogEntries}) -

- -
- ); -}; +const AuditLogPage = ({ totalLogEntries }) => ( +
+

+ {T.translate("audit_log.log_entries")} ({totalLogEntries}) +

+ +
+); const mapStateToProps = ({ auditLogState }) => ({ ...auditLogState From 7904166f60cd11976927f79a50d0351a4ea94f01 Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Wed, 29 Jul 2026 10:28:44 -0300 Subject: [PATCH 2/4] chore: adjust styles --- src/components/audit-logs/index.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/audit-logs/index.js b/src/components/audit-logs/index.js index beddd2280..2ddc054d1 100644 --- a/src/components/audit-logs/index.js +++ b/src/components/audit-logs/index.js @@ -74,7 +74,9 @@ const AuditLogs = ({ { columnKey: "action_description", header: T.translate("audit_log.action"), - sortable: false + sortable: false, + width: 600, + truncateText: true }, { columnKey: "event_id", @@ -175,16 +177,18 @@ const AuditLogs = ({ - + - - @@ -197,6 +201,7 @@ const AuditLogs = ({ Date: Wed, 29 Jul 2026 12:32:19 -0300 Subject: [PATCH 3/4] chore: a few bug fixes --- src/components/audit-logs/index.js | 9 +++++++++ src/i18n/en.json | 3 ++- src/reducers/audit_log/audit-log-reducer.js | 7 ++----- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/components/audit-logs/index.js b/src/components/audit-logs/index.js index 2ddc054d1..2a52b35e9 100644 --- a/src/components/audit-logs/index.js +++ b/src/components/audit-logs/index.js @@ -8,6 +8,7 @@ import { OPERATORS } from "openstack-uicore-foundation/lib/components/mui/grid-filter"; import { queryMembers } from "openstack-uicore-foundation/lib/utils/query-actions"; +import CustomAlert from "openstack-uicore-foundation/lib/components/mui/custom-alert"; import T from "i18n-react"; import { connect } from "react-redux"; import { @@ -64,6 +65,11 @@ const AuditLogs = ({ }) => { const [searchTerm, setSearchTerm] = useState(term); const { parsedFilter, resetFilters } = useGridFilter(FILTER_ID); + const userTimeZone = new Intl.DateTimeFormat(undefined, { + timeZoneName: "long" + }) + .formatToParts(new Date()) + .find((part) => part.type === "timeZoneName").value; const auditLogColumns = [ { @@ -192,6 +198,9 @@ const AuditLogs = ({ + {logEntries.length === 0 && (
{T.translate("audit_log.no_log_entries")}
diff --git a/src/i18n/en.json b/src/i18n/en.json index 9d6b91683..deb0c5dc4 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -11,7 +11,7 @@ "entity_not_found": "The entity you are looking for was not found.", "maximum_files": "Maximum number of files has been reached", "payment_profile_not_found_title": "Payment Profile not found", - "payment_profile_not_found": "Missing payment profile for summit {{summitName}}. Please contact support." + "payment_profile_not_found": "Missing payment profile for summit {summitName}. Please contact support." }, "general": { "summit": "Event", @@ -3750,6 +3750,7 @@ "event": "Event", "order": "Order", "apply_filters": "Apply Filters", + "timezone_info": "All dates appear in user local timezone {tz}", "placeholders": { "search_log": "Search By Action / Entity Id / OTEL Span ID / OTEL Trace ID / Ray ID", "user_id": "Filter by User", diff --git a/src/reducers/audit_log/audit-log-reducer.js b/src/reducers/audit_log/audit-log-reducer.js index dee803c66..572a1179e 100644 --- a/src/reducers/audit_log/audit-log-reducer.js +++ b/src/reducers/audit_log/audit-log-reducer.js @@ -11,8 +11,7 @@ * limitations under the License. * */ -import moment from "moment-timezone"; -import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods"; +import { epochToMoment } from "openstack-uicore-foundation/lib/utils/methods"; import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; import { CLEAR_LOG_PARAMS, @@ -66,9 +65,7 @@ const auditLogReducer = (state = DEFAULT_STATE, action) => { user: `${userFullName || e.user.email} ${ e.user?.id ? `(${e.user.id})` : "" }`, - created: moment( - epochToMomentTimeZone(e.created, state.summitTZ) - ).format("MMMM Do YYYY, h:mm a"), + created: epochToMoment(e.created).format("MMMM Do YYYY, h:mm a"), action: formatAuditLog(logEntryAction) }; }); From c0ef6130066d92ec68c5bae9297c3bdee6efeecc Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Wed, 29 Jul 2026 13:18:43 -0300 Subject: [PATCH 4/4] fix: use parsed action instead of raw action_description --- src/components/audit-logs/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/audit-logs/index.js b/src/components/audit-logs/index.js index 2a52b35e9..1f7f53fcd 100644 --- a/src/components/audit-logs/index.js +++ b/src/components/audit-logs/index.js @@ -78,7 +78,7 @@ const AuditLogs = ({ sortable: true }, { - columnKey: "action_description", + columnKey: "action", header: T.translate("audit_log.action"), sortable: false, width: 600,