refactor: workflow

This commit is contained in:
puriphatt 2024-10-28 11:04:58 +07:00
parent 249f7d59f5
commit 3395995f68
4 changed files with 562 additions and 165 deletions

View file

@ -0,0 +1,285 @@
<script lang="ts" setup>
import { nextTick } from 'vue';
import {
WorkflowUserInTable,
WorkflowTemplatePayload,
} from 'src/stores/workflow-template/types';
import FormFlow from 'src/components/04_flow-management/FormFlow.vue';
import DialogForm from 'src/components/DialogForm.vue';
import SideMenu from 'src/components/SideMenu.vue';
import DrawerInfo from 'src/components/DrawerInfo.vue';
import {
UndoButton,
SaveButton,
EditButton,
DeleteButton,
} from 'src/components/button';
const model = defineModel<boolean>({ required: true, default: false });
const drawerModel = defineModel<boolean>('drawerModel', {
required: true,
default: false,
});
const registerBranchId = defineModel('registerBranchId', { default: '' });
const flowData = defineModel<WorkflowTemplatePayload>('flowData', {
required: true,
default: {
name: '',
step: [],
},
});
const userInTable = defineModel<WorkflowUserInTable[]>('userInTable', {
default: [],
});
withDefaults(
defineProps<{
readonly?: boolean;
isEdit?: boolean;
}>(),
{ readonly: false, isEdit: false },
);
defineEmits<{
(e: 'submit'): void;
(e: 'close'): void;
(e: 'changeStatus'): void;
(e: 'drawerUndo'): void;
(e: 'drawerEdit'): void;
(e: 'drawerDelete'): void;
}>();
async function addStep() {
flowData.value.step.push({
responsiblePersonId: [],
value: [],
name: '',
});
await nextTick();
const scrollTarget = document.getElementById(
`input-flow-step-name-${flowData.value.step.length - 1}`,
);
if (scrollTarget)
scrollTarget.scrollIntoView({ behavior: 'instant', inline: 'center' });
}
</script>
<template>
<DialogForm
:title="$t('flow.title')"
v-model:modal="model"
:submit="() => $emit('submit')"
:close="() => $emit('close')"
>
<div
class="col surface-1 rounded bordered scroll row relative-position"
:class="{
'q-mx-lg q-my-md': $q.screen.gt.sm,
'q-mx-md q-my-sm': !$q.screen.gt.sm,
}"
>
<section
class="col"
style="height: 100%; max-height: 100; overflow-y: auto"
v-if="$q.screen.gt.sm"
>
<div class="q-py-md q-pl-md q-pr-sm">
<SideMenu
:menu="[
{
name: $t('general.name', { msg: $t('flow.template') }),
anchor: 'form-flow-template',
},
{
name: $t('flow.processStep'),
anchor: 'form-flow-step',
useBtn: true,
},
]"
background="transparent"
:active="{
background: 'hsla(var(--blue-6-hsl) / .2)',
foreground: 'var(--blue-6)',
}"
scroll-element="#flow-form"
>
<template v-slot:btn-form-flow-step>
<q-btn
dense
flat
icon="mdi-plus"
size="sm"
rounded
id="btn-add-step"
padding="0px 0px"
style="color: var(--stone-9)"
@click.stop="addStep"
:disabled="readonly"
/>
</template>
</SideMenu>
</div>
</section>
<section
class="col-12 col-md-10"
:class="{
'q-py-md q-pr-md ': $q.screen.gt.sm,
'q-py-md q-px-lg': !$q.screen.gt.sm,
}"
style="height: 100%; max-height: 100%; overflow-y: auto"
id="flow-form"
>
<FormFlow
v-model:flow-data="flowData"
v-model:register-branch-id="registerBranchId"
/>
</section>
</div>
</DialogForm>
<DrawerInfo
bg-on
hide-action
:is-edit="isEdit"
:title="flowData.name"
v-model:drawerOpen="drawerModel"
:submit="() => $emit('submit')"
:close="() => $emit('close')"
>
<div class="col column full-height">
<div
style="flex: 1; width: 100%; overflow-y: auto"
id="drawer-user-form"
:class="{
'q-px-lg q-py-md': $q.screen.gt.sm,
'q-px-md q-py-sm': !$q.screen.gt.sm,
}"
>
<div
class="col surface-1 full-height rounded bordered scroll row relative-position"
>
<div
class="rounded row"
:class="{
'q-py-md q-px-lg': $q.screen.gt.sm,
'q-py-sm q-px-lg': !$q.screen.gt.sm,
}"
style="position: absolute; z-index: 999; top: 0; right: 0"
>
<div
v-if="flowData.status !== 'INACTIVE'"
class="surface-1 row rounded"
>
<UndoButton
v-if="isEdit"
id="btn-info-basic-undo"
icon-only
@click="
() => {
$emit('drawerUndo');
}
"
type="button"
/>
<SaveButton
v-if="isEdit"
id="btn-info-basic-save"
icon-only
type="submit"
/>
<EditButton
v-if="!isEdit"
id="btn-info-basic-edit"
icon-only
@click="
() => {
$emit('drawerEdit');
// infoDrawerEdit = true;
// isImageEdit = true;
}
"
type="button"
/>
<DeleteButton
v-if="!isEdit"
id="btn-info-basic-delete"
icon-only
@click="
() => {
$emit('drawerDelete');
// onDelete(currentUser.id);
}
"
type="button"
/>
</div>
</div>
<section
class="col"
style="height: 100%; max-height: 100; overflow-y: auto"
v-if="$q.screen.gt.sm"
>
<div class="q-py-md q-pl-md q-pr-sm">
<SideMenu
:menu="[
{
name: $t('general.name', { msg: $t('flow.template') }),
anchor: 'form-flow-template',
},
{
name: $t('flow.processStep'),
anchor: 'form-flow-step',
useBtn: true,
},
]"
background="transparent"
:active="{
background: 'hsla(var(--blue-6-hsl) / .2)',
foreground: 'var(--blue-6)',
}"
scroll-element="#flow-form"
>
<template v-slot:btn-form-flow-step>
<q-btn
dense
flat
icon="mdi-plus"
size="sm"
rounded
id="btn-add-step"
padding="0px 0px"
style="color: var(--stone-9)"
@click.stop="addStep"
:disabled="readonly"
/>
</template>
</SideMenu>
</div>
</section>
<section
class="col-12 col-md-10"
:class="{
'q-py-md q-pr-md ': $q.screen.gt.sm,
'q-py-md q-px-lg': !$q.screen.gt.sm,
}"
style="height: 100%; max-height: 100%; overflow-y: auto"
id="flow-form"
>
<FormFlow
:readonly
@change-status="$emit('changeStatus')"
onDrawer
v-model:user-in-table="userInTable"
v-model:flow-data="flowData"
v-model:register-branch-id="registerBranchId"
/>
</section>
</div>
</div>
</div>
</DrawerInfo>
</template>
<style scoped></style>

