mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-10-01 10:59:45 +00:00
Compare commits
9 Commits
chill-bund
...
3.0.0-RC1
Author | SHA1 | Date | |
---|---|---|---|
6db36d5ab6
|
|||
59f721934e
|
|||
84ce8a93f3
|
|||
ab5f2ffb65
|
|||
73bae8ccb9
|
|||
dcfa569e3a
|
|||
4b07fe3622
|
|||
48bf359d2e
|
|||
60c7ea601c
|
6
.changes/unreleased/Feature-20240530-160003.yaml
Normal file
6
.changes/unreleased/Feature-20240530-160003.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: |
|
||||
Upgrade import of address list to the last version of compiled addresses of belgian-best-address
|
||||
time: 2024-05-30T16:00:03.440767606+02:00
|
||||
custom:
|
||||
Issue: ""
|
6
.changes/unreleased/Feature-20240531-190242.yaml
Normal file
6
.changes/unreleased/Feature-20240531-190242.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: |
|
||||
Upgrade CKEditor and refactor configuration with use of typescript
|
||||
time: 2024-05-31T19:02:42.776662753+02:00
|
||||
custom:
|
||||
Issue: ""
|
@@ -55,7 +55,7 @@ Arborescence:
|
||||
- person
|
||||
- personvendee
|
||||
- household_edit_metadata
|
||||
- index.ts
|
||||
- index.js
|
||||
```
|
||||
|
||||
## Organisation des feuilles de styles
|
||||
|
@@ -119,7 +119,6 @@
|
||||
"Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
|
||||
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
|
||||
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
|
||||
"Chill\\TicketBundle\\": "src/Bundle/ChillTicketBundle/src",
|
||||
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
|
||||
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src",
|
||||
"Chill\\Utils\\Rector\\": "utils/rector/src"
|
||||
@@ -127,9 +126,8 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"App\\": "tests",
|
||||
"App\\": "tests/",
|
||||
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
|
||||
"Chill\\TicketBundle\\Tests\\": "src/Bundle/ChillTicketBundle/tests",
|
||||
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
|
||||
"Chill\\Utils\\Rector\\Tests\\": "utils/rector/tests"
|
||||
}
|
||||
@@ -151,6 +149,7 @@
|
||||
"scripts": {
|
||||
"auto-scripts": {
|
||||
"cache:clear": "symfony-cmd"
|
||||
}
|
||||
},
|
||||
"php-cs-fixer": "php-cs-fixer fix --config=./.php-cs-fixer.dist.php --show-progress=none"
|
||||
}
|
||||
}
|
||||
|
15
package.json
15
package.json
@@ -6,15 +6,16 @@
|
||||
"@apidevtools/swagger-cli": "^4.0.4",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@ckeditor/ckeditor5-build-classic": "^35.3.2",
|
||||
"@ckeditor/ckeditor5-dev-utils": "^31.1.13",
|
||||
"@ckeditor/ckeditor5-build-classic": "^41.4.2",
|
||||
"@ckeditor/ckeditor5-dev-utils": "^40.2.0",
|
||||
"@ckeditor/ckeditor5-dev-webpack-plugin": "^31.1.13",
|
||||
"@ckeditor/ckeditor5-markdown-gfm": "^35.3.2",
|
||||
"@ckeditor/ckeditor5-theme-lark": "^35.3.2",
|
||||
"@ckeditor/ckeditor5-vue": "^4.0.1",
|
||||
"@ckeditor/ckeditor5-dev-translations": "^40.2.0",
|
||||
"@ckeditor/ckeditor5-markdown-gfm": "^41.4.2",
|
||||
"@ckeditor/ckeditor5-theme-lark": "^41.4.2",
|
||||
"@ckeditor/ckeditor5-vue": "^5.1.0",
|
||||
"@symfony/webpack-encore": "^4.1.0",
|
||||
"@tsconfig/node14": "^1.0.1",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"bindings": "^1.5.0",
|
||||
"bootstrap": "5.2.3",
|
||||
"chokidar": "^3.5.1",
|
||||
@@ -31,7 +32,7 @@
|
||||
"select2-bootstrap-theme": "0.1.0-beta.10",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-loader": "^9.3.1",
|
||||
"typescript": "^4.7.2",
|
||||
"typescript": "^5.4.5",
|
||||
"vue-loader": "^17.0.0",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
|
@@ -49,10 +49,6 @@
|
||||
<!-- temporarily removed, the time to find a fix -->
|
||||
<exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php</exclude>
|
||||
</testsuite>
|
||||
|
||||
<testsuite name="TicketBundle">
|
||||
<directory suffix="Test.php">src/Bundle/ChillTicketBundle/tests/</directory>
|
||||
</testsuite>
|
||||
<!--
|
||||
<testsuite name="ReportBundle">
|
||||
<directory suffix="Test.php">src/Bundle/ChillReportBundle/Tests/</directory>
|
||||
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
|
||||
class UserGroupApiController extends ApiController {}
|
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\DataFixtures\ORM;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
|
||||
class LoadUserGroup extends Fixture implements FixtureGroupInterface
|
||||
{
|
||||
public static function getGroups(): array
|
||||
{
|
||||
return ['user-group'];
|
||||
}
|
||||
|
||||
public function load(ObjectManager $manager)
|
||||
{
|
||||
$centerASocial = $manager->getRepository(User::class)->findOneBy(['username' => 'center a_social']);
|
||||
$centerBSocial = $manager->getRepository(User::class)->findOneBy(['username' => 'center b_social']);
|
||||
$multiCenter = $manager->getRepository(User::class)->findOneBy(['username' => 'multi_center']);
|
||||
$administrativeA = $manager->getRepository(User::class)->findOneBy(['username' => 'center a_administrative']);
|
||||
$administrativeB = $manager->getRepository(User::class)->findOneBy(['username' => 'center b_administrative']);
|
||||
|
||||
$level1 = $this->generateLevelGroup('Niveau 1', '#eec84aff', '#000000ff', 'level');
|
||||
$level1->addUser($centerASocial)->addUser($centerBSocial);
|
||||
$manager->persist($level1);
|
||||
|
||||
$level2 = $this->generateLevelGroup('Niveau 2', ' #e2793dff', '#000000ff', 'level');
|
||||
$level2->addUser($multiCenter);
|
||||
$manager->persist($level2);
|
||||
|
||||
$level3 = $this->generateLevelGroup('Niveau 3', ' #df4949ff', '#000000ff', 'level');
|
||||
$level3->addUser($multiCenter);
|
||||
$manager->persist($level3);
|
||||
|
||||
$tss = $this->generateLevelGroup('Travailleur sociaux', '#43b29dff', '#000000ff', '');
|
||||
$tss->addUser($multiCenter)->addUser($centerASocial)->addUser($centerBSocial);
|
||||
$manager->persist($tss);
|
||||
$admins = $this->generateLevelGroup('Administratif', '#334d5cff', '#000000ff', '');
|
||||
$admins->addUser($administrativeA)->addUser($administrativeB);
|
||||
$manager->persist($admins);
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
private function generateLevelGroup(string $title, string $backgroundColor, string $foregroundColor, string $excludeKey): UserGroup
|
||||
{
|
||||
$userGroup = new UserGroup();
|
||||
|
||||
return $userGroup
|
||||
->setLabel(['fr' => $title])
|
||||
->setBackgroundColor($backgroundColor)
|
||||
->setForegroundColor($foregroundColor)
|
||||
->setExcludeKey($excludeKey)
|
||||
;
|
||||
}
|
||||
}
|
@@ -24,7 +24,6 @@ use Chill\MainBundle\Controller\LocationTypeController;
|
||||
use Chill\MainBundle\Controller\NewsItemController;
|
||||
use Chill\MainBundle\Controller\RegroupmentController;
|
||||
use Chill\MainBundle\Controller\UserController;
|
||||
use Chill\MainBundle\Controller\UserGroupApiController;
|
||||
use Chill\MainBundle\Controller\UserJobApiController;
|
||||
use Chill\MainBundle\Controller\UserJobController;
|
||||
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
|
||||
@@ -60,7 +59,6 @@ use Chill\MainBundle\Entity\LocationType;
|
||||
use Chill\MainBundle\Entity\NewsItem;
|
||||
use Chill\MainBundle\Entity\Regroupment;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Form\CenterType;
|
||||
use Chill\MainBundle\Form\CivilityType;
|
||||
@@ -805,21 +803,6 @@ class ChillMainExtension extends Extension implements
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'class' => UserGroup::class,
|
||||
'controller' => UserGroupApiController::class,
|
||||
'name' => 'user-group',
|
||||
'base_path' => '/api/1.0/main/user-group',
|
||||
'base_role' => 'ROLE_USER',
|
||||
'actions' => [
|
||||
'_index' => [
|
||||
'methods' => [
|
||||
Request::METHOD_GET => true,
|
||||
Request::METHOD_HEAD => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
@@ -1,139 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'chill_main_user_group')]
|
||||
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['user_group' => UserGroup::class])]
|
||||
class UserGroup
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private array $label = [];
|
||||
|
||||
/**
|
||||
* @var \Doctrine\Common\Collections\Collection<int, \Chill\MainBundle\Entity\User>
|
||||
*/
|
||||
#[ORM\ManyToMany(targetEntity: User::class)]
|
||||
#[ORM\JoinTable(name: 'chill_main_user_group_user')]
|
||||
private Collection $users;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#ffffffff'])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private string $backgroundColor = '#ffffffff';
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#000000ff'])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private string $foregroundColor = '#000000ff';
|
||||
|
||||
/**
|
||||
* Groups with same exclude key are mutually exclusive: adding one in a many-to-one relationship
|
||||
* will exclude others.
|
||||
*
|
||||
* An empty string means "no exclusion"
|
||||
*/
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private string $excludeKey = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->users = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function addUser(User $user): self
|
||||
{
|
||||
if (!$this->users->contains($user)) {
|
||||
$this->users[] = $user;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeUser(User $user): self
|
||||
{
|
||||
if ($this->users->contains($user)) {
|
||||
$this->users->removeElement($user);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): array
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function getUsers(): Collection
|
||||
{
|
||||
return $this->users;
|
||||
}
|
||||
|
||||
public function getForegroundColor(): string
|
||||
{
|
||||
return $this->foregroundColor;
|
||||
}
|
||||
|
||||
public function getExcludeKey(): string
|
||||
{
|
||||
return $this->excludeKey;
|
||||
}
|
||||
|
||||
public function getBackgroundColor(): string
|
||||
{
|
||||
return $this->backgroundColor;
|
||||
}
|
||||
|
||||
public function setForegroundColor(string $foregroundColor): self
|
||||
{
|
||||
$this->foregroundColor = $foregroundColor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBackgroundColor(string $backgroundColor): self
|
||||
{
|
||||
$this->backgroundColor = $backgroundColor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setExcludeKey(string $excludeKey): self
|
||||
{
|
||||
$this->excludeKey = $excludeKey;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLabel(array $label): self
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -76,24 +76,6 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
|
||||
->formatOutOfCountryCallingNumber($phoneNumber, $this->config['default_carrier_code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NumberParseException
|
||||
*/
|
||||
public function parse(string $phoneNumber): PhoneNumber
|
||||
{
|
||||
$sanitizedPhoneNumber = $phoneNumber;
|
||||
|
||||
if (str_starts_with($sanitizedPhoneNumber, '00')) {
|
||||
$sanitizedPhoneNumber = '+'.substr($sanitizedPhoneNumber, 2, null);
|
||||
}
|
||||
|
||||
if (!str_starts_with($sanitizedPhoneNumber, '+') && !str_starts_with($sanitizedPhoneNumber, '0')) {
|
||||
$sanitizedPhoneNumber = '+'.$sanitizedPhoneNumber;
|
||||
}
|
||||
|
||||
return $this->phoneNumberUtil->parse($sanitizedPhoneNumber, $this->config['default_carrier_code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type (mobile, landline, ...) for phone number.
|
||||
*/
|
||||
|
@@ -59,6 +59,10 @@ export const ISOToDatetime = (str: string|null): Date|null => {
|
||||
[hours, minutes, seconds] = time.split(':').map(s => parseInt(s));
|
||||
;
|
||||
|
||||
if ('0000' === timezone) {
|
||||
return new Date(Date.UTC(year, month-1, date, hours, minutes, seconds));
|
||||
}
|
||||
|
||||
return new Date(year, month-1, date, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
|
@@ -15,9 +15,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var mime = require('mime')
|
||||
import mime from 'mime';
|
||||
|
||||
var download_report = (url, container) => {
|
||||
export const download_report = (url, container) => {
|
||||
var download_text = container.dataset.downloadText,
|
||||
alias = container.dataset.alias;
|
||||
|
||||
@@ -63,5 +63,3 @@ var download_report = (url, container) => {
|
||||
.replaceChild(problem_text, container.firstChild);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = download_report;
|
||||
|
@@ -39,23 +39,5 @@ ClassicEditor.defaultConfig = {
|
||||
'redo'
|
||||
]
|
||||
},
|
||||
language: 'fr'
|
||||
language: 'fr',
|
||||
};
|
||||
|
||||
|
||||
let Fields = [];
|
||||
Fields.push.apply(Fields, document.querySelectorAll('textarea[ckeditor]'));
|
||||
// enable for custom fields
|
||||
//Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));
|
||||
|
||||
Fields.forEach(function(field) {
|
||||
ClassicEditor
|
||||
.create( field )
|
||||
.then( editor => {
|
||||
//console.log( 'CkEditor was initialized', editor );
|
||||
})
|
||||
.catch( error => {
|
||||
console.error( error.stack );
|
||||
})
|
||||
;
|
||||
});
|
@@ -0,0 +1,15 @@
|
||||
import ClassicEditor from "./editor_config";
|
||||
|
||||
const ckeditorFields: NodeListOf<HTMLTextAreaElement> = document.querySelectorAll('textarea[ckeditor]');
|
||||
ckeditorFields.forEach((field: HTMLTextAreaElement): void => {
|
||||
ClassicEditor
|
||||
.create( field )
|
||||
.then( editor => {
|
||||
//console.log( 'CkEditor was initialized', editor );
|
||||
})
|
||||
.catch( error => {
|
||||
console.error( error.stack );
|
||||
})
|
||||
;
|
||||
});
|
||||
//Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));
|
@@ -0,0 +1,16 @@
|
||||
import {download_report} from "../../lib/download-report/download-report";
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function(e) {
|
||||
const export_generate_url = window.export_generate_url;
|
||||
|
||||
if (typeof export_generate_url === 'undefined') {
|
||||
console.error('Alias not found!');
|
||||
throw new Error('Alias not found!');
|
||||
}
|
||||
|
||||
const query = window.location.search,
|
||||
container = document.querySelector("#download_container")
|
||||
;
|
||||
|
||||
download_report(export_generate_url + "?" + query.toString(), container);
|
||||
});
|
@@ -1,175 +1,164 @@
|
||||
export interface DateTime {
|
||||
datetime: string;
|
||||
datetime8601: string;
|
||||
datetime: string;
|
||||
datetime8601: string
|
||||
}
|
||||
|
||||
export interface Civility {
|
||||
id: number;
|
||||
// TODO
|
||||
id: number;
|
||||
// TODO
|
||||
}
|
||||
|
||||
export interface Job {
|
||||
id: number;
|
||||
type: "user_job";
|
||||
label: {
|
||||
fr: string; // could have other key. How to do that in ts ?
|
||||
};
|
||||
id: number;
|
||||
type: "user_job";
|
||||
label: {
|
||||
"fr": string; // could have other key. How to do that in ts ?
|
||||
}
|
||||
}
|
||||
|
||||
export interface Center {
|
||||
id: number;
|
||||
type: "center";
|
||||
name: string;
|
||||
id: number;
|
||||
type: "center";
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Scope {
|
||||
id: number;
|
||||
type: "scope";
|
||||
name: {
|
||||
fr: string;
|
||||
};
|
||||
id: number;
|
||||
type: "scope";
|
||||
name: {
|
||||
"fr": string
|
||||
}
|
||||
}
|
||||
|
||||
export interface User {
|
||||
type: "user";
|
||||
id: number;
|
||||
username: string;
|
||||
text: string;
|
||||
text_without_absence: string;
|
||||
email: string;
|
||||
user_job: Job;
|
||||
label: string;
|
||||
// todo: mainCenter; mainJob; etc..
|
||||
type: "user";
|
||||
id: number;
|
||||
username: string;
|
||||
text: string;
|
||||
text_without_absence: string;
|
||||
email: string;
|
||||
user_job: Job;
|
||||
label: string;
|
||||
// todo: mainCenter; mainJob; etc..
|
||||
}
|
||||
|
||||
export interface UserGroup {
|
||||
type: "chill_main_user_group" | "user_group";
|
||||
id: number;
|
||||
label: TranslatableString;
|
||||
backgroundColor: string;
|
||||
foregroundColor: string;
|
||||
excludeKey: string;
|
||||
}
|
||||
|
||||
export type UserGroupOrUser = User | UserGroup;
|
||||
|
||||
export interface UserAssociatedInterface {
|
||||
type: "user";
|
||||
id: number;
|
||||
}
|
||||
|
||||
export type TranslatableString = {
|
||||
fr?: string;
|
||||
nl?: string;
|
||||
type: "user";
|
||||
id: number;
|
||||
};
|
||||
|
||||
export type TranslatableString = {
|
||||
fr?: string;
|
||||
nl?: string;
|
||||
}
|
||||
|
||||
export interface Postcode {
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
center: Point;
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
center: Point;
|
||||
}
|
||||
|
||||
export type Point = {
|
||||
type: "Point";
|
||||
coordinates: [lat: number, lon: number];
|
||||
};
|
||||
|
||||
export interface Country {
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
code: string;
|
||||
type: "Point";
|
||||
coordinates: [lat: number, lon: number];
|
||||
}
|
||||
|
||||
export type AddressRefStatus = "match" | "to_review" | "reviewed";
|
||||
export interface Country {
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
code: string;
|
||||
}
|
||||
|
||||
export type AddressRefStatus = 'match'|'to_review'|'reviewed';
|
||||
|
||||
export interface Address {
|
||||
type: "address";
|
||||
address_id: number;
|
||||
text: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
postcode: Postcode;
|
||||
country: Country;
|
||||
floor: string | null;
|
||||
corridor: string | null;
|
||||
steps: string | null;
|
||||
flat: string | null;
|
||||
buildingName: string | null;
|
||||
distribution: string | null;
|
||||
extra: string | null;
|
||||
confidential: boolean;
|
||||
lines: string[];
|
||||
addressReference: AddressReference | null;
|
||||
validFrom: DateTime;
|
||||
validTo: DateTime | null;
|
||||
point: Point | null;
|
||||
refStatus: AddressRefStatus;
|
||||
isNoAddress: boolean;
|
||||
type: "address";
|
||||
address_id: number;
|
||||
text: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
postcode: Postcode;
|
||||
country: Country;
|
||||
floor: string | null;
|
||||
corridor: string | null;
|
||||
steps: string | null;
|
||||
flat: string | null;
|
||||
buildingName: string | null;
|
||||
distribution: string | null;
|
||||
extra: string | null;
|
||||
confidential: boolean;
|
||||
lines: string[];
|
||||
addressReference: AddressReference | null;
|
||||
validFrom: DateTime;
|
||||
validTo: DateTime | null;
|
||||
point: Point | null;
|
||||
refStatus: AddressRefStatus;
|
||||
isNoAddress: boolean;
|
||||
}
|
||||
|
||||
export interface AddressWithPoint extends Address {
|
||||
point: Point;
|
||||
point: Point
|
||||
}
|
||||
|
||||
export interface AddressReference {
|
||||
id: number;
|
||||
createdAt: DateTime | null;
|
||||
deletedAt: DateTime | null;
|
||||
municipalityCode: string;
|
||||
point: Point;
|
||||
postcode: Postcode;
|
||||
refId: string;
|
||||
source: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
updatedAt: DateTime | null;
|
||||
id: number;
|
||||
createdAt: DateTime | null;
|
||||
deletedAt: DateTime | null;
|
||||
municipalityCode: string;
|
||||
point: Point;
|
||||
postcode: Postcode;
|
||||
refId: string;
|
||||
source: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
updatedAt: DateTime | null;
|
||||
}
|
||||
|
||||
export interface SimpleGeographicalUnit {
|
||||
id: number;
|
||||
layerId: number;
|
||||
unitName: string;
|
||||
unitRefId: string;
|
||||
id: number;
|
||||
layerId: number;
|
||||
unitName: string;
|
||||
unitRefId: string;
|
||||
}
|
||||
|
||||
export interface GeographicalUnitLayer {
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
refId: string;
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
refId: string;
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
type: "location";
|
||||
id: number;
|
||||
active: boolean;
|
||||
address: Address | null;
|
||||
availableForUsers: boolean;
|
||||
createdAt: DateTime | null;
|
||||
createdBy: User | null;
|
||||
updatedAt: DateTime | null;
|
||||
updatedBy: User | null;
|
||||
email: string | null;
|
||||
name: string;
|
||||
phonenumber1: string | null;
|
||||
phonenumber2: string | null;
|
||||
locationType: LocationType;
|
||||
type: "location";
|
||||
id: number;
|
||||
active: boolean;
|
||||
address: Address | null;
|
||||
availableForUsers: boolean;
|
||||
createdAt: DateTime | null;
|
||||
createdBy: User | null;
|
||||
updatedAt: DateTime | null;
|
||||
updatedBy: User | null;
|
||||
email: string | null
|
||||
name: string;
|
||||
phonenumber1: string | null;
|
||||
phonenumber2: string | null;
|
||||
locationType: LocationType;
|
||||
}
|
||||
|
||||
export interface LocationAssociated {
|
||||
type: "location";
|
||||
id: number;
|
||||
type: "location";
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface LocationType {
|
||||
type: "location-type";
|
||||
id: number;
|
||||
active: boolean;
|
||||
addressRequired: "optional" | "required";
|
||||
availableForUsers: boolean;
|
||||
editableByUsers: boolean;
|
||||
contactData: "optional" | "required";
|
||||
title: TranslatableString;
|
||||
type: "location-type";
|
||||
id: number;
|
||||
active: boolean;
|
||||
addressRequired: "optional" | "required";
|
||||
availableForUsers: boolean;
|
||||
editableByUsers: boolean;
|
||||
contactData: "optional" | "required";
|
||||
title: TranslatableString;
|
||||
}
|
||||
|
||||
export interface NewsItemType {
|
||||
|
@@ -1,24 +1,13 @@
|
||||
<template>
|
||||
<span class="chill-entity entity-user">
|
||||
{{ user.label }}
|
||||
<span class="user-job" v-if="user.user_job !== null"
|
||||
>({{ user.user_job.label.fr }})</span
|
||||
>
|
||||
<span class="main-scope" v-if="user.main_scope !== null"
|
||||
>({{ user.main_scope.name.fr }})</span
|
||||
>
|
||||
<span
|
||||
v-if="user.isAbsent"
|
||||
class="badge bg-danger rounded-pill"
|
||||
:title="Absent"
|
||||
>A</span
|
||||
>
|
||||
</span>
|
||||
<span class="chill-entity entity-user">
|
||||
{{ user.label }}
|
||||
<span class="user-job" v-if="user.user_job !== null">({{ user.user_job.label.fr }})</span> <span class="main-scope" v-if="user.main_scope !== null">({{ user.main_scope.name.fr }})</span> <span v-if="user.isAbsent" class="badge bg-danger rounded-pill" :title="Absent">A</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "UserRenderBoxBadge",
|
||||
props: ["user"],
|
||||
};
|
||||
props: ['user'],
|
||||
}
|
||||
</script>
|
||||
|
@@ -0,0 +1,306 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
SASS Assets Catalogue
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<style media="screen">
|
||||
h2 { margin: 1.5em 0; }
|
||||
div.flex-table ul, div.flex-bloc ul { padding-left: 1rem; }
|
||||
div.flex-table div.item-bloc div.item-row div.item-col:first-child { flex-basis: 20%; }
|
||||
div.flex-bloc div.item-bloc { flex-basis: 50%; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-10">
|
||||
<h1 class="display-4">{{ block('title') }}</h1>
|
||||
|
||||
<b>Voir aussi: </b>
|
||||
<a href="{{ path('sass_assets_test1') }}">Test 1</a> |
|
||||
<a href="{{ path('sass_assets_test2') }}">Test 2</a>
|
||||
|
||||
<h2>Flex-table et flex-bloc</h2>
|
||||
<p>Base d'un placement flex alternatif à l'usage des tables.
|
||||
Flex-table et flex-bloc utilisent la même structure html (seul la root class change).
|
||||
Le placement est responsive.
|
||||
La bordure utilise box-shadow pour simuler border-collapse (table).
|
||||
</p>
|
||||
<p>Une classe separator peut être appliquée sur item-row</p>
|
||||
|
||||
<xmp>
|
||||
<div class="flex-table">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col"></div>
|
||||
<div class="item-col"></div>
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
<div class="item-col"></div>
|
||||
<div class="item-col"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col"></div>
|
||||
<div class="item-col"></div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col"></div>
|
||||
<div class="item-col"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
<h3>Flex-table</h3>
|
||||
<p>On fixe manuellement la largeur de la première colonne :
|
||||
<pre>div.flex-table div.item-bloc div.item-row div.item-col:first-child { flex-basis: 20%; }</pre>
|
||||
</p>
|
||||
<div class="flex-table debug">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row1</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-content">
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
<div class="item-col">Title row2</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row3</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row1</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-content">
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row2</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Flex-bloc</h3>
|
||||
<p>On fixe manuellement la largeur des blocs :
|
||||
<pre>div.flex-bloc div.item-bloc { flex-basis: 50%; }</pre>
|
||||
</p>
|
||||
<div class="flex-bloc debug">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row1</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-content">
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row separator">
|
||||
<div class="item-col">Title row2</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row3</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row1</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-content">
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
<li>Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-row">
|
||||
<div class="item-col">Title row2</div>
|
||||
<div class="item-col">Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac viverra et a dui.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Wrap-list</h2>
|
||||
<p>Une liste inline qui s'aligne, puis glisse sous son titre.</p>
|
||||
<div class="wrap-list debug">
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title">Usagers concernés</div>
|
||||
<div class="wl-col list">
|
||||
<p class="wl-item"><a href="#">Gaston Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Alain Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Adèle Gaillot</a></p>
|
||||
<p class="wl-item"><a href="#">Corentine Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Justin Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Michel Sardou</a></p>
|
||||
<p class="wl-item"><a href="#">Carine Rousseau</a></p>
|
||||
<p class="wl-item"><a href="#">Mohamed Martin</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title">Problématiques sociales</div>
|
||||
<div class="wl-col list">
|
||||
<p class="wl-item"><a href="#">Gaston Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Alain Bah</a></p>
|
||||
<p class="wl-item"><a href="#">Adèle Gaillot</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<xmp>
|
||||
<div class="wrap-list">
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title">title</div>
|
||||
<div class="wl-col list">
|
||||
<p class="wl-item">item</p>
|
||||
<p class="wl-item">item</p>
|
||||
...
|
||||
</div>
|
||||
</div>
|
||||
...
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
<h2>Wrap-header</h2>
|
||||
<p>Réglage d'une zone de titre sur 2 lignes.</p>
|
||||
<div class="wrap-header debug">
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">
|
||||
<span class="h3"><b>Title</b></span>
|
||||
<span class="badge rounded-pill bg-danger">badge</span>
|
||||
</div>
|
||||
<div class="wh-col">
|
||||
<span class="badge rounded-pill bg-primary">badge</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">from startdate to enddate</div>
|
||||
<div class="wh-col">text</div>
|
||||
</div>
|
||||
</div>
|
||||
<xmp>
|
||||
<div class="wrap-header">
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">line1 left</div>
|
||||
<div class="wh-col">line1 right</div>
|
||||
</div>
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">line2 left</div>
|
||||
<div class="wh-col">line2 right</div>
|
||||
</div>
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
<h2>Float-button top</h2>
|
||||
<p>Une zone de bouton flotte à droite d'un contenu. On peut voir en faisant varier la largeur que celui-ci vient s'adapter harmonieusement autour des boutons.</p>
|
||||
<div class="float-button top debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac. Proin fermentum mauris quam, ut suscipit nisl auctor at. Ut vestibulum ligula eget ex congue, efficitur interdum ipsum tincidunt. Integer id sapien et nibh tristique viverra et a dui. Ut blandit pharetra consectetur. Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
</div>
|
||||
<xmp>
|
||||
<div class="float-button top">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
floating button
|
||||
</div>
|
||||
content ...
|
||||
</div>
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
<h2>Float-button bottom</h2>
|
||||
<p>Avec la même structure, on accroche la zone de bouton en bas, toujours à droite. Voir <a href="https://css-tricks.com/float-an-element-to-the-bottom-corner/">source</a>. </p>
|
||||
<div class="float-button bottom debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac. Proin fermentum mauris quam, ut suscipit nisl auctor at. Ut vestibulum ligula eget ex congue, efficitur interdum ipsum tincidunt. Integer id sapien et nibh tristique viverra et a dui. Ut blandit pharetra consectetur. Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
</div>
|
||||
<xmp>
|
||||
<div class="float-button bottom">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
floating button
|
||||
</div>
|
||||
content ...
|
||||
</div>
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
|
||||
<h1>Buttons</h1>
|
||||
<ul class="record_actions">
|
||||
<li><a href="#" class="btn btn-submit">submit</a></li>
|
||||
<li><a href="#" class="btn btn-save">save</a></li>
|
||||
<li><a href="#" class="btn btn-create">create</a></li>
|
||||
<li><a href="#" class="btn btn-new">new</a></li>
|
||||
<li><a href="#" class="btn btn-duplicate">duplicate</a></li>
|
||||
<li><a href="#" class="btn btn-not-duplicate">not-duplicate</a></li>
|
||||
<li><a href="#" class="btn btn-reset">reset</a></li>
|
||||
<li><a href="#" class="btn btn-delete">delete</a></li>
|
||||
<li><a href="#" class="btn btn-danger">danger</a></li>
|
||||
<li><a href="#" class="btn btn-remove">remove</a></li>
|
||||
<li><a href="#" class="btn btn-unlink">unlink</a></li>
|
||||
<li><a href="#" class="btn btn-action">action</a></li>
|
||||
<li><a href="#" class="btn btn-edit">edit</a></li>
|
||||
<li><a href="#" class="btn btn-update">update</a></li>
|
||||
<li><a href="#" class="btn btn-show">show</a></li>
|
||||
<li><a href="#" class="btn btn-view">view</a></li>
|
||||
<li><a href="#" class="btn btn-misc">misc</a></li>
|
||||
<li><a href="#" class="btn btn-cancel">cancel</a></li>
|
||||
<li><a href="#" class="btn btn-choose">choose</a></li>
|
||||
<li><a href="#" class="btn btn-notify">notify</a></li>
|
||||
<li><a href="#" class="btn btn-tpchild">tpchild</a></li>
|
||||
<li><a href="#" class="btn btn-chill-beige">my button</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>Variants of <pre>record_actions</pre></h2>
|
||||
|
||||
<h3><pre>small</pre></h3>
|
||||
|
||||
<ul class="record_actions small">
|
||||
<li><a href="#" class="btn btn-create"></a></li>
|
||||
</ul>
|
||||
|
||||
<h3><pre>inline</pre></h3>
|
||||
|
||||
<div>
|
||||
This is inline and small
|
||||
|
||||
<ul class="record_actions small inline">
|
||||
<li><a href="#" class="btn btn-create"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<xmp><a class="btn btn-submit">Text</a></xmp>
|
||||
Toutes les classes btn-* de bootstrap sont fonctionnelles
|
||||
</div>
|
||||
{% endblock %}
|
@@ -0,0 +1,84 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
SASS Assets Tests - page 1
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<style media="screen">
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-8">
|
||||
<h1>CSS Tests - page 1 : float-button</h1>
|
||||
|
||||
<h2>1) avec des li</h2>
|
||||
<div class="float-button bottom debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="list-content fa-ul">
|
||||
<li><i class="fa fa-li fa-file-text-o"></i>Sed efficitur magna vel massa efficitur venenatis. Sed odio massa, scelerisque sit amet mauris eu, tristique dictum arcu. Sed posuere, elit eget cursus rhoncus, arcu ligula blandit nisi, in vulputate eros massa non risus.</li>
|
||||
<li><i class="fa fa-li fa-map-marker"></i>
|
||||
<div class="chill-entity entity-address my-3" data-v-8b2170ec="">
|
||||
<div class="address multiline" data-v-8b2170ec="">
|
||||
<p class="street" data-v-8b2170ec="">97, chemin Franck Julien, </p>
|
||||
<p class="postcode" data-v-8b2170ec="">1000 Bruxelles</p>
|
||||
<p class="country" data-v-8b2170ec="">Belgique</p>
|
||||
</div>
|
||||
<div class="address-more" data-v-8b2170ec="">
|
||||
<div data-v-8b2170ec="">
|
||||
<span class="corridor" data-v-8b2170ec="">
|
||||
<b data-v-8b2170ec="">Couloir</b>: 3
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li><i class="fa fa-li fa-mobile"></i><a href="tel: +33 8 27 17 12 19">+33 8 27 17 12 19</a></li>
|
||||
<li><i class="fa fa-li fa-envelope-o"></i><a href="mailto: gusikowski.yesenia@hotmail.com">gusikowski.yesenia@hotmail.com</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h2>2) avec des p</h2>
|
||||
|
||||
<div class="float-button bottom debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>Voir <a href="https://css-tricks.com/float-an-element-to-the-bottom-corner/">trick</a>.</p>
|
||||
<p>Sed efficitur magna vel massa efficitur venenatis. Sed odio massa, scelerisque sit amet mauris eu, tristique dictum arcu. Sed posuere, elit eget cursus rhoncus, arcu ligula blandit nisi, in vulputate eros massa non risus. Proin lacinia, sapien in pharetra ultricies, justo urna fermentum lectus, non tempor ipsum leo a ante. Aenean porta, ipsum in fringilla hendrerit, nisi justo vestibulum ex, non lacinia risus felis vitae diam. Curabitur sem eros, consectetur a auctor vel, facilisis sit amet sem.</p>
|
||||
<p>Aenean finibus a nisl a scelerisque. Donec bibendum facilisis odio id euismod. Pellentesque luctus justo ligula, eget dictum ligula ultrices quis. Pellentesque at nunc est. Aenean luctus, tortor in lacinia porta, ex nisl dignissim magna, non vehicula elit risus at elit. Suspendisse in velit non augue egestas laoreet. Etiam blandit lacus at semper aliquam. Integer leo nunc, condimentum sagittis accumsan sit amet, consectetur vel massa. Aenean convallis nibh vel augue ullamcorper tempus. Integer eu laoreet sapien.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>3) avec des div</h2>
|
||||
<div class="float-button bottom debug">
|
||||
<div class="box">
|
||||
<div class="action">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>Voir <a href="https://css-tricks.com/float-an-element-to-the-bottom-corner/">trick</a>.</div>
|
||||
<div>Sed efficitur magna vel massa efficitur venenatis. Sed odio massa, scelerisque sit amet mauris eu, tristique dictum arcu. Sed posuere, elit eget cursus rhoncus, arcu ligula blandit nisi, in vulputate eros massa non risus. Proin lacinia, sapien in pharetra ultricies, justo urna fermentum lectus, non tempor ipsum leo a ante. Aenean porta, ipsum in fringilla hendrerit, nisi justo vestibulum ex, non lacinia risus felis vitae diam.
|
||||
<a href="#">Curabitur</a> sem eros, consectetur a auctor vel, facilisis sit amet sem.</div>
|
||||
<div>Aenean finibus a nisl a scelerisque. Donec bibendum facilisis odio id euismod. Pellentesque luctus justo ligula, eget dictum ligula ultrices quis. Pellentesque at nunc est. Aenean luctus, tortor in lacinia porta, ex nisl dignissim magna, non vehicula elit risus at elit. Suspendisse in velit non augue egestas laoreet. Etiam blandit lacus at semper aliquam. Integer leo nunc, condimentum sagittis accumsan sit amet, consectetur vel massa. Aenean convallis nibh vel augue ullamcorper tempus. Integer eu laoreet sapien.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
@@ -0,0 +1,78 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title %}
|
||||
SASS Assets Tests - page 2
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-md-10">
|
||||
<h1>CSS Tests - page 2: grid layout</h1>
|
||||
|
||||
<h2>1) mgrid 1-2: avec grid-column et grid-row</h2>
|
||||
|
||||
<div class="mgrid debug">
|
||||
<div class="area1">
|
||||
Nam rhoncus tristique ligula, tincidunt iaculis augue tincidunt ac. Proin fermentum mauris quam, ut suscipit nisl auctor at. Ut vestibulum ligula eget ex congue, efficitur interdum ipsum tincidunt. Integer id sapien et nibh tristique viverra et a dui. Ut blandit pharetra consectetur. Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="area2">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>2) lgrid 3-4: avec grid-template-areas</h2>
|
||||
|
||||
<div class="lgrid debug">
|
||||
<div class="area3">
|
||||
<i>La zone qu'on crée avec les noms doit être rectangulaires. Actuellement, il n'existe pas de méthode pour créer une zone avec une forme de L (bien que la spécification indique qu'une prochaine version pourrait couvrir cette fonctionnalité).
|
||||
[...] Si des zones ne sont pas rectangulaires, cela sera également considéré comme invalide.</i>
|
||||
Voir sur MDN: <a target="_blank" href="https://developer.mozilla.org/fr/docs/Web/CSS/CSS_Grid_Layout/Grid_Template_Areas#occuper_plusieurs_cellules">Définir des zones sur une grille</a>
|
||||
|
||||
</div>
|
||||
<div class="area4">
|
||||
<ul class="record_actions">
|
||||
<li><button type="button" name="button">Annuler</button></li>
|
||||
<li><button type="button" name="button">Voir</button></li>
|
||||
<li><button type="button" name="button">Enregistrer</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>3) cgrid 5-6-7-8: avec masonry</h2>
|
||||
<p>Expérimental: dans FF <i>about:config</i>, il faut mettre <i>layout.css.grid-template-masonry-value.enabled = true</i></p>
|
||||
|
||||
<div class="cgrid debug">
|
||||
<div class="item">
|
||||
1 Integer id sapien et nibh tristique viverra et a dui. Ut blandit pharetra consectetur. Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="item">
|
||||
2 Sed scelerisque eget purus at tempus. Etiam sit amet tellus et eros semper tempor. Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="item">
|
||||
3 Curabitur suscipit pulvinar enim at lobortis. Ut nisl augue, cursus vel hendrerit sed, posuere vel sapien. Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="item">
|
||||
4 Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan. In vitae tristique leo, a fringilla nisl.
|
||||
</div>
|
||||
<div class="item">
|
||||
5 Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim a accumsan.
|
||||
</div>
|
||||
<div class="item">
|
||||
6 Proin hendrerit arcu velit, eu ultrices dui interdum eget. Vestibulum consectetur sodales enim.
|
||||
</div>
|
||||
<div class="item">
|
||||
7 Proin hendrerit arcu velit, eu ultrices dui interdum eget.
|
||||
</div>
|
||||
<div class="item">
|
||||
8 Eu ultrices dui interdum eget.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
@@ -22,15 +22,13 @@
|
||||
|
||||
{% block js %}
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("DOMContentLoaded", function(e) {
|
||||
var url = "{{ path('chill_main_export_generate', { 'alias' : alias } ) }}",
|
||||
query = window.location.search,
|
||||
container = document.querySelector("#download_container")
|
||||
;
|
||||
|
||||
chill.download_report(url+query, container);
|
||||
});
|
||||
window.export_generate_url = "{{ path('chill_main_export_generate', { 'alias' : alias } ) }}";
|
||||
</script>
|
||||
{{ encore_entry_link_tags('page_download_exports') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ encore_entry_script_tags('page_download_exports') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -69,37 +69,35 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block wrapping_content %}
|
||||
{% block content %}
|
||||
<div class="col-8 main_search">
|
||||
{% if app.user.isAbsent %}
|
||||
<div class="d-flex flex-row mb-5 alert alert-warning" role="alert">
|
||||
<p class="m-2">{{'absence.You are marked as being absent'|trans }}</p>
|
||||
<span class="ms-auto">
|
||||
<a class="btn btn-remove" title="Modifier" href="{{ path('chill_main_user_absence_index') }}">{{ 'absence.Unset absence'|trans }}</a>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h2>{{ 'Search'|trans }}</h2>
|
||||
{% block content %}
|
||||
<div class="col-8 main_search">
|
||||
{% if app.user.isAbsent %}
|
||||
<div class="d-flex flex-row mb-5 alert alert-warning" role="alert">
|
||||
<p class="m-2">{{'absence.You are marked as being absent'|trans }}</p>
|
||||
<span class="ms-auto">
|
||||
<a class="btn btn-remove" title="Modifier" href="{{ path('chill_main_user_absence_index') }}">{{ 'absence.Unset absence'|trans }}</a>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h2>{{ 'Search'|trans }}</h2>
|
||||
|
||||
<form action="{{ path('chill_main_search') }}" method="get">
|
||||
<input class="form-control form-control-lg" name="q" type="search" placeholder="{{ 'Search persons, ...'|trans }}" />
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-lg btn-warning mt-3">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
|
||||
</button>
|
||||
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<form action="{{ path('chill_main_search') }}" method="get">
|
||||
<input class="form-control form-control-lg" name="q" type="search" placeholder="{{ 'Search persons, ...'|trans }}" />
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-lg btn-warning mt-3">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
|
||||
</button>
|
||||
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
|
||||
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{# DISABLED {{ chill_widget('homepage', {} ) }} #}
|
||||
{# DISABLED {{ chill_widget('homepage', {} ) }} #}
|
||||
|
||||
{% include '@ChillMain/Homepage/index.html.twig' %}
|
||||
{% include '@ChillMain/Homepage/index.html.twig' %}
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
|
@@ -18,7 +18,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
class AddressReferenceBEFromBestAddress
|
||||
{
|
||||
private const RELEASE = 'https://gitea.champs-libres.be/api/v1/repos/Chill-project/belgian-bestaddresses-transform/releases/tags/v1.0.0';
|
||||
private const RELEASE = 'https://gitea.champs-libres.be/api/v1/repos/Chill-project/belgian-bestaddresses-transform/releases/tags/v1.1.1';
|
||||
|
||||
public function __construct(private readonly HttpClientInterface $client, private readonly AddressReferenceBaseImporter $baseImporter, private readonly AddressToReferenceMatcher $addressToReferenceMatcher) {}
|
||||
|
||||
|
@@ -12,7 +12,6 @@ declare(strict_types=1);
|
||||
namespace Chill\MainBundle\Tests\Phonenumber;
|
||||
|
||||
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
@@ -53,36 +52,12 @@ final class PhonenumberHelperTest extends KernelTestCase
|
||||
];
|
||||
}
|
||||
|
||||
public static function providePhoneNumbersToParse(): iterable
|
||||
{
|
||||
$util = PhoneNumberUtil::getInstance();
|
||||
|
||||
yield [
|
||||
'FR',
|
||||
'+32486544999',
|
||||
$util->parse('+32486544999', 'FR'),
|
||||
];
|
||||
|
||||
yield [
|
||||
'FR',
|
||||
'32486544999',
|
||||
$util->parse('+32486544999', 'FR'),
|
||||
];
|
||||
|
||||
yield [
|
||||
'FR',
|
||||
'0228858040',
|
||||
$util->parse('+33228858040', 'FR'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider formatPhonenumbers
|
||||
*/
|
||||
public function testFormatPhonenumbers(string $defaultCarrierCode, string $phoneNumber, string $expected)
|
||||
{
|
||||
$util = PhoneNumberUtil::getInstance();
|
||||
|
||||
$subject = new PhonenumberHelper(
|
||||
new ArrayAdapter(),
|
||||
new ParameterBag([
|
||||
@@ -95,24 +70,4 @@ final class PhonenumberHelperTest extends KernelTestCase
|
||||
|
||||
$this->assertEquals($expected, $subject->format($util->parse($phoneNumber)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providePhoneNumbersToParse
|
||||
*/
|
||||
public function testParsePhonenumbers(string $defaultCarrierCode, string $phoneNumber, PhoneNumber $expected): void
|
||||
{
|
||||
$subject = new PhonenumberHelper(
|
||||
new ArrayAdapter(),
|
||||
new ParameterBag([
|
||||
'chill_main.phone_helper' => [
|
||||
'default_carrier_code' => $defaultCarrierCode,
|
||||
],
|
||||
]),
|
||||
new NullLogger()
|
||||
);
|
||||
|
||||
$actual = $subject->parse($phoneNumber);
|
||||
|
||||
self::assertTrue($expected->equals($actual));
|
||||
}
|
||||
}
|
||||
|
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Tests\Validation\Validator;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\MainBundle\Validation\Validator\UserGroupDoNotExclude;
|
||||
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class UserGroupDoNotExcludeTest extends ConstraintValidatorTestCase
|
||||
{
|
||||
protected function createValidator()
|
||||
{
|
||||
return new UserGroupDoNotExclude(
|
||||
new class () implements TranslatableStringHelperInterface {
|
||||
public function localize(array $translatableStrings): ?string
|
||||
{
|
||||
return $translatableStrings['fr'];
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function testEmptyArrayIsValid(): void
|
||||
{
|
||||
$this->validator->validate([], new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude());
|
||||
|
||||
$this->assertNoViolation();
|
||||
}
|
||||
|
||||
public function testMixedUserGroupAndUsersIsValid(): void
|
||||
{
|
||||
$this->validator->validate(
|
||||
[new User(), new UserGroup()],
|
||||
new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude()
|
||||
);
|
||||
|
||||
$this->assertNoViolation();
|
||||
}
|
||||
|
||||
public function testDifferentExcludeKeysIsValid(): void
|
||||
{
|
||||
$this->validator->validate(
|
||||
[(new UserGroup())->setExcludeKey('A'), (new UserGroup())->setExcludeKey('B')],
|
||||
new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude()
|
||||
);
|
||||
|
||||
$this->assertNoViolation();
|
||||
}
|
||||
|
||||
public function testMultipleGroupsWithEmptyExcludeKeyIsValid(): void
|
||||
{
|
||||
$this->validator->validate(
|
||||
[(new UserGroup())->setExcludeKey(''), (new UserGroup())->setExcludeKey('')],
|
||||
new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude()
|
||||
);
|
||||
|
||||
$this->assertNoViolation();
|
||||
}
|
||||
|
||||
public function testSameExclusionKeyWillRaiseError(): void
|
||||
{
|
||||
$this->validator->validate(
|
||||
[
|
||||
(new UserGroup())->setExcludeKey('A')->setLabel(['fr' => 'Group 1']),
|
||||
(new UserGroup())->setExcludeKey('A')->setLabel(['fr' => 'Group 2']),
|
||||
],
|
||||
new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude()
|
||||
);
|
||||
|
||||
$this->buildViolation('The groups {{ excluded_groups }} do exclude themselves. Please choose one between them')
|
||||
->setParameter('excluded_groups', 'Group 1, Group 2')
|
||||
->setCode('e16c8226-0090-11ef-8560-f7239594db09')
|
||||
->assertRaised();
|
||||
}
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||
class UserGroupDoNotExclude extends Constraint
|
||||
{
|
||||
public string $message = 'The groups {{ excluded_groups }} do exclude themselves. Please choose one between them';
|
||||
public string $code = 'e16c8226-0090-11ef-8560-f7239594db09';
|
||||
|
||||
public function getTargets()
|
||||
{
|
||||
return [self::PROPERTY_CONSTRAINT];
|
||||
}
|
||||
|
||||
public function validatedBy()
|
||||
{
|
||||
return \Chill\MainBundle\Validation\Validator\UserGroupDoNotExclude::class;
|
||||
}
|
||||
}
|
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Validation\Validator;
|
||||
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedValueException;
|
||||
|
||||
final class UserGroupDoNotExclude extends ConstraintValidator
|
||||
{
|
||||
public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
|
||||
public function validate($value, Constraint $constraint)
|
||||
{
|
||||
if (!$constraint instanceof \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude) {
|
||||
throw new UnexpectedTypeException($constraint, UserGroupDoNotExclude::class);
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_iterable($value)) {
|
||||
throw new UnexpectedValueException($value, 'iterable');
|
||||
}
|
||||
|
||||
$groups = [];
|
||||
|
||||
foreach ($value as $gr) {
|
||||
if ($gr instanceof UserGroup) {
|
||||
$groups[$gr->getExcludeKey()][] = $gr;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($groups as $excludeKey => $groupByKey) {
|
||||
if ('' === $excludeKey) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (1 < count($groupByKey)) {
|
||||
$excludedGroups = implode(
|
||||
', ',
|
||||
array_map(
|
||||
fn (UserGroup $group) => $this->translatableStringHelper->localize($group->getLabel()),
|
||||
$groupByKey
|
||||
)
|
||||
);
|
||||
|
||||
$this->context
|
||||
->buildViolation($constraint->message)
|
||||
->setCode($constraint->code)
|
||||
->setParameters(['excluded_groups' => $excludedGroups])
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -29,42 +29,6 @@ components:
|
||||
type: string
|
||||
text:
|
||||
type: string
|
||||
UserById:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- user
|
||||
UserGroup:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- user_group
|
||||
label:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
backgroundColor:
|
||||
type: string
|
||||
foregroundColor:
|
||||
type: string
|
||||
exclusionKey:
|
||||
type: string
|
||||
UserGroupById:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- user_group
|
||||
Center:
|
||||
type: object
|
||||
properties:
|
||||
@@ -944,19 +908,3 @@ paths:
|
||||
$ref: '#/components/schemas/NewsItem'
|
||||
403:
|
||||
description: "Unauthorized"
|
||||
/1.0/main/user-group.json:
|
||||
get:
|
||||
tags:
|
||||
- user-group
|
||||
summary: Return a list of users-groups
|
||||
responses:
|
||||
200:
|
||||
description: "ok"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserGroup'
|
||||
403:
|
||||
description: "Unauthorized"
|
||||
|
@@ -1,10 +1,10 @@
|
||||
const CKEditorWebpackPlugin = require( '@ckeditor/ckeditor5-dev-webpack-plugin' );
|
||||
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );
|
||||
const {CKEditorTranslationsPlugin} = require("@ckeditor/ckeditor5-dev-translations");
|
||||
|
||||
buildCKEditor = function(encore)
|
||||
{
|
||||
encore
|
||||
.addPlugin( new CKEditorWebpackPlugin( {
|
||||
.addPlugin( new CKEditorTranslationsPlugin( {
|
||||
language: 'fr',
|
||||
addMainLanguageTranslationsToAllAssets: true,
|
||||
verbose: !encore.isProduction(),
|
||||
@@ -52,12 +52,14 @@ module.exports = function(encore, entries)
|
||||
Tabs: __dirname + '/Resources/public/lib/tabs'
|
||||
});
|
||||
|
||||
|
||||
// Page entrypoints
|
||||
encore.addEntry('page_login', __dirname + '/Resources/public/page/login/index.js');
|
||||
encore.addEntry('page_location', __dirname + '/Resources/public/page/location/index.js');
|
||||
encore.addEntry('page_workflow_show', __dirname + '/Resources/public/page/workflow-show/index.js');
|
||||
encore.addEntry('page_homepage_widget', __dirname + '/Resources/public/page/homepage_widget/index.js');
|
||||
encore.addEntry('page_export', __dirname + '/Resources/public/page/export/index.js');
|
||||
encore.addEntry('page_download_exports', __dirname + '/Resources/public/page/export/download-export.js');
|
||||
|
||||
buildCKEditor(encore);
|
||||
|
||||
@@ -65,7 +67,7 @@ module.exports = function(encore, entries)
|
||||
encore.addEntry('mod_collection', __dirname + '/Resources/public/module/collection/index.ts');
|
||||
encore.addEntry('mod_forkawesome', __dirname + '/Resources/public/module/forkawesome/index.js');
|
||||
encore.addEntry('mod_bootstrap', __dirname + '/Resources/public/module/bootstrap/index.js');
|
||||
encore.addEntry('mod_ckeditor5', __dirname + '/Resources/public/module/ckeditor5/index.js');
|
||||
encore.addEntry('mod_ckeditor5', __dirname + '/Resources/public/module/ckeditor5/index');
|
||||
encore.addEntry('mod_disablebuttons', __dirname + '/Resources/public/module/disable-buttons/index.js');
|
||||
encore.addEntry('mod_blur', __dirname + '/Resources/public/module/blur/index.js');
|
||||
encore.addEntry('mod_input_address', __dirname + '/Resources/public/vuejs/Address/mod_input_address_index.js');
|
||||
|
@@ -3,9 +3,6 @@ services:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\MainBundle\Validation\:
|
||||
resource: '../../Validation'
|
||||
|
||||
chill_main.validator_user_circle_consistency:
|
||||
class: Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistencyValidator
|
||||
arguments:
|
||||
|
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20240416145021 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Create tables for user_group';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE SEQUENCE chill_main_user_group_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE chill_main_user_group (id INT NOT NULL, label JSON DEFAULT \'[]\' NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE TABLE chill_main_user_group_user (usergroup_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY(usergroup_id, user_id))');
|
||||
$this->addSql('CREATE INDEX IDX_1E07F044D2112630 ON chill_main_user_group_user (usergroup_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1E07F044A76ED395 ON chill_main_user_group_user (user_id)');
|
||||
$this->addSql('ALTER TABLE chill_main_user_group_user ADD CONSTRAINT FK_1E07F044D2112630 FOREIGN KEY (usergroup_id) REFERENCES chill_main_user_group (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_main_user_group_user ADD CONSTRAINT FK_1E07F044A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP SEQUENCE chill_main_user_group_id_seq');
|
||||
$this->addSql('DROP TABLE chill_main_user_group_user');
|
||||
$this->addSql('DROP TABLE chill_main_user_group');
|
||||
}
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20240422091752 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add colors and exclude string to user groups';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_main_user_group ADD backgroundColor TEXT DEFAULT \'#ffffffff\' NOT NULL');
|
||||
$this->addSql('ALTER TABLE chill_main_user_group ADD foregroundColor TEXT DEFAULT \'#000000ff\' NOT NULL');
|
||||
$this->addSql('ALTER TABLE chill_main_user_group ADD excludeKey TEXT DEFAULT \'\' NOT NULL');
|
||||
$this->addSql('ALTER INDEX idx_1e07f044d2112630 RENAME TO IDX_738BC82BD2112630');
|
||||
$this->addSql('ALTER INDEX idx_1e07f044a76ed395 RENAME TO IDX_738BC82BA76ED395');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_main_user_group DROP backgroundColor');
|
||||
$this->addSql('ALTER TABLE chill_main_user_group DROP foregroundColor');
|
||||
$this->addSql('ALTER TABLE chill_main_user_group DROP excludeKey');
|
||||
$this->addSql('ALTER INDEX idx_738bc82bd2112630 RENAME TO idx_1e07f044d2112630');
|
||||
$this->addSql('ALTER INDEX idx_738bc82ba76ed395 RENAME TO idx_1e07f044a76ed395');
|
||||
}
|
||||
}
|
@@ -28,11 +28,7 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final readonly class GeographicalUnitStatAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private RollingDateConverterInterface $rollingDateConverter
|
||||
) {}
|
||||
public function __construct(private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository, private TranslatableStringHelperInterface $translatableStringHelper, private RollingDateConverterInterface $rollingDateConverter) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
|
@@ -21,8 +21,6 @@ use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\NonUniqueResultException;
|
||||
use Doctrine\ORM\Query;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use libphonenumber\PhoneNumberFormat;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
final readonly class PersonACLAwareRepository implements PersonACLAwareRepositoryInterface
|
||||
@@ -300,27 +298,4 @@ final readonly class PersonACLAwareRepository implements PersonACLAwareRepositor
|
||||
\array_map(static fn (Center $c) => $c->getId(), $authorizedCenters)
|
||||
);
|
||||
}
|
||||
|
||||
public function findByPhone(PhoneNumber $phoneNumber, int $start = 0, int $limit = 20): array
|
||||
{
|
||||
$authorizedCenters = $this->authorizationHelper
|
||||
->getReachableCenters($this->security->getUser(), PersonVoter::SEE);
|
||||
|
||||
if ([] === $authorizedCenters) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$util = \libphonenumber\PhoneNumberUtil::getInstance();
|
||||
|
||||
return $this->em->createQuery(
|
||||
'SELECT p FROM '.Person::class.' p LEFT JOIN p.otherPhoneNumbers opn JOIN p.centerCurrent pcc '.
|
||||
'WHERE (p.phonenumber LIKE :phone OR p.mobilenumber LIKE :phone OR opn.phonenumber LIKE :phone) '.
|
||||
'AND pcc.center IN (:centers)'
|
||||
)
|
||||
->setMaxResults($limit)
|
||||
->setFirstResult($start)
|
||||
->setParameter('phone', $util->format($phoneNumber, PhoneNumberFormat::E164))
|
||||
->setParameter('centers', $authorizedCenters)
|
||||
->getResult();
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,6 @@ namespace Chill\PersonBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Search\SearchApiQuery;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use libphonenumber\PhoneNumber;
|
||||
|
||||
interface PersonACLAwareRepositoryInterface
|
||||
{
|
||||
@@ -61,13 +60,4 @@ interface PersonACLAwareRepositoryInterface
|
||||
?string $phonenumber = null,
|
||||
?string $city = null
|
||||
): array;
|
||||
|
||||
/**
|
||||
* @return list<Person>
|
||||
*/
|
||||
public function findByPhone(
|
||||
PhoneNumber $phoneNumber,
|
||||
int $start = 0,
|
||||
int $limit = 20
|
||||
): array;
|
||||
}
|
||||
|
@@ -12,12 +12,10 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Repository;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\PersonPhone;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use libphonenumber\PhoneNumber;
|
||||
|
||||
class PersonRepository implements ObjectRepository
|
||||
{
|
||||
@@ -31,8 +29,6 @@ class PersonRepository implements ObjectRepository
|
||||
/**
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function countByPhone(
|
||||
string $phonenumber,
|
||||
@@ -75,8 +71,6 @@ class PersonRepository implements ObjectRepository
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*
|
||||
* @deprecated Use @see{self::findByPhoneNumber} or use a dedicated method in PersonACLAwareRepository
|
||||
*/
|
||||
public function findByPhone(
|
||||
string $phonenumber,
|
||||
@@ -97,25 +91,6 @@ class PersonRepository implements ObjectRepository
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a person which is associated to the given phonenumber, without restrictions
|
||||
* on any.
|
||||
*
|
||||
* @return list<Person>
|
||||
*/
|
||||
public function findByPhoneNumber(PhoneNumber $phoneNumber, int $firstResult = 0, int $maxResults = 50): array
|
||||
{
|
||||
$qb = $this->repository->createQueryBuilder('p');
|
||||
$qb->select('p');
|
||||
|
||||
$this->searchByPhoneNumbers($qb, $phoneNumber);
|
||||
|
||||
$qb->setFirstResult($firstResult)
|
||||
->setMaxResults($maxResults);
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
public function findOneBy(array $criteria)
|
||||
{
|
||||
return $this->repository->findOneBy($criteria);
|
||||
@@ -134,20 +109,6 @@ class PersonRepository implements ObjectRepository
|
||||
}
|
||||
}
|
||||
|
||||
private function searchByPhoneNumbers(QueryBuilder $qb, PhoneNumber $phoneNumber): void
|
||||
{
|
||||
$qb->setParameter('number', $phoneNumber, 'phone_number');
|
||||
|
||||
$orX = $qb->expr()->orX();
|
||||
$orX->add($qb->expr()->eq('p.mobilenumber', ':number'));
|
||||
$orX->add($qb->expr()->eq('p.phonenumber', ':number'));
|
||||
$orX->add(
|
||||
$qb->expr()->exists('SELECT 1 FROM '.PersonPhone::class.' k WHERE k.phonenumber = :number AND k.person = p')
|
||||
);
|
||||
|
||||
$qb->andWhere($orX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
|
@@ -52,7 +52,7 @@
|
||||
|
||||
<script>
|
||||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||
import ClassicEditor from "ChillMainAssets/module/ckeditor5";
|
||||
import ClassicEditor from "../../../../../../ChillMainBundle/Resources/public/module/ckeditor5/editor_config";
|
||||
import { mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
|
@@ -3,10 +3,10 @@
|
||||
<h2><a id="section-10"></a>{{ $t('persons_associated.title')}}</h2>
|
||||
|
||||
<div v-if="currentParticipations.length > 0">
|
||||
<label class="col-form-label">{{ $t('persons_associated.counter', { count: counter }) }}</label>
|
||||
<label class="col-form-label">{{ $tc('persons_associated.counter', counter) }}</label>
|
||||
</div>
|
||||
<div v-else>
|
||||
<label class="chill-no-data-statement">{{ $t('persons_associated.counter', { count: counter }) }}</label>
|
||||
<label class="chill-no-data-statement">{{ $tc('persons_associated.counter', counter) }}</label>
|
||||
</div>
|
||||
|
||||
<div v-if="participationWithoutHousehold.length > 0" class="alert alert-warning no-household">
|
||||
|
@@ -4,10 +4,10 @@
|
||||
<h2><a id="section-90"></a>{{ $t('resources.title')}}</h2>
|
||||
|
||||
<div v-if="resources.length > 0">
|
||||
<label class="col-form-label">{{ $t('resources.counter', { count: counter }) }}</label>
|
||||
<label class="col-form-label">{{ $tc('resources.counter', counter) }}</label>
|
||||
</div>
|
||||
<div v-else>
|
||||
<label class="chill-no-data-statement">{{ $t('resources.counter', { count: counter }) }}</label>
|
||||
<label class="chill-no-data-statement">{{ $tc('resources.counter', counter) }}</label>
|
||||
</div>
|
||||
|
||||
<div class="flex-table mb-3">
|
||||
|
@@ -41,7 +41,7 @@
|
||||
import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue';
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||
import ClassicEditor from "ChillMainAssets/module/ckeditor5";
|
||||
import ClassicEditor from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
|
||||
export default {
|
||||
name: "WriteComment",
|
||||
|
@@ -331,7 +331,7 @@
|
||||
import {mapState, mapGetters,} from 'vuex';
|
||||
import {dateToISO, ISOToDate, ISOToDatetime} from 'ChillMainAssets/chill/js/date';
|
||||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||
import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js';
|
||||
import ClassicEditor from 'ChillMainAssets/module/ckeditor5/editor_config';
|
||||
import AddResult from './components/AddResult.vue';
|
||||
import AddEvaluation from './components/AddEvaluation.vue';
|
||||
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
|
||||
|
@@ -195,7 +195,7 @@
|
||||
<script>
|
||||
import {dateToISO, ISOToDate, ISOToDatetime} from 'ChillMainAssets/chill/js/date';
|
||||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||
import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js';
|
||||
import ClassicEditor from 'ChillMainAssets/module/ckeditor5/editor_config';
|
||||
import { mapGetters, mapState } from 'vuex';
|
||||
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
|
||||
import {buildLink} from 'ChillDocGeneratorAssets/lib/document-generator';
|
||||
|
@@ -23,7 +23,7 @@
|
||||
data-bs-toggle="collapse"
|
||||
aria-expanded="false"
|
||||
@click="toggleHouseholdSuggestion">
|
||||
{{ $t('household_members_editor.show_household_suggestion', { count: countHouseholdSuggestion }) }}
|
||||
{{ $tc('household_members_editor.show_household_suggestion', countHouseholdSuggestion) }}
|
||||
</button>
|
||||
<button v-if="showHouseholdSuggestion"
|
||||
class="accordion-button"
|
||||
|
@@ -75,7 +75,7 @@ div.participation-details {
|
||||
import { mapGetters } from 'vuex';
|
||||
import PersonRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue';
|
||||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||
import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js';
|
||||
import ClassicEditor from 'ChillMainAssets/module/ckeditor5/editor_config';
|
||||
|
||||
export default {
|
||||
name: 'MemberDetails',
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
<script>
|
||||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||
import ClassicEditor from "ChillMainAssets/module/ckeditor5";
|
||||
import ClassicEditor from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
|
||||
export default {
|
||||
name: "PersonComment.vue",
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<div class="search">
|
||||
|
||||
<label class="col-form-label" style="float: right;">
|
||||
{{ $t('add_persons.suggested_counter', { count: suggestedCounter }) }}
|
||||
{{ $tc('add_persons.suggested_counter', suggestedCounter) }}
|
||||
</label>
|
||||
|
||||
<input id="search-persons"
|
||||
@@ -42,7 +42,7 @@
|
||||
</a>
|
||||
</span>
|
||||
<span v-if="selectedCounter > 0">
|
||||
{{ $t('add_persons.selected_counter', { count: selectedCounter }) }}
|
||||
{{ $tc('add_persons.selected_counter', selectedCounter) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -52,7 +52,9 @@
|
||||
{{ $t('renderbox.deathdate') + ' ' + deathdate }}
|
||||
</time>
|
||||
|
||||
<span v-if="options.addAge && person.birthdate" class="age">{{ $t('renderbox.years_old', { n: person.age }) }}</span>
|
||||
<span v-if="options.addAge && person.birthdate" class="age">{{
|
||||
$tc('renderbox.years_old', person.age)
|
||||
}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<span :class="'altname altname-' + altNameKey"> ({{ altNameLabel }})</span>
|
||||
</span>
|
||||
<span v-if="person.suffixText" class="suffixtext"> {{ person.suffixText }}</span>
|
||||
<span class="age" v-if="this.addAge && person.birthdate !== null && person.deathdate === null">{{ $t('renderbox.years_old', { n: person.age }) }}</span>
|
||||
<span class="age" v-if="this.addAge && person.birthdate !== null && person.deathdate === null">{{ $tc('renderbox.years_old', person.age) }}</span>
|
||||
<span v-else-if="this.addAge && person.deathdate !== null"> (‡)</span>
|
||||
</span>
|
||||
</template>
|
||||
|
@@ -15,7 +15,7 @@ const personMessages = {
|
||||
person: {
|
||||
firstname: "Prénom",
|
||||
lastname: "Nom",
|
||||
born: (ctx) => {
|
||||
born: (ctx: {gender: "man"|"woman"|"unknown"}) => {
|
||||
if (ctx.gender === 'man') {
|
||||
return 'Né le';
|
||||
} else if (ctx.gender === 'woman') {
|
@@ -11,17 +11,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\PersonBundle\Tests\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Repository\CenterRepositoryInterface;
|
||||
use Chill\MainBundle\Repository\CountryRepository;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\PersonPhone;
|
||||
use Chill\PersonBundle\Repository\PersonACLAwareRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
@@ -101,67 +98,4 @@ final class PersonACLAwareRepositoryTest extends KernelTestCase
|
||||
$this->assertStringContainsString('diallo', strtolower($person->getFirstName().' '.$person->getLastName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providePersonsWithPhoneNumbers
|
||||
*/
|
||||
public function testFindByPhonenumber(\libphonenumber\PhoneNumber $phoneNumber, ?int $expectedId): void
|
||||
{
|
||||
$user = new User();
|
||||
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class);
|
||||
$authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(PersonVoter::SEE))
|
||||
->willReturn($this->centerRepository->findAll());
|
||||
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->getUser()->willReturn($user);
|
||||
|
||||
$repository = new PersonACLAwareRepository(
|
||||
$security->reveal(),
|
||||
$this->entityManager,
|
||||
$this->countryRepository,
|
||||
$authorizationHelper->reveal()
|
||||
);
|
||||
|
||||
$actual = $repository->findByPhone($phoneNumber, 0, 10);
|
||||
|
||||
if (null === $expectedId) {
|
||||
self::assertCount(0, $actual);
|
||||
} else {
|
||||
$actualIds = array_map(fn (Person $person) => $person->getId(), $actual);
|
||||
|
||||
self::assertContains($expectedId, $actualIds);
|
||||
}
|
||||
}
|
||||
|
||||
public static function providePersonsWithPhoneNumbers(): iterable
|
||||
{
|
||||
self::bootKernel();
|
||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||
$center = $em->createQuery('SELECT c FROM '.Center::class.' c ')->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
$util = \libphonenumber\PhoneNumberUtil::getInstance();
|
||||
|
||||
$mobile = $util->parse('+32486123456');
|
||||
$fixed = $util->parse('+3281136917');
|
||||
$anotherMobile = $util->parse('+32486123478');
|
||||
$person = (new Person())->setFirstName('diallo')->setLastName('diallo')->setCenter($center);
|
||||
$person->setMobilenumber($mobile)->setPhonenumber($fixed);
|
||||
$otherPhone = new PersonPhone();
|
||||
$otherPhone->setPerson($person);
|
||||
$otherPhone->setPhonenumber($anotherMobile);
|
||||
$otherPhone->setType('mobile');
|
||||
|
||||
$em->persist($person);
|
||||
$em->persist($otherPhone);
|
||||
|
||||
$em->flush();
|
||||
|
||||
self::ensureKernelShutdown();
|
||||
|
||||
yield [$mobile, $person->getId()];
|
||||
yield [$anotherMobile, $person->getId()];
|
||||
yield [$fixed, $person->getId()];
|
||||
yield [$util->parse('+331234567890'), null];
|
||||
}
|
||||
}
|
||||
|
@@ -1,163 +0,0 @@
|
||||
components:
|
||||
schemas:
|
||||
Motive:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
label:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
example:
|
||||
fr: Retard de livraison
|
||||
active:
|
||||
type: boolean
|
||||
MotiveById:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- ticket_motive
|
||||
required:
|
||||
- id
|
||||
- type
|
||||
|
||||
paths:
|
||||
/1.0/ticket/motive.json:
|
||||
get:
|
||||
tags:
|
||||
- ticket
|
||||
summary: A list of available ticket's motive
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
|
||||
/1.0/ticket/{id}/motive/set:
|
||||
post:
|
||||
tags:
|
||||
- ticket
|
||||
summary: Replace the existing ticket's motive by a new one
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The ticket id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
motive:
|
||||
$ref: "#/components/schemas/MotiveById"
|
||||
responses:
|
||||
201:
|
||||
description: "ACCEPTED"
|
||||
422:
|
||||
description: "UNPROCESSABLE ENTITY"
|
||||
|
||||
/1.0/ticket/{id}/comment/add:
|
||||
post:
|
||||
tags:
|
||||
- ticket
|
||||
summary: Add a comment to an existing ticket
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The ticket id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
responses:
|
||||
201:
|
||||
description: "ACCEPTED"
|
||||
422:
|
||||
description: "UNPROCESSABLE ENTITY"
|
||||
|
||||
/1.0/ticket/{id}/addressees/set:
|
||||
post:
|
||||
tags:
|
||||
- ticket
|
||||
summary: Set the addresses for an existing ticket (will replace all the existing addresses)
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The ticket id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
addressees:
|
||||
type: array
|
||||
items:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/UserGroupById'
|
||||
- $ref: '#/components/schemas/UserById'
|
||||
|
||||
|
||||
responses:
|
||||
201:
|
||||
description: "ACCEPTED"
|
||||
422:
|
||||
description: "UNPROCESSABLE ENTITY"
|
||||
|
||||
/1.0/ticket/{id}/addressee/add:
|
||||
post:
|
||||
tags:
|
||||
- ticket
|
||||
summary: Add an addressee to a ticket, without removing existing ones.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The ticket id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
addressee:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/UserGroupById'
|
||||
- $ref: '#/components/schemas/UserById'
|
||||
|
||||
|
||||
responses:
|
||||
201:
|
||||
description: "ACCEPTED"
|
||||
422:
|
||||
description: "UNPROCESSABLE ENTITY"
|
@@ -1,4 +0,0 @@
|
||||
module.exports = function(encore, entries) {
|
||||
encore.addEntry('page_ticket', __dirname + '/src/Resources/public/page/ticket/index.ts');
|
||||
encore.addEntry('vue_ticket_app', __dirname + '/src/Resources/public/vuejs/TicketApp/index.ts');
|
||||
};
|
@@ -1,29 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
|
||||
/**
|
||||
* Add a single addressee to the ticket.
|
||||
*
|
||||
* This command is converted into an "SetAddresseesCommand" for handling
|
||||
*/
|
||||
final readonly class AddAddresseeCommand
|
||||
{
|
||||
public function __construct(
|
||||
#[Groups(['read'])]
|
||||
public User|UserGroup $addressee
|
||||
) {}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
final readonly class AddCommentCommand
|
||||
{
|
||||
public function __construct(
|
||||
#[Assert\NotBlank()]
|
||||
#[Assert\NotNull]
|
||||
#[Serializer\Groups(['write'])]
|
||||
public ?string $content = null,
|
||||
) {}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket;
|
||||
|
||||
class AssociateByPhonenumberCommand
|
||||
{
|
||||
public function __construct(
|
||||
public string $phonenumber,
|
||||
) {}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket;
|
||||
|
||||
final readonly class CreateTicketCommand
|
||||
{
|
||||
public function __construct(
|
||||
public string $externalReference = '',
|
||||
) {}
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket\Handler;
|
||||
|
||||
use Chill\TicketBundle\Action\Ticket\AddCommentCommand;
|
||||
use Chill\TicketBundle\Entity\Comment;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final readonly class AddCommentCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
|
||||
public function handle(Ticket $ticket, AddCommentCommand $command): void
|
||||
{
|
||||
$comment = new Comment($command->content, $ticket);
|
||||
|
||||
$this->entityManager->persist($comment);
|
||||
}
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket\Handler;
|
||||
|
||||
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
|
||||
use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
|
||||
use Chill\TicketBundle\Action\Ticket\AssociateByPhonenumberCommand;
|
||||
use Chill\TicketBundle\Entity\PersonHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
|
||||
class AssociateByPhonenumberCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private PersonACLAwareRepositoryInterface $personRepository,
|
||||
private PhonenumberHelper $phonenumberHelper,
|
||||
private ClockInterface $clock,
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
|
||||
public function __invoke(Ticket $ticket, AssociateByPhonenumberCommand $command): void
|
||||
{
|
||||
$phone = $this->phonenumberHelper->parse($command->phonenumber);
|
||||
$persons = $this->personRepository->findByPhone($phone);
|
||||
|
||||
foreach ($persons as $person) {
|
||||
$history = new PersonHistory($person, $ticket, $this->clock->now());
|
||||
$this->entityManager->persist($history);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket\Handler;
|
||||
|
||||
use Chill\TicketBundle\Action\Ticket\CreateTicketCommand;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
|
||||
class CreateTicketCommandHandler
|
||||
{
|
||||
public function __invoke(CreateTicketCommand $command): Ticket
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$ticket->setExternalRef($command->externalReference);
|
||||
|
||||
return $ticket;
|
||||
}
|
||||
}
|
@@ -1,55 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket\Handler;
|
||||
|
||||
use Chill\TicketBundle\Action\Ticket\ReplaceMotiveCommand;
|
||||
use Chill\TicketBundle\Entity\MotiveHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
|
||||
final readonly class ReplaceMotiveCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ClockInterface $clock,
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
|
||||
public function handle(Ticket $ticket, ReplaceMotiveCommand $command): void
|
||||
{
|
||||
if (null === $command->motive) {
|
||||
throw new \InvalidArgumentException('The new motive cannot be null');
|
||||
}
|
||||
|
||||
// will add if there are no existing motive
|
||||
$readyToAdd = 0 === count($ticket->getMotiveHistories());
|
||||
|
||||
foreach ($ticket->getMotiveHistories() as $history) {
|
||||
if (null !== $history->getEndDate()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($history->getMotive() === $command->motive) {
|
||||
// we apply the same motive, we do nothing
|
||||
continue;
|
||||
}
|
||||
|
||||
$history->setEndDate($this->clock->now());
|
||||
$readyToAdd = true;
|
||||
}
|
||||
|
||||
if ($readyToAdd) {
|
||||
$history = new MotiveHistory($command->motive, $ticket, $this->clock->now());
|
||||
$this->entityManager->persist($history);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket\Handler;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\TicketBundle\Action\Ticket\SetAddresseesCommand;
|
||||
use Chill\TicketBundle\Entity\AddresseeHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
final readonly class SetAddresseesCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ClockInterface $clock,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private Security $security,
|
||||
) {}
|
||||
|
||||
public function handle(Ticket $ticket, SetAddresseesCommand $command): void
|
||||
{
|
||||
// remove existing addresses which are not in the new addresses
|
||||
foreach ($ticket->getAddresseeHistories() as $addressHistory) {
|
||||
if (null !== $addressHistory->getEndDate()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_array($addressHistory->getAddressee(), $command->addressees, true)) {
|
||||
$addressHistory->setEndDate($this->clock->now());
|
||||
if (($user = $this->security->getUser()) instanceof User) {
|
||||
$addressHistory->setRemovedBy($user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add new addresses
|
||||
foreach ($command->addressees as $address) {
|
||||
if (in_array($address, $ticket->getCurrentAddressee(), true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$history = new AddresseeHistory($address, $this->clock->now(), $ticket);
|
||||
$this->entityManager->persist($history);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket;
|
||||
|
||||
use Chill\TicketBundle\Entity\Motive;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
final readonly class ReplaceMotiveCommand
|
||||
{
|
||||
public function __construct(
|
||||
#[Assert\NotNull]
|
||||
#[Groups(['write'])]
|
||||
public ?Motive $motive,
|
||||
) {}
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
<?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\TicketBundle\Action\Ticket;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
use Symfony\Component\Validator\Constraints\GreaterThan;
|
||||
|
||||
final readonly class SetAddresseesCommand
|
||||
{
|
||||
public function __construct(
|
||||
/**
|
||||
* @var list<UserGroup|User>
|
||||
*/
|
||||
#[UserGroupDoNotExclude]
|
||||
#[GreaterThan(0)]
|
||||
#[Groups(['read'])]
|
||||
public array $addressees
|
||||
) {}
|
||||
|
||||
public static function fromAddAddresseeCommand(AddAddresseeCommand $command, Ticket $ticket): self
|
||||
{
|
||||
return new self([
|
||||
$command->addressee,
|
||||
...$ticket->getCurrentAddressee(),
|
||||
]);
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
<?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\TicketBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class ChillTicketBundle extends Bundle {}
|
@@ -1,68 +0,0 @@
|
||||
<?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\TicketBundle\Controller;
|
||||
|
||||
use Chill\TicketBundle\Action\Ticket\AddCommentCommand;
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\AddCommentCommandHandler;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
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\SerializerInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
final readonly class AddCommentController
|
||||
{
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private SerializerInterface $serializer,
|
||||
private ValidatorInterface $validator,
|
||||
private AddCommentCommandHandler $addCommentCommandHandler,
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
|
||||
#[Route('/api/1.0/ticket/{id}/comment/add', name: 'chill_ticket_comment_add', methods: ['POST'])]
|
||||
public function __invoke(Ticket $ticket, Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER')) {
|
||||
throw new AccessDeniedHttpException('Only user can add ticket comments.');
|
||||
}
|
||||
|
||||
$command = $this->serializer->deserialize($request->getContent(), AddCommentCommand::class, 'json', ['groups' => 'write']);
|
||||
|
||||
$errors = $this->validator->validate($command);
|
||||
|
||||
if (count($errors) > 0) {
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($errors, 'json'),
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY,
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
$this->addCommentCommandHandler->handle($ticket, $command);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($ticket, 'json', ['groups' => 'read']),
|
||||
Response::HTTP_CREATED,
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
<?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\TicketBundle\Controller;
|
||||
|
||||
use Chill\TicketBundle\Action\Ticket\AssociateByPhonenumberCommand;
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\AssociateByPhonenumberCommandHandler;
|
||||
use Chill\TicketBundle\Action\Ticket\CreateTicketCommand;
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\CreateTicketCommandHandler;
|
||||
use Chill\TicketBundle\Repository\TicketRepositoryInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
final readonly class CreateTicketController
|
||||
{
|
||||
public function __construct(
|
||||
private CreateTicketCommandHandler $createTicketCommandHandler,
|
||||
private AssociateByPhonenumberCommandHandler $associateByPhonenumberCommandHandler,
|
||||
private Security $security,
|
||||
private UrlGeneratorInterface $urlGenerator,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private TicketRepositoryInterface $ticketRepository,
|
||||
) {}
|
||||
|
||||
#[Route('{_locale}/ticket/ticket/create')]
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER')) {
|
||||
throw new AccessDeniedHttpException('Only users are allowed to create tickets.');
|
||||
}
|
||||
|
||||
if ('' !== $extId = $request->query->get('extId', '')) {
|
||||
if (null !== $ticket = $this->ticketRepository->findOneByExternalRef($extId)) {
|
||||
return new RedirectResponse(
|
||||
$this->urlGenerator->generate('chill_ticket_ticket_edit', ['id' => $ticket->getId()])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$createCommand = new CreateTicketCommand($request->query->get('extId', ''));
|
||||
$ticket = $this->createTicketCommandHandler->__invoke($createCommand);
|
||||
|
||||
$this->entityManager->persist($ticket);
|
||||
|
||||
if ($request->query->has('caller')) {
|
||||
$associateByPhonenumberCommand = new AssociateByPhonenumberCommand($request->query->get('caller'));
|
||||
$this->associateByPhonenumberCommandHandler->__invoke($ticket, $associateByPhonenumberCommand);
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new RedirectResponse(
|
||||
$this->urlGenerator->generate('chill_ticket_ticket_edit', ['id' => $ticket->getId()])
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
<?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\TicketBundle\Controller;
|
||||
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Twig\Environment;
|
||||
|
||||
class EditTicketController
|
||||
{
|
||||
public function __construct(
|
||||
private Environment $templating
|
||||
) {}
|
||||
|
||||
#[Route('/{_locale}/ticket/ticket/{id}/edit', name: 'chill_ticket_ticket_edit')]
|
||||
public function __invoke(
|
||||
Ticket $ticket
|
||||
): Response {
|
||||
return new Response(
|
||||
$this->templating->render(
|
||||
'@ChillTicket/Ticket/edit.html.twig',
|
||||
[
|
||||
'ticket' => $ticket,
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
<?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\TicketBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
|
||||
use Chill\PersonBundle\Repository\PersonRepository;
|
||||
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
|
||||
use libphonenumber\NumberParseException;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* Controller for a rest api to find a caller for a given phonenumber.
|
||||
*
|
||||
* TODO: currently, this rest api is not secured
|
||||
*/
|
||||
class FindCallerController
|
||||
{
|
||||
public function __construct(private PhonenumberHelper $phonenumberHelper, private PersonRepository $personRepository, private PersonRenderInterface $personRender) {}
|
||||
|
||||
#[Route('/public/api/1.0/ticket/find-caller', name: 'find-caller', methods: ['GET'])]
|
||||
public function findCaller(Request $request): Response
|
||||
{
|
||||
$caller = $request->query->get('caller', '');
|
||||
|
||||
if ('' === $caller) {
|
||||
throw new BadRequestHttpException('Missing "caller" query parameter');
|
||||
}
|
||||
|
||||
try {
|
||||
$phoneNumber = $this->phonenumberHelper->parse($caller);
|
||||
} catch (NumberParseException $e) {
|
||||
throw new BadRequestHttpException('Unable to parse number', $e);
|
||||
}
|
||||
|
||||
$persons = $this->personRepository->findByPhoneNumber($phoneNumber, 0, 2);
|
||||
|
||||
$asArray = match (count($persons)) {
|
||||
0 => ['found' => false, 'name' => null],
|
||||
1 => ['found' => true, 'name' => $this->personRender->renderString($persons[0], ['addAge' => false])],
|
||||
default => ['found' => true, 'name' => 'multiple'],
|
||||
};
|
||||
|
||||
return new JsonResponse($asArray);
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
<?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\TicketBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class MotiveApiController extends ApiController
|
||||
{
|
||||
protected function customizeQuery(string $action, Request $request, $query): void
|
||||
{
|
||||
/* @var $query QueryBuilder */
|
||||
$query->andWhere('e.active = TRUE');
|
||||
}
|
||||
}
|
@@ -1,69 +0,0 @@
|
||||
<?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\TicketBundle\Controller;
|
||||
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\ReplaceMotiveCommandHandler;
|
||||
use Chill\TicketBundle\Action\Ticket\ReplaceMotiveCommand;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
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 Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
final readonly class ReplaceMotiveController
|
||||
{
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private ReplaceMotiveCommandHandler $replaceMotiveCommandHandler,
|
||||
private SerializerInterface $serializer,
|
||||
private ValidatorInterface $validator,
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
|
||||
#[Route('/api/1.0/ticket/{id}/motive/set', name: 'chill_ticket_motive_set', methods: ['POST'])]
|
||||
public function __invoke(Ticket $ticket, Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER')) {
|
||||
throw new AccessDeniedHttpException('');
|
||||
}
|
||||
|
||||
$command = $this->serializer->deserialize($request->getContent(), ReplaceMotiveCommand::class, 'json', [
|
||||
AbstractNormalizer::GROUPS => ['write'],
|
||||
]);
|
||||
|
||||
$errors = $this->validator->validate($command);
|
||||
|
||||
if (0 < $errors->count()) {
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($errors, 'json'),
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY,
|
||||
);
|
||||
}
|
||||
|
||||
$this->replaceMotiveCommandHandler->handle($ticket, $command);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($ticket, 'json', ['groups' => 'read']),
|
||||
Response::HTTP_CREATED,
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,85 +0,0 @@
|
||||
<?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\TicketBundle\Controller;
|
||||
|
||||
use Chill\TicketBundle\Action\Ticket\AddAddresseeCommand;
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\SetAddresseesCommandHandler;
|
||||
use Chill\TicketBundle\Action\Ticket\SetAddresseesCommand;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
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 Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
final readonly class SetAddresseesController
|
||||
{
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private SerializerInterface $serializer,
|
||||
private SetAddresseesCommandHandler $addressesCommandHandler,
|
||||
private ValidatorInterface $validator,
|
||||
) {}
|
||||
|
||||
#[Route('/api/1.0/ticket/{id}/addressees/set', methods: ['POST'])]
|
||||
public function setAddressees(Ticket $ticket, Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER')) {
|
||||
throw new AccessDeniedHttpException('Only users can set addressees.');
|
||||
}
|
||||
|
||||
$command = $this->serializer->deserialize($request->getContent(), SetAddresseesCommand::class, 'json', [AbstractNormalizer::GROUPS => ['read']]);
|
||||
|
||||
return $this->registerSetAddressees($command, $ticket);
|
||||
}
|
||||
|
||||
#[Route('/api/1.0/ticket/{id}/addressee/add', methods: ['POST'])]
|
||||
public function addAddressee(Ticket $ticket, Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER')) {
|
||||
throw new AccessDeniedHttpException('Only users can add addressees.');
|
||||
}
|
||||
|
||||
$command = $this->serializer->deserialize($request->getContent(), AddAddresseeCommand::class, 'json', [AbstractNormalizer::GROUPS => ['read']]);
|
||||
|
||||
return $this->registerSetAddressees(SetAddresseesCommand::fromAddAddresseeCommand($command, $ticket), $ticket);
|
||||
}
|
||||
|
||||
private function registerSetAddressees(SetAddresseesCommand $command, Ticket $ticket): Response
|
||||
{
|
||||
if (0 < count($errors = $this->validator->validate($command))) {
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($errors, 'json'),
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY,
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
$this->addressesCommandHandler->handle($ticket, $command);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($ticket, 'json', ['groups' => 'read']),
|
||||
Response::HTTP_OK,
|
||||
[],
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,82 +0,0 @@
|
||||
<?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\TicketBundle\DataFixtures\ORM;
|
||||
|
||||
use Chill\TicketBundle\Entity\Motive;
|
||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
|
||||
final class LoadMotives extends Fixture implements FixtureGroupInterface
|
||||
{
|
||||
public static function getGroups(): array
|
||||
{
|
||||
return ['ticket'];
|
||||
}
|
||||
|
||||
public function load(ObjectManager $manager)
|
||||
{
|
||||
foreach (explode("\n", self::MOTIVES) as $label) {
|
||||
if ('' === trim($label)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$motive = new Motive();
|
||||
$motive->setLabel(['fr' => trim($label)]);
|
||||
|
||||
$manager->persist($motive);
|
||||
}
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
private const MOTIVES = <<<'TXT'
|
||||
Coordonnées
|
||||
Horaire de passage
|
||||
Retard de livraison
|
||||
Erreur de livraison
|
||||
Colis incomplet
|
||||
MATLOC
|
||||
Retard DASRI
|
||||
Planning d'astreintes
|
||||
Planning des tournées
|
||||
Contrôle pompe
|
||||
Changement de rendez-vous
|
||||
Renseignement facturation/prestation
|
||||
Décès patient
|
||||
Demande de prise en charge
|
||||
Information absence
|
||||
Demande bulletin de situation
|
||||
Difficultés accès logement
|
||||
Déplacement inutile
|
||||
Problème de prélèvement/de commande
|
||||
Parc auto
|
||||
Demande d'admission
|
||||
Retrait de matériel au domicile
|
||||
Comptes-rendus
|
||||
Démarchage commercial
|
||||
Demande de transport
|
||||
Demande laboratoire
|
||||
Demande admission
|
||||
Suivi de prise en charge
|
||||
Mauvaise adresse
|
||||
Patient absent
|
||||
Annulation
|
||||
Colis perdu
|
||||
Changement de rendez-vous
|
||||
Coordination interservices
|
||||
Problème de substitution produits
|
||||
Problème ordonnance
|
||||
Réclamations facture
|
||||
Préparation urgente
|
||||
TXT;
|
||||
}
|
@@ -1,64 +0,0 @@
|
||||
<?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\TicketBundle\DependencyInjection;
|
||||
|
||||
use Chill\TicketBundle\Controller\MotiveApiController;
|
||||
use Chill\TicketBundle\Entity\Motive;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\Loader;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class ChillTicketExtension extends Extension implements PrependExtensionInterface
|
||||
{
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
||||
$loader->load('services.yaml');
|
||||
}
|
||||
|
||||
public function prepend(ContainerBuilder $container)
|
||||
{
|
||||
$this->prependApi($container);
|
||||
}
|
||||
|
||||
private function prependApi(ContainerBuilder $container): void
|
||||
{
|
||||
$container->prependExtensionConfig('chill_main', [
|
||||
'apis' => [
|
||||
[
|
||||
'class' => Motive::class,
|
||||
'name' => 'motive',
|
||||
'base_path' => '/api/1.0/ticket/motive',
|
||||
'controller' => MotiveApiController::class,
|
||||
'base_role' => 'ROLE_USER',
|
||||
'actions' => [
|
||||
'_index' => [
|
||||
'methods' => [
|
||||
Request::METHOD_GET => true,
|
||||
Request::METHOD_HEAD => true,
|
||||
],
|
||||
],
|
||||
'_entity' => [
|
||||
'methods' => [
|
||||
Request::METHOD_GET => true,
|
||||
Request::METHOD_HEAD => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
@@ -1,130 +0,0 @@
|
||||
<?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\TicketBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
#[ORM\Entity()]
|
||||
#[ORM\Table(name: 'addressee_history', schema: 'chill_ticket')]
|
||||
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['ticket_addressee_history' => AddresseeHistory::class])]
|
||||
class AddresseeHistory implements TrackUpdateInterface, TrackCreationInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
use TrackUpdateTrait;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
private ?User $addresseeUser = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: UserGroup::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
private ?UserGroup $addresseeGroup = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => 'null'])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?\DateTimeImmutable $endDate = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?User $removedBy = null;
|
||||
|
||||
public function __construct(
|
||||
User|UserGroup $addressee,
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: false)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private \DateTimeImmutable $startDate,
|
||||
#[ORM\ManyToOne(targetEntity: Ticket::class)]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Ticket $ticket,
|
||||
) {
|
||||
if ($addressee instanceof User) {
|
||||
$this->addresseeUser = $addressee;
|
||||
} else {
|
||||
$this->addresseeGroup = $addressee;
|
||||
}
|
||||
|
||||
$this->ticket->addAddresseeHistory($this);
|
||||
}
|
||||
|
||||
#[Serializer\Groups(['read'])]
|
||||
public function getAddressee(): UserGroup|User
|
||||
{
|
||||
if (null !== $this->addresseeGroup) {
|
||||
return $this->addresseeGroup;
|
||||
}
|
||||
|
||||
return $this->addresseeUser;
|
||||
}
|
||||
|
||||
public function getAddresseeGroup(): ?UserGroup
|
||||
{
|
||||
return $this->addresseeGroup;
|
||||
}
|
||||
|
||||
public function getAddresseeUser(): ?User
|
||||
{
|
||||
return $this->addresseeUser;
|
||||
}
|
||||
|
||||
public function getEndDate(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getStartDate(): \DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function getTicket(): Ticket
|
||||
{
|
||||
return $this->ticket;
|
||||
}
|
||||
|
||||
public function getRemovedBy(): ?User
|
||||
{
|
||||
return $this->removedBy;
|
||||
}
|
||||
|
||||
public function setRemovedBy(?User $removedBy): self
|
||||
{
|
||||
$this->removedBy = $removedBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEndDate(?\DateTimeImmutable $endDate): self
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
<?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\TicketBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\ORM\Mapping\JoinColumn;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
#[ORM\Entity()]
|
||||
#[ORM\Table(name: 'comment', schema: 'chill_ticket')]
|
||||
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['ticket_comment' => Comment::class])]
|
||||
class Comment implements TrackCreationInterface, TrackUpdateInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
use TrackUpdateTrait;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
public function __construct(
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private string $content,
|
||||
#[ORM\ManyToOne(targetEntity: Ticket::class, inversedBy: 'comments')]
|
||||
#[JoinColumn(nullable: false)]
|
||||
private Ticket $ticket,
|
||||
) {
|
||||
$ticket->addComment($this);
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getContent(): string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function getTicket(): Ticket
|
||||
{
|
||||
return $this->ticket;
|
||||
}
|
||||
}
|
@@ -1,91 +0,0 @@
|
||||
<?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\TicketBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity()]
|
||||
#[ORM\Table(name: 'input_history', schema: 'chill_ticket')]
|
||||
class InputHistory
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Person::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
private ?Person $person = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: ThirdParty::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
private ?ThirdParty $thirdParty = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
|
||||
private ?\DateTimeImmutable $endDate = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
private ?User $removedBy = null;
|
||||
|
||||
public function __construct(
|
||||
Person|ThirdParty $input,
|
||||
#[ORM\ManyToOne(targetEntity: Ticket::class)]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Ticket $ticket,
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: false)]
|
||||
private \DateTimeImmutable $startDate,
|
||||
) {
|
||||
if ($input instanceof Person) {
|
||||
$this->person = $input;
|
||||
} else {
|
||||
$this->thirdParty = $input;
|
||||
}
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getEndDate(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function getRemovedBy(): ?User
|
||||
{
|
||||
return $this->removedBy;
|
||||
}
|
||||
|
||||
public function getStartDate(): \DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function getTicket(): Ticket
|
||||
{
|
||||
return $this->ticket;
|
||||
}
|
||||
|
||||
public function getInput(): Person|ThirdParty
|
||||
{
|
||||
if (null !== $this->person) {
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
return $this->thirdParty;
|
||||
}
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
<?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\TicketBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
#[ORM\Entity()]
|
||||
#[ORM\Table(name: 'motive', schema: 'chill_ticket')]
|
||||
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['ticket_motive' => Motive::class])]
|
||||
class Motive
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private array $label = [];
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN, nullable: false, options: ['default' => true])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private bool $active = true;
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function setActive(bool $active): void
|
||||
{
|
||||
$this->active = $active;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): array
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(array $label): void
|
||||
{
|
||||
$this->label = $label;
|
||||
}
|
||||
}
|
@@ -1,80 +0,0 @@
|
||||
<?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\TicketBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'motives_history', schema: 'chill_ticket')]
|
||||
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['ticket_motive_history' => MotiveHistory::class])]
|
||||
class MotiveHistory implements TrackCreationInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?\DateTimeImmutable $endDate = null;
|
||||
|
||||
public function __construct(
|
||||
#[ORM\ManyToOne(targetEntity: Motive::class)]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private Motive $motive,
|
||||
#[ORM\ManyToOne(targetEntity: Ticket::class)]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Ticket $ticket,
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: false)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private \DateTimeImmutable $startDate = new \DateTimeImmutable('now')
|
||||
) {
|
||||
$ticket->addMotiveHistory($this);
|
||||
}
|
||||
|
||||
public function getEndDate(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getMotive(): Motive
|
||||
{
|
||||
return $this->motive;
|
||||
}
|
||||
|
||||
public function getStartDate(): \DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function getTicket(): Ticket
|
||||
{
|
||||
return $this->ticket;
|
||||
}
|
||||
|
||||
public function setEndDate(?\DateTimeImmutable $endDate): void
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
}
|
||||
}
|
@@ -1,94 +0,0 @@
|
||||
<?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\TicketBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'person_history', schema: 'chill_ticket')]
|
||||
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['ticket_person_history' => PersonHistory::class])]
|
||||
class PersonHistory implements TrackCreationInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?\DateTimeImmutable $endDate = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?User $removedBy = null;
|
||||
|
||||
public function __construct(
|
||||
#[ORM\ManyToOne(targetEntity: Person::class, fetch: 'EAGER')]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private Person $person,
|
||||
#[ORM\ManyToOne(targetEntity: Ticket::class)]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Ticket $ticket,
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: false)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private \DateTimeImmutable $startDate,
|
||||
) {
|
||||
// keep ticket instance in sync with this
|
||||
$this->ticket->addPersonHistory($this);
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPerson(): Person
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
public function getEndDate(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function getTicket(): Ticket
|
||||
{
|
||||
return $this->ticket;
|
||||
}
|
||||
|
||||
public function getStartDate(): \DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function getRemovedBy(): ?User
|
||||
{
|
||||
return $this->removedBy;
|
||||
}
|
||||
|
||||
public function setEndDate(?\DateTimeImmutable $endDate): self
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -1,231 +0,0 @@
|
||||
<?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\TicketBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\ReadableCollection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'ticket', schema: 'chill_ticket')]
|
||||
class Ticket implements TrackCreationInterface, TrackUpdateInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
use TrackUpdateTrait;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, AddresseeHistory>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: AddresseeHistory::class, mappedBy: 'ticket')]
|
||||
private Collection $addresseeHistory;
|
||||
|
||||
/**
|
||||
* @var Collection<int, Comment>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'ticket')]
|
||||
private Collection $comments;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
|
||||
private string $externalRef = '';
|
||||
|
||||
/**
|
||||
* @var Collection<int, InputHistory>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: InputHistory::class, mappedBy: 'ticket')]
|
||||
private Collection $inputHistories;
|
||||
|
||||
/**
|
||||
* @var Collection<int, MotiveHistory>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: MotiveHistory::class, mappedBy: 'ticket')]
|
||||
private Collection $motiveHistories;
|
||||
|
||||
/**
|
||||
* @var Collection<int, PersonHistory>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: PersonHistory::class, mappedBy: 'ticket')]
|
||||
private Collection $personHistories;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
private ?User $updatedBy = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true)]
|
||||
private ?\DateTimeImmutable $createdAt = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->addresseeHistory = new ArrayCollection();
|
||||
$this->comments = new ArrayCollection();
|
||||
$this->motiveHistories = new ArrayCollection();
|
||||
$this->personHistories = new ArrayCollection();
|
||||
$this->inputHistories = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getExternalRef(): string
|
||||
{
|
||||
return $this->externalRef;
|
||||
}
|
||||
|
||||
public function setExternalRef(string $externalRef): void
|
||||
{
|
||||
$this->externalRef = $externalRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Person>
|
||||
*/
|
||||
public function getPersons(): array
|
||||
{
|
||||
return $this->personHistories
|
||||
->filter(fn (PersonHistory $personHistory) => null === $personHistory->getEndDate())
|
||||
->map(fn (PersonHistory $personHistory) => $personHistory->getPerson())
|
||||
->getValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal use @see{Comment::__construct} instead
|
||||
*/
|
||||
public function addComment(Comment $comment): void
|
||||
{
|
||||
$this->comments->add($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a PersonHistory.
|
||||
*
|
||||
* @internal use @see{PersonHistory::__construct} instead
|
||||
*/
|
||||
public function addPersonHistory(PersonHistory $personHistory): void
|
||||
{
|
||||
$this->personHistories->add($personHistory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal use @see{MotiveHistory::__construct} instead
|
||||
*/
|
||||
public function addMotiveHistory(MotiveHistory $motiveHistory): void
|
||||
{
|
||||
$this->motiveHistories->add($motiveHistory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal use @see{AddresseHistory::__construct} instead
|
||||
*/
|
||||
public function addAddresseeHistory(AddresseeHistory $addresseeHistory): void
|
||||
{
|
||||
$this->addresseeHistory->add($addresseeHistory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<UserGroup|User>
|
||||
*/
|
||||
public function getCurrentAddressee(): array
|
||||
{
|
||||
$addresses = [];
|
||||
|
||||
foreach ($this->addresseeHistory
|
||||
->filter(fn (AddresseeHistory $addresseeHistory) => null === $addresseeHistory->getEndDate()) as $addressHistory) {
|
||||
$addresses[] = $addressHistory->getAddressee();
|
||||
}
|
||||
|
||||
return $addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReadableCollection<int, Comment>
|
||||
*/
|
||||
public function getComments(): ReadableCollection
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<ThirdParty|Person>
|
||||
*/
|
||||
public function getCurrentInputs(): array
|
||||
{
|
||||
$inputs = [];
|
||||
|
||||
foreach ($this->inputHistories
|
||||
->filter(fn (InputHistory $inputHistory) => null === $inputHistory->getEndDate()) as $inputHistory
|
||||
) {
|
||||
$inputs[] = $inputHistory->getInput();
|
||||
}
|
||||
|
||||
return $inputs;
|
||||
}
|
||||
|
||||
public function getMotive(): ?Motive
|
||||
{
|
||||
foreach ($this->motiveHistories as $motiveHistory) {
|
||||
if (null === $motiveHistory->getEndDate()) {
|
||||
return $motiveHistory->getMotive();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReadableCollection<int, MotiveHistory>
|
||||
*/
|
||||
public function getMotiveHistories(): ReadableCollection
|
||||
{
|
||||
return $this->motiveHistories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReadableCollection<int, PersonHistory>
|
||||
*/
|
||||
public function getPersonHistories(): ReadableCollection
|
||||
{
|
||||
return $this->personHistories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReadableCollection<int, AddresseeHistory>
|
||||
*/
|
||||
public function getAddresseeHistories(): ReadableCollection
|
||||
{
|
||||
return $this->addresseeHistory;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): \DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function getUpdatedBy(): ?User
|
||||
{
|
||||
return $this->updatedBy;
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
<?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\TicketBundle\Repository;
|
||||
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
final readonly class TicketRepository implements TicketRepositoryInterface
|
||||
{
|
||||
private ObjectRepository $repository;
|
||||
|
||||
public function __construct(EntityManagerInterface $objectManager)
|
||||
{
|
||||
$this->repository = $objectManager->getRepository($this->getClassName());
|
||||
}
|
||||
|
||||
public function find($id): ?Ticket
|
||||
{
|
||||
return $this->repository->find($id);
|
||||
}
|
||||
|
||||
public function findAll(): array
|
||||
{
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||
{
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
public function findOneBy(array $criteria): ?Ticket
|
||||
{
|
||||
return $this->repository->findOneBy($criteria);
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
{
|
||||
return Ticket::class;
|
||||
}
|
||||
|
||||
public function findOneByExternalRef(string $extId): ?Ticket
|
||||
{
|
||||
return $this->repository->findOneBy(['externalRef' => $extId]);
|
||||
}
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
<?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\TicketBundle\Repository;
|
||||
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
|
||||
/**
|
||||
* @extends ObjectRepository<Ticket>
|
||||
*/
|
||||
interface TicketRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function findOneByExternalRef(string $extId): ?Ticket;
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
@import '~ChillMainAssets/module/bootstrap/shared';
|
||||
|
||||
div.banner {
|
||||
div#header-ticket-main {
|
||||
background: none repeat scroll 0 0 #ae986fFF;
|
||||
color: $white;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
div#header-ticket-details {
|
||||
background: none repeat scroll 0 0 #d3c7b1FF;
|
||||
color: $white;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
div.contact {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
& > * {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1 +0,0 @@
|
||||
import './banner.scss';
|
@@ -1,86 +0,0 @@
|
||||
import {
|
||||
DateTime,
|
||||
TranslatableString,
|
||||
User,
|
||||
UserGroupOrUser
|
||||
} from "../../../../ChillMainBundle/Resources/public/types";
|
||||
import { Person } from "../../../../ChillPersonBundle/Resources/public/types";
|
||||
|
||||
export interface Motive {
|
||||
type: "ticket_motive"
|
||||
id: number,
|
||||
active: boolean,
|
||||
label: TranslatableString
|
||||
}
|
||||
|
||||
interface TicketHistory<T extends string, D extends object> {
|
||||
event_type: T,
|
||||
at: DateTime,
|
||||
by: User,
|
||||
data: D
|
||||
}
|
||||
|
||||
export interface PersonHistory {
|
||||
type: "ticket_person_history",
|
||||
id: number,
|
||||
startDate: DateTime,
|
||||
endDate: null|DateTime,
|
||||
person: Person,
|
||||
removedBy: null,
|
||||
createdBy: User|null,
|
||||
createdAt: DateTime|null
|
||||
}
|
||||
|
||||
export interface MotiveHistory {
|
||||
type: "ticket_motive_history",
|
||||
id: number,
|
||||
startDate: null,
|
||||
endDate: null|DateTime,
|
||||
motive: Motive,
|
||||
createdBy: User|null,
|
||||
createdAt: DateTime|null,
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
type: "ticket_comment",
|
||||
id: number,
|
||||
content: string,
|
||||
createdBy: User|null,
|
||||
createdAt: DateTime|null,
|
||||
updatedBy: User|null,
|
||||
updatedAt: DateTime|null,
|
||||
}
|
||||
|
||||
export interface AddresseeHistory {
|
||||
type: "ticket_addressee_history",
|
||||
id: number,
|
||||
startDate: DateTime|null,
|
||||
addressee: UserGroupOrUser,
|
||||
endDate: DateTime|null,
|
||||
removedBy: User|null,
|
||||
createdBy: User|null,
|
||||
createdAt: DateTime|null,
|
||||
updatedBy: User|null,
|
||||
updatedAt: DateTime|null,
|
||||
}
|
||||
|
||||
interface AddPersonEvent extends TicketHistory<"add_person", PersonHistory> {};
|
||||
interface AddCommentEvent extends TicketHistory<"add_comment", Comment> {};
|
||||
interface SetMotiveEvent extends TicketHistory<"set_motive", MotiveHistory> {};
|
||||
interface AddAddressee extends TicketHistory<"add_addressee", AddresseeHistory> {};
|
||||
interface RemoveAddressee extends TicketHistory<"remove_addressee", AddresseeHistory> {};
|
||||
|
||||
type TicketHistoryLine = AddPersonEvent | AddCommentEvent | SetMotiveEvent | AddAddressee | RemoveAddressee;
|
||||
|
||||
export interface Ticket {
|
||||
type: "ticket_ticket",
|
||||
id: number,
|
||||
externalRef: string,
|
||||
currentAddressees: UserGroupOrUser[],
|
||||
currentPersons: Person[],
|
||||
currentMotive: null|Motive,
|
||||
history: TicketHistoryLine[],
|
||||
createdAt: DateTime|null,
|
||||
updatedBy: User|null,
|
||||
}
|
||||
|
@@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<banner-component :ticket="ticket" />
|
||||
<div class="container-xxl pt-1" style="padding-bottom: 55px">
|
||||
<ticket-selector-component :tickets="[]" />
|
||||
<ticket-history-list-component :history="ticketHistory" />
|
||||
</div>
|
||||
<action-toolbar-component />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, inject, onMounted, ref } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
// Types
|
||||
import { Motive, Ticket } from "../../types";
|
||||
|
||||
// Components
|
||||
import TicketSelectorComponent from "./components/TicketSelectorComponent.vue";
|
||||
import TicketHistoryListComponent from "./components/TicketHistoryListComponent.vue";
|
||||
import ActionToolbarComponent from "./components/ActionToolbarComponent.vue";
|
||||
import BannerComponent from "./components/BannerComponent.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "App",
|
||||
components: {
|
||||
TicketSelectorComponent,
|
||||
TicketHistoryListComponent,
|
||||
ActionToolbarComponent,
|
||||
BannerComponent,
|
||||
},
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const toast = inject("toast") as any;
|
||||
|
||||
store.commit("setTicket", JSON.parse(window.initialTicket) as Ticket);
|
||||
|
||||
const motives = computed(() => store.getters.getMotives as Motive[]);
|
||||
const ticket = computed(() => store.getters.getTicket as Ticket);
|
||||
|
||||
const ticketHistory = computed(
|
||||
() => store.getters.getDistinctAddressesHistory
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await store.dispatch("fetchMotives");
|
||||
await store.dispatch("fetchUserGroups");
|
||||
await store.dispatch("fetchUsers");
|
||||
} catch (error) {
|
||||
toast.error(error);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
ticketHistory,
|
||||
motives,
|
||||
ticket,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,254 +0,0 @@
|
||||
<template>
|
||||
<div class="fixed-bottom">
|
||||
<div class="footer-ticket-details" v-if="activeTab">
|
||||
<div class="tab-content p-2">
|
||||
<div>
|
||||
<label class="col-form-label">
|
||||
{{ $t(`${activeTab}.title`) }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="submitAction">
|
||||
<add-comment-component
|
||||
v-model="content"
|
||||
v-if="activeTab === 'add_comment'"
|
||||
/>
|
||||
|
||||
<addressee-selector-component
|
||||
v-model="addressees"
|
||||
:user-groups="userGroups"
|
||||
:users="users"
|
||||
v-if="activeTab === 'add_addressee'"
|
||||
/>
|
||||
|
||||
<motive-selector-component
|
||||
v-model="motive"
|
||||
:motives="motives"
|
||||
v-if="activeTab === 'set_motive'"
|
||||
/>
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="cancel">
|
||||
<button
|
||||
@click="activeTab = ''"
|
||||
class="btn btn-cancel"
|
||||
>
|
||||
{{ $t("ticket.cancel") }}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-create" type="submit">
|
||||
{{ $t("ticket.save") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-ticket-main">
|
||||
<ul class="nav nav-tabs justify-content-end">
|
||||
<li class="nav-item p-2">
|
||||
<button
|
||||
type="button"
|
||||
:class="`btn ${
|
||||
activeTab === 'set_motive'
|
||||
? 'btn-primary'
|
||||
: 'btn-light'
|
||||
}`"
|
||||
@click="
|
||||
activeTab === 'set_motive'
|
||||
? (activeTab = '')
|
||||
: (activeTab = 'set_motive')
|
||||
"
|
||||
>
|
||||
<i :class="actionIcons['set_motive']"></i>
|
||||
{{ $t("set_motive.title") }}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item p-2">
|
||||
<button
|
||||
type="button"
|
||||
:class="`btn ${
|
||||
activeTab === 'add_comment'
|
||||
? 'btn-primary'
|
||||
: 'btn-light'
|
||||
}`"
|
||||
@click="
|
||||
activeTab === 'add_comment'
|
||||
? (activeTab = '')
|
||||
: (activeTab = 'add_comment')
|
||||
"
|
||||
>
|
||||
<i :class="actionIcons['add_comment']"></i>
|
||||
{{ $t("add_comment.title") }}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item p-2">
|
||||
<button
|
||||
type="button"
|
||||
:class="`btn ${
|
||||
activeTab === 'add_addressee'
|
||||
? 'btn-primary'
|
||||
: 'btn-light'
|
||||
}`"
|
||||
@click="
|
||||
activeTab === 'add_addressee'
|
||||
? (activeTab = '')
|
||||
: (activeTab = 'add_addressee')
|
||||
"
|
||||
>
|
||||
<i :class="actionIcons['add_addressee']"></i>
|
||||
{{ $t("add_addressee.title") }}
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<li class="nav-item p-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-light"
|
||||
@click="handleClick()"
|
||||
>
|
||||
<i class="fa fa-bolt"></i>
|
||||
{{ $t("ticket.close") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, inject, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
// Types
|
||||
import {
|
||||
User,
|
||||
UserGroup,
|
||||
UserGroupOrUser,
|
||||
} from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import { Comment, Motive, Ticket } from "../../../types";
|
||||
|
||||
// Component
|
||||
import MotiveSelectorComponent from "./MotiveSelectorComponent.vue";
|
||||
import AddresseeSelectorComponent from "./AddresseeSelectorComponent.vue";
|
||||
import AddCommentComponent from "./AddCommentComponent.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "ActionToolbarComponent",
|
||||
components: {
|
||||
AddCommentComponent,
|
||||
MotiveSelectorComponent,
|
||||
AddresseeSelectorComponent,
|
||||
},
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const { t } = useI18n();
|
||||
const toast = inject("toast") as any;
|
||||
const activeTab = ref(
|
||||
"" as "" | "add_comment" | "set_motive" | "add_addressee"
|
||||
);
|
||||
|
||||
const ticket = computed(() => store.getters.getTicket as Ticket);
|
||||
const motives = computed(() => store.getters.getMotives as Motive[]);
|
||||
const userGroups = computed(
|
||||
() => store.getters.getUserGroups as UserGroup[]
|
||||
);
|
||||
const users = computed(() => store.getters.getUsers as User[]);
|
||||
|
||||
const motive = ref(
|
||||
ticket.value.currentMotive
|
||||
? ticket.value.currentMotive
|
||||
: ({} as Motive)
|
||||
);
|
||||
const content = ref("" as Comment["content"]);
|
||||
const addressees = ref(
|
||||
ticket.value.currentAddressees as Array<UserGroupOrUser>
|
||||
);
|
||||
|
||||
async function submitAction() {
|
||||
try {
|
||||
switch (activeTab.value) {
|
||||
case "add_comment":
|
||||
if (!content.value) {
|
||||
toast.error(t("add_comment.error"));
|
||||
} else {
|
||||
await store.dispatch("createComment", {
|
||||
ticketId: ticket.value.id,
|
||||
content: content.value,
|
||||
});
|
||||
content.value = "";
|
||||
activeTab.value = "";
|
||||
toast.success(t("add_comment.success"));
|
||||
}
|
||||
break;
|
||||
case "set_motive":
|
||||
if (!motive.value.id) {
|
||||
toast.error(t("set_motive.error"));
|
||||
} else {
|
||||
await store.dispatch("createMotive", {
|
||||
ticketId: ticket.value.id,
|
||||
motive: motive.value,
|
||||
});
|
||||
activeTab.value = "";
|
||||
toast.success(t("set_motive.success"));
|
||||
}
|
||||
break;
|
||||
case "add_addressee":
|
||||
if (!addressees.value.length) {
|
||||
toast.error(t("add_addressee.error"));
|
||||
} else {
|
||||
await store.dispatch("setAdressees", {
|
||||
ticketId: ticket.value.id,
|
||||
addressees: addressees.value,
|
||||
});
|
||||
activeTab.value = "";
|
||||
toast.success(t("add_addressee.success"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
alert("Sera disponible plus tard");
|
||||
}
|
||||
|
||||
return {
|
||||
actionIcons: ref(store.getters.getActionIcons),
|
||||
activeTab,
|
||||
ticket,
|
||||
motives,
|
||||
motive,
|
||||
userGroups,
|
||||
addressees,
|
||||
users,
|
||||
content,
|
||||
submitAction,
|
||||
handleClick,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sticky-form-buttons {
|
||||
margin-top: 0px;
|
||||
background: none;
|
||||
}
|
||||
div.footer-ticket-main {
|
||||
background: none repeat scroll 0 0 #cabb9f;
|
||||
}
|
||||
|
||||
div.footer-ticket-details {
|
||||
background: none repeat scroll 0 0 #efe2ca;
|
||||
}
|
||||
|
||||
.fixed-bottom {
|
||||
max-width: 1272px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<ckeditor
|
||||
name="content"
|
||||
:placeholder="$t('add_comment.content')"
|
||||
:editor="editor"
|
||||
v-model="content"
|
||||
tag-name="textarea"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from "vue";
|
||||
|
||||
import CKEditor from "@ckeditor/ckeditor5-vue";
|
||||
import ClassicEditor from "../../../../../../../ChillMainBundle/Resources/public/module/ckeditor5";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AddCommentComponent",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
ckeditor: CKEditor.component,
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
|
||||
setup(props, ctx) {
|
||||
const content = ref(props.modelValue);
|
||||
|
||||
watch(content, (content) => {
|
||||
ctx.emit("update:modelValue", content);
|
||||
});
|
||||
|
||||
return {
|
||||
content,
|
||||
editor: ClassicEditor,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<h3>
|
||||
<div class="col-12">
|
||||
<span
|
||||
class="badge m-1"
|
||||
:style="`background-color: ${userGroup.backgroundColor}; color: white;`"
|
||||
v-for="userGroup in userGroupLevels"
|
||||
:key="userGroup.id"
|
||||
>
|
||||
{{ userGroup.label.fr }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<span
|
||||
class="badge m-1"
|
||||
:style="`background-color: ${userGroup.backgroundColor}; color: white;`"
|
||||
v-for="userGroup in userGroups"
|
||||
:key="userGroup.id"
|
||||
>
|
||||
{{ userGroup.label.fr }}
|
||||
</span>
|
||||
</div>
|
||||
</h3>
|
||||
<div class="col-12">
|
||||
<span class="badge bg-primary m-1" v-for="user in users" :key="user.id">
|
||||
{{ user.label }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, computed, defineComponent, ref } from "vue";
|
||||
|
||||
// Types
|
||||
import {
|
||||
User,
|
||||
UserGroup,
|
||||
UserGroupOrUser,
|
||||
} from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AddresseeComponent",
|
||||
props: {
|
||||
addressees: {
|
||||
type: Array as PropType<UserGroupOrUser[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, ctx) {
|
||||
const userGroups = computed(
|
||||
() =>
|
||||
props.addressees.filter(
|
||||
(addressee) =>
|
||||
addressee.type == "user_group" &&
|
||||
addressee.excludeKey == ""
|
||||
) as UserGroup[]
|
||||
);
|
||||
const userGroupLevels = computed(
|
||||
() =>
|
||||
props.addressees.filter(
|
||||
(addressee) =>
|
||||
addressee.type == "user_group" &&
|
||||
addressee.excludeKey == "level"
|
||||
) as UserGroup[]
|
||||
);
|
||||
const users = computed(
|
||||
() =>
|
||||
props.addressees.filter(
|
||||
(addressee) => addressee.type == "user"
|
||||
) as User[]
|
||||
);
|
||||
return { userGroups, users, userGroupLevels };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,241 +0,0 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 col-md-6 text-center">
|
||||
<div class="mb-2">
|
||||
<span
|
||||
v-for="userGroupItem in userGroups.filter(
|
||||
(userGroup) => userGroup.excludeKey == 'level'
|
||||
)"
|
||||
:key="userGroupItem.id"
|
||||
class="m-1"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
class="btn-check"
|
||||
name="options-outlined"
|
||||
:id="`level-${userGroupItem.id}`"
|
||||
autocomplete="off"
|
||||
:value="userGroupItem"
|
||||
v-model="userGroupLevel"
|
||||
@click="
|
||||
Object.values(userGroupLevel).includes(
|
||||
userGroupItem.id
|
||||
)
|
||||
? (userGroupLevel = {})
|
||||
: (userGroupLevel = userGroupItem)
|
||||
"
|
||||
/>
|
||||
<label
|
||||
:class="`btn btn-${userGroupItem.id}`"
|
||||
:for="`level-${userGroupItem.id}`"
|
||||
:style="getUserGroupBtnColor(userGroupItem)"
|
||||
>
|
||||
{{ userGroupItem.label.fr }}
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<span
|
||||
v-for="userGroupItem in userGroups.filter(
|
||||
(userGroup) => userGroup.excludeKey == ''
|
||||
)"
|
||||
:key="userGroupItem.id"
|
||||
class="m-1"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="btn-check"
|
||||
name="options-outlined"
|
||||
:id="`user-group-${userGroupItem.id}`"
|
||||
autocomplete="off"
|
||||
:value="userGroupItem"
|
||||
v-model="userGroup"
|
||||
/>
|
||||
<label
|
||||
:class="`btn btn-${userGroupItem.id}`"
|
||||
:for="`user-group-${userGroupItem.id}`"
|
||||
:style="getUserGroupBtnColor(userGroupItem)"
|
||||
>
|
||||
{{ userGroupItem.label.fr }}
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6 col-md-6 mb-2 mb-2 text-center">
|
||||
<add-persons
|
||||
:options="addPersonsOptions"
|
||||
key="add-person-ticket"
|
||||
buttonTitle="add_addressee.user_label"
|
||||
modalTitle="add_addressee.user_label"
|
||||
ref="addPersons"
|
||||
@addNewPersons="addNewEntity"
|
||||
/>
|
||||
<div class="p-2">
|
||||
<ul class="list-suggest inline remove-items">
|
||||
<li v-for="user in users" :key="user.id">
|
||||
<span :title="user.username" @click="removeUser(user)">
|
||||
{{ user.username }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, computed, defineComponent, ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
// Types
|
||||
import {
|
||||
User,
|
||||
UserGroup,
|
||||
UserGroupOrUser,
|
||||
} from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
|
||||
// Components
|
||||
import AddPersons from "../../../../../../../ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AddresseeSelectorComponent",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array as PropType<UserGroupOrUser[]>,
|
||||
default: [],
|
||||
required: false,
|
||||
},
|
||||
userGroups: {
|
||||
type: Array as PropType<UserGroup[]>,
|
||||
required: true,
|
||||
},
|
||||
users: {
|
||||
type: Array as PropType<User[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
AddPersons,
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
|
||||
setup(props, ctx) {
|
||||
const addressees = ref([...props.modelValue] as UserGroupOrUser[]);
|
||||
const userGroups = [
|
||||
...props.modelValue.filter(
|
||||
(addressee) => addressee.type == "user_group"
|
||||
),
|
||||
] as UserGroup[];
|
||||
|
||||
const userGroupLevel = ref(
|
||||
userGroups.filter(
|
||||
(userGroup) => userGroup.excludeKey == "level"
|
||||
)[0] as UserGroup | {}
|
||||
);
|
||||
const userGroup = ref(
|
||||
userGroups.filter((userGroup) => userGroup.excludeKey == "") as
|
||||
| UserGroup[]
|
||||
);
|
||||
const users = ref([
|
||||
...props.modelValue.filter((addressee) => addressee.type == "user"),
|
||||
] as User[]);
|
||||
const addPersons = ref();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
function getUserGroupBtnColor(userGroup: UserGroup) {
|
||||
return [
|
||||
`.btn-check:checked + .btn-${userGroup.id} {
|
||||
color: ${userGroup.foregroundColor};
|
||||
background-color: ${userGroup.backgroundColor};
|
||||
}`,
|
||||
];
|
||||
}
|
||||
function addNewEntity(datas: any) {
|
||||
const { selected, modal } = datas;
|
||||
users.value = selected.map((selected: any) => selected.result);
|
||||
addressees.value = addressees.value.filter(
|
||||
(addressee) => addressee.type === "user_group"
|
||||
);
|
||||
addressees.value = [...addressees.value, ...users.value];
|
||||
ctx.emit("update:modelValue", addressees.value);
|
||||
addPersons.value.resetSearch();
|
||||
modal.showModal = false;
|
||||
}
|
||||
|
||||
const addPersonsOptions = computed(() => {
|
||||
return {
|
||||
uniq: false,
|
||||
type: ["user"],
|
||||
priority: null,
|
||||
button: {
|
||||
size: "btn-sm",
|
||||
class: "btn-submit",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
function removeUser(user: User) {
|
||||
users.value.splice(users.value.indexOf(user), 1);
|
||||
addressees.value = addressees.value.filter(
|
||||
(addressee) => addressee.id !== user.id
|
||||
);
|
||||
ctx.emit("update:modelValue", addressees.value);
|
||||
}
|
||||
|
||||
watch(userGroupLevel, (userGroupLevelAdd, userGroupLevelRem) => {
|
||||
const index = addressees.value.indexOf(
|
||||
userGroupLevelRem as UserGroup
|
||||
);
|
||||
if (index !== -1) {
|
||||
addressees.value.splice(index, 1);
|
||||
}
|
||||
addressees.value.push(userGroupLevelAdd as UserGroup);
|
||||
ctx.emit("update:modelValue", addressees.value);
|
||||
});
|
||||
|
||||
watch(userGroup, (userGroupAdd) => {
|
||||
const userGroupLevel = addressees.value.filter(
|
||||
(addressee) =>
|
||||
addressee.type == "user_group" &&
|
||||
addressee.excludeKey == "level"
|
||||
) as UserGroup[];
|
||||
const users = addressees.value.filter(
|
||||
(addressee) => addressee.type == "user"
|
||||
) as UserGroup[];
|
||||
addressees.value = [...users, ...userGroupLevel, ...userGroupAdd];
|
||||
ctx.emit("update:modelValue", addressees.value);
|
||||
});
|
||||
|
||||
return {
|
||||
addressees,
|
||||
userGroupLevel,
|
||||
userGroup,
|
||||
users,
|
||||
addPersons,
|
||||
addPersonsOptions,
|
||||
addNewEntity,
|
||||
removeUser,
|
||||
getUserGroupBtnColor,
|
||||
customUserGroupLabel(selectedUserGroup: UserGroup) {
|
||||
return selectedUserGroup.label
|
||||
? selectedUserGroup.label.fr
|
||||
: t("add_addresseeuser_group_label");
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.btn-check:checked + .btn,
|
||||
:not(.btn-check) + .btn:active,
|
||||
.btn:first-child:active,
|
||||
.btn.active,
|
||||
.btn.show {
|
||||
color: white;
|
||||
box-shadow: 0 0 0 0.2rem var(--bs-chill-green);
|
||||
outline: 0;
|
||||
}
|
||||
</style>
|
@@ -1,138 +0,0 @@
|
||||
<template>
|
||||
<Teleport to="#header-ticket-main">
|
||||
<div class="container-xxl text-primary">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12 ps-md-5 ps-xxl-0">
|
||||
<h2>#{{ ticket.id }}</h2>
|
||||
<h1 v-if="ticket.currentMotive">
|
||||
{{ ticket.currentMotive.label.fr }}
|
||||
</h1>
|
||||
<p class="chill-no-data-statement" v-else>
|
||||
{{ $t("banner.no_motive") }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<div class="d-flex justify-content-end">
|
||||
<h1>
|
||||
<span class="badge text-bg-chill-green text-white">
|
||||
{{ $t("banner.open") }}
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<h3 class="fst-italic" v-if="ticket.createdAt">
|
||||
{{
|
||||
$t("banner.since", {
|
||||
time: since,
|
||||
})
|
||||
}}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
<Teleport to="#header-ticket-details">
|
||||
<div class="container-xxl">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-md-6 col-sm-12 ps-md-5 ps-xxl-0">
|
||||
<h3 class="text-primary">
|
||||
{{ $t("banner.concerned_patient") }}
|
||||
</h3>
|
||||
|
||||
<person-render-box
|
||||
render="badge"
|
||||
v-for="person in ticket.currentPersons"
|
||||
:key="person.id"
|
||||
:person="person"
|
||||
:options="{
|
||||
addLink: true,
|
||||
addId: false,
|
||||
addAltNames: false,
|
||||
addEntity: true,
|
||||
addInfo: true,
|
||||
hLevel: 3,
|
||||
isMultiline: true,
|
||||
isConfidential: false,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<h3 class="text-primary">{{ $t("banner.speaker") }}</h3>
|
||||
<addressee-component
|
||||
:addressees="ticket.currentAddressees"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, computed, defineComponent, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
// Components
|
||||
import PersonRenderBox from "../../../../../../../ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue";
|
||||
import AddresseeComponent from "./AddresseeComponent.vue";
|
||||
|
||||
// Types
|
||||
import { Ticket } from "../../../types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "BannerComponent",
|
||||
props: {
|
||||
ticket: {
|
||||
type: Object as PropType<Ticket>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
PersonRenderBox,
|
||||
AddresseeComponent,
|
||||
},
|
||||
setup(props) {
|
||||
const { t } = useI18n();
|
||||
const today = ref(new Date());
|
||||
const createdAt = ref(props.ticket.createdAt as any);
|
||||
|
||||
setInterval(function () {
|
||||
today.value = new Date();
|
||||
}, 1000);
|
||||
|
||||
const since = computed(() => {
|
||||
const date = new Date(createdAt.value.date);
|
||||
|
||||
const timeDiff = Math.abs(today.value.getTime() - date.getTime());
|
||||
const daysDiff = Math.floor(timeDiff / (1000 * 3600 * 24));
|
||||
const hoursDiff = Math.floor(
|
||||
(timeDiff % (1000 * 3600 * 24)) / (1000 * 3600)
|
||||
);
|
||||
const minutesDiff = Math.floor(
|
||||
(timeDiff % (1000 * 3600)) / (1000 * 60)
|
||||
);
|
||||
const secondsDiff = Math.floor((timeDiff % (1000 * 60)) / 1000);
|
||||
|
||||
if (daysDiff < 1 && hoursDiff < 1 && minutesDiff < 1) {
|
||||
return `${t("banner.seconds", { count: secondsDiff })}`;
|
||||
} else if (daysDiff < 1) {
|
||||
return `${t("banner.hours", { count: hoursDiff })}
|
||||
${t("banner.minutes", { count: minutesDiff })}
|
||||
${t("banner.seconds", { count: secondsDiff })}`;
|
||||
} else {
|
||||
return `${t("banner.days", { count: daysDiff })}, ${t(
|
||||
"banner.hours",
|
||||
{
|
||||
count: hoursDiff,
|
||||
}
|
||||
)} ${t("banner.minutes", {
|
||||
count: minutesDiff,
|
||||
})}`;
|
||||
}
|
||||
});
|
||||
|
||||
return { since };
|
||||
},
|
||||
});
|
||||
</script>
|
@@ -1,72 +0,0 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<vue-multiselect
|
||||
name="selectMotive"
|
||||
id="selectMotive"
|
||||
label="label"
|
||||
:custom-label="customLabel"
|
||||
track-by="id"
|
||||
open-direction="top"
|
||||
:multiple="false"
|
||||
:searchable="true"
|
||||
:placeholder="$t('set_motive.label')"
|
||||
:select-label="$t('multiselect.select_label')"
|
||||
:deselect-label="$t('multiselect.deselect_label')"
|
||||
:selected-label="$t('multiselect.selected_label')"
|
||||
:options="motives"
|
||||
v-model="motive"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent, ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
|
||||
// Types
|
||||
import { Motive } from "../../../types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "MotiveSelectorComponent",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object as PropType<Motive>,
|
||||
required: false,
|
||||
},
|
||||
motives: {
|
||||
type: Object as PropType<Motive[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
VueMultiselect,
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
|
||||
setup(props, ctx) {
|
||||
const motive = ref(props.modelValue);
|
||||
const { t } = useI18n();
|
||||
|
||||
watch(motive, (motive) => {
|
||||
ctx.emit("update:modelValue", motive);
|
||||
});
|
||||
|
||||
return {
|
||||
motive,
|
||||
customLabel(motive: Motive) {
|
||||
return motive.label ? motive.label.fr : t("set_motive.label");
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#selectMotive {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
</style>
|
@@ -1,33 +0,0 @@
|
||||
<template>
|
||||
<div class="col-12">
|
||||
<addressee-component :addressees="addressees" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent } from "vue";
|
||||
|
||||
// Types
|
||||
import { UserGroupOrUser } from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
|
||||
// Components
|
||||
import AddresseeComponent from "./AddresseeComponent.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "TicketHistoryAddresseeComponenvt",
|
||||
props: {
|
||||
addressees: {
|
||||
type: Array as PropType<UserGroupOrUser[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
AddresseeComponent,
|
||||
},
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<div class="col-12">
|
||||
<blockquote class="chill-user-quote">
|
||||
<p v-html="convertMarkdownToHtml(commentHistory.content)"></p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent } from "vue";
|
||||
import { marked } from "marked";
|
||||
import DOMPurify from "dompurify";
|
||||
|
||||
// Types
|
||||
import { Comment } from "../../../types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "TicketHistoryCommentComponent",
|
||||
props: {
|
||||
commentHistory: {
|
||||
type: Object as PropType<Comment>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const preprocess = (markdown: string): string => {
|
||||
return markdown;
|
||||
};
|
||||
|
||||
const postprocess = (html: string): string => {
|
||||
DOMPurify.addHook("afterSanitizeAttributes", (node: any) => {
|
||||
if ("target" in node) {
|
||||
node.setAttribute("target", "_blank");
|
||||
node.setAttribute("rel", "noopener noreferrer");
|
||||
}
|
||||
if (
|
||||
!node.hasAttribute("target") &&
|
||||
(node.hasAttribute("xlink:href") ||
|
||||
node.hasAttribute("href"))
|
||||
) {
|
||||
node.setAttribute("xlink:show", "new");
|
||||
}
|
||||
});
|
||||
|
||||
return DOMPurify.sanitize(html);
|
||||
};
|
||||
|
||||
const convertMarkdownToHtml = (markdown: string): string => {
|
||||
marked.use({ hooks: { postprocess, preprocess } });
|
||||
const rawHtml = marked(markdown) as string;
|
||||
return rawHtml;
|
||||
};
|
||||
return {
|
||||
convertMarkdownToHtml,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,98 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="card my-2 bg-light"
|
||||
v-for="history_line in history"
|
||||
:key="history.indexOf(history_line)"
|
||||
>
|
||||
<template v-if="!Array.isArray(history_line)">
|
||||
<div class="card-header">
|
||||
<i :class="`${actionIcons[history_line.event_type]} me-1`"></i>
|
||||
<span class="fw-bold fst-italic mx-1">
|
||||
{{ formatDate(history_line.at) }}
|
||||
</span>
|
||||
<span class="badge bg-white text-black mx-1">
|
||||
{{ history_line.by.username }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body row">
|
||||
<ticket-history-person-component
|
||||
:personHistory="history_line.data"
|
||||
v-if="history_line.event_type == 'add_person'"
|
||||
/>
|
||||
<ticket-history-motive-component
|
||||
:motiveHistory="history_line.data"
|
||||
v-else-if="history_line.event_type == 'set_motive'"
|
||||
/>
|
||||
<ticket-history-comment-component
|
||||
:commentHistory="history_line.data"
|
||||
v-else-if="history_line.event_type == 'add_comment'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="card-header">
|
||||
<i class="fa fa-paper-plane me-1"></i>
|
||||
<span class="fw-bold fst-italic mx-1">
|
||||
{{ formatDate(history_line[0].at) }}
|
||||
</span>
|
||||
<span class="badge bg-white text-black mx-1">
|
||||
{{ history_line[0].by.username }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body row">
|
||||
<ticket-history-addressee-component
|
||||
:addressees="
|
||||
history_line
|
||||
.map((line) => line.data)
|
||||
.map((data) => data.addressee)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent, ref } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
// Types
|
||||
import { DateTime } from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import { Ticket } from "../../../types";
|
||||
|
||||
// Components
|
||||
import TicketHistoryPersonComponent from "./TicketHistoryPersonComponent.vue";
|
||||
import TicketHistoryMotiveComponent from "./TicketHistoryMotiveComponent.vue";
|
||||
import TicketHistoryCommentComponent from "./TicketHistoryCommentComponent.vue";
|
||||
import TicketHistoryAddresseeComponent from "./TicketHistoryAddresseeComponent.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "TicketHistoryListComponent",
|
||||
components: {
|
||||
TicketHistoryPersonComponent,
|
||||
TicketHistoryMotiveComponent,
|
||||
TicketHistoryCommentComponent,
|
||||
TicketHistoryAddresseeComponent,
|
||||
},
|
||||
props: {
|
||||
history: {
|
||||
type: Array as PropType<Ticket["history"]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
setup() {
|
||||
const store = useStore();
|
||||
|
||||
function formatDate(d: DateTime) {
|
||||
const date = new Date(d.datetime);
|
||||
const month = date.toLocaleString("default", { month: "long" });
|
||||
return `${date.getDate()} ${month} ${date.getFullYear()}, ${date.toLocaleTimeString()}`;
|
||||
}
|
||||
|
||||
return { actionIcons: ref(store.getters.getActionIcons), formatDate };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,26 +0,0 @@
|
||||
<template>
|
||||
<div class="col-12 fw-bolder">
|
||||
{{ motiveHistory.motive.label.fr }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent } from "vue";
|
||||
|
||||
// Types
|
||||
import { MotiveHistory } from "../../../types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "TicketHistoryMotiveComponent",
|
||||
props: {
|
||||
motiveHistory: {
|
||||
type: Object as PropType<MotiveHistory>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
setup() {},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<div class="col-12" v-if="personHistory.createdBy">
|
||||
<span class="mx-1">
|
||||
{{ $t("history.user") }}
|
||||
<span class="badge bg-primary m-1">
|
||||
{{ personHistory.createdBy.username }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<span class="mx-1">
|
||||
{{ $t("history.person") }}
|
||||
</span>
|
||||
<person-render-box
|
||||
render="badge"
|
||||
:key="personHistory.person.id"
|
||||
:person="personHistory.person"
|
||||
:options="{
|
||||
addLink: true,
|
||||
addId: false,
|
||||
addAltNames: false,
|
||||
addEntity: true,
|
||||
addInfo: true,
|
||||
hLevel: 3,
|
||||
isMultiline: true,
|
||||
isConfidential: false,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent } from "vue";
|
||||
|
||||
// Components
|
||||
import PersonRenderBox from "../../../../../../../ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue";
|
||||
|
||||
// Type
|
||||
import { PersonHistory } from "../../../types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "TicketHistoryPersonComponent",
|
||||
props: {
|
||||
personHistory: {
|
||||
type: Object as PropType<PersonHistory>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
PersonRenderBox,
|
||||
},
|
||||
|
||||
setup() {},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,41 +0,0 @@
|
||||
<template>
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="btn-group" @click="handleClick">
|
||||
<button type="button" class="btn btn-light dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{{ $t('ticket.previous_tickets') }}
|
||||
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-chill-green">
|
||||
{{ tickets.length }}
|
||||
<span class="visually-hidden">Tickets</span>
|
||||
</span>
|
||||
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
import { PropType, defineComponent } from 'vue';
|
||||
|
||||
// Types
|
||||
import { Ticket } from '../../../types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TicketSelectorComponent',
|
||||
props: {
|
||||
tickets: {
|
||||
type: Object as PropType<Ticket[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
function handleClick() {
|
||||
alert('Sera disponible plus tard')
|
||||
}
|
||||
return { handleClick }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -1,52 +0,0 @@
|
||||
import { multiSelectMessages } from "../../../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
|
||||
import { personMessages } from "../../../../../../../ChillPersonBundle/Resources/public/vuejs/_js/i18n";
|
||||
|
||||
const messages = {
|
||||
fr: {
|
||||
ticket: {
|
||||
previous_tickets: "Précédents tickets",
|
||||
cancel: "Annuler",
|
||||
save: "Enregistrer",
|
||||
close: "Fermer",
|
||||
},
|
||||
history: {
|
||||
person: "Ouverture par appel téléphonique de ",
|
||||
user: "Prise en charge par ",
|
||||
},
|
||||
add_comment: {
|
||||
title: "Commentaire",
|
||||
label: "Ajouter un commentaire",
|
||||
success: "Commentaire enregistré",
|
||||
content: "Ajouter un commentaire",
|
||||
error: "Aucun commentaire ajouté",
|
||||
},
|
||||
set_motive: {
|
||||
title: "Motif",
|
||||
label: "Choisir un motif",
|
||||
success: "Motif enregistré",
|
||||
error: "Aucun motif sélectionné",
|
||||
},
|
||||
add_addressee: {
|
||||
title: "Transfert",
|
||||
user_group_label: "Transferer vers un groupe",
|
||||
user_label: "Transferer vers un ou plusieurs utilisateurs",
|
||||
success: "Transfert effectué",
|
||||
error: "Aucun destinataire sélectionné",
|
||||
},
|
||||
banner: {
|
||||
concerned_patient: "Patient concerné",
|
||||
speaker: "Destinataire(s)",
|
||||
open: "Ouvert",
|
||||
since: "Depuis {time}",
|
||||
and: "et",
|
||||
days: "|1 jour|{count} jours",
|
||||
hours: "|1 heure et|{count} heures",
|
||||
minutes: "|1 minute|{count} minutes",
|
||||
seconds: "|1 seconde|{count} secondes",
|
||||
no_motive: "Pas de motif",
|
||||
},
|
||||
},
|
||||
};
|
||||
Object.assign(messages.fr, multiSelectMessages.fr);
|
||||
Object.assign(messages.fr, personMessages.fr);
|
||||
export default messages;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user