mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-30 10:29:42 +00:00
Merge master into translations branch
This commit is contained in:
@@ -31,6 +31,8 @@
|
||||
// Specific templates
|
||||
@import './scss/notification';
|
||||
|
||||
@import './scss/hover.scss';
|
||||
|
||||
/*
|
||||
* BASE LAYOUT POSITION
|
||||
*/
|
||||
@@ -496,6 +498,7 @@ div.workflow {
|
||||
div.breadcrumb {
|
||||
display: initial;
|
||||
margin-bottom: 0;
|
||||
margin-right: .5rem;
|
||||
padding-right: 0.5em;
|
||||
background-color: tint-color($chill-yellow, 90%);
|
||||
border: 1px solid $chill-yellow;
|
||||
|
@@ -233,7 +233,7 @@ div.wrap-header {
|
||||
}
|
||||
&:last-child {}
|
||||
|
||||
div.wh-col {
|
||||
& > div.wh-col {
|
||||
&:first-child {
|
||||
flex-grow: 0; flex-shrink: 1; flex-basis: auto;
|
||||
}
|
||||
|
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
.row.row-hover {
|
||||
padding: 0.3rem;
|
||||
|
||||
&:hover {
|
||||
background-color: $gray-100;
|
||||
border-top: 1px solid $gray-400;
|
||||
border-bottom: 1px solid $gray-400;
|
||||
}
|
||||
}
|
@@ -17,6 +17,10 @@ ul.record_actions {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&.slim {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@@ -1,12 +0,0 @@
|
||||
const buildLinkCreate = function(workflowName, relatedEntityClass, relatedEntityId) {
|
||||
let params = new URLSearchParams();
|
||||
params.set('entityClass', relatedEntityClass);
|
||||
params.set('entityId', relatedEntityId);
|
||||
params.set('workflow', workflowName);
|
||||
|
||||
return `/fr/main/workflow/create?`+params.toString();
|
||||
};
|
||||
|
||||
export {
|
||||
buildLinkCreate,
|
||||
};
|
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Constructs a URL for creating a workflow link associated with a specific entity.
|
||||
*
|
||||
* @param {string} workflowName - The name of the workflow to associate.
|
||||
* @param {string} relatedEntityClass - The class/type of the related entity.
|
||||
* @param {number|undefined} relatedEntityId - The unique identifier of the related entity. Must be defined.
|
||||
*
|
||||
* @returns {string} The constructed URL containing query parameters based on the provided arguments.
|
||||
*
|
||||
* @throws {Error} If the related entity ID is undefined.
|
||||
*/
|
||||
export const buildLinkCreate = (workflowName: string, relatedEntityClass: string, relatedEntityId: number|undefined): string => {
|
||||
if (typeof (relatedEntityId) === 'undefined') {
|
||||
throw new Error("the related entity id is not set");
|
||||
}
|
||||
let params = new URLSearchParams();
|
||||
params.set('entityClass', relatedEntityClass);
|
||||
params.set('entityId', relatedEntityId.toString(10));
|
||||
params.set('workflow', workflowName);
|
||||
|
||||
return `/fr/main/workflow/create?`+params.toString();
|
||||
};
|
@@ -10,6 +10,7 @@ import Modal from 'bootstrap/js/dist/modal';
|
||||
import Collapse from 'bootstrap/js/src/collapse';
|
||||
import Carousel from 'bootstrap/js/src/carousel';
|
||||
import Popover from 'bootstrap/js/src/popover';
|
||||
import 'bootstrap-icons/font/bootstrap-icons.css';
|
||||
|
||||
//
|
||||
// Carousel: ACHeaderSlider is a small slider used in banner of AccompanyingCourse Section
|
||||
@@ -59,4 +60,4 @@ const popoverList = triggerList.map(function (el) {
|
||||
return new Popover(el, {
|
||||
html: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -30,6 +30,12 @@
|
||||
*/
|
||||
import './collection.scss';
|
||||
|
||||
declare global {
|
||||
interface GlobalEventHandlersEventMap {
|
||||
'show-hide-show': CustomEvent<{id: number, froms: HTMLElement[], container: HTMLElement}>,
|
||||
}
|
||||
}
|
||||
|
||||
export class CollectionEventPayload {
|
||||
collection: HTMLUListElement;
|
||||
entry: HTMLLIElement;
|
||||
@@ -53,14 +59,17 @@ export const handleAdd = (button: any): void => {
|
||||
let
|
||||
empty_explain: HTMLLIElement | null = collection.querySelector('li[data-collection-empty-explain]'),
|
||||
entry = document.createElement('li'),
|
||||
counter = collection.childNodes.length + 1,
|
||||
content = prototype.replace(new RegExp('__name__', 'g'), counter.toString()),
|
||||
counter = collection.querySelectorAll('li.entry').length, // Updated counter logic
|
||||
content = prototype.replace(/__name__/g, counter.toString()),
|
||||
event = new CustomEvent('collection-add-entry', {detail: new CollectionEventPayload(collection, entry)});
|
||||
|
||||
console.log(counter)
|
||||
console.log(content)
|
||||
|
||||
entry.innerHTML = content;
|
||||
entry.classList.add('entry');
|
||||
|
||||
if ("dataCollectionRegular" in collection.dataset) {
|
||||
if ("collectionRegular" in collection.dataset) {
|
||||
initializeRemove(collection, entry);
|
||||
if (empty_explain !== null) {
|
||||
empty_explain.remove();
|
||||
@@ -104,20 +113,34 @@ export const buildRemoveButton = (collection: HTMLUListElement, entry: HTMLLIEle
|
||||
return button;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const collectionsInit = new Set<string>;
|
||||
const buttonsInit = new Set<string>();
|
||||
|
||||
const initialize = function (target: Document|Element): void {
|
||||
let
|
||||
addButtons: NodeListOf<HTMLButtonElement> = document.querySelectorAll("button[data-collection-add-target]"),
|
||||
collections: NodeListOf<HTMLUListElement> = document.querySelectorAll("ul[data-collection-regular]");
|
||||
|
||||
for (let i = 0; i < addButtons.length; i++) {
|
||||
let addButton = addButtons[i];
|
||||
const addButton = addButtons[i];
|
||||
const uniqid = addButton.dataset.uniqid as string;
|
||||
if (buttonsInit.has(uniqid)) {
|
||||
continue;
|
||||
}
|
||||
buttonsInit.add(uniqid);
|
||||
addButton.addEventListener('click', (e: Event) => {
|
||||
e.preventDefault();
|
||||
handleAdd(e.target);
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < collections.length; i++) {
|
||||
let entries: NodeListOf<HTMLLIElement> = collections[i].querySelectorAll(':scope > li');
|
||||
const collection = collections[i];
|
||||
const uniqid = collection.dataset.uniqid as string;
|
||||
if (collectionsInit.has(uniqid)) {
|
||||
continue;
|
||||
}
|
||||
collectionsInit.add(uniqid);
|
||||
let entries: NodeListOf<HTMLLIElement> = collection.querySelectorAll(':scope > li');
|
||||
for (let j = 0; j < entries.length; j++) {
|
||||
if (entries[j].dataset.collectionEmptyExplain === "1") {
|
||||
continue;
|
||||
@@ -125,4 +148,13 @@ window.addEventListener('load', () => {
|
||||
initializeRemove(collections[i], entries[j]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
initialize(document);
|
||||
});
|
||||
|
||||
window.addEventListener('show-hide-show', (event: CustomEvent<{id: number; container: HTMLElement; froms: HTMLElement[]}>) => {
|
||||
const container = event.detail.container as HTMLElement;
|
||||
initialize(container);
|
||||
})
|
||||
|
@@ -1,14 +1,16 @@
|
||||
import {createApp} from "vue";
|
||||
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) {
|
||||
document.querySelectorAll('.notification_toggle_read_status')
|
||||
.forEach(function (el, i) {
|
||||
createApp({
|
||||
template: `<notification-read-toggle
|
||||
window.addEventListener("DOMContentLoaded", function (e) {
|
||||
document
|
||||
.querySelectorAll(".notification_toggle_read_status")
|
||||
.forEach(function (el, i) {
|
||||
createApp({
|
||||
template: `<notification-read-toggle
|
||||
:notificationId="notificationId"
|
||||
:buttonClass="buttonClass"
|
||||
:buttonNoText="buttonNoText"
|
||||
@@ -17,40 +19,45 @@ window.addEventListener('DOMContentLoaded', function (e) {
|
||||
@markRead="onMarkRead"
|
||||
@markUnread="onMarkUnread">
|
||||
</notification-read-toggle>`,
|
||||
components: {
|
||||
NotificationReadToggle,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notificationId: el.dataset.notificationId,
|
||||
buttonClass: el.dataset.buttonClass,
|
||||
buttonNoText: 'false' === el.dataset.buttonText,
|
||||
showUrl: el.dataset.showButtonUrl,
|
||||
isRead: 1 === Number.parseInt(el.dataset.notificationCurrentIsRead),
|
||||
container: el.dataset.container
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getContainer() {
|
||||
return document.querySelectorAll(`div.${this.container}`);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onMarkRead() {
|
||||
if (typeof this.getContainer[i] !== 'undefined') {
|
||||
this.getContainer[i].classList.replace('read', 'unread');
|
||||
} else { throw 'data-container attribute is missing' }
|
||||
this.isRead = false;
|
||||
},
|
||||
onMarkUnread() {
|
||||
if (typeof this.getContainer[i] !== 'undefined') {
|
||||
this.getContainer[i].classList.replace('unread', 'read');
|
||||
} else { throw 'data-container attribute is missing' }
|
||||
this.isRead = true;
|
||||
},
|
||||
}
|
||||
})
|
||||
.use(i18n)
|
||||
.mount(el);
|
||||
});
|
||||
components: {
|
||||
NotificationReadToggle,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notificationId: parseInt(el.dataset.notificationId),
|
||||
buttonClass: el.dataset.buttonClass,
|
||||
buttonNoText: "false" === el.dataset.buttonText,
|
||||
showUrl: el.dataset.showButtonUrl,
|
||||
isRead: 1 === Number.parseInt(el.dataset.notificationCurrentIsRead),
|
||||
container: el.dataset.container,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
getContainer() {
|
||||
return document.querySelectorAll(`div.${this.container}`);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onMarkRead() {
|
||||
if (typeof this.getContainer[i] !== "undefined") {
|
||||
this.getContainer[i].classList.replace("read", "unread");
|
||||
} else {
|
||||
throw "data-container attribute is missing";
|
||||
}
|
||||
this.isRead = false;
|
||||
},
|
||||
onMarkUnread() {
|
||||
if (typeof this.getContainer[i] !== "undefined") {
|
||||
this.getContainer[i].classList.replace("unread", "read");
|
||||
} else {
|
||||
throw "data-container attribute is missing";
|
||||
}
|
||||
this.isRead = true;
|
||||
},
|
||||
},
|
||||
})
|
||||
.use(i18n)
|
||||
.mount(el);
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -0,0 +1,39 @@
|
||||
import { createApp } from "vue";
|
||||
import { _createI18n } from "../../vuejs/_js/i18n";
|
||||
import NotificationReadAllToggle from "../../vuejs/_components/Notification/NotificationReadAllToggle.vue";
|
||||
|
||||
const i18n = _createI18n({});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const elements = document.querySelectorAll(".notification_all_read");
|
||||
|
||||
elements.forEach((element) => {
|
||||
console.log('launch');
|
||||
createApp({
|
||||
template: `<notification-read-all-toggle @markAsRead="markAsRead" @markAsUnRead="markAsUnread"></notification-read-all-toggle>`,
|
||||
components: {
|
||||
NotificationReadAllToggle,
|
||||
},
|
||||
methods: {
|
||||
markAsRead(id: number) {
|
||||
const el = document.querySelector<HTMLDivElement>(`div.notification-status[data-notification-id="${id}"]`);
|
||||
if (el === null) {
|
||||
return;
|
||||
}
|
||||
el.classList.add('read');
|
||||
el.classList.remove('unread');
|
||||
},
|
||||
markAsUnread(id: number) {
|
||||
const el = document.querySelector<HTMLDivElement>(`div.notification-status[data-notification-id="${id}"]`);
|
||||
if (el === null) {
|
||||
return;
|
||||
}
|
||||
el.classList.remove('read');
|
||||
el.classList.add('unread');
|
||||
},
|
||||
}
|
||||
})
|
||||
.use(i18n)
|
||||
.mount(element);
|
||||
});
|
||||
});
|
@@ -44,7 +44,9 @@ function loadDynamicPicker(element) {
|
||||
':suggested="notPickedSuggested" ' +
|
||||
':label="label" ' +
|
||||
'@addNewEntity="addNewEntity" ' +
|
||||
'@removeEntity="removeEntity"></pick-entity>',
|
||||
'@removeEntity="removeEntity" ' +
|
||||
'@addNewEntityProcessEnded="addNewEntityProcessEnded"' +
|
||||
'></pick-entity>',
|
||||
components: {
|
||||
PickEntity,
|
||||
},
|
||||
@@ -97,10 +99,11 @@ function loadDynamicPicker(element) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.submit_on_adding_new_entity) {
|
||||
input.form.submit();
|
||||
}
|
||||
},
|
||||
addNewEntityProcessEnded() {
|
||||
if (this.submit_on_adding_new_entity) {
|
||||
input.form.submit();
|
||||
}
|
||||
},
|
||||
removeEntity({entity}) {
|
||||
if (-1 === this.suggested.findIndex(e => e.type === entity.type && e.id === entity.id)) {
|
||||
@@ -159,3 +162,5 @@ document.addEventListener('DOMContentLoaded', function(e) {
|
||||
loadDynamicPicker(document)
|
||||
})
|
||||
|
||||
window.loadDynamicPicker = loadDynamicPicker;
|
||||
|
||||
|
@@ -1,41 +1,145 @@
|
||||
import {ShowHide} from 'ChillMainAssets/lib/show_hide/show_hide.js';
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
let
|
||||
const
|
||||
divTransitions = document.querySelector('#transitions'),
|
||||
futureDestUsersContainer = document.querySelector('#futureDests')
|
||||
futureDestUsersContainer = document.querySelector('#futureDests'),
|
||||
personSignatureField = document.querySelector('#person-signature-field'),
|
||||
userSignatureField = document.querySelector('#user-signature-field'),
|
||||
signatureTypeChoices = document.querySelector('#signature-type-choice'),
|
||||
personChoice = document.querySelector('#workflow_step_isPersonOrUserSignature_0'),
|
||||
userChoice = document.querySelector('#workflow_step_isPersonOrUserSignature_1'),
|
||||
signatureZone = document.querySelector('#signature-zone'),
|
||||
transitionFilterContainer = document.querySelector('#transitionFilter'),
|
||||
transitionsContainer = document.querySelector('#transitions'),
|
||||
sendExternalContainer = document.querySelector('#sendExternalContainer')
|
||||
;
|
||||
|
||||
if (null !== divTransitions) {
|
||||
new ShowHide({
|
||||
load_event: null,
|
||||
froms: [divTransitions],
|
||||
container: [futureDestUsersContainer],
|
||||
test: function(divs, arg2, arg3) {
|
||||
for (let div of divs) {
|
||||
for (let input of div.querySelectorAll('input')) {
|
||||
if (input.checked) {
|
||||
if (input.dataset.toFinal === "1") {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ShowHide instance for future dest users
|
||||
new ShowHide({
|
||||
debug: false,
|
||||
load_event: null,
|
||||
froms: [divTransitions],
|
||||
container: [futureDestUsersContainer],
|
||||
test: function(froms, event) {
|
||||
for (let transition of froms) {
|
||||
for (let input of transition.querySelectorAll('input')) {
|
||||
if (input.checked) {
|
||||
if ('1' === input.dataset.toFinal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ('1' === input.dataset.isSentExternal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const inputData = JSON.parse(input.getAttribute('data-is-signature'))
|
||||
if (inputData.includes('person') || inputData.includes('user')) {
|
||||
return false;
|
||||
} else {
|
||||
personChoice.checked = false
|
||||
userChoice.checked = false
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// ShowHide instance for send external
|
||||
new ShowHide({
|
||||
debug: false,
|
||||
load_event: null,
|
||||
froms: [divTransitions],
|
||||
container: [sendExternalContainer],
|
||||
test: function(froms, event) {
|
||||
for (let transition of froms) {
|
||||
for (let input of transition.querySelectorAll('input')) {
|
||||
if (input.checked) {
|
||||
if ('1' === input.dataset.isSentExternal) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})
|
||||
|
||||
// ShowHide signature zone
|
||||
new ShowHide({
|
||||
debug: false,
|
||||
load_event: null,
|
||||
froms: [divTransitions],
|
||||
container: [signatureZone],
|
||||
test: function(froms, event) {
|
||||
for (let transition of froms) {
|
||||
for (let input of transition.querySelectorAll('input')) {
|
||||
if (input.checked) {
|
||||
const inputData = JSON.parse(input.getAttribute('data-is-signature'))
|
||||
if (inputData.includes('person') || inputData.includes('user')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// ShowHides for personSignatureField or userSignatureField within signature zone
|
||||
new ShowHide({
|
||||
debug: false,
|
||||
froms: [signatureTypeChoices],
|
||||
container: [personSignatureField],
|
||||
test: function(froms, event) {
|
||||
for (let container of froms) {
|
||||
return personChoice.checked;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
new ShowHide({
|
||||
debug: false,
|
||||
froms: [signatureTypeChoices],
|
||||
container: [userSignatureField],
|
||||
test: function(froms, event) {
|
||||
for (let container of froms) {
|
||||
return userChoice.checked;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
if (null !== divTransitions) {
|
||||
new ShowHide({
|
||||
load_event: null,
|
||||
froms: [divTransitions],
|
||||
container: [futureDestUsersContainer],
|
||||
test: function(divs, arg2, arg3) {
|
||||
for (let div of divs) {
|
||||
for (let input of div.querySelectorAll('input')) {
|
||||
if (input.checked) {
|
||||
if (input.dataset.toFinal === "1") {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let
|
||||
transitionFilterContainer = document.querySelector('#transitionFilter'),
|
||||
transitions = document.querySelector('#transitions')
|
||||
;
|
||||
|
||||
if (null !== transitionFilterContainer) {
|
||||
transitions.querySelectorAll('.form-check').forEach(function(row) {
|
||||
transitionsContainer.querySelectorAll('.form-check').forEach(function(row) {
|
||||
|
||||
const isForward = row.querySelector('input').dataset.isForward;
|
||||
|
||||
@@ -51,6 +155,7 @@ window.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
toggle_callback: function (c, dir) {
|
||||
for (let div of c) {
|
||||
@@ -66,5 +171,4 @@ window.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
@@ -1,164 +1,181 @@
|
||||
export interface DateTime {
|
||||
datetime: string;
|
||||
datetime8601: string
|
||||
datetime: string;
|
||||
datetime8601: string;
|
||||
}
|
||||
|
||||
export interface Civility {
|
||||
id: number;
|
||||
// TODO
|
||||
id: number;
|
||||
// TODO
|
||||
}
|
||||
|
||||
export interface Job {
|
||||
id: number;
|
||||
type: "user_job";
|
||||
label: {
|
||||
"fr": string; // could have other key. How to do that in ts ?
|
||||
}
|
||||
id: number;
|
||||
type: "user_job";
|
||||
label: {
|
||||
fr: string; // could have other key. How to do that in ts ?
|
||||
};
|
||||
}
|
||||
|
||||
export interface Center {
|
||||
id: number;
|
||||
type: "center";
|
||||
name: string;
|
||||
id: number;
|
||||
type: "center";
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Scope {
|
||||
id: number;
|
||||
type: "scope";
|
||||
name: {
|
||||
"fr": string
|
||||
}
|
||||
id: number;
|
||||
type: "scope";
|
||||
name: {
|
||||
fr: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ResultItem<T> {
|
||||
result: T;
|
||||
relevance: number;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
type: "user";
|
||||
id: number;
|
||||
username: string;
|
||||
text: string;
|
||||
text_without_absence: string;
|
||||
email: string;
|
||||
user_job: Job;
|
||||
label: string;
|
||||
// todo: mainCenter; mainJob; etc..
|
||||
type: "user";
|
||||
id: number;
|
||||
username: string;
|
||||
text: string;
|
||||
text_without_absence: string;
|
||||
email: string;
|
||||
user_job: Job;
|
||||
label: string;
|
||||
// todo: mainCenter; mainJob; etc..
|
||||
}
|
||||
|
||||
export interface UserGroup {
|
||||
type: "user_group";
|
||||
id: number;
|
||||
label: TranslatableString;
|
||||
backgroundColor: string;
|
||||
foregroundColor: string;
|
||||
excludeKey: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export type UserGroupOrUser = User | UserGroup;
|
||||
|
||||
export interface UserAssociatedInterface {
|
||||
type: "user";
|
||||
id: number;
|
||||
};
|
||||
|
||||
export type TranslatableString = {
|
||||
fr?: string;
|
||||
nl?: string;
|
||||
type: "user";
|
||||
id: number;
|
||||
}
|
||||
|
||||
export type TranslatableString = {
|
||||
fr?: string;
|
||||
nl?: string;
|
||||
};
|
||||
|
||||
export interface Postcode {
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
center: Point;
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
center: Point;
|
||||
}
|
||||
|
||||
export type Point = {
|
||||
type: "Point";
|
||||
coordinates: [lat: number, lon: number];
|
||||
}
|
||||
type: "Point";
|
||||
coordinates: [lat: number, lon: number];
|
||||
};
|
||||
|
||||
export interface Country {
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
code: string;
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
code: string;
|
||||
}
|
||||
|
||||
export type AddressRefStatus = 'match'|'to_review'|'reviewed';
|
||||
export type AddressRefStatus = "match" | "to_review" | "reviewed";
|
||||
|
||||
export interface Address {
|
||||
type: "address";
|
||||
address_id: number;
|
||||
text: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
postcode: Postcode;
|
||||
country: Country;
|
||||
floor: string | null;
|
||||
corridor: string | null;
|
||||
steps: string | null;
|
||||
flat: string | null;
|
||||
buildingName: string | null;
|
||||
distribution: string | null;
|
||||
extra: string | null;
|
||||
confidential: boolean;
|
||||
lines: string[];
|
||||
addressReference: AddressReference | null;
|
||||
validFrom: DateTime;
|
||||
validTo: DateTime | null;
|
||||
point: Point | null;
|
||||
refStatus: AddressRefStatus;
|
||||
isNoAddress: boolean;
|
||||
type: "address";
|
||||
address_id: number;
|
||||
text: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
postcode: Postcode;
|
||||
country: Country;
|
||||
floor: string | null;
|
||||
corridor: string | null;
|
||||
steps: string | null;
|
||||
flat: string | null;
|
||||
buildingName: string | null;
|
||||
distribution: string | null;
|
||||
extra: string | null;
|
||||
confidential: boolean;
|
||||
lines: string[];
|
||||
addressReference: AddressReference | null;
|
||||
validFrom: DateTime;
|
||||
validTo: DateTime | null;
|
||||
point: Point | null;
|
||||
refStatus: AddressRefStatus;
|
||||
isNoAddress: boolean;
|
||||
}
|
||||
|
||||
export interface AddressWithPoint extends Address {
|
||||
point: Point
|
||||
point: Point;
|
||||
}
|
||||
|
||||
export interface AddressReference {
|
||||
id: number;
|
||||
createdAt: DateTime | null;
|
||||
deletedAt: DateTime | null;
|
||||
municipalityCode: string;
|
||||
point: Point;
|
||||
postcode: Postcode;
|
||||
refId: string;
|
||||
source: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
updatedAt: DateTime | null;
|
||||
id: number;
|
||||
createdAt: DateTime | null;
|
||||
deletedAt: DateTime | null;
|
||||
municipalityCode: string;
|
||||
point: Point;
|
||||
postcode: Postcode;
|
||||
refId: string;
|
||||
source: string;
|
||||
street: string;
|
||||
streetNumber: string;
|
||||
updatedAt: DateTime | null;
|
||||
}
|
||||
|
||||
export interface SimpleGeographicalUnit {
|
||||
id: number;
|
||||
layerId: number;
|
||||
unitName: string;
|
||||
unitRefId: string;
|
||||
id: number;
|
||||
layerId: number;
|
||||
unitName: string;
|
||||
unitRefId: string;
|
||||
}
|
||||
|
||||
export interface GeographicalUnitLayer {
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
refId: string;
|
||||
id: number;
|
||||
name: TranslatableString;
|
||||
refId: string;
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
type: "location";
|
||||
id: number;
|
||||
active: boolean;
|
||||
address: Address | null;
|
||||
availableForUsers: boolean;
|
||||
createdAt: DateTime | null;
|
||||
createdBy: User | null;
|
||||
updatedAt: DateTime | null;
|
||||
updatedBy: User | null;
|
||||
email: string | null
|
||||
name: string;
|
||||
phonenumber1: string | null;
|
||||
phonenumber2: string | null;
|
||||
locationType: LocationType;
|
||||
type: "location";
|
||||
id: number;
|
||||
active: boolean;
|
||||
address: Address | null;
|
||||
availableForUsers: boolean;
|
||||
createdAt: DateTime | null;
|
||||
createdBy: User | null;
|
||||
updatedAt: DateTime | null;
|
||||
updatedBy: User | null;
|
||||
email: string | null;
|
||||
name: string;
|
||||
phonenumber1: string | null;
|
||||
phonenumber2: string | null;
|
||||
locationType: LocationType;
|
||||
}
|
||||
|
||||
export interface LocationAssociated {
|
||||
type: "location";
|
||||
id: number;
|
||||
type: "location";
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface LocationType {
|
||||
type: "location-type";
|
||||
id: number;
|
||||
active: boolean;
|
||||
addressRequired: "optional" | "required";
|
||||
availableForUsers: boolean;
|
||||
editableByUsers: boolean;
|
||||
contactData: "optional" | "required";
|
||||
title: TranslatableString;
|
||||
type: "location-type";
|
||||
id: number;
|
||||
active: boolean;
|
||||
addressRequired: "optional" | "required";
|
||||
availableForUsers: boolean;
|
||||
editableByUsers: boolean;
|
||||
contactData: "optional" | "required";
|
||||
title: TranslatableString;
|
||||
}
|
||||
|
||||
export interface NewsItemType {
|
||||
@@ -168,3 +185,8 @@ export interface NewsItemType {
|
||||
startDate: DateTime;
|
||||
endDate: DateTime | null;
|
||||
}
|
||||
|
||||
export interface WorkflowAvailable {
|
||||
name: string;
|
||||
text: string;
|
||||
}
|
||||
|
@@ -9,13 +9,16 @@
|
||||
</template>
|
||||
<template v-slot:tbody>
|
||||
<tr v-for="(w, i) in workflows.results" :key="`workflow-${i}`">
|
||||
<td>{{ w.title }}</td>
|
||||
<td>
|
||||
{{ w.title }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="workflow">
|
||||
<div class="breadcrumb">
|
||||
<i class="fa fa-circle me-1 text-chill-yellow mx-2"></i>
|
||||
<span class="mx-2">{{ getStep(w) }}</span>
|
||||
</div>
|
||||
<span v-if="w.isOnHoldAtCurrentStep" class="badge bg-success rounded-pill">{{ $t('on_hold') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td v-if="w.datas.persons !== null">
|
||||
|
@@ -41,6 +41,7 @@ const appMessages = {
|
||||
Step: "Étape",
|
||||
concerned_users: "Usagers concernés",
|
||||
Object_workflow: "Objet du workflow",
|
||||
on_hold: "En attente",
|
||||
show_entity: "Voir {entity}",
|
||||
the_activity: "l'échange",
|
||||
the_course: "le parcours",
|
||||
|
@@ -62,7 +62,7 @@ export default {
|
||||
required: false,
|
||||
}
|
||||
},
|
||||
emits: ['addNewEntity', 'removeEntity'],
|
||||
emits: ['addNewEntity', 'removeEntity', 'addNewEntityProcessEnded'],
|
||||
components: {
|
||||
AddPersons,
|
||||
},
|
||||
@@ -121,6 +121,7 @@ export default {
|
||||
);
|
||||
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||
modal.showModal = false;
|
||||
this.$emit('addNewEntityProcessEnded');
|
||||
},
|
||||
removeEntity(entity) {
|
||||
if (!this.$props.removableIfSet) {
|
||||
|
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<i :class="gender.icon"></i>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
const props = defineProps({
|
||||
gender: Object
|
||||
})
|
||||
|
||||
</script>
|
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import {UserGroup} from "../../../types";
|
||||
import {computed} from "vue";
|
||||
|
||||
interface UserGroupRenderBoxProps {
|
||||
userGroup: UserGroup;
|
||||
}
|
||||
|
||||
const props = defineProps<UserGroupRenderBoxProps>();
|
||||
|
||||
const styles = computed<{color: string, "background-color": string}>(() => {
|
||||
return {
|
||||
color: props.userGroup.foregroundColor,
|
||||
"background-color": props.userGroup.backgroundColor,
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="badge-user-group" :style="styles">{{ userGroup.label.fr }}</span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
@@ -6,7 +6,7 @@
|
||||
<div>
|
||||
<div class="item-row col">
|
||||
<h2>{{ w.title }}</h2>
|
||||
<div class="flex-grow-1 ms-3 h3">
|
||||
<div class="flex-grow-1 ms-3 h3">
|
||||
<div class="visually-hidden">
|
||||
{{ w.relatedEntityClass }}
|
||||
{{ w.relatedEntityId }}
|
||||
@@ -38,6 +38,7 @@
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
<span v-if="w.isOnHoldAtCurrentStep" class="badge bg-success rounded-pill">{{ $t('on_hold') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="item-row">
|
||||
|
@@ -1,20 +1,13 @@
|
||||
<template>
|
||||
|
||||
<button v-if="hasWorkflow"
|
||||
class="btn btn-primary"
|
||||
@click="openModal">
|
||||
<b>{{ countWorkflows }}</b>
|
||||
<template v-if="countWorkflows > 1">{{ trans(WORKFLOW_ASSOCIATED, { aw: 'other' }) }}</template>
|
||||
<template v-else>{{ trans(WORKFLOW_ASSOCIATED, { aw: 1 }) }}</template>
|
||||
</button>
|
||||
|
||||
<Pick-workflow v-else-if="allowCreate"
|
||||
<Pick-workflow
|
||||
:relatedEntityClass="this.relatedEntityClass"
|
||||
:relatedEntityId="this.relatedEntityId"
|
||||
:workflowsAvailables="workflowsAvailables"
|
||||
:preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate"
|
||||
:goToGenerateWorkflowPayload="this.goToGenerateWorkflowPayload"
|
||||
:countExistingWorkflows="countWorkflows"
|
||||
@go-to-generate-workflow="goToGenerateWorkflow"
|
||||
@click-open-list="openModal"
|
||||
></Pick-workflow>
|
||||
|
||||
<teleport to="body">
|
||||
@@ -39,6 +32,8 @@
|
||||
:workflowsAvailables="workflowsAvailables"
|
||||
:preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate"
|
||||
:goToGenerateWorkflowPayload="this.goToGenerateWorkflowPayload"
|
||||
:countExistingWorkflows="countWorkflows"
|
||||
:embedded-within-list-modal="true"
|
||||
@go-to-generate-workflow="this.goToGenerateWorkflow"
|
||||
></pick-workflow>
|
||||
</template>
|
||||
|
@@ -1,69 +1,104 @@
|
||||
<template>
|
||||
<template v-if="workflowsAvailables.length >= 1">
|
||||
<div class="dropdown d-grid gap-2">
|
||||
<template v-if="props.workflowsAvailables.length >= 1">
|
||||
<div v-if="countExistingWorkflows == 0 || embeddedWithinListModal" class="dropdown d-grid gap-2">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="createWorkflowButton" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Créer un workflow
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="createWorkflowButton">
|
||||
<li v-for="w in workflowsAvailables" :key="w.name">
|
||||
<a class="dropdown-item" :href="makeLink(w.name)" @click.prevent="goToGenerateWorkflow($event, w.name)">{{ w.text }}</a>
|
||||
<li v-for="w in props.workflowsAvailables" :key="w.name">
|
||||
<button class="dropdown-item" type="button" @click.prevent="goToGenerateWorkflow($event, w.name)">{{ w.text }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="btn-group">
|
||||
<button @click="emit('clickOpenList')" class="btn btn-primary">
|
||||
<template v-if="countExistingWorkflows === 1">
|
||||
1 workflow associé
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ countExistingWorkflows }} workflows associés
|
||||
</template>
|
||||
</button>
|
||||
<button class="btn btn-primary dropdown-toggle dropdown-toggle-split" type="button" id="createWorkflowButton" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<span class="visually-hidden">Liste des workflows disponibles</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="createWorkflowButton">
|
||||
<li v-for="w in props.workflowsAvailables" :key="w.name">
|
||||
<button class="dropdown-item" type="button" @click.prevent="goToGenerateWorkflow($event, w.name)">{{ w.text }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-if="countExistingWorkflows > 0" class="dropdown d-grid gap-2">
|
||||
<button @click="emit('clickOpenList')" class="btn btn-primary" type="button" id="createWorkflowButton" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<template v-if="countExistingWorkflows === 1">
|
||||
1 workflow associé
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ countExistingWorkflows }} workflows associés
|
||||
</template>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
import { buildLinkCreate } from 'ChillMainAssets/lib/entity-workflow/api.js';
|
||||
<script setup lang="ts">
|
||||
|
||||
// Define props
|
||||
const props = defineProps({
|
||||
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: () => ({}), // Use a function to return an object
|
||||
},
|
||||
});
|
||||
import {buildLinkCreate} from '../../../lib/entity-workflow/api';
|
||||
import {WorkflowAvailable} from "../../../types";
|
||||
|
||||
// Define emits
|
||||
const emit = defineEmits(['goToGenerateWorkflow']);
|
||||
|
||||
// Methods
|
||||
function makeLink(workflowName) {
|
||||
return buildLinkCreate(workflowName, props.relatedEntityClass, props.relatedEntityId);
|
||||
interface PickWorkflowConfig {
|
||||
relatedEntityClass: string;
|
||||
/**
|
||||
* Represents the identifier of a related entity.
|
||||
* This variable can store either a numerical value representing the entity's ID or an undefined value
|
||||
* if the related entity is not specified or available, for instance when the entity is created within
|
||||
* the interface and the id will be available later
|
||||
*/
|
||||
relatedEntityId: number|undefined;
|
||||
workflowsAvailables: WorkflowAvailable[];
|
||||
preventDefaultMoveToGenerate: boolean;
|
||||
goToGenerateWorkflowPayload: object;
|
||||
countExistingWorkflows: number;
|
||||
/**
|
||||
* if true, this button will not present a splitted button
|
||||
*/
|
||||
embeddedWithinListModal: boolean;
|
||||
}
|
||||
|
||||
function goToGenerateWorkflow(event, workflowName) {
|
||||
console.log('goToGenerateWorkflow', event, workflowName);
|
||||
const props = withDefaults(defineProps<PickWorkflowConfig>(), {preventDefaultMoveToGenerate: false, goToGenerateWorkflowPayload: {}, allowCreateWorkflow: false});
|
||||
|
||||
if (!props.preventDefaultMoveToGenerate) {
|
||||
console.log('to go generate');
|
||||
window.location.assign(makeLink(workflowName));
|
||||
const emit = defineEmits<{
|
||||
(e: 'goToGenerateWorkflow', {event: MouseEvent, workflowName: string, isLinkValid: boolean, link: string, payload: object}): void;
|
||||
(e: 'clickOpenList'): void;
|
||||
}>();
|
||||
|
||||
const makeLink = (workflowName: string): string => buildLinkCreate(workflowName, props.relatedEntityClass, props.relatedEntityId);
|
||||
|
||||
const goToGenerateWorkflow = (event: MouseEvent, workflowName: string): void => {
|
||||
console.log('goToGenerateWorkflow', event, workflowName);
|
||||
let link = '';
|
||||
let isLinkValid = false;
|
||||
|
||||
try {
|
||||
link = makeLink(workflowName);
|
||||
isLinkValid = true;
|
||||
} catch (e) {
|
||||
console.info("could not generate link to create workflow, maybe the relatedEntityId is not yet known", e);
|
||||
}
|
||||
|
||||
emit('goToGenerateWorkflow', {
|
||||
event,
|
||||
workflowName,
|
||||
link: makeLink(workflowName),
|
||||
payload: props.goToGenerateWorkflowPayload,
|
||||
});
|
||||
if (!props.preventDefaultMoveToGenerate) {
|
||||
window.location.assign(link);
|
||||
}
|
||||
|
||||
emit('goToGenerateWorkflow', {event, workflowName, link, isLinkValid, payload: props.goToGenerateWorkflowPayload});
|
||||
}
|
||||
|
||||
const goToDuplicateRelatedEntity = (event: MouseEvent, workflowName: string): void => {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<button v-if="idsMarkedAsRead.length === 0"
|
||||
class="btn btn-primary"
|
||||
type="button"
|
||||
@click="markAllRead"
|
||||
>
|
||||
<i class="fa fa-sm fa-envelope-open-o"></i> Marquer tout comme lu
|
||||
</button>
|
||||
<button v-else
|
||||
class="btn btn-primary"
|
||||
type="button"
|
||||
@click="undo"
|
||||
>
|
||||
<i class="fa fa-sm fa-envelope-open-o"></i> Annuler
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { makeFetch } from "../../../lib/api/apiMethods";
|
||||
import { ref } from "vue";
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'markAsRead', id: number): void,
|
||||
(e: 'markAsUnRead', id: number): void,
|
||||
}>();
|
||||
|
||||
const idsMarkedAsRead = ref([] as number[]);
|
||||
|
||||
async function markAllRead() {
|
||||
const ids: number[] = await makeFetch("POST", `/api/1.0/main/notification/mark/allread`, null);
|
||||
for (let i of ids) {
|
||||
idsMarkedAsRead.value.push(i);
|
||||
emit('markAsRead', i);
|
||||
}
|
||||
}
|
||||
|
||||
async function undo() {
|
||||
const touched: number[] = await makeFetch("POST", `/api/1.0/main/notification/mark/undoallread`, idsMarkedAsRead.value);
|
||||
while (idsMarkedAsRead.value.length > 0) {
|
||||
idsMarkedAsRead.value.pop();
|
||||
}
|
||||
for (let t of touched) {
|
||||
emit('markAsUnRead', t);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@@ -28,7 +28,8 @@
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<a v-if="isButtonGroup"
|
||||
<a
|
||||
v-if="isButtonGroup"
|
||||
type="button"
|
||||
class="btn btn-outline-primary"
|
||||
:href="showUrl"
|
||||
@@ -37,6 +38,20 @@
|
||||
<i class="fa fa-sm fa-comment-o"></i>
|
||||
</a>
|
||||
|
||||
<!-- "Mark All Read" button -->
|
||||
<button
|
||||
v-if="showMarkAllButton"
|
||||
class="btn"
|
||||
:class="overrideClass"
|
||||
type="button"
|
||||
:title="$t('markAllRead')"
|
||||
@click="markAllRead"
|
||||
>
|
||||
<i class="fa fa-sm fa-envelope-o"></i>
|
||||
<span v-if="!buttonNoText" class="ps-2">
|
||||
{{ $t("markAllRead") }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -101,6 +116,4 @@ const markAsRead = () => {
|
||||
});*/
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
<style lang="scss"></style>
|
||||
|
@@ -38,7 +38,9 @@ const messages = {
|
||||
person: "Usager",
|
||||
birthday: {
|
||||
man: "Né le",
|
||||
woman: "Née le"
|
||||
woman: "Née le",
|
||||
neutral: "Né·e le",
|
||||
unknown: "Né·e le",
|
||||
},
|
||||
deathdate: "Date de décès",
|
||||
household_without_address: "Le ménage de l'usager est sans adresse",
|
||||
|
Reference in New Issue
Block a user