View file

@ -1,22 +1,26 @@
<script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue';
import { onMounted, reactive, ref, watch } from 'vue';
import { QTableProps } from 'quasar';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import {
WorkflowUserInTable,
WorkflowTemplate,
WorkflowTemplatePayload,
} from 'src/stores/workflow-template/types';
import { useWorkflowTemplate } from 'src/stores/workflow-template';
import { dialog } from 'src/stores/utils';
import ButtonAddComponent from 'components/ButtonAddCompoent.vue';
import StatCardComponent from 'src/components/StatCardComponent.vue';
import CreateButton from 'src/components/AddButton.vue';
import PaginationComponent from 'src/components/PaginationComponent.vue';
import FlowDialog from '../04_product-service/FlowDialog.vue';
import FlowDialog from './FlowDialog.vue';
import NoData from 'src/components/NoData.vue';
import { QTableProps } from 'quasar';
import { storeToRefs } from 'pinia';
import { watch } from 'vue';
import KebabAction from 'src/components/shared/KebabAction.vue';
const { t } = useI18n();
const workflowStore = useWorkflowTemplate();
const {
data: workflowData,
@ -33,12 +37,17 @@ const pageState = reactive({
total: 0,
addModal: false,
viewDrawer: false,
isDrawerEdit: true,
});
const currWorkflowData = ref<WorkflowTemplate>();
const formDataWorkflow = ref<WorkflowTemplatePayload>({
status: 'CREATED',
name: '',
step: [],
});
const userInTable = ref<WorkflowUserInTable[]>([]);
const registeredBranchId = ref('');
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
const columns = [
@ -68,23 +77,149 @@ const columns = [
},
] satisfies QTableProps['columns'];
function triggerAddDialog() {
pageState.addModal = true;
function triggerDialog(type: 'add' | 'edit' | 'view') {
if (type === 'add') {
pageState.addModal = true;
pageState.isDrawerEdit = true;
}
if (type === 'view') {
pageState.viewDrawer = true;
pageState.isDrawerEdit = false;
}
if (type === 'edit') {
pageState.viewDrawer = true;
pageState.isDrawerEdit = true;
}
}
async function submitAdd() {
const res = await workflowStore.creatWorkflowTemplate({
async function deleteWorkflow(id?: string) {
const targetId = id || currWorkflowData.value?.id;
if (targetId === undefined) return;
dialog({
color: 'negative',
icon: 'mdi-trash-can-outline',
title: t('dialog.title.confirmDelete'),
actionText: t('general.delete'),
persistent: true,
message: t('dialog.message.confirmDelete'),
action: async () => {
await workflowStore.deleteWorkflowTemplate(targetId);
await fetchWorkflowList();
resetForm();
},
cancel: () => {},
});
}
async function changeStatus(id?: string) {
const targetId = id || currWorkflowData.value?.id;
if (targetId === undefined) return;
formDataWorkflow.value.status !== 'INACTIVE' ? 'INACTIVE' : 'ACTIVE';
const res = await workflowStore.editWorkflowTemplate({
id: targetId,
registeredBranchId: registeredBranchId.value,
...formDataWorkflow.value,
});
if (res) {
pageState.addModal = false;
await fetchWorkflowList();
console.log(res);
// if(currWorkflowData.value) currWorkflowData.value.status
}
}
function resetForm() {
async function triggerChangeStatus(id?: string, status?: string) {
const targetId = id || currWorkflowData.value?.id;
const targetStatus = status || currWorkflowData.value?.status;
if (targetId === undefined || targetStatus === undefined) return;
return await new Promise((resolve, reject) => {
dialog({
color: targetStatus !== 'INACTIVE' ? 'warning' : 'info',
icon:
targetStatus !== 'INACTIVE'
? 'mdi-alert'
: 'mdi-message-processing-outline',
title: t('dialog.title.confirmChangeStatus'),
actionText:
targetStatus !== 'INACTIVE' ? t('general.close') : t('general.open'),
message:
targetStatus !== 'INACTIVE'
? t('dialog.message.confirmChangeStatusOff')
: t('dialog.message.confirmChangeStatusOn'),
action: async () => {
await changeStatus(targetId).then(resolve).catch(reject);
},
cancel: () => {},
});
});
}
function undo() {
if (!currWorkflowData.value) return;
assignFormData(currWorkflowData.value);
pageState.isDrawerEdit = false;
}
async function submit() {
if (currWorkflowData.value?.id !== undefined) {
await workflowStore.editWorkflowTemplate({
id: currWorkflowData.value.id,
registeredBranchId: registeredBranchId.value,
...formDataWorkflow.value,
});
} else {
await workflowStore.creatWorkflowTemplate({
registeredBranchId: registeredBranchId.value,
...formDataWorkflow.value,
});
}
await fetchWorkflowList();
resetForm();
}
function assignFormData(workflowData: WorkflowTemplate) {
currWorkflowData.value = workflowData;
formDataWorkflow.value = {
status: workflowData.status,
name: workflowData.name,
step: workflowData.step.map((s, i) => {
userInTable.value[i] = { name: s.name, resposiblePerson: [] };
s.responsiblePerson.forEach((p) => {
userInTable.value[i].resposiblePerson.push({
id: p.user.id,
selectedImage: p.user.selectedImage,
gender: p.user.gender,
namePrefix: p.user.namePrefix,
firstName: p.user.firstName,
lastName: p.user.lastName,
firstNameEN: p.user.firstNameEN,
lastNameEN: p.user.lastNameEN,
code: p.user.code,
});
});
return {
name: s.name,
// type: s.type,
value: s.value || [],
responsiblePersonId: s.responsiblePerson.map((p) => p.userId),
};
}),
};
registeredBranchId.value = workflowData.registeredBranchId;
}
function resetForm() {
currWorkflowData.value = undefined;
pageState.isDrawerEdit = true;
pageState.addModal = false;
pageState.viewDrawer = false;
registeredBranchId.value = '';
userInTable.value = [];
formDataWorkflow.value = {
status: 'CREATED',
name: '',
step: [],
};
@ -122,7 +257,7 @@ watch(() => pageState.inputSearch, fetchWorkflowList);
<ButtonAddComponent
style="z-index: 999"
hide-icon
@click="triggerAddDialog"
@click="triggerDialog('add')"
></ButtonAddComponent>
<div class="column full-height no-wrap">
@ -234,7 +369,7 @@ watch(() => pageState.inputSearch, fetchWorkflowList);
<CreateButton
v-if="!pageState.inputSearch"
@click="triggerAddDialog"
@click="triggerDialog('add')"
label="general.add"
:i18n-args="{ text: $t('flow.title') }"
/>
@ -277,14 +412,41 @@ watch(() => pageState.inputSearch, fetchWorkflowList);
<q-td class="text-center">{{ props.rowIndex + 1 }}</q-td>
<q-td>{{ props.row.name }}</q-td>
<q-td class="text-right">{{ props.row.step.length }}</q-td>
<q-td class="text-right">
<q-btn
icon="mdi-eye-outline"
size="sm"
dense
round
flat
@click.stop="$emit('view', props.rowIndex)"
<q-td class="row items-center justify-end">
<div class="row">
<q-btn
icon="mdi-eye-outline"
size="sm"
dense
round
flat
@click.stop="
() => {
assignFormData(props.row);
triggerDialog('view');
}
"
/>
</div>
<KebabAction
:id-name="props.row.id"
:status="props.row.status"
@view="
() => {
assignFormData(props.row);
triggerDialog('view');
}
"
@edit="
() => {
assignFormData(props.row);
triggerDialog('edit');
}
"
@delete="() => deleteWorkflow(props.row.id)"
@change-status="
() => triggerChangeStatus(props.row.id, props.row.status)
"
/>
</q-td>
</q-tr>
@ -349,10 +511,18 @@ watch(() => pageState.inputSearch, fetchWorkflowList);
</div>
<FlowDialog
@change-status="triggerChangeStatus"
@drawer-delete="deleteWorkflow"
@drawer-edit="pageState.isDrawerEdit = true"
@drawer-undo="undo"
@close="resetForm"
@submit="submitAdd"
@submit="submit"
:readonly="!pageState.isDrawerEdit"
:isEdit="pageState.isDrawerEdit"
v-model="pageState.addModal"
v-model:user-in-table="userInTable"
v-model:flow-data="formDataWorkflow"
v-model:drawer-model="pageState.viewDrawer"
v-model:register-branch-id="registeredBranchId"
/>
</template>

View file

@ -1,119 +0,0 @@
<script lang="ts" setup>
import DialogForm from 'src/components/DialogForm.vue';
import SideMenu from 'src/components/SideMenu.vue';
import FormFlow from 'src/components/04_flow-management/FormFlow.vue';
import { WorkflowTemplatePayload } from 'src/stores/workflow-template/types';
import { nextTick } from 'vue';
const registerBranchId = defineModel('registerBranchId', { default: '' });
const model = defineModel<boolean>({ required: true, default: false });
const flowData = defineModel<WorkflowTemplatePayload>('flowData', {
required: true,
default: {
name: '',
step: [],
},
});
withDefaults(
defineProps<{
readonly?: boolean;
}>(),
{ readonly: false },
);
defineEmits<{
(e: 'submit'): void;
(e: 'close'): void;
}>();
async function addStep() {
flowData.value.step.push({
responsiblePersonId: [],
value: [],
name: '',
});
await nextTick();
const scrollTarget = document.getElementById(
`input-flow-step-name-${flowData.value.step.length - 1}`,
);
if (scrollTarget)
scrollTarget.scrollIntoView({ behavior: 'instant', inline: 'center' });
}
</script>
<template>
<DialogForm
:title="$t('flow.title')"
v-model:modal="model"
:submit="() => $emit('submit')"
:close="() => $emit('close')"
>
<div
class="col surface-1 rounded bordered scroll row relative-position"
:class="{
'q-mx-lg q-my-md': $q.screen.gt.sm,
'q-mx-md q-my-sm': !$q.screen.gt.sm,
}"
>
<section
class="col"
style="height: 100%; max-height: 100; overflow-y: auto"
v-if="$q.screen.gt.sm"
>
<div class="q-py-md q-pl-md q-pr-sm">
<SideMenu
:menu="[
{
name: $t('general.name', { msg: $t('flow.template') }),
anchor: 'form-flow-template',
},
{
name: $t('flow.processStep'),
anchor: 'form-flow-step',
useBtn: true,
},
]"
background="transparent"
:active="{
background: 'hsla(var(--blue-6-hsl) / .2)',
foreground: 'var(--blue-6)',
}"
scroll-element="#flow-form"
>
<template v-slot:btn-form-flow-step>
<q-btn
dense
flat
icon="mdi-plus"
size="sm"
rounded
id="btn-add-step"
padding="0px 0px"
style="color: var(--stone-9)"
@click.stop="addStep"
:disabled="readonly"
/>
</template>
</SideMenu>
</div>
</section>
<section
class="col-12 col-md-10"
:class="{
'q-py-md q-pr-md ': $q.screen.gt.sm,
'q-py-md q-px-lg': !$q.screen.gt.sm,
}"
style="height: 100%; max-height: 100%; overflow-y: auto"
id="flow-form"
>
<FormFlow
v-model:flow-data="flowData"
v-model:register-branch-id="registerBranchId"
/>
</section>
</div>
</DialogForm>
</template>
<style scoped></style>