Platform bundle: marketing publish, IT ops, packaging, agents mesh.
Volledige Foodlinkk Command Center uitbreiding met social automatisering, reclamefolder filters, Proxmox monitoring en documentatie.
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Packaging Studio · Foodlinkk{% endblock %}
|
||||
{% block extra_head %}
|
||||
<style>
|
||||
.pack-grid { display: grid; grid-template-columns: 1fr 340px; gap: 1rem; }
|
||||
@media (max-width: 1024px) { .pack-grid { grid-template-columns: 1fr; } }
|
||||
.pack-preview {
|
||||
min-height: 460px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(circle at top, rgba(0,229,255,.08), rgba(0,0,0,.35));
|
||||
border: 1px solid rgba(0,229,255,.2);
|
||||
border-radius: 12px;
|
||||
overflow: auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
.pack-preview svg { width: 100%; max-height: 72vh; }
|
||||
.pack-tools { display:flex; gap:.5rem; flex-wrap:wrap; margin-top:.75rem; }
|
||||
.elements-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.5rem; }
|
||||
.project-list { max-height: 260px; overflow: auto; display:flex; flex-direction:column; gap:.5rem; }
|
||||
.project-item { border:1px solid rgba(148,163,184,.15); border-radius:10px; padding:.6rem; background:rgba(0,0,0,.25); }
|
||||
.project-item small { color: var(--text-secondary); display:block; margin-top:.25rem; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div x-data="packagingStudio()" x-init="init()">
|
||||
<header class="page-header">
|
||||
<h1>Packaging Studio</h1>
|
||||
<p class="subtitle page-tip">Foodlinkk branded box, wrap en labels met live SVG preview.</p>
|
||||
</header>
|
||||
|
||||
<div class="pack-grid">
|
||||
<section class="panel">
|
||||
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.75rem;">
|
||||
<label>Type
|
||||
<select class="form-input" x-model="form.type">
|
||||
<option value="folding_box">Folding box</option>
|
||||
<option value="wrap">Wrap</option>
|
||||
<option value="round_label">Round label</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Barcode value
|
||||
<input class="form-input" x-model="form.barcode_value" placeholder="8710000000012" />
|
||||
</label>
|
||||
<label>Breedte (mm)<input type="number" class="form-input" x-model.number="form.width_mm" min="10" /></label>
|
||||
<label>Hoogte (mm)<input type="number" class="form-input" x-model.number="form.height_mm" min="10" /></label>
|
||||
<label>Diepte (mm)<input type="number" class="form-input" x-model.number="form.depth_mm" min="0" /></label>
|
||||
</div>
|
||||
|
||||
<h2 style="font-size:1rem;margin:1rem 0 .5rem;">Elementen</h2>
|
||||
<div class="elements-grid">
|
||||
<label><input type="checkbox" x-model="form.elements.barcode"> Barcode</label>
|
||||
<label><input type="checkbox" x-model="form.elements.logo_area"> Logo area</label>
|
||||
<label><input type="checkbox" x-model="form.elements.fold_lines"> Fold lines</label>
|
||||
<label><input type="checkbox" x-model="form.elements.cut_lines"> Cut lines</label>
|
||||
</div>
|
||||
|
||||
<div class="pack-tools">
|
||||
<button class="btn btn-primary" @click="generate()" :disabled="loading">
|
||||
<span x-show="!loading">Genereer verpakking</span>
|
||||
<span x-show="loading">Genereren…</span>
|
||||
</button>
|
||||
<a class="btn btn-ghost" :href="downloadUrl('svg')" x-show="projectId">Download SVG</a>
|
||||
<a class="btn btn-ghost" :href="downloadUrl('png')" x-show="projectId">Download PNG</a>
|
||||
<a class="btn btn-ghost" :href="downloadUrl('pdf')" x-show="projectId">Download PDF</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="panel">
|
||||
<h2 style="font-size:1rem;margin:0 0 .75rem;">Recente projecten</h2>
|
||||
<div class="project-list">
|
||||
<template x-for="p in projects" :key="p.id">
|
||||
<div class="project-item">
|
||||
<strong x-text="p.spec.type"></strong>
|
||||
<small x-text="p.spec.width_mm + '×' + p.spec.height_mm + '×' + p.spec.depth_mm + ' mm'"></small>
|
||||
<small x-text="(p.created_at || '').replace('T',' ').slice(0,19)"></small>
|
||||
<button class="btn btn-ghost" style="margin-top:.4rem;" @click="loadProject(p.id)">Open</button>
|
||||
</div>
|
||||
</template>
|
||||
<p class="page-tip" x-show="!projects.length">Nog geen packaging projecten.</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="panel" style="margin-top:1rem;">
|
||||
<div class="pack-preview">
|
||||
<div x-show="svg" x-html="svg"></div>
|
||||
<p class="page-tip" x-show="!svg && !loading">Start met Genereren om de preview te zien.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script>
|
||||
function packagingStudio() {
|
||||
return {
|
||||
loading: false,
|
||||
svg: '',
|
||||
projectId: '',
|
||||
projects: [],
|
||||
form: {
|
||||
type: 'folding_box',
|
||||
width_mm: 120,
|
||||
height_mm: 80,
|
||||
depth_mm: 40,
|
||||
barcode_value: '8710000000012',
|
||||
brand: {},
|
||||
elements: { barcode: true, logo_area: true, fold_lines: true, cut_lines: true }
|
||||
},
|
||||
async init() { await this.refreshProjects(); },
|
||||
downloadUrl(format) {
|
||||
if (!this.projectId) return '#';
|
||||
return '/api/packaging/download/' + this.projectId + '?format=' + format;
|
||||
},
|
||||
async refreshProjects() {
|
||||
try {
|
||||
const r = await Cockpit.api('/api/packaging/projects?limit=20');
|
||||
this.projects = r.items || [];
|
||||
} catch (e) {
|
||||
this.projects = [];
|
||||
}
|
||||
},
|
||||
async loadProject(id) {
|
||||
this.projectId = id;
|
||||
try {
|
||||
const data = await fetch('/api/packaging/download/' + id + '?format=svg').then(x => x.text());
|
||||
this.svg = data;
|
||||
} catch (e) {
|
||||
Cockpit.toast('Kon project niet laden', 'error');
|
||||
}
|
||||
},
|
||||
async generate() {
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
try {
|
||||
const r = await Cockpit.api('/api/packaging/generate', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(this.form)
|
||||
});
|
||||
this.svg = r.svg || '';
|
||||
this.projectId = r.id || '';
|
||||
await this.refreshProjects();
|
||||
Cockpit.toast('Packaging gegenereerd', 'success');
|
||||
} catch (e) {
|
||||
Cockpit.toast(e.message || 'Genereren mislukt', 'error');
|
||||
}
|
||||
this.loading = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user