{% extends 'base.html' %}
{% block content %}
<div class="asset-page-loader" id="asset-page-loader" role="status" aria-live="polite">
  <div class="asset-page-loader-card">
    <span class="asset-page-spinner" aria-hidden="true"></span>
    <strong>{{ t('assets.loading', 'Assets werden geladen …') }}</strong>
    <span class="muted">{{ t('assets.loading_hint', 'Tabellenansicht und gespeicherte Einstellungen werden vorbereitet.') }}</span>
  </div>
</div>
<div class="asset-list-page asset-list-initializing" id="asset-list-page" aria-busy="true">
<div class="toolbar">
  <h1>{{ t('app.assets', 'Assets') }}{% if selected %}: {{ selected.name }}{% endif %}</h1>
  <div class="toolbar-actions">
    {% if is_admin and assets %}
    <button type="button" class="button button-secondary" id="bulk-edit-button" data-base-label="✏ {{ t('assets.bulk_edit', 'Daten ändern')|e }}" disabled>✏ {{ t('assets.bulk_edit', 'Daten ändern') }}</button>
    <button type="button" class="button button-danger" id="bulk-delete-button" data-base-label="🗑 {{ t('assets.delete_selected', 'Markierte löschen')|e }}" disabled>🗑 {{ t('assets.delete_selected', 'Markierte löschen') }}</button>
    <button type="button" class="button button-secondary" id="merge-duplicates-button" disabled>⧉ {{ t('duplicates.merge', 'Duplikate zusammenführen') }}</button>
    <button type="button" class="button button-secondary" id="software-compare-button" disabled>⇄ {{ t('software.compare.button', 'Software vergleichen') }}</button>
    <div class="bulk-job-controls" aria-label="{{ t('jobs.bulk.controls_label')|e }}">
      <label class="visually-hidden" for="bulk-job-type">{{ t('jobs.type') }}</label>
      <select id="bulk-job-type" class="bulk-job-type" disabled>
        <option value="">{{ t('jobs.select_type') }}</option>
        <option value="software_inventory" data-platform="all">{{ t('jobs.type.software_inventory') }}</option>
        {% for definition in job_definitions %}<option value="definition:{{ definition.id }}" data-platform="{{ definition.platform }}">{{ definition.name }}</option>{% endfor %}
      </select>
      <button type="button" class="button button-secondary" id="bulk-job-start-button" disabled>▶ {{ t('jobs.start') }}</button>
    </div>
    {% endif %}
    <button type="button" class="button button-secondary" id="duplicate-search-button">⧉ {{ t('duplicates.search', 'Duplikate suchen') }}</button>
    {% if is_admin %}<button type="button" class="button{% if not job_filter %} button-secondary{% endif %}" id="job-filter-button">⌁ {{ t('assets.job_filter.button', 'Jobstatus filtern') }}{% if job_filter %} ({{ total_assets }}){% endif %}</button>{% endif %}
    <button type="button" class="button button-secondary" id="column-settings-button">▦ {{ t('assets.columns', 'Spalten') }}</button>
    <button type="button" class="button button-secondary" id="reload-assets-button" data-loading-label="⏳ {{ t('common.loading', 'Lade...')|e }}">🔄 {{ t('assets.reload', 'Inhalt neu laden') }}</button>
    <a class="button button-secondary" href="/settings/import-export">⇅ {{ t('settings.import_export', 'Import / Export') }}</a>
{% if is_admin %}
    <a class="button" href="/assets/new{% if selected %}?category_id={{ selected.id }}{% endif %}">➕ {{ t('assets.create', 'Asset anlegen') }}</a>{% endif %}
  </div>
</div>
<div class="filters"><a href="/assets">{{ t('common.all', 'Alle') }}</a>{% for c in categories %}<a href="/assets?category_id={{ c.id }}">{{ c.name }}</a>{% endfor %}</div>
<div id="asset-context-filter-status" class="table-filter-status asset-context-filter-status{% if selected %} is-active{% endif %}" {% if not selected %}hidden{% endif %} role="status" aria-live="polite"
     data-active-label="{{ t('assets.active_selection', 'Aktive Auswahl')|e }}"
     data-duplicate-template="{{ t('duplicates.active_filter', 'Duplikate: {field}')|e }}">
  <span class="table-filter-status-icon" aria-hidden="true">⌕</span>
  <strong class="table-filter-status-label">{{ t('assets.active_selection', 'Aktive Auswahl') }}</strong>
  <span class="table-filter-status-details asset-context-filter-details">
    {% if selected %}
    <span class="asset-filter-chip" id="asset-category-filter-chip">
      {{ t('assets.category_filter', 'Kategorie: {category}').format(category=selected.name) }}
      <button type="button" class="asset-filter-chip-remove" id="asset-category-filter-remove" title="{{ t('assets.clear_category_filter', 'Kategoriefilter entfernen')|e }}" aria-label="{{ t('assets.clear_category_filter', 'Kategoriefilter entfernen')|e }}">×</button>
    </span>
    {% endif %}
    <span class="asset-filter-chip" id="asset-duplicate-filter-chip" hidden>
      <span id="asset-duplicate-filter-label"></span>
      <button type="button" class="asset-filter-chip-remove" id="asset-duplicate-filter-remove" title="{{ t('duplicates.remove_filter', 'Dublettenfilter entfernen')|e }}" aria-label="{{ t('duplicates.remove_filter', 'Dublettenfilter entfernen')|e }}">×</button>
    </span>
  </span>
