premiere iteration de la gestion des documents
CI - Security, Lint & Tests / validate (push) Failing after 1m20s
CI - Security, Lint & Tests / validate (push) Failing after 1m20s
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ document.title }} - Admin{% endblock %}
|
||||
{% block page_title %}{{ document.title }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('admin.dashboard') }}">Admin</a> / <a href="{{ url_for('admin.documents') }}">{{ _('Documents') }}</a> / {{ document.title }}</span>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% set kind_labels = {
|
||||
'player': _('Player contract'),
|
||||
'coach': _('Coach contract'),
|
||||
'manager': _('Manager contract'),
|
||||
'team': _('Team agreement'),
|
||||
'code_of_conduct': _('Code of conduct'),
|
||||
'ethics_code': _('Ethics code'),
|
||||
} %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-file-upload"></i> {{ _('Publish a new version') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
{{ _('Type') }}: <strong>{{ kind_labels.get(document.kind, document.kind) }}</strong>
|
||||
{% if document.requires_signature %} · {{ _('signed by the member') }}
|
||||
{% else %} · {{ _('acknowledged by the member') }}{% endif %}
|
||||
</p>
|
||||
<form method="POST" action="{{ url_for('admin.publish_document_version', doc_id=document.id) }}" enctype="multipart/form-data" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="form-group">
|
||||
<label for="version_file">{{ _('New version file (PDF)') }}</label>
|
||||
<input type="file" id="version_file" name="version_file" accept=".pdf" required>
|
||||
<small class="form-text text-muted">{{ _('Accepted format: PDF') }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notes">{{ _('Version notes (optional)') }}</label>
|
||||
<textarea id="notes" name="notes" rows="2" placeholder="{{ _('What changed in this version?') }}"></textarea>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<a href="{{ url_for('admin.documents') }}" class="btn btn-secondary">{{ _('Back') }}</a>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-upload"></i> {{ _('Publish version') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-history"></i> {{ _('Versions') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Version') }}</th>
|
||||
<th>{{ _('File') }}</th>
|
||||
<th>{{ _('Notes') }}</th>
|
||||
<th>{{ _('Published') }}</th>
|
||||
<th>{{ _('Actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for version in versions %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ _('v%(number)s', number=version.version) }}
|
||||
{% if document.current_version_id == version.id %}
|
||||
<span class="badge badge-success">{{ _('Current') }}</span>{% endif %}
|
||||
</td>
|
||||
<td>{{ version.original_filename }}</td>
|
||||
<td>{{ version.notes or '—' }}</td>
|
||||
<td>{{ version.published_at.strftime('%Y-%m-%d %H:%M') if version.published_at else '—' }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('admin.download_document_version', version_id=version.id) }}" class="btn btn-sm btn-primary" title="{{ _('Download') }}">
|
||||
<i class="fas fa-download"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-muted">{{ _('No versions published yet.') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-list-check"></i> {{ _('Completion') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Member') }}</th>
|
||||
<th>{{ _('Role') }}</th>
|
||||
<th>{{ _('Status') }}</th>
|
||||
<th>{{ _('Completed at') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for assignment, user in assignments %}
|
||||
<tr>
|
||||
<td>{{ user.username }}</td>
|
||||
<td>{{ user.role | capitalize }}</td>
|
||||
<td>
|
||||
{% if assignment.status == 'signed' %}
|
||||
<span class="badge badge-success">{{ _('Signed') }}</span>
|
||||
{% elif assignment.status == 'acknowledged' %}
|
||||
<span class="badge badge-success">{{ _('Acknowledged') }}</span>
|
||||
{% else %}
|
||||
<span class="badge badge-warning">{{ _('Pending') }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ assignment.completed_at.strftime('%Y-%m-%d %H:%M') if assignment.completed_at else '—' }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="4" class="text-muted">
|
||||
{{ _('No one is assigned yet. Publish a version first.') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,118 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('Documents') }} - Admin{% endblock %}
|
||||
{% block page_title %}{{ _('Document management') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('admin.dashboard') }}">Admin</a> / {{ _('Documents') }}</span>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% set kind_labels = {
|
||||
'player': _('Player contract'),
|
||||
'coach': _('Coach contract'),
|
||||
'manager': _('Manager contract'),
|
||||
'team': _('Team agreement'),
|
||||
'code_of_conduct': _('Code of conduct'),
|
||||
'ethics_code': _('Ethics code'),
|
||||
} %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-plus-circle"></i> {{ _('Create a document') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" data-ajax action="{{ url_for('admin.create_document') }}" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="form-group">
|
||||
<label for="title">{{ _('Title') }}</label>
|
||||
<input type="text" id="title" name="title" class="form-control" required
|
||||
placeholder="{{ _('e.g. Player contract 2026-2027') }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="kind">{{ _('Type') }}</label>
|
||||
<select id="kind" name="kind" class="form-select" required>
|
||||
<option value="">{{ _('-- Select a type --') }}</option>
|
||||
{% for kind in kinds %}
|
||||
<option value="{{ kind }}">{{ kind_labels.get(kind, kind) }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<small class="form-text text-muted">
|
||||
{{ _('The type decides who must complete the document and how.') }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i> {{ _('Create document') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-file-contract"></i> {{ _('Existing documents') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Title') }}</th>
|
||||
<th>{{ _('Type') }}</th>
|
||||
<th>{{ _('Version') }}</th>
|
||||
<th>{{ _('Availability') }}</th>
|
||||
<th>{{ _('Progress') }}</th>
|
||||
<th>{{ _('Actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for document in documents %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('admin.document_detail', doc_id=document.id) }}">{{ document.title }}</a></td>
|
||||
<td>{{ kind_labels.get(document.kind, document.kind) }}</td>
|
||||
<td>
|
||||
{% if document.current_version %}
|
||||
{{ _('v%(number)s', number=document.current_version.version) }}
|
||||
{% else %}{{ _('None') }}{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if document.is_published %}
|
||||
<span class="badge badge-success">{{ _('Downloadable') }}</span>
|
||||
{% else %}
|
||||
<span class="badge badge-secondary">{{ _('Hidden') }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% set completed, pending = document.completion_counts() %}
|
||||
<span class="badge badge-info">{{ _('%(completed)s done', completed=completed) }}</span>
|
||||
<span class="badge badge-warning">{{ _('%(pending)s pending', pending=pending) }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<a href="{{ url_for('admin.document_detail', doc_id=document.id) }}" class="btn btn-sm btn-primary" title="{{ _('Manage') }}">
|
||||
<i class="fas fa-cog"></i> {{ _('Manage') }}
|
||||
</a>
|
||||
<form method="POST" data-ajax action="{{ url_for('admin.toggle_document_published', doc_id=document.id) }}" class="inline-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
{% if document.is_published %}
|
||||
<button type="submit" class="btn btn-sm btn-secondary" title="{{ _('Hide') }}">
|
||||
<i class="fas fa-eye-slash"></i> {{ _('Hide') }}
|
||||
</button>
|
||||
{% else %}
|
||||
<button type="submit" class="btn btn-sm btn-success" title="{{ _('Make downloadable') }}">
|
||||
<i class="fas fa-eye"></i> {{ _('Publish') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-muted">{{ _('No documents yet. Create one above.') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,173 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{{ _('My Documents') }} - UdeS team manager{% endblock %}
|
||||
{% block page_title %}{{ _('My Documents') }}{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.profile') }}">Profile</a> / {{ _('My Documents') }}</span>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% set kind_labels = {
|
||||
'player': _('Player contract'),
|
||||
'coach': _('Coach contract'),
|
||||
'manager': _('Manager contract'),
|
||||
'team': _('Team agreement'),
|
||||
'code_of_conduct': _('Code of conduct'),
|
||||
'ethics_code': _('Ethics code'),
|
||||
} %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-file-contract"></i> {{ _('Documents to sign or acknowledge') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if documents %}
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Document') }}</th>
|
||||
<th>{{ _('Type') }}</th>
|
||||
<th>{{ _('Version') }}</th>
|
||||
<th>{{ _('Status') }}</th>
|
||||
<th>{{ _('Actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in documents %}
|
||||
<tr>
|
||||
<td>{{ entry.document.title }}</td>
|
||||
<td>{{ kind_labels.get(entry.document.kind, entry.document.kind) }}</td>
|
||||
<td>
|
||||
{% if entry.document.current_version %}
|
||||
{{ _('Version %(number)s', number=entry.document.current_version.version) }}
|
||||
{% else %}—{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% set assignment = entry.assignment %}
|
||||
{% if assignment is not none and assignment.status == 'signed' %}
|
||||
<span class="badge badge-success">{{ _('Signed') }}</span>
|
||||
{% elif assignment is not none and assignment.status == 'acknowledged' %}
|
||||
<span class="badge badge-success">{{ _('Acknowledged') }}</span>
|
||||
{% else %}
|
||||
<span class="badge badge-warning">{{ _('Pending') }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
{% if entry.document.current_version %}
|
||||
<a href="{{ url_for('users.download_document', version_id=entry.document.current_version.id) }}" class="btn btn-sm btn-primary" title="{{ _('Download') }}">
|
||||
<i class="fas fa-download"></i> {{ _('Download') }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if assignment is not none and assignment.status == 'pending' %}
|
||||
{% if entry.document.requires_signature %}
|
||||
<button class="btn btn-sm btn-warning" data-action="show-sign-document" data-assignment-id="{{ assignment.id }}" title="{{ _('Return signed') }}">
|
||||
<i class="fas fa-upload"></i> {{ _('Sign') }}
|
||||
</button>
|
||||
{% else %}
|
||||
<form method="POST" data-ajax action="{{ url_for('users.acknowledge_document', assignment_id=assignment.id) }}" class="inline-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<button type="submit" class="btn btn-sm btn-success" data-confirm="{{ _('Confirm that you have read and understood this document') }}">
|
||||
<i class="fas fa-check"></i> {{ _('Acknowledge') }}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% elif not teams_progress %}
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-file-contract"></i>
|
||||
<h4>{{ _('No documents for now') }}</h4>
|
||||
<p>{{ _('Nothing has been published for you yet. Check back later.') }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if teams_progress %}
|
||||
{% for block in teams_progress %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-users"></i> {{ _('Team progress') }} — {{ block.team.name }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Member') }}</th>
|
||||
<th>{{ _('Role') }}</th>
|
||||
{% for doc in block.documents %}<th>{{ doc.title }}</th>{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in block.members %}
|
||||
<tr>
|
||||
<td>{{ member.user.username }}</td>
|
||||
<td>{{ member.user.role | capitalize }}</td>
|
||||
{% for doc in block.documents %}
|
||||
{% set status = member.statuses.get(doc.id) %}
|
||||
<td>
|
||||
{% if status == 'signed' %}<span class="badge badge-success">{{ _('Signed') }}</span>
|
||||
{% elif status == 'acknowledged' %}<span class="badge badge-success">{{ _('Acknowledged') }}</span>
|
||||
{% elif status == 'pending' %}<span class="badge badge-warning">{{ _('Pending') }}</span>
|
||||
{% else %}<span class="text-muted">{{ _('Not required') }}</span>{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Upload signed document modal -->
|
||||
<div id="signDocumentModal" class="modal hidden">
|
||||
<div class="modal-backdrop" data-action="hide-sign-document"></div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>{{ _('Upload Signed Document') }}</h3>
|
||||
<button class="modal-close" data-action="hide-sign-document">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="signDocumentForm" method="POST" enctype="multipart/form-data" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="form-group">
|
||||
<label for="signed_file">{{ _('Select Signed Document File') }}</label>
|
||||
<input type="file" id="signed_file" name="signed_file" accept=".pdf" required>
|
||||
<small class="form-text text-muted">{{ _('Accepted format: PDF') }}</small>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" data-action="hide-sign-document">{{ _('Cancel') }}</button>
|
||||
<button type="submit" class="btn btn-primary">{{ _('Upload Signed Document') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="{{ csp_nonce }}">
|
||||
function showSignDocumentForm(assignmentId) {
|
||||
document.getElementById('signDocumentForm').action = '/users/documents/assignments/' + assignmentId + '/sign';
|
||||
document.getElementById('signDocumentModal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function hideSignDocumentForm() {
|
||||
document.getElementById('signDocumentModal').classList.add('hidden');
|
||||
}
|
||||
|
||||
registerActions({
|
||||
'show-sign-document': function (element) {
|
||||
showSignDocumentForm(element.getAttribute('data-assignment-id'));
|
||||
},
|
||||
'hide-sign-document': hideSignDocumentForm,
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user