put activities in an ajax call
This commit is contained in:
parent
9af81b1d2c
commit
b5d56f1d85
|
|
@ -4,10 +4,24 @@ import {TabulatorFull as Tabulator} from 'tabulator-tables';
|
||||||
import {eyeIconLink, TABULATOR_FR_LANG} from "../js/global.js";
|
import {eyeIconLink, TABULATOR_FR_LANG} from "../js/global.js";
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static values = {aws: String};
|
static values = {aws: String,
|
||||||
|
id: String,
|
||||||
|
activities: Boolean,
|
||||||
|
table: Boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
static targets = ["activityList", "emptyMessage"]
|
||||||
connect() {
|
connect() {
|
||||||
this.table();
|
if(this.activitiesValue){
|
||||||
|
this.loadActivities();
|
||||||
|
setInterval(() => {
|
||||||
|
this.loadActivities();
|
||||||
|
}, 5000); // Refresh every 60 seconds
|
||||||
|
}
|
||||||
|
if (this.tableValue){
|
||||||
|
this.table();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table(){
|
table(){
|
||||||
|
|
@ -82,4 +96,59 @@ export default class extends Controller {
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadActivities() {
|
||||||
|
try {
|
||||||
|
// 1. Fetch the data using the ID from values
|
||||||
|
const response = await fetch(`/actions/organization/${this.idValue}/activities-ajax`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
const activities = await response.json();
|
||||||
|
|
||||||
|
// 2. Render
|
||||||
|
this.renderActivities(activities);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching activities:', error);
|
||||||
|
this.activityListTarget.innerHTML = `<div class="text-danger">Erreur lors du chargement.</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderActivities(activities) {
|
||||||
|
// Clear the loading spinner
|
||||||
|
this.activityListTarget.innerHTML = '';
|
||||||
|
|
||||||
|
if (activities.length === 0) {
|
||||||
|
// Show empty message
|
||||||
|
this.activityListTarget.innerHTML = this.emptyMessageTarget.innerHTML;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through JSON and build HTML
|
||||||
|
const html = activities.map(activity => {
|
||||||
|
return `
|
||||||
|
<div class="card shadow-sm mb-3 border-0 bg-white rounded-end"
|
||||||
|
style="border-left: 6px solid ${activity.color} !important;">
|
||||||
|
|
||||||
|
<div class="card-header bg-transparent border-0 pb-0 pt-3">
|
||||||
|
<h6 class="text-muted text-uppercase fw-bold mb-0" style="font-size: 0.85rem;">
|
||||||
|
${activity.date}
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body pt-2 pb-4">
|
||||||
|
<div class="card-text fs-5 lh-sm">
|
||||||
|
<span class="fw-bold text-dark">${activity.userName}</span>
|
||||||
|
<div class="text-secondary mt-1">${activity.actionType}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
this.activityListTarget.innerHTML = html;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Actions;
|
||||||
|
use App\Entity\Organizations;
|
||||||
|
use App\Service\ActionService;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
#[Route(path: '/actions', name: 'actions_')]
|
||||||
|
class ActionController extends AbstractController
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private EntityManagerInterface $entityManager,
|
||||||
|
private ActionService $actionService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/organization/{id}/activities-ajax', name: 'app_organization_activities_ajax', methods: ['GET'])]
|
||||||
|
public function fetchActivitiesAjax(Organizations $organization): JsonResponse
|
||||||
|
{
|
||||||
|
$actions = $this->entityManager->getRepository(Actions::class)->findBy(
|
||||||
|
['Organization' => $organization],
|
||||||
|
['date' => 'DESC'],
|
||||||
|
15
|
||||||
|
);
|
||||||
|
$formattedActivities = $this->actionService->formatActivities($actions);
|
||||||
|
|
||||||
|
return new JsonResponse($formattedActivities);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -40,11 +40,11 @@ readonly class ActionService
|
||||||
{
|
{
|
||||||
return array_map(function (Actions $activity) {
|
return array_map(function (Actions $activity) {
|
||||||
return [
|
return [
|
||||||
'date' => $activity->getDate(),
|
'date' => $activity->getDate()->format('d/m/Y H:i'),
|
||||||
'actionType' => $activity->getActionType(),
|
'actionType' => $activity->getActionType(),
|
||||||
'users' => $activity->getUsers(),
|
'userName' => $activity->getUsers()->getName(),
|
||||||
'organization' => $activity->getOrganization(),
|
// 'organization' => $activity->getOrganization(),
|
||||||
'description' => $activity->getDescription(),
|
// 'description' => $activity->getDescription(),
|
||||||
'color' => $this->getActivityColor($activity->getDate())
|
'color' => $this->getActivityColor($activity->getDate())
|
||||||
];
|
];
|
||||||
}, $activities);
|
}, $activities);
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
{% block body %}
|
|
||||||
|
|
||||||
|
|
||||||
<div class="card border-0">
|
|
||||||
<div class="card-header d-flex justify-content-between align-items-center border-0">
|
|
||||||
<h3>{{ title }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
{% if activities|length == 0 %}
|
|
||||||
<p>Aucune activité récente.</p>
|
|
||||||
{% else %}
|
|
||||||
{% set sortedActivities = activities|sort((a, b) => a.date <=> b.date)|reverse %}
|
|
||||||
<ul class="list-group gap-2">
|
|
||||||
{% for activity in sortedActivities%}
|
|
||||||
{% include 'user/organization/userActivity.html.twig' with {
|
|
||||||
activityTime: activity.date,
|
|
||||||
action: activity.actionType,
|
|
||||||
userName: activity.users.name,
|
|
||||||
color: activity.color
|
|
||||||
} %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
<div id="tabulator-org" data-controller="organization"
|
<div id="tabulator-org" data-controller="organization"
|
||||||
|
data-organization-table-value="true"
|
||||||
data-organization-data-value="{{ organizationsData|json_encode(constant("JSON_UNESCAPED_UNICODE"))|e("html_attr") }}"
|
data-organization-data-value="{{ organizationsData|json_encode(constant("JSON_UNESCAPED_UNICODE"))|e("html_attr") }}"
|
||||||
data-organization-aws-value="{{ aws_url }}"></div>
|
data-organization-aws-value="{{ aws_url }}"></div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{# single row so that activity and users tabs are next to each other#}
|
{# single row so that activity and users tabs are next to each other #}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{# User tables #}
|
{# User tables #}
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<div class="row mb-3 d-flex gap-2 ">
|
<div class="row mb-3 d-flex gap-2 ">
|
||||||
<div class="col mb-3 card no-header-bg">
|
<div class="col mb-3 card no-header-bg">
|
||||||
|
|
@ -49,7 +49,8 @@
|
||||||
<h2>
|
<h2>
|
||||||
Nouveaux utilisateurs
|
Nouveaux utilisateurs
|
||||||
</h2>
|
</h2>
|
||||||
<a href="{{ path('user_new', {'organizationId': organization.id}) }}" class="btn btn-primary">Ajouter un utilisateur</a>
|
<a href="{{ path('user_new', {'organizationId': organization.id}) }}"
|
||||||
|
class="btn btn-primary">Ajouter un utilisateur</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="tabulator-userListSmall" data-controller="user"
|
<div id="tabulator-userListSmall" data-controller="user"
|
||||||
|
|
@ -93,7 +94,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# APPLICATION ROW #}
|
{# APPLICATION ROW #}
|
||||||
{# TODO: Weird gap not going away#}
|
{# TODO: Weird gap not going away #}
|
||||||
<div class="row mb-3 ">
|
<div class="row mb-3 ">
|
||||||
{% for application in applications %}
|
{% for application in applications %}
|
||||||
<div class="col-6 mb-3">
|
<div class="col-6 mb-3">
|
||||||
|
|
@ -104,17 +105,37 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{# Activities col#}
|
{# Activities col #}
|
||||||
<div class="col-3 m-auto">
|
<div class="col-3 m-auto">
|
||||||
{% include 'organization/activity.html.twig' with {
|
<div class="card border-0"
|
||||||
title: 'Activités récentes',
|
data-controller="organization"
|
||||||
empty_message: 'Aucune activité récente.'
|
data-organization-activities-value = "true"
|
||||||
} %}
|
data-organization-id-value="{{ organization.id }}">
|
||||||
|
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center border-0">
|
||||||
|
<h3>Activité récente</h3>
|
||||||
|
|
||||||
|
<button class="btn btn-sm btn-outline-secondary" data-action="organization#loadActivities">
|
||||||
|
<i class="fas fa-sync"></i> Rafraîchir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body bg-light">
|
||||||
|
<div class="d-flex flex-column" data-organization-target="activityList">
|
||||||
|
<div class="text-center text-muted p-5">
|
||||||
|
<span class="spinner-border" aria-hidden="true"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# Empty state #}
|
||||||
|
<div class="d-none" data-organization-target="emptyMessage">
|
||||||
|
<div class="alert alert-light text-center shadow-sm">Aucune activité récente.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{# Ne pas enlever le 2ème /div#}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
{% block body %}
|
|
||||||
|
|
||||||
<div class="card border">
|
|
||||||
<div class="card-header d-flex align-items-center border-0">
|
|
||||||
<div class="row align-items-center">
|
|
||||||
|
|
||||||
<h4 class="mb-0">
|
|
||||||
<span style="display:inline-block; width:16px; height:16px; border-radius:50%; background:{{ color }}; margin-right:10px;"></span>
|
|
||||||
{{ activityTime|ago }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<p>{{ userName }} - {{ action }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
Loading…
Reference in New Issue