diff --git a/src/components/forms/event-category-form.js b/src/components/forms/event-category-form.js
index 8bde330d3..6a123b04e 100644
--- a/src/components/forms/event-category-form.js
+++ b/src/components/forms/event-category-form.js
@@ -11,434 +11,478 @@
* limitations under the License.
* */
-import React from "react";
+import React, { useEffect, useState } from "react";
+import { useFormik, FormikProvider } from "formik";
+import * as yup from "yup";
import T from "i18n-react/dist/i18n-react";
-import "awesome-bootstrap-checkbox/awesome-bootstrap-checkbox.css";
-import Input from "openstack-uicore-foundation/lib/components/inputs/text-input"
-import Panel from "openstack-uicore-foundation/lib/components/sections/panel"
-import TagInput from "openstack-uicore-foundation/lib/components/inputs/tag-input"
-import UploadInput from "openstack-uicore-foundation/lib/components/inputs/upload-input"
-import AccessLevelsInput from "openstack-uicore-foundation/lib/components/inputs/access-levels-input"
-import SortableTable from "openstack-uicore-foundation/lib/components/table-sortable";
-import TextEditorV3 from "openstack-uicore-foundation/lib/components/inputs/editor-input-v3";
import {
- isEmpty,
- scrollToError,
- shallowEqual,
- hasErrors
-} from "../../utils/methods";
-import TrackDropdown from "../inputs/track-dropdown";
-
-class EventCategoryForm extends React.Component {
- constructor(props) {
- super(props);
+ Accordion,
+ AccordionDetails,
+ AccordionSummary,
+ Autocomplete,
+ Box,
+ Button,
+ Grid2,
+ InputLabel,
+ TextField,
+ Tooltip,
+ Typography
+} from "@mui/material";
+import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
+import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
+import { UploadInputV3 } from "openstack-uicore-foundation/lib/components";
+import { useSnackbarMessage } from "openstack-uicore-foundation/lib/components/mui/snackbar-notification";
+import MuiTableSortable from "openstack-uicore-foundation/lib/components/mui/sortable-table";
+import {
+ queryTags,
+ queryAccessLevels
+} from "openstack-uicore-foundation/lib/utils/query-actions";
+import MuiFormikTextField from "openstack-uicore-foundation/lib/components/mui/formik-inputs/textfield";
+import MuiFormikCheckbox from "openstack-uicore-foundation/lib/components/mui/formik-inputs/checkbox";
+import FormikTextEditor from "openstack-uicore-foundation/lib/components/mui/formik-inputs/texteditor";
+import MuiFormikAsyncAutocomplete from "openstack-uicore-foundation/lib/components/mui/formik-inputs/async-select";
+import MuiFormikColorField from "../mui/formik-inputs/mui-formik-color-field";
+import useScrollToError from "../../hooks/useScrollToError";
+import {
+ requiredStringValidation,
+ requiredHTMLValidation,
+ hexColorValidation
+} from "../../utils/yup";
- this.state = {
- entity: { ...props.entity },
- errors: props.errors,
- showSection: "main",
- subtrack: null
- };
+const validationSchema = yup.object().shape({
+ name: requiredStringValidation(),
+ code: requiredStringValidation(),
+ color: hexColorValidation().required(T.translate("validation.required")),
+ text_color: hexColorValidation().required(T.translate("validation.required")),
+ description: requiredHTMLValidation()
+});
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleUploadPic = this.handleUploadPic.bind(this);
- this.handleRemovePic = this.handleRemovePic.bind(this);
- this.handleEditSubCategory = this.handleEditSubCategory.bind(this);
- this.handleLinkSubCategory = this.handleLinkSubCategory.bind(this);
- this.handleUnlinkSubCategory = this.handleUnlinkSubCategory.bind(this);
- this.handleUpdateSubCategoryOrder =
- this.handleUpdateSubCategoryOrder.bind(this);
+const subtrackColumns = [
+ { columnKey: "id", header: T.translate("general.id") },
+ { columnKey: "name", header: T.translate("event_category_list.name") },
+ { columnKey: "code", header: T.translate("event_category_list.code") },
+ {
+ columnKey: "color",
+ header: T.translate("event_category_list.color"),
+ render: (row) => (
+
+ )
}
+];
- componentDidUpdate(prevProps) {
- const state = {};
- scrollToError(this.props.errors);
+const EventCategoryForm = ({
+ currentSummit,
+ entity,
+ errors,
+ history,
+ onSubmit,
+ onRemoveImage,
+ onLinkSubCategory,
+ onUnlinkSubCategory,
+ onUpdateSubCategoryOrder
+}) => {
+ const { errorMessage } = useSnackbarMessage();
+ const [isSaving, setIsSaving] = useState(false);
+ const [subtrackToLink, setSubtrackToLink] = useState(null);
+ const [iconUrl, setIconUrl] = useState(entity.icon_url);
+ const [scheduleSettingsExpanded, setScheduleSettingsExpanded] =
+ useState(false);
- if (!shallowEqual(prevProps.entity, this.props.entity)) {
- state.entity = { ...this.props.entity };
- state.errors = {};
- }
+ useEffect(() => {
+ setIconUrl(entity.icon_url);
+ }, [entity.icon_url]);
- if (!shallowEqual(prevProps.errors, this.props.errors)) {
- state.errors = { ...this.props.errors };
- }
+ const handleSubmit = (values) => {
+ setIsSaving(true);
+ const normalizedValues = {
+ ...values,
+ allowed_tags: values.allowed_tags.map((t) => ({
+ id: parseInt(t.value, 10),
+ tag: t.label
+ })),
+ allowed_access_levels: values.allowed_access_levels.map((al) => ({
+ id: parseInt(al.value, 10),
+ name: al.label
+ }))
+ };
+ Promise.resolve(onSubmit(normalizedValues)).finally(() =>
+ setIsSaving(false)
+ );
+ };
- if (!isEmpty(state)) {
- this.setState({ ...this.state, ...state });
- }
- }
+ const formik = useFormik({
+ initialValues: {
+ id: entity.id,
+ name: entity.name,
+ code: entity.code,
+ color: entity.color,
+ text_color: entity.text_color,
+ description: entity.description,
+ session_count: entity.session_count,
+ alternate_count: entity.alternate_count,
+ lightning_count: entity.lightning_count,
+ lightning_alternate_count: entity.lightning_alternate_count,
+ voting_visible: entity.voting_visible,
+ chair_visible: entity.chair_visible,
+ allowed_tags: (entity.allowed_tags || []).map((t) => ({
+ value: String(t.id),
+ label: t.tag
+ })),
+ allowed_access_levels: (entity.allowed_access_levels || []).map((al) => ({
+ value: String(al.id),
+ label: al.name
+ })),
+ proposed_schedule_transition_time:
+ entity.proposed_schedule_transition_time ?? ""
+ },
+ validationSchema,
+ enableReinitialize: true,
+ onSubmit: handleSubmit
+ });
- handleChange(ev) {
- const entity = { ...this.state.entity };
- const errors = { ...this.state.errors };
- let { value, id } = ev.target;
+ useScrollToError(formik);
- if (ev.target.type === "checkbox") {
- value = ev.target.checked;
+ useEffect(() => {
+ if (errors && Object.keys(errors).length > 0) {
+ formik.setErrors(errors);
+ formik.setTouched(
+ Object.keys(errors).reduce((acc, key) => ({ ...acc, [key]: true }), {})
+ );
}
+ }, [errors]);
- errors[id] = "";
- entity[id] = value;
- this.setState({ entity, errors });
- }
+ const handleIconUploadComplete = (response) => {
+ setIconUrl(response.url ?? response.file_url ?? response.path ?? null);
+ };
- handleSubmit(ev) {
- ev.preventDefault();
- this.props.onSubmit(this.state.entity);
- }
+ const handleIconUploadError = () => {
+ errorMessage(T.translate("edit_event_category.icon_upload_error"));
+ };
- handleRemovePic() {
- this.props.onRemoveImage(this.state.entity.id);
- }
+ const handleRemoveIcon = () => {
+ setIconUrl(null);
+ onRemoveImage(entity.id);
+ };
- handleUploadPic(file) {
- const formData = new FormData();
- formData.append("file", file);
- this.props.onUploadImage(this.state.entity, formData);
- }
-
- toggleSection(section, ev) {
- const { showSection } = this.state;
- const newShowSection = showSection === section ? "main" : section;
- ev.preventDefault();
-
- this.setState({ showSection: newShowSection });
- }
-
- handleEditSubCategory(id) {
- const { history, currentSummit } = this.props;
- history.push(`/app/summits/${currentSummit.id}/event-categories/${id}`);
- }
+ const availableSubTracks = currentSummit.tracks.filter(
+ (t) =>
+ !t.parent_id &&
+ !entity.subtracks?.map((st) => st.id).includes(t.id) &&
+ t.id !== entity.id
+ );
- handleLinkSubCategory() {
- const { entity, subtrack } = this.state;
- this.setState({ subtrack: null });
- this.props.onLinkSubCategory(entity.id, subtrack);
- }
-
- handleUnlinkSubCategory(subtrackId) {
- const { entity } = this.state;
- this.props.onUnlinkSubCategory(entity.id, subtrackId);
- }
+ const handleAddSubtrack = () => {
+ if (!subtrackToLink) return;
+ onLinkSubCategory(entity.id, subtrackToLink.id);
+ setSubtrackToLink(null);
+ };
- handleUpdateSubCategoryOrder(subtracks, subtrackId, newOrder) {
- const { entity } = this.state;
- this.props.onUpdateSubCategoryOrder(entity.id, subtrackId, newOrder);
- }
+ const handleEditSubtrack = (subtrack) =>
+ history.push(
+ `/app/summits/${currentSummit.id}/event-categories/${subtrack.id}`
+ );
- render() {
- const { entity, errors, showSection } = this.state;
- const { currentSummit } = this.props;
+ const handleUnlinkSubtrack = (subtrackId) =>
+ onUnlinkSubCategory(entity.id, subtrackId);
- const availableSubTracks = currentSummit.tracks.filter(
- (t) =>
- !t.parent_id &&
- !entity.subtracks.map((t) => t.id).includes(t.id) &&
- t.id !== entity.id
- );
+ const handleReorderSubtracks = (_newOrder, subtrackId, newSubtrackOrder) =>
+ onUpdateSubCategoryOrder(entity.id, subtrackId, newSubtrackOrder);
- const table_options = {
- actions: {
- edit: { onClick: this.handleEditSubCategory },
- delete: { onClick: this.handleUnlinkSubCategory }
- }
- };
+ return (
+
+
+
+
+
+ {T.translate("edit_event_category.name")} *
+
+
+
+
+
+ {T.translate("edit_event_category.code")} *
+
+
+
+
+
+ {T.translate("edit_event_category.color")} *
+
+
+
+
+
+ {T.translate("edit_event_category.text_color")} *
+
+
+
+
- const columns = [
- { columnKey: "id", value: T.translate("general.id") },
- { columnKey: "name", value: T.translate("event_category_list.name") },
- { columnKey: "code", value: T.translate("event_category_list.code") },
- { columnKey: "color", value: T.translate("event_category_list.color") }
- ];
+
+
+
+ {T.translate("edit_event_category.description")} *
+
+
+
+
- return (
-
)}
-
-
- );
- }
-}
+
+
+
+
+
+ );
+};
export default EventCategoryForm;
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 90d959ede..6257bc345 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -1328,6 +1328,7 @@
},
"edit_event_category": {
"event_category": "Activity Category",
+ "add_new": "Add new",
"name": "Name",
"code": "Code",
"color": "Color",
@@ -1351,12 +1352,19 @@
"category_saved": "Activity Category saved successfully.",
"category_created": "Activity Category created successfully.",
"delete_warning": "Are you sure you want to delete extra question ",
+ "unlink_subtrack_warning": "Are you sure you want to unlink subtrack ",
+ "icon_upload_error": "There was an error uploading the image.",
"proposed_schedule_settings": {
"title": "Proposed Schedule Settings",
"transition_time": "Transition Time (in minutes)",
"placeholders": {
"transition_time": "Transition Time (in minutes)"
}
+ },
+ "placeholders": {
+ "select_tags": "Select tags...",
+ "select_access_levels": "Select access levels...",
+ "select_subtrack": "Select subtrack..."
}
},
"edit_event_category_question": {
diff --git a/src/pages/events/edit-event-category-page.js b/src/pages/events/edit-event-category-page.js
index 0ee45d679..68a17fc74 100644
--- a/src/pages/events/edit-event-category-page.js
+++ b/src/pages/events/edit-event-category-page.js
@@ -14,37 +14,51 @@
import React from "react";
import { connect } from "react-redux";
import T from "i18n-react/dist/i18n-react";
+import { Button } from "@mui/material";
import EventCategoryForm from "../../components/forms/event-category-form";
import { getSummitById } from "../../actions/summit-actions";
import {
getEventCategory,
resetEventCategoryForm,
saveEventCategory,
- uploadImage,
removeImage,
linkSubCategory,
unlinkSubCategory,
updateSubCategoryOrder
} from "../../actions/event-category-actions";
-import "../../styles/edit-event-category-page.less";
-import AddNewButton from "../../components/buttons/add-new-button";
-function EditEventCategoryPage({
+const EditEventCategoryPage = ({
currentSummit,
entity,
errors,
history,
- ...rest
-}) {
+ saveEventCategory,
+ removeImage,
+ linkSubCategory,
+ unlinkSubCategory,
+ updateSubCategoryOrder
+}) => {
const title = entity.id
? T.translate("general.edit")
: T.translate("general.add");
+ const handleAddNew = () => {
+ if (!entity?.id) return null;
+
+ history.push(`/app/summits/${currentSummit.id}/event-categories/new`);
+ };
+
return (
{title} {T.translate("edit_event_category.event_category")}
-
+
{currentSummit && (
@@ -53,17 +67,16 @@ function EditEventCategoryPage({
currentSummit={currentSummit}
entity={entity}
errors={errors}
- onSubmit={rest.saveEventCategory}
- onUploadImage={rest.uploadImage}
- onRemoveImage={rest.removeImage}
- onLinkSubCategory={rest.linkSubCategory}
- onUnlinkSubCategory={rest.unlinkSubCategory}
- onUpdateSubCategoryOrder={rest.updateSubCategoryOrder}
+ onSubmit={saveEventCategory}
+ onRemoveImage={removeImage}
+ onLinkSubCategory={linkSubCategory}
+ onUnlinkSubCategory={unlinkSubCategory}
+ onUpdateSubCategoryOrder={updateSubCategoryOrder}
/>
)}
);
-}
+};
const mapStateToProps = ({
currentSummitState,
@@ -78,7 +91,6 @@ export default connect(mapStateToProps, {
getEventCategory,
resetEventCategoryForm,
saveEventCategory,
- uploadImage,
removeImage,
linkSubCategory,
unlinkSubCategory,
diff --git a/src/pages/events/event-category-list-page.js b/src/pages/events/event-category-list-page.js
index 17b183787..84fa0733c 100644
--- a/src/pages/events/event-category-list-page.js
+++ b/src/pages/events/event-category-list-page.js
@@ -11,12 +11,13 @@
* limitations under the License.
* */
-import React from "react";
+import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import T from "i18n-react/dist/i18n-react";
-import Swal from "sweetalert2";
-import SortableTable from "openstack-uicore-foundation/lib/components/table-sortable";
-import SummitDropdown from "../../components/summit-dropdown";
+import { Box, Button, Grid2 } from "@mui/material";
+import AddIcon from "@mui/icons-material/Add";
+import MuiTableSortable from "openstack-uicore-foundation/lib/components/mui/sortable-table";
+import SummitsDropdown from "openstack-uicore-foundation/lib/components/mui/summits-dropdown";
import { getSummitById } from "../../actions/summit-actions";
import {
getEventCategories,
@@ -25,119 +26,133 @@ import {
updateEventCategoryOrder
} from "../../actions/event-category-actions";
-class EventCategoryListPage extends React.Component {
- constructor(props) {
- super(props);
+const columns = [
+ { columnKey: "id", header: T.translate("general.id") },
+ { columnKey: "name", header: T.translate("event_category_list.name") },
+ { columnKey: "code", header: T.translate("event_category_list.code") },
+ {
+ columnKey: "color",
+ header: T.translate("event_category_list.color"),
+ render: (row) => (
+
+ )
+ }
+];
- this.handleEdit = this.handleEdit.bind(this);
- this.handleCopyCategories = this.handleCopyCategories.bind(this);
- this.handleNew = this.handleNew.bind(this);
- this.handleDelete = this.handleDelete.bind(this);
+const EventCategoryListPage = ({
+ currentSummit,
+ eventCategories,
+ history,
+ getEventCategories,
+ deleteEventCategory,
+ copyEventCategories,
+ updateEventCategoryOrder
+}) => {
+ const [summitToCopy, setSummitToCopy] = useState(null);
- this.state = {};
- }
+ useEffect(() => {
+ if (currentSummit) getEventCategories();
+ }, [currentSummit?.id]);
- componentDidMount() {
- const { currentSummit } = this.props;
- if (currentSummit) {
- this.props.getEventCategories();
- }
- }
+ if (!currentSummit?.id) return null;
- handleEdit(categoryId) {
- const { currentSummit, history } = this.props;
+ const handleEdit = (category) =>
history.push(
- `/app/summits/${currentSummit.id}/event-categories/${categoryId}`
+ `/app/summits/${currentSummit.id}/event-categories/${category.id}`
);
- }
- handleCopyCategories(fromSummitId) {
- this.props.copyEventCategories(fromSummitId);
- }
-
- handleNew(ev) {
- const { currentSummit, history } = this.props;
+ const handleNew = () =>
history.push(`/app/summits/${currentSummit.id}/event-categories/new`);
- }
- handleDelete(categoryId) {
- const { deleteEventCategory, eventCategories } = this.props;
- const category = eventCategories.find((c) => c.id === categoryId);
-
- Swal.fire({
- title: T.translate("general.are_you_sure"),
- text: `${T.translate("event_category_list.delete_warning")} ${
- category.name
- }`,
- type: "warning",
- showCancelButton: true,
- confirmButtonColor: "#DD6B55",
- confirmButtonText: T.translate("general.yes_delete")
- }).then((result) => {
- if (result.value) {
- deleteEventCategory(categoryId);
- }
- });
- }
-
- render() {
- const { currentSummit, eventCategories } = this.props;
-
- const columns = [
- { columnKey: "id", value: T.translate("general.id") },
- { columnKey: "name", value: T.translate("event_category_list.name") },
- { columnKey: "code", value: T.translate("event_category_list.code") },
- { columnKey: "color", value: T.translate("event_category_list.color") }
- ];
-
- const table_options = {
- actions: {
- edit: { onClick: this.handleEdit },
- delete: { onClick: this.handleDelete }
- }
- };
-
- if (!currentSummit.id) return null;
-
- return (
-
-
{T.translate("event_category_list.event_category_list")}
-
-
-
-
-
+ const handleCopyCategories = () => copyEventCategories(summitToCopy);
+
+ return (
+
+
{T.translate("event_category_list.event_category_list")}
+
+
+ }
+ onClick={handleNew}
+ >
+ {T.translate("event_category_list.add_category")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {eventCategories.length === 0 && (
+
+ {T.translate("event_category_list.no_items")}
-
- {eventCategories.length === 0 && (
-
- {T.translate("event_category_list.no_items")}
-
- )}
-
- {eventCategories.length > 0 && (
-
-
-
- )}
-
- );
- }
-}
+ )}
+
+ {eventCategories.length > 0 && (
+
category.name}
+ onEdit={handleEdit}
+ onDelete={deleteEventCategory}
+ deleteDialogBody={(name) =>
+ `${T.translate("event_category_list.delete_warning")}${name}`
+ }
+ confirmButtonColor="error"
+ onReorder={updateEventCategoryOrder}
+ />
+ )}
+
+ );
+};
const mapStateToProps = ({
currentSummitState,
diff --git a/src/reducers/events/event-category-list-reducer.js b/src/reducers/events/event-category-list-reducer.js
index f7349b0fe..b338e2096 100644
--- a/src/reducers/events/event-category-list-reducer.js
+++ b/src/reducers/events/event-category-list-reducer.js
@@ -9,7 +9,9 @@
* 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 { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions";
import {
RECEIVE_EVENT_CATEGORIES,
@@ -20,7 +22,6 @@ import {
} from "../../actions/event-category-actions";
import { SET_CURRENT_SUMMIT } from "../../actions/summit-actions";
-import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions";
const DEFAULT_STATE = {
eventCategories: []
@@ -39,49 +40,45 @@ const eventCategoryListReducer = (state = DEFAULT_STATE, action) => {
case RECEIVE_EVENT_CATEGORIES: {
return {
...state,
- eventCategories: payload.response.data.map((e) => {
- return {
- id: e.id,
- name: e.name,
- code: e.code,
- color: `
`,
- order: e.order
- };
- })
+ eventCategories: payload.response.data.map((e) => ({
+ id: e.id,
+ name: e.name,
+ code: e.code,
+ color: e.color,
+ order: e.order
+ }))
};
}
case EVENT_CATEGORY_ORDER_UPDATED: {
- const tracks = payload.map((t) => {
- return {
- id: t.id,
- name: t.name,
- code: t.code,
- color: t.color,
- order: parseInt(t.order)
- };
- });
+ const tracks = payload.map((t) => ({
+ id: t.id,
+ name: t.name,
+ code: t.code,
+ color: t.color,
+ order: parseInt(t.order)
+ }));
return { ...state, eventCategories: tracks };
}
case EVENT_CATEGORIES_SEEDED: {
- let eventCategoriesAdded = payload.response.data.map((e) => {
- return {
- id: e.id,
- name: e.name
- };
- });
+ const eventCategoriesAdded = payload.response.data.map((e) => ({
+ id: e.id,
+ name: e.name,
+ code: e.code,
+ color: e.color,
+ order: e.order
+ }));
if (eventCategoriesAdded.length > 0) {
return {
...state,
eventCategories: [...state.eventCategories, ...eventCategoriesAdded]
};
- } else {
- return state;
}
+ return state;
}
case EVENT_CATEGORY_DELETED: {
- let { categoryId } = payload;
+ const { categoryId } = payload;
return {
...state,
eventCategories: state.eventCategories.filter(
diff --git a/src/styles/edit-event-category-page.less b/src/styles/edit-event-category-page.less
deleted file mode 100644
index 763d5abde..000000000
--- a/src/styles/edit-event-category-page.less
+++ /dev/null
@@ -1,21 +0,0 @@
-.subtrackddl {
- width: 200px;
- .Select-control {
- border-radius: 4px 0 0 4px;
- height: 100%;
- .Select-input {
- height: 32px;
- }
- }
-}
-
-.subtrackddlgrp {
- & > div {
- float: left;
- }
-
- .add-button {
- height: 38px;
- border-color: #afafaf;
- }
-}