pukaad-ui-lib 1.74.0 → 1.76.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 (26) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +1 -0
  3. package/dist/runtime/components/card/card-profile-about.d.vue.ts +2 -2
  4. package/dist/runtime/components/card/card-profile-about.vue +3 -3
  5. package/dist/runtime/components/card/card-profile-about.vue.d.ts +2 -2
  6. package/dist/runtime/components/card/card-reaction.d.vue.ts +39 -1
  7. package/dist/runtime/components/card/card-reaction.vue +108 -14
  8. package/dist/runtime/components/card/card-reaction.vue.d.ts +39 -1
  9. package/dist/runtime/components/card/card-user-blog.d.vue.ts +26 -7
  10. package/dist/runtime/components/card/card-user-blog.vue +24 -26
  11. package/dist/runtime/components/card/card-user-blog.vue.d.ts +26 -7
  12. package/dist/runtime/components/input/input-autocomplete.d.vue.ts +1 -1
  13. package/dist/runtime/components/input/input-autocomplete.vue.d.ts +1 -1
  14. package/dist/runtime/components/input/input-date-picker.vue +6 -1
  15. package/dist/runtime/components/input/input-tag.d.vue.ts +1 -1
  16. package/dist/runtime/components/input/input-tag.vue +71 -64
  17. package/dist/runtime/components/input/input-tag.vue.d.ts +1 -1
  18. package/dist/runtime/components/modal/modal-password-confirmed.d.vue.ts +15 -6
  19. package/dist/runtime/components/modal/modal-password-confirmed.vue +50 -36
  20. package/dist/runtime/components/modal/modal-password-confirmed.vue.d.ts +15 -6
  21. package/dist/runtime/components/modal/modal-profile-edit.vue +8 -21
  22. package/dist/runtime/components/picker/picker-option-menu/picker-option-menu-user.d.vue.ts +4 -4
  23. package/dist/runtime/components/picker/picker-option-menu/picker-option-menu-user.vue.d.ts +4 -4
  24. package/dist/runtime/plugins/format.d.ts +9 -0
  25. package/dist/runtime/plugins/format.js +29 -0
  26. package/package.json +1 -1
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
3
  "configKey": "pukaadUI",
4
- "version": "1.74.0",
4
+ "version": "1.76.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -81,6 +81,7 @@ const module$1 = defineNuxtModule({
81
81
  addPlugin(resolver.resolve("./runtime/plugins/loadingPage"));
82
82
  addPlugin(resolver.resolve("./runtime/plugins/alert"));
83
83
  addPlugin(resolver.resolve("./runtime/plugins/toast"));
84
+ addPlugin(resolver.resolve("./runtime/plugins/format"));
84
85
  addPlugin(resolver.resolve("./runtime/plugins/quill.client"));
