Full coverage report: https://OWNER.github.io/REPO/coverage-report/
A lightweight, mobile-first Progressive Web App scaffold using HTMX and Vite (as a static dev server and bundler). It provides basic screens and transitions only.
- Home, Jobs, Quotes, Invoices, Expenses, Clients, Assistant, Settings screens
- Bottom navigation and simple fade transitions
- HTMX partial loading with URL history
- PWA: manifest + service worker for offline shell
index.html– HTMX app shell and bottom navpages/– HTML partials for each screensrc/styles.css– mobile-first stylesmanifest.webmanifest– PWA manifestsw.js– service worker (cache shell + pages)
Note: There are some unused React files (src/main.jsx, etc.) from an earlier scaffold. The app does not use them.
- Install dependencies
npm install- Start dev server
npm run devVite will print a local URL (default http://localhost:5173). Open it in a mobile-sized viewport.
- Build for production
npm run build
npm run preview- The manifest is referenced from
/manifest.webmanifestand icons should be placed under/icons/. - The service worker is at
/sw.jsand registered via/src/swRegistration.js. - For full installability, add 192x192 and 512x512 PNG icons at:
icons/icon-192.pngicons/icon-512.pngicons/maskable-icon-192.pngicons/maskable-icon-512.png
- Hook up real data sources for each screen
- Implement voice assistant functionality
- Add offline data persistence (e.g., IndexedDB)
- Add theming and auth
This repo includes infrastructure to deploy:
- Backend (
backend/.NET API + SQLite) to Azure Container Apps (ACA) with a persistent Azure Files volume. - Frontend (
ai_mate_blazor/Blazor WASM) to Azure Static Web Apps (SWA) with a proxy to the backend.
Use Azure CLI to create a Resource Group, Storage Account + File Share (for DB persistence), and a Container Apps Environment. Replace placeholders as needed.
az login
az account set --subscription "<SUBSCRIPTION_ID>"
az group create -n rg-ai-mate -l westeurope
# Storage (Azure Files) for DB persistence
az storage account create -g rg-ai-mate -n aimatestorage$RANDOM -l westeurope --sku Standard_LRS
SA=$(az storage account list -g rg-ai-mate --query "[0].name" -o tsv)
SAKEY=$(az storage account keys list -g rg-ai-mate -n $SA --query "[0].value" -o tsv)
az storage share-rm create --resource-group rg-ai-mate --storage-account $SA --name aimate-db --quota 5
# Container Apps env
az containerapp env create -g rg-ai-mate -n cae-aimate -l westeuropePush to main to build/push the backend Docker image (see .github/workflows/backend.yml). The image will be available at ghcr.io/<owner>/<repo>/backend:latest.
Run the manual workflow "backend-aca-deploy" (Actions → backend-aca-deploy → Run workflow) with inputs:
image_ref:ghcr.io/<owner>/<repo>/backend:latestresource_group:rg-ai-matecontainerapp_name: e.g.,api-aimateenv_FRONTEND_ORIGINS:https://<your-frontend-domain>- (optional) seed ranges:
env_INVOICE_SEED_MIN/MAX,env_JOB_SEED_MIN/MAX
Required GitHub repository secrets for ACA deploy:
AZURE_CLIENT_ID,AZURE_TENANT_ID,AZURE_SUBSCRIPTION_ID(OIDC service principal for Azure Login)AIMATE_DB_PASSWORD(strong passphrase for encrypted DB; optional but recommended)
Notes:
- The app writes data to
/app/data. Mount an Azure Files volume when first creating the Container App (portal/CLI) so data persists. The workflow updates image/env; initial volume mount is a one-time portal/CLI step. - Seeding runs automatically on first boot, controlled by env vars:
INVOICE_SEED_MIN/MAX(defaults 250/450) andJOB_SEED_MIN/MAX(defaults 80/200).
Add repository secrets:
AZURE_STATIC_WEB_APPS_API_TOKEN(from SWA resource → Deployment tokens)BACKEND_URL(Container App FQDN such ashttps://api-aimate.<region>.azurecontainerapps.io)
Push to main (or manually run) the workflow .github/workflows/frontend_swa.yml. It will:
- Publish Blazor WASM to
publish_blazor/wwwroot - Inject SWA proxy config at
ai_mate_blazor/wwwroot/staticwebapp.config.jsonby replacing__BACKEND_URL__ - Upload to SWA
SWA will serve the frontend and proxy /api/* to your backend.
- Open the SWA URL (e.g.,
https://<yourapp>.azurestaticapps.net). - Dashboard should show realistic totals and charts.
- Backend health:
https://<backend-fqdn>/api/health.
- Reseed (dev only):
POST https://<backend-fqdn>/api/dev/reseed - Adjust seed volume without code changes via ACA env vars:
INVOICE_SEED_MIN/MAX,JOB_SEED_MIN/MAX. - CORS origins are controlled via
FRONTEND_ORIGINSenv in the backend.
- The workflow at
.github/workflows/dotnet-tests.ymlruns tests forai_mate_blazor.Testsandapi.Tests, collects coverage, generates HTML/Text/JSON badges, and enforces thresholds (95% line, 85% branch) globally and per-assembly (ai_mate_blazor,api). - On push to
main/master, thecoverage-report/is published to GitHub Pages for stable badge/report URLs. - To enable Pages: Settings → Pages → Source: GitHub Actions.
The backend uses per-company encrypted SQLite (SQLCipher) databases stored at backend/data/{companyId}.enc.db. The active company is identified by the X-Company-Id header (frontend sets this from localStorage['aimate_active_profile_id_v1']).
MASTER_KEY: Master secret used to derive per-company SQLCipher keys.ADMIN_API_KEY: Required to call admin/dev endpoints (send asX-Admin-Key).FRONTEND_ORIGINS: Comma-separated origins allowed by CORS in production.- Azure backup (optional):
AZURE_BLOB_CONN_STR: Azure Storage connection stringAZURE_BLOB_CONTAINER: Container for backups (e.g.,ai-mate-backups)BACKUP_RETENTION_DAYS: Optional, default 30 daysBACKUP_INTERVAL_MIN: Optional daily backup scheduler interval (>= 60)
POST /api/admin/backup: Create a zip ofbackend/data/and upload to Azure Blob (requires Azure env set). Triggers retention cleanup ifBACKUP_RETENTION_DAYSis set.POST /api/admin/restore?blob=backups/backup_YYYYMMDD_HHMMSS.zip: Restore from an Azure Blob backup.- Dev-only endpoints (also require
ENABLE_DEV_RESEED=true):POST /api/dev/backfill-company?from=<cid>&to=<cid>POST /api/dev/migrate-per-company
Global config sets X-Company-Id via use.extraHTTPHeaders. UI tests should also set the active company and onboarding completed in beforeEach:
await page.evaluate(() => {
localStorage.setItem('aimate_active_profile_id_v1','company-12343456');
localStorage.setItem('aimate_onboarding_completed_v1','1');
});We also include a Blazor WebAssembly PWA wrapper located at ai_mate_blazor/ with the same screens, branding, and PWA support.
dotnet run --project ai_mate_blazorThe dev server will print a local URL (e.g., http://localhost:5210).
Or start both (backend if present + frontend) with the helper script:
./scripts/start.sh
# override ports
BACKEND_PORT=5280 FRONTEND_PORT=5211 ./scripts/start.sh- Launch the iOS Simulator:
open -a Simulator
- Boot a device (if none is booted):
xcrun simctl boot "iPhone 16 Pro" - Open the app URL in the simulator Safari:
xcrun simctl openurl booted http://localhost:<FRONTEND_PORT>
- Add to Home Screen to test PWA install: Share → Add to Home Screen. Relaunch from the home screen to verify standalone mode, copper status bar, and that the install tip no longer shows.
Notes:
- The iOS install banner only appears on iOS Safari when not running in standalone mode and hides permanently after dismissal or installation.
- Safe-area insets are handled for the toolbar and bottom navigation.