TaxPal
TaxPal is an accounting-services brokerage platform in Japan. It connects three sides: customers who need an accountant or another expert, the sales and broker team who take each request and see it through, and the accountants and experts who deliver the service. Each side has its own area of the product, and all of them share the same companies, cases and service history.
In 2022 I was the Tech Lead of the team in Vietnam and coordinated the Vietnam side of the project through its main development phase. Our scope was the sales dashboard: the internal analytics the broker team and its managers use to follow cases, KPIs, sales performance and conversion — for each staff member, each team and the whole company.
Why it is a management-system project: the hard part was never drawing charts. It was deciding what a number means — which cases count, on which working days, in which fiscal period, for staff who have since left — and then serving those numbers quickly. It was also brownfield work: new dashboards on an existing AWS codebase, with its own GraphQL resolvers, database, authentication and release process.
The platform and who uses it
Customers bring a need. The sales and broker team turn it into a case, follow it up and move it towards a contract; their managers watch individual, team and company performance. Accountants and experts take the cases that reach them and have their own dashboard.
Diagram fitted to its frame
My responsibilities
- Tracked and controlled the team's technical progress, schedule and workload across menus and milestones.
- Coordinated backend, frontend and the IT communicator, and met the Japanese client regularly.
- Turned business requirements into technical designs; broke down and estimated the work.
- Reviewed API designs and pull requests.
- Unblocked hard problems in GraphQL, Prisma, the database and integration, and coordinated bug fixes between frontend and backend.
- Assessed schedule risk and cleared up unclear points in the business documents with the client.
- Managed hand-over and releases through the client's repository process, and reported status, issues, causes and fixes.
From request to contract: the case pipeline
Every customer need is a case, linked to a company record that becomes the master data for its appointments, progress and contracts. A salesperson can be assigned by hand — the system does not force an automatic assignment rule on the organisation. From there a case moves through many sales states: likely to get an appointment, blocked by an issue, difficult, contacted but not reached, appointment confirmed or cancelled, following up, contract signed, accounting contract signed, paused, cancelled, cancelled at renewal. In the version described, salespeople choose the status themselves on the case screen rather than a fully automatic state machine setting it.
Diagram fitted to its frame
What the sales dashboard shows
The dashboard turns cases into management views: new and in-progress cases, intake, KPIs, actual results, sales, success rates, and performance per staff member, per team and for the company. It includes a weekly table of cases per member and team, KPI results by day, month and year, a sales ranking, sales by service over the last four years, and the success rate of the pipeline over periods from two weeks to a year.
Diagram fitted to its frame
KPI targets against actuals
Managers enter sales targets by year, month, staff member and metric. An empty period opens ready for input; a period that already has targets opens them for editing. Targets are then compared with actual results at daily, monthly and yearly scale, so the gap and its trend are visible, not only the total.
Diagram fitted to its frame
Counting a customer once: the duplicate-case rule
The same customer can arrive more than once, through different contacts or at different times. The rule groups cases that share a representative, a matching email or phone number, and an intake date inside a set window. Groups can be chains: if A matches B and B matches C, all three belong together. For each group the dashboard picks one representative record and one forecast value, so mail, contact and case counts are not inflated.
Diagram fitted to its frame
Numbers that respect the calendar and the organisation
- Read-optimised analytics tables. To avoid recomputing everything from transactional tables on every page load, statistics tables store what aggregation needs: the period and its start and end dates, team order, the type of statistic, staff status and working dates. Reads get fast and queries simple; the trade-off is that a changed business rule can mean rebuilding that data.
- Working days. Remaining working days and valid intake dates are counted against holiday data, not the plain calendar.
- Fiscal periods. A year chosen on screen can mean a fiscal year running from October to September, so frontend and backend must agree on the date range rather than filter by calendar year.
- Staff who have left. A retired flag lives in the statistics data, so historical reports keep former staff without treating them as active in every calculation.
The expert dashboard
The project documentation also covers a dashboard for the accountants and experts themselves: income, business performance, strategy, people, succession, IT adoption and customer satisfaction, compared on radar charts against individual, group, company and reference-average benchmarks.
Diagram fitted to its frame
Architecture
The frontend never touches the database. Next.js and React call AWS AppSync through Apollo; the GraphQL schema is authorised with Cognito user pools and IAM; each request goes through resolvers, use cases and repositories, and Prisma reaches PostgreSQL on AWS. The codebase also includes Elasticsearch / OpenSearch for search and indexing, and the AWS infrastructure is defined in Terraform.
Diagram fitted to its frame
Adding an API meant the same path every time: schema, code generation, resolver, use case, repository, Prisma query, then test and seed data. Pull requests were reviewed on the client's side and every GitHub check had to pass. Database migrations and seed data went in pull requests of their own, so a schema change was never merged together with business code that would be hard to review or roll back.
Diagram fitted to its frame
Making the dashboard fast
A dashboard with many widgets and many statistics makes performance a daily concern. The issues found were concrete: APIs selecting far more fields than the screen used, whole relations loaded where a few fields would do, checks made with repeated requests, a screen of about 120 records taking more than a second, and some dashboards calling the API once per team. The fixes: select only what is needed, use relations instead of repeated queries, shrink responses, return several teams from one API, send fewer requests from the frontend, and lazy-load widgets the user has not reached yet — with loading states that tell "still loading" apart from "no data".
Diagram fitted to its frame
Charts and QA for a dashboard people manage by
A dashboard people manage by has to survive awkward data. Team colours had to stay readable as the number of teams grew, with teams beyond a threshold sharing one style, and some statistics showing only teams that had members. Values above 100% — up to around 200% — had to stay inside their chart, with readable tooltips and scale rules. Testing therefore went beyond the interface: field mapping, totals, working days, duplicate cases, sorting, statuses, date ranges, filters, zero and null values, and the structure of every API response.
What the project covers
| Concern in a large management system | How it shows up in TaxPal |
|---|---|
| Sales pipeline with many states | Case statuses from first contact to contract, set by the salespeople |
| KPI management | Targets by year, month, staff member and metric; target against actual by day, month and year |
| Multi-level aggregation | Staff, team and company views; rankings; sales by service over four years |
| Data-correctness rules | Duplicate-case groups, working days, fiscal periods, retired staff |
| Read performance | Pre-computed analytics tables, selecting only what is needed, one API for many teams, lazy loading |
| Brownfield delivery | New modules on an existing AppSync, Prisma and PostgreSQL codebase; migrations in their own pull requests |
| Working with a client in Japan | Regular meetings, requirement clarification, releases through the client's repository |
The diagrams are reconstructed at component level from the project's documents. They omit internal URLs, identifiers, proprietary code and client data. The screenshots are design mock-ups and a redrawn data model; staff names, a logo and a portrait are blurred.
Image 1 of 4
