pukaad-ui-lib 1.317.0 → 1.318.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.
Files changed (37) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +5 -0
  3. package/dist/runtime/assets/svg/flags/cn.svg +15 -0
  4. package/dist/runtime/assets/svg/flags/th.svg +12 -0
  5. package/dist/runtime/components/button.vue +10 -10
  6. package/dist/runtime/components/carousel.vue +31 -31
  7. package/dist/runtime/components/display/display-image-place.vue +98 -98
  8. package/dist/runtime/components/drawer/drawer-suggest-place/drawer-suggest-place.vue +2 -2
  9. package/dist/runtime/components/drawer/drawer-suggest-place/suggest-place-form.d.vue.ts +2 -2
  10. package/dist/runtime/components/drawer/drawer-suggest-place/suggest-place-form.vue +78 -43
  11. package/dist/runtime/components/drawer/drawer-suggest-place/suggest-place-form.vue.d.ts +2 -2
  12. package/dist/runtime/components/input/input-address.d.vue.ts +1 -1
  13. package/dist/runtime/components/input/input-address.vue.d.ts +1 -1
  14. package/dist/runtime/components/input/input-file.d.vue.ts +1 -1
  15. package/dist/runtime/components/input/input-file.vue.d.ts +1 -1
  16. package/dist/runtime/components/input/input-link.d.vue.ts +4 -0
  17. package/dist/runtime/components/input/input-link.vue +14 -38
  18. package/dist/runtime/components/input/input-link.vue.d.ts +4 -0
  19. package/dist/runtime/components/input/input-localized-name.d.vue.ts +21 -0
  20. package/dist/runtime/components/input/input-localized-name.vue +85 -0
  21. package/dist/runtime/components/input/input-localized-name.vue.d.ts +21 -0
  22. package/dist/runtime/components/input/input-password.d.vue.ts +1 -1
  23. package/dist/runtime/components/input/input-password.vue.d.ts +1 -1
  24. package/dist/runtime/components/input/input-rating.vue +6 -6
  25. package/dist/runtime/components/input/input-suggest.d.vue.ts +32 -0
  26. package/dist/runtime/components/input/input-suggest.vue +168 -0
  27. package/dist/runtime/components/input/input-suggest.vue.d.ts +32 -0
  28. package/dist/runtime/components/input/input-typed-field.d.vue.ts +29 -0
  29. package/dist/runtime/components/input/input-typed-field.vue +76 -0
  30. package/dist/runtime/components/input/input-typed-field.vue.d.ts +29 -0
  31. package/dist/runtime/components/modal/modal-media-view.vue +136 -136
  32. package/dist/runtime/components/modal/modal-password-confirmed.d.vue.ts +1 -1
  33. package/dist/runtime/components/modal/modal-password-confirmed.vue.d.ts +1 -1
  34. package/dist/runtime/components/modal/modal-password-verify.d.vue.ts +1 -1
  35. package/dist/runtime/components/modal/modal-password-verify.vue.d.ts +1 -1
  36. package/package.json +1 -1
  37. /package/dist/runtime/assets/svg/socials/{WhatsApp.svg → Whatsapp.svg} +0 -0
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
3
  "configKey": "pukaadUI",
4
- "version": "1.317.0",
4
+ "version": "1.318.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -95,6 +95,11 @@ const module$1 = defineNuxtModule({
95
95
  prefix: "pukaad-social",
96
96
  dir: resolver.resolve("./runtime/assets/svg/socials"),
97
97
  normalizeIconName: false
98
+ },
99
+ {
100
+ prefix: "pukaad-flag",
101
+ dir: resolver.resolve("./runtime/assets/svg/flags"),
102
+ normalizeIconName: false
98
103
  }
99
104
  ]
100
105
  });
@@ -0,0 +1,15 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_8131_652)">
3
+ <path d="M0 0H16V16H0V0Z" fill="#EE1C25"/>
4
+ <path d="M6.39998 4.21748L2.31998 6.85748L3.99998 2.53748L5.43998 6.85748L1.59998 4.21748H6.39998Z" fill="#FFFF00"/>
5
+ <path d="M7.393 1.97026L8.84678 2.68229L7.32506 2.94376L8.3115 1.79144L8.21651 3.3408L7.393 1.97026Z" fill="#FFFF00"/>
6
+ <path d="M9.24584 3.38149L10.3087 4.60393L8.80415 4.25241L10.1622 3.57431L9.47128 4.96553L9.24584 3.38149Z" fill="#FFFF00"/>
7
+ <path d="M9.58044 5.7041L10.0517 7.2548L8.82121 6.31908L10.3385 6.25456L9.13918 7.24294L9.58044 5.7041Z" fill="#FFFF00"/>
8
+ <path d="M8.31278 7.36347L8.14918 8.97427L7.37598 7.63747L8.79918 8.16307L7.31278 8.61147L8.31278 7.36347Z" fill="#FFFF00"/>
9
+ </g>
10
+ <defs>
11
+ <clipPath id="clip0_8131_652">
12
+ <rect width="16" height="16" rx="8" fill="white"/>
13
+ </clipPath>
14
+ </defs>
15
+ </svg>
@@ -0,0 +1,12 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_8131_1050)">
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H16V16H0V0Z" fill="#F4F5F8"/>
4
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M0 5.4187H16V10.75H0V5.4187Z" fill="#2D2A4A"/>
5
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H16V2.75H0V0ZM0 13.3344H16V16H0V13.3344Z" fill="#A51931"/>
6
+ </g>
7
+ <defs>
8
+ <clipPath id="clip0_8131_1050">
9
+ <rect width="16" height="16" rx="8" fill="white"/>
10
+ </clipPath>
11
+ </defs>
12
+ </svg>
@@ -4,16 +4,16 @@
4
4
  props.variant === 'text' || props.variant === 'link' ? '!p-0 !h-auto' : '',
5
5
  props.circle && 'rounded-full aspect-square',
6
6
  props.class
7
- ]" :variant="props.variant" :color="props.color" :size="resolvedSize" :type="props.type"
8
- :disabled="props.loading || props.disabled">
9
- <span class="inline-grid place-items-center">
10
- <span class="inline-flex items-center justify-center gap-[8px] [grid-area:1/1]"
11
- :class="{ invisible: props.loading }">
12
- <slot />
13
- </span>
14
- <ShadSpinner v-if="props.loading" class="[grid-area:1/1]" />
15
- </span>
16
- </ShadButton>
7
+ ]" :variant="props.variant" :color="props.color" :size="resolvedSize" :type="props.type"
8
+ :disabled="props.loading || props.disabled">
9
+ <span class="inline-grid place-items-center">
10
+ <span class="inline-flex items-center justify-center gap-[8px] [grid-area:1/1]"
11
+ :class="{ invisible: props.loading }">
12
+ <slot />
13
+ </span>
14
+ <ShadSpinner v-if="props.loading" class="[grid-area:1/1]" />
15
+ </span>
16
+ </ShadButton>
17
17
  </template>
18
18
 
19
19
  <script setup>
@@ -1,35 +1,35 @@
1
1
  <template>
