diff --git a/dashboard.php b/dashboard.php index 3056809..22b6fd2 100644 --- a/dashboard.php +++ b/dashboard.php @@ -14,11 +14,21 @@ // Load counts for admin/staff overview cards $newEstimates = []; $newRequests = []; +$myEstimates = []; if (in_array($role, ['admin', 'staff'], true)) { $allEstimates = portalLoadEstimateRequests(); $newEstimates = array_filter($allEstimates, function ($r) { return ($r['status'] ?? '') === 'new'; }); $allRequests = portalLoadRequests(); $newRequests = array_filter($allRequests, function ($r) { return ($r['status'] ?? '') === 'new'; }); +} elseif ($role === 'client') { + $allEstimates = portalLoadEstimateRequests(); + $myUsername = (string)($user['username'] ?? ''); + $myEstimates = array_values(array_filter($allEstimates, function ($r) use ($myUsername) { + return (string)($r['client_username'] ?? '') === $myUsername; + })); + usort($myEstimates, function ($a, $b) { + return strcmp((string)($b['created_at'] ?? ''), (string)($a['created_at'] ?? '')); + }); } $current_page = 'dashboard'; @@ -232,18 +242,13 @@
โž•
-
Submit New Request
+
Submit New Estimate
Start a project estimate
- -
๐Ÿ“ฅ
-
My Requests
-
View your requests
-
๐Ÿ“
-
My Estimates
-
Submit or review
+
My Estimate Requests
+
submitted
๐Ÿ“„
@@ -263,7 +268,7 @@
๐Ÿ› ๏ธ
Runlevel Tools
-
Dev Partner tools
+
Dev+1 tools
๐Ÿ™‹
@@ -272,6 +277,34 @@
+
+

My Estimate Requests

+ + + + + + + + + + + + + + + + + + + + + + + +
Estimate IDProject TypeStatusSubmitted Date
No estimate requests yet.
+
+
diff --git a/design-debug-deploy.php b/design-debug-deploy.php index 685e58c..e7ad4f3 100644 --- a/design-debug-deploy.php +++ b/design-debug-deploy.php @@ -6,25 +6,25 @@ - Dev Partner | Runlevel Systems - - + Dev+1 | Runlevel Systems + + -
+
-

Dev Partner

-

Your Development Team When You Need One

+

Dev+1

+

Your Extra Development Help When The Project Needs Momentum

Need help building, fixing, launching, or improving software? Runlevel Systems can work alongside your team or handle development for you.

DESIGN โ€ข DEBUG โ€ข DEPLOY

@@ -35,7 +35,7 @@
-

Who Dev Partner Is For

+

