diff --git a/view-doc.php/parker-view-doc.php b/view-doc.php similarity index 100% rename from view-doc.php/parker-view-doc.php rename to view-doc.php diff --git a/view-doc.php/parker-admin.php b/view-doc.php/parker-admin.php deleted file mode 100644 index 05cfcce..0000000 --- a/view-doc.php/parker-admin.php +++ /dev/null @@ -1,2233 +0,0 @@ -prepare("INSERT INTO admin_tokens (token, expires_at) VALUES (?, ?)") - ->execute([$token, date('Y-m-d H:i:s', time() + 86400)]); - db()->exec("DELETE FROM admin_tokens WHERE expires_at < NOW()"); - return $token; -} -function _verifyToken(string $token): bool { - if (!preg_match('/^[a-f0-9]{64}$/', $token)) return false; - $stmt = db()->prepare("SELECT token FROM admin_tokens WHERE token=? AND expires_at > NOW()"); - $stmt->execute([$token]); - return (bool)$stmt->fetch(); -} - -$isAjax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) || (($_SERVER['HTTP_ACCEPT'] ?? '') === 'application/json'); - -// ── Auth ────────────────────────────────────────────────────────────────────── -if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'login') { - if (($_POST['username'] ?? '') === ADMIN_USER && password_verify($_POST['password'] ?? '', ADMIN_PASS)) { - $t = _createToken(); - header('Location: /admin/?_t=' . $t); - } else { - header('Location: /admin/?err=1'); - } - exit; -} -$rawToken = preg_replace('/[^a-f0-9]/', '', $_GET['_t'] ?? $_POST['_t'] ?? ''); -if (($_GET['action'] ?? '') === 'logout') { - if ($rawToken) db()->prepare("DELETE FROM admin_tokens WHERE token=?")->execute([$rawToken]); - header('Location: /admin/'); exit; -} -$authed = $rawToken !== '' && _verifyToken($rawToken); -$token = $authed ? $rawToken : ''; - -// ── AJAX handlers ───────────────────────────────────────────────────────────── -if ($isAjax && !$authed) { - http_response_code(401); - header('Content-Type: application/json'); - echo json_encode(['error'=>'Session expired. Please log in again.']); - exit; -} -if ($isAjax) { - header('Content-Type: application/json'); - $action = $_POST['action'] ?? $_GET['action'] ?? ''; - - if ($action === 'update_status') { - $id = (int)($_POST['id'] ?? 0); - $status = $_POST['status'] ?? ''; - $allowed = ['pending','confirmed','completed','cancelled']; - if ($id && in_array($status, $allowed)) { - db()->prepare("UPDATE bookings SET status=? WHERE id=?")->execute([$status, $id]); - echo json_encode(['ok'=>true]); - } else { echo json_encode(['error'=>'Invalid']); } - exit; - } - - if ($action === 'save_admin_notes') { - $id = (int)($_POST['id'] ?? 0); - $notes = substr(trim($_POST['notes'] ?? ''), 0, 1000); - db()->prepare("UPDATE bookings SET admin_notes=? WHERE id=?")->execute([$notes, $id]); - echo json_encode(['ok'=>true]); - exit; - } - - if ($action === 'toggle_requirement') { - $id = (int)($_POST['id'] ?? 0); - $field = $_POST['field'] ?? ''; - $allowed_fields = ['insurance_verified','deposit_received','license_verified','helmet_provided','safety_course','operational_course']; - if ($id && in_array($field, $allowed_fields)) { - $stmt = db()->prepare("SELECT `{$field}` FROM bookings WHERE id=?"); - $stmt->execute([$id]); - $current = (int)$stmt->fetchColumn(); - $new = $current ? 0 : 1; - db()->prepare("UPDATE bookings SET `{$field}`=? WHERE id=?")->execute([$new, $id]); - echo json_encode(['ok'=>true,'value'=>$new]); - } else { echo json_encode(['error'=>'Invalid']); } - exit; - } - - if ($action === 'send_reminder') { - $id = (int)($_POST['id'] ?? 0); - $keys = array_filter(explode(',', $_POST['items'] ?? '')); - $stmt = db()->prepare("SELECT * FROM bookings WHERE id=?"); - $stmt->execute([$id]); - $b = $stmt->fetch(); - if (!$b) { echo json_encode(['error'=>'Not found']); exit; } - - $pkg = PACKAGES[$b['package']] ?? ['label' => $b['package']]; - $dateLabel = date('F j, Y', strtotime($b['rental_date'])); - $ref = $b['booking_ref']; - - $itemDefs = [ - 'waiver' => [ - 'label' => 'Sign Your Rental Agreement', - 'detail' => 'Your digital rental agreement still needs to be signed before your pickup. It only takes a minute and can be done on any device — no printer required.', - 'cta' => "
Sign Agreement →
", - ], - 'insurance' => [ - 'label' => 'Proof of Personal Auto Insurance', - 'detail' => 'You\'ll need to show proof of valid personal auto insurance at pickup. To speed things up, you can upload it now — a photo of your insurance card is fine.', - 'cta' => "
Upload Insurance Card →
", - ], - 'deposit' => [ - 'label' => 'Balance Due at Pickup', - 'detail' => 'Your $' . number_format(DEPOSIT_AMOUNT, 2) . ' deposit hold has been placed on your card. The remaining balance is due at pickup — cash or card accepted. Your deposit hold will be released upon safe return of the vehicle.', - 'cta' => '', - ], - 'license' => [ - 'label' => "Valid Driver's License", - 'detail' => "Please bring your valid driver's license to pickup — we'll verify it before you head out. You can also upload a photo in advance to keep things moving at arrival.", - 'cta' => "
Upload License Photo →
", - ], - ]; - - $rowsHtml = ''; - $n = 1; - foreach ($keys as $key) { - if (!isset($itemDefs[$key])) continue; - $d = $itemDefs[$key]; - $rowsHtml .= " - - - {$n} - - - " . htmlspecialchars($d['label']) . " -

