mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-14 22:34:24 +00:00
Merge branch 'ticket-app-create-template' into 'ticket-app-master'
Mise à jour des messages de l'interface utilisateur pour inclure les... See merge request Chill-Projet/chill-bundles!689
This commit is contained in:
commit
76c076a5f3
6
.changes/unreleased/Fixed-20240416-161817.yaml
Normal file
6
.changes/unreleased/Fixed-20240416-161817.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
kind: Fixed
|
||||||
|
body: Fix broken link in homepage when a evaluation from a closed acc period was present
|
||||||
|
in the homepage widget
|
||||||
|
time: 2024-04-16T16:18:17.888645172+02:00
|
||||||
|
custom:
|
||||||
|
Issue: "270"
|
3
.changes/v2.18.2.md
Normal file
3
.changes/v2.18.2.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## v2.18.2 - 2024-04-12
|
||||||
|
### Fixed
|
||||||
|
* ([#250](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/250)) Postal codes import : fix the source URL and the keys to handle each record
|
@ -6,6 +6,10 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
|||||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||||
|
|
||||||
|
|
||||||
|
## v2.18.2 - 2024-04-12
|
||||||
|
### Fixed
|
||||||
|
* ([#250](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/250)) Postal codes import : fix the source URL and the keys to handle each record
|
||||||
|
|
||||||
## v2.18.1 - 2024-03-26
|
## v2.18.1 - 2024-03-26
|
||||||
### Fixed
|
### Fixed
|
||||||
* Fix layout issue in document generation for admin (minor)
|
* Fix layout issue in document generation for admin (minor)
|
||||||
|
@ -8,6 +8,16 @@ Chill can store a list of geolocated address references, which are used to sugge
|
|||||||
|
|
||||||
Those addresses may be load from a dedicated source.
|
Those addresses may be load from a dedicated source.
|
||||||
|
|
||||||
|
Countries
|
||||||
|
=========
|
||||||
|
|
||||||
|
In order to load addresses into the chill application we first have to make sure that a list of countries is present.
|
||||||
|
To import the countries run the following command.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
bin/console chill:main:countries:populate
|
||||||
|
|
||||||
In France
|
In France
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<span class="chill-entity entity-user">
|
<span class="chill-entity entity-user">
|
||||||
{{ user.label }}
|
{{ user.label }}
|
||||||
|
{{ user }}
|
||||||
<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 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>
|
||||||
</template>
|
</template>
|
||||||
|
@ -23,7 +23,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|||||||
*/
|
*/
|
||||||
class PostalCodeFRFromOpenData
|
class PostalCodeFRFromOpenData
|
||||||
{
|
{
|
||||||
private const CSV = 'https://datanova.laposte.fr/data-fair/api/v1/datasets/laposte-hexasmal/data-files/019HexaSmal.csv';
|
private const CSV = 'https://datanova.laposte.fr/data-fair/api/v1/datasets/laposte-hexasmal/metadata-attachments/base-officielle-codes-postaux.csv';
|
||||||
|
|
||||||
public function __construct(private readonly PostalCodeBaseImporter $baseImporter, private readonly HttpClientInterface $client, private readonly LoggerInterface $logger) {}
|
public function __construct(private readonly PostalCodeBaseImporter $baseImporter, private readonly HttpClientInterface $client, private readonly LoggerInterface $logger) {}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class PostalCodeFRFromOpenData
|
|||||||
fseek($tmpfile, 0);
|
fseek($tmpfile, 0);
|
||||||
|
|
||||||
$csv = Reader::createFromStream($tmpfile);
|
$csv = Reader::createFromStream($tmpfile);
|
||||||
$csv->setDelimiter(';');
|
$csv->setDelimiter(',');
|
||||||
$csv->setHeaderOffset(0);
|
$csv->setHeaderOffset(0);
|
||||||
|
|
||||||
foreach ($csv as $offset => $record) {
|
foreach ($csv as $offset => $record) {
|
||||||
@ -63,23 +63,23 @@ class PostalCodeFRFromOpenData
|
|||||||
|
|
||||||
private function handleRecord(array $record): void
|
private function handleRecord(array $record): void
|
||||||
{
|
{
|
||||||
if ('' !== trim($record['coordonnees_geographiques'] ?? $record['coordonnees_gps'])) {
|
if ('' !== trim((string) $record['_geopoint'])) {
|
||||||
[$lat, $lon] = array_map(static fn ($el) => (float) trim($el), explode(',', $record['coordonnees_geographiques'] ?? $record['coordonnees_gps']));
|
[$lat, $lon] = array_map(static fn ($el) => (float) trim($el), explode(',', (string) $record['_geopoint']));
|
||||||
} else {
|
} else {
|
||||||
$lat = $lon = 0.0;
|
$lat = $lon = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ref = trim((string) $record['Code_commune_INSEE']);
|
$ref = trim((string) $record['code_commune_insee']);
|
||||||
|
|
||||||
if (str_starts_with($ref, '987')) {
|
if (str_starts_with($ref, '987')) {
|
||||||
// some differences in French Polynesia
|
// some differences in French Polynesia
|
||||||
$ref .= '.'.trim((string) $record['Libellé_d_acheminement']);
|
$ref .= '.'.trim((string) $record['libelle_d_acheminement']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->baseImporter->importCode(
|
$this->baseImporter->importCode(
|
||||||
'FR',
|
'FR',
|
||||||
trim((string) $record['Libellé_d_acheminement']),
|
trim((string) $record['libelle_d_acheminement']),
|
||||||
trim((string) $record['Code_postal']),
|
trim((string) $record['code_postal']),
|
||||||
$ref,
|
$ref,
|
||||||
'INSEE',
|
'INSEE',
|
||||||
$lat,
|
$lat,
|
||||||
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
|
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
|
||||||
|
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
@ -88,6 +89,7 @@ class AccompanyingPeriodWorkEvaluationRepository implements ObjectRepository
|
|||||||
->where(
|
->where(
|
||||||
$qb->expr()->andX(
|
$qb->expr()->andX(
|
||||||
$qb->expr()->isNull('e.endDate'),
|
$qb->expr()->isNull('e.endDate'),
|
||||||
|
$qb->expr()->neq('period.step', ':closed'),
|
||||||
$qb->expr()->gte(':now', $qb->expr()->diff('e.maxDate', 'e.warningInterval')),
|
$qb->expr()->gte(':now', $qb->expr()->diff('e.maxDate', 'e.warningInterval')),
|
||||||
$qb->expr()->orX(
|
$qb->expr()->orX(
|
||||||
$qb->expr()->eq('period.user', ':user'),
|
$qb->expr()->eq('period.user', ':user'),
|
||||||
@ -100,6 +102,7 @@ class AccompanyingPeriodWorkEvaluationRepository implements ObjectRepository
|
|||||||
->setParameters([
|
->setParameters([
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
'now' => new \DateTimeImmutable('now'),
|
'now' => new \DateTimeImmutable('now'),
|
||||||
|
'closed' => AccompanyingPeriod::STEP_CLOSED,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
<h2><a id="section-10"></a>{{ $t('persons_associated.title')}}</h2>
|
<h2><a id="section-10"></a>{{ $t('persons_associated.title')}}</h2>
|
||||||
|
|
||||||
<div v-if="currentParticipations.length > 0">
|
<div v-if="currentParticipations.length > 0">
|
||||||
<label class="col-form-label">{{ $tc('persons_associated.counter', counter) }}</label>
|
<label class="col-form-label">{{ $t('persons_associated.counter', { count: counter }) }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<label class="chill-no-data-statement">{{ $tc('persons_associated.counter', counter) }}</label>
|
<label class="chill-no-data-statement">{{ $t('persons_associated.counter', { count: counter }) }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="participationWithoutHousehold.length > 0" class="alert alert-warning no-household">
|
<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>
|
<h2><a id="section-90"></a>{{ $t('resources.title')}}</h2>
|
||||||
|
|
||||||
<div v-if="resources.length > 0">
|
<div v-if="resources.length > 0">
|
||||||
<label class="col-form-label">{{ $tc('resources.counter', counter) }}</label>
|
<label class="col-form-label">{{ $t('resources.counter', { count: counter }) }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<label class="chill-no-data-statement">{{ $tc('resources.counter', counter) }}</label>
|
<label class="chill-no-data-statement">{{ $t('resources.counter', { count: counter }) }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-table mb-3">
|
<div class="flex-table mb-3">
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
data-bs-toggle="collapse"
|
data-bs-toggle="collapse"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
@click="toggleHouseholdSuggestion">
|
@click="toggleHouseholdSuggestion">
|
||||||
{{ $tc('household_members_editor.show_household_suggestion', countHouseholdSuggestion) }}
|
{{ $t('household_members_editor.show_household_suggestion', { count: countHouseholdSuggestion }) }}
|
||||||
</button>
|
</button>
|
||||||
<button v-if="showHouseholdSuggestion"
|
<button v-if="showHouseholdSuggestion"
|
||||||
class="accordion-button"
|
class="accordion-button"
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<div class="search">
|
<div class="search">
|
||||||
|
|
||||||
<label class="col-form-label" style="float: right;">
|
<label class="col-form-label" style="float: right;">
|
||||||
{{ $tc('add_persons.suggested_counter', suggestedCounter) }}
|
{{ $t('add_persons.suggested_counter', { count: suggestedCounter }) }}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<input id="search-persons"
|
<input id="search-persons"
|
||||||
@ -42,7 +42,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="selectedCounter > 0">
|
<span v-if="selectedCounter > 0">
|
||||||
{{ $tc('add_persons.selected_counter', selectedCounter) }}
|
{{ $t('add_persons.selected_counter', { count: selectedCounter }) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,9 +52,7 @@
|
|||||||
{{ $t('renderbox.deathdate') + ' ' + deathdate }}
|
{{ $t('renderbox.deathdate') + ' ' + deathdate }}
|
||||||
</time>
|
</time>
|
||||||
|
|
||||||
<span v-if="options.addAge && person.birthdate" class="age">{{
|
<span v-if="options.addAge && person.birthdate" class="age">{{ $t('renderbox.years_old', { n: person.age }) }}</span>
|
||||||
$tc('renderbox.years_old', person.age)
|
|
||||||
}}</span>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<span :class="'altname altname-' + altNameKey"> ({{ altNameLabel }})</span>
|
<span :class="'altname altname-' + altNameKey"> ({{ altNameLabel }})</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="person.suffixText" class="suffixtext"> {{ person.suffixText }}</span>
|
<span v-if="person.suffixText" class="suffixtext"> {{ person.suffixText }}</span>
|
||||||
<span class="age" v-if="this.addAge && person.birthdate !== null && person.deathdate === null">{{ $tc('renderbox.years_old', person.age) }}</span>
|
<span class="age" v-if="this.addAge && person.birthdate !== null && person.deathdate === null">{{ $t('renderbox.years_old', { n: person.age }) }}</span>
|
||||||
<span v-else-if="this.addAge && person.deathdate !== null"> (‡)</span>
|
<span v-else-if="this.addAge && person.deathdate !== null"> (‡)</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -69,6 +69,13 @@ class Ticket implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
#[ORM\OneToMany(targetEntity: PersonHistory::class, mappedBy: 'ticket')]
|
#[ORM\OneToMany(targetEntity: PersonHistory::class, mappedBy: 'ticket')]
|
||||||
private Collection $personHistories;
|
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()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->addresseeHistory = new ArrayCollection();
|
$this->addresseeHistory = new ArrayCollection();
|
||||||
@ -76,6 +83,7 @@ class Ticket implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
$this->motiveHistories = new ArrayCollection();
|
$this->motiveHistories = new ArrayCollection();
|
||||||
$this->personHistories = new ArrayCollection();
|
$this->personHistories = new ArrayCollection();
|
||||||
$this->inputHistories = new ArrayCollection();
|
$this->inputHistories = new ArrayCollection();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
@ -211,4 +219,16 @@ class Ticket implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
{
|
{
|
||||||
return $this->addresseeHistory;
|
return $this->addresseeHistory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCreatedAt(): \DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUpdatedBy(): ?User
|
||||||
|
{
|
||||||
|
return $this->updatedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,9 @@ import {
|
|||||||
DateTime,
|
DateTime,
|
||||||
TranslatableString,
|
TranslatableString,
|
||||||
User,
|
User,
|
||||||
UserGroup,
|
|
||||||
UserGroupOrUser
|
UserGroupOrUser
|
||||||
} from "../../../../ChillMainBundle/Resources/public/types";
|
} from "../../../../ChillMainBundle/Resources/public/types";
|
||||||
import {Person} from "../../../../ChillPersonBundle/Resources/public/types";
|
import { Person } from "../../../../ChillPersonBundle/Resources/public/types";
|
||||||
|
|
||||||
export interface Motive {
|
export interface Motive {
|
||||||
type: "ticket_motive"
|
type: "ticket_motive"
|
||||||
@ -21,7 +20,7 @@ interface TicketHistory<T extends string, D extends object> {
|
|||||||
data: D
|
data: D
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PersonHistory {
|
export interface PersonHistory {
|
||||||
type: "ticket_person_history",
|
type: "ticket_person_history",
|
||||||
id: number,
|
id: number,
|
||||||
startDate: DateTime,
|
startDate: DateTime,
|
||||||
@ -32,7 +31,7 @@ interface PersonHistory {
|
|||||||
createdAt: DateTime|null
|
createdAt: DateTime|null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MotiveHistory {
|
export interface MotiveHistory {
|
||||||
type: "ticket_motive_history",
|
type: "ticket_motive_history",
|
||||||
id: number,
|
id: number,
|
||||||
startDate: null,
|
startDate: null,
|
||||||
@ -42,7 +41,7 @@ interface MotiveHistory {
|
|||||||
createdAt: DateTime|null,
|
createdAt: DateTime|null,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Comment {
|
export interface Comment {
|
||||||
type: "ticket_comment",
|
type: "ticket_comment",
|
||||||
id: number,
|
id: number,
|
||||||
content: string,
|
content: string,
|
||||||
@ -52,7 +51,7 @@ interface Comment {
|
|||||||
updatedAt: DateTime|null,
|
updatedAt: DateTime|null,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddresseeHistory {
|
export interface AddresseeHistory {
|
||||||
type: "ticket_addressee_history",
|
type: "ticket_addressee_history",
|
||||||
id: number,
|
id: number,
|
||||||
startDate: DateTime|null,
|
startDate: DateTime|null,
|
||||||
@ -69,8 +68,9 @@ interface AddPersonEvent extends TicketHistory<"add_person", PersonHistory> {};
|
|||||||
interface AddCommentEvent extends TicketHistory<"add_comment", Comment> {};
|
interface AddCommentEvent extends TicketHistory<"add_comment", Comment> {};
|
||||||
interface SetMotiveEvent extends TicketHistory<"set_motive", MotiveHistory> {};
|
interface SetMotiveEvent extends TicketHistory<"set_motive", MotiveHistory> {};
|
||||||
interface AddAddressee extends TicketHistory<"add_addressee", AddresseeHistory> {};
|
interface AddAddressee extends TicketHistory<"add_addressee", AddresseeHistory> {};
|
||||||
|
interface RemoveAddressee extends TicketHistory<"remove_addressee", AddresseeHistory> {};
|
||||||
|
|
||||||
type TicketHistoryLine = AddPersonEvent | AddCommentEvent | SetMotiveEvent | AddAddressee;
|
type TicketHistoryLine = AddPersonEvent | AddCommentEvent | SetMotiveEvent | AddAddressee | RemoveAddressee;
|
||||||
|
|
||||||
export interface Ticket {
|
export interface Ticket {
|
||||||
type: "ticket_ticket",
|
type: "ticket_ticket",
|
||||||
@ -80,5 +80,7 @@ export interface Ticket {
|
|||||||
currentPersons: Person[],
|
currentPersons: Person[],
|
||||||
currentMotive: null|Motive,
|
currentMotive: null|Motive,
|
||||||
history: TicketHistoryLine[],
|
history: TicketHistoryLine[],
|
||||||
|
createdAt: DateTime|null,
|
||||||
|
updatedBy: User|null,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,59 +1,161 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<Teleport to="#header-ticket-main">
|
||||||
<p>{{ ticket.externalRef }}</p>
|
<div class="container-xxl text-primary">
|
||||||
<p>{{ ticket.currentMotive }}</p>
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-sm-12 ps-md-5 ps-xxl-0">
|
||||||
|
<h2>#{{ ticket.externalRef }}</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>
|
||||||
|
|
||||||
<div v-for="person in ticket.currentPersons as Person[]" :key="person.id">
|
<div class="col-md-6 col-sm-12">
|
||||||
<p>{{ person.firstName }}</p>
|
<div class="float-end">
|
||||||
<p>{{ person.lastName }}</p>
|
<h1>
|
||||||
|
<span class="badge text-bg-chill-green text-white">
|
||||||
|
{{ $t("banner.open") }}
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<h3 class="fst-italic" v-if="ticket.createdAt">
|
||||||
|
{{
|
||||||
|
$t("banner.since", {
|
||||||
|
count: getSince(ticket.createdAt),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="ticket_history_line in ticket.history">
|
|
||||||
<p>{{ ticket_history_line.event_type}}</p>
|
|
||||||
<p>{{ ticket_history_line.data}}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
<Teleport to="#header-ticket-details">
|
||||||
|
<div class="container-xxl">
|
||||||
|
<div class="row justify-content-between">
|
||||||
|
<!-- <div class="col-md-4 col-sm-12">
|
||||||
|
<h3 class="text-primary">{{ $t("concerned_patient") }}</h3>
|
||||||
|
</div> -->
|
||||||
|
<div class="col-md-6 col-sm-12 ps-md-5 ps-xxl-0">
|
||||||
|
<h3 class="text-primary">{{ $t("banner.caller") }}</h3>
|
||||||
|
<h2>
|
||||||
|
<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,
|
||||||
|
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-sm-12">
|
||||||
|
<h3 class="text-primary">{{ $t("banner.speaker") }}</h3>
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
<span
|
||||||
|
class="badge text-bg-light m-1"
|
||||||
|
v-for="user_group in ticket.currentAddressees.filter((addressee) => addressee.type == 'user') as Array<User>"
|
||||||
|
:key="user_group.id"
|
||||||
|
>
|
||||||
|
{{ user_group.label }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="badge text-bg-light m-1"
|
||||||
|
v-for="user_group in ticket.currentAddressees.filter((addressee) => addressee.type == 'user_group') as Array<UserGroup>"
|
||||||
|
:key="user_group.id"
|
||||||
|
>
|
||||||
|
{{ user_group.label.fr }}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
<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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, inject, onMounted, ref } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
|
||||||
import { computed, defineComponent, inject, onMounted } from 'vue';
|
// Types
|
||||||
import { useStore } from 'vuex';
|
import {
|
||||||
|
DateTime,
|
||||||
|
User,
|
||||||
|
UserGroup,
|
||||||
|
} from "../../../../../../ChillMainBundle/Resources/public/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 PersonRenderBox from "../../../../../../ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue";
|
||||||
|
|
||||||
// Types
|
export default defineComponent({
|
||||||
import { Person } from '../../../../../../ChillPersonBundle/Resources/public/types';
|
name: "App",
|
||||||
import { Motive, Ticket } from '../../types';
|
components: {
|
||||||
|
TicketSelectorComponent,
|
||||||
|
TicketHistoryListComponent,
|
||||||
export default defineComponent({
|
ActionToolbarComponent,
|
||||||
name: 'App',
|
PersonRenderBox,
|
||||||
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const toast = inject('toast') as any;
|
const toast = inject("toast") as any;
|
||||||
|
const headline = ref({} as HTMLHeadingElement);
|
||||||
|
|
||||||
store.commit('setTicket', JSON.parse(window.initialTicket) as Ticket);
|
store.commit("setTicket", JSON.parse(window.initialTicket) as Ticket);
|
||||||
|
|
||||||
const motives = computed(() => store.getters.getMotives as Motive[])
|
const motives = computed(() => store.getters.getMotives as Motive[]);
|
||||||
const ticket = computed(() => store.getters.getTicket as Ticket)
|
const ticket = computed(() => store.getters.getTicket as Ticket);
|
||||||
|
const ticketHistory = computed(
|
||||||
|
() => store.getters.getDistinctAddressesHistory
|
||||||
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
function getSince(createdAt: any) {
|
||||||
|
const today = new Date();
|
||||||
|
const date = new Date(createdAt.date);
|
||||||
|
|
||||||
|
const timeDiff = Math.abs(today.getTime() - date.getTime());
|
||||||
|
const daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24));
|
||||||
|
return daysDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
store.dispatch('fetchMotives')
|
await store.dispatch("fetchMotives");
|
||||||
|
await store.dispatch("fetchUserGroups");
|
||||||
|
await store.dispatch("fetchUsers");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error)
|
toast.error(error);
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
ticketHistory,
|
||||||
|
headline,
|
||||||
motives,
|
motives,
|
||||||
ticket,
|
ticket,
|
||||||
|
getSince,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped></style>
|
||||||
</style>
|
|
||||||
|
@ -0,0 +1,191 @@
|
|||||||
|
<template>
|
||||||
|
<div class="fixed-bottom">
|
||||||
|
<div class="footer-ticket-details">
|
||||||
|
<div class="tab-content p-2">
|
||||||
|
|
||||||
|
<div v-if="activeTab">
|
||||||
|
<label class="col-form-label">
|
||||||
|
{{ $t(`${activeTab}.title`) }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="activeTab === 'comment'">
|
||||||
|
<form @submit.prevent="createComment">
|
||||||
|
<ckeditor
|
||||||
|
name="content"
|
||||||
|
:placeholder="$t('comment.content')"
|
||||||
|
:editor="editor"
|
||||||
|
v-model="content"
|
||||||
|
tag-name="textarea">
|
||||||
|
</ckeditor>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end p-2">
|
||||||
|
<button class="btn btn-chill-green text-white float-right" type="submit">
|
||||||
|
<i class="fa fa-pencil"></i>
|
||||||
|
{{ $t("comment.save") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="activeTab === 'transfert'">
|
||||||
|
<form @submit.prevent="setAdressees" v-if="userGroups.length && users.length">
|
||||||
|
<addressee-selector-component v-model="addressees" :user-groups="userGroups" :users="users" />
|
||||||
|
<div class="d-flex justify-content-end p-1">
|
||||||
|
<button class="btn btn-chill-green text-white float-right" type="submit">
|
||||||
|
<i class="fa fa-pencil"></i>
|
||||||
|
{{ $t("transfert.save") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="activeTab === 'motive'">
|
||||||
|
<form @submit.prevent="createMotive">
|
||||||
|
|
||||||
|
<motive-selector-component v-model="motive" :motives="motives" />
|
||||||
|
<div class="d-flex justify-content-end p-1">
|
||||||
|
<button class="btn btn-chill-green text-white float-right" type="submit">
|
||||||
|
<i class="fa fa-pencil"></i>
|
||||||
|
{{ $t("motive.save") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="footer-ticket-main">
|
||||||
|
<ul class="nav nav-tabs justify-content-end">
|
||||||
|
<li class="nav-item">
|
||||||
|
<button type="button" class="m-2 btn btn-light" @click="activeTab = 'comment'">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
{{ $t('comment.title') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button type="button" class="m-2 btn btn-light" @click="activeTab = 'transfert'">
|
||||||
|
<i class="fa fa-paper-plane"></i>
|
||||||
|
{{ $t('transfert.title') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button type="button" class="m-2 btn btn-light" @click="activeTab = 'motive'">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
{{ $t('motive.title') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button type="button" class="m-2 btn btn-light" @click="activeTab = ''">
|
||||||
|
<i class="fa fa-bolt"></i>
|
||||||
|
Fermer
|
||||||
|
</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";
|
||||||
|
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||||
|
import ClassicEditor from "../../../../../../../ChillMainBundle/Resources/public/module/ckeditor5";
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "ActionToolbarComponent",
|
||||||
|
components: {
|
||||||
|
MotiveSelectorComponent,
|
||||||
|
AddresseeSelectorComponent,
|
||||||
|
ckeditor: CKEditor.component,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const store = useStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const toast = inject('toast') as any;
|
||||||
|
const activeTab = ref("");
|
||||||
|
|
||||||
|
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([] as Array<UserGroupOrUser>);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function createMotive() {
|
||||||
|
try {
|
||||||
|
await store.dispatch("createMotive", {
|
||||||
|
ticketId: ticket.value.id,
|
||||||
|
motive: motive.value,
|
||||||
|
});
|
||||||
|
toast.success(t("motive.success"))
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createComment() {
|
||||||
|
try {
|
||||||
|
await store.dispatch("createComment", {
|
||||||
|
ticketId: ticket.value.id,
|
||||||
|
content: content.value,
|
||||||
|
});
|
||||||
|
content.value = "";
|
||||||
|
toast.success(t("comment.success"))
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setAdressees() {
|
||||||
|
try {
|
||||||
|
await store.dispatch("setAdressees", {
|
||||||
|
ticketId: ticket.value.id,
|
||||||
|
addressees: addressees.value,
|
||||||
|
});
|
||||||
|
toast.success(t("transfert.success"))
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
activeTab,
|
||||||
|
ticket,
|
||||||
|
motives,
|
||||||
|
motive,
|
||||||
|
userGroups,
|
||||||
|
addressees,
|
||||||
|
users,
|
||||||
|
content,
|
||||||
|
editor: ClassicEditor,
|
||||||
|
createMotive,
|
||||||
|
createComment,
|
||||||
|
setAdressees,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
div.fixed-bottom {
|
||||||
|
div.footer-ticket-main {
|
||||||
|
background: none repeat scroll 0 0 #cabb9f;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.footer-ticket-details {
|
||||||
|
background: none repeat scroll 0 0 #efe2ca;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,215 @@
|
|||||||
|
<template>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-lg-6 col-md-6 mb-2 text-center">
|
||||||
|
<span class="m-1">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
class="btn-check"
|
||||||
|
name="options-outlined"
|
||||||
|
id="level-none"
|
||||||
|
autocomplete="off"
|
||||||
|
:value="{}"
|
||||||
|
v-model="userGroupLevel"
|
||||||
|
/>
|
||||||
|
<label :class="`btn btn-outline-primary`" for="level-none">
|
||||||
|
Aucun
|
||||||
|
</label>
|
||||||
|
</span>
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
:class="`btn btn-${userGroupItem.id}`"
|
||||||
|
:for="`level-${userGroupItem.id}`"
|
||||||
|
:style="getUserGroupBtnColor(userGroupItem)"
|
||||||
|
>
|
||||||
|
{{ userGroupItem.label.fr }}
|
||||||
|
</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-6 col-md-6 mb-2 text-center">
|
||||||
|
<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 class="col-12 col-lg-6 col-md-6 mb-2 text-center">
|
||||||
|
<add-persons
|
||||||
|
:options="addPersonsOptions"
|
||||||
|
key="add-person-ticket"
|
||||||
|
buttonTitle="transfert.user_label"
|
||||||
|
modalTitle="transfert.user_label"
|
||||||
|
ref="addPersons"
|
||||||
|
@addNewPersons="addNewEntity"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-6 col-md-6 mb-2 mb-2 text-center">
|
||||||
|
<span class="badge text-bg-light m-1" v-for="user in users">
|
||||||
|
{{ user.username }}
|
||||||
|
</span>
|
||||||
|
</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) {
|
||||||
|
// Cant use UserGroupOrUser[] because of TS2367
|
||||||
|
// TS2367: This comparison appears to be unintentional because the types '"user" | "chill_main_user_group"' and '"user_group"' have no overlap.
|
||||||
|
const addressees = ref(props.modelValue as any[]);
|
||||||
|
const userGroupLevel = ref({} as UserGroupOrUser);
|
||||||
|
const userGroup = ref([] as UserGroupOrUser[]);
|
||||||
|
const users = ref([] 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",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(userGroupLevel, (userGroupLevelAdd, userGroupLevelRem) => {
|
||||||
|
if (userGroupLevelRem) {
|
||||||
|
addressees.value.splice(
|
||||||
|
addressees.value.indexOf(userGroupLevelRem),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
addressees.value.push(userGroupLevelAdd);
|
||||||
|
ctx.emit("update:modelValue", addressees.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(userGroup, (userGroupAdd) => {
|
||||||
|
addressees.value = addressees.value.filter(
|
||||||
|
(addressee) => addressee.excludeKey !== ""
|
||||||
|
);
|
||||||
|
addressees.value = [...addressees.value, ...userGroupAdd];
|
||||||
|
ctx.emit("update:modelValue", addressees.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
addressees,
|
||||||
|
userGroupLevel,
|
||||||
|
userGroup,
|
||||||
|
users,
|
||||||
|
addPersons,
|
||||||
|
addPersonsOptions,
|
||||||
|
addNewEntity,
|
||||||
|
getUserGroupBtnColor,
|
||||||
|
customUserGroupLabel(selectedUserGroup: UserGroup) {
|
||||||
|
return selectedUserGroup.label
|
||||||
|
? selectedUserGroup.label.fr
|
||||||
|
: t("transfert.user_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>
|
@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-lg-4 col-md-6">
|
||||||
|
<vue-multiselect name="selectMotive" id="selectMotive" label="label" :custom-label="customLabel"
|
||||||
|
track-by="id" open-direction="top" :multiple="false" :searchable="true"
|
||||||
|
:placeholder="$t('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" />
|
||||||
|
</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('motive.label');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<div class="col-12" >
|
||||||
|
<i class="fa fa-paper-plane" v-if="event_type === 'add_addressee'"></i>
|
||||||
|
<i class="fa fa-paper-plane-o" v-else></i>
|
||||||
|
<span class="mx-1" v-if="addressee.type == 'user_group'">
|
||||||
|
{{
|
||||||
|
$t(`history.${event_type}_user_group`, {
|
||||||
|
user_group: addressee.label.fr,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span class="mx-1" v-else-if="addressee.type == 'user'">
|
||||||
|
{{
|
||||||
|
$t(`history.${event_type}_user`, {
|
||||||
|
user: addressee.username,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { PropType, defineComponent, ref } from "vue";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { AddresseeHistory } from "../../../types";
|
||||||
|
import { UserGroupOrUser } from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "TicketHistoryAddresseeComponent",
|
||||||
|
props: {
|
||||||
|
addresseeHistory: {
|
||||||
|
type: Object as PropType<AddresseeHistory>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
event_type: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props, ctx) {
|
||||||
|
return {
|
||||||
|
addressee: ref(props.addresseeHistory.addressee as UserGroupOrUser),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<div class="col-12">
|
||||||
|
<i class="fa fa-comment"></i>
|
||||||
|
<span class="mx-1">
|
||||||
|
{{ $t("history.comment") }}
|
||||||
|
</span>
|
||||||
|
<div class="mt-2">
|
||||||
|
<div v-html="convertMarkdownToHtml(commentHistory.content)"></div>
|
||||||
|
</div>
|
||||||
|
</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>
|
@ -0,0 +1,91 @@
|
|||||||
|
<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">
|
||||||
|
<span class="fw-bold fst-italic">
|
||||||
|
{{ 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 fst-italic">
|
||||||
|
<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">
|
||||||
|
<span class="fw-bold fst-italic">
|
||||||
|
{{ 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 fst-italic"
|
||||||
|
v-for="addressee in history_line"
|
||||||
|
:key="history_line.indexOf(addressee)"
|
||||||
|
>
|
||||||
|
<ticket-history-addressee-component :addresseeHistory="addressee.data" :event_type="addressee.event_type"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { PropType, defineComponent } from "vue";
|
||||||
|
import { DateTime } from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
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 { formatDate };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div class="col-12">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
<span class="mx-1">
|
||||||
|
{{ $t('history.motive',{ motive: motiveHistory.motive.label.fr }) }}
|
||||||
|
</span>
|
||||||
|
</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>
|
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<div class="col-12" v-if="personHistory.createdBy">
|
||||||
|
<i class="fa fa-eyedropper"></i>
|
||||||
|
<span class="mx-1">
|
||||||
|
{{ $t("history.user", { username: personHistory.createdBy.username }) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<i class="fa fa-bolt" style="min-width: 16px"></i>
|
||||||
|
<span class="mx-1">
|
||||||
|
{{ $t("history.person", { person: personHistory.person.text }) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { PropType, defineComponent } from "vue";
|
||||||
|
|
||||||
|
// Type
|
||||||
|
import { PersonHistory, Ticket } from "../../../types";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "TicketHistoryPersonComponent",
|
||||||
|
props: {
|
||||||
|
personHistory: {
|
||||||
|
type: Object as PropType<PersonHistory>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
setup() {},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -0,0 +1,41 @@
|
|||||||
|
<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,5 +1,52 @@
|
|||||||
export const messages = {
|
import { multiSelectMessages } from "../../../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
|
||||||
|
import { personMessages } from "../../../../../../../ChillPersonBundle/Resources/public/vuejs/_js/i18n";
|
||||||
|
|
||||||
|
const messages = {
|
||||||
fr: {
|
fr: {
|
||||||
hello: "Bonjour {name}"
|
ticket: {
|
||||||
}
|
previous_tickets: "Précédents tickets",
|
||||||
|
},
|
||||||
|
history: {
|
||||||
|
person: "Ouverture par appel téléphonique de {person}",
|
||||||
|
user: "Prise en charge par {username}",
|
||||||
|
motive: "Motif indiqué: {motive}",
|
||||||
|
comment: "Commentaire",
|
||||||
|
add_addressee_user_group: "Groupe {user_group} transferé",
|
||||||
|
remove_addressee_user_group: "Groupe {user_group} retiré",
|
||||||
|
add_addressee_user: " Utilisateur {user} Transferé",
|
||||||
|
remove_addressee_user: "Utilisateur {user} retiré",
|
||||||
|
},
|
||||||
|
comment: {
|
||||||
|
title: "Commentaire",
|
||||||
|
label: "Ajouter un commentaire",
|
||||||
|
save: "Enregistrer",
|
||||||
|
succcess: "Commentaire enregistré",
|
||||||
|
content: "Ajouter un commentaire",
|
||||||
|
},
|
||||||
|
motive: {
|
||||||
|
title: "Motif",
|
||||||
|
label: "Choisir un motif",
|
||||||
|
save: "Enregistrer",
|
||||||
|
success: "Motif enregistré",
|
||||||
|
},
|
||||||
|
transfert: {
|
||||||
|
title: "Transfert",
|
||||||
|
user_group_label: "Transferer vers un groupe",
|
||||||
|
user_label: "Transferer vers un ou plusieurs utilisateurs",
|
||||||
|
save: "Enregistrer",
|
||||||
|
success: "Transfert effectué",
|
||||||
|
},
|
||||||
|
close: "Fermer",
|
||||||
|
banner: {
|
||||||
|
concerned_patient: "Patient concerné",
|
||||||
|
caller: "Appelant",
|
||||||
|
speaker: "Intervenant",
|
||||||
|
open: "Ouvert",
|
||||||
|
since: "Aucun jour | Depuis 1 jour | Depuis {count} jours",
|
||||||
|
no_motive: "Pas de motif",
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
Object.assign(messages.fr, multiSelectMessages.fr);
|
||||||
|
Object.assign(messages.fr, personMessages.fr);
|
||||||
|
export default messages;
|
||||||
|
@ -2,12 +2,12 @@ import App from './App.vue';
|
|||||||
import {createApp} from "vue";
|
import {createApp} from "vue";
|
||||||
|
|
||||||
import { _createI18n } from "../../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
|
import { _createI18n } from "../../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
|
||||||
import {messages} from "./i18n/messages";
|
|
||||||
|
|
||||||
import VueToast from 'vue-toast-notification';
|
import VueToast from 'vue-toast-notification';
|
||||||
import 'vue-toast-notification/dist/theme-sugar.css';
|
import 'vue-toast-notification/dist/theme-sugar.css';
|
||||||
|
|
||||||
import { store } from "./store";
|
import { store } from "./store";
|
||||||
|
import messages from './i18n/messages';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@ -15,7 +15,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const i18n = _createI18n(messages);
|
const i18n = _createI18n(messages, false);
|
||||||
|
|
||||||
const _app = createApp({
|
const _app = createApp({
|
||||||
template: '<app></app>',
|
template: '<app></app>',
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
import { createStore } from "vuex";
|
import { createStore } from "vuex";
|
||||||
import { State as MotiveStates, moduleMotive } from "./modules/motive";
|
import { State as MotiveStates, moduleMotive } from "./modules/motive";
|
||||||
import { State as TicketStates, moduleTicket } from "./modules/ticket";
|
import { State as TicketStates, moduleTicket } from "./modules/ticket";
|
||||||
|
import { State as CommentStates, moduleComment } from "./modules/comment";
|
||||||
|
import { State as UserStates, moduleUser } from "./modules/user";
|
||||||
|
|
||||||
export type RootState = {
|
export type RootState = {
|
||||||
motive: MotiveStates;
|
motive: MotiveStates;
|
||||||
ticket: TicketStates;
|
ticket: TicketStates;
|
||||||
|
comment: CommentStates;
|
||||||
|
user: UserStates;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const store = createStore({
|
export const store = createStore({
|
||||||
modules: {
|
modules: {
|
||||||
motive:moduleMotive,
|
motive:moduleMotive,
|
||||||
ticket:moduleTicket,
|
ticket:moduleTicket,
|
||||||
|
comment:moduleComment,
|
||||||
|
user:moduleUser,
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
import {
|
||||||
|
fetchResults,
|
||||||
|
makeFetch,
|
||||||
|
} from "../../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||||
|
|
||||||
|
import { Module } from "vuex";
|
||||||
|
import { RootState } from "..";
|
||||||
|
|
||||||
|
import { Comment } from "../../../../types";
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
comments: Array<Comment>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const moduleComment: Module<State, RootState> = {
|
||||||
|
state: () => ({
|
||||||
|
comments: [] as Array<Comment>,
|
||||||
|
}),
|
||||||
|
getters: {},
|
||||||
|
mutations: {},
|
||||||
|
actions: {
|
||||||
|
async createComment(
|
||||||
|
{ commit },
|
||||||
|
datas: { ticketId: number; content: Comment["content"] }
|
||||||
|
) {
|
||||||
|
const { ticketId, content } = datas;
|
||||||
|
try {
|
||||||
|
const result = await makeFetch(
|
||||||
|
"POST",
|
||||||
|
`/api/1.0/ticket/${ticketId}/comment/add`,
|
||||||
|
{ content }
|
||||||
|
);
|
||||||
|
commit("setTicket", result);
|
||||||
|
}
|
||||||
|
catch(e: any) {
|
||||||
|
throw e.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@ -1,4 +1,7 @@
|
|||||||
import { fetchResults, makeFetch } from "../../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
import {
|
||||||
|
fetchResults,
|
||||||
|
makeFetch,
|
||||||
|
} from "../../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||||
|
|
||||||
import { Module } from "vuex";
|
import { Module } from "vuex";
|
||||||
import { RootState } from "..";
|
import { RootState } from "..";
|
||||||
@ -9,31 +12,52 @@ export interface State {
|
|||||||
motives: Array<Motive>;
|
motives: Array<Motive>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const moduleMotive: Module<State, RootState> ={
|
export const moduleMotive: Module<State, RootState> = {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
motives: [] as Array<Motive>,
|
motives: [] as Array<Motive>,
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getMotives(state) {
|
getMotives(state) {
|
||||||
return state.motives;
|
return state.motives;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setMotives(state, motives) {
|
setMotives(state, motives) {
|
||||||
state.motives = motives;
|
state.motives = motives;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async fetchMotives({ commit }) {
|
async fetchMotives({ commit }) {
|
||||||
const results = await fetchResults("/api/1.0/ticket/motive.json") as Motive[];
|
try {
|
||||||
|
const results = (await fetchResults(
|
||||||
|
"/api/1.0/ticket/motive.json"
|
||||||
|
)) as Motive[];
|
||||||
commit("setMotives", results);
|
commit("setMotives", results);
|
||||||
return results;
|
} catch (e: any) {
|
||||||
},
|
throw e.name;
|
||||||
async createMotive({ commit }, datas: {currentMotiveId: number, motive: Motive}) {
|
|
||||||
const { currentMotiveId, motive } = datas;
|
|
||||||
const result = await makeFetch("POST", `/api/1.0/ticket/${currentMotiveId}/motive/set`, motive);
|
|
||||||
commit("setMotives", result);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async createMotive(
|
||||||
|
{ commit },
|
||||||
|
datas: { ticketId: number; motive: Motive }
|
||||||
|
) {
|
||||||
|
const { ticketId, motive } = datas;
|
||||||
|
try {
|
||||||
|
const result = await makeFetch(
|
||||||
|
"POST",
|
||||||
|
`/api/1.0/ticket/${ticketId}/motive/set`,
|
||||||
|
{
|
||||||
|
motive: {
|
||||||
|
id: motive.id,
|
||||||
|
type: motive.type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
commit("setTicket", result);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw e.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { fetchResults, makeFetch } from "../../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
|
||||||
|
|
||||||
import { Module } from "vuex";
|
import { Module } from "vuex";
|
||||||
import { RootState } from "..";
|
import { RootState } from "..";
|
||||||
|
|
||||||
@ -16,7 +14,24 @@ export const moduleTicket: Module<State, RootState> ={
|
|||||||
getters: {
|
getters: {
|
||||||
getTicket(state) {
|
getTicket(state) {
|
||||||
return state.ticket;
|
return state.ticket;
|
||||||
|
},
|
||||||
|
getDistinctAddressesHistory(state) {
|
||||||
|
const addresseeHistory = state.ticket.history.reduce((result, item) => {
|
||||||
|
const { datetime } = item.at;
|
||||||
|
if (!["add_addressee","remove_addressee"].includes(item.event_type)) {
|
||||||
|
result[datetime] = item
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!result[datetime]) {
|
||||||
|
result[datetime] = [];
|
||||||
|
}
|
||||||
|
result[datetime].push(item);
|
||||||
|
return result;
|
||||||
|
}, {} as any);
|
||||||
|
return Object.values(addresseeHistory) as Array<Ticket["history"]>;
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setTicket(state, ticket) {
|
setTicket(state, ticket) {
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
import {
|
||||||
|
fetchResults,
|
||||||
|
makeFetch,
|
||||||
|
} from "../../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||||
|
|
||||||
|
import { Module } from "vuex";
|
||||||
|
import { RootState } from "..";
|
||||||
|
|
||||||
|
import {
|
||||||
|
User,
|
||||||
|
UserGroup,
|
||||||
|
UserGroupOrUser,
|
||||||
|
} from "../../../../../../../../ChillMainBundle/Resources/public/types";
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
userGroups: Array<UserGroup>;
|
||||||
|
users: Array<User>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const moduleUser: Module<State, RootState> = {
|
||||||
|
state: () => ({
|
||||||
|
userGroups: [] as Array<UserGroup>,
|
||||||
|
users: [] as Array<User>,
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
getUserGroups(state) {
|
||||||
|
return state.userGroups;
|
||||||
|
},
|
||||||
|
getUsers(state) {
|
||||||
|
return state.users;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
setUserGroups(state, userGroups) {
|
||||||
|
state.userGroups = userGroups;
|
||||||
|
},
|
||||||
|
setUsers(state, users) {
|
||||||
|
state.users = users;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchUserGroups({ commit }) {
|
||||||
|
try {
|
||||||
|
fetchResults("/api/1.0/main/user-group.json").then(
|
||||||
|
(results) => {
|
||||||
|
commit("setUserGroups", results);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw e.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchUsers({ commit }) {
|
||||||
|
try {
|
||||||
|
fetchResults("/api/1.0/main/user.json").then((results) => {
|
||||||
|
commit("setUsers", results);
|
||||||
|
});
|
||||||
|
} catch (e: any) {
|
||||||
|
throw e.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async setAdressees(
|
||||||
|
{ commit },
|
||||||
|
datas: { ticketId: number; addressees: Array<UserGroupOrUser> }
|
||||||
|
) {
|
||||||
|
const { ticketId, addressees } = datas;
|
||||||
|
try {
|
||||||
|
const result = await makeFetch(
|
||||||
|
"POST",
|
||||||
|
`/api/1.0/ticket/${ticketId}/addressees/set`,
|
||||||
|
{
|
||||||
|
addressees: addressees.map((addressee) => {
|
||||||
|
return { id: addressee.id, type: addressee.type };
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
commit("setTicket", result);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw e.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@ -1,24 +1,8 @@
|
|||||||
<div class="banner banner-ticket">
|
<div class="banner banner-ticket ">
|
||||||
<div id="header-ticket-main" class="header-name">
|
<div id="header-ticket-main" class="header-name">
|
||||||
<div class="container-xxl">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6 ps-md-5 ps-xxl-0">
|
|
||||||
TODO
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
TODO
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="header-ticket-details" class="header-details">
|
<div id="header-ticket-details" class="header-details">
|
||||||
<div class="container-xxl">
|
|
||||||
<div class="row justify-content-between">
|
|
||||||
<div class="col-md-12 ps-md-5 ps-xxl-0 container">
|
|
||||||
<p>TODO</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,9 +13,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block wrapping_content %}
|
{% block wrapping_content %}
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-8 col-sm-12">
|
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -42,6 +42,8 @@ final class TicketNormalizer implements NormalizerInterface, NormalizerAwareInte
|
|||||||
'currentInputs' => $this->normalizer->normalize($object->getCurrentInputs(), $format, ['groups' => 'read']),
|
'currentInputs' => $this->normalizer->normalize($object->getCurrentInputs(), $format, ['groups' => 'read']),
|
||||||
'currentMotive' => $this->normalizer->normalize($object->getMotive(), $format, ['groups' => 'read']),
|
'currentMotive' => $this->normalizer->normalize($object->getMotive(), $format, ['groups' => 'read']),
|
||||||
'history' => array_values($this->serializeHistory($object, $format, ['groups' => 'read'])),
|
'history' => array_values($this->serializeHistory($object, $format, ['groups' => 'read'])),
|
||||||
|
'createdAt' => $object->getCreatedAt(),
|
||||||
|
'updatedBy' => $object->getUpdatedBy(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user