Skip to content

feat: create segment from CSV drawer - #8283

Open
Zaimwa9 wants to merge 1 commit into
mainfrom
feat/create-segment-from-csv
Open

feat: create segment from CSV drawer#8283
Zaimwa9 wants to merge 1 commit into
mainfrom
feat/create-segment-from-csv

Conversation

@Zaimwa9

@Zaimwa9 Zaimwa9 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor
  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Adds the "New Segment — from a CSV list" drawer behind the segment sources modal, and makes the sources modal config-driven instead of hardcoded.

Flag-driven sources. The modal now reads its cards from the JSON value of the create_segment_with_external_sources flag:

[
  { "active": true, "name": "csv", "visible": true },
  { "active": false, "name": "amplitude", "visible": true },
  { "active": false, "name": "mixpanel", "visible": true },
  { "active": false, "name": "adobe_journey_manager", "visible": true }
]

visible: false hides a card, active: false renders it as a Beta fake door, active: true makes it real (currently only CSV has an implementation — an active source without one safely stays a fake door). Array order controls display order. Copy and icons stay in the frontend. Flag enabled with an empty or invalid value falls back to the manual drawer.

CSV drawer.

  • Client-side CSV parsing (quoted fields, CRLF, BOM)
  • Optional header row
  • Identifier column selection with a highlighted 5-row preview
  • Ignored-row warnings (empty/duplicate) and a blocking error when a column yields no identifiers
  • Submission is wired in the follow-up PR

How did you test this code?

Unit tests for the CSV parser/extractor (common/utils/__tests__/csv.test.ts). Manually:

  1. Set the JSON value above on the create_segment_with_external_sources flag and enable it.
  2. Segments → Create Segment: modal shows the cards; non-active sources show the Beta chip and the request-access flow.
  3. Click "From a CSV list" (active): the drawer opens; upload a CSV with/without headers, switch the identifier column, and check the preview, warnings, and the empty-column blocking error.
  4. Disable the flag or set the value to []: the button opens the manual drawer directly.

Screens

Upload screen
image

Uploaded but unselected
image

Uploaded selected
image
image

Error states
image
image
image

@Zaimwa9 Zaimwa9 added the front-end Issue related to the React Front End Dashboard label Aug 13, 2026
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
flagsmith-frontend-preview Ready Ready Preview Aug 14, 2026 2:49pm
flagsmith-frontend-staging Ready Ready Preview Aug 14, 2026 2:49pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Aug 14, 2026 2:49pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a26107e1-44db-47b6-8cdd-04882ae171fe

📥 Commits

Reviewing files that changed from the base of the PR and between 05b2fcf and 033c67d.

📒 Files selected for processing (1)
  • frontend/web/components/modals/CreateSegmentSourcesModal/CreateSegmentSourcesModal.tsx

📝 Walkthrough

Walkthrough

Adds CSV parsing and identifier utilities with tests. Adds a drag-and-drop CSV upload component. Adds a segment creation form with header handling, identifier validation, row preview, metadata fields, and guarded submission. Derives segment sources from Flagsmith configuration. Routes active CSV and manual source selections through the segment page. Updates shared UI styling and modal title types.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to 033c6

The PR adds a config-driven segment-source modal and CSV preview flow. Malformed source configuration can render an invalid card, and rapid file replacements can leave the preview showing an older selection; these are bounded UI correctness risks, so the change is mergeable with explicit owner follow-up.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Zaimwa9

Zaimwa9 commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

@themis-blindfold review

Comment on lines +17 to +18
for (let i = 0; i < text.length; i++) {
const char = text[i]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nit · ⚡ Quick win

parseCsvText doesn't strip the UTF-8 BOM (U+FEFF)

Observed: a file starting with a BOM embeds the character in the first cell's value. Downstream .trim() in toParsedCsv and extractIdentifiers happens to strip it (ECMAScript's trim removes U+FEFF), so column names and identifiers are unaffected in practice. But the parser's own contract should handle it rather than relying on callers — the PR description lists BOM as a supported feature.

Suggested change
for (let i = 0; i < text.length; i++) {
const char = text[i]
let pos = 0
if (text.charCodeAt(0) === 0xfeff) pos = 1
for (let i = pos; i < text.length; i++) {

</>
)}
<div className='text-right py-3'>
<Button data-test='create-segment' disabled={!canSubmit} type='submit'>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nit · ⚡ Quick win

"Create Segment" button is enabled but does nothing

Once the form is filled out, the button is clickable but save is a no-op (e.preventDefault() only). Users testing behind the flag get zero feedback on click. Worth keeping it disabled (or labelled differently) until the follow-up PR wires submission.

@themis-blindfold

Copy link
Copy Markdown
Contributor

⚖️ Themis review: 🧹 Ship it, nits inside

TL;DR: Adds a flag-driven "Create Segment from CSV" drawer behind create_segment_with_external_sources. The CSV parser is solid (handles quoted fields, embedded commas, CRLF), the sources modal is now config-driven via a JSON flag value, and the e2e helpers are updated to match. Submission is intentionally deferred to a follow-up PR. Two small nits below.

Area Score
🎯 Correctness 4/5
🧪 Test coverage 4/5
📐 Code quality 4/5
🚀 Product impact 3/5

🧹 Nits

  • frontend/common/utils/csv.ts line 13 — parseCsvText doesn't strip UTF-8 BOM; see inline.
  • frontend/web/components/modals/CreateSegmentFromCsv/CreateSegmentFromCsv.tsx line 229 — "Create Segment" button is a no-op until the follow-up PR; see inline.
📝 Walkthrough
  • CSV utilities (common/utils/csv.ts) — new parseCsvText, toParsedCsv, and extractIdentifiers functions with unit tests covering quoting, CRLF, blank-line filtering, dedup, and empty-row counting.
  • CsvUpload component — reusable drag-and-drop file picker restricted to .csv, reads the file as text via FileReader and hands the content to a callback.
  • CreateSegmentFromCsv drawer — form with name, description, environment selector, CSV upload, header toggle, identifier column picker, 5-row preview table, and ignored-row warnings. Submit is wired to a TODO pending the cohorts API endpoint.
  • CreateSegmentSourcesModal refactor — sources are now read from the create_segment_with_external_sources flag's JSON value instead of a hardcoded array; active sources with a handler (currently only CSV) navigate directly, others stay as fake-door beta cards.
  • SegmentsPagenewSegment now checks getSegmentSources().length before showing the sources modal, and passes onCsv to open the new CSV drawer.
  • E2e helpers — updated to parse the flag as JSON and check for any visible source before clicking the manual-create button.
🧪 How to verify
  1. Enable create_segment_with_external_sources with [{"active":true,"name":"csv","visible":true}] — Segments → Create Segment should open the sources modal with CSV as a live card (no Beta chip), clicking it should open the CSV drawer.
  2. Upload a CSV with/without headers, switch the identifier column, and verify the 5-row preview, ignored-row warnings, and empty-column blocking error.
  3. Upload a CSV exported from Excel (UTF-8 BOM) and confirm the first column name and identifiers render correctly (BOM should be invisible in the preview and stripped from identifiers).
  4. Set the flag value to [] or disable the flag — the button should open the manual segment drawer directly.
  5. cd frontend && npx jest common/utils/__tests__/csv.test.ts — all 9 tests pass.

Product take: This is the front half of a CSV-based segment creation flow. The upload, preview, and validation UX is in good shape; the feature becomes useful once the follow-up PR wires submission to the cohorts API. Solid incremental progress on expanding segment sources beyond manual rules.

🧭 Assumptions & unverified claims
  • Assumed Utils.getFlagsmithJSONValue safely parses and returns the fallback on invalid JSON, matching the e2e helper's flagsmith.getValue(..., { json: true }) behaviour.
  • Assumed the existing CreateSegmentModal's onComplete + closeModal pattern works correctly when the sources modal closes first and then the CSV drawer opens (sequential openModal calls).

A form that accepts everything and submits nothing — the zen of incremental delivery. · reviewed at 497b491

@Zaimwa9
Zaimwa9 marked this pull request as ready for review August 14, 2026 08:32
@Zaimwa9
Zaimwa9 requested a review from a team as a code owner August 14, 2026 08:32
@Zaimwa9
Zaimwa9 requested review from talissoncosta and removed request for a team August 14, 2026 08:32
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Aug 14, 2026
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-api-test:pr-8283 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-8283 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-api:pr-8283 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-private-cloud:pr-8283 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-frontend:pr-8283 Finished ✅ Results

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bdd094c7-b3fb-4fd2-aeea-71e98539edeb

📥 Commits

Reviewing files that changed from the base of the PR and between dc99d65 and 497b491.

📒 Files selected for processing (11)
  • frontend/common/utils/__tests__/csv.test.ts
  • frontend/common/utils/csv.ts
  • frontend/e2e/helpers/e2e-helpers.playwright.ts
  • frontend/web/components/CsvUpload/CsvUpload.scss
  • frontend/web/components/CsvUpload/CsvUpload.tsx
  • frontend/web/components/CsvUpload/index.ts
  • frontend/web/components/modals/CreateSegmentFromCsv/CreateSegmentFromCsv.tsx
  • frontend/web/components/modals/CreateSegmentFromCsv/index.ts
  • frontend/web/components/modals/CreateSegmentSourcesModal/CreateSegmentSourcesModal.tsx
  • frontend/web/components/modals/CreateSegmentSourcesModal/index.ts
  • frontend/web/components/pages/SegmentsPage.tsx

