When “Just One More Option” Stops Working

This app started simple.

A few dropdowns.
A few options.
Nothing worth overthinking.


Then came the classic pattern:

“I’ll just add one more.”

And to be fair—it was easy.
Just hardcode another option in the React component. Done.

<Input
type="select" required
id="posting_url_domain"
maxLength={32}
name="posting_url_domain"
onChange={handleInputChange}
value={state.posting_url_domain || ''}>
<option value="">Select Posting Domain</option>
<option value="LinkedIn Easy Apply">LinkedIn Easy Apply</option>
<option value="Dice Easy Apply">Dice Easy Apply</option>
<option value="Indeed">Indeed</option>
// ...and then 12 more over time
</Input>

Until it wasn’t.

Two years later:

  • ~15 job sites
  • Employment types quietly growing from 2 → 5
  • Recruitment stages that refuse to be standardized no matter how nicely you ask

At some point, “just one more option” turns into:

Why is this still hardcoded?


Because now every small change means:

  • finding the right file
  • adding the next value
  • hoping naming stays consistent
  • and doing it again… and again… and again

Not hard. Just… dumb friction.


So that part’s gone.

Dropdowns are now configurable in the app.

const dropdownOptions = await apiRequest(DROPDOWN_OPTIONS_API_URL);
setState({
job_sites: dropdownOptions.job_sites,
employment_types: dropdownOptions.dropdown_options.employment_type,
recruitment_stages: dropdownOptions.dropdown_options.recruitment_stage,
});

Add, edit, move on. No code changes.


And once that was fixed, the bigger problem became obvious.

The dashboard.


All this data I’ve been collecting?

It was there. Technically.

This isn’t theoretical data either.
I use this app every day—copying job listings, tracking applications, and, more often than I’d like, logging the rejection that follows.

But actually using it meant dealing with a report that was just as rigid as the dropdowns used to be.


So that changed too.

The dashboard is now driven by configurable segments.

Which means I can finally:

  • change how things are grouped without touching Python
  • adjust the view when patterns change
  • and actually analyze the data instead of just storing it

And when the UI still isn’t enough… sometimes you just grab the data and throw it into Excel:

const exportToTSV = (columns, data) => {
if (!columns?.length || !data?.length) return; const headers = columns.map(col => col.name).join('\t'); const rows = data.map(row =>
columns
.map(col => {
try {
const value = col.selector ? col.selector(row) : '';
return value == null ? '' : String(value);
} catch {
return '';
}
})
.join('\t')
); const tsv = [headers, ...rows].join('\n'); navigator.clipboard.writeText(tsv);
};

And of course, the snazzy button itself

{enableExport && (
  <div style={{ marginBottom: '8px' }}>
    <button
      className="btn btn-secondary btn-sm"
      onClick={() => exportToTSV(columns, data)}>
        Copy TSV for Excel
    </button>
    <span style={{ fontSize: '0.75rem', color: '#555', marginLeft: '8px', }}>
      Click to copy table. Then paste directly into Excel. Use Excel’s <strong>Data → Text to Columns → Delimited → Tab</strong> option.
    </span>
  </div>
)}

There are a few other tweaks in here—wider tables, small UX fixes—but those aren’t the point.

The real change is this:

The system finally stopped fighting small changes.


And honestly, that matters more than adding another feature.

Because this app did what it was supposed to do:
I learned Django. I learned React.

Now it just needed to get out of its own way.