</div>
{% if job_filter %}
<div class="notice job-filter-summary">
  <strong>{{ t('assets.job_filter.active', 'Aktiver Jobfilter') }}:</strong>
  {% for spec in job_filter.specs %}{% if spec.label_key %}{{ t(spec.label_key, spec.label) }}{% else %}{{ spec.label }}{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}
  · {{ t('assets.job_filter.excluded_summary', 'Ausgeschlossen') }}:
  {% if job_filter.excluded_statuses %}
    {% for status in job_filter.excluded_statuses %}{{ t('software.status.' ~ status, status) }}{% if not loop.last %}, {% endif %}{% endfor %}
  {% else %}
    {{ t('assets.job_filter.excluded_none', 'keine Status') }}
  {% endif %}
  {% if job_filter.revision_mode == 'current' %}· {{ t('assets.job_filter.current_revision', 'nur aktuelle Revision') }}{% endif %}
  <span class="muted">· {{ t('assets.total', '{count} Assets').format(count=total_assets) }}</span>
  <a class="button button-secondary button-small" href="/assets{% if selected %}?category_id={{ selected.id }}{% endif %}">{{ t('assets.job_filter.clear', 'Jobfilter entfernen') }}</a>
  {% if is_admin and assets and job_filter.startable_specs %}
  {% if job_filter.startable_specs|length > 1 %}
  <label class="job-filter-start-choice">{{ t('assets.job_filter.start_job', 'Auszuführender Job') }}
    <select id="start-filtered-job-selection">
      {% for spec in job_filter.startable_specs %}<option value="{{ spec.selection }}">{% if spec.label_key %}{{ t(spec.label_key, spec.label) }}{% else %}{{ spec.label }}{% endif %}</option>{% endfor %}
    </select>
  </label>
  {% endif %}
  <button type="button" class="button button-small" id="start-filtered-job-button">▶ {{ t('assets.job_filter.start', 'Job für gefilterte Assets starten') }} ({{ total_assets }})</button>
  {% endif %}
</div>
{% endif %}
{% if selected %}<p class="table-hint">{{ t('assets.column_order_hint') }}</p>{% endif %}
{% if is_admin %}
<form id="bulk-edit-form" class="hidden-utility-form" method="post" action="/assets/bulk-edit">{% if selected %}<input type="hidden" name="category_id" value="{{ selected.id }}">{% endif %}<span id="bulk-edit-ids"></span></form>
<form id="bulk-delete-form" class="hidden-utility-form" method="post" action="/assets/bulk-delete" onsubmit="return confirm({{ t('assets.bulk_delete_confirm')|tojson }});">{% if selected %}<input type="hidden" name="category_id" value="{{ selected.id }}">{% endif %}<span id="bulk-delete-ids"></span></form>
<form id="merge-duplicates-form" class="hidden-utility-form" method="post" action="/assets/merge"><span id="merge-duplicate-ids"></span></form>
<form id="software-compare-form" class="hidden-utility-form" method="post" action="/assets/software-compare"><span id="software-compare-ids"></span></form>
<form id="bulk-job-form" class="hidden-utility-form" method="post" action="/jobs/bulk-create">
  <input type="hidden" name="job_type" id="bulk-job-type-value">
  <input type="hidden" name="definition_id" id="bulk-job-definition-value">
  <span id="bulk-job-ids"></span>
</form>
{% if job_filter %}
<form id="start-filtered-job-form" class="hidden-utility-form" method="post" action="/assets/job-filter/start">
  {% for selection in job_filter.selections %}<input type="hidden" name="job_filter" value="{{ selection }}">{% endfor %}
  <input type="hidden" name="job_filter_start" id="start-filtered-job-value" value="{{ job_filter.startable_specs[0].selection if job_filter.startable_specs else job_filter.selections[0] }}">
  <input type="hidden" name="job_filter_revision" value="{{ job_filter.revision_mode }}">
  {% for status in job_filter.excluded_statuses %}<input type="hidden" name="job_filter_exclude" value="{{ status }}">{% endfor %}
  {% if selected %}<input type="hidden" name="category_id" value="{{ selected.id }}">{% endif %}
