Skip to content

fix: move media file type list page and edit into MUI components#897

Open
tomrndom wants to merge 2 commits intomasterfrom
fix/migrate-media-file-type-list
Open

fix: move media file type list page and edit into MUI components#897
tomrndom wants to merge 2 commits intomasterfrom
fix/migrate-media-file-type-list

Conversation

@tomrndom
Copy link
Copy Markdown

@tomrndom tomrndom commented Apr 24, 2026

ref: https://app.clickup.com/t/86b9jqczj

image image

Signed-off-by: Tomás Castillo tcastilloboireau@gmail.com

Summary by CodeRabbit

  • New Features

    • In-page dialog for creating/editing media file types with form validation
    • Updated delete confirmation message to include the item name
  • Refactor

    • Migrated list UI to Material-UI with improved search, pagination, and per-page handling
    • Standardized save flow to always use snackbar notifications and consistent loading cleanup
  • Tests

    • Added comprehensive tests for the list page and dialog component

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

📝 Walkthrough

Walkthrough

Refactors media file type management: class -> hooks-based list page with in-page dialog, unified snackbar-based error/success handlers in actions, loader cleanup moved to .finally, reducer pagination/total state extended, and new unit tests and i18n delete message updated.

Changes

Cohort / File(s) Summary
Actions
src/actions/media-file-type-actions.js
Switched authErrorHandler -> snackbarErrorHandler across requests; changed loader cleanup to .finally(...); removed noAlert from saveMediaFileType and always dispatch snackbar success on save.
List Page
src/pages/media_file_types/media-file-type-list-page.js
Converted class component to functional hooks component; replaced bootstrap layout with MUI (Grid2, Button, MuiTable, SearchInput); in-page MediaFileTypeDialog for create/edit; wired getMediaFileType, saveMediaFileType, resetMediaFileTypeForm; updated delete flow to use table delete confirmation.
Dialog Component
src/pages/media_file_types/components/media-file-type-dialog.js
Added default-exported Formik-based MediaFileTypeDialog with enableReinitialize, validation for name, conversion of allowed_extensions between array and CSV string, and proper close/reset handling.
Reducers
src/reducers/media_file_types/media-file-type-list-reducer.js
Initialized term to empty string, added totalMediaFileTypes, persisted perPage from request, and stored response total on receive; minor refactor to use const/arrow-object syntax.
Tests
src/pages/media_file_types/__tests__/media-file-type-list-page.test.js, src/pages/media_file_types/components/__tests__/media-file-type-dialog.test.js
Added comprehensive RTL tests for list page and dialog covering mount dispatch, empty/non-empty rendering, add/edit/delete flows, search behavior, form validation, and save callbacks.
Localization
src/i18n/en.json
Updated media_file_type.delete_warning to include {name} placeholder and trailing question mark.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant List as MediaFileTypeListPage
    participant Dialog as MediaFileTypeDialog
    participant Actions as ActionCreators
    participant Reducer as Reducer
    participant Snackbar as SnackbarHandler

    User->>List: Click "Edit" / "Add"
    List->>Actions: dispatch(getMediaFileType(id)) / open dialog
    Actions->>Reducer: REQUEST_MEDIA_FILE_TYPE
    Reducer-->>List: loading state
    Actions->>Reducer: RECEIVE_MEDIA_FILE_TYPE
    Reducer-->>List: entity loaded
    List->>Dialog: open with entity

    User->>Dialog: Submit form
    Dialog->>List: onSave(formData)
    List->>Actions: dispatch(saveMediaFileType(entity))
    Actions->>Reducer: REQUEST_SAVE_MEDIA_FILE_TYPE
    Reducer-->>List: loading
    Actions->>Actions: perform API request
    Actions->>Snackbar: dispatch(snackbarSuccessHandler / snackbarErrorHandler)
    Snackbar-->>User: show notification
    Actions->>Reducer: RECEIVE_SAVE_MEDIA_FILE_TYPE
    Reducer-->>List: updated state
    List->>Actions: dispatch(getMediaFileTypes())
    List-->>User: refreshed list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • smarcet
  • martinquiroga-exo
  • santipalenque

Poem

🐰 A dialog blooms where routes once led,
Formik hops through fields of text and thread,
Snackbars chirp secrets—success, or whoops!—in view,
Hooks bound the list and fetch a fresher crew,
Hooray, the refactor's stitched and true! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective of the changeset—migrating the media file type list page and edit functionality from legacy components to MUI (Material UI) components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/migrate-media-file-type-list

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/actions/media-file-type-actions.js (1)

