ff cda00f5f8a fix: restore Continue button as page indicator in claim step3 to prevent browser close
Using paymentGroupId label as the page-load wait caused TimeoutException when
the claim flow's page structure differed, returning ERROR and triggering
_minimize_browser (about:blank + minimize). Now waits for the btn-primary
Continue button (original safe indicator) and wraps both dropdown selections
in try/except so the step never fails due to a missed dropdown.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 22:26:21 -04:00
2026-04-04 22:13:55 -04:00
2026-04-04 22:13:55 -04:00

Dental Manager - Starter

A monorepo setup to manage both Backend and Frontend of the Dental Manager application.

🖥️ Setup Guide (Fresh Machine)

Follow these steps in order after cloning the repository.

Step 1 — Clone the repository

git clone <your-repo-url>
cd DentalManagementMHAprilgg

Step 2 — Install Node.js

Required to run the Backend and Frontend.

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

# Verify
node -v   # should print v20.x.x
npm -v

Step 3 — Install Python

Required to run the Selenium and OCR services.

sudo apt-get install -y python3 python3-pip python3-venv

# Verify
python3 --version   # should print 3.10 or higher

Step 4 — Install Chrome

Required for the Selenium service to control a browser.

wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt-get update
sudo apt-get install -y google-chrome-stable

# Verify
google-chrome --version

The webdriver-manager package (included in requirements.txt) automatically downloads the matching ChromeDriver — no manual driver setup needed.

Step 5 — Install PostgreSQL

Primary database for the application.

sudo apt-get install -y postgresql postgresql-contrib
sudo systemctl enable postgresql
sudo systemctl start postgresql

# Create the database the app uses
sudo -u postgres psql -c "CREATE DATABASE dentalapp OWNER postgres;"

# Set the postgres user password to match packages/db/.env
sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD 'mypassword';"

Enable password authentication over TCP

By default Debian uses scram-sha-256 or peer auth for local connections, which blocks password login. Switch to md5:

