Initial commit
This commit is contained in:
212
templates/projects/detail.html
Normal file
212
templates/projects/detail.html
Normal file
@@ -0,0 +1,212 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ project.title }} — {{ config.seo.site_title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<!-- ─── En-tête projet ───────────────────────────────────────────────────── -->
|
||||
<section class="project-hero">
|
||||
<div class="project-hero-container">
|
||||
<nav class="breadcrumb">
|
||||
<a href="{% url 'projects:home' %}">Accueil</a>
|
||||
<span class="breadcrumb-sep">›</span>
|
||||
<a href="{% url 'projects:list' %}">Projets</a>
|
||||
<span class="breadcrumb-sep">›</span>
|
||||
<span>{{ project.title }}</span>
|
||||
</nav>
|
||||
<div class="project-hero-content">
|
||||
<div class="project-meta-top">
|
||||
<span class="project-category-badge">{{ project.category }}</span>
|
||||
{% if project.context %}<span class="project-context-badge">{{ project.context }}</span>{% endif %}
|
||||
{% if project.complexity %}<span class="complexity-badge complexity-{{ project.complexity|lower }}">{{ project.complexity }}</span>{% endif %}
|
||||
</div>
|
||||
<h1 class="project-hero-title">{{ project.title }}</h1>
|
||||
<p class="project-hero-desc">{{ project.short_description }}</p>
|
||||
<div class="project-techs-header">
|
||||
{% for tech in project.technologies %}
|
||||
<span class="tech-tag tech-tag-lg">{{ tech }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if project.github_url or project.demo_url %}
|
||||
<div class="project-links">
|
||||
{% if project.github_url %}
|
||||
<a href="{{ project.github_url }}" target="_blank" class="btn btn-outline btn-sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/></svg>
|
||||
Voir le code
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if project.demo_url %}
|
||||
<a href="{{ project.demo_url }}" target="_blank" class="btn btn-primary btn-sm">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||||
Voir la démo
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Contenu détaillé ─────────────────────────────────────────────────── -->
|
||||
<section class="section">
|
||||
<div class="detail-container">
|
||||
<div class="detail-main">
|
||||
|
||||
<!-- Galerie images -->
|
||||
{% if project.images %}
|
||||
<div class="gallery-section">
|
||||
<h2 class="detail-section-title">Galerie</h2>
|
||||
<div class="gallery-grid">
|
||||
{% for img in project.images %}
|
||||
<div class="gallery-item" onclick="openLightbox('{{ img }}')">
|
||||
<img src="{{ img }}" alt="{{ project.title }} — image {{ forloop.counter }}" loading="lazy">
|
||||
<div class="gallery-overlay">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Problématique & Solution -->
|
||||
{% if project.problem or project.solution %}
|
||||
<div class="problem-solution-grid">
|
||||
{% if project.problem %}
|
||||
<div class="ps-card ps-problem">
|
||||
<div class="ps-icon">⚡</div>
|
||||
<h3 class="ps-title">Problématique</h3>
|
||||
<p class="ps-text">{{ project.problem }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if project.solution %}
|
||||
<div class="ps-card ps-solution">
|
||||
<div class="ps-icon">✓</div>
|
||||
<h3 class="ps-title">Solution apportée</h3>
|
||||
<p class="ps-text">{{ project.solution }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Description complète -->
|
||||
<div class="description-section">
|
||||
<h2 class="detail-section-title">Description du projet</h2>
|
||||
<div class="description-content">
|
||||
{{ project.full_description|linebreaks }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Résultat -->
|
||||
{% if project.outcome %}
|
||||
<div class="outcome-section">
|
||||
<h2 class="detail-section-title">Résultat</h2>
|
||||
<div class="outcome-card">
|
||||
<span class="outcome-icon">🎯</span>
|
||||
<p>{{ project.outcome }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ─── Sidebar ──────────────────────────────────────────────────── -->
|
||||
<aside class="detail-sidebar">
|
||||
|
||||
<!-- Infos clés -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
Informations
|
||||
</h3>
|
||||
<ul class="info-list">
|
||||
{% if project.context %}
|
||||
<li class="info-item"><span class="info-label">Contexte</span><span class="info-value">{{ project.context }}</span></li>
|
||||
{% endif %}
|
||||
{% if project.period %}
|
||||
<li class="info-item"><span class="info-label">Période</span><span class="info-value">{{ project.period }}</span></li>
|
||||
{% endif %}
|
||||
{% if project.role %}
|
||||
<li class="info-item"><span class="info-label">Rôle</span><span class="info-value">{{ project.role }}</span></li>
|
||||
{% endif %}
|
||||
{% if project.complexity %}
|
||||
<li class="info-item"><span class="info-label">Complexité</span><span class="info-value">{{ project.complexity }}</span></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Points clés -->
|
||||
{% if project.highlights %}
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
|
||||
Points clés
|
||||
</h3>
|
||||
<ul class="highlights-list">
|
||||
{% for highlight in project.highlights %}
|
||||
<li class="highlight-item">
|
||||
<span class="highlight-check">✓</span>
|
||||
{{ highlight }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Technologies -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
||||
Technologies
|
||||
</h3>
|
||||
<div class="sidebar-techs">
|
||||
{% for tech in project.technologies %}
|
||||
<a href="{% url 'projects:list' %}?tech={{ tech }}" class="tech-tag tech-tag-clickable">{{ tech }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Catégorie -->
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-card-title">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 20h16M4 4h16M9 9h6M9 15h6"/></svg>
|
||||
Catégorie
|
||||
</h3>
|
||||
<a href="{% url 'projects:list' %}?category={{ project.category }}" class="category-link">
|
||||
{{ project.category }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Navigation projet précédent / suivant ────────────────────────────── -->
|
||||
<section class="project-nav-section">
|
||||
<div class="project-nav-container">
|
||||
{% if prev_project %}
|
||||
<a href="{% url 'projects:detail' prev_project.slug %}" class="project-nav-card project-nav-prev">
|
||||
<span class="nav-direction">← Précédent</span>
|
||||
<span class="nav-project-title">{{ prev_project.title }}</span>
|
||||
</a>
|
||||
{% else %}<div></div>{% endif %}
|
||||
|
||||
<a href="{% url 'projects:list' %}" class="btn btn-outline">Tous les projets</a>
|
||||
|
||||
{% if next_project %}
|
||||
<a href="{% url 'projects:detail' next_project.slug %}" class="project-nav-card project-nav-next">
|
||||
<span class="nav-direction">Suivant →</span>
|
||||
<span class="nav-project-title">{{ next_project.title }}</span>
|
||||
</a>
|
||||
{% else %}<div></div>{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Lightbox ─────────────────────────────────────────────────────────── -->
|
||||
<div class="lightbox" id="lightbox" onclick="closeLightbox()">
|
||||
<button class="lightbox-close" onclick="closeLightbox()">✕</button>
|
||||
<img class="lightbox-img" id="lightboxImg" src="" alt="">
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
167
templates/projects/list.html
Normal file
167
templates/projects/list.html
Normal file
@@ -0,0 +1,167 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Projets — Portfolio Alexandre{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<!-- ─── En-tête page projets ─────────────────────────────────────────────── -->
|
||||
<section class="page-header">
|
||||
<div class="page-header-container">
|
||||
<span class="section-tag">Réalisations</span>
|
||||
<h1 class="page-title">Mes Projets</h1>
|
||||
<p class="page-sub">{{ total_count }} projet{{ total_count|pluralize }} — filtrez par technologie ou catégorie</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Filtres ──────────────────────────────────────────────────────────── -->
|
||||
<section class="filters-section">
|
||||
<div class="filters-container">
|
||||
<form method="GET" action="{% url 'projects:list' %}" class="filters-form" id="filtersForm">
|
||||
|
||||
<!-- Filtre catégorie -->
|
||||
<div class="filter-group">
|
||||
<label class="filter-label">Catégorie</label>
|
||||
<div class="filter-pills" id="categoryPills">
|
||||
<button type="button"
|
||||
class="filter-pill {% if not selected_category %}active{% endif %}"
|
||||
data-filter="category"
|
||||
data-value="">
|
||||
Toutes
|
||||
</button>
|
||||
{% for cat in categories %}
|
||||
<button type="button"
|
||||
class="filter-pill {% if selected_category == cat %}active{% endif %}{% if forloop.counter > 5 %} pill-hidden{% endif %}"
|
||||
data-filter="category"
|
||||
data-value="{{ cat }}"
|
||||
{% if forloop.counter > 5 %}data-extra="true"{% endif %}>
|
||||
{{ cat }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
{% if categories|length > 5 %}
|
||||
<button type="button" class="filter-pill pill-more" data-target="categoryPills" data-total="{{ categories|length|add:'-5' }}">
|
||||
+{{ categories|length|add:"-5" }} voir plus
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<input type="hidden" name="category" id="categoryInput" value="{{ selected_category }}">
|
||||
</div>
|
||||
|
||||
<!-- Filtre technologie -->
|
||||
<div class="filter-group">
|
||||
<label class="filter-label">Technologie</label>
|
||||
<div class="filter-pills" id="techPills">
|
||||
<button type="button"
|
||||
class="filter-pill {% if not selected_tech %}active{% endif %}"
|
||||
data-filter="tech"
|
||||
data-value="">
|
||||
Toutes
|
||||
</button>
|
||||
{% for tech in technologies %}
|
||||
<button type="button"
|
||||
class="filter-pill {% if selected_tech == tech %}active{% endif %}{% if forloop.counter > 5 %} pill-hidden{% endif %}"
|
||||
data-filter="tech"
|
||||
data-value="{{ tech }}"
|
||||
{% if forloop.counter > 5 %}data-extra="true"{% endif %}>
|
||||
{{ tech }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
{% if technologies|length > 5 %}
|
||||
<button type="button" class="filter-pill pill-more" data-target="techPills" data-total="{{ technologies|length|add:'-5' }}">
|
||||
+{{ technologies|length|add:"-5" }} voir plus
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<input type="hidden" name="tech" id="techInput" value="{{ selected_tech }}">
|
||||
</div>
|
||||
|
||||
{% if selected_category or selected_tech %}
|
||||
<a href="{% url 'projects:list' %}" class="btn btn-ghost btn-sm">
|
||||
✕ Réinitialiser les filtres
|
||||
</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Grille projets ───────────────────────────────────────────────────── -->
|
||||
<section class="section">
|
||||
<div class="section-container">
|
||||
|
||||
{% if selected_category or selected_tech %}
|
||||
<div class="filter-result-info">
|
||||
<span>{{ total_count }} résultat{{ total_count|pluralize }}</span>
|
||||
{% if selected_category %}<span class="filter-tag-active">{{ selected_category }}</span>{% endif %}
|
||||
{% if selected_tech %}<span class="filter-tag-active">{{ selected_tech }}</span>{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="projects-grid projects-grid-full" id="projectsGrid">
|
||||
{% for project in projects %}
|
||||
<div class="project-card">
|
||||
<div class="card-image-wrapper">
|
||||
{% if project.images %}
|
||||
<img src="{{ project.images.0 }}" alt="{{ project.title }}" class="card-image" onerror="this.parentElement.classList.add('no-image')">
|
||||
{% else %}
|
||||
<div class="card-image-placeholder">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-category-badge">{{ project.category }}</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">{{ project.title }}</h3>
|
||||
<p class="card-desc">{{ project.short_description }}</p>
|
||||
<div class="card-techs">
|
||||
{% for tech in project.technologies|slice:":5" %}
|
||||
<span class="tech-tag">{{ tech }}</span>
|
||||
{% endfor %}
|
||||
{% if project.technologies|length > 5 %}
|
||||
<span class="tech-tag tech-tag-more">+{{ project.technologies|length|add:"-5" }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<a href="{% url 'projects:detail' project.slug %}" class="card-link">
|
||||
Voir le projet
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="empty-state-container">
|
||||
<div class="empty-icon">🔍</div>
|
||||
<h3>Aucun projet trouvé</h3>
|
||||
<p>Essayez de modifier vos filtres ou <a href="{% url 'projects:list' %}">réinitialisez</a>.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
document.querySelectorAll('.pill-more').forEach(btn => {
|
||||
const container = document.getElementById(btn.dataset.target);
|
||||
// Sélectionne uniquement les pills "extra" (au-delà des 5 premières)
|
||||
const extraPills = container.querySelectorAll('.pill-hidden[data-filter]');
|
||||
const total = parseInt(btn.dataset.total || extraPills.length);
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
const isExpanded = btn.dataset.expanded === 'true';
|
||||
|
||||
if (!isExpanded) {
|
||||
container.querySelectorAll('.pill-hidden[data-filter]').forEach(p => p.classList.remove('pill-hidden'));
|
||||
btn.textContent = 'voir moins ↑';
|
||||
btn.classList.add('pill-less');
|
||||
btn.dataset.expanded = 'true';
|
||||
} else {
|
||||
container.querySelectorAll('.filter-pill[data-extra="true"]').forEach(p => p.classList.add('pill-hidden'));
|
||||
btn.textContent = '+' + total + ' voir plus';
|
||||
btn.classList.remove('pill-less');
|
||||
btn.dataset.expanded = 'false';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user