@crazy-web/feedback
Version:
A feedback form handler package that shows a popup based on allowed URLs.
430 lines (422 loc) • 23.5 kB
JavaScript
;
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;