2
- <div class="relative w-full h-full">
3
- <div class="absolute top-[20px] left-1/2 transform -translate-x-1/2 z-[50]">
4
- <div class="rounded-full py-[6px] px-[16px] bg-black/60 flex items-center justify-center">
5
- <span class="text-white text-sm font-medium tracking-widest">
6
- {{ currentIndex + 1 }} / {{ props.items.length }}
7
- </span>
8
- </div>
9
- </div>
10
-
11
- <ShadCarousel
12
- :opts="{ startIndex: props.selectIndex }"
13
- class="carousel-media w-full h-full"
14
- @init-api="onInitApi"
15
- >
16
- <ShadCarouselContent class="h-full">
17
- <ShadCarouselItem
18
- v-for="(item, i) in props.items"
19
- :key="i"
20
- class="h-full flex items-center justify-center"
21
- >
22
- <img
23
- :src="item.url"
24
- class="w-full h-full object-contain select-none"
25
- draggable="false"
26
- />
27
- </ShadCarouselItem>
28
- </ShadCarouselContent>
29
- <ShadCarouselPrevious v-if="props.items.length > 1" class="!left-[12px]" />
30
- <ShadCarouselNext v-if="props.items.length > 1" class="!right-[12px]" />
31
- </ShadCarousel>
32
- </div>
2
+ <div class="relative w-full h-full">
3
+ <div class="absolute top-[20px] left-1/2 transform -translate-x-1/2 z-[50]">
4
+ <div class="rounded-full py-[6px] px-[16px] bg-black/60 flex items-center justify-center">
5
+ <span class="text-white text-sm font-medium tracking-widest">
6
+ {{ currentIndex + 1 }} / {{ props.items.length }}
7
+ </span>
8
+ </div>
9
+ </div>
10
+
11
+ <ShadCarousel
12
+ :opts="{ startIndex: props.selectIndex }"
13
+ class="carousel-media w-full h-full"
14
+ @init-api="onInitApi"
15
+ >
16
+ <ShadCarouselContent class="h-full">
17
+ <ShadCarouselItem
18
+ v-for="(item, i) in props.items"
19
+ :key="i"
20
+ class="h-full flex items-center justify-center"
21
+ >
22
+ <img
23
+ :src="item.url"
24
+ class="w-full h-full object-contain select-none"
25
+ draggable="false"
26
+ />
27
+ </ShadCarouselItem>
28
+ </ShadCarouselContent>
29
+ <ShadCarouselPrevious v-if="props.items.length > 1" class="!left-[12px]" />
30
+ <ShadCarouselNext v-if="props.items.length > 1" class="!right-[12px]" />
31
+ </ShadCarousel>
32
+ </div>
33
33
  </template>
34
34
 
35
35
  <script setup>
@@ -1,102 +1,102 @@
1
1
  <template>
