mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
group admin init
This commit is contained in:
parent
19c09a1f74
commit
f5a193f3b6
11 changed files with 422 additions and 339 deletions
123
frontend/src/components/Admin/ManageUsers/GroupCard.vue
Normal file
123
frontend/src/components/Admin/ManageUsers/GroupCard.vue
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Confirmation
|
||||||
|
ref="deleteGroupConfirm"
|
||||||
|
title="Confirm Group Deletion"
|
||||||
|
:message="`Are you sure you want to delete <b>${group.name}<b/>`"
|
||||||
|
icon="mdi-alert"
|
||||||
|
@confirm="deleteGroup"
|
||||||
|
:width="450"
|
||||||
|
@close="closeGroupDelete"
|
||||||
|
/>
|
||||||
|
<v-card class="ma-auto" tile min-height="325px">
|
||||||
|
<v-list dense>
|
||||||
|
<v-card-title class="py-1">{{ group.name }}</v-card-title>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-subheader>Group ID: {{ group.id }}</v-subheader>
|
||||||
|
<v-list-item-group color="primary">
|
||||||
|
<v-list-item v-for="property in groupProps" :key="property.text">
|
||||||
|
<v-list-item-icon>
|
||||||
|
<v-icon> {{ property.icon || "mdi-account" }} </v-icon>
|
||||||
|
</v-list-item-icon>
|
||||||
|
<v-list-item-content>
|
||||||
|
<v-list-item-title class="pl-4 flex row justify-space-between">
|
||||||
|
<div>{{ property.text }}</div>
|
||||||
|
<div>{{ property.value }}</div>
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item-content>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list-item-group>
|
||||||
|
</v-list>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn
|
||||||
|
small
|
||||||
|
color="error"
|
||||||
|
@click="confirmDelete"
|
||||||
|
:disabled="ableToDelete"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</v-btn>
|
||||||
|
<!-- Coming Soon! -->
|
||||||
|
<v-btn small color="success" disabled>
|
||||||
|
Edit
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const RENDER_EVENT = "update";
|
||||||
|
import Confirmation from "@/components/UI/Confirmation";
|
||||||
|
import api from "@/api";
|
||||||
|
export default {
|
||||||
|
components: { Confirmation },
|
||||||
|
props: {
|
||||||
|
group: {
|
||||||
|
default: {
|
||||||
|
name: "DEFAULT_NAME",
|
||||||
|
id: 1,
|
||||||
|
users: [],
|
||||||
|
mealplans: [],
|
||||||
|
categories: [],
|
||||||
|
webhookUrls: [],
|
||||||
|
webhookTime: "00:00",
|
||||||
|
webhookEnable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
groupProps: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
ableToDelete() {
|
||||||
|
return this.group.users.length >= 1 ? true : false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.buildData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmDelete() {
|
||||||
|
this.$refs.deleteGroupConfirm.open();
|
||||||
|
},
|
||||||
|
async deleteGroup() {
|
||||||
|
await api.groups.delete(this.group.id);
|
||||||
|
this.$emit(RENDER_EVENT);
|
||||||
|
},
|
||||||
|
closeGroupDelete() {
|
||||||
|
console.log("Close Delete");
|
||||||
|
},
|
||||||
|
buildData() {
|
||||||
|
this.groupProps = [
|
||||||
|
{
|
||||||
|
text: "Total Users",
|
||||||
|
icon: "mdi-account",
|
||||||
|
value: this.group.users.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Total MealPlans",
|
||||||
|
icon: "mdi-food",
|
||||||
|
value: this.group.mealplans.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Webhooks Enabled",
|
||||||
|
icon: "mdi-webhook",
|
||||||
|
value: this.group.webhookEnable ? "True" : "False",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Webhook Time",
|
||||||
|
icon: "mdi-clock-outline",
|
||||||
|
value: this.group.webhookTime,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
120
frontend/src/components/Admin/ManageUsers/GroupDashboard.vue
Normal file
120
frontend/src/components/Admin/ManageUsers/GroupDashboard.vue
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-card outlined class="mt-n1">
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<div width="100px">
|
||||||
|
<v-text-field
|
||||||
|
v-model="filter"
|
||||||
|
clearable
|
||||||
|
class="mr-2 pt-0"
|
||||||
|
append-icon="mdi-filter"
|
||||||
|
label="Filter"
|
||||||
|
single-line
|
||||||
|
hide-details
|
||||||
|
></v-text-field>
|
||||||
|
</div>
|
||||||
|
<v-dialog v-model="groupDialog" max-width="400">
|
||||||
|
<template v-slot:activator="{ on, attrs }">
|
||||||
|
<v-btn
|
||||||
|
class="mx-2"
|
||||||
|
small
|
||||||
|
color="success"
|
||||||
|
dark
|
||||||
|
v-bind="attrs"
|
||||||
|
v-on="on"
|
||||||
|
>
|
||||||
|
Create Group
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<v-card>
|
||||||
|
<v-app-bar dark dense color="primary">
|
||||||
|
<v-icon left>
|
||||||
|
mdi-account-group
|
||||||
|
</v-icon>
|
||||||
|
|
||||||
|
<v-toolbar-title class="headline">
|
||||||
|
Create Group
|
||||||
|
</v-toolbar-title>
|
||||||
|
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
</v-app-bar>
|
||||||
|
|
||||||
|
<v-card-text>
|
||||||
|
<v-form ref="newGroup">
|
||||||
|
<v-text-field
|
||||||
|
v-model="newGroupName"
|
||||||
|
label="Group Name"
|
||||||
|
:rules="[existsRule]"
|
||||||
|
></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn color="grey" text @click="groupDialog = false">
|
||||||
|
Cancel
|
||||||
|
</v-btn>
|
||||||
|
<v-btn color="primary" @click="createGroup">
|
||||||
|
Create
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</v-card-actions>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col
|
||||||
|
:sm="6"
|
||||||
|
:md="6"
|
||||||
|
:lg="3"
|
||||||
|
:xl="3"
|
||||||
|
v-for="group in groups"
|
||||||
|
:key="group.id"
|
||||||
|
>
|
||||||
|
<GroupCard
|
||||||
|
:group="group"
|
||||||
|
@update="$store.dispatch('requestAllGroups')"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { validators } from "@/mixins/validators";
|
||||||
|
import api from "@/api";
|
||||||
|
import GroupCard from "@/components/Admin/ManageUsers/GroupCard";
|
||||||
|
export default {
|
||||||
|
components: { GroupCard },
|
||||||
|
mixins: [validators],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
filter: "",
|
||||||
|
groupDialog: false,
|
||||||
|
newGroupName: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
groups() {
|
||||||
|
return this.$store.getters.getGroups;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async createGroup() {
|
||||||
|
this.groupLoading = true;
|
||||||
|
let response = await api.groups.create(this.newGroupName);
|
||||||
|
if (response.created) {
|
||||||
|
this.groupLoading = false;
|
||||||
|
this.groupDialog = false;
|
||||||
|
this.$store.dispatch("requestAllGroups");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -1,256 +0,0 @@
|
||||||
<template>
|
|
||||||
<v-card outlined class="mt-n1">
|
|
||||||
<Confirmation
|
|
||||||
ref="deleteUserDialog"
|
|
||||||
title="Confirm User Deletion"
|
|
||||||
:message="
|
|
||||||
`Are you sure you want to delete the user <b>${activeName} ID: ${activeId}<b/>`
|
|
||||||
"
|
|
||||||
icon="mdi-alert"
|
|
||||||
@confirm="deleteUser"
|
|
||||||
:width="450"
|
|
||||||
@close="closeDelete"
|
|
||||||
/>
|
|
||||||
<v-toolbar flat>
|
|
||||||
<v-icon large color="accent" class="mr-1">
|
|
||||||
mdi-account-group
|
|
||||||
</v-icon>
|
|
||||||
<v-toolbar-title class="headine">
|
|
||||||
User Groups
|
|
||||||
</v-toolbar-title>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<v-select class="mt-7 ml-5" dense flat solo> </v-select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<v-spacer> </v-spacer>
|
|
||||||
<v-dialog v-model="dialog" max-width="600px">
|
|
||||||
<template v-slot:activator="{ on, attrs }">
|
|
||||||
<v-btn small color="success" dark v-bind="attrs" v-on="on">
|
|
||||||
Create Group
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
<v-card>
|
|
||||||
<v-app-bar dark dense color="primary">
|
|
||||||
<v-icon left>
|
|
||||||
mdi-account
|
|
||||||
</v-icon>
|
|
||||||
|
|
||||||
<v-toolbar-title class="headline">
|
|
||||||
{{ formTitle }}
|
|
||||||
</v-toolbar-title>
|
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-toolbar-title class="headline">
|
|
||||||
User ID: {{ editedItem.id }}
|
|
||||||
</v-toolbar-title>
|
|
||||||
</v-app-bar>
|
|
||||||
|
|
||||||
<v-card-text>
|
|
||||||
<v-form ref="newUser">
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="12" sm="12" md="6">
|
|
||||||
<v-text-field
|
|
||||||
v-model="editedItem.fullName"
|
|
||||||
label="Full Name"
|
|
||||||
:rules="[existsRule]"
|
|
||||||
validate-on-blur
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="12" md="6">
|
|
||||||
<v-text-field
|
|
||||||
v-model="editedItem.email"
|
|
||||||
label="Email"
|
|
||||||
:rules="[existsRule, emailRule]"
|
|
||||||
validate-on-blur
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="12" md="6">
|
|
||||||
<v-text-field
|
|
||||||
v-model="editedItem.group"
|
|
||||||
label="Group Group"
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="12" md="6" v-if="showPassword">
|
|
||||||
<v-text-field
|
|
||||||
v-model="editedItem.password"
|
|
||||||
label="User Password"
|
|
||||||
:rules="[existsRule, minRule]"
|
|
||||||
></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="12" md="3">
|
|
||||||
<v-switch v-model="editedItem.admin" label="Admin"></v-switch>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-form>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions>
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-btn color="grey" text @click="close">
|
|
||||||
Cancel
|
|
||||||
</v-btn>
|
|
||||||
<v-btn color="primary" @click="save">
|
|
||||||
Save
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</v-toolbar>
|
|
||||||
<v-divider></v-divider>
|
|
||||||
<v-card-text>
|
|
||||||
<v-data-table :headers="headers" :items="users" sort-by="calories">
|
|
||||||
<template v-slot:item.actions="{ item }">
|
|
||||||
<v-btn class="mr-1" small color="error" @click="deleteItem(item)">
|
|
||||||
<v-icon small left>
|
|
||||||
mdi-delete
|
|
||||||
</v-icon>
|
|
||||||
Delete
|
|
||||||
</v-btn>
|
|
||||||
<v-btn small color="success" @click="editItem(item)">
|
|
||||||
<v-icon small left class="mr-2">
|
|
||||||
mdi-pencil
|
|
||||||
</v-icon>
|
|
||||||
Edit
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
<template v-slot:item.admin="{ item }">
|
|
||||||
{{ item.admin ? "Admin" : "User" }}
|
|
||||||
</template>
|
|
||||||
<template v-slot:no-data>
|
|
||||||
<v-btn color="primary" @click="initialize">
|
|
||||||
Reset
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
</v-data-table>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Confirmation from "@/components/UI/Confirmation";
|
|
||||||
import api from "@/api";
|
|
||||||
import { validators } from "@/mixins/validators";
|
|
||||||
export default {
|
|
||||||
components: { Confirmation },
|
|
||||||
mixins: [validators],
|
|
||||||
data: () => ({
|
|
||||||
dialog: false,
|
|
||||||
activeId: null,
|
|
||||||
activeName: null,
|
|
||||||
headers: [
|
|
||||||
{
|
|
||||||
text: "User IDs",
|
|
||||||
align: "start",
|
|
||||||
sortable: false,
|
|
||||||
value: "id",
|
|
||||||
},
|
|
||||||
{ text: "Full Name", value: "fullName" },
|
|
||||||
{ text: "Email", value: "email" },
|
|
||||||
{ text: "Group", value: "group" },
|
|
||||||
{ text: "Admin", value: "admin" },
|
|
||||||
{ text: "", value: "actions", sortable: false, align: "center" },
|
|
||||||
],
|
|
||||||
users: [],
|
|
||||||
editedIndex: -1,
|
|
||||||
editedItem: {
|
|
||||||
id: 0,
|
|
||||||
fullName: "",
|
|
||||||
password: "",
|
|
||||||
email: "",
|
|
||||||
group: "",
|
|
||||||
admin: false,
|
|
||||||
},
|
|
||||||
defaultItem: {
|
|
||||||
id: 0,
|
|
||||||
fullName: "",
|
|
||||||
password: "",
|
|
||||||
email: "",
|
|
||||||
group: "",
|
|
||||||
admin: false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
formTitle() {
|
|
||||||
return this.editedIndex === -1 ? "New User" : "Edit User";
|
|
||||||
},
|
|
||||||
showPassword() {
|
|
||||||
return this.editedIndex === -1 ? true : false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
dialog(val) {
|
|
||||||
val || this.close();
|
|
||||||
},
|
|
||||||
dialogDelete(val) {
|
|
||||||
val || this.closeDelete();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.initialize();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async initialize() {
|
|
||||||
this.users = await api.users.allUsers();
|
|
||||||
},
|
|
||||||
|
|
||||||
async deleteUser() {
|
|
||||||
await api.users.delete(this.activeId);
|
|
||||||
this.initialize();
|
|
||||||
},
|
|
||||||
|
|
||||||
editItem(item) {
|
|
||||||
this.editedIndex = this.users.indexOf(item);
|
|
||||||
this.editedItem = Object.assign({}, item);
|
|
||||||
this.dialog = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteItem(item) {
|
|
||||||
this.activeId = item.id;
|
|
||||||
this.activeName = item.fullName;
|
|
||||||
this.editedIndex = this.users.indexOf(item);
|
|
||||||
this.editedItem = Object.assign({}, item);
|
|
||||||
this.$refs.deleteUserDialog.open();
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteItemConfirm() {
|
|
||||||
this.users.splice(this.editedIndex, 1);
|
|
||||||
this.closeDelete();
|
|
||||||
},
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.dialog = false;
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.editedItem = Object.assign({}, this.defaultItem);
|
|
||||||
this.editedIndex = -1;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
closeDelete() {
|
|
||||||
this.dialogDelete = false;
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.editedItem = Object.assign({}, this.defaultItem);
|
|
||||||
this.editedIndex = -1;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async save() {
|
|
||||||
if (this.editedIndex > -1) {
|
|
||||||
api.users.update(this.editedItem);
|
|
||||||
this.close();
|
|
||||||
} else if (this.$refs.newUser.validate()) {
|
|
||||||
api.users.create(this.editedItem);
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
await this.initialize();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
|
@ -68,7 +68,7 @@
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
|
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-data-table :headers="headers" :items="links" sort-by="calories">
|
<v-data-table :headers="headers" :items="links" sort-by="calories">
|
||||||
<template v-slot:item.token="{ item }">
|
<template v-slot:item.token="{ item }">
|
||||||
|
|
|
@ -12,35 +12,18 @@
|
||||||
@close="closeDelete"
|
@close="closeDelete"
|
||||||
/>
|
/>
|
||||||
<v-toolbar flat>
|
<v-toolbar flat>
|
||||||
<v-icon large color="accent" class="mr-1">
|
|
||||||
mdi-account
|
|
||||||
</v-icon>
|
|
||||||
<v-toolbar-title class="headine">
|
|
||||||
Users
|
|
||||||
</v-toolbar-title>
|
|
||||||
|
|
||||||
<v-spacer> </v-spacer>
|
<v-spacer> </v-spacer>
|
||||||
<v-text-field
|
<div width="100px">
|
||||||
v-model="search"
|
<v-text-field
|
||||||
append-icon="mdi-filter"
|
v-model="search"
|
||||||
label="Filter"
|
class="mr-2"
|
||||||
single-line
|
append-icon="mdi-filter"
|
||||||
hide-details
|
label="Filter"
|
||||||
></v-text-field>
|
single-line
|
||||||
<v-dialog>
|
hide-details
|
||||||
<template v-slot:activator="{ on, attrs }">
|
></v-text-field>
|
||||||
<v-btn
|
</div>
|
||||||
class="mx-2"
|
|
||||||
small
|
|
||||||
color="success"
|
|
||||||
dark
|
|
||||||
v-bind="attrs"
|
|
||||||
v-on="on"
|
|
||||||
>
|
|
||||||
Create Group
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
</v-dialog>
|
|
||||||
<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">
|
||||||
|
@ -83,11 +66,12 @@
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="12" md="6">
|
<v-col cols="12" sm="12" md="6">
|
||||||
<v-text-field
|
<v-select
|
||||||
dense
|
dense
|
||||||
v-model="editedItem.group"
|
v-model="editedItem.group"
|
||||||
label="Group Group"
|
:items="existingGroups"
|
||||||
></v-text-field>
|
label="User Group"
|
||||||
|
></v-select>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="12" md="6" v-if="showPassword">
|
<v-col cols="12" sm="12" md="6" v-if="showPassword">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
|
@ -203,6 +187,9 @@ export default {
|
||||||
showPassword() {
|
showPassword() {
|
||||||
return this.editedIndex === -1 ? true : false;
|
return this.editedIndex === -1 ? true : false;
|
||||||
},
|
},
|
||||||
|
existingGroups() {
|
||||||
|
return this.$store.getters.getGroupNames;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
53
frontend/src/components/UI/BasicModal.vue
Normal file
53
frontend/src/components/UI/BasicModal.vue
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-dialog v-model="dialog" :width="modalWidth + 'px'">
|
||||||
|
<v-app-bar dark :color="color" class="mt-n1 mb-2">
|
||||||
|
<v-icon large left v-if="!loading">
|
||||||
|
{{ titleIcon }}
|
||||||
|
</v-icon>
|
||||||
|
<v-progress-circular
|
||||||
|
v-else
|
||||||
|
indeterminate
|
||||||
|
color="white"
|
||||||
|
large
|
||||||
|
class="mr-2"
|
||||||
|
>
|
||||||
|
</v-progress-circular>
|
||||||
|
<v-toolbar-title class="headline"> {{ title }} </v-toolbar-title>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
</v-app-bar>
|
||||||
|
</v-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
color: {
|
||||||
|
default: "primary",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
default: "Modal Title",
|
||||||
|
},
|
||||||
|
titleIcon: {
|
||||||
|
default: "mdi-account",
|
||||||
|
},
|
||||||
|
modalWidth: {
|
||||||
|
default: "500",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialog: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
this.dialog = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
|
@ -8,10 +8,10 @@
|
||||||
@keydown.esc="cancel"
|
@keydown.esc="cancel"
|
||||||
>
|
>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-toolbar v-if="Boolean(title)" :color="color" dense flat dark>
|
<v-app-bar v-if="Boolean(title)" :color="color" dense flat dark>
|
||||||
<v-icon v-if="Boolean(icon)" left> {{ icon }}</v-icon>
|
<v-icon v-if="Boolean(icon)" left> {{ icon }}</v-icon>
|
||||||
<v-toolbar-title v-text="title" />
|
<v-toolbar-title v-text="title" />
|
||||||
</v-toolbar>
|
</v-app-bar>
|
||||||
|
|
||||||
<v-card-text
|
<v-card-text
|
||||||
v-show="!!message"
|
v-show="!!message"
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
<TheSignUpTable />
|
<TheSignUpTable />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
<v-tab-item>
|
<v-tab-item>
|
||||||
<TheGroupTable />
|
<GroupDashboard />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
</v-tabs-items>
|
</v-tabs-items>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
@ -43,15 +43,18 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TheUserTable from "@/components/Admin/ManageUsers/TheUserTable";
|
import TheUserTable from "@/components/Admin/ManageUsers/TheUserTable";
|
||||||
import TheGroupTable from "@/components/Admin/ManageUsers/TheGroupTable";
|
import GroupDashboard from "@/components/Admin/ManageUsers/GroupDashboard";
|
||||||
import TheSignUpTable from "@/components/Admin/ManageUsers/TheSignUpTable";
|
import TheSignUpTable from "@/components/Admin/ManageUsers/TheSignUpTable";
|
||||||
export default {
|
export default {
|
||||||
components: { TheUserTable, TheGroupTable, TheSignUpTable },
|
components: { TheUserTable, GroupDashboard, TheSignUpTable },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tab: 0,
|
tab: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch("requestAllGroups");
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,17 @@
|
||||||
outlined
|
outlined
|
||||||
:flat="isFlat"
|
:flat="isFlat"
|
||||||
elavation="0"
|
elavation="0"
|
||||||
v-model="planCategories"
|
v-model="groupSettings.categories"
|
||||||
:items="categories"
|
:items="categories"
|
||||||
item-text="name"
|
item-text="name"
|
||||||
item-value="name"
|
return-object
|
||||||
multiple
|
multiple
|
||||||
chips
|
chips
|
||||||
:hint="$t('meal-plan.only-recipes-with-these-categories-will-be-used-in-meal-plans')"
|
:hint="
|
||||||
|
$t(
|
||||||
|
'meal-plan.only-recipes-with-these-categories-will-be-used-in-meal-plans'
|
||||||
|
)
|
||||||
|
"
|
||||||
class="mt-2"
|
class="mt-2"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
>
|
>
|
||||||
|
@ -50,12 +54,15 @@
|
||||||
"settings.webhooks.the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at"
|
"settings.webhooks.the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at"
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
<strong>{{ time }}</strong>
|
<strong>{{ groupSettings.webhookTime }}</strong>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<v-row dense align="center">
|
<v-row dense align="center">
|
||||||
<v-col cols="12" md="2" sm="5">
|
<v-col cols="12" md="2" sm="5">
|
||||||
<v-switch v-model="enabled" :label="$t('general.enabled')"></v-switch>
|
<v-switch
|
||||||
|
v-model="groupSettings.webhookEnable"
|
||||||
|
:label="$t('general.enabled')"
|
||||||
|
></v-switch>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" md="3" sm="5">
|
<v-col cols="12" md="3" sm="5">
|
||||||
<TimePickerDialog @save-time="saveTime" />
|
<TimePickerDialog @save-time="saveTime" />
|
||||||
|
@ -68,7 +75,12 @@
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<v-row v-for="(url, index) in webhooks" :key="index" align="center" dense>
|
<v-row
|
||||||
|
v-for="(url, index) in groupSettings.webhookUrls"
|
||||||
|
:key="index"
|
||||||
|
align="center"
|
||||||
|
dense
|
||||||
|
>
|
||||||
<v-col cols="1">
|
<v-col cols="1">
|
||||||
<v-btn icon color="error" @click="removeWebhook(index)">
|
<v-btn icon color="error" @click="removeWebhook(index)">
|
||||||
<v-icon>mdi-minus</v-icon>
|
<v-icon>mdi-minus</v-icon>
|
||||||
|
@ -76,7 +88,7 @@
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col>
|
<v-col>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="webhooks[index]"
|
v-model="groupSettings.webhookUrls[index]"
|
||||||
:label="$t('settings.webhooks.webhook-url')"
|
:label="$t('settings.webhooks.webhook-url')"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
@ -87,7 +99,7 @@
|
||||||
<v-icon>mdi-plus</v-icon>
|
<v-icon>mdi-plus</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn color="success" @click="saveWebhooks" class="mr-2 mb-1">
|
<v-btn color="success" @click="saveGroupSettings" class="mr-2 mb-1">
|
||||||
<v-icon left> mdi-content-save </v-icon>
|
<v-icon left> mdi-content-save </v-icon>
|
||||||
{{ $t("general.save") }}
|
{{ $t("general.save") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
@ -104,14 +116,19 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
name: "main",
|
groupSettings: {
|
||||||
webhooks: [],
|
name: "home",
|
||||||
enabled: false,
|
id: 1,
|
||||||
time: "",
|
mealplans: [],
|
||||||
planCategories: [],
|
categories: [],
|
||||||
|
webhookUrls: [],
|
||||||
|
webhookTime: "00:00",
|
||||||
|
webhookEnable: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
async mounted() {
|
||||||
|
await this.$store.dispatch("requestCurrentGroup");
|
||||||
this.getSiteSettings();
|
this.getSiteSettings();
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -119,44 +136,39 @@ export default {
|
||||||
return this.$store.getters.getCategories;
|
return this.$store.getters.getCategories;
|
||||||
},
|
},
|
||||||
isFlat() {
|
isFlat() {
|
||||||
return this.planCategories ? true : false;
|
return this.groupSettings.categories >= 1 ? true : false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
saveTime(value) {
|
saveTime(value) {
|
||||||
this.time = value;
|
this.groupSettings.webhookTime = value;
|
||||||
},
|
},
|
||||||
async getSiteSettings() {
|
getSiteSettings() {
|
||||||
let settings = await api.settings.requestAll();
|
let settings = this.$store.getters.getCurrentGroup;
|
||||||
this.webhooks = settings.webhooks.webhookURLs;
|
|
||||||
this.name = settings.name;
|
this.groupSettings.name = settings.name;
|
||||||
this.time = settings.webhooks.webhookTime;
|
this.groupSettings.id = settings.id;
|
||||||
this.enabled = settings.webhooks.enabled;
|
this.groupSettings.categories = settings.categories;
|
||||||
this.planCategories = settings.planCategories;
|
this.groupSettings.webhookUrls = settings.webhookUrls;
|
||||||
|
this.groupSettings.webhookTime = settings.webhookTime;
|
||||||
|
this.groupSettings.webhookEnable = settings.webhookEnable;
|
||||||
},
|
},
|
||||||
addWebhook() {
|
addWebhook() {
|
||||||
this.webhooks.push(" ");
|
this.groupSettings.webhookUrls.push(" ");
|
||||||
},
|
},
|
||||||
removeWebhook(index) {
|
removeWebhook(index) {
|
||||||
this.webhooks.splice(index, 1);
|
this.groupSettings.webhookUrls.splice(index, 1);
|
||||||
},
|
},
|
||||||
saveWebhooks() {
|
async saveGroupSettings() {
|
||||||
const body = {
|
await api.groups.update(this.groupSettings);
|
||||||
name: this.name,
|
await this.$store.dispatch("requestCurrentGroup");
|
||||||
planCategories: this.planCategories,
|
this.getSiteSettings();
|
||||||
webhooks: {
|
|
||||||
webhookURLs: this.webhooks,
|
|
||||||
webhookTime: this.time,
|
|
||||||
enabled: this.enabled,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
api.settings.update(body);
|
|
||||||
},
|
},
|
||||||
testWebhooks() {
|
testWebhooks() {
|
||||||
api.settings.testWebhooks();
|
api.settings.testWebhooks();
|
||||||
},
|
},
|
||||||
removeCategory(index) {
|
removeCategory(index) {
|
||||||
this.planCategories.splice(index, 1);
|
this.groupSettings.categories.splice(index, 1);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<v-container>
|
<div>
|
||||||
<AdminSidebar />
|
<v-container height="100%">
|
||||||
<v-slide-x-transition hide-on-leave>
|
<AdminSidebar />
|
||||||
<router-view></router-view>
|
<v-slide-x-transition hide-on-leave>
|
||||||
</v-slide-x-transition>
|
<router-view></router-view>
|
||||||
<!-- <v-footer fixed>
|
</v-slide-x-transition>
|
||||||
<v-col class="text-center" cols="12">
|
</v-container>
|
||||||
|
<!-- <v-footer absolute>
|
||||||
|
<div class="flex text-center" cols="12">
|
||||||
{{ $t("settings.current") }}
|
{{ $t("settings.current") }}
|
||||||
{{ version }} |
|
{{ version }} |
|
||||||
{{ $t("settings.latest") }}
|
{{ $t("settings.latest") }}
|
||||||
|
@ -21,9 +23,9 @@
|
||||||
>
|
>
|
||||||
{{ $t("settings.contribute") }}
|
{{ $t("settings.contribute") }}
|
||||||
</a>
|
</a>
|
||||||
</v-col>
|
</div>
|
||||||
</v-footer> -->
|
</v-footer> -->
|
||||||
</v-container>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
39
frontend/src/store/modules/groups.js
Normal file
39
frontend/src/store/modules/groups.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import api from "@/api";
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
groups: [],
|
||||||
|
currentGroup: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
setGroups(state, payload) {
|
||||||
|
state.groups = payload;
|
||||||
|
},
|
||||||
|
setCurrentGroup(state, payload) {
|
||||||
|
state.currentGroup = payload;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
async requestAllGroups({ commit }) {
|
||||||
|
const groups = await api.groups.allGroups();
|
||||||
|
commit("setGroups", groups);
|
||||||
|
},
|
||||||
|
async requestCurrentGroup({ commit }) {
|
||||||
|
const group = await api.groups.current();
|
||||||
|
commit("setCurrentGroup", group);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
getGroups: state => state.groups,
|
||||||
|
getGroupNames: state => Array.from(state.groups, x => x.name),
|
||||||
|
getCurrentGroup: state => state.currentGroup
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
state,
|
||||||
|
mutations,
|
||||||
|
actions,
|
||||||
|
getters,
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue