false, 'ERROR' => 'missing_token', 'MESSAGE' => 'Rating token is required.']); } $qRating = queryOne(" SELECT r.*, t.Title, u_for.FirstName AS ForFirstName, u_for.LastName AS ForLastName, u_by.FirstName AS ByFirstName, u_by.LastName AS ByLastName FROM TaskRatings r JOIN Tasks t ON t.ID = r.TaskID LEFT JOIN Users u_for ON u_for.ID = r.ForUserID LEFT JOIN Users u_by ON u_by.ID = r.ByUserID WHERE r.AccessToken = ? LIMIT 1 ", [$token]); if (!$qRating) { apiAbort(['OK' => false, 'ERROR' => 'invalid_token', 'MESSAGE' => 'Rating not found or link is invalid.']); } if (strtotime($qRating['ExpiresOn']) < time()) { apiAbort(['OK' => false, 'ERROR' => 'expired', 'MESSAGE' => 'This rating link has expired.']); } if (!empty($qRating['CompletedOn'])) { apiAbort(['OK' => false, 'ERROR' => 'already_submitted', 'MESSAGE' => 'This rating has already been submitted.']); } // Check if this is a submission or info request $isSubmission = isset($data['onTime']) || isset($data['completedScope']) || isset($data['requiredFollowup']) || isset($data['continueAllow']) || isset($data['prepared']) || isset($data['respectful']) || isset($data['wouldAutoAssign']); if (!$isSubmission) { $result = [ 'OK' => true, 'RatingID' => (int) $qRating['ID'], 'Direction' => $qRating['Direction'], 'Title' => $qRating['Title'], 'ForUserName' => trim($qRating['ForFirstName'] . ' ' . $qRating['ForLastName']), 'ExpiresOn' => toISO8601($qRating['ExpiresOn']), ]; if ($qRating['Direction'] === 'customer_rates_worker' || $qRating['Direction'] === 'admin_rates_worker') { $result['Questions'] = [ 'onTime' => 'Was the worker on time?', 'completedScope' => 'Was the scope completed?', 'requiredFollowup' => 'Was follow-up required?', 'continueAllow' => 'Continue to allow these tasks?', ]; } elseif ($qRating['Direction'] === 'worker_rates_customer') { $result['Questions'] = [ 'prepared' => 'Was the customer prepared?', 'completedScope' => 'Was the scope clear?', 'respectful' => 'Was the customer respectful?', 'wouldAutoAssign' => 'Would you serve this customer again?', ]; } jsonResponse($result); } // Process submission $ratingId = (int) $qRating['ID']; if ($qRating['Direction'] === 'customer_rates_worker' || $qRating['Direction'] === 'admin_rates_worker') { queryTimed(" UPDATE TaskRatings SET OnTime = ?, CompletedScope = ?, RequiredFollowup = ?, ContinueAllow = ?, CompletedOn = NOW() WHERE ID = ? ", [ isset($data['onTime']) ? ($data['onTime'] ? 1 : 0) : null, isset($data['completedScope']) ? ($data['completedScope'] ? 1 : 0) : null, isset($data['requiredFollowup']) ? ($data['requiredFollowup'] ? 1 : 0) : null, isset($data['continueAllow']) ? ($data['continueAllow'] ? 1 : 0) : null, $ratingId, ]); } elseif ($qRating['Direction'] === 'worker_rates_customer') { queryTimed(" UPDATE TaskRatings SET Prepared = ?, CompletedScope = ?, Respectful = ?, WouldAutoAssign = ?, CompletedOn = NOW() WHERE ID = ? ", [ isset($data['prepared']) ? ($data['prepared'] ? 1 : 0) : null, isset($data['completedScope']) ? ($data['completedScope'] ? 1 : 0) : null, isset($data['respectful']) ? ($data['respectful'] ? 1 : 0) : null, isset($data['wouldAutoAssign']) ? ($data['wouldAutoAssign'] ? 1 : 0) : null, $ratingId, ]); } jsonResponse(['OK' => true, 'MESSAGE' => 'Rating submitted successfully. Thank you for your feedback!']); } catch (Exception $e) { jsonResponse(['OK' => false, 'ERROR' => 'server_error', 'MESSAGE' => 'Error submitting rating', 'DETAIL' => $e->getMessage()]); }