frontera con upstream: lo del movil sale de los ficheros del dev

docs/FRONTERA_UPSTREAM.md mide como cambia upstream (39 releases de la 1.0.1 a
la 1.3.3; backend.js en 34, main_views en 31, OasisMobile.css en 21) y fija que
se toca: backend, middleware, modelos, server y traducciones de upstream, no;
vistas de upstream, solo una linea de gancho; hojas, vistas, router y modelos
propios, si.

Fase A, lo del 19/10:
- backend.js, workflows_model.js y modules_view.js vuelven a como estaban
- seccion apagada, /modules/enable y ?saved=1: en el router propio, que envuelve
  las redirecciones de upstream en vez de cambiar checkMod
- Modulos del movil en views/mobile_modules_view.js, servida por el router propio
- workflow propio "acampada" registrado desde models/fork_workflows.js en vez de
  redefinir "activists"
- votaciones, tribus, ajustes, borrar, pie y bienvenida: la logica en
  views/mobile_ui.js; en cada vista de upstream queda la llamada
This commit is contained in:
SITO 2026-10-19 13:04:38 +02:00
parent 9b391cfc3d
commit d04d99ece2
14 changed files with 571 additions and 267 deletions

View file

@ -1,74 +1,9 @@
const { form, button, div, h2, h3, p, section, table, tr, td, input, a, br, ul, li, span, strong, label } = require("../server/node_modules/hyperaxe");
const { template, i18n, LAUNCHER_GROUPS } = require('./main_views');
const { iconNode } = require('./mobile_icons');
const { form, button, div, h2, p, section, table, tr, td, input, a, br, ul, li, span, strong } = require("../server/node_modules/hyperaxe");
const { template, i18n } = require('./main_views');
const { getConfig } = require('../configs/config-manager.js');
const { PRESETS: PRESET_LIST } = require('../models/workflows_model');
// en el movil: los modulos agrupados como en el lanzador, cada uno una fila que se
// toca entera con un interruptor, y Guardar siempre a la vista. La tabla de 52 filas
// con casillas de 16px eran 11 pantallas
const mobileModulesView = (visibleModules, moduleStates, opts) => {
const groupOf = {};
(LAUNCHER_GROUPS || []).forEach(g => g.mods.forEach(m => { if (m.mod) groupOf[m.mod.replace(/Mod$/, '')] = g; }));
// lo que no sale en el lanzador: Industria va con la economia; lo demas, al final
const economy = (LAUNCHER_GROUPS || []).find(g => g.id === 'economy');
if (economy && !groupOf.industry) groupOf.industry = economy;
const groups = [];
const byId = {};
visibleModules.forEach(mod => {
const g = groupOf[mod.name] || { id: 'other', titleKey: 'menuTools' };
if (!byId[g.id]) { byId[g.id] = { g, mods: [] }; groups.push(byId[g.id]); }
byId[g.id].mods.push(mod);
});
const order = (LAUNCHER_GROUPS || []).map(g => g.id);
const rank = (id) => { const i = order.indexOf(id); return i === -1 ? order.length : i; };
groups.sort((x, y) => rank(x.g.id) - rank(y.g.id));
const on = visibleModules.filter(m => moduleStates[`${m.name}Mod`] === 'on').length;
const blocked = opts.blocked && visibleModules.find(m => `${m.name}Mod` === opts.blocked.mod);
const blockedBox = blocked ? div({ class: 'module-blocked' },
iconNode('module-blocked-ico', blocked.name),
p({ class: 'module-blocked-text' }, String(i18n.moduleBlockedText || '{name} is turned off.').replace('{name}', blocked.label)),
form({ method: 'POST', action: '/modules/enable' },
input({ type: 'hidden', name: 'mod', value: blocked.name }),
input({ type: 'hidden', name: 'returnTo', value: opts.blocked.path || `/${blocked.name}` }),
button({ type: 'submit', class: 'create-button' }, String(i18n.moduleBlockedAction || 'Turn on {name}').replace('{name}', blocked.label))
)
) : null;
return template(
i18n.modules,
section(div({ class: 'tags-header module-header-line' },
h2(i18n.modulesViewTitle),
p(String(i18n.modulesOnCount || '{on} of {total} sections on').replace('{on}', on).replace('{total}', visibleModules.length))
)),
blockedBox,
opts.saved ? p({ class: 'module-saved', role: 'status' }, i18n.modulesSaved || 'Saved.') : null,
section(
form({ action: '/save-modules', method: 'post', class: 'module-switches' },
...groups.map(({ g, mods }) => div({ class: 'module-group' },
h3({ class: 'module-group-title' }, i18n[g.titleKey] || g.id),
...mods.map(mod => label({ class: 'module-switch', for: `${mod.name}Mod` },
iconNode('module-switch-ico', mod.name),
span({ class: 'module-switch-text' },
span({ class: 'module-switch-name' }, mod.label),
span({ class: 'module-switch-desc' }, mod.description)
),
input({
type: 'checkbox', role: 'switch', id: `${mod.name}Mod`, name: `${mod.name}Form`, value: 'on', class: 'switch-input',
checked: moduleStates[`${mod.name}Mod`] === 'on' ? true : undefined
})
))
)),
div({ class: 'module-save-bar' },
button({ type: 'submit', class: 'create-button' }, i18n.saveSettings)
)
)
)
);
};
const modulesView = (_states, opts = {}) => {
const modulesView = () => {
const config = getConfig().modules;
const modules = [
{ name: 'agenda', label: i18n.modulesAgendaLabel, description: i18n.modulesAgendaDescription },
@ -141,8 +76,6 @@ const modulesView = (_states, opts = {}) => {
return acc;
}, {});
if (isMobile) return mobileModulesView(visibleModules, moduleStates, opts);
const activeModulesCount = visibleModules.filter(mod => moduleStates[`${mod.name}Mod`] === 'on').length;
const totalModulesCount = visibleModules.length;
const disabledModulesCount = totalModulesCount - activeModulesCount;