# Contracts Admin MVP This is a single-file PHP admin and JSON API that lets you: - List all `.md` contracts stored in a `/contracts` folder - Edit a contract in a modal and save changes back to disk - Email a signing link to a client and mark the item as "sent" - Mark contracts as "signed" automatically from your existing `contract.php` page, or manually from the UI ## Quick start 1. Copy `contracts-admin.php` to a folder that is **not** public or is protected by HTTP auth. 2. Make sure your contracts live in a folder named `contracts` alongside this file. Each file is `clientid.md`. 3. Open the admin in a browser and log in with `admin / changeme`. 4. Click **Edit** to update an `.md`. Click **Email link** to send the client a link like: ``` https://modulosdesign.com.au/contracts/contract.php?clientid=3043 ``` The "sent" tick updates automatically on success. ## Configuration Open the top of `contracts-admin.php` and adjust: - `CONTRACTS_DIR` absolute path to your contracts folder - `BASE_URL` base URL where the public `contract.php` lives - `ADMIN_SHARED_SECRET` shared token used by `contract.php` to mark items as signed - `ADMIN_USER` and `ADMIN_PASS` for Basic Auth - Database: by default uses SQLite `contracts.sqlite`. You can override with `DB_DSN`, `DB_USER`, `DB_PASS` for MySQL or Postgres If you already have a `config.php` with `$pdo` or constants, keep it next to the file. It will be included automatically. ## Database The app auto-creates a table named `contract_status`. ```sql CREATE TABLE IF NOT EXISTS contract_status ( clientid TEXT PRIMARY KEY, sent INTEGER DEFAULT 0, sent_at TEXT NULL, signed INTEGER DEFAULT 0, signed_at TEXT NULL, last_email_to TEXT NULL, pdf_path TEXT NULL, signer_name TEXT NULL, signer_ip TEXT NULL ); ``` On MySQL, use `VARCHAR(64)` for `clientid` and `DATETIME` for timestamps. ## Hooking into your existing `contract.php` After the client signs and the PDF has been generated and emailed, call the admin endpoint to mark it signed. **Add this snippet near the end of your `contract.php` after a successful send:** ```php // Mark as signed in the admin database $clientid = $_GET['clientid'] ?? ''; $signerName = $clientName ?? null; // replace with your variable if available $pdfPath = $savedPdfPath ?? null; // replace with your PDF path if you store it $ch = curl_init('https://your-admin-url/contracts-admin.php'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, [ 'action' => 'mark_signed', 'secret' => ADMIN_SHARED_SECRET, // define the same secret in both places 'clientid' => $clientid, 'name' => $signerName, 'pdf' => $pdfPath, ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_exec($ch); curl_close($ch); ``` If you prefer direct DB access from `contract.php`, insert or update `contract_status` with `signed=1` instead. ## Email sending The MVP uses PHP `mail()` by default. If PHPMailer is present and SMTP constants are defined (`SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_SECURE`), it will send via SMTP. The email body is simple and friendly. Tweak it in `send_contract_email()`. ## Security - Protect this admin with HTTP Basic Auth or an allowlist in your reverse proxy - The signing webhook uses a shared secret - Filenames are validated to prevent path traversal ## Embedding You can embed the admin as an iframe inside Grav's admin route accessible only to you. Example shortcode: ```html ``` For better integration, use an Nginx rule to restrict by your IP or VPN. ## Styling and UX - Sticky header for the table - Search box filters by client id or last sent email - Buttons for edit, email, copy link, and open You can brand it by adding your logo and colors in the `