Files
lego-instructions-manager/app/templates/sets/detail.html

416 lines
23 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ set.set_number }}: {{ set.set_name }} - {{ app_name }}{% endblock %}
{% block content %}
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('main.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('sets.list_sets') }}">Sets</a></li>
<li class="breadcrumb-item active">{{ set.set_number }}</li>
</ol>
</nav>
<div class="row">
<!-- Set Image and Details -->
<div class="col-lg-4 mb-4">
<div class="card shadow-sm">
{% if set.cover_image %}
<img src="{{ url_for('static', filename='uploads/' + set.cover_image.replace('\\', '/')) }}"
class="card-img-top" alt="{{ set.set_name }}"
style="max-height: 400px; object-fit: contain; background-color: #f8f9fa; padding: 20px;">
{% elif set.image_url %}
<img src="{{ set.image_url }}" class="card-img-top" alt="{{ set.set_name }}"
style="max-height: 400px; object-fit: contain; background-color: #f8f9fa; padding: 20px;">
{% else %}
<div class="d-flex align-items-center justify-content-center bg-light" style="height: 400px;">
<i class="bi bi-image display-1 text-muted"></i>
</div>
{% endif %}
<div class="card-body">
<h5 class="card-title">
{{ set.set_number }}
{% if set.is_moc %}
<span class="badge bg-warning text-dark">
<i class="bi bi-star-fill"></i> MOC
</span>
{% endif %}
</h5>
<h6 class="card-subtitle mb-3 text-muted">{{ set.set_name }}</h6>
<table class="table table-sm table-borderless">
<tr>
<th width="40%">Theme:</th>
<td><span class="badge bg-primary">{{ set.theme }}</span></td>
</tr>
<tr>
<th>Year:</th>
<td><span class="badge bg-warning text-dark">{{ set.year_released }}</span></td>
</tr>
{% if set.is_moc %}
<tr>
<th>Type:</th>
<td><span class="badge bg-info">My Own Creation</span></td>
</tr>
{% if set.moc_designer %}
<tr>
<th>Designer:</th>
<td>{{ set.moc_designer }}</td>
</tr>
{% endif %}
{% endif %}
{% if set.piece_count %}
<tr>
<th>Pieces:</th>
<td>{{ set.piece_count }}</td>
</tr>
{% endif %}
<tr>
<th>Instructions:</th>
<td>{{ set.instructions.count() }} file(s)</td>
</tr>
<tr>
<th>Added:</th>
<td>{{ set.created_at.strftime('%b %d, %Y') }}</td>
</tr>
</table>
</div>
<div class="card-footer bg-transparent">
<div class="d-grid gap-2">
<a href="{{ url_for('instructions.upload', set_id=set.id) }}" class="btn btn-success">
<i class="bi bi-cloud-upload"></i> Upload Instructions
</a>
<a href="{{ url_for('sets.edit_set', set_id=set.id) }}" class="btn btn-outline-secondary">
<i class="bi bi-pencil"></i> Edit Set Details
</a>
<form method="POST" action="{{ url_for('sets.delete_set', set_id=set.id) }}"
onsubmit="return confirm('Are you sure you want to delete this set and all its instructions?');">
<button type="submit" class="btn btn-outline-danger w-100">
<i class="bi bi-trash"></i> Delete Set
</button>
</form>
</div>
</div>
</div>
</div>
<!-- Instructions -->
<div class="col-lg-8">
<div class="card shadow-sm mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="bi bi-file-pdf"></i> Instructions</h5>
<a href="{{ url_for('instructions.upload', set_id=set.id) }}" class="btn btn-sm btn-success">
<i class="bi bi-plus-circle"></i> Upload
</a>
</div>
<div class="card-body">
{% if pdf_instructions or image_instructions %}
<!-- PDF Instructions (Books) -->
{% if pdf_instructions %}
<h6 class="mb-3"><i class="bi bi-book-fill text-danger"></i> PDF Instruction Books</h6>
<div class="row mb-4">
{% for instruction in pdf_instructions %}
<div class="col-md-6 col-lg-4 mb-3">
<div class="card h-100">
{% if instruction.thumbnail_path %}
<a href="{{ url_for('instructions.view', instruction_id=instruction.id) }}" target="_blank">
<img src="{{ url_for('static', filename='uploads/' + instruction.thumbnail_path.replace('\\', '/')) }}"
class="card-img-top"
alt="{{ instruction.file_name }}"
style="height: 200px; object-fit: contain; background-color: #f8f9fa; padding: 10px;">
</a>
{% else %}
<a href="{{ url_for('instructions.view', instruction_id=instruction.id) }}" target="_blank">
<div class="card-img-top d-flex align-items-center justify-content-center bg-light"
style="height: 200px;">
<i class="bi bi-file-pdf display-1 text-danger"></i>
</div>
</a>
{% endif %}
<div class="card-body">
<h6 class="card-title">{{ instruction.file_name }}</h6>
<p class="card-text small text-muted">
<i class="bi bi-file-pdf"></i> {{ instruction.file_size_mb }} MB<br>
<i class="bi bi-calendar"></i> {{ instruction.uploaded_at.strftime('%b %d, %Y') }}
</p>
</div>
<div class="card-footer bg-transparent">
<div class="d-flex gap-2">
<a href="{{ url_for('instructions.view', instruction_id=instruction.id) }}"
class="btn btn-sm btn-primary flex-fill" target="_blank">
<i class="bi bi-eye"></i> Open
</a>
<form method="POST" action="{{ url_for('instructions.delete', instruction_id=instruction.id) }}"
onsubmit="return confirm('Delete this PDF?');" class="flex-fill">
<button type="submit" class="btn btn-sm btn-outline-danger w-100">
<i class="bi bi-trash"></i>
</button>
</form>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<!-- Image Instructions (Single Card) -->
{% if image_instructions %}
<h6 class="mb-3"><i class="bi bi-images text-primary"></i> Scanned Instructions</h6>
<div class="row">
<div class="col-md-6 col-lg-4 mb-3">
<div class="card h-100">
{% set first_image = image_instructions[0] %}
<a href="{{ url_for('instructions.image_viewer', set_id=set.id) }}">
<img src="{{ url_for('static', filename='uploads/' + first_image.file_path.replace('\\', '/')) }}"
class="card-img-top"
alt="Instructions Preview"
style="height: 200px; object-fit: contain; background-color: #f8f9fa; padding: 10px; cursor: pointer;">
</a>
<div class="card-body">
<h6 class="card-title">
<i class="bi bi-file-image"></i> Image Instructions
</h6>
<p class="card-text small text-muted">
<i class="bi bi-files"></i> {{ image_instructions|length }} page(s)<br>
<i class="bi bi-calendar"></i> Uploaded {{ first_image.uploaded_at.strftime('%b %d, %Y') }}
</p>
</div>
<div class="card-footer bg-transparent">
<div class="d-flex gap-2">
<a href="{{ url_for('instructions.image_viewer', set_id=set.id) }}"
class="btn btn-sm btn-primary flex-fill">
<i class="bi bi-book-half"></i> View Instructions
</a>
<button type="button" class="btn btn-sm btn-outline-danger flex-fill"
data-bs-toggle="modal" data-bs-target="#deleteImagesModal">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Delete All Images Modal -->
<div class="modal fade" id="deleteImagesModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete All Image Instructions?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>This will delete all {{ image_instructions|length }} image instruction pages.</p>
<p class="text-danger"><strong>This cannot be undone!</strong></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form method="POST" action="{{ url_for('instructions.delete_all_images', set_id=set.id) }}" style="display: inline;">
<button type="submit" class="btn btn-danger">Delete All Images</button>
</form>
</div>
</div>
</div>
</div>
{% endif %}
{% else %}
<div class="text-center py-5">
<i class="bi bi-file-earmark-x display-1 text-muted"></i>
<h5 class="mt-3">No Instructions Yet</h5>
<p class="text-muted">Upload PDF or image files to get started.</p>
<a href="{{ url_for('instructions.upload', set_id=set.id) }}" class="btn btn-success">
<i class="bi bi-cloud-upload"></i> Upload Instructions
</a>
</div>
{% endif %}
</div>
</div>
<!-- Extra Files Section -->
<div class="card shadow-sm mt-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="bi bi-file-earmark-plus"></i> Extra Files</h5>
<a href="{{ url_for('extra_files.upload', set_id=set.id) }}" class="btn btn-sm btn-success">
<i class="bi bi-cloud-upload"></i> Upload Files
</a>
</div>
<div class="card-body">
{% set extra_files_list = set.extra_files.all() %}
{% if extra_files_list %}
<!-- Files Grid -->
<div class="row g-3">
{% for file in extra_files_list %}
<div class="col-md-6 col-lg-4">
<div class="card h-100 border-light">
<!-- Preview for images -->
{% if file.is_image %}
<a href="{{ url_for('extra_files.preview', file_id=file.id) }}" target="_blank">
<img src="{{ url_for('extra_files.preview', file_id=file.id) }}"
class="card-img-top"
alt="{{ file.original_filename }}"
style="height: 150px; object-fit: cover; cursor: pointer;">
</a>
{% else %}
<div class="card-img-top bg-light d-flex align-items-center justify-content-center"
style="height: 150px;">
<i class="bi bi-{{ file.file_icon }} display-3 text-muted"></i>
</div>
{% endif %}
<div class="card-body p-2">
<h6 class="card-title small mb-1">
<i class="bi bi-{{ file.file_icon }}"></i>
{{ file.original_filename }}
</h6>
{% if file.category and file.category != 'other' %}
<span class="badge bg-info text-dark small mb-1">
{{ file.category|replace('_', ' ')|title }}
</span>
{% endif %}
<p class="card-text small text-muted mb-1">
<i class="bi bi-hdd"></i> {{ file.file_size_formatted }}
<br>
<i class="bi bi-calendar"></i> {{ file.uploaded_at.strftime('%b %d, %Y') }}
</p>
{% if file.description %}
<p class="card-text small text-muted mb-1">
{{ file.description|truncate(60) }}
</p>
{% endif %}
</div>
<div class="card-footer bg-transparent p-2">
<div class="d-flex gap-1">
{% if file.can_preview %}
<a href="{{ url_for('extra_files.preview', file_id=file.id) }}"
target="_blank"
class="btn btn-sm btn-outline-primary flex-fill"
title="Preview">
<i class="bi bi-eye"></i>
</a>
{% endif %}
<a href="{{ url_for('extra_files.download', file_id=file.id) }}"
class="btn btn-sm btn-outline-success flex-fill"
title="Download">
<i class="bi bi-download"></i>
</a>
<form method="POST"
action="{{ url_for('extra_files.delete', file_id=file.id) }}"
class="flex-fill"
onsubmit="return confirm('Delete {{ file.original_filename }}?');">
<button type="submit"
class="btn btn-sm btn-outline-danger w-100"
title="Delete">
<i class="bi bi-trash"></i>
</button>
</form>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-4">
<i class="bi bi-files display-3 text-muted"></i>
<p class="text-muted mt-2 mb-1">No extra files yet</p>
<p class="small text-muted">
Upload BrickLink XMLs, Stud.io files, box art, photos, or any other related files
</p>
<a href="{{ url_for('extra_files.upload', set_id=set.id) }}" class="btn btn-sm btn-success">
<i class="bi bi-cloud-upload"></i> Upload Files
</a>
</div>
{% endif %}
</div>
</div>
<!-- Set Information -->
<div class="card shadow-sm mt-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-info-circle"></i> Additional Information</h5>
</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-4">Set Number:</dt>
<dd class="col-sm-8">{{ set.set_number }}</dd>
<dt class="col-sm-4">Set Name:</dt>
<dd class="col-sm-8">{{ set.set_name }}</dd>
<dt class="col-sm-4">Theme:</dt>
<dd class="col-sm-8">{{ set.theme }}</dd>
<dt class="col-sm-4">Year Released:</dt>
<dd class="col-sm-8">{{ set.year_released }}</dd>
{% if set.piece_count %}
<dt class="col-sm-4">Piece Count:</dt>
<dd class="col-sm-8">{{ set.piece_count }} pieces</dd>
{% endif %}
{% if set.is_moc %}
<dt class="col-sm-4">Type:</dt>
<dd class="col-sm-8">
<span class="badge bg-warning text-dark">
<i class="bi bi-star-fill"></i> My Own Creation (MOC)
</span>
</dd>
{% if set.moc_designer %}
<dt class="col-sm-4">Designer:</dt>
<dd class="col-sm-8">{{ set.moc_designer }}</dd>
{% endif %}
{% if set.moc_description %}
<dt class="col-sm-4">Description:</dt>
<dd class="col-sm-8">{{ set.moc_description }}</dd>
{% endif %}
{% endif %}
{% if set.brickset_id %}
<dt class="col-sm-4">Brickset ID:</dt>
<dd class="col-sm-8">{{ set.brickset_id }}</dd>
{% endif %}
<dt class="col-sm-4">Added By:</dt>
<dd class="col-sm-8">{{ set.added_by.username }}</dd>
<dt class="col-sm-4">Date Added:</dt>
<dd class="col-sm-8">{{ set.created_at.strftime('%B %d, %Y at %I:%M %p') }}</dd>
<dt class="col-sm-4">Last Updated:</dt>
<dd class="col-sm-8">{{ set.updated_at.strftime('%B %d, %Y at %I:%M %p') }}</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="mt-4">
<a href="{{ url_for('sets.list_sets') }}" class="btn btn-secondary">
<i class="bi bi-arrow-left"></i> Back to Sets
</a>
</div>
{% endblock %}
{% block extra_css %}
<style>
.instruction-thumbnail {
cursor: pointer;
transition: transform 0.2s;
}
.instruction-thumbnail:hover {
transform: scale(1.05);
}
</style>
{% endblock %}