sudo sed -i 's/scram-sha-256/md5/g' /etc/postgresql/*/main/pg_hba.conf
sudo systemctl restart postgresql

Verify

psql -U postgres -d dentalapp -h 127.0.0.1 -W
# Enter: mypassword
# You should see:  dentalapp=#

The DATABASE_URL in packages/db/.env is already set to:

DATABASE_URL="postgresql://postgres:mypassword@localhost:5432/dentalapp"

Step 6 — Install Redis

Used as the job queue for Selenium and OCR background tasks.

sudo apt-get install -y redis-server
sudo systemctl enable redis-server
sudo systemctl start redis-server

# Verify
redis-cli ping   # should print: PONG

Step 7 — Install Node.js dependencies

npm install

Step 8 — Install Python dependencies

Python dependencies are installed automatically by npm install (Step 7) via each service's postinstall script. Each service creates its own .venv virtual environment — no manual pip commands needed.

This approach is required on Debian 13+ where system-wide pip installs are blocked (PEP 668).

Step 9 — Set up environment variables

Copy the .env.example files and fill in the required values.

npm run setup:env

Step 9a — Set the Cloudflare subdomain for this office

After running npm run setup:env, open the two .env files and fill in this office's Cloudflare subdomain.

apps/Frontend/.env

VITE_CLOUDFLARE_HOST=yoursubdomain.mydentalofficemanagement.com

apps/Backend/.env

CLOUDFLARE_HOST=yoursubdomain.mydentalofficemanagement.com
FRONTEND_URLS=http://localhost:3000,http://yoursubdomain.mydentalofficemanagement.com,https://yoursubdomain.mydentalofficemanagement.com

Replace yoursubdomain with this office's actual subdomain (e.g. summitdentalcare).

If you skip this step, Cloudflare tunnel access will not work. LAN access still works without it.

Step 10 — Set up the database

# Run migrations
npm run db:migrate

# Generate Prisma types
npm run db:generate

# Insert seed data
npm run db:seed

Step 11 — Configure nginx

The repo includes nginx.conf in the project root. Install it as the active site config:

sudo cp nginx.conf /etc/nginx/sites-available/dental-app
sudo ln -sf /etc/nginx/sites-available/dental-app /etc/nginx/sites-enabled/dental-app
sudo nginx -t && sudo systemctl reload nginx

Important: The /api/ location block must include proxy_set_header Authorization $http_authorization; Without it, nginx strips the Authorization header and the backend returns "Access denied. No token provided."

Step 12 — Run the app

Open two terminals:

Terminal 1 — Backend + Frontend:

npm run dev

On first boot the server automatically seeds all AI chat templates, SMS templates, and greeting messages for every user — no manual configuration needed.

Terminal 2 — Selenium service:

cd apps/SeleniumService
.venv/bin/python3 agent.py

Step 13 — Create desktop shortcuts (optional)

Instead of opening two terminals manually every time, you can create desktop shortcuts that start and stop all services with a single double-click.

Run this once after cloning and installing:

bash ~/Desktop/DentalManagementMH06/setup-desktop-shortcut.sh

Two shortcuts will appear on your desktop:

  • Dental App — starts the app. Double-clicking it opens:
    • A terminal running npm run dev (Backend + Frontend + Python services)
    • A terminal running the Selenium service
  • Stop Dental App — stops all services and frees all ports (5000, 5001, 5002, 5003, 3000/3001)

No username or path editing needed — the script automatically detects the current user's home folder.


📖 Developer Documentation


This is a Turborepo. What's inside?

Apps and Packages

  • apps/Backend — Express.js API server
  • apps/Frontend — React + Vite frontend
  • apps/SeleniumService — Python FastAPI service for browser automation (insurance eligibility, claims)
  • apps/PaymentOCRService — Python service for payment OCR extraction
  • @repo/eslint-config — shared ESLint configuration
  • @repo/typescript-config — shared tsconfig.jsons

Each package/app is 100% TypeScript (except the Python services).

Utilities


Cloudflare Tunnel Setup (Remote Access per Office)

This connects each office's local app to a public subdomain via a Cloudflare Tunnel — no port forwarding needed, local network access is unchanged.

How it works:

  • Local network: other office PCs reach the app directly at http://<machine-ip>:3000
  • Internet: anyone reaches the app via https://<subdomain>.mydentalofficemanagement.com
  • Both paths hit the same app simultaneously with no conflict

Step 1 — Add domain to Cloudflare (done once for all offices)

Skip this step if the domain is already on Cloudflare.

  1. Go to dash.cloudflare.comAdd a site → enter mydentalofficemanagement.com (free plan)
  2. Cloudflare scans existing DNS records — review and keep them
  3. Cloudflare gives you 2 nameservers (e.g. holly.ns.cloudflare.com, amir.ns.cloudflare.com)
  4. Log into Ionos → replace the domain's nameservers with Cloudflare's two
  5. Wait 1030 min → Cloudflare emails you when active
  6. DNSSEC: if not purchased on Ionos, it was never enabled — nothing to turn off

Step 2 — Install cloudflared on the office PC

Run this in a terminal on the office machine:

curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb
cloudflared --version

Step 3 — Authenticate with Cloudflare

cloudflared tunnel login

A browser window opens → click mydentalofficemanagement.com → terminal shows success and saves a certificate to ~/.cloudflared/cert.pem.

Step 4 — Create a tunnel for this office

Use a unique tunnel name per office:

cloudflared tunnel create <office-tunnel-name>
# Example: cloudflared tunnel create summit-dental-app

Note the tunnel ID (UUID) printed — you need it in Step 5.

Step 5 — Create the config file

sudo mkdir -p /etc/cloudflared
sudo nano /etc/cloudflared/config.yml

Paste (replace tunnel ID, credentials path, and subdomain for this office):

tunnel: <tunnel-ID>
credentials-file: /home/ee/.cloudflared/<tunnel-ID>.json

ingress:
  - hostname: <subdomain>.mydentalofficemanagement.com
    service: http://localhost:3000
  - service: http_status:404

Save: Ctrl+O → Enter → Ctrl+X.

Step 6 — Route DNS for this office's subdomain

cloudflared tunnel route dns <office-tunnel-name> <subdomain>.mydentalofficemanagement.com

Step 7 — Install as a system service (auto-starts on boot)

sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sudo systemctl status cloudflared

Step 8 — Allow the subdomain in Vite

In apps/Frontend/vite.config.js, add the subdomain to server.allowedHosts so Vite does not block external requests.

Step 9 — Allow the subdomain in backend CORS

In apps/Backend, add the subdomain URL to the allowed CORS origins so login and API calls work from the public URL.


Example — Adding Summit Dental Care

Office subdomain: summitdentalcare.mydentalofficemanagement.com

Step 1: Already done (domain is on Cloudflare).

Step 2: Install cloudflared on Summit Dental's PC.

Step 3: Run cloudflared tunnel login on Summit Dental's PC.

Step 4:

cloudflared tunnel create summit-dental-app
# Example output: Created tunnel summit-dental-app with id a1b2c3d4-...

Step 5 — /etc/cloudflared/config.yml:

tunnel: a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx
credentials-file: /home/ee/.cloudflared/a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json

ingress:
  - hostname: summitdentalcare.mydentalofficemanagement.com
    service: http://localhost:3000
  - service: http_status:404

Step 6:

cloudflared tunnel route dns summit-dental-app summitdentalcare.mydentalofficemanagement.com

Step 7:

sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared

Step 8: Add summitdentalcare.mydentalofficemanagement.com to allowedHosts in vite.config.js.

Step 9: Add https://summitdentalcare.mydentalofficemanagement.com to backend CORS allowed origins.


Multi-office overview

Each office runs its own cloudflared tunnel on its own PC. Ports never conflict because each PC is a separate machine.

Office Local access Public access Tunnel name
Community Dentists of Lowell http://192.168.1.236:3000 https://communitydentistsoflowell.mydentalofficemanagement.com dental-app
Summit Dental Care http://<its-ip>:3000 https://summitdentalcare.mydentalofficemanagement.com summit-dental-app
Next office http://<its-ip>:3000 https://<subdomain>.mydentalofficemanagement.com <office>-app

License Key Generator

The license key generator is a private tool that lives only on your dev PC. Use it to generate a new license key for any office every 3 months.

Location: /home/ff/Desktop/LicenseGenerator/

Generate a 3-month key (default):

node /home/ff/Desktop/LicenseGenerator/generate-license.js

Generate a key with a custom duration:

node /home/ff/Desktop/LicenseGenerator/generate-license.js --months=6

Example output:

=== Dental App License Key ===

License Key: DENTAL-8ED7AAEF3E0CA008D98CC1E0-2026-08-26
Expires:     2026-08-26
Duration:    3 month(s)

Paste this key into the Activation page in the app.

Workflow:

  1. Office pays renewal fee
  2. Run the generator script above
  3. Copy the License Key
  4. Paste it into the Activation page in the app (via RustDesk or in person)
  5. Record the key, office name, expiry, and payment in your records

Important — secret.key:

  • /home/ff/Desktop/LicenseGenerator/secret.key is the private secret used to sign all keys
  • Back it up on a USB drive or password manager
  • If lost, all existing keys become invalid and new keys must be issued to all offices

Claude Code Memory

Claude Code (the AI assistant used to build this project) stores its memory locally on the PC. This memory contains project context, architecture decisions, feature history, and working preferences — allowing Claude to pick up where it left off in new sessions.

Memory location:

/home/ff/.claude/projects/-home-ff-Desktop-DentalManagementMH06/memory/

To copy to a new PC:

  1. On the old PC, copy the memory folder:
cp -r /home/ff/.claude/projects/-home-ff-Desktop-DentalManagementMH06/memory/ /media/usb/claude-memory-backup/
  1. On the new PC, recreate the directory and paste:
mkdir -p /home/ff/.claude/projects/-home-ff-Desktop-DentalManagementMH06/memory/
cp -r /media/usb/claude-memory-backup/* /home/ff/.claude/projects/-home-ff-Desktop-DentalManagementMH06/memory/

The memory is plain markdown files and can also be copied manually via a USB drive or file manager. Enable "show hidden files" (Ctrl+H) in the file manager to see the .claude folder.

Description
No description provided
Readme 157 MiB
Languages
JavaScript 58.5%
TypeScript 37.3%
Python 4.2%