2
- <!-- Personal state → Featured layout — 1 รูป: w-full -->
3
- <div v-if="layoutMode === 'featured' && photos.length === 1" class="h-[360px]">
4
- <div class="w-full h-full rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 0)">
5
- <Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
6
- </div>
7
- </div>
8
-
9
- <!-- Personal state → Featured layout — 2 รูป: แบ่งครึ่ง w-full -->
10
- <div v-else-if="layoutMode === 'featured' && photos.length === 2" class="flex gap-[8px] h-[360px]">
11
- <div class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 0)">
12
- <Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
13
- </div>
14
- <div class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 1)">
15
- <Image :src="photos[1].url" class="w-full h-full object-cover" width="auto" height="auto" />
16
- </div>
17
- </div>
18
-
19
- <!-- Personal state → Featured layout — 3+ รูป: layout เดิม -->
20
- <div v-else-if="layoutMode === 'featured'" class="flex gap-[8px] h-[360px]">
21
- <div v-if="photos[0]" class="w-[620px] shrink-0 rounded-[8px] overflow-hidden cursor-pointer"
22
- @click="openLightbox('photo', 0)">
23
- <Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
24
- </div>
25
-
26
- <div v-if="photos[1]" class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 1)">
27
- <Image :src="photos[1].url" class="w-full h-full object-cover" width="auto" height="auto" />
28
- </div>
29
-
30
- <div class="w-[306px] shrink-0 flex flex-col gap-[8px]">
31
- <div v-if="photos[2]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
32
- @click="openLightbox('photo', 2)">
33
- <Image :src="photos[2].url" class="w-full h-full object-cover" width="auto" height="auto" />
34
- <div v-if="featuredPhoto2Overflow > 0"
35
- class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
36
- <Icon name="fa6-solid:image" :size="42" />
37
- <span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto2Overflow) }})</span>
38
- </div>
39
- </div>
40
-
41
- <div v-if="videos[0]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
42
- @click="openLightbox('video', 0)">
43
- <Image :src="videos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
44
- <div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
45
- <Icon name="fa6-solid:video" :size="42" />
46
- <span class="font-body-large">วิดีโอ ({{ videos.length }})</span>
47
- </div>
48
- </div>
49
- <div v-else-if="photos[3]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
50
- @click="openLightbox('photo', 3)">
51
- <Image :src="photos[3].url" class="w-full h-full object-cover" width="auto" height="auto" />
52
- <div v-if="featuredPhoto3Overflow > 0"
53
- class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
54
- <Icon name="fa6-solid:image" :size="42" />
55
- <span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto3Overflow) }})</span>
56
- </div>
57
- </div>
58
- </div>
59
- </div>
60
-
61
- <!-- Office / Business state → Album layout -->
62
- <div v-else-if="layoutMode === 'album'" class="grid grid-cols-5 gap-[10px]">
63
- <!-- Row 1: photos[0..3] -->
64
- <div v-for="(photo, i) in galleryRow1" :key="`g1-${i}`"
65
- class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', i)">
66
- <Image :src="photo.url" class="w-full h-full object-cover" width="auto" height="auto" />
67
- </div>
68
-
69
- <!-- Row 1 col 5: image count overlay -->
70
- <div v-show="galleryCountPhoto != null"
71
- class="w-[132px] h-[132px] relative rounded-[8px] overflow-hidden cursor-pointer"
72
- @click="openLightbox('photo', 8)">
73
- <Image v-if="galleryCountPhoto" :src="galleryCountPhoto.url" class="w-full h-full object-cover" width="auto"
74
- height="auto" />
75
- <div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
76
- <Icon name="fa6-solid:image" :size="35" />
77
- <span class="font-label-medium">รูปภาพ ({{ convertNumber(photos.length - 8) }})</span>
78
- </div>
79
- </div>
80
-
81
- <!-- Row 2: photos[4..7] -->
82
- <div v-for="(photo, i) in galleryRow2" :key="`g2-${i}`"
83
- class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 4 + i)">
84
- <Image :src="photo.url" class="w-full h-full object-cover" width="auto" height="auto" />
85
- </div>
86
-
87
- <!-- Row 2 col 5: video overlay -->
88
- <div v-if="videos[0]" class="w-[132px] h-[132px] relative rounded-[8px] overflow-hidden cursor-pointer"
89
- @click="openLightbox('video', 0)">
90
- <Image :src="videos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
91
- <div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
92
- <Icon name="fa6-solid:video" :size="35" />
93
- <span class="font-label-medium">วิดีโอ ({{ videos.length }})</span>
94
- </div>
95
- </div>
96
- </div>
97
-
98
- <!-- Lightbox modal -->
99
- <ModalMediaView v-model="isMediaOpen" :items="mediaItems" :title="props.title" :start-index="mediaStartIndex" />
2
+ <!-- Personal state → Featured layout — 1 รูป: w-full -->
3
+ <div v-if="layoutMode === 'featured' && photos.length === 1" class="h-[360px]">
4
+ <div class="w-full h-full rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 0)">
5
+ <Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
6
+ </div>
7
+ </div>
8
+
9
+ <!-- Personal state → Featured layout — 2 รูป: แบ่งครึ่ง w-full -->
10
+ <div v-else-if="layoutMode === 'featured' && photos.length === 2" class="flex gap-[8px] h-[360px]">
11
+ <div class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 0)">
12
+ <Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
13
+ </div>
14
+ <div class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 1)">
15
+ <Image :src="photos[1].url" class="w-full h-full object-cover" width="auto" height="auto" />
16
+ </div>
17
+ </div>
18
+
19
+ <!-- Personal state → Featured layout — 3+ รูป: layout เดิม -->
20
+ <div v-else-if="layoutMode === 'featured'" class="flex gap-[8px] h-[360px]">
21
+ <div v-if="photos[0]" class="w-[620px] shrink-0 rounded-[8px] overflow-hidden cursor-pointer"
22
+ @click="openLightbox('photo', 0)">
23
+ <Image :src="photos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
24
+ </div>
25
+
26
+ <div v-if="photos[1]" class="flex-1 rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 1)">
27
+ <Image :src="photos[1].url" class="w-full h-full object-cover" width="auto" height="auto" />
28
+ </div>
29
+
30
+ <div class="w-[306px] shrink-0 flex flex-col gap-[8px]">
31
+ <div v-if="photos[2]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
32
+ @click="openLightbox('photo', 2)">
33
+ <Image :src="photos[2].url" class="w-full h-full object-cover" width="auto" height="auto" />
34
+ <div v-if="featuredPhoto2Overflow > 0"
35
+ class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
36
+ <Icon name="fa6-solid:image" :size="42" />
37
+ <span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto2Overflow) }})</span>
38
+ </div>
39
+ </div>
40
+
41
+ <div v-if="videos[0]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
42
+ @click="openLightbox('video', 0)">
43
+ <Image :src="videos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
44
+ <div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
45
+ <Icon name="fa6-solid:video" :size="42" />
46
+ <span class="font-body-large">วิดีโอ ({{ videos.length }})</span>
47
+ </div>
48
+ </div>
49
+ <div v-else-if="photos[3]" class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
50
+ @click="openLightbox('photo', 3)">
51
+ <Image :src="photos[3].url" class="w-full h-full object-cover" width="auto" height="auto" />
52
+ <div v-if="featuredPhoto3Overflow > 0"
53
+ class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
54
+ <Icon name="fa6-solid:image" :size="42" />
55
+ <span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto3Overflow) }})</span>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+
61
+ <!-- Office / Business state → Album layout -->
62
+ <div v-else-if="layoutMode === 'album'" class="grid grid-cols-5 gap-[10px]">
63
+ <!-- Row 1: photos[0..3] -->
64
+ <div v-for="(photo, i) in galleryRow1" :key="`g1-${i}`"
65
+ class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', i)">
66
+ <Image :src="photo.url" class="w-full h-full object-cover" width="auto" height="auto" />
67
+ </div>
68
+
69
+ <!-- Row 1 col 5: image count overlay -->
70
+ <div v-show="galleryCountPhoto != null"
71
+ class="w-[132px] h-[132px] relative rounded-[8px] overflow-hidden cursor-pointer"
72
+ @click="openLightbox('photo', 8)">
73
+ <Image v-if="galleryCountPhoto" :src="galleryCountPhoto.url" class="w-full h-full object-cover" width="auto"
74
+ height="auto" />
75
+ <div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
76
+ <Icon name="fa6-solid:image" :size="35" />
77
+ <span class="font-label-medium">รูปภาพ ({{ convertNumber(photos.length - 8) }})</span>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- Row 2: photos[4..7] -->
82
+ <div v-for="(photo, i) in galleryRow2" :key="`g2-${i}`"
83
+ class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer" @click="openLightbox('photo', 4 + i)">
84
+ <Image :src="photo.url" class="w-full h-full object-cover" width="auto" height="auto" />
85
+ </div>
86
+
87
+ <!-- Row 2 col 5: video overlay -->
88
+ <div v-if="videos[0]" class="w-[132px] h-[132px] relative rounded-[8px] overflow-hidden cursor-pointer"
89
+ @click="openLightbox('video', 0)">
90
+ <Image :src="videos[0].url" class="w-full h-full object-cover" width="auto" height="auto" />
91
+ <div class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]">
92
+ <Icon name="fa6-solid:video" :size="35" />
93
+ <span class="font-label-medium">วิดีโอ ({{ videos.length }})</span>
94
+ </div>
95
+ </div>
96
+ </div>
97
+
98
+ <!-- Lightbox modal -->
99
+ <ModalMediaView v-model="isMediaOpen" :items="mediaItems" :title="props.title" :start-index="mediaStartIndex" />
100
100
  </template>