</form>
{% endif %}
{% endif %}
<div class="table-scrollbar-top" id="asset-table-scroll-top" aria-hidden="true"><div></div></div>
<div class="table-scroll sticky-table" id="asset-table-scroll">
<table class="data-table asset-table-fixed" id="asset-table" data-storage-key="asset-columns-{{ selected.id if selected else 'all' }}" data-fixed-column-width="{% if is_admin %}224{% else %}180{% endif %}">
  <thead><tr>
    <th class="asset-visual-column" data-no-sort="1" data-no-filter="1" data-field="asset_visual" data-label="{{ t('assets.device_visual', 'Gerät')|e }}" title="{{ t('assets.device_visual', 'Gerät')|e }}">{{ t('assets.device_visual', 'Gerät') }}</th>
    {% if is_admin %}<th class="selection-column" data-no-sort="1" data-no-filter="1"><input type="checkbox" id="select-all-assets" aria-label="{{ t('assets.select_all') }}"></th>{% endif %}
    {% set has_name = fields|selectattr('field_name', 'equalto', 'name')|list|length > 0 %}
    {% if not has_name %}<th draggable="true" data-field="name" data-column-width="220" data-label="{{ t('field.name', 'Bezeichnung')|e }}" style="width:220px;min-width:220px;max-width:220px">{{ t('field.name', 'Bezeichnung') }}</th>{% endif %}
    {% if not selected %}<th draggable="true" data-field="category" data-column-width="180" data-label="{{ t('common.category', 'Kategorie')|e }}" style="width:180px;min-width:180px;max-width:180px">{{ t('common.category', 'Kategorie') }}</th>{% endif %}
    {% for f in fields %}{% set list_width = f.definition.list_width if f.definition and f.definition.list_width else (220 if f.field_name == 'name' else 180) %}<th draggable="true" data-field="{{ f.field_name }}" data-column-width="{{ list_width }}" data-label="{{ f.label|e }}" style="width:{{ list_width }}px;min-width:{{ list_width }}px;max-width:{{ list_width }}px">{{ f.label }}</th>{% endfor %}
    {% if job_filter %}
    <th data-field="job_filter_status" data-label="{{ t('assets.job_filter.status', 'Jobstatus')|e }}" style="width:180px;min-width:180px;max-width:180px">{{ t('assets.job_filter.status', 'Jobstatus') }}</th>
    <th data-field="job_filter_last_execution" data-label="{{ t('assets.job_filter.last_execution', 'Letzte Ausführung')|e }}" style="width:175px;min-width:175px;max-width:175px">{{ t('assets.job_filter.last_execution', 'Letzte Ausführung') }}</th>
    <th data-field="job_filter_last_success" data-label="{{ t('assets.job_filter.last_success', 'Letzter Erfolg')|e }}" style="width:175px;min-width:175px;max-width:175px">{{ t('assets.job_filter.last_success', 'Letzter Erfolg') }}</th>
    <th data-field="job_filter_revision" data-label="{{ t('assets.job_filter.revision', 'Revision')|e }}" style="width:100px;min-width:100px;max-width:100px">{{ t('assets.job_filter.revision', 'Revision') }}</th>
    {% endif %}
    <th data-field="mesh_presence" data-label="{{ t('presence.title')|e }}" style="width:125px;min-width:125px;max-width:125px">{{ t('presence.title') }}</th>
    <th data-no-sort="1" data-no-filter="1" class="actions-column">{{ t('common.actions', 'Aktionen') }}</th>
  </tr></thead>
  <tbody>
  {% for a in assets %}<tr data-platform="{{ asset_platforms.get(a.id, 'unknown') }}">
    <td class="asset-visual-column" data-field="asset_visual">
      <a href="/assets/{{ a.id }}" class="asset-visual-link" aria-label="{{ a.name|e }}">
      {% set visual_source = a.visual_source or 'auto' %}
      {% if visual_source == 'asset_image' %}
        {% if a.image_path %}<img src="{{ a.image_path }}" alt="" class="asset-list-device-image">{% else %}<span class="asset-list-device-placeholder" aria-hidden="true">▣</span>{% endif %}
      {% elif visual_source == 'category_image' %}
        {% if a.category and a.category.image_path %}<img src="{{ a.category.image_path }}" alt="" class="asset-list-device-image category-device-image" title="{{ a.category.name|e }}">{% else %}<span class="asset-list-device-placeholder" aria-hidden="true">▣</span>{% endif %}
      {% elif visual_source == 'mesh_icon' %}
        {% if a.mesh_icon and a.mesh_icon >= 1 and a.mesh_icon <= 8 %}<img src="/static/img/meshcentral-device-icons/icons256-{{ a.mesh_icon }}-1.png" alt="" class="asset-list-device-image meshcentral-device-icon" title="{{ t('assets.mesh_icon_title', 'MeshCentral-Geräte-Icon {icon}').format(icon=a.mesh_icon)|e }}">{% else %}<span class="asset-list-device-placeholder" aria-hidden="true">▣</span>{% endif %}
      {% elif a.image_path %}
        <img src="{{ a.image_path }}" alt="" class="asset-list-device-image">
      {% elif a.category and a.category.image_path %}
        <img src="{{ a.category.image_path }}" alt="" class="asset-list-device-image category-device-image" title="{{ a.category.name|e }}">
      {% elif a.mesh_icon and a.mesh_icon >= 1 and a.mesh_icon <= 8 %}
        <img src="/static/img/meshcentral-device-icons/icons256-{{ a.mesh_icon }}-1.png" alt="" class="asset-list-device-image meshcentral-device-icon" title="{{ t('assets.mesh_icon_title', 'MeshCentral-Geräte-Icon {icon}').format(icon=a.mesh_icon)|e }}">
      {% else %}
        <span class="asset-list-device-placeholder" aria-hidden="true">▣</span>
      {% endif %}
      </a>
    </td>
    {% if is_admin %}<td class="selection-column"><input type="checkbox" class="asset-select" value="{{ a.id }}" aria-label="{{ t('assets.select_named', 'Asset {name} markieren').format(name=a.name)|e }}"></td>{% endif %}
    {% if not has_name %}<td data-field="name"><a href="/assets/{{ a.id }}">{{ a.name }}</a></td>{% endif %}
    {% if not selected %}<td data-field="category">{{ a.category.name }}</td>{% endif %}
    {% for f in fields %}{% set list_width = f.definition.list_width if f.definition and f.definition.list_width else (220 if f.field_name == 'name' else 180) %}<td data-field="{{ f.field_name }}" class="multiline" style="width:{{ list_width }}px;min-width:{{ list_width }}px;max-width:{{ list_width }}px">{% if f.field_name == 'name' %}<a href="/assets/{{ a.id }}">{{ a.name }}</a>{% else %}{{ a|attr(f.field_name) or '' }}{% endif %}</td>{% endfor %}
    {% if job_filter %}{% set job_state = job_state_by_asset.get(a.id) %}
    <td data-field="job_filter_status" style="width:180px;min-width:180px;max-width:180px"><span class="job-status job-status-{{ job_state.status }}">{{ t('software.status.' ~ job_state.status, t('assets.job_filter.never_run', 'Noch nie ausgeführt') if job_state.status == 'never_run' else job_state.status) }}</span>{% if job_filter.specs|length > 1 and job_state.job_label %}<small class="muted job-filter-source">{{ job_state.job_label }}</small>{% endif %}</td>
    <td data-field="job_filter_last_execution" style="width:175px;min-width:175px;max-width:175px">{% if job_state.last_execution %}<time class="local-time" datetime="{{ utc_iso(job_state.last_execution) }}" data-utc="{{ utc_iso(job_state.last_execution) }}">{{ job_state.last_execution }}</time>{% else %}<span class="muted">—</span>{% endif %}</td>
    <td data-field="job_filter_last_success" style="width:175px;min-width:175px;max-width:175px">{% if job_state.last_success %}<time class="local-time" datetime="{{ utc_iso(job_state.last_success) }}" data-utc="{{ utc_iso(job_state.last_success) }}">{{ job_state.last_success }}</time>{% else %}<span class="muted">—</span>{% endif %}</td>
    <td data-field="job_filter_revision" style="width:100px;min-width:100px;max-width:100px">{% if job_state.definition_revision %}{{ job_state.definition_revision }}{% else %}<span class="muted">—</span>{% endif %}</td>
    {% endif %}
    <td data-field="mesh_presence" style="width:125px;min-width:125px;max-width:125px">
      {% if a.mesh_node_id %}
        <span
          class="presence-badge presence-{{ a.mesh_online_state or 'unknown' }}"
          data-presence-asset="{{ a.id }}"
          data-label-online="{{ t('presence.online') }}"
          data-label-offline="{{ t('presence.offline') }}"
          data-label-unknown="{{ t('presence.unknown') }}"
          data-last-seen-label="{{ t('presence.last_seen') }}"
          title="{{ t('presence.' ~ (a.mesh_online_state or 'unknown')) }} · {{ t('presence.last_seen') }}: {% if a.mesh_last_seen %}{{ a.mesh_last_seen }}{% else %}–{% endif %}"
        ><span class="presence-dot" aria-hidden="true"></span><span data-presence-label>{{ t('presence.' ~ (a.mesh_online_state or 'unknown')) }}</span></span>
      {% else %}
        <span class="muted">–</span>
      {% endif %}
    </td>
    <td class="row-actions">{% if is_admin %}
      {% if issue_status_name and a.status == issue_status_name %}<form class="inline-form" method="post" action="/assets/{{a.id}}/return"><input type="hidden" name="return_to" value="list"><button class="icon-button button-return" title="{{ t('assets.return', 'Zurücknehmen') }}">↩</button></form>{% else %}<button type="button" class="icon-button button-issue" data-issue-action="/assets/{{a.id}}/issue" data-return-to="list" title="{{ t('assets.issue', 'Ausgeben') }}">↗</button>{% endif %}
      <a class="icon-button button-edit" href="/assets/{{a.id}}/edit" title="{{ t('common.edit', 'Bearbeiten') }}" aria-label="{{ t('common.edit', 'Bearbeiten') }}">&#9998;</a>
      <a class="icon-button button-duplicate" href="/assets/{{a.id}}/duplicate" title="{{ t('common.duplicate', 'Duplizieren') }}">⧉</a><form class="inline-form" method="post" action="/assets/{{a.id}}/delete" onsubmit="return confirm({{ t('assets.delete_confirm').format(name=a.name)|tojson }});"><button class="icon-button button-danger" title="{{ t('common.delete', 'Löschen') }}">🗑</button></form>{% else %}<span class="muted">{{ t('common.read_only', 'Nur lesen') }}</span>{% endif %}</td>
  </tr>{% else %}<tr><td colspan="40" class="empty-state">{{ t('assets.none', 'Keine Assets vorhanden.') }}</td></tr>{% endfor %}
  </tbody>