" . htmlspecialchars($d['detail']) . "

- {$d['cta']} - -"; - $n++; - } - - if (!$rowsHtml) { echo json_encode(['error'=>'No items selected']); exit; } - - $html = " -
-
-

Parker County Slingshot Rentals

-
-
-

Almost Ready — A Few Things Before Pickup

-

Hey " . htmlspecialchars($b['name']) . ", your " . htmlspecialchars($pkg['label']) . " rental on {$dateLabel} is coming up! (Ref: {$ref})

-

To make sure pickup goes smoothly, here's what still needs to be taken care of:

- {$rowsHtml}
-
-

Questions? Call or text (817) 555-0199 or reply to this email — we're happy to help.

-
-

Ride on,
The Parker County Slingshot Team

-
-
-

© " . date('Y') . " Parker County Slingshot Rentals — Weatherford, TX

-
-
"; - - $sent = sendEmail($b['email'], $b['name'], "Action Needed Before Your Rental — {$ref}", $html); - if ($sent) { - echo json_encode(['ok'=>true]); - } else { - echo json_encode(['ok'=>false,'error'=>'Email service not configured. Set MAILJET_API_KEY and MAILJET_SECRET_KEY in db.php.']); - } - exit; - } - - if ($action === 'square_capture') { - $id = (int)($_POST['id'] ?? 0); - $stmt = db()->prepare("SELECT square_payment_id FROM bookings WHERE id=?"); - $stmt->execute([$id]); - $b = $stmt->fetch(); - $pid = $b['square_payment_id'] ?? ''; - if (!$pid) { echo json_encode(['error'=>'No payment on file']); exit; } - $resp = squareApi('POST', "/payments/{$pid}/complete"); - if (($resp['payment']['status'] ?? '') === 'COMPLETED') { - db()->prepare("UPDATE bookings SET square_payment_status='COMPLETED', deposit_paid=?, deposit_received=1 WHERE id=?") - ->execute([DEPOSIT_AMOUNT, $id]); - echo json_encode(['ok'=>true,'status'=>'COMPLETED']); - } else { - echo json_encode(['error' => $resp['errors'][0]['detail'] ?? 'Capture failed']); - } - exit; - } - - if ($action === 'square_void') { - $id = (int)($_POST['id'] ?? 0); - $stmt = db()->prepare("SELECT square_payment_id FROM bookings WHERE id=?"); - $stmt->execute([$id]); - $b = $stmt->fetch(); - $pid = $b['square_payment_id'] ?? ''; - if (!$pid) { echo json_encode(['error'=>'No payment on file']); exit; } - $resp = squareApi('POST', "/payments/{$pid}/cancel"); - if (($resp['payment']['status'] ?? '') === 'CANCELED') { - db()->prepare("UPDATE bookings SET square_payment_status='CANCELED', status='cancelled' WHERE id=?")->execute([$id]); - echo json_encode(['ok'=>true,'status'=>'CANCELED']); - } else { - echo json_encode(['error' => $resp['errors'][0]['detail'] ?? 'Void failed']); - } - exit; - } - - if ($action === 'square_refund') { - $id = (int)($_POST['id'] ?? 0); - $stmt = db()->prepare("SELECT square_payment_id, deposit_paid FROM bookings WHERE id=?"); - $stmt->execute([$id]); - $b = $stmt->fetch(); - $pid = $b['square_payment_id'] ?? ''; - if (!$pid) { echo json_encode(['error'=>'No payment on file']); exit; } - $cents = (int)(((float)($b['deposit_paid'] ?: DEPOSIT_AMOUNT)) * 100); - $resp = squareApi('POST', '/refunds', [ - 'idempotency_key' => $pid . '-refund-' . time(), - 'payment_id' => $pid, - 'amount_money' => ['amount' => $cents, 'currency' => 'USD'], - 'reason' => 'Security deposit refund — booking returned in good condition', - ]); - if (!empty($resp['refund']['id'])) { - db()->prepare("UPDATE bookings SET square_payment_status='REFUNDED', square_refund_id=?, deposit_paid=0, status='cancelled' WHERE id=?") - ->execute([$resp['refund']['id'], $id]); - echo json_encode(['ok'=>true,'status'=>'REFUNDED']); - } else { - echo json_encode(['error' => $resp['errors'][0]['detail'] ?? 'Refund failed']); - } - exit; - } - - if ($action === 'charge_balance') { - $id = (int)($_POST['id'] ?? 0); - $stmt = db()->prepare("SELECT * FROM bookings WHERE id=?"); - $stmt->execute([$id]); - $b = $stmt->fetch(); - if (!$b) { echo json_encode(['error'=>'Not found']); exit; } - $cardId = $b['square_card_id'] ?? ''; - $customerId = $b['square_customer_id'] ?? ''; - if (!$cardId) { echo json_encode(['error'=>'No card on file']); exit; } - $balance = (float)$b['amount'] - DEPOSIT_AMOUNT; - if ($balance <= 0) { echo json_encode(['error'=>'No balance due']); exit; } - $cents = (int)($balance * 100); - $payBody = [ - 'source_id' => $cardId, - 'idempotency_key' => $b['booking_ref'] . '-bal-' . time(), - 'amount_money' => ['amount' => $cents, 'currency' => 'USD'], - 'autocomplete' => true, - 'location_id' => SQUARE_LOCATION_ID, - 'note' => "Balance charge — booking " . $b['booking_ref'], - 'reference_id' => $b['booking_ref'], - ]; - if ($customerId) $payBody['customer_id'] = $customerId; - $resp = squareApi('POST', '/payments', $payBody); - if (!empty($resp['payment']['id'])) { - $pid = $resp['payment']['id']; - db()->prepare("UPDATE bookings SET square_payment_status='BAL_COMPLETED', deposit_received=1 WHERE id=?") - ->execute([$id]); - echo json_encode(['ok'=>true,'payment_id'=>$pid,'amount'=>$balance]); - } else { - $errCode = $resp['errors'][0]['code'] ?? ''; - $errMsg = match($errCode) { - 'CARD_DECLINED','CARD_DECLINED_VERIFICATION_REQUIRED' => 'Card was declined.', - 'INSUFFICIENT_FUNDS' => 'Insufficient funds.', - default => $resp['errors'][0]['detail'] ?? 'Charge failed.', - }; - echo json_encode(['error'=>$errMsg]); - } - exit; - } - - if ($action === 'update_card') { - $id = (int)($_POST['id'] ?? 0); - $nonce = trim($_POST['nonce'] ?? ''); - if (!$id || !$nonce) { echo json_encode(['error'=>'Missing data']); exit; } - $stmt = db()->prepare("SELECT * FROM bookings WHERE id=?"); - $stmt->execute([$id]); - $b = $stmt->fetch(); - if (!$b) { echo json_encode(['error'=>'Not found']); exit; } - - // Disable old card if exists - $oldCardId = $b['square_card_id'] ?? ''; - if ($oldCardId) squareApi('POST', "/cards/{$oldCardId}/disable"); - - // Get or create Square customer - $customerId = $b['square_customer_id'] ?? ''; - if (!$customerId) { - $custResp = squareApi('POST', '/customers', [ - 'idempotency_key' => $b['booking_ref'] . '-cust2', - 'given_name' => $b['name'], - 'email_address' => $b['email'], - 'phone_number' => $b['phone'] ?: null, - 'reference_id' => $b['booking_ref'], - ]); - $customerId = $custResp['customer']['id'] ?? null; - } - - // Create new card on file - $cardBody = ['idempotency_key'=>$b['booking_ref'].'-card-'.time(),'source_id'=>$nonce,'card'=>['cardholder_name'=>$b['name']]]; - if ($customerId) $cardBody['card']['customer_id'] = $customerId; - $cardResp = squareApi('POST', '/cards', $cardBody); - $newCardId = $cardResp['card']['id'] ?? null; - $last4 = $cardResp['card']['last_4'] ?? null; - $brand = $cardResp['card']['card_brand'] ?? null; - if (!$newCardId) { - echo json_encode(['error' => $cardResp['errors'][0]['detail'] ?? 'Could not save card']); exit; - } - db()->prepare("UPDATE bookings SET square_customer_id=?, square_card_id=?, card_last4=?, card_brand=? WHERE id=?") - ->execute([$customerId, $newCardId, $last4, $brand, $id]); - echo json_encode(['ok'=>true,'last4'=>$last4,'brand'=>$brand]); - exit; - } - - if ($action === 'mark_returned') { - $id = (int)($_POST['id'] ?? 0); - if (!$id) { echo json_encode(['error'=>'Invalid']); exit; } - $stmt = db()->prepare("SELECT * FROM bookings WHERE id=?"); - $stmt->execute([$id]); - $b = $stmt->fetch(); - if (!$b) { echo json_encode(['error'=>'Not found']); exit; } - // Mark returned, set completed, wipe card data - db()->prepare("UPDATE bookings SET slingshot_returned=1, returned_at=NOW(), status='completed', - square_card_id=NULL, card_last4=NULL, card_brand=NULL, square_customer_id=NULL - WHERE id=?") - ->execute([$id]); - echo json_encode(['ok'=>true]); - exit; - } - - if ($action === 'block_date') { - $date = $_POST['date'] ?? ''; - $reason = substr($_POST['reason'] ?? '', 0, 200); - if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) { - db()->prepare("INSERT IGNORE INTO blocked_dates (block_date, reason) VALUES (?,?)")->execute([$date, $reason]); - $newId = (int)db()->lastInsertId(); - echo json_encode(['ok'=>true, 'id'=>$newId, 'date'=>$date, 'reason'=>$reason]); - } else { echo json_encode(['error'=>'Invalid date']); } - exit; - } - if ($action === 'unblock_date') { - $id = (int)($_POST['id'] ?? 0); - db()->prepare("DELETE FROM blocked_dates WHERE id=?")->execute([$id]); - echo json_encode(['ok'=>true]); - exit; - } - - if ($action === 'update_email') { - $id = (int)($_POST['id'] ?? 0); - $email = trim($_POST['email'] ?? ''); - if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { echo json_encode(['error'=>'Invalid email']); exit; } - db()->prepare("UPDATE bookings SET email=? WHERE id=?")->execute([$email, $id]); - echo json_encode(['ok'=>true,'email'=>$email]); - exit; - } - - if ($action === 'resend_confirmation') { - $id = (int)($_POST['id'] ?? 0); - $email = trim($_POST['email'] ?? ''); - $stmt = db()->prepare("SELECT * FROM bookings WHERE id=?"); - $stmt->execute([$id]); - $b = $stmt->fetch(); - if (!$b) { echo json_encode(['error'=>'Not found']); exit; } - if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $email = $b['email']; - $pkg = PACKAGES[$b['package']] ?? ['label'=>$b['package'],'price'=>$b['amount']]; - $dateLabel = date('F j, Y', strtotime($b['rental_date'])); - $ref = $b['booking_ref']; - $amtLabel = '$' . number_format((float)$b['amount'], 2); - $depLabel = '$' . number_format(DEPOSIT_AMOUNT, 2); - $balLabel = '$' . number_format(max(0, (float)$b['amount'] - DEPOSIT_AMOUNT), 2); - $confirmHtml = "
-
-

Parker County Slingshot Rentals

-
-
-

Booking Confirmation

-

Hey " . htmlspecialchars($b['name']) . ", here's a copy of your booking confirmation.

-
-

Booking Reference

-

{$ref}

-

Package: " . htmlspecialchars($pkg['label']) . "

-

Date: {$dateLabel}

-

Total: {$amtLabel}

-

Deposit hold: {$depLabel}

-

Balance at pickup: {$balLabel}

-
-
-

Sign Your Rental Agreement

- Sign Rental Agreement → -
-

Questions? Call or text " . ADMIN_PHONE . " or reply to this email.

-

Ride on,
The Parker County Slingshot Team

-
-
-

© " . date('Y') . " Parker County Slingshot Rentals — Weatherford, TX

-
-
"; - $sent = sendEmail($email, $b['name'], "Booking Confirmation {$ref} — Parker County Slingshot Rentals", $confirmHtml); - echo json_encode($sent ? ['ok'=>true,'email'=>$email] : ['error'=>'Email send failed — check Mailjet credentials']); - exit; - } - - if ($action === 'customer_save') { - $cid = (int)($_POST['id'] ?? 0); - $name = substr(trim($_POST['name'] ?? ''), 0, 150); - $email = trim($_POST['email'] ?? ''); - $phone = substr(trim($_POST['phone'] ?? ''), 0, 30); - $dob = preg_match('/^\d{4}-\d{2}-\d{2}$/', $_POST['dob'] ?? '') ? $_POST['dob'] : null; - $addr = substr(trim($_POST['address'] ?? ''), 0, 500); - $notes = substr(trim($_POST['notes'] ?? ''), 0, 1000); - $active = (int)($_POST['is_active'] ?? 1); - if (!$name || !filter_var($email, FILTER_VALIDATE_EMAIL)) { - echo json_encode(['error' => 'Name and valid email are required']); exit; - } - if ($cid) { - db()->prepare("UPDATE pcs_customers SET name=?,email=?,phone=?,dob=?,address=?,notes=?,is_active=? WHERE id=?") - ->execute([$name,$email,$phone,$dob,$addr,$notes,$active,$cid]); - } else { - db()->prepare("INSERT INTO pcs_customers (name,email,phone,dob,address,notes,is_active) VALUES (?,?,?,?,?,?,?)") - ->execute([$name,$email,$phone,$dob,$addr,$notes,1]); - $cid = (int)db()->lastInsertId(); - } - echo json_encode(['ok'=>true,'id'=>$cid]); exit; - } - - if ($action === 'customer_delete') { - $cid = (int)($_POST['id'] ?? 0); - if ($cid) db()->prepare("DELETE FROM pcs_customers WHERE id=?")->execute([$cid]); - echo json_encode(['ok'=>true]); exit; - } - - exit; -} - -// ── Login page ───────────────────────────────────────────────────────────────── -if (!$authed) { ?> - - - - -Admin Login — Parker County Slingshot Rentals - - - -
-

