diff --git a/backend/src/routes/admin.js b/backend/src/routes/admin.js
index f5accb5..bb68d31 100644
--- a/backend/src/routes/admin.js
+++ b/backend/src/routes/admin.js
@@ -18,6 +18,13 @@ const {
createQuestionSetEmpty,
addQuestionToSet
} = require('../services/questionService');
+const {
+ listResultModel,
+ createResultModel,
+ updateResultModel,
+ exportResultModelTemplate,
+ importResultModelExcel
+} = require('../services/resultModelService');
const upload = multer();
const router = express.Router();
@@ -183,6 +190,60 @@ router.get('/question-sets/:id/questions', async (req, res, next) => {
}
});
+router.get('/result-model', async (req, res, next) => {
+ try {
+ const rows = await listResultModel(req.tenantPool);
+ res.json(rows);
+ } catch (err) {
+ next(err);
+ }
+});
+
+router.post('/result-model', async (req, res, next) => {
+ try {
+ const { dimension, variable, subvariable, score_value, max_score, sort_order } = req.body;
+ if (!dimension || !variable || !subvariable) {
+ return res.status(400).json({ error: 'dimension, variable y subvariable requeridos' });
+ }
+ const result = await createResultModel(req.tenantPool, { dimension, variable, subvariable, score_value, max_score, sort_order });
+ res.json(result);
+ } catch (err) {
+ next(err);
+ }
+});
+
+router.patch('/result-model/:id', async (req, res, next) => {
+ try {
+ const { score_value, max_score, sort_order } = req.body;
+ await updateResultModel(req.tenantPool, req.params.id, { score_value, max_score, sort_order });
+ res.json({ ok: true });
+ } catch (err) {
+ next(err);
+ }
+});
+
+router.get('/result-model/template', async (req, res, next) => {
+ try {
+ const buffer = await exportResultModelTemplate();
+ res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ res.setHeader('Content-Disposition', 'attachment; filename=\"clima_result_model.xlsx\"');
+ res.send(buffer);
+ } catch (err) {
+ next(err);
+ }
+});
+
+router.post('/result-model/import', upload.single('file'), async (req, res, next) => {
+ try {
+ if (!req.file) return res.status(400).json({ error: 'Archivo requerido' });
+ const mode = req.body?.mode || 'replace';
+ const result = await importResultModelExcel(req.file.buffer, req.tenantPool, mode);
+ res.json(result);
+ } catch (err) {
+ next(err);
+ }
+});
+
router.get('/dimensions', async (req, res, next) => {
try {
const rows = await listDimensions(req.tenantPool);
diff --git a/backend/src/scripts/generate_result_model_template.js b/backend/src/scripts/generate_result_model_template.js
new file mode 100644
index 0000000..5b0aadf
--- /dev/null
+++ b/backend/src/scripts/generate_result_model_template.js
@@ -0,0 +1,21 @@
+const path = require('path');
+const ExcelJS = require('exceljs');
+
+async function main() {
+ const workbook = new ExcelJS.Workbook();
+ const sheet = workbook.addWorksheet('Resultados');
+ sheet.addRow(['dimension', 'variable', 'subvariable', 'max_score', 'sort_order']);
+ sheet.addRow(['CREDIBILIDAD', 'Comunicación', 'Accesibilidad', 100, 1]);
+ sheet.addRow(['CREDIBILIDAD', 'Comunicación', 'Información', 100, 2]);
+ sheet.addRow(['RESPETO', 'Apoyo', 'Desarrollo', 100, 3]);
+ sheet.addRow(['IMPARCIALIDAD', 'Equidad', 'Ausencia de favoritismo', 100, 4]);
+
+ const outPath = path.resolve(__dirname, '../../../templates/plantilla_matriz_clima.xlsx');
+ await workbook.xlsx.writeFile(outPath);
+ console.log('Template generado:', outPath);
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/backend/src/services/resultModelService.js b/backend/src/services/resultModelService.js
new file mode 100644
index 0000000..7f8d152
--- /dev/null
+++ b/backend/src/services/resultModelService.js
@@ -0,0 +1,89 @@
+const ExcelJS = require('exceljs');
+
+async function listResultModel(pool) {
+ const [rows] = await pool.query(
+ 'SELECT id, dimension, variable, subvariable, score_value, max_score, sort_order FROM climate_result_model ORDER BY sort_order, id'
+ );
+ return rows;
+}
+
+async function createResultModel(pool, data) {
+ const { dimension, variable, subvariable, score_value, max_score, sort_order } = data;
+ const [res] = await pool.query(
+ 'INSERT INTO climate_result_model (dimension, variable, subvariable, score_value, max_score, sort_order) VALUES (?, ?, ?, ?, ?, ?)',
+ [dimension, variable, subvariable, score_value || null, max_score || null, sort_order || 0]
+ );
+ return { id: res.insertId };
+}
+
+async function updateResultModel(pool, id, data) {
+ const { score_value, max_score, sort_order } = data;
+ await pool.query(
+ 'UPDATE climate_result_model SET score_value = COALESCE(?, score_value), max_score = COALESCE(?, max_score), sort_order = COALESCE(?, sort_order) WHERE id = ?',
+ [score_value, max_score, sort_order, id]
+ );
+}
+
+async function exportResultModelTemplate() {
+ const workbook = new ExcelJS.Workbook();
+ const sheet = workbook.addWorksheet('Resultados');
+ sheet.addRow(['dimension', 'variable', 'subvariable', 'score_value', 'max_score', 'sort_order']);
+ sheet.addRow(['CREDIBILIDAD', 'Comunicación', 'Accesibilidad', 37.33, '', 1]);
+ return workbook.xlsx.writeBuffer();
+}
+
+async function importResultModelExcel(buffer, pool, mode = 'replace') {
+ const workbook = new ExcelJS.Workbook();
+ await workbook.xlsx.load(buffer);
+ const sheet = workbook.worksheets[0];
+ if (!sheet) return { processed: 0, errors: ['No hay hoja en el archivo'] };
+
+ const headers = {};
+ const headerRow = sheet.getRow(1);
+ headerRow.eachCell((cell, idx) => {
+ const key = String(cell.value || '').trim().toLowerCase();
+ if (key) headers[key] = idx;
+ });
+
+ const required = ['dimension', 'variable', 'subvariable'];
+ for (const r of required) {
+ if (!headers[r]) return { processed: 0, errors: [`Falta columna ${r}`] };
+ }
+
+ if (mode === 'replace') {
+ await pool.query('DELETE FROM climate_result_model');
+ }
+
+ let processed = 0;
+ const errors = [];
+
+ for (let i = 2; i <= sheet.rowCount; i++) {
+ const row = sheet.getRow(i);
+ const dimension = String(row.getCell(headers.dimension).value || '').trim();
+ const variable = String(row.getCell(headers.variable).value || '').trim();
+ const subvariable = String(row.getCell(headers.subvariable).value || '').trim();
+ if (!dimension || !variable || !subvariable) {
+ errors.push(`Fila ${i}: dimension, variable y subvariable son obligatorios`);
+ continue;
+ }
+ const score_value = headers.score_value ? row.getCell(headers.score_value).value : null;
+ const max_score = headers.max_score ? row.getCell(headers.max_score).value : null;
+ const sort_order = headers.sort_order ? row.getCell(headers.sort_order).value : null;
+
+ await pool.query(
+ 'INSERT INTO climate_result_model (dimension, variable, subvariable, score_value, max_score, sort_order) VALUES (?, ?, ?, ?, ?, ?)',
+ [dimension, variable, subvariable, score_value || null, max_score || null, sort_order || 0]
+ );
+ processed += 1;
+ }
+
+ return { processed, errors };
+}
+
+module.exports = {
+ listResultModel,
+ createResultModel,
+ updateResultModel,
+ exportResultModelTemplate,
+ importResultModelExcel
+};
diff --git a/backend/src/sql/tenant_init.sql b/backend/src/sql/tenant_init.sql
index 2b13e3d..cdcc8eb 100644
--- a/backend/src/sql/tenant_init.sql
+++ b/backend/src/sql/tenant_init.sql
@@ -1,4 +1,5 @@
SET FOREIGN_KEY_CHECKS=0;
SOURCE tenant_schema.sql;
SOURCE seed_questions.sql;
+SOURCE seed_result_model.sql;
SET FOREIGN_KEY_CHECKS=1;
diff --git a/backend/src/sql/tenant_migration_questions.sql b/backend/src/sql/tenant_migration_questions.sql
index 022ea97..d5e739c 100644
--- a/backend/src/sql/tenant_migration_questions.sql
+++ b/backend/src/sql/tenant_migration_questions.sql
@@ -75,3 +75,47 @@ UPDATE survey_questions sq JOIN questions q ON q.id = sq.question_id SET sq.ques
UPDATE responses r JOIN questions q ON q.id=r.question_id SET r.question_code = q.code WHERE r.question_code IS NULL OR r.question_code='';
-- Add unique constraints if not exists (run separately if needed)
+
+-- Result model table and seed
+CREATE TABLE IF NOT EXISTS climate_result_model (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ dimension VARCHAR(120) NOT NULL,
+ variable VARCHAR(120) NOT NULL,
+ subvariable VARCHAR(160) NOT NULL,
+ score_value DECIMAL(6,2) NULL,
+ max_score DECIMAL(6,2) NULL,
+ sort_order INT NOT NULL DEFAULT 0,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+INSERT INTO climate_result_model (dimension, variable, subvariable, score_value, max_score, sort_order)
+SELECT * FROM (
+ SELECT 'CREDIBILIDAD', 'Comunicación', 'Accesibilidad', NULL, 100, 1 UNION ALL
+ SELECT 'CREDIBILIDAD', 'Comunicación', 'Información', NULL, 100, 2 UNION ALL
+ SELECT 'CREDIBILIDAD', 'Integridad', 'Confiabilidad', NULL, 100, 3 UNION ALL
+ SELECT 'CREDIBILIDAD', 'Integridad', 'Honestidad', NULL, 100, 4 UNION ALL
+ SELECT 'CREDIBILIDAD', 'Liderazgo', 'Coordinación', NULL, 100, 5 UNION ALL
+ SELECT 'CREDIBILIDAD', 'Liderazgo', 'Delegación', NULL, 100, 6 UNION ALL
+ SELECT 'CREDIBILIDAD', 'Liderazgo', 'Visión', NULL, 100, 7 UNION ALL
+ SELECT 'RESPETO', 'Apoyo', 'Desarrollo', NULL, 100, 8 UNION ALL
+ SELECT 'RESPETO', 'Apoyo', 'Reconocimiento', NULL, 100, 9 UNION ALL
+ SELECT 'RESPETO', 'Cuidado', 'Entorno de Trabajo', NULL, 100, 10 UNION ALL
+ SELECT 'RESPETO', 'Cuidado', 'Vida Personal', NULL, 100, 11 UNION ALL
+ SELECT 'RESPETO', 'Sinergia de Equipo', 'Participación', NULL, 100, 12 UNION ALL
+ SELECT 'IMPARCIALIDAD', 'Equidad', 'Ausencia de favoritismo', NULL, 100, 13 UNION ALL
+ SELECT 'IMPARCIALIDAD', 'Equidad', 'Equidad en el trato', NULL, 100, 14 UNION ALL
+ SELECT 'IMPARCIALIDAD', 'Equidad', 'Equidad en la remuneración', NULL, 100, 15 UNION ALL
+ SELECT 'IMPARCIALIDAD', 'Justicia', 'Capacidad de apelación', NULL, 100, 16 UNION ALL
+ SELECT 'IMPARCIALIDAD', 'Justicia', 'Justicia en el trato a las personas', NULL, 100, 17 UNION ALL
+ SELECT 'CAMARADERIA', 'Fraternidad', 'Compañerismo', NULL, 100, 18 UNION ALL
+ SELECT 'CAMARADERIA', 'Hospitalidad', 'Hospitalidad de personas', NULL, 100, 19 UNION ALL
+ SELECT 'CAMARADERIA', 'Hospitalidad', 'Hospitalidad del lugar', NULL, 100, 20 UNION ALL
+ SELECT 'CAMARADERIA', 'Hospitalidad', 'Sentido de equipo', NULL, 100, 21 UNION ALL
+ SELECT 'PERTENENCIA', 'Orgullo', 'Orgullo del Equipo', NULL, 100, 22 UNION ALL
+ SELECT 'PERTENENCIA', 'Orgullo', 'Orgullo del Trabajo', NULL, 100, 23 UNION ALL
+ SELECT 'PERTENENCIA', 'Orgullo', 'Orgullo por la Institución', NULL, 100, 24
+) AS data
+WHERE NOT EXISTS (SELECT 1 FROM climate_result_model);
+
+UPDATE climate_result_model SET max_score = 100 WHERE max_score IS NULL;
+UPDATE climate_result_model SET score_value = NULL;
diff --git a/backend/src/sql/tenant_schema.sql b/backend/src/sql/tenant_schema.sql
index 74d0638..fd67b8a 100644
--- a/backend/src/sql/tenant_schema.sql
+++ b/backend/src/sql/tenant_schema.sql
@@ -157,3 +157,14 @@ CREATE TABLE IF NOT EXISTS survey_rules (
rule_json JSON NOT NULL,
is_active TINYINT(1) NOT NULL DEFAULT 1
);
+
+CREATE TABLE IF NOT EXISTS climate_result_model (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ dimension VARCHAR(120) NOT NULL,
+ variable VARCHAR(120) NOT NULL,
+ subvariable VARCHAR(160) NOT NULL,
+ score_value DECIMAL(6,2) NULL,
+ max_score DECIMAL(6,2) NULL,
+ sort_order INT NOT NULL DEFAULT 0,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
diff --git a/desarrollo/METODO_RESULTADOS_CLIMA.md b/desarrollo/METODO_RESULTADOS_CLIMA.md
new file mode 100644
index 0000000..dae9187
--- /dev/null
+++ b/desarrollo/METODO_RESULTADOS_CLIMA.md
@@ -0,0 +1,80 @@
+# Método de presentación de resultados (basado en Rankmi) y adaptación al sistema
+
+Este documento describe **cómo trabajar los resultados** de la encuesta de clima laboral en el sistema, **sin cambiar el contenido de la lista de preguntas actual**. Se toma como guía el enfoque de Rankmi de “7 pasos” y se adapta a nuestro flujo y módulos existentes. citeturn0search0
+
+## Principios base
+
+- La comunicación de resultados debe ir **de lo general a lo específico** y con lenguaje constructivo (enfoque “sándwich”). citeturn0search0
+- Siempre presentar **participación**, **resultados globales**, **ranking de mejores/peores**, y **prioridades de acción**. citeturn0search0
+- Los reportes deben ser **agregados** (sin identificación individual) y con **mínimos de respuestas** si aplica. citeturn0search2turn0search5
+
+## 7 pasos (Rankmi) — cómo se aplica en nuestro sistema
+
+1) **Agradecer**
+ - Mensaje breve al cerrar la medición.
+ - En PDF: incluir un párrafo de agradecimiento.
+ citeturn0search0
+
+2) **Recordar objetivos de la encuesta**
+ - Sección fija en el PDF y en el dashboard (texto corto).
+ citeturn0search0
+
+3) **Informar participación**
+ - Usar cálculo automático ya implementado (tasa de participación).
+ - Mostrar en dashboard y PDF.
+ citeturn0search0
+
+4) **Resumen del proceso / histórico**
+ - Mostrar comparativo con medición anterior (si existe).
+ - En PDF: bloque “Histórico y acciones previas”.
+ citeturn0search0
+
+5) **Resultados generales**
+ - Índice global, promedio por dimensión, clasificación (Bajo/Medio/Alto).
+ - Se explica brevemente cómo se calculó (promedios).
+ citeturn0search0
+
+6) **Mejor y peor evaluado**
+ - Ranking de dimensiones y/o subvariables.
+ - En PDF: “Top fortalezas” y “Principales oportunidades”.
+ citeturn0search0
+
+7) **Prioridades / plan de acción**
+ - En PDF: listado de 3–5 prioridades sugeridas según resultados.
+ - Deben ser medibles y trazables.
+ citeturn0search0
+
+## Qué NO se cambia
+
+- **La lista de preguntas actual permanece igual**.
+- Solo cambia la forma de **presentar y modelar resultados**.
+
+## Cómo quedamos en el sistema (Fase 1)
+
+- Resultados automáticos:
+ - Promedios por pregunta, dimensión, global.
+ - Clasificación Bajo/Medio/Alto.
+ - Tasa de participación.
+- Reportes:
+ - PDF con logo, código de medición, resumen ejecutivo, resultados por dimensión, históricos.
+ - Excel con crudos + consolidados.
+
+## Estructura de “Matriz” (modelo base)
+
+Se usa la tabla `climate_result_model` como plantilla editable:
+- **dimension** / **variable** / **subvariable**
+- **max_score** (por defecto 100)
+- **score_value** se llena solo cuando hay resultados (no visible en matriz de configuración).
+
+## Recomendación de flujo operativo
+
+1) Crear medición y activar encuesta.
+2) Cerrar medición (se calculan resultados).
+3) Revisar dashboard (global, dimensión, histórico).
+4) Generar PDF y Excel.
+5) Presentar resultados siguiendo los 7 pasos.
+
+## Nota sobre resultados por área (futuro)
+
+Rankmi recomienda visualización por área/jerarquía y mínimos de respuestas.
+Podemos integrar en Fase 2 (si se requiere). citeturn0search1turn0search5
diff --git a/desarrollo/PARA_NUEVO_CHAT.md b/desarrollo/PARA_NUEVO_CHAT.md
index ac8826c..df93d02 100644
--- a/desarrollo/PARA_NUEVO_CHAT.md
+++ b/desarrollo/PARA_NUEVO_CHAT.md
@@ -51,6 +51,11 @@ Proyecto en `/var/www/react/clima_laboral` con backend Node/Express y frontend R
- User: `usuario.prueba@demo.com` / `Usuario2026!`
- Question sets en DB `P123456`: `QSET-DEFAULT`, `QSET-20260303-03`, `QSET-20260303-04`, `QSET-20260303-05` (algunos activos/inactivos).
+## Credenciales (recordatorio)
+- Superadmin: `superadmin@demo.com` / `SuperAdmin2026`
+- Admin: `admin.prueba@demo.com` / `Admin2026!` (company_code: `123456`)
+- User: `usuario.prueba@demo.com` / `Usuario2026!` (company_code: `123456`)
+
## Problemas frecuentes
- Si “Versiones de Encuesta” sale vacío, es porque no se entró con `company_code` (usar Superadmin > Empresas > Acceder).
- Si `/admin/dimensions` da 404, reiniciar backend para cargar rutas nuevas.
@@ -60,6 +65,13 @@ Proyecto en `/var/www/react/clima_laboral` con backend Node/Express y frontend R
- Sugerencia de nombre de set corregida para usar máximo existente + 1.
- Vista Editar Set agregada con lista de preguntas y edición.
- Popup 404 silenciado para `/admin/dimensions`.
+- Repo Git creado y primer commit limpio (sin node_modules ni token).
+
+## Git / Repo
+- Gitea: `https://gitea.linkedcorp.net/`
+- Repo: `mgonzalez/claboral.linkedcorp.net`
+- Rama: `main`
+- Remote: `origin`
## Pendientes
- A gusto del usuario, agregar etiqueta “Escala 1–5” visible en encuestas/preguntas.
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 910d9ec..d1da04c 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -16,9 +16,25 @@
},
"devDependencies": {
"@vitejs/plugin-react": "^4.3.2",
+ "autoprefixer": "^10.4.19",
+ "postcss": "^8.4.38",
+ "tailwindcss": "^3.4.3",
"vite": "^5.4.8"
}
},
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
@@ -742,6 +758,44 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/@remix-run/router": {
"version": "1.23.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
@@ -1181,12 +1235,77 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
+ "node_modules/autoprefixer": {
+ "version": "10.4.27",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
+ "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001774",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
"node_modules/axios": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
@@ -1211,6 +1330,32 @@
"node": ">=6.0.0"
}
},
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/browserslist": {
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
@@ -1258,6 +1403,16 @@
"node": ">= 0.4"
}
},
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/caniuse-lite": {
"version": "1.0.30001775",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
@@ -1279,6 +1434,44 @@
],
"license": "CC-BY-4.0"
},
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -1291,6 +1484,16 @@
"node": ">= 0.8"
}
},
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -1298,6 +1501,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -1325,6 +1541,20 @@
"node": ">=0.4.0"
}
},
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -1440,6 +1670,59 @@
"node": ">=6"
}
},
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
@@ -1476,6 +1759,20 @@
"node": ">= 6"
}
},
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -1547,6 +1844,19 @@
"node": ">= 0.4"
}
},
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -1598,6 +1908,78 @@
"node": ">= 0.4"
}
},
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.7",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -1630,6 +2012,26 @@
"node": ">=6"
}
},
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -1661,6 +2063,30 @@
"node": ">= 0.4"
}
},
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -1689,6 +2115,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -1715,6 +2153,43 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -1722,6 +2197,39 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/postcss": {
"version": "8.5.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
@@ -1751,12 +2259,167 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+ "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
+ "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
+ "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.1.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "jiti": ">=1.21.0",
+ "postcss": ">=8.0.9",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -1833,6 +2496,61 @@
"react-dom": ">=16.8"
}
},
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/rollup": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
@@ -1878,6 +2596,30 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
"node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@@ -1907,6 +2649,171 @@
"node": ">=0.10.0"
}
},
+ "node_modules/sucrase": {
+ "version": "3.35.1",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
+ "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "tinyglobby": "^0.2.11",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.4.19",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
+ "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.6.0",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.2",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.21.7",
+ "lilconfig": "^3.1.3",
+ "micromatch": "^4.0.8",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.1.1",
+ "postcss": "^8.4.47",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
+ "postcss-nested": "^6.2.0",
+ "postcss-selector-parser": "^6.1.2",
+ "resolve": "^1.22.8",
+ "sucrase": "^3.35.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
@@ -1938,6 +2845,13 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/vite": {
"version": "5.4.21",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 4c6214f..4014090 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -16,6 +16,9 @@
},
"devDependencies": {
"@vitejs/plugin-react": "^4.3.2",
+ "autoprefixer": "^10.4.19",
+ "postcss": "^8.4.38",
+ "tailwindcss": "^3.4.3",
"vite": "^5.4.8"
}
}
diff --git a/frontend/postcss.config.cjs b/frontend/postcss.config.cjs
new file mode 100644
index 0000000..5cbc2c7
--- /dev/null
+++ b/frontend/postcss.config.cjs
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {}
+ }
+};
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 692fbc2..533cf4c 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -8,7 +8,6 @@ import ImportUsers from './pages/ImportUsers.jsx';
import ParticipantSurvey from './pages/ParticipantSurvey.jsx';
import QuestionBank from './pages/QuestionBank.jsx';
import creatorLogo from './assets/creator-logo.png';
-import './styles/app.css';
export default function App() {
const [notice, setNotice] = useState(null);
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx
index 3fe4a20..091bf57 100644
--- a/frontend/src/main.jsx
+++ b/frontend/src/main.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
-import './styles/app.css';
+import './styles/index.css';
createRoot(document.getElementById('root')).render(
{data.current ? `${data.current.score} (${data.current.classification})` : 'Sin datos'}
diff --git a/frontend/src/pages/QuestionBank.jsx b/frontend/src/pages/QuestionBank.jsx index a4ed97f..c49f8ab 100644 --- a/frontend/src/pages/QuestionBank.jsx +++ b/frontend/src/pages/QuestionBank.jsx @@ -1,7 +1,9 @@ import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import api from '../utils/api'; export default function QuestionBank() { + const navigate = useNavigate(); const [questions, setQuestions] = useState([]); const [sets, setSets] = useState([]); const [file, setFile] = useState(null); @@ -16,6 +18,9 @@ export default function QuestionBank() { const [newQuestionText, setNewQuestionText] = useState(''); const [newSetInfo, setNewSetInfo] = useState(null); const companyCode = localStorage.getItem('company_code'); + const [resultModel, setResultModel] = useState([]); + const [matrixFile, setMatrixFile] = useState(null); + const [newMatrixRow, setNewMatrixRow] = useState({ dimension: '', variable: '', subvariable: '', max_score: '', sort_order: '' }); const load = async () => { try { @@ -38,7 +43,6 @@ export default function QuestionBank() { useEffect(() => { if (!companyCode) { - window.dispatchEvent(new CustomEvent('app:notice', { detail: { type: 'error', message: 'Selecciona una empresa antes de entrar a Preguntas.' } })); return; } load(); @@ -95,6 +99,51 @@ export default function QuestionBank() { window.URL.revokeObjectURL(url); }; + const loadResultModel = async () => { + const res = await api.get('/admin/result-model'); + setResultModel(res.data); + }; + + const downloadMatrixTemplate = async () => { + const res = await api.get('/admin/result-model/template', { responseType: 'blob' }); + const url = window.URL.createObjectURL(new Blob([res.data])); + const a = document.createElement('a'); + a.href = url; + a.download = 'clima_result_model.xlsx'; + a.click(); + window.URL.revokeObjectURL(url); + }; + + const importMatrix = async () => { + if (!matrixFile) return; + const form = new FormData(); + form.append('file', matrixFile); + form.append('mode', 'replace'); + const res = await api.post('/admin/result-model/import', form, { headers: { 'Content-Type': 'multipart/form-data' } }); + setMsg(`Matriz: ${res.data.processed} filas. Errores: ${res.data.errors.length}`); + await loadResultModel(); + }; + + const addMatrixRow = async () => { + const { dimension, variable, subvariable } = newMatrixRow; + if (!dimension || !variable || !subvariable) return; + const payload = { + ...newMatrixRow, + max_score: newMatrixRow.max_score === '' ? 100 : newMatrixRow.max_score + }; + await api.post('/admin/result-model', payload); + setNewMatrixRow({ dimension: '', variable: '', subvariable: '', max_score: '', sort_order: '' }); + await loadResultModel(); + }; + + const saveMatrixRow = async (row) => { + await api.patch(`/admin/result-model/${row.id}`, { + max_score: row.max_score === '' ? null : row.max_score, + sort_order: row.sort_order === '' ? null : row.sort_order + }); + await loadResultModel(); + }; + const createDimension = async () => { if (!newDimName) return; const res = await api.post('/admin/dimensions', { name: newDimName }); @@ -116,6 +165,9 @@ export default function QuestionBank() {Selecciona una empresa para ver las versiones de encuesta.
+| Dimensión | Variable | Subvariable | Máximo | Orden | Guardar | +
|---|---|---|---|---|---|
| {row.dimension} | +{row.variable} | +{row.subvariable} | +setResultModel(resultModel.map(r => r.id === row.id ? { ...r, max_score: e.target.value } : r))} /> | +setResultModel(resultModel.map(r => r.id === row.id ? { ...r, sort_order: e.target.value } : r))} /> | ++ + | +
{msg}
} +