Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
359 changes: 359 additions & 0 deletions client/proposal-view.php

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions client/proposal.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
define('WDS_SYSTEM', true);
require_once __DIR__ . '/../includes/portal-helpers.php';

portalRequireLogin();
if (portalGetRole() !== 'client') {
header('Location: /dashboard.php');
exit;
}
// Forward to the new unified proposal view page
$proposalId = isset($_GET['proposal_id']) ? '?proposal_id=' . urlencode((string)$_GET['proposal_id']) : '';
header('Location: /client/proposal-view.php' . $proposalId);
exit;


$user = portalGetUser();
$username = (string)($user['username'] ?? '');
Expand Down
106 changes: 90 additions & 16 deletions client/proposals.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
exit;
}

$user = portalGetUser();
$user = portalGetUser();
$username = (string)($user['username'] ?? '');
$requests = array_values(array_filter(portalLoadProjectRequests(), function ($r) use ($username) {
return (string)($r['client_username'] ?? '') === $username;
Expand All @@ -21,48 +21,121 @@
|| (string)($p['client_username'] ?? '') === $username;
}));

// Only show proposals that have been sent (not draft)
$proposals = array_values(array_filter($proposals, function ($p) {
$st = (string)($p['proposal_status'] ?? $p['status'] ?? 'draft');
return !in_array($st, ['draft', 'cancelled'], true);
}));

usort($proposals, function ($a, $b) {
return strcmp((string)($b['updated_at'] ?? $b['created_at'] ?? ''), (string)($a['updated_at'] ?? $a['created_at'] ?? ''));
return strcmp(
(string)($b['updated_at'] ?? $b['created_at'] ?? ''),
(string)($a['updated_at'] ?? $a['created_at'] ?? '')
);
});

$statusLabels = [
'sent' => 'Awaiting Your Review',
'changes_requested'=> 'Changes Requested',
'accepted' => 'Accepted',
'payment_pending' => 'Payment Pending',
'paid_start' => 'Paid — Work Starting',
'in_progress' => 'In Progress',
'completed' => 'Completed',
];

$statusColors = [
'sent' => '#36f3ff',
'changes_requested'=> '#ffc600',
'accepted' => '#86efac',
'payment_pending' => '#fbbf24',
'paid_start' => '#34d399',
'in_progress' => '#60a5fa',
'completed' => '#4ade80',
];

