mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-27 17:15:02 +00:00
Enhance entity creation: Add CreateModal
and integrate with AddPersons
workflow.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { GenericDoc } from "ChillDocStoreAssets/types/generic_doc";
|
import {GenericDoc} from "ChillDocStoreAssets/types/generic_doc";
|
||||||
import { StoredObject, StoredObjectStatus } from "ChillDocStoreAssets/types";
|
import {StoredObject, StoredObjectStatus} from "ChillDocStoreAssets/types";
|
||||||
|
import {CreatableEntityType} from "ChillPersonAssets/types";
|
||||||
|
|
||||||
export interface DateTime {
|
export interface DateTime {
|
||||||
datetime: string;
|
datetime: string;
|
||||||
@@ -278,3 +279,12 @@ export interface addNewEntities {
|
|||||||
selected: Selected[];
|
selected: Selected[];
|
||||||
modal: Modal;
|
modal: Modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for the CreateModal and Create component
|
||||||
|
*/
|
||||||
|
export interface CreateComponentConfig {
|
||||||
|
action?: string;
|
||||||
|
allowedTypes: CreatableEntityType[];
|
||||||
|
query?: string;
|
||||||
|
}
|
||||||
|
@@ -46,34 +46,29 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from "vue";
|
import {computed, onMounted, ref} from "vue";
|
||||||
import OnTheFlyPerson from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
|
import OnTheFlyPerson from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
|
||||||
import OnTheFlyThirdparty from "ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue";
|
import OnTheFlyThirdparty from "ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue";
|
||||||
import {
|
import {ONTHEFLY_CREATE_PERSON, ONTHEFLY_CREATE_THIRDPARTY, trans,} from "translator";
|
||||||
trans,
|
import {CreatableEntityType} from "ChillPersonAssets/types";
|
||||||
ONTHEFLY_CREATE_PERSON,
|
import {CreateComponentConfig} from "ChillMainAssets/types";
|
||||||
ONTHEFLY_CREATE_THIRDPARTY,
|
|
||||||
} from "translator";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps<CreateComponentConfig>();
|
||||||
action: String,
|
const type = ref<CreatableEntityType| null>(null);
|
||||||
allowedTypes: Array,
|
|
||||||
query: String,
|
|
||||||
});
|
|
||||||
|
|
||||||
const type = ref(null);
|
const radioType = computed<CreatableEntityType| null>({
|
||||||
|
|
||||||
const radioType = computed({
|
|
||||||
get: () => type.value,
|
get: () => type.value,
|
||||||
set: (val) => {
|
set: (val: CreatableEntityType | null) => {
|
||||||
type.value = val;
|
type.value = val;
|
||||||
console.log("## type:", val, ", action:", props.action);
|
console.log("## type:", val, ", action:", props.action);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const castPerson = ref(null);
|
type AnyComponentInstance = InstanceType<typeof OnTheFlyPerson> | InstanceType<typeof OnTheFlyThirdparty> | null;
|
||||||
const castThirdparty = ref(null);
|
|
||||||
|
const castPerson = ref<AnyComponentInstance>(null);
|
||||||
|
const castThirdparty = ref<AnyComponentInstance>(null);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
type.value =
|
type.value =
|
||||||
@@ -82,22 +77,27 @@ onMounted(() => {
|
|||||||
: "person";
|
: "person";
|
||||||
});
|
});
|
||||||
|
|
||||||
function isActive(tab) {
|
function isActive(tab: CreatableEntityType) {
|
||||||
return type.value === tab;
|
return type.value === tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
function castDataByType() {
|
// Types for data structures coming from child components are not declared in TS yet.
|
||||||
|
// We conservatively type them as any to preserve runtime behavior while enabling TS in this component.
|
||||||
|
function castDataByType(): any {
|
||||||
switch (radioType.value) {
|
switch (radioType.value) {
|
||||||
case "person":
|
case "person":
|
||||||
return castPerson.value.$data.person;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
case "thirdparty":
|
return (castPerson.value as any)?.$data?.person;
|
||||||
let data = castThirdparty.value.$data.thirdparty;
|
case "thirdparty": {
|
||||||
if (data.address !== undefined && data.address !== null) {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
let data: any = (castThirdparty.value as any)?.$data?.thirdparty;
|
||||||
|
if (data && data.address !== undefined && data.address !== null) {
|
||||||
data.address = { id: data.address.address_id };
|
data.address = { id: data.address.address_id };
|
||||||
} else {
|
} else if (data) {
|
||||||
data.address = null;
|
data.address = null;
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw Error("Invalid type of entity");
|
throw Error("Invalid type of entity");
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||||
|
import Create from "ChillMainAssets/vuejs/OnTheFly/components/Create.vue";
|
||||||
|
import {CreateComponentConfig} from "ChillMainAssets/types";
|
||||||
|
|
||||||
|
const props = defineProps<CreateComponentConfig>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<teleport to="body">
|
||||||
|
<modal
|
||||||
|
@close="() => emit('close')"
|
||||||
|
:modal-dialog-class="modalDialogClass"
|
||||||
|
:hide-footer="false"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<h3 class="modal-title">{{ modalTitle }}</h3>
|
||||||
|
</template>
|
||||||
|
<template #body-head>
|
||||||
|
<div class="modal-body">
|
||||||
|
<Create :allowed-types="props.allowed-types" :action="props.action" :query="props.query"></Create>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</modal>
|
||||||
|
</teleport>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
@@ -261,11 +261,20 @@ export interface AccompanyingPeriodWorkEvaluationDocument {
|
|||||||
workflows: object[];
|
workflows: object[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EntityType =
|
/**
|
||||||
|
* Entity types that a user can create
|
||||||
|
*/
|
||||||
|
export type CreatableEntityType =
|
||||||
|
| "person"
|
||||||
|
| "thirdparty";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entities that can be search and selected by a user
|
||||||
|
*/
|
||||||
|
export type EntityType = CreatableEntityType
|
||||||
| "user_group"
|
| "user_group"
|
||||||
| "user"
|
| "user"
|
||||||
| "person"
|
|
||||||
| "thirdparty"
|
|
||||||
| "household";
|
| "household";
|
||||||
|
|
||||||
export type Entities = (UserGroup | User | Person | Thirdparty | Household) & {
|
export type Entities = (UserGroup | User | Person | Thirdparty | Household) & {
|
||||||
@@ -303,7 +312,8 @@ export interface Search {
|
|||||||
|
|
||||||
export interface SearchOptions {
|
export interface SearchOptions {
|
||||||
uniq: boolean;
|
uniq: boolean;
|
||||||
type: string[];
|
/** @deprecated */
|
||||||
|
type: EntityType[];
|
||||||
priority: number | null;
|
priority: number | null;
|
||||||
button: {
|
button: {
|
||||||
size: string;
|
size: string;
|
||||||
|
@@ -9,22 +9,31 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<person-choose-modal
|
<person-choose-modal
|
||||||
v-if="showModal"
|
v-if="showModalChoose"
|
||||||
:show="showModal"
|
|
||||||
:modal-title="modalTitle"
|
:modal-title="modalTitle"
|
||||||
:options="options"
|
:options="options"
|
||||||
:suggested="suggested"
|
:suggested="suggested"
|
||||||
:selected="selected"
|
:selected="selected"
|
||||||
:modal-dialog-class="'modal-dialog-scrollable modal-xl'"
|
:modal-dialog-class="'modal-dialog-scrollable modal-xl'"
|
||||||
|
:allow-create="props.allowCreate"
|
||||||
@close="closeModal"
|
@close="closeModal"
|
||||||
@addNewPersons="payload => $emit('addNewPersons', payload)"
|
@addNewPersons="payload => emit('addNewPersons', payload)"
|
||||||
|
@onAskForCreate="onAskForCreate"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<CreateModal
|
||||||
|
v-if="creatableEntityTypes.length > 0 && showModalCreate"
|
||||||
|
:allowed-types="creatableEntityTypes"
|
||||||
|
></CreateModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import PersonChooseModal from './AddPersons/PersonChooseModal.vue';
|
import PersonChooseModal from './AddPersons/PersonChooseModal.vue';
|
||||||
import type { Suggestion, SearchOptions } from 'ChillPersonAssets/types';
|
import type {Suggestion, SearchOptions, CreatableEntityType, EntityType} from 'ChillPersonAssets/types';
|
||||||
|
import {marked} from "marked";
|
||||||
|
import options = marked.options;
|
||||||
|
import CreateModal from "ChillMainAssets/vuejs/OnTheFly/components/CreateModal.vue";
|
||||||
|
|
||||||
interface AddPersonsConfig {
|
interface AddPersonsConfig {
|
||||||
suggested?: Suggestion[];
|
suggested?: Suggestion[];
|
||||||
@@ -33,19 +42,22 @@ interface AddPersonsConfig {
|
|||||||
modalTitle: string;
|
modalTitle: string;
|
||||||
options: SearchOptions;
|
options: SearchOptions;
|
||||||
allowCreate?: boolean;
|
allowCreate?: boolean;
|
||||||
|
types?: EntityType|undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<AddPersonsConfig>(), {
|
const props = withDefaults(defineProps<AddPersonsConfig>(), {
|
||||||
suggested: () => [],
|
suggested: () => [],
|
||||||
selected: () => [],
|
selected: () => [],
|
||||||
allowCreate: () => true,
|
allowCreate: () => true,
|
||||||
|
types: () => undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'addNewPersons', payload: { selected: Suggestion[] }): void;
|
(e: 'addNewPersons', payload: { selected: Suggestion[] }): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const showModal = ref(false);
|
const showModalChoose = ref(false);
|
||||||
|
const showModalCreate = ref(false);
|
||||||
|
|
||||||
const getClassButton = computed(() => {
|
const getClassButton = computed(() => {
|
||||||
const size = props.options?.button?.size ?? '';
|
const size = props.options?.button?.size ?? '';
|
||||||
@@ -57,12 +69,25 @@ const displayTextButton = computed(() =>
|
|||||||
props.options?.button?.display !== undefined ? props.options.button.display : true,
|
props.options?.button?.display !== undefined ? props.options.button.display : true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const creatableEntityTypes = computed<CreatableEntityType[]>(() => {
|
||||||
|
if (typeof props.options.type !== 'undefined') {
|
||||||
|
return props.options.type.filter((e: EntityType) => e === 'thirdparty' || e === 'person');
|
||||||
|
}
|
||||||
|
return props.type.filter((e: EntityType) => e === 'thirdparty' || e === 'person');
|
||||||
|
})
|
||||||
|
|
||||||
|
function onAskForCreate({query}: {query: string}) {
|
||||||
|
console.log('onAskForCreate', query);
|
||||||
|
showModalChoose.value = false;
|
||||||
|
showModalCreate.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
function openModal() {
|
function openModal() {
|
||||||
showModal.value = true;
|
showModalChoose.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeModal() {
|
function closeModal() {
|
||||||
showModal.value = false;
|
showModalChoose.value = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -1,10 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<teleport to="body">
|
<teleport to="body">
|
||||||
<modal
|
<modal
|
||||||
v-if="show"
|
|
||||||
@close="() => emit('close')"
|
@close="() => emit('close')"
|
||||||
:modal-dialog-class="modalDialogClass"
|
:modal-dialog-class="modalDialogClass"
|
||||||
:show="show"
|
|
||||||
:hide-footer="false"
|
:hide-footer="false"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -58,7 +56,6 @@
|
|||||||
:item="item"
|
:item="item"
|
||||||
:search="search"
|
:search="search"
|
||||||
:type="checkUniq"
|
:type="checkUniq"
|
||||||
@save-form-on-the-fly="saveFormOnTheFly"
|
|
||||||
@new-prior-suggestion="newPriorSuggestion"
|
@new-prior-suggestion="newPriorSuggestion"
|
||||||
@update-selected="updateSelected"
|
@update-selected="updateSelected"
|
||||||
/>
|
/>
|
||||||
@@ -84,6 +81,7 @@
|
|||||||
@save-form-on-the-fly="saveFormOnTheFly"
|
@save-form-on-the-fly="saveFormOnTheFly"
|
||||||
ref="onTheFly"
|
ref="onTheFly"
|
||||||
/>
|
/>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -102,7 +100,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, nextTick, watch } from 'vue';
|
import {ref, reactive, computed, nextTick, watch, onMounted} from 'vue';
|
||||||
import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue';
|
import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue';
|
||||||
import PersonSuggestion from './PersonSuggestion.vue';
|
import PersonSuggestion from './PersonSuggestion.vue';
|
||||||
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue';
|
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue';
|
||||||
@@ -130,7 +128,6 @@ import type {
|
|||||||
type Result = OriginalResult & { addressId?: number };
|
type Result = OriginalResult & { addressId?: number };
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
show: boolean;
|
|
||||||
modalTitle: string;
|
modalTitle: string;
|
||||||
options: SearchOptions;
|
options: SearchOptions;
|
||||||
suggested?: Suggestion[];
|
suggested?: Suggestion[];
|
||||||
@@ -148,22 +145,19 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'close'): void;
|
(e: 'close'): void;
|
||||||
|
/** @deprecated use 'onPickEntities' */
|
||||||
(e: 'addNewPersons', payload: { selected: Suggestion[] }): void;
|
(e: 'addNewPersons', payload: { selected: Suggestion[] }): void;
|
||||||
(e: 'onPickEntities', payload: { selected: EntitiesOrMe[] }): void;
|
(e: 'onPickEntities', payload: { selected: EntitiesOrMe[] }): void;
|
||||||
|
(e: 'onAskForCreate', payload: { query: string }): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const searchRef = ref<HTMLInputElement | null>(null);
|
const searchRef = ref<HTMLInputElement | null>(null);
|
||||||
const onTheFly = ref<InstanceType<typeof OnTheFly> | null>(null);
|
const onTheFly = ref<InstanceType<typeof OnTheFly> | null>(null);
|
||||||
|
|
||||||
watch(
|
onMounted(() => {
|
||||||
() => props.show,
|
// give the focus on the search bar
|
||||||
async (visible) => {
|
searchRef.value?.focus();
|
||||||
if (visible) {
|
});
|
||||||
await nextTick();
|
|
||||||
searchRef.value?.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const search = reactive({
|
const search = reactive({
|
||||||
query: '' as string,
|
query: '' as string,
|
||||||
@@ -318,6 +312,8 @@ function pickEntities(): void {
|
|||||||
emit('close');
|
emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO remove this
|
||||||
async function saveFormOnTheFly({ type, data }: { type: string; data: Result }) {
|
async function saveFormOnTheFly({ type, data }: { type: string; data: Result }) {
|
||||||
try {
|
try {
|
||||||
if (type === 'person') {
|
if (type === 'person') {
|
||||||
@@ -362,6 +358,8 @@ async function saveFormOnTheFly({ type, data }: { type: string; data: Result })
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
defineExpose({ resetSearch });
|
defineExpose({ resetSearch });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user