diff --git a/src/components/mui/async-select-input.js b/src/components/mui/async-select-input.js new file mode 100644 index 000000000..eb6ce8b03 --- /dev/null +++ b/src/components/mui/async-select-input.js @@ -0,0 +1,61 @@ +import React, { useEffect, useState } from "react"; +import { Autocomplete, CircularProgress, TextField } from "@mui/material"; + +const AsyncSelectInput = ({ + id, + label, + value, + onChange, + queryFunction, + formatOption = (item) => ({ value: item.id, label: item.name }) +}) => { + const [options, setOptions] = useState([]); + const [loading, setLoading] = useState(false); + + const fetchOptions = (input) => { + setLoading(true); + queryFunction(input, (results) => { + setOptions(results.map(formatOption)); + setLoading(false); + }); + }; + + useEffect(() => { + fetchOptions(""); + }, []); + + return ( + option.label || ""} + isOptionEqualToValue={(option, val) => option.value === val.value} + onInputChange={(ev, newInput) => fetchOptions(newInput)} + onChange={(ev, selected) => + onChange({ target: { id, value: selected?.value ?? "" } }) + } + renderInput={(params) => ( + + {loading && } + {params.InputProps?.endAdornment} + + ) + } + }} + /> + )} + /> + ); +}; + +export default AsyncSelectInput; diff --git a/src/components/mui/chip-multi-select.js b/src/components/mui/chip-multi-select.js new file mode 100644 index 000000000..2a0e59efa --- /dev/null +++ b/src/components/mui/chip-multi-select.js @@ -0,0 +1,56 @@ +import React from "react"; +import { + Box, + Chip, + FormControl, + InputLabel, + MenuItem, + OutlinedInput, + Select +} from "@mui/material"; +import CancelIcon from "@mui/icons-material/Cancel"; + +const ChipMultiSelect = ({ id, label, value, onChange, options, sx }) => { + const handleDelete = (val) => + onChange({ target: { value: value.filter((v) => v !== val) } }); + + return ( + + {label} + + + ); +}; + +export default ChipMultiSelect; diff --git a/src/i18n/en.json b/src/i18n/en.json index 58eb3a2c0..9406755e9 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -3265,6 +3265,7 @@ } }, "emails": { + "emails": "Emails", "email_templates": "Email Templates", "sent": "Sent", "templates": "Templates", @@ -3319,6 +3320,9 @@ "email_logs": "Email Logs", "email_list": "Email List", "apply_filters": "Apply Filters", + "enabled_filters": "Enabled Filters", + "all": "All", + "not_sent": "Not Sent", "email_templates": "Email Templates", "subject": "Subject", "from_email": "From Email", @@ -3328,7 +3332,6 @@ "payload": "Payload", "select_fields": "Show Columns", "placeholders": { - "select_fields": "Select data to display", "template": "Filter by Template", "sent_date_from": "Filter Sent Date from", "sent_date_to": "Filter Sent Date to" diff --git a/src/pages/emails/email-log-list-page.js b/src/pages/emails/email-log-list-page.js index 417c16571..1796a482c 100644 --- a/src/pages/emails/email-log-list-page.js +++ b/src/pages/emails/email-log-list-page.js @@ -14,37 +14,47 @@ import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import T from "i18n-react/dist/i18n-react"; -import { Pagination } from "react-bootstrap"; -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 DateTimePicker from "openstack-uicore-foundation/lib/components/inputs/datetimepicker"; +import { + Box, + Button, + Grid2, + ToggleButton, + ToggleButtonGroup +} from "@mui/material"; +import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment"; +import MuiTable from "openstack-uicore-foundation/lib/components/mui/table"; +import SearchInput from "openstack-uicore-foundation/lib/components/mui/search-input"; import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods"; -import { SegmentedControl } from "segmented-control"; -import { getSentEmails } from "../../actions/email-actions"; -import "../../styles/email-logs-page.less"; -import EmailTemplateInput from "../../components/inputs/email-template-input"; +import { getSentEmails, queryTemplates } from "../../actions/email-actions"; +import ChipMultiSelect from "../../components/mui/chip-multi-select"; +import AsyncSelectInput from "../../components/mui/async-select-input"; import { DATE_FILTER_ARRAY_SIZE, DEFAULT_CURRENT_PAGE } from "../../utils/constants"; -const SentEmailListPage = function ({ +const SentEmailListPage = ({ emails, - lastPage, currentPage, term, order, orderDir, totalEmails, - match, perPage, filters, - getSentEmails, - ...props -}) { + getSentEmails +}) => { useEffect(() => { - getSentEmails(term, currentPage, perPage, order, orderDir, filters); + getSentEmails( + term, + DEFAULT_CURRENT_PAGE, + perPage, + order, + orderDir, + filters + ); }, []); const defaultFilters = { @@ -70,10 +80,21 @@ const SentEmailListPage = function ({ getSentEmails(term, newPage, perPage, order, orderDir, emailFilters); }; - const handleSort = (index, key, dir, func) => { + const handleSort = (index, key, dir) => { getSentEmails(term, currentPage, perPage, key, dir, emailFilters); }; + const handlePerPageChange = (newPerPage) => { + getSentEmails( + term, + DEFAULT_CURRENT_PAGE, + newPerPage, + order, + orderDir, + emailFilters + ); + }; + const handleSearch = (newTerm) => { getSentEmails( newTerm, @@ -113,37 +134,19 @@ const SentEmailListPage = function ({ } }; - const handleChangeDateFilter = (ev, lastDate) => { - const { value, id } = ev.target; - const newDateFilter = emailFilters[id]; + const handleChangeDateFilter = (value, lastDate) => { + const newDateFilter = emailFilters.sent_date_filter; setEmailFilters({ ...emailFilters, - [id]: lastDate - ? [newDateFilter[0], value.unix()] - : [value.unix(), newDateFilter[1]] + sent_date_filter: lastDate + ? [newDateFilter[0], value ? value.unix() : null] + : [value ? value.unix() : null, newDateFilter[1]] }); }; const handleEmailFilterChange = (ev) => { - const { type, id } = ev.target; - let { value } = ev.target; - if (type === "operatorinput") { - value = Array.isArray(value) - ? value - : `${ev.target.operator}${ev.target.value}`; - if (id === "duration_filter") { - value = Array.isArray(value) - ? value - : `${ev.target.operator}${ev.target.value}`; - } - } - if (type === "mediatypeinput") { - value = { - operator: ev.target.operator, - value: ev.target.value - }; - } + const { id, value } = ev.target; setEmailFilters({ ...emailFilters, [id]: value }); }; @@ -170,11 +173,11 @@ const SentEmailListPage = function ({ }; const fieldNames = [ - { columnKey: "last_error", value: "last_error" }, + { columnKey: "last_error", header: "last_error" }, { columnKey: "payload", - value: "payload", - render: (row, data) =>
{data}
+ header: "payload", + render: (row, data) => {data} } ]; @@ -183,7 +186,7 @@ const SentEmailListPage = function ({ .map((f2) => { let c = { columnKey: f2.columnKey, - value: T.translate(`email_logs.${f2.value}`), + header: T.translate(`email_logs.${f2.header}`), sortable: f2.sortable }; // optional fields @@ -198,20 +201,20 @@ const SentEmailListPage = function ({ { columnKey: "id", value: T.translate("general.id"), sortable: true }, { columnKey: "template", - value: T.translate("email_logs.email_templates"), + header: T.translate("email_logs.email_templates"), styles: { wordBreak: "break-all" }, sortable: true }, - { columnKey: "subject", value: T.translate("email_logs.subject") }, - { columnKey: "from_email", value: T.translate("email_logs.from_email") }, + { columnKey: "subject", header: T.translate("email_logs.subject") }, + { columnKey: "from_email", header: T.translate("email_logs.from_email") }, { columnKey: "to_email", - value: T.translate("email_logs.to_email"), + header: T.translate("email_logs.to_email"), styles: { wordBreak: "break-word" } }, { columnKey: "sent_date", - value: T.translate("email_logs.sent_date"), + header: T.translate("email_logs.sent_date"), sortable: true } ]; @@ -236,172 +239,168 @@ const SentEmailListPage = function ({ return (
-

- {" "} - {T.translate("email_logs.email_list")} ({totalEmails}) -

-
-
- -
-
-
-
-
- {T.translate("email_logs.email_list")} + + + + {totalEmails} {T.translate("emails.emails")} + + + + + + + + + + + -
-
- -
-
-
+ + + + {enabledFilters.includes("is_sent_filter") && ( -
- handleSetSentFilter(newValue)} - style={{ + + handleSetSentFilter(newValue)} + sx={(theme) => ({ width: "100%", height: 40, - color: "#337ab7", - fontSize: "10px" - }} - /> -
+ "& .MuiToggleButtonGroup-grouped": { + flex: 1, + "&.Mui-selected": { + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, + "&:hover": { + backgroundColor: theme.palette.primary.dark + } + } + } + })} + > + + {T.translate("email_logs.all")} + + + {T.translate("emails.sent")} + + + {T.translate("email_logs.not_sent")} + + +
)} {enabledFilters.includes("sent_date_filter") && ( - <> -
+ + handleChangeDateFilter(ev, false)} + label={T.translate("email_logs.placeholders.sent_date_from")} + format="YYYY-MM-DD HH:mm" + onChange={(value) => handleChangeDateFilter(value, false)} timezone="UTC" value={epochToMomentTimeZone( emailFilters.sent_date_filter[0], "UTC" )} - className="event-list-date-picker" + slotProps={{ + textField: { fullWidth: true, size: "small" } + }} /> -
-
+ + handleChangeDateFilter(ev, true)} + label={T.translate("email_logs.placeholders.sent_date_to")} + format="YYYY-MM-DD HH:mm" + onChange={(value) => handleChangeDateFilter(value, true)} timezone="UTC" value={epochToMomentTimeZone( emailFilters.sent_date_filter[1], "UTC" )} - className="event-list-date-picker" + slotProps={{ + textField: { fullWidth: true, size: "small" } + }} /> -
- + + )} {enabledFilters.includes("template_filter") && ( -
- + ({ + value: t.identifier, + label: t.identifier + })} /> -
+ )} -
-
-
- - -
-
+ + + + {emails.length === 0 &&
{T.translate("emails.no_emails")}
} {emails.length > 0 && ( - <> -
- - - - + )} ); diff --git a/src/styles/email-logs-page.less b/src/styles/email-logs-page.less deleted file mode 100644 index 3e3ad68c4..000000000 --- a/src/styles/email-logs-page.less +++ /dev/null @@ -1,7 +0,0 @@ -.email-logs-table-wrapper { - overflow: auto; -} - -.email-table-payload { - max-width: 300px; -}