diff --git a/public/assets/app.css b/public/assets/app.css index f112b03..c08e0ea 100644 --- a/public/assets/app.css +++ b/public/assets/app.css @@ -1,10 +1,51 @@ -body { background: #f5f7fb; } -.navbar-brand { letter-spacing: .02em; } -.metric-card { border: 0; box-shadow: 0 .25rem 1rem rgba(24, 39, 75, .06); } -.sidebar-link.active { background: rgba(13, 110, 253, .1); color: #0d6efd; font-weight: 600; } -.login-card { max-width: 430px; margin: 10vh auto; } +:root { + --jc-ink: #172033; + --jc-muted: #667085; + --jc-primary: #635bff; + --jc-primary-dark: #5148e5; + --jc-surface: #ffffff; + --jc-bg: #f6f7fb; + --jc-border: #e8eaf2; + --jc-shadow: 0 14px 38px rgba(20, 28, 55, .08); +} +* { box-sizing: border-box; } +body { background: var(--jc-bg); color: var(--jc-ink); font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; letter-spacing: -.01em; } +a { color: var(--jc-primary); } +.navbar { background: linear-gradient(135deg, #1d2540 0%, #302c68 58%, #5148e5 100%) !important; min-height: 68px; box-shadow: 0 4px 18px rgba(31, 36, 76, .18); } +.navbar-brand { letter-spacing: -.035em; font-size: 1.2rem; } +.sidebar-link { color: #667085; border-radius: 10px; padding: .7rem .85rem; font-weight: 500; transition: .18s ease; } +.sidebar-link:hover { background: #f0efff; color: var(--jc-primary); transform: translateX(2px); } +.sidebar-link.active { background: #eeecff; color: var(--jc-primary); font-weight: 700; } +main { min-width: 0; } +.card { border: 1px solid var(--jc-border); border-radius: 16px; box-shadow: var(--jc-shadow); } +.card-header, .card-body { border-radius: inherit; } +.card h1, .card h2, .card h3 { letter-spacing: -.03em; } +.metric-card { border: 0; overflow: hidden; position: relative; box-shadow: var(--jc-shadow); } +.metric-card::after { content: ""; position: absolute; width: 92px; height: 92px; border-radius: 50%; right: -28px; top: -28px; background: rgba(99, 91, 255, .1); } +.metric-card .display-6 { color: var(--jc-ink); letter-spacing: -.06em; } +.btn-primary { background: var(--jc-primary); border-color: var(--jc-primary); box-shadow: 0 8px 18px rgba(99, 91, 255, .22); } +.btn-primary:hover, .btn-primary:focus { background: var(--jc-primary-dark); border-color: var(--jc-primary-dark); } +.btn-outline-primary { color: var(--jc-primary); border-color: #bdb8ff; } +.btn-outline-primary:hover { background: var(--jc-primary); border-color: var(--jc-primary); } +.form-control, .form-select { border-color: #dfe2ec; border-radius: 10px; padding: .68rem .8rem; } +.form-control:focus, .form-select:focus { border-color: #9b95ff; box-shadow: 0 0 0 .22rem rgba(99, 91, 255, .12); } +.table { --bs-table-bg: transparent; } +.table thead th { color: var(--jc-muted); font-size: .74rem; text-transform: uppercase; letter-spacing: .08em; font-weight: 700; border-bottom-color: var(--jc-border); } +.table tbody td { border-bottom-color: #f0f1f5; vertical-align: middle; } +.badge { border-radius: 999px; padding: .45em .72em; font-weight: 650; } +.login-card { max-width: 440px; margin: 10vh auto; border: 0; box-shadow: 0 24px 70px rgba(31, 36, 76, .16); } +.login-card::before { content: ""; display: block; height: 7px; border-radius: 16px 16px 0 0; background: linear-gradient(90deg, #635bff, #9b95ff); } +.client-tabs { display: flex; gap: .4rem; flex-wrap: wrap; padding: .45rem; margin-bottom: 1.35rem; background: #fff; border: 1px solid var(--jc-border); border-radius: 14px; box-shadow: 0 8px 24px rgba(20, 28, 55, .05); } +.client-tab { border: 0; background: transparent; border-radius: 10px; color: var(--jc-muted); padding: .68rem 1rem; font-weight: 650; transition: .18s ease; } +.client-tab:hover { background: #f2f1ff; color: var(--jc-primary); } +.client-tab.active { background: var(--jc-primary); color: #fff; box-shadow: 0 6px 14px rgba(99, 91, 255, .2); } +.client-section-hidden { display: none !important; } +.section-kicker { color: var(--jc-primary); font-size: .75rem; text-transform: uppercase; letter-spacing: .1em; font-weight: 800; } @media (max-width: 767.98px) { .desktop-table { display: none; } .mobile-card { display: block; } + .sidebar-link { padding: .55rem .7rem; } + .client-tabs { overflow-x: auto; flex-wrap: nowrap; scrollbar-width: thin; } + .client-tab { white-space: nowrap; padding: .62rem .8rem; } } @media (min-width: 768px) { .mobile-card { display: none; } } diff --git a/public/index.php b/public/index.php index 3935646..f945313 100644 --- a/public/index.php +++ b/public/index.php @@ -529,20 +529,21 @@ if ($route === 'client') { $credentialRows = $credentialStmt->fetchAll(); } render_header('Client details'); - echo '
← Back to clients View history

' . e($client['name']) . '

Client profile and support contacts.

' . e(ucfirst($client['status'])) . '
' . (isset($_GET['contact_created']) ? '
Contact added successfully.
' : '') . (isset($_GET['sla_updated']) ? '
SLA agreement updated.
' : '') . ($contactErrors ? '
' . e(implode(' ', $contactErrors)) . '
' : '') . ($slaErrors ? '
' . e(implode(' ', $slaErrors)) . '
' : '') . '

Support information

Email
' . e((string)($client['support_email'] ?? '—')) . '
Phone
' . e((string)($client['support_phone'] ?? '—')) . '
Preferred method
' . e((string)($client['preferred_contact_method'] ?? '—')) . '
Address
' . nl2br(e((string)($client['physical_address'] ?? '—'))) . '

Contacts

'; + echo '
Jobcard history
'; + echo '
← Back to clients View history

' . e($client['name']) . '

Client profile and support contacts.

' . e(ucfirst($client['status'])) . '
' . (isset($_GET['contact_created']) ? '
Contact added successfully.
' : '') . (isset($_GET['sla_updated']) ? '
SLA agreement updated.
' : '') . ($contactErrors ? '
' . e(implode(' ', $contactErrors)) . '
' : '') . ($slaErrors ? '
' . e(implode(' ', $slaErrors)) . '
' : '') . '

Support information

Email
' . e((string)($client['support_email'] ?? '—')) . '
Phone
' . e((string)($client['support_phone'] ?? '—')) . '
Preferred method
' . e((string)($client['preferred_contact_method'] ?? '—')) . '
Address
' . nl2br(e((string)($client['physical_address'] ?? '—'))) . '

Contacts

'; if (!$contacts) echo '

No contacts recorded.

'; foreach ($contacts as $contact) { echo '
' . e($contact['name']) . ($contact['is_primary'] ? ' Primary' : '') . '
' . e((string)($contact['email'] ?? '')) . ' ' . e((string)($contact['phone'] ?? '')) . '
'; if (can('clients.manage')) { echo '
'; } echo '
'; } if (can('clients.manage')) echo '

Add contact

'; echo '
'; if (can('sla.view') || can('sla.manage')) { - echo '

SLA agreement

'; + echo '

SLA agreement

'; if (can('sla.manage')) { echo '
'; } elseif ($slaAgreement) echo '
Type
' . e((string)($slaAgreement['agreement_type'] ?? '—')) . '
Allocation
' . e(number_format((float)$slaAgreement['allocated_hours'], 2)) . ' hours / ' . e($slaAgreement['period_type']) . '
'; else echo '

No SLA agreement configured.

'; echo '
'; } if (can('credentials.view') || can('credentials.manage')) { - echo '

Protected credentials

'; + echo '

Saved client information

'; if ($credentialErrors) echo '
' . e(implode(' ', $credentialErrors)) . '
'; if (isset($_GET['credential_created'])) echo '
Credential saved securely.
'; foreach ($credentialRows as $credentialRow) { @@ -550,17 +551,16 @@ if ($route === 'client') { if (can('credentials.view') && !($revealedCredential && $revealedCredential['id'] === (int)$credentialRow['id'])) echo '
'; echo '
'; } - if (can('credentials.manage')) { echo '

Add credential

'; } echo '
'; } if (can('technical.view') || can('technical.manage')) { $technicalRows = (new \App\Domain\Credential\TechnicalInformationRepository(db()))->forClient($clientId); - echo '

Technical information

'; + echo '

Technical information

'; foreach ($technicalRows as $technical) { $display = $technical['display']; echo '
' . e(ucfirst($technical['category'])) . '
' . e((string)$display['label']) . ($display['username'] ? ' · ' . e((string)$display['username']) : '') . '
' . nl2br(e((string)($display['notes'] ?? ''))) . '
'; } - if (can('technical.manage')) { echo '
'; } + if (can('credentials.manage')) { echo '

Add client information

'; } echo '
'; } - if (can('clients.manage')) echo '

Edit client

'; + if (can('clients.manage')) echo '

Edit client

'; render_footer(); exit; }