Compare commits

..

5 Commits

226 changed files with 2218 additions and 6521 deletions

View File

@@ -11,48 +11,9 @@ and this project adheres to
## Unreleased
<!-- write down unreleased development here -->
* renommer "dossier numéro" en "parcours numéro" dans les résultats de recherche
* renomme date de début en date d'ouverture dans le formulaire parcours
## Test releases
### test release 2021-01-31
[fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
[homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
* [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409)
* [person] accompanying course: close modal when edit participation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [person] accompanying course: treat validation error when editing on-the-fly entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [activity] show activity attendee (présence) in the activity list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/412)
* [activity] admin: change validation rule for social action visible field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/413)
* [parcours]: component added to change the opening date of a parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/411)
* [search]: listing of parcours display changed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/410)
* [user]: page with accompanying periods to which is user is referent (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/408)
* [person] age added to renderstring + renderbox/ vue component created to display person text (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/389)
* [household member editor] allow to push to existing household
* [workflow][notification] improve how notifications and workflows are 'attached' to entities: contextual list, counter, buttons and vue modal
### test release 2021-01-28
* [person] improve filiations vis graph: disable physics, use chill colors for persons-households-course, increase label of relations, remove labels on household arrows and other improvements (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/286, https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/362)
* [activity] Order activity by date and by id (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/364)
* [main] increase length of 4 Address fields (change to TEXT, no size limits) (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/277)
* [main] Add confidential option for address, in edit and view (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/165)
* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
* [person] Add residential address entity, form and list for each person
* [aside_activity]: dynamicUserPickerType used (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/399)
* dispatching list
### test release 2021-01-26
* [parcours] comments truncated if too long + link added (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/406)
* [person]: possibility to add person resources (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/382)
* [person ressources]: module added
### test release 2022-01-24
* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
@@ -71,6 +32,7 @@ and this project adheres to
* add My workflow section with my opened subscriptions
* apply workflow on documents, accompanyingCourseWork and Evaluations
* [wopi-link] a new vue component allow to open wopi link in a fullscreen chill-themed modal
<!-- end -->
### test release 2022-01-19
* vuejs: add dead information on all on-the-fly person render boxes, in vis graph and other templates (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/271)
@@ -88,14 +50,10 @@ and this project adheres to
* [main] Add mainLocation field to User entity and add it in user form type
* [course list in person context] show full username/label for ref
* [accompanying period work] remove the possibility to generate document from an accompanying period work
* vuejs: add validation on required fields for AddPerson, Address and Location components
* vuejs: treat 422 validation errors in locations and AddPerson components
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
## Test releases
* vuejs: add validation on required fields for AddPerson, Address and Location components
* vuejs: treat 422 validation errors in locations and AddPerson components
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
### test release 2022-01-12

View File

@@ -52,6 +52,9 @@
"twig/string-extra": "^3.3",
"twig/twig": "^3.0"
},
"conflict": {
"symfony/symfony": "*"
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3",
"drupol/php-conventions": "^5",
@@ -68,17 +71,23 @@
"symfony/var-dumper": "^4.4",
"symfony/web-profiler-bundle": "^4.4"
},
"conflict": {
"symfony/symfony": "*"
"config": {
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true,
"vendor-dir": "tests/app/vendor",
"allow-plugins": {
"composer/package-versions-deprecated": true,
"phpstan/extension-installer": true,
"ergebnis/composer-normalize": true,
"phpro/grumphp": true
}
},
"autoload": {
"psr-4": {
"Chill\\ActivityBundle\\": "src/Bundle/ChillActivityBundle",
"Chill\\AsideActivityBundle\\": "src/Bundle/ChillAsideActivityBundle/src",
"Chill\\BudgetBundle\\": "src/Bundle/ChillBudgetBundle",
"Chill\\CalendarBundle\\": "src/Bundle/ChillCalendarBundle",
"Chill\\CustomFieldsBundle\\": "src/Bundle/ChillCustomFieldsBundle",
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
"Chill\\FamilyMemberBundle\\": "src/Bundle/ChillFamilyMemberBundle",
@@ -87,6 +96,9 @@
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
"Chill\\AsideActivityBundle\\": "src/Bundle/ChillAsideActivityBundle/src",
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\CalendarBundle\\": "src/Bundle/ChillCalendarBundle",
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src"
}
},
@@ -99,10 +111,10 @@
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true,
"phpstan/extension-installer": true,
"ergebnis/composer-normalize": true,
"ocramius/package-versions": true,
"phpro/grumphp": true,
"phpstan/extension-installer": true
"ocramius/package-versions": true
},
"bin-dir": "bin",
"optimize-autoloader": true,
@@ -111,8 +123,8 @@
},
"scripts": {
"auto-scripts": {
"assets:install %PUBLIC_DIR%": "symfony-cmd",
"cache:clear": "symfony-cmd"
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
}
}
}

3
package.json.txt Normal file
View File

@@ -0,0 +1,3 @@
add npm/yarn dependency in package.json :
"select2-bootstrap-theme": "0.1.0-beta.10",

View File

@@ -80,6 +80,11 @@ parameters:
count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Foreach overwrites \\$action with its value variable\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Foreach overwrites \\$action with its value variable\\.$#"
count: 1

View File

@@ -30,6 +30,36 @@ parameters:
count: 2
path: src/Bundle/ChillPersonBundle/Household/MembersEditorFactory.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:countBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$action$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$limit$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$offset$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$orderBy$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Variable variables are not allowed\\.$#"
count: 4

View File

@@ -400,6 +400,11 @@ parameters:
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid return type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\QueryBuilder\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3

View File

@@ -256,7 +256,7 @@ final class ActivityController extends AbstractController
if ($person instanceof Person) {
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $person);
$activities = $this->activityACLAwareRepository
->findByPerson($person, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']);
->findByPerson($person, ActivityVoter::SEE, 0, null);
$event = new PrivacyEvent($person, [
'element_class' => Activity::class,
@@ -269,7 +269,7 @@ final class ActivityController extends AbstractController
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $accompanyingPeriod);
$activities = $this->activityACLAwareRepository
->findByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']);
->findByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE);
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
}

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Entity;
use Chill\ActivityBundle\Validator\Constraints as ActivityValidator;
use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
@@ -61,7 +62,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod")
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
*/
private ?AccompanyingPeriod $accompanyingPeriod = null;
@@ -75,19 +76,16 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityPresence")
* @Groups({"docgen:read"})
*/
private ?ActivityPresence $attendee = null;
/**
* @ORM\Embedded(class="Chill\MainBundle\Entity\Embeddable\CommentEmbeddable", columnPrefix="comment_")
* @Groups({"docgen:read"})
*/
private CommentEmbeddable $comment;
/**
* @ORM\Column(type="datetime")
* @Groups({"docgen:read"})
*/
private DateTime $date;
@@ -103,7 +101,6 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\Column(type="boolean", options={"default": false})
* @Groups({"docgen:read"})
*/
private bool $emergency = false;
@@ -134,19 +131,16 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToMany(targetEntity="Chill\ActivityBundle\Entity\ActivityReason")
* @Groups({"docgen:read"})
*/
private Collection $reasons;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
* @Groups({"docgen:read"})
*/
private ?Scope $scope = null;
/**
* @ORM\Column(type="string", options={"default": ""})
* @Groups({"docgen:read"})
*/
private string $sentReceived = '';
@@ -177,13 +171,12 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
* @Groups({"docgen:read"})
*/
private User $user;
/**
* @ORM\ManyToMany(targetEntity="Chill\MainBundle\Entity\User")
* @Groups({"read", "docgen:read"})
* @Groups({"read"})
*/
private ?Collection $users = null;
@@ -310,18 +303,6 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->documents;
}
/**
* @Groups({"docgen:read"})
*/
public function getDurationMinute(): int
{
if (null === $this->durationTime) {
return 0;
}
return (int) round(($this->durationTime->getTimestamp() + $this->durationTime->getOffset()) / 60.0, 0);
}
public function getDurationTime(): ?DateTime
{
return $this->durationTime;
@@ -422,18 +403,6 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->travelTime;
}
/**
* @Groups({"docgen:read"})
*/
public function getTravelTimeMinute(): int
{
if (null === $this->travelTime) {
return 0;
}
return (int) round(($this->travelTime->getTimestamp() + $this->travelTime->getOffset()) / 60.0, 0);
}
/**
* @deprecated
*/

View File

@@ -12,7 +12,6 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation as Serializer;
/**
* Class ActivityPresence.
@@ -32,14 +31,11 @@ class ActivityPresence
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @Serializer\Groups({"docgen:read"})
*/
private ?int $id;
/**
* @ORM\Column(type="json")
* @Serializer\Groups({"docgen:read"})
* @Serializer\Context({"is-translatable": true}, groups={"docgen:read"})
*/
private array $name = [];

View File

@@ -13,10 +13,8 @@ namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use InvalidArgumentException;
use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Class ActivityType.
@@ -120,7 +118,6 @@ class ActivityType
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @Groups({"docgen:read"})
*/
private ?int $id;
@@ -136,8 +133,7 @@ class ActivityType
/**
* @ORM\Column(type="json")
* @Groups({"read", "docgen:read"})
* @Serializer\Context({"is-translatable": true}, groups={"docgen:read"})
* @Groups({"read"})
*/
private array $name = [];
@@ -194,6 +190,7 @@ class ActivityType
/**
* @ORM\Column(type="smallint", nullable=false, options={"default": 1})
* @Assert\EqualTo(propertyPath="socialIssuesVisible", message="This parameter must be equal to social issue parameter")
*/
private int $socialActionsVisible = self::FIELD_INVISIBLE;
@@ -263,23 +260,6 @@ class ActivityType
*/
private int $userVisible = self::FIELD_REQUIRED;
/**
* @Assert\Callback
*
* @param mixed $payload
*/
public function checkSocialActionsVisibility(ExecutionContextInterface $context, $payload)
{
if ($this->socialIssuesVisible !== $this->socialActionsVisible) {
if (!($this->socialIssuesVisible === 2 && $this->socialActionsVisible === 1)) {
$context
->buildViolation('The socialActionsVisible value is not compatible with the socialIssuesVisible value')
->atPath('socialActionsVisible')
->addViolation();
}
}
}
/**
* Get active
* return true if the type is active.

View File

@@ -34,8 +34,6 @@ p.date-label {
font-size: 18pt;
}
div.dashboard,
h4.badge-title,
h3.badge-title,
h2.badge-title {
ul.list-content {
font-size: 70%;

View File

@@ -1,48 +1,45 @@
<template>
<teleport to="#add-persons" v-if="isComponentVisible">
<teleport to="#add-persons" v-if="isComponentVisible">
<div class="flex-bloc concerned-groups" :class="getContext">
<persons-bloc
v-for="bloc in contextPersonsBlocs"
v-bind:key="bloc.key"
v-bind:bloc="bloc"
v-bind:blocWidth="getBlocWidth"
v-bind:setPersonsInBloc="setPersonsInBloc">
</persons-bloc>
</div>
<div v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0">
<ul class="list-suggest add-items inline">
<li v-for="(p, i) in suggestedEntities" @click="addSuggestedEntity(p)" :key="`suggestedEntities-${i}`">
<person-text v-if="p.type === 'person'" :person="p"></person-text>
<span v-else>{{ p.text }}</span>
</li>
</ul>
</div>
<div class="flex-bloc concerned-groups" :class="getContext">
<persons-bloc
v-for="bloc in contextPersonsBlocs"
v-bind:key="bloc.key"
v-bind:bloc="bloc"
v-bind:blocWidth="getBlocWidth"
v-bind:setPersonsInBloc="setPersonsInBloc">
</persons-bloc>
</div>
<div v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0">
<ul class="list-suggest add-items inline">
<li v-for="(p, i) in suggestedEntities" @click="addSuggestedEntity(p)" :key="`suggestedEntities-${i}`">
<span>{{ p.text }}</span>
</li>
</ul>
</div>
<add-persons
buttonTitle="activity.add_persons"
modalTitle="activity.add_persons"
v-bind:key="addPersons.key"
v-bind:options="addPersonsOptions"
@addNewPersons="addNewPersons"
ref="addPersons">
</add-persons>
<add-persons
buttonTitle="activity.add_persons"
modalTitle="activity.add_persons"
v-bind:key="addPersons.key"
v-bind:options="addPersonsOptions"
@addNewPersons="addNewPersons"
ref="addPersons">
</add-persons>
</teleport>
</teleport>
</template>
<script>
import { mapState, mapGetters } from 'vuex';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import PersonsBloc from './ConcernedGroups/PersonsBloc.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default {
name: "ConcernedGroups",
components: {
AddPersons,
PersonsBloc,
PersonText
PersonsBloc
},
data() {
return {

View File

@@ -1,29 +1,21 @@
<template>
<li>
<span :title="person.text">
<span class="chill_denomination" @click.prevent="$emit('remove', person)">
<person-text :person="person" :isCut="true"></person-text>
</span>
<span class="chill_denomination" @click.prevent="$emit('remove', person)">{{ textCutted }}</span>
</span>
</li>
</template>
<script>
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default {
name: "PersonBadge",
props: ['person'],
components: {
PersonText
computed: {
textCutted() {
let more = (this.person.text.length > 15) ?'…' : '';
return this.person.text.slice(0,15) + more;
}
},
// computed: {
// textCutted() {
// let more = (this.person.text.length > 15) ?'…' : '';
// return this.person.text.slice(0,15) + more;
// }
// },
emits: ['remove'],
}
</script>

View File

@@ -41,17 +41,6 @@
</div>
{% endif %}
{% if activity.attendee and t.attendeeVisible %}
<div class="wl-row">
<div class="wl-col title"><h3>{{ 'Attendee'|trans }}</h3></div>
<div class="wl-col list">
<p class="wl-item">
{{ activity.attendee.name|localize_translatable_string }}
</p>
</div>
</div>
{% endif %}
{% if activity.sentReceived is not empty and t.sentReceivedVisible %}
<div class="wl-row">
<div class="wl-col title"><h3>{{ 'Sent received'|trans }}</h3></div>

View File

@@ -2,12 +2,10 @@
{% if is_granted('CHILL_ACTIVITY_SEE_DETAILS', activity) %}
{% if no_action is not defined or no_action == false %}
<li>
<a class="btn btn-misc" href="{{ chill_path_add_return_path('chill_main_notification_create', {
<a class="btn btn-notify" href="{{ chill_path_add_return_path('chill_main_notification_create', {
'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity',
'entityId': activity.id
}) }}">
<i class="fa fa-paper-plane fa-fw"></i>
{{ 'notification.Notify'|trans }}</a>
}) }}">{{ 'notification.Notify'|trans }}</a>
</li>
{% endif %}
{% if context == 'person' and activity.accompanyingPeriod is not empty %}

View File

@@ -48,7 +48,7 @@
<li class="associated-persons">
<span class="item-key">{{ 'Participants'|trans ~ ' : ' }}</span>
{% for p in activity.personsAssociated %}
<span class="badge-person">{{ p|chill_entity_render_box({'addAgeBadge': true}) }}</span>
<span class="badge-person">{{ p|chill_entity_render_box }}</span>
{% endfor %}
</li>
</ul>

View File

@@ -165,7 +165,11 @@
<dt class="inline">{{ 'Attendee'|trans }}</dt>
<dd>
{% if entity.attendee is not null %}
{{ entity.attendee.name|localize_translatable_string }}
{% if entity.attendee %}
{{ 'present'|trans|capitalize }}
{% else %}
{{ 'not present'|trans|capitalize }}
{% endif %}
{% else %}
<span class="chill-no-data-statement">{{ 'None'|trans|capitalize }}</span>
{% endif %}
@@ -177,13 +181,6 @@
</div>
</div>
<div class="notification notification-list">
{% set notifications = chill_list_notifications('Chill\\ActivityBundle\\Entity\\Activity', entity.id) %}
{% if notifications is not empty %}
{{ notifications|raw }}
{% endif %}
</div>
{% set person_id = null %}
{% if person %}
{% set person_id = person.id %}
@@ -200,21 +197,18 @@
{{ 'Back to the list'|trans }}
</a>
</li>
<li>
<a class="btn btn-notify" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity', 'entityId': entity.id}) }}">
{{ 'notification.Notify'|trans }}
</a>
</li>
{% if is_granted('CHILL_ACTIVITY_UPDATE', entity) %}
<li>
<a href="{{ path('chill_activity_activity_edit', { 'id': entity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id }) }}"
class="btn btn-update">{{ 'Edit'|trans }}</a>
</li>
<a class="btn btn-update" href="{{ path('chill_activity_activity_edit', { 'id': entity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id }) }}">
{{ 'Edit'|trans }}
</a>
</li>
{% endif %}
{% if is_granted('CHILL_ACTIVITY_DELETE', entity) %}
<li>
<a href="{{ path('chill_activity_activity_delete', { 'id': entity.id, 'person_id' : person_id, 'accompanying_period_id': accompanying_course_id } ) }}"
class="btn btn-delete" title="{{ 'Delete'|trans }}"></a>
<a href="{{ path('chill_activity_activity_delete', { 'id': entity.id, 'person_id' : person_id, 'accompanying_period_id': accompanying_course_id } ) }}" class="btn btn-delete">
{{ 'Delete'|trans }}
</a>
</li>
{% endif %}
</ul>

View File

@@ -25,5 +25,19 @@
{% endblock content %}
{% block block_post_menu %}
<div class="post-menu pt-4"></div>
<div class="post-menu pt-4">
<div class="d-grid gap-2">
<a class="btn btn-primary" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity', 'entityId': entity.id}) }}">
<i class="fa fa-paper-plane fa-fw"></i>
{{ 'notification.Notify'|trans }}
</a>
</div>
{% set notifications = chill_list_notifications('Chill\\ActivityBundle\\Entity\\Activity', entity.id) %}
{% if notifications is not empty %}
{{ notifications|raw }}
{% endif %}
</div>
{% endblock %}

View File

@@ -28,7 +28,8 @@
<div class="post-menu pt-4">
<div class="d-grid gap-2">
<a class="btn btn-notify" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity', 'entityId': entity.id}) }}">
<a class="btn btn-primary" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity', 'entityId': entity.id}) }}">
<i class="fa fa-paper-plane fa-fw"></i>
{{ 'notification.Notify'|trans }}
</a>
</div>

View File

@@ -24,6 +24,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Templating\Entity\PersonRender;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
@@ -32,6 +33,8 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
class ActivityContext implements
DocGeneratorContextWithAdminFormInterface,
DocGeneratorContextWithPublicFormInterface

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
namespace Chill\ActivityBundle\Tests\Aggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
namespace Chill\ActivityBundle\Tests\Aggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
namespace Chill\ActivityBundle\Tests\Aggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Filter;
namespace Chill\ActivityBundle\Tests\Filter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\Common\Collections\ArrayCollection;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Export\Filter;
namespace Chill\ActivityBundle\Tests\Filter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use DateTime;

View File

@@ -29,13 +29,9 @@ use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
final class ActivityVoterTest extends KernelTestCase
{
use PrepareActivityTrait;
use PrepareCenterTrait;
use PreparePersonTrait;
use PrepareScopeTrait;
use PrepareUserTrait;
/**

View File

@@ -20,4 +20,3 @@ For this type of activity, you must add at least one social action: Pour ce type
# admin
This parameter must be equal to social issue parameter: Ce paramètre doit être égal au paramètre "Visibilité du champs Problématiques sociales"
The socialActionsVisible value is not compatible with the socialIssuesVisible value: Cette valeur du paramètre "Visibilité du champs Actions sociales" n'est pas compatible avec la valeur du paramètre "Visibilité du champs Problématiques sociales"

View File

@@ -32,8 +32,6 @@ final class AsideActivityController extends CRUDController
{
$asideActivity = new AsideActivity();
$asideActivity->setAgent($this->getUser());
$duration = $request->query->get('duration', '300');
$duration = DateTime::createFromFormat('U', $duration);
$asideActivity->setDuration($duration);

View File

@@ -14,9 +14,9 @@ namespace Chill\AsideActivityBundle\Form;
use Chill\AsideActivityBundle\Entity\AsideActivity;
use Chill\AsideActivityBundle\Entity\AsideActivityCategory;
use Chill\AsideActivityBundle\Templating\Entity\CategoryRender;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use DateInterval;
use DateTime;
use DateTimeImmutable;
@@ -68,10 +68,22 @@ final class AsideActivityFormType extends AbstractType
];
$builder
->add('agent', PickUserDynamicType::class, [
'label' => 'For agent',
'required' => true,
])
->add(
'agent',
EntityType::class,
[
'label' => 'For agent',
'required' => true,
'class' => User::class,
'data' => $this->storage->getToken()->getUser(),
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('u')->where('u.enabled = true');
},
'attr' => ['class' => 'select2 '],
'placeholder' => 'Choose the agent for whom this activity is created',
'choice_label' => 'username',
]
)
->add(
'date',
ChillDateType::class,

View File

@@ -1,15 +1,5 @@
{% extends '@ChillMain/Admin/layout.html.twig' %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_pickentity_type') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %}
{% block title %}
{% include('@ChillMain/CRUD/_new_title.html.twig') %}
{% endblock %}
@@ -18,5 +8,4 @@
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
{% block content_form_actions_save_and_show %}{% endblock %}
{% endembed %}
{% endblock %}
{% endblock %}

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests\CustomFields;
namespace Chill\CustomFieldsBundle\Tests;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice;
use Chill\CustomFieldsBundle\Entity\CustomField;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests\CustomFields;
namespace Chill\CustomFieldsBundle\Tests;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldNumber;
use Chill\CustomFieldsBundle\Entity\CustomField;

View File

@@ -9,11 +9,10 @@
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests\CustomFields;
namespace Chill\CustomFieldsBundle\Tests;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldText;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Chill\CustomFieldsBundle\Tests\CustomFieldTestHelper;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests\Routing;
namespace Chill\CustomFieldsBundle\Tests;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

View File

@@ -17,7 +17,7 @@
<select class="form-select" v-model="template">
<option disabled selected value="">{{ $t('choose_a_template') }}</option>
<template v-for="t in templates">
<option v-bind:value="t.id">{{ t.name.fr || 'Aucun nom défini' }}</option>
<option v-bind:value="t.id">{{ t.name.fr }}</option>
</template>
</select>
<button v-if="canGenerate" class="btn btn-update btn-sm change-icon" type="button" @click="generateDocument"><i class="fa fa-fw fa-cog"></i></button>
@@ -48,7 +48,6 @@ export default {
required: true,
},
templates: {
type: Array,
required: true,
},
// beforeMove execute "something" before
@@ -74,11 +73,7 @@ export default {
return true;
},
getDescription() {
let desc = this.templates.find(t => t.id === this.template);
if (null === desc) {
return '';
}
return desc.description || '';
return this.templates.find(t => t.id === this.template).description || '';
},
},
methods: {

View File

@@ -21,28 +21,24 @@
</div>
</div>
{% set freezed = false %}
{% for step in entity_workflow.stepsChained %}
{% if loop.last %}
{% if step.previous is not null and step.previous.freezeAfter == true %}
{% set freezed = true %}
{% endif %}
{% endif %}
{% endfor %}
{% if display_action is defined and display_action == true %}
<ul class="record_actions">
<li>
{{ m.download_button(document.object, document.title) }}
</li>
<li>
{% if not freezed %}
{% set button = {
'changeIcon': 'fa-unlock',
} %}{#
{#
data-button is optional !
OPTIONS:
'changeIcon' string
'changeClass' string
'noText' boolean
#}
#}{% set button = {
'changeIcon': 'fa-unlock',
} %}
{# vue component #}
<span
data-module="wopi-link"
@@ -51,11 +47,6 @@
data-doc-type="{{ document.object.type|e('html_attr') }}"
data-button="{{ button|json_encode }}"
></span>
{% else %}
<a class="btn btn-update change-icon disabled" href="#" title="{{ 'workflow.freezed document'|trans }}">
<i class="fa fa-lock me-2"></i>{{ 'Update document'|trans }}
</a>
{% endif %}
</li>
</ul>
{% endif %}

View File

@@ -56,18 +56,17 @@
</a>
</li>
{% endif %}
{% set workflows_frame = chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) %}
{% if workflows_frame is not empty %}
<li>
{{ workflows_frame|raw }}
</li>
{% endif %}
</ul>
</div>
{% endblock %}
{% block block_post_menu %}
<div class="post-menu pt-4"></div>
<div class="post-menu pt-4">
{% set workflows_frame = chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) %}
{% if workflows_frame is not empty %}
{{ workflows_frame|raw }}
{% endif %}
</div>
{% endblock %}
{% block js %}

View File

@@ -13,19 +13,13 @@ 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;
/**
@@ -35,26 +29,12 @@ class NotificationApiController
{
private EntityManagerInterface $entityManager;
private NotificationRepository $notificationRepository;
private PaginatorFactory $paginatorFactory;
private Security $security;
private SerializerInterface $serializer;
public function __construct(
EntityManagerInterface $entityManager,
NotificationRepository $notificationRepository,
PaginatorFactory $paginatorFactory,
Security $security,
SerializerInterface $serializer
) {
public function __construct(EntityManagerInterface $entityManager, Security $security)
{
$this->entityManager = $entityManager;
$this->notificationRepository = $notificationRepository;
$this->paginatorFactory = $paginatorFactory;
$this->security = $security;
$this->serializer = $serializer;
}
/**
@@ -73,37 +53,6 @@ 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)) {

View File

@@ -41,11 +41,6 @@ class Configuration implements ConfigurationInterface
$rootNode
->children()
->scalarNode('phonenumber_default_country_code')
->cannotBeEmpty()
->isRequired()
->defaultValue('+32')
->end() // end of scalar 'phonenumber_default_country_code'
->scalarNode('installation_name')
->cannotBeEmpty()
->defaultValue('Chill')

View File

@@ -42,16 +42,10 @@ class Address
*/
private $buildingName;
/**
* @ORM\Column(type="boolean")
* @Groups({"write"})
*/
private bool $confidential = false;
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
* @ORM\Column(type="string", length=16, nullable=true)
* @Groups({"write"})
*/
private $corridor;
@@ -84,7 +78,7 @@ class Address
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
* @ORM\Column(type="string", length=16, nullable=true)
* @Groups({"write"})
*/
private $flat;
@@ -92,7 +86,7 @@ class Address
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
* @ORM\Column(type="string", length=16, nullable=true)
* @Groups({"write"})
*/
private $floor;
@@ -149,7 +143,7 @@ class Address
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
* @ORM\Column(type="string", length=16, nullable=true)
* @Groups({"write"})
*/
private $steps;
@@ -198,7 +192,6 @@ class Address
return (new Address())
->setAddressReference($original->getAddressReference())
->setBuildingName($original->getBuildingName())
->setConfidential($original->getConfidential())
->setCorridor($original->getCorridor())
->setCustoms($original->getCustoms())
->setDistribution($original->getDistribution())
@@ -236,11 +229,6 @@ class Address
return $this->buildingName;
}
public function getConfidential(): bool
{
return $this->confidential;
}
public function getCorridor(): ?string
{
return $this->corridor;
@@ -381,13 +369,6 @@ class Address
return $this;
}
public function setConfidential(bool $confidential): self
{
$this->confidential = $confidential;
return $this;
}
public function setCorridor(?string $corridor): self
{
$this->corridor = $corridor;

View File

@@ -20,9 +20,10 @@ use Doctrine\ORM\Mapping as ORM;
class CommentEmbeddable
{
/**
* @var string
* @ORM\Column(type="text", nullable=true)
*/
private ?string $comment = null;
private $comment;
/**
* @var DateTime
@@ -38,7 +39,10 @@ class CommentEmbeddable
*/
private $userId;
public function getComment(): ?string
/**
* @return string
*/
public function getComment()
{
return $this->comment;
}
@@ -64,6 +68,9 @@ class CommentEmbeddable
return empty($this->getComment());
}
/**
* @param string $comment
*/
public function setComment(?string $comment)
{
$this->comment = $comment;

View File

@@ -64,7 +64,7 @@ class Location implements TrackCreationInterface, TrackUpdateInterface
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Serializer\Groups({"read", "write", "docgen:read"})
* @Serializer\Groups({"read", "write"})
*/
private ?string $email = null;

View File

@@ -1,166 +0,0 @@
<?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;
}
}

View File

@@ -25,7 +25,6 @@ use Iterator;
use RuntimeException;
use Symfony\Component\Serializer\Annotation as Serializer;
use function count;
use function is_array;
/**
* @ORM\Entity
@@ -38,7 +37,6 @@ use function is_array;
class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
use TrackUpdateTrait;
/**
@@ -52,6 +50,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"read"})
*/
private ?int $id = null;
@@ -73,11 +72,6 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
*/
private Collection $steps;
/**
* @var null|array|EntityWorkflowStep[]
*/
private ?array $stepsChainedCache = null;
/**
* @ORM\ManyToMany(targetEntity=User::class)
* @ORM\JoinTable(name="chill_main_workflow_entity_subscriber_to_final")
@@ -135,6 +129,10 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
if (!$this->steps->contains($step)) {
$this->steps[] = $step;
$step->setEntityWorkflow($this);
if ($this->isFinalize()) {
$step->setFinalizeAfter(true);
}
}
return $this;
@@ -255,33 +253,27 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
public function getStepsChained(): array
{
if (is_array($this->stepsChainedCache)) {
return $this->stepsChainedCache;
}
$iterator = $this->steps->getIterator();
$current = null;
$previous = $next = $current = null;
$steps = [];
$iterator->rewind();
do {
while ($iterator->valid()) {
$previous = $current;
$current = $iterator->current();
$steps[] = $current;
$steps[] = $current = $iterator->current();
$current->setPrevious($previous);
$iterator->next();
if ($iterator->valid()) {
$current->setNext($iterator->current());
$next = $iterator->current();
} else {
$current->setNext(null);
$next = null;
}
} while ($iterator->valid());
$this->stepsChainedCache = $steps;
$current->setNext($next);
}
return $steps;
}
@@ -316,7 +308,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
return $this->workflowName;
}
public function isFinal(): bool
public function isFinalize(): bool
{
$steps = $this->getStepsChained();
@@ -328,7 +320,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
/** @var EntityWorkflowStep $last */
$last = end($steps);
return $last->isFinal();
return $last->getPrevious()->isFinalizeAfter();
}
public function isFreeze(): bool

View File

@@ -24,7 +24,6 @@ use Doctrine\ORM\Mapping as ORM;
class EntityWorkflowComment implements TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
use TrackUpdateTrait;
/**

View File

@@ -53,6 +53,11 @@ class EntityWorkflowStep
*/
private ?EntityWorkflow $entityWorkflow = null;
/**
* @ORM\Column(type="boolean", options={"default": false})
*/
private bool $finalizeAfter = false;
/**
* @ORM\Column(type="boolean", options={"default": false})
*/
@@ -65,11 +70,6 @@ class EntityWorkflowStep
*/
private ?int $id = null;
/**
* @ORM\Column(type="boolean", options={"default": false})
*/
private bool $isFinal = false;
/**
* filled by @see{EntityWorkflow::getStepsChained}.
*/
@@ -187,9 +187,9 @@ class EntityWorkflowStep
return $this->transitionByEmail;
}
public function isFinal(): bool
public function isFinalizeAfter(): bool
{
return $this->isFinal;
return $this->finalizeAfter;
}
public function isFreezeAfter(): bool
@@ -244,16 +244,16 @@ class EntityWorkflowStep
return $this;
}
public function setFreezeAfter(bool $freezeAfter): EntityWorkflowStep
public function setFinalizeAfter(bool $finalizeAfter): EntityWorkflowStep
{
$this->freezeAfter = $freezeAfter;
$this->finalizeAfter = $finalizeAfter;
return $this;
}
public function setIsFinal(bool $isFinal): EntityWorkflowStep
public function setFreezeAfter(bool $freezeAfter): EntityWorkflowStep
{
$this->isFinal = $isFinal;
$this->freezeAfter = $freezeAfter;
return $this;
}

View File

@@ -12,15 +12,11 @@ declare(strict_types=1);
namespace Chill\MainBundle\Form\Type\DataTransformer;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\Person;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface;
use UnexpectedValueException;
use function array_key_exists;
class EntityToJsonTransformer implements DataTransformerInterface
@@ -56,10 +52,6 @@ class EntityToJsonTransformer implements DataTransformerInterface
);
}
if ('' === $value) {
return null;
}
return $this->denormalizeOne($denormalized);
}
@@ -77,7 +69,7 @@ class EntityToJsonTransformer implements DataTransformerInterface
]);
}
private function denormalizeOne(array $item)
private function denormalizeOne(array $item): User
{
if (!array_key_exists('type', $item)) {
throw new TransformationFailedException('the key "type" is missing on element');
@@ -87,30 +79,10 @@ class EntityToJsonTransformer implements DataTransformerInterface
throw new TransformationFailedException('the key "id" is missing on element');
}
switch ($this->type) {
case 'user':
$class = User::class;
break;
case 'person':
$class = Person::class;
break;
case 'thirdparty':
$class = ThirdParty::class;
break;
default:
throw new UnexpectedValueException('This type is not supported');
}
return
$this->denormalizer->denormalize(
['type' => $item['type'], 'id' => $item['id']],
$class,
$this->type,
'json',
[AbstractNormalizer::GROUPS => ['read']],
);

View File

@@ -1,73 +0,0 @@
<?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,
]);
}
}

View File

@@ -15,7 +15,6 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use LogicException;
use Symfony\Component\Form\AbstractType;
@@ -25,7 +24,6 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\Transition;
use function array_key_exists;
class WorkflowStepType extends AbstractType
{
@@ -33,13 +31,10 @@ class WorkflowStepType extends AbstractType
private Registry $registry;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(EntityWorkflowManager $entityWorkflowManager, Registry $registry, TranslatableStringHelperInterface $translatableStringHelper)
public function __construct(EntityWorkflowManager $entityWorkflowManager, Registry $registry)
{
$this->entityWorkflowManager = $entityWorkflowManager;
$this->registry = $registry;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -47,7 +42,6 @@ class WorkflowStepType extends AbstractType
/** @var \Chill\MainBundle\Entity\Workflow\EntityWorkflow $entityWorkflow */
$entityWorkflow = $options['entity_workflow'];
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
if (true === $options['transition']) {
if (null === $options['entity_workflow']) {
@@ -59,49 +53,20 @@ class WorkflowStepType extends AbstractType
->getEnabledTransitions($entityWorkflow);
$choices = array_combine(
array_map(
static function (Transition $transition) {
return $transition->getName();
},
$transitions
),
array_map(static function (Transition $transition) { return $transition->getName(); }, $transitions),
$transitions
);
$builder
->add('transition', ChoiceType::class, [
'label' => 'workflow.Transition to apply',
'label' => 'workflow.Transition',
'mapped' => false,
'multiple' => false,
'expanded' => true,
'choices' => $choices,
'choice_label' => function (Transition $transition) use ($workflow) {
$meta = $workflow->getMetadataStore()->getTransitionMetadata($transition);
if (array_key_exists('label', $meta)) {
return $this->translatableStringHelper->localize($meta['label']);
}
return $transition->getName();
},
'choice_attr' => static function (Transition $transition) use ($workflow) {
$toFinal = true;
foreach ($transition->getTos() as $to) {
$meta = $workflow->getMetadataStore()->getPlaceMetadata($to);
if (
!array_key_exists('isFinal', $meta) || false === $meta['isFinal']
) {
$toFinal = false;
}
}
return [
'data-is-transition' => 'data-is-transition',
'data-to-final' => $toFinal ? '1' : '0',
];
},
'choice_label' => static function (Transition $transition) {
return implode(', ', $transition->getTos());
},
])
->add('future_dest_users', PickUserDynamicType::class, [
'label' => 'workflow.dest for next steps',
@@ -123,6 +88,11 @@ class WorkflowStepType extends AbstractType
}
$builder
->add('finalizeAfter', CheckboxType::class, [
'required' => false,
'label' => 'workflow.Finalize',
'help' => 'workflow.The workflow will be finalized',
])
->add('comment', ChillTextareaType::class, [
'required' => false,
'label' => 'Comment',

View File

@@ -11,27 +11,17 @@ declare(strict_types=1);
namespace Chill\MainBundle\Notification\Templating;
use Chill\MainBundle\Entity\NotificationComment;
use Chill\MainBundle\Form\NotificationCommentType;
use Chill\MainBundle\Notification\NotificationPresence;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Twig\Environment;
use Twig\Extension\RuntimeExtensionInterface;
class NotificationTwigExtensionRuntime implements RuntimeExtensionInterface
{
private FormFactoryInterface $formFactory;
private NotificationPresence $notificationPresence;
private UrlGeneratorInterface $urlGenerator;
public function __construct(FormFactoryInterface $formFactory, NotificationPresence $notificationPresence, UrlGeneratorInterface $urlGenerator)
public function __construct(NotificationPresence $notificationPresence)
{
$this->formFactory = $formFactory;
$this->notificationPresence = $notificationPresence;
$this->urlGenerator = $urlGenerator;
}
public function counterNotificationFor(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = []): string
@@ -57,24 +47,8 @@ class NotificationTwigExtensionRuntime implements RuntimeExtensionInterface
return '';
}
$appendCommentForms = [];
foreach ($notifications as $notification) {
$appendComment = new NotificationComment();
$appendCommentForms[$notification->getId()] = $this->formFactory->create(
NotificationCommentType::class,
$appendComment,
[
'action' => $this->urlGenerator->generate(
'chill_main_notification_show',
['id' => $notification->getId()]
),
]
)->createView();
}
return $environment->render('@ChillMain/Notification/extension_list_notifications_for.html.twig', [
'notifications' => $notifications, 'appendCommentForms' => $appendCommentForms,
'notifications' => $notifications,
]);
}
}

View File

@@ -193,29 +193,6 @@ 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;

View File

@@ -1,59 +0,0 @@
<?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()
;
}
*/
}

View File

@@ -474,7 +474,6 @@ div.workflow {
// Override bootstrap popover styles
div.popover {
box-shadow: 0 0 10px -5px $dark;
z-index: 9999;
.popover-arrow {}
.popover-header {}
.popover-body {}

View File

@@ -12,7 +12,6 @@
display: block;
top: calc(50% - 7px);
right: 10px;
line-height: 11px;
}
}
@@ -63,19 +62,14 @@ 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: 1.75rem;
padding-right: .75rem;
@include remove_link;
}
}

View File

@@ -21,8 +21,7 @@ $chill-theme-buttons: (
"download": $gray-300,
"cancel": $gray-300,
"choose": $gray-300,
"notify": $chill-blue,
"search": $gray-300,
"notify": $gray-300,
"unlink": $chill-red,
"tpchild": $chill-pink,
);
@@ -81,7 +80,6 @@ $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;
@@ -110,7 +108,6 @@ $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
}
@@ -136,18 +133,3 @@ $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;
}
}

View File

@@ -39,7 +39,6 @@ var ShowHide = function(options) {
contents.push(el);
}
container_content.push(contents);
// console.log('container content', container_content);
}
// attach the listener on each input

View File

@@ -1,19 +1,21 @@
require('./blur.scss');
document.querySelectorAll('.confidential').forEach(function (el) {
let i = document.createElement('i');
const classes = ['fa', 'fa-eye', 'toggle'];
i.classList.add(...classes);
el.appendChild(i);
const toggleBlur = function(e) {
for (let child of el.children) {
if (!child.classList.contains('toggle')) {
child.classList.toggle('blur');
}
}
i.classList.toggle('fa-eye');
i.classList.toggle('fa-eye-slash');
}
i.addEventListener('click', toggleBlur);
toggleBlur();
});
var toggleBlur = function(e){
var btn = e.target;
btn.previousElementSibling.classList.toggle("blur");
btn.classList.toggle("fa-eye");
btn.classList.toggle("fa-eye-slash");
}
var infos = document.getElementsByClassName("confidential");
for(var i=0; i < infos.length; i++){
infos[i].insertAdjacentHTML('beforeend', '<i class="fa fa-eye toggle" aria-hidden="true"></i>');
}
var toggles = document.getElementsByClassName("toggle");
for(var i=0; i < toggles.length; i++){
toggles[i].addEventListener("click", toggleBlur);
}

View File

