refactor: redesign calendar notes and documents view with chronological sequencing blocks
This commit is contained in:
parent
2ef730508f
commit
499ffe824e
5 changed files with 233 additions and 180 deletions
|
|
@ -1182,8 +1182,9 @@
|
|||
border-color: #b0c4de !important;
|
||||
}
|
||||
|
||||
.cal-nd-filter-btn {
|
||||
transition: all 0.15s ease;
|
||||
.cal-sequence-block {
|
||||
background-color: #f8fafc;
|
||||
border-color: #e2e8f0 !important;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5138,32 +5138,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||
if (summaryText) {
|
||||
summaryText.textContent = `${notesText} • ${docsText}`;
|
||||
}
|
||||
|
||||
// Setup filter buttons
|
||||
const filterBtns = container.querySelectorAll('.cal-nd-filter-btn');
|
||||
const notesSection = container.querySelector('#cal-modal-notes-section');
|
||||
const docsSection = container.querySelector('#cal-modal-docs-section');
|
||||
|
||||
filterBtns.forEach(btn => {
|
||||
btn.addEventListener('click', function () {
|
||||
filterBtns.forEach(b => {
|
||||
b.classList.remove('active');
|
||||
b.classList.remove('btn-primary');
|
||||
b.classList.add('btn-outline-primary');
|
||||
});
|
||||
this.classList.add('active');
|
||||
this.classList.remove('btn-outline-primary');
|
||||
this.classList.add('btn-primary');
|
||||
const filter = this.getAttribute('data-filter');
|
||||
|
||||
if (notesSection) {
|
||||
notesSection.style.display = (filter === 'all' || filter === 'notes') ? '' : 'none';
|
||||
}
|
||||
if (docsSection) {
|
||||
docsSection.style.display = (filter === 'all' || filter === 'docs') ? '' : 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
container.innerHTML = `<div class="text-muted small p-3 text-center">${gettext('Impossible de charger les notes et documents.')}</div>`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,98 +1,54 @@
|
|||
{% load i18n tz short_name interventions_tags document_viewer_tags %}
|
||||
|
||||
<div class="calendar-notes-docs-wrapper">
|
||||
<!-- Barre d'outils filtres / compteurs -->
|
||||
<!-- En-tête résumé -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 pb-2 border-bottom">
|
||||
<div class="btn-group btn-group-sm" role="group" aria-label="{% translate 'Filtrer les notes et documents' %}">
|
||||
<button type="button" class="btn btn-outline-primary active cal-nd-filter-btn" data-filter="all">
|
||||
<i class="bi bi-grid me-1"></i>{% translate "Tout" %}
|
||||
<span class="badge text-bg-primary rounded-pill ms-1" id="cal-count-all">{{ total_count }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary cal-nd-filter-btn" data-filter="notes">
|
||||
<i class="bi bi-sticky me-1"></i>{% translate "Notes" %}
|
||||
<span class="badge text-bg-secondary rounded-pill ms-1" id="cal-count-notes">{{ notes_count }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary cal-nd-filter-btn" data-filter="docs">
|
||||
<i class="bi bi-paperclip me-1"></i>{% translate "Documents" %}
|
||||
<span class="badge text-bg-secondary rounded-pill ms-1" id="cal-count-docs">{{ documents_count }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-muted small">
|
||||
<h6 class="mb-0 fw-bold text-dark d-flex align-items-center" style="font-size: 0.88rem;">
|
||||
<i class="bi bi-journal-text text-primary me-2"></i>{% translate "Notes & Documents" %}
|
||||
</h6>
|
||||
{% if total_count > 0 %}
|
||||
<span>{{ total_count }} {% if total_count > 1 %}{% translate "éléments" %}{% else %}{% translate "élément" %}{% endif %}</span>
|
||||
<span class="badge text-bg-light border text-muted" style="font-size: 0.75rem;">
|
||||
{{ total_count }} {% if total_count > 1 %}{% translate "éléments" %}{% else %}{% translate "élément" %}{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if total_count == 0 %}
|
||||
{% if not has_items %}
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="bi bi-journal-x fs-1 d-block mb-2 text-secondary opacity-50"></i>
|
||||
<p class="mb-1 fw-semibold text-secondary">{% translate "Aucune note ni document" %}</p>
|
||||
<p class="small text-muted mb-0">{% translate "Cette intervention n'a pas encore de note ni de document associé." %}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
<!-- Section Notes -->
|
||||
<div id="cal-modal-notes-section" class="cal-nd-section mb-3">
|
||||
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||
<h6 class="mb-0 fw-bold text-dark d-flex align-items-center" style="font-size: 0.85rem;">
|
||||
<i class="bi bi-sticky-fill text-warning me-2"></i>{% translate "Notes" %}
|
||||
<span class="badge bg-secondary-subtle text-secondary-emphasis rounded-pill ms-2" style="font-size: 0.72rem;">{{ notes_count }}</span>
|
||||
</h6>
|
||||
</div>
|
||||
|
||||
{% if notes %}
|
||||
<div class="cal-notes-list d-flex flex-column gap-2">
|
||||
{% for note in notes %}
|
||||
<div class="card shadow-none border bg-light-subtle rounded-3 cal-note-card">
|
||||
<div class="card-body p-2 px-3">
|
||||
<div class="d-flex justify-content-between align-items-start gap-2 mb-1">
|
||||
<div class="d-flex flex-wrap align-items-center gap-1">
|
||||
<span class="badge {{ note.type_badge_class }} d-inline-flex align-items-center gap-1 py-1 px-2" style="font-size: 0.72rem;">
|
||||
<i class="bi {{ note.type_icon_class }}"></i>
|
||||
{{ note.get_note_type_display }}
|
||||
<div class="cal-chronological-blocks d-flex flex-column gap-3">
|
||||
{% for block in sequenced_blocks %}
|
||||
{% if block.has_items %}
|
||||
<div class="cal-sequence-block p-3 rounded-3 bg-light-subtle border">
|
||||
<!-- En-tête de la phase chronologique -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 pb-2 border-bottom">
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<span class="badge {{ block.badge_class }} py-1 px-2 d-inline-flex align-items-center gap-1" style="font-size: 0.8rem;">
|
||||
<i class="bi {{ block.icon }}"></i>
|
||||
{{ block.title }}
|
||||
</span>
|
||||
<span class="text-secondary small fw-medium" style="font-size: 0.75rem;">
|
||||
{% if block.documents_count > 0 %}{{ block.documents_count }} {% if block.documents_count > 1 %}{% translate "fichiers" %}{% else %}{% translate "fichier" %}{% endif %}{% endif %}
|
||||
{% if block.documents_count > 0 and block.notes_count > 0 %}•{% endif %}
|
||||
{% if block.notes_count > 0 %}{{ block.notes_count }} {% if block.notes_count > 1 %}{% translate "notes" %}{% else %}{% translate "note" %}{% endif %}{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-muted small text-nowrap" style="font-size: 0.75rem;">
|
||||
<i class="bi bi-clock me-1"></i>{{ note.note_time|timezone:"Europe/Paris"|date:"d/m/Y H:i" }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="text-dark small note-content-text py-1" style="white-space: pre-wrap; line-height: 1.45; word-break: break-word;">{{ note.content }}</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-1 pt-1 border-top border-light-subtle" style="font-size: 0.72rem;">
|
||||
<span class="text-muted">
|
||||
<i class="bi bi-person me-1"></i>{{ note.note_author|short_name|default:"—" }}
|
||||
</span>
|
||||
{% if note.updated_at %}
|
||||
<span class="text-muted fst-italic" title="{% translate 'Mis à jour le' %} {{ note.updated_at|timezone:'Europe/Paris'|date:'d/m/Y H:i' }}{% if note.updated_by %} {% translate 'par' %} {{ note.updated_by|short_name }}{% endif %}">
|
||||
<i class="bi bi-pencil-fill me-1" style="font-size: 0.65rem;"></i>{% translate "Modifié" %}
|
||||
{% if block.period_display %}
|
||||
<span class="text-muted small text-nowrap" style="font-size: 0.72rem;">
|
||||
<i class="bi bi-clock me-1"></i>{{ block.period_display }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-muted small fst-italic py-2 px-1">
|
||||
<i class="bi bi-info-circle me-1"></i>{% translate "Aucune note pour cette intervention." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Section Documents -->
|
||||
<div id="cal-modal-docs-section" class="cal-nd-section {% if notes %}mt-4 pt-3 border-top{% endif %}">
|
||||
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||
<h6 class="mb-0 fw-bold text-dark d-flex align-items-center" style="font-size: 0.85rem;">
|
||||
<i class="bi bi-paperclip text-primary me-2"></i>{% translate "Documents & Photos" %}
|
||||
<span class="badge bg-secondary-subtle text-secondary-emphasis rounded-pill ms-2" style="font-size: 0.72rem;">{{ documents_count }}</span>
|
||||
</h6>
|
||||
</div>
|
||||
|
||||
{% if documents %}
|
||||
<!-- Documents & Photos de la phase -->
|
||||
{% if block.documents %}
|
||||
<div class="cal-block-documents mb-3">
|
||||
<div class="row g-2">
|
||||
{% for doc in documents %}
|
||||
{% for doc in block.documents %}
|
||||
{% private_file_url doc 'file' as file_url %}
|
||||
{% if doc.thumbnail %}
|
||||
{% private_file_url doc 'thumbnail' as thumb_url %}
|
||||
|
|
@ -130,12 +86,48 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-muted small fst-italic py-2 px-1">
|
||||
<i class="bi bi-info-circle me-1"></i>{% translate "Aucun document pour cette intervention." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Notes de la phase -->
|
||||
{% if block.notes %}
|
||||
<div class="cal-block-notes {% if block.documents %}border-top pt-2 mt-2{% endif %} d-flex flex-column gap-2">
|
||||
{% for note in block.notes %}
|
||||
<div class="card shadow-none border bg-white rounded-3 cal-note-card">
|
||||
<div class="card-body p-2 px-3">
|
||||
<div class="d-flex justify-content-between align-items-start gap-2 mb-1">
|
||||
<div class="d-flex flex-wrap align-items-center gap-1">
|
||||
<span class="badge {{ note.type_badge_class }} d-inline-flex align-items-center gap-1 py-1 px-2" style="font-size: 0.72rem;">
|
||||
<i class="bi {{ note.type_icon_class }}"></i>
|
||||
{{ note.get_note_type_display }}
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-muted small text-nowrap" style="font-size: 0.75rem;">
|
||||
<i class="bi bi-clock me-1"></i>{{ note.note_time|timezone:"Europe/Paris"|date:"d/m/Y H:i" }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="text-dark small note-content-text py-1" style="white-space: pre-wrap; line-height: 1.45; word-break: break-word;">{{ note.content }}</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-1 pt-1 border-top border-light-subtle" style="font-size: 0.72rem;">
|
||||
<span class="text-muted">
|
||||
<i class="bi bi-person me-1"></i>{{ note.note_author|short_name|default:"—" }}
|
||||
</span>
|
||||
{% if note.updated_at %}
|
||||
<span class="text-muted fst-italic" title="{% translate 'Mis à jour le' %} {{ note.updated_at|timezone:'Europe/Paris'|date:'d/m/Y H:i' }}{% if note.updated_by %} {% translate 'par' %} {{ note.updated_by|short_name }}{% endif %}">
|
||||
<i class="bi bi-pencil-fill me-1" style="font-size: 0.65rem;"></i>{% translate "Modifié" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
from unittest import mock
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.test import SimpleTestCase, RequestFactory
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
|
|
@ -114,10 +114,15 @@ class CalendarInterventionDetailsAjaxSimpleTests(SimpleTestCase):
|
|||
def test_authorized_user_receives_notes_and_documents(
|
||||
self, mock_get_obj, mock_notes_qs, mock_can_view, mock_can_view_summary
|
||||
):
|
||||
now = timezone.now()
|
||||
mock_itv = mock.MagicMock(id=123, title="Intervention Réparation Éclairage")
|
||||
mock_itv.begin_time = None
|
||||
mock_itv.end_time = None
|
||||
mock_itv.creation_time = now
|
||||
mock_itv.events.filter.return_value.order_by.return_value.first.return_value = None
|
||||
mock_itv.realized_events.filter.return_value.order_by.return_value.first.return_value = None
|
||||
mock_get_obj.return_value = mock_itv
|
||||
|
||||
now = timezone.now()
|
||||
author = DummyUser(id=1, first_name="Jean", last_name="Dupont")
|
||||
|
||||
# Notes
|
||||
|
|
@ -167,6 +172,7 @@ class CalendarInterventionDetailsAjaxSimpleTests(SimpleTestCase):
|
|||
)
|
||||
|
||||
mock_docs_qs = mock.MagicMock()
|
||||
mock_docs_qs.all.return_value = mock_docs_qs
|
||||
mock_docs_qs.select_related.return_value = mock_docs_qs
|
||||
mock_docs_qs.order_by.return_value = [doc_img, doc_pdf]
|
||||
mock_itv.documents = mock_docs_qs
|
||||
|
|
@ -174,6 +180,8 @@ class CalendarInterventionDetailsAjaxSimpleTests(SimpleTestCase):
|
|||
user = DummyUser(id=99)
|
||||
request = self._create_request(user)
|
||||
|
||||
from django.utils import translation
|
||||
with translation.override('fr'):
|
||||
response = intervention_calendar_details_ajax(request, intervention_id=123)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
|
@ -184,6 +192,7 @@ class CalendarInterventionDetailsAjaxSimpleTests(SimpleTestCase):
|
|||
self.assertEqual(data['total_count'], 4)
|
||||
|
||||
# Content assertions
|
||||
self.assertIn("Avant réalisation", data['html'])
|
||||
self.assertIn("Nacelle 15m requise", data['html'])
|
||||
self.assertIn("Attention présence de câbles haute tension", data['html'])
|
||||
self.assertIn("photo_degats.jpg", data['html'])
|
||||
|
|
@ -207,6 +216,11 @@ class CalendarInterventionDetailsAjaxSimpleTests(SimpleTestCase):
|
|||
self, mock_get_obj, mock_notes_qs, mock_can_view, mock_can_view_summary
|
||||
):
|
||||
mock_itv = mock.MagicMock(id=456, title="Intervention Sans Notes")
|
||||
mock_itv.begin_time = None
|
||||
mock_itv.end_time = None
|
||||
mock_itv.creation_time = timezone.now()
|
||||
mock_itv.events.filter.return_value.order_by.return_value.first.return_value = None
|
||||
mock_itv.realized_events.filter.return_value.order_by.return_value.first.return_value = None
|
||||
mock_get_obj.return_value = mock_itv
|
||||
|
||||
mock_filtered_notes = mock.MagicMock()
|
||||
|
|
@ -216,6 +230,7 @@ class CalendarInterventionDetailsAjaxSimpleTests(SimpleTestCase):
|
|||
mock_notes_qs.filter.return_value = mock_filtered_notes
|
||||
|
||||
mock_docs_qs = mock.MagicMock()
|
||||
mock_docs_qs.all.return_value = mock_docs_qs
|
||||
mock_docs_qs.select_related.return_value = mock_docs_qs
|
||||
mock_docs_qs.order_by.return_value = []
|
||||
mock_itv.documents = mock_docs_qs
|
||||
|
|
@ -232,3 +247,75 @@ class CalendarInterventionDetailsAjaxSimpleTests(SimpleTestCase):
|
|||
self.assertEqual(data['documents_count'], 0)
|
||||
self.assertEqual(data['total_count'], 0)
|
||||
self.assertIn("Aucune note ni document", data['html'])
|
||||
|
||||
@mock.patch('interventions.views.calendar.can_view_intervention_summary', return_value=False)
|
||||
@mock.patch('interventions.views.calendar.can_view_intervention', return_value=True)
|
||||
@mock.patch('interventions.views.calendar.InterventionNote.objects')
|
||||
@mock.patch('interventions.views.calendar.get_object_or_404')
|
||||
def test_chronological_sequencing_multiple_blocks(
|
||||
self, mock_get_obj, mock_notes_qs, mock_can_view, mock_can_view_summary
|
||||
):
|
||||
now = timezone.now()
|
||||
author = DummyUser(id=1, first_name="Jean", last_name="Dupont")
|
||||
|
||||
mock_itv = mock.MagicMock(id=789, title="Intervention Multi Phases")
|
||||
mock_itv.begin_time = now - timedelta(hours=2)
|
||||
mock_itv.end_time = now + timedelta(hours=2)
|
||||
mock_itv.creation_time = now - timedelta(days=2)
|
||||
mock_itv.events.filter.return_value.order_by.return_value.first.return_value = None
|
||||
mock_itv.realized_events.filter.return_value.order_by.return_value.first.return_value = None
|
||||
mock_get_obj.return_value = mock_itv
|
||||
|
||||
# Note during work
|
||||
note_during = DummyNote(
|
||||
id=1,
|
||||
note_type=InterventionNote.NoteType.WORK_DONE,
|
||||
type_display="Travaux réalisés",
|
||||
badge_class="text-bg-success text-white",
|
||||
icon_class="bi-check-circle",
|
||||
content="Remplacement du ballast effectué avec succès",
|
||||
note_time=now,
|
||||
note_author=author,
|
||||
)
|
||||
mock_filtered_notes = mock.MagicMock()
|
||||
mock_filtered_notes.select_related.return_value = mock_filtered_notes
|
||||
mock_filtered_notes.annotate.return_value = mock_filtered_notes
|
||||
mock_filtered_notes.order_by.return_value = [note_during]
|
||||
mock_notes_qs.filter.return_value = mock_filtered_notes
|
||||
|
||||
# Document before work (yesterday)
|
||||
doc_before = DummyDoc(
|
||||
id=20,
|
||||
filename="constat_initial.jpg",
|
||||
thumbnail_path="thumbnails/constat_initial.jpg",
|
||||
upload_date=now - timedelta(days=1),
|
||||
uploaded_by=author,
|
||||
)
|
||||
mock_docs_qs = mock.MagicMock()
|
||||
mock_docs_qs.all.return_value = mock_docs_qs
|
||||
mock_docs_qs.select_related.return_value = mock_docs_qs
|
||||
mock_docs_qs.order_by.return_value = [doc_before]
|
||||
mock_itv.documents = mock_docs_qs
|
||||
|
||||
user = DummyUser(id=99)
|
||||
request = self._create_request(user)
|
||||
|
||||
from django.utils import translation
|
||||
with translation.override('fr'):
|
||||
response = intervention_calendar_details_ajax(request, intervention_id=789)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
self.assertTrue(data['success'])
|
||||
self.assertEqual(data['notes_count'], 1)
|
||||
self.assertEqual(data['documents_count'], 1)
|
||||
self.assertEqual(data['total_count'], 2)
|
||||
|
||||
import html as html_module
|
||||
unescaped_html = html_module.unescape(data['html'])
|
||||
|
||||
# Both chronological blocks are rendered
|
||||
self.assertIn("Avant réalisation", unescaped_html)
|
||||
self.assertIn("constat_initial.jpg", unescaped_html)
|
||||
self.assertIn("Exécution de l'intervention", unescaped_html)
|
||||
self.assertIn("Remplacement du ballast effectué avec succès", unescaped_html)
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ from interventions.permissions import (intervention_permission_required, interve
|
|||
from interventions.permissions import ALLOWED_TRANSITIONS, get_allowed_transitions, user_allowed_transitions
|
||||
from interventions.views.drafts import InterventionDraftManager
|
||||
from interventions.utils import resolve_intervention_localisations
|
||||
from interventions.services.document_sequencing import get_sequenced_intervention_documents
|
||||
|
||||
from common.models import UserConfig, UserContractAccess, UserContractStatusPermission, Thematic, UserThematics
|
||||
from contracts.models import ContractOrder, ContractPost, Contract, Company, CompanyTeam, CompanyMember, UserCompanyTeamAccess, CompanyMemberHistory, CompanyMemberAbsence
|
||||
|
|
@ -1759,7 +1760,8 @@ def team_daily_members_ajax(request, team_id):
|
|||
@require_GET
|
||||
def intervention_calendar_details_ajax(request, intervention_id):
|
||||
"""
|
||||
Retourne les notes et documents d'une intervention pour la modale du calendrier.
|
||||
Retourne les notes et documents d'une intervention pour la modale du calendrier,
|
||||
organisés sous forme chronologique (phases: avant réalisation, exécution, après réalisation).
|
||||
"""
|
||||
intervention = get_object_or_404(Intervention, pk=intervention_id)
|
||||
|
||||
|
|
@ -1774,35 +1776,32 @@ def intervention_calendar_details_ajax(request, intervention_id):
|
|||
output_field=IntegerField(),
|
||||
)
|
||||
|
||||
notes = list(
|
||||
notes = (
|
||||
InterventionNote.objects
|
||||
.filter(intervention=intervention)
|
||||
.select_related('note_author', 'updated_by')
|
||||
.annotate(type_priority_order=note_priority_case)
|
||||
.order_by('type_priority_order', '-note_time', '-id')
|
||||
.order_by('type_priority_order', 'note_time', 'id')
|
||||
)
|
||||
|
||||
sequenced_data = get_sequenced_intervention_documents(intervention, notes_qs=notes)
|
||||
|
||||
IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'}
|
||||
documents = list(
|
||||
intervention.documents
|
||||
.select_related('uploaded_by')
|
||||
.order_by('-upload_date', '-id')
|
||||
)
|
||||
|
||||
for doc in documents:
|
||||
for block in sequenced_data['blocks']:
|
||||
for doc in block['documents']:
|
||||
fn = getattr(doc, 'filename', '') or ''
|
||||
ext = os.path.splitext(fn)[1].lower() if fn else ''
|
||||
doc.ext = ext
|
||||
doc.is_image = bool(doc.thumbnail or ext in IMAGE_EXTENSIONS)
|
||||
doc.is_image = bool(getattr(doc, 'thumbnail', None) or ext in IMAGE_EXTENSIONS)
|
||||
doc.is_pdf = ext == '.pdf'
|
||||
|
||||
context = {
|
||||
'intervention': intervention,
|
||||
'notes': notes,
|
||||
'documents': documents,
|
||||
'notes_count': len(notes),
|
||||
'documents_count': len(documents),
|
||||
'total_count': len(notes) + len(documents),
|
||||
'sequenced_blocks': sequenced_data['blocks'],
|
||||
'notes_count': sequenced_data['total_notes_count'],
|
||||
'documents_count': sequenced_data['total_documents_count'],
|
||||
'total_count': sequenced_data['total_items_count'],
|
||||
'has_items': sequenced_data['has_items'],
|
||||
}
|
||||
|
||||
html = render_to_string(
|
||||
|
|
@ -1813,9 +1812,9 @@ def intervention_calendar_details_ajax(request, intervention_id):
|
|||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'notes_count': len(notes),
|
||||
'documents_count': len(documents),
|
||||
'total_count': len(notes) + len(documents),
|
||||
'notes_count': sequenced_data['total_notes_count'],
|
||||
'documents_count': sequenced_data['total_documents_count'],
|
||||
'total_count': sequenced_data['total_items_count'],
|
||||
'html': html,
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue