Skip to content
← All projects
Case study

bist-excel-app

JavaScriptOpen repository ↗

Problem

If you get Borsa İstanbul data as an Excel file, you are stuck between two poor options: open it in Excel and filter by hand, or upload it to some website and hand your data to someone else's server. I wanted a third: an interface where the file never leaves the browser, yet you can filter, sort, read it through a colour scale, and export it back out as Excel or PDF. That constraint also buys a deployment win — with no server involved, the whole thing ships as a static site.

Approach

A Vite + React 19 single-page app. The file is read with FileReader as an array buffer, parsed in-browser by SheetJS (xlsx), and the first sheet becomes a row array via sheet_to_json. I deliberately split state across two hooks: useBistData only handles loading and computing the price column's min/max, while useTableState owns search, sorting and pagination. The filtered and sorted lists are derived from the data with useMemo rather than copied into their own state. Price cells are coloured through a seven-stop gradient, with text colour computed from each cell's luminance. Export goes two ways: xlsx via SheetJS + file-saver, pdf via jsPDF + autoTable. Deployment is GitHub Actions to GitHub Pages, with Vite's manualChunks splitting the react, xlsx and pdf bundles apart.

Decisions and trade-offs

  • I chose never to send the file to a server — parsing happens in-browser via FileReader + readAsArrayBuffer — because uploading a financial spreadsheet is an unnecessary privacy risk; the alternative was an upload endpoint, which brings both hosting cost and data responsibility. The side benefit turned out to be the big one: with no server, the app ships as a static build on GitHub Pages.
  • I split state across two hooks (useBistData for loading and min/max, useTableState for search, sort and pagination), because I didn't want to re-parse the workbook every time someone hit a sort button when the file hadn't changed; the alternative of keeping it all in one component blurred derived lists together with the raw data in the same state.
  • I handled Turkish number and text conventions explicitly: toNumber strips '.' as the thousands separator and turns the ',' decimal into '.', while text sorting uses localeCompare(..., 'tr'). "1.234,56" parses straight to NaN in JavaScript, and 'İ/ı' sort wrongly under the default locale; the alternative — plain Number() and default comparison — produced quietly wrong results rather than errors.
  • I made the colour scale a seven-stop gradient and derived text colour from each cell (luminance 0.2126·R + 0.7152·G + 0.0722·B, threshold 0.6), because a fixed text colour became unreadable at the blue/violet end of the gradient; the alternative was a three-step fixed palette, which showed the price distribution too coarsely.
  • I split the react, xlsx and pdf bundles with Vite's manualChunks, because xlsx and jspdf are heavy and irrelevant to first paint; the alternative single bundle made every visitor download the PDF library even if they never exported anything.

Outcome

This is the one genuinely LIVE project in the set: https://msgxr.github.io/bist-excel-app/ — the GitHub Pages API reports status "built" with HTTPS enforced, the URL returns HTTP 200, and the served HTML carries the "BIST Dashboard" title and loads the expected react/xlsx/pdf chunks (verified while preparing this content). The pipeline is .github/workflows/deploy.yml: checkout → Node 20 → npm ci → npm run build → upload-pages-artifact → deploy-pages, with the last successful run on 2026-05-05. The working capabilities are verified in code: .xlsx/.xls upload, search by name, ascending/descending sort by name and price, pagination at 10 rows per page, price-driven colour scale, Excel and PDF export, plus error and empty-state messaging. There is NO performance or usage measurement — no bundle size, no Lighthouse score, no visitor count. I'm not claiming a number here; what I'm claiming is a working, reachable URL.

Stack

React 19Vite 7SheetJS (xlsx)jsPDF + jspdf-autotablefile-saverElle yazılmış CSS (Tailwind YOK)ESLint 9GitHub Actions → GitHub Pages

Sources

Every technical claim on this page was derived from the sources below.

  • https://github.com/msgxr/bist-excel-app
  • https://msgxr.github.io/bist-excel-app/
  • https://github.com/msgxr/bist-excel-app/blob/main/package.json
  • https://github.com/msgxr/bist-excel-app/blob/main/vite.config.js
  • https://github.com/msgxr/bist-excel-app/blob/main/src/hooks/useBistData.js
  • https://github.com/msgxr/bist-excel-app/blob/main/src/hooks/useTableState.js
  • https://github.com/msgxr/bist-excel-app/blob/main/src/utils/colorScale.js
  • https://github.com/msgxr/bist-excel-app/blob/main/src/utils/numberParser.js
  • https://github.com/msgxr/bist-excel-app/blob/main/src/utils/exportUtils.js
  • https://github.com/msgxr/bist-excel-app/blob/main/src/constants/columns.js
  • https://github.com/msgxr/bist-excel-app/blob/main/.github/workflows/deploy.yml

What I could not verify

Points I could not confirm while writing. I keep them visible rather than asserting them silently.

  • DEPO README'Sİ YANLIŞ (kart metni bunu tekrarlamamalı): README "React + Vite + Tailwind" diyor ama package.json'da Tailwind bağımlılığı YOK ve postcss/tailwind yapılandırması bulunmuyor; stiller elle yazılmış CSS (src/index.css, src/App.css). Vaka sayfası Tailwind demememeli.
  • DEPO README'Sİ YANLIŞ: README ve GitHub açıklaması "Formüller, grafikler ve gelişmiş analiz özellikleri" vaat ediyor; kodda formül motoru da grafik/chart kütüphanesi de YOK. Gerçekte olan: tablo, arama, sıralama, sayfalama, renk skalası, Excel/PDF dışa aktarma. lib/projects.ts'teki 'Data Visualization' etiketi de bu yüzden fazla iddialı — renk skalası var, grafik yok. Mevcut kart açıklaması ("Excel benzeri veri görselleştirme ve elektronik tablo uygulaması") düzeltilmeli.
  • Kodda doğrulanmış ama README'de yazmayan kısıt: beklenen Excel sütun adları sabit — 'BIST Adı', 'BIST Fiyatı', 'Açılış', 'Kapanış', 'Görev' (src/constants/columns.js). Farklı başlıklı bir dosya yüklenirse tablo boş/anlamsız görünür, hata da vermez. Vaka sayfası demoyu tanıtırken bu beklentiyi söylemeli.
  • README'de kişisel e-posta adresi yazıyor. Site içerik ilkesi gereği özel iletişim bilgisi siteye taşınmamalı — vaka sayfasına kopyalanmasın.
  • Küçük tutarsızlık: README'deki klon komutu depo adını 'Bist-Excel-App' olarak yazıyor; kanonik ad 'bist-excel-app'.
  • DOGRULANAMADI: Uygulamanın gerçek BIST verisiyle mi yoksa yalnızca örnek dosyalarla mı test edildiği. Depoda örnek bir .xlsx yok, bu yüzden uçtan uca hangi veriyle doğrulandığı belirsiz — demo ziyaretçisinin kendi dosyasını getirmesi gerekiyor.