Parker Admin

-

Slingshot Rentals Management

- -

Invalid username or password.

- -
- - - - - - -
-
- - -query("SELECT * FROM bookings ORDER BY rental_date ASC, created_at DESC")->fetchAll(); -$blocked = db()->query("SELECT * FROM blocked_dates ORDER BY block_date ASC")->fetchAll(); -$customers = db()->query(" - SELECT c.* - FROM pcs_customers c ORDER BY c.created_at DESC -")->fetchAll(); -// Group bookings by customer email in PHP (avoids cross-table collation issues) -$bookingsByEmail = []; -foreach ($bookings as $_b) { - $bookingsByEmail[strtolower(trim($_b['email']))][] = $_b; -} - -$stats = db()->query(" - SELECT - COUNT(*) AS total, - SUM(status='pending') AS pending, - SUM(status='confirmed') AS confirmed, - SUM(status='completed') AS completed, - SUM(status='cancelled') AS cancelled, - SUM(CASE WHEN status IN ('confirmed','completed') THEN amount ELSE 0 END) AS revenue, - SUM(waiver_signed) AS waivers_signed, - SUM(insurance_verified) AS insurance_done, - SUM(deposit_received) AS deposits_done - FROM bookings -")->fetch(); -?> - - - - -Admin — Parker County Slingshot Rentals - - - -
-

