spiderly 19.8.6 → 19.8.8

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.
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: deployment
3
- description: Deploy a Spiderly project to your own infrastructure with Docker, Caddy, and Terraform. Use when deploying, redeploying, shipping, releasing, or rolling out the .NET backend or Angular admin to production — first-time setup or an ongoing deploy — and when diagnosing a down or erroring production origin (502/521, container crash-loop, failed deploy workflow). Also covers CI/CD pipelines, TLS with Cloudflare origin certificates, database backups and restores, and infrastructure-as-code layout for a Spiderly app.
3
+ description: Deploy a Spiderly project to production and keep it running. Use when deploying, redeploying, shipping, releasing, or rolling out the .NET backend or Angular admin — first-time setup or an ongoing deploy — and when diagnosing a down or erroring production origin (502/521, container crash-loop, failed deploy workflow). Covers the recommended VPS + Docker Compose + Caddy + Cloudflare + Terraform stack, CI/CD pipelines, TLS with Cloudflare origin certificates, and infrastructure-as-code layout.
4
4
  ---
5
5
 
6
6
  # Deployment
7
7
 
8
- Spiderly is Docker-first by design. The recommended production setup is a single VPS running Docker Compose, fronted by Caddy and Cloudflare, with all infrastructure declared in Terraform.
8
+ Spiderly runs anywhere a .NET app does — there is no required host, orchestrator, or reverse proxy. The stack below is the one we **recommend** and test end to end: a single VPS running Docker Compose, fronted by Caddy and Cloudflare, with all infrastructure declared in Terraform. Swap any tier for your own — the only genuinely Spiderly-specific deploy concerns are flagged inline (EF migrations before app start, forwarded-header/proxy trust, cookie domain across subdomains, the CORS frontend URL, and the api + admin subdomain split).
9
9
 
10
10
  ## Recommended stack
11
11
 
12
12
  | Tier | Choice | Why |
13
13
  |---|---|---|
14
14
  | Compute | **Hetzner Cloud** (or any VPS) | Predictable monthly cost, full control, no PaaS lock-in |
15
- | Orchestration | **Docker Compose** | Single-host simplicity; matches Spiderly's Docker-first philosophy |
15
+ | Orchestration | **Docker Compose** | Single-host simplicity; reproducible, agent-friendly deploys |
16
16
  | Reverse proxy / TLS | **Caddy v2** | Auto-config from Cloudflare origin certs, simple Caddyfile |
17
17
  | DNS / WAF / CDN | **Cloudflare** (orange-cloud) | DDoS protection, origin certs, Turnstile |
18
18
  | IaC | **Terraform** | Declarative; one source of truth for VPS + DNS + certs |
@@ -154,7 +154,7 @@ ENTRYPOINT ["dotnet", "<YourApp>.WebAPI.dll"]
154
154
 
155
155
  **Run as non-root.** The aspnet image ships an `app` user (UID 1654). Add `USER app` after `COPY` for defense-in-depth — limits the blast radius of a container escape and matches the platform's sandbox model.
156
156
 
157
- **Log volumes:** if you're using a Serilog File sink (not just Console) with a Hangfire log-archival job, see the **Backups → Log archival** section below for the full bind-mount-vs-Console-only trade-off. Greenfield deploys can stick with Console sink + Docker's `json-file` rotation and skip the volume entirely.
157
+ **Log volumes:** if you're using a Serilog File sink (not just Console) with a Hangfire log-archival job, see **Log archival** in the `backups` skill for the full bind-mount-vs-Console-only trade-off. Greenfield deploys can stick with Console sink + Docker's `json-file` rotation and skip the volume entirely.
158
158
 
159
159
  ## Angular admin Dockerfile
160
160
 
@@ -343,146 +343,7 @@ For migration mechanics (creating migrations, the dedicated `*.Migrations` start
343
343
 
344
344
  ## Backups
345
345
 
346
- If you ship this stack to prod, you ship a backup with it. Postgres data lives in a Docker volume on a single VPS — disk failure, accidental `docker volume rm`, or `terraform destroy` all wipe it without a snapshot to restore from.
347
-
348
- ### Database backups (required)
349
-
350
- Daily `pg_dump` to a Cloudflare R2 bucket, 7-day retention both local and remote. ~24h RPO; if you need tighter, layer WAL archiving on top (separate skill).
351
-
352
- **1. R2 bucket — Terraform-managed.** No chicken-and-egg here (only the *state* bucket has to be bootstrapped manually); manage data buckets like any other resource:
353
-
354
- ```hcl
355
- # infrastructure/cloudflare-r2.tf
356
- resource "cloudflare_r2_bucket" "db_backups" {
357
- account_id = var.cloudflare_account_id
358
- name = "<your-app>-db-backups"
359
- location = "EEUR"
360
-
361
- lifecycle {
362
- prevent_destroy = true
363
- }
364
- }
365
- ```
366
-
367
- **2. Backup script — `infrastructure/scripts/pg_backup_s3.sh`** (deployed to `/usr/local/bin/` on the VPS). Note the `trap` cleanup, the `aws s3api list-objects-v2 --query` for retention (structured + locale-safe vs parsing `aws s3 ls` with awk), and the per-iteration warn-on-failure inside the `while` subshell (where `set -e` does NOT propagate):
368
-
369
- ```bash
370
- #!/usr/bin/env bash
371
- set -euo pipefail
372
-
373
- CONTAINER_NAME="<your-app>-postgres-1"
374
- DB_NAME="<your-db>"
375
- DB_USER="postgres"
376
- CLOUDFLARE_ACCOUNT_ID="<account-id>"
377
- S3_BUCKET="s3://<your-app>-db-backups"
378
- R2_ENDPOINT="https://${CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com"
379
- RETENTION_DAYS=7
380
- LOCAL_BACKUP_DIR="/var/backups/postgresql"
381
- LOG_FILE="/var/log/<your-app>_pg_backup.log"
382
-
383
- TIMESTAMP=$(date +%Y-%m-%d_%H%M%S)
384
- BACKUP_FILE="<your-app>_${TIMESTAMP}.sql.gz"
385
- LOCAL_PATH="${LOCAL_BACKUP_DIR}/${BACKUP_FILE}"
386
-
387
- log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"; }
388
-
389
- trap 'rc=$?; [[ $rc -ne 0 ]] && rm -f "$LOCAL_PATH" && log "Aborted, removed partial $LOCAL_PATH"; exit $rc' ERR INT TERM
390
-
391
- mkdir -p "$LOCAL_BACKUP_DIR"
392
-
393
- if ! docker exec "$CONTAINER_NAME" pg_dump -U "$DB_USER" "$DB_NAME" | gzip > "$LOCAL_PATH"; then
394
- log "ERROR: pg_dump failed"; exit 1
395
- fi
396
- log "Backup created: ${LOCAL_PATH} ($(du -h "$LOCAL_PATH" | cut -f1))"
397
-
398
- if ! aws s3 --endpoint-url "$R2_ENDPOINT" cp "$LOCAL_PATH" "${S3_BUCKET}/${BACKUP_FILE}"; then
399
- log "ERROR: S3 upload failed"; exit 1
400
- fi
401
- log "Uploaded to ${S3_BUCKET}/${BACKUP_FILE}"
402
-
403
- find "$LOCAL_BACKUP_DIR" -name "<your-app>_*.sql.gz" -mtime +"$RETENTION_DAYS" -delete
404
-
405
- CUTOFF_ISO=$(date -u -d "-${RETENTION_DAYS} days" +%Y-%m-%dT%H:%M:%SZ)
406
- BUCKET_NAME="${S3_BUCKET#s3://}"
407
- OLD_KEYS=$(aws s3api --endpoint-url "$R2_ENDPOINT" list-objects-v2 \
408
- --bucket "$BUCKET_NAME" \
409
- --query "Contents[?LastModified<'${CUTOFF_ISO}'].Key" \
410
- --output text 2>/dev/null || echo "")
411
- if [[ -n "$OLD_KEYS" && "$OLD_KEYS" != "None" ]]; then
412
- for KEY in $OLD_KEYS; do
413
- aws s3 --endpoint-url "$R2_ENDPOINT" rm "${S3_BUCKET}/${KEY}" \
414
- && log "Deleted remote: ${KEY}" \
415
- || log "WARN: failed to delete remote ${KEY}"
416
- done
417
- fi
418
- ```
419
-
420
- **3. VPS prerequisites.** Add `awscli` and `cron` to `cloud-init` `packages:` so future server replacements have them. Configure R2 credentials in `/root/.aws/credentials` (same keys as the Terraform state backend — they're account-scoped):
421
-
422
- ```ini
423
- [default]
424
- aws_access_key_id = <R2 access key>
425
- aws_secret_access_key = <R2 secret>
426
- ```
427
-
428
- **4. Cron entry** (`/etc/cron.d/<your-app>-pg-backup`). Wrap with `flock -n` so a hung run doesn't get a second instance started 24h later:
429
-
430
- ```
431
- 0 2 * * * root /usr/bin/flock -n /var/lock/<your-app>-pg-backup.lock /usr/local/bin/pg_backup_s3.sh >> /var/log/<your-app>_pg_backup.log 2>&1
432
- ```
433
-
434
- **5. Restore — `scripts/db-restore.sh`** (run from local). Lists server-side backups via SSH, prompts for selection, takes a safety dump first (with size check — refuses to proceed if pg_dump silently produced an empty file), then restores. Note `set -euo pipefail` inside the SSH heredocs (without it, a `pg_dump` failure inside a pipeline is masked by a successful `gzip`) and the `OVERWRITE <db>` confirmation phrase (a bare DB name is too easy to typo into):
435
-
436
- ```bash
437
- #!/usr/bin/env bash
438
- set -euo pipefail
439
- SSH_ALIAS="<your-app>"
440
- CONTAINER="<your-app>-postgres-1"
441
- DB="<your-db>"
442
- REMOTE_DIR="/var/backups/postgresql"
443
- MIN_SAFETY_BYTES=1024
444
-
445
- trap 'echo "Interrupted — DB may be inconsistent. Latest safety snapshot is in $SSH_ALIAS:$REMOTE_DIR."' INT TERM
446
-
447
- ssh -o ConnectTimeout=5 "$SSH_ALIAS" "docker exec $CONTAINER pg_isready -U postgres -d $DB" >/dev/null \
448
- || { echo "Postgres not ready"; exit 1; }
449
-
450
- mapfile -t BACKUPS < <(ssh "$SSH_ALIAS" "ls -1t $REMOTE_DIR/<your-app>_*.sql.gz 2>/dev/null | xargs -n1 basename")
451
- [[ ${#BACKUPS[@]} -gt 0 ]] || { echo "No backups found"; exit 1; }
452
- for i in "${!BACKUPS[@]}"; do printf " %2d) %s\n" "$((i+1))" "${BACKUPS[$i]}"; done
453
- read -rp "Pick: " PICK
454
- [[ "$PICK" =~ ^[0-9]+$ ]] && (( PICK >= 1 && PICK <= ${#BACKUPS[@]} )) || { echo "Invalid"; exit 1; }
455
- CHOSEN="${BACKUPS[$((PICK-1))]}"
456
-
457
- read -rp "Type 'OVERWRITE $DB' to confirm: " CONFIRM
458
- [[ "$CONFIRM" == "OVERWRITE $DB" ]] || { echo "aborted"; exit 1; }
459
-
460
- SAFETY="<your-app>_pre_restore_$(date +%Y-%m-%d_%H%M%S).sql.gz"
461
- ssh "$SSH_ALIAS" "set -euo pipefail; docker exec $CONTAINER pg_dump -U postgres $DB | gzip > $REMOTE_DIR/$SAFETY"
462
- SIZE=$(ssh "$SSH_ALIAS" "stat -c%s $REMOTE_DIR/$SAFETY")
463
- (( SIZE >= MIN_SAFETY_BYTES )) || { echo "Safety snapshot suspiciously small ($SIZE B) — aborting"; exit 1; }
464
-
465
- ssh "$SSH_ALIAS" "
466
- set -euo pipefail
467
- docker exec $CONTAINER psql -U postgres -d postgres -c \"DROP DATABASE IF EXISTS \\\"$DB\\\" WITH (FORCE);\"
468
- docker exec $CONTAINER psql -U postgres -d postgres -c \"CREATE DATABASE \\\"$DB\\\";\"
469
- gunzip -c $REMOTE_DIR/$CHOSEN | docker exec -i $CONTAINER psql -U postgres -d $DB
470
- "
471
- echo "Restored. Safety snapshot at $SSH_ALIAS:$REMOTE_DIR/$SAFETY."
472
- ```
473
-
474
- **6. Restore drill — quarterly.** A backup you've never restored from is not a backup. At least once a quarter, restore the latest dump into a throwaway local Postgres and verify schema + row counts. Calendar reminder.
475
-
476
- ### Log archival (optional)
477
-
478
- When to use it: you need long-term log retention beyond Docker's `json-file` rotation buffer (default ~150 MB rolling per container, configured in compose).
479
-
480
- Pattern: a Hangfire recurring job watches `/app/logs/`, ships files older than N days to R2 once total > threshold, deletes locally.
481
-
482
- To make `/app/logs` writable under `USER app`:
483
-
484
- - **Bind mount with chowned host dir** (recommended): `volumes: - /var/log/<your-app>:/app/logs` in compose, one-time `mkdir -p /var/log/<your-app> && chown 1654:1654 /var/log/<your-app>` on the VPS. Bind mounts respect host ownership; named volumes overlay the path with root-owned storage which `app` can't write to.
485
- - **No File sink at all** (simpler): rely on Console + Docker rotation. Drop the `/app/logs` volume entirely. Right answer for greenfield deploys.
346
+ Postgres data lives in a Docker volume on a single VPS — disk failure, accidental `docker volume rm`, or `terraform destroy` all wipe it without a snapshot to restore from. **If you ship this stack to prod, ship a backup with it:** daily `pg_dump` to Cloudflare R2 with local + remote retention, tested restore + restore-drill scripts, and optional log archival. See the **`backups`** skill for the full setup (R2 bucket Terraform, backup and restore scripts, cron, log archival).
486
347
 
487
348
  ## Pitfalls
488
349
 
@@ -626,10 +626,16 @@ function isExcelFileType(mimeType) {
626
626
  }
627
627
  return false;
628
628
  }
629
+ function saveResponseAsFile(res, fallbackName) {
630
+ if (res?.body == null) {
631
+ return;
632
+ }
633
+ let fileName = getFileNameFromContentDisposition(res, fallbackName);
634
+ FileSaver.saveAs(res.body, decodeURIComponent(fileName));
635
+ }
629
636
  function exportListToExcel(exportListToExcelObservableMethod, filter) {
630
637
  exportListToExcelObservableMethod(filter).subscribe((res) => {
631
- let fileName = getFileNameFromContentDisposition(res, 'ExcelExport.xlsx');
632
- FileSaver.saveAs(res.body, decodeURIComponent(fileName));
638
+ saveResponseAsFile(res, 'ExcelExport.xlsx');
633
639
  });
634
640
  }
635
641
  function getPrimengNamebookOptions(namebookList) {
@@ -4161,6 +4167,34 @@ class SpiderlyDataTableComponent {
4161
4167
  return;
4162
4168
  this.navigateToDetails(row.id);
4163
4169
  }
4170
+ /*
4171
+ * Handle a cell click. Only columns that opt in (have an `onCellClick` callback) react; for them
4172
+ * we stop propagation so the click does not also trigger row navigation, then invoke the consumer
4173
+ * callback with a fully-populated CellClickEvent. Inert cells fall through to onRowClick unchanged.
4174
+ */
4175
+ onCellClick(col, rowData, event) {
4176
+ if (!col.onCellClick)
4177
+ return;
4178
+ event.stopPropagation();
4179
+ col.onCellClick({
4180
+ ...this.buildClickEvent(rowData, event),
4181
+ field: col.field,
4182
+ value: rowData[col.field],
4183
+ displayValue: this.getRowData(rowData, col),
4184
+ });
4185
+ }
4186
+ /*
4187
+ * Build the base click payload shared by custom action clicks and cell clicks. The clicked element
4188
+ * is captured here, not read later from `originalEvent.currentTarget` (which nulls after dispatch).
4189
+ */
4190
+ buildClickEvent(rowData, event) {
4191
+ return {
4192
+ id: rowData[this.idField],
4193
+ row: rowData,
4194
+ element: event.currentTarget,
4195
+ originalEvent: event,
4196
+ };
4197
+ }
4164
4198
  deleteObject(rowId) {
4165
4199
  this.openDeleteConfirmation({
4166
4200
  deleteItemFromTableObservableMethod: this.deleteItemFromTableObservableMethod,
@@ -4253,12 +4287,7 @@ class SpiderlyDataTableComponent {
4253
4287
  case 'Delete':
4254
4288
  return this.deleteObject(rowData[this.idField]);
4255
4289
  default:
4256
- return action.onClick({
4257
- id: rowData[this.idField],
4258
- row: rowData,
4259
- element: event.currentTarget,
4260
- originalEvent: event,
4261
- });
4290
+ return action.onClick(this.buildClickEvent(rowData, event));
4262
4291
  }
4263
4292
  }
4264
4293
  getRowData(rowData, col) {
@@ -4415,7 +4444,7 @@ class SpiderlyDataTableComponent {
4415
4444
  }
4416
4445
  }
4417
4446
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyDataTableComponent, deps: [{ token: i3$2.Router }, { token: i1$5.DialogService }, { token: i3$2.ActivatedRoute }, { token: SpiderlyMessageService }, { token: i1.TranslocoService }, { token: ConfigServiceBase }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Component }); }
4418
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyDataTableComponent, isStandalone: true, selector: "spiderly-data-table", inputs: { tableTitle: "tableTitle", tableIcon: "tableIcon", items: "items", rows: "rows", cols: "cols", showPaginator: "showPaginator", showCardWrapper: "showCardWrapper", readonly: "readonly", idField: "idField", getPaginatedListObservableMethod: "getPaginatedListObservableMethod", exportListToExcelObservableMethod: "exportListToExcelObservableMethod", deleteItemFromTableObservableMethod: "deleteItemFromTableObservableMethod", deleteListFromTableObservableMethod: "deleteListFromTableObservableMethod", newlySelectedItems: "newlySelectedItems", unselectedItems: "unselectedItems", selectionMode: "selectionMode", selectedLazyLoadObservableMethod: "selectedLazyLoadObservableMethod", additionalFilterIdLong: "additionalFilterIdLong", showAddButton: "showAddButton", showExportToExcelButton: "showExportToExcelButton", showReloadTableButton: "showReloadTableButton", getFormArrayItems: "getFormArrayItems", hasLazyLoad: "hasLazyLoad", stateKey: "stateKey", stateStorage: "stateStorage", getAlreadySelectedItemIds: "getAlreadySelectedItemIds", getAlreadySelectedItems: "getAlreadySelectedItems", getFormControl: "getFormControl", additionalIndexes: "additionalIndexes", navigateOnRowClick: "navigateOnRowClick", rowNavigationPath: "rowNavigationPath" }, outputs: { onTotalRecordsChange: "onTotalRecordsChange", onLazyLoad: "onLazyLoad", onIsAllSelectedChange: "onIsAllSelectedChange", onRowSelect: "onRowSelect", onRowUnselect: "onRowUnselect" }, viewQueries: [{ propertyName: "table", first: true, predicate: ["dt"], descendants: true }], ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n showCardWrapper ? 'card responsive-card-padding overflow-auto' : ''\n \"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowHover]=\"true\"\n [paginator]=\"showPaginator\"\n responsiveLayout=\"scroll\"\n [lazy]=\"hasLazyLoad\"\n (onLazyLoad)=\"lazyLoad($event)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"items === undefined || loading === true\"\n [selectionMode]=\"selectionMode\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n sortMode=\"multiple\"\n [stateKey]=\"resolvedStateKey\"\n [stateStorage]=\"stateStorage\"\n >\n <ng-template pTemplate=\"caption\">\n <div class=\"table-header overflow-auto\">\n <div style=\"display: flex; align-items: center; gap: 8px\">\n <i class=\"{{ tableIcon }}\" style=\"font-size: 20px\"></i>\n <div style=\"margin: 0px; font-size: 17.5px\">{{ tableTitle }}</div>\n </div>\n <div style=\"display: flex; gap: 8px\">\n <button\n pButton\n [label]=\"t('ClearFilters')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-filter-slash\"\n (click)=\"clear(dt)\"\n ></button>\n <button\n pButton\n *ngIf=\"showExportToExcelButton\"\n [label]=\"t('ExportToExcel')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-download\"\n (click)=\"exportListToExcel()\"\n ></button>\n <button\n pButton\n *ngIf=\"showReloadTableButton\"\n [label]=\"t('Reload')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-refresh\"\n (click)=\"reload()\"\n ></button>\n <button\n pButton\n *ngIf=\"deleteListFromTableObservableMethod && rowsSelectedNumber > 0\"\n [label]=\"t('DeleteSelected') + ' (' + rowsSelectedNumber + ')'\"\n class=\"p-button-danger\"\n style=\"flex: none\"\n icon=\"pi pi-trash\"\n (click)=\"deleteSelectedObjects()\"\n ></button>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th style=\"width: 0rem\" *ngIf=\"selectionMode == 'multiple'\">\n <div style=\"display: flex; gap: 8px\">\n <p-checkbox\n *ngIf=\"showSelectAllCheckbox\"\n [disabled]=\"readonly\"\n (onChange)=\"selectAll($event.checked)\"\n [(ngModel)]=\"fakeIsAllSelected\"\n [binary]=\"true\"\n ></p-checkbox>\n ({{ rowsSelectedNumber }})\n </div>\n </th>\n <ng-container *ngFor=\"let col of cols; trackBy: colTrackByFn\">\n <th\n [pSortableColumn]=\"col.field\"\n [pSortableColumnDisabled]=\"col.sortable === false || !col.field\"\n [style]=\"getColHeaderWidth(col.filterType)\"\n >\n <div\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n \"\n >\n <span style=\"display: flex; align-items: center; gap: 4px\">\n {{ col.name }}\n <p-sortIcon\n *ngIf=\"col.sortable !== false && col.field\"\n [field]=\"col.field!\"\n ></p-sortIcon>\n </span>\n <p-columnFilter\n *ngIf=\"col.filterType != null && col.filterType !== 'blob'\"\n [type]=\"col.filterType\"\n [field]=\"col.filterField ?? col.field\"\n display=\"menu\"\n [placeholder]=\"col.filterPlaceholder\"\n [showOperator]=\"false\"\n [showMatchModes]=\"col.showMatchModes\"\n [showAddButton]=\"col.showAddButton\"\n [matchModeOptions]=\"getColMatchModeOptions(col.filterType)\"\n [matchMode]=\"getColMatchMode(col.filterType)\"\n >\n <ng-template\n *ngIf=\"isDropOrMulti(col.filterType)\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"col.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '240px' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n <ng-template\n *ngIf=\"col.filterType == 'date'\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-datepicker\n [ngModel]=\"value\"\n [showTime]=\"col.showTime\"\n (onSelect)=\"filter($event)\"\n ></p-datepicker>\n </ng-template>\n </p-columnFilter>\n </div>\n </th>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template\n pTemplate=\"body\"\n let-rowData\n let-index=\"rowIndex\"\n let-editing=\"editing\"\n >\n <tr\n [class.clickable]=\"navigateOnRowClick\"\n (click)=\"onRowClick(rowData)\"\n >\n <td *ngIf=\"selectionMode == 'multiple'\">\n <p-checkbox\n [disabled]=\"readonly\"\n (onChange)=\"selectRow(rowData[idField], rowData.index)\"\n [ngModel]=\"isRowSelected(rowData[idField])\"\n [binary]=\"true\"\n ></p-checkbox>\n </td>\n <ng-container *ngFor=\"let col of cols; trackBy: colTrackByFn\">\n <td [style]=\"getStyleForBodyColumn(col)\" *ngIf=\"!col.editable\">\n <div\n style=\"\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 18px;\n \"\n >\n <ng-container\n *ngFor=\"let action of col.actions; trackBy: actionTrackByFn\"\n >\n <span\n [pTooltip]=\"action.name\"\n [class]=\"getClassForAction(action)\"\n [style]=\"getStyleForAction(action)\"\n (click)=\"getMethodForAction(action, rowData, $event)\"\n ></span>\n </ng-container>\n </div>\n <ng-container *ngIf=\"col.filterType === 'blob'\">\n <img width=\"45\" [src]=\"getRowData(rowData, col)\" alt=\"\" />\n </ng-container>\n <ng-container *ngIf=\"col.filterType !== 'blob'\">\n {{ getRowData(rowData, col) }}\n </ng-container>\n </td>\n <td *ngIf=\"col.editable\">\n <spiderly-number\n [control]=\"getFormArrayControlByIndex(col.field, rowData.index)\"\n [showLabel]=\"false\"\n ></spiderly-number>\n </td>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td\n [attr.colspan]=\"\n cols?.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"NoRecordsFound\") }}\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"loadingbody\">\n <tr>\n <td\n [attr.colspan]=\"\n cols?.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"Loading\") }}...\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template pTemplate=\"paginatorright\">\n <div style=\"display: flex; justify-content: end; gap: 10px\">\n <spiderly-button\n *ngIf=\"showAddButton\"\n [label]=\"t('AddNew')\"\n icon=\"pi pi-plus\"\n (onClick)=\"navigateToDetails(0)\"\n ></spiderly-button>\n </div>\n </ng-template>\n </p-table>\n </div>\n</ng-container>\n", styles: [".table-header{display:flex;justify-content:space-between;align-items:center}@media (max-width: 640px){.table-header{display:flex;flex-direction:column;align-items:start;gap:14px}}.spiderly-table .p-paginator{padding:1rem}@media (min-width: 1400px){.spiderly-table .p-paginator-left-content{position:absolute;padding:14px;left:0}}@media (min-width: 1400px){.spiderly-table .p-paginator-right-content{position:absolute;padding:14px;right:0}}:host ::ng-deep .p-datatable-thead{position:unset!important}:host ::ng-deep .p-datatable-header{border-radius:6px 6px 0 0}:host ::ng-deep .p-datatable{border-radius:var(--p-content-border-radius);border:1px solid var(--p-datatable-border-color)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: SpiderlyControlsModule }, { kind: "component", type: SpiderlyButtonComponent, selector: "spiderly-button", inputs: ["type"] }, { kind: "component", type: SpiderlyNumberComponent, selector: "spiderly-number", inputs: ["prefix", "showButtons", "decimal", "maxFractionDigits"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i10.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i10.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i10.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "component", type: i10.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i12.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i4$5.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "fluid", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "size", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i4$1.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "size", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }] }); }
4447
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyDataTableComponent, isStandalone: true, selector: "spiderly-data-table", inputs: { tableTitle: "tableTitle", tableIcon: "tableIcon", items: "items", rows: "rows", cols: "cols", showPaginator: "showPaginator", showCardWrapper: "showCardWrapper", readonly: "readonly", idField: "idField", getPaginatedListObservableMethod: "getPaginatedListObservableMethod", exportListToExcelObservableMethod: "exportListToExcelObservableMethod", deleteItemFromTableObservableMethod: "deleteItemFromTableObservableMethod", deleteListFromTableObservableMethod: "deleteListFromTableObservableMethod", newlySelectedItems: "newlySelectedItems", unselectedItems: "unselectedItems", selectionMode: "selectionMode", selectedLazyLoadObservableMethod: "selectedLazyLoadObservableMethod", additionalFilterIdLong: "additionalFilterIdLong", showAddButton: "showAddButton", showExportToExcelButton: "showExportToExcelButton", showReloadTableButton: "showReloadTableButton", getFormArrayItems: "getFormArrayItems", hasLazyLoad: "hasLazyLoad", stateKey: "stateKey", stateStorage: "stateStorage", getAlreadySelectedItemIds: "getAlreadySelectedItemIds", getAlreadySelectedItems: "getAlreadySelectedItems", getFormControl: "getFormControl", additionalIndexes: "additionalIndexes", navigateOnRowClick: "navigateOnRowClick", rowNavigationPath: "rowNavigationPath" }, outputs: { onTotalRecordsChange: "onTotalRecordsChange", onLazyLoad: "onLazyLoad", onIsAllSelectedChange: "onIsAllSelectedChange", onRowSelect: "onRowSelect", onRowUnselect: "onRowUnselect" }, viewQueries: [{ propertyName: "table", first: true, predicate: ["dt"], descendants: true }], ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n showCardWrapper ? 'card responsive-card-padding overflow-auto' : ''\n \"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowHover]=\"true\"\n [paginator]=\"showPaginator\"\n responsiveLayout=\"scroll\"\n [lazy]=\"hasLazyLoad\"\n (onLazyLoad)=\"lazyLoad($event)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"items === undefined || loading === true\"\n [selectionMode]=\"selectionMode\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n sortMode=\"multiple\"\n [stateKey]=\"resolvedStateKey\"\n [stateStorage]=\"stateStorage\"\n >\n <ng-template pTemplate=\"caption\">\n <div class=\"table-header overflow-auto\">\n <div style=\"display: flex; align-items: center; gap: 8px\">\n <i class=\"{{ tableIcon }}\" style=\"font-size: 20px\"></i>\n <div style=\"margin: 0px; font-size: 17.5px\">{{ tableTitle }}</div>\n </div>\n <div style=\"display: flex; gap: 8px\">\n <button\n pButton\n [label]=\"t('ClearFilters')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-filter-slash\"\n (click)=\"clear(dt)\"\n ></button>\n <button\n pButton\n *ngIf=\"showExportToExcelButton\"\n [label]=\"t('ExportToExcel')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-download\"\n (click)=\"exportListToExcel()\"\n ></button>\n <button\n pButton\n *ngIf=\"showReloadTableButton\"\n [label]=\"t('Reload')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-refresh\"\n (click)=\"reload()\"\n ></button>\n <button\n pButton\n *ngIf=\"deleteListFromTableObservableMethod && rowsSelectedNumber > 0\"\n [label]=\"t('DeleteSelected') + ' (' + rowsSelectedNumber + ')'\"\n class=\"p-button-danger\"\n style=\"flex: none\"\n icon=\"pi pi-trash\"\n (click)=\"deleteSelectedObjects()\"\n ></button>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th style=\"width: 0rem\" *ngIf=\"selectionMode == 'multiple'\">\n <div style=\"display: flex; gap: 8px\">\n <p-checkbox\n *ngIf=\"showSelectAllCheckbox\"\n [disabled]=\"readonly\"\n (onChange)=\"selectAll($event.checked)\"\n [(ngModel)]=\"fakeIsAllSelected\"\n [binary]=\"true\"\n ></p-checkbox>\n ({{ rowsSelectedNumber }})\n </div>\n </th>\n <ng-container *ngFor=\"let col of cols; trackBy: colTrackByFn\">\n <th\n [pSortableColumn]=\"col.field\"\n [pSortableColumnDisabled]=\"col.sortable === false || !col.field\"\n [style]=\"getColHeaderWidth(col.filterType)\"\n >\n <div\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n \"\n >\n <span style=\"display: flex; align-items: center; gap: 4px\">\n {{ col.name }}\n <p-sortIcon\n *ngIf=\"col.sortable !== false && col.field\"\n [field]=\"col.field!\"\n ></p-sortIcon>\n </span>\n <p-columnFilter\n *ngIf=\"col.filterType != null && col.filterType !== 'blob'\"\n [type]=\"col.filterType\"\n [field]=\"col.filterField ?? col.field\"\n display=\"menu\"\n [placeholder]=\"col.filterPlaceholder\"\n [showOperator]=\"false\"\n [showMatchModes]=\"col.showMatchModes\"\n [showAddButton]=\"col.showAddButton\"\n [matchModeOptions]=\"getColMatchModeOptions(col.filterType)\"\n [matchMode]=\"getColMatchMode(col.filterType)\"\n >\n <ng-template\n *ngIf=\"isDropOrMulti(col.filterType)\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"col.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '240px' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n <ng-template\n *ngIf=\"col.filterType == 'date'\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-datepicker\n [ngModel]=\"value\"\n [showTime]=\"col.showTime\"\n (onSelect)=\"filter($event)\"\n ></p-datepicker>\n </ng-template>\n </p-columnFilter>\n </div>\n </th>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template\n pTemplate=\"body\"\n let-rowData\n let-index=\"rowIndex\"\n let-editing=\"editing\"\n >\n <tr\n [class.clickable]=\"navigateOnRowClick\"\n (click)=\"onRowClick(rowData)\"\n >\n <td *ngIf=\"selectionMode == 'multiple'\">\n <p-checkbox\n [disabled]=\"readonly\"\n (onChange)=\"selectRow(rowData[idField], rowData.index)\"\n [ngModel]=\"isRowSelected(rowData[idField])\"\n [binary]=\"true\"\n ></p-checkbox>\n </td>\n <ng-container *ngFor=\"let col of cols; trackBy: colTrackByFn\">\n <td\n [style]=\"getStyleForBodyColumn(col)\"\n [class.clickable]=\"col.onCellClick\"\n (click)=\"onCellClick(col, rowData, $event)\"\n *ngIf=\"!col.editable\"\n >\n <div\n style=\"\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 18px;\n \"\n >\n <ng-container\n *ngFor=\"let action of col.actions; trackBy: actionTrackByFn\"\n >\n <span\n [pTooltip]=\"action.name\"\n [class]=\"getClassForAction(action)\"\n [style]=\"getStyleForAction(action)\"\n (click)=\"getMethodForAction(action, rowData, $event)\"\n ></span>\n </ng-container>\n </div>\n <ng-container *ngIf=\"col.filterType === 'blob'\">\n <img width=\"45\" [src]=\"getRowData(rowData, col)\" alt=\"\" />\n </ng-container>\n <ng-container *ngIf=\"col.filterType !== 'blob'\">\n {{ getRowData(rowData, col) }}\n </ng-container>\n </td>\n <td *ngIf=\"col.editable\">\n <spiderly-number\n [control]=\"getFormArrayControlByIndex(col.field, rowData.index)\"\n [showLabel]=\"false\"\n ></spiderly-number>\n </td>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td\n [attr.colspan]=\"\n cols?.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"NoRecordsFound\") }}\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"loadingbody\">\n <tr>\n <td\n [attr.colspan]=\"\n cols?.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"Loading\") }}...\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template pTemplate=\"paginatorright\">\n <div style=\"display: flex; justify-content: end; gap: 10px\">\n <spiderly-button\n *ngIf=\"showAddButton\"\n [label]=\"t('AddNew')\"\n icon=\"pi pi-plus\"\n (onClick)=\"navigateToDetails(0)\"\n ></spiderly-button>\n </div>\n </ng-template>\n </p-table>\n </div>\n</ng-container>\n", styles: [".table-header{display:flex;justify-content:space-between;align-items:center}@media (max-width: 640px){.table-header{display:flex;flex-direction:column;align-items:start;gap:14px}}.spiderly-table .p-paginator{padding:1rem}@media (min-width: 1400px){.spiderly-table .p-paginator-left-content{position:absolute;padding:14px;left:0}}@media (min-width: 1400px){.spiderly-table .p-paginator-right-content{position:absolute;padding:14px;right:0}}:host ::ng-deep .clickable{cursor:pointer}:host ::ng-deep td.clickable:hover{text-decoration:underline}:host ::ng-deep .p-datatable-thead{position:unset!important}:host ::ng-deep .p-datatable-header{border-radius:6px 6px 0 0}:host ::ng-deep .p-datatable{border-radius:var(--p-content-border-radius);border:1px solid var(--p-datatable-border-color)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: SpiderlyControlsModule }, { kind: "component", type: SpiderlyButtonComponent, selector: "spiderly-button", inputs: ["type"] }, { kind: "component", type: SpiderlyNumberComponent, selector: "spiderly-number", inputs: ["prefix", "showButtons", "decimal", "maxFractionDigits"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i10.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i10.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i10.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "component", type: i10.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i12.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i4$5.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "fluid", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "size", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i4$1.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "size", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }] }); }
4419
4448
  }
4420
4449
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyDataTableComponent, decorators: [{
4421
4450
  type: Component,
@@ -4430,7 +4459,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
4430
4459
  DatePickerModule,
4431
4460
  CheckboxModule,
4432
4461
  TooltipModule,
4433
- ], template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n showCardWrapper ? 'card responsive-card-padding overflow-auto' : ''\n \"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowHover]=\"true\"\n [paginator]=\"showPaginator\"\n responsiveLayout=\"scroll\"\n [lazy]=\"hasLazyLoad\"\n (onLazyLoad)=\"lazyLoad($event)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"items === undefined || loading === true\"\n [selectionMode]=\"selectionMode\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n sortMode=\"multiple\"\n [stateKey]=\"resolvedStateKey\"\n [stateStorage]=\"stateStorage\"\n >\n <ng-template pTemplate=\"caption\">\n <div class=\"table-header overflow-auto\">\n <div style=\"display: flex; align-items: center; gap: 8px\">\n <i class=\"{{ tableIcon }}\" style=\"font-size: 20px\"></i>\n <div style=\"margin: 0px; font-size: 17.5px\">{{ tableTitle }}</div>\n </div>\n <div style=\"display: flex; gap: 8px\">\n <button\n pButton\n [label]=\"t('ClearFilters')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-filter-slash\"\n (click)=\"clear(dt)\"\n ></button>\n <button\n pButton\n *ngIf=\"showExportToExcelButton\"\n [label]=\"t('ExportToExcel')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-download\"\n (click)=\"exportListToExcel()\"\n ></button>\n <button\n pButton\n *ngIf=\"showReloadTableButton\"\n [label]=\"t('Reload')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-refresh\"\n (click)=\"reload()\"\n ></button>\n <button\n pButton\n *ngIf=\"deleteListFromTableObservableMethod && rowsSelectedNumber > 0\"\n [label]=\"t('DeleteSelected') + ' (' + rowsSelectedNumber + ')'\"\n class=\"p-button-danger\"\n style=\"flex: none\"\n icon=\"pi pi-trash\"\n (click)=\"deleteSelectedObjects()\"\n ></button>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th style=\"width: 0rem\" *ngIf=\"selectionMode == 'multiple'\">\n <div style=\"display: flex; gap: 8px\">\n <p-checkbox\n *ngIf=\"showSelectAllCheckbox\"\n [disabled]=\"readonly\"\n (onChange)=\"selectAll($event.checked)\"\n [(ngModel)]=\"fakeIsAllSelected\"\n [binary]=\"true\"\n ></p-checkbox>\n ({{ rowsSelectedNumber }})\n </div>\n </th>\n <ng-container *ngFor=\"let col of cols; trackBy: colTrackByFn\">\n <th\n [pSortableColumn]=\"col.field\"\n [pSortableColumnDisabled]=\"col.sortable === false || !col.field\"\n [style]=\"getColHeaderWidth(col.filterType)\"\n >\n <div\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n \"\n >\n <span style=\"display: flex; align-items: center; gap: 4px\">\n {{ col.name }}\n <p-sortIcon\n *ngIf=\"col.sortable !== false && col.field\"\n [field]=\"col.field!\"\n ></p-sortIcon>\n </span>\n <p-columnFilter\n *ngIf=\"col.filterType != null && col.filterType !== 'blob'\"\n [type]=\"col.filterType\"\n [field]=\"col.filterField ?? col.field\"\n display=\"menu\"\n [placeholder]=\"col.filterPlaceholder\"\n [showOperator]=\"false\"\n [showMatchModes]=\"col.showMatchModes\"\n [showAddButton]=\"col.showAddButton\"\n [matchModeOptions]=\"getColMatchModeOptions(col.filterType)\"\n [matchMode]=\"getColMatchMode(col.filterType)\"\n >\n <ng-template\n *ngIf=\"isDropOrMulti(col.filterType)\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"col.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '240px' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n <ng-template\n *ngIf=\"col.filterType == 'date'\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-datepicker\n [ngModel]=\"value\"\n [showTime]=\"col.showTime\"\n (onSelect)=\"filter($event)\"\n ></p-datepicker>\n </ng-template>\n </p-columnFilter>\n </div>\n </th>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template\n pTemplate=\"body\"\n let-rowData\n let-index=\"rowIndex\"\n let-editing=\"editing\"\n >\n <tr\n [class.clickable]=\"navigateOnRowClick\"\n (click)=\"onRowClick(rowData)\"\n >\n <td *ngIf=\"selectionMode == 'multiple'\">\n <p-checkbox\n [disabled]=\"readonly\"\n (onChange)=\"selectRow(rowData[idField], rowData.index)\"\n [ngModel]=\"isRowSelected(rowData[idField])\"\n [binary]=\"true\"\n ></p-checkbox>\n </td>\n <ng-container *ngFor=\"let col of cols; trackBy: colTrackByFn\">\n <td [style]=\"getStyleForBodyColumn(col)\" *ngIf=\"!col.editable\">\n <div\n style=\"\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 18px;\n \"\n >\n <ng-container\n *ngFor=\"let action of col.actions; trackBy: actionTrackByFn\"\n >\n <span\n [pTooltip]=\"action.name\"\n [class]=\"getClassForAction(action)\"\n [style]=\"getStyleForAction(action)\"\n (click)=\"getMethodForAction(action, rowData, $event)\"\n ></span>\n </ng-container>\n </div>\n <ng-container *ngIf=\"col.filterType === 'blob'\">\n <img width=\"45\" [src]=\"getRowData(rowData, col)\" alt=\"\" />\n </ng-container>\n <ng-container *ngIf=\"col.filterType !== 'blob'\">\n {{ getRowData(rowData, col) }}\n </ng-container>\n </td>\n <td *ngIf=\"col.editable\">\n <spiderly-number\n [control]=\"getFormArrayControlByIndex(col.field, rowData.index)\"\n [showLabel]=\"false\"\n ></spiderly-number>\n </td>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td\n [attr.colspan]=\"\n cols?.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"NoRecordsFound\") }}\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"loadingbody\">\n <tr>\n <td\n [attr.colspan]=\"\n cols?.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"Loading\") }}...\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template pTemplate=\"paginatorright\">\n <div style=\"display: flex; justify-content: end; gap: 10px\">\n <spiderly-button\n *ngIf=\"showAddButton\"\n [label]=\"t('AddNew')\"\n icon=\"pi pi-plus\"\n (onClick)=\"navigateToDetails(0)\"\n ></spiderly-button>\n </div>\n </ng-template>\n </p-table>\n </div>\n</ng-container>\n", styles: [".table-header{display:flex;justify-content:space-between;align-items:center}@media (max-width: 640px){.table-header{display:flex;flex-direction:column;align-items:start;gap:14px}}.spiderly-table .p-paginator{padding:1rem}@media (min-width: 1400px){.spiderly-table .p-paginator-left-content{position:absolute;padding:14px;left:0}}@media (min-width: 1400px){.spiderly-table .p-paginator-right-content{position:absolute;padding:14px;right:0}}:host ::ng-deep .p-datatable-thead{position:unset!important}:host ::ng-deep .p-datatable-header{border-radius:6px 6px 0 0}:host ::ng-deep .p-datatable{border-radius:var(--p-content-border-radius);border:1px solid var(--p-datatable-border-color)}\n"] }]
4462
+ ], template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n showCardWrapper ? 'card responsive-card-padding overflow-auto' : ''\n \"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowHover]=\"true\"\n [paginator]=\"showPaginator\"\n responsiveLayout=\"scroll\"\n [lazy]=\"hasLazyLoad\"\n (onLazyLoad)=\"lazyLoad($event)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"items === undefined || loading === true\"\n [selectionMode]=\"selectionMode\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n sortMode=\"multiple\"\n [stateKey]=\"resolvedStateKey\"\n [stateStorage]=\"stateStorage\"\n >\n <ng-template pTemplate=\"caption\">\n <div class=\"table-header overflow-auto\">\n <div style=\"display: flex; align-items: center; gap: 8px\">\n <i class=\"{{ tableIcon }}\" style=\"font-size: 20px\"></i>\n <div style=\"margin: 0px; font-size: 17.5px\">{{ tableTitle }}</div>\n </div>\n <div style=\"display: flex; gap: 8px\">\n <button\n pButton\n [label]=\"t('ClearFilters')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-filter-slash\"\n (click)=\"clear(dt)\"\n ></button>\n <button\n pButton\n *ngIf=\"showExportToExcelButton\"\n [label]=\"t('ExportToExcel')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-download\"\n (click)=\"exportListToExcel()\"\n ></button>\n <button\n pButton\n *ngIf=\"showReloadTableButton\"\n [label]=\"t('Reload')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-refresh\"\n (click)=\"reload()\"\n ></button>\n <button\n pButton\n *ngIf=\"deleteListFromTableObservableMethod && rowsSelectedNumber > 0\"\n [label]=\"t('DeleteSelected') + ' (' + rowsSelectedNumber + ')'\"\n class=\"p-button-danger\"\n style=\"flex: none\"\n icon=\"pi pi-trash\"\n (click)=\"deleteSelectedObjects()\"\n ></button>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th style=\"width: 0rem\" *ngIf=\"selectionMode == 'multiple'\">\n <div style=\"display: flex; gap: 8px\">\n <p-checkbox\n *ngIf=\"showSelectAllCheckbox\"\n [disabled]=\"readonly\"\n (onChange)=\"selectAll($event.checked)\"\n [(ngModel)]=\"fakeIsAllSelected\"\n [binary]=\"true\"\n ></p-checkbox>\n ({{ rowsSelectedNumber }})\n </div>\n </th>\n <ng-container *ngFor=\"let col of cols; trackBy: colTrackByFn\">\n <th\n [pSortableColumn]=\"col.field\"\n [pSortableColumnDisabled]=\"col.sortable === false || !col.field\"\n [style]=\"getColHeaderWidth(col.filterType)\"\n >\n <div\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n \"\n >\n <span style=\"display: flex; align-items: center; gap: 4px\">\n {{ col.name }}\n <p-sortIcon\n *ngIf=\"col.sortable !== false && col.field\"\n [field]=\"col.field!\"\n ></p-sortIcon>\n </span>\n <p-columnFilter\n *ngIf=\"col.filterType != null && col.filterType !== 'blob'\"\n [type]=\"col.filterType\"\n [field]=\"col.filterField ?? col.field\"\n display=\"menu\"\n [placeholder]=\"col.filterPlaceholder\"\n [showOperator]=\"false\"\n [showMatchModes]=\"col.showMatchModes\"\n [showAddButton]=\"col.showAddButton\"\n [matchModeOptions]=\"getColMatchModeOptions(col.filterType)\"\n [matchMode]=\"getColMatchMode(col.filterType)\"\n >\n <ng-template\n *ngIf=\"isDropOrMulti(col.filterType)\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"col.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '240px' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n <ng-template\n *ngIf=\"col.filterType == 'date'\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-datepicker\n [ngModel]=\"value\"\n [showTime]=\"col.showTime\"\n (onSelect)=\"filter($event)\"\n ></p-datepicker>\n </ng-template>\n </p-columnFilter>\n </div>\n </th>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template\n pTemplate=\"body\"\n let-rowData\n let-index=\"rowIndex\"\n let-editing=\"editing\"\n >\n <tr\n [class.clickable]=\"navigateOnRowClick\"\n (click)=\"onRowClick(rowData)\"\n >\n <td *ngIf=\"selectionMode == 'multiple'\">\n <p-checkbox\n [disabled]=\"readonly\"\n (onChange)=\"selectRow(rowData[idField], rowData.index)\"\n [ngModel]=\"isRowSelected(rowData[idField])\"\n [binary]=\"true\"\n ></p-checkbox>\n </td>\n <ng-container *ngFor=\"let col of cols; trackBy: colTrackByFn\">\n <td\n [style]=\"getStyleForBodyColumn(col)\"\n [class.clickable]=\"col.onCellClick\"\n (click)=\"onCellClick(col, rowData, $event)\"\n *ngIf=\"!col.editable\"\n >\n <div\n style=\"\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 18px;\n \"\n >\n <ng-container\n *ngFor=\"let action of col.actions; trackBy: actionTrackByFn\"\n >\n <span\n [pTooltip]=\"action.name\"\n [class]=\"getClassForAction(action)\"\n [style]=\"getStyleForAction(action)\"\n (click)=\"getMethodForAction(action, rowData, $event)\"\n ></span>\n </ng-container>\n </div>\n <ng-container *ngIf=\"col.filterType === 'blob'\">\n <img width=\"45\" [src]=\"getRowData(rowData, col)\" alt=\"\" />\n </ng-container>\n <ng-container *ngIf=\"col.filterType !== 'blob'\">\n {{ getRowData(rowData, col) }}\n </ng-container>\n </td>\n <td *ngIf=\"col.editable\">\n <spiderly-number\n [control]=\"getFormArrayControlByIndex(col.field, rowData.index)\"\n [showLabel]=\"false\"\n ></spiderly-number>\n </td>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td\n [attr.colspan]=\"\n cols?.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"NoRecordsFound\") }}\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"loadingbody\">\n <tr>\n <td\n [attr.colspan]=\"\n cols?.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"Loading\") }}...\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template pTemplate=\"paginatorright\">\n <div style=\"display: flex; justify-content: end; gap: 10px\">\n <spiderly-button\n *ngIf=\"showAddButton\"\n [label]=\"t('AddNew')\"\n icon=\"pi pi-plus\"\n (onClick)=\"navigateToDetails(0)\"\n ></spiderly-button>\n </div>\n </ng-template>\n </p-table>\n </div>\n</ng-container>\n", styles: [".table-header{display:flex;justify-content:space-between;align-items:center}@media (max-width: 640px){.table-header{display:flex;flex-direction:column;align-items:start;gap:14px}}.spiderly-table .p-paginator{padding:1rem}@media (min-width: 1400px){.spiderly-table .p-paginator-left-content{position:absolute;padding:14px;left:0}}@media (min-width: 1400px){.spiderly-table .p-paginator-right-content{position:absolute;padding:14px;right:0}}:host ::ng-deep .clickable{cursor:pointer}:host ::ng-deep td.clickable:hover{text-decoration:underline}:host ::ng-deep .p-datatable-thead{position:unset!important}:host ::ng-deep .p-datatable-header{border-radius:6px 6px 0 0}:host ::ng-deep .p-datatable{border-radius:var(--p-content-border-radius);border:1px solid var(--p-datatable-border-color)}\n"] }]
4434
4463
  }], ctorParameters: () => [{ type: i3$2.Router }, { type: i1$5.DialogService }, { type: i3$2.ActivatedRoute }, { type: SpiderlyMessageService }, { type: i1.TranslocoService }, { type: ConfigServiceBase }, { type: undefined, decorators: [{
4435
4464
  type: Inject,
4436
4465
  args: [LOCALE_ID]
@@ -4521,7 +4550,7 @@ class Action {
4521
4550
  }
4522
4551
  }
4523
4552
  class Column {
4524
- constructor({ name, field, filterField, filterType, filterPlaceholder, showMatchModes, showAddButton, dropdownOrMultiselectValues, actions, editable, showTime, decimalPlaces, sortable, } = {}) {
4553
+ constructor({ name, field, filterField, filterType, filterPlaceholder, showMatchModes, showAddButton, dropdownOrMultiselectValues, actions, editable, showTime, decimalPlaces, sortable, onCellClick, } = {}) {
4525
4554
  this.name = name;
4526
4555
  this.field = field;
4527
4556
  this.filterField = filterField;
@@ -4535,6 +4564,7 @@ class Column {
4535
4564
  this.showTime = showTime;
4536
4565
  this.decimalPlaces = decimalPlaces;
4537
4566
  this.sortable = sortable;
4567
+ this.onCellClick = onCellClick;
4538
4568
  }
4539
4569
  }
4540
4570
  class RowClickEvent {
@@ -5209,5 +5239,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
5209
5239
  * Generated bundle index. Do not edit.
5210
5240
  */
5211
5241
 
5212
- export { Action, AllClickEvent, ApiErrorCodes, ApiSecurityService, AppSidebarComponent, AuthCardComponent, AuthGuard, AuthResult, AuthResultWithCookies, AuthServiceBase, BaseAutocompleteControl, BaseControl, BaseDropdownControl, BaseEntity, BaseFormComponent, BaseFormService, CardSkeletonComponent, Codebook, Column, ConfigServiceBase, DEFAULT_EXTERNAL_PROVIDER_ICONS, ExternalLoginComponent, ExternalProvider, ExternalProviderPublic, Filter, FilterRule, FilterSortMeta, FooterComponent, IndexCardComponent, InfoCardComponent, InitCompanyAuthDialogDetails, InitTopBarData, IsAuthorizedForSaveEvent, LastMenuIconIndexClicked, LayoutServiceBase, LazyLoadSelectedIdsResult, Login, LoginVerificationComponent, LoginVerificationToken, MatchModeCodes, MenuChangeEvent, MenuitemComponent, Namebook, NotAuthGuard, NotFoundComponent, PROPS_KEY, PaginatedResult, PanelBodyComponent, PanelFooterComponent, PanelHeaderComponent, PrimengOption, ProfileAvatarComponent, ReflectProp, RefreshTokenRequest, RequiredComponent, RowClickEvent, SecurityPermissionCodes, SendLoginVerificationEmailResult, SideMenuTopBarComponent, SidebarMenuComponent, SimpleSaveResult, SpiderlyAutocompleteComponent, SpiderlyButtonBaseComponent, SpiderlyButtonComponent, SpiderlyCalendarComponent, SpiderlyCardComponent, SpiderlyCheckboxComponent, SpiderlyColorPickerComponent, SpiderlyControlsModule, SpiderlyDataTableComponent, SpiderlyDataViewComponent, SpiderlyDeleteConfirmationComponent, SpiderlyDropdownComponent, SpiderlyEditorComponent, SpiderlyErrorHandler, SpiderlyFileComponent, SpiderlyFileSelectEvent, SpiderlyFormArray, SpiderlyFormControl, SpiderlyFormGroup, SpiderlyLayoutComponent, SpiderlyLoginComponent, SpiderlyMarkdownComponent, SpiderlyMessageService, SpiderlyMultiAutocompleteComponent, SpiderlyMultiSelectComponent, SpiderlyNumberComponent, SpiderlyPanelComponent, SpiderlyPanelsModule, SpiderlyPasswordComponent, SpiderlyReturnButtonComponent, SpiderlySplitButtonComponent, SpiderlyTab, SpiderlyTemplateTypeDirective, SpiderlyTextareaComponent, SpiderlyTextboxComponent, SpiderlyTranslocoLoader, TopBarComponent, UserBase, UserRole, ValidatorAbstractService, VerificationTokenRequest, VerificationTypeCodes, VerificationWrapperComponent, adjustColor, authInitializer, capitalizeFirstChar, deleteAction, exportListToExcel, firstCharToUpper, getFileNameFromContentDisposition, getHtmlImgDisplayString64, getImageDimensions, getMimeTypeForFileName, getMonth, getParentUrl, getPrimengAutocompleteCodebookOptions, getPrimengAutocompleteNamebookOptions, getPrimengDropdownCodebookOptions, getPrimengDropdownNamebookOptions, getPrimengNamebookOptions, httpLoadingInterceptor, isExcelFileType, isFileImageType, isNullOrEmpty, jsonHttpInterceptor, jwtInterceptor, kebabToTitleCase, nameOf, nameof, parseDateOnlyLocal, primitiveArrayTypes, pushAction, selectedTab, singleOrDefault, splitPascalCase, toCommaSeparatedString, unauthorizedInterceptor, validatePrecisionScale };
5242
+ export { Action, AllClickEvent, ApiErrorCodes, ApiSecurityService, AppSidebarComponent, AuthCardComponent, AuthGuard, AuthResult, AuthResultWithCookies, AuthServiceBase, BaseAutocompleteControl, BaseControl, BaseDropdownControl, BaseEntity, BaseFormComponent, BaseFormService, CardSkeletonComponent, Codebook, Column, ConfigServiceBase, DEFAULT_EXTERNAL_PROVIDER_ICONS, ExternalLoginComponent, ExternalProvider, ExternalProviderPublic, Filter, FilterRule, FilterSortMeta, FooterComponent, IndexCardComponent, InfoCardComponent, InitCompanyAuthDialogDetails, InitTopBarData, IsAuthorizedForSaveEvent, LastMenuIconIndexClicked, LayoutServiceBase, LazyLoadSelectedIdsResult, Login, LoginVerificationComponent, LoginVerificationToken, MatchModeCodes, MenuChangeEvent, MenuitemComponent, Namebook, NotAuthGuard, NotFoundComponent, PROPS_KEY, PaginatedResult, PanelBodyComponent, PanelFooterComponent, PanelHeaderComponent, PrimengOption, ProfileAvatarComponent, ReflectProp, RefreshTokenRequest, RequiredComponent, RowClickEvent, SecurityPermissionCodes, SendLoginVerificationEmailResult, SideMenuTopBarComponent, SidebarMenuComponent, SimpleSaveResult, SpiderlyAutocompleteComponent, SpiderlyButtonBaseComponent, SpiderlyButtonComponent, SpiderlyCalendarComponent, SpiderlyCardComponent, SpiderlyCheckboxComponent, SpiderlyColorPickerComponent, SpiderlyControlsModule, SpiderlyDataTableComponent, SpiderlyDataViewComponent, SpiderlyDeleteConfirmationComponent, SpiderlyDropdownComponent, SpiderlyEditorComponent, SpiderlyErrorHandler, SpiderlyFileComponent, SpiderlyFileSelectEvent, SpiderlyFormArray, SpiderlyFormControl, SpiderlyFormGroup, SpiderlyLayoutComponent, SpiderlyLoginComponent, SpiderlyMarkdownComponent, SpiderlyMessageService, SpiderlyMultiAutocompleteComponent, SpiderlyMultiSelectComponent, SpiderlyNumberComponent, SpiderlyPanelComponent, SpiderlyPanelsModule, SpiderlyPasswordComponent, SpiderlyReturnButtonComponent, SpiderlySplitButtonComponent, SpiderlyTab, SpiderlyTemplateTypeDirective, SpiderlyTextareaComponent, SpiderlyTextboxComponent, SpiderlyTranslocoLoader, TopBarComponent, UserBase, UserRole, ValidatorAbstractService, VerificationTokenRequest, VerificationTypeCodes, VerificationWrapperComponent, adjustColor, authInitializer, capitalizeFirstChar, deleteAction, exportListToExcel, firstCharToUpper, getFileNameFromContentDisposition, getHtmlImgDisplayString64, getImageDimensions, getMimeTypeForFileName, getMonth, getParentUrl, getPrimengAutocompleteCodebookOptions, getPrimengAutocompleteNamebookOptions, getPrimengDropdownCodebookOptions, getPrimengDropdownNamebookOptions, getPrimengNamebookOptions, httpLoadingInterceptor, isExcelFileType, isFileImageType, isNullOrEmpty, jsonHttpInterceptor, jwtInterceptor, kebabToTitleCase, nameOf, nameof, parseDateOnlyLocal, primitiveArrayTypes, pushAction, saveResponseAsFile, selectedTab, singleOrDefault, splitPascalCase, toCommaSeparatedString, unauthorizedInterceptor, validatePrecisionScale };
5213
5243
  //# sourceMappingURL=spiderly.mjs.map