pukaad-ui-lib 1.356.0 → 1.358.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.356.0",
4
+ "version": "1.358.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -247,6 +247,7 @@ const module$1 = defineNuxtModule({
247
247
  _nuxt.options.runtimeConfig.public.RECAPTCHA_KEY ??= process.env.RECAPTCHA_KEY ?? "";
248
248
  _nuxt.hook("prepare:types", ({ references }) => {
249
249
  references.push({ path: resolver.resolve("./types/convert.d.ts") });
250
+ references.push({ path: resolver.resolve("./types/google-translate.d.ts") });
250
251
  });
251
252
  }
252
253
  });
@@ -1,58 +1,139 @@
1
1
  <template>
2
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" />
3
+ <div
4
+ v-if="layoutMode === 'featured' && photos.length === 1"
5
+ class="h-[360px]"
6
+ >
7
+ <div
8
+ class="w-full h-full rounded-[8px] overflow-hidden cursor-pointer"
9
+ @click="openLightbox('photo', 0)"
10
+ >
11
+ <Image
12
+ :src="photos[0].url"
13
+ class="w-full h-full object-cover"
14
+ width="auto"
15
+ height="auto"
16
+ />
6
17
  </div>
7
18
  </div>
8
19
 
9
20
  <!-- 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" />
21
+ <div
22
+ v-else-if="layoutMode === 'featured' && photos.length === 2"
23
+ class="flex gap-[8px] h-[360px]"
24
+ >
25
+ <div
26
+ class="flex-1 rounded-[8px] overflow-hidden cursor-pointer"
27
+ @click="openLightbox('photo', 0)"
28
+ >
29
+ <Image
30
+ :src="photos[0].url"
31
+ class="w-full h-full object-cover"
32
+ width="auto"
33
+ height="auto"
34
+ />
13
35
  </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" />
36
+ <div
37
+ class="flex-1 rounded-[8px] overflow-hidden cursor-pointer"
38
+ @click="openLightbox('photo', 1)"
39
+ >
40
+ <Image
41
+ :src="photos[1].url"
42
+ class="w-full h-full object-cover"
43
+ width="auto"
44
+ height="auto"
45
+ />
16
46
  </div>
17
47
  </div>
18
48
 
19
49
  <!-- Personal state → Featured layout — 3+ รูป: layout เดิม -->
20
50
  <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" />
51
+ <div
52
+ v-if="photos[0]"
53
+ class="w-[620px] shrink-0 rounded-[8px] overflow-hidden cursor-pointer"
54
+ @click="openLightbox('photo', 0)"
55
+ >
56
+ <Image
57
+ :src="photos[0].url"
58
+ class="w-full h-full object-cover"
59
+ width="auto"
60
+ height="auto"
61
+ />
24
62
  </div>
25
63
 
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" />
64
+ <div
65
+ v-if="photos[1]"
66
+ class="flex-1 rounded-[8px] overflow-hidden cursor-pointer"
67
+ @click="openLightbox('photo', 1)"
68
+ >
69
+ <Image
70
+ :src="photos[1].url"
71
+ class="w-full h-full object-cover"
72
+ width="auto"
73
+ height="auto"
74
+ />
28
75
  </div>
29
76
 
30
77
  <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]">
78
+ <div
79
+ v-if="photos[2]"
80
+ class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
81
+ @click="openLightbox('photo', 2)"
82
+ >
83
+ <Image
84
+ :src="photos[2].url"
85
+ class="w-full h-full object-cover"
86
+ width="auto"
87
+ height="auto"
88
+ />
89
+ <div
90
+ v-if="featuredPhoto2Overflow > 0"
91
+ class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]"
92
+ >
36
93
  <Icon name="fa6-solid:image" :size="42" />
37
- <span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto2Overflow) }})</span>
94
+ <span class="font-body-large"
95
+ >รูปภาพ ({{ convertNumber(featuredPhoto2Overflow) }})</span
96
+ >
38
97
  </div>
39
98
  </div>
40
99
 
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]">
100
+ <div
101
+ v-if="videos[0]"
102
+ class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
103
+ @click="openLightbox('video', 0)"
104
+ >
105
+ <Image
106
+ :src="videos[0].url"
107
+ class="w-full h-full object-cover"
108
+ width="auto"
109
+ height="auto"
110
+ />
111
+ <div
112
+ class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]"
113
+ >
45
114
  <Icon name="fa6-solid:video" :size="42" />
46
115
  <span class="font-body-large">วิดีโอ ({{ videos.length }})</span>
47
116
  </div>
48
117
  </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]">
118
+ <div
119
+ v-else-if="photos[3]"
120
+ class="flex-1 min-h-0 relative rounded-[8px] overflow-hidden cursor-pointer"
121
+ @click="openLightbox('photo', 3)"
122
+ >
123
+ <Image
124
+ :src="photos[3].url"
125
+ class="w-full h-full object-cover"
126
+ width="auto"
127
+ height="auto"
128
+ />
129
+ <div
130
+ v-if="featuredPhoto3Overflow > 0"
131
+ class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]"
132
+ >
54
133
  <Icon name="fa6-solid:image" :size="42" />