</table></div>

<p class="muted asset-total">{{ t('assets.total', '{count} Assets').format(count=total_assets) }}</p>



{% if is_admin %}
<dialog id="job-filter-dialog" class="duplicate-search-dialog">
  <form method="get" action="/assets" id="job-filter-form">
    <div class="dialog-heading">
      <h2>{{ t('assets.job_filter.title', 'Assets nach Jobstatus filtern') }}</h2>
      <button type="button" onclick="this.closest('dialog').close()" class="icon-button button-secondary" aria-label="{{ t('common.close', 'Schließen') }}">×</button>
    </div>
    <p class="muted">{{ t('assets.job_filter.help', 'Die Jobinformationen werden nur bei aktivem Filter geladen. Die normale Assetliste bleibt unverändert schnell.') }}</p>
    {% if selected %}<input type="hidden" name="category_id" value="{{ selected.id }}">{% endif %}
    <fieldset class="job-filter-status-fieldset">
      <legend>{{ t('assets.job_filter.jobs', 'Jobs') }}</legend>
      <p class="muted">{{ t('assets.job_filter.jobs_help', 'Mehrere Jobs werden als alternative Ausführungen zusammengefasst. Maßgeblich ist der zuletzt ausgeführte ausgewählte Job.') }}</p>
      <div class="job-filter-job-options">
        <label class="checkbox-label">
          <input type="checkbox" class="checkbox job-filter-job-checkbox" name="job_filter" value="software_inventory"{% if job_filter and 'software_inventory' in job_filter.selections %} checked{% endif %}>
          {{ t('jobs.type.software_inventory', 'Softwareinventur') }}
        </label>
        {% for definition in job_filter_definitions %}
        <label class="checkbox-label">
          <input type="checkbox" class="checkbox job-filter-job-checkbox" name="job_filter" value="definition:{{ definition.id }}" data-revision="{{ definition.revision }}"{% if job_filter and 'definition:' ~ definition.id in job_filter.selections %} checked{% endif %}>
          {{ definition.name }}
        </label>
        {% endfor %}
      </div>
    </fieldset>
    <fieldset class="job-filter-status-fieldset">
      <legend>{{ t('assets.job_filter.excluded_statuses', 'Auszuschließende Status') }}</legend>
      <p class="muted">{{ t('assets.job_filter.excluded_help', 'Markierte Status werden nicht in der Assetliste angezeigt. Ohne Markierung werden alle Assets angezeigt.') }}</p>
      <div class="job-filter-status-options">
        {% for status in job_filter_statuses %}
        <label class="checkbox-label">
          <input type="checkbox" class="checkbox job-filter-status-checkbox" name="job_filter_exclude" value="{{ status }}"{% if job_filter and status in job_filter.excluded_statuses %} checked{% endif %}>
          {{ t('software.status.' ~ status, status) }}
        </label>
        {% endfor %}
      </div>
      <div class="form-actions job-filter-status-actions">
        <button type="button" class="button button-secondary button-small" id="job-filter-exclude-all">{{ t('assets.job_filter.exclude_all', 'Alle markieren') }}</button>
        <button type="button" class="button button-secondary button-small" id="job-filter-exclude-none">{{ t('assets.job_filter.exclude_none', 'Auswahl aufheben') }}</button>
      </div>
    </fieldset>
    <label class="checkbox-label" id="job-filter-revision-label">
      <input type="checkbox" class="checkbox" id="job-filter-current-revision"{% if job_filter and job_filter.revision_mode == 'current' %} checked{% endif %}>
      {{ t('assets.job_filter.current_revision', 'nur aktuelle Revision') }}
      <input type="hidden" name="job_filter_revision" id="job-filter-revision" value="{{ job_filter.revision_mode if job_filter else 'all' }}">
    </label>
    <div class="form-actions">
      <a class="button button-secondary" href="/assets{% if selected %}?category_id={{ selected.id }}{% endif %}">{{ t('assets.job_filter.clear', 'Jobfilter entfernen') }}</a>
      <button type="submit" class="button">{{ t('assets.job_filter.apply', 'Filter anwenden') }}</button>
    </div>
  </form>
