Add printable label sheet template
This commit is contained in:
1 parent
e36331e2d6
commit
88fdfe0169
1 file changed
+234
@@ -0,0 +1,234 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Parts labels - {{ layout.preset.name }}</title>
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
||||
|
||||
<style>
|
||||
@page {
|
||||
size: {{ layout.preset.page_size.width }}mm {{ layout.preset.page_size.height }}mm;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body { background: #e9ecef; font-family: 'Inter', Arial, sans-serif; margin: 0; }
|
||||
|
||||
.sheet {
|
||||
position: relative;
|
||||
width: {{ layout.preset.page_size.width }}mm;
|
||||
height: {{ layout.preset.page_size.height }}mm;
|
||||
margin: 10mm auto;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 14px rgba(0,0,0,.18);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.lbl {
|
||||
position: absolute;
|
||||
width: {{ layout.preset.label_width }}mm;
|
||||
height: {{ layout.preset.label_height }}mm;
|
||||
border-radius: {{ layout.preset.corner_radius }}mm;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 0.8mm;
|
||||
{% if layout.options.outline %}outline: 0.2mm solid #ced4da; outline-offset: -0.1mm;{% endif %}
|
||||
}
|
||||
|
||||
.lbl .accent {
|
||||
flex: 0 0 1.6mm;
|
||||
align-self: stretch;
|
||||
border-radius: 0.6mm;
|
||||
margin-right: 1.2mm;
|
||||
}
|
||||
|
||||
.lbl .art {
|
||||
flex: 0 0 {{ layout.options.image_percent }}%;
|
||||
height: 100%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin-right: 1.2mm;
|
||||
}
|
||||
.lbl .art img { max-width: 100%; max-height: 100%; object-fit: contain; }
|
||||
|
||||
.lbl .body { flex: 1 1 auto; min-width: 0; line-height: 1.12; }
|
||||
|
||||
.lbl .title {
|
||||
font-size: {{ layout.typography.title }}pt;
|
||||
font-weight: 700;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.lbl .sub {
|
||||
font-size: {{ layout.typography.sub }}pt;
|
||||
font-weight: 500; color: #333;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.lbl .meta {
|
||||
font-size: {{ layout.typography.meta }}pt;
|
||||
color: #666;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.lbl .loc { font-weight: 600; color: #111; }
|
||||
|
||||
.toolbar { max-width: 1000px; margin: 1rem auto 0; }
|
||||
|
||||
@media print {
|
||||
body { background: #fff; }
|
||||
.toolbar, .no-print { display: none !important; }
|
||||
.sheet {
|
||||
margin: 0; box-shadow: none;
|
||||
page-break-after: always; break-after: page;
|
||||
}
|
||||
.sheet:last-child { page-break-after: auto; break-after: auto; }
|
||||
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Print controls: never printed -->
|
||||
<div class="toolbar no-print">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start flex-wrap gap-2 mb-3">
|
||||
<div>
|
||||
<h5 class="mb-1"><i class="bi bi-printer"></i> {{ layout.total_labels }} labels on {{ layout.pages|length }} sheet{{ '' if layout.pages|length == 1 else 's' }}</h5>
|
||||
<div class="small text-muted">{{ layout.preset.name }}{% if layout.preset.note %} — {{ layout.preset.note }}{% endif %}</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-danger" onclick="window.print()"><i class="bi bi-printer"></i> Print</button>
|
||||
{% if pdf_available %}
|
||||
<button class="btn btn-outline-danger" form="optionsForm" formaction="{{ url_for('labels.print_pdf') }}">
|
||||
<i class="bi bi-file-pdf"></i> PDF
|
||||
</button>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('labels.index') }}" class="btn btn-outline-secondary">Back</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-light border small mb-3">
|
||||
<strong>Before you burn a sheet of Averys:</strong> in the print dialog set margins to
|
||||
<em>None</em> and scale to <em>100%</em> (not "Fit to page"). Tick "Outline each label",
|
||||
print one on plain paper, hold it against the sheet, then nudge the offsets below.
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ url_for('labels.print_sheet') }}" id="optionsForm" class="row g-3">
|
||||
<input type="hidden" name="ids" value="{{ ids }}">
|
||||
|
||||
<div class="col-md-5">
|
||||
<label class="form-label small">Sheet</label>
|
||||
<select name="preset" class="form-select form-select-sm">
|
||||
{% for key, preset in presets.items() %}
|
||||
<option value="{{ key }}" {% if key == layout.preset.key %}selected{% endif %}>{{ preset.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-6 col-md-2">
|
||||
<label class="form-label small">Copies each</label>
|
||||
<input type="number" name="copies" min="1" max="99" value="{{ layout.options.copies }}" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-6 col-md-2">
|
||||
<label class="form-label small">Skip positions</label>
|
||||
<input type="number" name="skip" min="0" value="{{ layout.options.skip }}" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<label class="form-label small">Text size</label>
|
||||
<input type="number" name="font_scale" step="0.05" min="0.5" max="2" value="{{ layout.options.font_scale }}" class="form-control form-control-sm">
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-md-2">
|
||||
<label class="form-label small">Offset X (mm)</label>
|
||||
<input type="number" name="offset_x" step="0.1" value="{{ layout.options.offset_x }}" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-6 col-md-2">
|
||||
<label class="form-label small">Offset Y (mm)</label>
|
||||
<input type="number" name="offset_y" step="0.1" value="{{ layout.options.offset_y }}" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<label class="form-label small">Image width (%)</label>
|
||||
<input type="number" name="image_percent" min="0" max="60" value="{{ layout.options.image_percent }}" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-6 col-md-5 d-flex align-items-end">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="outline" id="outline" value="1" {% if layout.options.outline %}checked{% endif %}>
|
||||
<label class="form-check-label small" for="outline">Outline each label (cutting / alignment)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label small">Show on each label</label>
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
{% for value, text in field_options %}
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="fields" value="{{ value }}"
|
||||
id="f-{{ value }}" {% if value in layout.fields %}checked{% endif %}>
|
||||
<label class="form-check-label small" for="f-{{ value }}">{{ text }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<button class="btn btn-sm btn-outline-danger"><i class="bi bi-arrow-repeat"></i> Update preview</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- The sheets themselves -->
|
||||
{% for cells in layout.pages %}
|
||||
<div class="sheet">
|
||||
{% for cell in cells %}
|
||||
{% set label = cell.label %}
|
||||
<div class="lbl" style="left: {{ cell.left }}mm; top: {{ cell.top }}mm;">
|
||||
{% if 'accent' in layout.fields and label.category %}
|
||||
<div class="accent" style="background: {{ label.category.accent_colour() }};"></div>
|
||||
{% endif %}
|
||||
|
||||
{% if 'image' in layout.fields and label.get_image() and layout.options.image_percent %}
|
||||
<div class="art"><img src="{{ label.get_image() }}" alt=""></div>
|
||||
{% endif %}
|
||||
|
||||
<div class="body">
|
||||
{% if 'name' in layout.fields %}
|
||||
<div class="title">{{ label.display_title if 'dimensions' in layout.fields else label.name }}</div>
|
||||
{% elif 'dimensions' in layout.fields %}
|
||||
<div class="title">{{ label.dimensions or '' }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if 'colour' in layout.fields and label.colour_name %}
|
||||
<div class="sub">{{ label.colour_name }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if 'category' in layout.fields and label.category %}
|
||||
<div class="sub">{{ label.category.full_name }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% set meta = [] %}
|
||||
{% if 'part_number' in layout.fields and label.part_number %}{% set _ = meta.append(label.part_number) %}{% endif %}
|
||||
{% if 'alt_numbers' in layout.fields and label.alt_part_numbers %}{% set _ = meta.append(label.alt_part_numbers) %}{% endif %}
|
||||
{% if 'category_code' in layout.fields and label.category and label.category.display_code %}{% set _ = meta.append(label.category.display_code) %}{% endif %}
|
||||
{% if meta %}
|
||||
<div class="meta">{{ meta|join(' · ') }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if 'location' in layout.fields and label.location %}
|
||||
<div class="meta loc">{{ label.location }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="toolbar no-print text-center text-muted small pb-4">
|
||||
Created by: Jess Rogerson (yelling commands at Claude.AI)
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user