# 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 `