| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  | <template> | 
					
						
							|  |  |  |   <BaseDialog | 
					
						
							|  |  |  |     v-model="inviteDialog" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     :title="$t('profile.get-invite-link')" | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |     :icon="$globals.icons.accountPlusOutline" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     color="primary" | 
					
						
							|  |  |  |   > | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |     <v-container> | 
					
						
							|  |  |  |       <v-form class="mt-5"> | 
					
						
							|  |  |  |         <v-select | 
					
						
							|  |  |  |           v-if="groups && groups.length" | 
					
						
							|  |  |  |           v-model="selectedGroup" | 
					
						
							|  |  |  |           :items="groups" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           item-title="name" | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |           item-value="id" | 
					
						
							|  |  |  |           :return-object="false" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           variant="filled" | 
					
						
							|  |  |  |           :label="$t('group.user-group')" | 
					
						
							|  |  |  |           :rules="[validators.required]" | 
					
						
							|  |  |  |         /> | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |         <v-select | 
					
						
							|  |  |  |           v-if="households && households.length" | 
					
						
							|  |  |  |           v-model="selectedHousehold" | 
					
						
							|  |  |  |           :items="filteredHouseholds" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           item-title="name" | 
					
						
							|  |  |  |           item-value="id" | 
					
						
							|  |  |  |           :return-object="false" | 
					
						
							|  |  |  |           variant="filled" | 
					
						
							|  |  |  |           :label="$t('household.user-household')" | 
					
						
							|  |  |  |           :rules="[validators.required]" | 
					
						
							|  |  |  |         /> | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |         <v-row> | 
					
						
							|  |  |  |           <v-col cols="9"> | 
					
						
							|  |  |  |             <v-text-field | 
					
						
							| 
									
										
										
										
											2025-06-28 15:59:58 +02:00
										 |  |  |               v-model="generatedSignupLink" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |               :label="$t('profile.invite-link')" | 
					
						
							|  |  |  |               type="text" | 
					
						
							|  |  |  |               readonly | 
					
						
							|  |  |  |               variant="filled" | 
					
						
							|  |  |  |             /> | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |           </v-col> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           <v-col | 
					
						
							|  |  |  |             cols="3" | 
					
						
							|  |  |  |             class="pl-1 mt-3" | 
					
						
							|  |  |  |           > | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |             <AppButtonCopy | 
					
						
							|  |  |  |               :icon="false" | 
					
						
							|  |  |  |               color="info" | 
					
						
							|  |  |  |               :copy-text="generatedSignupLink" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |               :disabled="generatedSignupLink" | 
					
						
							|  |  |  |             /> | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |           </v-col> | 
					
						
							|  |  |  |         </v-row> | 
					
						
							|  |  |  |         <v-text-field | 
					
						
							|  |  |  |           v-model="sendTo" | 
					
						
							|  |  |  |           :label="$t('user.email')" | 
					
						
							|  |  |  |           :rules="[validators.email]" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           variant="outlined" | 
					
						
							|  |  |  |           @keydown.enter="sendInvite" | 
					
						
							|  |  |  |         /> | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |       </v-form> | 
					
						
							|  |  |  |     </v-container> | 
					
						
							|  |  |  |     <template #custom-card-action> | 
					
						
							|  |  |  |       <BaseButton | 
					
						
							|  |  |  |         :disabled="!validEmail" | 
					
						
							|  |  |  |         :loading="loading" | 
					
						
							|  |  |  |         :icon="$globals.icons.email" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         @click="sendInvite" | 
					
						
							|  |  |  |       > | 
					
						
							|  |  |  |         {{ $t("group.invite") }} | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |       </BaseButton> | 
					
						
							|  |  |  |     </template> | 
					
						
							|  |  |  |   </BaseDialog> | 
					
						
							|  |  |  | </template> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <script lang="ts"> | 
					
						
							|  |  |  | import { watchEffect } from "vue"; | 
					
						
							|  |  |  | import { useUserApi } from "@/composables/api"; | 
					
						
							|  |  |  | import BaseDialog from "~/components/global/BaseDialog.vue"; | 
					
						
							|  |  |  | import AppButtonCopy from "~/components/global/AppButtonCopy.vue"; | 
					
						
							|  |  |  | import BaseButton from "~/components/global/BaseButton.vue"; | 
					
						
							|  |  |  | import { validators } from "~/composables/use-validators"; | 
					
						
							|  |  |  | import { alert } from "~/composables/use-toast"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | import type { GroupInDB } from "~/lib/api/types/user"; | 
					
						
							|  |  |  | import type { HouseholdInDB } from "~/lib/api/types/household"; | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  | import { useGroups } from "~/composables/use-groups"; | 
					
						
							|  |  |  | import { useAdminHouseholds } from "~/composables/use-households"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | export default defineNuxtComponent({ | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |   name: "UserInviteDialog", | 
					
						
							|  |  |  |   components: { | 
					
						
							|  |  |  |     BaseDialog, | 
					
						
							|  |  |  |     AppButtonCopy, | 
					
						
							|  |  |  |     BaseButton, | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   props: { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     modelValue: { | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |       type: Boolean, | 
					
						
							|  |  |  |       default: false, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |   emits: ["update:modelValue"], | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |   setup(props, context) { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     const i18n = useI18n(); | 
					
						
							|  |  |  |     const $auth = useMealieAuth(); | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     const isAdmin = computed(() => $auth.user.value?.admin); | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |     const token = ref(""); | 
					
						
							|  |  |  |     const selectedGroup = ref<string | null>(null); | 
					
						
							|  |  |  |     const selectedHousehold = ref<string | null>(null); | 
					
						
							|  |  |  |     const groups = ref<GroupInDB[]>([]); | 
					
						
							|  |  |  |     const households = ref<HouseholdInDB[]>([]); | 
					
						
							|  |  |  |     const api = useUserApi(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const fetchGroupsAndHouseholds = () => { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       if (isAdmin.value) { | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |         const groupsResponse = useGroups(); | 
					
						
							|  |  |  |         const householdsResponse = useAdminHouseholds(); | 
					
						
							|  |  |  |         watchEffect(() => { | 
					
						
							|  |  |  |           groups.value = groupsResponse.groups.value || []; | 
					
						
							|  |  |  |           households.value = householdsResponse.households.value || []; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const inviteDialog = computed<boolean>({ | 
					
						
							|  |  |  |       get() { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         return props.modelValue; | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |       set(val) { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         context.emit("update:modelValue", val); | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async function getSignupLink(group: string | null = null, household: string | null = null) { | 
					
						
							|  |  |  |       const payload = (group && household) ? { uses: 1, group_id: group, household_id: household } : { uses: 1 }; | 
					
						
							|  |  |  |       const { data } = await api.households.createInvitation(payload); | 
					
						
							|  |  |  |       if (data) { | 
					
						
							|  |  |  |         token.value = data.token; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const filteredHouseholds = computed(() => { | 
					
						
							|  |  |  |       if (!selectedGroup.value) return []; | 
					
						
							|  |  |  |       return households.value?.filter(household => household.groupId === selectedGroup.value); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function constructLink(token: string) { | 
					
						
							|  |  |  |       return token ? `${window.location.origin}/register?token=${token}` : ""; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const generatedSignupLink = computed(() => { | 
					
						
							|  |  |  |       return constructLink(token.value); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // =================================================
 | 
					
						
							|  |  |  |     // Email Invitation
 | 
					
						
							|  |  |  |     const state = reactive({ | 
					
						
							|  |  |  |       loading: false, | 
					
						
							|  |  |  |       sendTo: "", | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async function sendInvite() { | 
					
						
							|  |  |  |       state.loading = true; | 
					
						
							|  |  |  |       if (!token.value) { | 
					
						
							|  |  |  |         getSignupLink(selectedGroup.value, selectedHousehold.value); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const { data } = await api.email.sendInvitation({ | 
					
						
							|  |  |  |         email: state.sendTo, | 
					
						
							|  |  |  |         token: token.value, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (data && data.success) { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         alert.success(i18n.t("profile.email-sent")); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							|  |  |  |         alert.error(i18n.t("profile.error-sending-email")); | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |       } | 
					
						
							|  |  |  |       state.loading = false; | 
					
						
							|  |  |  |       inviteDialog.value = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const validEmail = computed(() => { | 
					
						
							|  |  |  |       if (state.sendTo === "") { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const valid = validators.email(state.sendTo); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Explicit bool check because validators.email sometimes returns a string
 | 
					
						
							|  |  |  |       if (valid === true) { | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       sendInvite, | 
					
						
							|  |  |  |       validators, | 
					
						
							|  |  |  |       validEmail, | 
					
						
							|  |  |  |       inviteDialog, | 
					
						
							|  |  |  |       getSignupLink, | 
					
						
							|  |  |  |       generatedSignupLink, | 
					
						
							|  |  |  |       selectedGroup, | 
					
						
							|  |  |  |       selectedHousehold, | 
					
						
							|  |  |  |       filteredHouseholds, | 
					
						
							|  |  |  |       groups, | 
					
						
							|  |  |  |       households, | 
					
						
							|  |  |  |       fetchGroupsAndHouseholds, | 
					
						
							|  |  |  |       ...toRefs(state), | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       isAdmin, | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   watch: { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     modelValue: { | 
					
						
							| 
									
										
										
										
											2024-11-12 04:30:08 +01:00
										 |  |  |       immediate: false, | 
					
						
							|  |  |  |       handler(val) { | 
					
						
							|  |  |  |         if (val && !this.isAdmin) { | 
					
						
							|  |  |  |           this.getSignupLink(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     selectedHousehold(newVal) { | 
					
						
							|  |  |  |       if (newVal && this.selectedGroup) { | 
					
						
							|  |  |  |         this.getSignupLink(this.selectedGroup, this.selectedHousehold); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   created() { | 
					
						
							|  |  |  |     this.fetchGroupsAndHouseholds(); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | </script> |