2020-08-08 23:59:13 +02:00
|
|
|
|
<template>
|
2020-08-12 15:15:44 +02:00
|
|
|
|
<v-container fluid>
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
<v-card>
|
|
|
|
|
|
<v-card-title>
|
2020-08-12 15:15:44 +02:00
|
|
|
|
<v-toolbar flat>
|
|
|
|
|
|
<v-toolbar-title>
|
|
|
|
|
|
{{
|
|
|
|
|
|
$route.params.sequence
|
|
|
|
|
|
? ($route.params.sequence.includes && $route.params.sequence.includes(";"))
|
|
|
|
|
|
? `Sequences ${$route.params.sequence.split(";").sort().join(", ")}`
|
|
|
|
|
|
: `Sequence ${$route.params.sequence}`
|
|
|
|
|
|
: $route.params.date0
|
|
|
|
|
|
? $route.params.date1
|
|
|
|
|
|
? `Between ${$route.params.date0} and ${$route.params.date1}`
|
|
|
|
|
|
: `On ${$route.params.date0}`
|
|
|
|
|
|
: "All events"
|
|
|
|
|
|
}}
|
|
|
|
|
|
</v-toolbar-title>
|
2020-08-22 20:43:23 +02:00
|
|
|
|
|
|
|
|
|
|
<dougal-event-edit-dialog
|
|
|
|
|
|
v-model="eventDialog"
|
|
|
|
|
|
:allowed-labels="userLabels"
|
|
|
|
|
|
:preset-remarks="presetRemarks"
|
|
|
|
|
|
:default-timestamp="defaultEventTimestamp"
|
2020-09-04 01:29:00 +02:00
|
|
|
|
:default-sequence="defaultSequence"
|
|
|
|
|
|
:default-shotpoint="point"
|
|
|
|
|
|
:event-mode="online?'seq':'timed'"
|
2020-08-22 20:43:23 +02:00
|
|
|
|
@save="saveEvent"
|
|
|
|
|
|
></dougal-event-edit-dialog>
|
|
|
|
|
|
|
2020-08-12 15:15:44 +02:00
|
|
|
|
<v-spacer></v-spacer>
|
|
|
|
|
|
<v-text-field
|
|
|
|
|
|
v-model="filter"
|
|
|
|
|
|
append-icon="mdi-magnify"
|
2020-08-25 13:02:56 +02:00
|
|
|
|
label="Filter"
|
2020-08-12 15:15:44 +02:00
|
|
|
|
single-line
|
|
|
|
|
|
hide-details></v-text-field>
|
2020-08-22 20:43:23 +02:00
|
|
|
|
</v-toolbar>
|
|
|
|
|
|
</v-card-title>
|
|
|
|
|
|
<v-card-text>
|
|
|
|
|
|
|
|
|
|
|
|
<v-data-table
|
|
|
|
|
|
:headers="headers"
|
|
|
|
|
|
:items="rows"
|
|
|
|
|
|
item-key="tstamp"
|
|
|
|
|
|
sort-by="tstamp"
|
|
|
|
|
|
:sort-desc="true"
|
|
|
|
|
|
:search="filter"
|
2020-09-07 17:23:00 +02:00
|
|
|
|
:custom-filter="searchTable"
|
2020-08-22 20:43:23 +02:00
|
|
|
|
:loading="loading"
|
|
|
|
|
|
fixed-header
|
|
|
|
|
|
>
|
2020-08-12 15:15:44 +02:00
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
<template v-slot:item.tstamp="{value}">
|
|
|
|
|
|
<span style="white-space:nowrap;" v-if="value">
|
|
|
|
|
|
{{ value.replace(/(.{10})T(.{8}).{4}Z$/, "$1 $2") }}
|
2020-08-12 15:15:44 +02:00
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
<template v-slot:item.remarks="{item}">
|
|
|
|
|
|
<v-edit-dialog v-if="item.items"
|
|
|
|
|
|
large
|
|
|
|
|
|
@save="rowEditorSave"
|
|
|
|
|
|
@cancel="rowEditorCancel"
|
|
|
|
|
|
@open="rowEditorOpen(item)"
|
|
|
|
|
|
@close="rowEditorClose"
|
|
|
|
|
|
> <div v-html="item.items.map(i => i.remarks).join('<br/>')"></div>
|
|
|
|
|
|
<template v-slot:input>
|
|
|
|
|
|
<h3>{{
|
|
|
|
|
|
editedRow.sequence
|
|
|
|
|
|
? `${editedRow.sequence} @ ${editedRow.point}`
|
|
|
|
|
|
: editedRow.tstamp
|
|
|
|
|
|
? editedRow.tstamp.replace(/(.{10})T(.{8}).{4}Z$/, "$1 $2")
|
|
|
|
|
|
: editedRow.key
|
|
|
|
|
|
}}</h3><hr/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<dougal-context-menu
|
|
|
|
|
|
:value="remarksMenu"
|
|
|
|
|
|
@input="addPresetRemark"
|
|
|
|
|
|
:items="presetRemarks"
|
|
|
|
|
|
absolute
|
|
|
|
|
|
></dougal-context-menu>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<template v-for="editedItem in editedRow.items">
|
|
|
|
|
|
<v-text-field
|
|
|
|
|
|
v-model="editedItem.remarks"
|
|
|
|
|
|
label="Edit"
|
|
|
|
|
|
single-line
|
|
|
|
|
|
hide-details="auto"
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
|
|
<template v-slot:prepend>
|
|
|
|
|
|
<v-icon v-show="!editedItem.remarks && presetRemarks"
|
|
|
|
|
|
title="Select predefined comments"
|
|
|
|
|
|
color="primary"
|
|
|
|
|
|
@click="(e) => {remarksMenuItem = editedItem; remarksMenu = e}"
|
|
|
|
|
|
>
|
|
|
|
|
|
mdi-dots-vertical
|
|
|
|
|
|
</v-icon>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<template v-slot:append v-if="editedItem.remarks || editedItem.labels.filter(l => labels[l].model.user).length">
|
|
|
|
|
|
<v-hover v-slot:default="{hover}">
|
|
|
|
|
|
<v-icon
|
|
|
|
|
|
title="Remove comment"
|
|
|
|
|
|
:color="hover ? 'error' : 'error lighten-4'"
|
|
|
|
|
|
@click="removeEvent(editedItem, editedRow)"
|
|
|
|
|
|
>mdi-minus-circle</v-icon>
|
|
|
|
|
|
</v-hover>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</v-text-field>
|
|
|
|
|
|
|
|
|
|
|
|
<v-container>
|
|
|
|
|
|
<v-row no-gutters>
|
|
|
|
|
|
<v-col class="flex-grow-0">
|
|
|
|
|
|
<!-- Add a new label control -->
|
|
|
|
|
|
<v-edit-dialog
|
|
|
|
|
|
large
|
|
|
|
|
|
@save="addLabel(editedItem)"
|
|
|
|
|
|
@cancel="selectedLabels=[]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<v-icon
|
|
|
|
|
|
small
|
|
|
|
|
|
title="Add label"
|
|
|
|
|
|
>mdi-tag-plus</v-icon>
|
|
|
|
|
|
<template v-slot:input>
|
|
|
|
|
|
<v-autocomplete
|
|
|
|
|
|
:items="availableLabels(editedItem.labels)"
|
|
|
|
|
|
v-model="selectedLabels"
|
|
|
|
|
|
label="Add label"
|
|
|
|
|
|
chips
|
|
|
|
|
|
deletable-chips
|
|
|
|
|
|
multiple
|
|
|
|
|
|
autofocus
|
|
|
|
|
|
@keydown.stop="(e) => {if (e.key == 'Enter') debug(e)}"
|
|
|
|
|
|
@input="labelSearch = null;"
|
|
|
|
|
|
:search-input.sync="labelSearch"
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
|
|
<template v-slot:selection="data">
|
|
|
|
|
|
<v-chip
|
|
|
|
|
|
v-bind="data.attrs"
|
|
|
|
|
|
:input-value="data.selected"
|
|
|
|
|
|
small
|
|
|
|
|
|
@click="data.select"
|
|
|
|
|
|
:color="labels[data.item].view.colour"
|
|
|
|
|
|
:title="labels[data.item].view.description"
|
|
|
|
|
|
>{{data.item}}</v-chip>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</v-autocomplete>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</v-edit-dialog>
|
|
|
|
|
|
</v-col>
|
|
|
|
|
|
<v-col class="flex-grow-0">
|
|
|
|
|
|
<v-chip-group>
|
|
|
|
|
|
<v-chip v-for="label in editedItem.labels" :key="label"
|
|
|
|
|
|
small
|
|
|
|
|
|
:close="labels[label].model.user"
|
|
|
|
|
|
:color="labels[label].view.colour"
|
|
|
|
|
|
:title="labels[label].view.description"
|
|
|
|
|
|
@click:close="removeLabel(label, editedItem)"
|
|
|
|
|
|
>{{label}}</v-chip>
|
|
|
|
|
|
</v-chip-group>
|
|
|
|
|
|
</v-col>
|
|
|
|
|
|
</v-row>
|
|
|
|
|
|
</v-container>
|
|
|
|
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<v-icon v-if="editedRow.items.length == 0 || editedRow.items[editedRow.items.length-1].remarks"
|
|
|
|
|
|
color="primary"
|
|
|
|
|
|
title="Add comment"
|
|
|
|
|
|
class="mb-2"
|
|
|
|
|
|
@click="addEvent"
|
|
|
|
|
|
>mdi-plus-circle</v-icon>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</v-edit-dialog>
|
|
|
|
|
|
<v-edit-dialog v-else
|
|
|
|
|
|
@save="rowEditorSave"
|
|
|
|
|
|
@cancel="rowEditorCancel"
|
|
|
|
|
|
@open="rowEditorOpen"
|
|
|
|
|
|
@close="rowEditorClose"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template v-slot:input>
|
|
|
|
|
|
<v-text-field
|
|
|
|
|
|
v-model="props.item.remarks[0]"
|
|
|
|
|
|
label="Edit"
|
|
|
|
|
|
single-line
|
|
|
|
|
|
></v-text-field>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</v-edit-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Labels column -->
|
|
|
|
|
|
<template v-slot:item.labels="{item}">
|
|
|
|
|
|
<!-- Existing labels for row -->
|
|
|
|
|
|
<v-chip v-for="label in item.items.map(i => i.labels).flat()"
|
|
|
|
|
|
class="ma-1"
|
2020-08-12 15:15:44 +02:00
|
|
|
|
small
|
2020-08-22 20:43:23 +02:00
|
|
|
|
:color="labels[label].view.colour"
|
|
|
|
|
|
:title="labels[label].view.description"
|
|
|
|
|
|
:key="label"
|
|
|
|
|
|
@click:close="removeLabel(label, item)"
|
|
|
|
|
|
>{{label}}</v-chip>
|
2020-08-12 15:15:44 +02:00
|
|
|
|
</template>
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
<!-- Actions column (FIXME currently not used) -->
|
|
|
|
|
|
<template v-slot:item.actions="{ item }">
|
|
|
|
|
|
<div style="white-space:nowrap;">
|
|
|
|
|
|
<v-icon v-if="$root.user || true"
|
|
|
|
|
|
small
|
|
|
|
|
|
class="mr-2"
|
|
|
|
|
|
title="View on map"
|
|
|
|
|
|
@click="viewOnMap(item)"
|
|
|
|
|
|
disabled
|
|
|
|
|
|
>
|
|
|
|
|
|
mdi-map
|
|
|
|
|
|
</v-icon>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
2020-08-12 15:15:44 +02:00
|
|
|
|
|
|
|
|
|
|
</v-data-table>
|
2020-08-22 20:43:23 +02:00
|
|
|
|
</v-card-text>
|
|
|
|
|
|
</v-card>
|
2020-08-12 15:15:44 +02:00
|
|
|
|
</v-container>
|
2020-08-08 23:59:13 +02:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2020-08-26 17:48:55 +02:00
|
|
|
|
import { mapActions, mapGetters } from 'vuex';
|
2020-08-22 20:43:23 +02:00
|
|
|
|
import DougalContextMenu from '@/components/context-menu';
|
|
|
|
|
|
import DougalEventEditDialog from '@/components/event-edit-dialog'
|
|
|
|
|
|
|
|
|
|
|
|
function ArraysEqual (a, b) {
|
|
|
|
|
|
return a.every(i => b.includes(i)) && b.every(i => a.includes(i));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function EventKey (e) {
|
|
|
|
|
|
return e.type+e.id;
|
|
|
|
|
|
}
|
2020-08-08 23:59:13 +02:00
|
|
|
|
|
|
|
|
|
|
export default {
|
2020-08-12 15:15:44 +02:00
|
|
|
|
name: "Log",
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
components: {
|
|
|
|
|
|
DougalEventEditDialog,
|
|
|
|
|
|
DougalContextMenu
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2020-08-12 15:15:44 +02:00
|
|
|
|
data () {
|
|
|
|
|
|
return {
|
|
|
|
|
|
headers: [
|
|
|
|
|
|
{
|
2020-08-22 20:43:23 +02:00
|
|
|
|
value: "tstamp",
|
2020-08-12 15:15:44 +02:00
|
|
|
|
text: "Timestamp",
|
|
|
|
|
|
width: "20ex"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: "sequence",
|
|
|
|
|
|
text: "Sequence",
|
|
|
|
|
|
align: "end",
|
|
|
|
|
|
width: "10ex"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2020-08-22 20:43:23 +02:00
|
|
|
|
value: "point",
|
2020-08-12 15:15:44 +02:00
|
|
|
|
text: "Shotpoint",
|
|
|
|
|
|
align: "end",
|
|
|
|
|
|
width: "10ex"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: "remarks",
|
|
|
|
|
|
text: "Text",
|
|
|
|
|
|
width: "100%"
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2020-08-22 20:43:23 +02:00
|
|
|
|
value: "labels",
|
2020-08-12 15:15:44 +02:00
|
|
|
|
text: "Labels"
|
|
|
|
|
|
},
|
2020-08-22 20:43:23 +02:00
|
|
|
|
{
|
|
|
|
|
|
value: "actions",
|
|
|
|
|
|
text: "Actions",
|
|
|
|
|
|
sortable: false
|
|
|
|
|
|
}
|
2020-08-12 15:15:44 +02:00
|
|
|
|
],
|
|
|
|
|
|
items: [],
|
|
|
|
|
|
labels: {},
|
|
|
|
|
|
options: {},
|
|
|
|
|
|
filter: "",
|
2020-08-22 20:43:23 +02:00
|
|
|
|
eventCount: null,
|
|
|
|
|
|
eventDialog: false,
|
|
|
|
|
|
defaultEventTimestamp: null,
|
|
|
|
|
|
presetRemarks: null,
|
|
|
|
|
|
remarksMenu: null,
|
|
|
|
|
|
remarksMenuItem: null,
|
|
|
|
|
|
editedRow: {
|
|
|
|
|
|
key: null,
|
|
|
|
|
|
tstamp: null,
|
|
|
|
|
|
sequence: null,
|
|
|
|
|
|
point: null,
|
|
|
|
|
|
items: []
|
|
|
|
|
|
},
|
|
|
|
|
|
selectedLabels: [],
|
|
|
|
|
|
labelSearch: null
|
2020-08-12 15:15:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
rows () {
|
|
|
|
|
|
const rows = {};
|
|
|
|
|
|
this.items.forEach(i => {
|
2020-08-22 20:43:23 +02:00
|
|
|
|
const key = i.tstamp || (i.sequence+"@"+i.point);
|
2020-08-12 15:15:44 +02:00
|
|
|
|
if (!rows[key]) {
|
2020-08-22 20:43:23 +02:00
|
|
|
|
rows[key] = {
|
|
|
|
|
|
key,
|
|
|
|
|
|
tstamp: i.tstamp,
|
|
|
|
|
|
sequence: i.sequence,
|
|
|
|
|
|
point: i.point,
|
|
|
|
|
|
items: []
|
|
|
|
|
|
}
|
2020-08-12 15:15:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const row = rows[key];
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
row.items.push(i);
|
2020-08-12 15:15:44 +02:00
|
|
|
|
});
|
|
|
|
|
|
return Object.values(rows);
|
2020-08-22 20:43:23 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
userLabels () {
|
|
|
|
|
|
const filtered = {};
|
|
|
|
|
|
for (const key in this.labels) {
|
|
|
|
|
|
if (this.labels[key].model.user) {
|
|
|
|
|
|
filtered[key] = this.labels[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return filtered;
|
2020-08-26 17:48:55 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
2020-09-04 01:29:00 +02:00
|
|
|
|
defaultSequence () {
|
|
|
|
|
|
if (this.$route.params.sequence) {
|
|
|
|
|
|
return Number(this.$route.params.sequence.split(";").pop());
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return this.sequence;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
...mapGetters(['loading', 'online', 'sequence', 'line', 'point', 'lineName'])
|
2020-08-26 17:48:55 +02:00
|
|
|
|
|
2020-08-12 15:15:44 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
watch: {
|
|
|
|
|
|
options: {
|
|
|
|
|
|
handler () {
|
|
|
|
|
|
//this.getLines();
|
|
|
|
|
|
},
|
|
|
|
|
|
deep: true
|
2020-08-22 20:43:23 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
eventDialog (val) {
|
|
|
|
|
|
if (val) {
|
|
|
|
|
|
// If not online
|
|
|
|
|
|
this.defaultEventTimestamp = Date.now();
|
|
|
|
|
|
}
|
2020-08-12 15:15:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
debug (value) {
|
|
|
|
|
|
console.log("DEBUG", value);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2020-08-12 15:15:44 +02:00
|
|
|
|
async getEventCount () {
|
|
|
|
|
|
//this.eventCount = await this.api([`/project/${this.$route.params.project}/event/?count`]);
|
|
|
|
|
|
this.eventCount = null;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
async getEvents () {
|
|
|
|
|
|
|
|
|
|
|
|
const query = new URLSearchParams(this.options);
|
|
|
|
|
|
if (this.options.itemsPerPage < 0) {
|
|
|
|
|
|
query.delete("itemsPerPage");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this.$route.params.sequence) {
|
|
|
|
|
|
query.set("sequence", this.$route.params.sequence);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this.$route.params.date0) {
|
|
|
|
|
|
query.set("date0", this.$route.params.date0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this.$route.params.date1) {
|
|
|
|
|
|
query.set("date1", this.$route.params.date1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const url = `/project/${this.$route.params.project}/event?${query.toString()}`;
|
|
|
|
|
|
|
|
|
|
|
|
this.items = await this.api([url]) || [];
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
async getLabelDefinitions () {
|
2020-08-12 15:15:44 +02:00
|
|
|
|
const url = `/project/${this.$route.params.project}/label`;
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
const labelSet = {};
|
2020-08-12 15:15:44 +02:00
|
|
|
|
const labels = await this.api([url]) || [];
|
2020-08-22 20:43:23 +02:00
|
|
|
|
labels.forEach( l => labelSet[l.name] = l.data );
|
|
|
|
|
|
this.labels = labelSet;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
async getPresetRemarks () {
|
|
|
|
|
|
const url = `/project/${this.$route.params.project}/configuration/events/presetRemarks`;
|
|
|
|
|
|
|
|
|
|
|
|
this.presetRemarks = await this.api([url]);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
newItem (from = {}) {
|
|
|
|
|
|
const type = (from.sequence && from.point) ? "sequence" : "timed";
|
|
|
|
|
|
const tstamp = from.tstamp || (new Date).toISOString();
|
|
|
|
|
|
const sequence = from.sequence || null; // FIXME TODO Use vuex
|
|
|
|
|
|
const point = from.point || null; // FIXME TODO Use vuex
|
|
|
|
|
|
const geometry = from.geometry || null; // FIXME TODO Use vuex
|
|
|
|
|
|
return {
|
|
|
|
|
|
type,
|
|
|
|
|
|
id: null,
|
|
|
|
|
|
tstamp,
|
|
|
|
|
|
sequence,
|
|
|
|
|
|
point,
|
|
|
|
|
|
remarks: "",
|
|
|
|
|
|
labels: [],
|
|
|
|
|
|
geometry
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
async saveEvent (event) {
|
|
|
|
|
|
console.log("Saving", event);
|
|
|
|
|
|
const url = `/project/${this.$route.params.project}/event`;
|
|
|
|
|
|
await this.api([url, {
|
|
|
|
|
|
method: "POST",
|
|
|
|
|
|
body: event
|
|
|
|
|
|
}]);
|
|
|
|
|
|
this.showSnack(["New event saved", "success"]);
|
|
|
|
|
|
this.getEvents();
|
|
|
|
|
|
console.log("Done");
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
saveLabels (event) {
|
|
|
|
|
|
console.log("saveLabels", event);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
rowEditorOpen (row) {
|
|
|
|
|
|
console.log(row);
|
|
|
|
|
|
this.editedRow = JSON.parse(JSON.stringify(row));
|
|
|
|
|
|
for (const item of this.editedRow.items) {
|
|
|
|
|
|
if (item.type != "timed" && item.type != "sequence") {
|
|
|
|
|
|
if (item.sequence && item.point) {
|
|
|
|
|
|
item.type = "sequence";
|
|
|
|
|
|
} else {
|
|
|
|
|
|
item.type = "timed";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
rowEditorClose () {
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
async rowEditorSave () {
|
|
|
|
|
|
|
|
|
|
|
|
// Helper – returns a callback that checks if two events have the same key
|
|
|
|
|
|
function within (item) {
|
|
|
|
|
|
return function (i) {
|
|
|
|
|
|
return EventKey(i) == EventKey(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const originalRow = this.rows.find(r => r.key == this.editedRow.key);
|
|
|
|
|
|
|
|
|
|
|
|
if (!originalRow) {
|
|
|
|
|
|
this.showSnack(["Cannot find the original item that was to be edited!", "error"]);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log("rowEditorSave", this.editedRow, originalRow);
|
|
|
|
|
|
|
|
|
|
|
|
const promises = [];
|
|
|
|
|
|
|
|
|
|
|
|
for (const editedItem of this.editedRow.items) {
|
|
|
|
|
|
|
|
|
|
|
|
// Discard non user writable labels
|
|
|
|
|
|
editedItem.labels = editedItem.labels.filter(l => this.labels[l].model.user);
|
|
|
|
|
|
|
|
|
|
|
|
// Try to find this event in originalRow
|
|
|
|
|
|
const originalItem = originalRow.items.find(within(editedItem));
|
|
|
|
|
|
|
|
|
|
|
|
if (originalItem) {
|
|
|
|
|
|
// If found, check to see if its remarks or labels have changed
|
|
|
|
|
|
|
|
|
|
|
|
if (originalItem.remarks != editedItem.remarks || !ArraysEqual(originalItem.labels, editedItem.labels)) {
|
|
|
|
|
|
// This item has been modified
|
|
|
|
|
|
|
|
|
|
|
|
console.log("PUT modified item", editedItem, originalItem);
|
|
|
|
|
|
const url = `/project/${this.$route.params.project}/event/${editedItem.type}/${editedItem.id}/`;
|
|
|
|
|
|
const request = this.api([url, {
|
|
|
|
|
|
method: "PUT",
|
|
|
|
|
|
body: editedItem
|
|
|
|
|
|
}]);
|
|
|
|
|
|
|
|
|
|
|
|
promises.push(request);
|
|
|
|
|
|
} // Else, the item was not modified
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// If not found, this is a new event
|
|
|
|
|
|
|
|
|
|
|
|
if (!editedItem.remarks.trim() && !editedItem.labels.length) {
|
|
|
|
|
|
// There is nothing to post, discard
|
|
|
|
|
|
console.log("Discard empty event", editedItem);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log("POST new item", editedItem);
|
|
|
|
|
|
|
|
|
|
|
|
const url = `/project/${this.$route.params.project}/event/`;
|
|
|
|
|
|
const request = this.api([url, {
|
|
|
|
|
|
method: "POST",
|
|
|
|
|
|
body: editedItem
|
|
|
|
|
|
}]);
|
|
|
|
|
|
|
|
|
|
|
|
promises.push(request);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const originalItem of originalRow.items) {
|
|
|
|
|
|
if (originalItem.virtual) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Try to find this event in editedRow
|
|
|
|
|
|
const editedItem = this.editedRow.items.find(within(originalItem));
|
|
|
|
|
|
|
|
|
|
|
|
if (!editedItem) {
|
|
|
|
|
|
// If not found, it has been deleted
|
|
|
|
|
|
console.log("DELETE old item", originalItem);
|
|
|
|
|
|
|
|
|
|
|
|
const url = `/project/${this.$route.params.project}/event/${originalItem.type}/${originalItem.id}/`;
|
|
|
|
|
|
const request = this.api([url, {method: "DELETE"}]);
|
|
|
|
|
|
|
|
|
|
|
|
promises.push(request);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.rowEditorCancel();
|
|
|
|
|
|
if (promises.length) {
|
|
|
|
|
|
this.showSnack(["Saving data…", "info"]);
|
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
|
this.showSnack(["The changes have been saved", "success"]);
|
|
|
|
|
|
this.getEvents();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
rowEditorCancel () {
|
|
|
|
|
|
this.$nextTick( () => {
|
|
|
|
|
|
this.editedRow = {
|
|
|
|
|
|
key: null,
|
|
|
|
|
|
tstamp: null,
|
|
|
|
|
|
sequence: null,
|
|
|
|
|
|
point: null,
|
|
|
|
|
|
items: []
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
addEvent () {
|
|
|
|
|
|
const count = this.editedRow.items.length;
|
|
|
|
|
|
const newItem = this.newItem(this.editedRow.items[count-1]);
|
|
|
|
|
|
this.editedRow.items.push(newItem);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
removeEvent (event, row) {
|
|
|
|
|
|
const index = row.items.indexOf(event);
|
|
|
|
|
|
if (index >= 0) {
|
|
|
|
|
|
console.log(JSON.parse(JSON.stringify(event)));
|
|
|
|
|
|
if (!event.virtual) {
|
|
|
|
|
|
// A virtual event will not be deletable so we can't
|
|
|
|
|
|
// remove it
|
|
|
|
|
|
row.items.splice(index, 1);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// What we do instead is clear any user-entered info
|
|
|
|
|
|
event.remarks = "";
|
|
|
|
|
|
event.labels = event.labels.filter(l => !this.labels[l].model.user);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
addPresetRemark ({text}) {
|
|
|
|
|
|
if (this.remarksMenuItem) {
|
|
|
|
|
|
this.remarksMenuItem.remarks = text;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.remarksMenuItem = null;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
availableLabels (usedLabels) {
|
|
|
|
|
|
return Object.keys(this.labels)
|
|
|
|
|
|
.filter(k => !usedLabels.includes(k) && this.labels[k].model.user);
|
2020-08-12 15:15:44 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
addLabel (editedItem) {
|
|
|
|
|
|
console.log("addLabel", this.selectedLabels, editedItem);
|
|
|
|
|
|
|
|
|
|
|
|
this.selectedLabels.forEach(label => {
|
|
|
|
|
|
if (!editedItem.labels.includes(label)) {
|
|
|
|
|
|
editedItem.labels.push(label);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.selectedLabels = [];
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
removeLabel (label, item) {
|
|
|
|
|
|
console.log("removeLabel", label, item);
|
|
|
|
|
|
|
|
|
|
|
|
item.labels = item.labels.filter(l => l != label);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
const url = `/project/${this.$route.params.project}/event/${item.type}/${item.id}/label/${label}`;
|
|
|
|
|
|
|
|
|
|
|
|
this.api([url, {method: "DELETE"}])
|
|
|
|
|
|
.then( () => this.showSnack([`Label ${item.label} removed`, "info"]) );
|
|
|
|
|
|
*/
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
handleKeyboardEvent (e) {
|
|
|
|
|
|
if (e.ctrlKey && !e.altKey && !e.shiftKey && (e.keyCode == 13 || e.key == "F2")) {
|
|
|
|
|
|
console.log("Add timed event if offline or shot event if online");
|
|
|
|
|
|
this.eventDialog = true;
|
|
|
|
|
|
} else if (e.ctrlKey && !e.altKey && e.shiftKey && (e.keyCode == 13 || e.key == "F2")) {
|
|
|
|
|
|
console.log("Add timed event (even if online)");
|
|
|
|
|
|
this.eventDialog = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2020-09-07 17:23:00 +02:00
|
|
|
|
searchTable (value, search, item) {
|
|
|
|
|
|
if (!value && !search) return true;
|
|
|
|
|
|
const s = search.toLowerCase();
|
|
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
|
|
return value.toLowerCase().includes(s);
|
|
|
|
|
|
} else if (typeof value === 'number') {
|
|
|
|
|
|
return value == search;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return item.items.some( i => i.remarks.toLowerCase().includes(s) ) ||
|
|
|
|
|
|
item.items.some( i => i.labels.some( l => l.toLowerCase().includes(s) ));
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2020-08-22 20:43:23 +02:00
|
|
|
|
...mapActions(["api", "showSnack"])
|
2020-08-12 15:15:44 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
async mounted () {
|
2020-08-22 20:43:23 +02:00
|
|
|
|
await this.getLabelDefinitions()
|
2020-08-12 15:15:44 +02:00
|
|
|
|
this.getEventCount();
|
|
|
|
|
|
this.getEvents();
|
2020-08-22 20:43:23 +02:00
|
|
|
|
this.getPresetRemarks();
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('keyup', this.handleKeyboardEvent);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
beforeDestroy () {
|
|
|
|
|
|
window.removeEventListener('keyup', this.handleKeyboardEvent);
|
2020-08-12 15:15:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-08 23:59:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
</script>
|