101
101
 
102
102
  <script setup>
@@ -152,6 +152,7 @@ watch(isOpen, (val) => {
152
152
  extraPhones: [],
153
153
  contactChannels: [],
154
154
  categories: [],
155
+ localizedNames: [],
155
156
  reviewPhotos: []
156
157
  };
157
158
  }, 300);
@@ -189,8 +190,7 @@ const onSubmit = async () => {
189
190
  method,
190
191
  body: {
191
192
  business_name: d.businessName ?? "",
192
- name_th: d.nameTh ?? "",
193
- name_en: d.nameEn ?? "",
193
+ localized_names: (d.localizedNames ?? []).map((ln) => ({ language: ln.language, name: ln.name })),
194
194
  description: d.description ?? "",
195
195
  address: {
196
196
  province_id: d.address?.province_id,
@@ -1,4 +1,5 @@
1
1
  import type { InputAddressValue } from "#pukaad-ui/runtime/components/input/input-address.vue";
2
+ import type { LocalizedNameItem } from "#pukaad-ui/types/components/input/input-localized-name";
2
3
  interface FileItem {
3
4
  file?: File;
4
5
  url: string;
@@ -14,8 +15,7 @@ export interface SuggestPlaceData {
14
15
  lng: number;
15
16
  } | null;
16
17
  businessName?: string;
17
- nameTh?: string;
18
- nameEn?: string;
18
+ localizedNames?: LocalizedNameItem[];
19
19
  categories?: {
20
20
  id?: string;
21
21
  value?: string;
@@ -2,11 +2,15 @@
2
2
  <div class="flex gap-[16px] w-full">
3
3
  <!-- กรอกข้อมูล -->
4
4
  <div class="flex flex-col gap-[16px] w-[490px]">
5
- <InputAddress name="address" v-model="modelValue.address" :fixed-province-id="props.fixedProvinceId" />
5
+ <InputAddress
6
+ name="address"
7
+ v-model="modelValue.address"
8
+ :fixed-province-id="props.fixedProvinceId"
9
+ />
6
10
  <template v-if="isAddressCompleted">
7
11
  <div class="font-body-large-prominent">รายละเอียด</div>
8
12
  <div class="flex flex-col gap-[4px]">
9
- <InputAutocomplete
13
+ <InputSuggest
10
14
  name="businessName"
11
15
  v-model="modelValue.businessName"
12
16
  label="ชื่อสถานที่ธุรกิจ"
@@ -14,33 +18,14 @@
14
18
  required
15
19
  show-counter
16
20
  :limit="180"
17
- :free-text="true"
18
21
  :fetch-fn="fetchApprovedPlaces"
19
22
  value-key="business_name"
20
23
  label-key="business_name"
21
24
  />
22
- <InputTextField
23
- v-if="extraNameCount >= 1"
24
- name="nameTh"
25
- v-model="modelValue.nameTh"
26
- label="ชื่อภาษาไทย"
27
- placeholder="ใส่ในกรณีที่ต่างจากชื่อหลัก"
28
- show-counter
29
- :limit="180"
25
+ <InputLocalizedName
26
+ name="localizedNames"
27
+ v-model="modelValue.localizedNames"
30
28
  />
31
- <InputTextField
32
- v-if="extraNameCount >= 2"
33
- name="nameEn"
34
- v-model="modelValue.nameEn"
35
- label="ชื่อภาษาอังกฤษ"
36
- placeholder="ใส่ในกรณีที่ต่างจากชื่อหลัก"
37
- show-counter
38
- :limit="180"
39
- />
40
- <Button v-if="extraNameCount < 2" variant="text" color="primary" class="w-[145px]" @click="extraNameCount++">
41
- <Icon name="lucide:plus" />
42
- เพิ่มชื่อสถานที่
43
- </Button>
44
29
  </div>
45
30
  <InputCombobox
46
31
  name="categories"
@@ -61,9 +46,17 @@
61
46
  :limit="220"
62
47
  show-counter
63
48
  />
64
- <InputDateOpening name="openingHours" v-model="modelValue.openingHours" />
49
+ <InputDateOpening
50
+ name="openingHours"
51
+ v-model="modelValue.openingHours"
52
+ />
65
53
  <div class="flex flex-col gap-[8px]">
66
- <InputTextField name="phone" v-model="modelValue.phone" label="เบอร์โทรศัพท์" placeholder="กรอกเบอร์โทรศัพท์" />
54
+ <InputTextField
55
+ name="phone"
56
+ v-model="modelValue.phone"
57
+ label="เบอร์โทรศัพท์"
58
+ placeholder="กรอกเบอร์โทรศัพท์"
59
+ />
67
60
  <InputTextField
68
61
  v-for="(_, index) in modelValue.extraPhones"
69
62
  :key="index"
@@ -72,17 +65,35 @@
72
65
  placeholder="กรอกเบอร์โทรศัพท์"
73
66
  v-model="modelValue.extraPhones[index]"
74
67
  />
75
- <Button variant="text" color="primary" class="w-[145px]" @click="modelValue.extraPhones.push('')">
68
+ <Button
69
+ variant="text"
70
+ color="primary"
71
+ class="w-[145px]"
72
+ @click="modelValue.extraPhones.push('')"
73
+ >
76
74
  <Icon name="lucide:plus" />
77
75
  เพิ่มเบอร์โทรศัพท์
78
76
  </Button>
79
- <InputLink name="contactChannels" format="contact_channel" default-first v-model="modelValue.contactChannels" />
77
+ <InputLink
78
+ name="contactChannels"
79
+ format="contact_channel"
80
+ default-first
81
+ v-model="modelValue.contactChannels"
82
+ />
80
83
  </div>
81
84
  <template v-if="props.state === 'personal'">
82
85
  <div class="flex flex-col gap-[16px]">
83
- <InputCheckbox name="isReview" v-model="modelValue.isReview" label="คุณต้องการรีวิวสถานที่นี้" />
86
+ <InputCheckbox
87
+ name="isReview"
88
+ v-model="modelValue.isReview"
89
+ label="คุณต้องการรีวิวสถานที่นี้"
90
+ />
84
91
  <template v-if="modelValue.isReview">
85
- <InputRating name="rating" v-model="modelValue.rating" class="flex py-4 justify-center" />
92
+ <InputRating
93
+ name="rating"
94
+ v-model="modelValue.rating"
95
+ class="flex py-4 justify-center"
96
+ />
86
97
  <InputTextarea
87
98
  name="reviewDescription"
88
99
  v-model="modelValue.reviewDescription"
@@ -92,12 +103,20 @@
92
103
  />
93
104
  <div class="flex flex-col gap-[8px]">
94
105
  <div class="flex flex-col gap-[4px]">
95
- <div class="font-body-large-prominent text-gray">เพิ่มภาพถ่าย</div>
106
+ <div class="font-body-large-prominent text-gray">
107
+ เพิ่มภาพถ่าย
108
+ </div>
96
109
  <div class="font-body-small text-gray">สูงสุด 9 รายการ</div>
97
110
  </div>
98
- <InputFile name="reviewPhotos" v-model="modelValue.reviewPhotos" accept="image/*" :limit="9" />
111
+ <InputFile
112
+ name="reviewPhotos"
113
+ v-model="modelValue.reviewPhotos"
114
+ accept="image/*"
115
+ :limit="9"
116
+ />
99
117
  <div class="font-body-small text-gray w-[250px]">
100
- รองรับไฟล์ *.jpg *.jpeg *.png *.webp *.bmp *.gif ขนาดไฟล์ไม่เกิน 30 mb
118
+ รองรับไฟล์ *.jpg *.jpeg *.png *.webp *.bmp *.gif
119
+ ขนาดไฟล์ไม่เกิน 30 mb
101
120
  </div>
102
121
  </div>
103
122
  </template>
@@ -128,9 +147,10 @@
128
147
  </template>
129
148
 
130
149
  <script setup>
131
- import { ref, computed, onMounted, watch, reactive } from "vue";
150
+ import { computed, onMounted, watch, reactive, ref } from "vue";
132
151
  import { useApi } from "#pukaad-ui/runtime/composables/useApi";
133
152
  import SuggestPlaceMap from "./suggest-place-map.vue";
153
+ import InputLocalizedName from "../../input/input-localized-name.vue";
134
154
  const props = defineProps({
135
155
  state: { type: String, required: false, default: "personal" },
136
156
  fixedProvinceId: { type: Number, required: false }
@@ -141,18 +161,26 @@ const modelValue = reactive({
141
161
  address: {},
142
162
  extraPhones: [],
143
163
  categories: [],
164
+ localizedNames: [],
144
165
  reviewPhotos: [],
145
166
  photos: [],
146
167
  videos: [],
147
168
  ..._model.value
148
169
  });
149
- watch(_model, (val) => {
150
- if (val) Object.assign(modelValue, val);
151
- }, { deep: false });
152
- watch(modelValue, (val) => {
153
- _model.value = { ...val };
154
- }, { deep: true });
155
- const extraNameCount = ref(modelValue.nameEn ? 2 : modelValue.nameTh ? 1 : 0);
170
+ watch(
171
+ _model,
172
+ (val) => {
173
+ if (val) Object.assign(modelValue, val);
174
+ },
175
+ { deep: false }
176
+ );
177
+ watch(
178
+ modelValue,
179
+ (val) => {
180
+ _model.value = { ...val };
181
+ },
182
+ { deep: true }
183
+ );
156
184
  const isAddressCompleted = computed(() => {
157
185
  const v = modelValue.address || {};
158
186
  return !!(v.province_id && v.amphur_id && v.tambon_id && v.zipcode);
@@ -180,7 +208,12 @@ const categoryEndpointMap = {
180
208
  const fetchApprovedPlaces = async (page, pageSize, search) => {
181
209
  const base = listEndpointMap[props.state] ?? "/personal/suggest-places";
182
210
  const res = await api(base, {
183
- query: { q: search ?? "", status: "PENDING,APPROVED", page, page_size: pageSize }
211
+ query: {
212
+ q: search ?? "",
213
+ status: "PENDING,APPROVED",
214
+ page,
215
+ page_size: pageSize
216
+ }
184
217
  });
185
218
  const data = (res.data ?? []).map((item) => ({
186
219
  ...item,
@@ -195,7 +228,9 @@ const categoryOptions = ref([]);
195
228
  onMounted(async () => {
196
229
  try {
197
230
  const endpoint = categoryEndpointMap[props.state] ?? "/personal/suggest-places/categories";
198
- const res = await api(endpoint);
231
+ const res = await api(
232
+ endpoint
233
+ );
199
234
  categoryOptions.value = (res.data ?? []).map((c) => ({
200
235
  label: c.category_name,
201
236
  value: c.id
@@ -1,4 +1,5 @@
1
1
  import type { InputAddressValue } from "#pukaad-ui/runtime/components/input/input-address.vue";
2
+ import type { LocalizedNameItem } from "#pukaad-ui/types/components/input/input-localized-name";
2
3
  interface FileItem {
3
4
  file?: File;
4
5
  url: string;
@@ -14,8 +15,7 @@ export interface SuggestPlaceData {
14
15
  lng: number;
15
16
  } | null;
16
17
  businessName?: string;
17
- nameTh?: string;
18
- nameEn?: string;
18
+ localizedNames?: LocalizedNameItem[];
19
19
  categories?: {
20
20
  id?: string;
21
21
  value?: string;
@@ -49,11 +49,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
49
49
  label: string;
50
50
  name: string;
51
51
  required: boolean;
52
+ gap: string;
52
53
  placeholder: string;
53
54
  labelDetail: string;
54
55
  placeholderDetail: string;
55
56
  requiredDetail: boolean;
56
- gap: string;
57
57
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
58
58
  declare const _default: typeof __VLS_export;
59
59
  export default _default;
@@ -49,11 +49,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
49
49
  label: string;
50
50
  name: string;
51
51
  required: boolean;
52
+ gap: string;
52
53
  placeholder: string;
53
54
  labelDetail: string;
54
55
  placeholderDetail: string;
55
56
  requiredDetail: boolean;
56
- gap: string;
57
57
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
58
58
  declare const _default: typeof __VLS_export;
59
59
  export default _default;