Added Switch dialog, and link token creation.

This commit is contained in:
Andrew Lalis 2022-05-24 00:32:40 +02:00
parent 2cc3c2259a
commit c0c036d223
9 changed files with 525 additions and 254 deletions

View File

@ -30,7 +30,7 @@ export function getLinkTokens(rs) {
/** /**
* Creates a new link token. * Creates a new link token.
* @param {RailSystem} rs * @param {RailSystem} rs
* @param {LinkToken} data * @param {{label: String, componentIds: Number[]}} data
* @return {Promise<string>} A promise that resolves to the token that was created. * @return {Promise<string>} A promise that resolves to the token that was created.
*/ */
export function createLinkToken(rs, data) { export function createLinkToken(rs, data) {

View File

@ -5,7 +5,6 @@
> >
<q-item-section> <q-item-section>
<q-item-label>{{railSystem.name}}</q-item-label> <q-item-label>{{railSystem.name}}</q-item-label>
<q-item-label caption>Id: {{railSystem.id}}</q-item-label>
</q-item-section> </q-item-section>
</q-item> </q-item>
</template> </template>

View File

@ -16,19 +16,19 @@
</div> </div>
<q-page-sticky position="bottom-right" :offset="[25, 25]"> <q-page-sticky position="bottom-right" :offset="[25, 25]">
<q-fab icon="add" direction="up" color="accent"> <q-fab icon="add" direction="up" color="accent">
<q-fab-action @click="addSignalDialog = true"> <q-fab-action @click="addSignalData.toggle = true">
<q-icon><img src="~assets/icons/signal_icon.svg"/></q-icon> <q-icon><img src="~assets/icons/signal_icon.svg"/></q-icon>
<q-tooltip>Add Signal</q-tooltip> <q-tooltip>Add Signal</q-tooltip>
</q-fab-action> </q-fab-action>
<q-fab-action @click="addSegmentBoundaryDialog = true"> <q-fab-action @click="addSegmentBoundaryData.toggle = true">
<q-icon><img src="~assets/icons/segment-boundary_icon.svg"/></q-icon> <q-icon><img src="~assets/icons/segment-boundary_icon.svg"/></q-icon>
<q-tooltip>Add Segment Boundary</q-tooltip> <q-tooltip>Add Segment Boundary</q-tooltip>
</q-fab-action> </q-fab-action>
<q-fab-action @click="addSwitchDialog = true"> <q-fab-action @click="addSwitchData.toggle = true">
<q-icon><img src="~assets/icons/switch_icon.svg"/></q-icon> <q-icon><img src="~assets/icons/switch_icon.svg"/></q-icon>
<q-tooltip>Add Switch</q-tooltip> <q-tooltip>Add Switch</q-tooltip>
</q-fab-action> </q-fab-action>
<q-fab-action @click="addLabelDialog = true"> <q-fab-action @click="addLabelData.toggle = true">
<q-icon><img src="~assets/icons/label_icon.svg"/></q-icon> <q-icon><img src="~assets/icons/label_icon.svg"/></q-icon>
<q-tooltip>Add Label</q-tooltip> <q-tooltip>Add Label</q-tooltip>
</q-fab-action> </q-fab-action>
@ -36,174 +36,145 @@
</q-page-sticky> </q-page-sticky>
<!-- Add Signal Dialog --> <!-- Add Signal Dialog -->
<q-dialog v-model="addSignalDialog" style="min-width: 400px" @hide="resetAll"> <add-component-dialog
<q-card> v-model="addSignalData"
<q-form @submit="onAddSignalSubmit" @reset="resetAll"> type="SIGNAL"
<q-card-section> :rail-system="railSystem"
<div class="text-h6">Add Signal</div> title="Add Signal"
<p> success-message="Signal added."
Add a signal to the rail system. >
</p> <template #subtitle>
</q-card-section> <p>
<q-card-section> Add a signal to the rail system.
<q-input </p>
label="Name" </template>
type="text" <template #default>
v-model="addComponentData.name" <q-card-section>
/> <q-select
<div class="row"> v-model="addSignalData.segment"
<q-input :options="railSystem.segments"
label="X" option-value="id"
type="number" option-label="name"
class="col-sm-4" label="Segment"
v-model="addComponentData.position.x" />
/> </q-card-section>
<q-input </template>
label="Y" </add-component-dialog>
type="number"
class="col-sm-4"
v-model="addComponentData.position.y"
/>
<q-input
label="Z"
type="number"
class="col-sm-4"
v-model="addComponentData.position.z"
/>
</div>
</q-card-section>
<q-card-section>
<q-select
v-model="addSignalData.segment"
:options="railSystem.segments"
option-value="id"
option-label="name"
label="Segment"
/>
</q-card-section>
<q-card-actions align="right" class="text-primary">
<q-btn flat label="Cancel" type="reset" @click="addSignalDialog = false"/>
<q-btn flat label="Add Signal" type="submit"/>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<!-- Add Segment boundary --> <!-- Add Segment boundary -->
<q-dialog v-model="addSegmentBoundaryDialog" style="min-width: 400px" @hide="resetAll"> <add-component-dialog
<q-card> title="Add Segment Boundary"
<q-form @submit="onAddSegmentBoundarySubmit" @reset="resetAll"> success-message="Segment boundary added."
<q-card-section> :rail-system="railSystem"
<div class="text-h6">Add Segment Boundary</div> type="SEGMENT_BOUNDARY"
<p> v-model="addSegmentBoundaryData"
Add a segment boundary to the rail system. A segment boundary is a >
point where a train can cross between two segments, or "blocks" in <template #subtitle>
the system. For example, a train may move from a junction segment to <p>
a main line. This boundary is a place where a detector component can Add a segment boundary to the rail system.
update the system as trains pass. </p>
</p> </template>
</q-card-section> <template #default>
<q-card-section> <q-card-section>
<q-input <q-select
label="Name" v-model="addSegmentBoundaryData.segments"
type="text" :options="railSystem.segments"
v-model="addComponentData.name" multiple
/> :option-value="segment => segment"
<div class="row"> :option-label="segment => segment.name"
<q-input label="Segments"
label="X" />
type="number" </q-card-section>
class="col-sm-4" </template>
v-model="addComponentData.position.x" </add-component-dialog>
/>
<q-input
label="Y"
type="number"
class="col-sm-4"
v-model="addComponentData.position.y"
/>
<q-input
label="Z"
type="number"
class="col-sm-4"
v-model="addComponentData.position.z"
/>
</div>
</q-card-section>
<q-card-section>
<q-select
v-model="addSegmentBoundaryData.segments"
:options="railSystem.segments"
multiple
option-value="id"
option-label="name"
label="Segments"
/>
</q-card-section>
<q-card-actions align="right" class="text-primary">
<q-btn flat label="Cancel" type="reset" @click="addSegmentBoundaryDialog = false"/>
<q-btn flat label="Add Segment Boundary" type="submit"/>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<!-- Add Switch dialog --> <!-- Add Switch dialog -->
<q-dialog v-model="addSwitchDialog" @hide="resetAll"></q-dialog> <add-component-dialog
title="Add Switch"
success-message="Switch added."
:rail-system="railSystem"
type="SWITCH"
v-model="addSwitchData"
>
<template #subtitle>
<p>
Add a switch to the rail system.
</p>
</template>
<template #default>
<q-card-section>
<div class="row">
<q-list>
<q-item
v-for="config in addSwitchData.possibleConfigurations"
:key="config.key"
>
<q-item-section>
<q-item-label>
<q-chip
v-for="node in config.nodes"
:key="node.id"
:label="node.name"
/>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</div>
<!-- Add Label dialog --> <div class="row">
<q-dialog v-model="addLabelDialog" style="min-width: 400px" @hide="resetAll"> <div class="col-sm-6">
<q-card> <q-select
<q-form @submit="onAddLabelSubmit" @reset="resetAll"> v-model="addSwitchData.pathNode1"
<q-card-section> :options="getEligibleSwitchNodes()"
<div class="text-h6">Add Label</div> :option-value="segment => segment"
<p> :option-label="segment => segment.name"
Add a label to the rail system as a piece of text on the map. Labels label="First Path Node"
are purely a visual component, and do not interact with the system
in any way, besides being a helpful point of reference for users.
</p>
</q-card-section>
<q-card-section>
<q-input
label="Name"
type="text"
v-model="addComponentData.name"
/>
<div class="row">
<q-input
label="X"
type="number"
class="col-sm-4"
v-model="addComponentData.position.x"
/>
<q-input
label="Y"
type="number"
class="col-sm-4"
v-model="addComponentData.position.y"
/>
<q-input
label="Z"
type="number"
class="col-sm-4"
v-model="addComponentData.position.z"
/> />
</div> </div>
</q-card-section> <div class="col-sm-6">
<q-card-section> <q-select
<q-input v-model="addSwitchData.pathNode2"
label="Label Text" :options="getEligibleSwitchNodes()"
type="text" :option-value="segment => segment"
v-model="addLabelData.text" :option-label="segment => segment.name"
/> label="Second Path Node"
</q-card-section> />
<q-card-actions align="right" class="text-primary"> </div>
<q-btn flat label="Cancel" type="reset" @click="addLabelDialog = false"/> </div>
<q-btn flat label="Add Label" type="submit"/> <div class="row">
</q-card-actions> <q-btn label="Add Configuration" color="primary" @click="addSwitchConfiguration"/>
</q-form> </div>
</q-card>
</q-dialog> </q-card-section>
</template>
</add-component-dialog>
<!-- Add Label dialog -->
<add-component-dialog
title="Add Label"
success-message="Added label."
:rail-system="railSystem"
type="LABEL"
v-model="addLabelData"
>
<template #subtitle>
<p>
Add a label to the rail system as a piece of text on the map. Labels
are purely a visual component, and do not interact with the system
in any way, besides being a helpful point of reference for users.
</p>
</template>
<template #default>
<q-card-section>
<q-input
label="Label Text"
type="text"
v-model="addLabelData.text"
/>
</q-card-section>
</template>
</add-component-dialog>
</template> </template>
<script> <script>
@ -212,10 +183,11 @@ import { draw, initMap } from "src/render/mapRenderer";
import SelectedComponentView from "components/rs/SelectedComponentView.vue"; import SelectedComponentView from "components/rs/SelectedComponentView.vue";
import { useQuasar } from "quasar"; import { useQuasar } from "quasar";
import { createComponent } from "src/api/components"; import { createComponent } from "src/api/components";
import AddComponentDialog from "components/rs/add_component/AddComponentDialog.vue";
export default { export default {
name: "MapView", name: "MapView",
components: { SelectedComponentView }, components: { AddComponentDialog, SelectedComponentView },
setup() { setup() {
const quasar = useQuasar(); const quasar = useQuasar();
return {quasar}; return {quasar};
@ -228,23 +200,39 @@ export default {
}, },
data() { data() {
return { return {
addSignalDialog: false, addSignalData: {
addSegmentBoundaryDialog: false,
addSwitchDialog: false,
addLabelDialog: false,
addComponentData: {
name: "", name: "",
position: { position: {
x: 0, y: 0, z: 0 x: 0, y: 0, z: 0
} },
}, segment: null,
addSignalData: { toggle: false
segment: null
}, },
addSegmentBoundaryData: { addSegmentBoundaryData: {
segments: [] name: "",
position: {
x: 0, y: 0, z: 0
},
toggle: false,
segments: [],
connectedNodes: []
},
addSwitchData: {
name: "",
position: {
x: 0, y: 0, z: 0
},
toggle: false,
possibleConfigurations: [],
// Utility properties for the UI for adding configurations.
pathNode1: null,
pathNode2: null
}, },
addLabelData: { addLabelData: {
position: {
x: 0, y: 0, z: 0
},
toggle: false,
text: "" text: ""
} }
} }
@ -264,50 +252,38 @@ export default {
} }
}, },
methods: { methods: {
onAddSignalSubmit() { getEligibleSwitchNodes() {
const data = {...this.addComponentData, ...this.addSignalData}; return this.railSystem.components.filter(component => {
data.type = "SIGNAL"; return component.connectedNodes !== undefined && component.connectedNodes !== null;
this.attemptCreateComponent(data, "Signal added.", () => this.addSignalDialog = false); });
}, },
onAddSegmentBoundarySubmit() { addSwitchConfiguration() {
const data = {...this.addComponentData, ...this.addSegmentBoundaryData}; if (
data.type = "SEGMENT_BOUNDARY"; this.addSwitchData.pathNode1 === null ||
this.attemptCreateComponent(data, "Segment boundary added.", () => this.addSegmentBoundaryDialog = false); this.addSwitchData.pathNode2 === null ||
}, this.addSwitchData.pathNode1.id === this.addSwitchData.pathNode2.id ||
segmentBoundarySegmentsValid() { this.addSwitchData.possibleConfigurations.some(config => {
const set = new Set(this.addSegmentBoundaryData.segments); // Check if there's already a configuration containing both of these nodes.
return set.size === 2 && set.size === this.addSegmentBoundaryData.segments.length; return config.nodes.every(node =>
}, node.id === this.addSwitchData.pathNode1.id ||
onAddLabelSubmit() { node.id === this.addSwitchData.pathNode2.id);
const data = {...this.addComponentData, ...this.addLabelData};
data.type = "LABEL";
this.attemptCreateComponent(data, "Label added.", () => this.addLabelDialog = false);
},
resetAll() {
this.addComponentData.name = "";
this.addComponentData.position.x = 0;
this.addComponentData.position.y = 0;
this.addComponentData.position.z = 0;
this.addSignalDialog.segment = null;
this.addSegmentBoundaryData.segments = [];
this.addLabelData.text = "";
},
attemptCreateComponent(data, successMessage, successHandler) {
createComponent(this.railSystem, data)
.then(() => {
this.quasar.notify({
color: "positive",
message: successMessage
});
successHandler();
}) })
.catch(error => { ) {
console.log(error); this.quasar.notify({
this.quasar.notify({ color: "warning",
color: "negative", message: "Invalid switch configuration."
message: "An error occurred: " + error.response.data.message
});
}); });
return;
}
// All good!
this.addSwitchData.possibleConfigurations.push({
nodes: [
this.addSwitchData.pathNode1,
this.addSwitchData.pathNode2
],
// A unique key, just for the frontend to use. This is not used by the API.
key: this.addSwitchData.pathNode1.id + "_" + this.addSwitchData.pathNode2.id
});
} }
} }
}; };

View File

@ -25,7 +25,7 @@
id="signalInfo" id="signalInfo"
label="Signal Information" label="Signal Information"
v-if="component.type === 'SIGNAL'" v-if="component.type === 'SIGNAL'"
content-inset-level="0.5" :content-inset-level="0.5"
switch-toggle-side switch-toggle-side
expand-separator expand-separator
> >
@ -38,7 +38,7 @@
<q-expansion-item <q-expansion-item
label="Connected Nodes" label="Connected Nodes"
v-if="component.connectedNodes !== undefined && component.connectedNodes !== null" v-if="component.connectedNodes !== undefined && component.connectedNodes !== null"
content-inset-level="0.5" :content-inset-level="0.5"
switch-toggle-side switch-toggle-side
expand-separator expand-separator
class="q-gutter-md" class="q-gutter-md"
@ -63,7 +63,7 @@
<q-expansion-item <q-expansion-item
label="Connected Segments" label="Connected Segments"
v-if="component.type === 'SEGMENT_BOUNDARY'" v-if="component.type === 'SEGMENT_BOUNDARY'"
content-inset-level="0.5" :content-inset-level="0.5"
switch-toggle-side switch-toggle-side
expand-separator expand-separator
> >
@ -76,7 +76,7 @@
<q-expansion-item <q-expansion-item
label="Switch Information" label="Switch Information"
v-if="component.type === 'SWITCH'" v-if="component.type === 'SWITCH'"
content-inset-level="0.5" :content-inset-level="0.5"
switch-toggle-side switch-toggle-side
expand-separator expand-separator
> >
@ -86,14 +86,16 @@
:key="config.id" :key="config.id"
> >
<q-item-section> <q-item-section>
<q-item-label class="q-gutter-md"> <q-item-label class="q-gutter-sm">
<q-btn <q-chip
v-for="node in config.nodes" v-for="node in config.nodes"
:key="node.id" :key="node.id"
dense dense
size="sm" size="sm"
:label="node.name" :label="node.name"
:color="component.activeConfiguration && component.activeConfiguration.id === config.id ? 'primary' : 'secondary'" :color="component.activeConfiguration && component.activeConfiguration.id === config.id ? 'primary' : 'secondary'"
:text-color="'white'"
clickable
@click="select(node)" @click="select(node)"
/> />
</q-item-label> </q-item-label>

View File

@ -1,12 +1,6 @@
<template> <template>
<q-list> <q-list>
<q-expansion-item <link-tokens-view :rail-system="railSystem"/>
expand-separator
label="Component Links"
caption="Link components to your system."
>
<link-tokens-view :rail-system="railSystem"/>
</q-expansion-item>
</q-list> </q-list>
</template> </template>

View File

@ -0,0 +1,120 @@
<template>
<q-dialog v-model="componentData.toggle" style="min-width: 400px" @hide="onReset">
<q-card>
<q-form @submit="onSubmit" @reset="onReset" @change="onChange">
<q-card-section>
<div class="text-h6">{{title}}</div>
<slot name="subtitle"></slot>
</q-card-section>
<q-card-section>
<q-input
label="Name"
type="text"
v-model="componentData.name"
autofocus
/>
<div class="row">
<q-input
label="X"
type="number"
class="col-sm-4"
v-model="componentData.position.x"
/>
<q-input
label="Y"
type="number"
class="col-sm-4"
v-model="componentData.position.y"
/>
<q-input
label="Z"
type="number"
class="col-sm-4"
v-model="componentData.position.z"
/>
</div>
</q-card-section>
<slot :component-data="componentData"></slot>
<q-card-actions align="right" class="text-primary">
<q-btn flat label="Cancel" type="reset" @click="componentData.toggle = false"/>
<q-btn flat label="Add" type="submit"/>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<script>
import { createComponent } from "src/api/components";
import { useQuasar } from "quasar";
import { RailSystem } from "src/api/railSystems";
export default {
name: "AddComponentDialog",
props: {
modelValue: {
type: Object,
required: true
},
type: {
type: String,
required: true
},
railSystem: {
type: RailSystem,
required: true
},
title: {
type: String,
required: true
},
successMessage: {
type: String,
required: true
}
},
setup() {
const quasar = useQuasar();
return {quasar};
},
data() {
return {
componentData: this.modelValue
}
},
methods: {
onSubmit() {
const postData = {type: this.type, ...this.componentData};
createComponent(this.railSystem, postData)
.then(() => {
this.quasar.notify({
color: "positive",
message: this.successMessage
});
this.componentData.toggle = false;
})
.catch(error => {
console.log(error);
this.quasar.notify({
color: "negative",
message: "An error occurred: " + error.response.data.message
});
});
},
onReset() {
this.componentData.name = "";
this.componentData.position.x = 0;
this.componentData.position.y = 0;
this.componentData.position.z = 0;
this.$emit("reset");
},
onChange() {
this.$emit("update:modelValue", this.componentData);
}
}
};
</script>
<style scoped>
</style>

View File

@ -0,0 +1,68 @@
<template>
<add-component-dialog
v-model="addSignalData"
type="SIGNAL"
:rail-system="railSystem"
title="Add Signal"
success-message="Signal added."
@reset="onReset"
>
<template #subtitle>
<p>
Add a signal to the rail system.
</p>
</template>
<template #default>
<q-card-section>
<q-select
v-model="addSignalData.segment"
:options="railSystem.segments"
option-value="id"
option-label="name"
label="Segment"
/>
</q-card-section>
</template>
</add-component-dialog>
</template>
<script>
import { RailSystem } from "src/api/railSystems";
import AddComponentDialog from "components/rs/add_component/AddComponentDialog.vue";
export default {
name: "AddSignalDialog",
components: {AddComponentDialog},
props: {
railSystem: {
type: RailSystem,
required: true
},
modelValue: {
type: Boolean,
required: true
}
},
data() {
return {
addSignalData: {
name: "",
position: {
x: 0, y: 0, z: 0
},
segment: null,
toggle: this.modelValue
}
}
},
methods: {
onReset() {
this.addSignalData.segment = null;
}
}
};
</script>
<style scoped>
</style>

View File

@ -1,26 +1,98 @@
<template> <template>
<q-list> <q-expansion-item
<q-item> expand-separator
<q-item-section side> label="Component Links"
<q-btn label="Add Link" color="positive" /> caption="Link components to your system."
</q-item-section> @before-show="refreshLinkTokens"
</q-item> switch-toggle-side
<q-item :content-inset-level="0.5"
v-for="token in linkTokens" >
:key="token.id" <q-list>
> <q-item
<q-item-section> v-for="token in linkTokens"
<q-item-label>{{token.name}}</q-item-label> :key="token.id"
<q-item-label caption>{{token.id}}</q-item-label> >
</q-item-section> <q-item-section>
</q-item> <q-item-label>{{token.label}}</q-item-label>
</q-list> <q-item-label caption>{{token.id}}</q-item-label>
</q-item-section>
</q-item>
<q-item v-if="linkTokens.length === 0">
<q-item-section>
<q-item-label caption>There are no link tokens. Add one via the <em>Add Link</em> button below.</q-item-label>
</q-item-section>
</q-item>
<q-separator/>
<q-item>
<q-item-section side>
<q-btn label="Add Link" color="primary" @click="addTokenDialog = true" />
</q-item-section>
</q-item>
</q-list>
</q-expansion-item>
<q-dialog v-model="addTokenDialog" style="min-width: 400px;" @hide="onReset">
<q-card>
<q-form @submit="onSubmit" @reset="onReset">
<q-card-section>
<div class="text-h6">Add Link Token</div>
<p>
Create a new link token that gives external components access to
this rail system's live data streams. Use a link token to let signals
in your system get real-time updates, and to let segment boundaries
send real-time status updates as trains move.
</p>
<p>
You will be shown the link token <strong>only once</strong> when the
token is first created. If you lose it, you will need to delete the
token and create a new one.
</p>
</q-card-section>
<q-card-section>
<q-input
label="Label"
type="text"
v-model="addTokenData.label"
autofocus
/>
<q-select
label="Components"
multiple
v-model="addTokenData.selectedComponents"
:options="getEligibleComponents()"
:option-value="component => component.id"
:option-label="component => component.name"
use-chips
stack-label
/>
</q-card-section>
<q-card-section v-if="token">
<q-banner class="bg-primary text-white">
<p>
Link token created: <code class="text-bold">{{token}}</code>
</p>
<p>
Copy this token now; it won't be shown again.
</p>
</q-banner>
</q-card-section>
<q-card-actions align="right" class="text-primary">
<q-btn flat label="Close" type="reset" @click="addTokenDialog = false"/>
<q-btn flat label="Add" type="submit" v-if="!token"/>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template> </template>
<script> <script>
import { RailSystem } from "src/api/railSystems"; import { RailSystem } from "src/api/railSystems";
import { getLinkTokens } from "src/api/linkTokens"; import { createLinkToken, getLinkTokens } from "src/api/linkTokens";
import { useQuasar } from "quasar";
export default { export default {
name: "LinkTokensView", name: "LinkTokensView",
@ -30,15 +102,53 @@ export default {
required: true required: true
} }
}, },
setup() {
const quasar = useQuasar();
return {quasar};
},
data() { data() {
return { return {
linkTokens: [], linkTokens: [],
addTokenDialog: false addTokenDialog: false,
addTokenData: {
label: "",
selectedComponents: [],
componentIds: []
},
token: null
}; };
}, },
mounted() { methods: {
getLinkTokens(this.railSystem) refreshLinkTokens() {
.then(tokens => this.linkTokens = tokens); getLinkTokens(this.railSystem)
.then(tokens => {
this.linkTokens = tokens;
});
},
getEligibleComponents() {
return this.railSystem.components.filter(component => {
return component.type === "SIGNAL" || component.type === "SEGMENT_BOUNDARY" || component.type === "SWITCH";
});
},
onSubmit() {
this.addTokenData.componentIds = this.addTokenData.selectedComponents.map(c => c.id);
createLinkToken(this.railSystem, this.addTokenData)
.then(token => {
this.token = token;
})
.catch(error => {
this.quasar.notify({
color: "negative",
message: "An error occurred: " + error.response.data.message
});
});
},
onReset() {
this.addTokenData.name = "";
this.addTokenData.selectedComponents.length = 0;
this.addTokenData.componentIds.length = 0;
this.token = null;
}
} }
}; };
</script> </script>

View File

@ -40,7 +40,9 @@ export default {
data() { data() {
return { return {
panel: "map", panel: "map",
railSystem: null railSystem: null,
linkTokens: []
} }
}, },
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {