{# # ============================================================================ # PAGE: MOODIA DASHBOARD # ============================================================================ # # Hub central listant les cours créés par l'utilisateur. # Redesigné avec Tailwind CSS en conservant le design original. # # ============================================================================ # DONNÉES REQUISES (depuis MoodiaController) # ============================================================================ # # | Variable | Type | Description | # |---------------|--------|--------------------------------------| # | courses | array | Liste des cours paginés | # | courseStats | array | {total, ...} | # | typeStats | array | {course, course_quiz, quiz_only} | # | typeFilter | string | Filtre actif (all, course, ...) | # | currentPage | int | Page courante | # | totalPages | int | Nombre total de pages | # | search | string | Recherche courante (?q=), '' si aucune | # # ============================================================================ #} {% extends 'base.html.twig' %} {% block title %}Dashboard - Moodia{% endblock %} {% block meta_description %}Gérez vos cours Moodle générés avec l'IA. Suivez vos brouillons et cours déployés.{% endblock %} {% block body %}
{# ==================================================================== EN-TÊTE DE PAGE (pattern Monalisia/Slidia) ==================================================================== #}
{# Titre avec icône produit #}
{% include '_atoms/icon/icon.html.twig' with { name: 'bubul-moodia', size: '3xl', colorPrimary: 'primary', colorSecondary: 'primary' } only %}

Moodia

Transformez un PDF ou une idée en cours Moodle complet, prêt à être déployé.

{% if medianTokenCost is defined and medianTokenCost > 0 %} {% endif %}
{# Boutons d'action #}
{# Toujours cliquable : le blocage tokens se fait à l'étape des 3 choix (méthodes IA). #} {% include '_atoms/button/button.html.twig' with { label: 'Créer un cours', variant: 'primary', size: 'lg', icon: 'plus', iconSize: 'lg', href: path('app_moodia_new'), radius: 'lg' } only %}
{# ==================================================================== CONTENU PRINCIPAL ==================================================================== #}
{% if courseStats.total > 0 %} {# ============================================================ FILTRES PAR TYPE + RECHERCHE ============================================================ #} {# Paramètre « q » à propager sur les onglets et la pagination : sans lui, un clic perdrait la recherche en cours (motif `qp` repris de templates/scormia/index.html.twig et templates/slidia/index.html.twig). #} {% set qp = search is not empty ? {'q': search} : {} %} {# shared--library-search porté par CE conteneur, pas par la racine : le contrôleur doit seulement être un ancêtre du champ (motif repris de templates/slidia/index.html.twig). Composant partagé `tool-toolbar` utilisé ici dans son comportement PAR DÉFAUT (onglets + recherche) : Moodia n'a pas de filtre supplémentaire à ajouter, donc pas de bloc `leading` à surcharger. #}
{% include '_organisms/tool-toolbar/tool-toolbar.html.twig' with { filters: [ { id: 'all', label: 'Tous', count: courseStats.total, href: path('app_moodia', qp|merge({type: 'all'})) }, { id: 'course', label: 'Cours', count: typeStats.course, href: path('app_moodia', qp|merge({type: 'course'})) }, { id: 'course_quiz', label: 'Cours + Quiz', count: typeStats.course_quiz, href: path('app_moodia', qp|merge({type: 'course_quiz'})) }, { id: 'quiz_only', label: 'Quiz', count: typeStats.quiz_only, href: path('app_moodia', qp|merge({type: 'quiz_only'})) } ], active: typeFilter, name: 'generationType', search: { id: 'moodia-search', placeholder: 'Rechercher un cours…', value: search, action: 'input->shared--library-search#search' } } only %}
{# ============================================================ GRILLE DE COURS (Redesign UX) ============================================================ #}
{% for course in courses %} {% set isDeployed = course.status == 'deployed' %} {% set genType = course.generationType %} {# Configuration couleurs par type #} {% set typeConfig = { 'deployed': { 'gradient': 'from-success to-emerald-400', 'icon': 'check-circle', 'iconColor': 'success', 'label': 'Déployé', 'labelClass': 'bg-success/10 text-success', 'hoverText': 'group-hover:text-success' }, 'course_quiz': { 'gradient': 'from-[#8B5CF6] to-[#A78BFA]', 'icon': 'check-circle', 'iconColor': 'info', 'label': 'Cours + Quiz', 'labelClass': 'bg-[#8B5CF6]/10 text-[#8B5CF6]', 'hoverText': 'group-hover:text-[#8B5CF6]' }, 'quiz_only': { 'gradient': 'from-warning to-amber-400', 'icon': 'help', 'iconColor': 'warning', 'label': 'Quiz', 'labelClass': 'bg-warning/10 text-amber-600', 'hoverText': 'group-hover:text-amber-600' }, 'course': { 'gradient': 'from-primary to-primary-light', 'icon': 'book', 'iconColor': 'primary', 'label': 'Cours', 'labelClass': 'bg-primary/10 text-primary', 'hoverText': 'group-hover:text-primary' } } %} {% set config = isDeployed ? typeConfig['deployed'] : (typeConfig[genType] ?? typeConfig['course']) %} {# Level config #} {% set levelLabels = { 'debutant': { 'num': 1, 'label': 'Débutant' }, 'intermediaire': { 'num': 2, 'label': 'Intermédiaire' }, 'avance': { 'num': 3, 'label': 'Avancé' }, 'expert': { 'num': 4, 'label': 'Expert' } } %} {% set levelData = levelLabels[course.level] ?? null %} {# ======== CARTE DE COURS (Design aéré + hauteur fixe) ======== #}
{# Bordure supérieure colorée au hover (animation scale) #}
{# Contenu principal (flex-grow pour pousser le footer en bas) #}
{# Header: Type label + Status #}
{{ config.label }} {% if isDeployed %} {% include '_atoms/icon/icon.html.twig' with { name: 'check', size: 'xs', color: 'success' } only %} Déployé {% endif %}
{# Titre (hauteur fixe pour 2 lignes) #}

{{ course.title }}

{# Stats inline #}

{% if genType == 'quiz_only' %} {{ course.questionsCount }} question{{ course.questionsCount > 1 ? 's' : '' }} {% else %} {{ course.sectionsCount }} section{{ course.sectionsCount > 1 ? 's' : '' }} · {{ course.activitiesCount }} activité{{ course.activitiesCount > 1 ? 's' : '' }} {% endif %}

{# Metadata row #}
{# Level indicator (style dots) #} {% if levelData %} {% include '_molecules/level-indicator/level-indicator.html.twig' with { level: levelData.num, maxLevel: 4, size: 'md', style: 'dots', variant: 'muted', showLabel: true, labels: ['Débutant', 'Intermédiaire', 'Avancé', 'Expert'] } only %} {% endif %} {# Durée #} {% if course.duration %} {% include '_atoms/icon/icon.html.twig' with { name: 'clock', size: 'sm', color: 'gray' } only %} {{ course.duration }}h {% endif %} {# Langue #} {% set langLabels = { 'fr': 'Français', 'en': 'English', 'es': 'Español', 'de': 'Deutsch', 'it': 'Italiano', 'pt': 'Português' } %} {% set courseLang = course.language|default('fr') %} {% include '_atoms/icon/icon.html.twig' with { name: 'earth', size: 'sm', color: 'gray' } only %} {{ langLabels[courseLang]|default('Français') }}
{# Footer avec date et consommation (toujours en bas) #}
{{ course.updatedAt|relative_date }}
{# Tokens #} {% if course.tokensUsed %} {% include '_atoms/icon/icon.html.twig' with { name: 'token', size: 'sm', color: 'primary' } only %} {{ course.tokensUsed|format_tokens }} {% endif %} {# Énergie #} {% if course.energyConsumptionWh %} {% include '_atoms/icon/icon.html.twig' with { name: 'bolt', size: 'sm', color: 'warning' } only %} {{ course.energyConsumptionWh|format_energy }} {% endif %}
{# Action-menu hors du pour éviter la navigation au clic #}
{% include '_molecules/action-menu/action-menu.html.twig' with { withTrigger: true, position: 'right-0 top-full mt-1', items: [ {label: 'Renommer', icon: 'edit', attr: { 'data-action': 'click->moodia--dashboard#openRenameModal', 'data-course-uuid': course.uuid, 'data-course-title': course.title, 'data-csrf-token': csrf_token('rename-course-' ~ course.uuid) }}, {label: 'Dupliquer', icon: 'copy', attr: { 'data-action': 'click->moodia--dashboard#duplicateCourse', 'data-course-uuid': course.uuid, 'data-csrf-token': csrf_token('duplicate-course-' ~ course.uuid) }} ], dangerItems: [ {label: 'Supprimer', icon: 'trash', attr: { 'data-action': 'click->moodia--dashboard#openDeleteModal', 'data-course-uuid': course.uuid, 'data-course-title': course.title, 'data-csrf-token': csrf_token('delete-course-' ~ course.uuid) }} ], accentHex: tool_color_details['moodia'].primary } only %}
{% endfor %}
{# ============================================================ AUCUN RÉSULTAT (recherche ou catégorie vide) ============================================================ Distinct de l'état vide ci-dessous : celui-ci est gouverné par courseStats.total, un compteur NON filtré (cf. MoodiaController::index()). Une recherche ou un filtre de type sans résultat tombe donc dans CETTE branche (bibliothèque non vide), pas dans l'état vide de la bibliothèque (motif repris de templates/scormia/index.html.twig). #} {% if courses is empty %}

{% if search is not empty %} Aucun cours ne correspond à votre recherche. {% else %} Aucun cours dans cette catégorie. {% endif %}

{% endif %} {# ============================================================ PAGINATION ============================================================ #} {% if totalPages > 1 %} {% include '_molecules/pagination/pagination.html.twig' with { currentPage: currentPage, totalPages: totalPages, baseUrl: path('app_moodia', qp|merge({type: typeFilter})), variant: 'moodia' } only %} {% endif %} {% else %} {# ============================================================ ÉTAT VIDE ============================================================ #}
{% include '_molecules/empty-state/empty-state.html.twig' with { title: 'Créez votre premier cours', description: 'Importez un PDF ou décrivez votre cours, et laissez Moodia générer automatiquement le contenu pédagogique pour Moodle.', icon: 'bubul-moodia', iconColor: 'primary', actionText: 'Commencer', actionHref: path('app_moodia_new'), actionIcon: 'plus', size: 'lg', variant: 'card' } only %}
{% endif %}
{# ============================================================ MODALE RENOMMAGE COURS ============================================================ #} {% include '_organisms/rename-modal/rename-modal.html.twig' with { title: 'Renommer le cours', placeholder: 'Titre du cours...', confirmLabel: 'Enregistrer', color: tool_color_details['moodia'], modalAttr: {'data-moodia--dashboard-target': 'renameModal', 'data-action': 'keydown.esc@window->moodia--dashboard#closeRenameModal'}, inputAttr: {'data-moodia--dashboard-target': 'renameInput', 'data-action': 'keydown.enter->moodia--dashboard#confirmRename'}, cancelAttr: {'data-action': 'click->moodia--dashboard#closeRenameModal'}, confirmAttr: {'data-action': 'click->moodia--dashboard#confirmRename'} } only %} {# ============================================================ MODALE SUPPRESSION COURS ============================================================ #} {% embed '_organisms/delete-modal/delete-modal.html.twig' with { modalAttr: {'data-moodia--dashboard-target': 'deleteModal', 'data-action': 'keydown.esc@window->moodia--dashboard#closeDeleteModal'}, cancelAttr: {'data-action': 'click->moodia--dashboard#closeDeleteModal'}, confirmAttr: {'data-action': 'click->moodia--dashboard#confirmDelete'} } only %} {% block titleBlock %}

Supprimer « » ?

{% endblock %} {% block description %}

Cette action est irréversible. Le cours et toutes ses sections, activités et quiz seront supprimés définitivement.

{% endblock %} {% endembed %}
{% endblock %}