Parker County Slingshot — Admin

- Sign Out -
-
- - -
-
Total Bookings
-
Pending
-
Confirmed
-
Completed
-
$
Revenue
-
Waivers Signed
-
Insurance OK
-
Deposits Rcvd
-
- - -
-
-

Bookings

-
- - - - - -
-
- - -
No bookings found.
- -
- - - - - - - - - - - - - - - $b['package']]; - - // Determine each step's state - $stepConfirmed = in_array($b['status'], ['confirmed','completed']); - $stepWaiver = (bool)$b['waiver_signed']; - $stepInsurance = (bool)$b['insurance_verified']; - $insFile = $b['insurance_file'] ?? ''; - $stepDeposit = (bool)$b['deposit_received']; - $stepLicense = (bool)$b['license_verified']; - $licFile = $b['license_file'] ?? ''; - $stepHelmet = (bool)$b['helmet_provided']; - $stepSafety = (bool)$b['safety_course']; - $stepOps = (bool)$b['operational_course']; - $stepReturned = (bool)$b['slingshot_returned']; - - // Dot colors: done=green, if cancelled skip all - $cancelled = $b['status'] === 'cancelled'; - $dotClass = function($done) use ($cancelled) { - if ($cancelled) return 'dot-skip'; - return $done ? 'dot-done' : 'dot-pending'; - }; - - $allDone = $stepConfirmed && $stepWaiver && $stepInsurance && $stepDeposit && $stepLicense && $stepHelmet && $stepSafety && $stepOps && $stepReturned; - $pendingCount = ($cancelled ? 0 : ( - (!$stepConfirmed?1:0)+(!$stepWaiver?1:0)+(!$stepInsurance?1:0)+(!$stepDeposit?1:0)+(!$stepLicense?1:0)+ - (!$stepHelmet?1:0)+(!$stepSafety?1:0)+(!$stepOps?1:0)+(!$stepReturned?1:0) - )); - ?> - - - - - - - - - - - - - - - - - -
CustomerRental DatePackageAmountStatusProgressSubmitted
- - -
- -
- - -
- -
$ - - -
-
-
-
-
-
-
-
-
-
-
- - pending - - All done ✓ - -
-
- - -
-

