Documentation/Reference apps/BrightSmile, a dental practice app where the patient accepts the plan
BrightSmile, a dental practice app where the patient accepts the plan
BrightSmile is an open-source dental practice app for a four-clinic group, with priced treatment plans patients accept. Code on GitHub, and it runs live.
- apps
- healthcare
- dental
- treatment-plans
- workflows
Overview
Four clinics share this open-source dental practice app, and the patient signs off a priced treatment plan line by line.
Chris Bennett is the seeded demo patient at BrightSmile. His treatment plan is seeded with four lines. Two start out
proposed: a porcelain crown on tooth #3 at $1,150, and four upper veneers on #7 to #10 at $5,600. The patient portal puts an Accept button next to any proposed line. The hosted demo is shared, so an earlier visitor may already have pressed it.Dental software lives on that button. A cleaning gets booked and done. A crown gets quoted first, and then somebody has to say yes to the number.
A plan line is a row the patient owns
The
Treatment schema in schemas.py is nine attributes long, and that file sits with the rest of the app in the brightsmile folder on GitHub. These carry the idea:python
{"name": "name", "type": "string", "mandatory": True},
{"name": "category", "type": "string", "values": SERVICE_CATEGORIES},
{"name": "tooth", "type": "string"},
{"name": "cost", "type": "float"},
{"name": "treatment_state", "type": "string", "mandatory": True,
"values": ["proposed", "accepted", "in_progress", "completed"]},In setup.py the patient role,
tenant_user, has can_read, can_create and can_update on treatment, filtered by filter_field="owner_username" against $user.name. That is policy-based row scoping. It also means the Accept button needs no endpoint of its own: the portal in ui/app.js calls client.updateObject('treatment', t.uuid, { treatment_state: 'accepted' }, t), then runs a workflow named treatment_accepted that emails the patient a confirmation, and a line under the list says that no payment is taken there.The seed data plays the dentist. It writes four lines for Chris: the crown, the veneers, a $220 filling on #19 already
completed, and a $4,200 Invisalign course already accepted. The staff console has no screen for proposing a plan. The staff role holds create and update rights on treatment in the policy, so that screen is yours to add.Four clinics as rows in one dental practice app
BrightSmile has clinics in Tribeca, Park Slope, SoMa and the Marina. It has exactly one tenant.
Location is an ordinary schema, and the dentist, patient and appointment schemas each carry a location_name string. The staff console has a location selector that filters the schedule and the counters, and its dashboard draws a small bar chart of open visits per clinic.Compare Medora in the same repo, where every hospital is its own tenant, a separate pool of records. The choice turns on who is allowed to read what. One BrightSmile front desk account sees all four clinics, and a patient who had a filling in Tribeca can book whitening in Park Slope without becoming a different person in the database, so a column does the job and a tenant boundary would only get in the way. If your group's clinics must not see each other's patients, copy Medora's
config.py.Three of the six schemas are public. A visitor who has not signed in can browse four locations, eight dentists, ten services.
The reminder that takes its stamp back
Staff send reminders from the schedule. The workflow,
appointment_reminder, emails the patient, texts them, and writes reminded_at on the appointment. Its on_error is compensate, and the last step declares what undoing it means:python
{"id": "stamp", "type": "crud_operation", "operation": "update", "object_type": "brightsmile:appointment",
"record_uuid": "{{input.appointment_uuid}}", "data": {"reminded_at": "{{context.timestamp}}"},
"compensate": {"type": "crud_operation", "operation": "update", "object_type": "brightsmile:appointment",
"record_uuid": "{{input.appointment_uuid}}", "data": {"reminded_at": None}}},I read the
compensate blocks in all 19 apps' setup.py files. Every other one puts a state enum back, refunds a Stripe payment, or is marked as an acknowledged skip. This is the only one that clears a timestamp. Small, yes. It is also the one to study if your own records carry fields that mean "we told the customer at 14:02", where a stamp left behind by a run that was undone would be a false statement about what the customer was told.Booking is quieter. Creating an appointment fires
@create:brightsmile:appointment, bound to a one-step workflow that emails the patient to say the request arrived. The booking page carries a symptom helper as well: it hands the patient's description and the list of services to services.ai.complete and shows the suggestion beside the form.Chart notes stay with the care team
Eight appointments are seeded for Chris. The three completed ones have
chart_notes and a diagnosis, and the patient's rule on appointment lists both fields under hidden_fields. For a tenant_user they are absent from the response body, removed server-side. The staff console shows them in a panel beside the schedule.BrightSmile models the shape of that access control and stops there. Its README calls it a reference app and warns against entering real patient records.
Running your own copy takes four commands:
bash
git clone https://github.com/supero-platform/supero-apps.git
cd supero-apps/apps/healthcare/brightsmile
cp .env.example .env # set SUPERO_DOMAIN (any free name) + SUPERO_PASSWORD
./run.shClone, change into the folder, copy the env file and run. You need no account on Supero to do this. The script registers the domain name you chose and the app comes up on port 5667, running in about two minutes, with both demo logins (front desk and patient) printed on the sign-in form.
Everything in the brightsmile folder is MIT-licensed app code. The platform behind it is a hosted service, not open source.
Sign in as the patient on the live demo to see the plan, then clone the folder and add the screen where staff propose one.
The 661 lines of
app.js are plain JavaScript, with no build step between an edit and a refresh. A dental app that begins from your own schema file starts at supero.dev.On this page