</dialog>
{% endif %}

<dialog id="duplicate-search-dialog" class="duplicate-search-dialog" data-result-summary-template="{{ t('duplicates.result_summary', '{records} Datensätze in {groups} Dublettengruppen gefunden.')|e }}">
  <form method="dialog">
    <div class="dialog-heading">
      <h2>{{ t('duplicates.search_title') }}</h2>
      <button type="submit" class="icon-button button-secondary" aria-label="{{ t('common.close', 'Schließen') }}">×</button>
    </div>
    <p class="muted">{{ t('duplicates.visible_only_help') }}</p>
    <label>{{ t('common.field') }}
      <select id="duplicate-field-select"></select>
    </label>
    <label class="checkbox-label">
      <input type="checkbox" class="checkbox" id="duplicate-ignore-empty" checked>
      {{ t('duplicates.ignore_empty') }}
    </label>
    <label class="checkbox-label">
      <input type="checkbox" class="checkbox" id="duplicate-case-insensitive" checked>
      {{ t('duplicates.ignore_case') }}
    </label>
    <div id="duplicate-search-summary" class="notice" hidden></div>
    <div class="form-actions">
      <button type="button" class="button button-secondary" id="duplicate-clear-button">{{ t('duplicates.clear_filter') }}</button>
      <button type="button" class="button" id="duplicate-apply-button">{{ t('duplicates.show') }}</button>
      <button type="submit" class="button button-secondary">{{ t('common.close', 'Schließen') }}</button>
    </div>
  </form>
