Compare commits

...

10 Commits

Author SHA1 Message Date
171f7585c2 WIP translation conventions 2025-03-26 11:10:02 +01:00
fe6949ea26 Update chill bundles to v3.10.3 2025-03-18 13:47:31 +01:00
8a444a12f4 Turn off eslint error ban-ts-comment 2025-03-18 10:57:24 +01:00
8b7b5ceed7 Release v3.10.2 2025-03-17 22:30:17 +01:00
b0e826d05a Revert "Remove unnecessary ts-expect-error"
This reverts commit 7f101ba616.
2025-03-17 22:22:37 +01:00
6fb9c3af3f Release v3.10.1 2025-03-17 21:59:32 +01:00
7f101ba616 Remove unnecessary ts-expect-error 2025-03-17 21:59:16 +01:00
cea82fac10 Merge branch '333-removing-node-deps-to-symfony-ux-translator' into 'master'
Copy Symfony UX Translator module into to chill-bundles

Closes #333

See merge request Chill-Projet/chill-bundles!808
2025-03-17 20:45:48 +00:00
1344fc33e1 Copy Symfony UX Translator module into to chill-bundles 2025-03-17 20:45:48 +00:00
c8e09a28e6 Eslint fixes 2025-03-17 17:32:28 +01:00
22 changed files with 518 additions and 44 deletions

3
.changes/v3.10.1.md Normal file
View File

@@ -0,0 +1,3 @@
## v3.10.1 - 2025-03-17
### DX
* Remove yarn dependency to symfony/ux-translator, to ease the build process

3
.changes/v3.10.2.md Normal file
View File

@@ -0,0 +1,3 @@
## v3.10.2 - 2025-03-17
### Fixed
* Replace a ts-expect-error with a ts-ignore

3
.changes/v3.10.3.md Normal file
View File

