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');
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 holderElement = document.getElementById('editorjs');
holderElement.innerHTML = '';
if (savedData) {
const parsedData = JSON.parse(savedData);
console.log('Loaded parsedData:', parsedData);
document.getElementById('title').value = parsedData.title || '';
document.getElementById('slogan').value = parsedData.slogan || '';
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");
const overlay = document.getElementById("overlay");
const menu = document.getElementById("floatingButtons");
const checkoutErrorMessage = document.getElementById("checkout-error-message");
function initializeEventListeners() {
// Prevent ENTER key in slogan element
document.getElementById('slogan').addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
event.preventDefault();
}
});
function openDialog() {
checkoutErrorMessage.style.display = "none";
dialog.style.display = "block";
overlay.style.display = "block";
menu.style.display = "none";
}
document.getElementById("buyButton").addEventListener("click", () => openDialog(checkoutDialog));
document.getElementById("closeDialogButton").addEventListener("click", () => closeDialog());
function closeDialog() {
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);
// // Editor save
document.getElementById('title').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('editModeButton').addEventListener('click', editModeWrapper);
document.getElementById("continueToEditMode").addEventListener('click', function() {
editMode(document.getElementById("dashEditInput").value);
});
document.getElementById('editModeButton').addEventListener('click', openEditModeDialog);
document.getElementById("continueToEditModeButton").addEventListener('click', () =>
editMode(document.getElementById("editModeDirectoryInput").value)
);
document.getElementById('continueEditingModeButton').addEventListener('click', continueMode);
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() {
const banner = document.getElementById('banner').src;
const title = document.getElementById('title').value;
const slogan = document.getElementById('slogan').value;
const titleValue = document.getElementById('title').value.trim();
const dataToSave = {
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) => {
const dataToSave = {
directory: sanitizeDirectoryTitle(title),
banner: banner || '/static/svg/banner.svg',
title: title,
slogan: slogan,
editor_data: editor_data
};
dataToSave.editor_data = editor_data;
localStorage.setItem('conex_data', JSON.stringify(dataToSave));
console.log('Editor data saved to localStorage');
}).catch((error) => {
@ -60,35 +162,22 @@ function saveEditorData() {
});
}
let typingTimeout;
let hideTimeout;
const directoryInput = document.getElementById('title');
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
});
function setupDirectoryInput(inputElement, debounceTime = 500) {
inputElement.addEventListener('input', () => {
clearTimeout(typingTimeout);
const directoryBuyInput = document.getElementById('dashEditInput');
directoryBuyInput.addEventListener('input', () => {
clearTimeout(typingTimeout);
typingTimeout = setTimeout(() => {
const directoryTitle = directoryBuyInput.value.trim();
if (directoryTitle.length > 0) {
const directory = sanitizeDirectoryTitle(directoryTitle);
checkDirectoryReverse(directory);
} else {
hidePopup();
}
}, 500); // Debounce
});
typingTimeout = setTimeout(() => {
const inputValue = inputElement.value.trim();
if (inputValue.length > 0) {
const sanitizedValue = sanitizeDirectoryTitle(inputValue); // Sanitize the input value
checkDirectory(sanitizedValue);
} else {
hidePopup();
}
}, debounceTime);
});
}
function sanitizeDirectoryTitle(title) {
return title
@ -100,48 +189,33 @@ function sanitizeDirectoryTitle(title) {
}
function checkDirectory(directory) {
if (directory.length < 4) {
return;
}
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');
});
if (!validateDirectoryLength(directory)) return;
fetchDirectoryStatus(directory, 'exists', 'available', 'El sitio web ya existe', 'Se publicará en');
}
function checkDirectoryReverse(directory) {
if (directory.length < 4) {
return;
function validateDirectoryLength(directory) {
if (directory.length < 4 || directory.length > 35) {
showPopup('El título debe tener entre 4 y 35 caracteres', 'exists');
return false;
}
if (directory.length > 35) {
showPopup(`El título no puede exceder los 35 caracteres`, 'exists');
return;
}
fetch(`/api/directory/${encodeURIComponent(directory)}`)
return true;
}
function fetchDirectoryStatus(directory, failureStatus, successStatus, failureMessage, successMessage) {
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(data => {
if (data.exists) {
showPopup(`Editar el sitio web conex.one/${directory}`, 'available');
} else {
showPopup(`El sitio conex.one/${directory} no está registrado`, 'exists');
}
})
.then(data => data.exists)
.catch(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) {
if (!popup) {
return;
}
popup.classList.remove('show');
setTimeout(() => {
popup.classList.remove(status);
}, 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 parsedData = savedData ? JSON.parse(savedData) : null;
const directory = parsedData?.directory || "temp";
@ -193,6 +274,10 @@ document.getElementById('imageUpload').addEventListener('change', function (even
formData.append('file', file);
formData.append('directory', directory);
uploadButton.disabled = true;
loader.style.display = 'inline-block';
imageIcon.style.display = 'none';
fetch('/api/upload', {
method: 'POST',
body: formData,
@ -205,9 +290,13 @@ document.getElementById('imageUpload').addEventListener('change', function (even
}
}).catch(error => {
console.error('Error uploading the image:', error);
}).finally(() => {
uploadButton.disabled = false;
loader.style.display = 'none';
imageIcon.style.display = 'inline-block';
});
}
});
}
function loadLanguage(lang) {
fetch(`./lang/${lang}.json`)
@ -230,12 +319,10 @@ function loadLanguage(lang) {
}
function dashboardMode() {
document.getElementById("dashOverlay").style.display = "none";
document.getElementById("dashDialog").style.display = "none";
const dashboard = document.querySelector('.dashboard');
loadEditorState();
const dashboard = document.getElementById('dashboard');
dashboard.style.display = 'flex';
dashboard.style.opacity = '0';
setTimeout(() => {
dashboard.style.transition = 'opacity 0.5s ease';
dashboard.style.opacity = '1';
@ -243,9 +330,15 @@ function dashboardMode() {
}
function buyMode() {
document.getElementById("openDialogButton").style.display = "block";
document.getElementById("updateSiteButton").style.display = "none";
const dashboard = document.querySelector('.dashboard');
localStorage.removeItem('conex_data');
loadEditorState();
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.opacity = '0';
@ -254,15 +347,61 @@ function buyMode() {
}, 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) {
const success = await loadEditorData(dir);
closeDialog();
const success = await fetchAndStoreData(dir);
if (!success) {
console.error("Data could not be loaded, aborting UI changes");
return;
}
document.getElementById("openDialogButton").style.display = "none";
document.getElementById("updateSiteButton").style.display = "block";
const dashboard = document.querySelector('.dashboard');
document.getElementById("buyButton").style.display = "none";
document.getElementById("editButton").style.display = "block";
const dashboard = document.getElementById('dashboard');
dashboard.style.transition = 'opacity 0.5s ease';
dashboard.style.opacity = '0';
@ -271,40 +410,21 @@ async function editMode(dir) {
}, 500);
}
function editModeWrapper() {
document.getElementById("dashOverlay").style.display = "block";
document.getElementById("dashDialog").style.display = "block";
document.getElementById("dashEditInput").style.display = "block";
}
function loadEditorData(dirname) {
return new Promise((resolve, reject) => {
fetch(`/api/fetch/${dirname}`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
// 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
});
});
async function fetchAndStoreData(directoryName) {
try {
const response = await fetch(`/api/fetch/${encodeURIComponent(directoryName)}`);
if (!response.ok) {
throw new Error(`Failed to fetch data for directory: ${directoryName}`);
}
const data = await response.json();
localStorage.setItem('conex_data', JSON.stringify(data));
console.log('Data fetched and stored in localStorage:', data);
loadEditorState();
return true;
} catch (error) {
console.error('Error fetching and storing data:', error);
return false;
}
}

View file

@ -1,179 +1,179 @@
const savedData = localStorage.getItem('conex_data');
const parsedData = savedData ? JSON.parse(savedData) : null;
const directory = parsedData?.directory || "temp";
function initializeEditor(conex_data) {
directory = conex_data?.directory || "temp";
const editor = new EditorJS({
// readOnly: false,
holder: 'editorjs',
editor = new EditorJS({
// readOnly: false,
holder: 'editorjs',
inlineToolbar: ['marker', 'bold', 'italic'],
inlineToolbar: true,
tools: {
/**
* Each Tool is a Plugin. Pass them via 'class' option with necessary settings {@link docs/tools.md}
*/
header: {
class: Header,
config: {
placeholder: 'Inserta un título',
levels: [1, 2, 3],
defaultLevel: 1,
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
inlineToolbar: ['marker', 'bold', 'italic'],
inlineToolbar: true,
tools: {
/**
* Each Tool is a Plugin. Pass them via 'class' option with necessary settings {@link docs/tools.md}
*/
header: {
class: Header,
config: {
placeholder: 'Inserta un título',
levels: [1, 2, 3],
defaultLevel: 1,
shortcut: 'CMD+SHIFT+H'
}
},
{
type : 'paragraph',
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"
image: {
class: ImageTool,
config: {
endpoints: {
byFile: `${window.location.origin}/api/upload`,
},
field: 'file',
types: 'image/*',
additionalRequestData: {
directory: directory,
},
},
"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": {
"Add": "Insertar"
{
type : 'paragraph',
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
* 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"
* Section for translation Tool Names: both block and inline tools
*/
"delete": {
"Delete": "Quitar bloque"
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",
},
"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>
<html lang="es">
<head>
<title>Builder | CONEX.one </title>
<link rel="canonical" href="https://builder.conex.one">
<title>Editor | CONEX.one </title>
<link rel="canonical" href="https://editor.conex.one">
<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="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="index, follow">
<meta charset="utf-8">
<link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/editorjs-dark.css">
</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="mode-button-container">
<h1 class="dashboard-text" data-translate="welcome"></h1>
<p class="dashboard-text" data-translate="about"></p>
<span data-translate="buyModeButton" id="buyModeButton" class="mode-button buyModeButton"></span>
<span data-translate="editModeButton" id="editModeButton" class="mode-button editModeButton"></span>
</div>
<div id="dashOverlay"></div>
<div id="dashDialog">
<h2 data-translate="editDialogHeader"></h2>
<p data-translate="editDialogParagraph"></p>
<input type="text" id="dashBuyInput" name="title" maxlength="35" class="input-title" data-translate="sampleTitle" placeholder="" >
<input type="text" id="dashEditInput" name="title" maxlength="35" class="input-title" data-translate="sampleTitle" placeholder="" >
<button id="continueToEditMode">
<span data-translate="continueToEditMode"></span>
</button>
<div class="dashboard-content">
<h1 data-translate="welcome"></h1>
<p data-translate="about"></p>
<span id="buyModeButton">
<h4 data-translate="buyModeButton"></h4>
<p class="localstorage-exists" data-translate="buyModeButtonDisclaimer"></p>
</span>
<span id="continueEditingModeButton">
<h4 data-translate="continueEditingModeButton"></h4>
</span>
<span id="editModeButton">
<h4 data-translate="editModeButton"></h4>
<p class="localstorage-exists" data-translate="editModeButtonDisclaimer"></p>
</span>
</div>
</div>
<div class="banner">
<input type="file" id="imageUpload" accept="image/*">
<label for="imageUpload" class="upload-button">
<img src="/static/svg/image.svg" alt="Edit Icon" class="icon">
</label>
<img id="banner" name="banner" src="/static/svg/banner.svg" class="banner-image"/>
<div class="desc">
<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 class="banner-toolbar">
<input type="file" id="uploadBannerBtn" class="tool-button-input" accept="image/*">
<label for="uploadBannerBtn" class="tool-button">
<img src="/static/svg/image.svg" alt="Edit Icon" class="icon">
<span class="loader"></span>
</label>
</div>
<div id="status-popup" class="status-popup">
<span class="close-popup" onclick="hidePopup()">×</span>
<span id="status-message"></span>
<img id="banner" src="/static/svg/banner.svg" class="banner-image"/>
<div class="desc">
<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>
<body>
<div class="content">
<div id="editor-container">
<div id="editorjs"></div>
</div>
</div>
<div id="editorjs"></div>
</body>
<div id="overlay"></div>
<div class="floating-buttons" id="floatingButtons">
<button class="floating-button" id="openDialogButton">
<div class="floating-buttons-container" id="floatingButtons">
<button class="floating-button" id="buyButton">
<img src="/static/svg/check.svg">
<span data-translate="buyButton"></span>
</button>
<button class="floating-button" id="updateSiteButton">
<button class="floating-button" id="editButton">
<img src="/static/svg/check.svg">
<span data-translate="editButton"></span>
</button>
@ -70,32 +67,33 @@
</button>
</div>
<div id="dialog">
<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 id="checkout-error-message"></div>
<div id="checkout-success-message"></div>
<div id="paypal-button-container"></div>
<button id="cancelDialogButton" type="button">
<button id="closeDialogButton" type="button">
<picture>
<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>
</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>
<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>

View file

@ -2,13 +2,17 @@
"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\"",
"buyModeButton": "Diseñar un nuevo sitio",
"buyModeButtonDisclaimer": "Limpiará el progreso actual para diseñar un sitio desde cero",
"editModeButton": "Editar sitio existente",
"editModeButtonDisclaimer": "Limpiará el progreso actual para cargar un sitio existente",
"continueEditingModeButton": "Continuar editando",
"editDialogHeader": "Editar un sitio existente",
"editDialogParagraph": "El sitio ya debe estar registrado en conex.one para poder cargarlo localmente.",
"continueToEditMode": "Editar este sitio",
"continueToEditModeButton": "Editar este sitio",
"buyButton": "Guardar",
"editButton": "Aplicar cambios",
"dashButton": "Panel principal",
"checkoutSaveNotif": "¡El progreso se guardó con éxito!",
"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:",
"supportEmail": "Correo electrónico: soporte@conex.one",

View file

@ -90,8 +90,11 @@ paypal.Buttons({
<p>
Estado: <strong>${transaction.status}</strong><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>
<a href="https://conex.one/${savedData.directory}/">conex.one/${savedData.directory}</a>
</p>
`,);
document.querySelector("#paypal-button-container").style.display = "none";
console.log(

View file

@ -12,6 +12,7 @@
--line-height: 1.7;
--smaller-font: 0.75em;
--hyper-color: #0f82af;
--border-radius-regular: 10px;
text-align: justify;
margin: auto;
}
@ -80,234 +81,79 @@ a {
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 {
font-size: 2em;
font-weight: bold;
text-align: center;
width: 100%;
background-color: #00000000;
border-color: #00000000;
color: white;
margin-bottom: -0.2em;
}
.input-slogan {
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;
#editorjs {
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;
width: 100%;
margin: 1em 0;
}
footer {
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 {
.floating-buttons-container {
display: flex;
position: fixed;
flex-direction: row;
align-items: center;
bottom: 0.2em;
right: 0.5em;
gap: 0.4em;
z-index: 2000;
bottom: 1.5em;
right: 1.5em;
gap: 0.6em;
z-index: 30;
}
.floating-button {
height: 2.5em;
cursor: pointer;
font-size: 1em;
z-index: 2000;
border-radius: 999px;
box-sizing: border-box;
color: rgba(255, 255, 255, 0.85);
font-size: 1em;
font-weight: bold;
outline: 0 solid transparent;
padding: 8px 18px;
color: #ffffff;
padding: 0 1em;
width: fit-content;
word-break: break-word;
border: 0;
@ -323,16 +169,12 @@ button {
margin-left: 0.6em;
}
.floating-button:hover {
background-color: #0056b3;
}
#updateSiteButton {
#editButton {
background: linear-gradient(135deg, #214353, #4c9abf);
box-shadow: #0099c5 0 10px 20px -15px;
}
#openDialogButton {
#buyButton {
background: linear-gradient(135deg, #21532a, #4fc764);
box-shadow: #27d100 0 10px 20px -15px;
}
@ -342,94 +184,33 @@ button {
box-shadow: #8f53b9 0 10px 20px -15px;
}
.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;
}
#imageUpload {
#overlay {
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;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 50;
}
#dashboard {
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;
align-items: center;
flex-direction: column;
z-index: 3000;
z-index: 40;
}
.dashboard-text {
.dashboard-content {
color: white;
}
.mode-button-container {
width: var(--page-width);
display: flex;
flex-direction: column;
@ -438,38 +219,50 @@ button {
height: 100vh;
}
.mode-button {
.dashboard-content {
position: fixed;
top: -10%;
}
#buyModeButton,
#editModeButton,
#continueEditingModeButton {
width: 90%;
padding: 1em;
margin: 1em 0;
border-radius: 15px;
border-radius: var(--border-radius-regular);
text-align: center;
text-decoration: none;
font-weight: bold;
color: white;
cursor: pointer;
}
.buyModeButton {
background: linear-gradient(45deg, #21532a, #4fc764);
#buyModeButton h4,
#editModeButton h4,
#continueEditingModeButton h4 {
margin: 0;
}
.buyModeButton:hover {
background: linear-gradient(45deg, #173a1f, #3a9a4d);
#buyModeButton p,
#editModeButton p,
#continueEditingModeButton p {
margin: 0;
font-weight: normal;
display: none;
font-size: 0.8em;
}
.editModeButton {
background: linear-gradient(45deg, #3498db, #007bff);
#buyModeButton {
background-image: linear-gradient(45deg, #043b0c, #39994b);
}
.editModeButton:hover {
background: linear-gradient(45deg, #2980b9, #0056b3);
#editModeButton {
background-image: linear-gradient(45deg, #154162, #56a2e8);
}
.dashboard {
background: linear-gradient(315deg, #0c4848 3%, #071832 98%);
animation: gradient 15s ease infinite;
background-size: 400% 400%;
#continueEditingModeButton {
display: none;
background-image: linear-gradient(45deg, #4a3b72, #4a3b72);
}
@keyframes gradient {
@ -531,3 +324,151 @@ button {
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);
}
}