</dialog>

<dialog id="column-settings-dialog" class="column-settings-dialog">
  <form method="dialog">
    <div class="dialog-heading">
      <h2>{{ t('assets.columns', 'Spalten') }}</h2>
      <button type="submit" class="icon-button button-secondary" aria-label="{{ t('common.close', 'Schließen') }}">×</button>
    </div>
    <p class="muted">{{ t('assets.columns_hint', 'Spalten ein- oder ausblenden. Die Auswahl wird in diesem Browser gespeichert.') }}</p>
    <div id="column-settings-list" class="column-settings-list"></div>
    <div class="form-actions">
      <button type="button" class="button button-secondary" id="show-all-columns">{{ t('assets.show_all_columns', 'Alle einblenden') }}</button>
      <button type="button" class="button button-secondary" id="reset-columns">{{ t('assets.reset_columns', 'Zurücksetzen') }}</button>
      <button type="submit" class="button">{{ t('common.close', 'Schließen') }}</button>
    </div>
  </form>
</dialog>
<div id="column-context-menu" class="column-context-menu" hidden>
  <button type="button" id="context-hide-column">{{ t('assets.hide_column', 'Spalte ausblenden') }}</button>
  <button type="button" id="context-open-columns">{{ t('assets.manage_columns', 'Spalten verwalten…') }}</button>
</div>

{% if is_admin %}
<script>
(() => {
  const dialog = document.getElementById('job-filter-dialog');
  const openButton = document.getElementById('job-filter-button');
  const revisionLabel = document.getElementById('job-filter-revision-label');
  const revisionCheckbox = document.getElementById('job-filter-current-revision');
  const revisionValue = document.getElementById('job-filter-revision');
  const form = document.getElementById('job-filter-form');
  const statusCheckboxes = () => [...document.querySelectorAll('.job-filter-status-checkbox')];
  const jobCheckboxes = () => [...document.querySelectorAll('.job-filter-job-checkbox')];
  const refreshFields = () => {
    const hasDefinition = jobCheckboxes().some(box => box.checked && String(box.value).startsWith('definition:'));
    if (revisionLabel) revisionLabel.hidden = !hasDefinition;
    if (revisionValue) revisionValue.value = revisionCheckbox?.checked && hasDefinition ? 'current' : 'all';
  };
  openButton?.addEventListener('click', () => { refreshFields(); dialog?.showModal(); });
  jobCheckboxes().forEach(box => box.addEventListener('change', refreshFields));
  revisionCheckbox?.addEventListener('change', refreshFields);
  form?.addEventListener('submit', event => {
    refreshFields();
    if (!jobCheckboxes().some(box => box.checked)) {
      event.preventDefault();
      window.alert({{ t('assets.job_filter.select_at_least_one', 'Bitte mindestens einen Job auswählen.')|tojson }});
    }
  });
  document.getElementById('job-filter-exclude-all')?.addEventListener('click', () => statusCheckboxes().forEach(box => { box.checked = true; }));
  document.getElementById('job-filter-exclude-none')?.addEventListener('click', () => statusCheckboxes().forEach(box => { box.checked = false; }));
  refreshFields();

  const startButton = document.getElementById('start-filtered-job-button');
  const startForm = document.getElementById('start-filtered-job-form');
  const startSelection = document.getElementById('start-filtered-job-selection');
  const startValue = document.getElementById('start-filtered-job-value');
  startButton?.addEventListener('click', () => {
    if (startValue && startSelection) startValue.value = startSelection.value;
    const template = {{ t('assets.job_filter.start_confirm', 'Den ausgewählten Job für alle {count} gefilterten Assets erstellen und starten?')|tojson }};
    const message = template.replace('{count}', {{ total_assets|tojson }});
    if (window.confirm(message)) startForm?.requestSubmit();
  });
})();
</script>
{% endif %}

<script>
(() => {
  const table = document.getElementById('asset-table');
  if (!table) return;
  const key = table.dataset.storageKey;
  const movableHeaders = () => [...table.querySelectorAll('thead tr:first-child th[data-field]')];
  const fields = () => movableHeaders().map(th => th.dataset.field);
  function applyOrder(order) {
    if (!Array.isArray(order)) return;
    const current = fields();
    const valid = order.filter(f => current.includes(f));
    current.forEach(f => { if (!valid.includes(f)) valid.push(f); });
    const headerRow = table.tHead.rows[0];
    const headerActions = headerRow.lastElementChild;
    const headerFragment = document.createDocumentFragment();
    valid.forEach(field => {
      const header = headerRow.querySelector(`th[data-field="${CSS.escape(field)}"]`);
      if (header) headerFragment.appendChild(header);
    });
    headerRow.insertBefore(headerFragment, headerActions);

    [...table.tBodies[0].rows].forEach(row => {
      const actions = row.lastElementChild;
      const fragment = document.createDocumentFragment();
      valid.forEach(field => {
        const cell = row.querySelector(`td[data-field="${CSS.escape(field)}"]`);
        if (cell) fragment.appendChild(cell);
      });
      row.insertBefore(fragment, actions);
    });
  }
  try { applyOrder(JSON.parse(window.AssetBrowserState?.get(key) || 'null')); } catch (_) {}
  table.dataset.columnOrderReady = '1';
  window.dispatchEvent(new Event('asset-table-column-order-ready'));
  let dragged = null;
  movableHeaders().forEach(th => {
    th.addEventListener('dragstart', () => { dragged = th.dataset.field; th.classList.add('dragging'); });
    th.addEventListener('dragend', () => { th.classList.remove('dragging'); dragged = null; });
    th.addEventListener('dragover', e => e.preventDefault());
    th.addEventListener('drop', e => {
      e.preventDefault();
      const target = th.dataset.field;
      if (!dragged || dragged === target) return;
      const order = fields();
      order.splice(order.indexOf(dragged), 1);
      order.splice(order.indexOf(target), 0, dragged);
      applyOrder(order);
      window.AssetBrowserState?.set(key, JSON.stringify(order));
      window.dispatchEvent(new Event('asset-table-columns-changed'));
    });
  });
})();
</script>
{% if is_admin %}
<script>
(() => {
  const table = document.getElementById('asset-table');
  if (!table) return;
  const boxes = () => [...table.querySelectorAll('.asset-select')];
  const all = document.getElementById('select-all-assets');
  const editButton = document.getElementById('bulk-edit-button');
  const deleteButton = document.getElementById('bulk-delete-button');
  const mergeButton = document.getElementById('merge-duplicates-button');
  const softwareCompareButton = document.getElementById('software-compare-button');
  const jobTypeSelect = document.getElementById('bulk-job-type');
  const jobStartButton = document.getElementById('bulk-job-start-button');
  const isVisible = box => {
    const row = box.closest('tr');
    if (!row || row.hidden || row.classList.contains('hidden')) return false;
    return true;
  };
  const visibleBoxes = () => boxes().filter(isVisible);
  const selected = () => visibleBoxes().filter(box => box.checked).map(box => box.value);
  function selectedPlatforms() {
    return [...new Set(visibleBoxes().filter(box => box.checked).map(box => box.closest('tr')?.dataset.platform || 'unknown'))];
  }
  function filterJobDefinitions() {
    if (!jobTypeSelect) return;
    const platforms = selectedPlatforms();
    const singlePlatform = platforms.length === 1 && platforms[0] !== 'unknown' ? platforms[0] : null;
    [...jobTypeSelect.options].forEach((option, index) => {
      if (index === 0) { option.hidden = false; option.disabled = false; return; }
      const platform = option.dataset.platform || 'all';
      const compatible = !singlePlatform || platform === 'all' || platform === singlePlatform;
      option.hidden = !compatible;
      option.disabled = !compatible;
    });
    if (jobTypeSelect.selectedOptions[0]?.disabled) jobTypeSelect.value = '';
  }
  function refresh() {
    // Hidden rows must never remain part of a bulk operation after filtering.
    boxes().filter(box => !isVisible(box)).forEach(box => { box.checked = false; });
    const visible = visibleBoxes();
    const count = selected().length;
    if (editButton) { editButton.disabled = count === 0; editButton.textContent = count ? `✏ {{ t('assets.bulk_edit', 'Daten ändern') }} (${count})` : '✏ {{ t('assets.bulk_edit', 'Daten ändern') }}'; }
    if (deleteButton) { deleteButton.disabled = count === 0; deleteButton.textContent = count ? `🗑 {{ t('assets.delete_selected', 'Markierte löschen') }} (${count})` : '🗑 {{ t('assets.delete_selected', 'Markierte löschen') }}'; }
    if (mergeButton) {
      mergeButton.disabled = count < 2;
      mergeButton.textContent = count >= 2 ? `⧉ {{ t('duplicates.merge') }} (${count})` : '⧉ {{ t('duplicates.merge') }}';
    }
    if (softwareCompareButton) {
      softwareCompareButton.disabled = count !== 2;
      softwareCompareButton.textContent = count === 2 ? `⇄ {{ t('software.compare.button', 'Software vergleichen') }} (2)` : '⇄ {{ t('software.compare.button', 'Software vergleichen') }}';
    }
    if (jobTypeSelect) { jobTypeSelect.disabled = count === 0; filterJobDefinitions(); }
    if (jobStartButton) {
      jobStartButton.disabled = count === 0 || !jobTypeSelect?.value;
      jobStartButton.textContent = count
        ? `▶ {{ t('jobs.start') }} (${count})`
        : '▶ {{ t('jobs.start') }}';
    }
    if (all) { all.checked = visible.length > 0 && count === visible.length; all.indeterminate = count > 0 && count < visible.length; }
  }
  function submit(formId, containerId) {
    const form = document.getElementById(formId);
    const container = document.getElementById(containerId);
    container.innerHTML = '';
    selected().forEach(id => { const input = document.createElement('input'); input.type='hidden'; input.name='asset_ids'; input.value=id; container.appendChild(input); });
    form.requestSubmit();
  }
  table.addEventListener('change', event => {
    if (event.target.matches('.asset-select')) refresh();
  });
  all?.addEventListener('change', () => { visibleBoxes().forEach(box => box.checked = all.checked); boxes().filter(box => !isVisible(box)).forEach(box => box.checked = false); refresh(); });
  editButton?.addEventListener('click', () => submit('bulk-edit-form', 'bulk-edit-ids'));
  deleteButton?.addEventListener('click', () => submit('bulk-delete-form', 'bulk-delete-ids'));
  mergeButton?.addEventListener('click', () => submit('merge-duplicates-form', 'merge-duplicate-ids'));
  softwareCompareButton?.addEventListener('click', () => submit('software-compare-form', 'software-compare-ids'));
  jobTypeSelect?.addEventListener('change', refresh);
  jobStartButton?.addEventListener('click', () => {
    const count = selected().length;
    const jobType = jobTypeSelect?.value || '';
    if (!count || !jobType) return;

    const label = jobTypeSelect?.selectedOptions[0]?.textContent?.trim() || jobType;
    const template = {{ t('jobs.bulk.confirm', 'Für {count} markierte Assets jeweils den Job „{job}“ erstellen und starten?')|tojson }};
    const message = template.replace('{count}', String(count)).replace('{job}', label);
    if (!window.confirm(message)) return;
    const jobTypeValue = document.getElementById('bulk-job-type-value');
    const definitionValue = document.getElementById('bulk-job-definition-value');
    if (jobType.startsWith('definition:')) {
      if (jobTypeValue) jobTypeValue.value = 'job_definition';
      if (definitionValue) definitionValue.value = jobType.split(':', 2)[1];
    } else {
      if (jobTypeValue) jobTypeValue.value = jobType;
      if (definitionValue) definitionValue.value = '';
    }
    submit('bulk-job-form', 'bulk-job-ids');
  });
  // Table filters may hide rows by style, class or hidden attribute. Observe all
  // such changes and keep the selection restricted to currently displayed rows.
  const observer = new MutationObserver(refresh);
  const observeRows = () => table.querySelectorAll('tbody tr').forEach(row => observer.observe(row, {attributes:true, attributeFilter:['style','class','hidden']}));
  observeRows();
  window.addEventListener('asset-table-content-reloaded', () => {
    all.checked = false;
    all.indeterminate = false;
    observeRows();
    refresh();
  });
  table.addEventListener('input', () => setTimeout(refresh, 0));
  table.addEventListener('change', () => setTimeout(refresh, 0));
  window.addEventListener('asset-table-filter-changed', refresh);
  refresh();
})();
</script>
{% endif %}
{% if is_admin %}<dialog id="issue-dialog"><form method="post" id="issue-form"><input type="hidden" name="return_to" id="issue-return-to"><h2>{{ t('assets.issue_asset') }}</h2><label>{{ t('common.name') }}<input name="assigned_to" required></label><label>{{ t('common.date') }}<input type="date" name="assigned_on" value="{{ now().strftime('%Y-%m-%d') if now is defined else '' }}" required></label><div class="form-actions"><button type="button" onclick="this.closest('dialog').close()">{{ t('common.cancel') }}</button><button type="submit">↗ {{ t('assets.issue') }}</button></div></form></dialog>{% endif %}<script src="/static/js/issue-dialog.js"></script>

<script>
(() => {
  const btn = document.getElementById('reload-assets-button');
  if (!btn) return;
  btn.addEventListener('click', async () => {
    const tbody = document.querySelector('#asset-table tbody');
    if (!tbody) return;
    btn.disabled = true;
    const old = btn.textContent;
    btn.textContent = btn.dataset.loadingLabel || '⏳ {{ t('common.loading') }}';
    try {
      const response = await fetch(window.location.href, {
        headers: {'X-Requested-With': 'XMLHttpRequest'}
      });
      const html = await response.text();
      const doc = new DOMParser().parseFromString(html, 'text/html');
      const newBody = doc.querySelector('#asset-table tbody');
      const count = doc.querySelector('.asset-total');
      if (newBody) {
        const table = document.getElementById('asset-table');

        if (table) {
          const orderedFields = [...table.tHead.rows[0].cells]
            .map(cell => cell.dataset.field)
            .filter(Boolean);

          [...newBody.rows].forEach(row => {
            const actions = row.lastElementChild;
            const fragment = document.createDocumentFragment();

            orderedFields.forEach(field => {
              const cell = row.querySelector(`td[data-field="${CSS.escape(field)}"]`);
              if (cell) fragment.appendChild(cell);
            });

            if (actions) row.insertBefore(fragment, actions);
          });
        }

        // Keep the existing tbody element. Table filters, sorting and delegated
        // event handlers are bound to it and would otherwise still reference the
        // detached old body after an AJAX reload.
        tbody.replaceChildren(...newBody.childNodes);
        table?.assetRebuildFilterCache?.();

        window.dispatchEvent(new CustomEvent('asset-table-content-reloaded', {
          detail: { table }
        }));
        window.dispatchEvent(new Event('asset-table-columns-changed'));
      }
      if (count) {
        const current = document.querySelector('.asset-total');
        if (current) current.replaceWith(count);
      }
    } finally {
      btn.disabled = false;
      btn.textContent = old;
    }
  });
})();
</script>

</div>
<script>requestAnimationFrame(()=>requestAnimationFrame(()=>document.getElementById('asset-list-page')?.classList.remove('asset-list-initializing')));</script>
{% endblock %}
