Просмотр исходного кода

[frontend] Update ModalUpdateThing component

Dima 1 год назад
Родитель
Сommit
71dec267e4

+ 24 - 29
web/src/assets/modal.css

@@ -1,43 +1,23 @@
-#add-thing-modal .btn.add {
+#modal-add-thing .btn.add, #modal-add-image .btn.add, #modal-tags .btn.add {
     color: green;
     padding-top: 2px;
 }
 
-#add-thing-modal .btn.delete {
+#modal-add-thing .btn.delete, #modal-add-image .btn.delete, #modal-tags .btn.delete {
     color: #cf0000;
     padding-top: 2px;
 }
 
-#add-image-modal .btn.add {
-    color: green;
-    padding-top: 2px;
-}
-
-#add-image-modal .btn.delete {
-    color: #cf0000;
-    padding-top: 2px;
-}
-
-.search-results {
+.search-results, .notification-results {
     font-size: 14px;
 }
 
-#tags-modal .btn.add {
-    color: green;
-    padding-top: 2px;
-}
-
-#tags-modal .btn.delete {
-    color: #cf0000;
-    padding-top: 2px;
-}
-
-#tags-modal .btn.edit {
+#modal-tags .btn.edit {
     color: #003bcf;
     padding-top: 2px;
 }
 
-#tags-modal .btn:focus {
+#modal-tags .btn:focus {
     outline: 0;
     -webkit-box-shadow: none;
     -o-box-shadow: none;
@@ -45,19 +25,34 @@
     box-shadow: none;
 }
 
+#modal-expired-notifications .btn.edit {
+    color: #003bcf;
+    padding: 0;
+    margin: 0;
+}
+
+#modal-expired-notifications .btn.delete {
+    color: #cf0000;
+    padding: 0;
+    margin: 0 0 0 10px;
+}
+
 @media (max-width: 767px) {
-    #search-thing-modal button.search {
+    #modal-search-thing button.search {
         margin-top: 15px;
     }
-    #add-image-modal .btn.add, #add-image-modal .btn.delete {
+
+    #modal-add-image .btn.add, #modal-add-image .btn.delete {
         padding-top: 1px;
         padding-left: 0;
     }
-    #add-thing-modal .btn.add, #add-thing-modal .btn.delete {
+
+    #modal-add-thing .btn.add, #modal-add-thing .btn.delete {
         padding-top: 1px;
         padding-left: 0;
     }
-    #tags-modal .btn.edit, #tags-modal .btn.delete {
+
+    #modal-tags .btn.edit, #modal-tags .btn.delete {
         padding-top: 1px;
         padding-left: 0;
     }

+ 7 - 0
web/src/client/client.js

@@ -48,6 +48,13 @@ export const routeDeleteTag = "/api/v1/tags/{tagId}"
 export const routeAddThingTag = "/api/v1/tags/{tagId}/thing/{thingId}"
 export const routeDeleteThingTag = "/api/v1/tags/{tagId}/thing/{thingId}"
 
+// Notifications
+export const routeAddNotification = "/api/v1/things/notifications"
+export const routeGetExpiredNotifications = "/api/v1/things/notifications/expired"
+export const routeGetNotification = "/api/v1/things/notifications/{thingId}"
+export const routeUpdateNotification = "/api/v1/things/notifications/{thingId}"
+export const routeDeleteNotification = "/api/v1/things/notifications/{thingId}"
+
 export function getHost() {
     return window.location.protocol + "//" + window.location.hostname + ":" + serverPort;
 }

+ 30 - 9
web/src/components/MainPage.vue

@@ -14,14 +14,15 @@ import ModalAddUser from './modal/ModalAddUser.vue'
 import ModalUpdateUsername from './modal/ModalUpdateUsername.vue'
 import ModalUpdatePassword from "./modal/ModalUpdatePassword.vue"
 import ModalToast from "./modal/ModalToast.vue"
+import ModalExpiredNotifications from "./modal/ModalExpiredNotifications.vue"
 import {useAuthStore} from '../stores/auth.js'
 import {usePlaceStore} from '../stores/place.js'
-import {useThingStore, typePlace} from '../stores/thing.js'
+import {typePlace, useThingStore} from '../stores/thing.js'
 import {useImageStore} from '../stores/image.js'
 import {useTagStore} from '../stores/tag.js'
 import * as auth from "../auth/auth.js"
 import * as client from "../client/client.js"
-import {formatDate} from "../helpers/date.js"
+import {formatDateRusStr} from "../helpers/date.js"
 
 export default {
     components: {
@@ -36,6 +37,7 @@ export default {
         ModalSearchThing,
         ModalTags,
         ModalShowImage,
+        ModalExpiredNotifications,
         ModalAddUser,
         ModalUpdateUsername,
         ModalUpdatePassword,
@@ -89,9 +91,7 @@ export default {
                 if (name === "setSelectedThing" && args.length) {
                     if (args[0] !== this.thingStore.selectedThing) {
                         after(() => {
-                            let thingID = this.thingStore.selectedThing
-
-                            this.refreshThingImages(thingID)
+                            this.refreshThingImages(this.thingStore.selectedThing)
                         })
                     }
                 }
@@ -103,6 +103,7 @@ export default {
                 switch (name) {
                     case "setAuth":
                         this.refreshPlaces()
+                        this.refreshExpiredNotifications()
                         break
                     case "resetAuth":
                         this.resetPlaces()
@@ -114,6 +115,7 @@ export default {
         // Refresh places after start
         if (this.authStore.isAuth) {
             this.refreshPlaces()
+            this.refreshExpiredNotifications()
         }
     },
     methods: {
@@ -191,7 +193,7 @@ export default {
                             "id": thing.id,
                             "title": thing.title,
                             "desc": thing.description,
-                            "date": formatDate(thing.updated_at),
+                            "date": formatDateRusStr(thing.updated_at),
                             "tags": thing.tags
                         })
                     }
@@ -211,7 +213,7 @@ export default {
                         "image": host + image.image,
                         "place_id": image.place_id,
                         "thing_id": image.thing_id,
-                        "date": formatDate(image.created_at),
+                        "date": formatDateRusStr(image.created_at),
                     })
                 });
             }
@@ -229,12 +231,25 @@ export default {
                         "image": host + image.image,
                         "place_id": image.place_id,
                         "thing_id": image.thing_id,
-                        "date": formatDate(image.created_at),
+                        "date": formatDateRusStr(image.created_at),
                     })
                 });
             }
         },
 
+        refreshExpiredNotifications() {
+            const interval = setInterval(() => {
+                if (this.$refs.modalExpiredNotifications) {
+                    clearInterval(interval)
+
+                    let res = this.request(client.methodGet, client.routeGetExpiredNotifications)
+                    if (Array.isArray(res.data.notifications) && res.data.notifications.length) {
+                        this.$refs.modalExpiredNotifications.init(res.data.notifications)
+                    }
+                }
+            }, 100)
+        },
+
         // Actions
 
         addPlace() {
@@ -361,7 +376,12 @@ export default {
                 }
             }
         },
-
+        afterExpiredNotification(placeID, thingID) {
+            this.resetTags()
+            this.refreshPlaces(placeID)
+            this.refreshThings(placeID)
+            this.thingStore.setSelectedThing(thingID)
+        },
         logout() {
             auth.clearToken()
             this.authStore.resetAuth()
@@ -586,6 +606,7 @@ export default {
     <ModalSearchThing ref="modalSearchThing" @after-search-thing="afterSearchThing" @after-filter-tag="afterFilterTag"></ModalSearchThing>
     <ModalTags ref="modalTags" @after-tags="afterTags"></ModalTags>
     <ModalShowImage ref="modalShowImage"></ModalShowImage>
+    <ModalExpiredNotifications ref="modalExpiredNotifications" @after-expired-notification="afterExpiredNotification"></ModalExpiredNotifications>
     <ModalAddUser ref="modalAddUser" @after-add-user="afterAddUser"></ModalAddUser>
     <ModalUpdateUsername ref="modalUpdateUsername" @after-update-username="afterUpdateUsername"></ModalUpdateUsername>
     <ModalUpdatePassword ref="modalUpdatePassword" @after-update-password="afterUpdatePassword"></ModalUpdatePassword>

+ 1 - 2
web/src/components/modal/ModalDeletePlace.vue

@@ -67,8 +67,7 @@ export default {
                     <div v-else>
                         Подтвердите удаление <b>{{ form.title }}</b>
                         <br><br>
-                        <small class="text-secondary">Будут удалены все вещи и фото, прикрепленные к данному
-                            месту</small>
+                        <small class="text-secondary">Будут удалены все вещи и фото, прикрепленные к данному месту</small>
                     </div>
                 </div>
                 <div class="modal-footer">

+ 77 - 0
web/src/components/modal/ModalExpiredNotifications.vue

@@ -0,0 +1,77 @@
+<script>
+import * as client from "../../client/client.js"
+import {Modal} from 'bootstrap'
+
+export default {
+    expose: ['init'],
+    data() {
+        return {
+            notificationList: [],
+            modal: Object,
+        }
+    },
+    methods: {
+        init(req) {
+            if (req.length === 0) {
+                return
+            }
+
+            this.notificationList = req
+            this.modal = new Modal(document.getElementById('modal-expired-notifications'), {})
+            this.modal.show()
+        },
+        showResult(placeID, thingID) {
+            this.modal.hide()
+            this.$emit("after-expired-notification", placeID, thingID);
+        },
+        deleteNotification(thingID) {
+            let res = client.jsonRequest(client.methodDelete, client.routeDeleteNotification.replace("{thingId}", thingID))
+            if (res.status === client.statusOK) {
+                for(let i = 0; i < this.notificationList.length; i++){
+                    if (this.notificationList[i].thing_id === thingID) {
+                        this.notificationList.splice(i, 1);
+                    }
+                }
+            }
+        },
+    },
+}
+</script>
+
+<template>
+    <div class="modal" tabindex="-1" id="modal-expired-notifications">
+        <div class="modal-dialog">
+            <div class="modal-content">
+                <div class="modal-body">
+                    <div class="mb-2">
+                        Напоминания
+                    </div>
+                    <div
+                        class="row notification-results"
+                        v-for="notif in notificationList">
+                        <div class="col-8">
+                            <a
+                                href="#"
+                                class="link-primary"
+                                @click="showResult(notif.place_id, notif.thing_id)">
+                                {{ notif.thing_title }}
+                                ({{ notif.place_title }})
+                            </a>
+                        </div>
+                        <div class="col-4 text-end">
+                            <button
+                                class="btn delete"
+                                title="Удалить напоминание"
+                                @click="deleteNotification(notif.thing_id)">
+                                <i class="bi bi-trash-fill"></i>
+                            </button>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Закрыть</button>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>

+ 2 - 2
web/src/components/modal/ModalSearchThing.vue

@@ -95,7 +95,7 @@ export default {
 
             this.$emit("after-filter-tag", this.form.tagID);
         },
-        showResult(thingID, placeID) {
+        showResult(placeID, thingID) {
             this.modal.hide()
 
             this.$emit("after-search-thing", placeID, thingID);
@@ -158,7 +158,7 @@ export default {
                             href="#"
                             class="link-primary"
                             v-for="thing in thingList"
-                            @click="showResult(thing.id, thing.place_id)">
+                            @click="showResult(thing.place_id, thing.id)">
                             {{ thing.title }}
                         </a>
                     </div>

+ 2 - 4
web/src/components/modal/ModalShowImage.vue

@@ -46,12 +46,10 @@ export default {
                                 <img :src="image.image" class="d-block w-100">
                             </div>
                         </div>
-                        <button class="carousel-control-prev" type="button" data-bs-target="#imagesCarousel"
-                                data-bs-slide="prev">
+                        <button class="carousel-control-prev" type="button" data-bs-target="#imagesCarousel" data-bs-slide="prev">
                             <span class="carousel-control-prev-icon"></span>
                         </button>
-                        <button class="carousel-control-next" type="button" data-bs-target="#imagesCarousel"
-                                data-bs-slide="next">
+                        <button class="carousel-control-next" type="button" data-bs-target="#imagesCarousel" data-bs-slide="next">
                             <span class="carousel-control-next-icon" aria-hidden="true"></span>
                         </button>
                     </div>

+ 1 - 2
web/src/components/modal/ModalToast.vue

@@ -28,8 +28,7 @@ export default {
 
 <template>
     <div class="toast-container position-fixed bottom-0 end-0 p-3">
-        <div id="modal-toast-success" class="toast bg-success text-white" role="alert" aria-live="assertive"
-             aria-atomic="true">
+        <div id="modal-toast-success" class="toast bg-success text-white" role="alert" aria-live="assertive" aria-atomic="true">
             <div class="toast-body">
                 {{ message }}
             </div>

+ 44 - 0
web/src/components/modal/ModalUpdateThing.vue

@@ -1,5 +1,6 @@
 <script>
 import {useThingStore} from '../../stores/thing.js'
+import {formatDate} from "../../helpers/date.js";
 import * as client from "../../client/client.js"
 import {getPlacesListWithNestedTitles} from "../../helpers/places.js";
 import {Modal} from 'bootstrap'
@@ -14,9 +15,11 @@ export default {
     data() {
         return {
             modal: Object,
+            initDate: "",
             form: {
                 title: "",
                 desc: "",
+                date: "",
                 placeID: 0,
                 placeList: [],
                 tagList: [],
@@ -38,6 +41,8 @@ export default {
             this.form.placeID = 0;
             this.form.title = ""
             this.form.desc = ""
+            this.form.date = ""
+            this.initDate = ""
 
             let res = client.jsonRequest(client.methodGet, client.routeGetThing.replace("{thingId}", selectedThing))
             if (res.status === client.statusOK) {
@@ -94,6 +99,12 @@ export default {
                 }
             }
 
+            let notificationRes = client.jsonRequest(client.methodGet, client.routeGetNotification.replace("{thingId}", selectedThing))
+            if (notificationRes.status === client.statusOK) {
+                this.form.date = formatDate(notificationRes.data.notification_date)
+                this.initDate = this.form.date
+            }
+
             this.modal = new Modal(document.getElementById('modal-update-thing'), {})
             this.modal.show()
         },
@@ -105,6 +116,28 @@ export default {
                 return
             }
 
+            if (this.form.date === "") {
+                if (this.initDate !== "") {
+                    // Delete
+                    client.jsonRequest(client.methodDelete, client.routeGetNotification.replace("{thingId}", selectedThing))
+                }
+            } else {
+                if (this.initDate === "") {
+                    // Add
+                    let data = {
+                        notification_date: this.form.date + "T00:00:00.000Z",
+                        thing_id: selectedThing,
+                    }
+                    client.jsonRequest(client.methodPost, client.routeAddNotification, data)
+                } else if (this.form.date !== this.initDate) {
+                    // Update
+                    let data = {
+                        notification_date: this.form.date + "T00:00:00.000Z",
+                    }
+                    client.jsonRequest(client.methodPut, client.routeUpdateNotification.replace("{thingId}", selectedThing), data)
+                }
+            }
+
             // Delete
             this.form.initialTags.forEach(tagID => {
                 if (this.form.selectedTags.indexOf(tagID) < 0) {
@@ -179,6 +212,17 @@ export default {
                              </textarea>
                         </div>
                     </div>
+                    <div class="row mb-3">
+                        <label class="col-sm-3 col-form-label col-form-label-sm">
+                            <b>Напоминание</b>
+                        </label>
+                        <div class="col-sm-9">
+                            <input
+                                type="date"
+                                class="form-control form-control-sm"
+                                v-model.trim="form.date">
+                        </div>
+                    </div>
                     <div class="row">
                         <label class="col-sm-3 col-form-label col-form-label-sm">
                             <b>Теги</b>

+ 15 - 2
web/src/helpers/date.js

@@ -1,6 +1,6 @@
 "use strict"
 
-export function formatDate(str) {
+export function formatDateRusStr(str) {
     let date = new Date(str);
     let res = date.getDate() + " "
 
@@ -43,5 +43,18 @@ export function formatDate(str) {
     }
 
     res += " " + date.getFullYear()
+
     return res
-}
+}
+
+export function formatDate(str) {
+    let date = new Date(str);
+    let yyyy = date.getFullYear();
+    let mm = date.getMonth() + 1;
+    let dd = date.getDate();
+
+    if (dd < 10) dd = '0' + dd;
+    if (mm < 10) mm = '0' + mm;
+
+    return yyyy + '-' + mm + '-' + dd;
+}