Skip to content

Commit 5130e95

Browse files
committed
Merge branch 'feat/devops' of github.com:LabSyncro/LabSyncro into feat/devops
2 parents b4ec923 + b750bde commit 5130e95

69 files changed

Lines changed: 3154 additions & 170 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lockb

1.01 KB
Binary file not shown.

components/app/Checkout/LabSearchBox.vue

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,20 @@ function unfocusSearchItem () {
4545
<template>
4646
<div ref="dropdown" class="relative">
4747
<div class="relative">
48-
<input
49-
v-model="searchText"
50-
class="bg-white border-2 w-[100%] p-1 px-2 rounded-md text-md"
51-
type="search" @keydown.down="focusNextSearchItem" @keydown.up="focusPrevSearchItem" @keydown.enter="focusedSearchItemIndex !== null && setInput(focusedSearchItemIndex)" @keydown.esc="unfocusSearchItem">
48+
<Input
49+
v-model="searchText" class="bg-white border-2 w-[100%] p-4 rounded-xl text-lg" type="search"
50+
@keydown.down="focusNextSearchItem" @keydown.up="focusPrevSearchItem"
51+
@keydown.enter="focusedSearchItemIndex !== null && setInput(focusedSearchItemIndex)"
52+
@keydown.esc="unfocusSearchItem" @input="emits('select', null)" />
5253
</div>
5354

54-
<div v-if="searchItems.length" :class="`${isDropdownActive ? 'flex' : 'hidden'} flex-col gap-1 absolute bg-white p-1 mt-1 w-[100%] z-50 shadow-[0_0px_16px_1px_rgba(0,0,0,0.3)]`">
55-
<button v-for="(item, index) in searchItems" :key="item.id" :class="`px-2 text-normal p-1 flex gap-2 hover:bg-gray-100 ${focusedSearchItemIndex === index ? 'bg-secondary-light' : ''}`" @click="setInput(index)">
55+
<div
56+
v-if="searchItems.length"
57+
:class="`${isDropdownActive ? 'flex' : 'hidden'} flex-col gap-1 absolute bg-white p-1 mt-1 w-[100%] z-50 shadow-[0_0px_16px_1px_rgba(0,0,0,0.3)]`">
58+
<button
59+
v-for="(item, index) in searchItems" :key="item.id"
60+
:class="`px-2 text-normal p-1 flex gap-2 hover:bg-gray-100 ${focusedSearchItemIndex === index ? 'bg-secondary-light' : ''}`"
61+
@click="setInput(index)">
5662
{{ `${item.room}, ${item.branch} - ${item.name}` }}
5763
</button>
5864
</div>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { Icon } from '#components';
2+
import type { DeviceSchema } from './schema';
3+
import type { AugmentedColumnDef } from '~/components/common/DataTable/column';
4+
5+
export function createColumns ({
6+
onDeviceKindLinkClick,
7+
}: {
8+
onDeviceKindLinkClick: (id: string) => void;
9+
}): AugmentedColumnDef<DeviceSchema>[] {
10+
return [
11+
{
12+
id: 'fullId',
13+
title: 'Mã định danh',
14+
cell: ({ row }) =>
15+
h('p', { class: 'text-normal pl-3' }, [
16+
`${row.original.kind}/${row.original.id}`.toUpperCase(),
17+
]),
18+
enableSorting: true,
19+
},
20+
{
21+
id: 'status',
22+
title: 'Tình trạng',
23+
cell: ({ row }) =>
24+
h(
25+
'span',
26+
{
27+
class:
28+
'bg-gray-100 text-black rounded-sm p-1 px-2 border border-gray-200 text-normal',
29+
},
30+
(statusMap as any)[row.original.status],
31+
),
32+
},
33+
{
34+
id: 'room',
35+
title: 'Phòng thí nghiệm',
36+
cell: ({ row }) =>
37+
h(
38+
'p',
39+
{
40+
class:
41+
'line-clamp-2 text-slate-500 text-normal leading-6 font-normal',
42+
},
43+
`${row.original.room}, ${row.original.branch}`,
44+
),
45+
enableSorting: true,
46+
},
47+
{
48+
id: 'price',
49+
title: 'Đơn giá (VND)',
50+
cell: ({ row }) =>
51+
h(
52+
'p',
53+
{
54+
class:
55+
'text-slate-500 text-right text-normal leading-6 font-normal',
56+
},
57+
row.original.price,
58+
),
59+
enableSorting: true,
60+
},
61+
{
62+
id: 'createdAt',
63+
title: 'Ngày nhập',
64+
cell: ({ row }) => {
65+
return h(
66+
'span',
67+
{
68+
class: 'text-slate-500 text-sm font-normal leading-tight',
69+
},
70+
row.original.createdAt,
71+
);
72+
},
73+
enableSorting: true,
74+
},
75+
{
76+
id: 'link',
77+
title: '',
78+
cell: ({ row }) =>
79+
h(
80+
'div',
81+
{
82+
class: 'flex justify-end items-center text-lg hover:cursor-pointer',
83+
onClick: () => onDeviceKindLinkClick(row.original.id),
84+
},
85+
h(Icon, { name: 'i-heroicons-arrow-top-right-on-square' }),
86+
),
87+
},
88+
];
89+
}

components/app/DeviceNew/DataSheetTable/index.vue

Whitespace-only changes.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Icon } from '#components';
2+
import type { DeviceSchema } from './schema';
3+
import type { AugmentedColumnDef } from '~/components/common/DataTable/column';
4+
import moment from 'moment';
5+
6+
const statusMap = {
7+
healthy: 'Tốt',
8+
broken: 'Hư',
9+
discarded: 'Đã thanh lý',
10+
assessing: 'Đang kiểm kê',
11+
shipping: 'Đang vận chuyển',
12+
maintaining: 'Bảo trì',
13+
borrowing: 'Đang mượn',
14+
lost: 'Mất',
15+
};
16+
17+
export function createColumns ({
18+
onDeviceKindLinkClick,
19+
}: {
20+
onDeviceKindLinkClick: (id: string) => void;
21+
}): AugmentedColumnDef<DeviceSchema>[] {
22+
return [
23+
{
24+
id: 'fullId',
25+
title: 'Mã định danh',
26+
cell: ({ row }) =>
27+
h('p', { class: 'text-normal pl-3' }, [
28+
`${row.original.kind}/${row.original.id}`.toUpperCase(),
29+
]),
30+
enableSorting: true,
31+
},
32+
{
33+
id: 'status',
34+
title: 'Tình trạng',
35+
cell: ({ row }) =>
36+
h(
37+
'span',
38+
{
39+
class:
40+
'bg-gray-100 text-black rounded-sm p-1 px-2 border border-gray-200 text-normal',
41+
},
42+
(statusMap as any)[row.original.status],
43+
),
44+
},
45+
{
46+
id: 'room',
47+
title: 'Phòng thí nghiệm',
48+
cell: ({ row }) =>
49+
h(
50+
'p',
51+
{
52+
class:
53+
'line-clamp-2 text-slate-500 text-normal leading-6 font-normal',
54+
},
55+
`${row.original.room}, ${row.original.branch}`,
56+
),
57+
enableSorting: true,
58+
},
59+
{
60+
id: 'price',
61+
title: 'Đơn giá (VND)',
62+
cell: ({ row }) =>
63+
h(
64+
'p',
65+
{
66+
class:
67+
'text-slate-500 text-center text-normal leading-6 font-normal',
68+
},
69+
row.original.price,
70+
),
71+
enableSorting: true,
72+
},
73+
{
74+
id: 'createdAt',
75+
title: 'Ngày nhập',
76+
cell: ({ row }) => {
77+
return h(
78+
'span',
79+
{
80+
class:
81+
'text-slate-500 text-center text-sm font-normal leading-tight',
82+
},
83+
moment(row.original.createdAt).format('DD/MM/YYYY'),
84+
);
85+
},
86+
enableSorting: true,
87+
},
88+
{
89+
id: 'printedAt',
90+
title: 'Ngày in',
91+
cell: ({ row }) => {
92+
return h(
93+
'span',
94+
{
95+
class: 'text-slate-500 text-sm font-normal leading-tight',
96+
},
97+
row.original.printedAt,
98+
);
99+
},
100+
enableSorting: true,
101+
},
102+
{
103+
id: 'link',
104+
title: '',
105+
cell: ({ row }) =>
106+
h(
107+
'div',
108+
{
109+
class: 'flex justify-end items-center text-lg hover:cursor-pointer',
110+
onClick: () => onDeviceKindLinkClick(row.original.id),
111+
},
112+
h(Icon, { name: 'i-heroicons-arrow-top-right-on-square' }),
113+
),
114+
},
115+
];
116+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<script setup lang="ts">
2+
import { deviceService } from '~/services/devices';
3+
import { createColumns } from './column';
4+
import type { AugmentedColumnDef } from '~/components/common/DataTable/column';
5+
6+
const props = defineProps<{
7+
kindId: string;
8+
kindName: string;
9+
}>();
10+
11+
const emits = defineEmits<{
12+
'device-kind-link-click': [];
13+
}>();
14+
15+
const showAddModal = ref(false);
16+
17+
async function fetchData (offset: number, length: number, options: { desc?: boolean, sortField?: string, searchText?: string, searchFields?: string[] }): Promise<{ data: unknown[], totalPages: number }> {
18+
const res = await deviceService.getByKind(props.kindId, offset, length, { searchText: options.searchText, searchFields: ['device_id'], sortField: options.sortField as any, desc: options.desc });
19+
return {
20+
data: res.devices,
21+
totalPages: res.totalPages,
22+
};
23+
}
24+
25+
async function onDeviceKindLinkClick () {
26+
emits('device-kind-link-click');
27+
}
28+
29+
</script>
30+
31+
<template>
32+
<DataTable
33+
:selectable="false" :searchable="true" :qrable="true" :fetch-fn="fetchData"
34+
:add-trigger-fn="() => { showAddModal = true }"
35+
:columns="createColumns({ onDeviceKindLinkClick }) as AugmentedColumnDef<unknown>[]" />
36+
<DeviceNewModal v-model:is-open="showAddModal" :device-kind-id="kindId" :device-kind-name="kindName" />
37+
</template>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Type } from '@sinclair/typebox';
2+
import type { Static } from '@sinclair/typebox';
3+
4+
export const DeviceSchema = Type.Object({
5+
id: Type.String(),
6+
name: Type.String(),
7+
kind: Type.String(),
8+
status: Type.String(),
9+
room: Type.String(),
10+
branch: Type.String(),
11+
price: Type.Number(),
12+
createdAt: Type.String(),
13+
printedAt: Type.String(),
14+
});
15+
16+
export type DeviceSchema = Static<typeof DeviceSchema>;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { MetaSchema } from './schema';
2+
import type { AugmentedColumnDef } from '~/components/common/DataTable/column';
3+
4+
export const columns: AugmentedColumnDef<MetaSchema>[] = [
5+
{
6+
id: 'attribute',
7+
title: 'Thuộc tính',
8+
cell: ({ row }) =>
9+
h(
10+
'p',
11+
{
12+
class:
13+
'line-clamp-2 text-slate-500 text-right text-normal leading-6 font-normal',
14+
},
15+
row.original.attribute,
16+
),
17+
enableSorting: true,
18+
},
19+
{
20+
id: 'value',
21+
title: 'Giá trị',
22+
cell: ({ row }) => {
23+
return h(
24+
'span',
25+
{
26+
class: 'text-slate-500 text-sm font-normal leading-tight',
27+
},
28+
row.original.value,
29+
);
30+
},
31+
enableSorting: true,
32+
},
33+
];
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script setup lang="ts">
2+
import { deviceKindService } from '@/services';
3+
import { columns } from './column';
4+
import type { AugmentedColumnDef } from '~/components/common/DataTable/column';
5+
6+
const props = defineProps<{
7+
kindId: string;
8+
}>();
9+
10+
11+
const showAddModal = ref(false);
12+
13+
async function fetchData (offset: number, length: number, options: { desc?: boolean, sortField?: string, searchText?: string, searchFields?: string[] }): Promise<{ data: unknown[], totalPages: number }> {
14+
const res = await deviceKindService.getByKind(props.kindId, offset, length, { searchText: options.searchText, searchFields: ['device_id'], sortField: options.sortField as any, desc: options.desc });
15+
return {
16+
data: res.devices,
17+
totalPages: res.totalPages,
18+
};
19+
}
20+
21+
</script>
22+
23+
<template>
24+
<DataTable
25+
:selectable="false" :searchable="true" :qrable="true" :fetch-fn="fetchData"
26+
:add-trigger-fn="() => { showAddModal = true; }" :columns="columns as AugmentedColumnDef<unknown>[]" />
27+
</template>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Type } from '@sinclair/typebox';
2+
import type { Static } from '@sinclair/typebox';
3+
4+
export const MetaSchema = Type.Object({
5+
attribute: Type.String(),
6+
value: Type.String(),
7+
});
8+
9+
export type MetaSchema = Static<typeof MetaSchema>;

0 commit comments

Comments
 (0)