85
86
  _nuxt.hook("nitro:config", (nitroConfig) => {
86
87
  nitroConfig.publicAssets ||= [];
@@ -18,12 +18,12 @@ export interface CardProfileAboutItem {
18
18
  links: CardProfileAboutLink[];
19
19
  }
20
20
  type __VLS_Props = {
21
- edit?: boolean;
21
+ isMyProfile?: boolean;
22
22
  item?: CardProfileAboutItem;
23
23
  };
24
24
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
25
25
  item: CardProfileAboutItem;
26
- edit: boolean;
26
+ isMyProfile: boolean;
27
27
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
28
28
  declare const _default: typeof __VLS_export;
29
29
  export default _default;
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <Card title="ข้อมูลทั่วไป" class="flex-col flex gap-[16px]">
3
- <template v-if="props.edit && !isEmpty" #header>
3
+ <template v-if="props.isMyProfile && !isEmpty" #header>
4
4
  <div class="flex justify-between items-center">
5
5
  <div class="font-title-medium-prominent">ข้อมูลทั่วไป</div>
6
6
  <Button variant="text" color="primary" @click="isShowEdit = true">
@@ -10,7 +10,7 @@
10
10
  </div>
11
11
  </template>
12
12
  <div
13
- v-if="props.edit && isEmpty"
13
+ v-if="props.isMyProfile && isEmpty"
14
14
  class="flex flex-col gap-[8px] font-body-large"
15
15
  >
16
16
  <Button class="w-full" @click="isShowEdit = true">
@@ -85,7 +85,7 @@
85
85
  <script setup>
86
86
  import { ref, computed } from "vue";
87
87
  const props = defineProps({
88
- edit: { type: Boolean, required: false, default: false },
88
+ isMyProfile: { type: Boolean, required: false, default: false },
89
89
  item: { type: Object, required: false, default: () => ({
90
90
  description: "",
91
91
  category: 0,
@@ -18,12 +18,12 @@ export interface CardProfileAboutItem {
18
18
  links: CardProfileAboutLink[];
19
19
  }
20
20
  type __VLS_Props = {
21
- edit?: boolean;
21
+ isMyProfile?: boolean;
22
22
  item?: CardProfileAboutItem;
23
23
  };
24
24
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
25
25
  item: CardProfileAboutItem;
26
- edit: boolean;
26
+ isMyProfile: boolean;
27
27
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
28
28
  declare const _default: typeof __VLS_export;
29
29
  export default _default;
@@ -1,3 +1,41 @@
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
+ interface Count {
2
+ liked?: number;
3
+ bookmarked?: number;
4
+ viewed?: number;
5
+ commented?: number;
6
+ shared?: number;
7
+ }
8
+ interface Reacted {
9
+ liked?: boolean;
10
+ bookmarked?: boolean;
11
+ }
12
+ type __VLS_Props = {
13
+ itemCount?: Count;
14
+ itemActive?: Reacted;
15
+ padding?: string | number;
16
+ disabledPadding?: boolean;
17
+ disabledDividerTop?: boolean;
18
+ disabledDividerBottom?: boolean;
19
+ disabled?: boolean;
20
+ iconSize?: number | string;
21
+ };
22
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
23
+ "update:liked": (value: boolean) => any;
24
+ "update:bookmarked": (value: boolean) => any;
25
+ share: () => any;
26
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
27
+ "onUpdate:liked"?: ((value: boolean) => any) | undefined;
28
+ "onUpdate:bookmarked"?: ((value: boolean) => any) | undefined;
29
+ onShare?: (() => any) | undefined;
30
+ }>, {
31
+ disabled: boolean;
32
+ disabledPadding: boolean;
33
+ itemCount: Count;
34
+ itemActive: Reacted;
35
+ padding: string | number;
36
+ disabledDividerTop: boolean;
37
+ disabledDividerBottom: boolean;
38
+ iconSize: number | string;
39
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
2
40
  declare const _default: typeof __VLS_export;
3
41
  export default _default;
@@ -1,35 +1,129 @@
1
1
  <template>
2
- <!-- <div>
2
+ <div>
3
3
  <Divider v-if="!props.disabledDividerTop" :height="0" />
4
4
  <div class="flex justify-between w-full font-body-large text-gray">
5
5
  <div
6
6
  v-for="(react, i) in listReaction"
7
7
  :key="i"
8
- :class="[
9
- 'flex items-center gap-[4px] justify-center w-full',
10
- react.action && 'cursor-pointer hover:bg-bright',
11
- props.disabled && 'pointer-events-none',
12
- ]"
13
- :style="{
14
- padding: `${props.padding}px`,
15
- }"
16
- :aria-disabledd="props.disabled || !react.action"
8
+ :class="[
9
+ 'flex items-center gap-[4px] justify-center w-full',
10
+ react.action && 'cursor-pointer hover:bg-bright',
11
+ props.disabled && 'pointer-events-none'
12
+ ]"
13
+ :style="{
14
+ padding: `${computedPadding}px`
15
+ }"
16
+ :aria-disabled="props.disabled || !react.action"
17
17
  @click="react.action"
18
18
  >
19
19
  <Icon
20
20
  :name="react.reacted ? react.iconReacted : react.icon"
21
- :size="props.size"
21
+ :size="props.iconSize"
22
22
  />
23
23
  <div :class="[react.reacted && 'text-primary']">
24
- {{ react.count }}
24
+ {{ $format.number(react.count) }}
25
25
  </div>
26
26
  </div>
27
27
  </div>
28
28
  <Divider v-if="!props.disabledDividerBottom" :height="0" />
29
+ <ModalShare v-model="isOpenModalShare" />
29
30
  </div>
30
- <ModalShare v-model="isOpenModalShare" /> -->
31
31
  </template>
32
32
 
33
33
  <script setup>
34
-
34
+ import { computed, reactive, ref, watch } from "vue";
35
+ import { useNuxtApp } from "nuxt/app";
36
+ const { $format } = useNuxtApp();
37
+ const isOpenModalShare = ref(false);
38
+ const props = defineProps({
39
+ itemCount: { type: Object, required: false, default: () => ({
40
+ liked: 0,
41
+ bookmarked: 0,
42
+ viewed: 0,
43
+ commented: 0,
44
+ shared: 0
45
+ }) },
46
+ itemActive: { type: Object, required: false, default: () => ({
47
+ liked: false,
48
+ bookmarked: false
49
+ }) },
50
+ padding: { type: [String, Number], required: false, default: 16 },
51
+ disabledPadding: { type: Boolean, required: false, default: false },
52
+ disabledDividerTop: { type: Boolean, required: false, default: false },
53
+ disabledDividerBottom: { type: Boolean, required: false, default: false },
54
+ disabled: { type: Boolean, required: false, default: false },
55
+ iconSize: { type: [Number, String], required: false, default: 20 }
56
+ });
57
+ const emit = defineEmits(["update:liked", "update:bookmarked", "share"]);
58
+ const computedPadding = computed(
59
+ () => props.disabledPadding ? 0 : props.padding
60
+ );
61
+ const counts = reactive({ ...props.itemCount });
62
+ const reaction = reactive({ ...props.itemActive });
63
+ watch(
64
+ () => props.itemCount,
65
+ (newVal) => {
66
+ Object.assign(counts, newVal);
67
+ },
68
+ { immediate: true, deep: true }
69
+ );
70
+ watch(
71
+ () => props.itemActive,
72
+ (newVal) => {
73
+ Object.assign(reaction, newVal);
74
+ },
75
+ { immediate: true, deep: true }
76
+ );
77
+ const listReaction = computed(() => {
78
+ const keys = Object.keys(props.itemCount || {});
79
+ const showAll = keys.length === 0;
80
+ return reactions.filter((re) => showAll || keys.includes(re.name)).map((re) => {
81
+ return {
82
+ ...re,
83
+ count: counts[re.name] ?? 0,
84
+ reacted: reaction[re.name] ?? false
85
+ };
86
+ });
87
+ });
88
+ const reactions = [
89
+ {
90
+ name: "liked",
91
+ icon: "pukaad:thumbs-up-regular",
92
+ iconReacted: "pukaad:thumbs-up-solid",
93
+ action: toggleLike
94
+ },
95
+ {
96
+ name: "bookmarked",
97
+ icon: "lucide:bookmark",
98
+ iconReacted: "pukaad:bookmark-solid",
99
+ action: toggleBookmark
100
+ },
101
+ {
102
+ name: "viewed",
103
+ icon: "lucide:eye"
104
+ },
105
+ {
106
+ name: "commented",
107
+ icon: "lucide:message-circle-more"
108
+ },
109
+ {
110
+ name: "shared",
111
+ icon: "uil:share",
112
+ action: toggleShared
113
+ }
114
+ ];
115
+ function toggleLike() {
116
+ reaction.liked = !reaction.liked;
117
+ counts.liked += reaction.liked ? 1 : -1;
118
+ emit("update:liked", reaction.liked);
119
+ }
120
+ function toggleBookmark() {
121
+ reaction.bookmarked = !reaction.bookmarked;
122
+ counts.bookmarked += reaction.bookmarked ? 1 : -1;
123
+ emit("update:bookmarked", reaction.bookmarked);
124
+ }
125
+ function toggleShared() {
126
+ isOpenModalShare.value = !isOpenModalShare.value;
127
+ emit("share");
128
+ }
35
129
  </script>
@@ -1,3 +1,41 @@
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
+ interface Count {
2
+ liked?: number;
3
+ bookmarked?: number;
4
+ viewed?: number;
5
+ commented?: number;
6
+ shared?: number;
7
+ }
8
+ interface Reacted {
9
+ liked?: boolean;
10
+ bookmarked?: boolean;
11
+ }
12
+ type __VLS_Props = {
13
+ itemCount?: Count;
14
+ itemActive?: Reacted;
15
+ padding?: string | number;
16
+ disabledPadding?: boolean;
17
+ disabledDividerTop?: boolean;
18
+ disabledDividerBottom?: boolean;
19
+ disabled?: boolean;
20
+ iconSize?: number | string;
21
+ };
22
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
23
+ "update:liked": (value: boolean) => any;
24
+ "update:bookmarked": (value: boolean) => any;
25
+ share: () => any;
26
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
27
+ "onUpdate:liked"?: ((value: boolean) => any) | undefined;
28
+ "onUpdate:bookmarked"?: ((value: boolean) => any) | undefined;
29
+ onShare?: (() => any) | undefined;
30
+ }>, {
31
+ disabled: boolean;
32
+ disabledPadding: boolean;
33
+ itemCount: Count;
34
+ itemActive: Reacted;
35
+ padding: string | number;
36
+ disabledDividerTop: boolean;
37
+ disabledDividerBottom: boolean;
38
+ iconSize: number | string;
39
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
2
40
  declare const _default: typeof __VLS_export;
3
41
  export default _default;
@@ -1,18 +1,37 @@
1
- import type { CardUserBlogProps } from "@/types/components/card/card-user-blog";
1
+ interface User {
2
+ id: string;
3
+ name: string;
4
+ avatar: string;
5
+ path_name: string;
6
+ verified?: boolean;
7
+ view_count: number;
8
+ }
9
+ interface Content {
10
+ create_at: string;
11
+ title: string;
12
+ description: string;
13
+ image?: string;
14
+ likes_count: number;
15
+ comments_count: number;
16
+ shares_count: number;
17
+ liked?: boolean;
18
+ }
19
+ interface CardUserBlogProps {
20
+ id: string;
21
+ ispinned?: boolean;
22
+ user: User;
23
+ content: Content;
24
+ }
2
25
  type __VLS_Props = {
3
26
  item: CardUserBlogProps;
4
- isProfile?: boolean;
27
+ isMyProfile?: boolean;
5
28
  };
6
29
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
7
- "blog-pin": () => any;
8
- "blog-unpin": () => any;
9
30
  "blog-edit": (item: CardUserBlogProps) => any;
10
31
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
11
- "onBlog-pin"?: (() => any) | undefined;
12
- "onBlog-unpin"?: (() => any) | undefined;
13
32
  "onBlog-edit"?: ((item: CardUserBlogProps) => any) | undefined;
14
33
  }>, {
15
- isProfile: boolean;
34
+ isMyProfile: boolean;
16
35
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
36
  declare const _default: typeof __VLS_export;
18
37
  export default _default;
@@ -1,43 +1,42 @@
1
1
  <template>
2
2
  <div>
3
3
  <div
4
- class="py-[16px] flex flex-col gap-[16px] w-[668px] border-mercury border-b-[1px]"
4
+ class="flex flex-col gap-[16px] w-[668px] border-mercury border-b-[1px]"
5
5
  >
6
6
  <div class="flex gap-[16px] items-center w-full">
7
7
  <div class="flex gap-[8px] items-center w-full">
8
- <!-- <Avatar :src="props.item.user.avatar" :size="40" class="cursor-pointer" @click="onProfileClick" /> -->
8
+ <Avatar
9
+ :src="props.item.user.avatar"
10
+ :size="40"
11
+ class="cursor-pointer"
12
+ @click="onProfileClick"
13
+ />
9
14
  <div class="flex flex-col gap-[4px]">
10
- <div class="flex gap-[4px] items-center">
11
- <div
15
+ <div>
16
+ <span
12
17
  class="font-body-large cursor-pointer"
13
18
  @click="onProfileClick"
14
- >
15
- {{ props.item.user.name }}
16
- </div>
17
- <Icon
19
+ >{{ props.item.user.name }}</span
20
+ ><Icon
18
21
  v-if="props.item.user.verified"
19
22
  name="pukaad:verify"
20
23
  :size="16"
24
+ class="inline-block align-middle ml-[8px]"
21
25
  />
22
26
  </div>
23
27
  <div class="text-gray font-body-small flex gap-[4px] items-center">
24
28
  <div>{{ props.item.content.create_at }}</div>
25
29
  <div>•</div>
26
- <div>{{ props.item.content.view_count }}</div>
30
+ <div>{{ $format.number(props.item.user.view_count) }} วิว</div>
27
31
  </div>
28
32
  </div>
29
33
  </div>
30
- <Button
31
- v-if="isPinned"
32
- prepend-icon="lucide:pin"
33
- variant="text"
34
- :icon-size="20"
35
- disabled-padding
36
- color="black"
37
- />
34
+
35
+ <Icon v-if="isPinned" name="lucide:pin" :size="20" />
36
+
38
37
  <PickerOptionMenuUser
39
38
  :state="
40
- props.isProfile ? isPinned ? 'my-blog-pined' : 'my-blog' : 'report-announce'
39
+ props.isMyProfile ? isPinned ? 'my-blog-pined' : 'my-blog' : 'report-announce'
41
40
  "
42
41
  disabled-padding
43
42
  @blog-unpin="onBlogUnpin"
@@ -55,11 +54,13 @@
55
54
  <div class="font-title-medium-prominent">
56
55
  {{ props.item.content.title }}
57
56
  </div>
58
- <div class="text-gray font-body-large">
57
+ <div
58
+ v-if="!props.item.content.image"
59
+ class="text-gray font-body-large line-clamp-4"
60
+ >
59
61
  {{ props.item.content.description }}
60
62
  </div>
61
63
  <CardReaction
62
- padding="0"
63
64
  disabled-divider-top
64
65
  disabled-divider-bottom
65
66
  :item-count="{
@@ -67,7 +68,7 @@
67
68
  commented: props.item.content.comments_count,
68
69
  shared: props.item.content.shares_count
69
70
  }"
70
- :itemReacted="{
71
+ :itemActive="{
71
72
  liked: props.item.content.liked
72
73
  }"
73
74
  />
@@ -76,15 +77,14 @@
76
77
  </template>
77
78
 
78
79
  <script setup>
79
- import Image from "../image/image.vue";
80
80
  import { useRouter } from "vue-router";
81
81
  import { ref, watch } from "vue";
82
82
  const props = defineProps({
83
83
  item: { type: Object, required: true },
84
- isProfile: { type: Boolean, required: false, default: false }
84
+ isMyProfile: { type: Boolean, required: false, default: false }
85
85
  });
86
86
  const router = useRouter();
87
- const emit = defineEmits(["blog-pin", "blog-unpin", "blog-edit"]);
87
+ const emit = defineEmits(["blog-edit"]);
88
88
  const isPinned = ref(props.item.ispinned);
89
89
  watch(
90
90
  () => props.item.ispinned,
@@ -97,11 +97,9 @@ const onProfileClick = () => {
97
97
  };
98
98
  const onBlogPin = () => {
99
99
  isPinned.value = true;
100
- emit("blog-pin");
101
100
  };
102
101
  const onBlogUnpin = () => {
103
102
  isPinned.value = false;
104
- emit("blog-unpin");
105
103
  };
106
104
  const onBlogEdit = () => {
107
105
  emit("blog-edit", props.item);
@@ -1,18 +1,37 @@
1
- import type { CardUserBlogProps } from "@/types/components/card/card-user-blog";
1
+ interface User {
2
+ id: string;
3
+ name: string;
4
+ avatar: string;
5
+ path_name: string;
6
+ verified?: boolean;
7
+ view_count: number;
8
+ }
9
+ interface Content {
10
+ create_at: string;
11
+ title: string;
12
+ description: string;
13
+ image?: string;
14
+ likes_count: number;
15
+ comments_count: number;
16
+ shares_count: number;
17
+ liked?: boolean;
18
+ }
19
+ interface CardUserBlogProps {
20
+ id: string;
21
+ ispinned?: boolean;
22
+ user: User;
23
+ content: Content;
24
+ }
2
25
  type __VLS_Props = {
3
26
  item: CardUserBlogProps;
4
- isProfile?: boolean;
27
+ isMyProfile?: boolean;
5
28
  };
6
29
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
7
- "blog-pin": () => any;
8
- "blog-unpin": () => any;
9
30
  "blog-edit": (item: CardUserBlogProps) => any;
10
31
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
11
- "onBlog-pin"?: (() => any) | undefined;
12
- "onBlog-unpin"?: (() => any) | undefined;
13
32
  "onBlog-edit"?: ((item: CardUserBlogProps) => any) | undefined;
14
33
  }>, {
15
- isProfile: boolean;
34
+ isMyProfile: boolean;
16
35
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
36
  declare const _default: typeof __VLS_export;
18
37
  export default _default;
@@ -36,9 +36,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
36
36
  id: string;
37
37
  name: string;
38
38
  description: string;
39
+ limit: number;
39
40
  options: AutocompleteOption[] | string[] | number[];
40
41
  placeholder: string;
41
- limit: number;
42
42
  disabledErrorMessage: boolean;
43
43
  disabledBorder: boolean;
44
44
  showCounter: boolean;
@@ -36,9 +36,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
36
36
  id: string;
37
37
  name: string;
38
38
  description: string;
39
+ limit: number;
39
40
  options: AutocompleteOption[] | string[] | number[];
40
41
  placeholder: string;
41
- limit: number;
42
42
  disabledErrorMessage: boolean;
43
43
  disabledBorder: boolean;
44
44
  showCounter: boolean;
@@ -17,7 +17,7 @@
17
17
  </ShadFormLabel>
18
18
 
19
19
  <ShadFormControl>
20
- <Popover>
20
+ <Popover v-model:open="isPopoverOpen">
21
21
  <PopoverTrigger as-child>
22
22
  <Button
23
23
  variant="outline"
@@ -151,6 +151,7 @@ const minCalendarValue = computed(() => {
151
151
  });
152
152
  const modelValue = defineModel({ type: null, ...{ default: void 0 } });
153
153
  const fieldRef = ref();
154
+ const isPopoverOpen = ref(false);
154
155
  const hours = Array.from({ length: 24 }, (_, i) => i);
155
156
  const minutes = Array.from({ length: 12 }, (_, i) => i * 5);
156
157
  const selectedHour = computed(() => modelValue.value?.getHours() ?? -1);
@@ -172,6 +173,9 @@ const calendarValue = computed({
172
173
  newDate.setMinutes(modelValue.value.getMinutes());
173
174
  }
174
175
  modelValue.value = newDate;
176
+ if (!props.showTime) {
177
+ isPopoverOpen.value = false;
178
+ }
175
179
  } else {
176
180
  modelValue.value = void 0;
177
181
  }
@@ -184,6 +188,7 @@ function handleTimeChange(type, value) {
184
188
  newDate.setHours(value);
185
189
  } else if (type === "minute") {
186
190
  newDate.setMinutes(value);
191
+ isPopoverOpen.value = false;
187
192
  }
188
193
  modelValue.value = newDate;
189
194
  }
@@ -18,8 +18,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
18
18
  "onUpdate:modelValue"?: ((value: string[]) => any) | undefined;
19
19
  }>, {
20
20
  name: string;
21
- placeholder: string;
22
21
  limit: number;
22
+ placeholder: string;
23
23
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
24
24
  declare const _default: typeof __VLS_export;
25
25
  export default _default;
@@ -58,23 +58,12 @@
58
58
  v-for="tag in filteredPopularTags"
59
59
  :key="tag.value"
60
60
  :value="tag.value"
61
- :disabled="isOptionDisabled(tag.value)"
62
- :class="[
63
- 'flex w-full justify-between',
64
- isOptionDisabled(tag.value) && 'cursor-not-allowed opacity-50'
65
- ]"
61
+ class="flex w-full justify-between"
66
62
  @select="onSelectTag(tag)"
67
63
  >
68
64
  <span class="font-body-medium">{{ tag.label }}</span>
69
- <div class="flex items-center gap-2">
70
- <div class="font-body-small text-gray">
71
- {{ tag.postCount }}
72
- </div>
73
- <Icon
74
- v-if="isSelected(tag.value)"
75
- name="lucide:check"
76
- class="h-4 w-4"
77
- />
65
+ <div class="font-body-small text-gray">
66
+ {{ $format.number(tag.postCount) }} โพสต์
78
67
  </div>
79
68
  </ShadCommandItem>
80
69
  </ShadCommandGroup>
@@ -97,23 +86,12 @@
97
86
  v-for="tag in filteredLatestTags"
98
87
  :key="tag.value"
99
88
  :value="tag.value"
100
- :disabled="isOptionDisabled(tag.value)"
101
- :class="[
102
- 'flex w-full justify-between',
103
- isOptionDisabled(tag.value) && 'cursor-not-allowed opacity-50'
104
- ]"
89
+ class="flex w-full justify-between"
105
90
  @select="onSelectTag(tag)"
106
91
  >
107
92
  <span class="font-body-medium">{{ tag.label }}</span>
108
- <div class="flex items-center gap-2">
109
- <div class="font-body-small text-gray">
110
- {{ tag.postCount }}
111
- </div>
112
- <Icon
113
- v-if="isSelected(tag.value)"
114
- name="lucide:check"
115
- class="h-4 w-4"
116
- />
93
+ <div class="font-body-small text-gray">
94
+ {{ $format.number(tag.postCount) }} โพสต์
117
95
  </div>
118
96
  </ShadCommandItem>
119
97
  </ShadCommandGroup>
@@ -134,7 +112,7 @@
134
112
  </template>
135
113
 
136
114
  <script setup>
137
- import { ref, computed, nextTick } from "vue";
115
+ import { ref, computed, onMounted } from "vue";
138
116
  const props = defineProps({
139
117
  name: { type: String, required: false, default: "input-tag" },
140
118
  rules: { type: [Object, String, Function], required: false },
@@ -151,28 +129,59 @@ const popoverOpen = ref(false);
151
129
  const currentTab = ref("popular");
152
130
  const searchQuery = ref("");
153
131
  const inputRef = ref();
154
- const allTags = ref([
155
- // Popular tags
156
- { label: "\u0E1A\u0E38\u0E23\u0E35\u0E23\u0E31\u0E21\u0E22\u0E4C", value: "buriram", postCount: "46.5 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
157
- { label: "\u0E01\u0E23\u0E38\u0E07\u0E40\u0E17\u0E1E", value: "bangkok", postCount: "120.3 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
158
- { label: "\u0E40\u0E0A\u0E35\u0E22\u0E07\u0E43\u0E2B\u0E21\u0E48", value: "chiangmai", postCount: "85.2 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
159
- { label: "\u0E20\u0E39\u0E40\u0E01\u0E47\u0E15", value: "phuket", postCount: "72.1 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
160
- { label: "\u0E1E\u0E31\u0E17\u0E22\u0E32", value: "pattaya", postCount: "55.8 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
161
- { label: "\u0E2B\u0E31\u0E27\u0E2B\u0E34\u0E19", value: "huahin", postCount: "34.6 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
162
- { label: "\u0E02\u0E2D\u0E19\u0E41\u0E01\u0E48\u0E19", value: "khonkaen", postCount: "28.4 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
163
- { label: "\u0E19\u0E04\u0E23\u0E23\u0E32\u0E0A\u0E2A\u0E35\u0E21\u0E32", value: "korat", postCount: "22.9 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
164
- // Latest tags
165
- { label: "\u0E2A\u0E38\u0E02\u0E38\u0E21\u0E27\u0E34\u0E17", value: "sukhumvit", postCount: "12.3 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
166
- { label: "\u0E2A\u0E22\u0E32\u0E21", value: "siam", postCount: "8.7 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
167
- { label: "\u0E2D\u0E32\u0E23\u0E35\u0E22\u0E4C", value: "ari", postCount: "6.2 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
168
- { label: "\u0E17\u0E2D\u0E07\u0E2B\u0E25\u0E48\u0E2D", value: "thonglor", postCount: "15.4 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
169
- { label: "\u0E40\u0E2D\u0E01\u0E21\u0E31\u0E22", value: "ekkamai", postCount: "9.8 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
170
- { label: "\u0E2D\u0E42\u0E28\u0E01", value: "asoke", postCount: "11.2 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
171
- { label: "\u0E2A\u0E35\u0E25\u0E21", value: "silom", postCount: "18.6 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" },
172
- { label: "\u0E23\u0E31\u0E0A\u0E14\u0E32", value: "ratchada", postCount: "7.5 \u0E1E\u0E31\u0E19 \u0E42\u0E1E\u0E2A\u0E15\u0E4C" }
173
- ]);
174
- const popularTags = computed(() => allTags.value.slice(0, 8));
175
- const latestTags = computed(() => allTags.value.slice(8));
132
+ const isLoading = ref(false);
133
+ const allTags = ref([]);
134
+ const popularTags = ref([]);
135
+ const latestTags = ref([]);
136
+ const mockFetchTags = () => {
137
+ return new Promise((resolve) => {
138
+ setTimeout(() => {
139
+ resolve({
140
+ popular: [
141
+ { label: "\u0E1A\u0E38\u0E23\u0E35\u0E23\u0E31\u0E21\u0E22\u0E4C", value: "buriram", postCount: 4500 },
142
+ { label: "\u0E01\u0E23\u0E38\u0E07\u0E40\u0E17\u0E1E", value: "bangkok", postCount: 120300 },
143
+ {
144
+ label: "\u0E40\u0E0A\u0E35\u0E22\u0E07\u0E43\u0E2B\u0E21\u0E48",
145
+ value: "chiangmai",
146
+ postCount: 85200
147
+ },
148
+ { label: "\u0E20\u0E39\u0E40\u0E01\u0E47\u0E15", value: "phuket", postCount: 72100 },
149
+ { label: "\u0E1E\u0E31\u0E17\u0E22\u0E32", value: "pattaya", postCount: 55800 },
150
+ { label: "\u0E2B\u0E31\u0E27\u0E2B\u0E34\u0E19", value: "huahin", postCount: 34600 },
151
+ { label: "\u0E02\u0E2D\u0E19\u0E41\u0E01\u0E48\u0E19", value: "khonkaen", postCount: 28400 },
152
+ { label: "\u0E19\u0E04\u0E23\u0E23\u0E32\u0E0A\u0E2A\u0E35\u0E21\u0E32", value: "korat", postCount: 22900 }
153
+ ],
154
+ latest: [
155
+ {
156
+ label: "\u0E2A\u0E38\u0E02\u0E38\u0E21\u0E27\u0E34\u0E17",
157
+ value: "sukhumvit",
158
+ postCount: 12300
159
+ },
160
+ { label: "\u0E2A\u0E22\u0E32\u0E21", value: "siam", postCount: 8700 },
161
+ { label: "\u0E2D\u0E32\u0E23\u0E35\u0E22\u0E4C", value: "ari", postCount: 6200 },
162
+ { label: "\u0E17\u0E2D\u0E07\u0E2B\u0E25\u0E48\u0E2D", value: "thonglor", postCount: 15400 },
163
+ { label: "\u0E40\u0E2D\u0E01\u0E21\u0E31\u0E22", value: "ekkamai", postCount: 9800 },
164
+ { label: "\u0E2D\u0E42\u0E28\u0E01", value: "asoke", postCount: 11200 },
165
+ { label: "\u0E2A\u0E35\u0E25\u0E21", value: "silom", postCount: 18600 },
166
+ { label: "\u0E23\u0E31\u0E0A\u0E14\u0E32", value: "ratchada", postCount: 7500 }
167
+ ]
168
+ });
169
+ }, 500);
170
+ });
171
+ };
172
+ onMounted(async () => {
173
+ isLoading.value = true;
174
+ try {
175
+ const response = await mockFetchTags();
176
+ popularTags.value = response.popular;
177
+ latestTags.value = response.latest;
178
+ allTags.value = [...response.popular, ...response.latest];
179
+ } catch (error) {
180
+ console.error("Failed to fetch tags:", error);
181
+ } finally {
182
+ isLoading.value = false;
183
+ }
184
+ });
176
185
  const selectedTags = computed(() => {
177
186
  return modelValue.value.map((value) => allTags.value.find((tag) => tag.value === value)).filter((tag) => tag !== void 0);
178
187
  });
@@ -199,28 +208,25 @@ const handleInteractOutside = (event) => {
199
208
  }
200
209
  };
201
210
  const filteredPopularTags = computed(() => {
202
- if (!searchQuery.value) return popularTags.value;
211
+ const availableTags = popularTags.value.filter(
212
+ (tag) => !modelValue.value.includes(tag.value)
213
+ );
214
+ if (!searchQuery.value) return availableTags;
203
215
  const query = searchQuery.value.toLowerCase();
204
- return popularTags.value.filter(
216
+ return availableTags.filter(
205
217
  (tag) => tag.label.toLowerCase().includes(query) || tag.value.toLowerCase().includes(query)
206
218
  );
207
219
  });
208
220
  const filteredLatestTags = computed(() => {
209
- if (!searchQuery.value) return latestTags.value;
221
+ const availableTags = latestTags.value.filter(
222
+ (tag) => !modelValue.value.includes(tag.value)
223
+ );
224
+ if (!searchQuery.value) return availableTags;
210
225
  const query = searchQuery.value.toLowerCase();
211
- return latestTags.value.filter(
226
+ return availableTags.filter(
212
227
  (tag) => tag.label.toLowerCase().includes(query) || tag.value.toLowerCase().includes(query)
213
228
  );
214
229
  });
215
- const isSelected = (value) => {
216
- return modelValue.value.includes(value);
217
- };
218
- const isOptionDisabled = (value) => {
219
- if (isLimitReached.value && !isSelected(value)) {
220
- return true;
221
- }
222
- return false;
223
- };
224
230
  const onSelectTag = (tag) => {
225
231
  if (modelValue.value.includes(tag.value)) {
226
232
  modelValue.value = modelValue.value.filter((v) => v !== tag.value);
@@ -229,6 +235,7 @@ const onSelectTag = (tag) => {
229
235
  return;
230
236
  }
231
237
  modelValue.value = [...modelValue.value, tag.value];
238
+ popoverOpen.value = false;
232
239
  }
233
240
  searchQuery.value = "";
234
241
  };
@@ -18,8 +18,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
18
18
  "onUpdate:modelValue"?: ((value: string[]) => any) | undefined;
19
19
  }>, {
20
20
  name: string;
21
- placeholder: string;
22
21
  limit: number;
22
+ placeholder: string;
23
23
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
24
24
  declare const _default: typeof __VLS_export;
25
25
  export default _default;
@@ -1,22 +1,31 @@
1
- import type { ModalPasswordProps } from "@/types/components/modal/modal-password";
2
- type __VLS_Props = ModalPasswordProps;
1
+ interface Props {
2
+ title?: string;
3
+ confirmText?: string;
4
+ loginToken?: string;
5
+ description?: string;
6
+ disabledForgotPassword?: boolean;
7
+ }
8
+ type __VLS_Props = Props;
3
9
  type __VLS_ModelProps = {
4
10
  modelValue?: boolean;
5
11
  };
6
12
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
7
13
  declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
8
14
  "update:modelValue": (value: boolean) => any;
9
- } & {
10
- complete: (is_verify: boolean) => any;
15
+ complete: (data: {
16
+ secId: string;
17
+ }) => any;
11
18
  close: () => any;
12
19
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
13
20
  "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
14
- onComplete?: ((is_verify: boolean) => any) | undefined;
21
+ onComplete?: ((data: {
22
+ secId: string;
23
+ }) => any) | undefined;
15
24
  onClose?: (() => any) | undefined;
16
25
  }>, {
17
26
  title: string;
18
- confirmedText: string;
19
27
  disabledForgotPassword: boolean;
28
+ confirmText: string;
20
29
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
30
  declare const _default: typeof __VLS_export;
22
31
  export default _default;
@@ -1,19 +1,23 @@
1
1
  <template>
2
2
  <Modal
3
3
  :title="props.title"
4
- :loading="loadingModal"
5
- @close="emit('close')"
6
- @submit="onVerifyPassword"
4
+ :loading="loading"
7
5
  v-model="isOpen"
6
+ @close="emit('close')"
7
+ @submit="handleSubmit"
8
8
  >
9
9
  <div v-if="props.description" class="font-body-large">
10
10
  {{ props.description }}
11
11
  </div>
12
12
  <InputPassword
13
+ ref="inputRef"
14
+ disabled-forgot-password
15
+ required
13
16
  label="รหัสผ่าน"
14
17
  placeholder="กรอกรหัสผ่าน"
15
18
  v-model="password"
16
19
  />
20
+
17
21
  <template #footer="{ meta }">
18
22
  <Button
19
23
  type="submit"
@@ -21,52 +25,62 @@
21
25
  class="w-full"
22
26
  :disabled="!meta.valid"
23
27
  >
24
- {{ props.confirmedText }}
28
+ {{ props.confirmText }}
25
29
  </Button>
26
30
  </template>
27
31
  </Modal>
28
32
  </template>
29
33
 
30
34
  <script setup>
31
- import { onMounted, ref, watch } from "vue";
32
- import { useRuntimeConfig } from "nuxt/app";
33
- const { BASE_URL_API } = useRuntimeConfig().public;
34
- const emit = defineEmits(["close", "complete"]);
35
+ import { ref, computed, watch } from "vue";
36
+ import { useNuxtApp, useRuntimeConfig } from "nuxt/app";
35
37
  const props = defineProps({
36
- confirmedText: { type: String, required: false, default: "\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19" },
37
38
  title: { type: String, required: false, default: "\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E23\u0E2B\u0E31\u0E2A\u0E1C\u0E48\u0E32\u0E19" },
38
- disabledForgotPassword: { type: Boolean, required: false, default: false },
39
- description: { type: String, required: false }
39
+ confirmText: { type: String, required: false, default: "\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19" },
40
+ loginToken: { type: String, required: false },
41
+ description: { type: String, required: false },
42
+ disabledForgotPassword: { type: Boolean, required: false, default: false }
40
43
  });
41
- const isOpen = defineModel({ type: Boolean, ...{
42
- default: false
43
- } });
44
- const inputPasswordRef = ref();
44
+ const emit = defineEmits(["complete", "close"]);
45
+ const { $toast } = useNuxtApp();
46
+ const config = useRuntimeConfig();
47
+ const isOpen = defineModel({ type: Boolean, ...{ default: false } });
48
+ const apiPath = computed(
49
+ () => config.public.APP_TYPE === "office" ? "/office/auth/password/verify" : "/user/auth/password/verify"
50
+ );
51
+ const loading = ref(false);
45
52
  const password = ref("");
46
- const loadingModal = ref(false);
47
- const onVerifyPassword = async () => {
48
- loadingModal.value = true;
53
+ const inputRef = ref();
54
+ watch(isOpen, (open) => {
55
+ if (!open) {
56
+ password.value = "";
57
+ }
58
+ });
59
+ const handleSubmit = async () => {
60
+ if (!props.loginToken) {
61
+ $toast?.error?.("\u0E44\u0E21\u0E48\u0E1E\u0E1A login token");
62
+ return;
63
+ }
64
+ loading.value = true;
49
65
  try {
50
- const { status } = await $fetch(
51
- `${BASE_URL_API}/me/password-verify`,
52
- {
53
- credentials: "include",
54
- method: "POST",
55
- body: {
56
- password: password.value
57
- }
66
+ const res = await $fetch(`${config.public.BASE_URL_API}${apiPath.value}`, {
67
+ method: "POST",
68
+ credentials: "include",
69
+ body: {
70
+ login_token: props.loginToken,
71
+ password: password.value
58
72
  }
59
- );
60
- if (status !== "success") throw "\u0E1E\u0E1A\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14\u0E1A\u0E32\u0E07\u0E2D\u0E22\u0E48\u0E32\u0E07";
61
- emit("complete", false);
62
- isOpen.value = false;
63
- } catch (error) {
64
- inputPasswordRef.value.setErrors("\u0E23\u0E2B\u0E31\u0E2A\u0E1C\u0E48\u0E32\u0E19\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07");
73
+ });
74
+ if (res.code === 200 && res.data) {
75
+ emit("complete", { secId: res.data.sec_id });
76
+ isOpen.value = false;
77
+ } else {
78
+ inputRef.value?.setErrors?.(res.message || "\u0E23\u0E2B\u0E31\u0E2A\u0E1C\u0E48\u0E32\u0E19\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07");
79
+ }
80
+ } catch (e) {
81
+ inputRef.value?.setErrors?.(e?.data?.message || "\u0E23\u0E2B\u0E31\u0E2A\u0E1C\u0E48\u0E32\u0E19\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07");
65
82
  } finally {
66
- loadingModal.value = false;
83
+ loading.value = false;
67
84
  }
68
85
  };
69
- watch(isOpen, (value) => {
70
- password.value = "";
71
- });
72
86
  </script>
@@ -1,22 +1,31 @@
1
- import type { ModalPasswordProps } from "@/types/components/modal/modal-password";
2
- type __VLS_Props = ModalPasswordProps;
1
+ interface Props {
2
+ title?: string;
3
+ confirmText?: string;
4
+ loginToken?: string;
5
+ description?: string;
6
+ disabledForgotPassword?: boolean;
7
+ }
8
+ type __VLS_Props = Props;
3
9
  type __VLS_ModelProps = {
4
10
  modelValue?: boolean;
5
11
  };
6
12
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
7
13
  declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
8
14
  "update:modelValue": (value: boolean) => any;
9
- } & {
10
- complete: (is_verify: boolean) => any;
15
+ complete: (data: {
16
+ secId: string;
17
+ }) => any;
11
18
  close: () => any;
12
19
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
13
20
  "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
14
- onComplete?: ((is_verify: boolean) => any) | undefined;
21
+ onComplete?: ((data: {
22
+ secId: string;
23
+ }) => any) | undefined;
15
24
  onClose?: (() => any) | undefined;
16
25
  }>, {
17
26
  title: string;
18
- confirmedText: string;
19
27
  disabledForgotPassword: boolean;
28
+ confirmText: string;
20
29
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
30
  declare const _default: typeof __VLS_export;
22
31
  export default _default;
@@ -8,28 +8,15 @@
8
8
  >
9
9
  <template #header>
10
10
  <div v-if="step !== 'menu'">
11
- <div class="flex justify-between items-center w-full">
12
- <Button
13
- variant="text"
14
- color="black"
15
- disabled-padding
16
- icon-size="24"
17
- prepend-icon="lucide:chevron-left"
18
- @click="step = 'menu'"
19
- />
11
+ <div class="flex gap-[8px] items-center w-full">
12
+ <Button variant="text" @click="step = 'menu'">
13
+ <Icon name="lucide:chevron-left" :size="24" />
14
+ </Button>
20
15
  <div class="font-title-medium-prominent">
21
16
  <div v-if="step === 'profile-name'">แก้ไขชื่อโปรไฟล์</div>
22
17
  <div v-if="step === 'premium-id'">พรีเมียม ID</div>
23
18
  <div v-if="step === 'category'">หมวดหมู่</div>
24
19
  </div>
25
- <Button
26
- variant="text"
27
- color="black"
28
- disabled-padding
29
- icon-size="24"
30
- prepend-icon="lucide:x"
31
- @click="onClose"
32
- />
33
20
  </div>
34
21
  </div>
35
22
  </template>
@@ -141,12 +128,13 @@
141
128
  <InputTextField
142
129
  v-model="namePremiumID"
143
130
  label="ซื้อพรีเมียม ID"
144
- full-width
145
131
  show-counter
146
132
  :limit="18"
147
133
  prepend-icon="lucide:at-sign"
148
134
  />
149
- <Button color="primary" :disabled="!namePremiumID"
135
+ <Button
136
+ color="primary"
137
+ :disabled="!namePremiumID || namePremiumID.length > 18"
150
138
  >ซื้อพรีเมียม ID</Button
151
139
  >
152
140
  </div>
@@ -157,11 +145,10 @@
157
145
  <div class="font-body-large">เลือกหมวดหมู่ที่สอดคล้องกับโปรไฟล์</div>
158
146
  <InputAutocomplete
159
147
  v-model="profileCategory"
160
- :items="profileCategoryItems"
148
+ :options="profileCategoryItems"
161
149
  name="profileCategory"
162
150
  placeholder="ค้นหาหรือเลือกหมวดหมู่ที่เกี่ยวข้อง"
163
151
  label="หมวดหมู่"
164
- full-width
165
152
  />
166
153
  <Button color="primary" :disabled="!profileCategory">บันทึก</Button>
167
154
  </div>
@@ -1,9 +1,9 @@
1
1
  import type { PickerOptionMenuUserProps } from "@/types/components/picker/picker-option-menu/picker-option-menu-user";
2
2
  declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
3
- "blog-pin": () => any;
4
- "blog-unpin": () => any;
5
3
  "blog-edit": () => any;
6
4
  "report-announce": () => any;
5
+ "blog-unpin": () => any;
6
+ "blog-pin": () => any;
7
7
  "profile-edit": () => any;
8
8
  "profile-share": () => any;
9
9
  "profile-follower-delete": () => any;
@@ -18,10 +18,10 @@ declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserPr
18
18
  "review-delete": () => any;
19
19
  archive: () => any;
20
20
  }, string, import("vue").PublicProps, Readonly<PickerOptionMenuUserProps> & Readonly<{
21
- "onBlog-pin"?: (() => any) | undefined;
22
- "onBlog-unpin"?: (() => any) | undefined;
23
21
  "onBlog-edit"?: (() => any) | undefined;
24
22
  "onReport-announce"?: (() => any) | undefined;
23
+ "onBlog-unpin"?: (() => any) | undefined;
24
+ "onBlog-pin"?: (() => any) | undefined;
25
25
  "onProfile-edit"?: (() => any) | undefined;
26
26
  "onProfile-share"?: (() => any) | undefined;
27
27
  "onProfile-follower-delete"?: (() => any) | undefined;
@@ -1,9 +1,9 @@
1
1
  import type { PickerOptionMenuUserProps } from "@/types/components/picker/picker-option-menu/picker-option-menu-user";
2
2
  declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
3
- "blog-pin": () => any;
4
- "blog-unpin": () => any;
5
3
  "blog-edit": () => any;
6
4
  "report-announce": () => any;
5
+ "blog-unpin": () => any;
6
+ "blog-pin": () => any;
7
7
  "profile-edit": () => any;
8
8
  "profile-share": () => any;
9
9
  "profile-follower-delete": () => any;
@@ -18,10 +18,10 @@ declare const __VLS_export: import("vue").DefineComponent<PickerOptionMenuUserPr
18
18
  "review-delete": () => any;
19
19
  archive: () => any;
20
20
  }, string, import("vue").PublicProps, Readonly<PickerOptionMenuUserProps> & Readonly<{
21
- "onBlog-pin"?: (() => any) | undefined;
22
- "onBlog-unpin"?: (() => any) | undefined;
23
21
  "onBlog-edit"?: (() => any) | undefined;
24
22
  "onReport-announce"?: (() => any) | undefined;
23
+ "onBlog-unpin"?: (() => any) | undefined;
24
+ "onBlog-pin"?: (() => any) | undefined;
25
25
  "onProfile-edit"?: (() => any) | undefined;
26
26
  "onProfile-share"?: (() => any) | undefined;
27
27
  "onProfile-follower-delete"?: (() => any) | undefined;
@@ -0,0 +1,9 @@
1
+ export declare class Format {
2
+ number(num: number): string;
3
+ }
4
+ declare const _default: import("nuxt/app").Plugin<{
5
+ format: Format;
6
+ }> & import("nuxt/app").ObjectPlugin<{
7
+ format: Format;
8
+ }>;
9
+ export default _default;
@@ -0,0 +1,29 @@
1
+ import { defineNuxtPlugin } from "nuxt/app";
2
+ export class Format {
3
+ number(num) {
4
+ if (num >= 1e6) {
5
+ const value = Math.floor(num / 1e6 * 10) / 10;
6
+ return value.toString().replace(/\.0$/, "") + " \u0E25\u0E49\u0E32\u0E19";
7
+ }
8
+ if (num >= 1e5) {
9
+ const value = Math.floor(num / 1e5 * 10) / 10;
10
+ return value.toString().replace(/\.0$/, "") + " \u0E41\u0E2A\u0E19";
11
+ }
12
+ if (num >= 1e4) {
13
+ const value = Math.floor(num / 1e4 * 10) / 10;
14
+ return value.toString().replace(/\.0$/, "") + " \u0E2B\u0E21\u0E37\u0E48\u0E19";
15
+ }
16
+ if (num >= 1e3) {
17
+ const value = Math.floor(num / 1e3 * 10) / 10;
18
+ return value.toString().replace(/\.0$/, "") + " \u0E1E\u0E31\u0E19";
19
+ }
20
+ return num.toString();
21
+ }
22
+ }
23
+ export default defineNuxtPlugin(() => {
24
+ return {
25
+ provide: {
26
+ format: new Format()
27
+ }
28
+ };
29
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pukaad-ui-lib",
3
- "version": "1.74.0",
3
+ "version": "1.76.0",
4
4
  "description": "pukaad-ui for MeMSG",
5
5
  "repository": {
6
6
  "type": "git",