211-212: ⚠️ Potential issue | 🟠 Major

Normalize and filter allowed_extensions before sending.

An empty field currently becomes [""], and inputs like PDF, DOC keep the leading space on DOC. That can persist invalid extension values or trip backend validation.

Suggested fix
-  normalizedEntity.allowed_extensions = entity.allowed_extensions.split(",");
+  normalizedEntity.allowed_extensions = (entity.allowed_extensions ?? "")
+    .split(",")
+    .map((ext) => ext.trim())
+    .filter(Boolean);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/media-file-type-actions.js` around lines 211 - 212, Update the
assignment that builds normalizedEntity.allowed_extensions so it trims
whitespace and removes empty entries: when entity.allowed_extensions exists,
split on "," then map each part with trim() and filter out empty strings (e.g.,
filter(Boolean)); if entity.allowed_extensions is falsy, set
normalizedEntity.allowed_extensions to an empty array. Locate the
normalizedEntity.allowed_extensions = entity.allowed_extensions.split(","); line
and replace it with this normalized-and-filtered logic to avoid values like ""
or " DOC".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/media_file_types/media-file-type-list-page.js`:
- Around line 74-99: The async UI handlers (handleRowEdit, handleSave,
handleDelete and similar) currently start promise chains without returning them
or adding terminal catches; update each to return the promise chain and attach a
.catch that routes errors to the existing snackbarErrorHandler (or otherwise
handles the error) so rejections are not unhandled; specifically: return
getMediaFileType(...) from handleRowEdit, return the reset/setOpen flows where
applicable, make handleSave return the
saveMediaFileType(...).then(getMediaFileTypes...).then(...).catch(snackbarErrorHandler)
so Formik can await it, and return
deleteMediaFileType(...).then(getMediaFileTypes...).catch(snackbarErrorHandler)
from handleDelete; keep using resetMediaFileTypeForm and setOpen as before but
ensure all chains are returned and terminally caught.
- Around line 186-187: The delete dialog is using the old i18n key
"media_file_type.remove_warning" so the translation is missing; update the
T.translate call inside the deleteDialogBody prop (currently
T.translate("media_file_type.remove_warning", { name })) to use the new key
"media_file_type.delete_warning" while keeping the { name } interpolation intact
so the localized message displays correctly.

---

Outside diff comments:
In `@src/actions/media-file-type-actions.js`:
- Around line 211-212: Update the assignment that builds
normalizedEntity.allowed_extensions so it trims whitespace and removes empty
entries: when entity.allowed_extensions exists, split on "," then map each part
with trim() and filter out empty strings (e.g., filter(Boolean)); if
entity.allowed_extensions is falsy, set normalizedEntity.allowed_extensions to
an empty array. Locate the normalizedEntity.allowed_extensions =
entity.allowed_extensions.split(","); line and replace it with this
normalized-and-filtered logic to avoid values like "" or " DOC".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0974360a-d264-4b65-b15d-cf377a433a8d

📥 Commits

Reviewing files that changed from the base of the PR and between e601202 and 3ce4a71.

📒 Files selected for processing (7)
  • src/actions/media-file-type-actions.js
  • src/i18n/en.json
  • src/pages/media_file_types/__tests__/media-file-type-list-page.test.js
  • src/pages/media_file_types/components/__tests__/media-file-type-dialog.test.js
  • src/pages/media_file_types/components/media-file-type-dialog.js
  • src/pages/media_file_types/media-file-type-list-page.js
  • src/reducers/media_file_types/media-file-type-list-reducer.js

Comment on lines +74 to +99
const handleRowEdit = (row) => {
getMediaFileType(row.id).then(() => setOpen(true));
};

const handleNew = () => {
resetMediaFileTypeForm();
setOpen(true);
};

const handleClose = () => {
resetMediaFileTypeForm();
setOpen(false);
};

const handleSave = (entity) => {
saveMediaFileType(entity)
.then(() =>
getMediaFileTypes(term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir)
)
.then(() => setOpen(false));
};

const handleDelete = (mediaFileTypeId) => {
deleteMediaFileType(mediaFileTypeId).then(() =>
getMediaFileTypes(term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir)
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return/catch these async UI flows.

These handlers start request chains without a terminal catch, so failed fetch/save/delete calls can surface as unhandled promise rejections. handleSave also needs to return the promise so Formik can await the submit lifecycle.

Suggested fix
-  const handleRowEdit = (row) => {
-    getMediaFileType(row.id).then(() => setOpen(true));
-  };
+  const handleRowEdit = async (row) => {
+    try {
+      await getMediaFileType(row.id);
+      setOpen(true);
+    } catch {
+      // snackbarErrorHandler already surfaced the error
+    }
+  };
@@
-  const handleSave = (entity) => {
-    saveMediaFileType(entity)
-      .then(() =>
-        getMediaFileTypes(term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir)
-      )
-      .then(() => setOpen(false));
-  };
+  const handleSave = async (entity) => {
+    try {
+      await saveMediaFileType(entity);
+      await getMediaFileTypes(
+        term,
+        DEFAULT_CURRENT_PAGE,
+        perPage,
+        order,
+        orderDir
+      );
+      setOpen(false);
+    } catch {
+      // snackbarErrorHandler already surfaced the error
+    }
+  };
@@
-  const handleDelete = (mediaFileTypeId) => {
-    deleteMediaFileType(mediaFileTypeId).then(() =>
-      getMediaFileTypes(term, DEFAULT_CURRENT_PAGE, perPage, order, orderDir)
-    );
-  };
+  const handleDelete = async (mediaFileTypeId) => {
+    try {
+      await deleteMediaFileType(mediaFileTypeId);
+      await getMediaFileTypes(
+        term,
+        DEFAULT_CURRENT_PAGE,
+        perPage,
+        order,
+        orderDir
+      );
+    } catch {
+      // snackbarErrorHandler already surfaced the error
+    }
+  };

Based on learnings, snackbarErrorHandler passed to request helpers already handles errors internally, and adding a catch after such requests is intentional to prevent unhandled promise rejections.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/media_file_types/media-file-type-list-page.js` around lines 74 -
99, The async UI handlers (handleRowEdit, handleSave, handleDelete and similar)
currently start promise chains without returning them or adding terminal
catches; update each to return the promise chain and attach a .catch that routes
errors to the existing snackbarErrorHandler (or otherwise handles the error) so
rejections are not unhandled; specifically: return getMediaFileType(...) from
handleRowEdit, return the reset/setOpen flows where applicable, make handleSave
return the
saveMediaFileType(...).then(getMediaFileTypes...).then(...).catch(snackbarErrorHandler)
so Formik can await it, and return
deleteMediaFileType(...).then(getMediaFileTypes...).catch(snackbarErrorHandler)
from handleDelete; keep using resetMediaFileTypeForm and setOpen as before but
ensure all chains are returned and terminally caught.

Comment thread src/pages/media_file_types/media-file-type-list-page.js Outdated
…fix typo

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/pages/media_file_types/media-file-type-list-page.js (1)

116-120: Consider defensive check for allowed_extensions.

The render assumes allowed_extensions is always an array. If the API ever returns null or undefined for this field, calling .join() will throw.

🛡️ Optional defensive fix
     {
       columnKey: "allowed_extensions",
       header: T.translate("media_file_type.allowed_extensions"),
-      render: (row) => row.allowed_extensions.join(", ")
+      render: (row) => (row.allowed_extensions || []).join(", ")
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/media_file_types/media-file-type-list-page.js` around lines 116 -
120, The render function for the column with columnKey "allowed_extensions"
assumes row.allowed_extensions is always an array, which will throw if it's
null/undefined; update the render in media-file-type-list-page (the render
callback) to defensively handle non-array values by checking
Array.isArray(row.allowed_extensions) (or using a fallback like an empty array
or a placeholder string) before calling join, so it safely returns a string when
allowed_extensions is missing or not an array.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/pages/media_file_types/media-file-type-list-page.js`:
- Around line 116-120: The render function for the column with columnKey
"allowed_extensions" assumes row.allowed_extensions is always an array, which
will throw if it's null/undefined; update the render in
media-file-type-list-page (the render callback) to defensively handle non-array
values by checking Array.isArray(row.allowed_extensions) (or using a fallback
like an empty array or a placeholder string) before calling join, so it safely
returns a string when allowed_extensions is missing or not an array.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ab7c805-66c9-4bf9-a359-67053eecc659

📥 Commits

Reviewing files that changed from the base of the PR and between 3ce4a71 and 2d3dbd9.

📒 Files selected for processing (2)
  • src/pages/media_file_types/components/media-file-type-dialog.js
  • src/pages/media_file_types/media-file-type-list-page.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant