Shopfloor PC type system, webapp enhancements, slim Blancco GRUB
- Shopfloor PC type menu (CMM, WaxAndTrace, Keyence, Genspect, Display, Standard) - Baseline scripts: OpenText CSF, Start Menu shortcuts, network/WinRM, power/display - Standard type: eDNC + MarkZebra with 64-bit path mirroring - CMM type: Hexagon CLM Tools, PC-DMIS 2016/2019 R2 - Display sub-type: Lobby vs Dashboard - Webapp: enrollment management, image config editor, UI refresh - Upload-Image.ps1: robocopy MCL cache to PXE server - Download-Drivers.ps1: Dell driver download pipeline - Slim Blancco GRUB EFI (10MB -> 660KB) for old hardware compat - Shopfloor display imaging guide docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
331
webapp/templates/image_config.html
Normal file
331
webapp/templates/image_config.html
Normal file
@@ -0,0 +1,331 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ friendly_name }} - Configuration{% endblock %}
|
||||
|
||||
{% block extra_head %}
|
||||
<style>
|
||||
.section-card { margin-bottom: 1.5rem; }
|
||||
.section-card .card-header { padding: 0.6rem 1rem; font-size: 0.95rem; }
|
||||
.badge-disk { font-size: 0.75rem; }
|
||||
.orphan-section { background-color: #fff8e1; }
|
||||
.config-table td, .config-table th { vertical-align: middle; }
|
||||
.config-table .form-control-sm { min-width: 120px; }
|
||||
.text-truncate-cell { max-width: 250px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1">{{ friendly_name }}</h2>
|
||||
<small class="text-muted">
|
||||
Image Configuration
|
||||
— OS Selection: <strong>{{ config.os_selection or 'Not set' }}</strong>
|
||||
</small>
|
||||
</div>
|
||||
<a href="{{ url_for('unattend_editor', image_type=image_type) }}" class="btn btn-outline-secondary btn-sm">
|
||||
Edit Unattend
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{# ==================== SECTION 1: Hardware Models ==================== #}
|
||||
<div class="card section-card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>Hardware Models
|
||||
<span class="badge bg-secondary ms-1">{{ config.hardware_models|length }}</span>
|
||||
</span>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" id="addHwModel">
|
||||
Add
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-success ms-1" id="saveHwModels">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<form method="POST" action="{{ url_for('image_config_save', image_type=image_type) }}" id="hwModelsForm">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="section" value="hardware_models">
|
||||
<input type="hidden" name="payload" id="hwModelsData" value="[]">
|
||||
</form>
|
||||
<table class="table table-sm table-hover mb-0 config-table" id="hwModelsTable">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width:40px">#</th>
|
||||
<th>Model</th>
|
||||
<th>Driver Family ID</th>
|
||||
<th style="width:90px">On Disk</th>
|
||||
<th style="width:60px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for hm in config.hardware_models %}
|
||||
<tr>
|
||||
<td class="order-num">{{ loop.index }}</td>
|
||||
<td><input type="text" class="form-control form-control-sm" data-field="Model" value="{{ hm.Model }}"></td>
|
||||
<td><input type="text" class="form-control form-control-sm" data-field="Id" value="{{ hm.Id }}"></td>
|
||||
<td>
|
||||
{% if hm._on_disk %}
|
||||
<span class="badge bg-success badge-disk">Yes</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger badge-disk">No</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-outline-danger btn-row-action remove-row">
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if not config.hardware_models %}
|
||||
<div class="text-center text-muted py-3 empty-message" id="hwModelsEmpty">
|
||||
No hardware models configured.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ==================== SECTION 2: Driver Packs ==================== #}
|
||||
<div class="card section-card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>Driver Packs
|
||||
<span class="badge bg-secondary ms-1">{{ config.drivers|length }}</span>
|
||||
</span>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-success" id="saveDrivers">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<form method="POST" action="{{ url_for('image_config_save', image_type=image_type) }}" id="driversForm">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="section" value="drivers">
|
||||
<input type="hidden" name="payload" id="driversData" value="[]">
|
||||
</form>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0 config-table" id="driversTable">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width:40px">#</th>
|
||||
<th>Family</th>
|
||||
<th>Models</th>
|
||||
<th>File Name</th>
|
||||
<th style="width:70px">OS IDs</th>
|
||||
<th style="width:90px">On Disk</th>
|
||||
<th style="width:60px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for drv in config.drivers %}
|
||||
<tr data-json='{{ drv | tojson }}'>
|
||||
<td class="order-num">{{ loop.index }}</td>
|
||||
<td class="text-truncate-cell" title="{{ drv.family }}">{{ drv.family }}</td>
|
||||
<td class="text-truncate-cell" title="{{ drv.models }}">{{ drv.models }}</td>
|
||||
<td class="text-truncate-cell" title="{{ drv.FileName or drv.get('fileName','') }}">
|
||||
<small>{{ drv.FileName or drv.get('fileName','') }}</small>
|
||||
</td>
|
||||
<td><small>{{ drv.osId }}</small></td>
|
||||
<td>
|
||||
{% if drv._on_disk %}
|
||||
<span class="badge bg-success badge-disk">Yes</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger badge-disk">No</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-outline-danger btn-row-action remove-row">
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% if not config.drivers %}
|
||||
<div class="text-center text-muted py-3 empty-message" id="driversEmpty">
|
||||
No driver packs configured.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Orphan drivers sub-section #}
|
||||
{% if config.orphan_drivers %}
|
||||
<div class="card-footer orphan-section p-0">
|
||||
<div class="px-3 py-2">
|
||||
<a class="text-decoration-none" data-bs-toggle="collapse" href="#orphanDrivers" role="button">
|
||||
<strong>Unregistered Drivers ({{ config.orphan_drivers|length }})</strong>
|
||||
<small class="text-muted ms-1">zip files on disk not in any JSON</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse" id="orphanDrivers">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>File Name</th>
|
||||
<th>Relative Path</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for orph in config.orphan_drivers %}
|
||||
<tr>
|
||||
<td><small>{{ orph.fileName }}</small></td>
|
||||
<td><small class="text-muted">{{ orph.relPath }}</small></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# ==================== SECTION 3: Operating Systems ==================== #}
|
||||
<div class="card section-card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>Operating Systems
|
||||
<span class="badge bg-secondary ms-1">{{ config.operating_systems|length }}</span>
|
||||
</span>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-success" id="saveOs">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<form method="POST" action="{{ url_for('image_config_save', image_type=image_type) }}" id="osForm">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="section" value="operating_systems">
|
||||
<input type="hidden" name="payload" id="osData" value="[]">
|
||||
</form>
|
||||
<table class="table table-sm table-hover mb-0 config-table" id="osTable">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width:40px">#</th>
|
||||
<th>Product Name</th>
|
||||
<th>Version</th>
|
||||
<th>Build</th>
|
||||
<th style="width:60px">ID</th>
|
||||
<th style="width:70px">Active</th>
|
||||
<th style="width:90px">WIM On Disk</th>
|
||||
<th style="width:60px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in config.operating_systems %}
|
||||
{% set osv = entry.operatingSystemVersion %}
|
||||
<tr data-json='{{ entry | tojson }}'>
|
||||
<td class="order-num">{{ loop.index }}</td>
|
||||
<td>{{ osv.productName }}</td>
|
||||
<td>{{ osv.versionNumber }}</td>
|
||||
<td>{{ osv.buildNumber }}</td>
|
||||
<td>{{ osv.id }}</td>
|
||||
<td>
|
||||
{% if osv.isActive %}
|
||||
<span class="badge bg-success badge-disk">Active</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary badge-disk">Inactive</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if entry._on_disk %}
|
||||
<span class="badge bg-success badge-disk">Yes</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger badge-disk">No</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-outline-danger btn-row-action remove-row">
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if not config.operating_systems %}
|
||||
<div class="text-center text-muted py-3 empty-message" id="osEmpty">
|
||||
No operating systems configured.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ==================== SECTION 4: Packages ==================== #}
|
||||
<div class="card section-card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>Packages
|
||||
<span class="badge bg-secondary ms-1">{{ config.packages|length }}</span>
|
||||
</span>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-success" id="savePackages">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<form method="POST" action="{{ url_for('image_config_save', image_type=image_type) }}" id="packagesForm">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="section" value="packages">
|
||||
<input type="hidden" name="payload" id="packagesData" value="[]">
|
||||
</form>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0 config-table" id="packagesTable">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width:40px">#</th>
|
||||
<th>Name</th>
|
||||
<th>Comment</th>
|
||||
<th>File</th>
|
||||
<th style="width:70px">OS IDs</th>
|
||||
<th style="width:80px">Enabled</th>
|
||||
<th style="width:90px">On Disk</th>
|
||||
<th style="width:60px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for pkg in config.packages %}
|
||||
<tr data-json='{{ pkg | tojson }}'>
|
||||
<td class="order-num">{{ loop.index }}</td>
|
||||
<td class="text-truncate-cell" title="{{ pkg.name }}"><small>{{ pkg.name }}</small></td>
|
||||
<td class="text-truncate-cell" title="{{ pkg.comment }}"><small>{{ pkg.comment }}</small></td>
|
||||
<td class="text-truncate-cell" title="{{ pkg.fileName or pkg.get('FileName','') }}">
|
||||
<small>{{ pkg.fileName or pkg.get('FileName','') }}</small>
|
||||
</td>
|
||||
<td><small>{{ pkg.osId }}</small></td>
|
||||
<td>
|
||||
{% if pkg.enabled %}
|
||||
<span class="badge bg-success badge-disk">Yes</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary badge-disk">No</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if pkg._on_disk %}
|
||||
<span class="badge bg-success badge-disk">Yes</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger badge-disk">No</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-outline-danger btn-row-action remove-row">
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% if not config.packages %}
|
||||
<div class="text-center text-muted py-3 empty-message" id="packagesEmpty">
|
||||
No packages configured.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user