mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-11-03 18:53:17 -05:00 
			
		
		
		
	Feature/event notifications (#399)
* additional server events * sort 'recent recipes' by updated * remove duplicate code * fixes #396 * set color * consolidate tag/category pages * set colors * list unorganized recipes * cleanup old code * remove flash message, switch to global snackbar * cancel to close * cleanup * notifications first pass * test notification * complete notification feature * use background tasks * add url param * update documentation Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
import { baseURL } from "./api-utils";
 | 
			
		||||
import { apiReq } from "./api-utils";
 | 
			
		||||
import i18n from "@/i18n.js";
 | 
			
		||||
 | 
			
		||||
const prefix = baseURL + "about";
 | 
			
		||||
 | 
			
		||||
@@ -12,6 +13,10 @@ const aboutURLs = {
 | 
			
		||||
  statistics: `${prefix}/statistics`,
 | 
			
		||||
  events: `${prefix}/events`,
 | 
			
		||||
  event: id => `${prefix}/events/${id}`,
 | 
			
		||||
 | 
			
		||||
  allNotifications: `${prefix}/events/notifications`,
 | 
			
		||||
  testNotifications: `${prefix}/events/notifications/test`,
 | 
			
		||||
  notification: id => `${prefix}/events/notifications/${id}`,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const aboutAPI = {
 | 
			
		||||
@@ -27,6 +32,39 @@ export const aboutAPI = {
 | 
			
		||||
    const resposne = await apiReq.delete(aboutURLs.events);
 | 
			
		||||
    return resposne.data;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  async allEventNotifications() {
 | 
			
		||||
    const response = await apiReq.get(aboutURLs.allNotifications);
 | 
			
		||||
    return response.data;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  async createNotification(data) {
 | 
			
		||||
    const response = await apiReq.post(aboutURLs.allNotifications, data);
 | 
			
		||||
    return response.data;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  async deleteNotification(id) {
 | 
			
		||||
    const response = await apiReq.delete(aboutURLs.notification(id));
 | 
			
		||||
    return response.data;
 | 
			
		||||
  },
 | 
			
		||||
  async testNotificationByID(id) {
 | 
			
		||||
    const response = await apiReq.post(
 | 
			
		||||
      aboutURLs.testNotifications,
 | 
			
		||||
      { id: id },
 | 
			
		||||
      () => i18n.t("events.something-went-wrong"),
 | 
			
		||||
      () => i18n.t("events.test-message-sent")
 | 
			
		||||
    );
 | 
			
		||||
    return response.data;
 | 
			
		||||
  },
 | 
			
		||||
  async testNotificationByURL(url) {
 | 
			
		||||
    const response = await apiReq.post(
 | 
			
		||||
      aboutURLs.testNotifications,
 | 
			
		||||
      { test_url: url },
 | 
			
		||||
      () => i18n.t("events.something-went-wrong"),
 | 
			
		||||
      () => i18n.t("events.test-message-sent")
 | 
			
		||||
    );
 | 
			
		||||
    return response.data;
 | 
			
		||||
  },
 | 
			
		||||
  //   async getAppInfo() {
 | 
			
		||||
  //     const response = await apiReq.get(aboutURLs.version);
 | 
			
		||||
  //     return response.data;
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ export const userAPI = {
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  delete(id) {
 | 
			
		||||
    return apiReq.delete(usersURLs.userID(id), null, deleteErrorText, function() {
 | 
			
		||||
    return apiReq.delete(usersURLs.userID(id), null, deleteErrorText, () => {
 | 
			
		||||
      return i18n.t("user.user-deleted");
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
  <div>
 | 
			
		||||
    <slot name="open" v-bind="{ open }"> </slot>
 | 
			
		||||
    <v-dialog v-model="dialog" :width="modalWidth + 'px'" :content-class="top ? 'top-dialog' : undefined">
 | 
			
		||||
      <v-card class="pb-10" height="100%">
 | 
			
		||||
      <v-card height="100%">
 | 
			
		||||
        <v-app-bar dark :color="color" class="mt-n1 mb-0">
 | 
			
		||||
          <v-icon large left>
 | 
			
		||||
            {{ titleIcon }}
 | 
			
		||||
@@ -11,7 +11,9 @@
 | 
			
		||||
          <v-spacer></v-spacer>
 | 
			
		||||
        </v-app-bar>
 | 
			
		||||
        <v-progress-linear class="mt-1" v-if="loading" indeterminate color="primary"></v-progress-linear>
 | 
			
		||||
        <slot> </slot>
 | 
			
		||||
 | 
			
		||||
        <slot v-bind="{ submitEvent }"> </slot>
 | 
			
		||||
 | 
			
		||||
        <v-card-actions>
 | 
			
		||||
          <slot name="card-actions">
 | 
			
		||||
            <v-btn text color="grey" @click="dialog = false">
 | 
			
		||||
@@ -22,13 +24,15 @@
 | 
			
		||||
            <v-btn color="error" text @click="deleteEvent" v-if="$listeners.delete">
 | 
			
		||||
              {{ $t("general.delete") }}
 | 
			
		||||
            </v-btn>
 | 
			
		||||
            <v-btn color="success" @click="submitEvent">
 | 
			
		||||
            <v-btn color="success" type="submit" @click="submitEvent">
 | 
			
		||||
              {{ submitText }}
 | 
			
		||||
            </v-btn>
 | 
			
		||||
          </slot>
 | 
			
		||||
        </v-card-actions>
 | 
			
		||||
 | 
			
		||||
        <slot name="below-actions"> </slot>
 | 
			
		||||
        <div class="pb-4" v-if="$slots['below-actions']">
 | 
			
		||||
          <slot name="below-actions"> </slot>
 | 
			
		||||
        </div>
 | 
			
		||||
      </v-card>
 | 
			
		||||
    </v-dialog>
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -59,6 +63,9 @@ export default {
 | 
			
		||||
    submitText: {
 | 
			
		||||
      default: () => i18n.t("general.create"),
 | 
			
		||||
    },
 | 
			
		||||
    keepOpen: {
 | 
			
		||||
      default: false,
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
@@ -68,7 +75,7 @@ export default {
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    determineClose() {
 | 
			
		||||
      return this.submitted && !this.loading;
 | 
			
		||||
      return this.submitted && !this.loading && !this.keepOpen;
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
@@ -82,6 +89,7 @@ export default {
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    submitEvent() {
 | 
			
		||||
      console.log("Submit");
 | 
			
		||||
      this.$emit("submit");
 | 
			
		||||
      this.submitted = true;
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,10 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="text-center ma-2">
 | 
			
		||||
    <v-snackbar v-model="snackbar.open" top :color="snackbar.color" timeout="3500">
 | 
			
		||||
      <v-icon dark left>
 | 
			
		||||
        {{ icon }}
 | 
			
		||||
      </v-icon>
 | 
			
		||||
 | 
			
		||||
      {{ snackbar.title }}
 | 
			
		||||
      {{ snackbar.text }}
 | 
			
		||||
 | 
			
		||||
@@ -25,6 +29,18 @@ export default {
 | 
			
		||||
        return this.$store.getters.getSnackbar;
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    icon() {
 | 
			
		||||
      switch (this.snackbar.color) {
 | 
			
		||||
        case "error":
 | 
			
		||||
          return "mdi-alert";
 | 
			
		||||
        case "success":
 | 
			
		||||
          return "mdi-checkbox-marked-circle";
 | 
			
		||||
        case "info":
 | 
			
		||||
          return "mdi-information";
 | 
			
		||||
        default:
 | 
			
		||||
          return "mdi-bell-alert";
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
@@ -31,6 +31,16 @@
 | 
			
		||||
    "category-updated": "Category updated",
 | 
			
		||||
    "uncategorized-count": "Uncategorized {count}"
 | 
			
		||||
  },
 | 
			
		||||
  "events": {
 | 
			
		||||
    "notification": "Notification",
 | 
			
		||||
    "apprise-url": "Apprise URL",
 | 
			
		||||
    "subscribed-events": "Subscribed Events",
 | 
			
		||||
    "scheduled": "Scheduled",
 | 
			
		||||
    "database": "Database",
 | 
			
		||||
    "test-message-sent": "Test Message Sent",
 | 
			
		||||
    "something-went-wrong": "Something Went Wrong!",
 | 
			
		||||
    "new-notification-form-description": "Mealie uses the Apprise library to generate notifications. They offer many options for services to use for notifications. Refer to their wiki for a comprehensive guide on how to create the URL for your service. If available, selecting the type of your notification may include extra features."
 | 
			
		||||
  },
 | 
			
		||||
  "general": {
 | 
			
		||||
    "apply": "Apply",
 | 
			
		||||
    "cancel": "Cancel",
 | 
			
		||||
@@ -55,6 +65,7 @@
 | 
			
		||||
    "file-uploaded": "File uploaded",
 | 
			
		||||
    "filter": "Filter",
 | 
			
		||||
    "friday": "Friday",
 | 
			
		||||
    "general": "General",
 | 
			
		||||
    "get": "Get",
 | 
			
		||||
    "image": "Image",
 | 
			
		||||
    "image-upload-failed": "Image upload failed",
 | 
			
		||||
@@ -70,6 +81,7 @@
 | 
			
		||||
    "random": "Random",
 | 
			
		||||
    "rating": "Rating",
 | 
			
		||||
    "recent": "Recent",
 | 
			
		||||
    "recipe": "Recipe",
 | 
			
		||||
    "recipes": "Recipes",
 | 
			
		||||
    "rename-object": "Rename {0}",
 | 
			
		||||
    "reset": "Reset",
 | 
			
		||||
@@ -88,6 +100,8 @@
 | 
			
		||||
    "thursday": "Thursday",
 | 
			
		||||
    "token": "Token",
 | 
			
		||||
    "tuesday": "Tuesday",
 | 
			
		||||
    "type": "Type",
 | 
			
		||||
    "test": "Test",
 | 
			
		||||
    "update": "Update",
 | 
			
		||||
    "updated": "Updated",
 | 
			
		||||
    "upload": "Upload",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <base-dialog
 | 
			
		||||
    <BaseDialog
 | 
			
		||||
      ref="assignDialog"
 | 
			
		||||
      title-icon="mdi-tag"
 | 
			
		||||
      color="primary"
 | 
			
		||||
@@ -33,7 +33,7 @@
 | 
			
		||||
          :single-column="true"
 | 
			
		||||
        />
 | 
			
		||||
      </template>
 | 
			
		||||
    </base-dialog>
 | 
			
		||||
    </BaseDialog>
 | 
			
		||||
 | 
			
		||||
    <v-btn @click="openDialog" small color="success">
 | 
			
		||||
      {{ $t("settings.toolbox.bulk-assign") }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										236
									
								
								frontend/src/pages/Admin/ToolBox/EventNotification.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								frontend/src/pages/Admin/ToolBox/EventNotification.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,236 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <v-card outlined class="mt-n1">
 | 
			
		||||
      <v-card-actions>
 | 
			
		||||
        <v-spacer></v-spacer>
 | 
			
		||||
        <BaseDialog
 | 
			
		||||
          :keep-open="keepDialogOpen"
 | 
			
		||||
          title-icon="mdi-bell-alert"
 | 
			
		||||
          :title="$t('general.new') + ' ' + $t('events.notification')"
 | 
			
		||||
          @submit="createNotification"
 | 
			
		||||
        >
 | 
			
		||||
          <template v-slot:open="{ open }">
 | 
			
		||||
            <v-btn small color="info" @click="open">
 | 
			
		||||
              <v-icon left>
 | 
			
		||||
                mdi-plus
 | 
			
		||||
              </v-icon>
 | 
			
		||||
              {{ $t("events.notification") }}
 | 
			
		||||
            </v-btn>
 | 
			
		||||
          </template>
 | 
			
		||||
          <template v-slot:default>
 | 
			
		||||
            <v-card-text class="mt-2">
 | 
			
		||||
              {{ $t("events.new-notification-form-description") }}
 | 
			
		||||
 | 
			
		||||
              <div class="d-flex justify-space-around mt-1 mb-3">
 | 
			
		||||
                <a href="https://github.com/caronc/apprise/wiki" target="_blanks"> Apprise </a>
 | 
			
		||||
                <a href="https://github.com/caronc/apprise/wiki/Notify_gotify" target="_blanks"> Gotify </a>
 | 
			
		||||
                <a href="https://github.com/caronc/apprise/wiki/Notify_discord" target="_blanks"> Discord </a>
 | 
			
		||||
                <a href="https://github.com/caronc/apprise/wiki/Notify_homeassistant" target="_blanks">
 | 
			
		||||
                  Home Assistant
 | 
			
		||||
                </a>
 | 
			
		||||
                <a href="https://github.com/caronc/apprise/wiki/Notify_matrix" target="_blanks"> Matrix </a>
 | 
			
		||||
                <a href="https://github.com/caronc/apprise/wiki/Notify_pushover" target="_blanks"> Pushover </a>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <v-form ref="notificationForm">
 | 
			
		||||
                <v-select
 | 
			
		||||
                  :label="$t('general.type')"
 | 
			
		||||
                  :rules="[existsRule]"
 | 
			
		||||
                  :items="notificationTypes"
 | 
			
		||||
                  item-value="text"
 | 
			
		||||
                  v-model="newNotification.type"
 | 
			
		||||
                >
 | 
			
		||||
                </v-select>
 | 
			
		||||
                <v-text-field :rules="[existsRule]" :label="$t('general.name')" v-model="newNotification.name">
 | 
			
		||||
                </v-text-field>
 | 
			
		||||
                <v-text-field
 | 
			
		||||
                  required
 | 
			
		||||
                  :rules="[existsRule]"
 | 
			
		||||
                  :label="$t('events.apprise-url')"
 | 
			
		||||
                  v-model="newNotification.notificationUrl"
 | 
			
		||||
                >
 | 
			
		||||
                </v-text-field>
 | 
			
		||||
                <v-btn class="d-flex ml-auto" small color="info" @click="testByURL(newNotification.notificationUrl)">
 | 
			
		||||
                  <v-icon left> mdi-test-tube</v-icon>
 | 
			
		||||
                  {{ $t("general.test") }}
 | 
			
		||||
                </v-btn>
 | 
			
		||||
                <v-subheader class="pa-0 mb-0">
 | 
			
		||||
                  {{ $t("events.subscribed-events") }}
 | 
			
		||||
                </v-subheader>
 | 
			
		||||
                <v-row class="mt-1">
 | 
			
		||||
                  <v-col cols="3" v-for="(item, key, index) in newNotificationOptions" :key="index">
 | 
			
		||||
                    <v-checkbox class="my-n3 py-0" v-model="newNotificationOptions[key]" :label="key"> </v-checkbox>
 | 
			
		||||
                  </v-col>
 | 
			
		||||
                </v-row>
 | 
			
		||||
              </v-form>
 | 
			
		||||
            </v-card-text>
 | 
			
		||||
          </template>
 | 
			
		||||
        </BaseDialog>
 | 
			
		||||
      </v-card-actions>
 | 
			
		||||
      <v-simple-table>
 | 
			
		||||
        <template v-slot:default>
 | 
			
		||||
          <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <th class="text-center">
 | 
			
		||||
                {{ $t("general.type") }}
 | 
			
		||||
              </th>
 | 
			
		||||
              <th class="text-center">
 | 
			
		||||
                {{ $t("general.name") }}
 | 
			
		||||
              </th>
 | 
			
		||||
              <th class="text-center">
 | 
			
		||||
                {{ $t("general.general") }}
 | 
			
		||||
              </th>
 | 
			
		||||
              <th class="text-center">
 | 
			
		||||
                {{ $t("general.recipe") }}
 | 
			
		||||
              </th>
 | 
			
		||||
              <th class="text-center">
 | 
			
		||||
                {{ $t("events.database") }}
 | 
			
		||||
              </th>
 | 
			
		||||
              <th class="text-center">
 | 
			
		||||
                {{ $t("events.scheduled") }}
 | 
			
		||||
              </th>
 | 
			
		||||
              <th class="text-center">
 | 
			
		||||
                {{ $t("settings.migrations") }}
 | 
			
		||||
              </th>
 | 
			
		||||
              <th class="text-center">
 | 
			
		||||
                {{ $t("group.group") }}
 | 
			
		||||
              </th>
 | 
			
		||||
              <th class="text-center">
 | 
			
		||||
                {{ $t("user.user") }}
 | 
			
		||||
              </th>
 | 
			
		||||
            </tr>
 | 
			
		||||
          </thead>
 | 
			
		||||
          <tbody>
 | 
			
		||||
            <tr v-for="(item, index) in notifications" :key="index">
 | 
			
		||||
              <td>
 | 
			
		||||
                <v-avatar size="35" class="ma-1" :color="getIcon(item.type).icon ? 'primary' : undefined">
 | 
			
		||||
                  <v-icon dark v-if="getIcon(item.type).icon"> {{ getIcon(item.type).icon }}</v-icon>
 | 
			
		||||
                  <v-img v-else :src="getIcon(item.type).image"> </v-img>
 | 
			
		||||
                </v-avatar>
 | 
			
		||||
                {{ item.type }}
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>
 | 
			
		||||
                {{ item.name }}
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-center">
 | 
			
		||||
                <v-icon color="success"> {{ item.general ? "mdi-check" : "" }} </v-icon>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-center">
 | 
			
		||||
                <v-icon color="success"> {{ item.recipe ? "mdi-check" : "" }} </v-icon>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-center">
 | 
			
		||||
                <v-icon color="success"> {{ item.backup ? "mdi-check" : "" }} </v-icon>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-center">
 | 
			
		||||
                <v-icon color="success"> {{ item.scheduled ? "mdi-check" : "" }} </v-icon>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-center">
 | 
			
		||||
                <v-icon color="success"> {{ item.migration ? "mdi-check" : "" }} </v-icon>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-center">
 | 
			
		||||
                <v-icon color="success"> {{ item.group ? "mdi-check" : "" }} </v-icon>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-center">
 | 
			
		||||
                <v-icon color="success"> {{ item.user ? "mdi-check" : "" }} </v-icon>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>
 | 
			
		||||
                <v-btn class="mx-1" small color="error" @click="deleteNotification(item.id)">
 | 
			
		||||
                  <v-icon> mdi-delete </v-icon>
 | 
			
		||||
                  {{ $t("general.delete") }}
 | 
			
		||||
                </v-btn>
 | 
			
		||||
                <v-btn small color="info" @click="testByID(item.id)">
 | 
			
		||||
                  <v-icon left> mdi-test-tube</v-icon>
 | 
			
		||||
                  {{ $t("general.test") }}
 | 
			
		||||
                </v-btn>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </template>
 | 
			
		||||
      </v-simple-table>
 | 
			
		||||
    </v-card>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import BaseDialog from "@/components/UI/Dialogs/BaseDialog";
 | 
			
		||||
import { api } from "@/api";
 | 
			
		||||
import { validators } from "@/mixins/validators";
 | 
			
		||||
export default {
 | 
			
		||||
  components: {
 | 
			
		||||
    BaseDialog,
 | 
			
		||||
  },
 | 
			
		||||
  mixins: [validators],
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      keepDialogOpen: false,
 | 
			
		||||
      notifications: [],
 | 
			
		||||
      newNotification: {
 | 
			
		||||
        type: "General",
 | 
			
		||||
        name: "",
 | 
			
		||||
        notificationUrl: "",
 | 
			
		||||
      },
 | 
			
		||||
      newNotificationOptions: {
 | 
			
		||||
        general: true,
 | 
			
		||||
        recipe: true,
 | 
			
		||||
        backup: true,
 | 
			
		||||
        scheduled: true,
 | 
			
		||||
        migration: true,
 | 
			
		||||
        group: true,
 | 
			
		||||
        user: true,
 | 
			
		||||
      },
 | 
			
		||||
      notificationTypes: [
 | 
			
		||||
        {
 | 
			
		||||
          text: "General",
 | 
			
		||||
          icon: "mdi-bell-alert",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          text: "Discord",
 | 
			
		||||
          image: "./static/discord.svg",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          text: "Gotify",
 | 
			
		||||
          image: "./static/gotify.png",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          text: "Home Assistant",
 | 
			
		||||
          image: "./static/home-assistant.png",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          text: "Pushover",
 | 
			
		||||
          image: "./static/pushover.svg",
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.getAllNotifications();
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    getIcon(textValue) {
 | 
			
		||||
      return this.notificationTypes.find(x => x.text === textValue);
 | 
			
		||||
    },
 | 
			
		||||
    async getAllNotifications() {
 | 
			
		||||
      this.notifications = await api.about.allEventNotifications();
 | 
			
		||||
    },
 | 
			
		||||
    async createNotification() {
 | 
			
		||||
      if (this.$refs.notificationForm.validate()) {
 | 
			
		||||
        this.keepDialogOpen = false;
 | 
			
		||||
        await api.about.createNotification({ ...this.newNotification, ...this.newNotificationOptions });
 | 
			
		||||
        this.getAllNotifications();
 | 
			
		||||
      } else {
 | 
			
		||||
        this.keepDialogOpen = true;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    async deleteNotification(id) {
 | 
			
		||||
      await api.about.deleteNotification(id);
 | 
			
		||||
      this.getAllNotifications();
 | 
			
		||||
    },
 | 
			
		||||
    async testByID(id) {
 | 
			
		||||
      await api.about.testNotificationByID(id);
 | 
			
		||||
    },
 | 
			
		||||
    async testByURL(url) {
 | 
			
		||||
      await api.about.testNotificationByURL(url);
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
@@ -4,6 +4,10 @@
 | 
			
		||||
      <v-tabs v-model="tab" background-color="primary" centered dark icons-and-text>
 | 
			
		||||
        <v-tabs-slider></v-tabs-slider>
 | 
			
		||||
 | 
			
		||||
        <v-tab href="#event-notifications">
 | 
			
		||||
          Notify
 | 
			
		||||
          <v-icon>mdi-bell-alert</v-icon>
 | 
			
		||||
        </v-tab>
 | 
			
		||||
        <v-tab href="#category-editor">
 | 
			
		||||
          {{ $t("recipe.categories") }}
 | 
			
		||||
          <v-icon>mdi-tag-multiple-outline</v-icon>
 | 
			
		||||
@@ -20,20 +24,23 @@
 | 
			
		||||
      </v-tabs>
 | 
			
		||||
 | 
			
		||||
      <v-tabs-items v-model="tab">
 | 
			
		||||
        <v-tab-item value="event-notifications"> <EventNotification /></v-tab-item>
 | 
			
		||||
        <v-tab-item value="category-editor"> <CategoryTagEditor :is-tags="false"/></v-tab-item>
 | 
			
		||||
        <v-tab-item value="tag-editor"> <CategoryTagEditor :is-tags="true" /> </v-tab-item>
 | 
			
		||||
        <v-tab-item value="organize"> <RecipeOrganizer :is-tags="true" /> </v-tab-item>
 | 
			
		||||
        <v-tab-item value="organize"> <RecipeOrganizer /> </v-tab-item>
 | 
			
		||||
      </v-tabs-items>
 | 
			
		||||
    </v-card>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import EventNotification from "./EventNotification";
 | 
			
		||||
import CategoryTagEditor from "./CategoryTagEditor";
 | 
			
		||||
import RecipeOrganizer from "./RecipeOrganizer";
 | 
			
		||||
export default {
 | 
			
		||||
  components: {
 | 
			
		||||
    CategoryTagEditor,
 | 
			
		||||
    EventNotification,
 | 
			
		||||
    RecipeOrganizer,
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user