@@ -0,0 +1,3 @@
## v3.10.3 - 2025-03-18
### DX
* Eslint fixes

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ docker/rabbitmq/data
# in this development bundle, we want to ignore directories related to a real app # in this development bundle, we want to ignore directories related to a real app
assets/* assets/*
!assets/translator.ts !assets/translator.ts
!assets/ux-translator
migrations/* migrations/*
templates/* templates/*
translations/* translations/*

View File

@@ -6,6 +6,18 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie). and is generated by [Changie](https://github.com/miniscruff/changie).
## v3.10.3 - 2025-03-18
### DX
* Eslint fixes
## v3.10.2 - 2025-03-17
### Fixed
* Replace a ts-expect-error with a ts-ignore
## v3.10.1 - 2025-03-17
### DX
* Remove yarn dependency to symfony/ux-translator, to ease the build process
## v3.10.0 - 2025-03-17 ## v3.10.0 - 2025-03-17
### Feature ### Feature
* ([#363](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/363)) Display social actions grouped per social issue within activity form * ([#363](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/363)) Display social actions grouped per social issue within activity form

View File

@@ -1,9 +1,7 @@
// @ts-ignore Cannot find module (when used within an app) import { trans, setLocale, setLocaleFallbacks } from "./ux-translator";
import { trans, setLocale, setLocaleFallbacks } from "@symfony/ux-translator";
setLocaleFallbacks({"en": "fr", "nl": "fr", "fr": "en"}); setLocaleFallbacks({"en": "fr", "nl": "fr", "fr": "en"});
setLocale('fr'); setLocale('fr');
export { trans }; export { trans };
// @ts-ignore Cannot find module (when used within an app)
export * from '../var/translations'; export * from '../var/translations';

View File

@@ -0,0 +1,3 @@
This directory import the symfony ux-translator files directly into chill-bundles.
This remove the yarn dependencies from the real package, which breaks our installation.

View File

@@ -0,0 +1 @@
export declare function format(id: string, parameters: Record<string, string | number>, locale: string): string;

View File

@@ -0,0 +1 @@
export declare function formatIntl(id: string, parameters: Record<string, string | number>, locale: string): string;

View File

@@ -0,0 +1,27 @@
export type DomainType = string;
export type LocaleType = string;
export type TranslationsType = Record<DomainType, {
parameters: ParametersType;
}>;
export type NoParametersType = Record<string, never>;
export type ParametersType = Record<string, string | number | Date> | NoParametersType;
export type RemoveIntlIcuSuffix<T> = T extends `${infer U}+intl-icu` ? U : T;
export type DomainsOf<M> = M extends Message<infer Translations, LocaleType> ? keyof Translations : never;
export type LocaleOf<M> = M extends Message<TranslationsType, infer Locale> ? Locale : never;
export type ParametersOf<M, D extends DomainType> = M extends Message<infer Translations, LocaleType> ? Translations[D] extends {
parameters: infer Parameters;
} ? Parameters : never : never;
export interface Message<Translations extends TranslationsType, Locale extends LocaleType> {
id: string;
translations: {
[domain in DomainType]: {
[locale in Locale]: string;
};
};
}
export declare function setLocale(locale: LocaleType | null): void;
export declare function getLocale(): LocaleType;
export declare function throwWhenNotFound(enabled: boolean): void;
export declare function setLocaleFallbacks(localeFallbacks: Record<LocaleType, LocaleType>): void;
export declare function getLocaleFallbacks(): Record<LocaleType, LocaleType>;
export declare function trans<M extends Message<TranslationsType, LocaleType>, D extends DomainsOf<M>, P extends ParametersOf<M, D>>(...args: P extends NoParametersType ? [message: M, parameters?: P, domain?: RemoveIntlIcuSuffix<D>, locale?: LocaleOf<M>] : [message: M, parameters: P, domain?: RemoveIntlIcuSuffix<D>, locale?: LocaleOf<M>]): string;

View File

@@ -0,0 +1 @@
export * from './translator';

View File

@@ -0,0 +1,283 @@
import { IntlMessageFormat } from 'intl-messageformat';
function strtr(string, replacePairs) {
const regex = Object.entries(replacePairs).map(([from]) => {
return from.replace(/([-[\]{}()*+?.\\^$|#,])/g, '\\$1');
});
if (regex.length === 0) {
return string;
}
return string.replace(new RegExp(regex.join('|'), 'g'), (matched) => replacePairs[matched].toString());
}
function format(id, parameters, locale) {
if (null === id || '' === id) {
return '';
}
if (typeof parameters['%count%'] === 'undefined' || Number.isNaN(parameters['%count%'])) {
return strtr(id, parameters);
}
const number = Number(parameters['%count%']);
let parts = [];
if (/^\|+$/.test(id)) {
parts = id.split('|');
}
else {
parts = id.match(/(?:\|\||[^|])+/g) || [];
}
const intervalRegex = /^(?<interval>({\s*(-?\d+(\.\d+)?[\s*,\s*\-?\d+(.\d+)?]*)\s*})|(?<left_delimiter>[[\]])\s*(?<left>-Inf|-?\d+(\.\d+)?)\s*,\s*(?<right>\+?Inf|-?\d+(\.\d+)?)\s*(?<right_delimiter>[[\]]))\s*(?<message>.*?)$/s;
const standardRules = [];
for (let part of parts) {
part = part.trim().replace(/\|\|/g, '|');
const matches = part.match(intervalRegex);
if (matches) {
const matchGroups = matches.groups || {};
if (matches[2]) {
for (const n of matches[3].split(',')) {
if (number === Number(n)) {
return strtr(matchGroups.message, parameters);
}
}
}
else {
const leftNumber = '-Inf' === matchGroups.left ? Number.NEGATIVE_INFINITY : Number(matchGroups.left);
const rightNumber = ['Inf', '+Inf'].includes(matchGroups.right)
? Number.POSITIVE_INFINITY
: Number(matchGroups.right);
if (('[' === matchGroups.left_delimiter ? number >= leftNumber : number > leftNumber) &&
(']' === matchGroups.right_delimiter ? number <= rightNumber : number < rightNumber)) {
return strtr(matchGroups.message, parameters);
}
}
}
else {
const ruleMatch = part.match(/^\w+:\s*(.*?)$/);
standardRules.push(ruleMatch ? ruleMatch[1] : part);
}
}
const position = getPluralizationRule(number, locale);
if (typeof standardRules[position] === 'undefined') {
if (1 === parts.length && typeof standardRules[0] !== 'undefined') {
return strtr(standardRules[0], parameters);
}
throw new Error(`Unable to choose a translation for "${id}" with locale "${locale}" for value "${number}". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %count% apples").`);
}
return strtr(standardRules[position], parameters);
}
function getPluralizationRule(number, locale) {
number = Math.abs(number);
let _locale = locale;
if (locale === 'pt_BR' || locale === 'en_US_POSIX') {
return 0;
}
_locale = _locale.length > 3 ? _locale.substring(0, _locale.indexOf('_')) : _locale;
switch (_locale) {
case 'af':
case 'bn':
case 'bg':
case 'ca':
case 'da':
case 'de':
case 'el':
case 'en':
case 'en_US_POSIX':
case 'eo':
case 'es':
case 'et':
case 'eu':
case 'fa':
case 'fi':
case 'fo':
case 'fur':
case 'fy':
case 'gl':
case 'gu':
case 'ha':
case 'he':
case 'hu':
case 'is':
case 'it':
case 'ku':
case 'lb':
case 'ml':
case 'mn':
case 'mr':
case 'nah':
case 'nb':
case 'ne':
case 'nl':
case 'nn':
case 'no':
case 'oc':
case 'om':
case 'or':
case 'pa':
case 'pap':
case 'ps':
case 'pt':
case 'so':
case 'sq':
case 'sv':
case 'sw':
case 'ta':
case 'te':
case 'tk':
case 'ur':
case 'zu':
return 1 === number ? 0 : 1;
case 'am':
case 'bh':
case 'fil':
case 'fr':
case 'gun':
case 'hi':
case 'hy':
case 'ln':
case 'mg':
case 'nso':
case 'pt_BR':
case 'ti':
case 'wa':
return number < 2 ? 0 : 1;
case 'be':
case 'bs':
case 'hr':
case 'ru':
case 'sh':
case 'sr':
case 'uk':
return 1 === number % 10 && 11 !== number % 100
? 0
: number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20)
? 1
: 2;
case 'cs':
case 'sk':
return 1 === number ? 0 : number >= 2 && number <= 4 ? 1 : 2;
case 'ga':
return 1 === number ? 0 : 2 === number ? 1 : 2;
case 'lt':
return 1 === number % 10 && 11 !== number % 100
? 0
: number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20)
? 1
: 2;
case 'sl':
return 1 === number % 100 ? 0 : 2 === number % 100 ? 1 : 3 === number % 100 || 4 === number % 100 ? 2 : 3;
case 'mk':
return 1 === number % 10 ? 0 : 1;
case 'mt':
return 1 === number
? 0
: 0 === number || (number % 100 > 1 && number % 100 < 11)
? 1
: number % 100 > 10 && number % 100 < 20
? 2
: 3;
case 'lv':
return 0 === number ? 0 : 1 === number % 10 && 11 !== number % 100 ? 1 : 2;
case 'pl':
return 1 === number
? 0
: number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14)
? 1
: 2;
case 'cy':
return 1 === number ? 0 : 2 === number ? 1 : 8 === number || 11 === number ? 2 : 3;
case 'ro':
return 1 === number ? 0 : 0 === number || (number % 100 > 0 && number % 100 < 20) ? 1 : 2;
case 'ar':
return 0 === number
? 0
: 1 === number
? 1
: 2 === number
? 2
: number % 100 >= 3 && number % 100 <= 10
? 3
: number % 100 >= 11 && number % 100 <= 99
? 4
: 5;
default:
return 0;
}
}
function formatIntl(id, parameters, locale) {
if (id === '') {
return '';
}
const intlMessage = new IntlMessageFormat(id, [locale.replace('_', '-')], undefined, { ignoreTag: true });
parameters = { ...parameters };
Object.entries(parameters).forEach(([key, value]) => {
if (key.includes('%') || key.includes('{')) {
delete parameters[key];
parameters[key.replace(/[%{} ]/g, '').trim()] = value;
}
});
return intlMessage.format(parameters);
}
let _locale = null;
let _localeFallbacks = {};
let _throwWhenNotFound = false;
function setLocale(locale) {
_locale = locale;
}
function getLocale() {
return (_locale ||
document.documentElement.getAttribute('data-symfony-ux-translator-locale') ||
(document.documentElement.lang ? document.documentElement.lang.replace('-', '_') : null) ||
'en');
}
function throwWhenNotFound(enabled) {
_throwWhenNotFound = enabled;
}
function setLocaleFallbacks(localeFallbacks) {
_localeFallbacks = localeFallbacks;
}
function getLocaleFallbacks() {
return _localeFallbacks;
}
function trans(message, parameters = {}, domain = 'messages', locale = null) {
if (typeof domain === 'undefined') {
domain = 'messages';
}
if (typeof locale === 'undefined' || null === locale) {
locale = getLocale();
}
if (typeof message.translations === 'undefined') {
return message.id;
}
const localesFallbacks = getLocaleFallbacks();
const translationsIntl = message.translations[`${domain}+intl-icu`];
if (typeof translationsIntl !== 'undefined') {
while (typeof translationsIntl[locale] === 'undefined') {
locale = localesFallbacks[locale];
if (!locale) {
break;
}
}
if (locale) {
return formatIntl(translationsIntl[locale], parameters, locale);
}
}
const translations = message.translations[domain];
if (typeof translations !== 'undefined') {
while (typeof translations[locale] === 'undefined') {
locale = localesFallbacks[locale];
if (!locale) {
break;
}
}
if (locale) {
return format(translations[locale], parameters, locale);
}
}
if (_throwWhenNotFound) {
throw new Error(`No translation message found with id "${message.id}".`);
}
return message.id;
}
export { getLocale, getLocaleFallbacks, setLocale, setLocaleFallbacks, throwWhenNotFound, trans };

1
assets/ux-translator/dist/utils.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export declare function strtr(string: string, replacePairs: Record<string, string | number>): string;

View File

@@ -0,0 +1,34 @@
{
"name": "@symfony/ux-translator",
"description": "Symfony Translator for JavaScript",
"license": "MIT",
"version": "1.0.0",
"main": "dist/translator_controller.js",
"types": "dist/translator_controller.d.ts",
"scripts": {
"build": "node ../../../bin/build_package.js .",
"watch": "node ../../../bin/build_package.js . --watch",
"test": "../../../bin/test_package.sh .",
"check": "biome check",
"ci": "biome ci"
},
"symfony": {
"importmap": {
"intl-messageformat": "^10.5.11",
"@symfony/ux-translator": "path:%PACKAGE%/dist/translator_controller.js",
"@app/translations": "path:var/translations/index.js",
"@app/translations/configuration": "path:var/translations/configuration.js"
}
},
"peerDependencies": {
"intl-messageformat": "^10.5.11"
},
"peerDependenciesMeta": {
"intl-messageformat": {
"optional": false
}
},
"devDependencies": {
"intl-messageformat": "^10.5.11"
}
}

View File

@@ -0,0 +1,107 @@
Translations
************
One source of truth
===================
As of January 2025 we have opted to use one source of truth for translations in our backend as well as our frontend.
You will find translations still being present in i18ns files for our vue components, but these will slowly be replaced.
The goal is to only use the messages.{locale}.yaml files to create our translations and keys.
Each time we do `symfony console cache:clear` a javascript and typescript file are generated containing all the keys and the corresponding translations.
These can then be imported into our vue components together with the `trans` method, for use in the vue templates.
Vue import example
^^^^^^^^^^^^^^^^^^
. code-block:: js
import {
ACTIVITY_BLOC_PERSONS,
ACTIVITY_BLOC_PERSONS_ASSOCIATED,
ACTIVITY_BLOC_THIRDPARTY,
ACTIVITY_BLOC_USERS,
ACTIVITY_ADD_PERSONS,
trans,
} from "translator";
Setup
=====
For development purposes we generally make use of the chill-bundles standalone project. Here the new translation setup will work out of the box.
However when working on a customer chill instance (with a root project and a chill-bundles implementation) it is required to execute the chill translations recipe
using the command
Translation key conventions
===========================
When adding new translation keys we have chosen to adhere to the following conventions as of April 2025.
Older translation keys will gradually be adapted to respect these conventions.
Conventions
^^^^^^^^^^^
Entity related messages
-----------------------
Translation keys will be structured as followed as follows:
`[bundle].[entity].[page or component].[action / message]`
. code-block:: yaml
person:
household:
index:
edit_comment: "Mettre à jour le commentaire"
So the key to be used will be `person.household.index.edit_comment` when used in a twig template
or
`PERSON_HOUSEHOLD_INDEX_EDIT_COMMENT` when used in a vue component.
Export related messages
-----------------------
Translation keys will be structured as followed as follows:
[bundle]
|
|__ export
|
|__ ['count | list' | 'filter' | 'aggregator']
|
|__ [export | filter | aggregator - name] OR [ properties (end of hierarchy) ]
|
|__ [action / message]
ex. Filter
. code-block:: yaml
activity:
export:
filter:
by_users_job:
title: Filtrer les échanges par type
'Filtered activity by users job: only %jobs%': 'Filtré par métier d''au moins un utilisateur participant: seulement %jobs%'
ex. Export type
. code-block:: yaml
activity:
export:
count:
count_persons_on_activity:
title: Nombre d'usagers concernés par les échanges
list:
activities_by_parcours:
title: Liste des échanges liés à un parcours
ex. Export properties shared by a certain export type
. code-block:: yaml
activity:
export:
list:
users ids: Identifiant des utilisateurs

View File

@@ -34,6 +34,7 @@ export default ts.config(
// override/add rules settings here, such as: // override/add rules settings here, such as:
"vue/multi-word-component-names": "off", "vue/multi-word-component-names": "off",
"@typescript-eslint/no-require-imports": "off", "@typescript-eslint/no-require-imports": "off",
"@typescript-eslint/ban-ts-comment": "off"
}, },
}, },
); );

View File

@@ -11,7 +11,6 @@
"@hotwired/stimulus": "^3.0.0", "@hotwired/stimulus": "^3.0.0",
"@luminateone/eslint-baseline": "^1.0.9", "@luminateone/eslint-baseline": "^1.0.9",
"@symfony/stimulus-bridge": "^3.2.0", "@symfony/stimulus-bridge": "^3.2.0",
"@symfony/ux-translator": "file:vendor/symfony/ux-translator/assets",
"@symfony/webpack-encore": "^4.1.0", "@symfony/webpack-encore": "^4.1.0",
"@tsconfig/node20": "^20.1.4", "@tsconfig/node20": "^20.1.4",
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.0.5",
@@ -59,6 +58,7 @@
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"dropzone": "^5.7.6", "dropzone": "^5.7.6",
"es6-promise": "^4.2.8", "es6-promise": "^4.2.8",
"intl-messageformat": "^10.5.11",
"leaflet": "^1.7.1", "leaflet": "^1.7.1",
"marked": "^12.0.2", "marked": "^12.0.2",
"masonry-layout": "^4.2.2", "masonry-layout": "^4.2.2",

View File

@@ -96,7 +96,7 @@
</div> </div>
</div> </div>
<FullCalendar :options="calendarOptions" ref="calendarRef"> <FullCalendar :options="calendarOptions" ref="calendarRef">
<template v-slot:eventContent="{ arg }: {arg: { event: EventApi }}"> <template v-slot:eventContent="{ arg }: { arg: { event: EventApi } }">
<span :class="eventClasses(arg.event)"> <span :class="eventClasses(arg.event)">
<b v-if="arg.event.extendedProps.is === 'remote'">{{ <b v-if="arg.event.extendedProps.is === 'remote'">{{
arg.event.title arg.event.title

View File

@@ -91,9 +91,7 @@
class="col-5 p-0 text-center turnSignature" class="col-5 p-0 text-center turnSignature"
> >
<button <button
:disabled=" :disabled="isFirstSignatureZone"
isFirstSignatureZone
"
class="btn btn-light btn-sm" class="btn btn-light btn-sm"
@click="turnSignature(-1)" @click="turnSignature(-1)"
> >
@@ -101,9 +99,7 @@
</button> </button>
<span>|</span> <span>|</span>
<button <button
:disabled=" :disabled="isLastSignatureZone"
isLastSignatureZone
"
class="btn btn-light btn-sm" class="btn btn-light btn-sm"
@click="turnSignature(1)" @click="turnSignature(1)"
> >
@@ -207,9 +203,7 @@
</button> </button>
<span>|</span> <span>|</span>
<button <button
:disabled=" :disabled="isLastSignatureZone"
isLastSignatureZone
"
class="btn btn-light btn-sm" class="btn btn-light btn-sm"
@click="turnSignature(1)" @click="turnSignature(1)"
> >
@@ -221,9 +215,7 @@
class="col-4 d-none d-xl-flex p-0 text-center turnSignature" class="col-4 d-none d-xl-flex p-0 text-center turnSignature"
> >
<button <button
:disabled=" :disabled="isFirstSignatureZone"
isFirstSignatureZone
"
class="btn btn-light btn-sm" class="btn btn-light btn-sm"
@click="turnSignature(-1)" @click="turnSignature(-1)"
> >
@@ -326,7 +318,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ref, Ref, reactive, computed} from "vue"; import { ref, Ref, computed } from "vue";
import { useToast } from "vue-toast-notification"; import { useToast } from "vue-toast-notification";
import "vue-toast-notification/dist/theme-sugar.css"; import "vue-toast-notification/dist/theme-sugar.css";
import { import {
@@ -344,18 +336,15 @@ import {
PDFPageProxy, PDFPageProxy,
} from "pdfjs-dist/types/src/display/api"; } from "pdfjs-dist/types/src/display/api";
// @ts-ignore // @ts-ignore incredible but the console.log is needed
import * as PdfWorker from "pdfjs-dist/build/pdf.worker.mjs"; import * as PdfWorker from "pdfjs-dist/build/pdf.worker.mjs";
console.log(PdfWorker); // incredible but this is needed console.log(PdfWorker);
// import { PdfWorker } from 'pdfjs-dist/build/pdf.worker.mjs' // import { PdfWorker } from 'pdfjs-dist/build/pdf.worker.mjs'
// pdfjsLib.GlobalWorkerOptions.workerSrc = PdfWorker; // pdfjsLib.GlobalWorkerOptions.workerSrc = PdfWorker;
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue"; import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { import { download_doc_as_pdf } from "../StoredObjectButton/helpers";
download_and_decrypt_doc,
download_doc_as_pdf,
} from "../StoredObjectButton/helpers";
pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.mjs"; pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.mjs";
@@ -426,13 +415,19 @@ const $toast = useToast();
const signature = window.signature; const signature = window.signature;
const isFirstSignatureZone = () => userSignatureZone.value?.index ? userSignatureZone.value.index < 1 : false const isFirstSignatureZone = () =>
const isLastSignatureZone = () => userSignatureZone.value?.index ? userSignatureZone.value.index >= signature.zones.length - 1 : false userSignatureZone.value?.index ? userSignatureZone.value.index < 1 : false;
const isLastSignatureZone = () =>
userSignatureZone.value?.index
? userSignatureZone.value.index >= signature.zones.length - 1
: false;
/** /**
* Return true if the user has selected a user zone (existing on the doc or created by the user) * Return true if the user has selected a user zone (existing on the doc or created by the user)
*/ */
const hasSignatureZoneSelected = computed<boolean>(() => userSignatureZone.value !== null); const hasSignatureZoneSelected = computed<boolean>(
() => userSignatureZone.value !== null,
);
const setZoomLevel = async (zoomLevel: string) => { const setZoomLevel = async (zoomLevel: string) => {
zoom.value = Number.parseFloat(zoomLevel); zoom.value = Number.parseFloat(zoomLevel);
@@ -755,7 +750,7 @@ const confirmSign = () => {
zone: userSignatureZone.value, zone: userSignatureZone.value,
}; };
makeFetch("POST", url, body) makeFetch("POST", url, body)
.then((r) => { .then(() => {
checkForReady(); checkForReady();
}) })
.catch((error) => { .catch((error) => {
@@ -777,9 +772,7 @@ const undoSign = async () => {
}; };
const toggleAddZone = () => { const toggleAddZone = () => {
canvasEvent.value === "select" canvasEvent.value = canvasEvent.value === "select" ? "add" : "select";
? (canvasEvent.value = "add")
: (canvasEvent.value = "select");
}; };
const addZoneEvent = async (e: PointerEvent, canvas: HTMLCanvasElement) => { const addZoneEvent = async (e: PointerEvent, canvas: HTMLCanvasElement) => {

View File

@@ -3,9 +3,8 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { reactive } from "vue"; import { reactive } from "vue";
import HistoryButtonList from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue"; import HistoryButtonList from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue";
import { import {
StoredObject, StoredObjectVersion, StoredObject,
StoredObjectVersionWithPointInTime, StoredObjectVersionWithPointInTime,
StoredObjectPointInTime
} from "./../../../types"; } from "./../../../types";
interface HistoryButtonListConfig { interface HistoryButtonListConfig {
@@ -29,7 +28,9 @@ const open = () => {
state.opened = true; state.opened = true;
}; };
const onRestoreVersion = (payload : { newVersion: StoredObjectVersionWithPointInTime }) => emit('restoreVersion', payload) const onRestoreVersion = (payload: {
newVersion: StoredObjectVersionWithPointInTime;
}) => emit("restoreVersion", payload);
defineExpose({ open }); defineExpose({ open });
</script> </script>
@@ -45,9 +46,7 @@ defineExpose({ open });
:versions="props.versions" :versions="props.versions"
:can-edit="canEdit" :can-edit="canEdit"
:stored-object="storedObject" :stored-object="storedObject"
@restore-version=" @restore-version="onRestoreVersion"
onRestoreVersion
"
></history-button-list> ></history-button-list>
</template> </template>
</modal> </modal>

View File

@@ -46,8 +46,9 @@ const onPickGenericDoc = ({
emit("pickGenericDoc", { genericDoc }); emit("pickGenericDoc", { genericDoc });
}; };
const onRemoveAttachment = (payload: { attachment: WorkflowAttachment }) => { emit('removeAttachment', payload) }; const onRemoveAttachment = (payload: { attachment: WorkflowAttachment }) => {
emit("removeAttachment", payload);
};
</script> </script>
<template> <template>

View File

@@ -72,9 +72,13 @@ const placeTrans = (str: string): string => {
} }
}; };
const onPickDocument = (payload: { genericDoc: GenericDocForAccompanyingPeriod }) => emit('pickGenericDoc', payload); const onPickDocument = (payload: {
genericDoc: GenericDocForAccompanyingPeriod;
}) => emit("pickGenericDoc", payload);
const onRemoveGenericDoc = (payload: {genericDoc: GenericDocForAccompanyingPeriod}) => emit('removeGenericDoc', payload); const onRemoveGenericDoc = (payload: {
genericDoc: GenericDocForAccompanyingPeriod;
}) => emit("removeGenericDoc", payload);
const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => { const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
if (false === loaded.value) { if (false === loaded.value) {
@@ -250,9 +254,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
:genericDoc="g" :genericDoc="g"
:is-picked="isPicked(g)" :is-picked="isPicked(g)"
@pickGenericDoc="onPickDocument" @pickGenericDoc="onPickDocument"
@removeGenericDoc=" @removeGenericDoc="onRemoveGenericDoc"
onRemoveGenericDoc
"
></pick-generic-doc-item> ></pick-generic-doc-item>
</div> </div>
<div v-else class="text-center chill-no-data-statement"> <div v-else class="text-center chill-no-data-statement">