mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Layout of saved export page
This commit is contained in:
parent
9f12b42961
commit
3d9b9ea672
@ -18,6 +18,7 @@ use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\ExportManager;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\MainBundle\Form\SavedExportType;
|
||||
use Chill\MainBundle\Repository\ExportGenerationRepository;
|
||||
use Chill\MainBundle\Repository\SavedExportRepositoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\ExportGenerationVoter;
|
||||
use Chill\MainBundle\Security\Authorization\SavedExportVoter;
|
||||
@ -46,6 +47,7 @@ final readonly class SavedExportController
|
||||
private Security $security,
|
||||
private TranslatorInterface $translator,
|
||||
private UrlGeneratorInterface $urlGenerator,
|
||||
private ExportGenerationRepository $exportGenerationRepository,
|
||||
) {}
|
||||
|
||||
#[Route(path: '/{_locale}/exports/saved/{id}/delete', name: 'chill_main_export_saved_delete')]
|
||||
@ -172,7 +174,10 @@ final readonly class SavedExportController
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
$exports = $this->savedExportRepository->findByUser($user, ['title' => 'ASC']);
|
||||
$exports = array_filter(
|
||||
$this->savedExportRepository->findSharedWithUser($user, ['exportAlias' => 'ASC', 'title' => 'ASC']),
|
||||
fn (SavedExport $savedExport): bool => $this->security->isGranted(SavedExportVoter::GENERATE, $savedExport),
|
||||
);
|
||||
|
||||
// group by center
|
||||
/** @var array<string, array{saved: SavedExport, export: ExportInterface}> $exportsGrouped */
|
||||
@ -189,12 +194,20 @@ final readonly class SavedExportController
|
||||
|
||||
ksort($exportsGrouped);
|
||||
|
||||
// get last executions
|
||||
$lastExecutions = [];
|
||||
foreach ($exports as $savedExport) {
|
||||
$lastExecutions[$savedExport->getId()->toString()] = $this->exportGenerationRepository
|
||||
->findExportGenerationBySavedExportAndUser($savedExport, $user, 5);
|
||||
}
|
||||
|
||||
return new Response(
|
||||
$this->templating->render(
|
||||
'@ChillMain/SavedExport/index.html.twig',
|
||||
[
|
||||
'grouped_exports' => $exportsGrouped,
|
||||
'total' => \count($exports),
|
||||
'last_executions' => $lastExecutions,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -13,8 +13,10 @@ namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\SavedExport;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
/**
|
||||
@ -55,6 +57,30 @@ class SavedExportRepository implements SavedExportRepositoryInterface
|
||||
->where($qb->expr()->eq('se.user', ':user'))
|
||||
->setParameter('user', $user);
|
||||
|
||||
return $this->prepareResult($qb, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
public function findSharedWithUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array
|
||||
{
|
||||
$qb = $this->repository->createQueryBuilder('se');
|
||||
|
||||
$qb
|
||||
->where(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->eq('se.user', ':user'),
|
||||
$qb->expr()->isMemberOf(':user', 'se.sharedWithUsers'),
|
||||
$qb->expr()->exists(
|
||||
sprintf('SELECT 1 FROM %s ug WHERE ug MEMBER OF se.sharedWithGroups AND :user MEMBER OF ug.users', UserGroup::class)
|
||||
)
|
||||
)
|
||||
)
|
||||
->setParameter('user', $user);
|
||||
|
||||
return $this->prepareResult($qb, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
private function prepareResult(QueryBuilder $qb, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array
|
||||
{
|
||||
if (null !== $limit) {
|
||||
$qb->setMaxResults($limit);
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ interface SavedExportRepositoryInterface extends ObjectRepository
|
||||
*/
|
||||
public function findByUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
public function findSharedWithUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array;
|
||||
|
||||
public function findOneBy(array $criteria): ?SavedExport;
|
||||
|
||||
public function getClassName(): string;
|
||||
|
@ -392,4 +392,24 @@ Toutes les classes btn-* de bootstrap sont fonctionnelles
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h1>Badges</h1>
|
||||
|
||||
<p>
|
||||
<span class="badge bg-primary">Primary</span>
|
||||
<span class="badge bg-secondary">Secondary</span>
|
||||
<span class="badge bg-success">Success</span>
|
||||
<span class="badge bg-danger">Danger</span>
|
||||
<span class="badge bg-warning">Warning</span>
|
||||
<span class="badge bg-info">Info</span>
|
||||
<span class="badge bg-light">Light</span>
|
||||
<span class="badge bg-dark">Dark</span>
|
||||
<span class="badge bg-chill-blue">chill-blue</span>
|
||||
<span class="badge bg-chill-green">chill-green</span>
|
||||
<span class="badge bg-chill-yellow">chill-yellow</span>
|
||||
<span class="badge bg-chill-orange">chill-orange</span>
|
||||
<span class="badge bg-chill-red">chill-red</span>
|
||||
<span class="badge bg-chill-beige">chill-beige</span>
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -6,7 +6,7 @@
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ chill_path_forward_return_path('chill_main_export_saved_list_my') }}" class="nav-link {% if current == 'my' %}active{% endif %}">
|
||||
{{ 'saved_export.My saved exports'|trans }}
|
||||
{{ 'saved_export.Saved exports'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
@ -1,14 +1,74 @@
|
||||
{% extends "@ChillMain/layout.html.twig" %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_saved_export_button') }}
|
||||
<style lang="css">
|
||||
.export-title {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_saved_export_button') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{{ 'saved_export.My saved exports'|trans }}{% endblock %}
|
||||
{% block title %}{{ 'saved_export.Saved exports'|trans }}{% endblock %}
|
||||
|
||||
{% macro render_export_card(saved, export, export_alias, generations) %}
|
||||
<div class="col">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
{{ export.title|trans }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ saved.title }}</h2>
|
||||
{% if app.user is same as saved.createdBy %}
|
||||
<p class="card-text tags">
|
||||
{% if app.user is same as saved.createdBy %}<span class="badge bg-primary">{{ 'saved_export.Owner'|trans }}</span>{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
<p class="card-text my-3">{{ saved.description|chill_markdown_to_html }}</p>
|
||||
</div>
|
||||
{% if generations|length > 0 %}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for generation in generations %}
|
||||
<li class="list-group-item">
|
||||
<a href="{{ chill_path_add_return_path('chill_main_export-generation_wait', {'id': generation.id}) }}">{{ generation.createdAt|format_datetime('short', 'short') }}</a>
|
||||
{% if generation.status == 'pending' %}
|
||||
<span class="badge bg-info">{{ 'export.generation.Export generation is pending_short'|trans }}</span>
|
||||
{% elseif generation.status == 'failure' %}
|
||||
<span class="badge bg-warning">{{ 'export.generation.Error_short'|trans }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<div class="card-footer">
|
||||
<ul class="record_actions slim">
|
||||
<li>
|
||||
<div class="" data-generate-export-button data-saved-export-uuid="{{ saved.id|escape('html_attr') }}"></div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{{ 'Actions'|trans }}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ chill_path_add_return_path('chill_main_export_saved_edit', {'id': saved.id }) }}" class="dropdown-item"><i class="fa fa-pencil"></i> {{ 'saved_export.update_title_and_description'|trans }}</a></li>
|
||||
<li><a href="{{ chill_path_add_return_path('chill_main_export_new', {'alias': saved.exportAlias,'from_saved': saved.id }) }}" class="dropdown-item"><i class="fa fa-pencil"></i> {{ 'saved_export.update_filters_aggregators_and_execute'|trans }}</a></li>
|
||||
<li><a href="{{ chill_path_add_return_path('chill_main_export_saved_delete', {'id': saved.id }) }}" class="dropdown-item"><i class="fa fa-trash"></i> {{ 'Delete'|trans }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-10 exports-list">
|
||||
@ -23,73 +83,23 @@
|
||||
|
||||
{% for group, saveds in grouped_exports %}
|
||||
{% if group != '_' %}
|
||||
<h2 class="display-6">{{ group }}</h2>
|
||||
<div class="row flex-bloc">
|
||||
<h1 class="display-6 export-title">{{ group }}</h1>
|
||||
<div class="row row-cols-1 row-cols-md-3 g-2">
|
||||
{% for s in saveds %}
|
||||
<div class="item-bloc">
|
||||
<div class="item-row card-body">
|
||||
<p class="card-subtitle"><strong>{{ s.export.title|trans }}</strong></p>
|
||||
<h2 class="card-title">{{ s.saved.title }}</h2>
|
||||
|
||||
<div class="createdBy">{{ 'saved_export.Created on %date%'|trans({'%date%': s.saved.createdAt|format_datetime('long', 'short')}) }}</div>
|
||||
|
||||
<div class="card-text my-3">
|
||||
{{ s.saved.description|chill_markdown_to_html }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<div class="" data-generate-export-button data-saved-export-uuid="{{ s.saved.id|escape('html_attr') }}"></div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{{ 'Actions'|trans }}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ chill_path_add_return_path('chill_main_export_saved_edit', {'id': s.saved.id }) }}" class="dropdown-item"><i class="fa fa-pencil"></i> {{ 'saved_export.update_title_and_description'|trans }}</a></li>
|
||||
<li><a href="{{ chill_path_add_return_path('chill_main_export_new', {'alias': s.saved.exportAlias,'from_saved': s.saved.id }) }}" class="dropdown-item"><i class="fa fa-pencil"></i> {{ 'saved_export.update_filters_aggregators_and_execute'|trans }}</a></li>
|
||||
<li><a href="{{ chill_path_add_return_path('chill_main_export_saved_delete', {'id': s.saved.id }) }}" class="dropdown-item"><i class="fa fa-trash"></i> {{ 'Delete'|trans }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ _self.render_export_card(s.saved, s.export, s.saved.exportAlias, last_executions[s.saved.id.toString()]) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if grouped_exports|keys|length > 1 and grouped_exports['_']|default([])|length > 0 %}
|
||||
<h2 class="display-6">{{ 'Ungrouped exports'|trans }}</h2>
|
||||
<h2 class="display-6 export-title">{{ 'Ungrouped exports'|trans }}</h2>
|
||||
{% endif %}
|
||||
|
||||
<div class="row flex-bloc">
|
||||
{% for saveds in grouped_exports['_']|default([]) %}
|
||||
{% for s in saveds %}
|
||||
<div class="item-bloc">
|
||||
<div class="item-row card-body">
|
||||
<p class="card-subtitle"><strong>{{ s.export.title|trans }}</strong></p>
|
||||
<h2 class="card-title">{{ s.saved.title }}</h2>
|
||||
|
||||
<div class="createdBy">{{ 'saved_export.Created on %date%'|trans({'%date%': s.saved.createdAt|format_datetime('long', 'short')}) }}</div>
|
||||
|
||||
<div class="card-text my-3">
|
||||
{{ s.saved.description|chill_markdown_to_html }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul class="record_actions">
|
||||
<li><a href="{{ chill_path_add_return_path('chill_main_export_saved_delete', {'id': s.saved.id }) }}" class="btn btn-delete"></a></li>
|
||||
<li><a href="{{ chill_path_add_return_path('chill_main_export_saved_edit', {'id': s.saved.id }) }}" class="btn btn-edit"></a></li>
|
||||
<li><a href="{{ path('chill_main_export_generate_from_saved', { id: s.saved.id }) }}" class="btn btn-action"><i class="fa fa-cog"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ _self.render_export_card(s.saved, s.export, s.saved.exportAlias, last_executions[s.saved.id.toString()]) }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
@ -11,6 +11,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Export\DirectExportInterface;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\ExportManager;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
@ -20,7 +23,7 @@ class ChillExportVoter extends Voter
|
||||
|
||||
private readonly VoterHelperInterface $helper;
|
||||
|
||||
public function __construct(VoterHelperFactoryInterface $voterHelperFactory)
|
||||
public function __construct(VoterHelperFactoryInterface $voterHelperFactory, ExportManager $exportManager)
|
||||
{
|
||||
$this->helper = $voterHelperFactory
|
||||
->generate(self::class)
|
||||
@ -30,6 +33,13 @@ class ChillExportVoter extends Voter
|
||||
|
||||
protected function supports($attribute, $subject): bool
|
||||
{
|
||||
if (
|
||||
($subject instanceof ExportInterface or $subject instanceof DirectExportInterface)
|
||||
&& $attribute === $subject->requiredRole()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->helper->supports($attribute, $subject);
|
||||
}
|
||||
|
||||
|
@ -787,7 +787,7 @@ saved_export:
|
||||
Edit: Modifier un export enregistré
|
||||
Delete saved ?: Supprimer un export enregistré ?
|
||||
Are you sure you want to delete this saved ?: Êtes-vous sûr·e de vouloir supprimer cet export ?
|
||||
My saved exports: Mes exports enregistrés
|
||||
Saved exports: Exports enregistrés
|
||||
Export is deleted: L'export est supprimé
|
||||
Saved export is saved!: L'export est enregistré
|
||||
Created on %date%: Créé le %date%
|
||||
@ -795,6 +795,7 @@ saved_export:
|
||||
update_filters_aggregators_and_execute: Modifier les filtres et regroupements et télécharger
|
||||
execute: Générer
|
||||
Update existing: Mettre à jour le rapport enregistré existant
|
||||
Owner: Propriétaire
|
||||
|
||||
absence:
|
||||
# single letter for absence
|
||||
|
Loading…
x
Reference in New Issue
Block a user