diff --git a/controllers/admin/Admin_termination_config_controller.js b/controllers/admin/Admin_termination_config_controller.js new file mode 100644 index 0000000..6d4738b --- /dev/null +++ b/controllers/admin/Admin_termination_config_controller.js @@ -0,0 +1,175 @@ +const SessionService = require("../../services/SessionService"); +const app = require("express").Router(); +const db = require("../../models"); +const role = 1; + +// List termination configurations +app.get( + "/admin/termination-config", + SessionService.verifySessionMiddleware(role, "admin"), + async function (req, res, next) { + try { + const configs = await db.termination_config.findAll(); + res.render("admin/Termination_Config", { + configs, + get_page_name: () => "Termination Configuration", + _base_url: "/admin/termination-config", + }); + } catch (error) { + console.log(error); + res + .status(500) + .json({ + success: false, + message: "Failed to fetch termination configurations", + }); + } + } +); + +// Get termination configuration by ID +app.get( + "/admin/termination-config/:id", + SessionService.verifySessionMiddleware(role, "admin"), + async function (req, res, next) { + try { + const { id } = req.params; + const config = await db.termination_config.getByPK(id); + if (!config) { + return res + .status(404) + .json({ + success: false, + message: "Termination configuration not found", + }); + } + return res.json({ success: true, payload: config }); + } catch (error) { + console.log(error); + return res + .status(500) + .json({ + success: false, + message: "Failed to fetch termination configuration", + }); + } + } +); + +// Create new termination configuration +app.post( + "/admin/termination-config", + SessionService.verifySessionMiddleware(role, "admin"), + async function (req, res, next) { + try { + const { message, title, counter } = req.body; + const config = await db.termination_config.create({ + message, + title, + counter: parseInt(counter), + }); + return res.json({ + success: true, + payload: config, + message: "Termination configuration created successfully", + }); + } catch (error) { + console.log(error); + return res + .status(500) + .json({ + success: false, + message: "Failed to create termination configuration", + }); + } + } +); + +// Update termination configuration +app.put( + "/admin/termination-config/:id", + SessionService.verifySessionMiddleware(role, "admin"), + async function (req, res, next) { + try { + const { id } = req.params; + const { message, title, counter } = req.body; + const config = await db.termination_config.edit( + { + message, + title, + counter: parseInt(counter), + }, + id + ); + return res.json({ + success: true, + payload: config, + message: "Termination configuration updated successfully", + }); + } catch (error) { + console.log(error); + return res + .status(500) + .json({ + success: false, + message: "Failed to update termination configuration", + }); + } + } +); + +// Delete termination configuration +app.delete( + "/admin/termination-config/:id", + SessionService.verifySessionMiddleware(role, "admin"), + async function (req, res, next) { + try { + const { id } = req.params; + await db.termination_config.destroy(id); + return res.json({ + success: true, + message: "Termination configuration deleted successfully", + }); + } catch (error) { + console.log(error); + return res + .status(500) + .json({ + success: false, + message: "Failed to delete termination configuration", + }); + } + } +); + +// Get default termination configuration (for frontend) +app.get("/api/termination-config", async function (req, res, next) { + try { + // Get the first configuration or create a default one + let config = await db.termination_config.findOne(); + if (!config) { + // Create default configuration if none exists + config = await db.termination_config.create({ + message: + "You have an allergy to one of the main ingredients in our system. Our current system will not suit you.", + title: "Quiz will now be terminated", + counter: 10, + }); + } + return res.json({ success: true, payload: config }); + } catch (error) { + console.log(error); + // Return default values if database fails + return res.json({ + success: true, + payload: { + message: + "You have an allergy to one of the main ingredients in our system. Our current system will not suit you.", + title: "Quiz will now be terminated", + counter: 10, + }, + }); + } +}); + +module.exports = app; diff --git a/controllers/index.js b/controllers/index.js index fa5e1cf..4b4115c 100644 --- a/controllers/index.js +++ b/controllers/index.js @@ -7,6 +7,7 @@ const AdminActivesController = require("./admin/Admin_actives_controller"); const AdminOutputVariablesController = require("./admin/Admin_output_variables_controller"); const AdminResultProfileController = require("./admin/Admin_result_profile_controller"); const AdminGetProfileSystem = require("./admin/getProfileSystem"); +const AdminTerminationConfigController = require("./admin/Admin_termination_config_controller"); const PublicIndex = require("./public/index"); const AdminAnswerController = require("./admin/Admin_answer_controller"); const AdminDashboard = require("./admin/Dashboard"); @@ -20,6 +21,7 @@ module.exports = [ ShopifyWebhook, AdminResultProfileController, AdminGetProfileSystem, + AdminTerminationConfigController, AdminUserController, AdminQuizController, AdminQuestionController, diff --git a/models/termination_config.js b/models/termination_config.js new file mode 100644 index 0000000..a64e4c8 --- /dev/null +++ b/models/termination_config.js @@ -0,0 +1,125 @@ +/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2021*/ +/** + * termination_config Model + * @copyright 2021 Manaknightdigital Inc. + * @link https://manaknightdigital.com + * @license Proprietary Software licensing + * @author Ryan Wong + * + */ + +const moment = require("moment"); +const bcrypt = require("bcryptjs"); +const { Op } = require("sequelize"); +const { intersection } = require("lodash"); +const coreModel = require("./../core/models"); + +module.exports = (sequelize, DataTypes) => { + const TerminationConfig = sequelize.define( + "termination_config", + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + message: { + type: DataTypes.TEXT, + allowNull: false, + defaultValue: + "You have an allergy to one of the main ingredients in our system. Our current system will not suit you.", + }, + title: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "Quiz will now be terminated", + }, + counter: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 10, + }, + created_at: DataTypes.DATEONLY, + updated_at: DataTypes.DATE, + }, + { + timestamps: true, + freezeTableName: true, + tableName: "termination_config", + }, + { + underscoredAll: false, + underscored: false, + } + ); + + coreModel.call(this, TerminationConfig); + + TerminationConfig._preCreateProcessing = function (data) { + return data; + }; + TerminationConfig._postCreateProcessing = function (data) { + return data; + }; + TerminationConfig._customCountingConditions = function (data) { + return data; + }; + + TerminationConfig._filterAllowKeys = function (data) { + let cleanData = {}; + let allowedFields = TerminationConfig.allowFields(); + allowedFields.push(TerminationConfig._primaryKey()); + + for (const key in data) { + if (allowedFields.includes(key)) { + cleanData[key] = data[key]; + } + } + return cleanData; + }; + + TerminationConfig.associate = function (models) {}; + + TerminationConfig.allowFields = function () { + return ["id", "message", "title", "counter"]; + }; + + TerminationConfig.labels = function () { + return [ + "ID", + "Termination Message", + "Termination Title", + "Counter (seconds)", + ]; + }; + + TerminationConfig.validationRules = function () { + return [ + ["id", "ID", ""], + ["message", "Termination Message", "required"], + ["title", "Termination Title", "required"], + ["counter", "Counter", "required|integer|min:1|max:60"], + ]; + }; + + TerminationConfig.validationEditRules = function () { + return [ + ["id", "ID", ""], + ["message", "Termination Message", "required"], + ["title", "Termination Title", "required"], + ["counter", "Counter", "required|integer|min:1|max:60"], + ]; + }; + + // ex + TerminationConfig.intersection = function (fields) { + if (fields) { + return intersection( + ["id", "message", "title", "counter", "created_at", "updated_at"], + Object.keys(fields) + ); + } else return []; + }; + + return TerminationConfig; +}; diff --git a/public/frontend_css/style.css b/public/frontend_css/style.css index 887edd9..14c7d76 100644 --- a/public/frontend_css/style.css +++ b/public/frontend_css/style.css @@ -906,10 +906,16 @@ input::-ms-input-placeholder { #response-timer { display: block; text-align: center; - font-size: 2rem; - margin-top: 24px; - font-weight: bold; - color: #222; + font-size: 3.5rem; + margin: 32px auto; + font-weight: 700; + color: #231f20; + font-family: "Necto Mono", monospace; + background: #f3f4ee; + padding: 16px 32px; + border-top: 1px solid #101010; + border-radius: 0; + width: 70%; } /*page 5 ends here*/ diff --git a/public/frontend_js/quiz-test.js b/public/frontend_js/quiz-test.js index d926ec4..e34f21b 100644 --- a/public/frontend_js/quiz-test.js +++ b/public/frontend_js/quiz-test.js @@ -2171,34 +2171,77 @@ function handleImageMissing(self) { } // Implement checkAllergie for Quiz 4 -function checkAllergie(self) { +async function checkAllergie(self) { const value = self.dataset.val || $(self).data("val"); if (["Banana", "Olive", "Sunflowers"].includes(value)) { - // Simulate API fetch for message and counter - const terminationMessage = - "You have an allergy to one of the main ingredients in our system. Our current system will not suit you."; - const terminationTitle = "Quiz will now be terminated"; - let counter = 10; // seconds (simulate API value) - $("#termination-overlay .termination-message").text(terminationMessage); - $("#termination-overlay .termination-title").text(terminationTitle); - $("#termination-overlay .termination-counter").text(counter); - $("#termination-overlay").css("display", "flex"); - let interval = setInterval(() => { - counter--; - $("#termination-overlay .termination-counter").text(counter); - if (counter <= 0) { - clearInterval(interval); - window.location.href = "/"; + try { + // Fetch termination configuration from API + const response = await fetch("/api/termination-config"); + const data = await response.json(); + + if (data.success) { + const config = data.payload; + const terminationMessage = config.message; + const terminationTitle = config.title; + let counter = parseInt(config.counter); + + $("#termination-overlay .termination-message").text(terminationMessage); + $("#termination-overlay .termination-title").text(terminationTitle); + $("#termination-overlay .termination-counter").text(counter); + $("#termination-overlay").css("display", "flex"); + + let interval = setInterval(() => { + counter--; + $("#termination-overlay .termination-counter").text(counter); + if (counter <= 0) { + clearInterval(interval); + window.location.href = "/"; + } + }, 1000); + + // Block all interaction + $("body > *:not(#termination-overlay)").css("pointer-events", "none"); + return; + } else { + // Fallback to default values if API fails + console.error("Failed to fetch termination config:", data.message); + showDefaultTermination(); } - }, 1000); - // Block all interaction - $("body > *:not(#termination-overlay)").css("pointer-events", "none"); + } catch (error) { + console.error("Error fetching termination config:", error); + // Fallback to default values if API fails + showDefaultTermination(); + } return; } // If not terminating, proceed as normal (select/deselect logic) // (existing selection logic is handled by the .selectionBtn click handler) } +function showDefaultTermination() { + const terminationMessage = + "You have an allergy to one of the main ingredients in our system. Our current system will not suit you."; + const terminationTitle = "Quiz will now be terminated"; + let counter = 10; + + $("#termination-overlay .termination-message").text(terminationMessage); + $("#termination-overlay .termination-title").text(terminationTitle); + $("#termination-overlay .termination-counter").text(counter); + $("#termination-overlay").css("display", "flex"); + + let interval = setInterval(() => { + counter--; + $("#termination-overlay .termination-counter").text(counter); + if (counter <= 0) { + clearInterval(interval); + window.location.href = "/"; + } + }, 1000); + + // Block all interaction + $("body > *:not(#termination-overlay)").css("pointer-events", "none"); +} + $(document).on("click", "#resetQuizButton", async function () { if ( confirm( diff --git a/views/admin/Termination_Config.eta b/views/admin/Termination_Config.eta new file mode 100644 index 0000000..de62e2c --- /dev/null +++ b/views/admin/Termination_Config.eta @@ -0,0 +1,281 @@ +<% if(it.layout_clean_mode) {%> <% layout("../layouts/admin/Clean", {get_page_name:()=> "Termination Configuration"}) %><% } else {%> <% layout("../layouts/admin/Main",{get_page_name:()=> "Termination Configuration"}) %><%}%> + + + +
+ + diff --git a/views/partials/admin/Nav.eta b/views/partials/admin/Nav.eta index ba8d4cc..181f92f 100644 --- a/views/partials/admin/Nav.eta +++ b/views/partials/admin/Nav.eta @@ -15,6 +15,7 @@