55
- <span class="font-body-large">รูปภาพ ({{ convertNumber(featuredPhoto3Overflow) }})</span>
134
+ <span class="font-body-large"
135
+ >รูปภาพ ({{ convertNumber(featuredPhoto3Overflow) }})</span
136
+ >
56
137
  </div>
57
138
  </div>
58
139
  </div>
@@ -61,34 +142,73 @@
61
142
  <!-- Office / Business state → Album layout -->
62
143
  <div v-else-if="layoutMode === 'album'" class="grid grid-cols-5 gap-[10px]">
63
144
  <!-- 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" />
145
+ <div
146
+ v-for="(photo, i) in galleryRow1"
147
+ :key="`g1-${i}`"
148
+ class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer"
149
+ @click="openLightbox('photo', i)"
150
+ >
151
+ <Image
152
+ :src="photo.url"
153
+ class="w-full h-full object-cover"
154
+ width="auto"
155
+ height="auto"
156
+ />
67
157
  </div>
68
158
 
69
159
  <!-- Row 1 col 5: image count overlay -->
70
- <div v-show="galleryCountPhoto != null"
160
+ <div
161
+ v-show="galleryCountPhoto != null"
71
162
  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]">
163
+ @click="openLightbox('photo', 8)"
164
+ >
165
+ <Image
166
+ v-if="galleryCountPhoto"
167
+ :src="galleryCountPhoto.url"
168
+ class="w-full h-full object-cover"
169
+ width="auto"
170
+ height="auto"
171
+ />
172
+ <div
173
+ class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]"
174
+ >
76
175
  <Icon name="fa6-solid:image" :size="35" />
77
- <span class="font-label-medium">รูปภาพ ({{ convertNumber(photos.length - 8) }})</span>
176
+ <span class="font-label-medium"
177
+ >รูปภาพ ({{ convertNumber(photos.length - 8) }})</span
178
+ >
78
179
  </div>
79
180
  </div>
80
181
 
81
182
  <!-- 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" />
183
+ <div
184
+ v-for="(photo, i) in galleryRow2"
185
+ :key="`g2-${i}`"
186
+ class="w-[132px] h-[132px] rounded-[8px] overflow-hidden cursor-pointer"
187
+ @click="openLightbox('photo', 4 + i)"
188
+ >
189
+ <Image
190
+ :src="photo.url"
191
+ class="w-full h-full object-cover"
192
+ width="auto"
193
+ height="auto"
194
+ />
85
195
  </div>
86
196
 
87
197
  <!-- 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]">
198
+ <div
199
+ v-if="videos[0]"
200
+ class="w-[132px] h-[132px] relative rounded-[8px] overflow-hidden cursor-pointer"
201
+ @click="openLightbox('video', 0)"
202
+ >
203
+ <Image
204
+ :src="videos[0].url"
205
+ class="w-full h-full object-cover"
206
+ width="auto"
207
+ height="auto"
208
+ />
209
+ <div
210
+ class="absolute inset-0 bg-black/50 flex flex-col items-center justify-center text-white gap-[6px]"
211
+ >
92
212
  <Icon name="fa6-solid:video" :size="35" />
93
213
  <span class="font-label-medium">วิดีโอ ({{ videos.length }})</span>
94
214
  </div>
@@ -96,7 +216,12 @@
96
216
  </div>
97
217
 
98
218
  <!-- Lightbox modal -->
99
- <ModalMediaView v-model="isMediaOpen" :items="mediaItems" :title="props.title" :start-index="mediaStartIndex" />
219
+ <ModalMediaView
220
+ v-model="isMediaOpen"
221
+ :items="mediaItems"
222
+ :title="props.title"
223
+ :start-index="mediaStartIndex"
224
+ />
100
225
  </template>
101
226
 
102
227
  <script setup>
