- Technologies Used
- Features
- Installation
- Usage
- Project Structure
- Performance
- Assumptions
- Considerations
The project is built using the following technologies:
- React: JavaScript library for building the user interface. (Included)
- Recharts: A popular charting library for rendering interactive charts.
- Next.js: A React framework for server-rendered applications. (Included)
- PapaParse: A CSV parsing library for converting CSV data into JSON.
- Date-fns: A date utility library for handling date manipulation. (Included)
- Tailwind CSS: A utility-first CSS framework for styling the application. (Included)
- Displays line chart of BTC address balances in requested ranges.
- Filter the chart data by Year-to-Date (YTD), 12 months (12M), 3 months (3M), and 1 month (1M). ChartRange
- Various test cases for utilities functions and snapshot testing for commonly reused React Components [1] [2]
-
Download and unpack repo:
cd react-nextjs-btc-address-chart
-
Install the dependencies:
pnpm install
-
Start the development server:
pnpm run dev
- Access the application in your web browser at http://localhost:3000
- Explore the BTC address balance chart with filter options.
- Hover over the interactive chart visualization.
The project structure is organized as follows:
- app/: Next.js pages for rendering the web application.
- api/: API route for serving BTC address data from the CSV file.
- components/: React components for building the chart and user interface.
- public/: Static assets and data. (Includes the provided csv file)
- styles/: Styling using Tailwind CSS.
- utils/: Utility functions for parsing data and date formatting.
- **/__tests__/: Test cases for automated Jest testing.
- Rechart charting engine was used to create the WalletLineChart component
- Used the static data provided to build the API method
GET app/api/btc-addresses
- Formatting for the CSV for the JSON timeseries response object is within the mapCsvHeaders helper function. Some additional formatting was done directly in the api route. Please refer to Assumptions for additional notes
- API call to load the data inside of the React Component can be found in ChartContainer. Data is then stored with Context API under context/data.context.tsx
- Design Template was used for UI inspiration
- Depending on chart filter (ie - ALL vs YTD) the x-axis tick will change between
YYYY
andD. LLL
format. Logic can be found under tickFormatter - TableButtons will change filter state via Context API and also change colors under a conditional (tenary) operator.
- Depending on chart filter (ie - ALL vs YTD) the x-axis tick will change between
-
Server-side rendering (SSR) is used to enhance performance. For this project I decided to migrate from the Page Router to the App Router as it is server-centric while client-side render is opt-in.
-
The chart is loaded with server-generated data. As the api/btc-wallets route is cached it will only be required to invoke the expensive csv parsing logic once.
-
Filters are calculated on the client-side just once and stored in walletChartCache. Sequential requests from the same filter will be retrieved from the cache in
O(1)
time. -
Filter calculations have a time complexity of
O(log n)
. Due to the csvData being sorted by default we can utilize Binary Search in function findIndexOfMax
While comparing my output to the provided screenshots I realized some major differences. On closer examination I believe the csv headers provided decent context ("BTC / Addr Cnt of Bal ≥ $1K"
) to divide based on the wallet balances. Typically I would refer to the data sources' API documentation on how such data was derived rather than assuming. Since it at least visually looks correct after fixing it I hope this is the implementation you are expecting for the challenge. The correction is done on the server-side under updatedCsvDataList
Due to time constraints and prioritization of maximizing feature completeness and test coverage certain ideas were considered but not implemented for the following reasons:
-
Example implementation shows the ability to hover and "focus" on each line balance while the remaining lines are dimmed. The charting engine does not do this by default but is possible with the
onHover
methods if I had more time -
For large datasets it is common to use algos to downsample it to improve rendering while minimizing any loss data. In this scenerio I would have considered LTTB
-
I attempted to simplify the filter testing by using
dotenv
and assigningprocess.env.MOCK_TODAY_DATE = "2023-02-01"
mock TODAY's date. On testing I realized that I set the server-side clock but not my browsers. If I was able to completely render all the charts and filters on the server then maybe this could be reconsidered. -
Depending on use-case. If chart interactivity were not as important (ie: Chart screenshots for news articles on mobile), I would have liked to create routes for each filter and rendered them on the server-side during build. For example
api/btc/wallets/[filter]
. Unless the chart was streaming live data, this would be very helpful for daily snapshots for news articles. -
Normally I would also include supertesting the api endpoint. Since the GET endpoint does not accept any params and the data is retrieved from a static source at this time, it felt overkill to generate multiple mock CSV documents to test a hypothetical situation where I would be pulling from a database where I can control the Time ranges instead.