pukaad-ui-lib 1.206.0 → 1.207.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
3
  "configKey": "pukaadUI",
4
- "version": "1.206.0",
4
+ "version": "1.207.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -95,7 +95,7 @@ const onSubmit = async () => {
95
95
  loading.value = true;
96
96
  try {
97
97
  const res = await api("/profiles/me/about", {
98
- method: "PUT",
98
+ method: "PATCH",
99
99
  body: {
100
100
  description: form.value.description,
101
101
  address: form.value.address,
@@ -8,25 +8,58 @@
8
8
  <ShadTabsTrigger value="video">วิดีโอ</ShadTabsTrigger>
9
9
  </ShadTabsList>
10
10
  <ShadTabsContent value="business-info">
11
- <SuggestPlaceForm />
11
+ <SuggestPlaceForm :state="props.state" />
12
+ </ShadTabsContent>
13
+ <ShadTabsContent value="image">
14
+ <div class="flex flex-col gap-[16px] w-full">
15
+ <div class="flex justify-between items-center">
16
+ <div class="flex flex-col gap-[4px]">
17
+ <div class="font-body-large-prominent">รูปภาพ</div>
18
+ <div class="font-label-serif-medium text-gray">
19
+ รองรับไฟล์ *.jpg *.jpeg *.png *.webp *.bmp *.gif ขนาดไฟล์ไม่เกิน
20
+ 30 mb
21
+ </div>
22
+ </div>
23
+ <div class="flex gap-[8px]">
24
+ <Icon
25
+ name="lucide:list"
26
+ size="20"
27
+ :class="
28
+ photoColumns ? 'text-primary' : 'cursor-pointer text-gray'
29
+ "
30
+ @click="photoColumns = true"
31
+ />
32
+ <Icon
33
+ name="lucide:layout-grid"
34
+ size="20"
35
+ :class="
36
+ !photoColumns ? 'text-primary' : 'cursor-pointer text-gray'
37
+ "
38
+ @click="photoColumns = false"
39
+ />
40
+ </div>
41
+ </div>
42
+ <InputFile accept="image/*" :column="photoColumns" />
43
+ </div>
12
44
  </ShadTabsContent>
13
- <ShadTabsContent value="image"> รูปภาพ </ShadTabsContent>
14
45
  <ShadTabsContent value="video"> วิดีโอ </ShadTabsContent>
15
46
  </ShadTabs>
16
47
 
17
48
  <!-- personal / business: แสดงตรงๆ ไม่มี tabs -->
18
49
  <div v-else>
19
- <SuggestPlaceForm />
50
+ <SuggestPlaceForm :state="props.state" />
20
51
  </div>
21
52
  </Drawer>
22
53
  </template>
23
54
 
24
55
  <script setup>
25
- import { computed } from "vue";
56
+ import { computed, ref } from "vue";
26
57
  const props = defineProps({
27
58
  state: { type: String, required: false, default: "personal" }
28
59
  });
29
60
  const isOpen = defineModel({ type: Boolean, ...{ default: false } });
61
+ const photoColumns = ref(false);
62
+ console.log("photoColumns", photoColumns.value);
30
63
  const drawerTitle = computed(() => {
31
64
  if (props.state === "personal" || props.state === "business") {
32
65
  return "\u0E41\u0E19\u0E30\u0E19\u0E33\u0E2A\u0E16\u0E32\u0E19\u0E17\u0E35\u0E48\u0E17\u0E35\u0E48\u0E02\u0E32\u0E14\u0E44\u0E1B";
@@ -1,3 +1,8 @@
1
- declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
1
+ type __VLS_Props = {
2
+ state?: "personal" | "business" | "backoffice";
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
5
+ state: "personal" | "business" | "backoffice";
6
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
2
7
  declare const _default: typeof __VLS_export;
3
8
  export default _default;
@@ -1,24 +1,109 @@
1
- <template>
1
+ <template>
2
2
  <div class="flex gap-[16px] w-full">
3
3
  <!-- กรอกข้อมูล -->
4
4
  <div class="flex flex-col gap-[16px] w-[490px]">
5
- <InputAddress />
6
- <div class="font-body-large-prominent">รายละเอียด</div>
7
- <div class="flex flex-col gap-[4px]">
8
- <InputAutocomplete
9
- label="ชื่อสถานที่ธุรกิจ"
10
- placeholder="กรอกชื่อสถานที่"
5
+ <InputAddress v-model="addressValue" />
6
+ <template v-if="!isAddressCompleted">
7
+ <div class="font-body-large-prominent">รายละเอียด</div>
8
+ <div class="flex flex-col gap-[4px]">
9
+ <InputAutocomplete
10
+ label="ชื่อสถานที่ธุรกิจ"
11
+ placeholder="กรอกชื่อสถานที่"
12
+ required
13
+ show-counter
14
+ :limit="180"
15
+ />
16
+ <InputTextField
17
+ v-if="extraNameCount >= 1"
18
+ label="ชื่อภาษาไทย"
19
+ placeholder="ใส่ในกรณีที่ต่างจากชื่อหลัก"
20
+ show-counter
21
+ :limit="180"
22
+ />
23
+ <InputTextField
24
+ v-if="extraNameCount >= 2"
25
+ label="ชื่อภาษาอังกฤษ"
26
+ placeholder="ใส่ในกรณีที่ต่างจากชื่อหลัก"
27
+ show-counter
28
+ :limit="180"
29
+ />
30
+ <Button
31
+ v-if="extraNameCount < 2"
32
+ variant="text"
33
+ color="primary"
34
+ class="w-[145px]"
35
+ @click="extraNameCount++"
36
+ >
37
+ <Icon name="lucide:plus" />
38
+ เพิ่มชื่อสถานที่
39
+ </Button>
40
+ </div>
41
+ <InputCombobox
42
+ label="หมวดหมู่"
43
+ placeholder="เพิ่มหมวดหมู่ที่เกี่ยวข้องกับสถานที่"
44
+ :limit="3"
45
+ show-counter
46
+ :items="[]"
11
47
  required
48
+ multiple
49
+ />
50
+ <InputTextarea
51
+ label="คำอธิบาย"
52
+ placeholder="คำอธิบายเกี่ยวกับสถานที่ (สูงสุด 220 ตัวอักษร)"
53
+ :limit="220"
12
54
  show-counter
13
- :limit="180"
14
55
  />
15
- <Button variant="outline" class="w-[145px]">เพิ่มชื่อสถานที่</Button>
16
- </div>
17
- <InputDateOpening />
56
+ <InputDateOpening />
57
+ <div class="flex flex-col gap-[8px]">
58
+ <InputPhone label="เบอร์โทรศัพท์" placeholder="กรอกเบอร์โทรศัพท์" />
59
+ <InputPhone
60
+ v-for="(item, index) in extraPhones"
61
+ :key="index"
62
+ label="เบอร์โทรศัพท์"
63
+ placeholder="กรอกเบอร์โทรศัพท์"
64
+ v-model="extraPhones[index]"
65
+ />
66
+ <Button
67
+ variant="text"
68
+ color="primary"
69
+ class="w-[145px]"
70
+ @click="extraPhones.push('')"
71
+ >
72
+ <Icon name="lucide:plus" />
73
+ เพิ่มเบอร์โทรศัพท์
74
+ </Button>
75
+ </div>
76
+ <template v-if="props.state === 'personal'">
77
+ <div class="flex flex-col gap-[16px]">
78
+ <InputCheckbox v-model="review" label="คุณต้องการรีวิวสถานที่นี้" />
79
+ <template v-if="review">
80
+ <InputRating class="flex py-4 justify-center" />
81
+ <InputTextarea
82
+ label="คำอธิบาย"
83
+ placeholder="คำอธิบายเกี่ยวกับสถานที่"
84
+ show-counter
85
+ />
86
+ <div class="flex flex-col gap-[8px]">
87
+ <div class="flex flex-col gap-[4px]">
88
+ <div class="font-body-large-prominent text-gray">
89
+ เพิ่มภาพถ่าย
90
+ </div>
91
+ <div class="font-body-small text-gray">สูงสุด 9 รายการ</div>
92
+ </div>
93
+ <InputFile accept="image/*" :limit="9" />
94
+ <div class="font-body-small text-gray w-[250px]">
95
+ รองรับไฟล์ *.jpg *.jpeg *.png *.webp *.bmp *.gif
96
+ ขนาดไฟล์ไม่เกิน 30 mb
97
+ </div>
98
+ </div>
99
+ </template>
100
+ </div>
101
+ </template>
102
+ </template>
18
103
  </div>
19
104
 
20
105
  <!-- แสดง polygon -->
21
- <div class="flex flex-col gap-[16px] w-[334px]">
106
+ <div class="flex flex-col gap-[16px] w-[334px] sticky top-0 self-start">
22
107
  <div class="w-[334px] h-[434px] bg-gray-300 rounded-[12px]"></div>
23
108
  <InputTextField
24
109
  icon-prepend="lucide:locate-fixed"
@@ -27,5 +112,19 @@
27
112
  />
28
113
  </div>
29
114
  </div>
30
- </template>
31
- <script setup lang="ts"></script>
115
+ </template>
116
+
117
+ <script setup>
118
+ import { ref, computed } from "vue";
119
+ const props = defineProps({
120
+ state: { type: String, required: false, default: "personal" }
121
+ });
122
+ const review = ref(false);
123
+ const addressValue = ref({});
124
+ const extraNameCount = ref(0);
125
+ const extraPhones = ref([]);
126
+ const isAddressCompleted = computed(() => {
127
+ const v = addressValue.value;
128
+ return !!(v.province_id && v.amphur_id && v.tambon_id && v.zipcode);
129
+ });
130
+ </script>
@@ -1,3 +1,8 @@
1
- declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
1
+ type __VLS_Props = {
2
+ state?: "personal" | "business" | "backoffice";
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
5
+ state: "personal" | "business" | "backoffice";
6
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
2
7
  declare const _default: typeof __VLS_export;
3
8
  export default _default;
@@ -2,6 +2,7 @@ import type { InputFile } from "@/types/components/input/input-file";
2
2
  interface IFileItem {
3
3
  file?: File;
4
4
  url: string;
5
+ description?: string;
5
6
  }
6
7
  type __VLS_Props = InputFile;
7
8
  type __VLS_ModelProps = {
@@ -35,6 +36,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
35
36
  accept: string;
36
37
  labelIcon: string;
37
38
  disabledDrop: boolean;
39
+ column: boolean;
38
40
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
39
41
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
40
42
  declare const _default: typeof __VLS_export;
@@ -1,35 +1,54 @@
1
1
  <template>
2
2
  <div class="space-y-[4px]">
3
- <div class="flex flex-wrap gap-[8px]">
3
+ <div
4
+ :class="[
5
+ 'flex gap-[8px]',
6
+ props.column ? 'flex-col' : 'flex-wrap flex-row'
7
+ ]"
8
+ >
4
9
  <Draggable v-model="arrFiles">
5
10
  <template #item="{ item, index }">
6
- <div class="relative">
7
- <video
8
- v-if="item.file?.type?.startsWith('video')"
9
- :src="item.url"
10
- class="rounded-[4px] object-cover w-[98px] h-[98px]"
11
- />
12
- <Image
13
- v-else-if="!item.file || item.file?.type?.startsWith('image')"
14
- width="98"
15
- height="98"
16
- :src="item.url"
17
- class="rounded-[4px]"
18
- />
19
- <div
20
- v-else
21
- class="w-[98px] h-[98px] rounded-[4px] flex justify-center items-center"
22
- >
23
- <Icon
24
- name="material-symbols:lab-profile-rounded"
25
- size="34"
26
- ></Icon>
11
+ <div
12
+ :class="[
13
+ props.column ? 'flex gap-[16px] w-full items-start' : 'relative'
14
+ ]"
15
+ >
16
+ <div class="relative shrink-0">
17
+ <video
18
+ v-if="item.file?.type?.startsWith('video')"
19
+ :src="item.url"
20
+ class="rounded-[4px] object-cover w-[98px] h-[98px]"
21
+ />
22
+ <Image
23
+ v-else-if="!item.file || item.file?.type?.startsWith('image')"
24
+ width="98"
25
+ height="98"
26
+ :src="item.url"
27
+ class="rounded-[4px]"
28
+ />
29
+ <div
30
+ v-else
31
+ class="w-[98px] h-[98px] rounded-[4px] flex justify-center items-center"
32
+ >
33
+ <Icon
34
+ name="material-symbols:lab-profile-rounded"
35
+ size="34"
36
+ ></Icon>
37
+ </div>
38
+ <div
39
+ class="absolute top-[2px] right-[2px] p-[1px] w-[20px] h-[20px] bg-white rounded-full cursor-pointer flex justify-center items-center"
40
+ @click="onDeleteImage(index)"
41
+ >
42
+ <Icon name="gravity-ui:xmark" size="16"></Icon>
43
+ </div>
27
44
  </div>
28
- <div
29
- class="absolute top-[2px] right-[2px] p-[1px] w-[20px] h-[20px] bg-white rounded-full cursor-pointer flex justify-center items-center"
30
- @click="onDeleteImage(index)"
31
- >
32
- <Icon name="gravity-ui:xmark" size="16"></Icon>
45
+
46
+ <div v-if="props.column" class="flex-1 w-full h-[98px]">
47
+ <textarea
48
+ v-model="item.description"
49
+ placeholder="เพิ่มคำอธิบาย..."
50
+ class="w-full h-full p-[8px] rounded-[4px] border-[1px] border-cloud outline-none focus:border-primary focus:ring-1 focus:ring-primary resize-none font-body text-black bg-white"
51
+ ></textarea>
33
52
  </div>
34
53
  </div>
35
54
  </template>
@@ -40,11 +59,12 @@
40
59
  :class="[
41
60
  'relative flex flex-col gap-[4px] justify-center items-center cursor-pointer',
42
61
  'rounded-[4px] border-dashed',
62
+ props.column ? 'self-start' : '',
43
63
  actionOnDrop
44
64
  ]"
45
65
  :style="{
46
- width: props.fullWidth ? '100%' : props.width,
47
- height: props.fullHeight ? '100%' : props.height
66
+ width: props.fullWidth ? '100%' : String(props.width).match(/^[0-9]+$/) ? `${props.width}px` : props.width,
67
+ height: props.fullHeight ? '100%' : String(props.height).match(/^[0-9]+$/) ? `${props.height}px` : props.height
48
68
  }"
49
69
  @dragover.prevent="onDragOver"
50
70
  @dragenter.prevent
@@ -94,12 +114,13 @@ const props = defineProps({
94
114
  limit: { type: Number, required: false, default: 0 },
95
115
  label: { type: String, required: false, default: "" },
96
116
  labelIcon: { type: String, required: false, default: "" },
97
- width: { type: [Number, String], required: false, default: 98 },
98
- height: { type: [Number, String], required: false, default: 98 },
117
+ width: { type: [Number, String], required: false, default: 100 },
118
+ height: { type: [Number, String], required: false, default: 100 },
99
119
  fullWidth: { type: Boolean, required: false, default: false },
100
120
  fullHeight: { type: Boolean, required: false, default: false },
101
121
  disabledDrop: { type: Boolean, required: false, default: false },
102
- disabledErrorMessage: { type: Boolean, required: false, default: false }
122
+ disabledErrorMessage: { type: Boolean, required: false, default: false },
123
+ column: { type: Boolean, required: false, default: false }
103
124
  });
104
125
  const isDrag = ref(false);
105
126
  const { errorMessage, setErrors } = useField(props.name);
@@ -158,12 +179,15 @@ const onSelectFile = (event) => {
158
179
  return;
159
180
  }
160
181
  const fileItems = files.map(createFileItem);
161
- arrFiles.value.push(...fileItems);
182
+ arrFiles.value = [...arrFiles.value || [], ...fileItems];
162
183
  if (input) input.value = "";
163
184
  emit("select", arrFiles.value);
164
185
  };
165
186
  const onDeleteImage = (index) => {
166
- arrFiles.value?.splice(index, 1);
187
+ if (!arrFiles.value) return;
188
+ const newFiles = [...arrFiles.value];
189
+ newFiles.splice(index, 1);
190
+ arrFiles.value = newFiles;
167
191
  };
168
192
  const onDragOver = () => {
169
193
  isDrag.value = true;
@@ -2,6 +2,7 @@ import type { InputFile } from "@/types/components/input/input-file";
2
2
  interface IFileItem {
3
3
  file?: File;
4
4
  url: string;
5
+ description?: string;
5
6
  }
6
7
  type __VLS_Props = InputFile;
7
8
  type __VLS_ModelProps = {
@@ -35,6 +36,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
35
36
  accept: string;
36
37
  labelIcon: string;
37
38
  disabledDrop: boolean;
39
+ column: boolean;
38
40
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
39
41
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
40
42
  declare const _default: typeof __VLS_export;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
- "version": "1.206.0",
3
+ "version": "1.207.0",
4
4
  "description": "pukaad-ui for MeMSG",
5
5
  "repository": {
6
6
  "type": "git",
@@ -93,4 +93,4 @@
93
93
  "@types/vue-cropperjs": "^4.1.6",
94
94
  "@vue/compiler-sfc": "^3.5.24"
95
95
  }
96
- }
96
+ }