mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-24 07:35:03 +00:00
Merge branch 'refs/heads/master' into 321-text-editor
# Conflicts: # src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/editor_config.ts # src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/index.ts # src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Comment.vue # src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources/WriteComment.vue # src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue # src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue # src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/MemberDetails.vue # src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/PersonComment.vue # yarn.lock
This commit is contained in:
@@ -63,7 +63,6 @@ abstract class AbstractCRUDController extends AbstractController
|
||||
parent::getSubscribedServices(),
|
||||
[
|
||||
'chill_main.paginator_factory' => PaginatorFactory::class,
|
||||
ManagerRegistry::class => ManagerRegistry::class,
|
||||
'translator' => TranslatorInterface::class,
|
||||
AuthorizationHelper::class => AuthorizationHelper::class,
|
||||
EventDispatcherInterface::class => EventDispatcherInterface::class,
|
||||
@@ -213,7 +212,7 @@ abstract class AbstractCRUDController extends AbstractController
|
||||
|
||||
protected function getManagerRegistry(): ManagerRegistry
|
||||
{
|
||||
return $this->container->get(ManagerRegistry::class);
|
||||
return $this->container->get('doctrine');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +225,7 @@ abstract class AbstractCRUDController extends AbstractController
|
||||
|
||||
protected function getValidator(): ValidatorInterface
|
||||
{
|
||||
return $this->get('validator');
|
||||
return $this->container->get('validator');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -18,6 +18,7 @@ use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
class ExportType extends AbstractType
|
||||
{
|
||||
@@ -29,7 +30,15 @@ class ExportType extends AbstractType
|
||||
|
||||
final public const PICK_FORMATTER_KEY = 'pick_formatter';
|
||||
|
||||
public function __construct(private readonly ExportManager $exportManager, private readonly SortExportElement $sortExportElement) {}
|
||||
private array $personFieldsConfig;
|
||||
|
||||
public function __construct(
|
||||
private readonly ExportManager $exportManager,
|
||||
private readonly SortExportElement $sortExportElement,
|
||||
protected ParameterBagInterface $parameterBag,
|
||||
) {
|
||||
$this->personFieldsConfig = $parameterBag->get('chill_person.person_fields');
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
@@ -77,6 +86,17 @@ class ExportType extends AbstractType
|
||||
);
|
||||
|
||||
foreach ($aggregators as $alias => $aggregator) {
|
||||
/*
|
||||
* eventually mask aggregator
|
||||
*/
|
||||
if (str_starts_with((string) $alias, 'person_') and str_ends_with((string) $alias, '_aggregator')) {
|
||||
$field = preg_replace(['/person_/', '/_aggregator/'], '', (string) $alias);
|
||||
if (array_key_exists($field, $this->personFieldsConfig) and 'visible' !== $this->personFieldsConfig[$field]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$aggregatorBuilder->add($alias, AggregatorType::class, [
|
||||
'aggregator_alias' => $alias,
|
||||
'export_manager' => $this->exportManager,
|
||||
|
@@ -75,8 +75,8 @@ final class UserGroupRepository implements UserGroupRepositoryInterface, LocaleA
|
||||
->setWhereClauses('
|
||||
ug.active AND (
|
||||
SIMILARITY(LOWER(UNACCENT(?)), ug.label->>?) > 0.15
|
||||
OR ug.label->>? LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\')
|
||||
', [$pattern, $lang, $pattern, $lang]);
|
||||
OR LOWER(UNACCENT(ug.label->>?)) LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\')
|
||||
', [$pattern, $lang, $lang, $pattern]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
@@ -25,7 +25,34 @@ div.flex-table {
|
||||
div.item-col:last-child {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
div.item-two-col-grid {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
justify-content: stretch;
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
grid-template-areas:
|
||||
"title aside";
|
||||
grid-template-columns: 1fr minmax(8rem, 1fr);
|
||||
column-gap: 0.5em;
|
||||
}
|
||||
@include media-breakpoint-down(lg) {
|
||||
grid-template-areas:
|
||||
"aside"
|
||||
"title";
|
||||
}
|
||||
|
||||
& > div.title {
|
||||
grid-area: title;
|
||||
}
|
||||
|
||||
& > div.aside {
|
||||
grid-area: aside;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
h2, h3, h4, dl, p {
|
||||
|
@@ -3,7 +3,6 @@ import {
|
||||
Bold,
|
||||
Italic,
|
||||
Paragraph,
|
||||
Mention,
|
||||
Markdown,
|
||||
BlockQuote,
|
||||
Heading,
|
||||
@@ -16,9 +15,18 @@ import 'ckeditor5/ckeditor5.css';
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
|
||||
export default {
|
||||
plugins: [Essentials, Markdown, Bold, Italic, BlockQuote, Heading, Link, List, Paragraph],
|
||||
plugins: [
|
||||
Essentials,
|
||||
Markdown,
|
||||
Bold,
|
||||
Italic,
|
||||
BlockQuote,
|
||||
Heading,
|
||||
Link,
|
||||
List,
|
||||
Paragraph,
|
||||
],
|
||||
toolbar: {
|
||||
items: [
|
||||
"heading",
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import { createApp } from "vue";
|
||||
import NotificationReadToggle from "ChillMainAssets/vuejs/_components/Notification/NotificationReadToggle.vue";
|
||||
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
|
||||
import NotificationReadAllToggle from "ChillMainAssets/vuejs/_components/Notification/NotificationReadAllToggle.vue";
|
||||
|
||||
const i18n = _createI18n({});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function (e) {
|
||||
window.addEventListener("DOMContentLoaded", function () {
|
||||
document
|
||||
.querySelectorAll(".notification_toggle_read_status")
|
||||
.forEach(function (el, i) {
|
||||
|
@@ -26,12 +26,12 @@ function loadDynamicPicker(element) {
|
||||
? JSON.parse(input.value)
|
||||
: input.value === "[]" || input.value === ""
|
||||
? null
|
||||
: [JSON.parse(input.value)];
|
||||
(suggested = JSON.parse(el.dataset.suggested)),
|
||||
(as_id = parseInt(el.dataset.asId) === 1),
|
||||
(submit_on_adding_new_entity =
|
||||
parseInt(el.dataset.submitOnAddingNewEntity) === 1);
|
||||
label = el.dataset.label;
|
||||
: [JSON.parse(input.value)],
|
||||
suggested = JSON.parse(el.dataset.suggested),
|
||||
as_id = parseInt(el.dataset.asId) === 1,
|
||||
submit_on_adding_new_entity =
|
||||
parseInt(el.dataset.submitOnAddingNewEntity) === 1,
|
||||
label = el.dataset.label;
|
||||
|
||||
if (!isMultiple) {
|
||||
if (input.value === "[]") {
|
||||
@@ -177,7 +177,7 @@ document.addEventListener("pick-entity-type-action", function (e) {
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
loadDynamicPicker(document);
|
||||
});
|
||||
|
||||
|
@@ -1,45 +0,0 @@
|
||||
import { createApp } from "vue";
|
||||
import OpenWopiLink from "ChillMainAssets/vuejs/_components/OpenWopiLink";
|
||||
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
|
||||
|
||||
const i18n = _createI18n({});
|
||||
|
||||
//TODO move to chillDocStore or ChillWopi
|
||||
|
||||
/*
|
||||
|
||||
tags to load module:
|
||||
|
||||
<span data-module="wopi-link"
|
||||
data-wopi-url="{{ path('chill_wopi_file_edit', {'fileId': document.uuid}) }}"
|
||||
data-doc-type="{{ document.type|e('html_attr') }}"
|
||||
data-options="{{ options|json_encode }}"
|
||||
></span>
|
||||
|
||||
*/
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function (e) {
|
||||
document
|
||||
.querySelectorAll('span[data-module="wopi-link"]')
|
||||
.forEach(function (el) {
|
||||
createApp({
|
||||
template:
|
||||
'<open-wopi-link :wopiUrl="wopiUrl" :type="type" :options="options"></open-wopi-link>',
|
||||
components: {
|
||||
OpenWopiLink,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
wopiUrl: el.dataset.wopiUrl,
|
||||
type: el.dataset.docType,
|
||||
options:
|
||||
el.dataset.options !== "null"
|
||||
? JSON.parse(el.dataset.options)
|
||||
: {},
|
||||
};
|
||||
},
|
||||
})
|
||||
.use(i18n)
|
||||
.mount(el);
|
||||
});
|
||||
});
|
@@ -45,6 +45,10 @@ const onPickGenericDoc = ({
|
||||
}) => {
|
||||
emit("pickGenericDoc", { genericDoc });
|
||||
};
|
||||
|
||||
const onRemoveAttachment = (payload: { attachment: WorkflowAttachment }) => {
|
||||
emit("removeAttachment", payload);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -56,7 +60,7 @@ const onPickGenericDoc = ({
|
||||
></pick-generic-doc-modal>
|
||||
<attachment-list
|
||||
:attachments="props.attachments"
|
||||
@removeAttachment="(payload) => emit('removeAttachment', payload)"
|
||||
@removeAttachment="onRemoveAttachment"
|
||||
></attachment-list>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
|
@@ -72,6 +72,14 @@ const placeTrans = (str: string): string => {
|
||||
}
|
||||
};
|
||||
|
||||
const onPickDocument = (payload: {
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
}) => emit("pickGenericDoc", payload);
|
||||
|
||||
const onRemoveGenericDoc = (payload: {
|
||||
genericDoc: GenericDocForAccompanyingPeriod;
|
||||
}) => emit("removeGenericDoc", payload);
|
||||
|
||||
const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
|
||||
if (false === loaded.value) {
|
||||
return [];
|
||||
@@ -245,10 +253,8 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
|
||||
:accompanying-period-id="accompanyingPeriodId"
|
||||
:genericDoc="g"
|
||||
:is-picked="isPicked(g)"
|
||||
@pickGenericDoc="(payload) => emit('pickGenericDoc', payload)"
|
||||
@removeGenericDoc="
|
||||
(payload) => emit('removeGenericDoc', payload)
|
||||
"
|
||||
@pickGenericDoc="onPickDocument"
|
||||
@removeGenericDoc="onRemoveGenericDoc"
|
||||
></pick-generic-doc-item>
|
||||
</div>
|
||||
<div v-else class="text-center chill-no-data-statement">
|
||||
|
@@ -70,7 +70,8 @@ const clickOnAddButton = () => {
|
||||
<style scoped lang="scss">
|
||||
.item-bloc {
|
||||
&.isPicked {
|
||||
background: linear-gradient(
|
||||
background:
|
||||
linear-gradient(
|
||||
180deg,
|
||||
rgba(25, 135, 84, 1) 0px,
|
||||
rgba(25, 135, 84, 0) 9px
|
||||
|
@@ -1,61 +1,66 @@
|
||||
<template>
|
||||
<span v-if="entity.type === 'person'" class="badge rounded-pill bg-person">
|
||||
{{ $t("person") }}
|
||||
<span
|
||||
v-if="props.entity.type === 'person'"
|
||||
class="badge rounded-pill bg-person"
|
||||
>
|
||||
{{ trans(PERSON) }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="entity.type === 'thirdparty'"
|
||||
v-if="props.entity.type === 'thirdparty'"
|
||||
class="badge rounded-pill bg-thirdparty"
|
||||
>
|
||||
<template v-if="options.displayLong !== true">
|
||||
{{ $t("thirdparty.thirdparty") }}
|
||||
<template v-if="props.options.displayLong !== true">
|
||||
{{ trans(THIRDPARTY) }}
|
||||
</template>
|
||||
|
||||
<i class="fa fa-fw fa-user" v-if="entity.kind === 'child'" />
|
||||
<i class="fa fa-fw fa-user" v-if="props.entity.kind === 'child'" />
|
||||
<i
|
||||
class="fa fa-fw fa-hospital-o"
|
||||
v-else-if="entity.kind === 'company'"
|
||||
v-else-if="props.entity.kind === 'company'"
|
||||
/>
|
||||
<i class="fa fa-fw fa-user-md" v-else />
|
||||
|
||||
<template v-if="options.displayLong === true">
|
||||
<span v-if="entity.kind === 'child'">{{
|
||||
$t("thirdparty.child")
|
||||
<template v-if="props.options.displayLong === true">
|
||||
<span v-if="props.entity.kind === 'child'">{{
|
||||
trans(THIRDPARTY_CONTACT_OF)
|
||||
}}</span>
|
||||
<span v-else-if="entity.kind === 'company'">{{
|
||||
$t("thirdparty.company")
|
||||
<span v-else-if="props.entity.kind === 'company'">{{
|
||||
trans(THIRDPARTY_A_COMPANY)
|
||||
}}</span>
|
||||
<span v-else>{{ $t("thirdparty.contact") }}</span>
|
||||
<span v-else>{{ trans(THIRDPARTY_A_CONTACT) }}</span>
|
||||
</template>
|
||||
</span>
|
||||
|
||||
<span v-if="entity.type === 'user'" class="badge rounded-pill bg-user">
|
||||
{{ $t("user") }}
|
||||
<span
|
||||
v-if="props.entity.type === 'user'"
|
||||
class="badge rounded-pill bg-user"
|
||||
>
|
||||
{{ trans(ACCEPTED_USERS) }}
|
||||
</span>
|
||||
|
||||
<span v-if="entity.type === 'household'" class="badge rounded-pill bg-user">
|
||||
{{ $t("household") }}
|
||||
<span
|
||||
v-if="props.entity.type === 'household'"
|
||||
class="badge rounded-pill bg-user"
|
||||
>
|
||||
{{ trans(HOUSEHOLD) }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "BadgeEntity",
|
||||
props: ["options", "entity"],
|
||||
i18n: {
|
||||
messages: {
|
||||
fr: {
|
||||
person: "Usager",
|
||||
thirdparty: {
|
||||
thirdparty: "Tiers",
|
||||
child: "Personne de contact",
|
||||
company: "Personne morale",
|
||||
contact: "Personne physique",
|
||||
},
|
||||
user: "TMS",
|
||||
household: "Ménage",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
<script setup>
|
||||
import {
|
||||
trans,
|
||||
HOUSEHOLD,
|
||||
ACCEPTED_USERS,
|
||||
THIRDPARTY_A_CONTACT,
|
||||
THIRDPARTY_CONTACT_OF,
|
||||
THIRDPARTY_A_COMPANY,
|
||||
PERSON,
|
||||
THIRDPARTY,
|
||||
} from "translator";
|
||||
|
||||
const props = defineProps({
|
||||
options: Object,
|
||||
entity: Object,
|
||||
});
|
||||
</script>
|
||||
|
@@ -66,13 +66,13 @@
|
||||
<div v-if="useDatePane === true" class="address-more">
|
||||
<div v-if="address.validFrom">
|
||||
<span class="validFrom">
|
||||
<b>{{ $t("validFrom") }}</b
|
||||
<b>{{ trans(ADDRESS_VALID_FROM) }}</b
|
||||
>: {{ $d(address.validFrom.date) }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="address.validTo">
|
||||
<span class="validTo">
|
||||
<b>{{ $t("validTo") }}</b
|
||||
<b>{{ trans(ADDRESS_VALID_TO) }}</b
|
||||
>: {{ $d(address.validTo.date) }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -83,6 +83,7 @@
|
||||
<script>
|
||||
import Confidential from "ChillMainAssets/vuejs/_components/Confidential.vue";
|
||||
import AddressDetailsButton from "ChillMainAssets/vuejs/_components/AddressDetails/AddressDetailsButton.vue";
|
||||
import { trans, ADDRESS_VALID_FROM, ADDRESS_VALID_TO } from "translator";
|
||||
|
||||
export default {
|
||||
name: "AddressRenderBox",
|
||||
@@ -107,6 +108,9 @@ export default {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
return { trans, ADDRESS_VALID_FROM, ADDRESS_VALID_TO };
|
||||
},
|
||||
computed: {
|
||||
component() {
|
||||
return this.isMultiline === true ? "div" : "span";
|
||||
|
@@ -6,8 +6,8 @@
|
||||
v-if="!subscriberFinal"
|
||||
@click="subscribeTo('subscribe', 'final')"
|
||||
>
|
||||
<i class="fa fa-check fa-fw" />
|
||||
{{ $t("subscribe_final") }}
|
||||
<i class="fa fa-check fa-fw"></i>
|
||||
{{ trans(WORKFLOW_SUBSCRIBE_FINAL) }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-misc"
|
||||
@@ -15,8 +15,8 @@
|
||||
v-if="subscriberFinal"
|
||||
@click="subscribeTo('unsubscribe', 'final')"
|
||||
>
|
||||
<i class="fa fa-times fa-fw" />
|
||||
{{ $t("unsubscribe_final") }}
|
||||
<i class="fa fa-times fa-fw"></i>
|
||||
{{ trans(WORKFLOW_UNSUBSCRIBE_FINAL) }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-misc"
|
||||
@@ -24,8 +24,8 @@
|
||||
v-if="!subscriberStep"
|
||||
@click="subscribeTo('subscribe', 'step')"
|
||||
>
|
||||
<i class="fa fa-check fa-fw" />
|
||||
{{ $t("subscribe_all_steps") }}
|
||||
<i class="fa fa-check fa-fw"></i>
|
||||
{{ trans(WORKFLOW_SUBSCRIBE_ALL_STEPS) }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-misc"
|
||||
@@ -33,94 +33,55 @@
|
||||
v-if="subscriberStep"
|
||||
@click="subscribeTo('unsubscribe', 'step')"
|
||||
>
|
||||
<i class="fa fa-times fa-fw" />
|
||||
{{ $t("unsubscribe_all_steps") }}
|
||||
<i class="fa fa-times fa-fw"></i>
|
||||
{{ trans(WORKFLOW_UNSUBSCRIBE_ALL_STEPS) }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods.ts";
|
||||
import { defineProps, defineEmits } from "vue";
|
||||
import {
|
||||
trans,
|
||||
WORKFLOW_SUBSCRIBE_FINAL,
|
||||
WORKFLOW_UNSUBSCRIBE_FINAL,
|
||||
WORKFLOW_SUBSCRIBE_ALL_STEPS,
|
||||
WORKFLOW_UNSUBSCRIBE_ALL_STEPS,
|
||||
} from "translator";
|
||||
|
||||
export default {
|
||||
name: "EntityWorkflowVueSubscriber",
|
||||
i18n: {
|
||||
messages: {
|
||||
fr: {
|
||||
subscribe_final: "Recevoir une notification à l'étape finale",
|
||||
unsubscribe_final:
|
||||
"Ne plus recevoir de notification à l'étape finale",
|
||||
subscribe_all_steps:
|
||||
"Recevoir une notification à chaque étape du suivi",
|
||||
unsubscribe_all_steps:
|
||||
"Ne plus recevoir de notification à chaque étape du suivi",
|
||||
},
|
||||
},
|
||||
// props
|
||||
const props = defineProps({
|
||||
entityWorkflowId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
props: {
|
||||
entityWorkflowId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
subscriberStep: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
subscriberFinal: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
subscriberStep: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
emits: ["subscriptionUpdated"],
|
||||
methods: {
|
||||
subscribeTo(step, to) {
|
||||
let params = new URLSearchParams();
|
||||
params.set("subscribe", to);
|
||||
subscriberFinal: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const url =
|
||||
`/api/1.0/main/workflow/${this.entityWorkflowId}/${step}?` +
|
||||
params.toString();
|
||||
//methods
|
||||
const subscribeTo = (step, to) => {
|
||||
let params = new URLSearchParams();
|
||||
params.set("subscribe", to);
|
||||
|
||||
makeFetch("POST", url).then((response) => {
|
||||
this.$emit("subscriptionUpdated", response);
|
||||
});
|
||||
},
|
||||
},
|
||||
const url =
|
||||
`/api/1.0/main/workflow/${props.entityWorkflowId}/${step}?` +
|
||||
params.toString();
|
||||
|
||||
makeFetch("POST", url).then((response) => {
|
||||
emit("subscriptionUpdated", response);
|
||||
});
|
||||
};
|
||||
/*
|
||||
* ALTERNATIVES
|
||||
*
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="laststep">
|
||||
<label class="form-check-label" for="laststep">{{ $t('subscribe_final') }}</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="allsteps">
|
||||
<label class="form-check-label" for="allsteps">{{ $t('subscribe_all_steps') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="list-group my-3">
|
||||
<label class="list-group-item">
|
||||
<input class="form-check-input me-1" type="checkbox" value="">
|
||||
{{ $t('subscribe_final') }}
|
||||
</label>
|
||||
<label class="list-group-item">
|
||||
<input class="form-check-input me-1" type="checkbox" value="">
|
||||
{{ $t('subscribe_all_steps') }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="btn-group-vertical my-3" role="group">
|
||||
<button type="button" class="btn btn-outline-primary">
|
||||
<i class="fa fa-check fa-fw"></i>
|
||||
{{ $t('subscribe_final') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary">
|
||||
<i class="fa fa-check fa-fw"></i>
|
||||
{{ $t('subscribe_all_steps') }}
|
||||
</button>
|
||||
</div>
|
||||
*/
|
||||
// emit
|
||||
const emit = defineEmits(["subscriptionUpdated"]);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex-table workflow" id="workflow-list">
|
||||
<div
|
||||
v-for="(w, i) in workflows"
|
||||
v-for="(w, i) in props.workflows"
|
||||
:key="`workflow-${i}`"
|
||||
class="item-bloc"
|
||||
>
|
||||
@@ -48,7 +48,7 @@
|
||||
<span
|
||||
v-if="w.isOnHoldAtCurrentStep"
|
||||
class="badge bg-success rounded-pill"
|
||||
>{{ $t("on_hold") }}</span
|
||||
>{{ trans(WORKFLOW_ON_HOLD) }}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -56,11 +56,11 @@
|
||||
<div class="item-col flex-grow-1">
|
||||
<p v-if="isUserSubscribedToStep(w)">
|
||||
<i class="fa fa-check fa-fw"></i>
|
||||
{{ $t("you_subscribed_to_all_steps") }}
|
||||
{{ trans(WORKFLOW_YOU_SUBSCRIBED_TO_ALL_STEPS) }}
|
||||
</p>
|
||||
<p v-if="isUserSubscribedToFinal(w)">
|
||||
<i class="fa fa-check fa-fw"></i>
|
||||
{{ $t("you_subscribed_to_final_step") }}
|
||||
{{ trans(WORKFLOW_YOU_SUBSCRIBED_TO_FINAL_STEP) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="item-col">
|
||||
@@ -69,7 +69,7 @@
|
||||
<a
|
||||
:href="goToUrl(w)"
|
||||
class="btn btn-sm btn-show"
|
||||
:title="$t('action.show')"
|
||||
:title="trans(SEE)"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -79,85 +79,65 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import Popover from "bootstrap/js/src/popover";
|
||||
import { onMounted } from "vue";
|
||||
import {
|
||||
trans,
|
||||
BY_USER,
|
||||
SEE,
|
||||
WORKFLOW_YOU_SUBSCRIBED_TO_ALL_STEPS,
|
||||
WORKFLOW_YOU_SUBSCRIBED_TO_FINAL_STEP,
|
||||
WORKFLOW_ON_HOLD,
|
||||
WORKFLOW_AT,
|
||||
} from "translator";
|
||||
|
||||
const i18n = {
|
||||
messages: {
|
||||
fr: {
|
||||
you_subscribed_to_all_steps:
|
||||
"Vous recevrez une notification à chaque étape",
|
||||
you_subscribed_to_final_step:
|
||||
"Vous recevrez une notification à l'étape finale",
|
||||
by: "Par",
|
||||
at: "Le",
|
||||
on_hold: "En attente",
|
||||
},
|
||||
// props
|
||||
const props = defineProps({
|
||||
workflows: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
// methods
|
||||
const goToUrl = (w) => `/fr/main/workflow/${w.id}/show`;
|
||||
const getPopTitle = (step) => {
|
||||
if (step.transitionPrevious != null) {
|
||||
//console.log(step.transitionPrevious.text);
|
||||
let freezed = step.isFreezed
|
||||
? `<i class="fa fa-snowflake-o fa-sm me-1"></i>`
|
||||
: ``;
|
||||
return `${freezed}${step.transitionPrevious.text}`;
|
||||
}
|
||||
};
|
||||
const getPopContent = (step) => {
|
||||
if (step.transitionPrevious != null) {
|
||||
if (step.transitionPreviousBy !== null) {
|
||||
return `<ul class="small_in_title">
|
||||
<li><span class="item-key">${trans(BY_USER)} : </span><b>${step.transitionPreviousBy.text}</b></li>
|
||||
<li><span class="item-key">${trans(WORKFLOW_AT)} : </span><b>${formatDate(step.transitionPreviousAt.datetime)}</b></li>
|
||||
</ul>`;
|
||||
} else {
|
||||
return `<ul class="small_in_title">
|
||||
<li><span class="item-key">${trans(WORKFLOW_AT)} : </span><b>${formatDate(step.transitionPreviousAt.datetime)}</b></li>
|
||||
</ul>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
const formatDate = (datetime) =>
|
||||
datetime.split("T")[0] + " " + datetime.split("T")[1].substring(0, 5);
|
||||
const isUserSubscribedToStep = () => false;
|
||||
const isUserSubscribedToFinal = () => false;
|
||||
|
||||
export default {
|
||||
name: "ListWorkflow",
|
||||
i18n: i18n,
|
||||
props: {
|
||||
workflows: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
goToUrl(w) {
|
||||
return `/fr/main/workflow/${w.id}/show`;
|
||||
},
|
||||
getPopTitle(step) {
|
||||
if (step.transitionPrevious != null) {
|
||||
//console.log(step.transitionPrevious.text);
|
||||
let freezed = step.isFreezed
|
||||
? `<i class="fa fa-snowflake-o fa-sm me-1"></i>`
|
||||
: ``;
|
||||
return `${freezed}${step.transitionPrevious.text}`;
|
||||
}
|
||||
},
|
||||
getPopContent(step) {
|
||||
if (step.transitionPrevious != null) {
|
||||
if (step.transitionPreviousBy !== null) {
|
||||
return `<ul class="small_in_title">
|
||||
<li><span class="item-key">${i18n.messages.fr.by} : </span><b>${step.transitionPreviousBy.text}</b></li>
|
||||
<li><span class="item-key">${i18n.messages.fr.at} : </span><b>${this.formatDate(step.transitionPreviousAt.datetime)}</b></li>
|
||||
</ul>`;
|
||||
} else {
|
||||
return `<ul class="small_in_title">
|
||||
<li><span class="item-key">${i18n.messages.fr.at} : </span><b>${this.formatDate(step.transitionPreviousAt.datetime)}</b></li>
|
||||
</ul>`;
|
||||
}
|
||||
}
|
||||
},
|
||||
formatDate(datetime) {
|
||||
return (
|
||||
datetime.split("T")[0] +
|
||||
" " +
|
||||
datetime.split("T")[1].substring(0, 5)
|
||||
);
|
||||
},
|
||||
isUserSubscribedToStep(w) {
|
||||
// todo
|
||||
return false;
|
||||
},
|
||||
isUserSubscribedToFinal(w) {
|
||||
// todo
|
||||
return false;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const triggerList = [].slice.call(
|
||||
document.querySelectorAll('[data-bs-toggle="popover"]'),
|
||||
);
|
||||
const popoverList = triggerList.map(function (el) {
|
||||
//console.log('popover', el)
|
||||
return new Popover(el, {
|
||||
html: true,
|
||||
});
|
||||
onMounted(() => {
|
||||
const triggerList = [].slice.call(
|
||||
document.querySelectorAll('[data-bs-toggle="popover"]'),
|
||||
);
|
||||
triggerList.map(function (el) {
|
||||
return new Popover(el, {
|
||||
html: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@@ -1,23 +1,24 @@
|
||||
<template>
|
||||
<pick-workflow
|
||||
:relatedEntityClass="this.relatedEntityClass"
|
||||
:relatedEntityId="this.relatedEntityId"
|
||||
:workflowsAvailables="workflowsAvailables"
|
||||
:preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate"
|
||||
:goToGenerateWorkflowPayload="this.goToGenerateWorkflowPayload"
|
||||
<Pick-workflow
|
||||
:relatedEntityClass="props.relatedEntityClass"
|
||||
:relatedEntityId="props.relatedEntityId"
|
||||
:workflowsAvailables="props.workflowsAvailables"
|
||||
:preventDefaultMoveToGenerate="props.preventDefaultMoveToGenerate"
|
||||
:goToGenerateWorkflowPayload="props.goToGenerateWorkflowPayload"
|
||||
:countExistingWorkflows="countWorkflows"
|
||||
:embedded-within-list-modal="false"
|
||||
@go-to-generate-workflow="goToGenerateWorkflow"
|
||||
@click-open-list="openModal"
|
||||
></pick-workflow>
|
||||
></Pick-workflow>
|
||||
|
||||
<teleport to="body">
|
||||
<modal
|
||||
<Modal
|
||||
v-if="modal.showModal"
|
||||
:modalDialogClass="modal.modalDialogClass"
|
||||
@close="modal.showModal = false"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<h2 class="modal-title">{{ $t("workflow_list") }}</h2>
|
||||
<h2 class="modal-title">{{ trans(WORKFLOW_LIST) }}</h2>
|
||||
</template>
|
||||
|
||||
<template v-slot:body>
|
||||
@@ -27,103 +28,80 @@
|
||||
<template v-slot:footer>
|
||||
<pick-workflow
|
||||
v-if="allowCreate"
|
||||
:relatedEntityClass="this.relatedEntityClass"
|
||||
:relatedEntityId="this.relatedEntityId"
|
||||
:workflowsAvailables="workflowsAvailables"
|
||||
:relatedEntityClass="props.relatedEntityClass"
|
||||
:relatedEntityId="props.relatedEntityId"
|
||||
:workflowsAvailables="props.workflowsAvailables"
|
||||
:preventDefaultMoveToGenerate="
|
||||
this.$props.preventDefaultMoveToGenerate
|
||||
props.preventDefaultMoveToGenerate
|
||||
"
|
||||
:goToGenerateWorkflowPayload="
|
||||
this.goToGenerateWorkflowPayload
|
||||
props.goToGenerateWorkflowPayload
|
||||
"
|
||||
:countExistingWorkflows="countWorkflows"
|
||||
:embedded-within-list-modal="true"
|
||||
@go-to-generate-workflow="this.goToGenerateWorkflow"
|
||||
@go-to-generate-workflow="goToGenerateWorkflow"
|
||||
></pick-workflow>
|
||||
</template>
|
||||
</modal>
|
||||
</Modal>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, computed, defineProps, defineEmits } from "vue";
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal";
|
||||
import PickWorkflow from "ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue";
|
||||
import ListWorkflowVue from "ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue";
|
||||
import { trans, WORKFLOW_LIST } from "translator";
|
||||
|
||||
export default {
|
||||
name: "ListWorkflowModal",
|
||||
components: {
|
||||
Modal,
|
||||
PickWorkflow,
|
||||
ListWorkflowVue,
|
||||
// Define props
|
||||
const props = defineProps({
|
||||
workflows: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
emits: ["goToGenerateWorkflow"],
|
||||
props: {
|
||||
workflows: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
allowCreate: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
relatedEntityClass: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
relatedEntityId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
workflowsAvailables: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
preventDefaultMoveToGenerate: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
goToGenerateWorkflowPayload: {
|
||||
required: false,
|
||||
default: {},
|
||||
},
|
||||
allowCreate: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
modal: {
|
||||
showModal: false,
|
||||
modalDialogClass: "modal-dialog-scrollable modal-xl",
|
||||
},
|
||||
};
|
||||
relatedEntityClass: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
computed: {
|
||||
countWorkflows() {
|
||||
return this.workflows.length;
|
||||
},
|
||||
hasWorkflow() {
|
||||
return this.countWorkflows > 0;
|
||||
},
|
||||
relatedEntityId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
methods: {
|
||||
openModal() {
|
||||
this.modal.showModal = true;
|
||||
},
|
||||
goToGenerateWorkflow(data) {
|
||||
console.log("go to generate workflow intercepted", data);
|
||||
this.$emit("goToGenerateWorkflow", data);
|
||||
},
|
||||
workflowsAvailables: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
i18n: {
|
||||
messages: {
|
||||
fr: {
|
||||
workflow_list: "Liste des workflows associés",
|
||||
workflow: " workflow associé",
|
||||
workflows: " workflows associés",
|
||||
},
|
||||
},
|
||||
preventDefaultMoveToGenerate: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
goToGenerateWorkflowPayload: {
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
// Define emits
|
||||
const emit = defineEmits(["goToGenerateWorkflow"]);
|
||||
|
||||
// Reactive data
|
||||
const modal = ref({
|
||||
showModal: false,
|
||||
modalDialogClass: "modal-dialog-scrollable modal-xl",
|
||||
});
|
||||
|
||||
// Computed properties
|
||||
const countWorkflows = computed(() => props.workflows.length);
|
||||
|
||||
// Methods
|
||||
const openModal = () => (modal.value.showModal = true);
|
||||
|
||||
const goToGenerateWorkflow = (data) => emit("goToGenerateWorkflow", data);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@@ -8,28 +8,28 @@
|
||||
aria-modal="true"
|
||||
role="dialog"
|
||||
>
|
||||
<div class="modal-dialog" :class="modalDialogClass">
|
||||
<div class="modal-dialog" :class="props.modalDialogClass || {}">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<slot name="header" />
|
||||
<button class="close btn" @click="$emit('close')">
|
||||
<i class="fa fa-times" aria-hidden="true" />
|
||||
<slot name="header"></slot>
|
||||
<button class="close btn" @click="emits('close')">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="body-head">
|
||||
<slot name="body-head" />
|
||||
<slot name="body-head"></slot>
|
||||
</div>
|
||||
<slot name="body" />
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
<div class="modal-footer" v-if="!hideFooter">
|
||||
<button
|
||||
class="btn btn-cancel"
|
||||
@click="$emit('close')"
|
||||
@click="emits('close')"
|
||||
>
|
||||
{{ $t("action.close") }}
|
||||
{{ trans(MODAL_ACTION_CLOSE) }}
|
||||
</button>
|
||||
<slot name="footer" />
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -39,8 +39,7 @@
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
<script lang="ts" setup>
|
||||
/*
|
||||
* This Modal component is a mix between Vue3 modal implementation
|
||||
* [+] with 'v-if:showModal' directive:parameter, html scope is added/removed not just shown/hidden
|
||||
@@ -50,22 +49,23 @@ import { defineComponent } from "vue";
|
||||
* [+] using bootstrap css classes, the modal have a responsive behaviour,
|
||||
* [+] modal design can be configured using css classes (size, scroll)
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: "Modal",
|
||||
props: {
|
||||
modalDialogClass: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {},
|
||||
},
|
||||
hideFooter: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["close"],
|
||||
import { trans, MODAL_ACTION_CLOSE } from "translator";
|
||||
import { defineProps } from "vue";
|
||||
|
||||
export interface ModalProps {
|
||||
modalDialogClass: object | null;
|
||||
hideFooter: boolean;
|
||||
}
|
||||
|
||||
// Define the props
|
||||
const props = withDefaults(defineProps<ModalProps>(), {
|
||||
hideFooter: false,
|
||||
modalDialogClass: null,
|
||||
});
|
||||
|
||||
const emits = defineEmits<{
|
||||
close: [];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
@@ -9,12 +9,12 @@
|
||||
class="btn"
|
||||
:class="overrideClass"
|
||||
type="button"
|
||||
:title="$t('markAsUnread')"
|
||||
:title="trans(NOTIFICATION_MARK_AS_UNREAD)"
|
||||
@click="markAsUnread"
|
||||
>
|
||||
<i class="fa fa-sm fa-envelope-o" />
|
||||
<span v-if="!buttonNoText" class="ps-2">
|
||||
{{ $t("markAsUnread") }}
|
||||
<i class="fa fa-sm fa-envelope-o"></i>
|
||||
<span v-if="!props.buttonNoText" class="ps-2">
|
||||
{{ trans(NOTIFICATION_MARK_AS_UNREAD) }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
class="btn"
|
||||
:class="overrideClass"
|
||||
type="button"
|
||||
:title="$t('markAsRead')"
|
||||
:title="trans(NOTIFICATION_MARK_AS_READ)"
|
||||
@click="markAsRead"
|
||||
>
|
||||
<i class="fa fa-sm fa-envelope-open-o" />
|
||||
<i class="fa fa-sm fa-envelope-open-o"></i>
|
||||
<span v-if="!buttonNoText" class="ps-2">
|
||||
{{ $t("markAsRead") }}
|
||||
{{ trans(NOTIFICATION_MARK_AS_READ) }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
type="button"
|
||||
class="btn btn-outline-primary"
|
||||
:href="showUrl"
|
||||
:title="$t('action.show')"
|
||||
:title="trans(SEE)"
|
||||
>
|
||||
<i class="fa fa-sm fa-comment-o" />
|
||||
<i class="fa fa-sm fa-comment-o"></i>
|
||||
</a>
|
||||
|
||||
<!-- "Mark All Read" button -->
|
||||
@@ -51,7 +51,7 @@
|
||||
:title="$t('markAllRead')"
|
||||
@click="markAllRead"
|
||||
>
|
||||
<i class="fa fa-sm fa-envelope-o" />
|
||||
<i class="fa fa-sm fa-envelope-o"></i>
|
||||
<span v-if="!buttonNoText" class="ps-2">
|
||||
{{ $t("markAllRead") }}
|
||||
</span>
|
||||
@@ -59,89 +59,66 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods.ts";
|
||||
import {
|
||||
trans,
|
||||
NOTIFICATION_MARK_AS_READ,
|
||||
NOTIFICATION_MARK_AS_UNREAD,
|
||||
SEE,
|
||||
} from "translator";
|
||||
|
||||
export default {
|
||||
name: "NotificationReadToggle",
|
||||
props: {
|
||||
isRead: {
|
||||
required: true,
|
||||
type: Boolean,
|
||||
},
|
||||
notificationId: {
|
||||
required: true,
|
||||
type: Number,
|
||||
},
|
||||
// Optional
|
||||
buttonClass: {
|
||||
required: false,
|
||||
type: String,
|
||||
},
|
||||
buttonNoText: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
},
|
||||
showUrl: {
|
||||
required: false,
|
||||
type: String,
|
||||
},
|
||||
// Props
|
||||
const props = defineProps({
|
||||
isRead: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
emits: ["markRead", "markUnread"],
|
||||
computed: {
|
||||
/// [Option] override default button appearance (btn-misc)
|
||||
overrideClass() {
|
||||
return this.buttonClass ? this.buttonClass : "btn-misc";
|
||||
},
|
||||
/// [Option] don't display text on button
|
||||
buttonHideText() {
|
||||
return this.buttonNoText;
|
||||
},
|
||||
/// [Option] showUrl is href for show page second button.
|
||||
// When passed, the component return a button-group with 2 buttons.
|
||||
isButtonGroup() {
|
||||
return this.showUrl;
|
||||
},
|
||||
notificationId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
methods: {
|
||||
markAsUnread() {
|
||||
makeFetch(
|
||||
"POST",
|
||||
`/api/1.0/main/notification/${this.notificationId}/mark/unread`,
|
||||
[],
|
||||
).then(() => {
|
||||
this.$emit("markRead", { notificationId: this.notificationId });
|
||||
});
|
||||
},
|
||||
markAsRead() {
|
||||
makeFetch(
|
||||
"POST",
|
||||
`/api/1.0/main/notification/${this.notificationId}/mark/read`,
|
||||
[],
|
||||
).then(() => {
|
||||
this.$emit("markUnread", {
|
||||
notificationId: this.notificationId,
|
||||
});
|
||||
});
|
||||
},
|
||||
markAllRead() {
|
||||
makeFetch(
|
||||
"POST",
|
||||
`/api/1.0/main/notification/markallread`,
|
||||
[],
|
||||
).then(() => {
|
||||
this.$emit("markAllRead");
|
||||
});
|
||||
},
|
||||
buttonClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
i18n: {
|
||||
messages: {
|
||||
fr: {
|
||||
markAsUnread: "Marquer comme non-lu",
|
||||
markAsRead: "Marquer comme lu",
|
||||
},
|
||||
},
|
||||
buttonNoText: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
showUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(["markRead", "markUnread"]);
|
||||
|
||||
// Computed
|
||||
const overrideClass = computed(() => props.buttonClass || "btn-misc");
|
||||
const isButtonGroup = computed(() => props.showUrl);
|
||||
|
||||
// Methods
|
||||
const markAsUnread = () => {
|
||||
makeFetch(
|
||||
"POST",
|
||||
`/api/1.0/main/notification/${props.notificationId}/mark/unread`,
|
||||
[],
|
||||
).then(() => {
|
||||
emit("markRead", { notificationId: props.notificationId });
|
||||
});
|
||||
};
|
||||
|
||||
const markAsRead = () => {
|
||||
makeFetch(
|
||||
"POST",
|
||||
`/api/1.0/main/notification/${props.notificationId}/mark/read`,
|
||||
[],
|
||||
).then(() => {
|
||||
emit("markUnread", { notificationId: props.notificationId });
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@@ -1,253 +0,0 @@
|
||||
<template>
|
||||
<a
|
||||
v-if="isOpenDocument"
|
||||
class="btn"
|
||||
:class="[
|
||||
isChangeIcon ? 'change-icon' : '',
|
||||
isChangeClass ? options.changeClass : 'btn-wopilink',
|
||||
]"
|
||||
@click="openModal"
|
||||
>
|
||||
<i v-if="isChangeIcon" class="fa me-2" :class="options.changeIcon" />
|
||||
|
||||
<span v-if="!noText">
|
||||
{{ $t("online_edit_document") }}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<teleport to="body">
|
||||
<div class="wopi-frame" v-if="isOpenDocument">
|
||||
<modal
|
||||
v-if="modal.showModal"
|
||||
:modal-dialog-class="modal.modalDialogClass"
|
||||
:hide-footer="true"
|
||||
@close="modal.showModal = false"
|
||||
>
|
||||
<template #header>
|
||||
<img class="logo" :src="logo" height="45" />
|
||||
<span class="ms-auto me-3">
|
||||
<span v-if="options.title">{{ options.title }}</span>
|
||||
</span>
|
||||
<!--
|
||||
<a class="btn btn-outline-light">
|
||||
<i class="fa fa-save fa-fw"></i>
|
||||
{{ $t('save_and_quit') }}
|
||||
</a>
|
||||
-->
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<div v-if="loading" class="loading">
|
||||
<i
|
||||
class="fa fa-circle-o-notch fa-spin fa-3x"
|
||||
:title="$t('loading')"
|
||||
/>
|
||||
</div>
|
||||
<iframe :src="this.wopiUrl" @load="loaded" />
|
||||
</template>
|
||||
</modal>
|
||||
</div>
|
||||
<div v-else>
|
||||
<modal
|
||||
v-if="modal.showModal"
|
||||
modal-dialog-class="modal-sm"
|
||||
@close="modal.showModal = false"
|
||||
>
|
||||
<template #header>
|
||||
<h3>{{ $t("invalid_title") }}</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="alert alert-warning">
|
||||
{{ $t("invalid_message") }}
|
||||
</div>
|
||||
</template>
|
||||
</modal>
|
||||
</div>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal";
|
||||
import logo from "ChillMainAssets/chill/img/logo-chill-sans-slogan_white.png";
|
||||
|
||||
export default {
|
||||
name: "OpenWopiLink",
|
||||
components: {
|
||||
Modal,
|
||||
},
|
||||
props: {
|
||||
wopiUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
modal: {
|
||||
showModal: false,
|
||||
modalDialogClass: "modal-fullscreen", //modal-dialog-scrollable
|
||||
},
|
||||
logo: logo,
|
||||
loading: false,
|
||||
mime: [
|
||||
// TODO temporary hardcoded. to be replaced by twig extension or a collabora server query
|
||||
"application/clarisworks",
|
||||
"application/coreldraw",
|
||||
"application/macwriteii",
|
||||
"application/msword",
|
||||
"application/pdf",
|
||||
"application/vnd.lotus-1-2-3",
|
||||
"application/vnd.ms-excel",
|
||||
"application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
||||
"application/vnd.ms-excel.sheet.macroEnabled.12",
|
||||
"application/vnd.ms-excel.template.macroEnabled.12",
|
||||
"application/vnd.ms-powerpoint",
|
||||
"application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
||||
"application/vnd.ms-powerpoint.template.macroEnabled.12",
|
||||
"application/vnd.ms-visio.drawing",
|
||||
"application/vnd.ms-word.document.macroEnabled.12",
|
||||
"application/vnd.ms-word.template.macroEnabled.12",
|
||||
"application/vnd.ms-works",
|
||||
"application/vnd.oasis.opendocument.chart",
|
||||
"application/vnd.oasis.opendocument.formula",
|
||||
"application/vnd.oasis.opendocument.graphics",
|
||||
"application/vnd.oasis.opendocument.graphics-flat-xml",
|
||||
"application/vnd.oasis.opendocument.graphics-template",
|
||||
"application/vnd.oasis.opendocument.presentation",
|
||||
"application/vnd.oasis.opendocument.presentation-flat-xml",
|
||||
"application/vnd.oasis.opendocument.presentation-template",
|
||||
"application/vnd.oasis.opendocument.spreadsheet",
|
||||
"application/vnd.oasis.opendocument.spreadsheet-flat-xml",
|
||||
"application/vnd.oasis.opendocument.spreadsheet-template",
|
||||
"application/vnd.oasis.opendocument.text",
|
||||
"application/vnd.oasis.opendocument.text-flat-xml",
|
||||
"application/vnd.oasis.opendocument.text-master",
|
||||
"application/vnd.oasis.opendocument.text-master-template",
|
||||
"application/vnd.oasis.opendocument.text-template",
|
||||
"application/vnd.oasis.opendocument.text-web",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.template",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
||||
"application/vnd.sun.xml.calc",
|
||||
"application/vnd.sun.xml.calc.template",
|
||||
"application/vnd.sun.xml.chart",
|
||||
"application/vnd.sun.xml.draw",
|
||||
"application/vnd.sun.xml.draw.template",
|
||||
"application/vnd.sun.xml.impress",
|
||||
"application/vnd.sun.xml.impress.template",
|
||||
"application/vnd.sun.xml.math",
|
||||
"application/vnd.sun.xml.writer",
|
||||
"application/vnd.sun.xml.writer.global",
|
||||
"application/vnd.sun.xml.writer.template",
|
||||
"application/vnd.visio",
|
||||
"application/vnd.visio2013",
|
||||
"application/vnd.wordperfect",
|
||||
"application/x-abiword",
|
||||
"application/x-aportisdoc",
|
||||
"application/x-dbase",
|
||||
"application/x-dif-document",
|
||||
"application/x-fictionbook+xml",
|
||||
"application/x-gnumeric",
|
||||
"application/x-hwp",
|
||||
"application/x-iwork-keynote-sffkey",
|
||||
"application/x-iwork-numbers-sffnumbers",
|
||||
"application/x-iwork-pages-sffpages",
|
||||
"application/x-mspublisher",
|
||||
"application/x-mswrite",
|
||||
"application/x-pagemaker",
|
||||
"application/x-sony-bbeb",
|
||||
"application/x-t602",
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isOpenDocument() {
|
||||
if (this.mime.indexOf(this.type) !== -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
noText() {
|
||||
if (typeof this.options.noText !== "undefined") {
|
||||
return this.options.noText === true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
isChangeIcon() {
|
||||
if (typeof this.options.changeIcon !== "undefined") {
|
||||
return !(
|
||||
this.options.changeIcon === null ||
|
||||
this.options.changeIcon === ""
|
||||
);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
isChangeClass() {
|
||||
if (typeof this.options.changeClass !== "undefined") {
|
||||
return !(
|
||||
this.options.changeClass === null ||
|
||||
this.options.changeClass === ""
|
||||
);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openModal() {
|
||||
this.loading = true;
|
||||
this.modal.showModal = true;
|
||||
},
|
||||
loaded() {
|
||||
this.loading = false;
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
messages: {
|
||||
fr: {
|
||||
online_edit_document: "Éditer en ligne",
|
||||
save_and_quit: "Enregistrer et quitter",
|
||||
loading: "Chargement de l'éditeur en ligne",
|
||||
invalid_title: "Format incompatible",
|
||||
invalid_message:
|
||||
"Désolé, ce format de document n'est pas éditable en ligne.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
div.wopi-frame {
|
||||
div.modal-header {
|
||||
border-bottom: 0;
|
||||
background-color: var(--bs-primary);
|
||||
color: white;
|
||||
}
|
||||
div.modal-body {
|
||||
padding: 0;
|
||||
overflow-y: unset !important;
|
||||
iframe {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
div.loading {
|
||||
position: absolute;
|
||||
color: var(--bs-chill-gray);
|
||||
top: calc(50% - 30px);
|
||||
left: calc(50% - 30px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -136,6 +136,59 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Fix the title in the flex table</h2>
|
||||
|
||||
<p>This will fix the layout of the row, with a "title" element, and an aside element. Using <code>css grid</code>, this is quite safe and won't overflow</p>
|
||||
|
||||
<xmp>
|
||||
<div class="flex-table">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-two-col-grid">
|
||||
<div class="title">This is my title</div>
|
||||
<div class="aside">Aside value</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-two-col-grid">
|
||||
<div class="title">
|
||||
<div><h3>This is my title, which can be very long and take a lot of place. But it is wrapped successfully, and won't disturb the placement of the aside block</h3></div>
|
||||
<div>This is a second line</div>
|
||||
</div>
|
||||
<div class="aside">Aside value</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xmp>
|
||||
|
||||
<p>will render:</p>
|
||||
|
||||
<div class="flex-table">
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-two-col-grid">
|
||||
<div class="title">This is my title</div>
|
||||
<div class="aside">Aside value</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="item-two-col-grid">
|
||||
<div class="title">
|
||||
<div><h3>This is my title, which can be very long and take a lot of place. But it is wrapped successfully, and won't disturb the placement of the aside block</h3></div>
|
||||
<div>This is a second line</div>
|
||||
</div>
|
||||
<div class="aside">Aside value</div>
|
||||
</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">
|
||||
@@ -392,4 +445,12 @@ Toutes les classes btn-* de bootstrap sont fonctionnelles
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<h1>Badges</h1>
|
||||
|
||||
<span class="badge-accompanying-work-type-simple">Action d'accompagnement</span>
|
||||
<span class="badge-activity-type-simple">Type d'échange</span>
|
||||
<span class="badge-calendar-simple">Rendez-vous</span>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
@@ -9,7 +9,6 @@
|
||||
{{ encore_entry_script_tags('mod_pickentity_type') }}
|
||||
{{ encore_entry_script_tags('mod_entity_workflow_subscribe') }}
|
||||
{{ encore_entry_script_tags('page_workflow_show') }}
|
||||
{{ encore_entry_script_tags('mod_wopi_link') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{{ encore_entry_script_tags('mod_workflow_attachment') }}
|
||||
{% endblock %}
|
||||
@@ -19,7 +18,6 @@
|
||||
{{ encore_entry_link_tags('mod_pickentity_type') }}
|
||||
{{ encore_entry_link_tags('mod_entity_workflow_subscribe') }}
|
||||
{{ encore_entry_link_tags('page_workflow_show') }}
|
||||
{{ encore_entry_link_tags('mod_wopi_link') }}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{{ encore_entry_link_tags('mod_workflow_attachment') }}
|
||||
{% endblock %}
|
||||
|
@@ -34,6 +34,7 @@ class GenderDocGenNormalizer implements ContextAwareNormalizerInterface, Normali
|
||||
'id' => $gender->getId(),
|
||||
'label' => $this->translatableStringHelper->localize($gender->getLabel()),
|
||||
'genderTranslation' => $gender->getGenderTranslation(),
|
||||
'type' => 'chill_main_gender',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -68,8 +68,8 @@ class AddressReferenceBEFromBestAddress
|
||||
$csv->setDelimiter(',');
|
||||
$csv->setHeaderOffset(0);
|
||||
|
||||
$stmt = Statement::create()
|
||||
->process($csv);
|
||||
$stmt = new Statement();
|
||||
$stmt = $stmt->process($csv);
|
||||
|
||||
foreach ($stmt as $record) {
|
||||
$this->baseImporter->importAddress(
|
||||
|
@@ -55,32 +55,32 @@ class AddressReferenceFromBAN
|
||||
|
||||
$csv = Reader::createFromStream($csvDecompressed);
|
||||
$csv->setDelimiter(';')->setHeaderOffset(0);
|
||||
$stmt = Statement::create()
|
||||
->process($csv, [
|
||||
'id',
|
||||
'id_fantoir',
|
||||
'numero',
|
||||
'rep',
|
||||
'nom_voie',
|
||||
'code_postal',
|
||||
'code_insee',
|
||||
'nom_commune',
|
||||
'code_insee_ancienne_commune',
|
||||
'nom_ancienne_commune',
|
||||
'x',
|
||||
'y',
|
||||
'lon',
|
||||
'lat',
|
||||
'type_position',
|
||||
'alias',
|
||||
'nom_ld',
|
||||
'libelle_acheminement',
|
||||
'nom_afnor',
|
||||
'source_position',
|
||||
'source_nom_voie',
|
||||
'certification_commune',
|
||||
'cad_parcelles',
|
||||
]);
|
||||
$stmt = new Statement();
|
||||
$stmt = $stmt->process($csv, [
|
||||
'id',
|
||||
'id_fantoir',
|
||||
'numero',
|
||||
'rep',
|
||||
'nom_voie',
|
||||
'code_postal',
|
||||
'code_insee',
|
||||
'nom_commune',
|
||||
'code_insee_ancienne_commune',
|
||||
'nom_ancienne_commune',
|
||||
'x',
|
||||
'y',
|
||||
'lon',
|
||||
'lat',
|
||||
'type_position',
|
||||
'alias',
|
||||
'nom_ld',
|
||||
'libelle_acheminement',
|
||||
'nom_afnor',
|
||||
'source_position',
|
||||
'source_nom_voie',
|
||||
'certification_commune',
|
||||
'cad_parcelles',
|
||||
]);
|
||||
|
||||
foreach ($stmt as $record) {
|
||||
$this->baseImporter->importAddress(
|
||||
|
@@ -43,17 +43,17 @@ class AddressReferenceFromBano
|
||||
|
||||
$csv = Reader::createFromStream($file);
|
||||
$csv->setDelimiter(',');
|
||||
$stmt = Statement::create()
|
||||
->process($csv, [
|
||||
'refId',
|
||||
'streetNumber',
|
||||
'street',
|
||||
'postcode',
|
||||
'city',
|
||||
'_o',
|
||||
'lat',
|
||||
'lon',
|
||||
]);
|
||||
$stmt = new Statement();
|
||||
$stmt = $stmt->process($csv, [
|
||||
'refId',
|
||||
'streetNumber',
|
||||
'street',
|
||||
'postcode',
|
||||
'city',
|
||||
'_o',
|
||||
'lat',
|
||||
'lon',
|
||||
]);
|
||||
|
||||
foreach ($stmt as $record) {
|
||||
$this->baseImporter->importAddress(
|
||||
|
@@ -54,7 +54,8 @@ class AddressReferenceLU
|
||||
|
||||
private function process_address(Reader $csv, ?string $sendAddressReportToEmail = null): void
|
||||
{
|
||||
$stmt = Statement::create()->process($csv);
|
||||
$stmt = new Statement();
|
||||
$stmt = $stmt->process($csv);
|
||||
foreach ($stmt as $record) {
|
||||
$this->addressBaseImporter->importAddress(
|
||||
$record['id_geoportail'],
|
||||
@@ -74,7 +75,8 @@ class AddressReferenceLU
|
||||
|
||||
private function process_postal_code(Reader $csv): void
|
||||
{
|
||||
$stmt = Statement::create()->process($csv);
|
||||
$stmt = new Statement();
|
||||
$stmt = $stmt->process($csv);
|
||||
$arr_postal_codes = [];
|
||||
foreach ($stmt as $record) {
|
||||
if (false === \array_key_exists($record['code_postal'], $arr_postal_codes)) {
|
||||
|
@@ -25,6 +25,8 @@ use Symfony\Component\Workflow\Transition;
|
||||
#[AsMessageHandler]
|
||||
final readonly class CancelStaleWorkflowHandler
|
||||
{
|
||||
private const LOG_PREFIX = '[CancelStaleWorkflowHandler] ';
|
||||
|
||||
public function __construct(
|
||||
private EntityWorkflowRepository $workflowRepository,
|
||||
private Registry $registry,
|
||||
@@ -40,13 +42,13 @@ final readonly class CancelStaleWorkflowHandler
|
||||
|
||||
$workflow = $this->workflowRepository->find($message->getWorkflowId());
|
||||
if (null === $workflow) {
|
||||
$this->logger->alert('Workflow was not found!', [$workflowId]);
|
||||
$this->logger->alert(self::LOG_PREFIX.'Workflow was not found!', ['entityWorkflowId' => $workflowId]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (false === $workflow->isStaledAt($olderThanDate)) {
|
||||
$this->logger->alert('Workflow has transitioned in the meantime.', [$workflowId]);
|
||||
$this->logger->alert(self::LOG_PREFIX.'Workflow has transitioned in the meantime.', ['entityWorkflowId' => $workflowId]);
|
||||
|
||||
throw new UnrecoverableMessageHandlingException('the workflow is not staled any more');
|
||||
}
|
||||
@@ -67,14 +69,14 @@ final readonly class CancelStaleWorkflowHandler
|
||||
'transitionAt' => $this->clock->now(),
|
||||
'transition' => $transition->getName(),
|
||||
]);
|
||||
$this->logger->info('EntityWorkflow has been cancelled automatically.', [$workflowId]);
|
||||
$this->logger->info(self::LOG_PREFIX.'EntityWorkflow has been cancelled automatically.', ['entityWorkflowId' => $workflowId]);
|
||||
$transitionApplied = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$transitionApplied) {
|
||||
$this->logger->error('No valid transition found for EntityWorkflow.', [$workflowId]);
|
||||
$this->logger->error(self::LOG_PREFIX.'No valid transition found for EntityWorkflow.', ['entityWorkflowId' => $workflowId]);
|
||||
throw new UnrecoverableMessageHandlingException(sprintf('No valid transition found for EntityWorkflow %d.', $workflowId));
|
||||
}
|
||||
|
||||
|
@@ -61,6 +61,7 @@ final class GenderDocGenNormalizerTest extends TestCase
|
||||
'id' => 1,
|
||||
'label' => 'homme',
|
||||
'genderTranslation' => GenderEnum::MALE,
|
||||
'type' => 'chill_main_gender',
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $this->normalizer->normalize($gender));
|
||||
|
@@ -0,0 +1,156 @@
|
||||
<?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\Workflow\EventSubscriber;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Entity\StoredObjectPointInTime;
|
||||
use Chill\DocStoreBundle\Entity\StoredObjectPointInTimeReasonEnum;
|
||||
use Chill\DocStoreBundle\Service\StoredObjectRestoreInterface;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||
use Chill\MainBundle\Workflow\EntityWorkflowMarkingStore;
|
||||
use Chill\MainBundle\Workflow\EventSubscriber\OnCancelRestoreDocumentToEditableEventSubscriber;
|
||||
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Workflow\DefinitionBuilder;
|
||||
use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
use Symfony\Component\Workflow\SupportStrategy\WorkflowSupportStrategyInterface;
|
||||
use Symfony\Component\Workflow\Transition;
|
||||
use Symfony\Component\Workflow\Workflow;
|
||||
use Symfony\Component\Workflow\WorkflowInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class OnCancelRestoreDocumentToEditableEventSubscriberTest extends TestCase
|
||||
{
|
||||
private function buildRegistry(StoredObjectRestoreInterface $storedObjectRestore, ?StoredObject $storedObject): Registry
|
||||
{
|
||||
$builder = new DefinitionBuilder(
|
||||
['initial', 'intermediate', 'final', 'cancel'],
|
||||
[
|
||||
new Transition('to_intermediate', ['initial'], ['intermediate']),
|
||||
new Transition('intermediate_to_final', ['intermediate'], ['final']),
|
||||
new Transition('to_final', ['initial'], ['final']),
|
||||
new Transition('to_cancel', ['initial'], ['cancel']),
|
||||
]
|
||||
);
|
||||
|
||||
$builder->setMetadataStore(
|
||||
new InMemoryMetadataStore(
|
||||
placesMetadata: [
|
||||
'final' => ['isFinal' => true],
|
||||
'cancel' => ['isFinal' => true, 'isFinalPositive' => false],
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
$registry = new Registry();
|
||||
$workflow = new Workflow($builder->build(), new EntityWorkflowMarkingStore(), $eventDispatcher = new EventDispatcher(), 'dummy');
|
||||
|
||||
$manager = $this->createMock(EntityWorkflowManager::class);
|
||||
$manager->method('getAssociatedStoredObject')->willReturn($storedObject);
|
||||
|
||||
$eventSubscriber = new OnCancelRestoreDocumentToEditableEventSubscriber(
|
||||
$registry,
|
||||
$manager,
|
||||
$storedObjectRestore
|
||||
);
|
||||
$eventDispatcher->addSubscriber($eventSubscriber);
|
||||
|
||||
$registry->addWorkflow($workflow, new class () implements WorkflowSupportStrategyInterface {
|
||||
public function supports(WorkflowInterface $workflow, object $subject): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return $registry;
|
||||
}
|
||||
|
||||
public function testOnCancelRestoreDocumentToEditableExpectsRestoring(): void
|
||||
{
|
||||
$storedObject = new StoredObject();
|
||||
$version = $storedObject->registerVersion();
|
||||
new StoredObjectPointInTime($version, StoredObjectPointInTimeReasonEnum::KEEP_BEFORE_CONVERSION);
|
||||
$storedObject->registerVersion();
|
||||
|
||||
$restore = $this->createMock(StoredObjectRestoreInterface::class);
|
||||
$restore->expects($this->once())->method('restore')->with($version);
|
||||
|
||||
$registry = $this->buildRegistry($restore, $storedObject);
|
||||
$entityWorkflow = (new EntityWorkflow())->setWorkflowName('dummy');
|
||||
|
||||
$workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
|
||||
$context = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||
|
||||
$workflow->apply($entityWorkflow, 'to_cancel', [
|
||||
'context' => $context,
|
||||
'transition' => 'to_cancel',
|
||||
'transitionAt' => new \DateTimeImmutable('now'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function testOnCancelRestoreDocumentDoNotExpectRestoring(): void
|
||||
{
|
||||
$storedObject = new StoredObject();
|
||||
$version = $storedObject->registerVersion();
|
||||
new StoredObjectPointInTime($version, StoredObjectPointInTimeReasonEnum::KEEP_BEFORE_CONVERSION);
|
||||
$storedObject->registerVersion();
|
||||
|
||||
$restore = $this->createMock(StoredObjectRestoreInterface::class);
|
||||
$restore->expects($this->never())->method('restore')->withAnyParameters();
|
||||
|
||||
$registry = $this->buildRegistry($restore, $storedObject);
|
||||
$entityWorkflow = (new EntityWorkflow())->setWorkflowName('dummy');
|
||||
|
||||
$workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
|
||||
$context = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||
|
||||
$workflow->apply($entityWorkflow, 'to_intermediate', [
|
||||
'context' => $context,
|
||||
'transition' => 'to_intermediate',
|
||||
'transitionAt' => new \DateTimeImmutable('now'),
|
||||
]);
|
||||
|
||||
$workflow->apply($entityWorkflow, 'intermediate_to_final', [
|
||||
'context' => $context,
|
||||
'transition' => 'intermediate_to_final',
|
||||
'transitionAt' => new \DateTimeImmutable('now'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function testOnCancelRestoreDocumentToEditableToCancelStoredObjectWithoutKepts(): void
|
||||
{
|
||||
$storedObject = new StoredObject();
|
||||
$storedObject->registerVersion();
|
||||
|
||||
$restore = $this->createMock(StoredObjectRestoreInterface::class);
|
||||
$restore->expects($this->never())->method('restore')->withAnyParameters();
|
||||
|
||||
$registry = $this->buildRegistry($restore, $storedObject);
|
||||
$entityWorkflow = (new EntityWorkflow())->setWorkflowName('dummy');
|
||||
|
||||
$workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
|
||||
$context = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||
|
||||
$workflow->apply($entityWorkflow, 'to_cancel', [
|
||||
'context' => $context,
|
||||
'transition' => 'to_cancel',
|
||||
'transitionAt' => new \DateTimeImmutable('now'),
|
||||
]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
<?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\Workflow\EventSubscriber;
|
||||
|
||||
use Chill\DocStoreBundle\Service\StoredObjectRestoreInterface;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Workflow\Event\TransitionEvent;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
|
||||
final readonly class OnCancelRestoreDocumentToEditableEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private Registry $registry,
|
||||
private EntityWorkflowManager $manager,
|
||||
private StoredObjectRestoreInterface $storedObjectRestore,
|
||||
) {}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return ['workflow.transition' => ['onCancelRestoreDocumentToEditable', 0]];
|
||||
}
|
||||
|
||||
public function onCancelRestoreDocumentToEditable(TransitionEvent $event): void
|
||||
{
|
||||
$entityWorkflow = $event->getSubject();
|
||||
|
||||
if (!$entityWorkflow instanceof EntityWorkflow) {
|
||||
return;
|
||||
}
|
||||
|
||||
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
|
||||
|
||||
foreach ($event->getTransition()->getTos() as $place) {
|
||||
$metadata = $workflow->getMetadataStore()->getPlaceMetadata($place);
|
||||
|
||||
if (($metadata['isFinal'] ?? false) && !($metadata['isFinalPositive'] ?? true)) {
|
||||
$this->restoreDocument($entityWorkflow);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function restoreDocument(EntityWorkflow $entityWorkflow): void
|
||||
{
|
||||
$storedObject = $this->manager->getAssociatedStoredObject($entityWorkflow);
|
||||
|
||||
if (null === $storedObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
$version = $storedObject->getLastKeptBeforeConversionVersion();
|
||||
|
||||
if (null === $version) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->storedObjectRestore->restore($storedObject->getLastKeptBeforeConversionVersion());
|
||||
}
|
||||
}
|
@@ -943,6 +943,16 @@ paths:
|
||||
description: "ok"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
/1.0/main/gender.json:
|
||||
get:
|
||||
tags:
|
||||
- gender
|
||||
summary: Return all gender types
|
||||
responses:
|
||||
200:
|
||||
description: "ok"
|
||||
401:
|
||||
description: "Unauthorized"
|
||||
/1.0/main/user-job.json:
|
||||
get:
|
||||
tags:
|
||||
|
@@ -86,10 +86,6 @@ module.exports = function (encore, entries) {
|
||||
"mod_entity_workflow_pick",
|
||||
__dirname + "/Resources/public/module/entity-workflow-pick/index.js",
|
||||
);
|
||||
encore.addEntry(
|
||||
"mod_wopi_link",
|
||||
__dirname + "/Resources/public/module/wopi-link/index.js",
|
||||
);
|
||||
encore.addEntry(
|
||||
"mod_pick_postal_code",
|
||||
__dirname + "/Resources/public/module/pick-postal-code/index.js",
|
||||
|
@@ -44,6 +44,9 @@ address_fields: Données liées à l'adresse
|
||||
Datas: Données
|
||||
No title: Aucun titre
|
||||
icon: icône
|
||||
See: Voir
|
||||
Name: Nom
|
||||
Label: Nom
|
||||
|
||||
user:
|
||||
profile:
|
||||
@@ -56,6 +59,7 @@ user_group:
|
||||
inactive: Inactif
|
||||
with_users: Membres
|
||||
no_users: Aucun utilisateur associé
|
||||
no_user_groups: Aucune groupe d'utilisateurs
|
||||
no_admin_users: Aucun administrateur
|
||||
Label: Nom du groupe
|
||||
BackgroundColor: Couleur de fond du badge
|
||||
@@ -126,6 +130,49 @@ address:
|
||||
address_homeless: L'adresse est-elle celle d'un domicile fixe ?
|
||||
real address: Adresse d'un domicile
|
||||
consider homeless: Cette adresse est incomplète
|
||||
add_an_address_title: Créer une adresse
|
||||
edit_an_address_title: Modifier une adresse
|
||||
create_a_new_address: Créer une nouvelle adresse
|
||||
edit_address: Modifier l'adresse
|
||||
select_an_address_title: Sélectionner une adresse
|
||||
fill_an_address: Compléter l'adresse
|
||||
select_country: Choisir le pays
|
||||
country: Pays
|
||||
select_city: Choisir une localité
|
||||
city: Localité
|
||||
other_city: Autre localité
|
||||
select_address: Choisir une adresse
|
||||
address: Adresse
|
||||
other_address: Autre adresse
|
||||
create_address: Adresse inconnue. Cliquez ici pour créer une nouvelle adresse
|
||||
isNoAddress: Pas d'adresse complète
|
||||
isConfidential: Adresse confidentielle
|
||||
street: Nom de rue
|
||||
streetNumber: Numéro
|
||||
floor: Étage
|
||||
corridor: Couloir
|
||||
steps: Escalier
|
||||
flat: Appartement
|
||||
buildingName: Résidence
|
||||
extra: Complément d'adresse
|
||||
distribution: Cedex
|
||||
create_postal_code: Localité inconnue. Cliquez ici pour créer une nouvelle localité
|
||||
postalCode_name: Nom
|
||||
postalCode_code: Code postal
|
||||
date: Date de la nouvelle adresse
|
||||
valid_from: L'adresse est valable à partir du
|
||||
valid_to: L'adresse est valable jusqu'au
|
||||
back_to_the_list: Retour à la liste
|
||||
loading: chargement en cours...
|
||||
address_suggestions: Suggestion d'adresses
|
||||
address_new_success: La nouvelle adresse est enregistrée.
|
||||
address_edit_success: L'adresse a été mise à jour.
|
||||
wait_redirection: La page est redirigée.
|
||||
not_yet_address: Il n'y a pas encore d'adresse. Cliquez sur '+ Créer une adresse'
|
||||
use_this_address: Utiliser cette adresse
|
||||
household:
|
||||
move_date: Date du déménagement
|
||||
|
||||
address more:
|
||||
floor: ét
|
||||
corridor: coul
|
||||
@@ -510,6 +557,8 @@ Follow workflow: Suivre la décision
|
||||
Workflow history: Historique de la décision
|
||||
|
||||
workflow:
|
||||
list: Liste des workflows associés
|
||||
associated: workflow associé
|
||||
deleted: Workflow supprimé
|
||||
Created by: Créé par
|
||||
My decision: Ma décision
|
||||
@@ -555,6 +604,7 @@ workflow:
|
||||
Previous workflow transitionned help: Workflows où vous avez exécuté une action.
|
||||
For: Pour
|
||||
Cc: Cc
|
||||
At: Le
|
||||
You must select a next step, pick another decision if no next steps are available: Il faut une prochaine étape. Choissisez une autre décision si nécessaire.
|
||||
An access key was also sent to those addresses: Un lien d'accès a été envoyé à ces adresses
|
||||
Those users are also granted to apply a transition by using an access key: Ces utilisateurs ont obtenu l'accès grâce au lien reçu par email
|
||||
@@ -577,6 +627,12 @@ workflow:
|
||||
public_views_by_ip: Visualisation par adresse IP
|
||||
May not associate a document: Le workflow ne concerne pas un document
|
||||
|
||||
subscribe_final: Recevoir une notification à l'étape finale
|
||||
unsubscribe_final: Ne plus recevoir de notification à l'étape finale
|
||||
subscribe_all_steps: Recevoir une notification à chaque étape du suivi
|
||||
unsubscribe_all_steps: Ne plus recevoir de notification à chaque étape du suivi
|
||||
|
||||
|
||||
public_link:
|
||||
expired_link_title: Lien expiré
|
||||
expired_link_explanation: Le lien a expiré, vous ne pouvez plus visualiser ce document.
|
||||
@@ -658,6 +714,10 @@ notification:
|
||||
Remove an email: Supprimer l'adresse email
|
||||
Email with access link: Adresse email ayant reçu un lien d'accès
|
||||
|
||||
mark_as_read: Marquer comme lu
|
||||
mark_as_unread: Marquer comme non-lu
|
||||
|
||||
|
||||
export:
|
||||
address_helper:
|
||||
id: Identifiant de l'adresse
|
||||
@@ -678,6 +738,26 @@ export:
|
||||
steps: Escaliers
|
||||
_lat: Latitude
|
||||
_lon: Longitude
|
||||
social_action_list:
|
||||
id: Identifiant de l'action
|
||||
social_issue_id: Identifiant de la problématique sociale
|
||||
social_issue: Problématique sociale
|
||||
desactivation_date: Date de désactivation
|
||||
social_issue_ordering: Ordre de la problématique sociale
|
||||
action_label: Action d'accompagnement
|
||||
action_ordering: Ordre
|
||||
goal_label: Objectif
|
||||
goal_id: Identifiant de l'objectif
|
||||
goal_result_label: Résultat
|
||||
goal_result_id: Identifiant du résultat
|
||||
result_without_goal_label: Résultat (sans objectif)
|
||||
result_without_goal_id: Identifiant du résultat (sans objectif)
|
||||
evaluation_title: Évaluation
|
||||
evaluation_id: Identifiant de l'évaluation
|
||||
evaluation_url: Adresse URL externe (évaluation)
|
||||
evaluation_delay_month: Délai de notification (mois)
|
||||
evaluation_delay_week: Délai de notification (semaine)
|
||||
evaluation_delay_day: Délai de notification (jours)
|
||||
|
||||
rolling_date:
|
||||
year_previous_start: Début de l'année précédente
|
||||
@@ -797,4 +877,43 @@ gender:
|
||||
Select gender translation: Traduction grammaticale
|
||||
Select gender icon: Icône à utiliser
|
||||
|
||||
wopi:
|
||||
online_edit_document: Éditer en ligne
|
||||
save_and_quit: Enregistrer et quitter
|
||||
loading: Chargement de l'éditeur en ligne
|
||||
invalid_title: Format incompatible
|
||||
invalid_message: Désolé, ce format de document n'est pas éditable en ligne.
|
||||
|
||||
onthefly:
|
||||
show:
|
||||
person: Détails de l'usager
|
||||
thirdparty: Détails du tiers
|
||||
file_person: Ouvrir la fiche de l'usager
|
||||
file_thirdparty: Voir le Tiers
|
||||
edit:
|
||||
person: Modifier un usager
|
||||
thirdparty: Modifier un tiers
|
||||
create:
|
||||
button: Créer {q}
|
||||
title:
|
||||
default: Création d'un nouvel usager ou d'un tiers professionnel
|
||||
person: Création d'un nouvel usager
|
||||
thirdparty: Création d'un nouveau tiers professionnel
|
||||
person: un nouvel usager
|
||||
thirdparty: un nouveau tiers professionnel
|
||||
addContact:
|
||||
title: Créer un contact pour {q}
|
||||
resource_comment_title: Un commentaire est associé à cet interlocuteur
|
||||
|
||||
modal:
|
||||
action:
|
||||
close: Fermer
|
||||
|
||||
multiselect:
|
||||
placeholder: Choisir
|
||||
tag_placeholder: Créer un nouvel élément
|
||||
select_label: Entrée ou cliquez pour sélectionner
|
||||
deselect_label: Entrée ou cliquez pour désélectionner
|
||||
select_group_label: Appuyer sur "Entrée" pour sélectionner ce groupe
|
||||
deselect_group_label: Appuyer sur "Entrée" pour désélectionner ce groupe
|
||||
selected_label: Sélectionné'
|
||||
|
Reference in New Issue
Block a user