mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 14:43:49 +00:00
Merge branch 'foldlist_in_entity' of gitlab.com:Chill-Projet/chill-bundles into foldlist_in_entity
This commit is contained in:
@@ -13,13 +13,19 @@ namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Entity\Notification;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Repository\NotificationRepository;
|
||||
use Chill\MainBundle\Security\Authorization\NotificationVoter;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Chill\MainBundle\Serializer\Model\Counter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
@@ -29,12 +35,26 @@ class NotificationApiController
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private NotificationRepository $notificationRepository;
|
||||
|
||||
private PaginatorFactory $paginatorFactory;
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, Security $security)
|
||||
{
|
||||
private SerializerInterface $serializer;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $entityManager,
|
||||
NotificationRepository $notificationRepository,
|
||||
PaginatorFactory $paginatorFactory,
|
||||
Security $security,
|
||||
SerializerInterface $serializer
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->notificationRepository = $notificationRepository;
|
||||
$this->paginatorFactory = $paginatorFactory;
|
||||
$this->security = $security;
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +73,37 @@ class NotificationApiController
|
||||
return $this->markAs('unread', $notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/my/unread")
|
||||
*/
|
||||
public function myUnreadNotifications(Request $request): JsonResponse
|
||||
{
|
||||
$total = $this->notificationRepository->countUnreadByUser($this->security->getUser());
|
||||
|
||||
if ($request->query->getBoolean('countOnly')) {
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize(new Counter($total), 'json', ['groups' => ['read']]),
|
||||
JsonResponse::HTTP_OK,
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
$paginator = $this->paginatorFactory->create($total);
|
||||
$notifications = $this->notificationRepository->findUnreadByUser(
|
||||
$this->security->getUser(),
|
||||
$paginator->getItemsPerPage(),
|
||||
$paginator->getCurrentPageFirstItemNumber()
|
||||
);
|
||||
$collection = new Collection($notifications, $paginator);
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($collection, 'json', ['groups' => ['read']]),
|
||||
JsonResponse::HTTP_OK,
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private function markAs(string $target, Notification $notification): JsonResponse
|
||||
{
|
||||
if (!$this->security->isGranted(NotificationVoter::NOTIFICATION_TOGGLE_READ_STATUS, $notification)) {
|
||||
|
166
src/Bundle/ChillMainBundle/Entity/ResidentialAddress.php
Normal file
166
src/Bundle/ChillMainBundle/Entity/ResidentialAddress.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||
use Chill\MainBundle\Repository\ResidentialAddressRepository;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=ResidentialAddressRepository::class)
|
||||
* @ORM\Table(name="chill_main_residential_address")
|
||||
*/
|
||||
class ResidentialAddress
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Address::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
*/
|
||||
private ?Address $address = null;
|
||||
|
||||
/**
|
||||
* @ORM\Embedded(class="Chill\MainBundle\Entity\Embeddable\CommentEmbeddable", columnPrefix="residentialAddressComment_")
|
||||
*/
|
||||
private CommentEmbeddable $comment;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable", nullable=true)
|
||||
*/
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Person::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
*/
|
||||
private ?Person $hostPerson = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
*/
|
||||
private ?ThirdParty $hostThirdParty = null;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Person::class)
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*/
|
||||
private Person $person;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable")
|
||||
*/
|
||||
private ?DateTimeImmutable $startDate = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comment = new CommentEmbeddable();
|
||||
}
|
||||
|
||||
public function getAddress(): ?Address
|
||||
{
|
||||
return $this->address;
|
||||
}
|
||||
|
||||
public function getComment(): CommentEmbeddable
|
||||
{
|
||||
return $this->comment;
|
||||
}
|
||||
|
||||
public function getEndDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function getHostPerson(): ?Person
|
||||
{
|
||||
return $this->hostPerson;
|
||||
}
|
||||
|
||||
public function getHostThirdParty(): ?ThirdParty
|
||||
{
|
||||
return $this->hostThirdParty;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPerson(): ?Person
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
public function getStartDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function setAddress(?Address $address): self
|
||||
{
|
||||
$this->address = $address;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setComment(CommentEmbeddable $comment): self
|
||||
{
|
||||
$this->comment = $comment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEndDate(?DateTimeImmutable $endDate): self
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setHostPerson(?Person $hostPerson): self
|
||||
{
|
||||
$this->hostPerson = $hostPerson;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setHostThirdParty(?ThirdParty $hostThirdParty): self
|
||||
{
|
||||
$this->hostThirdParty = $hostThirdParty;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPerson(?Person $person): self
|
||||
{
|
||||
$this->person = $person;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStartDate(DateTimeImmutable $startDate): self
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Form\Type;
|
||||
|
||||
use Chill\MainBundle\Entity\ResidentialAddress;
|
||||
use Chill\PersonBundle\Form\Type\PickPersonDynamicType;
|
||||
use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class ResidentialAddressType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('startDate', DateType::class, [
|
||||
'required' => true,
|
||||
'input' => 'datetime_immutable',
|
||||
'widget' => 'single_text',
|
||||
])
|
||||
->add('endDate', DateType::class, [
|
||||
'required' => false,
|
||||
'input' => 'datetime_immutable',
|
||||
'widget' => 'single_text',
|
||||
])
|
||||
->add('comment', CommentType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
if ('person' === $options['kind']) {
|
||||
$builder
|
||||
->add('hostPerson', PickPersonDynamicType::class, [
|
||||
'label' => 'Person',
|
||||
]);
|
||||
}
|
||||
|
||||
if ('thirdparty' === $options['kind']) {
|
||||
$builder
|
||||
->add('hostThirdParty', PickThirdpartyDynamicType::class, [
|
||||
'label' => 'Third party',
|
||||
]);
|
||||
}
|
||||
|
||||
if ('address' === $options['kind']) {
|
||||
$builder
|
||||
->add('address', PickAddressType::class, [
|
||||
'required' => false,
|
||||
'label' => 'Address',
|
||||
'use_valid_from' => false,
|
||||
'use_valid_to' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => ResidentialAddress::class,
|
||||
'kind' => null,
|
||||
]);
|
||||
}
|
||||
}
|
@@ -193,6 +193,29 @@ final class NotificationRepository implements ObjectRepository
|
||||
return $this->repository->findOneBy($criteria, $orderBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|Notification[]
|
||||
*/
|
||||
public function findUnreadByUser(User $user, int $limit = 20, int $offset = 0): array
|
||||
{
|
||||
$rsm = new Query\ResultSetMappingBuilder($this->em);
|
||||
$rsm->addRootEntityFromClassMetadata(Notification::class, 'cmn');
|
||||
|
||||
$sql = 'SELECT ' . $rsm->generateSelectClause(['cmn' => 'cmn']) . ' ' .
|
||||
'FROM chill_main_notification cmn ' .
|
||||
'WHERE ' .
|
||||
'EXISTS (select 1 FROM chill_main_notification_addresses_unread cmnau WHERE cmnau.user_id = :userId and cmnau.notification_id = cmn.id) ' .
|
||||
'ORDER BY cmn.date DESC ' .
|
||||
'LIMIT :limit OFFSET :offset';
|
||||
|
||||
$nq = $this->em->createNativeQuery($sql, $rsm)
|
||||
->setParameter('userId', $user->getId())
|
||||
->setParameter('limit', $limit)
|
||||
->setParameter('offset', $offset);
|
||||
|
||||
return $nq->getResult();
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
{
|
||||
return Notification::class;
|
||||
|
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\ResidentialAddress;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @method ResidentialAddress|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method ResidentialAddress|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method ResidentialAddress[] findAll()
|
||||
* @method ResidentialAddress[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class ResidentialAddressRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, ResidentialAddress::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return ResidentialAddress[] Returns an array of ResidentialAddress objects
|
||||
// */
|
||||
/*
|
||||
public function findByExampleField($value)
|
||||
{
|
||||
return $this->createQueryBuilder('r')
|
||||
->andWhere('r.exampleField = :val')
|
||||
->setParameter('val', $value)
|
||||
->orderBy('r.id', 'ASC')
|
||||
->setMaxResults(10)
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public function findOneBySomeField($value): ?ResidentialAddress
|
||||
{
|
||||
return $this->createQueryBuilder('r')
|
||||
->andWhere('r.exampleField = :val')
|
||||
->setParameter('val', $value)
|
||||
->getQuery()
|
||||
->getOneOrNullResult()
|
||||
;
|
||||
}
|
||||
*/
|
||||
}
|
@@ -12,6 +12,7 @@
|
||||
display: block;
|
||||
top: calc(50% - 7px);
|
||||
right: 10px;
|
||||
line-height: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,14 +63,19 @@ ul.list-suggest {
|
||||
& span:hover {
|
||||
color: $chill-l-gray;
|
||||
}
|
||||
.person-text {
|
||||
span {
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.remove-items {
|
||||
li {
|
||||
position: relative;
|
||||
span {
|
||||
& > span {
|
||||
display: block;
|
||||
padding-right: .75rem;
|
||||
padding-right: 1.75rem;
|
||||
@include remove_link;
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ $chill-theme-buttons: (
|
||||
"cancel": $gray-300,
|
||||
"choose": $gray-300,
|
||||
"notify": $chill-blue,
|
||||
"search": $gray-300,
|
||||
"unlink": $chill-red,
|
||||
"tpchild": $chill-pink,
|
||||
);
|
||||
@@ -80,6 +81,7 @@ $chill-theme-buttons: (
|
||||
&.btn-notify::before,
|
||||
&.btn-tpchild::before,
|
||||
&.btn-download::before,
|
||||
&.btn-search::before,
|
||||
&.btn-cancel::before {
|
||||
font: normal normal normal 14px/1 ForkAwesome;
|
||||
margin-right: 0.5em;
|
||||
@@ -108,6 +110,7 @@ $chill-theme-buttons: (
|
||||
&.btn-notify::before { content: "\f1d8"; } // fa-paper-plane
|
||||
&.btn-tpchild::before { content: "\f007"; } // fa-user
|
||||
&.btn-download::before { content: "\f019"; } // fa-download
|
||||
&.btn-search::before { content: "\f002"; } // fa-search
|
||||
}
|
||||
|
||||
|
||||
@@ -133,3 +136,18 @@ $chill-theme-buttons: (
|
||||
.btn-sm, .btn-group-sm > .btn {
|
||||
min-width: 36px;
|
||||
}
|
||||
|
||||
// Homepage special fast action buttons
|
||||
div.sticky-buttons {
|
||||
position: fixed;
|
||||
bottom: 3em;
|
||||
right: 2em;
|
||||
.btn-circle {
|
||||
width: 50px; height: 50px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
padding: 0.45rem 0.7rem;
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,12 @@ function loadDynamicPicker(element) {
|
||||
input = element.querySelector('[data-input-uniqid="'+ el.dataset.uniqid +'"]'),
|
||||
picked = (isMultiple) ? (JSON.parse(input.value)) : ((input.value === '[]') ? (null) : ([JSON.parse(input.value)]));
|
||||
|
||||
if (!isMultiple) {
|
||||
if (input.value === '[]'){
|
||||
input.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
const app = createApp({
|
||||
template: '<pick-entity ' +
|
||||
':multiple="multiple" ' +
|
||||
|
@@ -0,0 +1,16 @@
|
||||
import { createApp } from 'vue';
|
||||
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
|
||||
import { appMessages } from 'ChillMainAssets/vuejs/HomepageWidget/js/i18n';
|
||||
import { store } from 'ChillMainAssets/vuejs/HomepageWidget/js/store';
|
||||
import App from 'ChillMainAssets/vuejs/HomepageWidget/App';
|
||||
|
||||
const i18n = _createI18n(appMessages);
|
||||
|
||||
const app = createApp({
|
||||
template: `<app></app>`,
|
||||
})
|
||||
.use(store)
|
||||
.use(i18n)
|
||||
.component('app', App)
|
||||
.mount('#homepage_widget')
|
||||
;
|
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
|
||||
<h2>{{ $t('main_title') }}</h2>
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
:class="{'active': activeTab === 'MyCustoms'}"
|
||||
@click="selectTab('MyCustoms')">
|
||||
<i class="fa fa-dashboard"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
:class="{'active': activeTab === 'MyNotifications'}"
|
||||
@click="selectTab('MyNotifications')">
|
||||
{{ $t('my_notifications.tab') }}
|
||||
<tab-counter :count="state.notifications.count"></tab-counter>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
:class="{'active': activeTab === 'MyAccompanyingCourses'}"
|
||||
@click="selectTab('MyAccompanyingCourses')">
|
||||
{{ $t('my_accompanying_courses.tab') }}
|
||||
<tab-counter :count="state.accompanyingCourses.count"></tab-counter>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
:class="{'active': activeTab === 'MyWorks'}"
|
||||
@click="selectTab('MyWorks')">
|
||||
{{ $t('my_works.tab') }}
|
||||
<tab-counter :count="state.works.count"></tab-counter>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
:class="{'active': activeTab === 'MyEvaluations'}"
|
||||
@click="selectTab('MyEvaluations')">
|
||||
{{ $t('my_evaluations.tab') }}
|
||||
<tab-counter :count="state.evaluations.count"></tab-counter>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link"
|
||||
:class="{'active': activeTab === 'MyTasks'}"
|
||||
@click="selectTab('MyTasks')">
|
||||
{{ $t('my_tasks.tab') }}
|
||||
<tab-counter :count="state.tasks.warning.count"></tab-counter>
|
||||
<tab-counter :count="state.tasks.alert.count"></tab-counter>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item loading ms-auto py-2" v-if="loading">
|
||||
<i class="fa fa-circle-o-notch fa-spin fa-lg text-chill-gray" :title="$t('loading')"></i>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="my-4">
|
||||
<my-customs
|
||||
v-if="activeTab === 'MyCustoms'">
|
||||
</my-customs>
|
||||
<my-works
|
||||
v-else-if="activeTab === 'MyWorks'">
|
||||
</my-works>
|
||||
<my-evaluations
|
||||
v-else-if="activeTab === 'MyEvaluations'">
|
||||
</my-evaluations>
|
||||
<my-tasks
|
||||
v-else-if="activeTab === 'MyTasks'">
|
||||
</my-tasks>
|
||||
<my-accompanying-courses
|
||||
v-else-if="activeTab === 'MyAccompanyingCourses'">
|
||||
</my-accompanying-courses>
|
||||
<my-notifications
|
||||
v-else-if="activeTab === 'MyNotifications'">
|
||||
</my-notifications>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MyCustoms from './MyCustoms';
|
||||
import MyWorks from './MyWorks';
|
||||
import MyEvaluations from './MyEvaluations';
|
||||
import MyTasks from './MyTasks';
|
||||
import MyAccompanyingCourses from './MyAccompanyingCourses';
|
||||
import MyNotifications from './MyNotifications';
|
||||
import TabCounter from './TabCounter';
|
||||
import { mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
MyCustoms,
|
||||
MyWorks,
|
||||
MyEvaluations,
|
||||
MyTasks,
|
||||
MyAccompanyingCourses,
|
||||
MyNotifications,
|
||||
TabCounter,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'MyCustoms'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'loading',
|
||||
]),
|
||||
// just to see all in devtool :
|
||||
...mapState({
|
||||
state: (state) => state,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
selectTab(tab) {
|
||||
this.$store.dispatch('getByTab', { tab: tab });
|
||||
this.activeTab = tab;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
for (const m of [
|
||||
'MyNotifications',
|
||||
'MyAccompanyingCourses',
|
||||
'MyWorks',
|
||||
'MyEvaluations',
|
||||
'MyTasks',
|
||||
]) {
|
||||
this.$store.dispatch('getByTab', { tab: m, param: "countOnly=1" });
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
a.nav-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="alert alert-light">{{ $t('my_accompanying_courses.description') }}</div>
|
||||
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span>
|
||||
<tab-table v-else>
|
||||
<template v-slot:thead>
|
||||
<th scope="col">id</th>
|
||||
<th scope="col">Ouvert le</th>
|
||||
<th scope="col">Usagers concernés</th>
|
||||
<th scope="col"></th>
|
||||
</template>
|
||||
<template v-slot:tbody>
|
||||
<tr v-for="(c, i) in accompanyingCourses.results" :key="`course-${i}`">
|
||||
<td>{{ c.id}}</td>
|
||||
<td>{{ $d(c.openingDate.datetime, 'long') }}</td>
|
||||
<td>{{ c.participations.length }}</td>
|
||||
<td>
|
||||
<a class="btn btn-sm btn-show" :href="getUrl(c)">
|
||||
{{ $t('show_entity', { entity: $t('the_course') }) }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tab-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import TabTable from "./TabTable";
|
||||
|
||||
export default {
|
||||
name: "MyAccompanyingCourses",
|
||||
components: {
|
||||
TabTable
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'accompanyingCourses',
|
||||
]),
|
||||
...mapGetters([
|
||||
'isAccompanyingCoursesLoaded',
|
||||
]),
|
||||
noResults() {
|
||||
if (!this.isAccompanyingCoursesLoaded) {
|
||||
return false;
|
||||
} else {
|
||||
return this.accompanyingCourses.count === 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getUrl(c) {
|
||||
return `/fr/parcours/${c.id}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_dashboard') }}</span>
|
||||
<div v-else id="dashboards" class="row g-3" data-masonry='{"percentPosition": true }'>
|
||||
|
||||
<div class="mbloc col col-sm-6 col-lg-4">
|
||||
<div class="custom1">
|
||||
<ul class="list-unstyled">
|
||||
<li v-if="counter.notifications > 0">
|
||||
<b>{{ counter.notifications }}</b> {{ $t('counter.unread_notifications') }}
|
||||
</li>
|
||||
<li v-if="counter.accompanyingCourses > 0">
|
||||
<b>{{ counter.accompanyingCourses }}</b> {{ $t('counter.assignated_courses') }}
|
||||
</li>
|
||||
<li v-if="counter.works > 0">
|
||||
<b>{{ counter.works }}</b> {{ $t('counter.assignated_actions') }}
|
||||
</li>
|
||||
<li v-if="counter.evaluations > 0">
|
||||
<b>{{ counter.evaluations }}</b> {{ $t('counter.assignated_evaluations') }}
|
||||
</li>
|
||||
<li v-if="counter.tasksAlert > 0">
|
||||
<b>{{ counter.tasksAlert }}</b> {{ $t('counter.alert_tasks') }}
|
||||
</li>
|
||||
<li v-if="counter.tasksWarning > 0">
|
||||
<b>{{ counter.tasksWarning }}</b> {{ $t('counter.warning_tasks') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="mbloc col col-sm-6 col-lg-4">
|
||||
<div class="custom2">
|
||||
Mon dashboard personnalisé
|
||||
</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>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import Masonry from 'masonry-layout/masonry';
|
||||
|
||||
export default {
|
||||
name: "MyCustoms",
|
||||
computed: {
|
||||
...mapGetters(['counter']),
|
||||
noResults() {
|
||||
return false
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const elem = document.querySelector('#dashboards');
|
||||
const masonry = new Masonry(elem, {});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div.custom4,
|
||||
div.custom3,
|
||||
div.custom2 {
|
||||
font-style: italic;
|
||||
color: var(--bs-chill-gray);
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="alert alert-light">{{ $t('my_evaluations.description') }}</div>
|
||||
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span>
|
||||
<tab-table v-else>
|
||||
<template v-slot:thead>
|
||||
<th scope="col">id</th>
|
||||
<th scope="col"></th>
|
||||
</template>
|
||||
<template v-slot:tbody>
|
||||
<tr v-for="(e, i) in evaluations.results" :key="`evaluation-${i}`">
|
||||
<td>{{ e.id}}</td>
|
||||
<td>
|
||||
<a class="btn btn-sm btn-show" :href="getUrl(e)">
|
||||
{{ $t('show_entity', { entity: $t('the_evaluation') }) }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tab-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import TabTable from "./TabTable";
|
||||
|
||||
export default {
|
||||
name: "MyEvaluations",
|
||||
components: {
|
||||
TabTable
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'evaluations',
|
||||
]),
|
||||
...mapGetters([
|
||||
'isEvaluationsLoaded',
|
||||
]),
|
||||
noResults() {
|
||||
if (!this.isEvaluationsLoaded) {
|
||||
return false;
|
||||
} else {
|
||||
return this.evaluations.count === 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getUrl(e) {
|
||||
let anchor = '#evaluations';
|
||||
return `/fr/person/accompanying-period/work/${e.id}/edit${anchor}`
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="alert alert-light">{{ $t('my_notifications.description') }}</div>
|
||||
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span>
|
||||
<tab-table v-else>
|
||||
<template v-slot:thead>
|
||||
<th scope="col">{{ $t('Date') }}</th>
|
||||
<th scope="col">{{ $t('Subject') }}</th>
|
||||
<th scope="col">{{ $t('From') }}</th>
|
||||
<th scope="col"></th>
|
||||
</template>
|
||||
<template v-slot:tbody>
|
||||
<tr v-for="(n, i) in notifications.results" :key="`notify-${i}`">
|
||||
<td>{{ $d(n.date.datetime, 'long') }}</td>
|
||||
<td>
|
||||
<span class="unread">
|
||||
<i class="fa fa-envelope-o"></i>
|
||||
<a :href="getNotificationUrl(n)">{{ n.title }}</a>
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ n.sender.text }}</td>
|
||||
<td>
|
||||
<a class="btn btn-sm btn-show"
|
||||
:href="getEntityUrl(n)">
|
||||
{{ $t('show_entity', { entity: getEntityName(n) }) }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tab-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import TabTable from "./TabTable";
|
||||
import { appMessages } from 'ChillMainAssets/vuejs/HomepageWidget/js/i18n';
|
||||
|
||||
|
||||
export default {
|
||||
name: "MyNotifications",
|
||||
components: {
|
||||
TabTable
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'notifications',
|
||||
]),
|
||||
...mapGetters([
|
||||
'isNotificationsLoaded',
|
||||
]),
|
||||
noResults() {
|
||||
if (!this.isNotificationsLoaded) {
|
||||
return false;
|
||||
} else {
|
||||
return this.notifications.count === 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNotificationUrl(n) {
|
||||
return `/fr/notification/${n.id}/show`
|
||||
},
|
||||
getEntityName(n) {
|
||||
switch (n.relatedEntityClass) {
|
||||
case 'Chill\\ActivityBundle\\Entity\\Activity':
|
||||
return appMessages.fr.the_activity;
|
||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
|
||||
return appMessages.fr.the_course;
|
||||
default:
|
||||
throw 'notification type unknown';
|
||||
}
|
||||
},
|
||||
getEntityUrl(n) {
|
||||
switch (n.relatedEntityClass) {
|
||||
case 'Chill\\ActivityBundle\\Entity\\Activity':
|
||||
return `/fr/activity/${n.relatedEntityId}/show`
|
||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
|
||||
return `/fr/parcours/${n.relatedEntityId}`
|
||||
default:
|
||||
throw 'notification type unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
span.unread {
|
||||
font-weight: bold;
|
||||
i {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
a {
|
||||
text-decoration: unset;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
|
||||
<div class="alert alert-light">{{ $t('my_tasks.description_alert') }}</div>
|
||||
<span v-if="noResultsWarning" class="chill-no-data-statement">{{ $t('no_data') }}</span>
|
||||
<tab-table v-else>
|
||||
<template v-slot:thead>
|
||||
<th scope="col">id</th>
|
||||
<th scope="col"></th>
|
||||
</template>
|
||||
<template v-slot:tbody>
|
||||
<tr v-for="(t, i) in tasks.warning" :key="`task-warning-${i}`">
|
||||
<td>{{ t.id}}</td>
|
||||
<td>
|
||||
<a class="btn btn-sm btn-show" :href="getUrl(t)">
|
||||
{{ $t('show_entity', { entity: $t('the_task') }) }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tab-table>
|
||||
|
||||
<div class="alert alert-light">{{ $t('my_tasks.description_warning') }}</div>
|
||||
<span v-if="noResultsAlert" class="chill-no-data-statement">{{ $t('no_data') }}</span>
|
||||
<tab-table v-else>
|
||||
<template v-slot:thead>
|
||||
<th scope="col">id</th>
|
||||
<th scope="col"></th>
|
||||
</template>
|
||||
<template v-slot:tbody>
|
||||
<tr v-for="(t, i) in tasks.alert" :key="`task-alert-${i}`">
|
||||
<td>{{ t.id}}</td>
|
||||
<td>
|
||||
<a class="btn btn-sm btn-show" :href="getUrl(t)">
|
||||
{{ $t('show_entity', { entity: $t('the_task') }) }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tab-table>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import TabTable from "./TabTable";
|
||||
|
||||
export default {
|
||||
name: "MyTasks",
|
||||
components: {
|
||||
TabTable
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'tasks',
|
||||
]),
|
||||
...mapGetters([
|
||||
'isTasksWarningLoaded',
|
||||
'isTasksAlertLoaded',
|
||||
]),
|
||||
noResultsAlert() {
|
||||
if (!this.isTasksAlertLoaded) {
|
||||
return false;
|
||||
} else {
|
||||
return this.tasks.alert.count === 0;
|
||||
}
|
||||
},
|
||||
noResultsWarning() {
|
||||
if (!this.isTasksWarningLoaded) {
|
||||
return false;
|
||||
} else {
|
||||
return this.tasks.warning.count === 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getUrl(t) {
|
||||
return `/fr/task/single-task/${t.id}/show`
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="accompanying_course_work">
|
||||
<div class="alert alert-light">{{ $t('my_works.description') }}</div>
|
||||
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span>
|
||||
<tab-table v-else>
|
||||
<template v-slot:thead>
|
||||
<th scope="col">{{ $t('StartDate') }}</th>
|
||||
<th scope="col">{{ $t('SocialAction') }}</th>
|
||||
<th scope="col"></th>
|
||||
</template>
|
||||
<template v-slot:tbody>
|
||||
<tr v-for="(w, i) in works.results" :key="`works-${i}`">
|
||||
<td>{{ $d(w.startDate.datetime, 'short') }}</td>
|
||||
<td>
|
||||
<h4 class="badge-title">
|
||||
<span class="title_label"></span>
|
||||
<span class="title_action">
|
||||
{{ w.socialAction.text }}
|
||||
</span>
|
||||
</h4>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group" aria-label="Actions">
|
||||
<a class="btn btn-sm btn-update" :href="getUrl(w)">
|
||||
{{ $t('show_entity', { entity: $t('the_action') }) }}
|
||||
</a>
|
||||
<a class="btn btn-sm btn-show" :href="getUrl(w.accompanyingPeriod)">
|
||||
{{ $t('show_entity', { entity: $t('the_course') }) }}
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tab-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import TabTable from "./TabTable";
|
||||
|
||||
export default {
|
||||
name: "MyWorks",
|
||||
components: {
|
||||
TabTable
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'works',
|
||||
]),
|
||||
...mapGetters([
|
||||
'isWorksLoaded',
|
||||
]),
|
||||
noResults() {
|
||||
if (!this.isWorksLoaded) {
|
||||
return false;
|
||||
} else {
|
||||
return this.works.count === 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getUrl(e) {
|
||||
switch (e.type) {
|
||||
case 'accompanying_period_work':
|
||||
return `/fr/person/accompanying-period/work/${e.id}/edit`
|
||||
case 'accompanying_period':
|
||||
return `/fr/parcours/${e.id}`
|
||||
default:
|
||||
throw 'entity type unknown';
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<span v-if="isCounterAvailable"
|
||||
class="badge rounded-pill bg-danger counter">
|
||||
{{ count }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TabCounter",
|
||||
props: ['count'],
|
||||
computed: {
|
||||
isCounterAvailable() {
|
||||
return (typeof this.count !== 'undefined' && this.count > 0 )
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<slot name="thead"></slot>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<slot name="tbody"></slot>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TabTable",
|
||||
props: []
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@@ -0,0 +1,54 @@
|
||||
const appMessages = {
|
||||
fr: {
|
||||
main_title: "Vue d'ensemble",
|
||||
my_works: {
|
||||
tab: "Mes actions",
|
||||
description: "Liste des actions d'accompagnement dont je suis référent et qui arrivent à échéance.",
|
||||
},
|
||||
my_evaluations: {
|
||||
tab: "Mes évaluations",
|
||||
description: "Liste des évaluations dont je suis référent et qui arrivent à échéance.",
|
||||
},
|
||||
my_tasks: {
|
||||
tab: "Mes tâches",
|
||||
description_alert: "Liste des tâches auxquelles je suis assigné et dont la date de rappel est dépassée.",
|
||||
description_warning: "Liste des tâches auxquelles je suis assigné et dont la date d'échéance est dépassée.",
|
||||
},
|
||||
my_accompanying_courses: {
|
||||
tab: "Mes parcours",
|
||||
description: "Liste des parcours d'accompagnement que l'on vient de m'attribuer.",
|
||||
},
|
||||
my_notifications: {
|
||||
tab: "Mes notifications",
|
||||
description: "Liste des notifications reçues et non lues.",
|
||||
},
|
||||
Date: "Date",
|
||||
From: "De",
|
||||
Subject: "Objet",
|
||||
Entity: "Associé à",
|
||||
show_entity: "Voir {entity}",
|
||||
the_activity: "l'échange",
|
||||
the_course: "le parcours",
|
||||
the_action: "l'action",
|
||||
the_evaluation: "l'évaluation",
|
||||
the_task: "la tâche",
|
||||
StartDate: "Date d'ouverture",
|
||||
SocialAction: "Action d'accompagnement",
|
||||
no_data: "Aucun résultats",
|
||||
no_dashboard: "Pas de tableaux de bord",
|
||||
counter: {
|
||||
unread_notifications: "notifications non lues",
|
||||
assignated_courses: "parcours récents assignés",
|
||||
assignated_actions: "actions assignées",
|
||||
assignated_evaluations: "évaluations assignées",
|
||||
alert_tasks: "tâches en rappel",
|
||||
warning_tasks: "tâches à échéances",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Object.assign(appMessages.fr);
|
||||
|
||||
export {
|
||||
appMessages
|
||||
};
|
@@ -0,0 +1,200 @@
|
||||
import 'es6-promise/auto';
|
||||
import { createStore } from 'vuex';
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||
import MyCustoms from "../MyCustoms";
|
||||
import MyWorks from "../MyWorks";
|
||||
import MyEvaluations from "../MyEvaluations";
|
||||
import MyTasks from "../MyTasks";
|
||||
import MyAccompanyingCourses from "../MyAccompanyingCourses";
|
||||
import MyNotifications from "../MyNotifications";
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
|
||||
const isEmpty = (obj) => {
|
||||
return obj
|
||||
&& Object.keys(obj).length <= 1
|
||||
&& Object.getPrototypeOf(obj) === Object.prototype;
|
||||
};
|
||||
|
||||
const store = createStore({
|
||||
strict: debug,
|
||||
state: {
|
||||
works: {},
|
||||
evaluations: {},
|
||||
tasks: {
|
||||
warning: {},
|
||||
alert: {}
|
||||
},
|
||||
accompanyingCourses: {},
|
||||
notifications: {},
|
||||
errorMsg: [],
|
||||
loading: false
|
||||
},
|
||||
getters: {
|
||||
isWorksLoaded(state) {
|
||||
return !isEmpty(state.works);
|
||||
},
|
||||
isEvaluationsLoaded(state) {
|
||||
return !isEmpty(state.evaluations);
|
||||
},
|
||||
isTasksWarningLoaded(state) {
|
||||
return !isEmpty(state.tasks.warning);
|
||||
},
|
||||
isTasksAlertLoaded(state) {
|
||||
return !isEmpty(state.tasks.alert);
|
||||
},
|
||||
isAccompanyingCoursesLoaded(state) {
|
||||
return !isEmpty(state.accompanyingCourses);
|
||||
},
|
||||
isNotificationsLoaded(state) {
|
||||
return !isEmpty(state.notifications);
|
||||
},
|
||||
counter(state) {
|
||||
return {
|
||||
works: state.works.count,
|
||||
evaluations: state.evaluations.count,
|
||||
tasksWarning: state.tasks.warning.count,
|
||||
tasksAlert: state.tasks.alert.count,
|
||||
accompanyingCourses: state.accompanyingCourses.count,
|
||||
notifications: state.notifications.count,
|
||||
}
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
addWorks(state, works) {
|
||||
console.log('addWorks', works);
|
||||
state.works = works;
|
||||
},
|
||||
addEvaluations(state, evaluations) {
|
||||
console.log('addEvaluations', evaluations);
|
||||
state.evaluations = evaluations;
|
||||
},
|
||||
addTasksWarning(state, tasks) {
|
||||
console.log('addTasksWarning', tasks);
|
||||
state.tasks.warning = tasks;
|
||||
},
|
||||
addTasksAlert(state, tasks) {
|
||||
console.log('addTasksAlert', tasks);
|
||||
state.tasks.alert = tasks;
|
||||
},
|
||||
addCourses(state, courses) {
|
||||
console.log('addCourses', courses);
|
||||
state.accompanyingCourses = courses;
|
||||
},
|
||||
addNotifications(state, notifications) {
|
||||
console.log('addNotifications', notifications);
|
||||
state.notifications = notifications;
|
||||
},
|
||||
setLoading(state, bool) {
|
||||
state.loading = bool;
|
||||
},
|
||||
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);
|
||||
const url = `/api/1.0/person/accompanying-period/work/my-near-end${'?'+ param}`;
|
||||
makeFetch('GET', url)
|
||||
.then((response) => {
|
||||
commit('addWorks', response);
|
||||
commit('setLoading', false);
|
||||
})
|
||||
.catch((error) => {
|
||||
commit('catchError', error);
|
||||
throw error;
|
||||
})
|
||||
;
|
||||
}
|
||||
break;
|
||||
case 'MyEvaluations':
|
||||
if (!getters.isEvaluationsLoaded) {
|
||||
commit('setLoading', true);
|
||||
const url = `/api/1.0/person/accompanying-period/work/evaluation/my-near-end${'?'+ param}`;
|
||||
makeFetch('GET', url)
|
||||
.then((response) => {
|
||||
commit('addEvaluations', response);
|
||||
commit('setLoading', false);
|
||||
})
|
||||
.catch((error) => {
|
||||
commit('catchError', error);
|
||||
throw error;
|
||||
})
|
||||
;
|
||||
}
|
||||
break;
|
||||
case 'MyTasks':
|
||||
if (!(getters.isTasksWarningLoaded && getters.isTasksAlertLoaded)) {
|
||||
commit('setLoading', true);
|
||||
const
|
||||
urlWarning = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=warning&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${'&'+ param}`,
|
||||
urlAlert = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=alert&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${'&'+ param}`
|
||||
;
|
||||
makeFetch('GET', urlWarning)
|
||||
.then((response) => {
|
||||
commit('addTasksWarning', response);
|
||||
commit('setLoading', false);
|
||||
})
|
||||
.catch((error) => {
|
||||
commit('catchError', error);
|
||||
throw error;
|
||||
})
|
||||
;
|
||||
makeFetch('GET', urlAlert)
|
||||
.then((response) => {
|
||||
commit('addTasksAlert', response);
|
||||
commit('setLoading', false);
|
||||
})
|
||||
.catch((error) => {
|
||||
commit('catchError', error);
|
||||
throw error;
|
||||
})
|
||||
;
|
||||
}
|
||||
break;
|
||||
case 'MyAccompanyingCourses':
|
||||
if (!getters.isAccompanyingCoursesLoaded) {
|
||||
commit('setLoading', true);
|
||||
const url = `/api/1.0/person/accompanying-course/list/by-recent-attributions${'?'+ param}`;
|
||||
makeFetch('GET', url)
|
||||
.then((response) => {
|
||||
commit('addCourses', response);
|
||||
commit('setLoading', false);
|
||||
})
|
||||
.catch((error) => {
|
||||
commit('catchError', error);
|
||||
throw error;
|
||||
})
|
||||
;
|
||||
}
|
||||
break;
|
||||
case 'MyNotifications':
|
||||
if (!getters.isNotificationsLoaded) {
|
||||
commit('setLoading', true);
|
||||
const url = `/api/1.0/main/notification/my/unread${'?'+ param}`;
|
||||
makeFetch('GET', url)
|
||||
.then((response) => {
|
||||
commit('addNotifications', response);
|
||||
commit('setLoading', false);
|
||||
})
|
||||
.catch((error) => {
|
||||
commit('catchError', error);
|
||||
throw error;
|
||||
})
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw 'tab '+ tab;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export { store };
|
@@ -24,6 +24,11 @@
|
||||
{{ $t('user')}}
|
||||
</span>
|
||||
|
||||
<span v-if="entity.type === 'household'" class="badge rounded-pill bg-user">
|
||||
{{ $t('household')}}
|
||||
</span>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -40,7 +45,8 @@ export default {
|
||||
company: "Personne morale",
|
||||
contact: "Personne physique",
|
||||
},
|
||||
user: 'TMS'
|
||||
user: 'TMS',
|
||||
household: 'Ménage',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="flex-table workflow" id="workflow-list">
|
||||
<div v-for="(w, i) in workflows" :key="`workflow-${i}`"
|
||||
class="item-bloc">
|
||||
|
||||
|
||||
<div>
|
||||
<div class="item-row col">
|
||||
<h2>Workflow</h2>
|
||||
@@ -13,11 +13,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="breadcrumb">
|
||||
<template v-for="(step, j) in w.steps">
|
||||
<template v-for="(step, j) in w.steps" :key="`step-${j}`">
|
||||
<span class="mx-2"
|
||||
:key="`step-${j}`"
|
||||
tabindex="0"
|
||||
data-bs-trigger="focus hover"
|
||||
data-bs-toggle="popover"
|
||||
@@ -25,7 +24,7 @@
|
||||
data-bs-custom-class="workflow-transition"
|
||||
:title="getPopTitle(step)"
|
||||
:data-bs-content="getPopContent(step)">
|
||||
|
||||
|
||||
<i v-if="step.currentStep.name === 'initial'"
|
||||
class="fa fa-circle me-1 text-chill-yellow">
|
||||
</i>
|
||||
@@ -40,7 +39,7 @@
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="item-row">
|
||||
<div class="item-col flex-grow-1">
|
||||
<p v-if="isUserSubscribedToStep(w)">
|
||||
@@ -60,7 +59,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -61,14 +61,15 @@ const messages = {
|
||||
woman: "Née le"
|
||||
},
|
||||
deathdate: "Date de décès",
|
||||
years_old: "ans",
|
||||
household_without_address: "Le ménage de l'usager est sans adresse",
|
||||
no_data: "Aucune information renseignée",
|
||||
type: {
|
||||
thirdparty: "Tiers",
|
||||
person: "Usager"
|
||||
},
|
||||
holder: "Titulaire"
|
||||
holder: "Titulaire",
|
||||
years_old: "an | {n} an | {n} ans",
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -0,0 +1,3 @@
|
||||
<div class="sticky-buttons">
|
||||
{# Override this file to add fast actions buttons #}
|
||||
</div>
|
@@ -0,0 +1,15 @@
|
||||
<div class="col-10 mt-5">
|
||||
|
||||
{# vue component #}
|
||||
<div id="homepage_widget"></div>
|
||||
|
||||
{% include '@ChillMain/Homepage/fast_actions.html.twig' %}
|
||||
</div>
|
||||
|
||||
{% block css %}
|
||||
{{ encore_entry_link_tags('page_homepage_widget') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ encore_entry_script_tags('page_homepage_widget') }}
|
||||
{% endblock %}
|
@@ -42,6 +42,3 @@
|
||||
{% endif %}
|
||||
|
||||
></span>
|
||||
|
||||
{{ encore_entry_script_tags('vue_onthefly') }}
|
||||
{{ encore_entry_link_tags('vue_onthefly') }}
|
||||
|
@@ -13,6 +13,7 @@
|
||||
{{ encore_entry_link_tags('mod_ckeditor5') }}
|
||||
{{ encore_entry_link_tags('chill') }}
|
||||
{{ encore_entry_link_tags('mod_blur') }}
|
||||
{{ encore_entry_link_tags('vue_onthefly') }}
|
||||
{% block css %}<!-- nothing added to css -->{% endblock %}
|
||||
</head>
|
||||
|
||||
@@ -59,26 +60,26 @@
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-8 main_search">
|
||||
<h2>{{ 'Search'|trans }}</h2>
|
||||
|
||||
<form action="{{ path('chill_main_search') }}" method="get">
|
||||
<input class="form-control form-control-lg" name="q" type="search" placeholder="{{ 'Search persons, ...'|trans }}" />
|
||||
<center>
|
||||
<button type="submit" class="btn btn-lg btn-warning mt-3">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
|
||||
</button>
|
||||
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
|
||||
</a>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<a href="{{ path('chill_crud_aside_activity_new', {'type' : 7, 'duration' : '600', 'note' : 'Pas des remarques' }) }}"><div class="bloc btn btn-success btn-md btn-block">Appel téléphonique</div></a>
|
||||
</div>
|
||||
|
||||
{{ chill_widget('homepage', {} ) }}
|
||||
<div class="col-8 main_search">
|
||||
<h2>{{ 'Search'|trans }}</h2>
|
||||
|
||||
<form action="{{ path('chill_main_search') }}" method="get">
|
||||
<input class="form-control form-control-lg" name="q" type="search" placeholder="{{ 'Search persons, ...'|trans }}" />
|
||||
<center>
|
||||
<button type="submit" class="btn btn-lg btn-warning mt-3">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
|
||||
</button>
|
||||
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
|
||||
</a>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{# DISABLED {{ chill_widget('homepage', {} ) }} #}
|
||||
|
||||
{% include '@ChillMain/Homepage/index.html.twig' %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
@@ -94,6 +95,7 @@
|
||||
{{ encore_entry_script_tags('mod_ckeditor5') }}
|
||||
{{ encore_entry_script_tags('mod_blur') }}
|
||||
{{ encore_entry_script_tags('chill') }}
|
||||
{{ encore_entry_script_tags('vue_onthefly') }}
|
||||
|
||||
<script type="text/javascript">
|
||||
window.addEventListener('DOMContentLoaded', function(e) {
|
||||
|
@@ -45,13 +45,15 @@ class SearchUserApiProvider implements SearchApiInterface
|
||||
$query
|
||||
->setSelectKey('user')
|
||||
->setSelectJsonbMetadata("jsonb_build_object('id', u.id)")
|
||||
->setSelectPertinence('GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical),
|
||||
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical))', [$pattern, $pattern])
|
||||
->setSelectPertinence('GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.label),
|
||||
SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical))', [$pattern, $pattern])
|
||||
->setFromClause('users AS u')
|
||||
->setWhereClauses('SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
|
||||
OR
|
||||
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical) > 0.15
|
||||
', [$pattern, $pattern]);
|
||||
->setWhereClauses('
|
||||
SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
|
||||
OR u.usernamecanonical LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
|
||||
OR SIMILARITY(LOWER(UNACCENT(?)), LOWER(UNACCENT(u.label))) > 0.15
|
||||
OR u.label LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
|
||||
', [$pattern, $pattern, $pattern, $pattern]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
@@ -22,6 +22,10 @@ class SearchApiQuery
|
||||
|
||||
private array $fromClauseParams = [];
|
||||
|
||||
private bool $isDistinct = false;
|
||||
|
||||
private ?string $isDistinctKey = null;
|
||||
|
||||
private ?string $jsonbMetadata = null;
|
||||
|
||||
private array $jsonbMetadataParams = [];
|
||||
@@ -105,6 +109,11 @@ class SearchApiQuery
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDistinct(): bool
|
||||
{
|
||||
return $this->isDistinct;
|
||||
}
|
||||
|
||||
public function getFromClause(): string
|
||||
{
|
||||
return $this->fromClause;
|
||||
@@ -139,6 +148,14 @@ class SearchApiQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDistinct(bool $distinct, string $distinctKey): self
|
||||
{
|
||||
$this->isDistinct = $distinct;
|
||||
$this->isDistinctKey = $distinctKey;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setFromClause(string $fromClause, array $params = []): self
|
||||
{
|
||||
$this->fromClause = $fromClause;
|
||||
@@ -185,7 +202,11 @@ class SearchApiQuery
|
||||
private function buildSelectClause(bool $countOnly = false): string
|
||||
{
|
||||
if ($countOnly) {
|
||||
return 'count(*) AS c';
|
||||
if (!$this->isDistinct) {
|
||||
return 'count(*) AS c';
|
||||
}
|
||||
|
||||
return 'count(distinct ' . $this->isDistinctKey . ') AS c';
|
||||
}
|
||||
|
||||
$selects = $this->getSelectClauses();
|
||||
@@ -202,7 +223,7 @@ class SearchApiQuery
|
||||
$selects[] = strtr('{pertinence} AS pertinence', ['{pertinence}' => $this->pertinence]);
|
||||
}
|
||||
|
||||
return implode(', ', $selects);
|
||||
return ($this->isDistinct ? 'DISTINCT ' : '') . implode(', ', $selects);
|
||||
}
|
||||
|
||||
private function buildSelectParams(bool $count = false): array
|
||||
|
41
src/Bundle/ChillMainBundle/Serializer/Model/Counter.php
Normal file
41
src/Bundle/ChillMainBundle/Serializer/Model/Counter.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Serializer\Model;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
class Counter implements JsonSerializable
|
||||
{
|
||||
private int $counter;
|
||||
|
||||
public function __construct(?int $counter)
|
||||
{
|
||||
$this->counter = $counter;
|
||||
}
|
||||
|
||||
public function getCounter(): ?int
|
||||
{
|
||||
return $this->counter;
|
||||
}
|
||||
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return ['count' => $this->counter];
|
||||
}
|
||||
|
||||
public function setCounter(?int $counter): Counter
|
||||
{
|
||||
$this->counter = $counter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -55,7 +55,7 @@ class CommentEmbeddableDocGenNormalizer implements ContextAwareNormalizerInterfa
|
||||
$user = $this->userRepository->find($object->getUserId());
|
||||
|
||||
return [
|
||||
'comment' => (string) $object->getComment(),
|
||||
'comment' => $object->getComment(),
|
||||
'isNull' => false,
|
||||
'date' => $this->normalizer->normalize($object->getDate(), $format, array_merge($context, [
|
||||
'docgen:expects' => DateTime::class,
|
||||
|
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||
|
||||
use ArrayObject;
|
||||
use Chill\MainBundle\Entity\Notification;
|
||||
use Chill\MainBundle\Notification\NotificationHandlerManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
class NotificationNormalizer implements NormalizerAwareInterface, NormalizerInterface
|
||||
{
|
||||
use NormalizerAwareTrait;
|
||||
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private NotificationHandlerManager $notificationHandlerManager;
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(NotificationHandlerManager $notificationHandlerManager, EntityManagerInterface $entityManager, Security $security)
|
||||
{
|
||||
$this->notificationHandlerManager = $notificationHandlerManager;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Notification $object
|
||||
*
|
||||
* @return array|ArrayObject|bool|float|int|string|void|null
|
||||
*/
|
||||
public function normalize($object, ?string $format = null, array $context = [])
|
||||
{
|
||||
dump($object);
|
||||
$entity = $this->entityManager
|
||||
->getRepository($object->getRelatedEntityClass())
|
||||
->find($object->getRelatedEntityId());
|
||||
|
||||
return [
|
||||
'type' => 'notification',
|
||||
'id' => $object->getId(),
|
||||
'addressees' => $this->normalizer->normalize($object->getAddressees(), $format, $context),
|
||||
'date' => $this->normalizer->normalize($object->getDate(), $format, $context),
|
||||
'isRead' => $object->isReadBy($this->security->getUser()),
|
||||
'message' => $object->getMessage(),
|
||||
'relatedEntityClass' => $object->getRelatedEntityClass(),
|
||||
'relatedEntityId' => $object->getRelatedEntityId(),
|
||||
'sender' => $this->normalizer->normalize($object->getSender(), $format, $context),
|
||||
'title' => $object->getTitle(),
|
||||
'entity' => null !== $entity ? $this->normalizer->normalize($entity, $format, $context) : null,
|
||||
];
|
||||
}
|
||||
|
||||
public function supportsNormalization($data, ?string $format = null)
|
||||
{
|
||||
return $data instanceof Notification && 'json' === $format;
|
||||
}
|
||||
}
|
@@ -134,6 +134,7 @@ paths:
|
||||
- search
|
||||
- person
|
||||
- thirdparty
|
||||
- household
|
||||
description: >
|
||||
The search is performed across multiple entities. The entities must be listed into
|
||||
`type` parameters.
|
||||
@@ -159,6 +160,7 @@ paths:
|
||||
- person
|
||||
- thirdparty
|
||||
- user
|
||||
- household
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
|
@@ -53,6 +53,7 @@ module.exports = function(encore, entries)
|
||||
encore.addEntry('page_login', __dirname + '/Resources/public/page/login/index.js');
|
||||
encore.addEntry('page_location', __dirname + '/Resources/public/page/location/index.js');
|
||||
encore.addEntry('page_workflow_show', __dirname + '/Resources/public/page/workflow-show/index.js');
|
||||
encore.addEntry('page_homepage_widget', __dirname + '/Resources/public/page/homepage_widget/index.js');
|
||||
|
||||
buildCKEditor(encore);
|
||||
|
||||
|
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Create a new entity ResidentialAddress.
|
||||
*/
|
||||
final class Version20220125134253 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP SEQUENCE chill_main_residential_address_id_seq CASCADE');
|
||||
$this->addSql('DROP TABLE chill_main_residential_address');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Create a new entity ResidentialAddress';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE SEQUENCE chill_main_residential_address_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE chill_main_residential_address (id INT NOT NULL, person_id INT NOT NULL, address_id INT DEFAULT NULL, startDate TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, endDate TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, residentialAddressComment_comment TEXT DEFAULT NULL, residentialAddressComment_date TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, residentialAddressComment_userId INT DEFAULT NULL, hostPerson_id INT DEFAULT NULL, hostThirdParty_id INT DEFAULT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_9BC1FD50217BBB47 ON chill_main_residential_address (person_id)');
|
||||
$this->addSql('CREATE INDEX IDX_9BC1FD50DCA38092 ON chill_main_residential_address (hostPerson_id)');
|
||||
$this->addSql('CREATE INDEX IDX_9BC1FD508DFC48DC ON chill_main_residential_address (hostThirdParty_id)');
|
||||
$this->addSql('CREATE INDEX IDX_9BC1FD50F5B7AF75 ON chill_main_residential_address (address_id)');
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_residential_address.startDate IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('COMMENT ON COLUMN chill_main_residential_address.endDate IS \'(DC2Type:datetime_immutable)\'');
|
||||
$this->addSql('ALTER TABLE chill_main_residential_address ADD CONSTRAINT FK_9BC1FD50217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_main_residential_address ADD CONSTRAINT FK_9BC1FD50DCA38092 FOREIGN KEY (hostPerson_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_main_residential_address ADD CONSTRAINT FK_9BC1FD508DFC48DC FOREIGN KEY (hostThirdParty_id) REFERENCES chill_3party.third_party (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_main_residential_address ADD CONSTRAINT FK_9BC1FD50F5B7AF75 FOREIGN KEY (address_id) REFERENCES chill_main_address (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
}
|
@@ -52,6 +52,8 @@ Since %date%: Depuis le %date%
|
||||
since %date%: depuis le %date%
|
||||
Until %date%: Jusqu'au %date%
|
||||
until %date%: jusqu'au %date%
|
||||
Since: Depuis le
|
||||
Until: Jusqu'au
|
||||
#elements used in software
|
||||
centers: centres
|
||||
Centers: Centres
|
||||
|
Reference in New Issue
Block a user