mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-10-27 16:24:31 -04:00
feat: Improve first time setup ux (#6106)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="d-flex justify-center pb-6 mt-n1">
|
||||
<div class="d-flex pb-6 mt-n1 ml-10">
|
||||
<div style="flex-basis: 500px">
|
||||
<strong> {{ $t("user.password-strength", { strength: pwStrength.strength.value }) }}</strong>
|
||||
<v-progress-linear
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-card-title>
|
||||
<v-card-title class="pt-0">
|
||||
<v-icon
|
||||
size="large"
|
||||
class="mr-3"
|
||||
@@ -10,7 +10,7 @@
|
||||
<span class="headline"> {{ $t("user-registration.account-details") }}</span>
|
||||
</v-card-title>
|
||||
<v-divider />
|
||||
<v-card-text>
|
||||
<v-card-text class="mt-2">
|
||||
<v-form
|
||||
ref="domAccountForm"
|
||||
@submit.prevent
|
||||
|
||||
48
frontend/components/global/AppLogo.vue
Normal file
48
frontend/components/global/AppLogo.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="icon-container">
|
||||
<v-divider class="icon-divider" />
|
||||
<v-avatar
|
||||
:class="['pa-2', 'icon-avatar']"
|
||||
color="primary"
|
||||
:size="size"
|
||||
>
|
||||
<slot>
|
||||
<svg
|
||||
class="icon-white"
|
||||
viewBox="0 0 24 24"
|
||||
:style="{ width: size + 'px', height: size + 'px' }"
|
||||
>
|
||||
<path
|
||||
d="M8.1,13.34L3.91,9.16C2.35,7.59 2.35,5.06 3.91,3.5L10.93,10.5L8.1,13.34M13.41,13L20.29,19.88L18.88,21.29L12,14.41L5.12,21.29L3.71,19.88L13.36,10.22L13.16,10C12.38,9.23 12.38,7.97 13.16,7.19L17.5,2.82L18.43,3.74L15.19,7L16.15,7.94L19.39,4.69L20.31,5.61L17.06,8.85L18,9.81L21.26,6.56L22.18,7.5L17.81,11.84C17.03,12.62 15.77,12.62 15,11.84L14.78,11.64L13.41,13Z"
|
||||
/>
|
||||
</svg>
|
||||
</slot>
|
||||
</v-avatar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { size } = withDefaults(defineProps<{ size?: number }>(), { size: 75 });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.icon-white {
|
||||
fill: white;
|
||||
}
|
||||
.icon-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
.icon-divider {
|
||||
width: 100%;
|
||||
margin-bottom: -2.5rem;
|
||||
}
|
||||
.icon-avatar {
|
||||
border-color: rgba(0, 0, 0, 0.12);
|
||||
border: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -33,9 +33,10 @@
|
||||
<!-- Check Box -->
|
||||
<v-checkbox
|
||||
v-if="inputField.type === fieldTypes.BOOLEAN"
|
||||
v-model="modelValue[inputField.varName]"
|
||||
v-model="model[inputField.varName]"
|
||||
:name="inputField.varName"
|
||||
:disabled="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (disabledFields && disabledFields.includes(inputField.varName))"
|
||||
:readonly="fieldState[inputField.varName]?.readonly"
|
||||
:disabled="fieldState[inputField.varName]?.disabled"
|
||||
:hint="inputField.hint"
|
||||
:hide-details="!inputField.hint"
|
||||
:persistent-hint="!!inputField.hint"
|
||||
@@ -51,9 +52,9 @@
|
||||
<!-- Text Field -->
|
||||
<v-text-field
|
||||
v-else-if="inputField.type === fieldTypes.TEXT || inputField.type === fieldTypes.PASSWORD"
|
||||
v-model="modelValue[inputField.varName]"
|
||||
:readonly="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (readonlyFields && readonlyFields.includes(inputField.varName))"
|
||||
:disabled="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (disabledFields && disabledFields.includes(inputField.varName))"
|
||||
v-model="model[inputField.varName]"
|
||||
:readonly="fieldState[inputField.varName]?.readonly"
|
||||
:disabled="fieldState[inputField.varName]?.disabled"
|
||||
:type="inputField.type === fieldTypes.PASSWORD ? 'password' : 'text'"
|
||||
variant="solo-filled"
|
||||
flat
|
||||
@@ -62,7 +63,7 @@
|
||||
:label="inputField.label"
|
||||
:name="inputField.varName"
|
||||
:hint="inputField.hint || ''"
|
||||
:rules="!(inputField.disableUpdate && updateMode) ? [...rulesByKey(inputField.rules), ...defaultRules] : []"
|
||||
:rules="!(inputField.disableUpdate && updateMode) ? [...rulesByKey(inputField.rules as any), ...defaultRules] : []"
|
||||
lazy-validation
|
||||
@blur="emitBlur"
|
||||
/>
|
||||
@@ -70,9 +71,9 @@
|
||||
<!-- Text Area -->
|
||||
<v-textarea
|
||||
v-else-if="inputField.type === fieldTypes.TEXT_AREA"
|
||||
v-model="modelValue[inputField.varName]"
|
||||
:readonly="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (readonlyFields && readonlyFields.includes(inputField.varName))"
|
||||
:disabled="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (disabledFields && disabledFields.includes(inputField.varName))"
|
||||
v-model="model[inputField.varName]"
|
||||
:readonly="fieldState[inputField.varName]?.readonly"
|
||||
:disabled="fieldState[inputField.varName]?.disabled"
|
||||
variant="solo-filled"
|
||||
flat
|
||||
rows="3"
|
||||
@@ -81,7 +82,7 @@
|
||||
:label="inputField.label"
|
||||
:name="inputField.varName"
|
||||
:hint="inputField.hint || ''"
|
||||
:rules="[...rulesByKey(inputField.rules), ...defaultRules]"
|
||||
:rules="[...rulesByKey(inputField.rules as any), ...defaultRules]"
|
||||
lazy-validation
|
||||
@blur="emitBlur"
|
||||
/>
|
||||
@@ -89,12 +90,11 @@
|
||||
<!-- Option Select -->
|
||||
<v-select
|
||||
v-else-if="inputField.type === fieldTypes.SELECT"
|
||||
v-model="modelValue[inputField.varName]"
|
||||
:readonly="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (readonlyFields && readonlyFields.includes(inputField.varName))"
|
||||
:disabled="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (disabledFields && disabledFields.includes(inputField.varName))"
|
||||
v-model="model[inputField.varName]"
|
||||
:readonly="fieldState[inputField.varName]?.readonly"
|
||||
:disabled="fieldState[inputField.varName]?.disabled"
|
||||
variant="solo-filled"
|
||||
flat
|
||||
:prepend-icon="inputField.icons ? modelValue[inputField.varName] : null"
|
||||
:label="inputField.label"
|
||||
:name="inputField.varName"
|
||||
:items="inputField.options"
|
||||
@@ -119,7 +119,7 @@
|
||||
<v-btn
|
||||
class="my-2 ml-auto"
|
||||
style="min-width: 200px"
|
||||
:color="modelValue[inputField.varName]"
|
||||
:color="model[inputField.varName]"
|
||||
dark
|
||||
v-bind="templateProps"
|
||||
>
|
||||
@@ -127,7 +127,7 @@
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-color-picker
|
||||
v-model="modelValue[inputField.varName]"
|
||||
v-model="model[inputField.varName]"
|
||||
value="#7417BE"
|
||||
hide-canvas
|
||||
hide-inputs
|
||||
@@ -138,11 +138,12 @@
|
||||
</v-menu>
|
||||
</div>
|
||||
|
||||
<!-- Object Type -->
|
||||
<div v-else-if="inputField.type === fieldTypes.OBJECT">
|
||||
<auto-form
|
||||
v-model="modelValue[inputField.varName]"
|
||||
v-model="model[inputField.varName]"
|
||||
:color="color"
|
||||
:items="inputField.items"
|
||||
:items="(inputField as any).items"
|
||||
@blur="emitBlur"
|
||||
/>
|
||||
</div>
|
||||
@@ -150,7 +151,7 @@
|
||||
<!-- List Type -->
|
||||
<div v-else-if="inputField.type === fieldTypes.LIST">
|
||||
<div
|
||||
v-for="(item, idx) in modelValue[inputField.varName]"
|
||||
v-for="(item, idx) in model[inputField.varName]"
|
||||
:key="idx"
|
||||
>
|
||||
<p>
|
||||
@@ -160,15 +161,15 @@
|
||||
class="ml-5"
|
||||
x-small
|
||||
delete
|
||||
@click="removeByIndex(modelValue[inputField.varName], idx)"
|
||||
@click="removeByIndex(model[inputField.varName], idx)"
|
||||
/>
|
||||
</span>
|
||||
</p>
|
||||
<v-divider class="mb-5 mx-2" />
|
||||
<auto-form
|
||||
v-model="modelValue[inputField.varName][idx]"
|
||||
v-model="model[inputField.varName][idx]"
|
||||
:color="color"
|
||||
:items="inputField.items"
|
||||
:items="(inputField as any).items"
|
||||
@blur="emitBlur"
|
||||
/>
|
||||
</div>
|
||||
@@ -176,7 +177,7 @@
|
||||
<v-spacer />
|
||||
<BaseButton
|
||||
small
|
||||
@click="modelValue[inputField.varName].push(getTemplate(inputField.items))"
|
||||
@click="model[inputField.varName].push(getTemplate((inputField as any).items))"
|
||||
>
|
||||
{{ $t("general.new") }}
|
||||
</BaseButton>
|
||||
@@ -197,7 +198,13 @@ const BLUR_EVENT = "blur";
|
||||
type ValidatorKey = keyof typeof validators;
|
||||
|
||||
// Use defineModel for v-model
|
||||
const modelValue = defineModel<[object, Array<any>]>();
|
||||
const modelValue = defineModel<Record<string, any> | any[]>({
|
||||
type: [Object, Array],
|
||||
required: true,
|
||||
});
|
||||
|
||||
// alias to avoid template TS complaining about possible undefined
|
||||
const model = modelValue as any;
|
||||
|
||||
const props = defineProps({
|
||||
updateMode: {
|
||||
@@ -238,26 +245,39 @@ const emit = defineEmits(["blur", "update:modelValue"]);
|
||||
|
||||
function rulesByKey(keys?: ValidatorKey[] | null) {
|
||||
if (keys === undefined || keys === null) {
|
||||
return [];
|
||||
return [] as any[];
|
||||
}
|
||||
|
||||
const list = [] as ((v: string) => boolean | string)[];
|
||||
const list: any[] = [];
|
||||
keys.forEach((key) => {
|
||||
const split = key.split(":");
|
||||
const validatorKey = split[0] as ValidatorKey;
|
||||
if (validatorKey in validators) {
|
||||
if (split.length === 1) {
|
||||
list.push(validators[validatorKey]);
|
||||
list.push((validators as any)[validatorKey]);
|
||||
}
|
||||
else {
|
||||
list.push(validators[validatorKey](split[1]));
|
||||
list.push((validators as any)[validatorKey](split[1] as any));
|
||||
}
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
const defaultRules = computed(() => rulesByKey(props.globalRules as ValidatorKey[]));
|
||||
const defaultRules = computed<any[]>(() => rulesByKey(props.globalRules as any));
|
||||
|
||||
// Combined state map for readonly and disabled fields
|
||||
const fieldState = computed<Record<string, { readonly: boolean; disabled: boolean }>>(() => {
|
||||
const map: Record<string, { readonly: boolean; disabled: boolean }> = {};
|
||||
(props.items || []).forEach((field: any) => {
|
||||
const base = (field.disableUpdate && props.updateMode) || (!props.updateMode && field.disableCreate);
|
||||
map[field.varName] = {
|
||||
readonly: base || !!props.readonlyFields?.includes(field.varName),
|
||||
disabled: base || !!props.disabledFields?.includes(field.varName),
|
||||
};
|
||||
});
|
||||
return map;
|
||||
});
|
||||
|
||||
function removeByIndex(list: never[], index: number) {
|
||||
// Removes the item at the index
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
<template>
|
||||
<div :style="`width: ${width}; height: 100%;`">
|
||||
<LanguageDialog v-model="langDialog" />
|
||||
<v-card>
|
||||
<div>
|
||||
<v-toolbar
|
||||
width="100%"
|
||||
color="primary"
|
||||
class="d-flex justify-center"
|
||||
style="margin-bottom: 4rem"
|
||||
dark
|
||||
>
|
||||
<v-toolbar-title class="headline text-h4 text-center mx-0">
|
||||
Mealie
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<div class="icon-container">
|
||||
<v-divider class="icon-divider" />
|
||||
<v-avatar
|
||||
class="pa-2 icon-avatar"
|
||||
color="primary"
|
||||
size="75"
|
||||
>
|
||||
<svg
|
||||
class="icon-white"
|
||||
style="width: 75"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M8.1,13.34L3.91,9.16C2.35,7.59 2.35,5.06 3.91,3.5L10.93,10.5L8.1,13.34M13.41,13L20.29,19.88L18.88,21.29L12,14.41L5.12,21.29L3.71,19.88L13.36,10.22L13.16,10C12.38,9.23 12.38,7.97 13.16,7.19L17.5,2.82L18.43,3.74L15.19,7L16.15,7.94L19.39,4.69L20.31,5.61L17.06,8.85L18,9.81L21.26,6.56L22.18,7.5L17.81,11.84C17.03,12.62 15.77,12.62 15,11.84L14.78,11.64L13.41,13Z"
|
||||
/>
|
||||
</svg>
|
||||
</v-avatar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-center grow items-center my-4">
|
||||
<slot :width="pageWidth" />
|
||||
</div>
|
||||
<div class="mx-2 my-4">
|
||||
<v-progress-linear
|
||||
v-if="wizardPage > 0"
|
||||
:value="Math.ceil((wizardPage / maxPageNumber) * 100)"
|
||||
striped
|
||||
height="10"
|
||||
/>
|
||||
</div>
|
||||
<v-divider class="ma-2" />
|
||||
<v-card-actions width="100%">
|
||||
<v-btn
|
||||
v-if="prevButtonShow"
|
||||
:disabled="!prevButtonEnable"
|
||||
:color="prevButtonColor"
|
||||
@click="decrementPage"
|
||||
>
|
||||
<v-icon v-if="prevButtonIconRef">
|
||||
{{ prevButtonIconRef }}
|
||||
</v-icon>
|
||||
{{ prevButtonTextRef }}
|
||||
</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
v-if="nextButtonShow"
|
||||
variant="elevated"
|
||||
:disabled="!nextButtonEnable"
|
||||
:color="nextButtonColorRef"
|
||||
@click="incrementPage"
|
||||
>
|
||||
<div v-if="isSubmitting">
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
color="white"
|
||||
size="24"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-icon v-if="nextButtonIconRef && !nextButtonIconAfter">
|
||||
{{ nextButtonIconRef }}
|
||||
</v-icon>
|
||||
{{ nextButtonTextRef }}
|
||||
<v-icon v-if="nextButtonIconRef && nextButtonIconAfter">
|
||||
{{ nextButtonIconRef }}
|
||||
</v-icon>
|
||||
</div>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
<v-card-actions class="justify-center flex-column py-8">
|
||||
<BaseButton
|
||||
large
|
||||
color="primary"
|
||||
@click="langDialog = true"
|
||||
>
|
||||
<template #icon>
|
||||
{{ $globals.icons.translate }}
|
||||
</template>
|
||||
{{ $t("language-dialog.choose-language") }}
|
||||
</BaseButton>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineNuxtComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
minPageNumber: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
maxPageNumber: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: "1200px",
|
||||
},
|
||||
pageWidth: {
|
||||
type: [String, Number],
|
||||
default: "600px",
|
||||
},
|
||||
prevButtonText: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
prevButtonIcon: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
prevButtonColor: {
|
||||
type: String,
|
||||
default: "grey-darken-3",
|
||||
},
|
||||
prevButtonShow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
prevButtonEnable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
nextButtonText: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
nextButtonIcon: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
nextButtonIconAfter: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
nextButtonColor: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
nextButtonShow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
nextButtonEnable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
nextButtonIsSubmit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
isSubmitting: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue", "submit"],
|
||||
setup(props, context) {
|
||||
const i18n = useI18n();
|
||||
const { $globals } = useNuxtApp();
|
||||
const ready = ref(false);
|
||||
const langDialog = ref(false);
|
||||
|
||||
const wizardPage = computed({
|
||||
get: () => props.modelValue,
|
||||
set: value => context.emit("update:modelValue", value),
|
||||
});
|
||||
|
||||
const prevButtonTextRef = computed(() => props.prevButtonText || i18n.t("general.back"));
|
||||
const prevButtonIconRef = computed(() => props.prevButtonIcon || $globals.icons.back);
|
||||
const nextButtonTextRef = computed(
|
||||
() => props.nextButtonText || (
|
||||
props.nextButtonIsSubmit ? i18n.t("general.submit") : i18n.t("general.next")
|
||||
),
|
||||
);
|
||||
const nextButtonIconRef = computed(
|
||||
() => props.nextButtonIcon || (
|
||||
props.nextButtonIsSubmit ? $globals.icons.createAlt : $globals.icons.forward
|
||||
),
|
||||
);
|
||||
const nextButtonColorRef = computed(
|
||||
() => props.nextButtonColor || (props.nextButtonIsSubmit ? "success" : "info"),
|
||||
);
|
||||
|
||||
function goToPage(page: number) {
|
||||
if (page < props.minPageNumber) {
|
||||
goToPage(props.minPageNumber);
|
||||
return;
|
||||
}
|
||||
else if (page > props.maxPageNumber) {
|
||||
goToPage(props.maxPageNumber);
|
||||
return;
|
||||
}
|
||||
wizardPage.value = page;
|
||||
}
|
||||
|
||||
function decrementPage() {
|
||||
goToPage(wizardPage.value - 1);
|
||||
}
|
||||
|
||||
function incrementPage() {
|
||||
if (props.nextButtonIsSubmit) {
|
||||
context.emit("submit", wizardPage.value);
|
||||
}
|
||||
else {
|
||||
goToPage(wizardPage.value + 1);
|
||||
}
|
||||
}
|
||||
|
||||
ready.value = true;
|
||||
|
||||
return {
|
||||
wizardPage,
|
||||
ready,
|
||||
langDialog,
|
||||
prevButtonTextRef,
|
||||
prevButtonIconRef,
|
||||
nextButtonTextRef,
|
||||
nextButtonIconRef,
|
||||
nextButtonColorRef,
|
||||
decrementPage,
|
||||
incrementPage,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.icon-primary {
|
||||
fill: var(--v-primary-base);
|
||||
}
|
||||
|
||||
.icon-white {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
|
||||
.icon-divider {
|
||||
width: 100%;
|
||||
margin-bottom: -2.5rem;
|
||||
}
|
||||
|
||||
.icon-avatar {
|
||||
border-color: rgba(0, 0, 0, 0.12);
|
||||
border: 2px;
|
||||
}
|
||||
|
||||
.bg-off-white {
|
||||
background: #f5f8fa;
|
||||
}
|
||||
|
||||
.preferred-width {
|
||||
width: 840px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,26 +52,7 @@
|
||||
Mealie
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<div class="icon-container">
|
||||
<v-divider class="icon-divider" />
|
||||
<v-avatar
|
||||
class="pa-2 icon-avatar"
|
||||
color="primary"
|
||||
size="100"
|
||||
>
|
||||
<svg
|
||||
class="icon-white"
|
||||
style="width: 100px; height: 100px"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M8.1,13.34L3.91,9.16C2.35,7.59 2.35,5.06 3.91,3.5L10.93,10.5L8.1,13.34M13.41,13L20.29,19.88L18.88,21.29L12,14.41L5.12,21.29L3.71,19.88L13.36,10.22L13.16,10C12.38,9.23 12.38,7.97 13.16,7.19L17.5,2.82L18.43,3.74L15.19,7L16.15,7.94L19.39,4.69L20.31,5.61L17.06,8.85L18,9.81L21.26,6.56L22.18,7.5L17.81,11.84C17.03,12.62 15.77,12.62 15,11.84L14.78,11.64L13.41,13Z"
|
||||
/>
|
||||
</svg>
|
||||
</v-avatar>
|
||||
</div>
|
||||
|
||||
<AppLogo :size="100" />
|
||||
<v-card-title class="text-h5 justify-center pb-3">
|
||||
{{ $t('user.sign-in') }}
|
||||
</v-card-title>
|
||||
|
||||
@@ -23,25 +23,7 @@
|
||||
Mealie
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<div class="icon-container">
|
||||
<v-divider class="icon-divider" />
|
||||
<v-avatar
|
||||
class="pa-2 icon-avatar"
|
||||
color="primary"
|
||||
size="75"
|
||||
>
|
||||
<svg
|
||||
class="icon-white"
|
||||
style="width: 75"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M8.1,13.34L3.91,9.16C2.35,7.59 2.35,5.06 3.91,3.5L10.93,10.5L8.1,13.34M13.41,13L20.29,19.88L18.88,21.29L12,14.41L5.12,21.29L3.71,19.88L13.36,10.22L13.16,10C12.38,9.23 12.38,7.97 13.16,7.19L17.5,2.82L18.43,3.74L15.19,7L16.15,7.94L19.39,4.69L20.31,5.61L17.06,8.85L18,9.81L21.26,6.56L22.18,7.5L17.81,11.84C17.03,12.62 15.77,12.62 15,11.84L14.78,11.64L13.41,13Z"
|
||||
/>
|
||||
</svg>
|
||||
</v-avatar>
|
||||
</div>
|
||||
<AppLogo />
|
||||
</div>
|
||||
|
||||
<!-- Form Container -->
|
||||
|
||||
4
frontend/types/components.d.ts
vendored
4
frontend/types/components.d.ts
vendored
@@ -15,7 +15,6 @@ import type BaseDivider from "@/components/global/BaseDivider.vue";
|
||||
import type BaseOverflowButton from "@/components/global/BaseOverflowButton.vue";
|
||||
import type BasePageTitle from "@/components/global/BasePageTitle.vue";
|
||||
import type BaseStatCard from "@/components/global/BaseStatCard.vue";
|
||||
import type BaseWizard from "@/components/global/BaseWizard.vue";
|
||||
import type ButtonLink from "@/components/global/ButtonLink.vue";
|
||||
import type ContextMenu from "@/components/global/ContextMenu.vue";
|
||||
import type CrudTable from "@/components/global/CrudTable.vue";
|
||||
@@ -56,7 +55,6 @@ declare module "vue" {
|
||||
BaseOverflowButton: typeof BaseOverflowButton;
|
||||
BasePageTitle: typeof BasePageTitle;
|
||||
BaseStatCard: typeof BaseStatCard;
|
||||
BaseWizard: typeof BaseWizard;
|
||||
ButtonLink: typeof ButtonLink;
|
||||
ContextMenu: typeof ContextMenu;
|
||||
CrudTable: typeof CrudTable;
|
||||
@@ -81,4 +79,4 @@ declare module "vue" {
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
export { };
|
||||
|
||||
Reference in New Issue
Block a user