This commit is contained in:
tavo 2024-09-20 14:21:32 -06:00
parent e8956d2eb3
commit b9d1620646
6 changed files with 747 additions and 681 deletions

View file

@ -1,58 +1,160 @@
document.addEventListener("DOMContentLoaded", function() { const PayPalSDK = "https://sandbox.paypal.com/sdk/js?client-id=AcCW43LI1S6lLQgtLkF4V8UOPfmXcqXQ8xfEl41hRuMxSskR2jkWNwQN6Ab1WK7E2E52GNaoYBHqgIKd&components=buttons&enable-funding=card&disable-funding=paylater,venmo"
const EditorJSComponents = [
"https://cdn.jsdelivr.net/npm/@editorjs/header@latest",
"https://cdn.jsdelivr.net/npm/@editorjs/image@latest",
"https://cdn.jsdelivr.net/npm/@editorjs/list@latest",
"https://cdn.jsdelivr.net/npm/@editorjs/quote@latest",
"https://cdn.jsdelivr.net/npm/@editorjs/code@latest",
"https://cdn.jsdelivr.net/npm/@editorjs/table@latest",
"https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest",
];
let typingTimeout;
let hideTimeout;
let editor;
document.addEventListener("DOMContentLoaded", function () {
const dialog = document.getElementById("dialog");
const overlay = document.getElementById("overlay");
const floatingButtons = document.getElementById("floatingButtons");
const checkoutDialog = document.getElementById("checkoutDialog");
const editDialog = document.getElementById("editDialog");
const buyDialog = document.getElementById("buyDialog");
checkoutDialog.style.display = "none";
editDialog.style.display = "none";
loadLanguage('es'); loadLanguage('es');
Promise.all(EditorJSComponents.map(src => loadScript(src))).then(() => {
return loadScript("/editor.js");
}).then(() => {
loadEditorState();
}).catch(err => console.error("Error loading editor:", err));
loadScript(PayPalSDK).then(() => {
return loadScript("/paypal.js");
}).catch(err => console.error(err));
initializeEventListeners();
});
function loadScript(src, async = true) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = async;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
document.head.appendChild(script);
});
}
function loadEditorState() {
const savedData = localStorage.getItem('conex_data'); const savedData = localStorage.getItem('conex_data');
const holderElement = document.getElementById('editorjs');
holderElement.innerHTML = '';
if (savedData) { if (savedData) {
const parsedData = JSON.parse(savedData); const parsedData = JSON.parse(savedData);
console.log('Loaded parsedData:', parsedData); console.log('Loaded parsedData:', parsedData);
document.getElementById('title').value = parsedData.title || ''; document.getElementById('title').value = parsedData.title || '';
document.getElementById('slogan').value = parsedData.slogan || ''; document.getElementById('slogan').value = parsedData.slogan || '';
document.getElementById('banner').src = parsedData.banner || '/static/svg/banner.svg'; document.getElementById('banner').src = parsedData.banner || '/static/svg/banner.svg';
const disclaimers = document.querySelectorAll(".localstorage-exists");
disclaimers.forEach((element) => {
element.style.display = "block";
});
initializeEditor(parsedData);
document.getElementById('continueEditingModeButton').style.display = "block";
fetch(`./lang/es.json`)
.then(response => response.json())
.then(translations => {
const translatedText = translations['continueEditingModeButton'];
const continueEditingModeButton = document.getElementById('continueEditingModeButton');
if (parsedData.title && translatedText) {
continueEditingModeButton.innerText = `${translatedText}: ${parsedData.title}`;
} else {
continueEditingModeButton.innerText = translatedText;
}
})
.catch(error => console.error('Error loading translation file:', error));
} else {
document.getElementById('title').value = '';
document.getElementById('slogan').value = '';
document.getElementById('banner').src = '/static/svg/banner.svg';
document.getElementById("continueEditingModeButton").style.display = "none";
const disclaimers = document.querySelectorAll(".localstorage-exists");
disclaimers.forEach((element) => {
element.style.display = "none";
});
initializeEditor();
} }
}
const dialog = document.getElementById("dialog"); function initializeEventListeners() {
const overlay = document.getElementById("overlay"); // Prevent ENTER key in slogan element
const menu = document.getElementById("floatingButtons"); document.getElementById('slogan').addEventListener('keydown', function(event) {
const checkoutErrorMessage = document.getElementById("checkout-error-message"); if (event.key === 'Enter') {
event.preventDefault();
}
});
function openDialog() { document.getElementById("buyButton").addEventListener("click", () => openDialog(checkoutDialog));
checkoutErrorMessage.style.display = "none"; document.getElementById("closeDialogButton").addEventListener("click", () => closeDialog());
dialog.style.display = "block";
overlay.style.display = "block";
menu.style.display = "none";
}
function closeDialog() { // // Editor save
checkoutErrorMessage.style.display = "none";
dialog.style.display = "none";
overlay.style.display = "none";
menu.style.display = "flex";
}
document.getElementById("openDialogButton").addEventListener("click", openDialog);
document.getElementById("cancelDialogButton").addEventListener("click", closeDialog);
document.getElementById('title').addEventListener('change', saveEditorData); document.getElementById('title').addEventListener('change', saveEditorData);
document.getElementById('slogan').addEventListener('change', saveEditorData); document.getElementById('slogan').addEventListener('change', saveEditorData);
// // Mode switching
// document.getElementById('buyModeButton').addEventListener('click', openBuyModeDialog);
document.getElementById('buyModeButton').addEventListener('click', buyMode); document.getElementById('buyModeButton').addEventListener('click', buyMode);
document.getElementById('editModeButton').addEventListener('click', editModeWrapper); document.getElementById('editModeButton').addEventListener('click', openEditModeDialog);
document.getElementById("continueToEditMode").addEventListener('click', function() { document.getElementById("continueToEditModeButton").addEventListener('click', () =>
editMode(document.getElementById("dashEditInput").value); editMode(document.getElementById("editModeDirectoryInput").value)
}); );
document.getElementById('continueEditingModeButton').addEventListener('click', continueMode);
document.getElementById('dashButton').addEventListener('click', dashboardMode); document.getElementById('dashButton').addEventListener('click', dashboardMode);
}); document.getElementById('uploadBannerBtn').addEventListener('change', handleImageUpload);
const titleElement = document.getElementById('title');
if (titleElement) {
setupDirectoryInput(titleElement);
}
}
function openDialog(content) {
dialog.style.display = "block";
content.style.display = "block";
overlay.style.display = "block";
floatingButtons.style.display = "none";
}
function closeDialog() {
checkoutDialog.style.display = "none";
editDialog.style.display = "none";
// buyDialog.style.display = "none";
dialog.style.display = "none";
overlay.style.display = "none";
floatingButtons.style.display = "flex";
document.getElementById('checkout-error-message').style.display = "none";
}
function saveEditorData() { function saveEditorData() {
const banner = document.getElementById('banner').src; const titleValue = document.getElementById('title').value.trim();
const title = document.getElementById('title').value; const dataToSave = {
const slogan = document.getElementById('slogan').value; banner: document.getElementById('banner').src || '/static/svg/banner.svg',
title: document.getElementById('title').value,
slogan: document.getElementById('slogan').value,
directory: sanitizeDirectoryTitle(titleValue)
};
editor.save().then((editor_data) => { editor.save().then((editor_data) => {
const dataToSave = { dataToSave.editor_data = editor_data;
directory: sanitizeDirectoryTitle(title),
banner: banner || '/static/svg/banner.svg',
title: title,
slogan: slogan,
editor_data: editor_data
};
localStorage.setItem('conex_data', JSON.stringify(dataToSave)); localStorage.setItem('conex_data', JSON.stringify(dataToSave));
console.log('Editor data saved to localStorage'); console.log('Editor data saved to localStorage');
}).catch((error) => { }).catch((error) => {
@ -60,35 +162,22 @@ function saveEditorData() {
}); });
} }
let typingTimeout; function setupDirectoryInput(inputElement, debounceTime = 500) {
let hideTimeout; inputElement.addEventListener('input', () => {
const directoryInput = document.getElementById('title'); clearTimeout(typingTimeout);
directoryInput.addEventListener('input', () => {
clearTimeout(typingTimeout);
typingTimeout = setTimeout(() => {
const directoryTitle = directoryInput.value.trim();
if (directoryTitle.length > 0) {
const directory = sanitizeDirectoryTitle(directoryTitle);
checkDirectory(directory);
} else {
hidePopup();
}
}, 500); // Debounce
});
const directoryBuyInput = document.getElementById('dashEditInput'); typingTimeout = setTimeout(() => {
directoryBuyInput.addEventListener('input', () => { const inputValue = inputElement.value.trim();
clearTimeout(typingTimeout);
typingTimeout = setTimeout(() => { if (inputValue.length > 0) {
const directoryTitle = directoryBuyInput.value.trim(); const sanitizedValue = sanitizeDirectoryTitle(inputValue); // Sanitize the input value
if (directoryTitle.length > 0) { checkDirectory(sanitizedValue);
const directory = sanitizeDirectoryTitle(directoryTitle); } else {
checkDirectoryReverse(directory); hidePopup();
} else { }
hidePopup(); }, debounceTime);
} });
}, 500); // Debounce }
});
function sanitizeDirectoryTitle(title) { function sanitizeDirectoryTitle(title) {
return title return title
@ -100,48 +189,33 @@ function sanitizeDirectoryTitle(title) {
} }
function checkDirectory(directory) { function checkDirectory(directory) {
if (directory.length < 4) { if (!validateDirectoryLength(directory)) return;
return; fetchDirectoryStatus(directory, 'exists', 'available', 'El sitio web ya existe', 'Se publicará en');
}
if (directory.length > 35) {
showPopup(`El título no puede exceder los 35 caracteres`, 'exists');
return;
}
fetch(`/api/directory/${encodeURIComponent(directory)}`)
.then(response => response.json())
.then(data => {
if (data.exists) {
showPopup(`El sitio web conex.one/${directory} ya existe`, 'exists');
} else {
showPopup(`Se publicará en conex.one/${directory}`, 'available');
}
})
.catch(error => {
console.error('Error checking directory:', error);
showPopup('Error checking directory.', 'exists');
});
} }
function checkDirectoryReverse(directory) { function validateDirectoryLength(directory) {
if (directory.length < 4) { if (directory.length < 4 || directory.length > 35) {
return; showPopup('El título debe tener entre 4 y 35 caracteres', 'exists');
return false;
} }
if (directory.length > 35) { return true;
showPopup(`El título no puede exceder los 35 caracteres`, 'exists'); }
return;
} function fetchDirectoryStatus(directory, failureStatus, successStatus, failureMessage, successMessage) {
fetch(`/api/directory/${encodeURIComponent(directory)}`) checkDirectoryExists(directory).then(exists => {
const message = exists ? `${failureMessage} conex.one/${directory}` : `${successMessage} conex.one/${directory}`;
const status = exists ? failureStatus : successStatus;
showPopup(message, status);
});
}
function checkDirectoryExists(directory) {
return fetch(`/api/directory/${encodeURIComponent(directory)}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => data.exists)
if (data.exists) {
showPopup(`Editar el sitio web conex.one/${directory}`, 'available');
} else {
showPopup(`El sitio conex.one/${directory} no está registrado`, 'exists');
}
})
.catch(error => { .catch(error => {
console.error('Error checking directory:', error); console.error('Error checking directory:', error);
showPopup('Error checking directory.', 'exists'); return false;
}); });
} }
@ -176,13 +250,20 @@ function showPopup(message, status) {
} }
function hidePopup(popup, status) { function hidePopup(popup, status) {
if (!popup) {
return;
}
popup.classList.remove('show'); popup.classList.remove('show');
setTimeout(() => { setTimeout(() => {
popup.classList.remove(status); popup.classList.remove(status);
}, 100); }, 100);
} }
document.getElementById('imageUpload').addEventListener('change', function (event) { function handleImageUpload() {
const uploadButton = document.getElementById('uploadBannerBtn');
const imageIcon = document.querySelector('.tool-button img');
const loader = document.querySelector('.loader');
const savedData = localStorage.getItem('conex_data'); const savedData = localStorage.getItem('conex_data');
const parsedData = savedData ? JSON.parse(savedData) : null; const parsedData = savedData ? JSON.parse(savedData) : null;
const directory = parsedData?.directory || "temp"; const directory = parsedData?.directory || "temp";
@ -193,6 +274,10 @@ document.getElementById('imageUpload').addEventListener('change', function (even
formData.append('file', file); formData.append('file', file);
formData.append('directory', directory); formData.append('directory', directory);
uploadButton.disabled = true;
loader.style.display = 'inline-block';
imageIcon.style.display = 'none';
fetch('/api/upload', { fetch('/api/upload', {
method: 'POST', method: 'POST',
body: formData, body: formData,
@ -205,9 +290,13 @@ document.getElementById('imageUpload').addEventListener('change', function (even
} }
}).catch(error => { }).catch(error => {
console.error('Error uploading the image:', error); console.error('Error uploading the image:', error);
}).finally(() => {
uploadButton.disabled = false;
loader.style.display = 'none';
imageIcon.style.display = 'inline-block';
}); });
} }
}); }
function loadLanguage(lang) { function loadLanguage(lang) {
fetch(`./lang/${lang}.json`) fetch(`./lang/${lang}.json`)
@ -230,12 +319,10 @@ function loadLanguage(lang) {
} }
function dashboardMode() { function dashboardMode() {
document.getElementById("dashOverlay").style.display = "none"; loadEditorState();
document.getElementById("dashDialog").style.display = "none"; const dashboard = document.getElementById('dashboard');
const dashboard = document.querySelector('.dashboard');
dashboard.style.display = 'flex'; dashboard.style.display = 'flex';
dashboard.style.opacity = '0';
setTimeout(() => { setTimeout(() => {
dashboard.style.transition = 'opacity 0.5s ease'; dashboard.style.transition = 'opacity 0.5s ease';
dashboard.style.opacity = '1'; dashboard.style.opacity = '1';
@ -243,9 +330,15 @@ function dashboardMode() {
} }
function buyMode() { function buyMode() {
document.getElementById("openDialogButton").style.display = "block"; localStorage.removeItem('conex_data');
document.getElementById("updateSiteButton").style.display = "none"; loadEditorState();
const dashboard = document.querySelector('.dashboard');
document.getElementById('checkout-success-message').style.display = "none";
document.querySelector("#paypal-button-container").style.display = "block";
document.getElementById("buyButton").style.display = "block";
document.getElementById("editButton").style.display = "none";
const dashboard = document.getElementById('dashboard');
dashboard.style.transition = 'opacity 0.5s ease'; dashboard.style.transition = 'opacity 0.5s ease';
dashboard.style.opacity = '0'; dashboard.style.opacity = '0';
@ -254,15 +347,61 @@ function buyMode() {
}, 500); }, 500);
} }
function continueMode() {
const savedData = localStorage.getItem('conex_data');
if (savedData) {
const parsedData = JSON.parse(savedData);
if (parsedData.directory) {
checkDirectoryExists(parsedData.directory).then(exists => {
if (exists) {
editMode(parsedData.directory);
} else {
continueBuyMode();
}
});
} else {
continueBuyMode();
}
} else {
buyMode();
}
}
function continueBuyMode() {
document.getElementById("buyButton").style.display = "block";
document.getElementById("editButton").style.display = "none";
const dashboard = document.getElementById('dashboard');
dashboard.style.transition = 'opacity 0.5s ease';
dashboard.style.opacity = '0';
setTimeout(() => {
dashboard.style.display = 'none';
}, 500);
}
function openBuyModeDialog() {
overlay.style.display = "block";
dialog.style.display = "block";
buyDialog.style.display = "block";
}
function openEditModeDialog() {
overlay.style.display = "block";
dialog.style.display = "block";
editDialog.style.display = "block";
}
async function editMode(dir) { async function editMode(dir) {
const success = await loadEditorData(dir); closeDialog();
const success = await fetchAndStoreData(dir);
if (!success) { if (!success) {
console.error("Data could not be loaded, aborting UI changes"); console.error("Data could not be loaded, aborting UI changes");
return; return;
} }
document.getElementById("openDialogButton").style.display = "none";
document.getElementById("updateSiteButton").style.display = "block"; document.getElementById("buyButton").style.display = "none";
const dashboard = document.querySelector('.dashboard'); document.getElementById("editButton").style.display = "block";
const dashboard = document.getElementById('dashboard');
dashboard.style.transition = 'opacity 0.5s ease'; dashboard.style.transition = 'opacity 0.5s ease';
dashboard.style.opacity = '0'; dashboard.style.opacity = '0';
@ -271,40 +410,21 @@ async function editMode(dir) {
}, 500); }, 500);
} }
function editModeWrapper() { async function fetchAndStoreData(directoryName) {
document.getElementById("dashOverlay").style.display = "block"; try {
document.getElementById("dashDialog").style.display = "block"; const response = await fetch(`/api/fetch/${encodeURIComponent(directoryName)}`);
document.getElementById("dashEditInput").style.display = "block"; if (!response.ok) {
} throw new Error(`Failed to fetch data for directory: ${directoryName}`);
}
function loadEditorData(dirname) {
return new Promise((resolve, reject) => { const data = await response.json();
fetch(`/api/fetch/${dirname}`) localStorage.setItem('conex_data', JSON.stringify(data));
.then(response => { console.log('Data fetched and stored in localStorage:', data);
if (!response.ok) {
throw new Error('Network response was not ok'); loadEditorState();
} return true;
return response.json(); } catch (error) {
}) console.error('Error fetching and storing data:', error);
.then(data => { return false;
// Populate the data into the form elements }
document.getElementById('banner').src = data.banner || '/static/svg/banner.svg';
document.getElementById('title').value = data.title || '';
document.getElementById('slogan').value = data.slogan || '';
// Load editor data if available
if (data.editor_data && typeof editor !== 'undefined') {
editor.render({
blocks: data.editor_data.blocks || []
});
}
console.log('Editor data loaded successfully');
resolve(true); // Resolve with success
})
.catch(error => {
console.error('Fetching and loading data failed:', error);
resolve(false); // Resolve with failure, but not reject to avoid unhandled errors
});
});
} }

View file

@ -1,179 +1,179 @@
const savedData = localStorage.getItem('conex_data'); function initializeEditor(conex_data) {
const parsedData = savedData ? JSON.parse(savedData) : null; directory = conex_data?.directory || "temp";
const directory = parsedData?.directory || "temp";
const editor = new EditorJS({ editor = new EditorJS({
// readOnly: false, // readOnly: false,
holder: 'editorjs', holder: 'editorjs',
inlineToolbar: ['marker', 'bold', 'italic'], inlineToolbar: ['marker', 'bold', 'italic'],
inlineToolbar: true, inlineToolbar: true,
tools: { tools: {
/** /**
* Each Tool is a Plugin. Pass them via 'class' option with necessary settings {@link docs/tools.md} * Each Tool is a Plugin. Pass them via 'class' option with necessary settings {@link docs/tools.md}
*/ */
header: { header: {
class: Header, class: Header,
config: { config: {
placeholder: 'Inserta un título', placeholder: 'Inserta un título',
levels: [1, 2, 3], levels: [1, 2, 3],
defaultLevel: 1, defaultLevel: 1,
shortcut: 'CMD+SHIFT+H' shortcut: 'CMD+SHIFT+H'
}
},
image: {
class: ImageTool,
config: {
endpoints: {
byFile: `${window.location.origin}/api/upload`,
},
field: 'file',
types: 'image/*',
additionalRequestData: {
directory: directory,
},
},
},
list: {
class: List,
inlineToolbar: true,
shortcut: 'CMD+SHIFT+L'
},
quote: {
class: Quote,
inlineToolbar: true,
config: {
quotePlaceholder: 'Insertar una cita',
captionPlaceholder: 'Autor de la cita',
},
shortcut: 'CMD+SHIFT+O'
},
table: {
class: Table,
inlineToolbar: true,
shortcut: 'CMD+ALT+T'
},
},
data: parsedData ? parsedData.editor_data : {
blocks: [
{
type: "header",
data: {
text: "Acerca de [Empresa]",
level: 1
} }
}, },
{
type : 'paragraph', image: {
data : { class: ImageTool,
text : 'En [Nombre de Tu Empresa], nos dedicamos a ofrecer [tu servicio/producto] de la más alta calidad con un servicio al cliente excepcional. Nuestro equipo de expertos se asegura de que cada aspecto de tu experiencia sea manejado con profesionalismo y cuidado.' config: {
} endpoints: {
}, byFile: `${window.location.origin}/api/upload`,
{ },
type : 'list', field: 'file',
data : { types: 'image/*',
items : [ additionalRequestData: {
'Resolvemos una necesidad clave de mercado', directory: directory,
'Inversión en crecimiento con presupuesto sostenible.',
'Enfoque en satisfacción del cliente',
],
style: 'unordered'
}
},
{
type: 'table',
data: {
content: [
['Servicios', 'Descripción', 'Costo'],
['Impresión', 'Breve descripción', '1000'],
['laminado', 'Breve descripción', '2000'],
]
}
},
]
},
i18n: {
messages: {
ui: {
"blockTunes": {
"toggler": {
"Click to tune": "Modificar",
"or drag to move": "or drag to move"
}, },
}, },
"inlineToolbar": { },
"converter": {
"Convert to": "Convertir a" list: {
class: List,
inlineToolbar: true,
shortcut: 'CMD+SHIFT+L'
},
quote: {
class: Quote,
inlineToolbar: true,
config: {
quotePlaceholder: 'Insertar una cita',
captionPlaceholder: 'Autor de la cita',
},
shortcut: 'CMD+SHIFT+O'
},
table: {
class: Table,
inlineToolbar: true,
shortcut: 'CMD+ALT+T'
},
},
data: conex_data?.editor_data || {
blocks: [
{
type: "header",
data: {
text: "Acerca de [Empresa]",
level: 1
} }
}, },
"toolbar": { {
"toolbox": { type : 'paragraph',
"Add": "Insertar" data : {
text : 'En [Nombre de Tu Empresa], nos dedicamos a ofrecer [tu servicio/producto] de la más alta calidad con un servicio al cliente excepcional. Nuestro equipo de expertos se asegura de que cada aspecto de tu experiencia sea manejado con profesionalismo y cuidado.'
}
},
{
type : 'list',
data : {
items : [
'Resolvemos una necesidad clave de mercado',
'Inversión en crecimiento con presupuesto sostenible.',
'Enfoque en satisfacción del cliente',
],
style: 'unordered'
}
},
{
type: 'table',
data: {
content: [
['Servicios', 'Descripción', 'Costo'],
['Impresión', 'Breve descripción', '1000'],
['laminado', 'Breve descripción', '2000'],
]
}
},
]
},
i18n: {
messages: {
ui: {
"blockTunes": {
"toggler": {
"Click to tune": "Modificar",
"or drag to move": "or drag to move"
},
},
"inlineToolbar": {
"converter": {
"Convert to": "Convertir a"
}
},
"toolbar": {
"toolbox": {
"Add": "Insertar"
}
} }
}
},
/**
* Section for translation Tool Names: both block and inline tools
*/
toolNames: {
"Text": "Texto",
"Heading": "Título",
"List": "Lista",
"Warning": "Advertencia",
"Quote": "Cita",
"Table": "Tabla",
"Link": "Link",
"Image": "Imagen",
"Bold": "Negrita",
"Italic": "Itálicas",
"InlineCode": "InlineCode",
},
/**
* TRANSLATIONS
* Each subsection is the i18n dictionary that will be passed to the corresponded plugin
* The name of a plugin should be equal the name you specify in the 'tool' section for that plugin
*/
tools: {
"warning": { // <-- 'Warning' tool will accept this dictionary section
"Title": "Título",
"Message": "Mensaje",
}, },
"link": {
"Add a link": "Agregar link"
},
"stub": {
'The block can not be displayed correctly.': 'No se puede visualizar este bloque'
}
},
blockTunes: {
/** /**
* Each subsection is the i18n dictionary that will be passed to the corresponded Block Tune plugin * Section for translation Tool Names: both block and inline tools
* The name of a plugin should be equal the name you specify in the 'tunes' section for that plugin
*
* Also, there are few internal block tunes: "delete", "moveUp" and "moveDown"
*/ */
"delete": { toolNames: {
"Delete": "Quitar bloque" "Text": "Texto",
"Heading": "Título",
"List": "Lista",
"Warning": "Advertencia",
"Quote": "Cita",
"Table": "Tabla",
"Link": "Link",
"Image": "Imagen",
"Bold": "Negrita",
"Italic": "Itálicas",
"InlineCode": "InlineCode",
}, },
"moveUp": {
"Move up": "Mover arriba" /**
* TRANSLATIONS
* Each subsection is the i18n dictionary that will be passed to the corresponded plugin
* The name of a plugin should be equal the name you specify in the 'tool' section for that plugin
*/
tools: {
"warning": { // <-- 'Warning' tool will accept this dictionary section
"Title": "Título",
"Message": "Mensaje",
},
"link": {
"Add a link": "Agregar link"
},
"stub": {
'The block can not be displayed correctly.': 'No se puede visualizar este bloque'
}
}, },
"moveDown": {
"Move down": "Mover abajo" blockTunes: {
} /**
}, * Each subsection is the i18n dictionary that will be passed to the corresponded Block Tune plugin
* The name of a plugin should be equal the name you specify in the 'tunes' section for that plugin
*
* Also, there are few internal block tunes: "delete", "moveUp" and "moveDown"
*/
"delete": {
"Delete": "Quitar bloque"
},
"moveUp": {
"Move up": "Mover arriba"
},
"moveDown": {
"Move down": "Mover abajo"
}
},
}
},
onChange: function(api, event) {
saveEditorData();
} }
}, });
onChange: function(api, event) { }
saveEditorData();
}
});

View file

@ -1,66 +1,63 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="es"> <html lang="es">
<head> <head>
<title>Builder | CONEX.one </title> <title>Editor | CONEX.one </title>
<link rel="canonical" href="https://builder.conex.one"> <link rel="canonical" href="https://editor.conex.one">
<link rel="icon" type="image/svg+xml" href="/static/svg/favicon.svg"> <link rel="icon" type="image/svg+xml" href="/static/svg/favicon.svg">
<meta name="description" content="Constructor de sitios web de CONEX.one"> <meta name="description" content="Constructor de sitios web de CONEX.one">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="index, follow">
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/editorjs-dark.css"> <link rel="stylesheet" href="/static/css/editorjs-dark.css">
</head> </head>
<div class="dashboard"> <div id="status-popup" class="status-popup">
<span class="close-popup" onclick="hidePopup()">×</span>
<span id="status-message"></span>
</div>
<div id="overlay"></div>
<div id="dashboard">
<div class="wave"></div> <div class="wave"></div>
<div class="wave"></div> <div class="wave"></div>
<div class="wave"></div> <div class="wave"></div>
<div class="mode-button-container"> <div class="dashboard-content">
<h1 class="dashboard-text" data-translate="welcome"></h1> <h1 data-translate="welcome"></h1>
<p class="dashboard-text" data-translate="about"></p> <p data-translate="about"></p>
<span data-translate="buyModeButton" id="buyModeButton" class="mode-button buyModeButton"></span> <span id="buyModeButton">
<span data-translate="editModeButton" id="editModeButton" class="mode-button editModeButton"></span> <h4 data-translate="buyModeButton"></h4>
</div> <p class="localstorage-exists" data-translate="buyModeButtonDisclaimer"></p>
<div id="dashOverlay"></div> </span>
<div id="dashDialog"> <span id="continueEditingModeButton">
<h2 data-translate="editDialogHeader"></h2> <h4 data-translate="continueEditingModeButton"></h4>
<p data-translate="editDialogParagraph"></p> </span>
<input type="text" id="dashBuyInput" name="title" maxlength="35" class="input-title" data-translate="sampleTitle" placeholder="" > <span id="editModeButton">
<input type="text" id="dashEditInput" name="title" maxlength="35" class="input-title" data-translate="sampleTitle" placeholder="" > <h4 data-translate="editModeButton"></h4>
<button id="continueToEditMode"> <p class="localstorage-exists" data-translate="editModeButtonDisclaimer"></p>
<span data-translate="continueToEditMode"></span> </span>
</button>
</div> </div>
</div> </div>
<div class="banner"> <div class="banner">
<input type="file" id="imageUpload" accept="image/*"> <div class="banner-toolbar">
<label for="imageUpload" class="upload-button"> <input type="file" id="uploadBannerBtn" class="tool-button-input" accept="image/*">
<img src="/static/svg/image.svg" alt="Edit Icon" class="icon"> <label for="uploadBannerBtn" class="tool-button">
</label> <img src="/static/svg/image.svg" alt="Edit Icon" class="icon">
<img id="banner" name="banner" src="/static/svg/banner.svg" class="banner-image"/> <span class="loader"></span>
<div class="desc"> </label>
<input type="text" id="title" name="title" maxlength="35" class="input-title" data-translate="sampleTitle" placeholder="" >
<textarea type="text" id="slogan" name="slogan" maxlength="100" class="input-slogan" data-translate="sampleSlogan" placeholder=""></textarea>
</div> </div>
<div id="status-popup" class="status-popup"> <img id="banner" src="/static/svg/banner.svg" class="banner-image"/>
<span class="close-popup" onclick="hidePopup()">×</span> <div class="desc">
<span id="status-message"></span> <input type="text" id="title" maxlength="35" class="input-title" data-translate="sampleTitle" placeholder="" >
<textarea type="text" id="slogan" maxlength="100" class="input-slogan" data-translate="sampleSlogan" placeholder=""></textarea>
</div> </div>
</div> </div>
<body> <body>
<div class="content"> <div id="editorjs"></div>
<div id="editor-container">
<div id="editorjs"></div>
</div>
</div>
</body> </body>
<div id="overlay"></div> <div class="floating-buttons-container" id="floatingButtons">
<div class="floating-buttons" id="floatingButtons"> <button class="floating-button" id="buyButton">
<button class="floating-button" id="openDialogButton">
<img src="/static/svg/check.svg"> <img src="/static/svg/check.svg">
<span data-translate="buyButton"></span> <span data-translate="buyButton"></span>
</button> </button>
<button class="floating-button" id="updateSiteButton"> <button class="floating-button" id="editButton">
<img src="/static/svg/check.svg"> <img src="/static/svg/check.svg">
<span data-translate="editButton"></span> <span data-translate="editButton"></span>
</button> </button>
@ -70,32 +67,33 @@
</button> </button>
</div> </div>
<div id="dialog"> <div id="dialog">
<h2 data-translate="checkoutHeader"></h2> <button id="closeDialogButton" type="button">
<p data-translate="checkoutParagraph"></p>
<ul>
<li data-translate="supportEmail"></li>
<!-- <li data-translate="supportPhone"></li> -->
<li data-translate="supportSchedule"></li>
</ul>
<div id="checkout-error-message"></div>
<div id="checkout-success-message"></div>
<div id="paypal-button-container"></div>
<button id="cancelDialogButton" type="button">
<picture> <picture>
<source srcset="/static/svg/xd.svg" media="(prefers-color-scheme: dark)"> <source srcset="/static/svg/xd.svg" media="(prefers-color-scheme: dark)">
<img src="/static/svg/x.svg" style="width: 0.7em; height: 0.7em;" alt="Close" id="closeIcon"> <img src="/static/svg/x.svg" style="width: 1em; height: 1em;" id="closeIcon">
</picture> </picture>
</button> </button>
<div id="checkoutDialog">
<div class="message success-message" style="display:block" data-translate="checkoutSaveNotif"></div>
<h2 data-translate="checkoutHeader"></h2>
<p data-translate="checkoutParagraph"></p>
<ul>
<li data-translate="supportEmail"></li>
<li data-translate="supportPhone"></li>
<li data-translate="supportSchedule"></li>
</ul>
<div class="message success-message" id="checkout-success-message"></div>
<div class="message error-message" id="checkout-error-message"></div>
<div id="paypal-button-container"></div>
</div>
<div id="editDialog">
<h2 data-translate="editDialogHeader"></h2>
<p data-translate="editDialogParagraph"></p>
<input class="input-dialog" type="text" id="editModeDirectoryInput" name="title" maxlength="35" placeholder="https://conex.one/mi-sitio"><br>
<button id="continueToEditModeButton" class="right">
<span data-translate="continueToEditModeButton"></span>
</button>
</div>
</div> </div>
<script src="/client.js"></script> <script src="/client.js"></script>
<script src="https://sandbox.paypal.com/sdk/js?client-id=AcCW43LI1S6lLQgtLkF4V8UOPfmXcqXQ8xfEl41hRuMxSskR2jkWNwQN6Ab1WK7E2E52GNaoYBHqgIKd&components=buttons&enable-funding=card&disable-funding=paylater,venmo"></script>
<script src="/paypal.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@editorjs/header@latest"></script><!-- Header -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/image@latest"></script><!-- Image -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/list@latest"></script><!-- List -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/quote@latest"></script><!-- Quote -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/code@latest"></script><!-- Code -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/table@latest"></script><!-- Table -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest"></script>
<script src="/editor.js"></script>
</html> </html>

View file

@ -2,13 +2,17 @@
"welcome": "Editor | CONEX.one", "welcome": "Editor | CONEX.one",
"about": "En editor.conex.one puede crear un sitio web de manera rápida y sencilla, por $20 al año. Para continuar editando un sitio, presione el botón \"Editar sitio existente\". Si desea crear un sitio desde cero, presione el botón \"Diseñar un nuevo sitio\"", "about": "En editor.conex.one puede crear un sitio web de manera rápida y sencilla, por $20 al año. Para continuar editando un sitio, presione el botón \"Editar sitio existente\". Si desea crear un sitio desde cero, presione el botón \"Diseñar un nuevo sitio\"",
"buyModeButton": "Diseñar un nuevo sitio", "buyModeButton": "Diseñar un nuevo sitio",
"buyModeButtonDisclaimer": "Limpiará el progreso actual para diseñar un sitio desde cero",
"editModeButton": "Editar sitio existente", "editModeButton": "Editar sitio existente",
"editModeButtonDisclaimer": "Limpiará el progreso actual para cargar un sitio existente",
"continueEditingModeButton": "Continuar editando",
"editDialogHeader": "Editar un sitio existente", "editDialogHeader": "Editar un sitio existente",
"editDialogParagraph": "El sitio ya debe estar registrado en conex.one para poder cargarlo localmente.", "editDialogParagraph": "El sitio ya debe estar registrado en conex.one para poder cargarlo localmente.",
"continueToEditMode": "Editar este sitio", "continueToEditModeButton": "Editar este sitio",
"buyButton": "Guardar", "buyButton": "Guardar",
"editButton": "Aplicar cambios", "editButton": "Aplicar cambios",
"dashButton": "Panel principal", "dashButton": "Panel principal",
"checkoutSaveNotif": "¡El progreso se guardó con éxito!",
"checkoutHeader": "Contratar por $20 al año", "checkoutHeader": "Contratar por $20 al año",
"checkoutParagraph": "Gracias por elegir nuestro servicio para la compra de sitios web. Luego de ser aprobado, su sitio será publicado en menos de 24 horas a partir de la confirmación de tu compra. Utilizaremos los medios de contacto que proporcione para comunicarnos en caso de cualquier inconveniente con la publicación. Si experimenta algún problema, no dude en ponerte en contacto con nosotros a través de los canales:", "checkoutParagraph": "Gracias por elegir nuestro servicio para la compra de sitios web. Luego de ser aprobado, su sitio será publicado en menos de 24 horas a partir de la confirmación de tu compra. Utilizaremos los medios de contacto que proporcione para comunicarnos en caso de cualquier inconveniente con la publicación. Si experimenta algún problema, no dude en ponerte en contacto con nosotros a través de los canales:",
"supportEmail": "Correo electrónico: soporte@conex.one", "supportEmail": "Correo electrónico: soporte@conex.one",

View file

@ -90,8 +90,11 @@ paypal.Buttons({
<p> <p>
Estado: <strong>${transaction.status}</strong><br> Estado: <strong>${transaction.status}</strong><br>
ID de transacción: ${transaction.id}<br> ID de transacción: ${transaction.id}<br>
Luego de una revisión positiva, su sitio será publicado en menos de 24 horas. Luego de una revisión positiva, su sitio será publicado en menos de 24 horas en el enlace:
</p>
<p> <p>
<a href="https://conex.one/${savedData.directory}/">conex.one/${savedData.directory}</a>
</p>
`,); `,);
document.querySelector("#paypal-button-container").style.display = "none"; document.querySelector("#paypal-button-container").style.display = "none";
console.log( console.log(

View file

@ -12,6 +12,7 @@
--line-height: 1.7; --line-height: 1.7;
--smaller-font: 0.75em; --smaller-font: 0.75em;
--hyper-color: #0f82af; --hyper-color: #0f82af;
--border-radius-regular: 10px;
text-align: justify; text-align: justify;
margin: auto; margin: auto;
} }
@ -80,234 +81,79 @@ a {
z-index: -1000; z-index: -1000;
} }
.tool-button-input {
display: none;
}
.tool-button {
position: absolute;
top: 0.8em;
left: 0.8em;
display: inline-flex;
width: 2.2em;
height: 2.2em;
border-radius: var(--border-radius-regular);
border: 1px solid var(--hover-border);
cursor: pointer;
align-items: center;
justify-content: center;
transition: background-color 0.2s ease;
overflow: hidden;
}
.tool-button:hover {
background-color: #ffffff40;
}
.tool-button .icon {
width: 1.2em;
height: 1.2em;
}
.input-title,
.input-slogan,
.input-title:focus,
.input-slogan:focus {
font-family: var(--font-family);
width: 100%;
background-color: #00000000;
border-color: #00000000;
color: white;
text-align: center;
outline: none;
line-height: 1em;
resize: none;
}
.input-title { .input-title {
font-size: 2em; font-size: 2em;
font-weight: bold; font-weight: bold;
text-align: center;
width: 100%;
background-color: #00000000;
border-color: #00000000;
color: white;
margin-bottom: -0.2em;
} }
.input-slogan { #editorjs {
font-family: var(--font-family);
text-align: center;
width: 100%;
background-color: #00000000;
border-color: #00000000;
color: white;
line-height: 1em;
margin: 0.5em auto 0em auto;
outline: none;
}
.input-title:focus,
.input-slogan:focus {
outline: none;
}
div.profilepicture img {
display: flex;
margin: 0 auto;
margin-top: 2em;
width: 50%;
max-width: 250px;
border-radius: 50%;
}
.content {
flex: 1 0 auto;
margin: 0 auto;
max-width: 800px;
width: 90%; width: 90%;
margin-bottom: 2em;
}
.content li {
line-height: 1.4em;
text-align: left;
}
.content p {
line-height: 1.4em;
text-align: justify;
}
.content img {
margin: 0 auto; margin: 0 auto;
width: 100%;
margin: 1em 0;
} }
footer { .floating-buttons-container {
background-color: var(--hover-background);
color: var(--unemph-color);
text-align: center;
width: 100%;
margin: 1em auto 0 auto;
padding-top: 1em;
padding-bottom: 1em;
}
#dashDialog,
#dialog {
width: 85%;
max-width: 30em;
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 1.5em;
background: var(--background-color);
border: 1px solid var(--hover-border);
box-shadow: 0 0 3em rgba(0, 0, 0, 0.4);
z-index: 1000;
border-radius: 10px;
text-align: left;
overflow: auto;
max-height: 70vh;
}
#dashDialog h2, #dashDialog p,
#dialog h2, #dialog p {
margin: 0;
padding: 0 0 0.5em 0;
text-align: justify;
}
#dashDialog button,
#dialog button {
margin: 0.5em 0.5em 0 0;
padding: 1em;
}
#dashOverlay,
#overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
}
#dashOverlay {
z-index: 1000;
}
#dashDialog input,
#dialog input {
text-align: left;
font-size: 1em;
display: block;
width: 90%;
color: var(--color);
background: var(--hover-background);
border: 1px solid var(--hover-border);
border-radius: 10px;
padding: 1em;
margin: 1em auto;
}
#dashDialog input {
display: none;
}
#form-container {
padding-bottom: 1em;
}
#warning-message {
display: none;
}
#warning-message p {
text-decoration: underline;
color: var(--warning-color);
}
button {
padding: 0.5em;
margin: 1em auto;
color: var(--unemph-color);
background: var(--background-color);
border: 1px solid var(--hover-border);
border-radius: 10px;
}
#dialog button:hover {
background: var(--hover-background);
color: var(--color);
}
#paypal-button-container > div {
margin: 1.5em 0 0 0;
}
#checkout-success-message,
#checkout-error-message {
display: none;
padding: 0.8em;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
margin: 2em 0;
}
#checkout-success-message {
background-color: #d4edda;
color: #155724;
}
#checkout-error-message {
background-color: #f8d7da;
color: #721c24;
}
#cancelDialogButton {
justify-content: center;
align-items: center;
display: flex;
background-color: var(--background-color);
border: 1px solid var(--hover-border);
color: var(--unemph-color);
position: absolute;
transition: 0.3s;
border-radius: 50%;
width: 1em;
height: 1em;
top: 0.5em;
right: 0.5em;
font-size: 1.1em;
}
.floating-buttons {
display: flex; display: flex;
position: fixed; position: fixed;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
bottom: 0.2em; bottom: 1.5em;
right: 0.5em; right: 1.5em;
gap: 0.4em; gap: 0.6em;
z-index: 2000; z-index: 30;
} }
.floating-button { .floating-button {
height: 2.5em; height: 2.5em;
cursor: pointer; cursor: pointer;
font-size: 1em; font-size: 1em;
z-index: 2000;
border-radius: 999px; border-radius: 999px;
box-sizing: border-box; box-sizing: border-box;
color: rgba(255, 255, 255, 0.85); color: #ffffff;
font-size: 1em; padding: 0 1em;
font-weight: bold;
outline: 0 solid transparent;
padding: 8px 18px;
width: fit-content; width: fit-content;
word-break: break-word; word-break: break-word;
border: 0; border: 0;
@ -323,16 +169,12 @@ button {
margin-left: 0.6em; margin-left: 0.6em;
} }
.floating-button:hover { #editButton {
background-color: #0056b3;
}
#updateSiteButton {
background: linear-gradient(135deg, #214353, #4c9abf); background: linear-gradient(135deg, #214353, #4c9abf);
box-shadow: #0099c5 0 10px 20px -15px; box-shadow: #0099c5 0 10px 20px -15px;
} }
#openDialogButton { #buyButton {
background: linear-gradient(135deg, #21532a, #4fc764); background: linear-gradient(135deg, #21532a, #4fc764);
box-shadow: #27d100 0 10px 20px -15px; box-shadow: #27d100 0 10px 20px -15px;
} }
@ -342,94 +184,33 @@ button {
box-shadow: #8f53b9 0 10px 20px -15px; box-shadow: #8f53b9 0 10px 20px -15px;
} }
.status-popup { #overlay {
position: fixed;
top: 19%;
left: 50%;
transform: translate(-50%, -50%);
padding: 12px;
border-radius: 8px;
background-color: #f0f0f0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
font-size: 16px;
text-align: left;
opacity: 0;
visibility: hidden;
transition: opacity 0.1s ease, visibility 0.1s ease;
z-index: 10000;
}
.status-popup.show {
opacity: 1;
visibility: visible;
}
.status-popup.exists {
background-color: #f8d7da;
color: #721c24;
}
.status-popup.available {
background-color: #d4edda;
color: #155724;
}
.close-popup {
position: absolute;
top: -4px;
right: 5px;
cursor: pointer;
font-weight: bold;
}
#imageUpload {
display: none; display: none;
}
.upload-button {
position: absolute;
top: 0.8em;
left: 0.8em;
display: inline-flex;
width: 2em;
height: 2em;
border-radius: 10px;
border: 1px solid var(--hover-border);
cursor: pointer;
align-items: center;
justify-content: center;
transition: background-color 0.2s ease;
overflow: hidden;
}
.upload-button:hover {
background-color: #ffffff40;
}
.upload-button .icon {
width: 1em;
height: 1em;
}
.dashboard {
background-color: var(--background-color);
height: 100%;
width: 100%;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 50;
}
#dashboard {
display: flex; display: flex;
background: linear-gradient(315deg, #0c4848 3%, #071832 98%);
animation: gradient 15s ease infinite;
background-size: 400% 400%;
height: 100%;
width: 100%;
position: fixed;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
z-index: 3000; z-index: 40;
} }
.dashboard-text { .dashboard-content {
color: white; color: white;
}
.mode-button-container {
width: var(--page-width); width: var(--page-width);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -438,38 +219,50 @@ button {
height: 100vh; height: 100vh;
} }
.mode-button { .dashboard-content {
position: fixed;
top: -10%;
}
#buyModeButton,
#editModeButton,
#continueEditingModeButton {
width: 90%; width: 90%;
padding: 1em; padding: 1em;
margin: 1em 0; margin: 1em 0;
border-radius: 15px; border-radius: var(--border-radius-regular);
text-align: center; text-align: center;
text-decoration: none;
font-weight: bold; font-weight: bold;
color: white; color: white;
cursor: pointer; cursor: pointer;
} }
.buyModeButton { #buyModeButton h4,
background: linear-gradient(45deg, #21532a, #4fc764); #editModeButton h4,
#continueEditingModeButton h4 {
margin: 0;
} }
.buyModeButton:hover { #buyModeButton p,
background: linear-gradient(45deg, #173a1f, #3a9a4d); #editModeButton p,
#continueEditingModeButton p {
margin: 0;
font-weight: normal;
display: none;
font-size: 0.8em;
} }
.editModeButton { #buyModeButton {
background: linear-gradient(45deg, #3498db, #007bff); background-image: linear-gradient(45deg, #043b0c, #39994b);
} }
.editModeButton:hover { #editModeButton {
background: linear-gradient(45deg, #2980b9, #0056b3); background-image: linear-gradient(45deg, #154162, #56a2e8);
} }
.dashboard { #continueEditingModeButton {
background: linear-gradient(315deg, #0c4848 3%, #071832 98%); display: none;
animation: gradient 15s ease infinite; background-image: linear-gradient(45deg, #4a3b72, #4a3b72);
background-size: 400% 400%;
} }
@keyframes gradient { @keyframes gradient {
@ -531,3 +324,151 @@ button {
transform: translateX(1); transform: translateX(1);
} }
} }
#dialog {
width: 85%;
max-width: 30em;
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 1.5em;
background: var(--background-color);
border: 1px solid var(--hover-border);
box-shadow: 0 0 3em rgba(0, 0, 0, 0.4);
z-index: 50;
border-radius: 10px;
text-align: left;
overflow: auto;
max-height: 70vh;
}
#dialog h2, #dialog p {
margin: 0;
padding: 0 0 0.5em 0;
text-align: justify;
}
#dialog button {
margin: 0.5em 0.5em 0 0;
padding: 1em;
}
#dialog button {
padding: 0.5em;
color: var(--unemph-color);
background: var(--background-color);
border: 1px solid var(--hover-border);
border-radius: 10px;
position: absolute;
right: 1em;
bottom: 1em;
}
#dialog button:hover {
background: var(--hover-background);
color: var(--color);
}
#dialog #closeDialogButton {
border: 0 solid black;
color: var(--unemph-color);
position: absolute;
width: 2em;
height: 2em;
top: 0em;
right: 0.2em;
font-size: 0.9em;
}
.input-dialog {
color: var(--color);
border-width: 0;
font-size: 1em;
background: var(--hover-background);
padding: 0.8em;
border-radius: var(--border-radius-regular);
margin: 1em auto 1em auto;
width: 95%;
display: block;
}
.status-popup {
position: fixed;
top: 19%;
left: 50%;
transform: translate(-50%, -50%);
padding: 12px;
border-radius: 8px;
background-color: #f0f0f0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
font-size: 16px;
text-align: left;
opacity: 0;
visibility: hidden;
transition: opacity 0.1s ease, visibility 0.1s ease;
z-index: 10000;
}
.status-popup.show {
opacity: 1;
visibility: visible;
}
.status-popup.exists {
background-color: #f8d7da;
color: #721c24;
}
.status-popup.available {
background-color: #d4edda;
color: #155724;
}
.close-popup {
position: absolute;
top: -4px;
right: 5px;
cursor: pointer;
font-weight: bold;
}
.message {
display: none;
padding: 0.8em;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
margin: 2em 0;
}
.success-message {
background-color: #d4edda;
color: #155724;
}
.error-message {
background-color: #f8d7da;
color: #721c24;
}
.loader {
width: 1.5em;
height: 1.5em;
border: 0.2em solid #FFF;
border-bottom-color: transparent;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: rotation 1s linear infinite;
display: none;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}