241 lines
6.9 KiB
HTML
241 lines
6.9 KiB
HTML
{% extends "admin/base_site.html" %}
|
||
{% load i18n static %}
|
||
|
||
{% block title %}📷 Ajout de photos — {{ property.name_fr }}{% endblock %}
|
||
|
||
{% block extrahead %}
|
||
{{ block.super }}
|
||
<style>
|
||
.upload-card {
|
||
background: #fff;
|
||
border: 1px solid #ccc;
|
||
border-radius: 6px;
|
||
padding: 24px;
|
||
max-width: 680px;
|
||
margin: 20px 0;
|
||
}
|
||
.upload-card h2 { margin-top: 0; font-size: 16px; color: #417690; }
|
||
.upload-card .form-row { margin-bottom: 16px; }
|
||
.upload-card label {
|
||
display: block;
|
||
font-weight: bold;
|
||
margin-bottom: 4px;
|
||
font-size: 13px;
|
||
}
|
||
.upload-card select,
|
||
.upload-card input[type=text],
|
||
.upload-card input[type=file] {
|
||
width: 100%;
|
||
padding: 6px 8px;
|
||
border: 1px solid #ccc;
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
box-sizing: border-box;
|
||
}
|
||
.upload-card .help { font-size: 11px; color: #888; margin-top: 4px; }
|
||
.upload-card .separator {
|
||
text-align: center;
|
||
color: #999;
|
||
font-size: 12px;
|
||
margin: 4px 0;
|
||
position: relative;
|
||
}
|
||
.drop-zone {
|
||
border: 2px dashed #417690;
|
||
border-radius: 6px;
|
||
padding: 32px 20px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: background .2s;
|
||
background: #f9fbfc;
|
||
}
|
||
.drop-zone.dragover { background: #e8f3fa; border-color: #2c6a8e; }
|
||
.drop-zone p { margin: 0; color: #417690; font-size: 14px; }
|
||
.drop-zone small { color: #888; font-size: 12px; }
|
||
#file-preview { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
|
||
.preview-item {
|
||
position: relative;
|
||
width: 80px;
|
||
height: 80px;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
border: 1px solid #ddd;
|
||
}
|
||
.preview-item img { width: 100%; height: 100%; object-fit: cover; }
|
||
.preview-item span {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: rgba(0,0,0,.55);
|
||
color: #fff;
|
||
font-size: 9px;
|
||
padding: 2px 3px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.submit-row { display: flex; gap: 10px; margin-top: 20px; }
|
||
.btn-upload {
|
||
background: #417690;
|
||
color: #fff;
|
||
border: none;
|
||
padding: 10px 22px;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
}
|
||
.btn-upload:hover { background: #2c6a8e; }
|
||
.btn-again {
|
||
background: #f0f0f0;
|
||
color: #333;
|
||
border: 1px solid #ccc;
|
||
padding: 10px 18px;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
}
|
||
#file-count { font-size: 12px; color: #417690; margin-top: 6px; }
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block breadcrumbs %}
|
||
<div class="breadcrumbs">
|
||
<a href="{% url 'admin:index' %}">Accueil</a> ›
|
||
<a href="{% url 'admin:app_list' app_label='properties' %}">Properties</a> ›
|
||
<a href="{% url 'admin:properties_property_changelist' %}">Biens immobiliers</a> ›
|
||
<a href="{% url 'admin:properties_property_change' property.pk %}">{{ property.name_fr }}</a> ›
|
||
Ajout de photos
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<h1>📷 Ajout groupé de photos — <em>{{ property.name_fr }}</em></h1>
|
||
|
||
{% if messages %}
|
||
{% for msg in messages %}
|
||
<p class="success">{{ msg }}</p>
|
||
{% endfor %}
|
||
{% endif %}
|
||
|
||
<form method="post" enctype="multipart/form-data" id="upload-form">
|
||
{% csrf_token %}
|
||
|
||
<div class="upload-card">
|
||
<h2>1. Sélectionner ou créer une pièce</h2>
|
||
|
||
<div class="form-row">
|
||
<label for="id_room">Pièce existante :</label>
|
||
<select name="room" id="id_room">
|
||
<option value="">— Photos générales (sans pièce) —</option>
|
||
{% for room in rooms %}
|
||
<option value="{{ room.pk }}">{{ room.name_fr }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<div class="separator">— ou créer une nouvelle pièce —</div>
|
||
|
||
<div class="form-row">
|
||
<label for="id_new_room_fr">Nom français de la nouvelle pièce :</label>
|
||
<input type="text" name="new_room_fr" id="id_new_room_fr"
|
||
placeholder="ex : Cuisine, Salon, Chambre 1…"
|
||
autocomplete="off">
|
||
<p class="help">Si renseigné, cette pièce est créée et les photos lui sont attribuées (prioritaire sur la sélection ci-dessus).</p>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<label for="id_new_room_en">Nom anglais (optionnel) :</label>
|
||
<input type="text" name="new_room_en" id="id_new_room_en"
|
||
placeholder="ex : Kitchen, Living room, Bedroom 1…"
|
||
autocomplete="off">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="upload-card">
|
||
<h2>2. Choisir les photos</h2>
|
||
|
||
<div class="drop-zone" id="drop-zone">
|
||
<p>📂 Glisser-déposer les photos ici</p>
|
||
<small>ou cliquer pour sélectionner (sélection multiple possible)</small>
|
||
<input type="file" name="images" id="id_images"
|
||
multiple accept="image/*"
|
||
style="position:absolute;opacity:0;width:0;height:0">
|
||
</div>
|
||
<div id="file-count"></div>
|
||
<div id="file-preview"></div>
|
||
</div>
|
||
|
||
<div class="submit-row">
|
||
<button type="submit" name="next" value="change" class="btn-upload">
|
||
✓ Télécharger et revenir à la fiche
|
||
</button>
|
||
<button type="submit" name="next" value="again" class="btn-again">
|
||
✓ Télécharger et ajouter une autre pièce
|
||
</button>
|
||
<a href="{% url 'admin:properties_property_change' property.pk %}"
|
||
style="padding:10px 16px;font-size:14px;color:#666">
|
||
Annuler
|
||
</a>
|
||
</div>
|
||
|
||
</form>
|
||
|
||
<script>
|
||
(function () {
|
||
const dropZone = document.getElementById('drop-zone');
|
||
const input = document.getElementById('id_images');
|
||
const preview = document.getElementById('file-preview');
|
||
const fileCount = document.getElementById('file-count');
|
||
let dt = new DataTransfer();
|
||
|
||
// Ouvrir le sélecteur au clic sur la zone
|
||
dropZone.addEventListener('click', () => input.click());
|
||
|
||
// Drag-and-drop
|
||
dropZone.addEventListener('dragover', (e) => {
|
||
e.preventDefault();
|
||
dropZone.classList.add('dragover');
|
||
});
|
||
dropZone.addEventListener('dragleave', () => dropZone.classList.remove('dragover'));
|
||
dropZone.addEventListener('drop', (e) => {
|
||
e.preventDefault();
|
||
dropZone.classList.remove('dragover');
|
||
addFiles(e.dataTransfer.files);
|
||
});
|
||
|
||
input.addEventListener('change', () => {
|
||
addFiles(input.files);
|
||
input.value = '';
|
||
});
|
||
|
||
function addFiles(files) {
|
||
Array.from(files).forEach(f => {
|
||
if (!f.type.startsWith('image/')) return;
|
||
dt.items.add(f);
|
||
showThumb(f);
|
||
});
|
||
syncInput();
|
||
}
|
||
|
||
function showThumb(file) {
|
||
const reader = new FileReader();
|
||
reader.onload = (e) => {
|
||
const div = document.createElement('div');
|
||
div.className = 'preview-item';
|
||
div.innerHTML = `<img src="${e.target.result}" alt="${file.name}">
|
||
<span>${file.name}</span>`;
|
||
preview.appendChild(div);
|
||
};
|
||
reader.readAsDataURL(file);
|
||
}
|
||
|
||
function syncInput() {
|
||
input.files = dt.files;
|
||
const n = dt.files.length;
|
||
fileCount.textContent = n > 0 ? `${n} photo(s) sélectionnée(s)` : '';
|
||
}
|
||
})();
|
||
</script>
|
||
{% endblock %}
|