| Server IP : 213.255.246.8 / Your IP : 216.73.217.138 Web Server : Apache System : Linux dublin.stapolin.com 5.14.0-362.18.1.el9_3.x86_64 #1 SMP PREEMPT_DYNAMIC Mon Jan 29 07:05:48 EST 2024 x86_64 User : stapolin ( 1019) PHP Version : 8.4.24 Disable Function : exec,passthru,shell_exec,system MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/stapolin/public_html/dash.stapolin.com/ |
Upload File : |
<?php
/**
* Client Detail Page
*/
require_once __DIR__ . '/includes/functions.php';
// Get client ID
$clientId = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if (!$clientId) {
redirect('clients.php');
}
// Get client details
try {
$db = getDb();
$client = $db->fetchOne("SELECT * FROM st_clients WHERE client_id = ?", [$clientId]);
if (!$client) {
redirect('clients.php');
}
// Get client's websites with update counts
$websites = $db->fetchAll(
"SELECT w.*,
(SELECT COUNT(*) FROM st_activity WHERE website_id = w.website_id AND object_type = 'Plugins') as plugin_count,
(SELECT COUNT(*) FROM st_activity WHERE website_id = w.website_id AND object_type = 'Themes') as theme_count,
(SELECT COUNT(*) FROM st_activity WHERE website_id = w.website_id AND object_type = 'Core') as core_count
FROM st_websites w
WHERE w.client_id = ?
ORDER BY w.website_url",
[$clientId]
);
// Get available websites not assigned to this client (for adding)
$availableWebsites = $db->fetchAll(
"SELECT * FROM st_websites WHERE client_id != ? OR client_id IS NULL ORDER BY website_url",
[$clientId]
);
} catch (Exception $e) {
$client = null;
$websites = [];
$availableWebsites = [];
}
$pageTitle = $client ? $client['client_name'] : 'Client Details';
require_once __DIR__ . '/includes/header.php';
// Handle success message
$successMessage = isset($_GET['success']) ? $_GET['success'] : null;
?>
<!-- Breadcrumb -->
<div class="flex items-center gap-2 text-sm text-muted-foreground mb-6">
<a href="clients.php" class="hover:text-foreground" data-testid="link-back-clients">Clients</a>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
<span class="text-foreground"><?= sanitize($client['client_name']) ?></span>
</div>
<?php if ($successMessage): ?>
<div class="mb-6 p-4 rounded-md bg-green-500/10 border border-green-500/20 text-green-600 dark:text-green-400" data-testid="alert-success">
<?= sanitize($successMessage) ?>
</div>
<?php endif; ?>
<!-- Client Info Card -->
<div class="card rounded-lg p-6 mb-6">
<div class="flex items-start justify-between gap-4">
<div class="flex items-center gap-4">
<div class="w-16 h-16 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0">
<span class="text-primary font-semibold text-2xl"><?= strtoupper(substr($client['client_name'], 0, 2)) ?></span>
</div>
<div>
<h2 class="text-2xl font-semibold mb-1" data-testid="text-client-name"><?= sanitize($client['client_name']) ?></h2>
<div class="flex items-center gap-4 text-sm text-muted-foreground">
<?php if (!empty($client['client_email'])): ?>
<span class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
</svg>
<?= sanitize($client['client_email']) ?>
</span>
<?php endif; ?>
<?php if (!empty($client['client_phone'])): ?>
<span class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>
</svg>
<?= sanitize($client['client_phone']) ?>
</span>
<?php endif; ?>
</div>
</div>
</div>
<div class="flex items-center gap-2">
<button onclick="document.getElementById('editClientModal').classList.remove('hidden')"
class="btn-secondary px-3 py-2 rounded-md text-sm font-medium flex items-center gap-2"
data-testid="button-edit-client">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit
</button>
</div>
</div>
<?php if (!empty($client['client_notes'])): ?>
<div class="mt-4 pt-4 border-t">
<p class="text-sm text-muted-foreground"><?= nl2br(sanitize($client['client_notes'])) ?></p>
</div>
<?php endif; ?>
</div>
<!-- Websites Section -->
<div class="card rounded-lg overflow-hidden">
<div class="px-6 py-4 border-b flex items-center justify-between gap-4">
<h3 class="text-lg font-semibold">Websites (<?= count($websites) ?>)</h3>
<button onclick="document.getElementById('addWebsiteModal').classList.remove('hidden')"
class="btn-primary px-3 py-2 rounded-md text-sm font-medium flex items-center gap-2"
data-testid="button-add-website">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
</svg>
Add Website
</button>
</div>
<?php if (empty($websites)): ?>
<div class="p-12 text-center">
<svg class="w-16 h-16 mx-auto text-muted-foreground/50 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/>
</svg>
<h4 class="text-lg font-medium mb-2">No websites yet</h4>
<p class="text-muted-foreground mb-4">Add websites to track their WordPress updates</p>
<button onclick="document.getElementById('addWebsiteModal').classList.remove('hidden')"
class="btn-primary px-4 py-2 rounded-md text-sm font-medium"
data-testid="button-add-first-website">
Add Website
</button>
</div>
<?php else: ?>
<div class="divide-y">
<?php foreach ($websites as $website): ?>
<a href="website-detail.php?id=<?= $website['website_id'] ?>"
class="flex items-center justify-between gap-4 px-6 py-4 hover:bg-accent/50 transition-colors"
data-testid="row-website-<?= $website['website_id'] ?>">
<div class="flex items-center gap-4 min-w-0">
<div class="w-10 h-10 rounded-lg bg-blue-500/10 flex items-center justify-center flex-shrink-0">
<svg class="w-5 h-5 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/>
</svg>
</div>
<div class="min-w-0">
<p class="font-medium truncate" data-testid="text-website-url-<?= $website['website_id'] ?>"><?= sanitize($website['website_url']) ?></p>
<p class="text-sm text-muted-foreground">
<?= $website['plugin_count'] + $website['theme_count'] + $website['core_count'] ?> total updates tracked
</p>
</div>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="badge badge-success" title="Plugin updates"><?= $website['plugin_count'] ?> plugins</span>
<span class="badge badge-warning" title="Theme updates"><?= $website['theme_count'] ?> themes</span>
<span class="badge badge-info" title="Core updates"><?= $website['core_count'] ?> core</span>
<svg class="w-5 h-5 text-muted-foreground" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</div>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- Edit Client Modal -->
<div id="editClientModal" class="hidden fixed inset-0 z-50 overflow-y-auto" aria-modal="true">
<div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 bg-black/50 transition-opacity" onclick="document.getElementById('editClientModal').classList.add('hidden')"></div>
<div class="inline-block align-bottom card rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<form action="api/clients.php" method="POST">
<input type="hidden" name="action" value="update">
<input type="hidden" name="client_id" value="<?= $client['client_id'] ?>">
<div class="px-6 py-4 border-b">
<h3 class="text-lg font-semibold">Edit Client</h3>
</div>
<div class="px-6 py-4 space-y-4">
<div>
<label for="edit_client_name" class="block text-sm font-medium mb-1">Client Name <span class="text-destructive">*</span></label>
<input type="text"
id="edit_client_name"
name="client_name"
required
value="<?= sanitize($client['client_name']) ?>"
class="w-full px-3 py-2 rounded-md text-sm"
data-testid="input-edit-client-name">
</div>
<div>
<label for="edit_client_email" class="block text-sm font-medium mb-1">Email Address</label>
<input type="email"
id="edit_client_email"
name="client_email"
value="<?= sanitize($client['client_email'] ?? '') ?>"
class="w-full px-3 py-2 rounded-md text-sm"
data-testid="input-edit-client-email">
</div>
<div>
<label for="edit_client_phone" class="block text-sm font-medium mb-1">Phone Number</label>
<input type="tel"
id="edit_client_phone"
name="client_phone"
value="<?= sanitize($client['client_phone'] ?? '') ?>"
class="w-full px-3 py-2 rounded-md text-sm"
data-testid="input-edit-client-phone">
</div>
<div>
<label for="edit_client_notes" class="block text-sm font-medium mb-1">Notes</label>
<textarea id="edit_client_notes"
name="client_notes"
rows="3"
class="w-full px-3 py-2 rounded-md text-sm"
data-testid="input-edit-client-notes"><?= sanitize($client['client_notes'] ?? '') ?></textarea>
</div>
</div>
<div class="px-6 py-4 border-t flex items-center justify-end gap-3">
<button type="button"
onclick="document.getElementById('editClientModal').classList.add('hidden')"
class="btn-secondary px-4 py-2 rounded-md text-sm font-medium">
Cancel
</button>
<button type="submit"
class="btn-primary px-4 py-2 rounded-md text-sm font-medium"
data-testid="button-submit-edit-client">
Save Changes
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Add Website Modal -->
<div id="addWebsiteModal" class="hidden fixed inset-0 z-50 overflow-y-auto" aria-modal="true">
<div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 bg-black/50 transition-opacity" onclick="document.getElementById('addWebsiteModal').classList.add('hidden')"></div>
<div class="inline-block align-bottom card rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<form action="api/websites.php" method="POST">
<input type="hidden" name="action" value="create">
<input type="hidden" name="client_id" value="<?= $client['client_id'] ?>">
<input type="hidden" name="redirect" value="client-detail.php?id=<?= $client['client_id'] ?>">
<div class="px-6 py-4 border-b">
<h3 class="text-lg font-semibold">Add Website for <?= sanitize($client['client_name']) ?></h3>
</div>
<div class="px-6 py-4 space-y-4">
<div>
<label for="website_url" class="block text-sm font-medium mb-1">Website URL <span class="text-destructive">*</span></label>
<input type="url"
id="website_url"
name="website_url"
required
class="w-full px-3 py-2 rounded-md text-sm"
placeholder="https://example.com"
data-testid="input-website-url">
</div>
<div>
<label for="website_name" class="block text-sm font-medium mb-1">Website Name</label>
<input type="text"
id="website_name"
name="website_name"
class="w-full px-3 py-2 rounded-md text-sm"
placeholder="My Website"
data-testid="input-website-name">
</div>
</div>
<div class="px-6 py-4 border-t flex items-center justify-end gap-3">
<button type="button"
onclick="document.getElementById('addWebsiteModal').classList.add('hidden')"
class="btn-secondary px-4 py-2 rounded-md text-sm font-medium">
Cancel
</button>
<button type="submit"
class="btn-primary px-4 py-2 rounded-md text-sm font-medium"
data-testid="button-submit-add-website">
Add Website
</button>
</div>
</form>
</div>
</div>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>