mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
Feature/submit on enter key (#224)
* general cleanup * submit on enter * fix signup form * fix duplicate slugs when testing Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
parent
223c198e83
commit
894d6b9c9b
23 changed files with 216 additions and 210 deletions
|
@ -24,7 +24,7 @@
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
v-on="on"
|
v-on="on"
|
||||||
>
|
>
|
||||||
{{ $t('user.create-group') }}
|
{{ $t("user.create-group") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-card>
|
<v-card>
|
||||||
|
@ -34,31 +34,30 @@
|
||||||
</v-icon>
|
</v-icon>
|
||||||
|
|
||||||
<v-toolbar-title class="headline">
|
<v-toolbar-title class="headline">
|
||||||
{{ $t('user.create-group') }}
|
{{ $t("user.create-group") }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
<v-form ref="newGroup" @submit="createGroup">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form ref="newGroup">
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="newGroupName"
|
v-model="newGroupName"
|
||||||
:label="$t('user.group-name')"
|
:label="$t('user.group-name')"
|
||||||
:rules="[existsRule]"
|
:rules="[existsRule]"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-form>
|
</v-card-text>
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="grey" text @click="groupDialog = false">
|
<v-btn color="grey" text @click="groupDialog = false">
|
||||||
{{ $t('general.cancel') }}
|
{{ $t("general.cancel") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="primary" @click="createGroup">
|
<v-btn color="primary" type="submit" @click.prevent="createGroup">
|
||||||
{{ $t('general.create') }}
|
{{ $t("general.create") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
</v-form>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
<Confirmation
|
<Confirmation
|
||||||
ref="deleteUserDialog"
|
ref="deleteUserDialog"
|
||||||
:title="$t('user.confirm-link-deletion')"
|
:title="$t('user.confirm-link-deletion')"
|
||||||
:message="$t('user.are-you-sure-you-want-to-delete-the-link', {link: activeName })"
|
:message="
|
||||||
|
$t('user.are-you-sure-you-want-to-delete-the-link', {
|
||||||
|
link: activeName,
|
||||||
|
})
|
||||||
|
"
|
||||||
icon="mdi-alert"
|
icon="mdi-alert"
|
||||||
@confirm="deleteUser"
|
@confirm="deleteUser"
|
||||||
:width="450"
|
:width="450"
|
||||||
|
@ -14,14 +18,14 @@
|
||||||
mdi-link-variant
|
mdi-link-variant
|
||||||
</v-icon>
|
</v-icon>
|
||||||
<v-toolbar-title class="headine">
|
<v-toolbar-title class="headine">
|
||||||
{{ $t('user.sign-up-links') }}
|
{{ $t("user.sign-up-links") }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-spacer> </v-spacer>
|
<v-spacer> </v-spacer>
|
||||||
<v-dialog v-model="dialog" max-width="600px">
|
<v-dialog v-model="dialog" max-width="600px">
|
||||||
<template v-slot:activator="{ on, attrs }">
|
<template v-slot:activator="{ on, attrs }">
|
||||||
<v-btn small color="success" dark v-bind="attrs" v-on="on">
|
<v-btn small color="success" dark v-bind="attrs" v-on="on">
|
||||||
{{ $t('user.create-link') }}
|
{{ $t("user.create-link") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-card>
|
<v-card>
|
||||||
|
@ -31,14 +35,13 @@
|
||||||
</v-icon>
|
</v-icon>
|
||||||
|
|
||||||
<v-toolbar-title class="headline">
|
<v-toolbar-title class="headline">
|
||||||
{{ $t('user.create-link') }}
|
{{ $t("user.create-link") }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
<v-form ref="newUser" @submit="save">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form ref="newUser">
|
|
||||||
<v-row class="justify-center mt-3">
|
<v-row class="justify-center mt-3">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
|
@ -52,18 +55,18 @@
|
||||||
:label="$t('user.admin')"
|
:label="$t('user.admin')"
|
||||||
></v-checkbox>
|
></v-checkbox>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-form>
|
</v-card-text>
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="grey" text @click="close">
|
<v-btn color="grey" text @click="close">
|
||||||
{{ $t('general.cancel') }}
|
{{ $t("general.cancel") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="primary" @click="save">
|
<v-btn color="primary" type="submit" @click.prevent="save">
|
||||||
{{ $t('general.save') }}
|
{{ $t("general.save") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
</v-form>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
@ -90,7 +93,7 @@
|
||||||
<v-icon small left>
|
<v-icon small left>
|
||||||
mdi-account-cog
|
mdi-account-cog
|
||||||
</v-icon>
|
</v-icon>
|
||||||
{{ item.admin ? $t('general.yes') : $t('general.no') }}
|
{{ item.admin ? $t("general.yes") : $t("general.no") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:item.actions="{ item }">
|
<template v-slot:item.actions="{ item }">
|
||||||
|
@ -98,7 +101,7 @@
|
||||||
<v-icon small left>
|
<v-icon small left>
|
||||||
mdi-delete
|
mdi-delete
|
||||||
</v-icon>
|
</v-icon>
|
||||||
{{ $t('general.delete') }}
|
{{ $t("general.delete") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
</v-data-table>
|
</v-data-table>
|
||||||
|
@ -113,21 +116,21 @@ import { validators } from "@/mixins/validators";
|
||||||
export default {
|
export default {
|
||||||
components: { Confirmation },
|
components: { Confirmation },
|
||||||
mixins: [validators],
|
mixins: [validators],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dialog: false,
|
dialog: false,
|
||||||
activeId: null,
|
activeId: null,
|
||||||
activeName: null,
|
activeName: null,
|
||||||
headers: [
|
headers: [
|
||||||
{
|
{
|
||||||
text: this.$t('user.link-id'),
|
text: this.$t("user.link-id"),
|
||||||
align: "start",
|
align: "start",
|
||||||
sortable: false,
|
sortable: false,
|
||||||
value: "id",
|
value: "id",
|
||||||
},
|
},
|
||||||
{ text: this.$t('general.name'), value: "name" },
|
{ text: this.$t("general.name"), value: "name" },
|
||||||
{ text: this.$t('general.token'), value: "token" },
|
{ text: this.$t("general.token"), value: "token" },
|
||||||
{ text: this.$t('user.admin'), value: "admin", align: "center" },
|
{ text: this.$t("user.admin"), value: "admin", align: "center" },
|
||||||
{ text: "", value: "actions", sortable: false, align: "center" },
|
{ text: "", value: "actions", sortable: false, align: "center" },
|
||||||
],
|
],
|
||||||
links: [],
|
links: [],
|
||||||
|
@ -144,7 +147,7 @@ export default {
|
||||||
admin: false,
|
admin: false,
|
||||||
id: 0,
|
id: 0,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -3,7 +3,12 @@
|
||||||
<Confirmation
|
<Confirmation
|
||||||
ref="deleteUserDialog"
|
ref="deleteUserDialog"
|
||||||
:title="$t('user.confirm-user-deletion')"
|
:title="$t('user.confirm-user-deletion')"
|
||||||
:message="$t('user.are-you-sure-you-want-to-delete-the-user', { activeName, activeId })"
|
:message="
|
||||||
|
$t('user.are-you-sure-you-want-to-delete-the-user', {
|
||||||
|
activeName,
|
||||||
|
activeId,
|
||||||
|
})
|
||||||
|
"
|
||||||
icon="mdi-alert"
|
icon="mdi-alert"
|
||||||
@confirm="deleteUser"
|
@confirm="deleteUser"
|
||||||
:width="450"
|
:width="450"
|
||||||
|
@ -25,7 +30,7 @@
|
||||||
<v-dialog v-model="dialog" max-width="600px">
|
<v-dialog v-model="dialog" max-width="600px">
|
||||||
<template v-slot:activator="{ on, attrs }">
|
<template v-slot:activator="{ on, attrs }">
|
||||||
<v-btn small color="success" dark v-bind="attrs" v-on="on">
|
<v-btn small color="success" dark v-bind="attrs" v-on="on">
|
||||||
{{$t('user.create-user')}}
|
{{ $t("user.create-user") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-card>
|
<v-card>
|
||||||
|
@ -40,12 +45,11 @@
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-toolbar-title class="headline">
|
<v-toolbar-title class="headline">
|
||||||
{{$t('user.user-id-with-value', {id: editedItem.id }) }}
|
{{ $t("user.user-id-with-value", { id: editedItem.id }) }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
<v-form ref="newUser" @submit="save">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form ref="newUser">
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" sm="12" md="6">
|
<v-col cols="12" sm="12" md="6">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
|
@ -80,21 +84,24 @@
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="12" md="3">
|
<v-col cols="12" sm="12" md="3">
|
||||||
<v-switch v-model="editedItem.admin" :label="$t('user.admin')"></v-switch>
|
<v-switch
|
||||||
|
v-model="editedItem.admin"
|
||||||
|
:label="$t('user.admin')"
|
||||||
|
></v-switch>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-form>
|
</v-card-text>
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="grey" text @click="close">
|
<v-btn color="grey" text @click="close">
|
||||||
{{$t('general.cancel')}}
|
{{ $t("general.cancel") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="primary" @click="save">
|
<v-btn color="primary" type="submit" @click.prevent="save">
|
||||||
{{$t('general.save')}}
|
{{ $t("general.save") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
</v-form>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
@ -111,13 +118,13 @@
|
||||||
<v-icon small left>
|
<v-icon small left>
|
||||||
mdi-delete
|
mdi-delete
|
||||||
</v-icon>
|
</v-icon>
|
||||||
{{$t('general.delete')}}
|
{{ $t("general.delete") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn small color="success" @click="editItem(item)">
|
<v-btn small color="success" @click="editItem(item)">
|
||||||
<v-icon small left class="mr-2">
|
<v-icon small left class="mr-2">
|
||||||
mdi-pencil
|
mdi-pencil
|
||||||
</v-icon>
|
</v-icon>
|
||||||
{{$t('general.edit')}}
|
{{ $t("general.edit") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:item.admin="{ item }">
|
<template v-slot:item.admin="{ item }">
|
||||||
|
@ -125,7 +132,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:no-data>
|
<template v-slot:no-data>
|
||||||
<v-btn color="primary" @click="initialize">
|
<v-btn color="primary" @click="initialize">
|
||||||
{{$t('general.reset')}}
|
{{ $t("general.reset") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
</v-data-table>
|
</v-data-table>
|
||||||
|
@ -140,7 +147,7 @@ import { validators } from "@/mixins/validators";
|
||||||
export default {
|
export default {
|
||||||
components: { Confirmation },
|
components: { Confirmation },
|
||||||
mixins: [validators],
|
mixins: [validators],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
search: "",
|
search: "",
|
||||||
dialog: false,
|
dialog: false,
|
||||||
|
@ -153,10 +160,10 @@ export default {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
value: "id",
|
value: "id",
|
||||||
},
|
},
|
||||||
{ text: this.$t('user.full-name'), value: "fullName" },
|
{ text: this.$t("user.full-name"), value: "fullName" },
|
||||||
{ text: this.$t('user.email'), value: "email" },
|
{ text: this.$t("user.email"), value: "email" },
|
||||||
{ text: this.$t('user.group'), value: "group" },
|
{ text: this.$t("user.group"), value: "group" },
|
||||||
{ text: this.$t('user.admin'), value: "admin" },
|
{ text: this.$t("user.admin"), value: "admin" },
|
||||||
{ text: "", value: "actions", sortable: false, align: "center" },
|
{ text: "", value: "actions", sortable: false, align: "center" },
|
||||||
],
|
],
|
||||||
users: [],
|
users: [],
|
||||||
|
@ -177,12 +184,14 @@ export default {
|
||||||
group: "",
|
group: "",
|
||||||
admin: false,
|
admin: false,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
formTitle() {
|
formTitle() {
|
||||||
return this.editedIndex === -1 ? this.$t('user.new-user') : this.$t('user.edit-user');
|
return this.editedIndex === -1
|
||||||
|
? this.$t("user.new-user")
|
||||||
|
: this.$t("user.edit-user");
|
||||||
},
|
},
|
||||||
showPassword() {
|
showPassword() {
|
||||||
return this.editedIndex === -1 ? true : false;
|
return this.editedIndex === -1 ? true : false;
|
||||||
|
|
|
@ -17,22 +17,24 @@
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
<v-card-title> </v-card-title>
|
<v-card-title> </v-card-title>
|
||||||
<v-card-text>
|
<v-form @submit="select">
|
||||||
<v-text-field
|
<v-card-text>
|
||||||
:label="$t('settings.theme.theme-name')"
|
<v-text-field
|
||||||
v-model="themeName"
|
:label="$t('settings.theme.theme-name')"
|
||||||
:rules="[rules.required]"
|
v-model="themeName"
|
||||||
></v-text-field>
|
:rules="[rules.required]"
|
||||||
</v-card-text>
|
></v-text-field>
|
||||||
<v-card-actions>
|
</v-card-text>
|
||||||
<v-spacer></v-spacer>
|
<v-card-actions>
|
||||||
<v-btn color="grey" text @click="dialog = false">
|
<v-spacer></v-spacer>
|
||||||
{{ $t("general.cancel") }}
|
<v-btn color="grey" text @click="dialog = false">
|
||||||
</v-btn>
|
{{ $t("general.cancel") }}
|
||||||
<v-btn color="success" text @click="Select" :disabled="!themeName">
|
</v-btn>
|
||||||
{{ $t("general.create") }}
|
<v-btn color="success" text type="submit" @click.prevent="select" :disabled="!themeName">
|
||||||
</v-btn>
|
{{ $t("general.create") }}
|
||||||
</v-card-actions>
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-form>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,7 +66,7 @@ export default {
|
||||||
randomColor() {
|
randomColor() {
|
||||||
return "#" + Math.floor(Math.random() * 16777215).toString(16);
|
return "#" + Math.floor(Math.random() * 16777215).toString(16);
|
||||||
},
|
},
|
||||||
Select() {
|
select() {
|
||||||
const newTheme = {
|
const newTheme = {
|
||||||
name: this.themeName,
|
name: this.themeName,
|
||||||
colors: {
|
colors: {
|
||||||
|
|
|
@ -13,11 +13,12 @@
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
>
|
>
|
||||||
</v-progress-circular>
|
</v-progress-circular>
|
||||||
<v-toolbar-title class="headline">{{$t('user.login')}}</v-toolbar-title>
|
<v-toolbar-title class="headline">{{ $t("user.login") }}</v-toolbar-title>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
<v-card-text>
|
|
||||||
<v-form>
|
<v-form @submit="login">
|
||||||
|
<v-card-text>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-if="!options.isLoggingIn"
|
v-if="!options.isLoggingIn"
|
||||||
v-model="user.name"
|
v-model="user.name"
|
||||||
|
@ -43,22 +44,24 @@
|
||||||
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
|
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
|
||||||
@click:append="showPassword = !showPassword"
|
@click:append="showPassword = !showPassword"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-form>
|
<v-card-actions>
|
||||||
<v-card-actions>
|
<v-btn
|
||||||
<v-btn
|
v-if="options.isLoggingIn"
|
||||||
v-if="options.isLoggingIn"
|
@click.prevent="login"
|
||||||
@click.prevent="login"
|
dark
|
||||||
dark
|
color="primary"
|
||||||
color="primary"
|
block="block"
|
||||||
block="block"
|
type="submit"
|
||||||
type="submit"
|
>{{ $t("user.sign-in") }}
|
||||||
>{{ $t("user.sign-in") }}</v-btn
|
</v-btn
|
||||||
>
|
>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
<v-alert v-if="error" outlined class="mt-3 mb-0" type="error">
|
|
||||||
{{$t('user.could-not-validate-credentials')}}
|
<v-alert v-if="error" outlined class="mt-3 mb-0" type="error">
|
||||||
</v-alert>
|
{{ $t("user.could-not-validate-credentials") }}
|
||||||
</v-card-text>
|
</v-alert>
|
||||||
|
</v-card-text>
|
||||||
|
</v-form>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
have a valid invitation link. If you haven't recieved an invitation you
|
have a valid invitation link. If you haven't recieved an invitation you
|
||||||
are unable to sign-up. To recieve a link, contact the sites administrator.
|
are unable to sign-up. To recieve a link, contact the sites administrator.
|
||||||
<v-divider class="mt-3"></v-divider>
|
<v-divider class="mt-3"></v-divider>
|
||||||
<v-form ref="signUpForm">
|
<v-form ref="signUpForm" @submit="signUp">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="user.name"
|
v-model="user.name"
|
||||||
light="light"
|
light="light"
|
||||||
|
@ -63,22 +63,22 @@
|
||||||
]"
|
]"
|
||||||
@click:append="showPassword = !showPassword"
|
@click:append="showPassword = !showPassword"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-btn
|
||||||
|
v-if="options.isLoggingIn"
|
||||||
|
@click.prevent="signUp"
|
||||||
|
dark
|
||||||
|
color="primary"
|
||||||
|
block="block"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
<v-alert dense v-if="error" outlined class="mt-3 mb-0" type="error">
|
||||||
|
Error Signing Up
|
||||||
|
</v-alert>
|
||||||
</v-form>
|
</v-form>
|
||||||
<v-card-actions>
|
|
||||||
<v-btn
|
|
||||||
v-if="options.isLoggingIn"
|
|
||||||
@click.prevent="signUp"
|
|
||||||
dark
|
|
||||||
color="primary"
|
|
||||||
block="block"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
Sign Up
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
<v-alert dense v-if="error" outlined class="mt-3 mb-0" type="error">
|
|
||||||
Error Signing Up
|
|
||||||
</v-alert>
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -21,9 +21,8 @@
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
<v-form ref="urlForm" @submit="createRecipe">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form ref="urlForm">
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="recipeURL"
|
v-model="recipeURL"
|
||||||
:label="$t('new-recipe.recipe-url')"
|
:label="$t('new-recipe.recipe-url')"
|
||||||
|
@ -35,29 +34,29 @@
|
||||||
:hint="$t('new-recipe.url-form-hint')"
|
:hint="$t('new-recipe.url-form-hint')"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-form>
|
|
||||||
|
|
||||||
<v-alert v-if="error" color="red" outlined type="success">
|
<v-alert v-if="error" color="red" outlined type="success">
|
||||||
{{ $t("new-recipe.error-message") }}
|
{{ $t("new-recipe.error-message") }}
|
||||||
</v-alert>
|
</v-alert>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="grey" text @click="reset">
|
<v-btn color="grey" text @click="reset">
|
||||||
{{ $t("general.close") }}
|
{{ $t("general.close") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
color="success"
|
color="success"
|
||||||
text
|
text
|
||||||
@click="createRecipe"
|
@click.prevent="createRecipe"
|
||||||
:loading="processing"
|
:loading="processing"
|
||||||
>
|
>
|
||||||
{{ $t("general.submit") }}
|
{{ $t("general.submit") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
</v-form>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
<v-speed-dial v-model="fab" fixed right bottom open-on-hover>
|
<v-speed-dial v-model="fab" fixed right bottom open-on-hover>
|
||||||
|
|
|
@ -42,7 +42,6 @@ def update_generics(func):
|
||||||
|
|
||||||
elif attribute:
|
elif attribute:
|
||||||
setattr(class_object, key, value)
|
setattr(class_object, key, value)
|
||||||
print("Complex", complex_attributed)
|
|
||||||
func(class_object, complex_attributed)
|
func(class_object, complex_attributed)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
|
@ -23,7 +23,6 @@ async def get_current_user(token: str = Depends(oauth2_scheme), session=Depends(
|
||||||
if username is None:
|
if username is None:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
token_data = TokenData(username=username)
|
token_data = TokenData(username=username)
|
||||||
print("Login Payload", token_data)
|
|
||||||
except JWTError:
|
except JWTError:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
user = db.users.get(session, token_data.username, "email")
|
user = db.users.get(session, token_data.username, "email")
|
||||||
|
|
|
@ -77,6 +77,5 @@ def get_today(
|
||||||
|
|
||||||
group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name")
|
group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name")
|
||||||
recipe = get_todays_meal(session, group_in_db)
|
recipe = get_todays_meal(session, group_in_db)
|
||||||
print(datetime.date.today())
|
|
||||||
|
|
||||||
return recipe.slug
|
return recipe.slug
|
||||||
|
|
|
@ -68,14 +68,11 @@ async def update_user(
|
||||||
|
|
||||||
token = None
|
token = None
|
||||||
if current_user.id == id or current_user.admin:
|
if current_user.id == id or current_user.admin:
|
||||||
print("Current User")
|
|
||||||
db.users.update(session, id, new_data.dict())
|
db.users.update(session, id, new_data.dict())
|
||||||
if current_user.id == id:
|
if current_user.id == id:
|
||||||
print(new_data.email)
|
access_token = security.create_access_token(data=dict(sub=new_data.email), expires_delta=timedelta(hours=2))
|
||||||
access_token = security.create_access_token(data=dict(sub=new_data.email), expires_delta=timedelta(hours=2))
|
token = {"access_token": access_token, "token_type": "bearer"}
|
||||||
token = {"access_token": access_token, "token_type": "bearer"}
|
|
||||||
|
|
||||||
print(SnackResponse.success("User Updated", token))
|
|
||||||
return SnackResponse.success("User Updated", token)
|
return SnackResponse.success("User Updated", token)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,10 @@ class ImportJob(BackupOptions):
|
||||||
"example": {
|
"example": {
|
||||||
"name": "my_local_backup.zip",
|
"name": "my_local_backup.zip",
|
||||||
"recipes": True,
|
"recipes": True,
|
||||||
"force": False,
|
"settings": True,
|
||||||
"rebase": False,
|
"themes": True,
|
||||||
"themes": False,
|
"groups": True,
|
||||||
"settings": False,
|
"users": True,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,13 +89,16 @@ class ImportDatabase:
|
||||||
# Migration from list to Object Type Data
|
# Migration from list to Object Type Data
|
||||||
try:
|
try:
|
||||||
if "" in recipe_dict["tags"]:
|
if "" in recipe_dict["tags"]:
|
||||||
recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if not tag == ""]
|
recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if tag != ""]
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if "" in recipe_dict["categories"]:
|
if "" in recipe_dict["categories"]:
|
||||||
recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if not cat == ""]
|
recipe_dict["categories"] = [
|
||||||
|
cat for cat in recipe_dict["categories"] if cat != ""
|
||||||
|
]
|
||||||
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -239,16 +242,15 @@ class ImportDatabase:
|
||||||
|
|
||||||
item = db_table.get(self.session, search_value, search_key)
|
item = db_table.get(self.session, search_value, search_key)
|
||||||
if item:
|
if item:
|
||||||
if self.force_imports:
|
if not self.force_imports:
|
||||||
primary_key = getattr(item, db_table.primary_key)
|
|
||||||
db_table.delete(self.session, primary_key)
|
|
||||||
else:
|
|
||||||
return return_model(
|
return return_model(
|
||||||
name=model_name,
|
name=model_name,
|
||||||
status=False,
|
status=False,
|
||||||
exception=f"Table entry with matching '{search_key}': '{search_value}' exists",
|
exception=f"Table entry with matching '{search_key}': '{search_value}' exists",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
primary_key = getattr(item, db_table.primary_key)
|
||||||
|
db_table.delete(self.session, primary_key)
|
||||||
try:
|
try:
|
||||||
db_table.create(self.session, model.dict())
|
db_table.create(self.session, model.dict())
|
||||||
import_status = return_model(name=model_name, status=True)
|
import_status = return_model(name=model_name, status=True)
|
||||||
|
@ -301,12 +303,10 @@ def import_database(
|
||||||
|
|
||||||
import_session.clean_up()
|
import_session.clean_up()
|
||||||
|
|
||||||
data = {
|
return {
|
||||||
"recipeImports": recipe_report,
|
"recipeImports": recipe_report,
|
||||||
"settingsImports": settings_report,
|
"settingsImports": settings_report,
|
||||||
"themeImports": theme_report,
|
"themeImports": theme_report,
|
||||||
"groupImports": group_report,
|
"groupImports": group_report,
|
||||||
"userImports": user_report,
|
"userImports": user_report,
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
|
@ -4,26 +4,16 @@ from typing import Union
|
||||||
import pytz
|
import pytz
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import create_session
|
from mealie.db.db_setup import create_session
|
||||||
from pydantic.tools import T
|
from mealie.schema.meal import MealIn, MealOut, MealPlanIn, MealPlanInDB, MealPlanProcessed
|
||||||
from mealie.schema.meal import (
|
|
||||||
MealIn,
|
|
||||||
MealOut,
|
|
||||||
MealPlanIn,
|
|
||||||
MealPlanInDB,
|
|
||||||
MealPlanProcessed,
|
|
||||||
)
|
|
||||||
from mealie.schema.recipe import Recipe
|
from mealie.schema.recipe import Recipe
|
||||||
from mealie.schema.user import GroupInDB
|
from mealie.schema.user import GroupInDB
|
||||||
|
from pydantic.tools import T
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
|
|
||||||
def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProcessed:
|
def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProcessed:
|
||||||
meals = []
|
meals = []
|
||||||
for x, meal in enumerate(meal_plan_base.meals):
|
for x, meal in enumerate(meal_plan_base.meals):
|
||||||
# europe = pytz.timezone("America/Anchorage")
|
|
||||||
# d = europe.localize(meal_plan_base.startDate)
|
|
||||||
# print(d)
|
|
||||||
|
|
||||||
meal: MealIn
|
meal: MealIn
|
||||||
try:
|
try:
|
||||||
recipe: Recipe = db.recipes.get(session, meal.slug)
|
recipe: Recipe = db.recipes.get(session, meal.slug)
|
||||||
|
|
|
@ -2,11 +2,12 @@ import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
from fastapi.logger import logger
|
||||||
from mealie.core.config import IMG_DIR, TEMP_DIR
|
from mealie.core.config import IMG_DIR, TEMP_DIR
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.schema.recipe import Recipe
|
from mealie.schema.recipe import Recipe
|
||||||
from sqlalchemy.orm.session import Session
|
|
||||||
from mealie.utils.unzip import unpack_zip
|
from mealie.utils.unzip import unpack_zip
|
||||||
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from yaml import CLoader as Loader
|
from yaml import CLoader as Loader
|
||||||
|
@ -50,11 +51,8 @@ def read_chowdown_file(recipe_file: Path) -> Recipe:
|
||||||
"tags": recipe_data.get("tags").split(","),
|
"tags": recipe_data.get("tags").split(","),
|
||||||
}
|
}
|
||||||
|
|
||||||
print(reformat_data)
|
reformated_list = [{"text": instruction} for instruction in reformat_data["recipeInstructions"]]
|
||||||
|
|
||||||
reformated_list = []
|
|
||||||
for instruction in reformat_data["recipeInstructions"]:
|
|
||||||
reformated_list.append({"text": instruction})
|
|
||||||
reformat_data["recipeInstructions"] = reformated_list
|
reformat_data["recipeInstructions"] = reformated_list
|
||||||
|
|
||||||
return Recipe(**reformat_data)
|
return Recipe(**reformat_data)
|
||||||
|
@ -63,21 +61,12 @@ def read_chowdown_file(recipe_file: Path) -> Recipe:
|
||||||
def chowdown_migrate(session: Session, zip_file: Path):
|
def chowdown_migrate(session: Session, zip_file: Path):
|
||||||
|
|
||||||
temp_dir = unpack_zip(zip_file)
|
temp_dir = unpack_zip(zip_file)
|
||||||
print(temp_dir.name)
|
|
||||||
|
|
||||||
path = Path(temp_dir.name)
|
|
||||||
for p in path.iterdir():
|
|
||||||
print("ItterDir", p)
|
|
||||||
for p in p.iterdir():
|
|
||||||
print("Sub Itter", p)
|
|
||||||
with temp_dir as dir:
|
with temp_dir as dir:
|
||||||
chow_dir = next(Path(dir).iterdir())
|
chow_dir = next(Path(dir).iterdir())
|
||||||
image_dir = TEMP_DIR.joinpath(chow_dir, "images")
|
image_dir = TEMP_DIR.joinpath(chow_dir, "images")
|
||||||
recipe_dir = TEMP_DIR.joinpath(chow_dir, "_recipes")
|
recipe_dir = TEMP_DIR.joinpath(chow_dir, "_recipes")
|
||||||
|
|
||||||
print(image_dir.exists())
|
|
||||||
print(recipe_dir.exists())
|
|
||||||
|
|
||||||
failed_recipes = []
|
failed_recipes = []
|
||||||
successful_recipes = []
|
successful_recipes = []
|
||||||
for recipe in recipe_dir.glob("*.md"):
|
for recipe in recipe_dir.glob("*.md"):
|
||||||
|
@ -86,17 +75,18 @@ def chowdown_migrate(session: Session, zip_file: Path):
|
||||||
db.recipes.create(session, new_recipe.dict())
|
db.recipes.create(session, new_recipe.dict())
|
||||||
successful_recipes.append(new_recipe.name)
|
successful_recipes.append(new_recipe.name)
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
|
session.rollback()
|
||||||
|
logger.error(inst)
|
||||||
failed_recipes.append(recipe.stem)
|
failed_recipes.append(recipe.stem)
|
||||||
|
|
||||||
failed_images = []
|
failed_images = []
|
||||||
for image in image_dir.iterdir():
|
for image in image_dir.iterdir():
|
||||||
try:
|
try:
|
||||||
if not image.stem in failed_recipes:
|
if image.stem not in failed_recipes:
|
||||||
shutil.copy(image, IMG_DIR.joinpath(image.name))
|
shutil.copy(image, IMG_DIR.joinpath(image.name))
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
print(inst)
|
logger.error(inst)
|
||||||
failed_images.append(image.name)
|
failed_images.append(image.name)
|
||||||
|
|
||||||
report = {"successful": successful_recipes, "failed": failed_recipes}
|
report = {"successful": successful_recipes, "failed": failed_recipes}
|
||||||
|
|
||||||
return report
|
return report
|
||||||
|
|
|
@ -52,8 +52,7 @@ class Cleaner:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def html(raw_html):
|
def html(raw_html):
|
||||||
cleanr = re.compile("<.*?>")
|
cleanr = re.compile("<.*?>")
|
||||||
cleantext = re.sub(cleanr, "", raw_html)
|
return re.sub(cleanr, "", raw_html)
|
||||||
return cleantext
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def image(image=None) -> str:
|
def image(image=None) -> str:
|
||||||
|
@ -142,12 +141,11 @@ class Cleaner:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def time(time_entry):
|
def time(time_entry):
|
||||||
print(time_entry, type(time_entry))
|
if time_entry is None:
|
||||||
if time_entry == None:
|
|
||||||
return None
|
return None
|
||||||
elif type(time_entry) == datetime:
|
elif type(time_entry) == datetime:
|
||||||
print(time_entry)
|
print(time_entry)
|
||||||
elif type(time_entry) != str:
|
elif type(time_entry) != str:
|
||||||
return str(time_entry)
|
return str(time_entry)
|
||||||
elif time_entry != None:
|
else:
|
||||||
return time_entry
|
return time_entry
|
||||||
|
|
|
@ -25,13 +25,10 @@ def create_from_url(url: str) -> Recipe:
|
||||||
"""
|
"""
|
||||||
r = requests.get(url)
|
r = requests.get(url)
|
||||||
new_recipe = extract_recipe_from_html(r.text, url)
|
new_recipe = extract_recipe_from_html(r.text, url)
|
||||||
print(new_recipe)
|
|
||||||
new_recipe = Cleaner.clean(new_recipe, url)
|
new_recipe = Cleaner.clean(new_recipe, url)
|
||||||
new_recipe = download_image_for_recipe(new_recipe)
|
new_recipe = download_image_for_recipe(new_recipe)
|
||||||
|
|
||||||
recipe = Recipe(**new_recipe)
|
return Recipe(**new_recipe)
|
||||||
|
|
||||||
return recipe
|
|
||||||
|
|
||||||
|
|
||||||
def extract_recipe_from_html(html: str, url: str) -> dict:
|
def extract_recipe_from_html(html: str, url: str) -> dict:
|
||||||
|
|
|
@ -4,13 +4,9 @@ import requests
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from mealie.app import app
|
from mealie.app import app
|
||||||
from mealie.core.config import SQLITE_DIR
|
from mealie.core.config import SQLITE_DIR
|
||||||
from mealie.db.database import db
|
|
||||||
from mealie.db.db_setup import generate_session, sql_global_init
|
from mealie.db.db_setup import generate_session, sql_global_init
|
||||||
from mealie.db.init_db import init_db
|
from mealie.db.init_db import init_db
|
||||||
from mealie.routes.deps import get_current_user
|
|
||||||
from mealie.schema.user import UserInDB
|
|
||||||
from pytest import fixture
|
from pytest import fixture
|
||||||
from sqlalchemy.orm.session import Session
|
|
||||||
|
|
||||||
from tests.test_config import TEST_DATA
|
from tests.test_config import TEST_DATA
|
||||||
|
|
||||||
|
|
25
tests/test_routes/test_import_routes.py
Normal file
25
tests/test_routes/test_import_routes.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def backup_data():
|
||||||
|
return {
|
||||||
|
"name": "dev_sample_data_2021-Feb-13.zip",
|
||||||
|
"force": False,
|
||||||
|
"recipes": True,
|
||||||
|
"settings": False, #! Broken
|
||||||
|
"themes": True,
|
||||||
|
"groups": True,
|
||||||
|
"users": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_import(api_client, backup_data):
|
||||||
|
response = api_client.post("/api/backups/dev_sample_data_2021-Feb-13.zip/import", json=backup_data)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
for key, value in json.loads(response.content).items():
|
||||||
|
for v in value:
|
||||||
|
assert v["status"] == True
|
|
@ -34,6 +34,7 @@ def test_upload_chowdown_zip(api_client, chowdown_zip):
|
||||||
|
|
||||||
|
|
||||||
def test_import_chowdown_directory(api_client, chowdown_zip):
|
def test_import_chowdown_directory(api_client, chowdown_zip):
|
||||||
|
api_client.delete(f"{RECIPES_PREFIX}/roasted-okra") # TODO: Manage Test Data better
|
||||||
selection = chowdown_zip.name
|
selection = chowdown_zip.name
|
||||||
response = api_client.post(f"{MIGRATIONS_PREFIX}/chowdown/{selection}/import")
|
response = api_client.post(f"{MIGRATIONS_PREFIX}/chowdown/{selection}/import")
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ def test_create_by_url(api_client, recipe_data: RecipeTestData):
|
||||||
|
|
||||||
|
|
||||||
def test_create_by_json(api_client):
|
def test_create_by_json(api_client):
|
||||||
|
api_client.delete(f"{RECIPES_PREFIX}/banana-bread")
|
||||||
response = api_client.post(RECIPES_CREATE, json=raw_recipe)
|
response = api_client.post(RECIPES_CREATE, json=raw_recipe)
|
||||||
|
|
||||||
assert response.status_code == 201
|
assert response.status_code == 201
|
||||||
|
|
|
@ -66,7 +66,6 @@ def test_update_user(api_client: requests, token):
|
||||||
response = api_client.put(f"{BASE}/1", headers=token, json=update_data)
|
response = api_client.put(f"{BASE}/1", headers=token, json=update_data)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
print(response.text)
|
|
||||||
assert json.loads(response.text).get("access_token")
|
assert json.loads(response.text).get("access_token")
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue