loko/streetup/projects/templates/projects/projects_detail_mobile.html
2026-07-22 14:48:40 +02:00

320 lines
13 KiB
HTML

{% extends 'mobile/mobile_base.html' %}
{% load static %}
{% load i18n %}
{% load document_viewer_tags %}
{% block title %}
{{ project.name }}
{% endblock title %}
{% block upleft %}
<a href="javascript:history.back()" class="nav-link" style="color: white;">
<i class="bi bi-arrow-left fs-3" title="Retour"></i>
</a>
{% endblock upleft %}
{% block content %}
<!-- En-tête du projet -->
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title text-primary">{{ project.name }}</h5>
<p class="card-text text-muted">{{ project.code }}</p>
<p class="mb-1"><strong>{% translate "Statut" %} :</strong>
<span class="badge bg-info">{{ project.get_status_display }}</span>
</p>
<p class="mb-1"><strong>{% translate "Type de maintenance" %} :</strong>
<span class="badge bg-secondary">{{ project.get_maintain_type_display }}</span>
</p>
<p class="mb-1"><strong>{% translate "Thématique" %} :</strong>
{% if project.thematic %}
{{ project.thematic.name }}
{% endif%}
</p>
<p class="mb-1"><strong>{% translate "Dates" %} :</strong>
{{ project.start_date|date:"d/m/Y" }} -
{% if project.end_date %}
{{ project.end_date|date:"d/m/Y" }}
{% else %}
En cours
{% endif%}
</p>
{% if project.description %}
<p class="mt-3"><strong>{% translate "Description" %} :</strong> {{ project.description }}</p>
{% endif %}
</div>
</div>
<!-- Phases du projet -->
<div class="mt-4">
<h5 class="text-primary">{% translate "Phases du projet" %}</h5>
{% if project.phases.all %}
<div class="list-group">
{% for phase in project.phases.all %}
<div class="list-group-item">
<h6 class="mb-1">{{ phase.name }}</h6>
<p class="small text-muted">
{{ phase.start_date|date:"d/m/Y" }} -
{% if phase.end_date %}
{{ phase.end_date|date:"d/m/Y" }}
{% else %}
{% translate "En cours" %}
{% endif%}
</p>
{% if phase.description %}
<p class="mb-0 small">{{ phase.description }}</p>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
<p class="text-muted">{% translate "Aucune phase définie pour ce projet." %}</p>
{% endif %}
</div>
<div class="mt-4">
<h5 class="text-primary">{% translate "Contrôles associés" %}</h5>
<a href="{% url 'controls:controls_add' %}?project_id={{ project.id }}&v=mobile" class="btn btn-light w-100 mt-3 mb-3">
<i class="bi bi-plus-circle px-3 text-primary"></i>{% translate "Ajouter un contrôle" %}
</a>
{% if project.controls.all %}
<div class="list-group">
{% for control in project.controls.all %}
<div class="list-group-item">
<h6 class="mb-1">
<a href="{% url 'mobile:controls_detail' control.id %}" >
{{ control.control_code }}
</a>
</h6>
<p class="small text-muted">
{% translate "Type" %} : <span class="badge bg-secondary">{{ control.get_control_type_display }}</span>
| {% translate "Statut" %} : <span class="badge bg-info">{{ control.get_status_display }}</span>
</p>
<p class="mb-0 small">{% translate "Lieu" %} : {{ control.location }}</p>
<p class="mb-0 text-muted small">{% translate "Mis à jour le" %} : {{ control.updated_at|date:"d/m/Y H:i" }}</p>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-muted">{% translate "Aucun contrôle associé à ce projet." %}</p>
{% endif %}
</div>
<!-- Documents -->
<div class="mt-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<h5 class="text-primary mb-0">{% translate "Documents" %}</h5>
{% if can_add_documents %}
<button class="btn btn-light border-0" title="{% translate 'Ajouter' %}" data-bs-toggle="modal" data-bs-target="#addDocumentModal">
<i class="bi bi-plus-circle text-primary fs-5"></i>
</button>
{% endif %}
</div>
<div id="documentsAlertContainer" class="m-1"></div>
{% if use_managed_documents %}
{% render_document_viewer project_documents modal_id="projectDocViewerModal" show_tags=True show_versions=True location_id=project.id %}
{% else %}
<p class="text-muted small">{% translate "Aucun document disponible." %}</p>
{% endif %}
</div>
{% if can_add_documents %}
<!-- Modal ajout de documents -->
<div class="modal fade" id="addDocumentModal" tabindex="-1" aria-labelledby="addDocumentModalLabel" aria-hidden="true" style="z-index: 10050;">
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addDocumentModalLabel">{% translate "Ajouter des documents" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
</div>
<div class="modal-body">
<form id="addDocumentForm" method="post" enctype="multipart/form-data" action="{% url 'projects:projects_add_document' project.id %}">
{% csrf_token %}
<div class="mb-3">
<label for="docFileInput" class="form-label">{% translate "Sélectionner des fichiers" %}</label>
<input type="file" id="docFileInput" name="files" multiple class="form-control">
</div>
<div id="docFileFieldsContainer">
{# Champs dynamiques (tag) injectés par JS #}
</div>
<div class="d-flex justify-content-end mt-3">
<button type="submit" class="btn btn-primary" id="documentSubmitButton">
{% translate "Ajouter" %}
<span class="spinner-border spinner-border-sm ms-2 d-none" role="status" aria-hidden="true" id="documentSubmitSpinner"></span>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endif %}
<!-- Modal historique des versions -->
<div class="modal fade" id="documentHistoryModal" tabindex="-1" aria-labelledby="documentHistoryModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-scrollable modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="documentHistoryModalLabel">{% translate "Historique des versions" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
</div>
<div class="modal-body">
<div id="documentHistoryContent" class="row g-3">
{# Contenu dynamique injecté via fetch #}
</div>
</div>
</div>
</div>
</div>
{# Config JSON pour le JS du formulaire d'ajout #}
<script type="application/json" id="projectDocumentConfigData">
{
"use_managed_documents": {% if use_managed_documents %}true{% else %}false{% endif %},
"can_add_unique_tag_documents": {% if can_add_unique_tag_documents %}true{% else %}false{% endif %},
"config_url": "{% url 'projects:projects_document_config' project.id %}"
}
</script>
{% endblock content %}
{% block extra_scripts %}
<script>
const project_id = {{ project.id }};
const document_history_url = "{% url 'projects:projects_document_history' project.id %}";
function showAlert(message, alertContainer, type) {
type = type || "success";
const alertBox = document.createElement("div");
alertBox.className = `alert alert-${type} alert-dismissible fade show`;
alertBox.role = "alert";
alertBox.innerHTML = `${message}<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>`;
alertContainer.appendChild(alertBox);
}
const docFileInput = document.getElementById('docFileInput');
const docFileContainer = document.getElementById('docFileFieldsContainer');
let docConfig = { use_managed_documents: false, can_add_unique_tag_documents: false, config_url: null };
let docAvailableTags = [];
let docAvailableFolders = [];
(function loadDocConfig() {
try {
const el = document.getElementById('projectDocumentConfigData');
if (el) { docConfig = JSON.parse(el.textContent); }
} catch (e) {}
if (!docConfig.config_url) return;
fetch(docConfig.config_url)
.then(r => r.json())
.then(data => {
docConfig.use_managed_documents = data.use_managed_documents;
docConfig.can_add_unique_tag_documents = data.can_add_unique_tag_documents;
docAvailableTags = data.tags || [];
docAvailableFolders = data.folders || [];
})
.catch(() => {});
})();
function renderDocFileFields() {
if (!docFileContainer || !docFileInput) return;
docFileContainer.innerHTML = '';
const files = Array.from(docFileInput.files);
files.forEach((file, index) => {
const row = document.createElement('div');
row.classList.add('d-flex', 'align-items-center', 'gap-2', 'mb-2', 'p-2', 'border', 'rounded', 'flex-wrap');
const fileName = document.createElement('span');
fileName.classList.add('flex-grow-1', 'text-truncate');
fileName.style.maxWidth = '200px';
fileName.textContent = file.name;
fileName.title = file.name;
row.appendChild(fileName);
if (docConfig.use_managed_documents && docAvailableTags.length > 0) {
const tagSel = document.createElement('select');
tagSel.name = `tag_${index}`;
tagSel.classList.add('form-select', 'form-select-sm', 'w-auto');
const def = document.createElement('option');
def.value = '';
def.textContent = gettext('-- Sans tag --');
tagSel.appendChild(def);
docAvailableTags.forEach(tag => {
const opt = document.createElement('option');
opt.value = tag.slug;
opt.textContent = tag.name;
opt.dataset.isUnique = tag.is_unique;
if (tag.is_unique && !docConfig.can_add_unique_tag_documents) {
opt.disabled = true;
opt.textContent += ' (non autorisé)';
}
tagSel.appendChild(opt);
});
row.appendChild(tagSel);
} else {
const hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = `document_type_${index}`;
hidden.value = 'other';
row.appendChild(hidden);
}
const delBtn = document.createElement('button');
delBtn.type = 'button';
delBtn.classList.add('btn', 'btn-outline-danger', 'btn-sm');
delBtn.innerHTML = '<i class="bi bi-x-lg"></i>';
delBtn.onclick = () => {
const remaining = Array.from(docFileInput.files).filter((_, i) => i !== index);
const dt = new DataTransfer();
remaining.forEach(f => dt.items.add(f));
docFileInput.files = dt.files;
renderDocFileFields();
};
row.appendChild(delBtn);
docFileContainer.appendChild(row);
});
const countInput = document.createElement('input');
countInput.type = 'hidden';
countInput.name = 'file_count';
countInput.value = files.length;
docFileContainer.appendChild(countInput);
}
if (docFileInput) {
docFileInput.addEventListener('change', renderDocFileFields);
}
// Historique des versions
const docHistoryModalEl = document.getElementById('documentHistoryModal');
if (docHistoryModalEl) {
docHistoryModalEl.addEventListener('show.bs.modal', function (event) {
const btn = event.relatedTarget;
const tagSlug = btn.getAttribute('data-tag-slug');
const useManaged = btn.getAttribute('data-use-managed') === 'true';
const uniqueParams = btn.getAttribute('data-unique-params');
const contentEl = document.getElementById('documentHistoryContent');
if (contentEl) {
contentEl.innerHTML = '<div class="text-center py-4"><div class="spinner-border text-primary" role="status"></div></div>';
}
if (!useManaged || !tagSlug) return;
let url = `${document_history_url}?tag_slug=${encodeURIComponent(tagSlug)}`;
if (uniqueParams && uniqueParams !== '{}' && uniqueParams !== 'null') {
url += `&unique_params=${encodeURIComponent(uniqueParams)}`;
}
fetch(url)
.then(r => r.json())
.then(data => { if (contentEl) contentEl.innerHTML = data.html || ''; })
.catch(() => { if (contentEl) contentEl.innerHTML = '<p class="text-muted">Impossible de charger l\'historique.</p>'; });
});
}
</script>
{% endblock extra_scripts %}