chore: script setup components (#7299)

This commit is contained in:
Kuchenpirat
2026-03-23 21:18:25 +01:00
committed by GitHub
parent 3ad2d9155d
commit 5ab6e98f9e
47 changed files with 1721 additions and 2453 deletions

View File

@@ -4,10 +4,10 @@
:disabled="!user || !tooltip"
location="end"
>
<template #activator="{ props }">
<template #activator="{ props: tooltipProps }">
<v-avatar
v-if="list"
v-bind="props"
v-bind="tooltipProps"
>
<v-img
:src="imageURL"
@@ -19,7 +19,7 @@
<v-avatar
v-else
:size="size"
v-bind="props"
v-bind="tooltipProps"
>
<v-img
:src="imageURL"
@@ -35,51 +35,40 @@
</v-tooltip>
</template>
<script lang="ts">
<script setup lang="ts">
import { useUserStore } from "~/composables/store/use-user-store";
export default defineNuxtComponent({
props: {
userId: {
type: String,
required: true,
},
list: {
type: Boolean,
default: false,
},
size: {
type: String,
default: "42",
},
tooltip: {
type: Boolean,
default: true,
},
const props = defineProps({
userId: {
type: String,
required: true,
},
setup(props) {
const state = reactive({
error: false,
});
const auth = useMealieAuth();
const { store: users } = useUserStore();
const user = computed(() => {
return users.value.find(user => user.id === props.userId);
});
const imageURL = computed(() => {
// Note: auth.user is a ref now
const authUser = auth.user.value;
const key = authUser?.cacheKey ?? "";
return `/api/media/users/${props.userId}/profile.webp?cacheKey=${key}`;
});
return {
user,
imageURL,
...toRefs(state),
};
list: {
type: Boolean,
default: false,
},
size: {
type: String,
default: "42",
},
tooltip: {
type: Boolean,
default: true,
},
});
const error = ref(false);
const auth = useMealieAuth();
const { store: users } = useUserStore();
const user = computed(() => {
return users.value.find(user => user.id === props.userId);
});
const imageURL = computed(() => {
// Note: auth.user is a ref now
const authUser = auth.user.value;
const key = authUser?.cacheKey ?? "";
return `/api/media/users/${props.userId}/profile.webp?cacheKey=${key}`;
});
</script>

View File

@@ -73,8 +73,7 @@
</BaseDialog>
</template>
<script lang="ts">
import { watchEffect } from "vue";
<script setup lang="ts">
import { useUserApi } from "@/composables/api";
import BaseDialog from "~/components/global/BaseDialog.vue";
import AppButtonCopy from "~/components/global/AppButtonCopy.vue";
@@ -86,147 +85,95 @@ import type { HouseholdInDB } from "~/lib/api/types/household";
import { useGroups } from "~/composables/use-groups";
import { useAdminHouseholds } from "~/composables/use-households";
export default defineNuxtComponent({
name: "UserInviteDialog",
components: {
BaseDialog,
AppButtonCopy,
BaseButton,
},
props: {
modelValue: {
type: Boolean,
default: false,
},
},
emits: ["update:modelValue"],
setup(props, context) {
const i18n = useI18n();
const auth = useMealieAuth();
const inviteDialog = defineModel<boolean>("modelValue", { type: Boolean, default: false });
const isAdmin = computed(() => auth.user.value?.admin);
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 i18n = useI18n();
const auth = useMealieAuth();
const fetchGroupsAndHouseholds = () => {
if (isAdmin.value) {
const groupsResponse = useGroups();
const householdsResponse = useAdminHouseholds();
watchEffect(() => {
groups.value = groupsResponse.groups.value || [];
households.value = householdsResponse.households.value || [];
});
}
};
const isAdmin = computed(() => auth.user.value?.admin);
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 inviteDialog = computed<boolean>({
get() {
return props.modelValue;
},
set(val) {
context.emit("update:modelValue", val);
},
const fetchGroupsAndHouseholds = () => {
if (isAdmin.value) {
const groupsResponse = useGroups();
const householdsResponse = useAdminHouseholds();
watchEffect(() => {
groups.value = groupsResponse.groups.value || [];
households.value = householdsResponse.households.value || [];
});
}
};
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;
}
}
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) {
alert.success(i18n.t("profile.email-sent"));
}
else {
alert.error(i18n.t("profile.error-sending-email"));
}
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),
isAdmin,
};
},
watch: {
modelValue: {
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();
},
const filteredHouseholds = computed(() => {
if (!selectedGroup.value) return [];
return households.value?.filter(household => household.groupId === selectedGroup.value);
});
function constructLink(tokenVal: string) {
return tokenVal ? `${window.location.origin}/register?token=${tokenVal}` : "";
}
const generatedSignupLink = computed(() => constructLink(token.value));
// Email Invitation
const state = reactive({
loading: false,
sendTo: "",
});
const { loading, sendTo } = toRefs(state);
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) {
alert.success(i18n.t("profile.email-sent"));
}
else {
alert.error(i18n.t("profile.error-sending-email"));
}
state.loading = false;
inviteDialog.value = false;
}
const validEmail = computed(() => {
if (sendTo.value === "") return false;
const valid = validators.email(sendTo.value);
return valid === true;
});
// Watchers (replacing options API watchers)
watch(inviteDialog, (val) => {
if (val && !isAdmin.value) {
getSignupLink();
}
});
watch(selectedHousehold, (newVal) => {
if (newVal && selectedGroup.value) {
getSignupLink(selectedGroup.value, selectedHousehold.value);
}
});
// initial fetch
fetchGroupsAndHouseholds();
</script>

View File

@@ -52,14 +52,14 @@
</v-card>
</template>
<script lang="ts" setup>
<script setup lang="ts">
interface LinkProp {
text: string;
url?: string;
to: string;
}
const props = defineProps({
defineProps({
link: {
type: Object as () => LinkProp,
required: true,
@@ -70,6 +70,4 @@ const props = defineProps({
default: "",
},
});
console.log("Props", props);
</script>

View File

@@ -78,57 +78,29 @@
</div>
</template>
<script lang="ts">
import { useDark } from "@vueuse/core";
<script setup lang="ts">
import { validators } from "~/composables/use-validators";
import { useUserRegistrationForm } from "~/composables/use-users/user-registration-form";
import { usePasswordField } from "~/composables/use-passwords";
import UserPasswordStrength from "~/components/Domain/User/UserPasswordStrength.vue";
definePageMeta({ layout: "blank" });
const inputAttrs = {
validateOnBlur: true,
class: "pb-1",
variant: "solo-filled" as any,
};
export default defineNuxtComponent({
components: { UserPasswordStrength },
setup() {
definePageMeta({
layout: "blank",
});
const isDark = useDark();
const langDialog = ref(false);
const pwFields = usePasswordField();
const {
accountDetails,
credentials,
emailErrorMessages,
usernameErrorMessages,
validateUsername,
validateEmail,
domAccountForm,
} = useUserRegistrationForm();
return {
accountDetails,
credentials,
emailErrorMessages,
inputAttrs,
isDark,
langDialog,
pwFields,
usernameErrorMessages,
validators,
// Validators
validateUsername,
validateEmail,
// Dom Refs
domAccountForm,
};
},
});
const pwFields = usePasswordField();
const {
accountDetails,
credentials,
emailErrorMessages,
usernameErrorMessages,
validateUsername,
validateEmail,
} = useUserRegistrationForm();
</script>
<style lang="css" scoped>