UNPKG

@crazy-web/feedback

Version:

A feedback form handler package that shows a popup based on allowed URLs.

430 lines (422 loc) 23.5 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.manageSubmitANewIdeaButton = exports.submitIdea = exports.renderIdeaForm = exports.ideasContainer = void 0; exports.renderFeedbacks = renderFeedbacks; const config_1 = require("../core/lib/config"); const enum_1 = require("../core/lib/enum"); const fetcPublicFeedbacks_1 = require("../core/usecases/fetcPublicFeedbacks"); const handleSubmit_1 = require("../core/usecases/handleSubmit"); const feedbackDetails_1 = require("./feedbackDetails"); const checkIsDarkMode_1 = require("./reuseables/checkIsDarkMode"); const closeIcon_1 = require("./reuseables/closeIcon"); const featureCard_1 = require("./reuseables/featureCard"); const ideasContainer = (data, selectedCard) => __awaiter(void 0, void 0, void 0, function* () { const { orgData, form, config } = data; const { primaryColor = config_1.configData === null || config_1.configData === void 0 ? void 0 : config_1.configData.primaryColor, heading = config_1.configData.heading, theme = config_1.configData.theme } = config !== null && config !== void 0 ? config : {}; const accountId = orgData === null || orgData === void 0 ? void 0 : orgData.organizationId; const websiteId = orgData === null || orgData === void 0 ? void 0 : orgData.websiteId; const isDarkMode = (0, checkIsDarkMode_1.checkIsDarkMode)({ theme }); const container = document.createElement("div"); container.className = "prodio-feedback-hidden-scrollbar"; // container.style.cssText = "display:flex;flex-direction:column;gap:15px;overflow-y:auto;padding:1px;height:500px"; container.style.overflowY = "auto"; container.style.display = "flex"; container.style.flexDirection = "column"; container.style.gap = "15px"; container.style.padding = "1px"; container.style.height = "calc(100vh - 190px)"; const ideaForm = document.createElement("div"); ideaForm.innerHTML = (0, exports.renderIdeaForm)(data); container.appendChild(ideaForm); const prodioTabContent = document.getElementById("prodio-tab-content"); prodioTabContent === null || prodioTabContent === void 0 ? void 0 : prodioTabContent.appendChild(container); const fields = ["slider-title", "slider-type", "slider-description"]; fields.forEach((field) => { const input = document.getElementById(field); const label = document.querySelector(`label[for="${field}"]`); if (input && label) { input.addEventListener("focus", () => { label.style.color = primaryColor; input.style.outline = "none"; // Removes focus outline input.style.border = "none"; // Removes border }); const labelColor = isDarkMode ? "#98989E" : "#6B7280"; input.addEventListener("blur", () => { label.style.color = labelColor; input.style.border = "none"; // Ensure border stays removed after blur }); } }); renderFeedbacks(accountId, websiteId, false, primaryColor, isDarkMode); // to submit the ideas (0, exports.submitIdea)({ orgData, primaryColor, isDarkMode }); // ✅to manage the submit a new idea button (0, exports.manageSubmitANewIdeaButton)(); // to manage click on the ideas feedback card (0, feedbackDetails_1.onFeedbackCardClick)(selectedCard, data, primaryColor, isDarkMode); }); exports.ideasContainer = ideasContainer; const renderIdeaForm = (data) => { const { orgData, form, config } = data; const { title = config_1.formMetaData === null || config_1.formMetaData === void 0 ? void 0 : config_1.formMetaData.title, description = config_1.formMetaData === null || config_1.formMetaData === void 0 ? void 0 : config_1.formMetaData.description } = form !== null && form !== void 0 ? form : {}; const { primaryColor = config_1.configData === null || config_1.configData === void 0 ? void 0 : config_1.configData.primaryColor, heading = config_1.configData.heading, theme = config_1.configData.theme } = config !== null && config !== void 0 ? config : {}; const isDarkMode = (0, checkIsDarkMode_1.checkIsDarkMode)({ theme }); const backgroundColor = "transparent"; const textColor = "#000"; const labelColor = isDarkMode ? "#98989E" : "#6B7280"; const fieldBg = isDarkMode ? "#222429" : "#FFFFFF"; const fieldTextColor = isDarkMode ? "#FAFAFA" : "#303540"; const fieldBorder = isDarkMode ? "1px solid #52526F40" : "1px solid #E5E7EB"; // <span class="prodio-feedback-form-description" style="color:#4747474">${description}</span> return ` <div id="form-container" class="prodio-feedback-form-container" style="position: relative;"> <span id="close-form-btn" type="button" style="position: absolute; top: 10px; right: 10px; background: transparent; border: none; cursor: pointer; font-size: 16px;"> ${(0, closeIcon_1.renderCloseIcon)({ size: "20px", stroke: isDarkMode ? "#9FA1A7" : "#718096" })} </span> <div id="feedback-form" style="display:flex;flex-direction:column;gap:15px;"> <h2 class="prodio-feedback-form-heading" style="color:${isDarkMode ? "#D2D3E0" : "#303540"};font-weight:bold;margin:0px;padding:0px;">${title}</h2> <hr style="height:0.2px;border:none; background: ${isDarkMode ? "#313337" : "#31333740"};"/> <div style="display: flex; flex-direction: column; gap: 8px;border-radius:2px;width: 100%;"> <label for="slider-type" style="display: block; text-align: left; font-size:13px; font-weight:550;color:${labelColor};">Type</label> <select id="slider-type" style="height:30px;padding: 6px 8px;font-size:12px; border-radius: 5px; background: ${fieldBg}; color: ${fieldTextColor}; border:${fieldBorder};" > ${enum_1.FeedbackTypesArray.map((option) => (`<option value="${option === null || option === void 0 ? void 0 : option.value}">${option === null || option === void 0 ? void 0 : option.label}</option>`))} </select> </div> <div style="display: flex; flex-direction: column; gap: 8px;border-radius:2px;width: 100%;"> <label for="slider-title" style="display: block; text-align: left; font-size:13px; font-weight:550;color:${labelColor};">Title</label> <input type="text" id="slider-title" placeholder="Enter a descriptive title" autocomplete="off" style="padding: 6px 8px;font-size:12px; border-radius: 5px; background: ${fieldBg}; color: ${fieldTextColor};border:${fieldBorder};" /> </div> <div style="display: flex; flex-direction: column; gap: 8px;border-radius:2px;width: 100%;"> <label for="slider-description" style="display: block; text-align: left; font-size:13px; font-weight:550;color:${labelColor};">Description</label> <textarea id="slider-description" placeholder="Enter a description" autocomplete="off" rows="5" style="padding: 6px 8px;font-size:12px; border-radius: 5px; background: ${fieldBg}; color: ${fieldTextColor}; resize: none;border:${fieldBorder};" ></textarea> </div> <span id="slider-error" style="color: red; display: none; font-size:12px;">All fields are required</span> <div style="display: flex;justify-content: start;gap: 12px;"> <button id="slider-submit" style="padding: 5px 20px; background-color: ${primaryColor}; border: none; border-radius: 5px; color: #fff; font-size: 14px; cursor: pointer;"> Submit </button> </div> </div> </div> <div id="feedback-list"></div> <div id="feedback-details" style="display: none;"></div> <div id="reopen-form-btn-container" style="display: none; position: fixed; bottom: 90px; margin:0 0 0 -15px; z-index: 12;"> <div style="display:flex; justify-content:center;width:400px; padding :10px 0;"> <button id="reopen-form-btn" style=" background-color: ${primaryColor}; color: white; border: none; border-radius: 5px; padding: 12px 20px; font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 8px; box-shadow: 0 4px 14px rgba(79, 70, 229, 0.3); cursor: pointer; transition: all 0.2s ease; z-index: 10; " > <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5" /> <path d="M9 18h6" /> <path d="M10 22h4" /> </svg> SUGGEST A NEW IDEA </button> </div> </div> `; }; exports.renderIdeaForm = renderIdeaForm; const submitIdea = ({ orgData, primaryColor, isDarkMode }) => { var _a; const inputs = [ document.getElementById("slider-type"), document.getElementById("slider-title"), document.getElementById("slider-description") ]; inputs.forEach(input => { input.addEventListener("input", () => { input.style.border = "none"; document.getElementById("slider-error").style.display = "none"; }); }); (_a = document.getElementById("slider-submit")) === null || _a === void 0 ? void 0 : _a.addEventListener("click", () => __awaiter(void 0, void 0, void 0, function* () { var _a, _b, _c; const submitButton = document.getElementById("slider-submit"); const type = document.getElementById("slider-type").value; const title = document.getElementById("slider-title").value; const description = document.getElementById("slider-description").value; const accountId = orgData === null || orgData === void 0 ? void 0 : orgData.organizationId; const websiteId = orgData === null || orgData === void 0 ? void 0 : orgData.websiteId; // let hasError = false; // inputs.forEach(input => { // if (!input.value.trim()) { // input.style.border = "1px solid red"; // hasError = true; // } // }); // if (hasError) { // document.getElementById("slider-error")!.style.display = "block"; // return; // } if (!title.trim() || !description.trim()) { document.getElementById("slider-error").style.display = "block"; return; } submitButton.disabled = true; submitButton.classList.add("prodio-disabled-btn"); const website = window.location.hostname; const dataToSend = { title: title, description: description !== null && description !== void 0 ? description : "", type: type, // How we will define its a type of feature, tasks, or bug, we can add more types here. feedback_id: "", source: { id: website, name: "slider", //popup type: "Website", }, user_id: (_a = orgData === null || orgData === void 0 ? void 0 : orgData.userData) === null || _a === void 0 ? void 0 : _a.id, user_name: (_b = orgData === null || orgData === void 0 ? void 0 : orgData.userData) === null || _b === void 0 ? void 0 : _b.name, user_email: (_c = orgData === null || orgData === void 0 ? void 0 : orgData.userData) === null || _c === void 0 ? void 0 : _c.email, created_at: new Date(), updated_at: new Date(), last_retrieved_message: false, account_id: orgData === null || orgData === void 0 ? void 0 : orgData.organizationId, meta_data: null, is_public: false, // false website_id: orgData === null || orgData === void 0 ? void 0 : orgData.websiteId, submission_mode: enum_1.SubmissionMode.feedback }; const res = yield (0, handleSubmit_1.handleSubmit)(dataToSend); if (res.error) { document.getElementById("slider-error").textContent = res.error; document.getElementById("slider-error").style.display = "block"; } else { renderFeedbacks(accountId, websiteId, true, primaryColor, isDarkMode); document.getElementById("slider-title").value = ""; document.getElementById("slider-description").value = ""; } submitButton.disabled = false; submitButton.classList.remove("prodio-disabled-btn"); })); }; exports.submitIdea = submitIdea; // export async function renderFeedbacks(accountId: string, resetData:boolean = false) { // try { // let currentPage = 1; // let isFetching = false; // let hasMoreData = true; // let isListenerAdded = false; // const container = document.querySelector(".prodio-feedback-hidden-scrollbar"); // const feedbackContainer = document.getElementById("feedback-list") // if (!container || !feedbackContainer) return; // if (resetData) { // currentPage = 1; // hasMoreData = true; // feedbackContainer.innerHTML = ""; // Clear previous feedbacks // } // const loadMoreFeedbacks = async () => { // if (isFetching || !hasMoreData) return; // isFetching = true; // const res = await fetchPublicFeedbacks({ organizationId: accountId, page: currentPage }); // const feedbacks = res?.data || []; // if (feedbacks.length > 0) { // const fragment = document.createDocumentFragment(); // feedbacks.forEach((feedback: any, index: number) => { // const card = document.createElement("div"); // card.innerHTML = FeatureCards(feedback, index, "card"); // card.setAttribute("data-feedback", JSON.stringify(feedback)); // Store ID for delegation // fragment.appendChild(card); // }); // feedbackContainer.style.cssText = "border: 1px solid #e5e7eb; border-radius: 12px;overflow: hidden;background-color: white;box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);" // feedbackContainer.appendChild(fragment); // Append all at once // currentPage++; // Move to next page // } else { // hasMoreData = false; // Stop fetching if no more data // } // isFetching = false; // }; // await loadMoreFeedbacks(); // Load initial feedbacks // const handleScroll = async () => { // const nearBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 20; // if (nearBottom) { // await loadMoreFeedbacks(); // } // }; // setTimeout(() => { // container.addEventListener("scroll", handleScroll); // }, 100); // } catch (error) { // console.error("Error loading feedbacks:", error); // } // } function renderFeedbacks(accountId_1, websiteId_1) { return __awaiter(this, arguments, void 0, function* (accountId, websiteId, resetData = false, primaryColor, isDarkMode) { try { let currentPage = 1; let isFetching = false; let hasMoreData = true; let isFirstLoad = true; // Flag to track first load const container = document.querySelector(".prodio-feedback-hidden-scrollbar"); const feedbackContainer = document.getElementById("feedback-list"); if (!container || !feedbackContainer) return; if (resetData) { currentPage = 1; hasMoreData = true; feedbackContainer.innerHTML = ""; // Clear previous feedbacks } if (isFirstLoad) { feedbackContainer.innerHTML = ` <div class="prodio-spinner-container"> <div class="prodio-spinner"></div> </div> `; } // const loadMoreFeedbacks = async () => { // if (isFetching || !hasMoreData) return; // isFetching = true; // const res = await fetchPublicFeedbacks({ organizationId: accountId, page: currentPage }); // const feedbacks = res?.data || []; // feedbackContainer.innerHTML = ""; // Remove loader on first load // isFirstLoad = false; // Set flag to false after first fetch // if (feedbacks.length > 0) { // const fragment = document.createDocumentFragment(); // feedbacks.forEach((feedback: any, index: number) => { // const card = document.createElement("div"); // card.innerHTML = FeatureCards(feedback, index, "card"); // card.setAttribute("data-feedback", JSON.stringify(feedback)); // Store ID for delegation // fragment.appendChild(card); // }); // feedbackContainer.style.cssText = "border: 1px solid #e5e7eb; border-radius: 12px;overflow: hidden;background-color: white;box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);"; // feedbackContainer.appendChild(fragment); // Append all at once // currentPage++; // Move to next page // } else if (currentPage === 1) { // hasMoreData = false; // // const noFeedbackMessage = document.createElement("div"); // // noFeedbackMessage.className = "no-feedback-message"; // // noFeedbackMessage.textContent = "No ideas found."; // // feedbackContainer.appendChild(noFeedbackMessage); // } else { // hasMoreData = false; // } // isFetching = false; // }; const loadMoreFeedbacks = () => __awaiter(this, void 0, void 0, function* () { if (isFetching || !hasMoreData) return; isFetching = true; const res = yield (0, fetcPublicFeedbacks_1.fetchPublicFeedbacks)({ organizationId: accountId, page: currentPage, websiteId: websiteId }); const feedbacks = (res === null || res === void 0 ? void 0 : res.data) || []; if (isFirstLoad) { feedbackContainer.innerHTML = ""; // Remove loader only on first load isFirstLoad = false; } if (feedbacks.length > 0) { const fragment = document.createDocumentFragment(); feedbacks.forEach((feedback, index) => { const card = document.createElement("div"); card.innerHTML = (0, featureCard_1.FeatureCards)(feedback, index, "card", primaryColor, 1, isDarkMode); card.setAttribute("data-feedback", JSON.stringify(feedback)); card.setAttribute("data-feedback-id", feedback === null || feedback === void 0 ? void 0 : feedback.id); fragment.appendChild(card); }); // feedbackContainer.style.cssText = "border: 1px solid #e5e7eb; border-radius: 12px;overflow: hidden;background-color: white;box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);"; feedbackContainer.style.cssText = "display:flex;flex-direction:column;gap:15px;overflow-y:hidden;"; feedbackContainer.appendChild(fragment); // Append all at once currentPage++; } else { if (currentPage === 1) { feedbackContainer.innerHTML = `<div class="prodio-no-data">No ideas found.</div>`; } hasMoreData = false; } isFetching = false; }); yield loadMoreFeedbacks(); // Load initial feedbacks const handleScroll = () => __awaiter(this, void 0, void 0, function* () { const nearBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 20; if (nearBottom) { yield loadMoreFeedbacks(); } }); setTimeout(() => { container.addEventListener("scroll", handleScroll); }, 100); } catch (error) { console.error("Error loading feedbacks:", error); } }); } const manageSubmitANewIdeaButton = () => { setTimeout(() => { const formContainer = document.getElementById("form-container"); const closeFormBtn = document.getElementById("close-form-btn"); const reopenFormBtnContainer = document.getElementById("reopen-form-btn-container"); const reopenFormBtn = document.getElementById("reopen-form-btn"); if (closeFormBtn && reopenFormBtnContainer && formContainer && reopenFormBtn) { closeFormBtn.addEventListener("click", function () { formContainer.style.display = "none"; // Hide form reopenFormBtnContainer.style.display = "block"; // Show reopen button }); reopenFormBtnContainer.addEventListener("click", function () { formContainer.style.display = "block"; // Show form reopenFormBtnContainer.style.display = "none"; // Hide reopen button }); reopenFormBtn.addEventListener("mouseenter", () => { reopenFormBtn.style.transform = "translateY(-2px)"; reopenFormBtn.style.boxShadow = "0 6px 18px rgba(79, 70, 229, 0.4)"; }); reopenFormBtn.addEventListener("mouseleave", () => { reopenFormBtn.style.transform = "translateY(0)"; reopenFormBtn.style.boxShadow = "0 4px 14px rgba(79, 70, 229, 0.3)"; }); const scrollableDiv = document.querySelector(".prodio-feedback-hidden-scrollbar"); // Scroll event to show/hide reopenFormBtnContainer based on scroll height if (scrollableDiv) { scrollableDiv.addEventListener("scroll", function () { const scrollPosition = scrollableDiv.scrollTop; const triggerHeight = 400; if (scrollPosition <= triggerHeight && formContainer.style.display === "none") { reopenFormBtnContainer.style.display = "block"; // Show when at 130vh } else { reopenFormBtnContainer.style.display = "none"; // Hide otherwise } }); } } }, 0); }; exports.manageSubmitANewIdeaButton = manageSubmitANewIdeaButton;