comparison client/src/components/importoverview/ImportOverview.vue @ 2651:9f3856337f55

import_overview: new unified interface as default
author Thomas Junk <thomas.junk@intevation.de>
date Thu, 14 Mar 2019 14:53:17 +0100
parents client/src/components/importoverview/ImportOverviewAlt.vue@6c1730fc3dc1
children 3c04c8e46bd4
comparison
equal deleted inserted replaced
2650:a308baa7e7af 2651:9f3856337f55
2 <div class="overview"> 2 <div class="overview">
3 <UIBoxHeader 3 <UIBoxHeader
4 icon="clipboard-check" 4 icon="clipboard-check"
5 title="Staging Area" 5 title="Staging Area"
6 :closeCallback="$parent.close" 6 :closeCallback="$parent.close"
7 :actions="[{ callback: loadLogs, icon: 'redo' }]"
7 /> 8 />
8 <div class="d-flex flex-row w-100 justify-content-end"> 9 <div class="position-relative">
9 <button 10 <transition name="fade">
10 class="btn btn-sm btn-outline-info align-self-start mr-3" 11 <div
11 @click="refresh" 12 class="loading d-flex justify-content-center align-items-center"
12 > 13 v-if="loading"
13 <font-awesome-icon icon="redo"></font-awesome-icon>
14 </button>
15 </div>
16 <div class="d-flex flex-row w-100 border-bottom">
17 <font-awesome-icon
18 class="pointer"
19 @click="toggleStaging()"
20 v-if="stagingVisible && staging.length > 0"
21 icon="angle-up"
22 fixed-width
23 ></font-awesome-icon>
24 <font-awesome-icon
25 class="pointer"
26 @click="toggleStaging()"
27 v-if="!stagingVisible && staging.length > 0"
28 icon="angle-down"
29 fixed-width
30 ></font-awesome-icon>
31 <span style="width:1.25em;" v-if="!(staging.length > 0)"></span>
32 <Staging v-if="stagingVisible && staging.length > 0"></Staging>
33 <div v-else class="d-flex flex-row">
34 <h6>
35 <small><translate>Review</translate></small>
36 </h6>
37 <small class="ml-3" v-if="!(staging.length > 0)"
38 ><translate>Nothing to review</translate></small
39 > 14 >
40 </div> 15 <font-awesome-icon icon="spinner" spin />
41 </div> 16 </div>
42 <div class="mt-2"> 17 </transition>
43 <div class="d-flex flex-row"> 18 <div class="p-2 d-flex flex-row flex-fill justify-content-between">
44 <font-awesome-icon 19 <Filters></Filters>
45 class="pointer" 20 <div>
46 @click="toggleLogs()" 21 <button
47 v-if="logsVisible" 22 class="btn btn-sm btn-info"
48 icon="angle-up" 23 :disabled="!reviewed.length"
49 fixed-width 24 @click="save"
50 ></font-awesome-icon> 25 >
51 <font-awesome-icon 26 <translate>Commit</translate> {{ reviewed.length }}
52 class="pointer" 27 </button>
53 @click="toggleLogs()"
54 v-if="!logsVisible"
55 icon="angle-down"
56 fixed-width
57 ></font-awesome-icon>
58 <Logs v-if="logsVisible" :reload="reload"></Logs>
59 <div v-else>
60 <h6>
61 <small><translate>Logs</translate></small>
62 </h6>
63 </div> 28 </div>
64 </div> 29 </div>
30 <LogEntry
31 class="border-top d-flex-flex-column w-100"
32 :entry="entry"
33 v-for="entry in imports"
34 :key="entry.id"
35 ></LogEntry>
65 </div> 36 </div>
66 </div> 37 </div>
67 </template> 38 </template>
39
40 <style lang="sass" scoped>
41 .loading
42 background: rgba(255, 255, 255, 0.9)
43 position: absolute
44 z-index: 99
45 top: 0
46 right: 0
47 bottom: 0
48 left: 0
49 </style>
68 50
69 <script> 51 <script>
70 /* This is Free Software under GNU Affero General Public License v >= 3.0 52 /* This is Free Software under GNU Affero General Public License v >= 3.0
71 * without warranty, see README.md and license for details. 53 * without warranty, see README.md and license for details.
72 * 54 *
78 * Software engineering by Intevation GmbH 60 * Software engineering by Intevation GmbH
79 * 61 *
80 * Author(s): 62 * Author(s):
81 * Thomas Junk <thomas.junk@intevation.de> 63 * Thomas Junk <thomas.junk@intevation.de>
82 */ 64 */
83 import { displayError } from "@/lib/errors.js"; 65
84 import { mapState } from "vuex"; 66 import { mapState } from "vuex";
67 import { displayError, displayInfo } from "@/lib/errors.js";
68 import { STATES } from "@/store/imports.js";
85 69
86 export default { 70 export default {
87 name: "importoverview", 71 name: "importoverviewalt",
72 components: {
73 Filters: () => import("./Filters.vue"),
74 LogEntry: () => import("./LogEntry.vue")
75 },
88 data() { 76 data() {
89 return { 77 return {
90 reload: false 78 loading: false
91 }; 79 };
92 }, 80 },
93 components: {
94 Staging: () => import("./staging/Staging.vue"),
95 Logs: () => import("./importlogs/Logs.vue")
96 },
97 computed: { 81 computed: {
98 ...mapState("imports", ["stagingVisible", "logsVisible", "staging"]) 82 ...mapState("imports", ["imports", "filters", "reviewed"])
99 }, 83 },
100 methods: { 84 methods: {
101 toggleStaging() {
102 this.$store.commit("imports/setStagingVisibility", !this.stagingVisible);
103 },
104 toggleLogs() {
105 this.$store.commit("imports/setLogsVisibility", !this.logsVisible);
106 },
107 refresh() {
108 this.reload = true;
109 this.loadImportQueue();
110 this.loadLogs();
111 },
112 loadImportQueue() {
113 this.$store
114 .dispatch("imports/getStaging")
115 .then(() => {
116 this.reload = false;
117 })
118 .catch(error => {
119 const { status, data } = error.response;
120 displayError({
121 title: "Backend Error",
122 message: `${status}: ${data.message || data}`
123 });
124 });
125 },
126 loadLogs() { 85 loadLogs() {
86 this.loading = true;
127 this.$store 87 this.$store
128 .dispatch("imports/getImports") 88 .dispatch("imports/getImports")
129 .then(() => { 89 .then(() => {
130 this.reload = false; 90 this.loading = false;
131 }) 91 })
132 .catch(error => { 92 .catch(error => {
133 const { status, data } = error.response; 93 const { status, data } = error.response;
134 displayError({ 94 displayError({
135 title: this.$gettext("Backend Error"), 95 title: this.$gettext("Backend Error"),
136 message: `${status}: ${data.message || data}` 96 message: `${status}: ${data.message || data}`
137 }); 97 });
138 }); 98 });
99 },
100 save() {
101 if (!this.reviewed.length) return;
102
103 let popupContent = `<table class="table table-sm small mb-0 border-0" style="margin-top: -1px;">`;
104 this.reviewed.forEach(r => {
105 let imp = this.imports.find(i => i.id === r.id);
106 let approved = STATES.APPROVED === r.status;
107 popupContent += `<tr>
108 <td>${imp.id}</td>
109 <td>${imp.kind.toUpperCase()}</td>
110 <td>${this.$options.filters.dateTime(imp.enqueued)}</td>
111 <td class="text-${approved ? "success" : "danger"}">
112 ${this.$gettext(approved ? "approved" : "declined")}
113 </td>
114 </tr>`;
115 });
116 popupContent += "</table>";
117
118 this.$store.commit("application/popup", {
119 icon: "clipboard-check",
120 title: this.$gettext("Finish Review"),
121 padding: false,
122 big: true,
123 content: popupContent,
124 confirm: {
125 icon: "check",
126 callback: () => {
127 let data = this.reviewed.map(r => ({
128 id: r.id,
129 state: r.status
130 }));
131 this.$store
132 .dispatch("imports/confirmReview", data)
133 .then(response => {
134 this.loadLogs();
135 this.$store.commit("imports/setReviewed", []);
136 const messages = response.data
137 .map(x => {
138 if (x.message) return x.message;
139 if (x.error) return x.error;
140 })
141 .join("\n\n");
142 displayInfo({
143 title: "Staging Area",
144 message: messages,
145 options: {
146 timeout: 0,
147 buttons: [{ text: "Ok", action: null, bold: true }]
148 }
149 });
150 })
151 .catch(error => {
152 const { status, data } = error.response;
153 displayError({
154 title: "Backend Error",
155 message: `${status}: ${data.message || data}`
156 });
157 });
158 }
159 },
160 cancel: {
161 label: this.$gettext("Cancel"),
162 icon: "times"
163 }
164 });
165 }
166 },
167 watch: {
168 filters() {
169 this.$store.dispatch("imports/getImports", this.filters);
139 } 170 }
140 }, 171 },
141 mounted() { 172 mounted() {
142 this.refresh(); 173 this.loadLogs();
143 } 174 }
144 }; 175 };
145 </script> 176 </script>
146
147 <style lang="scss" scoped>
148 .overview {
149 max-height: 850px;
150 overflow-y: auto;
151 }
152 </style>