feat: mise en page de la page login et page accueil

This commit is contained in:
2026-03-23 18:43:32 +01:00
parent 1b5fd98527
commit ba84b49134
17 changed files with 421 additions and 836 deletions

View File

@@ -1,5 +1,35 @@
@import "tailwindcss"; @import "tailwindcss";
body { /* Chargement des polices d'écriture */
background-color: bg-slate-100; @import url('https://fonts.googleapis.com/css2?family=Sora:wght@100..800&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Caveat:wght@400..700&family=Sora:wght@100..800&display=swap');
/* Configuration charte graphique */
@theme {
/* --- Couleurs de l'association --- */
--color-bouton: #4DD5C8;
--color-bouton-hover: #6CE0D6;
--color-title: #E6A638;
--color-text: #000000;
--color-bg-primaire: #F9FCF7;
--color-bg-secondaire: #23978B;
/* --- Couleurs liées à des actions --- */
--color-danger: #EF4444;
--color-danger-hover: #DC2626;
--color-success: #A7F3D0;
--color-success-text: #065F46;
--color-info: #BFDBFE;
--color-info-hover: #93C5FD;
/* --- Couleurs en plus --- */
--color-gris-clair: #E5E7EB;
--color-gris-moyen: #9CA3AF;
--color-gris-fonce: #4B5563;
/* Polices */
--font-sora: "Sora", system-ui, sans-serif;
--font-caveat: "Caveat", cursive;
} }

View File

@@ -1,6 +1,12 @@
security: security:
# Hierarchie des rôles #
role_hierarchy:
ROLE_ORGANISATION: ROLE_USER
ROLE_ADMIN_ORGANISATION: ROLE_ORGANISATION
ROLE_ADMIN: ROLE_USER
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
# comment sont hachés nos mots de passe # Comment sont hachés nos mots de passe
password_hashers: password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
@@ -33,6 +39,7 @@ security:
enable_csrf: true enable_csrf: true
username_parameter: _username username_parameter: _username
password_parameter: _password password_parameter: _password
default_target_path: app_home
logout: logout:
path: app_logout path: app_logout
# où rediriger après la déconnexion # où rediriger après la déconnexion
@@ -56,6 +63,7 @@ security:
# autorisations # autorisations
access_control: access_control:
- { path: ^/admin, roles: ROLE_ADMIN } - { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/organisation, roles: ROLE_ADMIN_ORGANISATION }
- { path: ^/user, roles: ROLE_USER } - { path: ^/user, roles: ROLE_USER }
when@test: when@test:

View File

@@ -1,631 +0,0 @@
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
/*
! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com
*/
/*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
*/
*,
::before,
::after {
box-sizing: border-box;
/* 1 */
border-width: 0;
/* 2 */
border-style: solid;
/* 2 */
border-color: #e5e7eb;
/* 2 */
}
::before,
::after {
--tw-content: '';
}
/*
1. Use a consistent sensible line-height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured `sans` font-family by default.
5. Use the user's configured `sans` font-feature-settings by default.
6. Use the user's configured `sans` font-variation-settings by default.
7. Disable tap highlights on iOS
*/
html,
:host {
line-height: 1.5;
/* 1 */
-webkit-text-size-adjust: 100%;
/* 2 */
-moz-tab-size: 4;
/* 3 */
-o-tab-size: 4;
tab-size: 4;
/* 3 */
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
/* 4 */
font-feature-settings: normal;
/* 5 */
font-variation-settings: normal;
/* 6 */
-webkit-tap-highlight-color: transparent;
/* 7 */
}
/*
1. Remove the margin in all browsers.
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
*/
body {
margin: 0;
/* 1 */
line-height: inherit;
/* 2 */
}
/*
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
3. Ensure horizontal rules are visible by default.
*/
hr {
height: 0;
/* 1 */
color: inherit;
/* 2 */
border-top-width: 1px;
/* 3 */
}
/*
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr:where([title]) {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
/*
Remove the default font size and weight for headings.
*/
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
/*
Reset links to optimize for opt-in styling instead of opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/*
Add the correct font weight in Edge and Safari.
*/
b,
strong {
font-weight: bolder;
}
/*
1. Use the user's configured `mono` font-family by default.
2. Use the user's configured `mono` font-feature-settings by default.
3. Use the user's configured `mono` font-variation-settings by default.
4. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/* 1 */
font-feature-settings: normal;
/* 2 */
font-variation-settings: normal;
/* 3 */
font-size: 1em;
/* 4 */
}
/*
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/*
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/*
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
3. Remove gaps between table borders by default.
*/
table {
text-indent: 0;
/* 1 */
border-color: inherit;
/* 2 */
border-collapse: collapse;
/* 3 */
}
/*
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
3. Remove default padding in all browsers.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
/* 1 */
font-feature-settings: inherit;
/* 1 */
font-variation-settings: inherit;
/* 1 */
font-size: 100%;
/* 1 */
font-weight: inherit;
/* 1 */
line-height: inherit;
/* 1 */
letter-spacing: inherit;
/* 1 */
color: inherit;
/* 1 */
margin: 0;
/* 2 */
padding: 0;
/* 3 */
}
/*
Remove the inheritance of text transform in Edge and Firefox.
*/
button,
select {
text-transform: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/
button,
input:where([type='button']),
input:where([type='reset']),
input:where([type='submit']) {
-webkit-appearance: button;
/* 1 */
background-color: transparent;
/* 2 */
background-image: none;
/* 2 */
}
/*
Use the modern Firefox focus style for all focusable elements.
*/
:-moz-focusring {
outline: auto;
}
/*
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/
:-moz-ui-invalid {
box-shadow: none;
}
/*
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/*
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/*
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
/*
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
/*
Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
fieldset {
margin: 0;
padding: 0;
}
legend {
padding: 0;
}
ol,
ul,
menu {
list-style: none;
margin: 0;
padding: 0;
}
/*
Reset default styling for dialogs.
*/
dialog {
padding: 0;
}
/*
Prevent resizing textareas horizontally by default.
*/
textarea {
resize: vertical;
}
/*
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
2. Set the default placeholder color to the user's configured gray 400 color.
*/
input::-moz-placeholder, textarea::-moz-placeholder {
opacity: 1;
/* 1 */
color: #9ca3af;
/* 2 */
}
input::placeholder,
textarea::placeholder {
opacity: 1;
/* 1 */
color: #9ca3af;
/* 2 */
}
/*
Set the default cursor for buttons.
*/
button,
[role="button"] {
cursor: pointer;
}
/*
Make sure disabled buttons don't get the pointer cursor.
*/
:disabled {
cursor: default;
}
/*
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
This can trigger a poorly considered lint error in some tools but is included by design.
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block;
/* 1 */
vertical-align: middle;
/* 2 */
}
/*
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/
img,
video {
max-width: 100%;
height: auto;
}
/* Make elements with the HTML hidden attribute stay hidden by default */
[hidden]:where(:not([hidden="until-found"])) {
display: none;
}
.visible {
visibility: visible;
}
.static {
position: static;
}
.fixed {
position: fixed;
}
.mb-3 {
margin-bottom: 0.75rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.block {
display: block;
}
.hidden {
display: none;
}
.transform {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.rounded-lg {
border-radius: 0.5rem;
}
.border {
border-width: 1px;
}
.border-red-200 {
--tw-border-opacity: 1;
border-color: rgb(254 202 202 / var(--tw-border-opacity, 1));
}
.bg-red-50 {
--tw-bg-opacity: 1;
background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1));
}
.p-4 {
padding: 1rem;
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-red-800 {
--tw-text-opacity: 1;
color: rgb(153 27 27 / var(--tw-text-opacity, 1));
}
.filter {
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.transition {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}

66
public/img/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 71 KiB

View File

@@ -33,20 +33,34 @@ class UserController extends AbstractController
* @throws TransportExceptionInterface * @throws TransportExceptionInterface
*/ */
#[Route('/user/{email}', name: 'app_user', methods: ['GET'])] // #[Route('/user/{email}', name: 'app_user', methods: ['GET'])]
public function index(string $email, KazApiService $apiClient): Response // public function index(string $email, KazApiService $apiClient): Response
// {
// $exist = $apiClient->getUserData($email);
//
// return $this->render('user/index.html.twig', [
// 'exist' => $exist,
// ]);
// }
#[Route('/mon-profil', name: 'app_user', methods: ['GET'])]
public function index(KazApiService $apiClient): Response
{ {
$exist = $apiClient->getUserData($email); // Récupération de l'utilisateur actuellement connecté
$user = $this->getUser();
// Utilisation de son email pour interroger l'API
$userData = $apiClient->getUserData($user->getUserIdentifier());
return $this->render('user/index.html.twig', [ return $this->render('user/index.html.twig', [
'exist' => $exist, 'userData' => $userData,
]); ]);
} }
#[Route('/user/mot-de-passe', name: 'app_user_edit_password', methods: ['GET', 'POST'])] #[Route('/mot-de-passe', name: 'app_user_edit_password', methods: ['GET', 'POST'])]
public function editPassword(Request $request, UserPasswordHasherInterface $hasher, EntityManagerInterface $entityManager): Response public function editPassword(Request $request, UserPasswordHasherInterface $hasher, EntityManagerInterface $entityManager): Response
{ {
# Récupération de l'adhérent actuellement connecté # Récupération de l'utilisateur actuellement connecté
$user = $this->getUser(); $user = $this->getUser();
# Création du formulaire # Création du formulaire
@@ -73,7 +87,7 @@ class UserController extends AbstractController
# Sauvegarde en BDD # Sauvegarde en BDD
$entityManager->flush(); $entityManager->flush();
# Message de succès pour l'adhérent # Message de succès pour l'utilisateur
$this->addFlash('success', 'Votre mot de passe a bien été mis à jour !'); $this->addFlash('success', 'Votre mot de passe a bien été mis à jour !');
return $this->redirectToRoute('app_user_edit_password'); return $this->redirectToRoute('app_user_edit_password');

View File

@@ -32,7 +32,7 @@ class AppFixtures extends Fixture
# Attribution d'un email aléatoire et unique # Attribution d'un email aléatoire et unique
$user->setEmail($faker->unique()->safeEmail()); $user->setEmail($faker->unique()->safeEmail());
# Définition des droits d'accès de l'utilisateur # Définition des droits d'accès de l'utilisateur
$user->setRoles(['ROLE_USER']); $user->setRoles(['ROLE_USER', 'ROLE_ORGANISATION']);
# Hachage sécurisé du mot de passe "password" # Hachage sécurisé du mot de passe "password"
$user->setPassword($this->hasher->hashPassword($user, 'password')); $user->setPassword($this->hasher->hashPassword($user, 'password'));
# Définition d'un NOM et Prénom # Définition d'un NOM et Prénom
@@ -52,7 +52,7 @@ class AppFixtures extends Fixture
// Création d'un compte de test fixe // Création d'un compte de test fixe
$admin = new User(); $admin = new User();
$admin->setEmail('admin@kaz.bzh'); $admin->setEmail('admin@kaz.bzh');
$admin->setRoles(['ROLE_USER', 'ROLE_ADMIN']); $admin->setRoles(['ROLE_USER', 'ROLE_ADMIN', 'ROLE_ORGANISATION']);
$admin->setPassword($this->hasher->hashPassword($admin, 'password')); $admin->setPassword($this->hasher->hashPassword($admin, 'password'));
$admin->setFirstName('Admin'); $admin->setFirstName('Admin');
$admin->setLastName('KAZ'); $admin->setLastName('KAZ');

View File

@@ -1,60 +0,0 @@
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\SecurityRequestAttributes;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class AppCustomAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
public function __construct(private UrlGeneratorInterface $urlGenerator)
{
}
public function authenticate(Request $request): Passport
{
$email = $request->getPayload()->getString('email');
$request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $email);
return new Passport(
new UserBadge($email),
new PasswordCredentials($request->getPayload()->getString('password')),
[
new CsrfTokenBadge('authenticate', $request->getPayload()->getString('_csrf_token')),
new RememberMeBadge(),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
// For example:
// return new RedirectResponse($this->urlGenerator->generate('some_route'));
throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}

View File

@@ -1,23 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./assets/**/*.js",
"./templates/**/*.html.twig",
],
theme: {
extend: {
colors: {
'brand-teal': '#4DD5C8', // Boutons
'brand-gold': '#E6A638', // Accent
'brand-dark': '#000000', // Police
'bg-primary': '#F9FCF7', // Fond principal
'bg-secondary': '#23978B', // Fond secondaire
},
fontFamily: {
'sans': ['Sora', 'system-ui', 'sans-serif'],
'title': ['Caveat', 'cursive'],
},
},
},
plugins: [],
}

View File

@@ -0,0 +1,65 @@
<nav class="bg-white border-b border-gris-clair shadow-sm py-4 px-6 sticky top-0 z-50 font-sora">
<div class="max-w-7xl mx-auto flex flex-col md:flex-row items-center justify-between gap-4">
<div class="flex flex-col md:flex-row items-center gap-4 md:gap-8 w-full md:w-auto">
<a href="{{ path('app_home') }}" class="flex items-center gap-2 transition-transform hover:scale-105">
<img src="{{ asset('img/logo.svg') }}" alt="Logo de l'association" class="h-10 w-auto object-contain">
</a>
<ul class="flex flex-wrap justify-center md:justify-start gap-2 md:gap-4">
{# Onglet : Mon profil #}
<li>
<a href="{{ path('app_user') }}"
class="px-4 py-2 text-sm font-semibold transition-colors block
{{ app.request.attributes.get('_route') == 'app_user'
? 'bg-bouton/20 border border-bouton text-text rounded-lg'
: 'text-gris-fonce hover:bg-gris-clair hover:text-text rounded-lg border border-transparent' }}">
Mon profil
</a>
</li>
{# Onglet : Mon offre #}
<li>
{# TODO : créer la route {{ path('app_offres') }} #}
<a href="#"
class="px-4 py-2 text-sm font-semibold transition-colors block text-gris-fonce hover:bg-gris-clair hover:text-text rounded-lg border border-transparent flex items-center gap-1">
Mon offre
</a>
</li>
{# Onglet : Gérer mes mots de passe #}
<li>
<a href="{{ path('app_user_edit_password') }}"
class="px-4 py-2 text-sm font-semibold transition-colors block
{{ app.request.attributes.get('_route') == 'app_user_edit_password'
? 'bg-bouton/20 border border-bouton text-text rounded-lg'
: 'text-gris-fonce hover:bg-gris-clair hover:text-text rounded-lg border border-transparent' }}">
Gérer mes mots de passe
</a>
</li>
{# Onglet : Mon organisation (ne s'affiche que si on a le rôle adéquat) #}
{% if is_granted('ROLE_ADMIN_ORGANISATION') %}
<li>
<a href="#"
class="px-4 py-2 text-sm font-semibold transition-colors block text-gris-fonce hover:bg-gris-clair hover:text-text rounded-lg border border-transparent">
Mon organisation
</a>
</li>
{% endif %}
</ul>
</div>
<div class="flex-shrink-0 mt-4 md:mt-0">
<a href="{{ path('app_logout') }}"
class="px-4 py-2 text-sm font-bold bg-danger text-white rounded-lg hover:bg-danger-hover transition-colors shadow flex items-center gap-2">
Se déconnecter
</a>
</div>
</div>
</nav>

View File

@@ -2,9 +2,13 @@
<html lang="fr"> <html lang="fr">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{% block title %}Association KAZ{% endblock %}</title> <title>{% block title %}Association KAZ{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>"> <link rel="icon" href="{{ asset("img/logo.svg") }}">
{% block stylesheets %} {% block stylesheets %}
<link rel="stylesheet" href="{{ asset('styles/app.css') }}">
{% endblock %} {% endblock %}
{% block javascripts %} {% block javascripts %}
@@ -13,18 +17,46 @@
</head> </head>
<body> <body>
<main class="container mx-auto mt-4 px-4"> {# entête du site #}
{# Section des notifications #} <header>
{% for label, messages in app.flashes %} <div class="container">
{% for message in messages %} {{ include('_navbar.html.twig') }}
<div class="p-4 mb-4 rounded-lg shadow-md border-l-4 {{ label == 'success' ? 'bg-brand-teal text-brand-dark border-bg-secondary' : 'bg-brand-gold text-brand-dark border-red-700' }}"> </div>
{{ message }} </header>
{# contenu principal de chaque page #}
<main>
<div>
{% block body %}
{% endblock body %}
</div> </div>
{% endfor %}
{% endfor %}
{% block body %}{% endblock %}
</main> </main>
{# pied-de-page du site #}
<footer class="bg-white border-t border-gris-clair py-6 sm:py-8 mt-auto w-full font-sora">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col md:flex-row justify-between items-center gap-4">
<div class="text-sm text-gris-fonce flex items-center gap-2 justify-center md:justify-start">
{# Logo de l'association #}
<img src="{{ asset('img/logo.svg') }}"
alt="Logo de l'association"
class="h-6 w-auto object-contain opacity-80 hover:opacity-100 transition-opacity">
{# Le texte et les liens #}
<span>
&copy; {{ 'now'|date('Y') }} | Kaz, le numérique sobre, libre, éthique et local.
</span>
</div>
<ul class="flex flex-wrap justify-center gap-4 sm:gap-6 text-sm text-gris-fonce">
<li>
<a href="https://kaz.bzh/mentions-legales/" class="hover:text-bouton transition-colors">Mentions légales et statuts</a>
</li>
<li>
<a href="https://status.kaz.bzh/status/kaz" class="hover:text-bouton transition-colors">Santé des services Kaz</a>
</li>
<li>
<a href="https://kaz.bzh/contact/" class="hover:text-bouton transition-colors">Contact</a>
</li>
</ul>
</div>
</footer>
</body> </body>
</html> </html>

View File

@@ -1 +0,0 @@
error404.html.twig

View File

@@ -1,78 +1,106 @@
{% extends 'base.html.twig' %} {% extends 'base.html.twig' %}
{% block title %}Tableau de bord | Association KAZ{% endblock %} {% block title %}Accueil | Association KAZ{% endblock %}
{% block body %} {% block body %}
<div class="min-h-screen bg-gray-50 py-8 w-full"> <div class="min-h-screen bg-bg-primaire py-8 w-full font-sora">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
{# --- EN-TÊTE DU TABLEAU DE BORD --- #} {# Bloc "message d'accueil" #}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 px-6 py-6 mb-8 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4"> <h1 class="text-4xl font-caveat text-text mb-6 text-center sm:text-center">
<div> Page d'accueil
<h1 class="text-2xl font-bold text-gray-900">Espace Adhérent KAZ</h1> </h1>
<p class="text-sm text-gray-500 mt-1">Bienvenue sur votre intranet associatif.</p>
<div class="bg-white rounded-xl shadow-sm border border-gris-clair p-6 sm:p-8 mb-10">
<h2 class="text-xl font-bold text-title mb-4 text-center">
Bienvenue sur ton espace kaznaute <span class="text-bouton">{{ app.user ? app.user.userIdentifier : 'visiteur' }}</span> !
</h2>
{# Zone réservée pour les futures données de l'API #}
<div class="bg-bouton/10 border border-bouton/30 rounded-lg p-5">
<h3 class="font-semibold text-title mb-3 flex items-center gap-2">
Votre abonnement actuellement :
</h3>
<ul class="space-y-2 text-sm text-text">
<li class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
<span class="font-semibold text-gris-fonce">Formule souscrite :</span>
{# TODO API : Remplacer par la vraie variable #}
<span class="italic opacity-70">Ajouter la vraie valeur</span>
</li>
<li class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
<span class="font-semibold text-gris-fonce">Date de validité :</span>
{# TODO API : Remplacer par la vraie variable #}
<span class="italic opacity-70">Ajouter la vraie valeur</span>
</li>
</ul>
</div>
</div> </div>
{% if app.user %}
<div class="flex items-center space-x-4"> {# Bloc "que souhaitez-vous faire ?" #}
<span class="inline-flex items-center rounded-full bg-blue-50 px-3 py-1 text-sm font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10"> <h2 class="text-2xl font-bold text-title mb-6 text-center sm:text-left">
👤 {{ app.user.userIdentifier }} Que souhaitez-vous faire ?
</span> </h2>
<a href="{{ path('app_logout') }}" class="rounded-md bg-red-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 transition-colors duration-200"> {# Gestion du responsive #}
Déconnexion <div class="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6">
{# LIGNE 1 : Profil & Offre #}
<a href="{{ path('app_user')|default('#') }}" class="group flex items-center p-5 bg-white border border-gris-clair rounded-xl shadow-sm hover:shadow-md hover:border-bouton transition-all duration-200">
<div class="flex-shrink-0 bg-blue-50 text-blue-600 rounded-lg p-3 group-hover:bg-blue-600 group-hover:text-white transition-colors">
<span class="text-2xl block">👤</span>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-title group-hover:text-bouton transition-colors">Mon Profil</h3>
<p class="text-sm text-gris-fonce">Consulter et modifier mes informations</p>
</div>
</a> </a>
<a href="#" class="group flex items-center p-5 bg-white border border-gris-clair rounded-xl shadow-sm hover:shadow-md hover:border-bouton transition-all duration-200">
<div class="flex-shrink-0 bg-green-50 text-green-600 rounded-lg p-3 group-hover:bg-green-600 group-hover:text-white transition-colors">
<span class="text-2xl block">💳</span>
</div> </div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-title group-hover:text-bouton transition-colors">Mon Offre</h3>
<p class="text-sm text-gris-fonce">Gérer mon adhésion KAZ</p>
</div>
</a>
{# LIGNE 2 : Sécurité & Organisation/Contact #}
<a href="{{ path('app_user_edit_password') }}" class="group flex items-center p-5 bg-white border border-gris-clair rounded-xl shadow-sm hover:shadow-md hover:border-bouton transition-all duration-200">
<div class="flex-shrink-0 bg-orange-50 text-orange-600 rounded-lg p-3 group-hover:bg-orange-600 group-hover:text-white transition-colors">
<span class="text-2xl block">🔒</span>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-title group-hover:text-bouton transition-colors">Sécurité</h3>
<p class="text-sm text-gris-fonce">Modifier mes mots de passe</p>
</div>
</a>
{# Si la personne gère une asso, on montre l'organisation. Sinon, un bouton Contact pour garder la grille de 6 éléments équilibrée #}
{% if is_granted('ROLE_ADMIN_ORGANISATION') %}
<a href="#" class="group flex items-center p-5 bg-white border border-bouton/30 rounded-xl shadow-sm hover:shadow-md hover:border-bouton transition-all duration-200">
<div class="flex-shrink-0 bg-bouton/20 text-bouton rounded-lg p-3 group-hover:bg-bouton group-hover:text-white transition-colors">
<span class="text-2xl block">🏢</span>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-title group-hover:text-bouton transition-colors">Mon Organisation</h3>
<p class="text-sm text-gris-fonce">Espace de gestion du bureau</p>
</div>
</a>
{% else %}
<a href="https://kaz.bzh/contact/" class="group flex items-center p-5 bg-white border border-gris-clair rounded-xl shadow-sm hover:shadow-md hover:border-bouton transition-all duration-200">
<div class="flex-shrink-0 bg-purple-50 text-purple-600 rounded-lg p-3 group-hover:bg-purple-600 group-hover:text-white transition-colors">
<span class="text-2xl block">✉️</span>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-title group-hover:text-bouton transition-colors">Nous contacter</h3>
<p class="text-sm text-gris-fonce">Besoin d'aide ou d'assistance ?</p>
</div>
</a>
{% endif %} {% endif %}
</div> </div>
{# --- GRILLE DES FONCTIONNALITÉS --- #}
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{# Carte 1 : Profil #}
<div class="bg-white overflow-hidden shadow-sm border border-gray-100 rounded-xl hover:shadow-md transition-shadow duration-200">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0 bg-blue-600 rounded-lg p-3">
<span class="text-2xl">📋</span>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">Mon Profil</dt>
<dd class="text-lg font-semibold text-gray-900">Gérer mes informations</dd>
</dl>
</div>
</div>
</div>
<div class="bg-gray-50 px-5 py-3 border-t border-gray-100">
<div class="text-sm">
<a href="#" class="font-medium text-blue-600 hover:text-blue-500">Voir mon profil &rarr;</a>
</div>
</div>
</div>
{# Carte 2 : Mon abonnement KAZ #}
<div class="bg-white overflow-hidden shadow-sm border border-gray-100 rounded-xl hover:shadow-md transition-shadow duration-200">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0 bg-green-500 rounded-lg p-3">
<span class="text-2xl">💳</span>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">Mon abonnement KAZ</dt>
<dd class="text-lg font-semibold text-gray-900">À jour</dd>
</dl>
</div>
</div>
</div>
<div class="bg-gray-50 px-5 py-3 border-t border-gray-100">
<div class="text-sm">
<a href="#" class="font-medium text-green-600 hover:text-green-500">Voir l'historique &rarr;</a>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -1,34 +1,92 @@
{% extends 'base.html.twig' %} <!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page de connexion | Association KAZ</title>
{% block title %}Se connecter | {{ parent() }}{% endblock %} {% block importmap %}{{ importmap('app') }}{% endblock %}
</head>
<body class="min-h-screen bg-bg-primaire font-sora text-text flex flex-col justify-between items-center p-4 sm:p-8">
<div class="flex-none"></div>
<main class="w-full max-w-md bg-white p-8 rounded-xl shadow-lg border border-gris-clair">
<header class="flex justify-center mb-6">
<img src="{{ asset('img/logo.svg') }}"
alt="Logo association"
class="h-16 md:h-24 w-auto object-contain">
</header>
<h1 class="text-4xl text-center mb-8 font-caveat text-text">Se connecter</h1>
{% block body %}
<form method="post">
{% if error %} {% if error %}
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div> <div class="bg-danger/10 border border-danger text-danger px-4 py-3 rounded-lg mb-6 text-sm" role="alert">
{% endif %} {{ error.messageKey|trans(error.messageData, 'security') }}
{% if app.user %}
<div class="mb-3">
You are logged in as {{ app.user.userIdentifier }}, <a href="{{ logout_path() }}">Logout</a>
</div> </div>
{% endif %} {% endif %}
<h1 class="h3 mb-3 font-weight-normal">Se connecter à mon tableau de bord KAZ</h1> <form method="post" class="space-y-5">
<label for="inputEmail">Email :</label> {# ajout du token csrf pour 2 fois plus de plaisir en toute sécurité #}
<input type="email" value="{{ last_username }}" name="_username" id="inputEmail" class="form-control" autocomplete="email" required autofocus> <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
<label for="inputPassword">Mot de passe : </label>
<input type="password" name="_password" id="inputPassword" class="form-control" autocomplete="current-password" required>
<input type="hidden" name="_csrf_token" data-controller="csrf-protection" value="{{ csrf_token('authenticate') }}">
<div class="checkbox mb-3"> {# champ adresse-mail #}
<label> <div class="space-y-1">
<input type="checkbox" name="_remember_me"> Se souvenir de moi <label for="username" class="block text-sm font-semibold text-text">
Adresse-mail :
</label>
<input type="email" value="{{ last_username }}" name="_username" id="username"
class="w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow"
placeholder="Saisissez votre e-mail" required autofocus autocomplete="email">
</div>
{# champ "mot de passe" #}
<div class="space-y-1">
<label for="password" class="block text-sm font-semibold text-text">
Mot de passe :
</label>
<input type="password" name="_password" id="password"
class="w-full px-4 py-3 border border-gris-clair rounded-lg focus:outline-none focus:border-bouton focus:ring-1 focus:ring-bouton placeholder-gris-moyen transition-shadow"
placeholder="Saisissez votre mot de passe" required autocomplete="current-password">
</div>
{# checkbox "se souvenir de moi" #}
<div class="flex items-center pt-1">
<input type="checkbox" name="_remember_me" id="remember_me"
class="w-4 h-4 text-bouton border-gris-clair rounded focus:ring-bouton cursor-pointer">
<label for="remember_me" class="ml-2 text-sm text-gris-fonce cursor-pointer select-none">
Se souvenir de moi
</label> </label>
</div> </div>
<button class="btn btn-lg btn-primary" type="submit"> <div class="flex flex-col sm:flex-row gap-4 pt-2">
<button type="submit"
class="flex-1 py-3 bg-bouton hover:bg-bouton-hover text-text font-bold rounded-lg shadow transition-colors">
Se connecter Se connecter
</button> </button>
<a href="https://kaz.bzh/offres/"
target="_blank"
rel="noopener noreferrer"
class="flex-1 flex items-center justify-center py-3 border border-gris-moyen text-text hover:bg-gris-clair font-bold rounded-lg transition-colors text-center">
M'inscrire
</a>
</div>
<div class="text-center pt-4">
{# TODO : faire route vers mot de passe oublié #}
<a href="#"
class="text-sm font-semibold text-title hover:text-bouton transition-colors">
Mot de passe oublié ?
</a>
</div>
</form> </form>
{% endblock %} </main>
<footer class="mt-8 text-center text-sm text-gris-fonce w-full">
&copy; {{ 'now'|date('Y') }} | Kaz, le numérique sobre, libre, éthique et local.
</footer>
</body>
</html>

View File

@@ -1,7 +1,6 @@
{# templates/user/edit_password.html.twig #}
{% extends 'base.html.twig' %} {% extends 'base.html.twig' %}
{% block title %}Modifier mon mot de passe | {{ parent() }}{% endblock %} {% block title %}Modifier mes mots de passe | {{ parent() }}{% endblock %}
{% block body %} {% block body %}
<div class="min-h-screen bg-bg-primary flex items-center justify-center p-4"> <div class="min-h-screen bg-bg-primary flex items-center justify-center p-4">
@@ -20,7 +19,7 @@
{{ form_errors(form.oldPassword) }} {{ form_errors(form.oldPassword) }}
</div> </div>
{# Champs Nouveau Mot de Passe (RepeatedType) #} {# Champs Nouveau Mot de Passe #}
<div class="space-y-4"> <div class="space-y-4">
{{ form_row(form.newPassword.first, {'attr': {'class': 'w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-brand-teal outline-none'}}) }} {{ form_row(form.newPassword.first, {'attr': {'class': 'w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-brand-teal outline-none'}}) }}
{{ form_row(form.newPassword.second, {'attr': {'class': 'w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-brand-teal outline-none'}}) }} {{ form_row(form.newPassword.second, {'attr': {'class': 'w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-brand-teal outline-none'}}) }}