Every labour complaint, tracked from intake to judgement.
We built a complaints and case system for an employment council. Staff log each labour complaint once, an AI model suggests its category and a likely resolution, and designated agents see the case through to a final judgement. The client is kept anonymous on this page.
Who it's for
An employment council's admins and designated agents
Runs on
Any web browser; Apache, PHP and MySQL on the server
Built with
PHP, MySQL, a Python AI model (scikit-learn, Flask)
Labour complaints reached the council on paper and by phone. There was no one place to see who was handling a case, what stage it had reached, or how long cases were taking.
Complaints also come in mixed. Unpaid wages and a dismissal can arrive in the same letter, and someone has to read each one and decide where it goes. The council needed:
One record per complaint, with its own case number and supporting documents
A clear status for every case, from Pending to Resolved or Dismissed
Help sorting complaints by category and urgency, without taking the decision away from staff
A way for admins to assign cases and see how long they take
02 — How we built it
The same six steps we use for every client.
Here's what each step looked like on this project.
1
Brief
Every complaint gets one record and a case number. Two kinds of user: admins, who see everything and assign cases, and designated agents, who work the cases assigned to them. The AI may suggest, but a person always decides.
2
Design decisions
We fixed the case stages (Pending, Assigned, Investigating, Resolved or Dismissed), kept the AI as a separate small service, and decided that every action on a case is logged. These choices are explained below.
3
Clickable prototype
The screens staff use most (the dashboard, logging a new complaint, a single case and the reports) are mapped as a clickable prototype with sample data, so you can follow a complaint through the system. Try it below.
4
Build in small pieces
The pieces came one at a time: complaint intake with document uploads, the case list with filters, assigning cases with an email to the agent, the AI suggestions, a timeline for each case, printable case reports, agent performance figures and a public status check.
5
Test and harden
We trained and checked the model in notebooks on labelled sample complaints. In the app, uploads are limited in size and file type, pages check the user's role, and if the AI service doesn't answer within a few seconds the complaint is still saved.
6
Ship and support
The system runs as a PHP and MySQL web app with the Python model running beside it. It includes a built-in user manual, and admins can export resolved cases as training data for the next update of the model.
03 — Try it
Click through the prototype.
This is the clickable prototype, running live on the page. Every name, case and figure in it is sample data, and it may be a step behind the real system.
Log a new complaint and run the AI analysis
Submit it, then assign the case to a designated agent
Open Reports to see cases by category and the AI accuracy check
When a complaint is logged, the model suggests one of five categories (such as wage dispute or unfair dismissal) and a likely resolution. The designated agent investigates and records the final judgement, which is what counts.
Show how sure it is, and the runner-up
Each suggestion comes with a confidence percentage and the second most likely category. Complaints often mix issues, like unpaid wages and a dismissal, so staff see when the model is unsure instead of trusting a single label.
The AI runs on its own, and can't block intake
The model is a small separate service on the same machine. The case system asks it for a suggestion when a complaint is submitted. If it doesn't reply within a few seconds, the case is saved anyway and marked "Not predicted", so no complaint is lost.
Every action leaves a trail
Opening, assigning, updating and resolving a case are all written to that case's timeline: who did what, and when. The printable case report includes the timeline, and complainants can check their status with just their case number.
Urgency is deliberately simple. Words like "harassment" or "threat" mark a complaint High, and words about unpaid or late wages mark it Medium. Rules like these are easy for staff to understand. The dashboard also flags any case that has sat untouched for 30 days or more.
05 — Tech stack
What it's made of.
Web app
PHP 7.4+, plain HTML, CSS and JavaScript, Chart.js for dashboard charts
Data
MySQL for cases, documents, users and the case audit log
AI model
Python and scikit-learn: TF-IDF text features with logistic regression, able to give several categories per complaint
AI service
A small Flask API that the PHP app calls when a complaint is submitted
Email
PHPMailer, so agents get an email when a case is assigned to them
Hosting
Apache, PHP and MySQL (XAMPP), with the model service running alongside
06 — What the client got
What was handed over.
Alongside case intake and tracking, the council got:
A dashboard with case counts, charts and overdue-case alerts
Filters by category, urgency, status and date range, and cases grouped by region
An agent performance report: resolution rate and average days to resolve
Printable case reports with the AI suggestion, the final judgement and the timeline
A public status check page and a built-in user manual
Each resolved case, with the agent's final judgement, can be exported as training data, so the model can be retrained on the council's own cases.
Your project
Want something like this for your business?
We follow the same steps for every client, and you see a clickable prototype before you pay for the build.