@@ -135,7 +260,9 @@ const galleryCountPhoto = computed(
135
260
  const isMediaOpen = ref(false);
136
261
  const mediaType = ref("photo");
137
262
  const mediaStartIndex = ref(0);
138
- const mediaItems = computed(() => mediaType.value === "photo" ? photos.value : videos.value);
263
+ const mediaItems = computed(
264
+ () => mediaType.value === "photo" ? photos.value : videos.value
265
+ );
139
266
  const openLightbox = (type, index) => {
140
267
  mediaType.value = type;
141
268
  mediaStartIndex.value = index;
@@ -0,0 +1,3 @@
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>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <div id="google_translate_element" class="hidden" />
3
+ </template>
4
+
5
+ <script setup>
6
+ import { onMounted, nextTick } from "vue";
7
+ import { useGoogleTranslate } from "#pukaad-ui/runtime/composables/useGoogleTranslate";
8
+ const { activeLanguage, setLanguage } = useGoogleTranslate();
9
+ const initGoogleTranslate = () => {
10
+ const googleTranslate = window.google?.translate;
11
+ if (!googleTranslate?.TranslateElement) return;
12
+ new googleTranslate.TranslateElement(
13
+ {
14
+ pageLanguage: "th",
15
+ includedLanguages: "th,en",
16
+ autoDisplay: false
17
+ },
18
+ "google_translate_element"
19
+ );
20
+ nextTick(() => setLanguage(activeLanguage.value));
21
+ };
22
+ onMounted(() => {
23
+ window.googleTranslateElementInit = initGoogleTranslate;
24
+ if (window.google?.translate) {
25
+ initGoogleTranslate();
26
+ return;
27
+ }
28
+ const existingScript = document.querySelector(
29
+ 'script[src*="translate.google.com/translate_a/element.js"]'
30
+ );
31
+ if (existingScript) return;
32
+ const script = document.createElement("script");
33
+ script.src = "https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit";
34
+ script.async = true;
35
+ document.head.appendChild(script);
36
+ });
37
+ </script>
38
+
39
+ <style>
40
+ .goog-te-banner-frame.skiptranslate,iframe.skiptranslate{display:none!important}body{top:0!important}
41
+ </style>
@@ -0,0 +1,3 @@
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>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -15,10 +15,25 @@
15
15
  <div
16
16
  v-if="!props.review && currentItem?.description"
17
17
  class="absolute bottom-0 w-full z-10"
18
- style="background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, transparent 100%);"
18
+ style="
19
+ background: linear-gradient(
20
+ to top,
21
+ rgba(0, 0, 0, 0.75) 0%,
22
+ transparent 100%
23
+ );
24
+ "
19
25
  >
20
- <div class="p-[16px] flex flex-col gap-[4px] text-white font-body-large">
21
- <div class="font-body-large-prominent">ผู้ดูแลระบบ</div>
26
+ <div
27
+ class="p-[16px] h-[125px] flex flex-col gap-[8px] text-white font-body-large"
28
+ >
29
+ <div class="flex justify-between items-center">
30
+ <div class="font-body-large-prominent">ผู้ดูแลระบบ</div>
31
+ <Icon
32
+ name="lucide:flag"
33
+ class="cursor-pointer"
34
+ @click="isOpenModalReport = true"
35
+ />
36
+ </div>
22
37
  <div>{{ currentItem.description }}</div>
23
38
  </div>
24
39
  </div>
@@ -27,7 +42,13 @@
27
42
  <div
28
43
  v-if="props.review"
29
44
  class="absolute bottom-0 w-full z-10"
30
- style="background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, transparent 100%);"
45
+ style="
46
+ background: linear-gradient(
47
+ to top,
48
+ rgba(0, 0, 0, 0.75) 0%,
49
+ transparent 100%
50
+ );
51
+ "
31
52
  >
32
53
  <div class="flex flex-col w-full">
33
54
  <div
@@ -65,7 +86,11 @@
65
86
  :model-value="props.review.review.rating"
66
87
  />
67
88
  <span class="font-body-small opacity-75">
68
- {{ convertDateTorelativeText(props.review.review.created_at ?? "") }}
89
+ {{
90
+ convertDateTorelativeText(
91
+ props.review.review.created_at ?? ""
92
+ )
93
+ }}
69
94
  </span>
70
95
  </div>
71
96
  </div>
@@ -118,7 +143,11 @@
118
143
  :model-value="props.review.review.rating"
119
144
  />
120
145
  <span class="font-body-small opacity-75">
121
- {{ convertDateTorelativeText(props.review.review.created_at ?? "") }}
146
+ {{
147
+ convertDateTorelativeText(
148
+ props.review.review.created_at ?? ""
149
+ )
150
+ }}
122
151
  </span>
123
152
  </div>
124
153
  </div>
@@ -135,6 +164,12 @@
135
164
  </div>
136
165
  </div>
137
166
  </Modal>
167
+
168
+ <ModalReport
169
+ v-model="isOpenModalReport"
170
+ state="media"
171
+ @submit="onReportSubmit"
172
+ />
138
173
  </template>
139
174
 
140
175
  <script setup>
@@ -150,5 +185,10 @@ const props = defineProps({
150
185
  const isOpen = defineModel({ type: Boolean, ...{ default: false } });
151
186
  const showMoreDescription = ref(false);
152
187
  const currentIndex = ref(props.startIndex ?? 0);
188
+ const isOpenModalReport = ref(false);
153
189
  const currentItem = computed(() => props.items[currentIndex.value] ?? null);
190
+ const onReportSubmit = (payload) => {
191
+ console.log("report payload:", payload);
192
+ isOpenModalReport.value = false;
193
+ };
154
194
  </script>
@@ -1,4 +1,4 @@
1
- import type { ModalReportProps } from "#pukaad-ui/types/components/modal/modal-report";
1
+ import type { ModalReportProps, ModalReportSubmitPayload } from "#pukaad-ui/types/components/modal/modal-report";
2
2
  type __VLS_Props = ModalReportProps;
3
3
  type __VLS_ModelProps = {
4
4
  modelValue?: boolean;
@@ -7,15 +7,9 @@ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
7
7
  declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
8
8
  "update:modelValue": (value: boolean) => any;
9
9
  } & {
10
- submit: (payload: {
11
- value: string;
12
- detail: string;
13
- }) => any;
10
+ submit: (payload: ModalReportSubmitPayload) => any;
14
11
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
15
- onSubmit?: ((payload: {
16
- value: string;
17
- detail: string;
18
- }) => any) | undefined;
12
+ onSubmit?: ((payload: ModalReportSubmitPayload) => any) | undefined;
19
13
  "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
20
14
  }>, {
21
15
  loading: boolean;
@@ -17,33 +17,34 @@
17
17
  name="report_value"
18
18
  :label="item[0]?.label"
19
19
  :items="item"
20
- v-model="formState.report_value"
20
+ v-model="formState.selectedValue"
21
21
  />
22
22
  <div
23
- v-show="formState.report_value === item[0]?.value"
23
+ v-show="formState.selectedValue === item[0]?.value"
24
+ class="mt-[8px]"
24
25
  >
25
26
  <InputTextarea
26
27
  name="report_detail"
27
28
  rows="4"
28
29
  height-scroll
29
- placeholder="คำอธิบายประกอบการรายงาน"
30
- v-model="formState.report_detail"
30
+ :placeholder="placeholderText"
31
+ v-model="formState.description"
31
32
  />
32
33
  </div>
33
34
  </div>
34
35
  </div>
35
36
  <template #footer>
36
- <Button class="w-full" variant="outline" type="button" @click="onCancel">
37
- ยกเลิก
38
- </Button>
39
- <Button
40
- class="w-full"
41
- type="submit"
42
- color="primary"
43
- :disabled="formState.report_value === ''"
44
- >
45
- ส่ง
46
- </Button>
37
+ <Button class="w-full" variant="outline" type="button" @click="onCancel">
38
+ ยกเลิก
39
+ </Button>
40
+ <Button
41
+ class="w-full"
42
+ type="submit"
43
+ color="primary"
44
+ :disabled="formState.selectedValue === ''"
45
+ >
46
+ ส่ง
47
+ </Button>
47
48
  </template>
48
49
  </Modal>
49
50
  </template>
@@ -55,46 +56,50 @@ const props = defineProps({
55
56
  state: { type: String, required: true },
56
57
  loading: { type: Boolean, required: false, default: false }
57
58
  });
58
- const initItems = [
59
- "\u0E40\u0E19\u0E37\u0E49\u0E2D\u0E2B\u0E32\u0E44\u0E21\u0E48\u0E40\u0E01\u0E35\u0E48\u0E22\u0E27\u0E02\u0E49\u0E2D\u0E07",
60
- "\u0E2B\u0E22\u0E32\u0E1A\u0E04\u0E32\u0E22 / \u0E44\u0E21\u0E48\u0E40\u0E2B\u0E21\u0E32\u0E30\u0E2A\u0E21",
61
- "\u0E25\u0E32\u0E21\u0E01\u0E2D\u0E19\u0E32\u0E08\u0E32\u0E23",
62
- "\u0E04\u0E27\u0E32\u0E21\u0E23\u0E38\u0E19\u0E41\u0E23\u0E07",
63
- "\u0E02\u0E21\u0E02\u0E39\u0E48 \u0E04\u0E38\u0E01\u0E04\u0E32\u0E21",
64
- "\u0E01\u0E25\u0E31\u0E48\u0E19\u0E41\u0E01\u0E25\u0E49\u0E07",
65
- "\u0E2A\u0E23\u0E49\u0E32\u0E07\u0E04\u0E27\u0E32\u0E21\u0E40\u0E01\u0E25\u0E35\u0E22\u0E14\u0E0A\u0E31\u0E07\u0E2B\u0E23\u0E37\u0E2D\u0E02\u0E31\u0E14\u0E41\u0E22\u0E49\u0E07",
66
- "\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E0B\u0E49\u0E33\u0E2B\u0E23\u0E37\u0E2D\u0E2A\u0E41\u0E1B\u0E21",
67
- "\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E40\u0E17\u0E47\u0E08",
68
- "\u0E25\u0E30\u0E40\u0E21\u0E34\u0E14\u0E17\u0E23\u0E31\u0E1E\u0E22\u0E4C\u0E2A\u0E34\u0E19\u0E17\u0E32\u0E07\u0E1B\u0E31\u0E0D\u0E0D\u0E32",
69
- "\u0E25\u0E30\u0E40\u0E21\u0E34\u0E14\u0E02\u0E49\u0E2D\u0E15\u0E01\u0E25\u0E07\u0E01\u0E32\u0E23\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19",
70
- "\u0E1C\u0E34\u0E14\u0E01\u0E0E\u0E2B\u0E21\u0E32\u0E22",
71
- "\u0E2A\u0E48\u0E07\u0E1C\u0E25\u0E01\u0E23\u0E30\u0E17\u0E1A\u0E15\u0E48\u0E2D\u0E1A\u0E38\u0E04\u0E04\u0E25",
72
- "\u0E40\u0E02\u0E49\u0E32\u0E02\u0E48\u0E32\u0E22\u0E2B\u0E25\u0E2D\u0E01\u0E25\u0E27\u0E07",
73
- "\u0E2D\u0E37\u0E48\u0E19\u0E46"
59
+ const placeEditOptions = [
60
+ { label: "\u0E1B\u0E34\u0E14\u0E2B\u0E23\u0E37\u0E2D\u0E2B\u0E22\u0E38\u0E14\u0E01\u0E34\u0E08\u0E01\u0E32\u0E23\u0E41\u0E25\u0E49\u0E27", value: "closed" },
61
+ { label: "\u0E44\u0E21\u0E48\u0E2D\u0E22\u0E39\u0E48\u0E17\u0E35\u0E48\u0E19\u0E35\u0E48", value: "not_exist" },
62
+ { label: "\u0E0B\u0E49\u0E33\u0E01\u0E31\u0E1A\u0E2A\u0E16\u0E32\u0E19\u0E17\u0E35\u0E48\u0E2D\u0E37\u0E48\u0E19", value: "duplicate" },
63
+ { label: "\u0E22\u0E49\u0E32\u0E22\u0E44\u0E1B\u0E22\u0E31\u0E07\u0E15\u0E33\u0E41\u0E2B\u0E19\u0E48\u0E07\u0E17\u0E35\u0E48\u0E15\u0E31\u0E49\u0E07\u0E43\u0E2B\u0E21\u0E48\u0E41\u0E25\u0E49\u0E27", value: "relocated" },
64
+ { label: "\u0E2D\u0E37\u0E48\u0E19\u0E46", value: "other" }
65
+ ];
66
+ const mediaOptions = [
67
+ { label: "\u0E23\u0E39\u0E1B\u0E20\u0E32\u0E1E\u0E2B\u0E23\u0E37\u0E2D\u0E27\u0E34\u0E14\u0E35\u0E42\u0E2D\u0E44\u0E21\u0E48\u0E40\u0E2B\u0E21\u0E32\u0E30\u0E2A\u0E21", value: "inappropriate" },
68
+ { label: "\u0E02\u0E49\u0E2D\u0E01\u0E31\u0E07\u0E27\u0E25\u0E40\u0E01\u0E35\u0E48\u0E22\u0E27\u0E01\u0E31\u0E1A\u0E04\u0E27\u0E32\u0E21\u0E40\u0E1B\u0E47\u0E19\u0E2A\u0E48\u0E27\u0E19\u0E15\u0E31\u0E27", value: "privacy" },
69
+ { label: "\u0E04\u0E38\u0E13\u0E20\u0E32\u0E1E\u0E15\u0E48\u0E33", value: "low_quality" },
70
+ { label: "\u0E40\u0E19\u0E37\u0E49\u0E2D\u0E2B\u0E32\u0E17\u0E35\u0E48\u0E21\u0E38\u0E48\u0E07\u0E23\u0E49\u0E32\u0E22 \u0E40\u0E1B\u0E47\u0E19\u0E2D\u0E31\u0E19\u0E15\u0E23\u0E32\u0E22 \u0E2B\u0E23\u0E37\u0E2D\u0E21\u0E35\u0E04\u0E27\u0E32\u0E21\u0E23\u0E38\u0E19\u0E41\u0E23\u0E07", value: "harmful" },
71
+ { label: "\u0E44\u0E21\u0E48\u0E43\u0E0A\u0E48\u0E23\u0E39\u0E1B\u0E20\u0E32\u0E1E\u0E2B\u0E23\u0E37\u0E2D\u0E27\u0E34\u0E14\u0E35\u0E42\u0E2D\u0E02\u0E2D\u0E07\u0E2A\u0E16\u0E32\u0E19\u0E17\u0E35\u0E48\u0E19\u0E35\u0E49", value: "wrong_place" },
72
+ { label: "\u0E2D\u0E37\u0E48\u0E19\u0E46", value: "other" }
74
73
  ];
75
74
  const title = computed(() => {
76
- if (props.state === "place") return "\u0E23\u0E32\u0E22\u0E07\u0E32\u0E19\u0E40\u0E19\u0E37\u0E49\u0E2D\u0E2B\u0E32\u0E19\u0E35\u0E49";
77
- if (props.state === "profile") return "\u0E23\u0E32\u0E22\u0E07\u0E32\u0E19\u0E42\u0E1B\u0E23\u0E44\u0E1F\u0E25\u0E4C\u0E19\u0E35\u0E49";
78
- if (props.state === "review") return "\u0E23\u0E32\u0E22\u0E07\u0E32\u0E19\u0E40\u0E19\u0E37\u0E49\u0E2D\u0E2B\u0E32\u0E19\u0E35\u0E49";
79
- if (props.state === "announce") return "\u0E23\u0E32\u0E22\u0E07\u0E32\u0E19\u0E23\u0E35\u0E27\u0E34\u0E27\u0E19\u0E35\u0E49";
80
- if (props.state === "blog") return "\u0E23\u0E32\u0E22\u0E07\u0E32\u0E19\u0E1A\u0E25\u0E47\u0E2D\u0E01\u0E19\u0E35\u0E49";
81
- if (props.state === "comment") return "\u0E23\u0E32\u0E22\u0E07\u0E32\u0E19\u0E04\u0E2D\u0E21\u0E40\u0E21\u0E19\u0E19\u0E35\u0E49";
82
- return "\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E43\u0E19 ModalReport";
75
+ if (props.state === "place-edit") return "\u0E41\u0E08\u0E49\u0E07\u0E41\u0E01\u0E49\u0E44\u0E02\u0E2A\u0E16\u0E32\u0E19\u0E17\u0E35\u0E48\u0E19\u0E35\u0E49";
76
+ if (props.state === "media") return "\u0E41\u0E08\u0E49\u0E07\u0E41\u0E01\u0E49\u0E44\u0E02\u0E2A\u0E37\u0E48\u0E2D\u0E19\u0E35\u0E49";
77
+ return "";
83
78
  });
79
+ const placeholderText = computed(() => "\u0E04\u0E33\u0E2D\u0E18\u0E34\u0E1A\u0E32\u0E22\u0E1B\u0E23\u0E30\u0E01\u0E2D\u0E1A\u0E01\u0E32\u0E23\u0E41\u0E08\u0E49\u0E07");
84
80
  const modelValue = defineModel({ type: Boolean, ...{
85
81
  default: false
86
82
  } });
87
83
  const formState = ref({
88
- report_value: "",
89
- report_detail: ""
84
+ selectedValue: "",
85
+ description: ""
86
+ });
87
+ const currentOptions = computed(() => {
88
+ if (props.state === "place-edit") {
89
+ return placeEditOptions;
90
+ }
91
+ if (props.state === "media") {
92
+ return mediaOptions;
93
+ }
94
+ return [];
90
95
  });
91
96
  const items = computed(() => {
92
- return initItems.map((item) => [{ label: item, value: item }]);
97
+ return currentOptions.value.map((opt) => [opt]);
93
98
  });
94
99
  const reset = () => {
95
100
  formState.value = {
96
- report_value: "",
97
- report_detail: ""
101
+ selectedValue: "",
102
+ description: ""
98
103
  };
99
104
  };
100
105
  const onCancel = () => {
@@ -102,9 +107,13 @@ const onCancel = () => {
102
107
  modelValue.value = false;
103
108
  };
104
109
  const onSendReport = () => {
110
+ const selectedOpt = currentOptions.value.find(
111
+ (opt) => opt.value === formState.value.selectedValue
112
+ );
105
113
  emit("submit", {
106
- value: formState.value.report_value,
107
- detail: formState.value.report_detail
114
+ value: formState.value.selectedValue,
115
+ label: selectedOpt?.label || "",
116
+ description: formState.value.description
108
117
  });
109
118
  };
110
119
  watch(modelValue, (isOpen) => {
@@ -1,4 +1,4 @@
1
- import type { ModalReportProps } from "#pukaad-ui/types/components/modal/modal-report";
1
+ import type { ModalReportProps, ModalReportSubmitPayload } from "#pukaad-ui/types/components/modal/modal-report";
2
2
  type __VLS_Props = ModalReportProps;
3
3
  type __VLS_ModelProps = {
4
4
  modelValue?: boolean;
@@ -7,15 +7,9 @@ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
7
7
  declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
8
8
  "update:modelValue": (value: boolean) => any;
9
9
  } & {
10
- submit: (payload: {
11
- value: string;
12
- detail: string;
13
- }) => any;
10
+ submit: (payload: ModalReportSubmitPayload) => any;
14
11
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
15
- onSubmit?: ((payload: {
16
- value: string;
17
- detail: string;
18
- }) => any) | undefined;
12
+ onSubmit?: ((payload: ModalReportSubmitPayload) => any) | undefined;
19
13
  "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
20
14
  }>, {
21
15
  loading: boolean;
@@ -1,4 +1,5 @@
1
1
  import type { PickerOptionMenuUserProps } from "#pukaad-ui/types/components/picker/picker-option-menu/picker-option-menu-user";
2
+ import type { ModalReportSubmitPayload } from "#pukaad-ui/types/components/modal/modal-report";
2
3
  declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
3
4
  "blog-edit": () => any;
4
5
  "report-announce": () => any;
@@ -21,6 +22,7 @@ declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserPr
21
22
  "report-profile": () => any;
22
23
  "place-close-duplicate": () => any;
23
24
  archive: () => any;
25
+ "report-submit": (payload: ModalReportSubmitPayload) => any;
24
26
  }, string, import("vue").PublicProps, Readonly<PickerOptionMenuUserProps> & Readonly<{
25
27
  "onBlog-edit"?: (() => any) | undefined;
26
28
  "onReport-announce"?: (() => any) | undefined;
@@ -43,6 +45,7 @@ declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserPr
43
45
  "onReport-profile"?: (() => any) | undefined;
44
46
  "onPlace-close-duplicate"?: (() => any) | undefined;
45
47
  onArchive?: (() => any) | undefined;
48
+ "onReport-submit"?: ((payload: ModalReportSubmitPayload) => any) | undefined;
46
49
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
47
50
  declare const _default: typeof __VLS_export;
48
51
  export default _default;
@@ -11,7 +11,11 @@
11
11
  />
12
12
 
13
13
  <ModalShare v-model="isOpenModalShare" />
14
- <ModalReport state v-model="isOpenModalReport" />
14
+ <ModalReport
15
+ :state="reportState"
16
+ v-model="isOpenModalReport"
17
+ @submit="onReportSubmit"
18
+ />
15
19
  <ModalProfileEdit
16
20
  v-model="isOpenModalProfileEdit"
17
21
  :profile-detail="props.profileDetail"
@@ -21,7 +25,7 @@
21
25
 
22
26
  <script setup>
23
27
  import { computed, ref } from "vue";
24
- const emit = defineEmits(["profile-edit", "profile-share", "profile-follower-delete", "profile-block", "blog-pin", "blog-unpin", "blog-edit", "blog-delete", "report-blog", "report-place", "report-profile", "report-announce", "report-review", "place-close-duplicate", "review-edit", "review-delete", "review-reply", "reply-edit", "reply-delete", "archive", "profile-name-updated"]);
28
+ const emit = defineEmits(["profile-edit", "profile-share", "profile-follower-delete", "profile-block", "blog-pin", "blog-unpin", "blog-edit", "blog-delete", "report-blog", "report-place", "report-profile", "report-announce", "report-review", "place-close-duplicate", "review-edit", "review-delete", "review-reply", "reply-edit", "reply-delete", "archive", "profile-name-updated", "report-submit"]);
25
29
  const props = defineProps({
26
30
  state: { type: String, required: false },
27
31
  profileDetail: { type: Object, required: false },
@@ -38,7 +42,11 @@ const props = defineProps({
38
42
  const isOpenModalShare = ref(false);
39
43
  const isOpenModalReport = ref(false);
40
44
  const isOpenModalProfileEdit = ref(false);
41
- const reportState = ref();
45
+ const reportState = ref("place-edit");
46
+ const onReportSubmit = (payload) => {
47
+ console.log(payload);
48
+ isOpenModalReport.value = false;
49
+ };
42
50
  const menu = [
43
51
  {
44
52
  label: "\u0E41\u0E01\u0E49\u0E44\u0E02\u0E42\u0E1B\u0E23\u0E44\u0E1F\u0E25\u0E4C",
@@ -135,12 +143,12 @@ const menu = [
135
143
  }
136
144
  },
137
145
  {
138
- label: "\u0E23\u0E32\u0E22\u0E07\u0E32\u0E19",
146
+ label: "\u0E41\u0E08\u0E49\u0E07\u0E41\u0E01\u0E49\u0E44\u0E02\u0E2A\u0E16\u0E32\u0E19\u0E17\u0E35\u0E48\u0E19\u0E35\u0E49",
139
147
  name: "report-place",
140
- icon: "lsicon:circle-information-outline",
148
+ icon: "lucide:flag",
141
149
  action: () => {
142
150
  isOpenModalReport.value = true;
143
- reportState.value = "place";
151
+ reportState.value = "place-edit";
144
152
  emit("report-place");
145
153
  }
146
154
  },
@@ -183,7 +191,7 @@ const menu = [
183
191
  }
184
192
  },
185
193
  {
186
- label: "\u0E41\u0E01\u0E49\u0E44\u0E02",
194
+ label: "\u0E41\u0E01\u0E49\u0E44\u0E02\u0E23\u0E35\u0E27\u0E34\u0E27",
187
195
  name: "review-edit",
188
196
  icon: "lucide:square-pen",
189
197
  action: () => {
@@ -191,16 +199,15 @@ const menu = [
191
199
  }
192
200
  },
193
201
  {
194
- label: "\u0E25\u0E1A",
202
+ label: "\u0E25\u0E1A\u0E23\u0E35\u0E27\u0E34\u0E27\u0E19\u0E35\u0E49",
195
203
  name: "review-delete",
196
204
  icon: "lucide:trash-2",
197
205
  action: () => {
198
206
  emit("review-delete");
199
207
  }
200
208
  },
201
- // เมนูของกล่อง "การตอบกลับจาก ..." ที่ธุรกิจตอบไว้ (Figma 2456:88751)
202
209
  {
203
- label: "\u0E41\u0E01\u0E49\u0E44\u0E02",
210
+ label: "\u0E41\u0E01\u0E49\u0E44\u0E02\u0E01\u0E32\u0E23\u0E15\u0E2D\u0E1A\u0E01\u0E25\u0E31\u0E1A",
204
211
  name: "reply-edit",
205
212
  icon: "lucide:square-pen",
206
213
  action: () => {
@@ -208,7 +215,7 @@ const menu = [
208
215
  }
209
216
  },
210
217
  {
211
- label: "\u0E25\u0E1A",
218
+ label: "\u0E25\u0E1A\u0E01\u0E32\u0E23\u0E15\u0E2D\u0E1A\u0E01\u0E25\u0E31\u0E1A",
212
219
  name: "reply-delete",
213
220
  icon: "lucide:trash-2",
214
221
  action: () => {
@@ -227,6 +234,11 @@ const items = computed(() => {
227
234
  (fil) => ["profile-edit", "profile-share", "archive"].includes(fil.name)
228
235
  );
229
236
  }
237
+ if (props.state === "report-review") {
238
+ return menu.filter(
239
+ (item) => ["report-review"].includes(item.name)
240
+ );
241
+ }
230
242
  if (props.state === "my-review") {
231
243
  return menu.filter(
232
244
  (fil) => ["review-edit", "review-delete"].includes(fil.name)
@@ -255,30 +267,23 @@ const items = computed(() => {
255
267
  );
256
268
  }
257
269
  if (props.state === "place") {
258
- return menu.filter(
259
- (item) => ["report-place", "place-close-duplicate"].includes(item.name)
260
- );
270
+ return [
271
+ {
272
+ label: "\u0E41\u0E08\u0E49\u0E07\u0E41\u0E01\u0E49\u0E44\u0E02\u0E2A\u0E16\u0E32\u0E19\u0E17\u0E35\u0E48\u0E19\u0E35\u0E49",
273
+ name: "report-place",
274
+ icon: "lucide:flag",
275
+ action: () => {
276
+ reportState.value = "place-edit";
277
+ isOpenModalReport.value = true;
278
+ }
279
+ }
280
+ ];
261
281
  }
262
282
  if (props.state === "report-announce") {
263
283
  return menu.filter(
264
284
  (item) => ["report-announce"].includes(item.name)
265
285
  );
266
286
  }
267
- if (props.state === "report-review") {
268
- return menu.filter(
269
- (item) => ["report-review"].includes(item.name)
270
- );
271
- }
272
- if (props.state === "business-review") {
273
- return menu.filter(
274
- (item) => ["review-reply", "report-review"].includes(item.name)
275
- );
276
- }
277
- if (props.state === "my-reply") {
278
- return menu.filter(
279
- (item) => ["reply-edit", "reply-delete"].includes(item.name)
280
- );
281
- }
282
- return menu;
287
+ return [];
283
288
  });
284
289
  </script>
@@ -1,4 +1,5 @@
1
1
  import type { PickerOptionMenuUserProps } from "#pukaad-ui/types/components/picker/picker-option-menu/picker-option-menu-user";
2
+ import type { ModalReportSubmitPayload } from "#pukaad-ui/types/components/modal/modal-report";
2
3
  declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
3
4
  "blog-edit": () => any;
4
5
  "report-announce": () => any;
@@ -21,6 +22,7 @@ declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserPr
21
22
  "report-profile": () => any;
22
23
  "place-close-duplicate": () => any;
23
24
  archive: () => any;
25
+ "report-submit": (payload: ModalReportSubmitPayload) => any;
24
26
  }, string, import("vue").PublicProps, Readonly<PickerOptionMenuUserProps> & Readonly<{
25
27
  "onBlog-edit"?: (() => any) | undefined;
26
28
  "onReport-announce"?: (() => any) | undefined;
@@ -43,6 +45,7 @@ declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserPr
43
45
  "onReport-profile"?: (() => any) | undefined;
44
46
  "onPlace-close-duplicate"?: (() => any) | undefined;
45
47
  onArchive?: (() => any) | undefined;
48
+ "onReport-submit"?: ((payload: ModalReportSubmitPayload) => any) | undefined;
46
49
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
47
50
  declare const _default: typeof __VLS_export;
48
51
  export default _default;
@@ -0,0 +1,4 @@
1
+ export declare const useGoogleTranslate: () => {
2
+ activeLanguage: import("vue").Ref<string, string>;
3
+ setLanguage: (language: string) => void;
4
+ };
@@ -0,0 +1,42 @@
1
+ import { ref, nextTick } from "vue";
2
+ const activeLanguage = ref("th");
3
+ const GOOGLE_TRANSLATE_COOKIE = "googtrans";
4
+ const getBrowserLanguage = () => {
5
+ if (import.meta.server) return "th";
6
+ return localStorage.getItem("google_translate_language") || "th";
7
+ };
8
+ const setTranslateCookie = (language) => {
9
+ if (import.meta.server) return;
10
+ const value = language === "th" ? "" : `/th/${language}`;
11
+ const expires = "expires=Thu, 01 Jan 1970 00:00:00 GMT";
12
+ if (!value) {
13
+ document.cookie = `${GOOGLE_TRANSLATE_COOKIE}=; path=/; ${expires}`;
14
+ document.cookie = `${GOOGLE_TRANSLATE_COOKIE}=; path=/; domain=.${location.hostname}; ${expires}`;
15
+ return;
16
+ }
17
+ document.cookie = `${GOOGLE_TRANSLATE_COOKIE}=${value}; path=/`;
18
+ };
19
+ const applyGoogleTranslateLanguage = (language) => {
20
+ if (import.meta.server) return;
21
+ const select = document.querySelector(".goog-te-combo");
22
+ if (!select) return;
23
+ select.value = language;
24
+ select.dispatchEvent(new Event("change"));
25
+ };
26
+ export const useGoogleTranslate = () => {
27
+ const setLanguage = (language) => {
28
+ activeLanguage.value = language;
29
+ if (import.meta.client) {
30
+ localStorage.setItem("google_translate_language", language);
31
+ setTranslateCookie(language);
32
+ nextTick(() => applyGoogleTranslateLanguage(language));
33
+ }
34
+ };
35
+ if (import.meta.client && activeLanguage.value === "th") {
36
+ activeLanguage.value = getBrowserLanguage();
37
+ }
38
+ return {
39
+ activeLanguage,
40
+ setLanguage
41
+ };
42
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
- "version": "1.356.0",
3
+ "version": "1.358.0",
4
4
  "description": "pukaad-ui for MeMSG",
5
5
  "repository": {
6
6
  "type": "git",