Who Dev+1 Is For

  • Customers who need help finishing a project
  • Customers who need a complete application built
  • diff --git a/estimate.php b/estimate.php index 234b081..0d41886 100644 --- a/estimate.php +++ b/estimate.php @@ -1,124 +1,162 @@ (string)($fullUser['display_name'] ?? ''), + 'email' => (string)($fullUser['email'] ?? ''), + 'phone' => (string)($fullUser['phone'] ?? ''), + 'discord_username' => (string)($fullUser['discord_username'] ?? ''), + 'contact_method' => 'Email', + 'project_type' => '', + 'repo_link' => '', + 'description' => '', + 'desired_username' => '', +]; + +$form = $defaults; +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + foreach ($form as $key => $value) { + $form[$key] = trim((string)($_POST[$key] ?? '')); + } } if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit_estimate'])) { - $name = trim($_POST['name'] ?? ''); - $email = trim($_POST['email'] ?? ''); - $phone = trim($_POST['phone'] ?? ''); - $contactMethod = trim($_POST['contact_method'] ?? ''); - $projectType = trim($_POST['project_type'] ?? ''); - $whatNeeded = trim($_POST['what_needed'] ?? ''); - $projectStage = trim($_POST['project_stage'] ?? ''); - $approxSize = trim($_POST['approx_size'] ?? ''); - $hasFiles = trim($_POST['has_files'] ?? ''); - $repoLink = trim($_POST['repo_link'] ?? ''); - $description = trim($_POST['description'] ?? ''); - $timeline = trim($_POST['timeline'] ?? ''); - $budget = trim($_POST['budget'] ?? ''); - $agreed = isset($_POST['agreement']); - - $isGameType = in_array($projectType, $gameTypes, true); + $name = $form['name']; + $email = $form['email']; + $phone = $form['phone']; + $discordUsername = $form['discord_username']; + $contactMethod = $form['contact_method']; + $projectType = $form['project_type']; + $repoLink = $form['repo_link']; + $description = $form['description']; + $desiredUsername = strtolower($form['desired_username']); if ($name === '') { $error = 'Please enter your name.'; - } elseif ($contactMethod === '') { - $error = 'Please select a preferred contact method.'; - } elseif ($projectType === '') { + } elseif ($email === '') { + $error = 'Email is required.'; + } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $error = 'Please enter a valid email address.'; + } elseif (!in_array($projectType, $projectTypes, true)) { $error = 'Please select a project type.'; - } elseif ($whatNeeded === '') { - $error = 'Please select what you need.'; } elseif ($description === '') { $error = 'Please describe your request.'; - } elseif (!$agreed) { - $error = 'Please check the acknowledgement checkbox before submitting.'; + } elseif (!in_array($contactMethod, $contactOptions, true)) { + $error = 'Please select a preferred contact method.'; } elseif ($repoLink !== '' && !filter_var($repoLink, FILTER_VALIDATE_URL)) { $error = 'The repository or project link does not appear to be a valid URL.'; - } elseif ($email === '' && $phone === '') { - // Soft validation: warn but allow submit if Discord is the preferred contact for a game project - if ($isGameType && $contactMethod === 'Discord') { - $warning = 'Please join our Discord and mention your Estimate ID so we can connect your message to this request.'; - } else { - $error = 'Please provide at least one way for us to contact you, such as email or phone.'; - } - } elseif ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) { - $error = 'The email address provided does not appear to be valid.'; + } elseif (!$isLoggedIn && $desiredUsername === '') { + $error = 'Please enter a desired username.'; + } elseif (!$isLoggedIn && !portalIsValidEstimateUsername($desiredUsername)) { + $error = 'Username must be at least 3 characters and use only letters, numbers, dash, or underscore.'; } + $createdAccount = null; + $clientUsername = ''; if ($error === '') { - // Determine rough category for confirmation display - $category = 'Software Project'; - if (in_array($projectType, ['Training / Simulation'], true)) { - $category = 'Simulation / Training Project'; - } elseif (in_array($whatNeeded, ['Fix something broken', 'Review or rescue a project'], true) || $projectStage === 'It worked before but broke') { - $category = 'Project Rescue'; - } elseif (in_array($approxSize, ['Very small task'], true) || $whatNeeded === 'Add a feature') { - $category = 'Quick Fix / Small Task'; - } elseif (in_array($approxSize, ['Ongoing work'], true) || $whatNeeded === 'Ongoing help') { - $category = 'Ongoing Development Support'; - } elseif (in_array($approxSize, ['Small project'], true) && $projectStage === 'Just an idea') { - $category = 'Starter Project'; - } elseif (in_array($projectType, ['Business application', 'Infrastructure / Backend'], true)) { - $category = 'Business / Infrastructure Project'; - } elseif ($isGameType) { - $category = 'Game / Interactive Project'; + if ($isLoggedIn && !empty($sessionUser['username'])) { + $clientUsername = (string)$sessionUser['username']; + } else { + $createError = ''; + $createdAccount = portalCreateClientUserFromEstimate( + $desiredUsername, + $name, + $email, + $phone, + $discordUsername, + $createError + ); + if ($createdAccount === false) { + $error = $createError !== '' ? $createError : 'Unable to create your client account right now.'; + } else { + $clientUsername = (string)$createdAccount['username']; + } } + } - $estimateId = generateEstimateId(); - + if ($error === '') { + $estimateId = portalGenerateUniqueEstimateId(); $request = [ - 'estimate_id' => $estimateId, - 'id' => bin2hex(random_bytes(8)), - 'name' => $name, - 'email' => $email, - 'phone' => $phone, + 'id' => bin2hex(random_bytes(8)), + 'estimate_id' => $estimateId, + 'client_username' => $clientUsername, + 'name' => $name, + 'email' => $email, + 'phone' => $phone, + 'discord_username' => $discordUsername, 'contact_method' => $contactMethod, - 'project_type' => $projectType, - 'what_needed' => $whatNeeded, - 'project_stage' => $projectStage, - 'approx_size' => $approxSize, - 'has_files' => $hasFiles, - 'repo_link' => $repoLink, - 'description' => $description, - 'timeline' => $timeline, - 'budget' => $budget, - 'category' => $category, - 'status' => 'new', - 'notes' => '', - 'created_at' => date('c'), + 'project_type' => $projectType, + 'repo_link' => $repoLink, + 'description' => $description, + 'status' => 'new', + 'notes' => '', + 'created_at' => date('c'), ]; - if (portalAppendEstimateRequest($request)) { - $success = true; - $submitted = $request; - } else { + if (!portalAppendEstimateRequest($request)) { $error = 'There was an error saving your request. Please try again or contact us directly.'; + } else { + $success = true; + $submitted = [ + 'request' => $request, + 'created_account' => $createdAccount, + 'needs_verification' => !$isLoggedIn, + 'show_discord' => in_array($projectType, $gameTypes, true), + ]; + + if ($createdAccount !== null) { + $verifyLink = 'https://' . ($_SERVER['HTTP_HOST'] ?? 'runlevel.systems') + . '/verify-email.php?token=' . urlencode((string)$createdAccount['verification_token']); + $subject = 'Verify your Runlevel Systems account'; + $body = "Hello {$name},\n\n" + . "We received your project estimate request.\n\n" + . "Estimate ID:\n{$estimateId}\n\n" + . "A client dashboard account was created for you:\n\n" + . "Username:\n{$clientUsername}\n\n" + . "Temporary password:\n{$createdAccount['temporary_password']}\n\n" + . "Please verify your email before logging in:\n\n" + . "{$verifyLink}\n\n" + . "After verification, you can log in to view your dashboard and track project information.\n\n" + . "Runlevel Systems\n" + . "DESIGN โ€ข DEBUG โ€ข DEPLOY\n"; + send_email($email, $subject, $body); + } } } } -// Determine if previously selected project type is game-related (for JS init) -$selectedType = $_POST['project_type'] ?? ''; -$isGameSelected = in_array($selectedType, $gameTypes, true); +$showDiscordHint = in_array($form['project_type'], $gameTypes, true) || $form['contact_method'] === 'Discord'; ?> @@ -128,42 +166,37 @@ function generateEstimateId() { Project Requirements & Estimate | Runlevel Systems - @@ -173,218 +206,135 @@ function generateEstimateId() {

    Project Requirements & Estimate

    -

    Tell us what you need. We will help figure out the next step.

    +

    Please complete and submit this estimate request so we have the information needed to review your project. After submission, we will give you an Estimate ID that you can reference when contacting us.

    -
    - +
    -
    -

    โœ… Request Received

    -

    Thank you, . We received your project information.

    - -
    -
    Your Estimate ID:
    -
    -
    -

    - Please save this ID. If you contact us later or submit another request, this helps us find your project quickly. -

    - -

    - Likely project category:
    - -

    - -
    -

    - Next step:
    - Runlevel Systems will review your request and contact you using the contact information provided. - You may also contact us directly and reference your Estimate ID. -

    - -

    - Preferred contact method: -

    - - -
    -

    - For game, server, or mod-related projects, you can also join our Discord and mention your Estimate ID. -

    - - Join Discord -
    - -

    - โ† Back to Pricing -

    + +
    +

    โœ… Request Received

    +

    We received your estimate request.

    +
    +
    Your Estimate ID:
    +
    - -
    - Submitting this form does not create a contract or payment obligation. - Runlevel Systems will review your request and contact you before any paid work begins. - Any final price, timeline, or deliverable will be confirmed in writing before work starts. -
    + +

    Your client account:

    + - -
    + +

    Please check your email to verify your account before logging in.

    +

    Temporary password:

    +

    Please save this. You can change it later once account settings are added.

    +

    TODO: Add password reset flow and remove temporary-password delivery.

    - -
    โš ๏ธ
    + +

    We will review your request and contact you using the information provided.

    + + +
    + Game or server project? You can also join our Discord and mention your Estimate ID. + + Join Discord +
    -
    - + +
    + - + +
    + + + + + +
    -

    ๐Ÿ“‹ Your Contact Information

    -
    - - -
    -
    -
    - - -
    -
    - - -
    -
    -
    - - +

    Account Information

    +

    We will create a free client dashboard account so you can track this request, view estimates, and communicate about the project.

    +

    Already have an account? Log in and continue estimate

    +
    + +
    + - -
    -

    ๐Ÿ—‚๏ธ About Your Project

    -
    - - - - -
    -

    - For game, server, or mod-related projects, you can also join our Discord and mention your Estimate ID. -

    - - Join Discord -
    +
    +

    Contact Information

    +
    +
    + +
    -
    - - +
    + + +
    +
    +
    +
    + +
    -
    - - + +
    -
    -
    - - -
    -
    - - -
    -
    - -
    -

    ๐Ÿ“ Files & Code

    -
    - - -
    -
    - - -
    +
    + +
    - -
    -

    ๐Ÿ’ฌ Details & Budget

    -
    - - -
    -
    - - -
    +
    + Game or server project? You can also join our Discord and mention your Estimate ID. + + Join Discord
    +
    - -
    - > - +
    +

    Project Details

    +
    + + +
    +
    + +
    +
    + + +
    +
    - - + + -
    +
    @@ -392,27 +342,28 @@ function generateEstimateId() { diff --git a/includes/email.php b/includes/email.php new file mode 100644 index 0000000..5a4f829 --- /dev/null +++ b/includes/email.php @@ -0,0 +1,21 @@ +\r\n"; + + return @mail($to, (string)$subject, (string)$body, $headers); +} diff --git a/includes/footer.php b/includes/footer.php index 6ac4033..d82e86e 100644 --- a/includes/footer.php +++ b/includes/footer.php @@ -81,7 +81,7 @@