| @ -1,3 +0,0 @@ | |||||
| body { | |||||
| background-color: skyblue; | |||||
| } | |||||
| @ -0,0 +1,8 @@ | |||||
| version: '3' | |||||
| services: | |||||
| ###> doctrine/doctrine-bundle ### | |||||
| database: | |||||
| ports: | |||||
| - "5432" | |||||
| ###< doctrine/doctrine-bundle ### | |||||
| @ -0,0 +1,26 @@ | |||||
| version: '3' | |||||
| services: | |||||
| ###> doctrine/doctrine-bundle ### | |||||
| database: | |||||
| image: postgres:${POSTGRES_VERSION:-16}-alpine | |||||
| environment: | |||||
| POSTGRES_DB: ${POSTGRES_DB:-app} | |||||
| # You should definitely change the password in production | |||||
| POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!} | |||||
| POSTGRES_USER: ${POSTGRES_USER:-app} | |||||
| healthcheck: | |||||
| test: ["CMD", "pg_isready", "-d", "${POSTGRES_DB:-app}", "-U", "${POSTGRES_USER:-app}"] | |||||
| timeout: 5s | |||||
| retries: 5 | |||||
| start_period: 60s | |||||
| volumes: | |||||
| - database_data:/var/lib/postgresql/data:rw | |||||
| # You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data! | |||||
| # - ./docker/db/data:/var/lib/postgresql/data:rw | |||||
| ###< doctrine/doctrine-bundle ### | |||||
| volumes: | |||||
| ###> doctrine/doctrine-bundle ### | |||||
| database_data: | |||||
| ###< doctrine/doctrine-bundle ### | |||||
| @ -0,0 +1,52 @@ | |||||
| doctrine: | |||||
| dbal: | |||||
| url: '%env(resolve:DATABASE_URL)%' | |||||
| # IMPORTANT: You MUST configure your server version, | |||||
| # either here or in the DATABASE_URL env var (see .env file) | |||||
| #server_version: '16' | |||||
| profiling_collect_backtrace: '%kernel.debug%' | |||||
| use_savepoints: true | |||||
| orm: | |||||
| auto_generate_proxy_classes: true | |||||
| enable_lazy_ghost_objects: true | |||||
| report_fields_where_declared: true | |||||
| validate_xml_mapping: true | |||||
| naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware | |||||
| auto_mapping: true | |||||
| mappings: | |||||
| App: | |||||
| type: attribute | |||||
| is_bundle: false | |||||
| dir: '%kernel.project_dir%/src/Entity' | |||||
| prefix: 'App\Entity' | |||||
| alias: App | |||||
| controller_resolver: | |||||
| auto_mapping: false | |||||
| when@test: | |||||
| doctrine: | |||||
| dbal: | |||||
| # "TEST_TOKEN" is typically set by ParaTest | |||||
| dbname_suffix: '_test%env(default::TEST_TOKEN)%' | |||||
| when@prod: | |||||
| doctrine: | |||||
| orm: | |||||
| auto_generate_proxy_classes: false | |||||
| proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' | |||||
| query_cache_driver: | |||||
| type: pool | |||||
| pool: doctrine.system_cache_pool | |||||
| result_cache_driver: | |||||
| type: pool | |||||
| pool: doctrine.result_cache_pool | |||||
| framework: | |||||
| cache: | |||||
| pools: | |||||
| doctrine.result_cache_pool: | |||||
| adapter: cache.app | |||||
| doctrine.system_cache_pool: | |||||
| adapter: cache.system | |||||
| @ -0,0 +1,6 @@ | |||||
| doctrine_migrations: | |||||
| migrations_paths: | |||||
| # namespace is arbitrary but should be different from App\Migrations | |||||
| # as migrations classes should NOT be autoloaded | |||||
| 'DoctrineMigrations': '%kernel.project_dir%/migrations' | |||||
| enable_profiler: false | |||||
| @ -0,0 +1,7 @@ | |||||
| # Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html | |||||
| # See the official DoctrineExtensions documentation for more details: https://github.com/doctrine-extensions/DoctrineExtensions/tree/main/doc | |||||
| stof_doctrine_extensions: | |||||
| default_locale: fr_FR | |||||
| orm: | |||||
| default: | |||||
| sluggable: true | |||||
| @ -1,7 +1,11 @@ | |||||
| twig: | twig: | ||||
| file_name_pattern: '*.twig' | file_name_pattern: '*.twig' | ||||
| form_themes: ['bootstrap_5_layout.html.twig'] | |||||
| globals: | globals: | ||||
| title: Gestionnaire de tâches simple | title: Gestionnaire de tâches simple | ||||
| menu: | |||||
| header: | |||||
| - { route: 'app_project', label: 'Projets' } | |||||
| when@test: | when@test: | ||||
| twig: | twig: | ||||
| @ -0,0 +1,11 @@ | |||||
| framework: | |||||
| validation: | |||||
| # Enables validator auto-mapping support. | |||||
| # For instance, basic validation constraints will be inferred from Doctrine's metadata. | |||||
| #auto_mapping: | |||||
| # App\Entity\: [] | |||||
| when@test: | |||||
| framework: | |||||
| validation: | |||||
| not_compromised_password: false | |||||
| @ -0,0 +1,17 @@ | |||||
| when@dev: | |||||
| web_profiler: | |||||
| toolbar: true | |||||
| intercept_redirects: false | |||||
| framework: | |||||
| profiler: | |||||
| only_exceptions: false | |||||
| collect_serializer_data: true | |||||
| when@test: | |||||
| web_profiler: | |||||
| toolbar: false | |||||
| intercept_redirects: false | |||||
| framework: | |||||
| profiler: { collect: false } | |||||
| @ -0,0 +1,8 @@ | |||||
| when@dev: | |||||
| web_profiler_wdt: | |||||
| resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' | |||||
| prefix: /_wdt | |||||
| web_profiler_profiler: | |||||
| resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' | |||||
| prefix: /_profiler | |||||
| @ -0,0 +1,31 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace DoctrineMigrations; | |||||
| use Doctrine\DBAL\Schema\Schema; | |||||
| use Doctrine\Migrations\AbstractMigration; | |||||
| /** | |||||
| * Auto-generated Migration: Please modify to your needs! | |||||
| */ | |||||
| final class Version20240720132139 extends AbstractMigration | |||||
| { | |||||
| public function getDescription(): string | |||||
| { | |||||
| return ''; | |||||
| } | |||||
| public function up(Schema $schema): void | |||||
| { | |||||
| // this up() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('CREATE TABLE project (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, description CLOB DEFAULT NULL)'); | |||||
| } | |||||
| public function down(Schema $schema): void | |||||
| { | |||||
| // this down() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('DROP TABLE project'); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,40 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace DoctrineMigrations; | |||||
| use Doctrine\DBAL\Schema\Schema; | |||||
| use Doctrine\Migrations\AbstractMigration; | |||||
| /** | |||||
| * Auto-generated Migration: Please modify to your needs! | |||||
| */ | |||||
| final class Version20240720143743 extends AbstractMigration | |||||
| { | |||||
| public function getDescription(): string | |||||
| { | |||||
| return ''; | |||||
| } | |||||
| public function up(Schema $schema): void | |||||
| { | |||||
| // this up() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('CREATE TEMPORARY TABLE __temp__project AS SELECT id, name, description FROM project'); | |||||
| $this->addSql('DROP TABLE project'); | |||||
| $this->addSql('CREATE TABLE project (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, description CLOB DEFAULT NULL, slug VARCHAR(255) NOT NULL)'); | |||||
| $this->addSql('INSERT INTO project (id, name, description) SELECT id, name, description FROM __temp__project'); | |||||
| $this->addSql('DROP TABLE __temp__project'); | |||||
| $this->addSql('CREATE UNIQUE INDEX UNIQ_2FB3D0EE989D9B62 ON project (slug)'); | |||||
| } | |||||
| public function down(Schema $schema): void | |||||
| { | |||||
| // this down() migration is auto-generated, please modify it to your needs | |||||
| $this->addSql('CREATE TEMPORARY TABLE __temp__project AS SELECT id, name, description FROM project'); | |||||
| $this->addSql('DROP TABLE project'); | |||||
| $this->addSql('CREATE TABLE project (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, description CLOB DEFAULT NULL)'); | |||||
| $this->addSql('INSERT INTO project (id, name, description) SELECT id, name, description FROM __temp__project'); | |||||
| $this->addSql('DROP TABLE __temp__project'); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,173 @@ | |||||
| <?php | |||||
| namespace App\Controller; | |||||
| use App\Entity\Project; | |||||
| use App\Form\ProjectType; | |||||
| use Doctrine\ORM\EntityManagerInterface; | |||||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | |||||
| use Symfony\Component\Form\Extension\Core\Type\SubmitType; | |||||
| use Symfony\Component\HttpFoundation\Request; | |||||
| use Symfony\Component\HttpFoundation\Response; | |||||
| use Symfony\Component\Routing\Attribute\Route; | |||||
| #[Route('/project')] | |||||
| class ProjectController extends AbstractController | |||||
| { | |||||
| #[Route('/', name: 'app_project')] | |||||
| public function index(EntityManagerInterface $entityManager): Response | |||||
| { | |||||
| $repository = $entityManager->getRepository(Project::class); | |||||
| $projects = $repository->findAll(); | |||||
| $this->addFlash( | |||||
| 'info', | |||||
| sprintf('%s projet%s trouvé%s', count($projects), (count($projects) > 1 ? 's' : ''), (count($projects) > 1 ? 's' : '')) | |||||
| ); | |||||
| return $this->render('project/index.html.twig', [ | |||||
| 'projects' => $projects, | |||||
| ]); | |||||
| } | |||||
| #[Route('/create', name: 'app_project_create')] | |||||
| public function create(Request $request, EntityManagerInterface $entityManager): Response | |||||
| { | |||||
| $project = new Project(); | |||||
| $createForm = $this->createForm(ProjectType::class, $project); | |||||
| $createForm->add('submit', SubmitType::class, [ | |||||
| 'label' => 'Créer', | |||||
| ]); | |||||
| $createForm->handleRequest($request); | |||||
| if ($createForm->isSubmitted() and $createForm->isValid()) { | |||||
| $project = $createForm->getData(); | |||||
| try { | |||||
| $entityManager->persist($project); | |||||
| $entityManager->flush(); | |||||
| $this->addFlash( | |||||
| 'success', | |||||
| 'Projet créé !' | |||||
| ); | |||||
| return $this->redirectToRoute('app_project'); | |||||
| } catch (\Exception $exception) { | |||||
| $this->addFlash( | |||||
| 'danger', | |||||
| 'Impossible de créer le projet !' | |||||
| ); | |||||
| } | |||||
| } | |||||
| return $this->render('project/create.html.twig', [ | |||||
| 'create_form' => $createForm, | |||||
| ]); | |||||
| } | |||||
| #[Route('/show/{slug}', name: 'app_project_show')] | |||||
| public function show(EntityManagerInterface $entityManager, $slug): Response | |||||
| { | |||||
| $repository = $entityManager->getRepository(Project::class); | |||||
| $project = $repository->findOneBySlug($slug); | |||||
| if (!$project) { | |||||
| $this->addFlash( | |||||
| 'warning', | |||||
| 'Projet non trouvé !' | |||||
| ); | |||||
| return $this->redirectToRoute('app_project'); | |||||
| } | |||||
| return $this->render('project/show.html.twig', [ | |||||
| 'project' => $project, | |||||
| ]); | |||||
| } | |||||
| #[Route('/update/{slug}', name: 'app_project_update')] | |||||
| public function update(Request $request, EntityManagerInterface $entityManager, $slug): Response | |||||
| { | |||||
| $repository = $entityManager->getRepository(Project::class); | |||||
| $project = $repository->findOneBySlug($slug); | |||||
| if (!$project) { | |||||
| $this->addFlash( | |||||
| 'warning', | |||||
| 'Projet non trouvé !' | |||||
| ); | |||||
| return $this->redirectToRoute('app_project'); | |||||
| } | |||||
| $updateForm = $this->createForm(ProjectType::class, $project); | |||||
| $updateForm->add('submit', SubmitType::class, [ | |||||
| 'label' => 'Modifier', | |||||
| ]); | |||||
| $updateForm->handleRequest($request); | |||||
| if ($updateForm->isSubmitted() and $updateForm->isValid()) { | |||||
| $project = $updateForm->getData(); | |||||
| try { | |||||
| $entityManager->persist($project); | |||||
| $entityManager->flush(); | |||||
| $this->addFlash( | |||||
| 'success', | |||||
| 'Projet modifié !' | |||||
| ); | |||||
| return $this->redirectToRoute('app_project'); | |||||
| } catch (\Exception $exception) { | |||||
| $this->addFlash( | |||||
| 'danger', | |||||
| 'Impossible de mopdifier le projet !' | |||||
| ); | |||||
| } | |||||
| } | |||||
| return $this->render('project/update.html.twig', [ | |||||
| 'project' => $project, | |||||
| 'update_form' => $updateForm, | |||||
| ]); | |||||
| } | |||||
| #[Route('/remove/{slug}', name: 'app_project_remove')] | |||||
| public function remove(EntityManagerInterface $entityManager, $slug): Response | |||||
| { | |||||
| $repository = $entityManager->getRepository(Project::class); | |||||
| $project = $repository->findOneBySlug($slug); | |||||
| if (!$project) { | |||||
| $this->addFlash( | |||||
| 'warning', | |||||
| 'Projet non trouvé !' | |||||
| ); | |||||
| return $this->redirectToRoute('app_project'); | |||||
| } | |||||
| try { | |||||
| $entityManager->remove($project); | |||||
| $entityManager->flush(); | |||||
| $this->addFlash( | |||||
| 'success', | |||||
| 'Projet supprimé !' | |||||
| ); | |||||
| return $this->redirectToRoute('app_project'); | |||||
| } catch (\Exception $exception) { | |||||
| $this->addFlash( | |||||
| 'danger', | |||||
| 'Impossible de supprimer le projet !' | |||||
| ); | |||||
| } | |||||
| return $this->redirectToRoute('app_project'); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,62 @@ | |||||
| <?php | |||||
| namespace App\Entity; | |||||
| use App\Repository\ProjectRepository; | |||||
| use Doctrine\DBAL\Types\Types; | |||||
| use Doctrine\ORM\Mapping as ORM; | |||||
| use Gedmo\Mapping\Annotation as Gedmo; | |||||
| #[ORM\Entity(repositoryClass: ProjectRepository::class)] | |||||
| class Project | |||||
| { | |||||
| #[ORM\Id] | |||||
| #[ORM\GeneratedValue] | |||||
| #[ORM\Column] | |||||
| private ?int $id = null; | |||||
| #[ORM\Column(length: 255)] | |||||
| private ?string $name = null; | |||||
| #[ORM\Column(type: Types::TEXT, nullable: true)] | |||||
| private ?string $description = null; | |||||
| #[ORM\Column(length: 255, unique: true)] | |||||
| #[Gedmo\Slug(fields: ['name'])] | |||||
| private ?string $slug = null; | |||||
| public function getId(): ?int | |||||
| { | |||||
| return $this->id; | |||||
| } | |||||
| public function getName(): ?string | |||||
| { | |||||
| return $this->name; | |||||
| } | |||||
| public function setName(string $name): static | |||||
| { | |||||
| $this->name = $name; | |||||
| return $this; | |||||
| } | |||||
| public function getDescription(): ?string | |||||
| { | |||||
| return $this->description; | |||||
| } | |||||
| public function setDescription(?string $description): static | |||||
| { | |||||
| $this->description = $description; | |||||
| return $this; | |||||
| } | |||||
| public function getSlug(): ?string | |||||
| { | |||||
| return $this->slug; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,31 @@ | |||||
| <?php | |||||
| namespace App\Form; | |||||
| use App\Entity\Project; | |||||
| use Symfony\Component\Form\AbstractType; | |||||
| use Symfony\Component\Form\FormBuilderInterface; | |||||
| use Symfony\Component\OptionsResolver\OptionsResolver; | |||||
| class ProjectType extends AbstractType | |||||
| { | |||||
| public function buildForm(FormBuilderInterface $builder, array $options): void | |||||
| { | |||||
| $builder | |||||
| ->add('name', null, [ | |||||
| 'label' => 'Nom', | |||||
| ]) | |||||
| ->add('description', null, [ | |||||
| 'label' => 'Description', | |||||
| 'help' => 'On peut mettre du Markdown ici…', | |||||
| ]) | |||||
| ; | |||||
| } | |||||
| public function configureOptions(OptionsResolver $resolver): void | |||||
| { | |||||
| $resolver->setDefaults([ | |||||
| 'data_class' => Project::class, | |||||
| ]); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,43 @@ | |||||
| <?php | |||||
| namespace App\Repository; | |||||
| use App\Entity\Project; | |||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | |||||
| use Doctrine\Persistence\ManagerRegistry; | |||||
| /** | |||||
| * @extends ServiceEntityRepository<Project> | |||||
| */ | |||||
| class ProjectRepository extends ServiceEntityRepository | |||||
| { | |||||
| public function __construct(ManagerRegistry $registry) | |||||
| { | |||||
| parent::__construct($registry, Project::class); | |||||
| } | |||||
| // /** | |||||
| // * @return Project[] Returns an array of Project objects | |||||
| // */ | |||||
| // public function findByExampleField($value): array | |||||
| // { | |||||
| // return $this->createQueryBuilder('p') | |||||
| // ->andWhere('p.exampleField = :val') | |||||
| // ->setParameter('val', $value) | |||||
| // ->orderBy('p.id', 'ASC') | |||||
| // ->setMaxResults(10) | |||||
| // ->getQuery() | |||||
| // ->getResult() | |||||
| // ; | |||||
| // } | |||||
| // public function findOneBySomeField($value): ?Project | |||||
| // { | |||||
| // return $this->createQueryBuilder('p') | |||||
| // ->andWhere('p.exampleField = :val') | |||||
| // ->setParameter('val', $value) | |||||
| // ->getQuery() | |||||
| // ->getOneOrNullResult() | |||||
| // ; | |||||
| // } | |||||
| } | |||||
| @ -0,0 +1,7 @@ | |||||
| <div class="container"> | |||||
| <div class="row bg-body-tertiary border-top p-2"> | |||||
| <div class="col"> | |||||
| <p class="text-muted mb-0"></p> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| @ -1,37 +1,18 @@ | |||||
| <header> | <header> | ||||
| <nav class="navbar navbar-expand-lg bg-body-tertiary"> | <nav class="navbar navbar-expand-lg bg-body-tertiary"> | ||||
| <div class="container-fluid"> | <div class="container-fluid"> | ||||
| <a class="navbar-brand" href="#">Navbar</a> | |||||
| <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> | |||||
| <a class="navbar-brand" href="{{ path('app_home') }}">{{ title }}</a> | |||||
| <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"> | |||||
| <span class="navbar-toggler-icon"></span> | <span class="navbar-toggler-icon"></span> | ||||
| </button> | </button> | ||||
| <div class="collapse navbar-collapse" id="navbarSupportedContent"> | |||||
| <div class="collapse navbar-collapse" id="navbar"> | |||||
| <ul class="navbar-nav me-auto mb-2 mb-lg-0"> | <ul class="navbar-nav me-auto mb-2 mb-lg-0"> | ||||
| {% for item in menu.header %} | |||||
| <li class="nav-item"> | <li class="nav-item"> | ||||
| <a class="nav-link active" aria-current="page" href="#">Home</a> | |||||
| </li> | |||||
| <li class="nav-item"> | |||||
| <a class="nav-link" href="#">Link</a> | |||||
| </li> | |||||
| <li class="nav-item dropdown"> | |||||
| <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> | |||||
| Dropdown | |||||
| </a> | |||||
| <ul class="dropdown-menu"> | |||||
| <li><a class="dropdown-item" href="#">Action</a></li> | |||||
| <li><a class="dropdown-item" href="#">Another action</a></li> | |||||
| <li><hr class="dropdown-divider"></li> | |||||
| <li><a class="dropdown-item" href="#">Something else here</a></li> | |||||
| </ul> | |||||
| </li> | |||||
| <li class="nav-item"> | |||||
| <a class="nav-link disabled" aria-disabled="true">Disabled</a> | |||||
| <a {% if app.request.attributes.get('_route') == item.route %}class="nav-link active" aria-current="page"{% else %}class="nav-link"{% endif %} href="{{ path(item.route) }}">{{ item.label }}</a> | |||||
| </li> | </li> | ||||
| {% endfor %} | |||||
| </ul> | </ul> | ||||
| <form class="d-flex" role="search"> | |||||
| <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search"> | |||||
| <button class="btn btn-outline-success" type="submit">Search</button> | |||||
| </form> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </nav> | </nav> | ||||
| @ -1 +0,0 @@ | |||||
| {% extends 'base.html.twig' %} | |||||
| @ -1,5 +1,11 @@ | |||||
| {% extends 'home/base.html.twig' %} | |||||
| {% extends 'base.html.twig' %} | |||||
| {% block main %} | {% block main %} | ||||
| home | |||||
| <div class="container"> | |||||
| <div class="row"> | |||||
| <div class="col"> | |||||
| ici la page d’accueil | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {% endblock %} | {% endblock %} | ||||
| @ -0,0 +1,12 @@ | |||||
| {% extends 'base.html.twig' %} | |||||
| {% block breadcrumb %} | |||||
| <li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li> | |||||
| <li class="breadcrumb-item"><a href="{{ path('app_project_create') }}">Créer un nouveau projet</a></li> | |||||
| {% endblock %} | |||||
| {% block page_title %}Créer un nouveau projet{% endblock %} | |||||
| {% block page_content %} | |||||
| {{ form(create_form) }} | |||||
| {% endblock %} | |||||
| @ -0,0 +1,31 @@ | |||||
| {% extends 'base.html.twig' %} | |||||
| {% block breadcrumb %} | |||||
| <li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li> | |||||
| {% endblock %} | |||||
| {% block page_title %}Projets{% endblock %} | |||||
| {% block page_content %} | |||||
| <div class="row"> | |||||
| <div class="col mb-3"> | |||||
| <a href="{{ path('app_project_create') }}" class="btn btn-primary">Créer un projet</a> | |||||
| </div> | |||||
| </div> | |||||
| {% if projects is not empty %} | |||||
| <div class="row"> | |||||
| {% for project in projects %} | |||||
| <div class="col col-md-4 mb-3"> | |||||
| <div class="card h-100"> | |||||
| <div class="card-body"> | |||||
| <h2 class="card-title">{{ project.name }}</h2> | |||||
| {% if project.description %}<p class="card-text">{{ project.description|markdown_to_html }}</p>{% endif %} | |||||
| <a href="{{ path('app_project_show', {'slug': project.slug}) }}" class="btn btn-primary">Voir</a> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {% endfor %} | |||||
| </div> | |||||
| {% endif %} | |||||
| {% endblock %} | |||||
| @ -0,0 +1,21 @@ | |||||
| {% extends 'base.html.twig' %} | |||||
| {% block breadcrumb %} | |||||
| <li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li> | |||||
| <li class="breadcrumb-item"><a href="{{ path('app_project_show', {'slug': project.slug}) }}">Projet {{ project.name }}</a></li> | |||||
| {% endblock %} | |||||
| {% block page_title %}Projet {{ project.name }}{% endblock %} | |||||
| {% block page_content %} | |||||
| <div class="row"> | |||||
| <div class="col mb-3"> | |||||
| <a href="{{ path('app_project_update', {'slug': project.slug}) }}" class="btn btn-primary">Modifier le projet</a> | |||||
| <a href="{{ path('app_project_remove', {'slug': project.slug}) }}" class="btn btn-primary">Supprimer le projet</a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="row"> | |||||
| <div class="col mb-3 lead">{{ project.description|markdown_to_html }}</div> | |||||
| </div> | |||||
| {% endblock %} | |||||
| @ -0,0 +1,12 @@ | |||||
| {% extends 'base.html.twig' %} | |||||
| {% block breadcrumb %} | |||||
| <li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li> | |||||
| <li class="breadcrumb-item"><a href="{{ path('app_project_create') }}">Modifier le projet {{ project.name }}</a></li> | |||||
| {% endblock %} | |||||
| {% block page_title %}Modifier le projet {{ project.name }}{% endblock %} | |||||
| {% block page_content %} | |||||
| {{ form(update_form) }} | |||||
| {% endblock %} | |||||