@@ -1,34 +1,49 @@
import { createApp } from "vue";
import ListWorkflowModalVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue';
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
import PickWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
import ListWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue';
const i18n = _createI18n({});
// list workflow
document.querySelectorAll('[data-list-workflows]')
.forEach(function (el) {
// pick workflow
document.querySelectorAll('[data-pick-workflow]')
.forEach(function(el) {
const app = {
components: {
ListWorkflowModalVue,
PickWorkflowVue
},
template:
'<list-workflow-modal-vue ' +
':workflows="workflows" ' +
':allowCreate="allowCreate" ' +
'<pick-workflow-vue ' +
':relatedEntityClass="relatedEntityClass" ' +
':relatedEntityId="relatedEntityId" ' +
':workflowsAvailables="workflowsAvailables" ' +
'></list-workflow-modal-vue>',
'></pick-workflow-vue>',
data() {
return {
workflows: JSON.parse(el.dataset.workflows),
allowCreate: el.dataset.allowCreate === "1",
relatedEntityClass: el.dataset.relatedEntityClass,
relatedEntityId: Number.parseInt(el.dataset.relatedEntityId),
workflowsAvailables: JSON.parse(el.dataset.workflowsAvailables),
}
}
};
createApp(app).use(i18n).mount(el);
createApp(app).mount(el);
})
;
// list workflow
document.querySelectorAll('[data-list-workflows]')
.forEach(function (el) {
const app = {
components: {
ListWorkflowVue,
},
template:
'<list-workflow-vue ' +
':workflows="workflows" ' +
'></list-workflow-vue>',
data() {
return {
workflows: JSON.parse(el.dataset.workflows),
}
}
};
createApp(app).mount(el);
})
;

View File

@@ -5,27 +5,18 @@ import { appMessages } from 'ChillMainAssets/vuejs/PickEntity/i18n';
const i18n = _createI18n(appMessages);
let appsOnPage = new Map();
window.addEventListener('DOMContentLoaded', function(e) {
function loadDynamicPicker(element) {
let apps = element.querySelectorAll('[data-module="pick-dynamic"]');
let apps = document.querySelectorAll('[data-module="pick-dynamic"]');
apps.forEach(function(el) {
const
isMultiple = parseInt(el.dataset.multiple) === 1,
uniqId = el.dataset.uniqid,
input = element.querySelector('[data-input-uniqid="'+ el.dataset.uniqid +'"]'),
picked = (isMultiple) ? (JSON.parse(input.value)) : ((input.value === '[]') ? (null) : ([JSON.parse(input.value)]));
input = document.querySelector('[data-input-uniqid="'+ el.dataset.uniqid +'"]'),
picked = isMultiple ? JSON.parse(input.value) : [JSON.parse(input.value)];
if (!isMultiple) {
if (input.value === '[]'){
input.value = null;
}
}
const app = createApp({
createApp({
template: '<pick-entity ' +
':multiple="multiple" ' +
':types="types" ' +
@@ -74,36 +65,5 @@ function loadDynamicPicker(element) {
})
.use(i18n)
.mount(el);
appsOnPage.set(uniqId, app);
});
}
document.addEventListener('show-hide-show', function(e) {
console.log('creation event caught')
loadDynamicPicker(e.detail.container)
})
document.addEventListener('show-hide-hide', function(e) {
console.log('hiding event caught')
e.detail.container.querySelectorAll('[data-module="pick-dynamic"]').forEach((el) => {
let uniqId = el.dataset.uniqid;
console.log(uniqId);
if (appsOnPage.has(uniqId)) {
appsOnPage.get(uniqId).unmount();
console.log('App has been unmounted')
appsOnPage.delete(uniqId);
}
})
})
document.addEventListener('DOMContentLoaded', function(e) {
console.log('loaded event', e)
loadDynamicPicker(document)
})
});

View File

@@ -1,16 +0,0 @@
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')
;

View File

@@ -562,7 +562,6 @@ export default {
this.entity.loaded.cities = [];
this.entity.loaded.countries = [];
this.entity.selected.confidential = this.context.edit ? this.entity.address.confidential : false;
this.entity.selected.isNoAddress = (this.context.edit && this.entity.address.text === '') ? true : false;
this.entity.selected.country = this.context.edit ? this.entity.address.country : {};
@@ -594,7 +593,6 @@ export default {
{
console.log('apply changes');
let newAddress = {
'confidential': this.entity.selected.confidential,
'isNoAddress': this.entity.selected.isNoAddress,
'street': this.entity.selected.isNoAddress ? '' : this.entity.selected.address.street,
'streetNumber': this.entity.selected.isNoAddress ? '' : this.entity.selected.address.streetNumber,

View File

@@ -6,6 +6,7 @@
<input class="form-control"
type="text"
name="floor"
maxlength=16
:placeholder="$t('floor')"
v-model="floor"/>
<label for="floor">{{ $t('floor') }}</label>
@@ -14,6 +15,7 @@
<input class="form-control"
type="text"
name="corridor"
maxlength=16
:placeholder="$t('corridor')"
v-model="corridor"/>
<label for="corridor">{{ $t('corridor') }}</label>
@@ -22,6 +24,7 @@
<input class="form-control"
type="text"
name="steps"
maxlength=16
:placeholder="$t('steps')"
v-model="steps"/>
<label for="steps">{{ $t('steps') }}</label>
@@ -30,6 +33,7 @@
<input class="form-control"
type="text"
name="flat"
maxlength=16
:placeholder="$t('flat')"
v-model="flat"/>
<label for="flat">{{ $t('flat') }}</label>

View File

@@ -17,22 +17,12 @@
<div class="row my-3">
<div class="col-lg-6">
<div class="form-check">
<input type="checkbox"
class="form-check-input"
id="isConfidential"
v-model="isConfidential"
:value="valueConfidential" />
<label class="form-check-label" for="isConfidential">
{{ $t('isConfidential') }}
</label>
</div>
<div class="form-check">
<input type="checkbox"
class="form-check-input"
id="isNoAddress"
v-model="isNoAddress"
:value="value" />
v-bind:value="value" />
<label class="form-check-label" for="isNoAddress">
{{ $t('isNoAddress') }}
</label>
@@ -128,8 +118,7 @@ export default {
emits: ['getCities', 'getReferenceAddresses'],
data() {
return {
value: false,
valueConfidential: false,
value: false
}
},
computed: {
@@ -145,14 +134,6 @@ export default {
addressMap() {
return this.entity.addressMap;
},
isConfidential: {
set(value) {
this.entity.selected.confidential = value;
},
get() {
return this.entity.selected.confidential;
}
},
isNoAddress: {
set(value) {
console.log('isNoAddress value', value);

View File

@@ -18,7 +18,6 @@ const addressMessages = {
other_address: 'Autre adresse',
create_address: 'Adresse inconnue. Cliquez ici pour créer une nouvelle adresse',
isNoAddress: 'Pas d\'adresse complète',
isConfidential: 'Adresse confidentielle',
street: 'Nom de rue',
streetNumber: 'Numéro',
floor: 'Étage',

View File

@@ -1,140 +0,0 @@
<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>

View File

@@ -1,60 +0,0 @@
<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>

View File

@@ -1,77 +0,0 @@
<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>

View File

@@ -1,57 +0,0 @@
<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>

View File

@@ -1,96 +0,0 @@
<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>

View File

@@ -1,85 +0,0 @@
<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>

View File

@@ -1,79 +0,0 @@
<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>

View File

@@ -1,18 +0,0 @@
<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>

View File

@@ -1,21 +0,0 @@
<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>

View File

@@ -1,54 +0,0 @@
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
};

View File

@@ -1,200 +0,0 @@
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 };

View File

@@ -1,5 +1,5 @@
<template>
<ul class="list-suggest remove-items" v-if="picked.length">
<ul class="list-suggest remove-items">
<li v-for="p in picked" @click="removeEntity(p)" :key="p.type+p.id">
<span class="chill_denomination">{{ p.text }}</span>
</li>
@@ -79,15 +79,11 @@ export default {
);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
console.log(this.picked)
},
removeEntity(entity) {
console.log('remove entity', entity);
this.$emit('removeEntity', entity);
}
},
mounted() {
console.log(this.picked);
}
}
</script>

View File

@@ -24,11 +24,6 @@
{{ $t('user')}}
</span>
<span v-if="entity.type === 'household'" class="badge rounded-pill bg-user">
{{ $t('household')}}
</span>
</template>
<script>
@@ -45,8 +40,7 @@ export default {
company: "Personne morale",
contact: "Personne physique",
},
user: 'TMS',
household: 'Ménage',
user: 'TMS'
}
}
}

View File

@@ -1,26 +1,22 @@
<template>
<div :class="classes">
<div class="confidential-content" :class="{ 'blur': isBlurred }">
<div class="confidential" v-on:click="toggleBlur">
<div class="confidential-content blur">
<slot name="confidential-content"></slot>
</div>
<div>
<i class="fa fa-eye toggle" aria-hidden="true" @click="toggleBlur"></i>
</div>
<i class="fa fa-eye toggle" aria-hidden="true"></i>
</div>
</template>
<script>
export default {
name: "Confidential",
data() {
return {
isBlurred: true,
};
},
methods : {
toggleBlur() {
console.log('toggle blur');
this.isBlurred = !this.isBlurred;
toggleBlur: function(e){
if(e.target.matches('.toggle')){
e.target.previousElementSibling.classList.toggle("blur");
e.target.classList.toggle("fa-eye");
e.target.classList.toggle("fa-eye-slash");
}
},
}
}
@@ -43,4 +39,4 @@ export default {
-moz-filter: blur(5px);
filter: blur(5px);
}
</style>
</style>

View File

@@ -1,58 +1,26 @@
<template>
<component :is="component" class="chill-entity entity-address my-3">
<component :is="component" class="address" :class="multiline">
<div v-if="isConfidential">
<confidential>
<template v-slot:confidential-content>
<div v-if="isMultiline === true">
<p v-for="(l, i) in address.lines" :key="`line-${i}`">
{{ l }}
</p>
</div>
<div v-else>
<p v-if="address.text"
class="street">
{{ address.text }}
</p>
<p v-if="address.postcode"
class="postcode">
{{ address.postcode.code }} {{ address.postcode.name }}
</p>
<p v-if="address.country"
class="country">
{{ address.country.name.fr }}
</p>
</div>
</template>
</confidential>
<div v-if="isMultiline === true">
<p v-for="(l, i) in address.lines" :key="`line-${i}`">
{{ l }}
</p>
</div>
<div v-if="!isConfidential">
<div v-if="isMultiline === true">
<p v-for="(l, i) in address.lines" :key="`line-${i}`">
{{ l }}
</p>
</div>
<div v-else>
<p v-if="address.text"
class="street">
{{ address.text }}
</p>
<p v-if="address.postcode"
class="postcode">
{{ address.postcode.code }} {{ address.postcode.name }}
</p>
<p v-if="address.country"
class="country">
{{ address.country.name.fr }}
</p>
</div>
<div v-else>
<p v-if="address.text"
class="street">
{{ address.text }}
</p>
<p v-if="address.postcode"
class="postcode">
{{ address.postcode.code }} {{ address.postcode.name }}
</p>
<p v-if="address.country"
class="country">
{{ address.country.name.fr }}
</p>
</div>
</component>
<!-- <div v-if="isMultiline === true" class="address-more">
@@ -110,14 +78,8 @@
</template>
<script>
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
export default {
name: 'AddressRenderBox',
components: {
Confidential
},
props: {
address: {
type: Object
@@ -138,9 +100,6 @@ export default {
multiline() {
//console.log(this.isMultiline, typeof this.isMultiline);
return this.isMultiline === true ? "multiline" : "";
},
isConfidential() {
return this.address.confidential;
}
}
};

View File

@@ -1,86 +1,26 @@
<template>
<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>
<div class="flex-grow-1 ms-3 h3">
<div class="visually-hidden">
{{ w.relatedEntityClass }}
{{ w.relatedEntityId }}
</div>
</div>
</div>
<div class="breadcrumb">
<template v-for="(step, j) in w.steps" :key="`step-${j}`">
<span class="mx-2"
tabindex="0"
data-bs-trigger="focus hover"
data-bs-toggle="popover"
data-bs-placement="bottom"
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>
<i v-if="step.isFreezed"
class="fa fa-snowflake-o fa-sm me-1">
</i>
{{ step.currentStep.text }}
</span>
<span v-if="j !== Object.keys(w.steps).length - 1">
</span>
</template>
</div>
</div>
<div class="item-row">
<div class="item-col flex-grow-1">
<p v-if="isUserSubscribedToStep(w)">
<i class="fa fa-check fa-fw"></i>
{{ $t('you_subscribed_to_all_steps') }}
</p>
<p v-if="isUserSubscribedToFinal(w)">
<i class="fa fa-check fa-fw"></i>
{{ $t('you_subscribed_to_final_step') }}
</p>
</div>
<div class="item-col">
<ul class="record_actions">
<li>
<a :href="goToUrl(w)" class="btn btn-sm btn-show" :title="$t('action.show')"></a>
</li>
</ul>
</div>
</div>
</div>
<div class="list-group my-2 workflow workflow-box">
<div class="list-group-item">
<h4>Workflow associés</h4>
</div>
<div class="list-group-item" v-for="w in workflows">
{{ w.id }}
<ul class="record_actions">
<li>
<a class="btn btn-sm btn-outline-primary"
title="voir"
:href="goToUrl(w)">
<i class="fa fa-eye fa-fw"></i>
</a>
</li>
</ul>
</div>
</div>
</template>
<script>
import Popover from 'bootstrap/js/src/popover';
const i18n = {
messages: {
fr: {
you_subscribed_to_all_steps: "Vous recevrez une notification à chaque étape",
you_subscribed_to_final_step: "Vous recevrez une notification à l'étape finale",
by: "Par",
at: "Le"
}
}
}
export default {
name: "ListWorkflow",
i18n: i18n,
props: {
workflows: {
type: Array,
@@ -90,42 +30,7 @@ export default {
methods: {
goToUrl(w) {
return `/fr/main/workflow/${w.id}/show`;
},
getPopTitle(step) {
if (step.transitionPrevious != null) {
let freezed = step.isFreezed ? `<i class="fa fa-snowflake-o fa-sm me-1"></i>` : ``;
return `${freezed}${step.currentStep.text}`;
}
},
getPopContent(step) {
if (step.transitionPrevious != null) {
return `<ul class="small_in_title">
<li><span class="item-key">${i18n.messages.fr.by} : </span><b>${step.transitionPreviousBy.text}</b></li>
<li><span class="item-key">${i18n.messages.fr.at} : </span><b>${this.formatDate(step.transitionPreviousAt.datetime)}</b></li>
</ul>`
;
}
},
formatDate(datetime) {
return datetime.split('T')[0] +' '+ datetime.split('T')[1].substring(0,5)
},
isUserSubscribedToStep(w) {
// todo
return false;
},
isUserSubscribedToFinal(w) {
// todo
return false;
},
},
mounted() {
const triggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
const popoverList = triggerList.map(function (el) {
//console.log('popover', el)
return new Popover(el, {
html: true,
});
});
}
}
}
</script>
</script>

View File

@@ -1,111 +0,0 @@
<template>
<button v-if="hasWorkflow"
class="btn btn-primary"
@click="openModal">
<b>{{ countWorkflows }}</b>
<template v-if="countWorkflows > 1">{{ $t('workflows') }}</template>
<template v-else>{{ $t('workflow') }}</template>
</button>
<pick-workflow v-else-if="allowCreate"
:relatedEntityClass="this.relatedEntityClass"
:relatedEntityId="this.relatedEntityId"
:workflowsAvailables="workflowsAvailables"
></pick-workflow>
<teleport to="body">
<modal v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false">
<template v-slot:header>
<h2 class="modal-title">{{ $t('workflow_list') }}</h2>
</template>
<template v-slot:body>
<list-workflow-vue
:workflows="workflows"
></list-workflow-vue>
</template>
<template v-slot:footer>
<pick-workflow v-if="allowCreate"
:relatedEntityClass="this.relatedEntityClass"
:relatedEntityId="this.relatedEntityId"
:workflowsAvailables="workflowsAvailables"
></pick-workflow>
</template>
</modal>
</teleport>
</template>
<script>
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
import ListWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue';
export default {
name: "ListWorkflowModal",
components: {
Modal,
PickWorkflow,
ListWorkflowVue
},
props: {
workflows: {
type: Array,
required: true,
},
allowCreate: {
type: Boolean,
required: true,
},
relatedEntityClass: {
type: String,
required: true,
},
relatedEntityId: {
type: Number,
required: false,
},
workflowsAvailables: {
type: Array,
required: true,
}
},
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl"
},
}
},
computed: {
countWorkflows() {
return this.workflows.length;
},
hasWorkflow() {
return this.countWorkflows > 0;
}
},
methods: {
openModal() {
this.modal.showModal = true;
},
},
i18n: {
messages: {
fr: {
workflow_list: "Liste des workflows associés",
workflow: " workflow associé",
workflows: " workflows associés",
}
}
}
}
</script>
<style scoped></style>

View File

@@ -61,15 +61,14 @@ 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",
years_old: "an | {n} an | {n} ans",
holder: "Titulaire"
}
}
};

View File

@@ -59,7 +59,7 @@
must be shown in such list
#}
{%- if render == 'list' -%}
<li class="chill-entity entity-address {% if address.confidential %}confidential{% endif %}">
<li class="chill-entity entity-address">
{% if options['with_picto'] %}
<i class="fa fa-li fa-map-marker"></i>
{% endif %}
@@ -68,7 +68,7 @@
{%- endif -%}
{%- if render == 'inline' -%}
<span class="chill-entity entity-address {% if address.confidential %}confidential{% endif %}">
<span class="chill-entity entity-address">
{% if options['with_picto'] %}
<i class="fa fa-fw fa-map-marker"></i>
{% endif %}
@@ -77,7 +77,7 @@
{%- endif -%}
{%- if render == 'bloc' -%}
<div class="chill-entity entity-address {% if address.confidential %}confidential{% endif %}">
<div class="chill-entity entity-address">
{% if options['has_no_address'] == true and address.isNoAddress == true %}
{% if address.postCode is not empty %}
<div class="address{% if options['multiline'] %} multiline{% endif %}{% if options['with_delimiter'] %} delimiter{% endif %}">

View File

@@ -1,3 +0,0 @@
<div class="sticky-buttons">
{# Override this file to add fast actions buttons #}
</div>

View File

@@ -1,15 +0,0 @@
<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 %}

View File

@@ -1,77 +0,0 @@
{% import '@ChillPerson/AccompanyingCourse/Comment/macro_showItem.html.twig' as m %}
{% macro recordAction(comment) %}
{% if is_granted('CHILL_MAIN_NOTIFICATION_COMMENT_EDIT', comment) %}
<li>
<a href="{{ chill_path_forward_return_path('chill_main_notification_show', {
'_fragment': 'comment-' ~ comment.id,
'edit': comment.id,
'id': comment.notification.id
}) }}" class="btn btn-edit" title="{{ 'Edit'|trans }}"
></a>
</li>
{% endif %}
{% endmacro %}
<div class="notification-comment-list my-5">
<h2 class="chill-blue">{{ 'notification.comments_list'|trans }}</h2>
{% if notification.comments|length > 0 %}
<div class="flex-table">
{% for comment in notification.comments %}
{% if editedCommentForm is null or editedCommentId != comment.id %}
{{ m.show_comment(comment, {
'recordAction': _self.recordAction(comment)
}) }}
{% else %}
<div class="item-bloc">
<div class="item-row row">
<a id="comment-{{ comment.id }}"></a>
{{ form_start(editedCommentForm) }}
{{ form_errors(editedCommentForm) }}
{{ form_widget(editedCommentForm.content) }}
<input type="hidden" name="form" value="edit" />
<ul class="record_actions">
<li class="cancel">
<a href="{{ chill_path_forward_return_path('chill_main_notification_show', {
'_fragment': 'comment-' ~ comment.id,
'id': notification.id }) }}" class="btn btn-cancel">
{{ 'cancel'|trans }}
</a>
</li>
<li>
<button class="btn btn-save" type="submit">{{ 'Save'|trans }}</button>
</li>
</ul>
{{ form_end(editedCommentForm) }}
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% else %}
<span class="chill-no-data-statement">{{ 'No comments'|trans }}</span>
{% endif %}
{% if appendCommentForm is not null %}
<div class="new-comment my-5">
<h2 class="chill-blue mb-4">{{ 'Write a new comment'|trans }}</h2>
{{ form_start(appendCommentForm) }}
{{ form_errors(appendCommentForm) }}
{{ form_widget(appendCommentForm.content) }}
<input type="hidden" name="form" value="append" />
<ul class="record_actions">
<li>
<button class="btn btn-create" type="submit">{{ 'notification.append_comment'|trans }}</button>
</li>
</ul>
{{ form_end(appendCommentForm) }}
</div>
{% endif %}
</div>

View File

@@ -52,11 +52,9 @@
{% macro content(c) %}
<div class="item-row separator">
{% if c.data is defined %}
<div class="mx-3 flex-grow-1">
{% include c.data.template with c.data.template_data %}
</div>
{% endif %}
<div class="mx-3 flex-grow-1">
{% include c.data.template with c.data.template_data %}
</div>
</div>
<div class="item-row">
<div class="notification-content">
@@ -70,44 +68,34 @@
</div>
{% if c.action_button is not defined or c.action_button != false %}
<div class="item-row separator">
<div class="item-col item-meta">
{# TODO twig extension to count comments #}
<div class="comment-counter visually-hidden">
<span>x commentaires</span>
</div>
</div>
<div class="item-col">
<ul class="record_actions">
<ul class="record_actions">
<li>
{# Vue component #}
<span class="notification_toggle_read_status"
data-notification-id="{{ c.notification.id }}"
data-notification-current-is-read="{{ c.notification.isReadBy(app.user) }}"
data-container="notification-status"
></span>
</li>
{% if is_granted('CHILL_MAIN_NOTIFICATION_UPDATE', c.notification) %}
<li>
{# Vue component #}
<span class="notification_toggle_read_status"
data-notification-id="{{ c.notification.id }}"
data-notification-current-is-read="{{ c.notification.isReadBy(app.user) }}"
data-container="notification-status"
></span>
<a href="{{ chill_path_add_return_path('chill_main_notification_edit', {'id': c.notification.id}) }}"
class="btn btn-edit" title="{{ 'Edit'|trans }}"></a>
</li>
{% if is_granted('CHILL_MAIN_NOTIFICATION_UPDATE', c.notification) %}
<li>
<a href="{{ chill_path_add_return_path('chill_main_notification_edit', {'id': c.notification.id}) }}"
class="btn btn-edit" title="{{ 'Edit'|trans }}"></a>
</li>
{% endif %}
{% if is_granted('CHILL_MAIN_NOTIFICATION_SEE', c.notification) %}
<li>
<a href="{{ chill_path_add_return_path('chill_main_notification_show', {'id': c.notification.id}) }}"
class="btn {% if not c.notification.isSystem %}btn-show change-icon{% else %}btn-misc{% endif %}" title="{{ 'notification.see_comments_thread'|trans }}">
{% if not c.notification.isSystem() %}
<i class="fa fa-comment"></i>
{% else %}
{{ 'Read more'|trans }}
{% endif %}
</a>
</li>
{% endif %}
</ul>
</div>
{% endif %}
{% if is_granted('CHILL_MAIN_NOTIFICATION_SEE', c.notification) %}
<li>
<a href="{{ chill_path_add_return_path('chill_main_notification_show', {'id': c.notification.id}) }}"
class="btn {% if not c.notification.isSystem %}btn-show change-icon{% else %}btn-misc{% endif %}" title="{{ 'notification.see_comments_thread'|trans }}">
{% if not c.notification.isSystem() %}
<i class="fa fa-comment"></i>
{% else %}
{{ 'Read more'|trans }}
{% endif %}
</a>
</li>
{% endif %}
</ul>
</div>
{% endif %}
{% endmacro %}
@@ -119,19 +107,15 @@
<button type="button" class="accordion-button collapsed"
data-bs-toggle="collapse" data-bs-target="#flush-collapse-{{ notification.id }}"
aria-expanded="false" aria-controls="flush-collapse-{{ notification.id }}">
{{ _self.title(_context) }}
</button>
{{ _self.header(_context) }}
</div>
<div id="flush-collapse-{{ notification.id }}"
class="accordion-collapse collapse"
aria-labelledby="flush-heading-{{ notification.id }}"
data-bs-parent="#notification-fold">
{{ _self.content(_context) }}
</div>
{% else %}
{{ _self.title(_context) }}

View File

@@ -20,8 +20,6 @@
{{ form_row(form.title, { 'label': 'notification.subject'|trans }) }}
{{ form_row(form.addressees, { 'label': 'notification.sent_to'|trans }) }}
{% include handler.template(notification) with handler.templateData(notification) %}
<div class="mb-3 row">
<label class="col-form-label col-sm-4" for="notification_message">{{ form_label(form.message) }}</label>
@@ -30,6 +28,8 @@
</div>
</div>
{% include handler.template(notification) with handler.templateData(notification) %}
{{ form_end(form) }}
<ul class="record_actions sticky-form-buttons">

View File

@@ -1,12 +1,46 @@
<h1 class="mt-5"><a id="notification-list"></a>{{ 'notification.Notifications'|trans }}</h1>
<div class="flex-table accordion accordion-flush" id="notification-fold">
<div class="list-group my-2 notification notification-box">
<div class="list-group-item">
<h4>{{ 'notification.Sent'|trans }}</h4>
</div>
{# TODO pagination or limit #}
{% for notification in notifications %}
{% include 'ChillMainBundle:Notification:_list_item.html.twig' with {
'full_content': true,
'fold_item': true,
'action_button': true,
} %}{#
#}
<div class="list-group-item notification-status {% if notification.isReadBy(app.user) %}read{% else %}unread{% endif %}">
{% if not notification.isSystem %}
{% if notification.sender == app.user %}
<h6 class="notification-title">
<abbr title="{{ 'Le ' ~ notification.date|format_date('long') ~ '\n' ~ notification.title }}">
{{ notification.date|format_datetime('short','short') }}
</abbr>
{# Vue component #}
<span class="notification_toggle_read_status"
data-notification-id="{{ notification.id }}"
data-notification-current-is-read="{{ notification.isReadBy(app.user) }}"
data-container="notification-status"
data-show-button-url="{{ chill_path_add_return_path('chill_main_notification_show', {'id': notification.id}, false) }}"
data-button-class="btn-outline-primary"
data-button-text="false"
></span>
</h6>
{% if notification.addressees|length > 0 %}
<abbr title="{{ 'notification.sent_to'|trans }}">{{ 'notification.to'|trans }}:</abbr>
{% endif %}
{% for a in notification.addressees %}
<span class="badge-user">
{{ a|chill_entity_render_string }}
</span>
{% endfor %}
{% else %}
<div>{{ 'notification.you were notified by %sender%'|trans({'%sender%': notification.sender|chill_entity_render_string }) }}</div>
{% endif %}
{% else %}
<div>{{ 'notification.you were notified by system'|trans }}</div>
{% endif %}
</div>
{% endfor %}
</div>

View File

@@ -14,6 +14,21 @@
{{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
{% endblock %}
{% import '@ChillPerson/AccompanyingCourse/Comment/macro_showItem.html.twig' as m %}
{% macro recordAction(comment) %}
{% if is_granted('CHILL_MAIN_NOTIFICATION_COMMENT_EDIT', comment) %}
<li>
<a href="{{ chill_path_forward_return_path('chill_main_notification_show', {
'_fragment': 'comment-' ~ comment.id,
'edit': comment.id,
'id': comment.notification.id
}) }}" class="btn btn-edit" title="{{ 'Edit'|trans }}"
></a>
</li>
{% endif %}
{% endmacro %}
{% block content %}
<div class="col-10 notification notification-show">
@@ -26,12 +41,70 @@
'template_data': handler.getTemplateData(notification)
},
'action_button': false,
'full_content': true,
'fold_item': false
'full_content': true
} %}
</div>
{% include 'ChillMainBundle:Notification:_item_comments.html.twig' %}
<div class="notification-comment-list my-5">
<h2 class="chill-blue">{{ 'notification.comments_list'|trans }}</h2>
{% if notification.comments|length > 0 %}
<div class="flex-table">
{% for comment in notification.comments %}
{% if editedCommentForm is null or editedCommentId != comment.id %}
{{ m.show_comment(comment, {
'recordAction': _self.recordAction(comment)
}) }}
{% else %}
<div class="item-bloc">
<div class="item-row row">
<a id="comment-{{ comment.id }}"></a>
{{ form_start(editedCommentForm) }}
{{ form_errors(editedCommentForm) }}
{{ form_widget(editedCommentForm.content) }}
<input type="hidden" name="form" value="edit" />
<ul class="record_actions">
<li class="cancel">
<a href="{{ chill_path_forward_return_path('chill_main_notification_show', {
'_fragment': 'comment-' ~ comment.id,
'id': notification.id }) }}" class="btn btn-cancel">
{{ 'cancel'|trans }}
</a>
</li>
<li>
<button class="btn btn-save" type="submit">{{ 'Save'|trans }}</button>
</li>
</ul>
{{ form_end(editedCommentForm) }}
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% if appendCommentForm is not null %}
<div class="new-comment my-5">
<h2 class="chill-blue mb-4">{{ 'Write a new comment'|trans }}</h2>
{{ form_start(appendCommentForm) }}
{{ form_errors(appendCommentForm) }}
{{ form_widget(appendCommentForm.content) }}
<input type="hidden" name="form" value="append" />
<ul class="record_actions">
<li>
<button class="btn btn-create" type="submit">{{ 'notification.append_comment'|trans }}</button>
</li>
</ul>
{{ form_end(appendCommentForm) }}
</div>
{% endif %}
</div>
<ul class="record_actions sticky-form-buttons">
<li class="cancel">

