mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-21 14:14:58 +00:00
Compare commits
96 Commits
v4.1.0
...
testing202
Author | SHA1 | Date | |
---|---|---|---|
aabf62d399
|
|||
6e36771349
|
|||
7a82cae155
|
|||
d2d297a377
|
|||
0541995a60
|
|||
29e054bd10
|
|||
da0099aafc
|
|||
3a18ea42fe
|
|||
d204df0316 | |||
|
82c02f442b | ||
f32a9dc7bc | |||
ea06a96f91 | |||
76433e2512 | |||
1fa464b87a | |||
e60435b8cc | |||
ab6ab19499 | |||
2a1762ea8d | |||
18ababbca9 | |||
f6179cd3a3 | |||
ddf8da4cee | |||
bf2181c2f1 | |||
d508fde8d2 | |||
14dba22181 | |||
7dc7e77c62 | |||
9d58904969 | |||
4d90c7028f | |||
3abb76d268 | |||
d62dd4396e | |||
59e8d9d516 | |||
7dcb8abe38 | |||
a0b2d92ba2 | |||
7843e5dfd1 | |||
32c847267b | |||
9b353f4d1b | |||
81a858f07a | |||
6a2ee232a9 | |||
56c43a0a76 | |||
eb724a730c | |||
18f98b6795 | |||
d73994edd0 | |||
70603570c8 | |||
df09dd2017 | |||
1c87280b1e | |||
445e093a28 | |||
3f91c65b30 | |||
9bc3c16b58 | |||
12dff82248 | |||
ab23a4efb5 | |||
204fb20475 | |||
f430d97152 | |||
4fa4d3b65c | |||
bd4c34cc1d | |||
4cea678e93 | |||
5e6833975b | |||
f523b9adb3 | |||
a211549432 | |||
17b1363113 | |||
3356ed8e57 | |||
2a7fa517ee | |||
85781c8e14 | |||
00eb435896 | |||
ed71cffd6a | |||
ae679e6997 | |||
e1d308fd97 | |||
d9acda67e3 | |||
e88da74882 | |||
591c44d1a0 | |||
bf04b7981c | |||
df33eec30f | |||
c657c98918 | |||
ef5eb5b907 | |||
d683fe002d | |||
555bbca59b | |||
e9e9d5c458 | |||
b1842a33ae | |||
6afeaccf24 | |||
fb76bac480 | |||
6ded185289 | |||
95adc29f9d | |||
4d0c3e683f | |||
018aafc773 | |||
c4aea4efc2 | |||
225e3ca13f | |||
8c1fa7956a | |||
e253d1b276 | |||
a52aac2d98 | |||
9e8cf60dd8 | |||
7682d81d50 | |||
5d31ce96c1 | |||
81ef64a246 | |||
49d1f78001 | |||
0d0f3528e2 | |||
d97d5e689a | |||
95d80ce13e | |||
668720984d | |||
245c3fa121 |
6
.changes/unreleased/Feature-20250211-142243.yaml
Normal file
6
.changes/unreleased/Feature-20250211-142243.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: Allow the merge of two accompanying period works
|
||||
time: 2025-02-11T14:22:43.134106669+01:00
|
||||
custom:
|
||||
Issue: "359"
|
||||
SchemaChange: No schema change
|
6
.changes/unreleased/Feature-20250403-100311.yaml
Normal file
6
.changes/unreleased/Feature-20250403-100311.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: Duplication of a document to another accompanying period work evaluation
|
||||
time: 2025-04-03T10:03:11.796736107+02:00
|
||||
custom:
|
||||
Issue: "369"
|
||||
SchemaChange: No schema change
|
6
.changes/unreleased/Feature-20250403-100857.yaml
Normal file
6
.changes/unreleased/Feature-20250403-100857.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: Fusion of two accompanying period works
|
||||
time: 2025-04-03T10:08:57.25079018+02:00
|
||||
custom:
|
||||
Issue: "359"
|
||||
SchemaChange: No schema change
|
10
.changes/v4.2.0.md
Normal file
10
.changes/v4.2.0.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## v4.2.0 - 2025-09-02
|
||||
### Feature
|
||||
* ([#64](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/64)) Add external identifier for a Person
|
||||
|
||||
**Schema Change**: Add columns or tables
|
||||
* ([#330](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/330) Allow users to choose for which notifications they want to receive an email
|
||||
### Fixed
|
||||
* ([#422](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/422)) Fixed html layout of pages for recovering password
|
||||
* Fix typo in 'uncheckAll' script for centers selection
|
||||
* Fix incorrect parameter name in event details link
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -18,6 +18,9 @@ migrations/*
|
||||
templates/*
|
||||
translations/*
|
||||
|
||||
# we allow developers to add customization on their installation, without commiting it
|
||||
config/packages/dev/*
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
/.env.local
|
||||
/.env.local.php
|
||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@@ -6,6 +6,17 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||
|
||||
|
||||
## v4.2.0 - 2025-09-02
|
||||
### Feature
|
||||
* ([#64](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/64)) Add external identifier for a Person
|
||||
|
||||
**Schema Change**: Add columns or tables
|
||||
* ([#330](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/330) Allow users to choose for which notifications they want to receive an email
|
||||
### Fixed
|
||||
* ([#422](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/422)) Fixed html layout of pages for recovering password
|
||||
* Fix typo in 'uncheckAll' script for centers selection
|
||||
* Fix incorrect parameter name in event details link
|
||||
|
||||
## v4.1.0 - 2025-08-26
|
||||
### Feature
|
||||
* ([#400](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/400)) Add filter to social actions list to filter out actions where current user intervenes
|
||||
|
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\CustomFieldsBundle\EntityRepository;
|
||||
|
||||
use Chill\CustomFieldsBundle\Entity\CustomFieldsDefaultGroup;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
class CustomFieldsDefaultGroupRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, CustomFieldsDefaultGroup::class);
|
||||
}
|
||||
|
||||
public function findOneByEntity(string $className): ?CustomFieldsDefaultGroup
|
||||
{
|
||||
return $this->findOneBy(['entity' => $className]);
|
||||
}
|
||||
}
|
@@ -127,3 +127,7 @@ services:
|
||||
factory: ["@doctrine", getRepository]
|
||||
arguments:
|
||||
- "Chill\\CustomFieldsBundle\\Entity\\CustomFieldLongChoice\\Option"
|
||||
|
||||
Chill\CustomFieldsBundle\EntityRepository\CustomFieldsDefaultGroupRepository:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
@@ -4,6 +4,7 @@ import { StoredObject, StoredObjectVersion } from "../../types";
|
||||
import DropFileWidget from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileWidget.vue";
|
||||
import { computed, reactive } from "vue";
|
||||
import { useToast } from "vue-toast-notification";
|
||||
import { DOCUMENT_REPLACE, DOCUMENT_ADD, trans } from "translator";
|
||||
|
||||
interface DropFileConfig {
|
||||
allowRemove: boolean;
|
||||
@@ -75,10 +76,10 @@ function closeModal(): void {
|
||||
@click="openModal"
|
||||
class="btn btn-create"
|
||||
>
|
||||
Ajouter un document
|
||||
{{ trans(DOCUMENT_ADD) }}
|
||||
</button>
|
||||
<button v-else @click="openModal" class="btn btn-edit">
|
||||
Remplacer le document
|
||||
<button v-else @click="openModal" class="dropdown-item">
|
||||
{{ trans(DOCUMENT_REPLACE) }}
|
||||
</button>
|
||||
<modal
|
||||
v-if="state.showModal"
|
||||
|
@@ -23,6 +23,8 @@ See the document: Voir le document
|
||||
|
||||
document:
|
||||
Any title: Aucun titre
|
||||
replace: Remplacer
|
||||
Add: Ajouter un document
|
||||
|
||||
generic_doc:
|
||||
filter:
|
||||
|
@@ -53,7 +53,7 @@
|
||||
{% set returnLabel = 'Back to %person% events'|trans({ '%person%' : currentPerson } ) %}
|
||||
|
||||
{% if is_granted('CHILL_EVENT_SEE_DETAILS', participation.event) %}
|
||||
<a href="{{ path('chill_event__event_show', { 'event_id' : participation.event.id, 'return_path' : currentPath, 'return_label' : returnLabel } ) }}"
|
||||
<a href="{{ path('chill_event__event_show', { 'id' : participation.event.id, 'return_path' : currentPath, 'return_label' : returnLabel } ) }}"
|
||||
class="btn btn-primary btn-sm" title="{{ 'See details of the event'|trans }}">
|
||||
<i class="fa fa-fw fa-eye"></i>
|
||||
</a>
|
||||
|
@@ -170,13 +170,14 @@ div.banner {
|
||||
font-weight: lighter;
|
||||
font-size: 50%;
|
||||
margin-left: 0.5em;
|
||||
&:before { content: '(n°'; }
|
||||
&:after { content: ')'; }
|
||||
|
||||
&.same-size {
|
||||
font-size: unset;
|
||||
font-weight: unset;
|
||||
}
|
||||
}
|
||||
span.age {
|
||||
margin-left: 0.5em;
|
||||
&:before { content: '('; }
|
||||
&:after { content: ')'; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -44,8 +44,6 @@ section.chill-entity {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
span.id-number {
|
||||
&:before { content: '(n°'; }
|
||||
&:after { content: ')'; }
|
||||
}
|
||||
}
|
||||
p.moreinfo {}
|
||||
|
@@ -63,8 +63,7 @@
|
||||
|
||||
<script>
|
||||
const uncheckAll = () => {
|
||||
const allCenters = document.getElementsByName('centers[center][]');
|
||||
|
||||
const allCenters = document.getElementsByName('centers[centers][]');
|
||||
allCenters.forEach(checkbox => checkbox.checked = false)
|
||||
}
|
||||
</script>
|
||||
|
@@ -280,11 +280,17 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block pick_linked_entities_widget %}
|
||||
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value|escape('html_attr') }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}" />
|
||||
<div data-input-uniqid="{{ form.vars['uniqid'] }}" data-module="pick-linked-entities" data-pick-entities-type="{{ form.vars['pick-entities-type'] }}"
|
||||
></div>
|
||||
|
||||
{% block pick_linked_entities_widget %}
|
||||
<input type="hidden" {{ block('widget_attributes') }}
|
||||
{% if value is not empty %}value="{{ value|escape('html_attr') }}" {% endif %}
|
||||
data-input-uniqid="{{ form.vars['uniqid'] }}"/>
|
||||
<div
|
||||
data-input-uniqid="{{ form.vars['uniqid'] }}"
|
||||
data-module="pick-linked-entities"
|
||||
data-pick-entities-type="{{ form.vars['pick-entities-type'] }}"
|
||||
data-suggested="{{ form.vars['suggested']|json_encode|escape('html_attr') }}"
|
||||
></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block pick_postal_code_widget %}
|
||||
|
@@ -0,0 +1,13 @@
|
||||
<header>
|
||||
<nav class="navbar navbar-dark bg-primary navbar-expand-md">
|
||||
<div class="container-xxl">
|
||||
|
||||
<div class="col-12">
|
||||
<a class="navbar-brand" href="{{ path('chill_main_homepage') }}">
|
||||
{{ include('@ChillMain/Layout/_header-logo.html.twig') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
@@ -26,11 +26,12 @@
|
||||
|
||||
{{ 'Welcome' | trans }}<br/>
|
||||
|
||||
<b>
|
||||
{{ app.user.username }}
|
||||
{{ render(controller('Chill\\MainBundle\\Controller\\UIController::showNotificationUserCounterAction')) }}
|
||||
</b>
|
||||
|
||||
{% if app.user %}
|
||||
<b>
|
||||
{{ app.user.username }}
|
||||
{{ render(controller('Chill\\MainBundle\\Controller\\UIController::showNotificationUserCounterAction')) }}
|
||||
</b>
|
||||
{% endif %}
|
||||
{% if is_granted('IS_IMPERSONATOR') %}
|
||||
<i class="fa fa-wrench fa-lg" title="Impersonate mode"></i>
|
||||
{% endif %}
|
||||
|
@@ -16,29 +16,16 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>
|
||||
{{ 'Login to %installation_name%' | trans({ '%installation_name%' : installation.name } ) }}
|
||||
</title>
|
||||
<link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon">
|
||||
{{ encore_entry_link_tags('chill') }}
|
||||
</head>
|
||||
<body>
|
||||
<header class="navigation container-fluid">
|
||||
<div class="col-4 d-md-none parent">
|
||||
<div class="col-10 col-md-12 offset-2 logo-container">
|
||||
<a href="{{ path('chill_main_homepage') }}">
|
||||
<img class="logo" src="{{ asset('build/images/logo-chill-sans-slogan_white.png') }}">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{% extends "@ChillMain/layout.html.twig" %}
|
||||
|
||||
<div id="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
{% set header_logo_only = 1 %}
|
||||
|
||||
{% block title %}{{ 'Login to %installation_name%' | trans({ '%installation_name%' : installation.name } ) }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="content">
|
||||
{% block password_content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
{% block title %}{{ "New password set"|trans }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block password_content %}
|
||||
<div class="col-10 centered">
|
||||
|
||||
<h1>{{ "New password set"|trans }}</h1>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block password_content %}
|
||||
<div class="col-10 centered">
|
||||
|
||||
<h1>{{ title }}</h1>
|
||||
|
@@ -22,7 +22,7 @@
|
||||
|
||||
{% block title %}{{"Recover password"|trans}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block password_content %}
|
||||
<div class="col-10 centered">
|
||||
|
||||
<h1>{{ 'Recover password'|trans }}</h1>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
{% block title "Check your email"|trans %}
|
||||
|
||||
{% block content %}
|
||||
{% block password_content %}
|
||||
|
||||
<div class="col-10 centered">
|
||||
|
||||
|
@@ -30,7 +30,11 @@
|
||||
{{ include('@ChillMain/Layout/_debug.html.twig') }}
|
||||
{% endif %}
|
||||
|
||||
{{ include('@ChillMain/Layout/_header.html.twig') }}
|
||||
{% if header_logo_only is defined and header_logo_only == 1 %}
|
||||
{{ include('@ChillMain/Layout/_header_logo_only.html.twig') }}
|
||||
{% else %}
|
||||
{{ include('@ChillMain/Layout/_header.html.twig') }}
|
||||
{% endif %}
|
||||
|
||||
{% block top_banner %}{#
|
||||
To use if you want to add a banner below the header (ie the menu)
|
||||
|
@@ -15,6 +15,7 @@ use Chill\MainBundle\Notification\FlagProviders\NotificationFlagProviderInterfac
|
||||
use Chill\PersonBundle\Actions\Remove\PersonMoveSqlHandlerInterface;
|
||||
use Chill\PersonBundle\DependencyInjection\CompilerPass\AccompanyingPeriodTimelineCompilerPass;
|
||||
use Chill\PersonBundle\Export\Helper\CustomizeListPersonHelperInterface;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierEngineInterface;
|
||||
use Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface;
|
||||
use Chill\PersonBundle\Widget\PersonListWidgetFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
@@ -38,5 +39,7 @@ class ChillPersonBundle extends Bundle
|
||||
->addTag('chill_person.list_person_customizer');
|
||||
$container->registerForAutoconfiguration(NotificationFlagProviderInterface::class)
|
||||
->addTag('chill_main.notification_flag_provider');
|
||||
$container->registerForAutoconfiguration(PersonIdentifierEngineInterface::class)
|
||||
->addTag('chill_person.person_identifier_engine');
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepos
|
||||
use Chill\PersonBundle\Service\AccompanyingPeriodWork\AccompanyingPeriodWorkMergeService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Translation\TranslatableMessage;
|
||||
@@ -32,7 +33,7 @@ class AccompanyingPeriodWorkDuplicateController extends AbstractController
|
||||
* @ParamConverter("accompanyingPeriodWork", options={"id": "acpw_id"})
|
||||
*/
|
||||
#[Route(path: '{_locale}/person/accompanying-period/work/{id}/assign-duplicate', name: 'chill_person_accompanying_period_work_assign_duplicate')]
|
||||
public function assignDuplicate(AccompanyingPeriodWork $acpw, Request $request)
|
||||
public function assignDuplicate(AccompanyingPeriodWork $acpw, Request $request): Response
|
||||
{
|
||||
$accompanyingPeriod = $acpw->getAccompanyingPeriod();
|
||||
|
||||
@@ -79,7 +80,7 @@ class AccompanyingPeriodWorkDuplicateController extends AbstractController
|
||||
* @ParamConverter("acpw2", options={"id": "acpw2_id"})
|
||||
*/
|
||||
#[Route(path: '/{_locale}/person/{acpw1_id}/duplicate/{acpw2_id}/confirm', name: 'chill_person_acpw_duplicate_confirm')]
|
||||
public function confirmAction(AccompanyingPeriodWork $acpw1, AccompanyingPeriodWork $acpw2, Request $request)
|
||||
public function confirmAction(AccompanyingPeriodWork $acpw1, AccompanyingPeriodWork $acpw2, Request $request): Response
|
||||
{
|
||||
$accompanyingPeriod = $acpw1->getAccompanyingPeriod();
|
||||
|
||||
@@ -98,6 +99,7 @@ class AccompanyingPeriodWorkDuplicateController extends AbstractController
|
||||
$session->getFlashBag()->add('success', new TranslatableMessage('acpw_duplicate.Successfully merged'));
|
||||
}
|
||||
|
||||
|
||||
return $this->redirectToRoute('chill_person_accompanying_period_work_show', ['id' => $acpw1->getId()]);
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Routing\ChillUrlGeneratorInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
||||
use Chill\PersonBundle\Service\AccompanyingPeriodWorkEvaluationDocument\AccompanyingPeriodWorkEvaluationDocumentDuplicator;
|
||||
@@ -24,15 +25,16 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
|
||||
class AccompanyingPeriodWorkEvaluationDocumentDuplicateController
|
||||
readonly class AccompanyingPeriodWorkEvaluationDocumentDuplicateController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AccompanyingPeriodWorkEvaluationDocumentDuplicator $duplicator,
|
||||
private readonly Security $security,
|
||||
private readonly SerializerInterface $serializer,
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly ChillUrlGeneratorInterface $urlGenerator,
|
||||
private AccompanyingPeriodWorkEvaluationDocumentDuplicator $duplicator,
|
||||
private Security $security,
|
||||
private SerializerInterface $serializer,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private ChillUrlGeneratorInterface $urlGenerator,
|
||||
) {}
|
||||
|
||||
#[Route('/api/1.0/person/accompanying-course-work-evaluation-document/{id}/duplicate', methods: ['POST'])]
|
||||
@@ -56,6 +58,32 @@ class AccompanyingPeriodWorkEvaluationDocumentDuplicateController
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ParamConverter("document", options={"id": "document_id"})
|
||||
* @ParamConverter("evaluation", options={"id": "evaluation_id"})
|
||||
*/
|
||||
#[Route('/api/1.0/person/accompanying-course-work-evaluation-document/{document_id}/evaluation/{evaluation_id}/duplicate', methods: ['POST'])]
|
||||
public function duplicateToEvaluationApi(AccompanyingPeriodWorkEvaluationDocument $document, AccompanyingPeriodWorkEvaluation $evaluation): Response
|
||||
{
|
||||
$work = $evaluation->getAccompanyingPeriodWork();
|
||||
|
||||
if (!$this->security->isGranted(AccompanyingPeriodWorkVoter::UPDATE, $work)) {
|
||||
throw new AccessDeniedHttpException('not allowed to edit this accompanying period work');
|
||||
}
|
||||
|
||||
$duplicatedDocument = $this->duplicator->duplicateToEvaluation($document, $evaluation);
|
||||
|
||||
$this->entityManager->persist($duplicatedDocument);
|
||||
$this->entityManager->persist($duplicatedDocument->getStoredObject());
|
||||
$this->entityManager->persist($evaluation);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($duplicatedDocument, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
||||
json: true
|
||||
);
|
||||
}
|
||||
|
||||
#[Route('/{_locale}/person/accompanying-course-work-evaluation-document/{id}/duplicate', name: 'chill_person_accompanying_period_work_evaluation_document_duplicate', methods: ['POST'])]
|
||||
public function duplicate(AccompanyingPeriodWorkEvaluationDocument $document): Response
|
||||
{
|
||||
|
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
|
||||
readonly class AccompanyingPeriodWorkEvaluationDocumentMoveController
|
||||
{
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private SerializerInterface $serializer,
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @ParamConverter("document", options={"id": "document_id"})
|
||||
* @ParamConverter("evaluation", options={"id": "evaluation_id"})
|
||||
*/
|
||||
#[Route('/api/1.0/person/accompanying-course-work-evaluation-document/{document_id}/evaluation/{evaluation_id}/move', methods: ['POST'])]
|
||||
public function moveToEvaluationApi(AccompanyingPeriodWorkEvaluationDocument $document, AccompanyingPeriodWorkEvaluation $evaluation): Response
|
||||
{
|
||||
$work = $evaluation->getAccompanyingPeriodWork();
|
||||
|
||||
if (!$this->security->isGranted(AccompanyingPeriodWorkVoter::UPDATE, $work)) {
|
||||
throw new AccessDeniedHttpException('not allowed to edit this accompanying period work');
|
||||
}
|
||||
|
||||
$document->setAccompanyingPeriodWorkEvaluation($evaluation);
|
||||
|
||||
$this->entityManager->persist($document);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($document, 'json', [AbstractNormalizer::GROUPS => ['read']]),
|
||||
json: true
|
||||
);
|
||||
}
|
||||
}
|
@@ -17,7 +17,6 @@ use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Form\CreationPersonType;
|
||||
use Chill\PersonBundle\Form\PersonType;
|
||||
use Chill\PersonBundle\Privacy\PrivacyEvent;
|
||||
use Chill\PersonBundle\Repository\PersonRepository;
|
||||
use Chill\PersonBundle\Search\SimilarPersonMatcher;
|
||||
@@ -49,56 +48,6 @@ final class PersonController extends AbstractController
|
||||
private readonly EntityManagerInterface $em,
|
||||
) {}
|
||||
|
||||
#[Route(path: '/{_locale}/person/{person_id}/general/edit', name: 'chill_person_general_edit')]
|
||||
public function editAction(int $person_id, Request $request)
|
||||
{
|
||||
$person = $this->_getPerson($person_id);
|
||||
|
||||
if (null === $person) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted(
|
||||
'CHILL_PERSON_UPDATE',
|
||||
$person,
|
||||
'You are not allowed to edit this person'
|
||||
);
|
||||
|
||||
$form = $this->createForm(
|
||||
PersonType::class,
|
||||
$person,
|
||||
[
|
||||
'cFGroup' => $this->getCFGroup(),
|
||||
]
|
||||
);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && !$form->isValid()) {
|
||||
$this->get('session')
|
||||
->getFlashBag()->add('error', $this->translator
|
||||
->trans('This form contains errors'));
|
||||
} elseif ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->em->flush();
|
||||
|
||||
$this->get('session')->getFlashBag()
|
||||
->add(
|
||||
'success',
|
||||
$this->translator
|
||||
->trans('The person data has been updated')
|
||||
);
|
||||
|
||||
return $this->redirectToRoute('chill_person_view', [
|
||||
'person_id' => $person->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
'@ChillPerson/Person/edit.html.twig',
|
||||
['person' => $person, 'form' => $form->createView()]
|
||||
);
|
||||
}
|
||||
|
||||
public function getCFGroup()
|
||||
{
|
||||
$cFGroup = null;
|
||||
|
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\CustomFieldsBundle\EntityRepository\CustomFieldsDefaultGroupRepository;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Form\PersonType;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Translation\TranslatableMessage;
|
||||
use Twig\Environment;
|
||||
|
||||
final readonly class PersonEditController
|
||||
{
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private FormFactoryInterface $formFactory,
|
||||
private CustomFieldsDefaultGroupRepository $customFieldsDefaultGroupRepository,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private UrlGeneratorInterface $urlGenerator,
|
||||
private Environment $twig,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @ParamConverter("person", options={"id": "person_id"})
|
||||
*/
|
||||
#[Route(path: '/{_locale}/person/{person_id}/general/edit', name: 'chill_person_general_edit')]
|
||||
public function editAction(Person $person, Request $request, Session $session)
|
||||
{
|
||||
if (!$this->security->isGranted(PersonVoter::UPDATE, $person)) {
|
||||
throw new AccessDeniedHttpException('You are not allowed to edit this person.');
|
||||
}
|
||||
|
||||
$form = $this->formFactory->create(
|
||||
PersonType::class,
|
||||
$person,
|
||||
['cFGroup' => $this->customFieldsDefaultGroupRepository->findOneByEntity(Person::class)?->getCustomFieldsGroup()]
|
||||
);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && !$form->isValid()) {
|
||||
$session
|
||||
->getFlashBag()->add('error', new TranslatableMessage('This form contains errors'));
|
||||
} elseif ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->entityManager->flush();
|
||||
|
||||
$session->getFlashBag()->add('success', new TranslatableMessage('The person data has been updated'));
|
||||
|
||||
return new RedirectResponse(
|
||||
$this->urlGenerator->generate('chill_person_view', ['person_id' => $person->getId()])
|
||||
);
|
||||
}
|
||||
|
||||
return new Response($this->twig->render('@ChillPerson/Person/edit.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'person' => $person,
|
||||
]));
|
||||
}
|
||||
}
|
@@ -110,6 +110,24 @@ class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
->end() // children for 'person_fields', parent = array 'person_fields'
|
||||
->end() // person_fields, parent = children of root
|
||||
->arrayNode('person_render')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->scalarNode('id_content_text')
|
||||
->defaultValue('n°[[ person_id ]]')
|
||||
->info(
|
||||
<<<'EOF'
|
||||
The way we display the person's id. Variables availables: "[[ person_id ]]", or, for person's
|
||||
identifier: "[[ identifier_xx ]]" where xx is the identifier's definition's id.
|
||||
|
||||
There are also conditions available: "[[ if:identifier_yy ]] [[ identifier_yy ]] [[ endif:identifier_yy ]]"
|
||||
|
||||
Take care of keeping exactly one space between "[[" and the placeholder's content, and exactly one space before "]]"
|
||||
EOF
|
||||
)
|
||||
->end()
|
||||
->end() // end of person_render's children
|
||||
->end() // end of person_render
|
||||
->arrayNode('household_fields')
|
||||
->canBeDisabled()
|
||||
->children()
|
||||
|
@@ -98,16 +98,6 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct
|
||||
|
||||
public function setAccompanyingPeriodWorkEvaluation(?AccompanyingPeriodWorkEvaluation $accompanyingPeriodWorkEvaluation): AccompanyingPeriodWorkEvaluationDocument
|
||||
{
|
||||
// if an evaluation is already associated, we cannot change the association (removing the association,
|
||||
// by setting a null value, is allowed.
|
||||
if (
|
||||
$this->accompanyingPeriodWorkEvaluation instanceof AccompanyingPeriodWorkEvaluation
|
||||
&& $accompanyingPeriodWorkEvaluation instanceof AccompanyingPeriodWorkEvaluation
|
||||
) {
|
||||
if ($this->accompanyingPeriodWorkEvaluation !== $accompanyingPeriodWorkEvaluation) {
|
||||
throw new \RuntimeException('It is not allowed to change the evaluation for a document');
|
||||
}
|
||||
}
|
||||
$this->accompanyingPeriodWorkEvaluation = $accompanyingPeriodWorkEvaluation;
|
||||
|
||||
return $this;
|
||||
|
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Entity\Identifier;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'chill_person_identifier')]
|
||||
class PersonIdentifier
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
||||
#[ORM\GeneratedValue]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Person::class)]
|
||||
#[ORM\JoinColumn(name: 'person_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
|
||||
private ?Person $person = null;
|
||||
|
||||
#[ORM\Column(name: 'value', type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]', 'jsonb' => true])]
|
||||
private array $value = [];
|
||||
|
||||
#[ORM\Column(name: 'canonical', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '[]'])]
|
||||
private string $canonical = '';
|
||||
|
||||
public function __construct(
|
||||
#[ORM\ManyToOne(targetEntity: PersonIdentifierDefinition::class)]
|
||||
#[ORM\JoinColumn(name: 'definition_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
|
||||
private PersonIdentifierDefinition $definition,
|
||||
) {}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setPerson(?Person $person): self
|
||||
{
|
||||
$this->person = $person;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPerson(): Person
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
public function getValue(): array
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue(array $value): void
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getCanonical(): string
|
||||
{
|
||||
return $this->canonical;
|
||||
}
|
||||
|
||||
public function setCanonical(string $canonical): void
|
||||
{
|
||||
$this->canonical = $canonical;
|
||||
}
|
||||
|
||||
public function getDefinition(): PersonIdentifierDefinition
|
||||
{
|
||||
return $this->definition;
|
||||
}
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Entity\Identifier;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'chill_person_identifier_definition')]
|
||||
class PersonIdentifierDefinition
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
||||
#[ORM\GeneratedValue]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(name: 'active', type: \Doctrine\DBAL\Types\Types::BOOLEAN, nullable: false, options: ['default' => true])]
|
||||
private bool $active = true;
|
||||
|
||||
public function __construct(
|
||||
#[ORM\Column(name: 'label', type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])]
|
||||
private array $label,
|
||||
#[ORM\Column(name: 'engine', type: \Doctrine\DBAL\Types\Types::STRING, length: 100)]
|
||||
private string $engine,
|
||||
#[ORM\Column(name: 'is_searchable', type: \Doctrine\DBAL\Types\Types::BOOLEAN, options: ['default' => false])]
|
||||
private bool $isSearchable = false,
|
||||
#[ORM\Column(name: 'is_editable_by_users', type: \Doctrine\DBAL\Types\Types::BOOLEAN, nullable: false, options: ['default' => false])]
|
||||
private bool $isEditableByUsers = false,
|
||||
#[ORM\Column(name: 'data', type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]', 'jsonb' => true])]
|
||||
private array $data = [],
|
||||
) {}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): array
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(array $label): void
|
||||
{
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
public function getEngine(): string
|
||||
{
|
||||
return $this->engine;
|
||||
}
|
||||
|
||||
public function setEngine(string $engine): void
|
||||
{
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
public function isSearchable(): bool
|
||||
{
|
||||
return $this->isSearchable;
|
||||
}
|
||||
|
||||
public function setIsSearchable(bool $isSearchable): void
|
||||
{
|
||||
$this->isSearchable = $isSearchable;
|
||||
}
|
||||
|
||||
public function isEditableByUsers(): bool
|
||||
{
|
||||
return $this->isEditableByUsers;
|
||||
}
|
||||
|
||||
public function setIsEditableByUsers(bool $isEditableByUsers): void
|
||||
{
|
||||
$this->isEditableByUsers = $isEditableByUsers;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function setActive(bool $active): self
|
||||
{
|
||||
$this->active = $active;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setData(array $data): void
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -31,6 +31,7 @@ use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
|
||||
use Chill\PersonBundle\Entity\Household\Household;
|
||||
use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterCurrent;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCurrentAddress;
|
||||
@@ -271,6 +272,9 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'person', targetEntity: PersonIdentifier::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
|
||||
private Collection $identifiers;
|
||||
|
||||
/**
|
||||
* The person's last name.
|
||||
*/
|
||||
@@ -418,6 +422,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
$this->resources = new ArrayCollection();
|
||||
$this->centerHistory = new ArrayCollection();
|
||||
$this->signatures = new ArrayCollection();
|
||||
$this->identifiers = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
@@ -498,6 +503,24 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addIdentifier(PersonIdentifier $identifier): self
|
||||
{
|
||||
if (!$this->identifiers->contains($identifier)) {
|
||||
$this->identifiers[] = $identifier;
|
||||
$identifier->setPerson($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeIdentifier(PersonIdentifier $identifier): self
|
||||
{
|
||||
$this->identifiers->removeElement($identifier);
|
||||
$identifier->setPerson(null);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeSignature(EntityWorkflowStepSignature $signature): self
|
||||
{
|
||||
$this->signatures->removeElement($signature);
|
||||
@@ -1129,6 +1152,14 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReadableCollection<int, PersonIdentifier>
|
||||
*/
|
||||
public function getIdentifiers(): ReadableCollection
|
||||
{
|
||||
return $this->identifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -1262,6 +1293,22 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this->spokenLanguages;
|
||||
}
|
||||
|
||||
public function addSpokenLanguage(Language $language): self
|
||||
{
|
||||
if (!$this->spokenLanguages->contains($language)) {
|
||||
$this->spokenLanguages->add($language);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeSpokenLanguage(Language $language): self
|
||||
{
|
||||
$this->spokenLanguages->removeElement($language);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->updatedAt;
|
||||
|
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Form\DataMapper;
|
||||
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||
use Chill\PersonBundle\PersonIdentifier\Exception\UnexpectedTypeException;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
|
||||
use Chill\PersonBundle\Repository\Identifier\PersonIdentifierDefinitionRepository;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
final readonly class PersonIdentifiersDataMapper implements DataMapperInterface
|
||||
{
|
||||
public function __construct(
|
||||
private PersonIdentifierManagerInterface $identifierManager,
|
||||
private PersonIdentifierDefinitionRepository $identifierDefinitionRepository,
|
||||
) {}
|
||||
|
||||
public function mapDataToForms($viewData, \Traversable $forms): void
|
||||
{
|
||||
if (!$viewData instanceof Collection) {
|
||||
throw new UnexpectedTypeException($viewData, Collection::class);
|
||||
}
|
||||
/** @var array<string, FormInterface> $formsByKey */
|
||||
$formsByKey = iterator_to_array($forms);
|
||||
|
||||
foreach ($this->identifierManager->getWorkers() as $worker) {
|
||||
if (!$worker->getDefinition()->isEditableByUsers()) {
|
||||
continue;
|
||||
}
|
||||
$form = $formsByKey['identifier_'.$worker->getDefinition()->getId()];
|
||||
$identifier = $viewData->findFirst(fn (int $key, PersonIdentifier $identifier) => $worker->getDefinition()->getId() === $identifier->getId());
|
||||
if (null === $identifier) {
|
||||
$identifier = new PersonIdentifier($worker->getDefinition());
|
||||
}
|
||||
$form->setData($identifier->getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public function mapFormsToData(\Traversable $forms, &$viewData): void
|
||||
{
|
||||
if (!$viewData instanceof Collection) {
|
||||
throw new UnexpectedTypeException($viewData, Collection::class);
|
||||
}
|
||||
|
||||
foreach ($forms as $name => $form) {
|
||||
$identifierId = (int) substr((string) $name, 11);
|
||||
$identifier = $viewData->findFirst(fn (int $key, PersonIdentifier $identifier) => $identifier->getId() === $identifierId);
|
||||
$definition = $this->identifierDefinitionRepository->find($identifierId);
|
||||
if (null === $identifier) {
|
||||
$identifier = new PersonIdentifier($definition);
|
||||
$viewData->add($identifier);
|
||||
}
|
||||
if (!$identifier->getDefinition()->isEditableByUsers()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$worker = $this->identifierManager->buildWorkerByPersonIdentifierDefinition($definition);
|
||||
$identifier->setValue($form->getData());
|
||||
$identifier->setCanonical($worker->canonicalizeValue($identifier->getValue()));
|
||||
}
|
||||
}
|
||||
}
|
@@ -24,7 +24,7 @@ class FindAccompanyingPeriodWorkType extends AbstractType
|
||||
{
|
||||
$builder
|
||||
->add('acpw', PickLinkedAccompanyingPeriodWorkType::class, [
|
||||
'label' => 'Social action',
|
||||
'label' => 'Accompanying period work',
|
||||
'multiple' => false,
|
||||
'accompanyingPeriod' => $options['accompanyingPeriod'],
|
||||
])
|
||||
|
48
src/Bundle/ChillPersonBundle/Form/PersonIdentifiersType.php
Normal file
48
src/Bundle/ChillPersonBundle/Form/PersonIdentifiersType.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Form\DataMapper\PersonIdentifiersDataMapper;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final class PersonIdentifiersType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private readonly PersonIdentifierManagerInterface $identifierManager,
|
||||
private readonly PersonIdentifiersDataMapper $identifiersDataMapper,
|
||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
foreach ($this->identifierManager->getWorkers() as $worker) {
|
||||
if (!$worker->getDefinition()->isEditableByUsers()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$subBuilder = $builder->create(
|
||||
'identifier_'.$worker->getDefinition()->getId(),
|
||||
options: [
|
||||
'compound' => true,
|
||||
'label' => $this->translatableStringHelper->localize($worker->getDefinition()->getLabel()),
|
||||
]
|
||||
);
|
||||
$worker->buildForm($subBuilder);
|
||||
$builder->add($subBuilder);
|
||||
}
|
||||
|
||||
$builder->setDataMapper($this->identifiersDataMapper);
|
||||
}
|
||||
}
|
@@ -72,8 +72,8 @@ class PersonType extends AbstractType
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('firstName')
|
||||
->add('lastName')
|
||||
->add('firstName', TextType::class, ['empty_data' => ''])
|
||||
->add('lastName', TextType::class, ['empty_data' => ''])
|
||||
->add('birthdate', ChillDateType::class, [
|
||||
'required' => false,
|
||||
])
|
||||
@@ -101,7 +101,7 @@ class PersonType extends AbstractType
|
||||
|
||||
if ('visible' === $this->config['memo']) {
|
||||
$builder
|
||||
->add('memo', ChillTextareaType::class, ['required' => false]);
|
||||
->add('memo', ChillTextareaType::class, ['required' => false, 'empty_data' => '']);
|
||||
}
|
||||
|
||||
if ('visible' === $this->config['employment_status']) {
|
||||
@@ -118,6 +118,7 @@ class PersonType extends AbstractType
|
||||
$builder->add('placeOfBirth', TextType::class, [
|
||||
'required' => false,
|
||||
'attr' => ['style' => 'text-transform: uppercase;'],
|
||||
'empty_data' => '',
|
||||
]);
|
||||
|
||||
$builder->get('placeOfBirth')->addModelTransformer(new CallbackTransformer(
|
||||
@@ -127,7 +128,9 @@ class PersonType extends AbstractType
|
||||
}
|
||||
|
||||
if ('visible' === $this->config['contact_info']) {
|
||||
$builder->add('contactInfo', ChillTextareaType::class, ['required' => false]);
|
||||
$builder->add('contactInfo', ChillTextareaType::class, [
|
||||
'required' => false, 'empty_data' => '', 'label' => 'Notes on contact information',
|
||||
]);
|
||||
}
|
||||
|
||||
if ('visible' === $this->config['phonenumber']) {
|
||||
@@ -152,12 +155,12 @@ class PersonType extends AbstractType
|
||||
'required' => false,
|
||||
]
|
||||
)
|
||||
->add('acceptSMS', CheckboxType::class, [
|
||||
->add('acceptSms', CheckboxType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
$builder->add('otherPhoneNumbers', ChillCollectionType::class, [
|
||||
$builder->add('otherPhonenumbers', ChillCollectionType::class, [
|
||||
'entry_type' => PersonPhoneType::class,
|
||||
'button_add_label' => 'Add new phone',
|
||||
'button_remove_label' => 'Remove phone',
|
||||
@@ -173,12 +176,12 @@ class PersonType extends AbstractType
|
||||
|
||||
if ('visible' === $this->config['email']) {
|
||||
$builder
|
||||
->add('email', EmailType::class, ['required' => false]);
|
||||
->add('email', EmailType::class, ['required' => false, 'empty_data' => '']);
|
||||
}
|
||||
|
||||
if ('visible' === $this->config['acceptEmail']) {
|
||||
$builder
|
||||
->add('acceptEmail', CheckboxType::class, ['required' => false]);
|
||||
->add('acceptEmail', CheckboxType::class, ['required' => false, 'empty_data' => '']);
|
||||
}
|
||||
|
||||
if ('visible' === $this->config['country_of_birth']) {
|
||||
@@ -222,6 +225,10 @@ class PersonType extends AbstractType
|
||||
]);
|
||||
}
|
||||
|
||||
$builder->add('identifiers', PersonIdentifiersType::class, [
|
||||
'by_reference' => false,
|
||||
]);
|
||||
|
||||
if ($options['cFGroup']) {
|
||||
$builder
|
||||
->add(
|
||||
@@ -232,10 +239,7 @@ class PersonType extends AbstractType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OptionsResolverInterface $resolver
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Person::class,
|
||||
@@ -251,10 +255,7 @@ class PersonType extends AbstractType
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'chill_personbundle_person';
|
||||
}
|
||||
|
@@ -16,18 +16,26 @@ use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
class PickLinkedAccompanyingPeriodWorkType extends AbstractType
|
||||
{
|
||||
public function __construct(private readonly NormalizerInterface $normalizer) {}
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars['multiple'] = $options['multiple'];
|
||||
$view->vars['types'] = ['acpw'];
|
||||
$view->vars['uniqid'] = uniqid('pick_acpw_dyn');
|
||||
$view->vars['suggested'] = [];
|
||||
$view->vars['as_id'] = true === $options['as_id'] ? '1' : '0';
|
||||
$view->vars['submit_on_adding_new_entity'] = false;
|
||||
$view->vars['pick-entities-type'] = 'acpw';
|
||||
$view->vars['attr']['data-accompanying-period-id'] = $options['accompanyingPeriod']->getId();
|
||||
|
||||
foreach ($options['suggested'] as $suggestion) {
|
||||
$view->vars['suggested'][] = $this->normalizer->normalize($suggestion, 'json', ['groups' => 'read']);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
@@ -38,6 +46,7 @@ class PickLinkedAccompanyingPeriodWorkType extends AbstractType
|
||||
->setDefault('multiple', false)
|
||||
->setAllowedTypes('multiple', ['bool'])
|
||||
->setDefault('compound', false)
|
||||
->setDefault('suggested', [])
|
||||
->setDefault('as_id', false)
|
||||
->setAllowedTypes('as_id', ['bool'])
|
||||
->setDefault('submit_on_adding_new_entity', false)
|
||||
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier\Exception;
|
||||
|
||||
class EngineNotFoundException extends \RuntimeException
|
||||
{
|
||||
public function __construct(string $name)
|
||||
{
|
||||
parent::__construct("Engine for EngineInterface not found: {$name}");
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier\Exception;
|
||||
|
||||
class PersonIdentifierDefinitionNotFoundException extends \RuntimeException
|
||||
{
|
||||
public function __construct(int $id, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct("Person identifier definition not found by his id: {$id}", previous: $previous);
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier\Exception;
|
||||
|
||||
class UnexpectedTypeException extends \InvalidArgumentException
|
||||
{
|
||||
public function __construct(mixed $value, string $expectedType)
|
||||
{
|
||||
parent::__construct(\sprintf('Expected argument of type "%s", "%s" given', $expectedType, get_debug_type($value)));
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier\Identifier;
|
||||
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierEngineInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final readonly class StringIdentifier implements PersonIdentifierEngineInterface
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'chill-person-bundle.string-identifier';
|
||||
}
|
||||
|
||||
public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string
|
||||
{
|
||||
return $value['content'] ?? '';
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, PersonIdentifierDefinition $personIdentifierDefinition): void
|
||||
{
|
||||
$builder->add('content', TextType::class, ['label' => false]);
|
||||
}
|
||||
|
||||
public function renderAsString(?PersonIdentifier $identifier, PersonIdentifierDefinition $definition): string
|
||||
{
|
||||
return $identifier?->getValue()['content'] ?? '';
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier;
|
||||
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
interface PersonIdentifierEngineInterface
|
||||
{
|
||||
public static function getName(): string;
|
||||
|
||||
public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string;
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, PersonIdentifierDefinition $personIdentifierDefinition): void;
|
||||
|
||||
public function renderAsString(?PersonIdentifier $identifier, PersonIdentifierDefinition $definition): string;
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier;
|
||||
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||
use Chill\PersonBundle\PersonIdentifier\Exception\EngineNotFoundException;
|
||||
use Chill\PersonBundle\Repository\Identifier\PersonIdentifierDefinitionRepository;
|
||||
|
||||
final readonly class PersonIdentifierManager implements PersonIdentifierManagerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private iterable $engines,
|
||||
private PersonIdentifierDefinitionRepository $personIdentifierDefinitionRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Build PersonIdentifierWorker's for all active definition.
|
||||
*
|
||||
* @return list<PersonIdentifierWorker>
|
||||
*/
|
||||
public function getWorkers(): array
|
||||
{
|
||||
$workers = [];
|
||||
foreach ($this->personIdentifierDefinitionRepository->findByActive() as $definition) {
|
||||
try {
|
||||
$worker = $this->getEngine($definition->getEngine());
|
||||
} catch (EngineNotFoundException) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$workers[] = new PersonIdentifierWorker($worker, $definition);
|
||||
|
||||
}
|
||||
|
||||
return $workers;
|
||||
}
|
||||
|
||||
public function buildWorkerByPersonIdentifierDefinition(PersonIdentifierDefinition $personIdentifierDefinition): PersonIdentifierWorker
|
||||
{
|
||||
return new PersonIdentifierWorker($this->getEngine($personIdentifierDefinition->getEngine()), $personIdentifierDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throw EngineNotFoundException
|
||||
*/
|
||||
private function getEngine(string $name): PersonIdentifierEngineInterface
|
||||
{
|
||||
foreach ($this->engines as $engine) {
|
||||
if ($engine->getName() === $name) {
|
||||
return $engine;
|
||||
}
|
||||
}
|
||||
|
||||
throw new EngineNotFoundException($name);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier;
|
||||
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||
|
||||
interface PersonIdentifierManagerInterface
|
||||
{
|
||||
/**
|
||||
* Build PersonIdentifierWorker's for all active definition.
|
||||
*
|
||||
* @return list<PersonIdentifierWorker>
|
||||
*/
|
||||
public function getWorkers(): array;
|
||||
|
||||
public function buildWorkerByPersonIdentifierDefinition(PersonIdentifierDefinition $personIdentifierDefinition): PersonIdentifierWorker;
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier;
|
||||
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final readonly class PersonIdentifierWorker
|
||||
{
|
||||
public function __construct(
|
||||
private PersonIdentifierEngineInterface $identifierEngine,
|
||||
private PersonIdentifierDefinition $definition,
|
||||
) {}
|
||||
|
||||
public function getIdentifierEngine(): PersonIdentifierEngineInterface
|
||||
{
|
||||
return $this->identifierEngine;
|
||||
}
|
||||
|
||||
public function getDefinition(): PersonIdentifierDefinition
|
||||
{
|
||||
return $this->definition;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder): void
|
||||
{
|
||||
$this->identifierEngine->buildForm($builder, $this->definition);
|
||||
}
|
||||
|
||||
public function canonicalizeValue(array $value): ?string
|
||||
{
|
||||
return $this->identifierEngine->canonicalizeValue($value, $this->definition);
|
||||
}
|
||||
|
||||
public function renderAsString(?PersonIdentifier $identifier): string
|
||||
{
|
||||
return $this->identifierEngine->renderAsString($identifier, $this->definition);
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier\Rendering;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
final readonly class PersonIdRendering implements PersonIdRenderingInterface
|
||||
{
|
||||
private string $idContentText;
|
||||
|
||||
public function __construct(
|
||||
ParameterBagInterface $parameterBag,
|
||||
private PersonIdentifierManagerInterface $personIdentifierManager,
|
||||
) {
|
||||
$this->idContentText = $parameterBag->get('chill_person')['person_render']['id_content_text'];
|
||||
}
|
||||
|
||||
public function renderPersonId(Person $person): string
|
||||
{
|
||||
$args = [
|
||||
'[[ person_id ]]' => $person->getId(),
|
||||
];
|
||||
|
||||
foreach ($person->getIdentifiers() as $identifier) {
|
||||
if (!$identifier->getDefinition()->isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = 'identifier_'.$identifier->getDefinition()->getId();
|
||||
|
||||
$args
|
||||
+= [
|
||||
"[[ {$key} ]]" => $this->personIdentifierManager->buildWorkerByPersonIdentifierDefinition($identifier->getDefinition())
|
||||
->renderAsString($identifier),
|
||||
"[[ if:{$key} ]]" => '',
|
||||
"[[ endif:{$key} ]]" => '',
|
||||
];
|
||||
// we remove the eventual conditions
|
||||
|
||||
|
||||
}
|
||||
|
||||
$rendered = strtr($this->idContentText, $args);
|
||||
|
||||
// Delete the conditions which are not met, for instance:
|
||||
// [[ if:identifier_99 ]] ... [[ endif:identifier_99 ]]
|
||||
// this match the same dumber for opening and closing of the condition
|
||||
return preg_replace(
|
||||
'/\[\[\s*if:identifier_(\d+)\s*\]\].*?\[\[\s*endif:identifier_\1\s*\]\]/s',
|
||||
'',
|
||||
$rendered
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier\Rendering;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
|
||||
interface PersonIdRenderingInterface
|
||||
{
|
||||
public function renderPersonId(Person $person): string;
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier\Rendering;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
final class PersonIdRenderingTwigExtension extends AbstractExtension
|
||||
{
|
||||
public function __construct(private readonly PersonIdRenderingInterface $personIdRendering) {}
|
||||
|
||||
public function getFilters(): array
|
||||
{
|
||||
return [
|
||||
new TwigFilter(
|
||||
'chill_person_id_render_text',
|
||||
fn (Person $person): string => $this->personIdRendering->renderPersonId($person)
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\PersonIdentifier\Rendering;
|
||||
|
||||
use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
|
||||
|
||||
/**
|
||||
* @template-implements ChillEntityRenderInterface<PersonIdentifier>
|
||||
*/
|
||||
final readonly class PersonIdentifierEntityRender implements ChillEntityRenderInterface
|
||||
{
|
||||
public function __construct(private PersonIdentifierManagerInterface $identifierManager) {}
|
||||
|
||||
public function renderBox(mixed $entity, array $options): string
|
||||
{
|
||||
return $this->renderString($entity, $options);
|
||||
}
|
||||
|
||||
public function renderString(mixed $entity, array $options): string
|
||||
{
|
||||
$worker = $this->identifierManager->buildWorkerByPersonIdentifierDefinition($entity->getDefinition());
|
||||
|
||||
return $worker->renderAsString($entity);
|
||||
}
|
||||
|
||||
public function supports(object $entity, array $options): bool
|
||||
{
|
||||
return $entity instanceof PersonIdentifier;
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Repository\Identifier;
|
||||
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @template-extends ServiceEntityRepository<PersonIdentifierDefinition>
|
||||
*/
|
||||
class PersonIdentifierDefinitionRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $managerRegistry)
|
||||
{
|
||||
parent::__construct($managerRegistry, PersonIdentifierDefinition::class);
|
||||
}
|
||||
|
||||
public function findByActive(): array
|
||||
{
|
||||
return $this->findBy(['active' => true]);
|
||||
}
|
||||
}
|
@@ -281,11 +281,6 @@ abbr.referrer { // still used ?
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.created-updated {
|
||||
border: 1px solid black;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/// Masonry blocs on AccompanyingCourse resume page
|
||||
div#dashboards {
|
||||
div.mbloc {
|
||||
|
@@ -41,7 +41,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
methods: {
|
||||
pickWork: function (payload: { work: AccompanyingPeriodWork }) {
|
||||
console.log("payload", payload);
|
||||
input.value = payload.work.id.toString();
|
||||
|
||||
input.value = payload.work.id?.toString() ?? "";
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@@ -84,7 +84,7 @@ export interface AccompanyingPeriodWorkEvaluationDocument {
|
||||
}
|
||||
|
||||
export interface AccompanyingPeriodWork {
|
||||
id: number;
|
||||
id?: number;
|
||||
accompanyingPeriod?: AccompanyingPeriod;
|
||||
accompanyingPeriodWorkEvaluations: AccompanyingPeriodWorkEvaluation[];
|
||||
createdAt?: string;
|
||||
|
@@ -972,7 +972,7 @@ div#workEditor {
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
i.fa {
|
||||
& > i.fa {
|
||||
padding: 0.25rem;
|
||||
color: $white;
|
||||
|
||||
|
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="row mb-3">
|
||||
<label class="col-sm-4 col-form-label visually-hidden">{{
|
||||
trans(EVALUATION_PUBLIC_COMMENT)
|
||||
}}</label>
|
||||
<div class="col-sm-12">
|
||||
<ckeditor
|
||||
:editor="ClassicEditor"
|
||||
:config="classicEditorConfig"
|
||||
:placeholder="trans(EVALUATION_COMMENT_PLACEHOLDER)"
|
||||
:value="comment"
|
||||
@input="$emit('update:comment', $event)"
|
||||
tag-name="textarea"
|
||||
></ckeditor>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
||||
import { ClassicEditor } from "ckeditor5";
|
||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
import {
|
||||
EVALUATION_PUBLIC_COMMENT,
|
||||
EVALUATION_COMMENT_PLACEHOLDER,
|
||||
trans,
|
||||
} from "translator";
|
||||
|
||||
defineProps(["comment"]);
|
||||
defineEmits(["update:comment"]);
|
||||
</script>
|
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div class="row mb-3">
|
||||
<label class="col-4 col-sm-2 col-md-4 col-lg-2 col-form-label">
|
||||
{{ trans(EVALUATION_STARTDATE) }}
|
||||
</label>
|
||||
<div class="col-8 col-sm-4 col-md-8 col-lg-4">
|
||||
<input
|
||||
class="form-control form-control-sm"
|
||||
type="date"
|
||||
:value="startDate"
|
||||
@input="$emit('update:startDate', $event.target.value)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label class="col-4 col-sm-2 col-md-4 col-lg-2 col-form-label">
|
||||
{{ trans(EVALUATION_ENDDATE) }}
|
||||
</label>
|
||||
<div class="col-8 col-sm-4 col-md-8 col-lg-4">
|
||||
<input
|
||||
class="form-control form-control-sm"
|
||||
type="date"
|
||||
:value="endDate"
|
||||
@input="$emit('update:endDate', $event.target.value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<label class="col-4 col-sm-2 col-md-4 col-lg-2 col-form-label">
|
||||
{{ trans(EVALUATION_MAXDATE) }}
|
||||
</label>
|
||||
<div class="col-8 col-sm-4 col-md-8 col-lg-4">
|
||||
<input
|
||||
class="form-control form-control-sm"
|
||||
type="date"
|
||||
:value="maxDate"
|
||||
@input="$emit('update:maxDate', $event.target.value)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label class="col-4 col-sm-2 col-md-4 col-lg-2 col-form-label">
|
||||
{{ trans(EVALUATION_WARNING_INTERVAL) }}
|
||||
</label>
|
||||
<div class="col-8 col-sm-4 col-md-8 col-lg-4">
|
||||
<input
|
||||
class="form-control form-control-sm"
|
||||
type="number"
|
||||
:value="warningInterval"
|
||||
@input="$emit('update:warningInterval', $event.target.value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
EVALUATION_STARTDATE,
|
||||
EVALUATION_ENDDATE,
|
||||
EVALUATION_MAXDATE,
|
||||
EVALUATION_WARNING_INTERVAL,
|
||||
trans,
|
||||
} from "translator";
|
||||
|
||||
defineProps(["startDate", "endDate", "maxDate", "warningInterval"]);
|
||||
defineEmits([
|
||||
"update:startDate",
|
||||
"update:endDate",
|
||||
"update:maxDate",
|
||||
"update:warningInterval",
|
||||
]);
|
||||
</script>
|
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="row mb-3">
|
||||
<h6>{{ trans(EVALUATION_DOCUMENT_ADD) }} :</h6>
|
||||
<pick-template
|
||||
entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation"
|
||||
:id="evaluation.id"
|
||||
:templates="templates"
|
||||
:preventDefaultMoveToGenerate="true"
|
||||
@go-to-generate-document="$emit('submitBeforeGenerate', $event)"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<label class="col-form-label">{{
|
||||
trans(EVALUATION_GENERATE_A_DOCUMENT)
|
||||
}}</label>
|
||||
</template>
|
||||
</pick-template>
|
||||
<div>
|
||||
<label class="col-form-label">{{
|
||||
trans(EVALUATION_DOCUMENT_UPLOAD)
|
||||
}}</label>
|
||||
<ul class="record_actions document-upload">
|
||||
<li>
|
||||
<drop-file-modal
|
||||
:allow-remove="false"
|
||||
@add-document="$emit('addDocument', $event)"
|
||||
></drop-file-modal>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import PickTemplate from "ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue";
|
||||
import DropFileModal from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileModal.vue";
|
||||
import {
|
||||
EVALUATION_DOCUMENT_ADD,
|
||||
EVALUATION_DOCUMENT_UPLOAD,
|
||||
EVALUATION_GENERATE_A_DOCUMENT,
|
||||
trans,
|
||||
} from "translator";
|
||||
|
||||
defineProps(["evaluation", "templates"]);
|
||||
defineEmits(["addDocument", "submitBeforeGenerate"]);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ul.document-upload {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,361 @@
|
||||
<template>
|
||||
<div class="row mb-3">
|
||||
<h5>{{ trans(EVALUATION_DOCUMENTS) }} :</h5>
|
||||
<div class="flex-table">
|
||||
<div
|
||||
class="item-bloc"
|
||||
v-for="(d, i) in documents"
|
||||
:key="d.id"
|
||||
:class="[
|
||||
parseInt(docAnchorId) === d.id ? 'bg-blink' : 'nothing',
|
||||
]"
|
||||
>
|
||||
<div :id="'document_' + d.id" class="item-row">
|
||||
<div class="input-group input-group-lg mb-3 row">
|
||||
<label class="col-sm-3 col-form-label"
|
||||
>Titre du document:</label
|
||||
>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
class="form-control document-title"
|
||||
type="text"
|
||||
:value="d.title"
|
||||
:id="d.id"
|
||||
:data-key="i"
|
||||
@input="$emit('inputDocumentTitle', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col item-meta">
|
||||
<p v-if="d.createdBy" class="createdBy">
|
||||
Créé par {{ d.createdBy.text }}<br />
|
||||
Le
|
||||
{{
|
||||
$d(ISOToDatetime(d.createdAt.datetime), "long")
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">
|
||||
<ul class="record_actions">
|
||||
<li
|
||||
v-if="
|
||||
d.workflows_availables.length > 0 ||
|
||||
d.workflows.length > 0
|
||||
"
|
||||
>
|
||||
<list-workflow-modal
|
||||
:workflows="d.workflows"
|
||||
:allowCreate="true"
|
||||
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument"
|
||||
:relatedEntityId="d.id"
|
||||
:workflowsAvailables="
|
||||
d.workflows_availables
|
||||
"
|
||||
:preventDefaultMoveToGenerate="true"
|
||||
:goToGenerateWorkflowPayload="{ doc: d }"
|
||||
@go-to-generate-workflow="
|
||||
$emit('goToGenerateWorkflow', $event)
|
||||
"
|
||||
></list-workflow-modal>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
v-if="AmIRefferer"
|
||||
class="btn btn-notify"
|
||||
@click="
|
||||
$emit(
|
||||
'goToGenerateNotification',
|
||||
d,
|
||||
false,
|
||||
)
|
||||
"
|
||||
></button>
|
||||
<template v-else>
|
||||
<button
|
||||
id="btnGroupNotifyButtons"
|
||||
type="button"
|
||||
class="btn btn-notify dropdown-toggle"
|
||||
:title="
|
||||
trans(EVALUATION_NOTIFICATION_SEND)
|
||||
"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu"
|
||||
aria-labelledby="btnGroupNotifyButtons"
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="
|
||||
$emit(
|
||||
'goToGenerateNotification',
|
||||
d,
|
||||
true,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{
|
||||
trans(
|
||||
EVALUATION_NOTIFICATION_NOTIFY_REFERRER,
|
||||
)
|
||||
}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="
|
||||
$emit(
|
||||
'goToGenerateNotification',
|
||||
d,
|
||||
false,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{
|
||||
trans(
|
||||
EVALUATION_NOTIFICATION_NOTIFY_ANY,
|
||||
)
|
||||
}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</li>
|
||||
<li>
|
||||
<document-action-buttons-group
|
||||
:stored-object="d.storedObject"
|
||||
:filename="d.title"
|
||||
:can-edit="true"
|
||||
:execute-before-leave="
|
||||
submitBeforeLeaveToEditor
|
||||
"
|
||||
:davLink="
|
||||
d.storedObject._links?.dav_link.href
|
||||
"
|
||||
:davLinkExpiration="
|
||||
d.storedObject._links?.dav_link
|
||||
.expiration
|
||||
"
|
||||
@on-stored-object-status-change="
|
||||
$emit('statusDocumentChanged', $event)
|
||||
"
|
||||
></document-action-buttons-group>
|
||||
</li>
|
||||
<li v-if="Number.isInteger(d.id)">
|
||||
<div class="duplicate-dropdown">
|
||||
<button
|
||||
class="btn btn-edit dropdown-toggle"
|
||||
type="button"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
{{ trans(EVALUATION_DOCUMENT_EDIT) }}
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<!--delete-->
|
||||
<li v-if="d.workflows.length === 0">
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="
|
||||
$emit('removeDocument', d)
|
||||
"
|
||||
>
|
||||
<i
|
||||
class="fa fa-trash-o"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{{
|
||||
trans(
|
||||
EVALUATION_DOCUMENT_DELETE,
|
||||
)
|
||||
}}
|
||||
</a>
|
||||
</li>
|
||||
<!--replace document-->
|
||||
<li
|
||||
v-if="
|
||||
d.storedObject._permissions
|
||||
.canEdit
|
||||
"
|
||||
>
|
||||
<drop-file-modal
|
||||
:existing-doc="d.storedObject"
|
||||
:allow-remove="false"
|
||||
@add-document="
|
||||
(arg) =>
|
||||
$emit(
|
||||
'replaceDocument',
|
||||
d,
|
||||
arg.stored_object,
|
||||
arg.stored_object_version,
|
||||
)
|
||||
"
|
||||
></drop-file-modal>
|
||||
</li>
|
||||
<!--duplicate document-->
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="
|
||||
$emit(
|
||||
'duplicateDocument',
|
||||
d,
|
||||
)
|
||||
"
|
||||
>
|
||||
<i
|
||||
class="fa fa-copy"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{{
|
||||
trans(
|
||||
EVALUATION_DOCUMENT_DUPLICATE_HERE,
|
||||
)
|
||||
}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="
|
||||
prepareDocumentDuplicationToWork(
|
||||
d,
|
||||
)
|
||||
"
|
||||
>
|
||||
<i
|
||||
class="fa fa-copy"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{{
|
||||
trans(
|
||||
EVALUATION_DOCUMENT_DUPLICATE_TO_OTHER_EVALUATION,
|
||||
)
|
||||
}}</a
|
||||
>
|
||||
</li>
|
||||
<!--move document-->
|
||||
<li
|
||||
v-if="
|
||||
d.storedObject._permissions
|
||||
.canEdit
|
||||
"
|
||||
>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="
|
||||
prepareDocumentMoveToWork(d)
|
||||
"
|
||||
>
|
||||
<i
|
||||
class="fa fa-arrows"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{{
|
||||
trans(
|
||||
EVALUATION_DOCUMENT_MOVE,
|
||||
)
|
||||
}}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AccompanyingPeriodWorkSelectorModal
|
||||
v-if="showAccompanyingPeriodSelector"
|
||||
v-model:selectedAcpw="selectedAcpw"
|
||||
:accompanying-period-id="accompanyingPeriodId"
|
||||
:is-evaluation-selector="true"
|
||||
:ignore-accompanying-period-work-ids="[]"
|
||||
@close-modal="showAccompanyingPeriodSelector = false"
|
||||
@update:selectedEvaluation="selectedEvaluation = $event"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ISOToDatetime } from "ChillMainAssets/chill/js/date";
|
||||
import ListWorkflowModal from "ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue";
|
||||
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
|
||||
import DropFileModal from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileModal.vue";
|
||||
import {
|
||||
EVALUATION_NOTIFICATION_NOTIFY_REFERRER,
|
||||
EVALUATION_NOTIFICATION_NOTIFY_ANY,
|
||||
EVALUATION_NOTIFICATION_SEND,
|
||||
EVALUATION_DOCUMENTS,
|
||||
EVALUATION_DOCUMENT_MOVE,
|
||||
EVALUATION_DOCUMENT_DELETE,
|
||||
EVALUATION_DOCUMENT_EDIT,
|
||||
EVALUATION_DOCUMENT_DUPLICATE_HERE,
|
||||
EVALUATION_DOCUMENT_DUPLICATE_TO_OTHER_EVALUATION,
|
||||
trans,
|
||||
} from "translator";
|
||||
import { ref, watch } from "vue";
|
||||
import AccompanyingPeriodWorkSelectorModal from "ChillPersonAssets/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue";
|
||||
|
||||
defineProps([
|
||||
"documents",
|
||||
"docAnchorId",
|
||||
"accompanyingPeriodId",
|
||||
"accompanyingPeriodWorkId",
|
||||
]);
|
||||
const emit = defineEmits([
|
||||
"inputDocumentTitle",
|
||||
"removeDocument",
|
||||
"duplicateDocument",
|
||||
"statusDocumentChanged",
|
||||
"goToGenerateWorkflow",
|
||||
"goToGenerateNotification",
|
||||
"duplicateDocumentToWork",
|
||||
]);
|
||||
|
||||
const showAccompanyingPeriodSelector = ref(false);
|
||||
const selectedEvaluation = ref(null);
|
||||
const selectedDocumentToDuplicate = ref(null);
|
||||
const selectedDocumentToMove = ref(null);
|
||||
|
||||
const prepareDocumentDuplicationToWork = (d) => {
|
||||
selectedDocumentToDuplicate.value = d;
|
||||
/** ensure selectedDocumentToMove is null */
|
||||
selectedDocumentToMove.value = null;
|
||||
|
||||
showAccompanyingPeriodSelector.value = true;
|
||||
};
|
||||
|
||||
const prepareDocumentMoveToWork = (d) => {
|
||||
selectedDocumentToMove.value = d;
|
||||
/** ensure selectedDocumentToDuplicate is null */
|
||||
selectedDocumentToDuplicate.value = null;
|
||||
|
||||
showAccompanyingPeriodSelector.value = true;
|
||||
};
|
||||
|
||||
watch(selectedEvaluation, (val) => {
|
||||
if (selectedDocumentToDuplicate.value) {
|
||||
emit("duplicateDocumentToEvaluation", {
|
||||
evaluation: val,
|
||||
document: selectedDocumentToDuplicate.value,
|
||||
});
|
||||
} else {
|
||||
emit("moveDocumentToEvaluation", {
|
||||
evaluationDest: val,
|
||||
document: selectedDocumentToMove.value,
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="row mb-3">
|
||||
<label class="col-4 col-sm-2 col-md-4 col-lg-2 col-form-label">
|
||||
{{ trans(EVALUATION_TIME_SPENT) }}
|
||||
</label>
|
||||
<div class="col-8 col-sm-4 col-md-8 col-lg-4">
|
||||
<select
|
||||
class="form-control form-control-sm"
|
||||
:value="timeSpent"
|
||||
@input="$emit('update:timeSpent', $event.target.value)"
|
||||
>
|
||||
<option disabled value="">
|
||||
{{ trans(EVALUATION_TIME_SPENT) }}
|
||||
</option>
|
||||
<option
|
||||
v-for="time in timeSpentChoices"
|
||||
:value="time.value"
|
||||
:key="time.value"
|
||||
>
|
||||
{{ time.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { EVALUATION_TIME_SPENT, trans } from "translator";
|
||||
|
||||
defineProps(["timeSpent", "timeSpentChoices"]);
|
||||
defineEmits(["update:timeSpent"]);
|
||||
</script>
|
@@ -11,12 +11,13 @@ import { findSocialActionsBySocialIssue } from "ChillPersonAssets/vuejs/_api/Soc
|
||||
import { create } from "ChillPersonAssets/vuejs/_api/AccompanyingCourseWork.js";
|
||||
import { fetchResults, makeFetch } from "ChillMainAssets/lib/api/apiMethods.ts";
|
||||
import { fetchTemplates } from "ChillDocGeneratorAssets/api/pickTemplate.js";
|
||||
import { duplicate } from "../_api/accompanyingCourseWorkEvaluationDocument";
|
||||
import {
|
||||
duplicate,
|
||||
duplicateDocumentToEvaluation,
|
||||
moveDocumentToEvaluation,
|
||||
} from "../_api/accompanyingCourseWorkEvaluationDocument";
|
||||
|
||||
const debug = process.env.NODE_ENV !== "production";
|
||||
const evalFQDN = encodeURIComponent(
|
||||
"Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluation",
|
||||
);
|
||||
|
||||
const store = createStore({
|
||||
strict: debug,
|
||||
@@ -298,15 +299,47 @@ const store = createStore({
|
||||
);
|
||||
},
|
||||
addDuplicatedDocument(state, { document, evaluation_key }) {
|
||||
console.log("add duplicated document", document);
|
||||
console.log("add duplicated dcuemnt - evaluation key", evaluation_key);
|
||||
|
||||
let evaluation = state.evaluationsPicked.find(
|
||||
(e) => e.key === evaluation_key,
|
||||
);
|
||||
document.key = evaluation.documents.length + 1;
|
||||
evaluation.documents.splice(0, 0, document);
|
||||
},
|
||||
addDuplicatedDocumentToEvaluation(state, { document, evaluation }) {
|
||||
let evaluationDest = state.evaluationsPicked.find(
|
||||
(e) => e.id === evaluation.id,
|
||||
);
|
||||
if (evaluationDest) {
|
||||
console.log("add duplicated document to evaluation", evaluationDest);
|
||||
document.key = evaluationDest.documents.length + 1;
|
||||
evaluationDest.documents.splice(0, 0, document);
|
||||
}
|
||||
},
|
||||
moveDocumentToEvaluation(
|
||||
state,
|
||||
{ evaluationInitial, evaluationDest, document },
|
||||
) {
|
||||
let evaluationA = state.evaluationsPicked.find(
|
||||
(e) => e.id === evaluationInitial.id,
|
||||
);
|
||||
let evaluationB = state.evaluationsPicked.find(
|
||||
(e) => e.id === evaluationDest.id,
|
||||
);
|
||||
|
||||
if (evaluationB) {
|
||||
// add document to chosen evaluation if evaluation is part of the same social work
|
||||
document.key = evaluationB.documents.length + 1;
|
||||
evaluationB.documents.splice(0, 0, document);
|
||||
}
|
||||
|
||||
// remove document from original evaluation
|
||||
const indexToRemove = evaluationA.documents.findIndex(
|
||||
(doc) => doc.id === document.id,
|
||||
);
|
||||
if (indexToRemove !== -1) {
|
||||
evaluationA.documents.splice(indexToRemove, 1);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Replaces a document in the state with a new document.
|
||||
*
|
||||
@@ -603,6 +636,44 @@ const store = createStore({
|
||||
const newDoc = await duplicate(document.id);
|
||||
commit("addDuplicatedDocument", { document: newDoc, evaluation_key });
|
||||
},
|
||||
async duplicateDocumentToEvaluation({ commit }, { document, evaluation }) {
|
||||
try {
|
||||
const newDoc = await duplicateDocumentToEvaluation(
|
||||
document.id,
|
||||
evaluation.id,
|
||||
);
|
||||
commit("addDuplicatedDocumentToEvaluation", {
|
||||
document: newDoc,
|
||||
evaluation,
|
||||
});
|
||||
|
||||
return newDoc;
|
||||
} catch (error) {
|
||||
console.error("Failed to move document:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
async moveDocumentToEvaluation(
|
||||
{ commit },
|
||||
{ evaluationInitial, evaluationDest, document },
|
||||
) {
|
||||
try {
|
||||
const response = await moveDocumentToEvaluation(
|
||||
document.id,
|
||||
evaluationDest.id,
|
||||
);
|
||||
commit("moveDocumentToEvaluation", {
|
||||
evaluationInitial,
|
||||
evaluationDest,
|
||||
document,
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Failed to move document:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
removeDocument({ commit }, payload) {
|
||||
commit("removeDocument", payload);
|
||||
},
|
||||
|
@@ -9,3 +9,23 @@ export const duplicate = async (
|
||||
`/api/1.0/person/accompanying-course-work-evaluation-document/${id}/duplicate`,
|
||||
);
|
||||
};
|
||||
|
||||
export const duplicateDocumentToEvaluation = async (
|
||||
document_id: number,
|
||||
evaluation_id: number,
|
||||
): Promise<AccompanyingPeriodWorkEvaluationDocument> => {
|
||||
return makeFetch<null, AccompanyingPeriodWorkEvaluationDocument>(
|
||||
"POST",
|
||||
`/api/1.0/person/accompanying-course-work-evaluation-document/${document_id}/evaluation/${evaluation_id}/duplicate`,
|
||||
);
|
||||
};
|
||||
|
||||
export const moveDocumentToEvaluation = async (
|
||||
document_id: number,
|
||||
evaluation_id: number,
|
||||
): Promise<AccompanyingPeriodWorkEvaluationDocument> => {
|
||||
return makeFetch<null, AccompanyingPeriodWorkEvaluationDocument>(
|
||||
"POST",
|
||||
`/api/1.0/person/accompanying-course-work-evaluation-document/${document_id}/evaluation/${evaluation_id}/move`,
|
||||
);
|
||||
};
|
||||
|
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<h2 class="badge-title">
|
||||
<span class="title_label"></span>
|
||||
<span class="title_action">
|
||||
<span>
|
||||
{{ trans(EVALUATION) }}:
|
||||
<span class="badge bg-light text-dark">
|
||||
{{ eval?.evaluation?.title.fr }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<ul class="small_in_title columns mt-1">
|
||||
<li>
|
||||
<span class="item-key">
|
||||
{{
|
||||
trans(
|
||||
ACCOMPANYING_COURSE_WORK_START_DATE,
|
||||
)
|
||||
}}
|
||||
:
|
||||
</span>
|
||||
<b>{{ formatDate(eval.startDate) }}</b>
|
||||
</li>
|
||||
|
||||
<li v-if="eval.endDate">
|
||||
<span class="item-key">
|
||||
{{
|
||||
trans(ACCOMPANYING_COURSE_WORK_END_DATE)
|
||||
}}
|
||||
:
|
||||
</span>
|
||||
<b>{{ formatDate(eval.endDate) }}</b>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ACCOMPANYING_COURSE_WORK_END_DATE,
|
||||
ACCOMPANYING_COURSE_WORK_START_DATE,
|
||||
EVALUATION,
|
||||
trans,
|
||||
} from "translator";
|
||||
import { ISOToDate } from "ChillMainAssets/chill/js/date";
|
||||
import { DateTime } from "ChillMainAssets/types";
|
||||
import { AccompanyingPeriodWorkEvaluation } from "../../../types";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const props = defineProps<{ eval: AccompanyingPeriodWorkEvaluation }>();
|
||||
const formatDate = (dateObject: DateTime) => {
|
||||
if (dateObject) {
|
||||
const parsedDate = ISOToDate(dateObject.datetime);
|
||||
if (parsedDate) {
|
||||
return new Intl.DateTimeFormat("default", {
|
||||
dateStyle: "short",
|
||||
}).format(parsedDate);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="results">
|
||||
<div
|
||||
v-for="evaluation in evaluations"
|
||||
:key="evaluation.id"
|
||||
class="list-item"
|
||||
>
|
||||
<label class="acpw-item">
|
||||
<div>
|
||||
<input
|
||||
type="radio"
|
||||
:value="evaluation"
|
||||
v-model="selectedEvaluation"
|
||||
name="item"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<accompanying-period-work-evaluation-item :eval="evaluation" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { AccompanyingPeriodWorkEvaluation } from "../../../types";
|
||||
import { defineProps, ref, watch } from "vue";
|
||||
import AccompanyingPeriodWorkEvaluationItem from "ChillPersonAssets/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkEvaluationItem.vue";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const props = defineProps<{
|
||||
evaluations: AccompanyingPeriodWorkEvaluation[];
|
||||
}>();
|
||||
const selectedEvaluation = ref<AccompanyingPeriodWorkEvaluation | null>(null);
|
||||
|
||||
// eslint-disable-next-line vue/valid-define-emits
|
||||
const emit = defineEmits();
|
||||
|
||||
watch(selectedEvaluation, (newValue) => {
|
||||
emit("update:selectedEvaluation", newValue);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.acpw-item {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
@@ -26,14 +26,24 @@ import AccompanyingPeriodWorkItem from "./AccompanyingPeriodWorkItem.vue";
|
||||
import { AccompanyingPeriodWork } from "../../../types";
|
||||
import { defineProps, ref, watch } from "vue";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const props = defineProps<{
|
||||
accompanyingPeriodWorks: AccompanyingPeriodWork[];
|
||||
selectedAcpw?: AccompanyingPeriodWork | null;
|
||||
}>();
|
||||
const selectedAcpw = ref<AccompanyingPeriodWork | null>(null);
|
||||
const selectedAcpw = ref<AccompanyingPeriodWork | null>(
|
||||
props.selectedAcpw ?? null,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line vue/valid-define-emits
|
||||
const emit = defineEmits();
|
||||
const emit = defineEmits<{
|
||||
"update:selectedAcpw": [value: AccompanyingPeriodWork | null];
|
||||
}>();
|
||||
|
||||
watch(
|
||||
() => props.selectedAcpw,
|
||||
(val) => {
|
||||
selectedAcpw.value = val ?? null;
|
||||
},
|
||||
);
|
||||
|
||||
watch(selectedAcpw, (newValue) => {
|
||||
emit("update:selectedAcpw", newValue);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="row justify-content-end">
|
||||
<div class="row justify-content-end" v-if="!isEvaluationSelector">
|
||||
<div class="col-md-6 col-sm-10" v-if="selectedAcpw">
|
||||
<ul class="list-suggest remove-items">
|
||||
<li>
|
||||
@@ -14,7 +14,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="record_actions">
|
||||
<ul v-if="!showModal" class="record_actions">
|
||||
<li>
|
||||
<a class="btn btn-sm btn-create mt-3" @click="openModal">
|
||||
{{ trans(ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK) }}
|
||||
@@ -40,9 +40,15 @@
|
||||
|
||||
<template #body>
|
||||
<accompanying-period-work-list
|
||||
v-if="evaluations.length === 0"
|
||||
:accompanying-period-works="accompanyingPeriodWorks"
|
||||
v-model:selectedAcpw="selectedAcpw"
|
||||
/>
|
||||
<accompanying-period-work-evaluation-list
|
||||
v-if="evaluations.length > 0"
|
||||
:evaluations="evaluations"
|
||||
v-model:selectedEvaluation="selectedEvaluation"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
@@ -60,58 +66,109 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import AccompanyingPeriodWorkList from "./AccompanyingPeriodWorkList.vue";
|
||||
import { AccompanyingPeriodWork } from "../../../types";
|
||||
import {
|
||||
trans,
|
||||
ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK,
|
||||
CONFIRM,
|
||||
trans,
|
||||
} from "translator";
|
||||
import { fetchResults } from "ChillMainAssets/lib/api/apiMethods";
|
||||
|
||||
interface AccompanyingPeriodWorkSelectorModalProps {
|
||||
accompanyingPeriodId: number;
|
||||
}
|
||||
import AccompanyingPeriodWorkEvaluationList from "ChillPersonAssets/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkEvaluationList.vue";
|
||||
import { AccompanyingPeriodWorkEvaluation } from "../../../types";
|
||||
|
||||
const selectedAcpw = ref<AccompanyingPeriodWork | null>(null);
|
||||
const selectedEvaluation = ref<AccompanyingPeriodWorkEvaluation | null>(null);
|
||||
const showModal = ref(false);
|
||||
const accompanyingPeriodWorks = ref<AccompanyingPeriodWork[]>([]);
|
||||
const props = defineProps<AccompanyingPeriodWorkSelectorModalProps>();
|
||||
const evaluations = ref<AccompanyingPeriodWorkEvaluation[]>([]);
|
||||
|
||||
const props = defineProps<{
|
||||
accompanyingPeriodId: string;
|
||||
isEvaluationSelector: boolean;
|
||||
ignoreAccompanyingPeriodWorkIds: number[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
pickWork: [payload: { work: AccompanyingPeriodWork | null }];
|
||||
closeModal: [];
|
||||
"update:selectedEvaluation": [evaluation: AccompanyingPeriodWorkEvaluation];
|
||||
}>();
|
||||
|
||||
onMounted(() => {
|
||||
if (props.accompanyingPeriodId) {
|
||||
getAccompanyingPeriodWorks(props.accompanyingPeriodId);
|
||||
getAccompanyingPeriodWorks(parseInt(props.accompanyingPeriodId));
|
||||
} else {
|
||||
console.error("No accompanyingperiod id was given");
|
||||
}
|
||||
|
||||
showModal.value = true;
|
||||
});
|
||||
const getAccompanyingPeriodWorks = async (periodId: number) => {
|
||||
const url = `/api/1.0/person/accompanying-course/${periodId}/works.json`;
|
||||
|
||||
try {
|
||||
accompanyingPeriodWorks.value = await fetchResults(url);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
const accompanyingPeriodWorksFetched =
|
||||
await fetchResults<AccompanyingPeriodWork>(url);
|
||||
if (props.isEvaluationSelector) {
|
||||
accompanyingPeriodWorks.value = accompanyingPeriodWorksFetched.filter(
|
||||
(acpw: AccompanyingPeriodWork) =>
|
||||
acpw.accompanyingPeriodWorkEvaluations.length > 0 &&
|
||||
typeof acpw.id !== "undefined" &&
|
||||
!props.ignoreAccompanyingPeriodWorkIds.includes(acpw.id),
|
||||
);
|
||||
} else {
|
||||
accompanyingPeriodWorks.value = accompanyingPeriodWorksFetched;
|
||||
}
|
||||
/* makeFetch<number, AccompanyingPeriodWork[]>("GET", url)
|
||||
.then((response) => {
|
||||
accompanyingPeriodWorks.value = response;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});*/
|
||||
};
|
||||
|
||||
const openModal = () => (showModal.value = true);
|
||||
const closeModal = () => (showModal.value = false);
|
||||
watch(selectedAcpw, (newValue) => {
|
||||
const inputField = document.getElementById(
|
||||
"find_accompanying_period_work_acpw",
|
||||
) as HTMLInputElement;
|
||||
if (inputField) {
|
||||
inputField.value = String(newValue?.id || "");
|
||||
}
|
||||
|
||||
/* if (!props.isEvaluationSelector) {
|
||||
console.log("Emitting from watch:", { work: newValue });
|
||||
emit("pickWork", { work: newValue });
|
||||
}*/
|
||||
});
|
||||
|
||||
const openModal = () => {
|
||||
showModal.value = true;
|
||||
};
|
||||
const closeModal = () => {
|
||||
showModal.value = false;
|
||||
selectedEvaluation.value = null;
|
||||
// selectedAcpw.value = null;
|
||||
emit("closeModal");
|
||||
};
|
||||
const confirmSelection = () => {
|
||||
emit("pickWork", { work: selectedAcpw.value });
|
||||
closeModal();
|
||||
selectedAcpw.value = selectedAcpw.value;
|
||||
console.log("selectedAcpw", selectedAcpw.value);
|
||||
|
||||
if (!props.isEvaluationSelector) {
|
||||
if (selectedAcpw.value) {
|
||||
// only emit if something is actually selected!
|
||||
emit("pickWork", { work: selectedAcpw.value });
|
||||
closeModal();
|
||||
}
|
||||
// optionally show some error or warning if not selected
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedAcpw.value && props.isEvaluationSelector) {
|
||||
evaluations.value =
|
||||
selectedAcpw.value.accompanyingPeriodWorkEvaluations;
|
||||
}
|
||||
|
||||
if (selectedEvaluation.value && props.isEvaluationSelector) {
|
||||
// console.log('evaluation log in modal', selectedEvaluation.value)
|
||||
emit("update:selectedEvaluation", selectedEvaluation.value);
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<h1>
|
||||
<i class="fa fa-random fa-fw"></i>
|
||||
{{ 'Accompanying Course'|trans }}
|
||||
<span class="id-number">{{ accompanyingCourse.id }}</span>
|
||||
<span class="id-number">({{ 'accompanying_period.number'|trans({ 'id': accompanyingCourse.id}) }})</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
{%- macro details(w, accompanyingCourse, options) -%}
|
||||
{% include '@ChillPerson/AccompanyingCourseWork/_item.html.twig' with {
|
||||
'displayAction': false,
|
||||
'displayAction': true,
|
||||
'displayContent': 'short',
|
||||
'displayFontSmall': true,
|
||||
'itemBlocClass': '',
|
||||
'displayNotification': false
|
||||
'displayNotification': true
|
||||
} %}
|
||||
{% endmacro %}
|
||||
|
@@ -2,10 +2,10 @@
|
||||
|
||||
{% set activeRouteKey = 'chill_person_accompanying_period_work_assign_duplicate' %}
|
||||
|
||||
{% block title %}{{ 'Assign an accompanying period work duplicate' }}{% endblock %}
|
||||
|
||||
{% import '@ChillPerson/AccompanyingPeriodWorkDuplicate/_details.html.twig' as details %}
|
||||
|
||||
{% block title %}{{ 'Assign an accompanying period work duplicate' }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="person-duplicate">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>{{ 'acpw_duplicate.Assign duplicate'|trans }}</h3>
|
||||
<h1>{{ 'acpw_duplicate.Assign duplicate'|trans }}</h1>
|
||||
{{ form_start(form) }}
|
||||
{%- if form.acpw is defined -%}
|
||||
{{ form_row(form.acpw) }}
|
||||
|
@@ -78,11 +78,6 @@
|
||||
{%- if options['addEntity'] -%}
|
||||
<span class="badge rounded-pill bg-secondary">{{ 'Person'|trans }}</span>
|
||||
{%- endif -%}
|
||||
{%- if options['addId'] -%}
|
||||
<span class="id-number" title="{{ 'Person'|trans ~ ' n° ' ~ person.id }}">
|
||||
{{ person.id|upper -}}
|
||||
</span>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
{%- if options['addInfo'] -%}
|
||||
<p class="moreinfo">
|
||||
@@ -99,6 +94,12 @@
|
||||
{%- if options['addAge'] -%}
|
||||
<span class="age"> {{ 'years_old'|trans({ 'age': person.age }) }}</span>
|
||||
{%- endif -%}
|
||||
{%- if options['addId'] -%}
|
||||
{%- set personId = person|chill_person_id_render_text %}
|
||||
<span class="id-number" title="{{ 'Person'|trans ~ ' ' ~ personId }}">
|
||||
({{ personId }})
|
||||
</span>
|
||||
{%- endif -%}
|
||||
{%- elseif person.birthdate is not null -%}
|
||||
<time datetime="{{ person.birthdate|date('Y-m-d') }}" title="{{ 'Birthdate'|trans }}">
|
||||
{{ 'Born the date'|trans({'gender': person.gender ? person.gender.genderTranslation.value : 'neutral',
|
||||
@@ -108,6 +109,12 @@
|
||||
<span class="age">{{- 'years_old'|trans({ 'age': person.age }) -}}</span>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- if options['addId'] -%}
|
||||
{%- set personId = person|chill_person_id_render_text %}
|
||||
<span class="id-number same-size" title="{{ 'Person'|trans ~ ' ' ~ personId }}">
|
||||
({{ personId }})
|
||||
</span>
|
||||
{%- endif -%}
|
||||
</p>
|
||||
{%- endif -%}
|
||||
{#- tricks to remove easily whitespace after template -#}
|
||||
|
@@ -31,7 +31,7 @@
|
||||
{% if form.memo is defined %}
|
||||
<fieldset>
|
||||
<legend><h2>{{ 'Memo'|trans }}</h2></legend>
|
||||
{{ form_row(form.memo, {'label' : 'Memo'} ) }}
|
||||
{{ form_widget(form.memo, {'label' : 'Memo'} ) }}
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
|
||||
@@ -85,15 +85,17 @@
|
||||
{{ form_row(form.mobilenumber, {'label': 'Mobilenumber'}) }}
|
||||
</div>
|
||||
<div id="personAcceptSMS">
|
||||
{{ form_row(form.acceptSMS, {'label' : 'Accept short text message ?'}) }}
|
||||
{{ form_row(form.acceptSms, {'label' : 'Accept short text message ?'}) }}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- if form.otherPhoneNumbers is defined -%}
|
||||
{{ form_widget(form.otherPhoneNumbers) }}
|
||||
{{ form_errors(form.otherPhoneNumbers) }}
|
||||
{%- if form.otherPhonenumbers is defined -%}
|
||||
{{ form_widget(form.otherPhonenumbers) }}
|
||||
{{ form_errors(form.otherPhonenumbers) }}
|
||||
{%- endif -%}
|
||||
{%- if form.contactInfo is defined -%}
|
||||
{{ form_row(form.contactInfo, {'label': 'Notes on contact information'}) }}
|
||||
{{ form_label(form.contactInfo) }}
|
||||
{{ form_widget(form.contactInfo) }}
|
||||
{{ form_errors(form.contactInfo) }}
|
||||
{%- endif -%}
|
||||
</fieldset>
|
||||
{%- endif -%}
|
||||
@@ -134,6 +136,20 @@
|
||||
</fieldset>
|
||||
{%- endif -%}
|
||||
|
||||
{% if form.identifiers|length > 0 %}
|
||||
<fieldset>
|
||||
<legend><h2>{{ 'person.Identifiers'|trans }}</h2></legend>
|
||||
<div>
|
||||
{% for f in form.identifiers %}
|
||||
{{ form_row(f) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</fieldset>
|
||||
{% else %}
|
||||
{{ form_widget(form.identifiers) }}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{{ form_rest(form) }}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
|
@@ -1,19 +1,3 @@
|
||||
{#
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
{% extends "@ChillPerson/Person/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = 'chill_person_view' %}
|
||||
@@ -78,6 +62,16 @@ This view should receive those arguments:
|
||||
{% else %}
|
||||
<dd>{{ 'gender.not defined'|trans }}</dd>
|
||||
{% endif %}
|
||||
|
||||
{% if person.genderComment.comment is not empty %}
|
||||
<dt>{{ 'Gender comment'|trans }} :</dt>
|
||||
<dd>
|
||||
<div class="chill-user-quote">
|
||||
{{ person.genderComment.comment|chill_markdown_to_html }}
|
||||
</div>
|
||||
</dd>
|
||||
{% endif %}
|
||||
|
||||
</dl>
|
||||
</figure>
|
||||
</div>
|
||||
@@ -126,16 +120,6 @@ This view should receive those arguments:
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
{% if person.genderComment.comment is not empty %}
|
||||
<div class="col-12">
|
||||
<figure class="person-details">
|
||||
<h2 class="chill-beige">{{ 'Gender comment'|trans }} :</h2>
|
||||
<div class="chill-user-quote">
|
||||
{{ person.genderComment.comment|chill_markdown_to_html }}
|
||||
</div>
|
||||
</figure>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -241,17 +225,20 @@ This view should receive those arguments:
|
||||
<span class="chill-no-data-statement">{{ 'No data given'|trans }}</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt>{{ 'Comment on the marital status'|trans }} :</dt>
|
||||
|
||||
<dd>
|
||||
{% if person.maritalStatusComment.comment is not empty %}
|
||||
<blockquote class="chill-user-quote">
|
||||
{{ person.maritalStatusComment.comment|chill_markdown_to_html }}
|
||||
</blockquote>
|
||||
{% else %}
|
||||
<span class="chill-no-data-statement">{{ 'No data given'|trans }}</span>
|
||||
{% if person.maritalStatusComment.comment is not empty %}
|
||||
<dt>{{ 'Comment on the marital status'|trans }} :</dt>
|
||||
<dd>
|
||||
<blockquote class="chill-user-quote">
|
||||
{{ person.maritalStatusComment.comment|chill_markdown_to_html }}
|
||||
</blockquote>
|
||||
</dd>
|
||||
{% endif %}
|
||||
{% for identifier in person.identifiers %}
|
||||
{% if identifier.definition.isActive and (identifier|chill_entity_render_string) is not empty %}
|
||||
<dt>{{ identifier.definition.label|localize_translatable_string }} :</dt>
|
||||
<dd>{{ identifier|chill_entity_render_box }}</dd>
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
{%- endif -%}
|
||||
</figure>
|
||||
@@ -341,7 +328,7 @@ This view should receive those arguments:
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="created-updated">
|
||||
<div>
|
||||
{% if person.createdBy %}
|
||||
<div class="createdBy">
|
||||
{{ 'Created by'|trans}}: <b>{{ person.createdBy|chill_entity_render_box({'at_date': person.createdAt}) }}</b>,<br>
|
||||
|
@@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Service\AccompanyingPeriodWorkEvaluationDocument;
|
||||
|
||||
use Chill\DocStoreBundle\Service\StoredObjectDuplicate;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
@@ -36,4 +37,17 @@ class AccompanyingPeriodWorkEvaluationDocumentDuplicator
|
||||
|
||||
return $newDocument;
|
||||
}
|
||||
|
||||
public function duplicateToEvaluation(AccompanyingPeriodWorkEvaluationDocument $document, AccompanyingPeriodWorkEvaluation $evaluation): AccompanyingPeriodWorkEvaluationDocument
|
||||
{
|
||||
$newDocument = new AccompanyingPeriodWorkEvaluationDocument();
|
||||
$newDocument
|
||||
->setTitle($document->getTitle().' ('.$this->translator->trans('accompanying_course_evaluation_document.duplicated_at', ['at' => $this->clock->now()]).')')
|
||||
->setStoredObject($this->storedObjectDuplicate->duplicate($document->getStoredObject()))
|
||||
;
|
||||
|
||||
$evaluation->addDocument($newDocument);
|
||||
|
||||
return $newDocument;
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,11 @@ class PersonRender implements PersonRenderInterface
|
||||
{
|
||||
use BoxUtilsChillEntityRenderTrait;
|
||||
|
||||
public function __construct(private readonly ConfigPersonAltNamesHelper $configAltNamesHelper, private readonly \Twig\Environment $engine, private readonly TranslatorInterface $translator) {}
|
||||
public function __construct(
|
||||
private readonly ConfigPersonAltNamesHelper $configAltNamesHelper,
|
||||
private readonly \Twig\Environment $engine,
|
||||
private readonly TranslatorInterface $translator,
|
||||
) {}
|
||||
|
||||
public function renderBox($person, array $options): string
|
||||
{
|
||||
|
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\PersonBundle\Tests\PersonIdentifier\Rendering;
|
||||
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\PersonIdentifier\Identifier\StringIdentifier;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierEngineInterface;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManager;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierWorker;
|
||||
use Chill\PersonBundle\PersonIdentifier\Rendering\PersonIdRendering;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class PersonIdRenderingTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @dataProvider provideRenderCases
|
||||
*/
|
||||
public function testRenderPersonId(Person $person, string $idContentText, string $expected): void
|
||||
{
|
||||
// Parameter bag mock returning the provided id_content_text
|
||||
$parameterBag = $this->prophesize(ParameterBagInterface::class);
|
||||
$parameterBag->get('chill_person')
|
||||
->willReturn(['person_render' => ['id_content_text' => $idContentText]]);
|
||||
|
||||
// PersonIdentifierManager is explicitly requested to be mocked in the spec.
|
||||
// It will return a PersonIdentifierWorker whose renderAsString behaves like StringIdentifier::renderAsString
|
||||
$personIdentifierManager = $this->prophesize(PersonIdentifierManagerInterface::class);
|
||||
$personIdentifierManager
|
||||
->buildWorkerByPersonIdentifierDefinition(Argument::type(PersonIdentifierDefinition::class))
|
||||
->will(function ($args) {
|
||||
/** @var PersonIdentifierDefinition $definition */
|
||||
$definition = $args[0];
|
||||
|
||||
$engine = new class () implements PersonIdentifierEngineInterface {
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'test';
|
||||
}
|
||||
|
||||
public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string
|
||||
{
|
||||
return $value['content'] ?? '';
|
||||
}
|
||||
|
||||
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, PersonIdentifierDefinition $personIdentifierDefinition): void {}
|
||||
|
||||
public function renderAsString(?PersonIdentifier $identifier, PersonIdentifierDefinition $definition): string
|
||||
{
|
||||
// same behavior as StringIdentifier::renderAsString
|
||||
return $identifier?->getValue()['content'] ?? '';
|
||||
}
|
||||
};
|
||||
|
||||
return new PersonIdentifierWorker($engine, $definition);
|
||||
});
|
||||
|
||||
$service = new PersonIdRendering($parameterBag->reveal(), $personIdentifierManager->reveal());
|
||||
|
||||
self::assertSame($expected, $service->renderPersonId($person));
|
||||
}
|
||||
|
||||
public function provideRenderCases(): iterable
|
||||
{
|
||||
// Case 1: one active identifier, one inactive identifier, should render person id and only active identifier
|
||||
$person1 = new Person();
|
||||
$this->setEntityId($person1, 123);
|
||||
|
||||
$defActive = new PersonIdentifierDefinition(label: ['en' => 'Active'], engine: 'string');
|
||||
$this->setEntityId($defActive, 10);
|
||||
$defActive->setActive(true);
|
||||
|
||||
$idActive = new PersonIdentifier($defActive);
|
||||
$idActive->setPerson($person1);
|
||||
$idActive->setValue(['content' => 'ABC']);
|
||||
$person1->addIdentifier($idActive);
|
||||
|
||||
$defInactive = new PersonIdentifierDefinition(label: ['en' => 'Inactive'], engine: 'string');
|
||||
$this->setEntityId($defInactive, 99);
|
||||
$defInactive->setActive(false);
|
||||
|
||||
$idInactive = new PersonIdentifier($defInactive);
|
||||
$idInactive->setPerson($person1);
|
||||
$idInactive->setValue(['content' => 'SHOULD_NOT_APPEAR']);
|
||||
$person1->addIdentifier($idInactive);
|
||||
|
||||
$template1 = 'ID: [[ person_id ]] - Active: [[ identifier_10 ]] - Inactive: [[ identifier_99 ]]';
|
||||
$expected1 = 'ID: 123 - Active: ABC - Inactive: [[ identifier_99 ]]';
|
||||
|
||||
yield
|
||||
'with active and inactive identifiers' => [$person1, $template1, $expected1]
|
||||
;
|
||||
|
||||
$template2 = 'ID: [[ person_id ]][[ if:identifier_10 ]] - Active: [[ identifier_10 ]][[ endif:identifier_10 ]]';
|
||||
$expected2 = 'ID: 123 - Active: ABC';
|
||||
|
||||
yield
|
||||
'rendering with conditional: condition are removed' => [$person1, $template2, $expected2]
|
||||
;
|
||||
|
||||
$template3 = 'ID: [[ person_id ]][[ if:identifier_99 ]] - Inactive: [[ identifier_10 ]][[ endif:identifier_99 ]]';
|
||||
$expected3 = 'ID: 123';
|
||||
|
||||
yield
|
||||
'rendering with conditional: the content between condition is removed' => [$person1, $template3, $expected3]
|
||||
;
|
||||
|
||||
$template4 = 'ID: [[ person_id ]][[ if:identifier_105 ]] - not present: [[ identifier_105 ]][[ endif:identifier_105 ]]';
|
||||
$expected4 = 'ID: 123';
|
||||
|
||||
yield
|
||||
'rendering with conditional: the content between condition is removed, the identifier is not associated with the person' => [$person1, $template4, $expected4]
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
private function setEntityId(object $entity, int $id): void
|
||||
{
|
||||
$refl = new \ReflectionClass($entity);
|
||||
$prop = $refl->getProperty('id');
|
||||
$prop->setAccessible(true);
|
||||
$prop->setValue($entity, $id);
|
||||
}
|
||||
}
|
@@ -1993,3 +1993,33 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
|
||||
/1.0/person/accompanying-course-work-evaluation-document/{document_id}/evaluation/{evaluation_id}/duplicate:
|
||||
post:
|
||||
tags:
|
||||
- accompanying-course-work-evaluation-document
|
||||
summary: Dupliate an an accompanying period work evaluation document to another evaluation
|
||||
parameters:
|
||||
- in: path
|
||||
name: document_id
|
||||
required: true
|
||||
description: The document's id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
- in: path
|
||||
name: evaluation_id
|
||||
required: true
|
||||
description: The evaluation's id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
|
@@ -95,3 +95,16 @@ services:
|
||||
Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodViewEntityInfoProvider:
|
||||
arguments:
|
||||
$unions: !tagged_iterator chill_person.accompanying_period_info_part
|
||||
|
||||
Chill\PersonBundle\PersonIdentifier\PersonIdentifierManager:
|
||||
arguments:
|
||||
$engines: !tagged_iterator chill_person.person_identifier_engine
|
||||
|
||||
Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface:
|
||||
alias: Chill\PersonBundle\PersonIdentifier\PersonIdentifierManager
|
||||
|
||||
Chill\PersonBundle\PersonIdentifier\Identifier\:
|
||||
resource: '../PersonIdentifier/Identifier'
|
||||
|
||||
Chill\PersonBundle\PersonIdentifier\Rendering\:
|
||||
resource: '../PersonIdentifier/Rendering'
|
||||
|
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Person;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20250822123819 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add person identifier tables: chill_person_identifier_definition and chill_person_identifier with FKs to person and definition; create supporting sequences and indexes.';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE SEQUENCE chill_person_identifier_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE SEQUENCE chill_person_identifier_definition_id_seq INCREMENT BY 1 MINVALUE 1 START 1000');
|
||||
$this->addSql(
|
||||
<<<'SQL'
|
||||
CREATE TABLE chill_person_identifier (
|
||||
id INT NOT NULL,
|
||||
person_id INT NOT NULL,
|
||||
definition_id INT NOT NULL,
|
||||
value JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
canonical TEXT NOT NULL DEFAULT '',
|
||||
PRIMARY KEY(id)
|
||||
)
|
||||
SQL
|
||||
);
|
||||
$this->addSql('CREATE INDEX IDX_BCA5A36B217BBB47 ON chill_person_identifier (person_id)');
|
||||
$this->addSql('CREATE INDEX IDX_BCA5A36BD11EA911 ON chill_person_identifier (definition_id)');
|
||||
$this->addSql(
|
||||
<<<'SQL'
|
||||
CREATE TABLE chill_person_identifier_definition (
|
||||
id INT NOT NULL,
|
||||
label JSON DEFAULT '[]' NOT NULL,
|
||||
engine VARCHAR(100) NOT NULL,
|
||||
is_searchable BOOLEAN DEFAULT false NOT NULL,
|
||||
is_editable_by_users BOOLEAN DEFAULT false NOT NULL,
|
||||
data JSONB DEFAULT '[]' NOT NULL,
|
||||
active BOOLEAN DEFAULT true NOT NULL,
|
||||
PRIMARY KEY(id))
|
||||
SQL
|
||||
);
|
||||
$this->addSql('ALTER TABLE chill_person_identifier ADD CONSTRAINT FK_BCA5A36B217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_person_identifier ADD CONSTRAINT FK_BCA5A36BD11EA911 FOREIGN KEY (definition_id) REFERENCES chill_person_identifier_definition (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP SEQUENCE chill_person_identifier_id_seq CASCADE');
|
||||
$this->addSql('DROP SEQUENCE chill_person_identifier_definition_id_seq CASCADE');
|
||||
$this->addSql('ALTER TABLE chill_person_identifier DROP CONSTRAINT FK_BCA5A36B217BBB47');
|
||||
$this->addSql('ALTER TABLE chill_person_identifier DROP CONSTRAINT FK_BCA5A36BD11EA911');
|
||||
$this->addSql('DROP TABLE chill_person_identifier');
|
||||
$this->addSql('DROP TABLE chill_person_identifier_definition');
|
||||
}
|
||||
}
|
@@ -21,6 +21,9 @@ accompanying_period:
|
||||
other {Participants}
|
||||
}
|
||||
|
||||
number: >-
|
||||
n° {id}
|
||||
|
||||
person:
|
||||
from_the: depuis le
|
||||
And himself: >-
|
||||
|
@@ -102,6 +102,9 @@ spokenLanguages: Langues parlées
|
||||
Employment status: Situation professionelle
|
||||
Administrative status: Situation administrative
|
||||
|
||||
person:
|
||||
Identifiers: Identifiants
|
||||
|
||||
|
||||
# dédoublonnage
|
||||
Old person: Doublon
|
||||
@@ -750,6 +753,42 @@ evaluation:
|
||||
delay: Délai
|
||||
notificationDelay: Délai de notification
|
||||
url: Lien internet
|
||||
title: Ecrire une évaluation
|
||||
status: Statut
|
||||
choose_a_status: Choisir un statut
|
||||
startdate: Date d'ouverture
|
||||
enddate: Date de fin
|
||||
maxdate: Date d'échéance
|
||||
warning_interval: Rappel (jours)
|
||||
public_comment: Note publique
|
||||
comment_placeholder: Commencez à écrire ...
|
||||
generate_a_document: Générer un document
|
||||
choose_a_template: Choisir un modèle
|
||||
add_a_document: Ajouter un document
|
||||
add: Ajouter une évaluation
|
||||
time_spent: Temps de rédaction
|
||||
select_time_spent: Indiquez le temps de rédaction
|
||||
Documents: Documents
|
||||
document_add: Générer ou téléverser un document
|
||||
document_upload: Téléverser un document
|
||||
document_title: Titre du document
|
||||
template_title: Nom du template
|
||||
browse: Ajouter un document
|
||||
replace: Remplacer
|
||||
download: Télécharger le fichier existant
|
||||
notification_notify_referrer: Notifier le référent
|
||||
notification_notify_any: Notifier d'autres utilisateurs
|
||||
notification_send: Envoyer une notification
|
||||
document:
|
||||
edit: Modifier
|
||||
delete: Supprimer
|
||||
move: Déplacer
|
||||
duplicate: Dupliquer
|
||||
duplicate_here: Dupliquer ici
|
||||
duplicate_to_other_evaluation: Dupliquer vers une autre évaluation
|
||||
duplicate_success: Le document d'évaluation a été dupliquer
|
||||
move_success: Le document d'évaluation a été déplacer
|
||||
|
||||
|
||||
goal:
|
||||
desactivationDate: Date de désactivation
|
||||
@@ -774,7 +813,6 @@ relation:
|
||||
reverseTitle: Deuxième membre
|
||||
|
||||
days: jours
|
||||
months: mois
|
||||
years: années
|
||||
|
||||
# specific to closing motive
|
||||
@@ -1522,3 +1560,6 @@ my_parcours_filters:
|
||||
parcours_intervening: Intervenant
|
||||
is_open: Parcours ouverts
|
||||
is_closed: Parcours clôturés
|
||||
|
||||
document_duplicate:
|
||||
to_evaluation_success: "Le document a été dupliquer"
|
||||
|
Reference in New Issue
Block a user