Merge branch 'develop'
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 10s
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 10s
This commit is contained in:
commit
96fc372bf2
16 changed files with 490 additions and 128 deletions
|
|
@ -1,8 +1,4 @@
|
||||||
name: Gitea Action
|
name: Deploy Local
|
||||||
|
|
||||||
run-name: Build ${{ github.actor }}
|
|
||||||
|
|
||||||
# Intended for local gitea instance only.
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
@ -37,22 +33,14 @@ jobs:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
tags: ${{ env.CONTAINER_IMAGE_NAME }}
|
tags: ${{ env.CONTAINER_IMAGE_NAME }}
|
||||||
push: true
|
push: true
|
||||||
- name: Remote Deploy Development
|
- name: Remote Deploy
|
||||||
uses: appleboy/ssh-action@v1.2.1
|
uses: appleboy/ssh-action@v1.2.1
|
||||||
with:
|
with:
|
||||||
host: ${{ vars.SSH_DEVELOPMENT_HOST }}
|
host: ${{ vars.SSH_DEPLOY_HOST }}
|
||||||
port: ${{ vars.SSH_DEVELOPMENT_PORT }}
|
port: ${{ vars.SSH_DEPLOY_PORT }}
|
||||||
username: ${{ secrets.SSH_DEVELOPMENT_USER }}
|
username: ${{ secrets.SSH_DEPLOY_USER }}
|
||||||
password: ${{ secrets.SSH_DEVELOPMENT_PASSWORD }}
|
password: ${{ secrets.SSH_DEPLOY_PASSWORD }}
|
||||||
script: eval "${{ secrets.SSH_DEVELOPMENT_DEPLOY_CMD }}" & wait
|
script: eval "${{ secrets.SSH_DEPLOY_CMD }}"
|
||||||
- name: Remote Deploy Test
|
|
||||||
uses: appleboy/ssh-action@v1.2.1
|
|
||||||
with:
|
|
||||||
host: ${{ vars.SSH_TEST_HOST }}
|
|
||||||
port: ${{ vars.SSH_TEST_PORT }}
|
|
||||||
username: ${{ secrets.SSH_TEST_USER }}
|
|
||||||
password: ${{ secrets.SSH_TEST_PASSWORD }}
|
|
||||||
script: eval "${{ secrets.SSH_TEST_DEPLOY_CMD }}" & wait
|
|
||||||
- name: Notify Discord Success
|
- name: Notify Discord Success
|
||||||
if: success()
|
if: success()
|
||||||
run: |
|
run: |
|
||||||
21
.forgejo/workflows/spellcheck.yaml
Normal file
21
.forgejo/workflows/spellcheck.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
name: Spell Check
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CLICOLOR: 1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
spelling:
|
||||||
|
name: Spell Check with Typos
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Actions Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Spell Check Repo
|
||||||
|
uses: crate-ci/typos@v1.29.9
|
||||||
|
with:
|
||||||
|
files: ./src
|
||||||
31
.github/workflows/local-build-dev.yaml
vendored
31
.github/workflows/local-build-dev.yaml
vendored
|
|
@ -1,31 +0,0 @@
|
||||||
name: local-build-dev
|
|
||||||
|
|
||||||
# Intended for local network use.
|
|
||||||
# Remote access is possible if the host has a public IP address.
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: ${{ vars.DOCKER_REGISTRY }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
local-build-dev:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Setup Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
with:
|
|
||||||
config-inline: |
|
|
||||||
[registry."${{ env.REGISTRY }}"]
|
|
||||||
http = true
|
|
||||||
insecure = true
|
|
||||||
- name: Build and Push Docker Image
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: true
|
|
||||||
tags: ${{ env.REGISTRY }}/jws/jws-frontend:dev
|
|
||||||
allow: security.insecure
|
|
||||||
2
.typos.toml
Normal file
2
.typos.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[default]
|
||||||
|
extend-ignore-re = ["(?Rm)^.*(#|//)\\s*spellchecker:disable-line$"]
|
||||||
|
|
@ -45,6 +45,8 @@ type ExclusiveProps = {
|
||||||
type: 'service' | 'product';
|
type: 'service' | 'product';
|
||||||
grid?: boolean;
|
grid?: boolean;
|
||||||
disabledWorkerId?: string[];
|
disabledWorkerId?: string[];
|
||||||
|
disabledProductFields?: (typeof columnsProduct)[number]['name'][];
|
||||||
|
disabledServiceFields?: (typeof columnsService)[number]['name'][];
|
||||||
rows: Product[];
|
rows: Product[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -56,6 +58,13 @@ const columnsProduct = [
|
||||||
field: (_) => '#check',
|
field: (_) => '#check',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
align: 'center',
|
||||||
|
label: 'general.order',
|
||||||
|
field: (e: Product & { _index: number }) => e._index + 1,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: '#productName',
|
name: '#productName',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
|
@ -76,7 +85,7 @@ const columnsProduct = [
|
||||||
label: 'productService.product.priceInformation',
|
label: 'productService.product.priceInformation',
|
||||||
field: (v: Product) => v,
|
field: (v: Product) => v,
|
||||||
},
|
},
|
||||||
] satisfies QTableColumn[];
|
] as const satisfies QTableColumn[];
|
||||||
|
|
||||||
const columnsService = [
|
const columnsService = [
|
||||||
{
|
{
|
||||||
|
|
@ -85,6 +94,12 @@ const columnsService = [
|
||||||
label: '',
|
label: '',
|
||||||
field: (_) => '#check',
|
field: (_) => '#check',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
align: 'center',
|
||||||
|
label: 'general.order',
|
||||||
|
field: (e: Service & { _index: number }) => e._index + 1,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: '#serviceName',
|
name: '#serviceName',
|
||||||
|
|
@ -110,7 +125,7 @@ const columnsService = [
|
||||||
label: 'general.createdAt',
|
label: 'general.createdAt',
|
||||||
field: (v: Service) => dateFormatJS({ date: v.createdAt }),
|
field: (v: Service) => dateFormatJS({ date: v.createdAt }),
|
||||||
},
|
},
|
||||||
] satisfies QTableColumn[];
|
] as const satisfies QTableColumn[];
|
||||||
|
|
||||||
const props = defineProps<ExclusiveProps>();
|
const props = defineProps<ExclusiveProps>();
|
||||||
|
|
||||||
|
|
@ -165,7 +180,18 @@ function selectedIndex(item: any) {
|
||||||
:props="props"
|
:props="props"
|
||||||
>
|
>
|
||||||
<q-th
|
<q-th
|
||||||
v-for="col in type === 'product' ? columnsProduct : columnsService"
|
v-for="col in type === 'product'
|
||||||
|
? columnsProduct.filter((v) =>
|
||||||
|
disabledProductFields
|
||||||
|
? !disabledProductFields.includes(v.name)
|
||||||
|
: true,
|
||||||
|
)
|
||||||
|
: columnsService.filter((v) =>
|
||||||
|
disabledServiceFields
|
||||||
|
? !disabledServiceFields.includes(v.name)
|
||||||
|
: true,
|
||||||
|
)"
|
||||||
|
:auto-width="col.name === '#check'"
|
||||||
:key="col.name"
|
:key="col.name"
|
||||||
:props="props"
|
:props="props"
|
||||||
>
|
>
|
||||||
|
|
@ -209,7 +235,17 @@ function selectedIndex(item: any) {
|
||||||
class="text-center"
|
class="text-center"
|
||||||
>
|
>
|
||||||
<q-td
|
<q-td
|
||||||
v-for="col in type === 'product' ? columnsProduct : columnsService"
|
v-for="col in type === 'product'
|
||||||
|
? columnsProduct.filter((v) =>
|
||||||
|
disabledProductFields
|
||||||
|
? !disabledProductFields.includes(v.name)
|
||||||
|
: true,
|
||||||
|
)
|
||||||
|
: columnsService.filter((v) =>
|
||||||
|
disabledServiceFields
|
||||||
|
? !disabledServiceFields.includes(v.name)
|
||||||
|
: true,
|
||||||
|
)"
|
||||||
:align="col.align"
|
:align="col.align"
|
||||||
:key="col.name"
|
:key="col.name"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export default {
|
||||||
confirm: 'Confirm',
|
confirm: 'Confirm',
|
||||||
login: 'Login',
|
login: 'Login',
|
||||||
logout: 'Logout',
|
logout: 'Logout',
|
||||||
manage: 'Manage',
|
manage: 'Manage {text}',
|
||||||
theme: 'Theme',
|
theme: 'Theme',
|
||||||
light: 'Light',
|
light: 'Light',
|
||||||
dark: 'Dark',
|
dark: 'Dark',
|
||||||
|
|
@ -915,6 +915,10 @@ export default {
|
||||||
|
|
||||||
salesRepresentative: 'Sales Representative',
|
salesRepresentative: 'Sales Representative',
|
||||||
ref: 'Reference',
|
ref: 'Reference',
|
||||||
|
action: {
|
||||||
|
title: 'Action',
|
||||||
|
configure: 'Configure',
|
||||||
|
},
|
||||||
status: {
|
status: {
|
||||||
work: {
|
work: {
|
||||||
Pending: 'Await for order',
|
Pending: 'Await for order',
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export default {
|
||||||
confirm: 'ยืนยัน',
|
confirm: 'ยืนยัน',
|
||||||
login: 'เข้าสู่ระบบ',
|
login: 'เข้าสู่ระบบ',
|
||||||
logout: 'ออกจากระบบ',
|
logout: 'ออกจากระบบ',
|
||||||
manage: 'จัดการ',
|
manage: 'จัดการ{text}',
|
||||||
theme: 'ธีม',
|
theme: 'ธีม',
|
||||||
light: 'สว่าง',
|
light: 'สว่าง',
|
||||||
dark: 'มืด',
|
dark: 'มืด',
|
||||||
|
|
@ -904,6 +904,10 @@ export default {
|
||||||
noWorkflowTemplate: 'คุณไม่ได้เลือกแม่แบบขั้นตอนการทำงาน',
|
noWorkflowTemplate: 'คุณไม่ได้เลือกแม่แบบขั้นตอนการทำงาน',
|
||||||
salesRepresentative: 'พนักงานขาย',
|
salesRepresentative: 'พนักงานขาย',
|
||||||
ref: 'อ้างอิง',
|
ref: 'อ้างอิง',
|
||||||
|
action: {
|
||||||
|
title: 'จัดการ',
|
||||||
|
configure: 'กำหนดค่า',
|
||||||
|
},
|
||||||
status: {
|
status: {
|
||||||
work: {
|
work: {
|
||||||
Pending: 'รอสั่งงาน',
|
Pending: 'รอสั่งงาน',
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ const responsibleUserId = defineModel<string>('responsibleUserId', {
|
||||||
defineProps<{
|
defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
districtId?: string;
|
districtId?: string;
|
||||||
cost?: boolean;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
watch(responsibleUserLocal, (lhs, rhs) => {
|
watch(responsibleUserLocal, (lhs, rhs) => {
|
||||||
|
|
|
||||||
229
src/pages/08_request-list/RequestAction.vue
Normal file
229
src/pages/08_request-list/RequestAction.vue
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { RequestWork } from 'src/stores/request-list';
|
||||||
|
import { DialogContainer, DialogHeader } from 'src/components/dialog';
|
||||||
|
import {
|
||||||
|
BackButton,
|
||||||
|
CancelButton,
|
||||||
|
MainButton,
|
||||||
|
SaveButton,
|
||||||
|
} from 'src/components/button';
|
||||||
|
import TableProductAndService from 'src/components/shared/table/TableProductAndService.vue';
|
||||||
|
import { Product } from 'src/stores/product-service/types';
|
||||||
|
import FormResponsibleUser from './FormResponsibleUser.vue';
|
||||||
|
import FormGroupHead from './FormGroupHead.vue';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
work: RequestWork[];
|
||||||
|
responsibleDistrictId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(
|
||||||
|
e: 'submit',
|
||||||
|
data: {
|
||||||
|
form: { responsibleUserLocal: boolean; responsibleUserId: string };
|
||||||
|
selected: { _work: RequestWork }[];
|
||||||
|
},
|
||||||
|
): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
enum Step {
|
||||||
|
Product = 1,
|
||||||
|
Configure = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = defineModel<boolean>('selected', { default: false });
|
||||||
|
const step = ref<Step>(Step.Product);
|
||||||
|
const selected = ref<{ _work: RequestWork }[]>([]);
|
||||||
|
const form = reactive({
|
||||||
|
responsibleUserLocal: false,
|
||||||
|
responsibleUserId: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
step.value = Step.Product;
|
||||||
|
selected.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function prev() {
|
||||||
|
step.value = Step.Product;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<DialogContainer v-model="open" :onOpen="reset">
|
||||||
|
<template #header>
|
||||||
|
<DialogHeader :title="$t('requestList.action.title')" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="surface-0 q-pa-md">
|
||||||
|
<div class="stepper-wrapper">
|
||||||
|
<div class="stepper">
|
||||||
|
<template
|
||||||
|
v-for="(label, i) in [
|
||||||
|
$t('menu.product'),
|
||||||
|
$t('requestList.action.configure'),
|
||||||
|
]"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
<span class="step" :class="{ ['step__active']: step > i }">
|
||||||
|
<span class="step-outer"><span class="step-inner" /></span>
|
||||||
|
<span class="step-label">{{ label }}</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="step-connector"
|
||||||
|
:class="{ ['step-connector__active']: step > i + 1 }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="surface-1 q-pa-md col">
|
||||||
|
<TableProductAndService
|
||||||
|
v-if="step === Step.Product"
|
||||||
|
type="product"
|
||||||
|
v-model:selected="selected"
|
||||||
|
:disabled-product-fields="[
|
||||||
|
'#priceInformation',
|
||||||
|
'productProcessingTime',
|
||||||
|
]"
|
||||||
|
:rows="
|
||||||
|
(work?.map((v) => ({
|
||||||
|
...v.productService.product,
|
||||||
|
type: 'product',
|
||||||
|
_work: v,
|
||||||
|
})) || []) as unknown as Product[]
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template v-if="step === Step.Configure">
|
||||||
|
<q-expansion-item
|
||||||
|
dense
|
||||||
|
class="overflow-hidden bordered full-width"
|
||||||
|
switch-toggle-side
|
||||||
|
style="border-radius: var(--radius-2)"
|
||||||
|
expand-icon="mdi-chevron-down-circle"
|
||||||
|
header-class="surface-1 q-py-sm text-medium text-body1"
|
||||||
|
default-opened
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<span>
|
||||||
|
{{ $t('requestList.employeeMessenger') }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<FormGroupHead>
|
||||||
|
{{
|
||||||
|
$t('general.select', { msg: $t('requestList.employeeMessenger') })
|
||||||
|
}}
|
||||||
|
</FormGroupHead>
|
||||||
|
|
||||||
|
<FormResponsibleUser
|
||||||
|
:district-id="responsibleDistrictId"
|
||||||
|
v-model:responsible-user-id="form.responsibleUserId"
|
||||||
|
v-model:responsible-user-local="form.responsibleUserLocal"
|
||||||
|
/>
|
||||||
|
</q-expansion-item>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="q-gutter-x-xs q-ml-auto">
|
||||||
|
<CancelButton
|
||||||
|
v-if="step === Step.Product"
|
||||||
|
id="btn-cancel"
|
||||||
|
outlined
|
||||||
|
@click="
|
||||||
|
reset();
|
||||||
|
open = false;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<BackButton
|
||||||
|
v-if="step === Step.Configure"
|
||||||
|
id="btn-back"
|
||||||
|
outlined
|
||||||
|
@click="prev"
|
||||||
|
/>
|
||||||
|
<MainButton
|
||||||
|
icon="mdi-check"
|
||||||
|
color="207 96% 32%"
|
||||||
|
solid
|
||||||
|
id="btn-next"
|
||||||
|
v-if="step === Step.Product"
|
||||||
|
@click="step = Step.Configure"
|
||||||
|
>
|
||||||
|
{{ $t('general.next') }}
|
||||||
|
</MainButton>
|
||||||
|
<SaveButton
|
||||||
|
v-if="step === Step.Configure"
|
||||||
|
id="btn-save"
|
||||||
|
solid
|
||||||
|
@click="$emit('submit', { form, selected })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</DialogContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.stepper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
margin-inline: 25%;
|
||||||
|
|
||||||
|
& > .step {
|
||||||
|
--__color: var(--gray-5);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
gap: 0.25rem;
|
||||||
|
|
||||||
|
&.step__active {
|
||||||
|
--__color: var(--brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .step-label {
|
||||||
|
position: absolute;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--__color);
|
||||||
|
white-space: nowrap;
|
||||||
|
top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .step-outer {
|
||||||
|
display: inline-flex;
|
||||||
|
border: 2px solid var(--__color);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
& > .step-inner {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--__color);
|
||||||
|
width: 0.7rem;
|
||||||
|
height: 0.7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .step-connector {
|
||||||
|
display: block;
|
||||||
|
border-bottom: 2px solid var(--gray-5);
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
&.step-connector__active {
|
||||||
|
border-color: var(--brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -11,9 +11,10 @@ import PropertiesExpansion from './PropertiesExpansion.vue';
|
||||||
import FormGroupHead from './FormGroupHead.vue';
|
import FormGroupHead from './FormGroupHead.vue';
|
||||||
import AvatarGroup from 'src/components/shared/AvatarGroup.vue';
|
import AvatarGroup from 'src/components/shared/AvatarGroup.vue';
|
||||||
import { StateButton } from 'components/button';
|
import { StateButton } from 'components/button';
|
||||||
import { CancelButton } from 'components/button';
|
import { CancelButton, MainButton } from 'components/button';
|
||||||
import DutyExpansion from './DutyExpansion.vue';
|
import DutyExpansion from './DutyExpansion.vue';
|
||||||
import MessengerExpansion from './MessengerExpansion.vue';
|
import MessengerExpansion from './MessengerExpansion.vue';
|
||||||
|
import RequestAction from './RequestAction.vue';
|
||||||
|
|
||||||
// NOTE: Store
|
// NOTE: Store
|
||||||
import {
|
import {
|
||||||
|
|
@ -68,9 +69,41 @@ const statusFile = ref<Attributes>({
|
||||||
const refDocumentExpansion = ref<InstanceType<typeof DocumentExpansion>[]>([]);
|
const refDocumentExpansion = ref<InstanceType<typeof DocumentExpansion>[]>([]);
|
||||||
const data = ref<RequestData>();
|
const data = ref<RequestData>();
|
||||||
const flow = ref<WorkflowTemplate>();
|
const flow = ref<WorkflowTemplate>();
|
||||||
|
const productsList = computed(() =>
|
||||||
|
workList.value
|
||||||
|
?.filter((v) =>
|
||||||
|
v.productService.work?.attributes.workflowStep?.[
|
||||||
|
pageState.currentStep - 1
|
||||||
|
]?.productsId.includes(v.productService.productId),
|
||||||
|
)
|
||||||
|
.map((v) => {
|
||||||
|
const _props =
|
||||||
|
v.productService.work?.attributes?.workflowStep[
|
||||||
|
pageState.currentStep - 1
|
||||||
|
]?.attributes?.properties;
|
||||||
|
return Object.assign(v, {
|
||||||
|
_documentExpansion: _props.some(
|
||||||
|
(v: PropVariant) => v.fieldName === 'documentCheck',
|
||||||
|
),
|
||||||
|
_formExpansion: _props.some(
|
||||||
|
(v: PropVariant) => v.fieldName === 'designForm',
|
||||||
|
),
|
||||||
|
_dutyExpansion: _props.some((v: PropVariant) => v.fieldName === 'duty'),
|
||||||
|
_messengerExpansion: _props.some(
|
||||||
|
(v: PropVariant) => v.fieldName === 'messenger',
|
||||||
|
),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.sort(
|
||||||
|
(lhs, rhs) =>
|
||||||
|
lhs.productService.installmentNo - rhs.productService.installmentNo,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const pageState = reactive({
|
const pageState = reactive({
|
||||||
hideMetaData: false,
|
hideMetaData: false,
|
||||||
currentStep: 1,
|
currentStep: 1,
|
||||||
|
requestActionDialog: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// NOTE: Function
|
// NOTE: Function
|
||||||
|
|
@ -342,6 +375,31 @@ function closeTab() {
|
||||||
cancel: () => {},
|
cancel: () => {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openRequestAction() {
|
||||||
|
pageState.requestActionDialog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitRequestAction(data: {
|
||||||
|
form: { responsibleUserLocal: boolean; responsibleUserId: string };
|
||||||
|
selected: { _work: RequestWork }[];
|
||||||
|
}) {
|
||||||
|
const requestWorksId = data.selected.map((s) => s._work.id);
|
||||||
|
|
||||||
|
const res = await requestListStore.actionRequestWork(
|
||||||
|
route.params['requestListId'] as string,
|
||||||
|
pageState.currentStep,
|
||||||
|
requestWorksId.map((v) => ({
|
||||||
|
responsibleUserId: data.form.responsibleUserId,
|
||||||
|
responsibleUserLocal: data.form.responsibleUserLocal,
|
||||||
|
requestWorkId: v!,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
if (res) {
|
||||||
|
await getData();
|
||||||
|
pageState.requestActionDialog = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="column surface-0 fullscreen" v-if="data">
|
<div class="column surface-0 fullscreen" v-if="data">
|
||||||
|
|
@ -671,40 +729,7 @@ function closeTab() {
|
||||||
</transition>
|
</transition>
|
||||||
</article>
|
</article>
|
||||||
<!-- product -->
|
<!-- product -->
|
||||||
<template
|
<template v-for="(value, index) in productsList" :key="value">
|
||||||
v-for="(value, index) in workList
|
|
||||||
?.filter((v) =>
|
|
||||||
v.productService.work?.attributes.workflowStep?.[
|
|
||||||
pageState.currentStep - 1
|
|
||||||
]?.productsId.includes(v.productService.productId),
|
|
||||||
)
|
|
||||||
.map((v) => {
|
|
||||||
const _props =
|
|
||||||
v.productService.work?.attributes?.workflowStep[
|
|
||||||
pageState.currentStep - 1
|
|
||||||
]?.attributes?.properties;
|
|
||||||
return Object.assign(v, {
|
|
||||||
_documentExpansion: _props.some(
|
|
||||||
(v: PropVariant) => v.fieldName === 'documentCheck',
|
|
||||||
),
|
|
||||||
_formExpansion: _props.some(
|
|
||||||
(v: PropVariant) => v.fieldName === 'designForm',
|
|
||||||
),
|
|
||||||
_dutyExpansion: _props.some(
|
|
||||||
(v: PropVariant) => v.fieldName === 'duty',
|
|
||||||
),
|
|
||||||
_messengerExpansion: _props.some(
|
|
||||||
(v: PropVariant) => v.fieldName === 'messenger',
|
|
||||||
),
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.sort(
|
|
||||||
(lhs, rhs) =>
|
|
||||||
lhs.productService.installmentNo -
|
|
||||||
rhs.productService.installmentNo,
|
|
||||||
)"
|
|
||||||
:key="value"
|
|
||||||
>
|
|
||||||
<ProductExpansion
|
<ProductExpansion
|
||||||
:cancel="data.requestDataStatus === RequestDataStatus.Canceled"
|
:cancel="data.requestDataStatus === RequestDataStatus.Canceled"
|
||||||
:readonly="
|
:readonly="
|
||||||
|
|
@ -873,13 +898,34 @@ function closeTab() {
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- SEC: Footer -->
|
<!-- SEC: Footer -->
|
||||||
<footer class="surface-1 q-pa-md full-width text-right">
|
<footer
|
||||||
|
class="surface-1 q-pa-md full-width row justify-end"
|
||||||
|
style="gap: var(--size-2)"
|
||||||
|
>
|
||||||
<CancelButton
|
<CancelButton
|
||||||
|
outlined
|
||||||
@click="closeTab()"
|
@click="closeTab()"
|
||||||
:label="$t('dialog.action.close')"
|
:label="$t('dialog.action.close')"
|
||||||
outlined
|
|
||||||
/>
|
/>
|
||||||
|
<MainButton
|
||||||
|
v-if="productsList.some((v) => v._messengerExpansion)"
|
||||||
|
solid
|
||||||
|
icon="mdi-account-outline"
|
||||||
|
color="207 96% 32%"
|
||||||
|
@click="openRequestAction"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
$t('general.manage', { text: $t('requestList.employeeMessenger') })
|
||||||
|
}}
|
||||||
|
</MainButton>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<RequestAction
|
||||||
|
v-model="pageState.requestActionDialog"
|
||||||
|
:work="workList"
|
||||||
|
:responsible-district-id="data?.quotation.customerBranch.districtId || ''"
|
||||||
|
@submit="submitRequestAction"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,8 @@ function handleCheckAll() {
|
||||||
validate
|
validate
|
||||||
? status === TaskStatus.Success ||
|
? status === TaskStatus.Success ||
|
||||||
status === TaskStatus.Complete ||
|
status === TaskStatus.Complete ||
|
||||||
status === TaskStatus.Redo
|
status === TaskStatus.Redo ||
|
||||||
|
status === TaskStatus.Restart
|
||||||
: status === TaskStatus.Failed ||
|
: status === TaskStatus.Failed ||
|
||||||
status === TaskStatus.Success ||
|
status === TaskStatus.Success ||
|
||||||
status === TaskStatus.Complete ||
|
status === TaskStatus.Complete ||
|
||||||
|
|
@ -196,6 +197,7 @@ function handleCheck(
|
||||||
row.taskStatus === TaskStatus.Success ||
|
row.taskStatus === TaskStatus.Success ||
|
||||||
row.taskStatus === TaskStatus.Complete ||
|
row.taskStatus === TaskStatus.Complete ||
|
||||||
row.taskStatus === TaskStatus.Redo ||
|
row.taskStatus === TaskStatus.Redo ||
|
||||||
|
row.taskStatus === TaskStatus.Restart ||
|
||||||
(selectedEmployee.value.length > 0 &&
|
(selectedEmployee.value.length > 0 &&
|
||||||
selectedEmployee.value.some((v) => v._template?.id !== row._template?.id))
|
selectedEmployee.value.some((v) => v._template?.id !== row._template?.id))
|
||||||
) {
|
) {
|
||||||
|
|
@ -311,7 +313,7 @@ function disableCheckAll() {
|
||||||
class=""
|
class=""
|
||||||
>
|
>
|
||||||
<q-menu :offset="[0, 4]">
|
<q-menu :offset="[0, 4]">
|
||||||
<q-list v-if="validate">
|
<q-list v-if="validate" dense>
|
||||||
<q-item
|
<q-item
|
||||||
v-if="
|
v-if="
|
||||||
!selectedEmployee.some(
|
!selectedEmployee.some(
|
||||||
|
|
@ -355,6 +357,25 @@ function disableCheckAll() {
|
||||||
></q-icon>
|
></q-icon>
|
||||||
{{ $t(`taskOrder.status.Redo`) }}
|
{{ $t(`taskOrder.status.Redo`) }}
|
||||||
</q-item>
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
class="items-center"
|
||||||
|
@click="
|
||||||
|
$emit('changeAllStatus', {
|
||||||
|
data: selectedEmployee,
|
||||||
|
status: TaskStatus.Restart,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
style="color: hsl(var(--negative-bg))"
|
||||||
|
name="mdi-file-remove-outline"
|
||||||
|
class="q-pr-sm"
|
||||||
|
size="xs"
|
||||||
|
></q-icon>
|
||||||
|
{{ $t(`taskOrder.status.Restart`) }}
|
||||||
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
<q-list v-if="!validate" dense>
|
<q-list v-if="!validate" dense>
|
||||||
<q-item
|
<q-item
|
||||||
|
|
@ -445,6 +466,7 @@ function disableCheckAll() {
|
||||||
props.row.taskStatus === TaskStatus.Success ||
|
props.row.taskStatus === TaskStatus.Success ||
|
||||||
props.row.taskStatus === TaskStatus.Complete ||
|
props.row.taskStatus === TaskStatus.Complete ||
|
||||||
props.row.taskStatus === TaskStatus.Redo ||
|
props.row.taskStatus === TaskStatus.Redo ||
|
||||||
|
props.row.taskStatus === TaskStatus.Restart ||
|
||||||
(selectedEmployee.length > 0 &&
|
(selectedEmployee.length > 0 &&
|
||||||
selectedEmployee.some(
|
selectedEmployee.some(
|
||||||
(v) => v._template?.id !== props.row._template?.id,
|
(v) => v._template?.id !== props.row._template?.id,
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ function inactiveCheck() {
|
||||||
currStatus.value?.value === TaskStatus.Redo ||
|
currStatus.value?.value === TaskStatus.Redo ||
|
||||||
currStatus.value?.value === TaskStatus.Success ||
|
currStatus.value?.value === TaskStatus.Success ||
|
||||||
currStatus.value?.value === TaskStatus.Complete ||
|
currStatus.value?.value === TaskStatus.Complete ||
|
||||||
currStatus.value?.value === TaskStatus.Canceled
|
currStatus.value?.value === TaskStatus.Canceled ||
|
||||||
|
currStatus.value?.value === TaskStatus.Restart
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,7 @@ const messengerListGroup = computed(() =>
|
||||||
});
|
});
|
||||||
|
|
||||||
if (indexUser === -1) {
|
if (indexUser === -1) {
|
||||||
|
// If user does not exist in acc, create a new entry
|
||||||
acc.push({
|
acc.push({
|
||||||
responsibleUser: task.responsibleUser,
|
responsibleUser: task.responsibleUser,
|
||||||
list: [
|
list: [
|
||||||
|
|
@ -236,27 +237,38 @@ const messengerListGroup = computed(() =>
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
if (selectedEmployee.value.length < acc.length) {
|
} else {
|
||||||
selectedEmployee.value.push([[]]);
|
const userEntry = acc[indexUser];
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexUser !== -1) {
|
// Find product in user's list
|
||||||
const indexProduct = acc[indexUser].list.findIndex(
|
const indexProduct = userEntry.list.findIndex(
|
||||||
(v) => v.product.id === task.requestWork.productService.product.id,
|
(v) => v.product.id === task.requestWork.productService.product.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (indexProduct === -1) {
|
if (indexProduct === -1) {
|
||||||
acc[indexUser].list.push({
|
// If product does not exist in user's list, add it
|
||||||
|
userEntry.list.push({
|
||||||
product: task.requestWork.productService.product,
|
product: task.requestWork.productService.product,
|
||||||
list: [record],
|
list: [record],
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// Append task to the correct product's list
|
||||||
|
userEntry.list[indexProduct].list.push(record);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indexProduct !== -1) {
|
// Ensure `selectedEmployee.value` grows dynamically
|
||||||
acc[indexUser].list[indexProduct].list.push(record);
|
while (selectedEmployee.value.length < acc.length) {
|
||||||
|
selectedEmployee.value.push([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
acc.forEach((accItem, index) => {
|
||||||
|
const length = accItem.list.length;
|
||||||
|
|
||||||
|
while (selectedEmployee.value[index].length < length) {
|
||||||
|
selectedEmployee.value[index].push([]);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
|
|
@ -1106,15 +1118,21 @@ watch(
|
||||||
step-on
|
step-on
|
||||||
:checkbox-on="
|
:checkbox-on="
|
||||||
view === TaskOrderStatus.Validate &&
|
view === TaskOrderStatus.Validate &&
|
||||||
|
(fullTaskOrder?.userTask.find(
|
||||||
|
(l) => l.userId === v.responsibleUser.id,
|
||||||
|
)?.userTaskStatus === UserTaskStatus.Submit ||
|
||||||
fullTaskOrder?.userTask.find(
|
fullTaskOrder?.userTask.find(
|
||||||
(l) => l.userId === v.responsibleUser.id,
|
(l) => l.userId === v.responsibleUser.id,
|
||||||
)?.userTaskStatus === UserTaskStatus.Submit
|
)?.userTaskStatus === UserTaskStatus.Restart)
|
||||||
"
|
"
|
||||||
:check-all="
|
:check-all="
|
||||||
view === TaskOrderStatus.Validate &&
|
(view === TaskOrderStatus.Validate &&
|
||||||
fullTaskOrder?.userTask.find(
|
fullTaskOrder?.userTask.find(
|
||||||
(l) => l.userId === v.responsibleUser.id,
|
(l) => l.userId === v.responsibleUser.id,
|
||||||
)?.userTaskStatus === UserTaskStatus.Submit
|
)?.userTaskStatus === UserTaskStatus.Submit) ||
|
||||||
|
fullTaskOrder?.userTask.find(
|
||||||
|
(l) => l.userId === v.responsibleUser.id,
|
||||||
|
)?.userTaskStatus === UserTaskStatus.Restart
|
||||||
"
|
"
|
||||||
:rows="sortList(list)"
|
:rows="sortList(list)"
|
||||||
@change-all-status="
|
@change-all-status="
|
||||||
|
|
|
||||||
|
|
@ -853,6 +853,21 @@ onMounted(async () => {
|
||||||
:label="$t('dialog.action.close')"
|
:label="$t('dialog.action.close')"
|
||||||
outlined
|
outlined
|
||||||
/>
|
/>
|
||||||
|
<SaveButton
|
||||||
|
v-if="pageState.mode === 'edit'"
|
||||||
|
:disabled="taskListGroup.length === 0"
|
||||||
|
@click="(e) => refForm?.submit(e)"
|
||||||
|
solid
|
||||||
|
/>
|
||||||
|
<EditButton
|
||||||
|
v-if="
|
||||||
|
pageState.mode === 'info' &&
|
||||||
|
creditNoteData?.creditNoteStatus === CreditNoteStatus.Waiting
|
||||||
|
"
|
||||||
|
class="no-print"
|
||||||
|
@click="pageState.mode = 'edit'"
|
||||||
|
solid
|
||||||
|
/>
|
||||||
<SaveButton
|
<SaveButton
|
||||||
v-if="
|
v-if="
|
||||||
!creditNoteData ||
|
!creditNoteData ||
|
||||||
|
|
@ -869,21 +884,6 @@ onMounted(async () => {
|
||||||
icon="mdi-account-multiple-check-outline"
|
icon="mdi-account-multiple-check-outline"
|
||||||
solid
|
solid
|
||||||
/>
|
/>
|
||||||
<SaveButton
|
|
||||||
v-if="pageState.mode === 'edit'"
|
|
||||||
:disabled="taskListGroup.length === 0"
|
|
||||||
@click="(e) => refForm?.submit(e)"
|
|
||||||
solid
|
|
||||||
/>
|
|
||||||
<EditButton
|
|
||||||
v-if="
|
|
||||||
pageState.mode === 'info' &&
|
|
||||||
creditNoteData?.creditNoteStatus === CreditNoteStatus.Waiting
|
|
||||||
"
|
|
||||||
class="no-print"
|
|
||||||
@click="pageState.mode = 'edit'"
|
|
||||||
solid
|
|
||||||
/>
|
|
||||||
</nav>
|
</nav>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1296,7 +1296,10 @@ async function submitAccepted() {
|
||||||
id="btn-close"
|
id="btn-close"
|
||||||
@click="closeTab()"
|
@click="closeTab()"
|
||||||
:label="$t('dialog.action.close')"
|
:label="$t('dialog.action.close')"
|
||||||
v-if="pageState.mode === 'info' && closeAble()"
|
v-if="
|
||||||
|
(pageState.mode === 'info' || pageState.mode === 'create') &&
|
||||||
|
closeAble()
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="row q-gutter-x-sm q-ml-xs">
|
<div class="row q-gutter-x-sm q-ml-xs">
|
||||||
|
|
|
||||||
|
|
@ -276,6 +276,24 @@ export const useRequestList = defineStore('request-list', () => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function actionRequestWork(
|
||||||
|
requestDataId: string,
|
||||||
|
step: number,
|
||||||
|
body: (Omit<Step, 'step'> & {
|
||||||
|
requestWorkId: string;
|
||||||
|
})[],
|
||||||
|
successAll?: boolean,
|
||||||
|
) {
|
||||||
|
const res = await api.put<Step[]>(
|
||||||
|
`/request-data/${requestDataId}/request-work/step-status/${step}`,
|
||||||
|
body,
|
||||||
|
{ params: { successAll } },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.status < 400) return res.data;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
page,
|
page,
|
||||||
|
|
@ -296,6 +314,8 @@ export const useRequestList = defineStore('request-list', () => {
|
||||||
editStatusRequestWork,
|
editStatusRequestWork,
|
||||||
|
|
||||||
cancelRequest,
|
cancelRequest,
|
||||||
|
|
||||||
|
actionRequestWork,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue