Files
oca-py.org/faq/index.html
T

253 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet, noimageindex" />
<meta name="googlebot" content="noindex, nofollow" />
<title>FAQ — OCA Paraguay</title>
<link rel="icon" type="image/svg+xml" href="../img/favicon.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Sora:wght@600;700;800&display=swap" rel="stylesheet" />
<style>
:root{
--plum:#714B67; --plum-dark:#4b2f43; --violet:#6C4CE0; --violet-soft:#8f7bf0;
--mint:#16C79A; --mint-dark:#0e8f6e; --py-red:#D52B1E; --py-blue:#0038A8;
--ink:#231b2b; --muted:#6b6473; --line:#e7e3ec; --bg:#faf9fc;
}
*{box-sizing:border-box}
body{margin:0;font-family:Inter,system-ui,sans-serif;background:var(--bg);color:var(--ink);line-height:1.55}
header{background:#fff;border-bottom:1px solid var(--line);position:sticky;top:0;z-index:20}
.wrap{max-width:920px;margin:0 auto;padding:0 20px}
.head{display:flex;align-items:center;gap:14px;padding:14px 0;flex-wrap:wrap}
.head img{width:38px;height:38px;border-radius:9px}
.head h1{font-family:Sora,sans-serif;font-size:1.05rem;margin:0}
.head h1 small{display:block;font-family:Inter,sans-serif;font-weight:500;font-size:.72rem;color:var(--muted);letter-spacing:.04em;text-transform:uppercase}
.tools{margin-left:auto;display:flex;align-items:center;gap:10px}
.noidx{font-size:.64rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--py-red);border:1px solid currentColor;border-radius:999px;padding:3px 9px;white-space:nowrap}
.lang{display:inline-flex;background:#f2eff8;border-radius:999px;padding:3px}
.lang button{border:none;background:transparent;font:inherit;font-size:.76rem;font-weight:700;padding:5px 11px;border-radius:999px;cursor:pointer;color:var(--muted)}
.lang button.on{background:#fff;color:var(--violet);box-shadow:0 1px 3px rgba(0,0,0,.12)}
main{padding:26px 0 60px}
.bar{margin-bottom:22px}
.bar input{width:100%;padding:12px 15px;border:1px solid var(--line);border-radius:12px;font:inherit;background:#fff}
.bar input:focus{outline:2px solid rgba(108,76,224,.22);border-color:var(--violet-soft)}
.msg{padding:12px 16px;border-radius:12px;margin-bottom:18px;font-size:.9rem;display:none}
.msg.show{display:block}
.msg.err{background:#fdf1ef;color:#8f2117;border:1px solid #f0c9c4}
.area{margin:0 0 26px}
.area h2{font-family:Sora,sans-serif;font-size:.8rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin:0 0 10px}
.card{background:#fff;border:1px solid var(--line);border-radius:14px;margin-bottom:10px;overflow:hidden;transition:border-color .18s,box-shadow .18s}
.card[open]{border-color:var(--violet-soft);box-shadow:0 8px 26px rgba(108,76,224,.09)}
.card summary{cursor:pointer;padding:15px 18px;font-weight:600;list-style:none;display:flex;align-items:center;gap:12px}
.card summary::-webkit-details-marker{display:none}
.card summary::after{content:"+";margin-left:auto;color:var(--violet);font-weight:800;font-size:1.15rem;line-height:1}
.card[open] summary::after{content:"–"}
.card .ans{padding:0 18px 17px;margin:0;white-space:pre-line;color:#41394a;font-size:.93rem}
.empty{color:var(--muted);padding:16px 0}
footer{border-top:1px solid var(--line);background:#fff;padding:18px 0;font-size:.76rem;color:var(--muted);text-align:center}
</style>
</head>
<body>
<header>
<div class="wrap head">
<img src="../img/logo.svg" alt="OCA Paraguay" width="38" height="38" />
<h1><span data-i18n="title">Preguntas frecuentes</span><small data-i18n="subtitle">OCA Paraguay · uso interno</small></h1>
<div class="tools">
<span class="noidx" data-i18n="badge">no indexada</span>
<span class="lang" role="group" aria-label="Language">
<button type="button" data-lang="es">ES</button>
<button type="button" data-lang="en">EN</button>
</span>
</div>
</div>
</header>
<main class="wrap">
<div class="msg" id="msg"></div>
<div class="bar">
<input type="search" id="q" data-i18n-placeholder="search" placeholder="Buscar…" aria-label="Buscar" />
</div>
<div id="list"><p class="empty" data-i18n="loading">Cargando…</p></div>
</main>
<footer><span data-i18n="footer">Página interna · no indexada</span> · <span id="count">—</span></footer>
<script>
const API = 'https://n8n.odoo4projects.com/webhook/oca-py/faq';
const el = (id) => document.getElementById(id);
const I18N = {
es: {
title: 'Preguntas frecuentes', subtitle: 'OCA Paraguay · uso interno', badge: 'no indexada',
search: 'Buscar pregunta, área o respuesta…', loading: 'Cargando…', empty: 'Sin resultados.',
footer: 'Página interna · no indexada', count: (n, total) => n + ' de ' + total + ' preguntas',
group: 'General', errRead: 'No se pudo leer el webhook',
errData: 'El webhook respondió sin datos',
hint: 'En n8n, poné el nodo Webhook en "Respond: When Last Node Finishes".',
},
en: {
title: 'Frequently asked questions', subtitle: 'OCA Paraguay · internal use', badge: 'not indexed',
search: 'Search question, area or answer…', loading: 'Loading…', empty: 'No results.',
footer: 'Internal page · not indexed', count: (n, total) => n + ' of ' + total + ' questions',
group: 'General', errRead: 'Could not read the webhook',
errData: 'The webhook returned no data',
hint: 'In n8n, set the Webhook node to "Respond: When Last Node Finishes".',
}
};
function store(key, val) {
try { localStorage.setItem(key, val); } catch (e) { /* private mode / blocked storage */ }
}
function stored(key) {
try { return localStorage.getItem(key); } catch (e) { return null; }
}
const state = {
lang: ((stored('ocaFaqLang') || (navigator.language || 'es').slice(0, 2)) === 'en') ? 'en' : 'es',
items: [],
};
const t = (k) => I18N[state.lang][k] ?? k;
function applyLang() {
document.documentElement.lang = state.lang;
document.querySelectorAll('[data-i18n]').forEach(n => { n.textContent = t(n.dataset.i18n); });
document.querySelectorAll('[data-i18n-placeholder]').forEach(n => { n.placeholder = t(n.dataset.i18nPlaceholder); });
document.querySelectorAll('.lang button').forEach(b => b.classList.toggle('on', b.dataset.lang === state.lang));
render();
}
function flash(text) {
const m = el('msg');
m.textContent = text;
m.className = 'msg show err';
}
function hideMsg() { el('msg').className = 'msg'; }
function normalize(payload) {
if (Array.isArray(payload)) {
if (payload.length && Array.isArray(payload[0].data)) return payload[0].data;
if (payload.length && payload[0].id !== undefined) return payload;
}
if (payload && Array.isArray(payload.data)) return payload.data;
return [];
}
// Optional per-language variants: Question_en / Answer_es / area_en …
function pick(it, field) {
const want = (field + '_' + state.lang).toLowerCase();
const key = Object.keys(it).find(k => k.toLowerCase() === want);
if (key && it[key]) return it[key];
return it[field] ?? '';
}
// One field, two languages: "texto ES\n=== EN ===\ntext EN"
const LANG_SPLIT = /\n\s*===\s*EN\s*===\s*\n/i;
function content(it, field) {
const raw = pick(it, field);
if (typeof raw !== 'string' || !LANG_SPLIT.test(raw)) return raw;
const parts = raw.split(LANG_SPLIT);
return (state.lang === 'en' ? parts[1] : parts[0]) || parts[0] || '';
}
// Optional language tag on an item (lang / language / idioma)
function itemLang(it) {
const key = Object.keys(it).find(k => ['lang', 'language', 'idioma'].includes(k.toLowerCase()));
const v = key ? String(it[key] || '').toLowerCase().slice(0, 2) : '';
return (v === 'es' || v === 'en') ? v : '';
}
function render() {
const list = el('list');
if (!state.items.length) {
list.innerHTML = '<p class="empty">' + t('empty') + '</p>';
el('count').textContent = t('count')(0, 0);
return;
}
const term = (el('q').value || '').trim().toLowerCase();
const shown = state.items.filter(it => {
const lang = itemLang(it);
if (lang && lang !== state.lang) return false;
if (!term) return true;
return [pick(it, 'area'), pick(it, 'Question'), pick(it, 'Answer')]
.join(' ').toLowerCase().includes(term);
});
el('count').textContent = t('count')(shown.length, state.items.length);
list.innerHTML = '';
if (!shown.length) { list.innerHTML = '<p class="empty">' + t('empty') + '</p>'; return; }
const groups = {};
shown.forEach(it => {
const area = pick(it, 'area') || t('group');
(groups[area] = groups[area] || []).push(it);
});
Object.keys(groups).sort().forEach(area => {
const sec = document.createElement('section');
sec.className = 'area';
const h = document.createElement('h2');
h.textContent = area;
sec.appendChild(h);
groups[area].forEach(it => {
const card = document.createElement('details');
card.className = 'card';
const sum = document.createElement('summary');
sum.textContent = content(it, 'Question') || '—';
const ans = document.createElement('p');
ans.className = 'ans';
ans.textContent = content(it, 'Answer') || '';
card.append(sum, ans);
sec.appendChild(card);
});
list.appendChild(sec);
});
}
async function load() {
try {
const res = await fetch(API, { headers: { Accept: 'application/json' } });
if (!res.ok) throw new Error('HTTP ' + res.status);
const payload = await res.json();
state.items = normalize(payload);
hideMsg();
render();
if (!state.items.length && !Array.isArray(payload)) {
flash(t('errData') + ' (' + JSON.stringify(payload) + '). ' + t('hint'));
}
} catch (err) {
state.items = [];
render();
flash(t('errRead') + ' (' + err.message + ').');
}
}
const langBox = document.querySelector('.lang');
if (langBox) {
langBox.addEventListener('click', (ev) => {
const b = ev.target.closest('button[data-lang]');
if (!b) return;
state.lang = b.dataset.lang;
store('ocaFaqLang', state.lang);
applyLang();
});
}
// Accordion: only one answer open at a time
el('list').addEventListener('toggle', (ev) => {
const d = ev.target;
if (!d || d.tagName !== 'DETAILS' || !d.open) return;
el('list').querySelectorAll('details[open]').forEach(o => { if (o !== d) o.open = false; });
}, true);
el('q').addEventListener('input', render);
applyLang();
load();
</script>
</body>
</html>