$current_page = 'client-portal';
$header_class = 'inner-header';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="../assets/images/RL-icon.png">
<title>My Proposals | Client Portal | Runlevel Systems</title>
<link href="../assets/css/coreloop.css" rel="stylesheet">
<style>
.portal-wrap{padding:30px 0 70px;}
.card{background:#0c1729;border:1px solid rgba(54,243,255,.18);border-radius:10px;padding:16px;}
.item{background:#09111d;border:1px solid rgba(54,243,255,.14);border-radius:8px;padding:12px;margin-bottom:10px;}
.card{background:#0c1729;border:1px solid rgba(54,243,255,.18);border-radius:10px;padding:18px;margin-bottom:14px;}
.how-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px;}
.how-list li{display:flex;align-items:flex-start;gap:10px;color:#a8bedc;font-size:.85rem;}
.how-step{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;min-width:22px;border-radius:50%;background:rgba(54,243,255,.12);border:1px solid rgba(54,243,255,.3);color:#36f3ff;font-size:.72rem;font-weight:700;}
.prop-card{background:#09111d;border:1px solid rgba(54,243,255,.14);border-radius:8px;padding:14px;margin-bottom:10px;}
.status-badge{display:inline-block;padding:2px 10px;border-radius:999px;font-size:.68rem;font-weight:700;}
.prop-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px;}
.btn{display:inline-block;border-radius:5px;padding:6px 12px;font-size:.8rem;font-weight:700;text-decoration:none;border:none;cursor:pointer;}
.btn-teal{background:rgba(54,243,255,.12);border:1px solid rgba(54,243,255,.3);color:#36f3ff;}
.btn-gold{background:#ffc600;color:#08111f;}
.btn-green{background:rgba(52,211,153,.12);border:1px solid rgba(52,211,153,.35);color:#34d399;}
</style>
</head>
<body>
<?php include __DIR__ . '/../includes/header.php'; ?>
<?php include __DIR__ . '/../includes/navigation.php'; ?>
<section class="portal-wrap">
<div class="container">
<a href="/dashboard.php" style="color:#36f3ff;font-size:.84rem;">← Back to Dashboard</a>
<a href="/dashboard.php" style="color:#36f3ff;font-size:.84rem;">← Dashboard</a>

<div class="card" style="margin-top:10px;">
<h2 style="margin:0 0 12px;color:#ffc600;font-size:1.1rem;">My Proposals</h2>
<h2 style="margin:0 0 12px;color:#ffc600;font-size:1rem;">How This Works</h2>
<ul class="how-list">
<li><span class="how-step">1</span> You tell us what you need.</li>
<li><span class="how-step">2</span> We create a proposal.</li>
<li><span class="how-step">3</span> You can accept it or request changes.</li>
<li><span class="how-step">4</span> Once accepted, you pay the amount due to start.</li>
<li><span class="how-step">5</span> We begin work.</li>
<li><span class="how-step">6</span> You review the delivered work.</li>
<li><span class="how-step">7</span> Additional milestones or final payment are handled according to the proposal.</li>
</ul>
</div>

<div class="card">
<h2 style="margin:0 0 12px;color:#ffc600;font-size:1rem;">My Proposals</h2>
<?php if (empty($proposals)): ?>
<p style="color:#7a9ac0;">No proposals available yet.</p>
<p style="color:#7a9ac0;margin:0;">No proposals available yet. Once we review your request and create a proposal, it will appear here.</p>
<?php else: ?>
<?php foreach ($proposals as $proposal): ?>
<div class="item">
<div style="display:flex;justify-content:space-between;gap:8px;flex-wrap:wrap;">
<div style="color:#ffc600;font-family:monospace;font-weight:700;"><?php echo pe($proposal['proposal_id'] ?? 'Proposal'); ?></div>
<div style="color:#a8bedc;font-size:.8rem;"><?php echo pe(ucfirst((string)($proposal['status'] ?? 'draft'))); ?></div>
<?php foreach ($proposals as $proposal):
$pid = (string)($proposal['proposal_id'] ?? '');
$status = (string)($proposal['proposal_status'] ?? $proposal['status'] ?? 'sent');
$label = $statusLabels[$status] ?? ucfirst(str_replace('_', ' ', $status));
$color = $statusColors[$status] ?? '#7a9ac0';
$updTs = strtotime((string)($proposal['updated_at'] ?? $proposal['created_at'] ?? '')) ?: 0;
?>
<div class="prop-card">
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:8px;flex-wrap:wrap;">
<div>
<div style="color:#ffc600;font-weight:700;font-size:.92rem;"><?php echo pe($proposal['project_title'] ?? 'Project Proposal'); ?></div>
<div style="color:#7a9ac0;font-size:.74rem;margin-top:2px;">ID: <?php echo pe($pid); ?></div>
</div>
<span class="status-badge" style="background:<?php echo pe($color); ?>18;border:1px solid <?php echo pe($color); ?>44;color:<?php echo pe($color); ?>;"><?php echo pe($label); ?></span>
</div>
<?php if (!empty($proposal['total_price']) || !empty($proposal['amount_due_to_start'])): ?>
<div style="display:flex;gap:16px;margin-top:8px;flex-wrap:wrap;">
<?php if (!empty($proposal['total_price'])): ?><div style="font-size:.8rem;color:#a8bedc;">Total: <strong style="color:#86efac;">$<?php echo pe($proposal['total_price']); ?></strong></div><?php endif; ?>
<?php if (!empty($proposal['amount_due_to_start'])): ?><div style="font-size:.8rem;color:#a8bedc;">Due to Start: <strong style="color:#ffc600;">$<?php echo pe($proposal['amount_due_to_start']); ?></strong></div><?php endif; ?>
</div>
<?php endif; ?>
<?php if ($updTs > 0): ?><div style="color:#5a7a9e;font-size:.72rem;margin-top:4px;">Updated <?php echo pe(date('M j, Y', $updTs)); ?></div><?php endif; ?>
<div class="prop-actions">
<a class="btn btn-teal" href="/client/proposal-view.php?proposal_id=<?php echo urlencode($pid); ?>">View Proposal</a>
<?php if ($status === 'sent' || $status === 'changes_requested'): ?>
<a class="btn btn-green" href="/client/proposal-view.php?proposal_id=<?php echo urlencode($pid); ?>#accept">Accept</a>
<a class="btn btn-gold" href="/client/proposal-view.php?proposal_id=<?php echo urlencode($pid); ?>#request-changes">Request Changes</a>
<?php endif; ?>
<?php if ($status === 'accepted'): ?><span style="color:#86efac;font-size:.78rem;padding:6px 0;">✓ Accepted — awaiting payment</span><?php endif; ?>
<?php if ($status === 'payment_pending'): ?><span style="color:#fbbf24;font-size:.78rem;padding:6px 0;">Payment pending</span><?php endif; ?>
</div>
<div style="color:#eaf3ff;font-size:.9rem;margin-top:4px;"><?php echo pe($proposal['project_title'] ?? 'Project Proposal'); ?></div>
<div style="color:#7a9ac0;font-size:.78rem;margin-top:4px;">Request ID: <?php echo pe($proposal['request_id'] ?? ''); ?></div>
<a style="display:inline-block;margin-top:6px;color:#36f3ff;font-size:.82rem;" href="/client/proposal.php?proposal_id=<?php echo urlencode((string)($proposal['proposal_id'] ?? '')); ?>">View proposal</a>
</div>
<?php endforeach; ?>
<?php endif; ?>
Expand All @@ -74,3 +147,4 @@
<script src="../assets/js/bootstrap.min.js"></script>
</body>
</html>

71 changes: 66 additions & 5 deletions dashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,41 @@
$awaitingApproval = 0;
$awaitingSignature = 0;

// Proposal status counters (new workflow)
$proposalsDraft = 0;
$proposalsSent = 0;
$proposalsChangesRequested = 0;
$proposalsAccepted = 0;
$proposalsAwaitingPayment = 0;
$proposalsPaidStart = 0;
$proposalsInProgress = 0;
$proposalsMyReview = 0; // client-facing: proposals awaiting client action

// Also count from all proposals directly for staff overview
foreach ($proposals as $prop) {
$ps = (string)($prop['proposal_status'] ?? $prop['status'] ?? 'draft');
if ($isStaffRole) {
switch ($ps) {
case 'draft': $proposalsDraft++; break;
case 'sent': $proposalsSent++; break;
case 'changes_requested': $proposalsChangesRequested++; break;
case 'accepted': $proposalsAccepted++; break;
case 'payment_pending': $proposalsAwaitingPayment++; break;
case 'paid_start': $proposalsPaidStart++; break;
case 'in_progress': $proposalsInProgress++; break;
}
} elseif ($role === 'client') {
// Only count proposals the client owns
$ownerUsername = (string)($prop['client_username'] ?? '');
if ($ownerUsername !== $myUsername && $ownerUsername !== '') {
continue;
}
if (in_array($ps, ['sent', 'changes_requested'], true)) {
$proposalsMyReview++;
}
}
}

foreach ($visibleRequests as $request) {
$projectId = portalGetRequestDisplayId((array)$request);
$requestStatus = (string)($request['status'] ?? 'new');
Expand Down Expand Up @@ -163,6 +198,7 @@
];
}


usort($projectRows, function ($a, $b) {
return ((int)$b['last_updated_ts']) <=> ((int)$a['last_updated_ts']);
});
Expand Down Expand Up @@ -319,21 +355,46 @@
</div>
<div class="portal-actions">
<a href="/estimate.php" class="portal-link-btn">Start Project</a>
<?php if ($isStaffRole): ?><a href="/staff/estimate-requests.php" class="portal-link-btn">All Projects</a><?php endif; ?>
<?php if ($isStaffRole): ?>
<a href="/staff/proposals.php" class="portal-link-btn">Proposals</a>
<a href="/staff/proposal-edit.php" class="portal-link-btn">+ New Proposal</a>
<a href="/staff/estimate-requests.php" class="portal-link-btn">All Requests</a>
<a href="/staff/payment-record.php" class="portal-link-btn">Record Payment</a>
<?php endif; ?>
<?php if ($isAdminRole): ?><a href="/settings.php" class="portal-link-btn">Settings</a><?php endif; ?>
<a href="/logout.php" class="portal-logout">Sign Out</a>
</div>
</div>

<?php if ($isStaffRole): ?>
<p class="portal-section-title">Proposal Pipeline</p>
<div class="portal-card-grid">
<a href="/staff/proposals.php?status=draft" class="portal-dash-card"><div class="card-label">Draft</div><div class="card-count"><?php echo $proposalsDraft; ?></div></a>
<a href="/staff/proposals.php?status=sent" class="portal-dash-card"><div class="card-label">Sent</div><div class="card-count"><?php echo $proposalsSent; ?></div></a>
<a href="/staff/proposals.php?status=changes_requested" class="portal-dash-card" style="border-color:rgba(251,191,36,.35);"><div class="card-label" style="color:#fbbf24;">Changes Requested</div><div class="card-count" style="color:#fbbf24;"><?php echo $proposalsChangesRequested; ?></div></a>
<a href="/staff/proposals.php?status=accepted" class="portal-dash-card" style="border-color:rgba(34,197,94,.35);"><div class="card-label" style="color:#86efac;">Accepted</div><div class="card-count" style="color:#86efac;"><?php echo $proposalsAccepted; ?></div></a>
<a href="/staff/proposals.php?status=payment_pending" class="portal-dash-card" style="border-color:rgba(251,191,36,.35);"><div class="card-label" style="color:#fbbf24;">Awaiting Payment</div><div class="card-count" style="color:#fbbf24;"><?php echo $proposalsAwaitingPayment; ?></div></a>
<a href="/staff/proposals.php?status=paid_start" class="portal-dash-card" style="border-color:rgba(54,243,255,.35);"><div class="card-label" style="color:#36f3ff;">Paid / Ready</div><div class="card-count" style="color:#36f3ff;"><?php echo $proposalsPaidStart; ?></div></a>
<a href="/staff/proposals.php?status=in_progress" class="portal-dash-card"><div class="card-label">In Progress</div><div class="card-count"><?php echo $proposalsInProgress; ?></div></a>
</div>

<p class="portal-section-title">Project Overview</p>
<div class="portal-card-grid">
<a href="#recent-projects" class="portal-dash-card"><div class="card-label">Recent Projects</div><div class="card-count"><?php echo count($recentProjects); ?></div></a>
<a href="#active-projects" class="portal-dash-card"><div class="card-label">Active</div><div class="card-count"><?php echo $activeProjects; ?></div></a>
<a href="#pending-projects" class="portal-dash-card"><div class="card-label">Pending</div><div class="card-count"><?php echo $pendingProjects; ?></div></a>
<a href="#completed-projects" class="portal-dash-card"><div class="card-label">Completed</div><div class="card-count"><?php echo $completedProjects; ?></div></a>
</div>
<?php else: ?>
<p class="portal-section-title">My Overview</p>
<div class="portal-card-grid">
<a href="/client/proposals.php" class="portal-dash-card" style="<?php echo $proposalsMyReview > 0 ? 'border-color:rgba(251,191,36,.5);' : ''; ?>"><div class="card-label" style="<?php echo $proposalsMyReview > 0 ? 'color:#fbbf24;' : ''; ?>">Proposals to Review</div><div class="card-count" style="<?php echo $proposalsMyReview > 0 ? 'color:#fbbf24;' : ''; ?>"><?php echo $proposalsMyReview; ?></div></a>
<a href="/client/proposals.php" class="portal-dash-card"><div class="card-label">My Proposals</div><div class="card-count"><?php echo count(array_filter($proposals, function($p) use ($myUsername) { return (string)($p['client_username'] ?? '') === $myUsername || (string)($p['client_id'] ?? '') === $myUsername; })); ?></div></a>
<a href="#recent-projects" class="portal-dash-card"><div class="card-label">My Requests</div><div class="card-count"><?php echo count($visibleRequests); ?></div></a>
<a href="#active-projects" class="portal-dash-card"><div class="card-label">Active Projects</div><div class="card-count"><?php echo $activeProjects; ?></div></a>
<a href="#pending-projects" class="portal-dash-card"><div class="card-label">Pending Projects</div><div class="card-count"><?php echo $pendingProjects; ?></div></a>
<a href="#completed-projects" class="portal-dash-card"><div class="card-label">Completed Projects</div><div class="card-count"><?php echo $completedProjects; ?></div></a>
<a href="#recent-activity" class="portal-dash-card"><div class="card-label">Awaiting Approval</div><div class="card-count"><?php echo $awaitingApproval; ?></div></a>
<a href="#recent-activity" class="portal-dash-card"><div class="card-label">Awaiting Signature</div><div class="card-count"><?php echo $awaitingSignature; ?></div></a>
</div>
<?php endif; ?>


<div class="portal-layout">
<div class="portal-panel" id="recent-projects">
Expand Down
3 changes: 3 additions & 0 deletions data/payments.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"payments": []
}
Loading
Loading