diff --git a/source/packages/plg_system_mokosuitefield/src/Helper/CustomerFeedbackHelper.php b/source/packages/plg_system_mokosuitefield/src/Helper/CustomerFeedbackHelper.php new file mode 100644 index 0000000..3536ba0 --- /dev/null +++ b/source/packages/plg_system_mokosuitefield/src/Helper/CustomerFeedbackHelper.php @@ -0,0 +1,128 @@ +get(DatabaseInterface::class); + + $db->setQuery($db->getQuery(true) + ->select('wo.id, wo.wo_number, wo.contact_id, cd.name AS customer_name, cd.email_to, cd.telephone') + ->from($db->quoteName('#__mokosuitefield_work_orders', 'wo')) + ->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = wo.contact_id') + ->where('wo.id = ' . (int) $woId)); + $wo = $db->loadObject(); + + if (!$wo || !$wo->email_to) { + return (object) ['success' => false, 'error' => 'No email for feedback request']; + } + + // Generate unique feedback token + $token = bin2hex(random_bytes(16)); + + $request = (object) [ + 'wo_id' => $woId, + 'contact_id' => $wo->contact_id, + 'token' => $token, + 'status' => 'sent', + 'sent_at' => Factory::getDate()->toSql(), + ]; + $db->insertObject('#__mokosuitefield_feedback_requests', $request, 'id'); + + return (object) ['success' => true, 'token' => $token, 'email' => $wo->email_to]; + } + + /** + * Submit feedback (called from public survey page via token). + */ + public static function submitFeedback(string $token, int $rating, int $npsScore, string $comments = ''): object + { + $db = Factory::getContainer()->get(DatabaseInterface::class); + + $db->setQuery($db->getQuery(true) + ->select('id, wo_id, contact_id, status') + ->from('#__mokosuitefield_feedback_requests') + ->where($db->quoteName('token') . ' = ' . $db->quote($token))); + $request = $db->loadObject(); + + if (!$request) return (object) ['success' => false, 'error' => 'Invalid feedback link']; + if ($request->status === 'completed') return (object) ['success' => false, 'error' => 'Feedback already submitted']; + + // Validate inputs + $rating = max(1, min(5, $rating)); + $npsScore = max(0, min(10, $npsScore)); + + $filter = \Joomla\Filter\InputFilter::getInstance(); + $comments = $filter->clean($comments, 'STRING'); + + $db->transactionStart(); + try { + // Record feedback + $feedback = (object) [ + 'request_id' => $request->id, + 'wo_id' => $request->wo_id, + 'contact_id' => $request->contact_id, + 'rating' => $rating, + 'nps_score' => $npsScore, + 'comments' => $comments, + 'submitted_at' => Factory::getDate()->toSql(), + ]; + $db->insertObject('#__mokosuitefield_feedback', $feedback); + + // Mark request as completed + $db->setQuery($db->getQuery(true) + ->update('#__mokosuitefield_feedback_requests') + ->set($db->quoteName('status') . ' = ' . $db->quote('completed')) + ->where('id = ' . (int) $request->id)); + $db->execute(); + + $db->transactionCommit(); + } catch (\Throwable $e) { + $db->transactionRollback(); + return (object) ['success' => false, 'error' => $e->getMessage()]; + } + + return (object) ['success' => true, 'rating' => $rating, 'nps' => $npsScore]; + } + + /** + * Get NPS score and satisfaction summary. + */ + public static function getSatisfactionSummary(string $from = '', string $to = ''): object + { + $db = Factory::getContainer()->get(DatabaseInterface::class); + $from = $from ?: date('Y-01-01'); + $to = $to ?: date('Y-m-d'); + + $db->setQuery($db->getQuery(true) + ->select('COUNT(*) AS total_responses') + ->select('COALESCE(AVG(rating), 0) AS avg_rating') + ->select('COALESCE(AVG(nps_score), 0) AS avg_nps') + ->select('SUM(CASE WHEN nps_score >= 9 THEN 1 ELSE 0 END) AS promoters') + ->select('SUM(CASE WHEN nps_score BETWEEN 7 AND 8 THEN 1 ELSE 0 END) AS passives') + ->select('SUM(CASE WHEN nps_score <= 6 THEN 1 ELSE 0 END) AS detractors') + ->from('#__mokosuitefield_feedback') + ->where('DATE(submitted_at) BETWEEN ' . $db->quote($from) . ' AND ' . $db->quote($to))); + + $stats = $db->loadObject() ?: (object) ['total_responses' => 0, 'avg_rating' => 0, 'avg_nps' => 0, 'promoters' => 0, 'passives' => 0, 'detractors' => 0]; + + $total = (int) $stats->total_responses; + $stats->nps_score = $total > 0 + ? round(((int) $stats->promoters - (int) $stats->detractors) / $total * 100) + : 0; + + return $stats; + } +}