itsluckysharma01's picture
Clean deployment
4ed7d03
/**
* NETRA UI Utilities
* Enhanced interactivity and utility functions for frontend
*/
// Toast Notification System
class Toast {
constructor() {
this.container = this.ensureContainer();
}
ensureContainer() {
let container = document.getElementById("toast-container");
if (!container) {
container = document.createElement("div");
container.id = "toast-container";
document.body.appendChild(container);
}
return container;
}
show(message, type = "info", duration = 3000) {
const toast = document.createElement("div");
toast.className = `toast ${type}`;
toast.textContent = message;
this.container.appendChild(toast);
// Auto remove after duration
setTimeout(() => {
toast.style.animation = "slideInRight 0.3s ease reverse";
setTimeout(() => toast.remove(), 300);
}, duration);
return toast;
}
success(message, duration = 3000) {
return this.show(message, "success", duration);
}
error(message, duration = 3000) {
return this.show(message, "error", duration);
}
warning(message, duration = 3000) {
return this.show(message, "warning", duration);
}
info(message, duration = 3000) {
return this.show(message, "info", duration);
}
}
// Modal Dialog System
class Modal {
constructor(title, content, options = {}) {
this.title = title;
this.content = content;
this.options = {
closeButton: true,
footer: true,
size: "medium",
onConfirm: null,
onCancel: null,
...options,
};
this.element = null;
this.overlay = null;
}
create() {
// Create overlay
this.overlay = document.createElement("div");
this.overlay.className = "modal-overlay";
// Create modal
this.element = document.createElement("div");
this.element.className = `modal modal-${this.options.size}`;
// Create header
const header = document.createElement("div");
header.className = "modal-header";
header.innerHTML = `<h2 class="modal-title">${this.title}</h2>`;
if (this.options.closeButton) {
const closeBtn = document.createElement("button");
closeBtn.className = "modal-close";
closeBtn.innerHTML = "&times;";
closeBtn.onclick = () => this.close();
header.appendChild(closeBtn);
}
// Create body
const body = document.createElement("div");
body.className = "modal-body";
if (typeof this.content === "string") {
body.innerHTML = this.content;
} else {
body.appendChild(this.content);
}
// Create footer
let footer = "";
if (this.options.footer) {
footer = `
<div class="modal-footer">
<button class="btn btn-secondary" onclick="this.closest('.modal').parentElement.nextElementSibling.click()">Cancel</button>
<button class="btn btn-primary" onclick="document.querySelector('.modal-confirm')?.click?.()">Confirm</button>
</div>
`;
}
this.element.appendChild(header);
this.element.appendChild(body);
if (footer) {
this.element.innerHTML += footer;
}
this.overlay.appendChild(this.element);
document.body.appendChild(this.overlay);
// Setup click handler for overlay (close on outside click)
this.overlay.addEventListener("click", (e) => {
if (e.target === this.overlay) {
this.close();
}
});
return this;
}
show() {
if (!this.element) this.create();
this.overlay.classList.add("open");
return this;
}
close() {
if (this.overlay) {
this.overlay.classList.remove("open");
setTimeout(() => {
this.overlay?.remove();
this.element = null;
this.overlay = null;
}, 300);
}
return this;
}
static confirm(title, message, onConfirm, onCancel) {
const modal = new Modal(title, `<p>${message}</p>`, {
footer: true,
onConfirm,
onCancel,
});
modal.show();
return modal;
}
static alert(title, message) {
const modal = new Modal(title, `<p>${message}</p>`, {
footer: false,
});
modal.show();
return modal;
}
}
// Loading State Manager
class LoadingState {
static setLoading(element, isLoading = true) {
if (!element) return;
if (isLoading) {
element.classList.add("loading");
element.disabled = true;
const originalText = element.textContent;
element.setAttribute("data-original-text", originalText);
element.innerHTML = `<span class="spinner"></span> Loading...`;
} else {
element.classList.remove("loading");
element.disabled = false;
const originalText =
element.getAttribute("data-original-text") || element.textContent;
element.textContent = originalText;
}
}
static setLoadingMultiple(elements, isLoading = true) {
elements.forEach((el) => this.setLoading(el, isLoading));
}
}
// Form Validation
class FormValidator {
static validate(form) {
const errors = {};
const inputs = form.querySelectorAll("input, textarea, select");
inputs.forEach((input) => {
const error = this.validateInput(input);
if (error) {
errors[input.name] = error;
}
});
return {
isValid: Object.keys(errors).length === 0,
errors,
};
}
static validateInput(input) {
if (input.hasAttribute("required") && !input.value.trim()) {
return `${input.name || "This field"} is required`;
}
if (input.type === "email" && input.value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(input.value)) {
return "Please enter a valid email address";
}
}
if (input.minLength && input.value.length < input.minLength) {
return `Minimum length is ${input.minLength} characters`;
}
if (input.maxLength && input.value.length > input.maxLength) {
return `Maximum length is ${input.maxLength} characters`;
}
return null;
}
static showErrors(form, errors) {
// Clear previous errors
form.querySelectorAll(".error-message").forEach((el) => el.remove());
Object.entries(errors).forEach(([name, message]) => {
const input = form.querySelector(`[name="${name}"]`);
if (input) {
input.classList.add("error");
const errorEl = document.createElement("div");
errorEl.className = "error-message";
errorEl.textContent = message;
input.parentNode.insertBefore(errorEl, input.nextSibling);
}
});
}
static clearErrors(form) {
form.querySelectorAll(".error-message").forEach((el) => el.remove());
form
.querySelectorAll(".error")
.forEach((el) => el.classList.remove("error"));
}
}
// Scroll Animation Observer
class ScrollAnimation {
static init() {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.style.opacity = "1";
entry.target.style.transform = "translateY(0)";
observer.unobserve(entry.target);
}
});
},
{
threshold: 0.1,
rootMargin: "0px 0px -50px 0px",
},
);
document.querySelectorAll(".reveal").forEach((el) => {
el.style.opacity = "0";
el.style.transform = "translateY(20px)";
el.style.transition = "opacity 0.6s ease, transform 0.6s ease";
observer.observe(el);
});
}
}
// Network Request Helper
class ApiClient {
static async request(url, options = {}) {
const defaultOptions = {
method: "GET",
headers: {
"Content-Type": "application/json",
},
...options,
};
try {
const response = await fetch(url, defaultOptions);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error("API request failed:", error);
throw error;
}
}
static get(url) {
return this.request(url, { method: "GET" });
}
static post(url, data) {
return this.request(url, {
method: "POST",
body: JSON.stringify(data),
});
}
static put(url, data) {
return this.request(url, {
method: "PUT",
body: JSON.stringify(data),
});
}
static delete(url) {
return this.request(url, { method: "DELETE" });
}
}
// Page Transitions
class PageTransition {
static fadeOut(element, duration = 300) {
return new Promise((resolve) => {
element.style.opacity = "0";
element.style.transition = `opacity ${duration}ms ease`;
setTimeout(resolve, duration);
});
}
static fadeIn(element, duration = 300) {
return new Promise((resolve) => {
element.style.opacity = "0";
setTimeout(() => {
element.style.transition = `opacity ${duration}ms ease`;
element.style.opacity = "1";
setTimeout(resolve, duration);
}, 10);
});
}
}
// Initialize utilities on DOM ready
document.addEventListener("DOMContentLoaded", () => {
ScrollAnimation.init();
});
// Export for use in other scripts
window.Toast = Toast;
window.Modal = Modal;
window.LoadingState = LoadingState;
window.FormValidator = FormValidator;
window.ApiClient = ApiClient;
window.PageTransition = PageTransition;
// Create global toast instance
window.toast = new Toast();