diff --git a/package.json b/package.json index 590976cfa..8ef78d1c7 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@symfony/webpack-encore": "^4.1.0", "@tsconfig/node14": "^1.0.1", "bindings": "^1.5.0", - "bootstrap": "^5.0.1", + "bootstrap": "^5.3.0", "chokidar": "^3.5.1", "fork-awesome": "^1.1.7", "jquery": "^3.6.0", @@ -31,7 +31,8 @@ "typescript": "^4.7.2", "vue-loader": "^17.0.0", "webpack": "^5.75.0", - "webpack-cli": "^5.0.1" + "webpack-cli": "^5.0.1", + "@types/dompurify": "^3.0.5" }, "dependencies": { "@fullcalendar/core": "^6.1.4", @@ -53,7 +54,9 @@ "vue-i18n": "^9.1.6", "vue-multiselect": "3.0.0-alpha.2", "vue-toast-notification": "^2.0", - "vuex": "^4.0.0" + "vuex": "^4.0.0", + "dompurify": "^3.0.6", + "marked": "^9.1.5" }, "browserslist": [ "Firefox ESR" diff --git a/src/Bundle/ChillMainBundle/Controller/AdminController.php b/src/Bundle/ChillMainBundle/Controller/AdminController.php index 7d3826823..46fbfb351 100644 --- a/src/Bundle/ChillMainBundle/Controller/AdminController.php +++ b/src/Bundle/ChillMainBundle/Controller/AdminController.php @@ -47,4 +47,12 @@ class AdminController extends AbstractController { return $this->render('@ChillMain/Admin/indexUser.html.twig'); } + + /** + * @Route("/{_locale}/admin/dashboard", name="chill_main_dashboard_admin") + */ + public function indexDashboardAction() + { + return $this->render('@ChillMain/Admin/indexDashboard.html.twig'); + } } diff --git a/src/Bundle/ChillMainBundle/Controller/DashboardApiController.php b/src/Bundle/ChillMainBundle/Controller/DashboardApiController.php new file mode 100644 index 000000000..e091d2f5a --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/DashboardApiController.php @@ -0,0 +1,41 @@ + 'top-left', + 'id' => 1, + 'type' => 'news', + 'metadata' => [ + // arbitrary data that will be store "some time" + 'only_unread' => false, + ], + ], + ]; + + return new JsonResponse($data, JsonResponse::HTTP_OK, []); + } +} diff --git a/src/Bundle/ChillMainBundle/Controller/NewsItemApiController.php b/src/Bundle/ChillMainBundle/Controller/NewsItemApiController.php new file mode 100644 index 000000000..5224228a8 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/NewsItemApiController.php @@ -0,0 +1,52 @@ +newsItemRepository->countWithDateFilter(); + $paginator = $this->paginatorFactory->create($total); + $newsItems = $this->newsItemRepository->findWithDateFilter( + $limit = $paginator->getItemsPerPage(), + $offset = $paginator->getCurrentPage()->getFirstItemNumber() + ); + + return new JsonResponse($this->serializer->serialize( + new Collection(array_values($newsItems), $paginator), + 'json', + [ + AbstractNormalizer::GROUPS => ['read'], + ] + ), JsonResponse::HTTP_OK, [], true); + } +} diff --git a/src/Bundle/ChillMainBundle/Controller/NewsItemController.php b/src/Bundle/ChillMainBundle/Controller/NewsItemController.php new file mode 100644 index 000000000..a94dd55b9 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/NewsItemController.php @@ -0,0 +1,27 @@ +addOrderBy('e.startDate', 'DESC'); + $query->addOrderBy('e.id', 'DESC'); + + return parent::orderQuery($action, $query, $request, $paginator); + } +} diff --git a/src/Bundle/ChillMainBundle/Controller/NewsItemHistoryController.php b/src/Bundle/ChillMainBundle/Controller/NewsItemHistoryController.php new file mode 100644 index 000000000..8c645828b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/NewsItemHistoryController.php @@ -0,0 +1,72 @@ +buildFilterOrder(false); + $total = $this->newsItemRepository->countAllFilteredByUser($filter->getQueryString()); + $newsItems = $this->newsItemRepository->findAllFilteredByUser($filter->getQueryString()); + + $pagination = $this->paginatorFactory->create($total); + + return $this->render('@ChillMain/NewsItem/news_items_history.html.twig', [ + 'entities' => $newsItems, + 'paginator' => $pagination, + 'filter_order' => $filter, + ]); + } + + /** + * @Route("/{_locale}/news-items/{id}", name="chill_main_single_news_item") + */ + public function showSingleItem(int $id, Request $request): Response + { + $newsItem = $this->newsItemRepository->findOneBy(['id' => $id]); + + return $this->render( + '@ChillMain/NewsItem/show.html.twig', + [ + 'entity' => $newsItem, + ] + ); + } + + private function buildFilterOrder($includeFilterByUser = true, $includeMissionType = false): FilterOrderHelper + { + $filterBuilder = $this->filterOrderHelperFactory + ->create(self::class) + ->addSearchBox(); + + return $filterBuilder->build(); + } +} diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 5c33ef1b3..63cfb94c4 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -19,6 +19,7 @@ use Chill\MainBundle\Controller\CountryController; use Chill\MainBundle\Controller\LanguageController; use Chill\MainBundle\Controller\LocationController; use Chill\MainBundle\Controller\LocationTypeController; +use Chill\MainBundle\Controller\NewsItemController; use Chill\MainBundle\Controller\RegroupmentController; use Chill\MainBundle\Controller\UserController; use Chill\MainBundle\Controller\UserJobApiController; @@ -53,6 +54,7 @@ use Chill\MainBundle\Entity\GeographicalUnitLayer; use Chill\MainBundle\Entity\Language; use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\LocationType; +use Chill\MainBundle\Entity\NewsItem; use Chill\MainBundle\Entity\Regroupment; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\UserJob; @@ -62,6 +64,7 @@ use Chill\MainBundle\Form\CountryType; use Chill\MainBundle\Form\LanguageType; use Chill\MainBundle\Form\LocationFormType; use Chill\MainBundle\Form\LocationTypeType; +use Chill\MainBundle\Form\NewsItemType; use Chill\MainBundle\Form\RegroupmentType; use Chill\MainBundle\Form\UserJobType; use Chill\MainBundle\Form\UserType; @@ -544,6 +547,27 @@ class ChillMainExtension extends Extension implements ], ], ], + [ + 'class' => NewsItem::class, + 'name' => 'news_item', + 'base_path' => '/admin/news_item', + 'form_class' => NewsItemType::class, + 'controller' => NewsItemController::class, + 'actions' => [ + 'index' => [ + 'role' => 'ROLE_ADMIN', + 'template' => '@ChillMain/NewsItem/index.html.twig', + ], + 'new' => [ + 'role' => 'ROLE_ADMIN', + 'template' => '@ChillMain/NewsItem/new.html.twig', + ], + 'edit' => [ + 'role' => 'ROLE_ADMIN', + 'template' => '@ChillMain/NewsItem/edit.html.twig', + ], + ], + ], ], 'apis' => [ [ diff --git a/src/Bundle/ChillMainBundle/Entity/DashboardConfigItem.php b/src/Bundle/ChillMainBundle/Entity/DashboardConfigItem.php new file mode 100644 index 000000000..5ad1107c5 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Entity/DashboardConfigItem.php @@ -0,0 +1,112 @@ +id; + } + + public function getType(): string + { + return $this->type; + } + + public function setType(string $type): self + { + $this->type = $type; + + return $this; + } + + public function getPosition(): string + { + return $this->position; + } + + public function setPosition(string $position): void + { + $this->position = $position; + } + + public function getUser(): User + { + return $this->user; + } + + public function setUser(User $user): void + { + $this->user = $user; + } + + public function getMetadata(): array + { + return $this->metadata; + } + + public function setMetadata(array $metadata): void + { + $this->metadata = $metadata; + } +} diff --git a/src/Bundle/ChillMainBundle/Entity/NewsItem.php b/src/Bundle/ChillMainBundle/Entity/NewsItem.php new file mode 100644 index 000000000..604c58c5f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Entity/NewsItem.php @@ -0,0 +1,128 @@ +title; + } + + public function setTitle(string $title): void + { + $this->title = $title; + } + + public function getContent(): string + { + return $this->content; + } + + public function setContent(string $content): void + { + $this->content = $content; + } + + public function getStartDate(): ?\DateTimeImmutable + { + return $this->startDate; + } + + public function setStartDate(?\DateTimeImmutable $startDate): void + { + $this->startDate = $startDate; + } + + public function getEndDate(): ?\DateTimeImmutable + { + return $this->endDate; + } + + public function setEndDate(?\DateTimeImmutable $endDate): void + { + $this->endDate = $endDate; + } + + public function getId(): ?int + { + return $this->id; + } +} diff --git a/src/Bundle/ChillMainBundle/Form/NewsItemType.php b/src/Bundle/ChillMainBundle/Form/NewsItemType.php new file mode 100644 index 000000000..01ae63882 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Form/NewsItemType.php @@ -0,0 +1,54 @@ +add('title', TextType::class, [ + 'required' => true, + ]) + ->add('content', ChillTextareaType::class, [ + 'required' => false, + ]) + ->add( + 'startDate', + ChillDateType::class, + [ + 'required' => true, + 'input' => 'datetime_immutable', + ] + ) + ->add('endDate', ChillDateType::class, [ + 'required' => false, + 'input' => 'datetime_immutable', + ]); + } + + /** + * @return void + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('data_class', NewsItem::class); + } +} diff --git a/src/Bundle/ChillMainBundle/Repository/NewsItemRepository.php b/src/Bundle/ChillMainBundle/Repository/NewsItemRepository.php new file mode 100644 index 000000000..88b597647 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/NewsItemRepository.php @@ -0,0 +1,137 @@ +repository = $entityManager->getRepository(NewsItem::class); + } + + public function createQueryBuilder(string $alias, string $indexBy = null): QueryBuilder + { + return $this->repository->createQueryBuilder($alias, $indexBy); + } + + public function find($id) + { + return $this->repository->find($id); + } + + public function findAll() + { + return $this->repository->findAll(); + } + + public function findBy(array $criteria, array $orderBy = null, int $limit = null, int $offset = null) + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria) + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName() + { + return NewsItem::class; + } + + public function buildBaseQuery( + string $pattern = null + ): QueryBuilder { + $qb = $this->createQueryBuilder('n'); + + if (null !== $pattern && '' !== $pattern) { + $qb->andWhere($qb->expr()->like('LOWER(UNACCENT(n.title))', 'LOWER(UNACCENT(:pattern))')) + ->orWhere($qb->expr()->like('LOWER(UNACCENT(n.content))', 'LOWER(UNACCENT(:pattern))')) + ->setParameter('pattern', '%'.$pattern.'%'); + } + + return $qb; + } + + public function findAllFilteredByUser(string $pattern = null) + { + $qb = $this->buildBaseQuery($pattern); + $qb->addOrderBy('n.startDate', 'DESC') + ->addOrderBy('n.id', 'DESC'); + + return $qb->getQuery()->getResult(); + } + + public function findWithDateFilter($limit = null, $offset = null) + { + $qb = $this->buildQueryWithDateFilter(); + + if ($limit) { + $qb->setMaxResults($limit); + } + + if ($offset) { + $qb->setFirstResult($offset); + } + + return $qb + ->getQuery() + ->getResult(); + } + + public function countAllFilteredByUser(string $pattern = null) + { + $qb = $this->buildBaseQuery($pattern); + + return $qb + ->select('COUNT(n)') + ->getQuery() + ->getSingleScalarResult(); + } + + public function countWithDateFilter() + { + return $this->buildQueryWithDateFilter() + ->select('COUNT(n)') + ->getQuery() + ->getSingleScalarResult(); + } + + public function buildQueryWithDateFilter(): QueryBuilder + { + $now = $this->clock->now(); + + $qb = $this->createQueryBuilder('n'); + $qb + ->where( + $qb->expr()->andX( + $qb->expr()->lte('n.startDate', ':now'), + $qb->expr()->orX( + $qb->expr()->gt('n.endDate', ':now'), + $qb->expr()->isNull('n.endDate') + ) + ) + ) + ->setParameter('now', $now); + + return $qb; + } +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/_shared.scss b/src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/_shared.scss index 96da20779..feca44382 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/_shared.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/_shared.scss @@ -11,6 +11,7 @@ // 3. Include remainder of required Bootstrap stylesheets @import "bootstrap/scss/variables"; +@import "bootstrap/scss/variables-dark"; // 4. Include any default map overrides here @import "custom/_maps"; diff --git a/src/Bundle/ChillMainBundle/Resources/public/types.ts b/src/Bundle/ChillMainBundle/Resources/public/types.ts index b31b70897..79dae2695 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/types.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/types.ts @@ -160,3 +160,11 @@ export interface LocationType { contactData: "optional" | "required"; title: TranslatableString; } + +export interface NewsItemType { + id: number; + title: string; + content: string; + startdate: { date: DateTime }; + enddate: { date: DateTime | null} +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue index 315fd863f..02763221a 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue @@ -97,6 +97,8 @@ import MyNotifications from './MyNotifications'; import MyWorkflows from './MyWorkflows.vue'; import TabCounter from './TabCounter'; import { mapState } from "vuex"; +import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; + export default { name: "App", @@ -112,7 +114,7 @@ export default { }, data() { return { - activeTab: 'MyCustoms' + activeTab: 'MyCustoms', } }, computed: { @@ -126,8 +128,11 @@ export default { }, methods: { selectTab(tab) { - this.$store.dispatch('getByTab', { tab: tab }); + if (tab !== 'MyCustoms') { + this.$store.dispatch('getByTab', { tab: tab }); + } this.activeTab = tab; + console.log(this.activeTab) } }, mounted() { diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/News.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/News.vue new file mode 100644 index 000000000..a33bebf5c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/News.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/NewsItem.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/NewsItem.vue new file mode 100644 index 000000000..345545fe9 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/NewsItem.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue index 5e9cb79df..58120e5cf 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue @@ -1,7 +1,7 @@ @@ -63,14 +51,20 @@ @@ -98,4 +101,4 @@ span.counter { background-color: unset; } } - \ No newline at end of file + diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js index 088cb93b7..1579a3d0c 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js @@ -96,13 +96,11 @@ const store = createStore({ }, catchError(state, error) { state.errorMsg.push(error); - } + }, }, actions: { getByTab({ commit, getters }, { tab, param }) { switch (tab) { - case 'MyCustoms': - break; // case 'MyWorks': // if (!getters.isWorksLoaded) { // commit('setLoading', true); @@ -221,8 +219,8 @@ const store = createStore({ default: throw 'tab '+ tab; } - } + }, }, }); -export { store }; \ No newline at end of file +export { store }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts index c509ac10f..05b115928 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts @@ -51,7 +51,14 @@ const messages = { years_old: "1 an | {n} an | {n} ans", residential_address: "Adresse de résidence", located_at: "réside chez" - } + }, + widget: { + news: { + title: "Actualités", + readMore: "Lire la suite", + date: "Date" + } + } } }; diff --git a/src/Bundle/ChillMainBundle/Resources/views/Admin/indexDashboard.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Admin/indexDashboard.html.twig new file mode 100644 index 000000000..9c7513d4c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Admin/indexDashboard.html.twig @@ -0,0 +1,13 @@ +{% extends "@ChillMain/Admin/layoutWithVerticalMenu.html.twig" %} + +{% block vertical_menu_content %} + {{ chill_menu('admin_news_item', { + 'layout': '@ChillMain/Admin/menu_admin_section.html.twig', + }) }} +{% endblock %} + +{% block layout_wvm_content %} + {% block admin_content %} +

{{ 'admin.dashboard.description' | trans }}

+ {% endblock %} +{% endblock %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/NewsItem/edit.html.twig b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/edit.html.twig new file mode 100644 index 000000000..4d55c480c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/edit.html.twig @@ -0,0 +1,11 @@ +{% extends '@ChillMain/CRUD/Admin/index.html.twig' %} + +{% block title %} + {% include('@ChillMain/CRUD/_edit_title.html.twig') %} +{% endblock %} + +{% block admin_content %} + {% embed '@ChillMain/CRUD/_edit_content.html.twig' %} + {% block content_form_actions_save_and_show %}{% endblock %} + {% endembed %} +{% endblock admin_content %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/NewsItem/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/index.html.twig new file mode 100644 index 000000000..4eb02945b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/index.html.twig @@ -0,0 +1,37 @@ +{% extends '@ChillMain/CRUD/Admin/index.html.twig' %} + +{% block admin_content %} + {% embed '@ChillMain/CRUD/_index.html.twig' %} + {% block table_entities_thead_tr %} + {{ 'Title'|trans }} + {{ 'news.startDate'|trans }} + {{ 'news.endDate'|trans }} + {% endblock %} + {% block table_entities_tbody %} + {% for entity in entities %} + + {{ entity.title }} + {{ entity.startDate|date }} + {% if entity.endDate is not null %} + {{ entity.endDate|date }} + {% else %} + {{ 'news.noDate'|trans }} + {% endif %} + + + + + {% endfor %} + {% endblock %} + + {% block actions_before %} +
  • + {{'Back to the admin'|trans}} +
  • + {% endblock %} + {% endembed %} +{% endblock %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/NewsItem/new.html.twig b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/new.html.twig new file mode 100644 index 000000000..7c204dddd --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/new.html.twig @@ -0,0 +1,11 @@ +{% extends '@ChillMain/CRUD/Admin/index.html.twig' %} + +{% block title %} + {% include('@ChillMain/CRUD/_new_title.html.twig') %} +{% endblock %} + +{% block admin_content %} + {% embed '@ChillMain/CRUD/_new_content.html.twig' %} + {% block content_form_actions_save_and_show %}{% endblock %} + {% endembed %} +{% endblock admin_content %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/NewsItem/news_items_history.html.twig b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/news_items_history.html.twig new file mode 100644 index 000000000..e462c3302 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/news_items_history.html.twig @@ -0,0 +1,74 @@ +{% extends "@ChillMain/layout.html.twig" %} + +{% block title %} + {{ 'news.title'|trans }} +{% endblock title %} + +{% block content %} +
    +

    {{ 'news.title'|trans }}

    + + {{ filter_order|chill_render_filter_order_helper }} + + {% if entities|length == 0 %} +

    + {{ "news.no_data"|trans }} +

    + {% else %} + +
    + + {% for entity in entities %} + +
    +
    + +
    +

    + {{ entity.title }} +

    +
    + {% if entity.startDate %} + {{ entity.startDate|format_date('long') }} + {% endif %} + {% if entity.endDate %} + - {{ entity.endDate|format_date('long') }} + {% endif %} +
    +
    +
    +
    +
    +{#
    #} + {{ entity.content|u.truncate(350, '…', false)|chill_markdown_to_html }} +{# {% if entity.content|length > 350 %}#} +{# {{ 'news.read_more'|trans }}#} +{# {% endif %}#} +{#
    #} +
    +
      +
    • + +
    • +
    +
    +
    +
    +
    +
    +
    + {% endfor %} +
    + + {{ chill_pagination(paginator) }} + + + {% endif %} +
    +{% endblock %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/NewsItem/show.html.twig b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/show.html.twig new file mode 100644 index 000000000..b86408183 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/NewsItem/show.html.twig @@ -0,0 +1,21 @@ +{% extends '@ChillMain/layout.html.twig' %} + +{% block title 'news.show_details'|trans %} + +{% block content %} +

    {{ entity.title }}

    + +
    +
    +
    + {{ entity.startDate|format_date('long') }} + {% if entity.endDate is not null %} + - {{ entity.endDate|format_date('long') }} + {% endif %} +
    +
    +
    + {{ entity.content|chill_markdown_to_html }} +
    +
    +{% endblock %} diff --git a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/AdminNewsMenuBuilder.php b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/AdminNewsMenuBuilder.php new file mode 100644 index 000000000..08e72884f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/AdminNewsMenuBuilder.php @@ -0,0 +1,45 @@ +authorizationChecker->isGranted('ROLE_ADMIN')) { + return; + } + + $menu->addChild('admin.dashboard.title', [ + 'route' => 'chill_main_dashboard_admin', + ]) + ->setAttribute('class', 'list-group-item-header') + ->setExtras([ + 'order' => 9000, + ]); + + $menu->addChild('admin.dashboard.news', [ + 'route' => 'chill_crud_news_item_index', + ])->setExtras(['order' => 9000]); + } + + public static function getMenuIds(): array + { + return ['admin_section', 'admin_news_item']; + } +} diff --git a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/SectionMenuBuilder.php b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/SectionMenuBuilder.php index 6247cf769..05e1fc26f 100644 --- a/src/Bundle/ChillMainBundle/Routing/MenuBuilder/SectionMenuBuilder.php +++ b/src/Bundle/ChillMainBundle/Routing/MenuBuilder/SectionMenuBuilder.php @@ -60,6 +60,14 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface 'order' => 20, ]); } + + $menu->addChild($this->translator->trans('news_history.menu'), [ + 'route' => 'chill_main_news_items_history', + ]) + ->setExtras([ + 'icons' => ['newspaper-o'], + 'order' => 5, + ]); } public static function getMenuIds(): array diff --git a/src/Bundle/ChillMainBundle/chill.api.specs.yaml b/src/Bundle/ChillMainBundle/chill.api.specs.yaml index 98e0e915e..8edbd93f6 100644 --- a/src/Bundle/ChillMainBundle/chill.api.specs.yaml +++ b/src/Bundle/ChillMainBundle/chill.api.specs.yaml @@ -10,6 +10,12 @@ servers: components: schemas: + Date: + type: object + properties: + datetime: + type: string + format: date-time User: type: object properties: @@ -131,6 +137,35 @@ components: id: type: integer + DashboardConfigItem: + type: object + properties: + id: + type: integer + type: + type: string + metadata: + type: object + userId: + type: integer + position: + type: string + + NewsItem: + type: object + properties: + id: + type: integer + title: + type: string + content: + type: string + startDate: + $ref: "#/components/schemas/Date" + endDate: + $ref: "#/components/schemas/Date" + + paths: /1.0/search.json: get: @@ -842,4 +877,34 @@ paths: $ref: '#/components/schemas/Workflow' 403: description: "Unauthorized" + /1.0/main/dashboard-config-item.json: + get: + tags: + - dashboard config item + summary: Returns the dashboard configuration for the current user. + responses: + 200: + description: "ok" + content: + application/json: + schema: + $ref: '#/components/schemas/DashboardConfigItem' + 403: + description: "Unauthorized" + /1.0/main/news.json: + get: + tags: + - news items + summary: Returns a list of news items + responses: + 200: + description: "ok" + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/NewsItem' + 403: + description: "Unauthorized" diff --git a/src/Bundle/ChillMainBundle/migrations/Version20231108141141.php b/src/Bundle/ChillMainBundle/migrations/Version20231108141141.php new file mode 100644 index 000000000..d4fe9b561 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20231108141141.php @@ -0,0 +1,55 @@ +addSql('CREATE SEQUENCE chill_main_dashboard_config_item_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE SEQUENCE chill_main_news_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_main_dashboard_config_item (id INT NOT NULL, user_id INT DEFAULT NULL, type VARCHAR(255) NOT NULL, position VARCHAR(255) NOT NULL, metadata JSONB DEFAULT \'{}\'::jsonb, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_CF59DFD6A76ED395 ON chill_main_dashboard_config_item (user_id)'); + $this->addSql('CREATE TABLE chill_main_news (id INT NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL, startDate DATE NOT NULL, endDate DATE DEFAULT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, createdBy_id INT DEFAULT NULL, updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_96922AFB3174800F ON chill_main_news (createdBy_id)'); + $this->addSql('CREATE INDEX IDX_96922AFB65FF1AEC ON chill_main_news (updatedBy_id)'); + $this->addSql('COMMENT ON COLUMN chill_main_news.startDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_main_news.endDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_main_news.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_main_news.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_main_dashboard_config_item ADD CONSTRAINT FK_CF59DFD6A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_news ADD CONSTRAINT FK_96922AFB3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_news ADD CONSTRAINT FK_96922AFB65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP SEQUENCE chill_main_dashboard_config_item_id_seq CASCADE'); + $this->addSql('DROP SEQUENCE chill_main_news_id_seq CASCADE'); + $this->addSql('ALTER TABLE chill_main_dashboard_config_item DROP CONSTRAINT FK_CF59DFD6A76ED395'); + $this->addSql('ALTER TABLE chill_main_news DROP CONSTRAINT FK_96922AFB3174800F'); + $this->addSql('ALTER TABLE chill_main_news DROP CONSTRAINT FK_96922AFB65FF1AEC'); + $this->addSql('DROP TABLE chill_main_dashboard_config_item'); + $this->addSql('DROP TABLE chill_main_news'); + } +} diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 91068275f..fa89abab6 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -82,7 +82,6 @@ Comment: Commentaire Comments: Commentaires Pinned comment: Commentaire épinglé Any comment: Aucun commentaire -Read more: Lire la suite (more...): (suite...) # comment embeddable @@ -438,6 +437,12 @@ crud: add_new: Ajouter un centre title_new: Nouveau centre title_edit: Modifier un centre + news_item: + index: + title: Liste des actualités + add_new: Créer une nouvelle actualité + title_new: Nouvelle actualité + title_edit: Modifier une actualité No entities: Aucun élément @@ -679,3 +684,19 @@ admin: undefined: non défini user: Utilisateur scope: Service + dashboard: + title: Tableau de bord + news: Actualités + description: Configuration du tableau de bord + + +news: + noDate: Pas de date de fin + startDate: Date de début + endDate: Date de fin + title: Historique des actualités + menu: Actualités + no_data: Aucune actualité + read_more: Lire la suite + +