Customer

-
-
-
- -
- -
Package
-
-
$
-
Rental Date
-
- -
Customer Message
-
- - -
-
Admin Notes
- - -
-
- - -
-

Booking Flow

-
- - -
-
-
- Booking Submitted - -
-
- - -
-
- -
-
- Booking Confirmed - - Confirmed — status: - Cancelled - Awaiting confirmation — change status above - - -
-
- - -
-
- -
-
- Rental Waiver Signed - - - Signed by - on - N/A - Not yet signed - - - - - -
-
- - -
-
- -
-
- Proof of Insurance Received - - - - - 📄 View Submitted Doc ↗ - - -
- - - - -
- -
-
- - -
-
- -
-
- Driver's License Verified - - - - - 📄 View Submitted Doc ↗ - - -
- - - - -
- -
-
- - - -
-
- -
-
- Deposit & Balance — $ held · $ at pickup - - - •••• on file - - - - - N/A - Balance charged to card on file - Captured — $ charged - Refunded — $ - Hold voided — no charge - Hold active — card authorized, not yet charged - Card was declined — contact customer - Deposit marked received (manual) - Pending — no card on file yet - - - -
- - - - - - - - - - - - - - - - - - -
- -
-
- - - -
- Pre-Departure Checklist -
- - -
-
- -
-
- DOT Helmet Provided & Fits - - - -
- -
-
-
- - -
-
- -
-
- Safety Course Completed - - - -
- -
-
-
- - -
-
- -
-
- Slingshot Operational Course Done - - - -
- -
-
-
- - -
-
- -
-
- Slingshot Returned — Close Out - - - Returned — booking closed, card data wiped - - Mark when slingshot is safely returned — closes booking & wipes card data - - - -
- -
- -
-
- - -
-
- - -
-

Send Reminder Email

-

Select what the customer still needs to do, then send them a nudge email with clear instructions.

- - -

Not applicable for cancelled bookings.

- -
-

Include in Reminder

-
- - - - -
- - -
- -
- Waiver Link - https://parkerslingshot.epictravelexpeditions.com/waiver.php?ref= -
- -
- Resend Confirmation Email -
- - -
- -
- -
- -
-
-
- -
- - -
-

Block Dates

-
-
- - -
-
- - -
- -
-
- -

No dates blocked.

- - -
- - - -
- - -
-
- - -
-
-

Customers

-
- - -
-
- - -
- -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - -
- -
- - -
No customers yet.
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameEmailPhoneBookingsStatusAdded
- - - - -
-
- -
- -
- - - - - - - -