Skip to content

feat(finance): import a watchlist file into a new list named after it - #163

Merged
ralyodio merged 1 commit into
masterfrom
finance-watchlist-import-new-list
Aug 3, 2026
Merged

feat(finance): import a watchlist file into a new list named after it#163
ralyodio merged 1 commit into
masterfrom
finance-watchlist-import-new-list

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #162. Import used to merge the file's tickers into whichever tab happened to be active; now it creates its own list.

  • Namingmy-tech-list.csvMy Tech List, the inverse of the export filename, so a list exported and re-imported comes back under its original name. A file the user capitalized themselves (FAANG picks.txtFAANG picks) is left alone; directories and extensions are stripped; the name is capped at MAX_WATCHLIST_NAME.
  • Collisions — importing the same file twice gives Tech 2, Tech 3… rather than a second identical tab (case-insensitive match, and the suffix stays within the length cap).
  • No empty lists — symbols are validated client-side with parseSymbolList before the list is created, so a junk or ticker-less file reports "No valid tickers found." and creates nothing.
  • Fallback — if list creation fails, the import still lands in the active list rather than being lost.

New pure helpers watchlistNameFromFilename and uniqueWatchlistName in src/lib/finance/watchlist.ts, both unit-tested including the export→import round trip. submitSymbols takes an optional target list id; paste-to-add is unchanged. No schema or API changes.

Checks: pnpm typecheck clean, pnpm build compiles, full suite 2531 passed / 7 skipped. Lint: 0 errors (5 pre-existing react-hooks/set-state-in-effect warnings in this file).

🤖 Generated with Claude Code

Import now creates its own list instead of merging into whatever tab was
active: "my-tech-list.csv" -> "My Tech List", so an exported list
round-trips. A name already in use gets a numeric suffix ("Tech 2")
rather than a second identical tab, and the file's symbols are validated
client-side first so a junk file never leaves an empty list behind.
If list creation fails, the import falls back to the active list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
});

it('strips directories, extensions and separators', () => {
expect(watchlistNameFromFilename('/tmp/dir/high_beta-names.csv')).toBe('High Beta Names');
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

225 finding(s)

HIGH/CRITICAL: 137 | MEDIUM: 88

Severity Rule Location
HIGH secret-private-key src/app/settings/seedbox-section.tsx:354
HIGH secret-generic-api-key docs/incidents/2026-05-okshanaby-supply-chain.md:18
HIGH tls-verification-disabled docs/tunein (2).py:34
HIGH tls-verification-disabled docs/tunein (2).py:37
HIGH tls-verification-disabled docs/tunein.py:34
HIGH tls-verification-disabled docs/tunein.py:37
HIGH manifest-typosquat package.json:85
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:37
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:65
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:87
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:88
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:112
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:134
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:135
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:158
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:188
HIGH secret-generic-credential src/app/api/auth/login/route.test.ts:213
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:70
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:71
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:119
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:120
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:171
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:172
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:193
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:194
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:213
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:214
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:231
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:232
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:251
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:252
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:269
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:270
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:289
HIGH secret-generic-credential src/app/api/auth/me/route.test.ts:290
HIGH secret-generic-credential src/app/api/auth/signup/route.test.ts:37
HIGH secret-generic-credential src/app/api/auth/signup/route.test.ts:65
HIGH secret-generic-credential src/app/api/auth/signup/route.test.ts:123
HIGH secret-generic-credential src/app/api/auth/signup/route.test.ts:164
HIGH secret-generic-credential src/app/api/auth/signup/route.test.ts:173
HIGH secret-generic-credential src/app/api/auth/signup/route.test.ts:196
HIGH secret-generic-credential src/app/api/auth/signup/route.test.ts:221
HIGH secret-generic-credential src/app/api/cron/expire-subscriptions/route.test.ts:56
HIGH secret-generic-credential src/app/api/cron/expire-subscriptions/route.test.ts:62
HIGH secret-generic-credential src/app/api/ice/turn/route.test.ts:18
HIGH secret-generic-credential src/app/api/iptv/playlists/route.test.ts:39
HIGH secret-generic-credential src/app/api/iptv/subscription/route.test.ts:66
HIGH secret-generic-credential src/app/api/webhooks/coinpayportal/route.test.ts:26
HIGH secret-generic-credential src/app/api/youtube/search/route.test.ts:45
HIGH secret-generic-credential src/app/api/youtube/search/route.test.ts:46

…and 175 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 5e8ad18 into master Aug 3, 2026
9 checks passed
@ralyodio
ralyodio deleted the finance-watchlist-import-new-list branch August 3, 2026 18:44
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