tailjng 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cli/execute/sync-app.js +192 -192
- package/cli/settings/colors-config-utils.js +188 -188
- package/cli/settings/init-packages.js +37 -39
- package/fesm2022/tailjng.mjs +84 -46
- package/fesm2022/tailjng.mjs.map +1 -1
- package/lib/services/report/upload.filter.service.d.ts +3 -0
- package/package.json +2 -4
- package/src/lib/components/colors-config/README.md +38 -38
- package/tailjng-0.1.5.tgz +0 -0
- package/tailjng-0.1.4.tgz +0 -0
package/fesm2022/tailjng.mjs
CHANGED
|
@@ -9,7 +9,6 @@ import { HttpParams } from '@angular/common/http';
|
|
|
9
9
|
import { Link2Off, Link, Clipboard, CircleAlert, Clock, Calendar, EllipsisVertical, Trash, Edit, Pencil, PencilLine, ListRestart, FileUp, Download, MonitorUp, FileSpreadsheet, Cpu, Trash2, Eraser, ArrowDownWideNarrow, Filter, ArrowBigRight, ChevronsRight, ChevronRight, ChevronLeft, ChevronsLeft, ListFilter, Table, Loader2, Moon, Sun, Save, Copy, Search, SquareDashedMousePointer, ChevronsUpDown, ChevronDown, ChevronUp, Eye, Upload, ImageOff, Images, Image, Minimize2, Scan, RefreshCcw, RotateCcw, RotateCw, ZoomOut, ZoomIn, Check, X, CircleHelp, TriangleAlert, CircleX, CircleCheck, Info } from 'lucide-angular';
|
|
10
10
|
import * as FileSaver from 'file-saver';
|
|
11
11
|
import * as ExcelJS from 'exceljs';
|
|
12
|
-
import * as XLSX from 'xlsx';
|
|
13
12
|
|
|
14
13
|
const TAILJNG_CONFIG = new InjectionToken('TAILJNG_CONFIG');
|
|
15
14
|
// providers: [
|
|
@@ -2097,18 +2096,33 @@ class JUploadFilterService {
|
|
|
2097
2096
|
setLoadingFn(true);
|
|
2098
2097
|
const reader = new FileReader();
|
|
2099
2098
|
reader.onload = (e) => {
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2099
|
+
void this.parseUploadedWorkbook(e, columns, button, afterProcessing, setLoadingFn, clearFileInputFn);
|
|
2100
|
+
};
|
|
2101
|
+
reader.readAsArrayBuffer(file);
|
|
2102
|
+
}
|
|
2103
|
+
async parseUploadedWorkbook(e, columns, button, afterProcessing, setLoadingFn, clearFileInputFn) {
|
|
2104
|
+
try {
|
|
2105
|
+
const buffer = e.target?.result;
|
|
2106
|
+
if (!(buffer instanceof ArrayBuffer)) {
|
|
2107
|
+
throw new Error('No se pudo leer el archivo Excel.');
|
|
2108
|
+
}
|
|
2109
|
+
const jsonRaw = await this.readWorkbookAsRowArrays(buffer);
|
|
2104
2110
|
const headers = jsonRaw[1];
|
|
2105
2111
|
const rows = jsonRaw.slice(2);
|
|
2106
|
-
|
|
2112
|
+
if (!headers?.length) {
|
|
2113
|
+
this.alertToastService.AlertToast({
|
|
2114
|
+
type: 'error',
|
|
2115
|
+
title: 'Archivo inválido',
|
|
2116
|
+
description: 'El archivo Excel no contiene encabezados en la fila esperada.',
|
|
2117
|
+
});
|
|
2118
|
+
clearFileInputFn();
|
|
2119
|
+
setLoadingFn(false);
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2107
2122
|
if (!this.validateHeaders(headers, columns, button.ignoreKeyUpload ?? [], setLoadingFn)) {
|
|
2108
2123
|
clearFileInputFn();
|
|
2109
2124
|
return;
|
|
2110
2125
|
}
|
|
2111
|
-
// Get the header key map
|
|
2112
2126
|
const mapHeader = this.getHeaderKeyMap(columns, button.ignoreKeyUpload ?? []);
|
|
2113
2127
|
const json = rows.map(row => {
|
|
2114
2128
|
const obj = {};
|
|
@@ -2120,7 +2134,6 @@ class JUploadFilterService {
|
|
|
2120
2134
|
if (columnConfig) {
|
|
2121
2135
|
value = this.converterService.parseData(value, columnConfig);
|
|
2122
2136
|
}
|
|
2123
|
-
// Force to string if it's an identification field
|
|
2124
2137
|
if (key?.toLowerCase().includes('identification_')) {
|
|
2125
2138
|
if (typeof value === 'number') {
|
|
2126
2139
|
value = value.toString();
|
|
@@ -2131,71 +2144,51 @@ class JUploadFilterService {
|
|
|
2131
2144
|
});
|
|
2132
2145
|
return obj;
|
|
2133
2146
|
});
|
|
2134
|
-
// Process selected IDs
|
|
2135
2147
|
forkJoin(this.getIdsSelectedData(button)).subscribe({
|
|
2136
2148
|
next: (results) => {
|
|
2137
2149
|
let hasMissing = false;
|
|
2138
2150
|
const missingMessages = [];
|
|
2139
2151
|
results.forEach((result) => {
|
|
2140
|
-
const {
|
|
2152
|
+
const { data, mappingInfo } = result;
|
|
2141
2153
|
const keyReturn = mappingInfo.keyReturn;
|
|
2142
2154
|
const keyAlternate = mappingInfo.keyAlternate;
|
|
2143
2155
|
const keyColumnSearch = mappingInfo.keyColumnSearch;
|
|
2144
2156
|
const isOptional = mappingInfo.optional ?? false;
|
|
2145
2157
|
if (!keyReturn || !keyColumnSearch)
|
|
2146
2158
|
return;
|
|
2147
|
-
// Get the column label to display in errors
|
|
2148
2159
|
const columnLabel = columns.find(col => col.key === keyColumnSearch)?.label
|
|
2149
2160
|
?? keyColumnSearch;
|
|
2150
2161
|
const missingValues = [];
|
|
2151
|
-
// Iterate over each row of the JSON loaded from Excel
|
|
2152
2162
|
json.forEach(item => {
|
|
2153
2163
|
const currentValue = item[keyColumnSearch];
|
|
2154
2164
|
if (currentValue !== undefined && currentValue !== null) {
|
|
2155
|
-
// For example, from keyColumnSearch = 'status.name_status'
|
|
2156
|
-
// you get fieldName = 'name_status'
|
|
2157
2165
|
const fieldName = keyColumnSearch.split('.').pop();
|
|
2158
|
-
// Search for the value in the catalog obtained from the API
|
|
2159
2166
|
const found = data.find((apiItem) => {
|
|
2160
2167
|
return apiItem[fieldName] === currentValue;
|
|
2161
2168
|
});
|
|
2162
2169
|
if (found) {
|
|
2163
|
-
// If it finds the value in the catalog:
|
|
2164
|
-
// - delete the textual column (e.g. 'status.name_status')
|
|
2165
|
-
// - and leave the id (e.g. id_status) in the object
|
|
2166
|
-
// delete item[keyColumnSearch];
|
|
2167
|
-
// If mappingInfo brings keyAlternate, use that field, if not, use keyReturn
|
|
2168
2170
|
const valueToAssign = found[keyAlternate ?? keyReturn];
|
|
2169
2171
|
item[keyReturn] = valueToAssign;
|
|
2170
2172
|
}
|
|
2171
|
-
else {
|
|
2172
|
-
if (isOptional) {
|
|
2173
|
-
// If it is optional and does not exist, simply delete the field
|
|
2174
|
-
delete item[keyColumnSearch];
|
|
2175
|
-
item[keyReturn] = null;
|
|
2176
|
-
}
|
|
2177
|
-
else {
|
|
2178
|
-
// If it is not optional and the value was not found, accumulate it to show an error
|
|
2179
|
-
missingValues.push(currentValue);
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
else {
|
|
2184
|
-
// If the value comes empty and is optional, delete the field and leave it as null
|
|
2185
|
-
if (isOptional) {
|
|
2173
|
+
else if (isOptional) {
|
|
2186
2174
|
delete item[keyColumnSearch];
|
|
2187
2175
|
item[keyReturn] = null;
|
|
2188
2176
|
}
|
|
2177
|
+
else {
|
|
2178
|
+
missingValues.push(currentValue);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
else if (isOptional) {
|
|
2182
|
+
delete item[keyColumnSearch];
|
|
2183
|
+
item[keyReturn] = null;
|
|
2189
2184
|
}
|
|
2190
2185
|
});
|
|
2191
|
-
// If there were values that were not found in the catalog list
|
|
2192
2186
|
if (missingValues.length > 0) {
|
|
2193
2187
|
hasMissing = true;
|
|
2194
2188
|
const uniqueMissing = Array.from(new Set(missingValues));
|
|
2195
2189
|
missingMessages.push(`No se encontraron coincidencias en la columna [${columnLabel}] para los valores: ${uniqueMissing.join(', ')}`);
|
|
2196
2190
|
}
|
|
2197
2191
|
});
|
|
2198
|
-
// If there were matching errors, show a warning message
|
|
2199
2192
|
if (hasMissing) {
|
|
2200
2193
|
this.alertToastService.AlertToast({
|
|
2201
2194
|
type: 'warning',
|
|
@@ -2208,11 +2201,8 @@ class JUploadFilterService {
|
|
|
2208
2201
|
return;
|
|
2209
2202
|
}
|
|
2210
2203
|
console.log('JSON final con IDs:', json);
|
|
2211
|
-
// Determine columns present in the Excel file
|
|
2212
2204
|
const fileHeadersUpper = (headers ?? []).map((h) => h?.trim().toUpperCase());
|
|
2213
|
-
// Generate the mapping of header -> key
|
|
2214
2205
|
this.getHeaderKeyMap(columns, button.ignoreKeyUpload ?? []);
|
|
2215
|
-
// Filter columns that are in the Excel file
|
|
2216
2206
|
const filteredColumns = columns.filter(col => {
|
|
2217
2207
|
const header = col.label?.trim().toUpperCase();
|
|
2218
2208
|
return (col.visible &&
|
|
@@ -2220,9 +2210,6 @@ class JUploadFilterService {
|
|
|
2220
2210
|
fileHeadersUpper.includes(header) &&
|
|
2221
2211
|
!button.ignoreKeyUpload.includes(col.key));
|
|
2222
2212
|
});
|
|
2223
|
-
// Object to store the equivalences
|
|
2224
|
-
// keyReturn -> { keyColumnSearch: value }
|
|
2225
|
-
// For example: { id_status: { name_status: 'Activo' } }
|
|
2226
2213
|
const equivalences = {};
|
|
2227
2214
|
results.forEach((result) => {
|
|
2228
2215
|
const { data, mappingInfo } = result;
|
|
@@ -2236,7 +2223,6 @@ class JUploadFilterService {
|
|
|
2236
2223
|
equivalences[keyReturn][item[idKey]] = item[keyColumnSearch.split('.').pop()];
|
|
2237
2224
|
});
|
|
2238
2225
|
});
|
|
2239
|
-
// Execute the callback passing the json and the filtered columns
|
|
2240
2226
|
afterProcessing(json, filteredColumns, equivalences);
|
|
2241
2227
|
clearFileInputFn();
|
|
2242
2228
|
},
|
|
@@ -2244,8 +2230,60 @@ class JUploadFilterService {
|
|
|
2244
2230
|
setLoadingFn(false);
|
|
2245
2231
|
}
|
|
2246
2232
|
});
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2233
|
+
}
|
|
2234
|
+
catch {
|
|
2235
|
+
this.alertToastService.AlertToast({
|
|
2236
|
+
type: 'error',
|
|
2237
|
+
title: 'Error al leer Excel',
|
|
2238
|
+
description: 'No se pudo procesar el archivo. Verifica que sea un .xlsx válido.',
|
|
2239
|
+
});
|
|
2240
|
+
clearFileInputFn();
|
|
2241
|
+
setLoadingFn(false);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
async readWorkbookAsRowArrays(buffer) {
|
|
2245
|
+
const workbook = new ExcelJS.Workbook();
|
|
2246
|
+
await workbook.xlsx.load(buffer);
|
|
2247
|
+
const sheet = workbook.worksheets[0];
|
|
2248
|
+
if (!sheet) {
|
|
2249
|
+
return [];
|
|
2250
|
+
}
|
|
2251
|
+
const rowCount = sheet.rowCount;
|
|
2252
|
+
const colCount = sheet.columnCount;
|
|
2253
|
+
const result = [];
|
|
2254
|
+
for (let rowIndex = 1; rowIndex <= rowCount; rowIndex += 1) {
|
|
2255
|
+
const row = sheet.getRow(rowIndex);
|
|
2256
|
+
const rowData = [];
|
|
2257
|
+
for (let colIndex = 1; colIndex <= colCount; colIndex += 1) {
|
|
2258
|
+
rowData.push(this.getCellPlainValue(row.getCell(colIndex)));
|
|
2259
|
+
}
|
|
2260
|
+
result.push(rowData);
|
|
2261
|
+
}
|
|
2262
|
+
return result;
|
|
2263
|
+
}
|
|
2264
|
+
getCellPlainValue(cell) {
|
|
2265
|
+
const value = cell.value;
|
|
2266
|
+
if (value == null || value === '') {
|
|
2267
|
+
return 'S/N';
|
|
2268
|
+
}
|
|
2269
|
+
if (value instanceof Date) {
|
|
2270
|
+
return value;
|
|
2271
|
+
}
|
|
2272
|
+
if (typeof value === 'object') {
|
|
2273
|
+
if ('result' in value) {
|
|
2274
|
+
return value.result ?? 'S/N';
|
|
2275
|
+
}
|
|
2276
|
+
if ('richText' in value && Array.isArray(value.richText)) {
|
|
2277
|
+
return value.richText.map((part) => part.text).join('');
|
|
2278
|
+
}
|
|
2279
|
+
if ('text' in value && typeof value.text === 'string') {
|
|
2280
|
+
return value.text;
|
|
2281
|
+
}
|
|
2282
|
+
if ('hyperlink' in value && 'text' in value) {
|
|
2283
|
+
return value.text ?? 'S/N';
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
return value;
|
|
2249
2287
|
}
|
|
2250
2288
|
/**
|
|
2251
2289
|
* Generates catalogs of valid values for select-type fields.
|