first commit
This commit is contained in:
136
templates/base.html.twig
Normal file
136
templates/base.html.twig
Normal file
@ -0,0 +1,136 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{appName}}{% block title %}{% endblock %}</title>
|
||||
<link rel="icon" href="">
|
||||
|
||||
<link rel="stylesheet" href="{{ asset('lib/bootstrap/css/bootstrap.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('lib/bootswatch/bootswatch.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('lib/fontawesome/css/all.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('lib/datatables/datatables.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('lib/select2/select2.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('lib/select2/select2-bootstrap-5-theme.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('lib/dropzone/dropzone.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('lib/dropzone/dropzone-bootstrap.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('lib/imgareaselect/css/imgareaselect-default.css') }}">
|
||||
|
||||
<link rel="stylesheet" href="{{ asset('lib/app/app.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('lib/app/selbg.css') }}">
|
||||
|
||||
<script src="{{ asset('lib/jquery/jquery.min.js') }}"></script>
|
||||
<script src="{{ asset('lib/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
|
||||
<script src="{{ asset('lib/fontawesome/fontawesome-free.index.js') }}"></script>
|
||||
<script src="{{ asset('lib/datatables/datatables.min.js') }}"></script>
|
||||
<script src="{{ asset('lib/datatables/datatables.init.js') }}"></script>
|
||||
<script src="{{ asset('lib/select2/select2.min.js') }}"></script>
|
||||
<script src="{{ asset('lib/select2/select2.init.js') }}"></script>
|
||||
<script src="{{ asset('lib/dropzone/dropzone.min.js') }}"></script>
|
||||
<script src="{{ asset('lib/imgareaselect/js/jquery.imgareaselect.dev.js') }}"></script>
|
||||
<script src="{{ asset('lib/jqueryui/jquery-ui.min.js') }}"></script>
|
||||
<script src="{{ asset('lib/chart/chart.js') }}"></script>
|
||||
|
||||
<script src="{{ asset('lib/app/app.js') }}"></script>
|
||||
|
||||
{% block javascripts %}
|
||||
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
{% block localstyle %}
|
||||
{% endblock %}
|
||||
|
||||
<body>
|
||||
{% if usemenu is defined and usemenu %}
|
||||
<nav class="navbar navbar-expand-lg bg-dark" data-bs-theme="dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{{ path('app_home') }}">
|
||||
<img src="{{asset("medias/logo/logo.png")}}"> {{appName}}
|
||||
</a>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarColor02">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="navbar-nav ms-auto ps-3 d-flex flex-row position-absolute" style="right: 15px; top:15px">
|
||||
{% if app.user %}
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
<a class="nav-link px-2" href="{{path('app_admin')}}"><i class="fa-solid fa-cog fa-2x"></i></a>
|
||||
{% endif %}
|
||||
<a class="nav-link px-2" href="{{path('app_user_profil')}}"><img src="{{asset(app.user.avatar)}}" class="avatar"></a>
|
||||
<a class="nav-link px-2" href="{{path('app_logout')}}"><i class="fa-solid fa-right-from-bracket fa-2x"></i></a>
|
||||
{% else %}
|
||||
<a class="nav-link px-2" href="{{path('app_login')}}"><i class="fa-solid fa-right-to-bracket fa-2x"></i></a>
|
||||
{% endif %}
|
||||
|
||||
<a class="navbar-toggler nav-link px-2" href="#" data-bs-toggle="collapse" data-bs-target="#navbarColor02" aria-controls="navbarColor02" aria-expanded="false" aria-label="Toggle navigation"><i class="fa-solid fa-bars fa-2x"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
<simplemain>
|
||||
{% block simplebody %}
|
||||
{% endblock %}
|
||||
</simplemain>
|
||||
|
||||
<main>
|
||||
{% if usesidebar is defined and usesidebar %}
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
<sidebar>
|
||||
<div>
|
||||
<span>GENERALE</span>
|
||||
|
||||
<a href="{{ path('app_admin_user') }}" title="Utilisateurs">
|
||||
<i class="fas fa-user fa-fw"></i>
|
||||
<span>Utilisateurs</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ path('app_admin_project') }}" title="Projets">
|
||||
<i class="fas fa-building fa-fw"></i>
|
||||
<span>Projets</span>
|
||||
</a>
|
||||
</div>
|
||||
</sidebar>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<content style="margin: 0px auto; {{ (maxwidth is defined ? "max-width:"~maxwidth~"px;":"") }}">
|
||||
{% block body %}
|
||||
{% endblock %}
|
||||
</content>
|
||||
</main>
|
||||
|
||||
<div class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 9999;">
|
||||
<div id="ajaxErrorToast" class="toast align-items-center text-bg-danger border-0" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body" id="ajaxErrorMessage">
|
||||
Une erreur est survenue.
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Fermer"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mymodal" class="modal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">INFORMATION</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<iframe id="framemodal" frameborder=0 width="100%" height="600px"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
{% block localscript %}
|
||||
{% endblock %}
|
||||
</html>
|
1
templates/home/blank.html.twig
Normal file
1
templates/home/blank.html.twig
Normal file
@ -0,0 +1 @@
|
||||
{% extends 'base.html.twig' %}
|
17
templates/home/home.html.twig
Normal file
17
templates/home/home.html.twig
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{%block body%}
|
||||
<h2>Projets</h2>
|
||||
|
||||
<div class='d-flex' style='justify-content: center'>
|
||||
{% for project in projects %}
|
||||
<div class='card'>
|
||||
<a href='{{ path('app_project_view',{id:project.id})}}' class='d-flex' style='flex-direction: column; text-align: center'>
|
||||
<img src='{{asset(project.logo)}}' style='width:250px'>
|
||||
<h5>{{project.title}}</h5>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
|
||||
{%endblock%}
|
13
templates/include/error.html.twig
Normal file
13
templates/include/error.html.twig
Normal file
@ -0,0 +1,13 @@
|
||||
{% for message in app.flashes('error') %}
|
||||
<div class="alert alert-danger mt-3">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% if form.vars.errors is not empty %}
|
||||
<div class="alert alert-danger mt-3">
|
||||
{% for error in form.vars.errors %}
|
||||
<li>{{ error.message }}</li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
46
templates/issue/edit.html.twig
Normal file
46
templates/issue/edit.html.twig
Normal file
@ -0,0 +1,46 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block localstyle %}
|
||||
<style>
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
= Modification Issue = {{title}}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>
|
||||
Modification Issue<br>
|
||||
</h1>
|
||||
<small class="text-muted" style="margin-top:-20px">{{title}}</small>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form.submit) }}
|
||||
<a href="{{ path(routecancel,{id:issue.project.id}) }}" class="btn btn-secondary ms-1">Annuler</a>
|
||||
|
||||
{% include('include/error.html.twig') %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mx-auto">
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">Information</div>
|
||||
<div class="card-body">
|
||||
{{ form_row(form.color) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#project_title").focus();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
465
templates/issue/list.html.twig
Normal file
465
templates/issue/list.html.twig
Normal file
@ -0,0 +1,465 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block localstyle %}
|
||||
<style>
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
= Liste des Issues
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>
|
||||
Liste des Issues
|
||||
</h1>
|
||||
|
||||
<div class="d-flex">
|
||||
<div style="width:320px; padding: 0px 10px 0px 0px">
|
||||
<div style="margin-bottom: 1em;">
|
||||
<button id="toggleView" class="btn btn-primary w-100"> Afficher Vue Graphique</button>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1em;">
|
||||
<label for="projectFilter"><strong>Filtrer par projet :</strong></label><br>
|
||||
<select id="projectFilter" multiple style="width: 100%; height: 150px;" class="select2">
|
||||
{# Options ajoutées dynamiquement #}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1em;">
|
||||
<label for="statusFilter"><strong>Filtrer par statut :</strong></label><br>
|
||||
<select id="statusFilter" multiple style="width: 100%; height: 150px;" class="select2">
|
||||
{# Options JS dynamiques #}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1em;">
|
||||
<label for="sprintFilter"><strong>Filtrer par sprint :</strong></label><br>
|
||||
<select id="sprintFilter" multiple style="width: 100%; height: 150px;" class="select2">
|
||||
{# Options JS dynamiques #}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1em;">
|
||||
<label for="versionFilter"><strong>Filtrer par version :</strong></label><br>
|
||||
<select id="versionFilter" multiple style="width: 100%; height: 150px;" class="select2">
|
||||
{# Options JS dynamiques #}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1em;">
|
||||
<label for="trackerFilter"><strong>Filtrer par tracker :</strong></label><br>
|
||||
<select id="trackerFilter" multiple style="width: 100%; height: 150px;" class="select2">
|
||||
{# Options JS dynamiques #}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1em;">
|
||||
<label for="categoryFilter"><strong>Filtrer par catégorie :</strong></label><br>
|
||||
<select id="categoryFilter" multiple style="width: 100%; height: 150px;" class="select2">
|
||||
{# Options JS dynamiques #}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1em;">
|
||||
<label for="assignedFilter"><strong>Filtrer par intervenant :</strong></label><br>
|
||||
<select id="assignedFilter" multiple style="width: 100%; height: 150px;" class="select2">
|
||||
{# Options JS dynamiques #}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="flex-grow:1">
|
||||
<div class="dataTable_wrapper">
|
||||
<table class="table table-striped table-bordered table-hover" id="dataTables" style="width:100%; zoom:80%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="70px">Id</th>
|
||||
<th width="130px">Projet</th>
|
||||
<th>Nom</th>
|
||||
<th>Statut</th>
|
||||
<th>Sprint</th>
|
||||
<th>Version</th>
|
||||
<th>Tracker</th>
|
||||
<th>Catégorie</th>
|
||||
<th>Affecté à</th>
|
||||
<th>Point</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for issue in issues %}
|
||||
<tr>
|
||||
<td>#{{issue.id}}</td>
|
||||
<td>{{issue.project.title}}</td>
|
||||
<td>{{issue.redmine.subject}}</td>
|
||||
<td>{{'%02d'|format(issue.rowStatus)}}-{{issue.redmine.status.name}}</td>
|
||||
<td>{{issue.sprintName}}</td>
|
||||
<td>{{issue.versionName}}</td>
|
||||
<td>{{issue.redmine.tracker.name}}</td>
|
||||
<td>{{issue.categoryName}}</td>
|
||||
<td>{{issue.assignedName}}</td>
|
||||
<td>{{issue.redmine.sprint.story_points}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="chartContainer" style="display:none; margin-top:2em;">
|
||||
<canvas id="statusChart" width="400" height="200"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
function restoreSelectValue(selectId) {
|
||||
const stored = localStorage.getItem(selectId);
|
||||
if (stored) {
|
||||
try {
|
||||
const values = JSON.parse(stored);
|
||||
$(`#${selectId}`).val(values).trigger('change');
|
||||
} catch (e) {
|
||||
console.error(`Erreur en restaurant le ${selectId}`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const idProject=1;
|
||||
const idStatus=3;
|
||||
const idSprint=4;
|
||||
const idVersion=5;
|
||||
const idTracker=6;
|
||||
const idCategory=7;
|
||||
const idAssigned=8;
|
||||
|
||||
const table = $('#dataTables').DataTable({
|
||||
columnDefs: [
|
||||
{ "targets": "no-sort", "orderable": false },
|
||||
{ "targets": "no-string", "type" : "num" }
|
||||
],
|
||||
responsive: true,
|
||||
iDisplayLength: 100,
|
||||
order: [[ 0, "desc" ]]
|
||||
});
|
||||
|
||||
// Étape 1 : Récupérer les filtres possible
|
||||
const projectSet = new Set();
|
||||
table.column(idProject).data().each(function(value) {
|
||||
projectSet.add(value);
|
||||
});
|
||||
const statusSet = new Set();
|
||||
table.column(idStatus).data().each(function(value) {
|
||||
statusSet.add(value);
|
||||
});
|
||||
const sprintSet = new Set();
|
||||
table.column(idSprint).data().each(function(value) {
|
||||
sprintSet.add(value);
|
||||
});
|
||||
const versionSet = new Set();
|
||||
table.column(idVersion).data().each(function(value) {
|
||||
versionSet.add(value);
|
||||
});
|
||||
const trackerSet = new Set();
|
||||
table.column(idTracker).data().each(function(value) {
|
||||
trackerSet.add(value);
|
||||
});
|
||||
const categorySet = new Set();
|
||||
table.column(idCategory).data().each(function(value) {
|
||||
categorySet.add(value);
|
||||
});
|
||||
const assignedSet = new Set();
|
||||
table.column(idAssigned).data().each(function(value) {
|
||||
assignedSet.add(value);
|
||||
});
|
||||
|
||||
// Étape 2 : Remplir dynamiquement les filtres
|
||||
const $selectProject = $('#projectFilter');
|
||||
Array.from(projectSet).sort().forEach(function(project) {
|
||||
const option = $('<option>', {
|
||||
value: project,
|
||||
text: project,
|
||||
selected: false
|
||||
});
|
||||
$selectProject.append(option);
|
||||
});
|
||||
const $statusSelect = $('#statusFilter');
|
||||
Array.from(statusSet).sort().forEach(function(status) {
|
||||
const option = $('<option>', {
|
||||
value: status,
|
||||
text: status,
|
||||
selected: false
|
||||
});
|
||||
$statusSelect.append(option);
|
||||
});
|
||||
const $sprintSelect = $('#sprintFilter');
|
||||
Array.from(sprintSet).sort().forEach(function(sprint) {
|
||||
const option = $('<option>', {
|
||||
value: sprint,
|
||||
text: (sprint?sprint:"Aucun"),
|
||||
selected: false
|
||||
});
|
||||
$sprintSelect.append(option);
|
||||
});
|
||||
const $versionSelect = $('#versionFilter');
|
||||
Array.from(versionSet).sort().forEach(function(version) {
|
||||
const option = $('<option>', {
|
||||
value: version,
|
||||
text: (version?version:"Aucune"),
|
||||
selected: false
|
||||
});
|
||||
$versionSelect.append(option);
|
||||
});
|
||||
const $trackerSelect = $('#trackerFilter');
|
||||
Array.from(trackerSet).sort().forEach(function(tracker) {
|
||||
const option = $('<option>', {
|
||||
value: tracker,
|
||||
text: tracker,
|
||||
selected: false
|
||||
});
|
||||
$trackerSelect.append(option);
|
||||
});
|
||||
const $categorySelect = $('#categoryFilter');
|
||||
Array.from(categorySet).sort().forEach(function(category) {
|
||||
const option = $('<option>', {
|
||||
value: category,
|
||||
text: (category?category:"Aucune"),
|
||||
selected: false
|
||||
});
|
||||
$categorySelect.append(option);
|
||||
});
|
||||
const $assignedSelect = $('#assignedFilter');
|
||||
Array.from(assignedSet).sort().forEach(function(assigned) {
|
||||
const option = $('<option>', {
|
||||
value: assigned,
|
||||
text: (assigned?assigned:"Aucun"),
|
||||
selected: false
|
||||
});
|
||||
$assignedSelect.append(option);
|
||||
});
|
||||
|
||||
// Etape 3 : restaurer les valeurs filtrés stocké en localstorage
|
||||
restoreSelectValue('projectFilter');
|
||||
restoreSelectValue('statusFilter');
|
||||
restoreSelectValue('sprintFilter');
|
||||
restoreSelectValue('versionFilter');
|
||||
restoreSelectValue('trackerFilter');
|
||||
restoreSelectValue('categoryFilter');
|
||||
restoreSelectValue('assignedFilter');
|
||||
|
||||
// Étape 4 : Ajouter le filtre personnalisé à DataTables
|
||||
$.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
|
||||
const selectedProjects = $('#projectFilter').val();
|
||||
const selectedStatuses = $('#statusFilter').val();
|
||||
const selectedSprints = $('#sprintFilter').val();
|
||||
const selectedVersions = $('#versionFilter').val();
|
||||
const selectedTrackers = $('#trackerFilter').val();
|
||||
const selectedCategories = $('#categoryFilter').val();
|
||||
const selectedAssigneds = $('#assignedFilter').val();
|
||||
|
||||
const project = data[idProject];
|
||||
const status = data[idStatus];
|
||||
const sprint = data[idSprint];
|
||||
const version = data[idVersion];
|
||||
const tracker = data[idTracker];
|
||||
const category = data[idCategory];
|
||||
const assigned = data[idAssigned];
|
||||
|
||||
const projectMatch = !selectedProjects || selectedProjects.length === 0 || selectedProjects.includes(project);
|
||||
const statusMatch = !selectedStatuses || selectedStatuses.length === 0 || selectedStatuses.includes(status);
|
||||
const sprintMatch = !selectedSprints || selectedSprints.length === 0 || selectedSprints.includes(sprint);
|
||||
const versionMatch = !selectedVersions || selectedVersions.length === 0 || selectedVersions.includes(version);
|
||||
const trackerMatch = !selectedTrackers || selectedTrackers.length === 0 || selectedTrackers.includes(tracker);
|
||||
const categoryMatch = !selectedCategories || selectedCategories.length === 0 || selectedCategories.includes(category);
|
||||
const assignedMatch = !selectedAssigneds || selectedAssigneds.length === 0 || selectedAssigneds.includes(assigned);
|
||||
|
||||
return projectMatch && statusMatch && sprintMatch && versionMatch && trackerMatch && categoryMatch && assignedMatch;
|
||||
});
|
||||
|
||||
|
||||
// Étape 5 : Rafraîchir le tableau quand le select change
|
||||
const filterIds = ['projectFilter', 'statusFilter', 'sprintFilter','versionFilter', 'trackerFilter', 'categoryFilter', 'assignedFilter'];
|
||||
filterIds.forEach(function(filterId) {
|
||||
$(`#${filterId}`).on('change', function() {
|
||||
const val = $(this).val();
|
||||
localStorage.setItem(filterId, JSON.stringify(val || []));
|
||||
table.draw();
|
||||
|
||||
// Si on est en vue graphique, mettre à jour le graphique
|
||||
if ($('#chartContainer').is(':visible')) {
|
||||
updateChart();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Etape 6 = chart
|
||||
let chartInstance = null;
|
||||
|
||||
function generateGradientColors(startColor, endColor, steps) {
|
||||
function hexToRgb(hex) {
|
||||
hex = hex.replace(/^#/, '');
|
||||
return {
|
||||
r: parseInt(hex.substring(0, 2), 16),
|
||||
g: parseInt(hex.substring(2, 4), 16),
|
||||
b: parseInt(hex.substring(4, 6), 16)
|
||||
};
|
||||
}
|
||||
|
||||
function rgbToHex(r, g, b) {
|
||||
return '#' + [r, g, b].map(x => {
|
||||
const hex = x.toString(16);
|
||||
return hex.length === 1 ? '0' + hex : hex;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
const start = hexToRgb(startColor);
|
||||
const end = hexToRgb(endColor);
|
||||
const gradient = [];
|
||||
|
||||
for (let i = 0; i < steps; i++) {
|
||||
const r = Math.round(start.r + ((end.r - start.r) * i) / (steps - 1));
|
||||
const g = Math.round(start.g + ((end.g - start.g) * i) / (steps - 1));
|
||||
const b = Math.round(start.b + ((end.b - start.b) * i) / (steps - 1));
|
||||
gradient.push(rgbToHex(r, g, b));
|
||||
}
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
$('#toggleView').on('click', function () {
|
||||
const isTableVisible = $('#dataTables').is(':visible');
|
||||
|
||||
if (isTableVisible) {
|
||||
// Passer en vue graphique
|
||||
$('#dataTables_wrapper').hide();
|
||||
$('#chartContainer').show();
|
||||
$(this).text('Afficher Vue Tableau');
|
||||
localStorage.setItem('currentView', 'chart');
|
||||
|
||||
if (!chartInstance) {
|
||||
drawChart();
|
||||
} else {
|
||||
updateChart();
|
||||
}
|
||||
} else {
|
||||
// Passer en vue tableau
|
||||
$('#chartContainer').hide();
|
||||
$('#dataTables_wrapper').show();
|
||||
$(this).text('Afficher Vue Graphique');
|
||||
localStorage.setItem('currentView', 'table');
|
||||
}
|
||||
});
|
||||
|
||||
function drawChart() {
|
||||
const dataByStatus = {};
|
||||
|
||||
// Parcours des lignes filtrées de DataTables
|
||||
table.rows({ filter: 'applied' }).every(function () {
|
||||
const data = this.data();
|
||||
const status = data[idStatus];
|
||||
const points = parseFloat(data[9]) || 0;
|
||||
|
||||
if (!dataByStatus[status]) {
|
||||
dataByStatus[status] = 0;
|
||||
}
|
||||
dataByStatus[status] += points;
|
||||
});
|
||||
|
||||
// Tri alphabétique des statuts
|
||||
const sortedLabels = Object.keys(dataByStatus).sort((a, b) =>
|
||||
a.localeCompare(b, 'fr', { sensitivity: 'base' })
|
||||
);
|
||||
|
||||
// Données triées
|
||||
const sortedDataPoints = sortedLabels.map(label => dataByStatus[label]);
|
||||
|
||||
// Dégradé du bleu vers le vert
|
||||
const gradientColors = generateGradientColors('#007bff', '#28a745', sortedLabels.length);
|
||||
|
||||
// Couleurs finales : rouge si "échec", sinon couleur du dégradé
|
||||
const colors = sortedLabels.map((label, index) => {
|
||||
const lowerLabel = label.toLowerCase();
|
||||
if (lowerLabel.includes('échec') || lowerLabel.includes('echec')) {
|
||||
return '#dc3545'; // rouge Bootstrap
|
||||
} else {
|
||||
return gradientColors[index];
|
||||
}
|
||||
});
|
||||
|
||||
// Création du graphique Chart.js
|
||||
const ctx = document.getElementById('statusChart').getContext('2d');
|
||||
chartInstance = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: sortedLabels,
|
||||
datasets: [{
|
||||
label: 'Points par Statut',
|
||||
data: sortedDataPoints,
|
||||
backgroundColor: colors,
|
||||
borderColor: colors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
return `${context.dataset.label}: ${context.parsed.y} points`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Total des Points'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Statuts'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateChart() {
|
||||
if (chartInstance) {
|
||||
chartInstance.destroy();
|
||||
chartInstance = null;
|
||||
}
|
||||
drawChart();
|
||||
}
|
||||
|
||||
const storedView = localStorage.getItem('currentView');
|
||||
if (storedView === 'chart') {
|
||||
$('#dataTables_wrapper').hide();
|
||||
$('#chartContainer').show();
|
||||
$('#toggleView').text('Afficher Vue Tableau');
|
||||
drawChart();
|
||||
} else {
|
||||
$('#chartContainer').hide();
|
||||
$('#dataTables_wrapper').show();
|
||||
$('#toggleView').text('Afficher Vue Graphique');
|
||||
}
|
||||
|
||||
|
||||
table.draw();
|
||||
updateChart();
|
||||
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
109
templates/issue/view.html.twig
Normal file
109
templates/issue/view.html.twig
Normal file
@ -0,0 +1,109 @@
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<div class="mb-0 d-flex" style="align-items:baseline">
|
||||
<h5 style="flex-grow:1">#{{ issue.redmine.id }} = {{ issue.redmine.subject }}</h5>
|
||||
<a href="{{redmineUrl}}/issues/{{issue.id}}" target="_blank" class="btn btn-primary me-1"><i class="fas fa-eye"></i></a>
|
||||
<a href="{{redmineUrl}}/issues/{{issue.id}}/edit" target="_blank" class="btn btn-primary me-1"><i class="fas fa-pencil"></i></a>
|
||||
<div class="btn btn-warning" onClick="hideIssue()"><i class="fas fa-window-close"></i></div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex">
|
||||
<small class="text-muted" style="flex-grow:1">Projet : {{ issue.redmine.project.name }} • Tracker : {{ issue.redmine.tracker.name }}</small>
|
||||
{% if issue.redmine.custom_fields is defined %}
|
||||
{% for field in issue.redmine.custom_fields %}
|
||||
{% if field.id==11 and field.value!="" %}
|
||||
<small class="text-muted"><strong>{{ field.name }} =</strong> <a href="{{field.value}}" target="_blank">{{field.value|split('/')|last}}</a></small>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<a href="{{path("app_issue_update",{id:issue.id})}}">Modifier</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="issueDescription card-body" style="height:500px;overflow-y:auto">
|
||||
<div class="d-flex">
|
||||
<div class="mb-3" style="flex-grow:0.5">
|
||||
<strong>Tracker =</strong> {{issue.redmine.tracker.name}}<br>
|
||||
<strong>Catégorie =</strong> {{issue.categoryName}}<br>
|
||||
<strong>Statut =</strong> {{ issue.redmine.status.name }}<br>
|
||||
<strong>Priorité =</strong> {{ issue.redmine.priority.name }}<br><br>
|
||||
<strong>Sprint =</strong> {{issue.sprintName}}<br>
|
||||
<strong>Version Cible =</strong> {{issue.versionName}}<br>
|
||||
<strong>Story Point =</strong> {{issue.redmine.sprint.story_points}}<br>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong>Auteur =</strong> {{ issue.redmine.author.name }}<br>
|
||||
<strong>Progression =</strong> {{ issue.redmine.done_ratio }}%<br>
|
||||
<strong>Créé le =</strong>{{ issue.redmine.created_on|date('d/m/Y H:i') }}<br>
|
||||
<strong>Mis à jour le =</strong>{{ issue.redmine.updated_on|date('d/m/Y H:i') }}<br>
|
||||
<strong>Date de début =</strong> {{ issue.redmine.start_date|date('d/m/Y') }}<br>
|
||||
<strong>Date de fin =</strong> {{ issue.redmine.due_date|date('d/m/Y') }}<br><br>
|
||||
|
||||
<strong>Affecté à =</strong> {{issue.assignedName}}<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if issue.redmine.custom_fields is defined %}
|
||||
{% for field in issue.redmine.custom_fields %}
|
||||
{% if field.id==32 %}
|
||||
<hr>
|
||||
<strong>{{ field.name }} </strong><br>
|
||||
{{ field.value|textile_to_html|raw }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if issue.parent %}
|
||||
<hr>
|
||||
<strong>Issues Parentes</strong>
|
||||
<div onClick='viewIssue({{ issue.parent.id }})' style="cursor:pointer">
|
||||
<small>#{{ issue.parent.id }} = {{ issue.parent.redmine.subject }}</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for child in issue.childs %}
|
||||
{% if loop.first %}
|
||||
<hr>
|
||||
<strong>Issues Liées</strong>
|
||||
{%endif%}
|
||||
<div onClick='viewIssue({{ child.id }})' style="cursor:pointer">
|
||||
<small>#{{ child.id }} = {{ child.redmine.subject }}</small>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if issue.redmine.description %}
|
||||
<div class="mb-3">
|
||||
<hr>
|
||||
<strong>Description :</strong>
|
||||
<p>{{ issue.redmine.description|textile_to_html|raw }}</p>
|
||||
<hr>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if issue.redmine.journals is defined %}
|
||||
{% for journal in issue.redmine.journals %}
|
||||
{% if journal.notes != "" %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Auteur =</strong> {{journal.user.name}}</strong><br>
|
||||
<small class="text-muted">Créé le =</strong>{{ journal.created_on|date('d/m/Y H:i') }}</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{ journal.notes |textile_to_html|raw }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<hr>
|
||||
|
||||
<br><br>
|
||||
</div>
|
||||
</div>
|
37
templates/label/edit.html.twig
Normal file
37
templates/label/edit.html.twig
Normal file
@ -0,0 +1,37 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %} = {{title}}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{title}}</h1>
|
||||
|
||||
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form.submit) }}
|
||||
<a href="{{ path(routecancel) }}" class="btn btn-secondary ms-1">Annuler</a>
|
||||
{%if mode=="update" %}<a href="{{ path(routedelete,{id:form.vars.value.id}) }}" class="btn btn-danger float-end" onclick="return confirm('Confirmez-vous la suppression de cet enregistrement ?')">Supprimer</a>{%endif%}
|
||||
|
||||
{% include('include/error.html.twig') %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mx-auto">
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">Information</div>
|
||||
<div class="card-body">
|
||||
{{ form_row(form.labelRedmine) }}
|
||||
{{ form_row(form.labelNinemine) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#user_username").focus();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
42
templates/label/list.html.twig
Normal file
42
templates/label/list.html.twig
Normal file
@ -0,0 +1,42 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %} = {{title}}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{title}}</h1>
|
||||
<a href="{{ path(routesubmit) }}" class="btn btn-success">Ajouter</a>
|
||||
|
||||
<div class="dataTable_wrapper">
|
||||
<table class="table table-striped table-bordered table-hover" id="dataTables" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="70px" class="no-sort">Action</th>
|
||||
<th class="no-sort">Label Redmine</th>
|
||||
<th>Label Ninemine</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for label in labels %}
|
||||
<tr>
|
||||
<td><a href="{{ path(routeupdate,{id:label.id}) }}"><i class="fas fa-file fa-2x"></i></a></td>
|
||||
<td>{{label.labelRedmine}}</td>
|
||||
<td>{{label.labelNinemine}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#dataTables').DataTable({
|
||||
columnDefs: [ { "targets": "no-sort", "orderable": false }, { "targets": "no-string", "type" : "num" } ],
|
||||
responsive: true,
|
||||
iDisplayLength: 100,
|
||||
order: [[ 2, "asc" ]]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
46
templates/project/edit.html.twig
Normal file
46
templates/project/edit.html.twig
Normal file
@ -0,0 +1,46 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %} = {{title}}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{title}}</h1>
|
||||
|
||||
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form.submit) }}
|
||||
<a href="{{ path(routecancel) }}" class="btn btn-secondary ms-1">Annuler</a>
|
||||
{%if mode=="update" %}<a href="{{ path(routedelete,{id:form.vars.value.id}) }}" class="btn btn-danger float-end" onclick="return confirm('Confirmez-vous la suppression de cet enregistrement ?')">Supprimer</a>{%endif%}
|
||||
|
||||
{% include('include/error.html.twig') %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mx-auto">
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">Information</div>
|
||||
<div class="card-body">
|
||||
{{ form_row(form.title) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mx-auto">
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">Permissions</div>
|
||||
<div class="card-body">
|
||||
{{ form_row(form.users) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#project_title").focus();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
47
templates/project/list.html.twig
Normal file
47
templates/project/list.html.twig
Normal file
@ -0,0 +1,47 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %} = {{title}}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{title}}</h1>
|
||||
<a href="{{ path(routesubmit) }}" class="btn btn-success">Ajouter</a>
|
||||
|
||||
<div class="dataTable_wrapper">
|
||||
<table class="table table-striped table-bordered table-hover" id="dataTables" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="100px" class="no-sort">Action</th>
|
||||
<th width="70px">Logo</th>
|
||||
<th width="70px">Modifier</th>
|
||||
<th>Nom</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for project in projects %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ path(routeupdate,{id:project.id}) }}" class="me-2"><i class="fas fa-file fa-2x"></i></a>
|
||||
<a href="{{ path('app_admin_project_reclone',{id:project.id}) }}" onclick="return confirm('Es-tu sûr de vouloir reinitialiser et reindexer le projet ?');"><i class="fas fa-rotate-right fa-2x"></i></a>
|
||||
</td>
|
||||
<td><img class="avatar" src="{{ asset(project.logo)}}"></td>
|
||||
<td>{{project.title}}</td>
|
||||
<td>{{project.redmine.updated_on}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#dataTables').DataTable({
|
||||
columnDefs: [ { "targets": "no-sort", "orderable": false }, { "targets": "no-string", "type" : "num" } ],
|
||||
responsive: true,
|
||||
iDisplayLength: 100,
|
||||
order: [[ 2, "asc" ]]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
43
templates/security/login.html.twig
Normal file
43
templates/security/login.html.twig
Normal file
@ -0,0 +1,43 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<form method="post">
|
||||
<img src="{{asset("/medias/logo/logo.png")}}" style="margin: 15px auto 15px auto; width:100px; display:block;">
|
||||
<center style="font-size:200%;margin-bottom:30px;">{{appName}}</center>
|
||||
<div class="card card-body m-auto" style="width:300px">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if app.user %}
|
||||
<div class="mb-3">
|
||||
Vous êtes déjà connecté avec le login {{ app.user.userIdentifier }} = <a href="{{ path('app_logout') }}">Logout</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="username">Login</label>
|
||||
<input class="form-control" type="text" value="{{ last_username }}" name="_username" id="username" class="form-control" autocomplete="username" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input class="form-control" type="password" name="_password" id="password" class="form-control" autocomplete="current-password" required>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
|
||||
|
||||
<button class="btn btn-lg btn-primary" type="submit">
|
||||
Valider
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$("#username").focus();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
48
templates/upload/crop01.html.twig
Normal file
48
templates/upload/crop01.html.twig
Normal file
@ -0,0 +1,48 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block localstyle %}
|
||||
<style>
|
||||
body {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<a class="btn btn-secondary" onClick="closeModal();">Annuler</a>
|
||||
<form action="{{ oneup_uploader_endpoint(endpoint) }}" class="dropzone" id="myDropzone" style="margin-top:10px">
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
Dropzone.options.myDropzone = {
|
||||
maxFiles: 1,
|
||||
acceptedMimeTypes: 'image/*',
|
||||
success: function(file, response){
|
||||
// Construction de l'url de retour
|
||||
url="{{ path('app_user_upload_crop02',{reportThumb: reportThumb, path:'xxx', file:'yyy'})|escape('js') }}";
|
||||
url=url.replace("xxx",response["path"]);
|
||||
url=url.replace("yyy",response["file"]);
|
||||
|
||||
// Navigation sur l'url de retour
|
||||
$(location).attr('href',url);
|
||||
},
|
||||
|
||||
|
||||
dictDefaultMessage: "Déposez vos fichiers ici pour les téléverser",
|
||||
dictFallbackMessage: "Votre navigateur ne supporte pas le téléversement de fichiers par glisser-déposer.",
|
||||
dictFallbackText: "Veuillez utiliser le formulaire ci-dessous pour téléverser vos fichiers.",
|
||||
dictFileTooBig: "Le fichier est trop volumineux .",
|
||||
dictInvalidFileType: "Vous ne pouvez pas téléverser des fichiers de ce type.",
|
||||
dictCancelUpload: "Annuler le téléversement",
|
||||
dictCancelUploadConfirmation: "Êtes-vous sûr de vouloir annuler ce téléversement ?",
|
||||
dictRemoveFile: "Supprimer le fichier",
|
||||
dictMaxFilesExceeded: "Vous ne pouvez pas téléverser plus de fichiers."
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
window.parent.$("#mymodal").modal('hide');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
83
templates/upload/crop02.html.twig
Normal file
83
templates/upload/crop02.html.twig
Normal file
@ -0,0 +1,83 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block localstyle %}
|
||||
<style>
|
||||
body {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form.submit) }} <a class="btn btn-secondary" onClick="closeModal();">Annuler</a>
|
||||
|
||||
<div id='preview' style='overflow:hidden; width:90px; height:90px; position: absolute; top: 0px; right: 10px;'>
|
||||
<img src="{{ asset(image) }}" style='position: relative;' alt='Thumbnail Preview' />
|
||||
</div>
|
||||
|
||||
<div style="margin-top:50px;">
|
||||
<img id="largeimg" src="{{ asset(image) }}">
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
function preview(img, selection) {
|
||||
var scaleX = 90 / selection.width;
|
||||
var scaleY = 90 / selection.height;
|
||||
|
||||
$('#preview img').css({
|
||||
width: Math.round(scaleX * $('#largeimg').width()) + 'px',
|
||||
height: Math.round(scaleY * $('#largeimg').height()) + 'px',
|
||||
marginLeft: '-' + Math.round(scaleX * selection.x1) + 'px',
|
||||
marginTop: '-' + Math.round(scaleY * selection.y1) + 'px'
|
||||
});
|
||||
$('#form_x1').val(selection.x1);
|
||||
$('#form_y1').val(selection.y1);
|
||||
$('#form_x2').val(selection.x2);
|
||||
$('#form_y2').val(selection.y2);
|
||||
$('#form_w').val(selection.width);
|
||||
$('#form_h').val(selection.height);
|
||||
}
|
||||
|
||||
function reportThumb() {
|
||||
window.parent.$("#{{reportThumb}}").val("{{thumb}}");
|
||||
url="{{ asset(thumb) }}";
|
||||
window.parent.$("#{{reportThumb}}_img").attr("src",url);
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
window.parent.$("#mymodal").modal('hide');
|
||||
}
|
||||
|
||||
$(window).on("load",function () {
|
||||
{% if toReport %}
|
||||
reportThumb();
|
||||
{% endif %}
|
||||
$('#largeimg').imgAreaSelect({ aspectRatio: '1:1', onSelectChange: preview });
|
||||
|
||||
|
||||
var selection = new Object();
|
||||
if($('#largeimg').height()<$('#largeimg').width()) {
|
||||
selection.width = $('#largeimg').height();
|
||||
selection.height = $('#largeimg').height();
|
||||
}
|
||||
else {
|
||||
selection.width = $('#largeimg').width();
|
||||
selection.height = $('#largeimg').width();
|
||||
}
|
||||
|
||||
selection.x1=0;
|
||||
selection.x2=0;
|
||||
selection.y1=0;
|
||||
selection.y2=0;
|
||||
|
||||
preview($('#largeimg'),selection);
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
65
templates/user/edit.html.twig
Normal file
65
templates/user/edit.html.twig
Normal file
@ -0,0 +1,65 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %} = {{title}}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{title}}</h1>
|
||||
|
||||
|
||||
{{ form_start(form) }}
|
||||
{{ form_widget(form.submit) }}
|
||||
<a href="{{ path(routecancel) }}" class="btn btn-secondary ms-1">Annuler</a>
|
||||
{%if mode=="update" %}<a href="{{ path(routedelete,{id:form.vars.value.id}) }}" class="btn btn-danger float-end" onclick="return confirm('Confirmez-vous la suppression de cet enregistrement ?')">Supprimer</a>{%endif%}
|
||||
|
||||
{% include('include/error.html.twig') %}
|
||||
|
||||
<div class="text-center d-flex flex-column align-items-center">
|
||||
<img id="user_avatar_img" src="{{asset(form.vars.value.avatar)}}" class="bigavatar mb-2">
|
||||
{{ form_row(form.avatar) }}
|
||||
<a class="btn btn-info" style="max-width:100%; margin-bottom:15px;" data-bs-toggle="modal" data-bs-target="#mymodal" onClick="ModalLoad('mymodal','Avatar','{{ path('app_user_upload_crop01',{endpoint:'avatar',reportThumb:'user_avatar'}) }}');" title='Ajouter un avatar'>Modifier</a>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mx-auto">
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">Information</div>
|
||||
<div class="card-body">
|
||||
{{ form_row(form.username) }}
|
||||
{%if form.password is defined%}
|
||||
{{ form_row(form.password) }}
|
||||
{% endif %}
|
||||
{{ form_row(form.email) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">Redmine</div>
|
||||
<div class="card-body">
|
||||
{{ form_row(form.apikey) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{%if form.roles is defined%}
|
||||
<div class="col-md-6">
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">Permissions</div>
|
||||
<div class="card-body">
|
||||
{{ form_row(form.roles) }}
|
||||
{{ form_row(form.projects) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#user_username").focus();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
54
templates/user/list.html.twig
Normal file
54
templates/user/list.html.twig
Normal file
@ -0,0 +1,54 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %} = {{title}}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{title}}</h1>
|
||||
<a href="{{ path(routesubmit) }}" class="btn btn-success">Ajouter</a>
|
||||
|
||||
<div class="dataTable_wrapper">
|
||||
<table class="table table-striped table-bordered table-hover" id="dataTables" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="70px" class="no-sort">Action</th>
|
||||
<th width="70px" class="no-sort">Avatar</th>
|
||||
<th>Login</th>
|
||||
<th>Rôles</th>
|
||||
<th>Compagnies</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td><a href="{{ path(routeupdate,{id:user.id}) }}"><i class="fas fa-file fa-2x"></i></a></td>
|
||||
<td><img class="avatar" src="{{ asset(user.avatar)}}"></td>
|
||||
<td>{{user.username}}</td>
|
||||
<td>
|
||||
{% for role in user.roles %}
|
||||
{{role}}<br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
{% for project in user.projects %}
|
||||
{{project.title}}<br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block localscript %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#dataTables').DataTable({
|
||||
columnDefs: [ { "targets": "no-sort", "orderable": false }, { "targets": "no-string", "type" : "num" } ],
|
||||
responsive: true,
|
||||
iDisplayLength: 100,
|
||||
order: [[ 2, "asc" ]]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user