Comment thread frontend/e2e/helpers/e2e-helpers.playwright.ts
Comment on lines +35 to +44
const { getInputProps, getRootProps } = useDropzone({
accept: {
'text/csv': ['.csv'],
},
multiple: false,
onDrop,
onDropRejected: () => {
setError('Please select a CSV file')
},
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add a maxSize limit to the dropzone.

The component accepts a CSV of any size. readAsText loads the whole file into a string, and the caller parses it synchronously on the main thread. A very large file freezes the tab with no feedback. Set maxSize and report the rejection through the existing onDropRejected path.

♻️ Proposed size guard
+const MAX_FILE_SIZE = 5 * 1024 * 1024
+
   const { getInputProps, getRootProps } = useDropzone({
     accept: {
       'text/csv': ['.csv'],
     },
+    maxSize: MAX_FILE_SIZE,
     multiple: false,
     onDrop,
-    onDropRejected: () => {
-      setError('Please select a CSV file')
-    },
+    onDropRejected: (rejections) => {
+      const tooLarge = rejections.some((rejection) =>
+        rejection.errors.some((e) => e.code === 'file-too-large'),
+      )
+      setError(
+        tooLarge
+          ? 'The file is larger than 5 MB'
+          : 'Please select a CSV file',
+      )
+    },
   })

Please confirm that react-dropzone 14.2.3 reports the file-too-large error code in onDropRejected.

📝 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
const { getInputProps, getRootProps } = useDropzone({
accept: {
'text/csv': ['.csv'],
},
multiple: false,
onDrop,
onDropRejected: () => {
setError('Please select a CSV file')
},
})
const MAX_FILE_SIZE = 5 * 1024 * 1024
const { getInputProps, getRootProps } = useDropzone({
accept: {
'text/csv': ['.csv'],
},
maxSize: MAX_FILE_SIZE,
multiple: false,
onDrop,
onDropRejected: (rejections) => {
const tooLarge = rejections.some((rejection) =>
rejection.errors.some((e) => e.code === 'file-too-large'),
)
setError(
tooLarge
? 'The file is larger than 5 MB'
: 'Please select a CSV file',
)
},
})

Comment on lines +126 to +137
<div className='mb-3'>
<label>Environment*</label>
<EnvironmentSelect
projectId={Number(projectId)}
idField='id'
value={environmentId}
onChange={(value) => setEnvironmentId(`${value}`)}
/>
<div className='fs-small text-muted mt-1'>
The uploaded identities will be targeted in this environment only.
</div>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- component map ---'
ast-grep outline frontend/web/components/modals/CreateSegmentFromCsv/CreateSegmentFromCsv.tsx

printf '%s\n' '--- relevant component and select definitions ---'
rg -n --glob '*.ts' --glob '*.tsx' \
  'EnvironmentSelect|Identifier column|data-test=.identifier-column-select.|inputId|htmlFor' \
  frontend common 2>/dev/null | head -n 240

printf '%s\n' '--- target file ---'
cat -n frontend/web/components/modals/CreateSegmentFromCsv/CreateSegmentFromCsv.tsx | sed -n '70,185p'

Repository: Flagsmith/flagsmith

Length of output: 12820


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- EnvironmentSelect implementation ---'
cat -n frontend/web/components/EnvironmentSelect.tsx | sed -n '1,125p'

printf '%s\n' '--- Select component and package declarations ---'
rg -n --glob '*.ts' --glob '*.tsx' --glob 'package.json' --glob '*lock*' \
  "from ['\"](common/|components/|project/|react-select)|react-select|<Select" \
  frontend package.json yarn.lock package-lock.json pnpm-lock.yaml 2>/dev/null | head -n 260

printf '%s\n' '--- inputId usage and target identifiers ---'
rg -n --glob '*.ts' --glob '*.tsx' \
  "inputId=['\"]segment-csv-environment|inputId=['\"]identifier-column-select|id=['\"]segment-csv-environment|id=['\"]identifier-column-select|htmlFor=['\"]segment-csv-environment|htmlFor=['\"]identifier-column-select" \
  frontend 2>/dev/null

Repository: Flagsmith/flagsmith

Length of output: 31662


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

target = Path("frontend/web/components/modals/CreateSegmentFromCsv/CreateSegmentFromCsv.tsx").read_text()
wrapper = Path("frontend/web/components/EnvironmentSelect.tsx").read_text()

checks = {
    "environment label has no htmlFor": bool(re.search(r"<label>Environment\*</label>", target)),
    "environment control has no inputId": bool(re.search(r"<EnvironmentSelect(?:(?!</EnvironmentSelect>).)*projectId=", target, re.S))
    and not bool(re.search(r"<EnvironmentSelect(?:(?!</EnvironmentSelect>).)*inputId=", target, re.S)),
    "identifier label has no htmlFor": bool(re.search(r"<label>Identifier column\*</label>", target)),
    "identifier control has no inputId": bool(re.search(r"<Select(?:(?!/>).)*data-test='identifier-column-select'", target, re.S))
    and not bool(re.search(r"<Select(?:(?!/>).)*inputId=", target, re.S)),
    "EnvironmentSelect forwards rest to Select": bool(re.search(r"<Select\s*\{\.\.\.rest\}", wrapper)),
    "EnvironmentSelect accepts inputId through react-select Props": bool(re.search(r"Partial<Omit<Props,\s*['\"]value['\"]>>", wrapper)),
}

for name, result in checks.items():
    print(f"{name}: {'yes' if result else 'no'}")

if not all(checks.values()):
    raise SystemExit("focused accessibility checks did not match the expected source shape")
PY

Repository: Flagsmith/flagsmith

Length of output: 424


Associate both labels with their select controls.

Set matching htmlFor and inputId values for EnvironmentSelect and Select. EnvironmentSelect forwards inputId to its underlying Select.

Comment on lines +67 to +74
return (config as SegmentSourceFlagEntry[])
.filter(
(entry) => entry?.visible !== false && SOURCE_DETAILS[entry?.name ?? ''],
)
.map((entry) => ({
...SOURCE_DETAILS[entry.name ?? ''],
active: !!entry.active,
}))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check own properties of SOURCE_DETAILS before you accept a configured entry.

SOURCE_DETAILS[entry?.name ?? ''] also resolves inherited Object.prototype members. If the flag contains an entry named constructor, toString, or valueOf, the filter passes and the spread copies a function instead of source metadata. The resulting object has no key, name, or description, so the card renders empty, key={source.key} becomes undefined, and trackSourceEvent reports source: undefined. Restrict the lookup to own properties.

🐛 Proposed own-property check
   return (config as SegmentSourceFlagEntry[])
-    .filter(
-      (entry) => entry?.visible !== false && SOURCE_DETAILS[entry?.name ?? ''],
-    )
+    .filter(
+      (entry) =>
+        entry?.visible !== false &&
+        Object.prototype.hasOwnProperty.call(SOURCE_DETAILS, entry?.name ?? ''),
+    )
     .map((entry) => ({
-      ...SOURCE_DETAILS[entry.name ?? ''],
+      ...SOURCE_DETAILS[entry.name as string],
       active: !!entry.active,
     }))
📝 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
return (config as SegmentSourceFlagEntry[])
.filter(
(entry) => entry?.visible !== false && SOURCE_DETAILS[entry?.name ?? ''],
)
.map((entry) => ({
...SOURCE_DETAILS[entry.name ?? ''],
active: !!entry.active,
}))
return (config as SegmentSourceFlagEntry[])
.filter(
(entry) =>
entry?.visible !== false &&
Object.prototype.hasOwnProperty.call(SOURCE_DETAILS, entry?.name ?? ''),
)
.map((entry) => ({
...SOURCE_DETAILS[entry.name as string],
active: !!entry.active,
}))

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor
✅ private-cloud · depot-ubuntu-latest-arm-16 — run #19364 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  38.6 seconds
commit  033c67d
info  🔄 Run: #19364 (attempt 1)

🗂️ Previous results
✅ private-cloud · depot-ubuntu-latest-16 — run #19364 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  19.1 seconds
commit  033c67d
info  🔄 Run: #19364 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #19364 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  41 seconds
commit  033c67d
info  🔄 Run: #19364 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #19364 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  41.4 seconds
commit  033c67d
info  🔄 Run: #19364 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-16 — run #19347 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  1 minute, 2 seconds
commit  05b2fcf
info  🔄 Run: #19347 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #19347 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  45.1 seconds
commit  05b2fcf
info  🔄 Run: #19347 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #19347 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  32.5 seconds
commit  05b2fcf
info  🔄 Run: #19347 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-arm-16 — run #19347 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  23 passed
skipped  1 skipped

Details

stats  24 tests across 18 suites
duration  1 minute, 23 seconds
commit  05b2fcf
info  🔄 Run: #19347 (attempt 1)

Skipped tests

firefox › tests/onboarding-tests.pw.ts › Onboarding › New user connects via the single-page onboarding flow @oss

✅ private-cloud · depot-ubuntu-latest-16 — run #19330 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  48.3 seconds
commit  497b491
info  🔄 Run: #19330 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-arm-16 — run #19330 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  38.4 seconds
commit  497b491
info  🔄 Run: #19330 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #19330 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  41.2 seconds
commit  497b491
info  🔄 Run: #19330 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #19330 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  40.5 seconds
commit  497b491
info  🔄 Run: #19330 (attempt 1)

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Visual Regression

19 screenshots compared. See report for details.
View full report

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0dfea727-677f-4ea7-9da2-f7310e48cc5e

📥 Commits

Reviewing files that changed from the base of the PR and between 497b491 and 05b2fcf.

⛔ Files ignored due to path filters (1)
  • frontend/documentation/TokenReference.generated.stories.tsx is excluded by !**/*.generated.*
📒 Files selected for processing (15)
  • frontend/common/theme/tokens.json
  • frontend/common/theme/tokens.ts
  • frontend/common/utils/__tests__/csv.test.ts
  • frontend/common/utils/csv.ts
  • frontend/web/components/CsvUpload/CsvUpload.scss
  • frontend/web/components/CsvUpload/CsvUpload.tsx
  • frontend/web/components/EnvironmentSelect.tsx
  • frontend/web/components/base/forms/Checkbox.tsx
  • frontend/web/components/modals/CreateSegmentFromCsv/CreateSegmentFromCsv.scss
  • frontend/web/components/modals/CreateSegmentFromCsv/CreateSegmentFromCsv.tsx
  • frontend/web/components/modals/base/Modal.tsx
  • frontend/web/components/pages/SegmentsPage.tsx
  • frontend/web/styles/3rdParty/_react-select.scss
  • frontend/web/styles/_token-utilities.scss
  • frontend/web/styles/_tokens.scss

Comment on lines +26 to +42
const onDrop = useCallback(
(acceptedFiles: File[]) => {
setError('')
const file = acceptedFiles[0]
if (!file) {
return
}
const reader = new FileReader()
reader.addEventListener('load', () => {
onChange(file, `${reader.result}`)
})
reader.addEventListener('error', () => {
setError('Error reading file')
})
reader.readAsText(file)
},
[onChange],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Ignore stale file-read callbacks.

FileReader completes asynchronously. If the user selects a second file before the first read completes, the first listener can run last and replace the newer selection through onChange.

Track the current read and ignore load and error events from older readers.

Proposed fix
-import { FC, useCallback, useState } from 'react'
+import { FC, useCallback, useRef, useState } from 'react'
 ...
 const CsvUpload: FC<CsvUploadType> = ({ onChange, rowCount, value }) => {
   const [error, setError] = useState('')
+  const readVersion = useRef(0)

   const onDrop = useCallback(
     (acceptedFiles: File[]) => {
       setError('')
       const file = acceptedFiles[0]
       if (!file) {
         return
       }
+      const version = ++readVersion.current
       const reader = new FileReader()
       reader.addEventListener('load', () => {
-        onChange(file, `${reader.result}`)
+        if (version === readVersion.current && typeof reader.result === 'string') {
+          onChange(file, reader.result)
+        }
       })
       reader.addEventListener('error', () => {
-        setError('Error reading file')
+        if (version === readVersion.current) {
+          setError('Error reading file')
+        }
       })
📝 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
const onDrop = useCallback(
(acceptedFiles: File[]) => {
setError('')
const file = acceptedFiles[0]
if (!file) {
return
}
const reader = new FileReader()
reader.addEventListener('load', () => {
onChange(file, `${reader.result}`)
})
reader.addEventListener('error', () => {
setError('Error reading file')
})
reader.readAsText(file)
},
[onChange],
const onDrop = useCallback(
(acceptedFiles: File[]) => {
setError('')
const file = acceptedFiles[0]
if (!file) {
return
}
const version = ++readVersion.current
const reader = new FileReader()
reader.addEventListener('load', () => {
if (version === readVersion.current && typeof reader.result === 'string') {
onChange(file, reader.result)
}
})
reader.addEventListener('error', () => {
if (version === readVersion.current) {
setError('Error reading file')
}
})
reader.readAsText(file)
},
[onChange],

readOnly?: boolean
idField?: 'id' | 'api_key'
ignore?: string[]
size?: 'default' | 'select-sm' | 'select-xsm'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract the size union into a named type.

Define EnvironmentSelectSize and use it for the size property.

As per coding guidelines: frontend/**/*.{ts,tsx} must extract inline union types into named types.

Proposed change
+type EnvironmentSelectSize = 'default' | 'select-sm' | 'select-xsm'
+
 type EnvironmentSelectType = Partial<Omit<Props, 'value'>> & {
 ...
-  size?: 'default' | 'select-sm' | 'select-xsm'
+  size?: EnvironmentSelectSize
 }
📝 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
size?: 'default' | 'select-sm' | 'select-xsm'
type EnvironmentSelectSize = 'default' | 'select-sm' | 'select-xsm'
type EnvironmentSelectType = Partial<Omit<Props, 'value'>> & {
size?: EnvironmentSelectSize
}

Source: Coding guidelines

Comment on lines +25 to +27
type CreateSegmentFromCsvType = {
projectId: number | string
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract the projectId union into a named type.

Line 26 defines an inline union type. Define a named type and use it for projectId.

Proposed fix
+type ProjectId = number | string
+
 type CreateSegmentFromCsvType = {
-  projectId: number | string
+  projectId: ProjectId
 }

As per coding guidelines, frontend/**/*.{ts,tsx} must extract inline union types into named types.

📝 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
type CreateSegmentFromCsvType = {
projectId: number | string
}
type ProjectId = number | string
type CreateSegmentFromCsvType = {
projectId: ProjectId
}

Source: Coding guidelines

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

Labels

feature New feature or request front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant