Merge branch 'ui-new' into develop

This commit is contained in:
Methapon2001 2024-07-15 09:17:40 +07:00
commit b9d54d5e01
53 changed files with 6634 additions and 2322 deletions

View file

@ -1,6 +1,6 @@
import { boot } from 'quasar/wrappers';
import VueDatePicker from '@vuepic/vue-datepicker';
import '@vuepic/vue-datepicker/dist/main.css'
import '@vuepic/vue-datepicker/dist/main.css';
import GlobalDialog from 'components/GlobalDialog.vue';
import GlobalLoading from 'components/GlobalLoading.vue';

View file

@ -2,10 +2,21 @@
defineProps<{
inactive?: boolean;
color?: 'none' | 'hq' | 'br';
data: Record<string, unknown>;
data: {
branchLabelCode: string;
branchLabelName: string;
branchLabelTel: string;
branchLabelAddress: string;
branchLabelType: string;
};
metadata?: unknown;
badgeField?: string[];
fieldSelected?: string[];
fieldSelected?: (
| 'branchLabelName'
| 'branchLabelAddress'
| 'branchLabelTel'
| 'branchLabelType'
)[];
footer?: boolean;
}>();
</script>
@ -21,112 +32,124 @@ defineProps<{
'branch-card__hq': color === 'hq',
'branch-card__br': color === 'br',
}"
@click="$emit('open')"
>
<div class="branch-card__header">
<div class="branch-card__wrapper">
<div class="branch-card__icon">
<q-icon
size="md"
style="scale: 0.8"
name="mdi-office-building-outline"
/>
</div>
</div>
<div class="branch-card__name">
<b>{{ data.branchLabelName }}</b>
<small class="branch-card__code">{{ data.branchLabelCode }}</small>
</div>
<div class="branch-card__action">
<q-btn icon="mdi-dots-vertical" size="sm" dense round flat @click.stop>
<slot name="action" />
</q-btn>
</div>
</div>
<div
class="branch-card__row"
:class="{
'branch-card__header': i === 0,
'branch-card__footer': footer && i === Object.keys(data).length - 1,
}"
v-for="([k, v], i) in Object.entries(data).filter(
([key], idx) =>
idx === 0 || (fieldSelected ? fieldSelected.includes(key) : true),
)"
:key="k"
style="
display: block;
width: 100%;
height: 1px;
background: hsla(0 0% 0% / 0.1);
margin-bottom: var(--size-2);
"
/>
<div
v-for="key in fieldSelected?.sort() || [
'branchLabelAddress',
'branchLabelTel',
'branchLabelType',
]"
class="branch-card__data"
>
<div class="branch-card__label">
<span>{{ $t(k) }}</span>
</div>
<div
class="branch-card__value"
:class="{ 'branch-card__badge': badgeField?.includes(k) }"
style="justify-content: space-between"
>
<span>{{ v }}</span>
<q-btn
:id="`${v}-view-detail`"
@click.stop="$emit('view-detail', metadata ?? data)"
:label="$t('viewDetail')"
rounded
outline
dense
style="text-wrap: nowrap; max-width: 80%"
no-caps
class="branch-card__view-detail q-px-md text-caption"
v-if="i === 0"
/>
</div>
<div>{{ $t(key) }}</div>
<div>{{ data[key as keyof typeof data] }}</div>
</div>
</div>
</template>
<style scoped>
.branch-card {
--_branch-card-row-fg: 0 0% 100%;
--_branch-card-row-bg: var(--blue-5-hsl);
--_branch-badge-fg: var(--green-8-hsl);
--_branch-badge-bg: var(--green-6-hsl);
--_branch-card-fg: 0 0% 100%;
--_branch-card-bg: var(--blue-5-hsl);
--_branch-status-color: var(--green-6-hsl);
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: var(--shadow-2);
padding: var(--size-3);
& > .branch-card__row {
& .branch-card__header {
display: flex;
margin-bottom: var(--size-2);
& .branch-card__icon {
background-color: hsla(var(--_branch-card-bg) / 0.15);
border-radius: 50%;
padding: var(--size-1);
position: relative;
transform: rotate(45deg);
aspect-ratio: 1;
&::after {
content: ' ';
display: block;
block-size: 0.5rem;
aspect-ratio: 1;
position: absolute;
border-radius: 50%;
right: -0.25rem;
top: calc(50% - 0.25rem);
bottom: calc(50% - 0.25rem);
background-color: hsla(var(--_branch-status-color) / 1);
}
& :deep(.q-icon) {
transform: rotate(-45deg);
color: hsla(var(--_branch-card-bg) / 1);
}
}
& .branch-card__name {
display: flex;
flex-direction: column;
flex: 1;
padding-inline: var(--size-2);
& .branch-card__code {
color: hsla(var(--text-mute) / 1);
}
}
& .branch-card__action {
margin-left: auto;
}
}
& .branch-card__data {
display: flex;
flex-wrap: nowrap;
padding-inline: var(--size-4);
padding-block: var(--size-2);
&:last-child {
flex-grow: 1;
}
&:nth-child(2n + 1) {
background-color: hsla(var(--_branch-card-row-bg) / 0.1);
}
&.branch-card__header,
&.branch-card__footer {
color: hsl(var(--_branch-card-row-fg));
background-color: hsl(var(--_branch-card-row-bg));
&:deep(*) {
font-weight: 600;
}
}
&.branch-card__header > * {
display: flex;
align-items: center;
}
& > .branch-card__label {
min-width: 120px;
width: 30%;
}
&:not(.branch-card__header) .branch-card__label {
color: hsl(var(--stone-6-hsl));
}
& > .branch-card__value {
width: 70%;
&.branch-card__badge > span {
display: inline-block;
border-radius: 999rem;
padding-inline: var(--size-2);
color: hsl(var(--_branch-badge-fg));
background-color: hsla(var(--_branch-badge-bg) / 0.1);
}
& > :first-child {
color: hsla(var(--text-mute) / 1);
width: 0%;
min-width: 40%;
}
}
&.branch-card__none {
--_branch-card-row-bg: var(--gray-3-hsl);
--_branch-card-bg: var(--gray-3-hsl);
&.branch-card__dark {
--_branch-card-row-bg: var(--gray-9-hsl);
--_branch-card-bg: var(--gray-9-hsl);
}
&:not(.branch-card__dark) .branch-card__header {
@ -139,17 +162,22 @@ defineProps<{
}
&.branch-card__hq {
--_branch-card-row-bg: var(--pink-6-hsl);
--_branch-card-bg: var(--pink-6-hsl);
}
&.branch-card__br {
--_branch-card-row-bg: var(--violet-11-hsl);
--_branch-card-bg: var(--violet-11-hsl);
&.branch-card__dark {
--_branch-card-bg: var(--violet-10-hsl);
}
}
&.branch-card__inactive {
--_branch-badge-fg: var(--red-4-hsl);
--_branch-status-color: var(--red-4-hsl);
--_branch-badge-bg: var(--red-4-hsl);
filter: grayscale(40%);
filter: grayscale(1);
background-color: hsl(var(--gray-6-hsl) / 0.1);
opacity: 0.5;
}
}

View file

@ -139,6 +139,11 @@ const branchNo = defineModel<number>('branchNo');
class="col-6"
:label="$t('taxNo')"
v-model="taxNo"
:rules="[
(val) =>
(val && val.length === 13 && /[0-9]+/.test(val)) ||
$t('formDialogInputTaxNoValidate'),
]"
/>
<q-input
for="input-registerName"

View file

@ -1,4 +1,5 @@
<script setup lang="ts">
import { getRole } from 'src/services/keycloak';
import { CustomerBranch } from 'src/stores/customer/types';
import { onMounted, ref } from 'vue';
@ -23,7 +24,7 @@ const customerBranch = defineModel<{
const employeeId = defineModel<string>('employeeId');
const nrcNo = defineModel<string>('nrcNo');
defineProps<{
const props = defineProps<{
dense?: boolean;
outlined?: boolean;
readonly?: boolean;
@ -38,8 +39,6 @@ defineProps<{
defineEmits<{
(e: 'filterOwnerBranch', val: string, update: void): void;
}>();
onMounted(async () => {});
</script>
<template>
@ -63,7 +62,16 @@ onMounted(async () => {});
option-value="id"
v-model="registeredBranchId"
:options="optionsBranch"
:rules="[(val) => !!val]"
:rules="[
(val) => {
const roles = getRole() || [];
const isSpecialRole = ['admin', 'system', 'head_of_admin'].some(
(role) => roles.includes(role),
);
return isSpecialRole || !!val || 'กรุณากรอกข้อมูล';
},
]"
clearable
/>
<q-input
@ -89,6 +97,11 @@ onMounted(async () => {});
class="col-6"
:label="$t('taxNo')"
v-model="taxNo"
:rules="[
(val) =>
(val && val.length === 13 && /[0-9]+/.test(val)) ||
$t('formDialogInputTaxNoValidate'),
]"
/>
<q-input

View file

@ -1,30 +1,27 @@
<script setup lang="ts">
import AppBox from 'components/app/AppBox.vue';
import { CustomerBranch } from 'src/stores/customer/types';
withDefaults(
defineProps<{
inactive?: boolean;
color?: 'none' | 'CORP' | 'PERS';
metadata?: unknown;
badgeField?: string[];
fieldSelected?: string[];
footer?: boolean;
data: {
customerBranchFormTab: number;
branchName: string;
address: string;
telephone: string;
businessTypePure: string;
status: string;
totalEmployee: number;
};
}>(),
{},
);
<script lang="ts" setup>
defineProps<{
inactive?: boolean;
color?: 'none' | 'pers' | 'corp';
data: {
customerBranchFormTab: number;
branchName: string;
address: string;
telephone: string;
businessTypePure: string;
totalEmployee: number;
};
metadata?: unknown;
badgeField?: string[];
fieldSelected?: (
| 'customerBranchFormTab'
| 'branchName'
| 'address'
| 'telephone'
| 'businessTypePure'
| 'totalEmployee'
)[];
footer?: boolean;
}>();
</script>
<template>
@ -34,114 +31,135 @@ withDefaults(
'branch-card__dark': $q.dark.isActive,
'branch-card__inactive': inactive,
'branch-card__none':
color !== 'CORP' && color !== 'PERS' && (!color || color === 'none'),
'branch-card__CORP': color === 'CORP',
'branch-card__PERS': color === 'PERS',
color !== 'pers' && color !== 'corp' && (!color || color === 'none'),
'branch-card__pers': color === 'pers',
'branch-card__corp': color === 'corp',
}"
@click="$emit('open')"
>
<div
class="branch-card__row"
:class="{
'branch-card__header': i === 0,
'branch-card__footer': footer && i === Object.keys(data).length - 1,
}"
v-for="([k, v], i) in Object.entries(data).filter(
([key], idx) =>
idx === 0 || (fieldSelected ? fieldSelected.includes(key) : true),
)"
:key="k"
>
<div class="branch-card__label">
<span>{{ $t(k) }}</span>
</div>
<div
class="branch-card__value"
:class="{ 'branch-card__badge': badgeField?.includes(k) }"
style="justify-content: space-between"
>
<span>{{ v }}</span>
<q-btn
@click.stop="$emit('view-detail', metadata ?? data)"
:label="$t('viewDetail')"
rounded
outline
dense
no-caps
class="branch-card__view-detail q-px-md text-caption"
v-if="i === 0"
<div class="branch-card__header">
<div class="branch-card__icon">
<q-icon
size="md"
style="scale: 0.8"
name="mdi-office-building-outline"
/>
</div>
<div class="branch-card__name">
<b>{{ data.branchName }}</b>
<small class="branch-card__code">{{ data.branchName }}</small>
</div>
<div class="branch-card__action">
<q-btn
icon="mdi-eye-outline"
size="sm"
dense
round
flat
@click.stop="$emit('viewDetail')"
>
<slot name="action" />
</q-btn>
</div>
</div>
<div
style="
display: block;
width: 100%;
height: 1px;
background: hsla(0 0% 0% / 0.1);
margin-bottom: var(--size-2);
"
/>
<div
v-for="key in fieldSelected?.sort() || [
'customerBranchFormTab',
'branchName',
'address',
'telephone',
'businessTypePure',
'totalEmployee',
]"
class="branch-card__data"
>
<div>{{ $t(key) }}</div>
<div>{{ data[key as keyof typeof data] }}</div>
</div>
</div>
</template>
<style scoped>
.branch-card {
--_branch-card-row-fg: 0 0% 100%;
--_branch-card-row-bg: var(--blue-5-hsl);
--_branch-badge-fg: var(--green-8-hsl);
--_branch-badge-bg: var(--green-6-hsl);
--_branch-card-fg: 0 0% 100%;
--_branch-card-bg: var(--blue-5-hsl);
--_branch-status-color: var(--green-6-hsl);
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: var(--shadow-2);
padding: var(--size-3);
& > .branch-card__row {
& .branch-card__header {
display: flex;
margin-bottom: var(--size-2);
& .branch-card__icon {
background-color: hsla(var(--_branch-card-bg) / 0.15);
border-radius: 50%;
padding: var(--size-1);
position: relative;
transform: rotate(45deg);
&::after {
content: ' ';
display: block;
block-size: 0.5rem;
aspect-ratio: 1;
position: absolute;
border-radius: 50%;
right: -0.25rem;
top: calc(50% - 0.25rem);
bottom: calc(50% - 0.25rem);
background-color: hsla(var(--_branch-status-color) / 1);
}
& :deep(.q-icon) {
transform: rotate(-45deg);
color: hsla(var(--_branch-card-bg) / 1);
}
}
& .branch-card__name {
display: flex;
flex-direction: column;
flex: 1;
padding-inline: var(--size-2);
& .branch-card__code {
color: hsla(var(--text-mute) / 1);
}
}
& .branch-card__action {
margin-left: auto;
}
}
& .branch-card__data {
display: flex;
flex-wrap: nowrap;
padding-inline: var(--size-4);
padding-block: var(--size-2);
&:last-child {
flex-grow: 1;
}
&:nth-child(2n + 1) {
background-color: hsla(var(--_branch-card-row-bg) / 0.05);
}
&.branch-card__header,
&.branch-card__footer {
color: hsl(var(--_branch-card-row-fg));
background-color: hsl(var(--_branch-card-row-bg));
&:deep(*) {
font-weight: 600;
}
}
&.branch-card__header > * {
display: flex;
align-items: center;
}
& > .branch-card__label {
min-width: 120px;
width: 30%;
}
&:not(.branch-card__header) .branch-card__label {
color: hsl(var(--stone-6-hsl));
}
& > .branch-card__value {
width: 70%;
&.branch-card__badge > span {
display: inline-block;
border-radius: 999rem;
padding-inline: var(--size-2);
color: hsl(var(--_branch-badge-fg));
background-color: hsla(var(--_branch-badge-bg) / 0.1);
}
& > :first-child {
color: hsla(var(--text-mute) / 1);
width: 0%;
min-width: 40%;
}
}
&.branch-card__none {
--_branch-card-row-bg: var(--gray-3-hsl);
--_branch-card-bg: var(--gray-3-hsl);
&.branch-card__dark {
--_branch-card-row-bg: var(--gray-9-hsl);
--_branch-card-bg: var(--gray-9-hsl);
}
&:not(.branch-card__dark) .branch-card__header {
@ -153,18 +171,23 @@ withDefaults(
}
}
&.branch-card__CORP {
--_branch-card-row-bg: var(--purple-11-hsl);
&.branch-card__pers {
--_branch-card-bg: var(--pink-6-hsl);
}
&.branch-card__PERS {
--_branch-card-row-bg: var(--teal-9-hsl);
&.branch-card__corp {
--_branch-card-bg: var(--violet-11-hsl);
&.branch-card__dark {
--_branch-card-bg: var(--violet-10-hsl);
}
}
&.branch-card__inactive {
--_branch-badge-fg: var(--red-4-hsl);
--_branch-status-color: var(--red-4-hsl);
--_branch-badge-bg: var(--red-4-hsl);
filter: grayscale(40%);
filter: grayscale(1);
background-color: hsl(var(--gray-6-hsl) / 0.1);
opacity: 0.5;
}
}

View file

@ -3,6 +3,7 @@ import { ref, onMounted, watch } from 'vue';
import useCustomerStore from 'src/stores/customer';
import useFlowStore from 'src/stores/flow';
import useOptionStore from 'src/stores/options';
import { Status } from 'src/stores/types';
import { CustomerBranch, CustomerType } from 'src/stores/customer/types';
@ -45,9 +46,9 @@ const prop = withDefaults(
},
);
const emit = defineEmits<{
defineEmits<{
(e: 'back'): void;
(e: 'viewDetail', branch: CustomerBranch[]): void;
(e: 'viewDetail', branch: CustomerBranch, index: number): void;
(e: 'dialog'): void;
}>();
@ -185,31 +186,29 @@ watch(currentStatus, async () => {
</div>
</div>
<div
class="row q-pa-lg q-col-gutter-xl"
class="row q-pa-md q-col-gutter-md"
:class="{ 'justify-center': totalBranch === 0 }"
style="min-height: 350px"
>
<NoData v-if="totalBranch === 0" :not-found="!!inputSearch" />
<div
v-for="(br, i) in branch?.sort((a, b) => a.branchNo - b.branchNo)"
:key="i"
class="col-4"
>
<div v-for="(br, i) in branch" :key="i" class="col-4">
<BranchCardCustomer
:inactive="br.status === 'INACTIVE'"
:color="customerType"
:color="customerType === 'CORP' ? 'corp' : 'pers'"
:badgeField="['status']"
:data="{
customerBranchFormTab: br.branchNo,
branchName: br.name,
address: br.address,
address:
$i18n.locale === 'en-US'
? `${br.addressEN || ''} ${br.subDistrict?.nameEN || ''} ${br.district?.nameEN || ''} ${br.province?.nameEN || ''}`
: `${br.address || ''} ${br.subDistrict?.name || ''} ${br.district?.name || ''} ${br.province?.name || ''}`,
telephone: br.telephoneNo,
businessTypePure: br.bussinessType,
status: 'ดำเนินการอยู่',
businessTypePure: useOptionStore().mapOption(br.bussinessType),
totalEmployee: br._count?.employee,
}"
@view-detail="emit('viewDetail', [br])"
@view-detail="$emit('viewDetail', br, i)"
/>
</div>
</div>

View file

@ -68,7 +68,7 @@ onMounted(async () => {
<q-select
id="select-business-type"
emit-value
option-value="label"
option-value="value"
option-label="label"
map-options
:dense="dense"
@ -95,7 +95,7 @@ onMounted(async () => {
<q-select
id="select-job-position"
emit-value
option-value="label"
option-value="value"
option-label="label"
map-options
:dense="dense"

View file

@ -73,10 +73,7 @@ defineProps<{
class="col-6"
:label="$t('formDialogInputPassportRef')"
v-model="previousPassportReference"
:rules="[
(val: string) =>
!!val || $t('inputValidate') + $t('formDialogInputPassportRef'),
]"
/>
<q-input
for="input-passport-place"

View file

@ -1,4 +1,5 @@
<script setup lang="ts">
import { getRole } from 'src/services/keycloak';
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
const { locale } = useI18n({ useScope: 'global' });
@ -10,9 +11,9 @@ const name = defineModel<string>('name');
const code = defineModel<string>('code');
const registeredBranchId = defineModel<string>('registeredBranchId');
const codeOption = ref([]);
const codeOption = ref<{ id: string; name: string }[]>([]);
defineProps<{
const props = defineProps<{
dense?: boolean;
outlined?: boolean;
readonly?: boolean;
@ -31,6 +32,15 @@ onMounted(async () => {
if (locale.value === 'th-th') {
codeOption.value = option.tha.typeProduct;
}
if(!!props.optionsBranch) {
registeredBranchId.value = props.optionsBranch[0].id ;
}
});
</script>
@ -71,18 +81,25 @@ onMounted(async () => {
map-options
options-dense
:label="$t('registeredBranch')"
class="col-3"
class="col-3 "
option-label="name"
option-value="id"
v-model="registeredBranchId"
:options="optionsBranch"
:rules="[(val) => !!val]"
:rules="[
(val) => {
const roles = getRole() || [];
const isSpecialRole = ['admin', 'system', 'head_of_admin'].some(role => roles.includes(role));
return isSpecialRole || !!val || 'กรุณากรอกข้อมูล';
}
]"
clearable
/>
<q-input
for="input-name"
:dense="dense"
:outlined="!readonly"
outlined
:readonly="readonly"
:borderless="readonly"
hide-bottom-space
@ -94,7 +111,7 @@ onMounted(async () => {
<q-input
for="input-detail"
:dense="dense"
:outlined="!readonly"
outlined
:readonly="readonly"
:borderless="readonly"
hide-bottom-space
@ -108,7 +125,7 @@ onMounted(async () => {
<div class="col-12">
<q-field
class="full-width"
:outlined="!readonly"
outlined
:readonly="readonly"
:borderless="readonly"
:label="$t('detail')"
@ -138,12 +155,12 @@ onMounted(async () => {
<q-input
for="input-process"
:dense="dense"
:outlined="!readonly"
outlined
:readonly="readonly"
:borderless="readonly"
hide-bottom-space
class="col-4"
:label="$t('processingTime')"
:label="$t('productProcessingTime')"
v-model="process"
type="number"
/>

View file

@ -1,5 +1,7 @@
<script setup lang="ts">
import { getRole } from 'src/services/keycloak';
import useOptionStore from 'src/stores/options';
import { onMounted } from 'vue';
const optionStore = useOptionStore();
@ -13,7 +15,7 @@ const serviceName = defineModel<string>('serviceNameTh');
const serviceDescription = defineModel<string>('serviceDescription');
const registeredBranchId = defineModel<string | null>('registeredBranchId');
defineProps<{
const props = defineProps<{
dense?: boolean;
outlined?: boolean;
readonly?: boolean;
@ -77,6 +79,7 @@ defineProps<{
<q-select
id="input-source-nationality"
:dense="dense"
outlined
:readonly="readonly"
@ -86,12 +89,19 @@ defineProps<{
map-options
options-dense
:label="$t('registeredBranch')"
class="col-3"
class="col-3 "
option-label="name"
option-value="id"
v-model="registeredBranchId"
:options="optionsBranch"
:rules="[(val) => !!val]"
:rules="[
(val) => {
const roles = getRole() || [];
const isSpecialRole = ['admin', 'system', 'head_of_admin'].some(role => roles.includes(role));
return isSpecialRole || !!val || 'กรุณากรอกข้อมูล';
}
]"
clearable
/>
<q-input

View file

@ -13,37 +13,40 @@ defineProps<{
</script>
<template>
<div class="col-3 app-text-muted"> {{ $t('priceeInformation') }}</div>
<div class="col-3 app-text-muted"> {{ $t('priceInformation') }}</div>
<div class="col-9 row q-col-gutter-md">
<q-input
:dense="dense"
:outlined="!readonly"
outlined
:readonly="readonly"
:borderless="readonly"
hide-bottom-space
class="col-4"
type="number"
:label="$t('salePrice')"
v-model="price"
/>
<q-input
:dense="dense"
:outlined="!readonly"
outlined
:readonly="readonly"
:borderless="readonly"
hide-bottom-space
class="col-4"
type="number"
:label="$t('agentPrice')"
v-model="agentPrice"
/>
<q-input
:dense="dense"
:outlined="!readonly"
outlined
:readonly="readonly"
:borderless="readonly"
hide-bottom-space
class="col-4"
type="number"
:label="$t('processingPrice')"
v-model="serviceCharge"
/>

View file

@ -37,7 +37,7 @@ withDefaults(
>
<div
class="relative-position rounded q-pb-lg"
:style="`background-color:hsla(${color}/0.1)`"
:style="`background-color:hsla(${color}/.2)`"
>
<div class="row justify-between items-center">
<q-avatar

View file

@ -42,8 +42,8 @@ withDefaults(
<div
bordered
:class="{ 'is-add-product': isAddProduct }"
class="column bordered rounded q-pa-sm no-wrap"
style="box-shadow: var(--shadow-3); height: 20rem"
class="column bordered rounded q-pa-sm no-wrap surface-1"
style="height: 20rem"
@click="$emit('select', data)"
>
<div class="row flex justify-between text-bold">

View file

@ -9,6 +9,7 @@ defineProps<{
<div class="row">
<q-btn
unelevated
id="btn-Add"
@click="$emit('trigger')"
size="lg"
class="color-btn"

View file

@ -1,7 +1,7 @@
<script setup lang="ts"></script>
<template>
<q-page-sticky position="bottom-right" :offset="[30, 18]">
<q-page-sticky position="bottom-right" :offset="[8, 8]">
<q-fab
id="btn-add"
padding="xs"
@ -9,9 +9,9 @@
direction="up"
color="primary"
>
<slot />
<!-- <q-fab-action padding="xs" color="primary" icon="mdi-account-plus" /> -->
<slot>
<q-fab-action padding="xs" color="primary" icon="mdi-account-plus" />
</slot>
</q-fab>
</q-page-sticky>
</template>

View file

@ -1,9 +1,32 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { computed, ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { storeToRefs } from 'pinia';
import { Icon } from '@iconify/vue';
import useMyBranch from 'stores/my-branch';
import { getUserId, getRole } from 'src/services/keycloak';
defineProps<{
mini?: boolean;
}>();
onMounted(async () => {
const uid = getUserId();
const role = getRole();
if (!uid) return;
if (role?.includes('system')) {
const result = await userBranch.fetchListOptionBranch();
if (result && result.total > 0) currentMyBranch.value = result.result[0];
}
const result = await userBranch.fetchListMyBranch(uid);
if (result && result.total > 0) currentMyBranch.value = result.result[0];
});
const router = useRouter();
const userBranch = useMyBranch();
const { currentMyBranch } = storeToRefs(userBranch);
const currentRoute = ref<string>('');
const currentPath = computed(() => {
@ -79,55 +102,119 @@ const labelMenu = ref<
]);
const leftDrawerOpen = defineModel<boolean>('leftDrawerOpen', {
default: false,
default: true,
});
function navigateTo(label: string, destination: string) {
currentRoute.value = label;
router.push(`${destination}`);
}
function branchSetting() {}
</script>
<template>
<!-- Drawer -->
<q-drawer
no-swipe-open
v-model="leftDrawerOpen"
side="left"
:breakpoint="599"
:width="$q.screen.lt.md ? 200 : 300"
class="column justify-between no-wrap"
:width="mini ? 80 : 256"
show-if-above
>
<div class="main-bar">
<!-- :width="$q.screen.lt.sm ? $q.screen.width - 16 : 256" -->
<div class="scroll" style="overflow-x: hidden; scrollbar-gutter: stable">
<div
class="column items-center justify-center q-pa-xl cursor-pointer"
class="flex justify-center items-center q-pl-sm cursor-pointer"
@click="$router.push('/')"
id="btn-drawer-home"
style="height: 128px"
>
<q-img src="/logo.png" style="width: 70%; height: 70%">
<template #error>
<img src="/logo.png" alt="" width="100%" />
</template>
</q-img>
<q-img
fit="contain"
:src="mini ? 'logo_jws.png' : '/logo.png'"
:style="{ filter: `brightness(${$q.dark.isActive ? '1.3' : '1'})` }"
style="height: 64px"
/>
</div>
<div id="drawer-menu" class="q-pl-md q-mr-xs">
<q-item
v-for="v in labelMenu"
dense
clickable
class="row items-center q-my-xs q-px-xs"
:key="v.label"
:disable="!!v.disabled"
:class="{
active: currentPath === v.route,
'border-active': currentPath === v.route,
dark: $q.dark.isActive,
}"
@click="navigateTo(v.label, v.route)"
>
<div class="row justify-center items-center">
<Icon :icon="v.icon" width="24px" />
<q-tooltip v-if="mini">
{{ $t(v.label) }}
</q-tooltip>
</div>
<div v-if="!mini" class="q-ml-md" style="white-space: nowrap">
{{ $t(v.label) }}
</div>
</q-item>
</div>
</div>
<div id="drawer-menu" class="q-pr-sm">
<q-item
v-for="v in labelMenu"
:key="v.label"
clickable
:disable="!!v.disabled"
@click="navigateTo(v.label, v.route)"
class="no-padding"
:class="{ active: currentPath === v.route, dark: $q.dark.isActive }"
<div
class="surface-2 q-px-md q-py-sm row items-center no-wrap justify-start"
:class="{ 'justify-center': mini }"
style="min-height: 56px; overflow-x: hidden"
>
<q-avatar
text-color="white"
size="md"
:style="`background-color: hsla(var(--violet-${$q.dark.isActive ? '10' : '11'}-hsl)/0.15)`"
class="relative-position"
>
<q-item-section id="btn-drawer-back ">
<q-item-label class="q-pl-lg row items-center">
<div class="box-border-left" />
<Icon :icon="v.icon" width="24px" class="q-mr-md" />
{{ $t(v.label) }}
</q-item-label>
</q-item-section>
</q-item>
<q-icon
name="mdi-home-group"
size="sm"
:style="`color: var(--violet-${$q.dark.isActive ? '10' : '11'})`"
/>
<div class="dot absolute-bottom-right" />
</q-avatar>
<div
v-if="!mini"
class="text-caption column q-ml-sm"
style="white-space: nowrap"
>
<span class="text-weight-bold">
{{
($i18n.locale === 'en-US'
? currentMyBranch?.nameEN
: currentMyBranch?.name) ?? '-'
}}
</span>
<span>
{{ currentMyBranch?.code ?? '-' }}
</span>
</div>
<q-btn
v-if="!mini"
dense
flat
rounded
icon="mdi-cog-outline"
size="sm"
@click="branchSetting"
style="margin-left: auto"
/>
</div>
</q-drawer>
</template>
@ -135,27 +222,47 @@ function navigateTo(label: string, destination: string) {
<style lang="scss" scoped>
#drawer-menu :deep(.q-item) {
color: var(--gray-6);
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
border-radius: var(--radius-2);
}
#drawer-menu :deep(.q-item.active) {
--_drawer-item-background-color: var(--brand-1) !important;
background-color: var(--_drawer-item-background-color);
color: white;
border-left: 10px solid $secondary;
&.dark {
--_drawer-item-background-color: var(--gray-10) !important;
border: 1px solid var(--brand-1);
border-left: 10px solid $secondary;
.box-border-left {
position: absolute;
width: 10px;
background: $secondary;
height: 48.5px;
top: -1px;
left: -10px;
}
}
// border-left: 10px solid $secondary;
}
#drawer-menu :deep(.q-item.active)::before {
display: block;
position: absolute;
content: ' ';
background: var(--brand-2);
border-radius: 99rem;
width: 6px;
left: -0.6rem;
top: 18%;
bottom: 18%;
cursor: context-menu;
}
:deep(.q-drawer) {
transition: 0.1s width ease-in-out;
background-color: transparent;
padding: var(--size-4);
padding-right: 0;
}
:deep(.q-drawer__content) {
border-radius: var(--radius-2);
background: var(--surface-1);
}
.dot {
height: 10px;
width: 10px;
background-color: var(--teal-6);
border-radius: 50%;
display: inline-block;
border: 1.5px solid white;
}
</style>

View file

@ -1,16 +1,22 @@
<script setup lang="ts">
import AppBox from 'components/app/AppBox.vue';
const props = withDefaults(
defineProps<{
branch: {
icon: string;
count: number;
label: string;
color: 'pink' | 'purple' | 'green' | 'orange';
color:
| 'pink'
| 'purple'
| 'green'
| 'orange'
| 'cyan'
| 'yellow'
| 'red'
| 'magenta';
}[];
dark?: boolean;
labelI18n?: boolean;
nowrap?: boolean;
}>(),
{
labelI18n: false,
@ -19,113 +25,101 @@ const props = withDefaults(
</script>
<template>
<div
class="row full-width"
style="gap: var(--size-4)"
:class="{ dark, 'no-wrap': nowrap }"
>
<AppBox
<div class="row no-wrap" style="gap: var(--size-4)" :class="{ dark }">
<div
v-for="v in props.branch"
:key="v.label"
class="bordered stat-card__wave"
class="no-padding stat-card"
:class="{ [`stat-card__${v.color}`]: true }"
style="
padding: 12px 16px;
height: 75px;
min-width: 232px;
box-shadow: var(--shadow-2);
"
:key="v.label"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1440 320"
style="width: 100%"
>
<path
class="box-path"
d="M0,256L40,245.3C80,235,160,213,240,202.7C320,192,400,192,480,170.7C560,149,640,107,720,106.7C800,107,880,149,960,154.7C1040,160,1120,128,1200,96C1280,64,1360,32,1400,16L1440,0L1440,320L1400,320C1360,320,1280,320,1200,320C1120,320,1040,320,960,320C880,320,800,320,720,320C640,320,560,320,480,320C400,320,320,320,240,320C160,320,80,320,40,320L0,320Z"
></path>
</svg>
<div class="stat-card__content row items-center q-pa-sm">
<div class="col-4 text-center">
<q-avatar
size="lg"
style="background-color: hsla(0 0% 100% /0.2)"
text-color="white"
:icon="v.icon"
/>
</div>
<div class="col-6 justify-center column">
<div class="col-6 ellipsis text-bold" style="width: 100%">
{{ labelI18n ? $t(v.label) : v.label }}
<q-tooltip
anchor="top middle"
self="bottom middle"
:offset="[10, 10]"
>
{{ labelI18n ? $t(v.label) : v.label }}
</q-tooltip>
</div>
<div class="stat-card__content">
<div class="text-h5 text-weight-bold">{{ v.count }}</div>
<div class="text-weight-bold text-no-wrap">
{{ labelI18n ? $t(v.label) : v.label }}
<div class="col-6">{{ v.count }}</div>
</div>
</div>
</AppBox>
</div>
</div>
</template>
<style scoped>
.stat-card {
--_color: var(--gray-6);
display: grid;
grid-template-columns: repeat(2, 1fr);
}
@media screen and (min-width: 1024px) {
.stat-card {
display: flex;
}
}
@media screen and (min-width: 1920px) {
.stat-card {
display: flex;
}
}
.stat-card__wave {
position: relative;
display: flex;
& svg {
width: 200px;
position: absolute;
left: 0px;
bottom: 0;
opacity: 0.1;
& .box-path {
fill: var(--_color);
fill-opacity: 1;
}
}
border-radius: var(--radius-2);
overflow: hidden;
box-shadow: var(--shadow-2);
}
.stat-card__content {
z-index: 2;
color: var(--_color);
color: hsla(var(--gray-0-hsl) / 1);
background: hsla(var(--_color) / 1);
height: 100%;
width: 200px;
min-width: 200px;
}
.stat-card__purple {
--_color: var(--violet-11);
--_color: var(--violet-11-hsl);
}
.stat-card__pink {
--_color: var(--pink-6);
--_color: var(--pink-6-hsl);
}
.stat-card__green {
--_color: var(--teal-10);
--_color: var(--teal-10-hsl);
}
.stat-card__cyan {
--_color: var(--cyan-7-hsl);
}
.stat-card__red {
--_color: var(--red-6-hsl);
}
.stat-card__yellow {
--_color: var(--orange-4-hsl);
}
.stat-card__orange {
--_color: var(--orange-5);
--_color: var(--orange-5-hsl);
}
.stat-card__magenta {
--_color: var(--pink-8-hsl);
}
.dark .stat-card__purple {
--_color: var(--purple-7);
--_color: var(--violet-10-hsl);
}
.dark .stat-card__green {
--_color: var(--teal-7);
--_color: var(--teal-8-hsl);
}
.dark .stat-card__orange {
--_color: var(--orange-8);
--_color: var(--orange-6-hsl);
}
.dark .stat-card__magenta {
--_color: var(--pink-7-hsl);
}
</style>

View file

@ -4,6 +4,7 @@ import { CustomerBranchCreate } from 'stores/customer/types';
defineProps<{
readonly?: boolean;
edit?: boolean;
}>();
const customerBranch = defineModel<CustomerBranchCreate[]>('customerBranch', {
@ -17,6 +18,8 @@ const index = ref<number>(0);
function addData() {
index.value++;
customerBranch.value.push({
code: '',
branchNo: undefined,
address: '',
addressEN: '',
provinceId: '',
@ -91,7 +94,8 @@ function close(index: number) {
v-for="(v, index) in customerBranch"
:key="index"
:name="index"
:label="`${$t('customerBranchFormTab')} ${index + 1}`"
:label="`${$t('customerBranchFormTab')} `"
:disable="tab !== index && edit"
@click="tab = index"
no-caps
:class="tab === index ? '' : 'bordered-b bordered-r'"
@ -114,7 +118,9 @@ function close(index: number) {
<q-tab-panels v-model="tab" class="rounded-borders">
<q-tab-panel
:id="`tab-branch-${index}`"
v-for="(v, index) in customerBranch"
v-for="(v, index) in customerBranch.sort(
(a, b) => (a.branchNo ?? 0) - (b.branchNo ?? 0),
)"
:key="index"
:name="index"
>

View file

@ -315,11 +315,11 @@ defineEmits<{
}
}
&.color__purple {
--_color: var(--purple-11-hsl);
--_color: var(--violet-11-hsl);
}
&.color__green {
--_color: var(--teal-9-hsl);
--_color: var(--teal-10-hsl);
}
}

View file

@ -5,275 +5,242 @@ import AppBox from 'components/app/AppBox.vue';
import AppCircle from 'components/app/AppCircle.vue';
defineProps<{
list: {
id: string;
data: {
name: string;
detail?: { label: string; value: string }[];
code: string;
male?: boolean;
female?: boolean;
disabled?: boolean;
badge?: string;
img?: string;
}[];
gridColumns?: number;
detail?: { icon: string; value: string }[];
};
tag?: [{ color: string; value: string }];
disabled?: boolean;
noHover?: boolean;
noAction?: boolean;
noDetail?: boolean;
noBg?: boolean;
detailColumnCount?: number;
canEditProfile?: boolean;
history?: boolean;
}>();
defineEmits<{
(e: 'editProfile'): void;
(e: 'history', id: string): void;
(e: 'deleteCard', id: string): void;
(
e: 'updateCard',
action: 'FORM' | 'INFO',
id: string,
isEdit?: boolean,
): void;
(e: 'enterCard', action: 'FORM' | 'INFO', id: string): void;
(e: 'toggleStatus', id: string, status: boolean): void;
(e: 'history'): void;
(e: 'deleteCard'): void;
(e: 'updateCard', action: 'FORM' | 'INFO', isEdit?: boolean): void;
(e: 'enterCard', action: 'FORM' | 'INFO'): void;
(e: 'toggleStatus', status: boolean): void;
}>();
</script>
<template>
<div
class="person-container"
:style="`grid-template-columns: repeat( ${
gridColumns
? `${gridColumns}, 1fr`
: $q.screen.gt.sm
? '4, 1fr'
: $q.screen.gt.xs
? '2, 1fr'
: '1, 1fr'
})`"
<AppBox
bordered
no-padding
class="column person-box"
:class="{
'person-box__disabled': disabled,
'person-box__no-hover': noHover,
'person-box__no-detail': !data.detail,
'person-box__no-bg': noBg,
}"
@click="$emit('enterCard', 'INFO')"
>
<AppBox
bordered
class="column person-box"
:class="{
'person-box__disabled': v.disabled,
'person-box__no-hover': noHover,
'person-box__no-detail': noDetail,
'person-box__no-bg': noBg,
}"
@click="$emit('enterCard', 'INFO', v.id)"
v-for="(v, i) in list"
:key="i"
>
<div class="q-pa-sm column items-center">
<!-- kebab menu -->
<div class="full-width text-right" v-if="!noAction">
<q-btn
v-if="history"
flat
round
class="app-text-muted-2"
padding="xs"
icon="mdi-history"
size="sm"
@click.stop="$emit('history', v.id)"
/>
<q-btn
flat
round
padding="xs"
class="app-text-muted-2"
icon="mdi-dots-vertical"
size="sm"
@click.stop
<div class="column items-center">
<!-- kebab menu -->
<div class="full-width flex" v-if="!noAction">
<div style="margin-right: auto">
<span
class="tags"
v-for="v in tag"
:class="{ [`tags__${v.color}`]: true }"
>
<q-menu class="bordered">
<q-list v-close-popup>
<q-item
clickable
dense
class="row q-py-sm"
style="white-space: nowrap"
@click="$emit('enterCard', 'INFO', v.id)"
>
<q-icon
name="mdi-eye-outline"
class="col-3"
size="xs"
style="color: hsl(var(--green-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('viewDetail') }}
</span>
</q-item>
<q-item
dense
clickable
class="row q-py-sm"
style="white-space: nowrap"
@click="$emit('updateCard', 'INFO', v.id, true)"
v-close-popup
>
<q-icon
name="mdi-pencil-outline"
class="col-3"
size="xs"
style="color: hsl(var(--cyan-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('edit') }}
</span>
</q-item>
<q-item
dense
clickable
@click="$emit('deleteCard', v.id)"
v-close-popup
>
<q-icon
name="mdi-trash-can-outline"
size="xs"
class="col-3 app-text-negative"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('delete') }}
</span>
</q-item>
<q-item dense>
<q-item-section class="q-py-sm">
<div class="q-pa-sm surface-2 rounded">
<q-toggle
dense
size="sm"
@click="
$emit('toggleStatus', v.id, v.disabled === false)
"
:model-value="!v.disabled"
val="xs"
padding="none"
>
<div class="q-ml-xs">
{{
!v.disabled
? $t('switchOnLabel')
: $t('switchOffLabel')
}}
</div>
</q-toggle>
</div>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<!-- profile -->
<div style="position: relative">
<AppCircle
bordered
class="avatar"
style="border: 2px solid var(--border-color)"
:class="{ 'edit-profile': canEditProfile }"
@click="
() => {
if (canEditProfile) $emit('editProfile');
}
"
>
<q-img
:src="v.img"
style="object-fit: cover; width: 100%; height: 100%"
/>
<div class="status-circle">
<q-icon
:name="`mdi-${v.disabled ? 'close' : 'check'}`"
:style="`color:${v.disabled ? 'var(--gray-6)' : 'white'}`"
/>
</div>
</AppCircle>
</div>
<!-- name symbol -->
<span class="items-center row q-my-sm">
{{ v.name }}
<Icon
class="q-pl-sm"
:class="{
'symbol-gender': v.male || v.female,
'symbol-gender__male': !v.disabled && v.male,
'symbol-gender__female': !v.disabled && v.female,
'symbol-gender__disable': v.disabled,
}"
:icon="`material-symbols:${v.male ? 'male' : 'female'}`"
width="24px"
/>
<!-- <Icon class="locale q-pl-sm" icon="circle-flags:th" width="24px" /> -->
</span>
<span
v-if="v.badge"
class="badge q-px-sm"
:class="{
'bg-gender': v.male || v.female,
'bg-gender__male': !v.disabled && v.male,
'bg-gender__female': !v.disabled && v.female,
'bg-gender__disable': v.disabled,
empty: !v.male && !v.female,
}"
>
{{ v.badge }}
</span>
</div>
<!-- detail -->
<q-separator v-if="!noDetail" />
<div
v-if="!noDetail"
class="q-pt-sm q-px-md q-pb-md person-detail rounded-b full-width"
:class="{
'bg-gender': v.male || v.female,
'bg-gender__light':
(!v.disabled && v.male) || (!v.disabled && v.female),
'bg-gender__male': !v.disabled && v.male,
'bg-gender__female': !v.disabled && v.female,
'bg-gender__disable': v.disabled,
}"
:style="
(detailColumnCount &&
`grid-template-columns: repeat(${detailColumnCount}, 1fr);`) ||
''
"
>
<div v-for="(d, j) in v.detail" :key="j">
<span class="app-text-muted-2">
{{ d.label }}
{{ v.value }}
</span>
<span>{{ d.value || '-' }}</span>
</div>
<q-btn
v-if="history"
flat
round
class="app-text-muted-2"
padding="xs"
icon="mdi-history"
size="sm"
@click.stop="$emit('history')"
/>
<q-btn
:key="data.code"
flat
round
padding="xs"
class="app-text-muted-2"
icon="mdi-dots-vertical"
size="sm"
@click.stop
>
<q-menu class="bordered">
<q-list v-close-popup>
<q-item
clickable
dense
class="row q-py-sm"
style="white-space: nowrap"
@click.stop="$emit('enterCard', 'INFO')"
>
<q-icon
name="mdi-eye-outline"
class="col-3"
size="xs"
style="color: hsl(var(--green-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('viewDetail') }}
</span>
</q-item>
<q-item
dense
clickable
class="row q-py-sm"
style="white-space: nowrap"
@click="$emit('updateCard', 'INFO', true)"
v-close-popup
>
<q-icon
name="mdi-pencil-outline"
class="col-3"
size="xs"
style="color: hsl(var(--cyan-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('edit') }}
</span>
</q-item>
<q-item
dense
clickable
@click="$emit('deleteCard')"
v-close-popup
>
<q-icon
name="mdi-trash-can-outline"
size="xs"
class="col-3 app-text-negative"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('delete') }}
</span>
</q-item>
<q-item dense>
<q-item-section class="q-py-sm">
<div class="q-pa-sm surface-2 rounded">
<q-toggle
dense
size="sm"
@click="$emit('toggleStatus', disabled === false)"
:model-value="!disabled"
val="xs"
padding="none"
>
<div class="q-ml-xs">
{{
!disabled ? $t('switchOnLabel') : $t('switchOffLabel')
}}
</div>
</q-toggle>
</div>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<!-- profile -->
<AppCircle
bordered
class="avatar"
style="border: 2px solid var(--border-color); overflow: visible"
@click="$emit('editProfile')"
>
<q-img
:src="data.img ?? '/no-profile.png'"
style="
object-fit: cover;
width: 100%;
height: 100%;
border-radius: 50%;
"
>
<template #error>
<div
style="background: none"
class="full-width full-height items-center justify-center flex"
>
<q-img src="/no-profile.png" width="5rem" />
</div>
</template>
</q-img>
<div class="avatar__status"></div>
</AppCircle>
<!-- name symbol -->
<span class="items-center row">
{{ data.name }}
<Icon
v-if="data.male || data.female"
class="q-pl-xs"
:class="{
'symbol-gender': data.male || data.female,
'symbol-gender__male': !disabled && data.male,
'symbol-gender__female': !disabled && data.female,
'symbol-gender__disable': disabled,
}"
:icon="`material-symbols:${data.male ? 'male' : 'female'}`"
width="24px"
/>
</span>
<span style="color: hsl(var(--text-mute)); scale: 0.9">
{{ data.code || '-' }}
</span>
</div>
<!-- detail -->
<q-separator v-if="data.detail" class="q-my-sm q-mx-md" />
<div class="row" v-for="v in data.detail" :key="v.value" v-if="data.detail">
<div class="column items-center" style="width: min-content">
<div class="detail-icon">
<q-icon size="md" style="scale: 0.5" :name="v.icon" />
</div>
</div>
</AppBox>
</div>
<div class="col row items-center full-width">
<span class="ellipsis">{{ v.value || '-' }}</span>
<q-tooltip
anchor="bottom left"
self="bottom left"
:offset="[10, 20]"
:delay="500"
>
{{ v.value || '-' }}
</q-tooltip>
</div>
</div>
</AppBox>
</template>
<style scoped>
.person-box {
background-color: var(--surface-2);
border-radius: var(--radius-2) !important;
background-color: var(--surface-1);
transition: 100ms ease-in-out;
padding: 0;
padding: var(--size-2);
&.person-box__disabled {
opacity: 0.4;
opacity: 0.5;
filter: grayscale(0.9);
.status-circle {
background-color: var(--surface-1);
}
.locale {
filter: grayscale();
}
}
&.person-box__no-detail {
@ -284,6 +251,13 @@ defineEmits<{
background-color: transparent;
}
& .detail-icon {
color: hsl(var(--text-mute-2));
background-color: hsla(var(--stone-6-hsl) / 0.15);
border-radius: 50%;
scale: 0.8;
}
& .bg-gender {
color: hsla(var(--_fg));
background-color: hsl(var(--_bg));
@ -297,16 +271,6 @@ defineEmits<{
color: unset;
background-color: hsla(var(--_bg) / 0.1);
}
&.bg-gender__male {
--_fg: 0 100 100%;
--_bg: var(--gender-male);
}
&.bg-gender__female {
--_fg: 0 100 100%;
--_bg: var(--gender-female);
}
}
& .symbol-gender {
@ -325,23 +289,6 @@ defineEmits<{
}
}
& .person-detail {
display: grid;
flex-grow: 1;
grid-template-columns: repeat(2, 1fr);
gap: var(--size-2);
overflow-x: scroll;
& > * {
display: flex;
flex-direction: column;
& > :first-child {
font-size: var(--font-size-0);
}
}
}
& .status-circle {
width: 18px;
height: 18px;
@ -360,11 +307,6 @@ defineEmits<{
--_hover: hsl(var(--gender-male));
cursor: pointer;
box-shadow: var(--shadow-2);
& .person-detail {
--_hover: hsl(var(--_bg));
box-shadow: inset 0em -5px hsl(var(--_bg));
}
}
}
@ -374,17 +316,25 @@ defineEmits<{
}
.avatar {
block-size: 7rem;
}
block-size: 5rem;
transform: rotate(45deg);
.badge {
display: inline-block;
border-radius: var(--radius-6);
background-color: var(--surface-2);
text-wrap: nowrap;
& .avatar__status {
content: ' ';
display: block;
block-size: 1rem;
aspect-ratio: 1;
position: absolute;
border-radius: 50%;
right: -0.5rem;
border: 1px solid var(--border-color);
top: calc(50% - 0.5rem);
bottom: calc(50% - 0.5rem);
background-color: hsla(var(--positive-bg) / 1);
}
&.empty {
background-color: var(--surface-tab);
& :deep(.q-img) {
transform: rotate(-45deg);
}
}
@ -396,4 +346,60 @@ defineEmits<{
opacity: 80%;
}
}
.tags {
display: inline-block;
color: hsla(var(--_color) / 1);
background: hsla(var(--_color) / 0.15);
border-radius: var(--radius-2);
padding-inline: var(--size-2);
}
.tags__purple {
--_color: var(--violet-11-hsl);
}
.tags__pink {
--_color: var(--pink-6-hsl);
}
.tags__green {
--_color: var(--teal-10-hsl);
}
.tags__cyan {
--_color: var(--cyan-7-hsl);
}
.tags__red {
--_color: var(--red-6-hsl);
}
.tags__yellow {
--_color: var(--orange-4-hsl);
}
.tags__orange {
--_color: var(--orange-5-hsl);
}
.tags__magenta {
--_color: var(--pink-8-hsl);
}
.dark .tags__purple {
--_color: var(--violet-10-hsl);
}
.dark .tags__green {
--_color: var(--teal-8-hsl);
}
.dark .tags__orange {
--_color: var(--orange-6-hsl);
}
.dark .tags__magenta {
--_color: var(--pink-7-hsl);
}
</style>

View file

@ -9,9 +9,9 @@ html {
--brand-1: #035aa1;
--brand-2: #f50000;
--border-color: var(--gray-4);
--border-color: var(--gray-2);
--foreground: black;
--background: var(--gray-1);
--background: var(--gray-2);
--surface-0: var(--background);
--surface-1: white;
--surface-2: var(--gray-0);
@ -46,15 +46,15 @@ html {
}
:where(.dark, .body--dark) {
--brand-1: var(--blue-5);
--brand-1: #035aa1;
--brand-2: #f50000;
--border-color: var(--gray-7);
--foreground: white;
--background: var(--gray-10);
--background: var(--gray-11);
--surface-0: var(--background);
--surface-1: var(--gray-11);
--surface-2: var(--gray-10);
--surface-3: var(--gray-9);
--surface-1: var(--gray-10);
--surface-2: var(--gray-9);
--surface-3: var(--gray-11);
--surface-tab: var(--gray-9);

View file

@ -6,8 +6,8 @@ $primary: var(--brand-1);
$secondary: var(--brand-2);
$accent: #9c27b0;
$dark: var(--gray-11);
$dark-page: var(--gray-10);
$dark: var(--gray-10);
$dark-page: var(--gray-11);
$positive: #00bd9d;
$negative: var(--red-9);
@ -24,6 +24,7 @@ $separator-dark-color: var(--border-color);
}
.q-field__control {
background: var(--surface-1);
color: $primary;
}

View file

@ -2,6 +2,7 @@ export default {
branchManagementCaption: 'Manage All Branch',
branchInHQ: 'Branch within the main office',
office: 'Office',
branchLabel: 'Branch',
branchHQLabel: 'Headquarters',
branchName: 'Branch Name',
@ -20,4 +21,6 @@ export default {
branchLabelStatus: 'Branch Status',
registeredBranch: 'Registered Branch',
branchStatTitle: 'Summary data of branch',
};

View file

@ -8,6 +8,9 @@ export default {
customerEmployeeTooltipCaption: 'Click + to add an employee',
customerEmployerAdd: 'Add employer',
customerEmployeeAdd: 'Add employee',
nameEmployee:'Name Employee',
EMPLOYER: 'Employer',
EMPLOYEE: 'Employee',
customerEmployerStatTitle: 'Employer data summary',
@ -53,4 +56,7 @@ export default {
editBy: 'Edit by',
valueBefore: 'Before',
valueAfter: 'After',
serviceWorkTotal: 'Service Work Total',
priceInformation: 'Price Information',
};

View file

@ -6,4 +6,6 @@ export default {
corporationEnglish: 'English company name',
companyOwnerName: 'Company Owner Name',
corporation: 'Corporation',
};

View file

@ -92,6 +92,7 @@ export default {
formDialogInputNationality: 'Nationality',
formDialogTitlePersonnelAddress: 'Personnel Address',
formDialogTitleEmployerAddress: 'Employer Address',
formDialogTitleAddressPure: 'Address',
formDialogTitleAddressPureEN: 'Address in English',

View file

@ -39,6 +39,8 @@ export default {
displayField: 'Display Fields',
deleteConfirmTitle: 'Comfirm Deletion',
deleteConfirmMessage: 'Do you want to delete this item?',
headquartersNotEstablished: 'You have not yet established the headquarters. You need to establish the headquarters before you can create personnel.',
changePassword: 'Change Password',
signature: 'Signature',
addSignature: 'Add Signature',
@ -59,6 +61,9 @@ export default {
baseOnDevice: 'Base on Device',
person: 'Person',
recordsPage: 'Showing {resultcurrentPage} out of {total} records',
showing: 'Showing',
dataSum: 'Data Summaries',
createdAt: 'Created At',
...status,
...main,
...address,

75
src/i18n/en-US/object.ts Normal file
View file

@ -0,0 +1,75 @@
export default {
branchManagement: {
label: {
branchManagementCaption: 'Manage All Branch',
branchLabel: 'Branch',
branchHQLabel: 'Headquarters',
},
table: {
title: {
office: 'Office',
ddress: 'Address',
// formDialogInputTelephone: 'Telephone Number',
telephone: 'Telephone Number',
type: 'Type',
},
},
input: {
label: {
search: 'Search',
},
},
manu: {
branchName: 'Branch Name',
branchLabelTel: 'Branch Telephone No',
branchLabelAddress: 'Branch Address',
branchLabelType: 'Branch Type',
viewDetail: 'View Detail',
edit: 'Edit',
delete: 'Delete',
switchOnLabel: 'Open',
switchOffLabel: 'Close',
},
},
formDialog: {
section: {
formDialogTitleInformation: 'Basic Information',
formDialogTitleContact: 'Contact Information',
formDialogTitleAddress: ' Head office address information',
formDialogTitleLocation: 'Head Office Location',
formDialogTitleImg: 'Office Image',
subtopic: {
formDialogAddress: 'Address in Thai',
formDialogAddressEN: 'Address in English',
},
},
input: {
formDialogInputCode: 'Head Office Code',
formDialogInputBrId: 'Branch Code',
formDialogInputTaxNo: 'Tax Identification Number',
formDialogInputNameSubBranch: 'Branch Name',
formDialogInputNameSubBranchEn: 'Branch Name (English)',
formDialogInputEmailSubBranch: 'Branch Contact Email',
formDialogInputTelephoneSubBranch: 'Branch Telephone Number',
formDialogInputContactName: 'Contact',
formDialogInputTelephoneContact: 'Contact Telephone Number',
address: 'Address',
province: 'Province',
district: 'District',
subDistrict: 'Sub-district',
zipCode: 'Zip Code',
},
button: {
formDialogUploadQrCode: 'Upload QR Code',
formDialogBtnLocation: 'Start sharing location',
formDialogBtnImg: 'Add Office Image',
agree: 'Ok',
cancel: 'Cancel',
},
},
};

View file

@ -12,7 +12,7 @@ export default {
DELEGATE: 'Delegate',
AGENCY: 'Agency',
personnelStatTitle: 'Summary data of ',
personnelStatTitle: 'Summary data',
personnelCardUserType: 'Type',
personnelCardTelephone: 'Telephone',

View file

@ -9,6 +9,8 @@ export default {
service: 'Service',
product: 'Product',
productGroup: 'Product Group: {name}',
productType: 'Product Type: {name}',
messageTooltipNoProduct: 'No Product and Service Groups',
messageTooltipProductCreate: 'Click + for product and service groups.',
@ -19,8 +21,9 @@ export default {
productCode: 'Product Code',
productName: 'Product Name',
processingTime: 'Processing Time (Day)',
priceeInformation: 'Price Information',
productDetail: 'Product Detail',
productProcessingTime: 'Processing Time (Day)',
priceInformation: 'Price Information',
salePrice: 'Sale Price',
agentPrice: 'Agent Price',
processingPrice: 'Processing Price',

View file

@ -2,6 +2,7 @@ export default {
branchManagementCaption: 'จัดการสาขาทั้งหมด',
branchInHQ: 'สาขาภายในสำนักงานใหญ่',
office: 'สำนักงาน',
branchLabel: 'สาขา',
branchHQLabel: 'สำนักงานใหญ่',
branchName: 'ชื่อสาขา',
@ -20,4 +21,6 @@ export default {
branchLabelStatus: 'สถานะสาขา',
registeredBranch: 'สาขาที่ลงทะเบียน',
branchStatTitle: 'สรุปจัดการสาขา',
};

View file

@ -8,6 +8,9 @@ export default {
customerEmployeeTooltipCaption: 'คลิก + เพื่อเพิ่มลูกจ้าง',
customerEmployerAdd: 'เพิ่มนายจ้าง',
customerEmployeeAdd: 'เพิ่มลูกจ้าง',
nameEmployee:'ชื่อลูกจ้าง',
EMPLOYER: 'นายจ้าง',
EMPLOYEE: 'ลูกจ้าง',
customerEmployerStatTitle: 'สรุปจำนวนข้อมูลนายจ้าง',
@ -61,4 +64,7 @@ export default {
editBy: 'แก้ไขโดย',
valueBefore: 'แก้ไขใหม่',
valueAfter: 'ค่าเดิม',
serviceWorkTotal: 'จำนวนงานทั้งหมด',
priceInformation: 'ข้อมูลราคา',
};

View file

@ -6,4 +6,6 @@ export default {
corporationEnglish: 'ชื่อ บริษัท ภาษาอังกฤษ',
companyOwnerName: 'ชื่อเจ้าของบริษัท',
corporation: 'ชื่อ บริษัท/นิติบุคคล',
};

View file

@ -90,6 +90,7 @@ export default {
formDialogInputNationality: 'สัญชาติ',
formDialogTitlePersonnelAddress: 'ข้อมูลที่อยู่พนักงาน',
formDialogTitleEmployerAddress: 'ข้อมูลที่อยู่นายจ้าง',
formDialogTitleAddressPure: 'ที่อยู่',
formDialogTitleAddressPureEN: 'ที่อยู่ภาษาอังกฤษ',

View file

@ -39,6 +39,7 @@ export default {
displayField: 'ข้อมูลที่แสดง',
deleteConfirmTitle: 'ยืนยันการลบข้อมูล',
deleteConfirmMessage: 'คุณต้องการลบข้อมูลใช่หรือไม่',
headquartersNotEstablished: 'ท่านยังไม่ได้สร้างสำนักงานใหญ่ ต้องสร้างสำนักงานใหญ่ก่อนจึงจะสร้าง บุคลากรได้',
changePassword: 'เปลี่ยนรหัสผ่าน',
signature: 'ลายเซ็น',
addSignature: 'เพิ่มลายเซ็น',
@ -59,6 +60,9 @@ export default {
baseOnDevice: 'สีตามอุปกรณ์',
person: 'คน',
recordsPage: 'แสดง {resultcurrentPage} รายการจาก {total} รายการ',
showing: 'แสดงทีละ',
dataSum: 'สรุปจำนวนข้อมูล',
createdAt: 'สร้างเมื่อ',
...status,
...main,
...address,

View file

@ -9,7 +9,7 @@ export default {
mainCustomerCaption: 'จัดการคนภายในองค์กร',
mainProductTitle: 'สินค้าและบริการ',
mainProductCaption: 'รายการสินค้าและบริการ',
mainProductCaption: 'ประเภทสินค้าและบริการ',
mainQuotationTitle: 'ใบเสนอราคา',
mainQuotationCaption: 'รายการใบเสนอราคา',

75
src/i18n/th-th/object.ts Normal file
View file

@ -0,0 +1,75 @@
export default {
branchManagement: {
label: {
branchManagementCaption: 'จัดการทุกสาขา',
branchLabel: 'สาขา',
branchHQLabel: 'สำนักงานใหญ่',
},
table: {
title: {
office: 'สำนักงาน',
address: 'ที่อยู่',
// formDialogInputTelephone: 'เบอร์โทรศัพท์',
telephone: 'เบอร์โทรศัพท์',
type: 'ประเภท',
},
},
input: {
label: {
search: 'ค้นหา',
},
},
manu: {
branchName: 'ชื่อสาขา',
branchLabelTel: 'เบอร์โทรศัพท์สาขา',
branchLabelAddress: 'ที่อยู่สาขา',
branchLabelType: 'ประเภทสาขา',
viewDetail: 'ดูรายละเอียด',
edit: 'แก้ไข',
delete: 'ลบ',
switchOnLabel: 'เปิด',
switchOffLabel: 'ปิด',
},
},
formDialog: {
section: {
formDialogTitleInformation: 'ข้อมูลพื้นฐาน',
formDialogTitleContact: 'ข้อมูลติดต่อ',
formDialogTitleAddress: 'ข้อมูลที่อยู่สำนักงานใหญ่',
formDialogTitleLocation: 'ตำแหน่งที่ตั้งสำนักงานใหญ่',
formDialogTitleImg: 'ภาพสำนักงาน',
subtopic: {
formDialogAddress: 'ที่อยู่ภาษาไทย',
formDialogAddressEN: 'ที่อยู่ภาษาอังกฤษ',
},
},
input: {
formDialogInputCode: 'รหัสสำนักงานใหญ่',
formDialogInputBrId: 'รหัสสาขา',
formDialogInputTaxNo: 'เลขประจำตัวผู้เสียภาษี',
formDialogInputNameSubBranch: 'ชื่อสาขา',
formDialogInputNameSubBranchEn: 'ชื่อสาขา (ภาษาอังกฤษ)',
formDialogInputEmailSubBranch: 'อีเมลติดต่อสาขา',
formDialogInputTelephoneSubBranch: 'เบอร์โทรศัพท์สาขา',
formDialogInputContactName: 'ชื่อผู้ติดต่อ',
formDialogInputTelephoneContact: 'เบอร์โทรศัพท์ผู้ติดต่อ',
address: 'ที่อยู่',
province: 'จังหวัด',
district: 'อำเภอ',
subDistrict: 'ตำบล',
zipCode: 'รหัสไปรษณีย์',
},
button: {
formDialogUploadQrCode: 'อัปโหลด QR Code',
formDialogBtnLocation: 'เริ่มแชร์ตำแหน่งที่ตั้ง',
formDialogBtnImg: 'เพิ่มภาพสำนักงาน',
agree: 'ตกลง',
cancel: 'ยกเลิก',
},
},
};

View file

@ -21,7 +21,8 @@ export default {
productCode: 'รหัสสินค้า',
productName: 'ชื่อสินค้า',
processingTime: 'ระยะเวลาดำเนินการ (วัน)',
productDetail: 'รายละเอียดสินค้า',
productProcessingTime: 'ระยะเวลาดำเนินการ (วัน)',
priceeInformation: 'ข้อมูลราคา',
salePrice: 'ราคาขาย',
agentPrice: 'ราคาตัวแทน',

View file

@ -16,6 +16,10 @@ import useOptionStore from 'src/stores/options';
import { dialog } from 'src/stores/utils';
import { setLocale } from 'src/utils/datetime';
import useUtilsStore from 'src/stores/utils';
import useMyBranchStore from 'src/stores/my-branch';
const useMyBranch = useMyBranchStore();
const { fetchListMyBranch } = useMyBranch;
interface NotificationButton {
item: string;
@ -41,7 +45,10 @@ const userStore = useUserStore();
const rawOption = ref();
const canvasModal = ref(false);
const leftDrawerOpen = ref(true);
const leftDrawerMini = ref(false);
const filterUnread = ref(false);
const unread = ref<number>(1);
// const filterRole = ref<string[]>();
@ -135,6 +142,8 @@ watch(
);
onMounted(async () => {
await fetchListMyBranch(getUserId() ?? '');
const getCurLang = localStorage.getItem('currentLanguage');
if (getCurLang) currentLanguage.value = getCurLang;
if (currentLanguage.value === 'English') {
@ -169,22 +178,25 @@ onMounted(async () => {
<template>
<q-layout view="lHh Lpr lFf">
<drawer-component v-model:leftDrawerOpen="leftDrawerOpen" />
<drawer-component
:mini="leftDrawerMini"
v-model:left-drawer-open="leftDrawerOpen"
/>
<q-page-container class="surface-1 q-pa-md">
<q-page-container>
<!-- drawer control -->
<div
style="position: relative"
:style="$q.screen.gt.xs ? 'z-index: 1000' : 'z-index: 10'"
>
<q-avatar
<div style="position: relative; z-index: 1000" :hidden="$q.screen.lt.sm">
<div
size="36px"
style="
position: absolute;
border-radius: 50%;
top: 28px;
left: -18px;
left: -22px;
background-color: var(--surface-1);
border: 4px solid var(--surface-1);
"
class="flex items-center justify-center"
>
<q-btn
flat
@ -197,30 +209,47 @@ onMounted(async () => {
background-color: hsl(var(--negative-bg) / 0.1);
overflow: hidden;
"
@click="leftDrawerOpen = !leftDrawerOpen"
@click="leftDrawerMini = !leftDrawerMini"
>
<q-icon
:name="leftDrawerOpen ? 'mdi-backburger' : 'mdi-forwardburger'"
:name="!leftDrawerMini ? 'mdi-backburger' : 'mdi-forwardburger'"
size="16px"
style="color: hsl(var(--negative-bg))"
/>
</q-btn>
</q-avatar>
</div>
</div>
<div
class="surface-3 bordered rounded q-pb-lg scroll column"
style="height: calc(100vh - 32px); flex-wrap: nowrap"
class="surface-0 scroll column"
style="height: 100vh; flex-wrap: nowrap; padding-bottom: var(--size-4)"
>
<!-- header -->
<div
class="q-px-lg row items-center justify-between q-pb-md surface-3 q-pt-lg"
class="q-px-lg surface-0 row items-center justify-start q-pb-md q-pt-lg"
style="position: sticky; top: 0; z-index: 8"
>
<q-btn
v-if="$q.screen.lt.sm"
icon="mdi-menu"
flat
dense
rounded
class="q-mr-md"
@click="
() => {
leftDrawerMini = false;
leftDrawerOpen = !leftDrawerOpen;
}
"
/>
<div class="column">
<span
class="title-gradient text-weight-bold"
:class="{ 'text-h6': $q.screen.gt.xs }"
:style="{
filter: `brightness(${$q.dark.isActive ? '2' : '1'})`,
}"
>
{{
utilsStore.currentTitle?.title
@ -230,16 +259,29 @@ onMounted(async () => {
: 'Jobs Worker Service'
}}
</span>
<span class="text-caption">
{{
utilsStore.currentTitle?.caption
? $t(utilsStore.currentTitle?.caption)
: ''
}}
</span>
<div class="flex items-center" style="gap: var(--size-1)">
<template v-for="(item, i) in utilsStore.currentTitle.path">
<span
class="text-caption cursor-pointer"
@click="item.handler?.()"
>
{{
item.text
? $t(item.text, {
...(item.argsi18n || {}),
})
: ''
}}
</span>
<q-icon
name="mdi-chevron-right"
v-if="i + 1 !== utilsStore.currentTitle.path.length"
/>
</template>
</div>
</div>
<div class="row q-gutter-x-md items-center">
<div class="row q-gutter-x-md items-center" style="margin-left: auto">
<!-- notification -->
<q-btn
round

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -99,7 +99,7 @@ const menu = [
onMounted(() => {
utilsStore.currentTitle.title = '';
utilsStore.currentTitle.caption = '';
utilsStore.currentTitle.path = [{ text: '' }];
});
</script>

View file

@ -33,6 +33,9 @@ export type Branch = {
telephoneNo: string;
lineId: string;
contact: BranchContact[];
_count:{
branch:number
}
};
export type BranchWithChildren = Branch & { branch: Branch[] };
@ -64,6 +67,6 @@ export type BranchUserStats = {
id: string;
nameEN: string;
name: string;
count: 0;
count: number;
isHeadOffice: boolean;
};

View file

@ -336,11 +336,13 @@ const useCustomerStore = defineStore('api-customer', () => {
transactionId?: string;
},
) {
const { code, branchNo, ...playload } = data;
const res = await api.put<
Customer & {
branch: CustomerBranch[];
}
>(`/customer-branch/${id}`, data, {
>(`/customer-branch/${id}`, playload, {
headers: {
'X-Session-Id': flow?.sessionId,
'X-Rtid': flow?.refTransactionId || flowStore.rtid,

View file

@ -4,6 +4,7 @@ import { Status } from '../types';
export type CustomerType = 'CORP' | 'PERS';
export type Customer = {
registeredBranchId: string;
imageUrl: string;
id: string;
code: string;
@ -63,7 +64,7 @@ export type CustomerBranch = {
export type CustomerBranchCreate = {
code?: string;
branchNo: number;
branchNo?: number;
address: string;
addressEN: string;
provinceId?: string | null;

View file

@ -71,7 +71,7 @@ const useEmployeeStore = defineStore('api-employee', () => {
transactionId?: string;
},
) {
const { image, ...payload } = data;
const { code, image, ...payload } = data;
const res = await api.post<
Employee & { profileImageUrl: string; profileImageUploadUrl: string }
>('/employee', payload, {
@ -104,7 +104,7 @@ const useEmployeeStore = defineStore('api-employee', () => {
transactionId?: string;
},
) {
const { image, ...payload } = data;
const { code, image, ...payload } = data;
const res = await api.put<
Employee & { imageUrl: string; profileImageUploadUrl: string }
>(`/employee/${id}`, payload, {

View file

@ -2,6 +2,8 @@ import { District, Province, SubDistrict } from '../address';
import { Status } from '../types';
import { User } from '../user/types';
import { Customer , CustomerBranch } from '../customer/types';
export type Employee = {
id: string;
code: string;
@ -45,14 +47,16 @@ export type Employee = {
district: District | null;
province: Province | null;
profileImageUrl: string | null;
customerBranch: CustomerBranch & { customer: Customer } ;
};
export type EmployeeCreate = {
code: string;
image: File | null;
customerBranchId: string;
status?: Status;
nrcNo: string;
dateOfBirth: Date | null;

View file

@ -8,6 +8,9 @@ import { Branch } from '../branch/types';
import useFlowStore from '../flow';
const useMyBranch = defineStore('useMyBranchStore', () => {
const myBranch = ref<Branch[]>();
const currentMyBranch = ref<Branch>();
const flowStore = useFlowStore();
async function fetchListOptionBranch<
Options extends {
@ -52,8 +55,37 @@ const useMyBranch = defineStore('useMyBranchStore', () => {
return false;
}
async function fetchListMyBranch(
userId: string,
flow?: {
sessionId?: string;
refTransactionId?: string;
transactionId?: string;
},
) {
const res = await api.get<Pagination<Branch[]>>(`/user/${userId}/branch`, {
headers: {
'X-Session-Id': flow?.sessionId,
'X-Rtid': flow?.refTransactionId || flowStore.rtid,
'X-Tid': flow?.transactionId,
},
});
if (!res) return false;
if (res.status === 200) {
myBranch.value = res.data.result;
return res.data;
}
return false;
}
return {
myBranch,
currentMyBranch,
fetchListOptionBranch,
fetchListMyBranch,
};
});

View file

@ -23,7 +23,11 @@ import {
import { ref } from 'vue';
import useFlowStore from '../flow';
import useMyBranchStore from 'src/stores/my-branch';
const useProductServiceStore = defineStore('api-product-service', () => {
const useMyBranch = useMyBranchStore();
// Product Type
const flowStore = useFlowStore();
@ -301,6 +305,10 @@ const useProductServiceStore = defineStore('api-product-service', () => {
v !== undefined && params.append(k, v.toString());
}
if (useMyBranch.myBranch?.length !== 0 && useMyBranch.myBranch) {
params.append('registeredBranchId', useMyBranch.myBranch[0].id);
}
const query = params.toString();
const res = await api.get<Pagination<ProductList[]>>(
@ -429,6 +437,10 @@ const useProductServiceStore = defineStore('api-product-service', () => {
v !== undefined && params.append(k, v.toString());
}
if (useMyBranch.myBranch?.length !== 0 && useMyBranch.myBranch) {
params.append('registeredBranchId', useMyBranch.myBranch[0].id);
}
const query = params.toString();
const res = await api.get<Pagination<Service[]>>(
@ -733,6 +745,10 @@ const useProductServiceStore = defineStore('api-product-service', () => {
v !== undefined && params.append(k, v.toString());
}
if (useMyBranch.myBranch?.length !== 0 && useMyBranch.myBranch) {
params.append('registeredBranchId', useMyBranch.myBranch[0].id);
}
const query = params.toString();
const res = await api.get<Pagination<ServiceAndProduct[]>>(

View file

@ -97,6 +97,7 @@ export type UserAttachmentDelete = {
};
export type UserTypeStats = {
[x: string]: number;
USER: number;
MESSENGER: number;
DELEGATE: number;

View file

@ -95,9 +95,21 @@ export function formatNumberDecimal(num: number, point: number): string {
}
const useUtilsStore = defineStore('utilsStore', () => {
const currentTitle = ref<{ title: string; caption: string }>({
const currentTitle = ref<{
title: string;
path: {
text: string;
argsi18n?: Record<string, string>;
handler?: () => unknown;
}[];
}>({
title: '',
caption: '',
path: [
{
text: '',
handler: () => {},
},
],
});
return {