Skip to content

fix: adjust props and function to format filename on upload input#786

Merged
smarcet merged 5 commits intomasterfrom
fix/image-upload-link-filename
Mar 4, 2026
Merged

fix: adjust props and function to format filename on upload input#786
smarcet merged 5 commits intomasterfrom
fix/image-upload-link-filename

Conversation

@tomrndom
Copy link

@tomrndom tomrndom commented Feb 10, 2026

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

image

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

Summary by CodeRabbit

  • New Features

    • Centralized media input handling for consistent image/file behavior across forms.
    • Added filename truncation and new trim-length constants to improve upload label display.
  • Bug Fixes

    • Cache-busting added to uploaded file URLs so updated uploads display reliably.
    • Adjusted filename trimming behavior for better start/end preservation.
  • Removed

    • Inventory item editor component, form template editor component, and the page-template edit routes/pages have been removed.
  • Tests

    • Added tests covering media input filename and path handling.

@tomrndom tomrndom requested a review from smarcet February 10, 2026 12:39
@coderabbitai
Copy link

coderabbitai bot commented Feb 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89cd9732-1c81-45bd-aa4d-e087591f841b

📥 Commits

Reviewing files that changed from the base of the PR and between 801039d and 516ee12.

📒 Files selected for processing (4)
  • src/components/forms/form-template-form.js
  • src/pages/sponsors-global/form-templates/form-template-item-list-page.js
  • src/utils/constants.js
  • src/utils/methods.js
💤 Files with no reviewable changes (1)
  • src/components/forms/form-template-form.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/utils/methods.js
  • src/utils/constants.js

📝 Walkthrough

Walkthrough

Centralizes media-value logic by adding getMediaInputValue in src/utils/methods.js, updates callers to use it, adds/trims several constants, removes InventoryItemForm, removes EditPageTemplatePage and its routes, and adds unit tests for the new media helper.

Changes

Cohort / File(s) Summary
Media utility & constants
src/utils/methods.js, src/utils/constants.js
Added getMediaInputValue(entity, fieldName = "images") (normalizes media items, computes filename basename, appends cache-busting ?t= timestamp). Added FILENAME_TRUNCATE_SIDE_PERCENT, TRIM_TEXT_LENGTH_20; changed TRIM_TEXT_LENGTH_40 from 50 → 40.
Pages & popups using media utility
src/pages/sponsors-global/form-templates/sponsor-inventory-popup.js, src/pages/sponsors-global/form-templates/form-template-item-list-page.js
Replaced in-file media value logic with import of getMediaInputValue; updated UploadInputV2 value usage; renamed import/usage from SponsorItemDialogSponsorInventoryDialog (same module).
Removed form components
src/components/forms/form-template-form.js, src/components/forms/inventory-item-form.js
Deleted FormTemplateForm and InventoryItemForm modules and their exported default components (removed UI, state, handlers, and exports).
Page template routing / pages removed
src/pages/sponsors-global/page-templates/edit-page-template-page.js, src/layouts/page-template-layout.js
Deleted EditPageTemplatePage; removed routes that rendered the edit page from page-template-layout, leaving list and NoMatch routes.
Tests
src/utils/__tests__/methods.test.js
Added tests for getMediaInputValue: guards for missing URL fields, basename stripping, and handling filenames without extensions.

Sequence Diagram(s)

(Skipped — changes are deletions/utility addition and do not introduce new multi-component sequential flows.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • romanetar

Poem

🐰 I hopped through files with curious eyes,
Nudged filenames tidy, added timestamp ties.
Old forms unburdened, routes gently cleared,
A little util stitched where logic veered. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the primary change: extracting and refactoring filename formatting logic into a shared utility function (getMediaInputValue) and adjusting how it's used in the upload input component.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/image-upload-link-filename

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

Copy link

@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: 1

🤖 Fix all issues with AI agents
In `@src/utils/methods.js`:
- Around line 536-571: In getMediaInputValue, guard against undefined fileUrl
and names without extensions: first derive fileUrl safely (fallback to empty
string or use img.filename||img.file_path||img.file_url || ""), check existence
before calling includes/split, and compute basename robustly (use lastSegment =
fileUrl.includes("/") ? ... only if fileUrl; otherwise use an empty string or
img.name). When splitting the filename, detect if there is no dot and treat the
whole name as nameWithoutExtension with ext as an empty string (or a default
like "bin") so filename construction doesn't produce ".name"; ensure final
filename assembly handles empty ext (e.g., omit ".") and that the timestamp
concat is applied to a string (use String(filename) + "?t=" + Date.now()).
Update references inside getMediaInputValue (variables fileUrl, filename, parts,
ext, nameWithoutExtension) accordingly.

import FormRepeater from "../form-repeater";
import InventoryItemMetaFieldForm from "./inventory-item-meta-field-form";
import { scrollToError, shallowEqual, hasErrors } from "../../utils/methods";
import {
Copy link

Choose a reason for hiding this comment

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

@tomrndom this is dead code please remove the file. No file in the codebase imports it. Inventory item editing is handled entirely by SponsorInventoryDialog

@@ -22,7 +22,12 @@ import TextEditorV3 from "openstack-uicore-foundation/lib/components/inputs/edit
import Swal from "sweetalert2";
Copy link

Choose a reason for hiding this comment

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

@tomrndom this is dead code
please remove it and remove it from here


and here

@fntechgit fntechgit deleted a comment from coderabbitai bot Mar 2, 2026
return mediaFiles.map((img) => {
const fileUrl = img.filename ?? img.file_path ?? img.file_url;

let filename = fileUrl.includes("/") ? fileUrl.split("/").pop() : fileUrl;
Copy link

@smarcet smarcet Mar 2, 2026

Choose a reason for hiding this comment

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

@tomrndom If img.filename, img.file_path, and img.file_url are all undefined/null, then fileUrl is undefined and fileUrl.includes("/") throws TypeError.
add a guard ( something like):

  const fileUrl = img.filename ?? img.file_path ?? img.file_url;
  if (!fileUrl) return { ...img, filename: "" };

add an unit test for this


let filename = fileUrl.includes("/") ? fileUrl.split("/").pop() : fileUrl;

const parts = filename.split(".");
Copy link

@smarcet smarcet Mar 2, 2026

Choose a reason for hiding this comment

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

@tomrndom
Files without extensions break
A filename like "README" (no dot) produces parts = ["README"], then ext = "README" and nameWithoutExtension = "". The result becomes ".README".

  • Handle the no-extension case
  • add proper unit test

name="image"
onUploadComplete={handleImageUploadComplete}
value={getMediaInputValue()}
value={getMediaInputValue(initialEntity)}
Copy link

@smarcet smarcet Mar 2, 2026

Choose a reason for hiding this comment

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

@tomrndom
Double truncation with UploadInputV2

UploadInputV2 already does its own truncation internally (https://github.com/OpenStackweb/openstack-uicore-foundation/blob/master/src/components/inputs/upload-input-v2/index.js):

  const FileNameMaxLen = 20;
  // ...
  if (path.length > FileNameMaxLen) {
      path = path.substring(0, FileNameMaxLen);
  }

So filenames get truncated twice , first by getMediaInputValue (middle-ellipsis) then again by UploadInputV2 (prefix). The component should be the only one responsible for display truncation.
So for a filename like "my-very-long-image-filename-for-sponsor.png":

  1. Original -> my-very-long-image-filename-for-sponsor.png
  2. After getMediaInputValue -> my-very-...sponsor.png?t=1709400000000
  3. UploadInputV2 splits ext -> ext = png?t=1709400000000
  4. UploadInputV2 truncates path -> my-very-...sponsor (already ≤20, no-op)
  5. Displayed -> my-very-...sponsor.png?t=1709400000000
    The cache-bust query string leaks into the visible filename displayed to the user. This was already happening with the old code (pre-PR), but the double truncation + ?t= makes the display worse

return {
...img,
public_url: img?.public_url || fileUrl,
filename: filename.concat("?t=", Date.now())
Copy link

@smarcet smarcet Mar 2, 2026

Choose a reason for hiding this comment

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

@tomrndom
t= cache-bust leaks into displayed filename

The ?t=Date.now() is appended to the filename property, but UploadInputV2 uses v.filename for:

  • The visible filename shown to the user (displayed as-is after the component's own split/truncation)
  • Extension extraction via filename.split('.').pop() . which yields "png?t=1709400000000" instead of "png"

The cache-bust should go on public_url (the actual image URL used for and ), not on filename. This way the browser fetches the fresh image, but the displayed filename stays
clean.

Suggested shape for the return value:

  return {
    ...img,
    public_url: (img?.public_url || fileUrl) + "?t=" + Date.now(),
    filename: fileUrl.includes("/") ? fileUrl.split("/").pop() : fileUrl
  };

This keeps filename clean for display and for the handleRemoveImage filter comparison (i.filename !== imageFile.name), and lets UploadInputV2 handle its own display truncation. The cache-bust on
public_url is where it belongs , on the URL the browser actually fetches.

please do add jest unit test for diplay name without ?t= and for handleRemoveImage logic

let nameWithoutExtension = parts.join(".");

if (nameWithoutExtension.length > maxLength) {
const startChars = Math.floor(maxLength * FILENAME_TRUNCATE_SIDE_PERCENT);
Copy link

Choose a reason for hiding this comment

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

@tomrndom please remove all this logic
let UploadInputV2 handle it

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@tomrndom tomrndom force-pushed the fix/image-upload-link-filename branch from ebbb962 to 3b3446a Compare March 3, 2026 14:17
Copy link

@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: 1

🤖 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/components/forms/inventory-item-form.js`:
- Line 323: getMediaInputValue(entity) is being recomputed every render (it
appends Date.now() in its output), causing UploadInputV2 value churn; memoize
the computed media value by entity.images so unrelated form edits via
handleChange don't trigger new values. Replace the direct call in the value prop
with a memoized value (useMemo or equivalent) computed from
getMediaInputValue(entity) with entity.images as the dependency, ensuring only
image add/remove operations change the memo and that UploadInputV2 continues to
receive the stable computed value.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebbb962 and 3b3446a.

📒 Files selected for processing (5)
  • src/components/forms/form-template-form.js
  • src/components/forms/inventory-item-form.js
  • src/pages/sponsors-global/form-templates/sponsor-inventory-popup.js
  • src/utils/constants.js
  • src/utils/methods.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/pages/sponsors-global/form-templates/sponsor-inventory-popup.js
  • src/components/forms/form-template-form.js
  • src/utils/constants.js
  • src/utils/methods.js

id="image"
onUploadComplete={handleImageUploadComplete}
value={getMediaInputValue()}
value={getMediaInputValue(entity)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== 1) Inspect getMediaInputValue output behavior ==="
rg -n -C4 'export const getMediaInputValue|filename: filename.concat|public_url' src/utils/methods.js

echo
echo "=== 2) Inspect handleRemoveImage payload expectations ==="
rg -n -C3 'const handleRemoveImage|imageFile\.name|imageFile\.filename' src/components/forms/inventory-item-form.js

echo
echo "=== 3) Find UploadInputV2 usages and onRemove handlers ==="
rg -n -C3 'UploadInputV2|onRemove=\{|handleRemoveImage' src/components

Repository: fntechgit/summit-admin

Length of output: 11940


🏁 Script executed:

# Check if entity state updates consistently create new images array reference
rg -n 'entity\.images.*=' src/components/forms/inventory-item-form.js | head -20

# Check if the useMemo dependency [entity?.images] is safe by verifying images immutability pattern
rg -n 'setEntity.*images' src/components/forms/inventory-item-form.js | head -10

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Read inventory-item-form.js to understand entity state management and handleChange
wc -l src/components/forms/inventory-item-form.js
head -120 src/components/forms/inventory-item-form.js | tail -50

# Check the actual state initialization and handleChange
sed -n '1,100p' src/components/forms/inventory-item-form.js | grep -A15 'useState\|handleChange\|const entity'

Repository: fntechgit/summit-admin

Length of output: 3049


Memoize media value to prevent timestamp churn on every render.

On line 323, getMediaInputValue(entity) is recomputed each render with Date.now() appended to filenames (line 569 of methods.js), making the value prop unstable during unrelated form edits. This causes unnecessary UploadInputV2 re-renders and potential list churn behavior.

The fix is to memoize by entity.images since only upload/remove operations mutate the images array—unrelated field changes via handleChange do not.

Proposed fix (memoize by images changes)
-import React, { useState, useEffect, useRef } from "react";
+import React, { useState, useEffect, useRef, useMemo } from "react";
@@
   const [entity, setEntity] = useState({ ...initialEntity });
   const [errors, setErrors] = useState(initialErrors);
+  const mediaInputValue = useMemo(
+    () => getMediaInputValue(entity),
+    [entity?.images]
+  );
@@
-            value={getMediaInputValue(entity)}
+            value={mediaInputValue}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
value={getMediaInputValue(entity)}
import React, { useState, useEffect, useRef, useMemo } from "react";
// ... other code ...
const [entity, setEntity] = useState({ ...initialEntity });
const [errors, setErrors] = useState(initialErrors);
const mediaInputValue = useMemo(
() => getMediaInputValue(entity),
[entity?.images]
);
// ... other code ...
value={mediaInputValue}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/forms/inventory-item-form.js` at line 323,
getMediaInputValue(entity) is being recomputed every render (it appends
Date.now() in its output), causing UploadInputV2 value churn; memoize the
computed media value by entity.images so unrelated form edits via handleChange
don't trigger new values. Replace the direct call in the value prop with a
memoized value (useMemo or equivalent) computed from getMediaInputValue(entity)
with entity.images as the dependency, ensuring only image add/remove operations
change the memo and that UploadInputV2 continues to receive the stable computed
value.

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

@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: 1

🤖 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/utils/methods.js`:
- Around line 544-545: The public_url construction and filename extraction can
produce malformed URLs and leak query/hash fragments; update the public_url
logic to append the cache-busting timestamp using the correct separator (use '&'
if img?.public_url or fileUrl already contains '?', otherwise use '?') when
building public_url (`public_url: ...`) and ensure filename (`filename: ...`) is
the sanitized basename by stripping any query string or fragment (split the
basename on /[?#]/ and take the first part) after extracting the final path
segment from img?.public_url or fileUrl; reference the existing symbols img,
public_url, fileUrl, and filename to locate and modify the code.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b3446a and 801039d.

📒 Files selected for processing (5)
  • src/components/forms/inventory-item-form.js
  • src/layouts/page-template-layout.js
  • src/pages/sponsors-global/page-templates/edit-page-template-page.js
  • src/utils/__tests__/methods.test.js
  • src/utils/methods.js
💤 Files with no reviewable changes (3)
  • src/pages/sponsors-global/page-templates/edit-page-template-page.js
  • src/layouts/page-template-layout.js
  • src/components/forms/inventory-item-form.js

tomrndom added 2 commits March 4, 2026 11:26
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@tomrndom tomrndom requested a review from smarcet March 4, 2026 14:50
Signed-off-by: sebastian marcet <smarcet@gmail.com>
Copy link

@smarcet smarcet left a comment

Choose a reason for hiding this comment

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

LGTM

@smarcet smarcet merged commit 4798a75 into master Mar 4, 2026
9 checks passed
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.

2 participants