View File

@@ -42,3 +42,6 @@
{% endif %}
></span>
{{ encore_entry_script_tags('vue_onthefly') }}
{{ encore_entry_link_tags('vue_onthefly') }}

View File

@@ -5,10 +5,14 @@
{{ form_row(transition_form.transition) }}
<div id="finalizeAfter">
{{ form_row(transition_form.finalizeAfter) }}
</div>
{% if transition_form.freezeAfter is defined %}
{{ form_row(transition_form.freezeAfter) }}
{% endif %}
<div id="futureDestUsers">
{{ form_row(transition_form.future_dest_users) }}
</div>
@@ -27,7 +31,7 @@
{% else %}
<div class="alert alert-chill-yellow">
{% if entity_workflow.currentStep.isFinal %}
{% if entity_workflow.currentStep.isFinalizeAfter %}
<p>{{ 'workflow.This workflow is finalized'|trans }}</p>
{% else %}
<p>{{ 'workflow.You are not allowed to apply a transition on this workflow'|trans }}</p>

View File

@@ -1,13 +1,13 @@
{% set acl = "0" %}
{% if is_granted('CHILL_MAIN_WORKFLOW_CREATE', blank_workflow) %}
{% set acl = "1" %}
{# vue component #}
<div data-pick-workflow="1"
data-related-entity-class="{{ blank_workflow.relatedEntityClass }}"
data-related-entity-id="{{ blank_workflow.relatedEntityId }}"
data-workflows-availables="{{ workflows_availables|json_encode()|e('html_attr') }}"
></div>
{% endif %}
{# vue component #}
<div data-list-workflows="1"
data-workflows="{{ entity_workflows_json|json_encode|e('html_attr') }}"
data-allow-create="{{ acl }}"
data-related-entity-class="{{ blank_workflow.relatedEntityClass }}"
data-related-entity-id="{{ blank_workflow.relatedEntityId }}"
data-workflows-availables="{{ workflows_availables|json_encode()|e('html_attr') }}"
></div>
{% if entity_workflows|length > 0 %}
{# vue component #}
<div data-list-workflows="1" data-workflows="{{ entity_workflows_json|json_encode|e('html_attr') }}"></div>
{% endif %}

View File

@@ -2,42 +2,30 @@
<div class="flex-table">
{% for step in entity_workflow.stepsChained %}
{% set place_labels = workflow_metadata(entity_workflow, 'label', step.currentStep) %}
{% set place_label = place_labels is null ? step.currentStep : place_labels|localize_translatable_string %}
<div class="item-bloc {{ 'bloc' ~ step.id }} {% if loop.first %}initial{% endif %}">
<div class="item-row">
{% if loop.first and step.next is null %}
<div class="item-col">
{{ 'workflow.No transitions'|trans }}
</div>
{% else %}
<div class="item-col">
{% if step.previous is not null and step.previous.freezeAfter == true %}
<i class="fa fa-snowflake-o fa-sm me-1" title="{{ 'workflow.Freezed'|trans }}"></i>
{% endif %}
</div>
<div class="item-col flex-column align-items-end">
<div class="decided">
{{ place_label }}
</div>
{#
<div class="decided">
<i class="fa fa-times fa-fw text-danger"></i>
Refusé
</div>
#}
</div>
{% endif %}
<div class="item-col flex-column align-items-end">
<div class="decided">
{% if not loop.first %}
<i class="fa fa-check fa-fw text-success"></i>
{% endif %}
{{ step.currentStep }}
</div>
{#
<div class="decided">
<i class="fa fa-times fa-fw text-danger"></i>
Refusé
</div>
#}
</div>
</div>
{% if step.next is not null %}
{% set transition = chill_workflow_transition_by_string(step.entityWorkflow, step.transitionAfter) %}
{% set transition_labels = workflow_metadata(step.entityWorkflow, 'label', transition) %}
{% set transition_label = transition_labels is null ? step.transitionAfter : transition_labels|localize_translatable_string %}
{% set forward = workflow_metadata(step.entityWorkflow, 'isForward', transition) %}
<div class="item-row separator">
<div class="item-col" style="width: inherit;">
{% if step.transitionBy is not null %}
@@ -52,12 +40,7 @@
<div class="item-col flex-column align-items-end">
<div class="to-decision">
<i class="fa fa-share fa-fw text-secondary" title="transféré"></i>
{% if forward %}
<i class="fa fa-check fa-fw text-success"></i>
{% else %}
<i class="fa fa-times fa-fw text-danger"></i>
{% endif %}
{{ transition_label }}
{{ step.next.currentStep }}
</div>
</div>
</div>

View File

@@ -1,62 +1,41 @@
{% macro popoverContent(step) %}
<ul class="small_in_title">
{% if step.previous is not null %}
<li>
<span class="item-key">{{ 'By'|trans ~ ' : ' }}</span>
<b>{{ step.previous.transitionBy|chill_entity_render_box }}</b>
</li>
<li>
<span class="item-key">{{ 'Le'|trans ~ ' : ' }}</span>
<b>{{ step.previous.transitionAt|format_datetime('short', 'short') }}</b>
</li>
{% else %}
<li>
<span class="item-key">{{ 'workflow.Created by'|trans ~ ' : ' }}</span>
<b>{{ step.entityWorkflow.createdBy|chill_entity_render_box }}</b>
</li>
<li>
<span class="item-key">{{ 'Le'|trans ~ ' : ' }}</span>
<b>{{ step.entityWorkflow.createdAt|format_datetime('short', 'short') }}</b>
</li>
{% endif %}
<li>
<span class="item-key">{{ 'By'|trans ~ ' : ' }}</span>
<b>{{ step.transitionBy|chill_entity_render_box }}</b>
</li>
<li>
<span class="item-key">{{ 'Le'|trans ~ ' : ' }}</span>
<b>{{ step.transitionAt|format_datetime('short', 'short') }}</b>
</li>
</ul>
{% endmacro %}
{% macro popoverTitle(step) %}
{% if step.previous is not null and step.previous.freezeAfter == true %}
<i class="fa fa-snowflake-o fa-sm me-1" title="{{ 'workflow.Freezed'|trans }}"></i>
{% endif %}
{% if step.previous is not null %}
{% set transition = chill_workflow_transition_by_string(step.entityWorkflow, step.previous.transitionAfter) %}
{% set labels = workflow_metadata(step.entityWorkflow, 'label', transition) %}
{% set label = labels is null ? step.previous.transitionAfter : labels|localize_translatable_string %}
{{ label }}
{% endif %}
{% endmacro %}
{% macro breadcrumb(_ctx) %}
<div class="breadcrumb">
{% for step in _ctx.entity_workflow.stepsChained %}
{% set labels = workflow_metadata(_ctx.entity_workflow, 'label', step.currentStep) %}
{% set label = labels is null ? step.currentStep : labels|localize_translatable_string %}
{% set popTitle = _self.popoverTitle(step) %}
{% set popContent = _self.popoverContent(step) %}
{% if step.previous is null %}
{#
{% set popContent = "Point de départ du workflow" %}
{{ dump(step) }}
#}
{% set popContent = _self.popoverContent(step) %}
{% else %}
{% set popContent = _self.popoverContent(step.previous) %}
{% endif %}
<span class="mx-2"
tabindex="0"
data-bs-trigger="focus hover"
data-bs-toggle="popover"
data-bs-placement="bottom"
data-bs-custom-class="workflow-transition"
title="{{ popTitle|e('html_attr') }}"
title="{{ step.currentStep }}"
data-bs-content="{{ popContent|e('html_attr') }}"
>
{% if step.currentStep == 'initial' %}
<i class="fa fa-circle me-1 text-chill-yellow"></i>
{% endif %}
{% if step.previous is not null and step.previous.freezeAfter == true %}
<i class="fa fa-snowflake-o fa-sm me-1" title="{{ 'workflow.Freezed'|trans }}"></i>
{% endif %}
{{ label }}
{{ step.currentStep }}
</span>
{% if not loop.last %}

View File

@@ -13,7 +13,6 @@
{{ 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>
@@ -60,26 +59,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>
{# DISABLED {{ chill_widget('homepage', {} ) }} #}
{% include '@ChillMain/Homepage/index.html.twig' %}
<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', {} ) }}
{% endblock %}
</div>
@@ -95,7 +94,6 @@
{{ 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) {

View File

@@ -45,15 +45,13 @@ class SearchUserApiProvider implements SearchApiInterface
$query
->setSelectKey('user')
->setSelectJsonbMetadata("jsonb_build_object('id', u.id)")
->setSelectPertinence('GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.label),
SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical))', [$pattern, $pattern])
->setSelectPertinence('GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical),
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical))', [$pattern, $pattern])
->setFromClause('users AS u')
->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]);
->setWhereClauses('SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
OR
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical) > 0.15
', [$pattern, $pattern]);
return $query;
}

View File

@@ -22,10 +22,6 @@ class SearchApiQuery
private array $fromClauseParams = [];
private bool $isDistinct = false;
private ?string $isDistinctKey = null;
private ?string $jsonbMetadata = null;
private array $jsonbMetadataParams = [];
@@ -109,11 +105,6 @@ class SearchApiQuery
]);
}
public function getDistinct(): bool
{
return $this->isDistinct;
}
public function getFromClause(): string
{
return $this->fromClause;
@@ -148,14 +139,6 @@ 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;
@@ -202,11 +185,7 @@ class SearchApiQuery
private function buildSelectClause(bool $countOnly = false): string
{
if ($countOnly) {
if (!$this->isDistinct) {
return 'count(*) AS c';
}
return 'count(distinct ' . $this->isDistinctKey . ') AS c';
return 'count(*) AS c';
}
$selects = $this->getSelectClauses();
@@ -223,7 +202,7 @@ class SearchApiQuery
$selects[] = strtr('{pertinence} AS pertinence', ['{pertinence}' => $this->pertinence]);
}
return ($this->isDistinct ? 'DISTINCT ' : '') . implode(', ', $selects);
return implode(', ', $selects);
}
private function buildSelectParams(bool $count = false): array

View File

@@ -1,41 +0,0 @@
<?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;
}
}

View File

@@ -45,7 +45,6 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw
'extra',
'validFrom' => DateTimeInterface::class,
'validTo' => DateTimeInterface::class,
'confidential',
];
private AddressRender $addressRender;
@@ -84,7 +83,6 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw
'buildingName' => $address->getBuildingName(),
'distribution' => $address->getDistribution(),
'extra' => $address->getExtra(),
'confidential' => $address->getConfidential(),
'lines' => $this->addressRender->renderLines($address),
];

Some files were not shown because too many files have changed in this diff Show More