mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-05 07:19:49 +00:00
Merge branch 'issue159_page_acceuil' into testing-20301
This commit is contained in:
commit
5f74682cba
@ -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"
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class DashboardApiController
|
||||
{
|
||||
/**
|
||||
* Get user dashboard config (not yet based on user id and still hardcoded for now).
|
||||
*
|
||||
* @Route("/api/1.0/main/dashboard-config-item.json", methods={"get"})
|
||||
*/
|
||||
public function getDashboardConfiguration(): JsonResponse
|
||||
{
|
||||
$data = [
|
||||
[
|
||||
'position' => '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, []);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Repository\NewsItemRepository;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
class NewsItemApiController
|
||||
{
|
||||
public function __construct(
|
||||
private NewsItemRepository $newsItemRepository,
|
||||
private SerializerInterface $serializer,
|
||||
private PaginatorFactory $paginatorFactory
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get list of news items filtered on start and end date.
|
||||
*
|
||||
* @Route("/api/1.0/main/news.json", methods={"get"})
|
||||
*/
|
||||
public function listCurrentNewsItems(): JsonResponse
|
||||
{
|
||||
$total = $this->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);
|
||||
}
|
||||
}
|
27
src/Bundle/ChillMainBundle/Controller/NewsItemController.php
Normal file
27
src/Bundle/ChillMainBundle/Controller/NewsItemController.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class NewsItemController extends CRUDController
|
||||
{
|
||||
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
|
||||
{
|
||||
$query->addOrderBy('e.startDate', 'DESC');
|
||||
$query->addOrderBy('e.id', 'DESC');
|
||||
|
||||
return parent::orderQuery($action, $query, $request, $paginator);
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Repository\NewsItemRepository;
|
||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class NewsItemHistoryController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly NewsItemRepository $newsItemRepository,
|
||||
private readonly PaginatorFactory $paginatorFactory,
|
||||
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/news-items/history", name="chill_main_news_items_history")
|
||||
*/
|
||||
public function listAction(Request $request): Response
|
||||
{
|
||||
$filter = $this->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();
|
||||
}
|
||||
}
|
@ -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' => [
|
||||
[
|
||||
|
112
src/Bundle/ChillMainBundle/Entity/DashboardConfigItem.php
Normal file
112
src/Bundle/ChillMainBundle/Entity/DashboardConfigItem.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*
|
||||
* @ORM\Table(name="chill_main_dashboard_config_item")
|
||||
*/
|
||||
class DashboardConfigItem
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
*
|
||||
* @ORM\GeneratedValue
|
||||
*
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @Serializer\Groups({"dashboardConfigItem:read", "read"})
|
||||
*/
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*
|
||||
* @Serializer\Groups({"dashboardConfigItem:read", "read"})
|
||||
*
|
||||
* @Assert\NotNull
|
||||
*/
|
||||
private string $type = '';
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*
|
||||
* @Serializer\Groups({"dashboardConfigItem:read", "read"})
|
||||
*
|
||||
* @Assert\NotNull
|
||||
*/
|
||||
private string $position = '';
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=User::class)
|
||||
*/
|
||||
private ?User $user = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="json" "jsonb"=true, options={"default": "[]"})
|
||||
*
|
||||
* @Serializer\Groups({"dashboardConfigItem:read"})
|
||||
*/
|
||||
private array $metadata = [];
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
128
src/Bundle/ChillMainBundle/Entity/NewsItem.php
Normal file
128
src/Bundle/ChillMainBundle/Entity/NewsItem.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*
|
||||
* @ORM\Table(name="chill_main_news")
|
||||
*/
|
||||
class NewsItem implements TrackCreationInterface, TrackUpdateInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
|
||||
use TrackUpdateTrait;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
*
|
||||
* @ORM\GeneratedValue
|
||||
*
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
*
|
||||
* @Groups({"read"})
|
||||
*
|
||||
* @Assert\NotBlank
|
||||
*
|
||||
* @Assert\NotNull
|
||||
*/
|
||||
private string $title = '';
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text")
|
||||
*
|
||||
* @Groups({"read"})
|
||||
*
|
||||
* @Assert\NotBlank
|
||||
*
|
||||
* @Assert\NotNull
|
||||
*/
|
||||
private string $content = '';
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="date_immutable", nullable=false)
|
||||
*
|
||||
* @Assert\NotNull
|
||||
*
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private ?\DateTimeImmutable $startDate = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
|
||||
*
|
||||
* @Assert\GreaterThanOrEqual(propertyPath="startDate")
|
||||
*
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private ?\DateTimeImmutable $endDate = null;
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
54
src/Bundle/ChillMainBundle/Form/NewsItemType.php
Normal file
54
src/Bundle/ChillMainBundle/Form/NewsItemType.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\NewsItem;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class NewsItemType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->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);
|
||||
}
|
||||
}
|
137
src/Bundle/ChillMainBundle/Repository/NewsItemRepository.php
Normal file
137
src/Bundle/ChillMainBundle/Repository/NewsItemRepository.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\NewsItem;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
|
||||
class NewsItemRepository implements ObjectRepository
|
||||
{
|
||||
private readonly EntityRepository $repository;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, private ClockInterface $clock)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
@ -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";
|
||||
|
@ -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}
|
||||
}
|
||||
|
@ -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) {
|
||||
if (tab !== 'MyCustoms') {
|
||||
this.$store.dispatch('getByTab', { tab: tab });
|
||||
}
|
||||
this.activeTab = tab;
|
||||
console.log(this.activeTab)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>{{ $t('widget.news.title') }}</h1>
|
||||
<ul class="scrollable">
|
||||
<NewsItem v-for="item in newsItems" :item="item" :key="item.id" />
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { makeFetch } from '../../../lib/api/apiMethods';
|
||||
import Modal from '../../_components/Modal.vue';
|
||||
import { NewsItemType } from '../../../types';
|
||||
import NewsItem from './NewsItem.vue';
|
||||
|
||||
const newsItems = ref<NewsItemType[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
makeFetch('GET', '/api/1.0/main/news.json')
|
||||
.then((response: { results: NewsItemType[] }) => {
|
||||
// console.log('news articles', response.results)
|
||||
newsItems.value = response.results
|
||||
})
|
||||
.catch((error: string) => {
|
||||
console.error('Error fetching news items', error);
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
.scrollable {
|
||||
overflow-y: auto;
|
||||
max-height: 150px; !* Same as .content max-height *!
|
||||
margin-bottom: 10px; !* To give space for the scrollbar *!
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
</style>
|
@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<li>
|
||||
<h2>{{ props.item.title }}</h2>
|
||||
<div class="content" v-if="shouldTruncate(item.content)">
|
||||
<div v-html="prepareContent(item.content)"></div>
|
||||
<span class="read-more" @click="() => openModal(item)">{{ $t('widget.news.readMore') }}</span>
|
||||
</div>
|
||||
<div class="content" v-else>
|
||||
<div v-html="convertMarkdownToHtml(item.content)"></div>
|
||||
</div>
|
||||
|
||||
<modal v-if="showModal" @close="closeModal">
|
||||
<template #header>
|
||||
<p class="news-title">{{ item.title }}</p>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="news-date">
|
||||
{{ $t('widget.news.date') }}: <span>{{ formatDate(item.startdate.date) }}</span>
|
||||
</p>
|
||||
<div v-html="convertMarkdownToHtml(item.content)"></div>
|
||||
</template>
|
||||
</modal>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { DateTime, NewsItemType } from "../../../types";
|
||||
import type { PropType } from 'vue'
|
||||
import { defineProps, ref } from "vue";
|
||||
import {dateToISO} from "../../../chill/js/date";
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object as PropType<NewsItemType>,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const selectedArticle = ref<NewsItemType | null>(null);
|
||||
const showModal = ref(false);
|
||||
|
||||
const openModal = (item: NewsItemType) => {
|
||||
selectedArticle.value = item;
|
||||
showModal.value = true;
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
selectedArticle.value = null;
|
||||
showModal.value = false;
|
||||
};
|
||||
|
||||
const shouldTruncate = (content: string, maxLength = 100): boolean => {
|
||||
return content.length > maxLength;
|
||||
};
|
||||
|
||||
const truncateContent = (content: string, maxLength = 100): string => {
|
||||
if (shouldTruncate(content, maxLength)) {
|
||||
let truncatedContent = content.slice(0, maxLength);
|
||||
let linkDepth = 0;
|
||||
let linkStartIndex = -1;
|
||||
|
||||
for (let i = 0; i < truncatedContent.length; i++) {
|
||||
const char = truncatedContent[i];
|
||||
|
||||
if (char === '[') {
|
||||
linkDepth++;
|
||||
if (linkDepth === 1) {
|
||||
linkStartIndex = i;
|
||||
}
|
||||
} else if (char === ']') {
|
||||
linkDepth = Math.max(0, linkDepth - 1);
|
||||
} else if (char === '(' && linkDepth === 0) {
|
||||
truncatedContent = truncatedContent.slice(0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (linkDepth > 0) {
|
||||
truncatedContent += ']';
|
||||
linkDepth--;
|
||||
}
|
||||
|
||||
// If a link was found, append the URL inside the parentheses
|
||||
if (linkStartIndex !== -1) {
|
||||
const linkEndIndex = content.indexOf(')', linkStartIndex);
|
||||
const url = content.slice(linkStartIndex + 1, linkEndIndex);
|
||||
truncatedContent = truncatedContent.slice(0, linkStartIndex) + `(${url})`;
|
||||
}
|
||||
|
||||
truncatedContent += '...';
|
||||
|
||||
return truncatedContent;
|
||||
} else {
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
const convertMarkdownToHtml = (markdown: string): string => {
|
||||
const rawHtml = marked(markdown);
|
||||
return DOMPurify.sanitize(rawHtml)
|
||||
};
|
||||
|
||||
const prepareContent = (content: string, maxLength = 200): string => {
|
||||
const htmlContent = convertMarkdownToHtml(content);
|
||||
return truncateContent(htmlContent, maxLength);
|
||||
};
|
||||
|
||||
const formatDate = (datetime: DateTime): string|null => {
|
||||
return dateToISO(new Date(datetime.toString()))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
li {
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-height: 100px; /* Adjust the max height as needed */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
background-color: #3498db;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.read-more {
|
||||
color: #3498db; /* Adjust the color as needed */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.news-date {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.news-title {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style>
|
@ -39,23 +39,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="mbloc col col-sm-6 col-lg-4">
|
||||
<div class="custom2">
|
||||
Mon dashboard personnalisé
|
||||
<div class="mbloc col col-lg-12 col-lg-4" v-if="this.dashboardItems">
|
||||
<div v-for="dashboardItem in this.dashboardItems">
|
||||
<News v-if="dashboardItem.type === 'news'"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mbloc col col-sm-6 col-lg-4">
|
||||
<div class="custom3">
|
||||
Mon dashboard personnalisé
|
||||
</div>
|
||||
</div>
|
||||
<div class="mbloc col col-sm-6 col-lg-4">
|
||||
<div class="custom4">
|
||||
Mon dashboard personnalisé
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@ -63,14 +51,20 @@
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import Masonry from 'masonry-layout/masonry';
|
||||
import {makeFetch} from "ChillMainAssets/lib/api/apiMethods";
|
||||
import News from './DashboardWidgets/News.vue';
|
||||
|
||||
export default {
|
||||
name: "MyCustoms",
|
||||
components: {
|
||||
News
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
counterClass: {
|
||||
counter: true //hack to pass class 'counter' in i18n-t
|
||||
}
|
||||
},
|
||||
dashboardItems: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -82,6 +76,15 @@ export default {
|
||||
mounted() {
|
||||
const elem = document.querySelector('#dashboards');
|
||||
const masonry = new Masonry(elem, {});
|
||||
//Fetch the dashboard items configured for user. Currently response is still hardcoded
|
||||
makeFetch('GET', '/api/1.0/main/dashboard-config-item.json')
|
||||
.then((response) => {
|
||||
this.dashboardItems = response;
|
||||
console.log('dashboarditems', this.dashboardItems)
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -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,7 +219,7 @@ const store = createStore({
|
||||
default:
|
||||
throw 'tab '+ tab;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -51,6 +51,13 @@ 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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 %}<!-- block content empty -->
|
||||
<h1>{{ 'admin.dashboard.description' | trans }}</h1>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@ -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 %}
|
@ -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 %}
|
||||
<th>{{ 'Title'|trans }}</th>
|
||||
<th>{{ 'news.startDate'|trans }}</th>
|
||||
<th>{{ 'news.endDate'|trans }}</th>
|
||||
{% endblock %}
|
||||
{% block table_entities_tbody %}
|
||||
{% for entity in entities %}
|
||||
<tr>
|
||||
<td>{{ entity.title }}</td>
|
||||
<td>{{ entity.startDate|date }}</td>
|
||||
{% if entity.endDate is not null %}
|
||||
<td>{{ entity.endDate|date }}</td>
|
||||
{% else %}
|
||||
<td>{{ 'news.noDate'|trans }}</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_crud_news_item_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block actions_before %}
|
||||
<li class='cancel'>
|
||||
<a href="{{ path('chill_main_admin_central') }}" class="btn btn-cancel">{{'Back to the admin'|trans}}</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
@ -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 %}
|
@ -0,0 +1,74 @@
|
||||
{% extends "@ChillMain/layout.html.twig" %}
|
||||
|
||||
{% block title %}
|
||||
{{ 'news.title'|trans }}
|
||||
{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-10 asideactivity-list">
|
||||
<h2>{{ 'news.title'|trans }}</h2>
|
||||
|
||||
{{ filter_order|chill_render_filter_order_helper }}
|
||||
|
||||
{% if entities|length == 0 %}
|
||||
<p class="chill-no-data-statement">
|
||||
{{ "news.no_data"|trans }}
|
||||
</p>
|
||||
{% else %}
|
||||
|
||||
<div class="flex-table">
|
||||
|
||||
{% for entity in entities %}
|
||||
|
||||
<div class="item-bloc">
|
||||
<div class="item-row wrap-header">
|
||||
|
||||
<div class="item-col">
|
||||
<h3>
|
||||
{{ entity.title }}
|
||||
</h3>
|
||||
<div>
|
||||
{% if entity.startDate %}
|
||||
<span>{{ entity.startDate|format_date('long') }}</span>
|
||||
{% endif %}
|
||||
{% if entity.endDate %}
|
||||
<span> - {{ entity.endDate|format_date('long') }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-col" style="justify-content: flex-end;">
|
||||
<div class="box">
|
||||
<div>
|
||||
{# <blockquote class="chill-user-quote">#}
|
||||
{{ entity.content|u.truncate(350, '…', false)|chill_markdown_to_html }}
|
||||
{# {% if entity.content|length > 350 %}#}
|
||||
{# <a href="{{ chill_path_add_return_path('chill_main_news_items_history', {'news_item_id': entity.id}) }}">{{ 'news.read_more'|trans }}</a>#}
|
||||
{# {% endif %}#}
|
||||
{# </blockquote>#}
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_main_single_news_item', { 'id': entity.id } ) }}" class="btn btn-show btn-mini"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{ chill_pagination(paginator) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_crud_aside_activity_new') }}" class="btn btn-create">
|
||||
{{ 'Create'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,21 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title 'news.show_details'|trans %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ entity.title }}</h1>
|
||||
|
||||
<div class="flex-table">
|
||||
<div class="item-row">
|
||||
<div>
|
||||
<span>{{ entity.startDate|format_date('long') }}</span>
|
||||
{% if entity.endDate is not null %}
|
||||
<span> - {{ entity.endDate|format_date('long') }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
{{ entity.content|chill_markdown_to_html }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Routing\MenuBuilder;
|
||||
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
|
||||
class AdminNewsMenuBuilder implements LocalMenuBuilderInterface
|
||||
{
|
||||
public function __construct(private readonly AuthorizationCheckerInterface $authorizationChecker) {}
|
||||
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
{
|
||||
if (!$this->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'];
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Create dashboard config item and news item.
|
||||
*/
|
||||
final class Version20231108141141 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Create dashboard config item and news item';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->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');
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user