Browse Source

near release ready

Brian Wiborg 1 year ago
parent
commit
0d73b4655e
Signed by: Brian Wiborg <brian@flux.fail> GPG Key ID: F1200FAE077A4313

+ 0
- 91
components/pages/flux/Report/Step1.vue View File

@@ -1,91 +0,0 @@
1
-<template>
2
-  <div>
3
-    <v-layout wrap justify-space-around>
4
-      <v-flex xs12 sm8>
5
-        <v-form ref="form">
6
-          <v-container fluid>
7
-            <v-layout wrap justify-space-between>
8
-              <v-flex xs4>
9
-                <v-autocomplete
10
-                  v-model="country"
11
-                  :items="isoCountries"
12
-                  :label="$t('fluxPage.report.form.label.country')"
13
-                  autofocus
14
-                />
15
-              </v-flex>
16
-              <v-flex xs8>
17
-                <v-combobox
18
-                  v-model="city"
19
-                  :label="$t('fluxPage.report.form.label.city')"
20
-                />
21
-              </v-flex>
22
-            </v-layout>
23
-            <v-layout>
24
-              <v-flex xs12>
25
-                <v-text-field
26
-                  v-model="location"
27
-                  :label="$t('fluxPage.report.form.label.location')"
28
-                  @keyup.enter="goNextFluxReportFormPage"
29
-                />
30
-              </v-flex>
31
-            </v-layout>
32
-          </v-container>
33
-        </v-form>
34
-      </v-flex>
35
-    </v-layout>
36
-    <v-card class="mb-5" />
37
-    <v-layout>
38
-      <v-flex class="text-xs-right">
39
-        <v-btn flat @click="cancelReport">
40
-          {{ $t('generic.form.btn.cancel') }}
41
-        </v-btn>
42
-        <v-btn color="#5ccbf0" class="yes" @click="goNextFluxReportFormPage">
43
-          {{ $t('generic.form.btn.next') }}
44
-        </v-btn>
45
-      </v-flex>
46
-    </v-layout>
47
-  </div>
48
-</template>
49
-
50
-<script>
51
-import { isoCountries } from '@/utils/countryCodes'
52
-
53
-export default {
54
-  data() {
55
-    return {
56
-      country: this.$store.state.report.form.country,
57
-      city: this.$store.state.report.form.city,
58
-      location: this.$store.state.report.form.location,
59
-      countryRules: [
60
-        v => !!v || this.$t('fluxPage.report.form.err.required'),
61
-        v => Object.keys(isoCountries).includes(v.toUpperCase()) || this.$t('unknownCountryCode')
62
-      ],
63
-      isRequiredRules: [
64
-        v => !!v || this.$t('fluxPage.report.form.err.required')
65
-      ],
66
-      isoCountries: Object.keys(isoCountries)
67
-    }
68
-  },
69
-
70
-  methods: {
71
-    cancelReport: function () {
72
-      this.$store.commit('report/reset')
73
-      this.$router.push(this.localePath('flux-stream'))
74
-    },
75
-    goNextFluxReportFormPage: function () {
76
-      if (this.$refs.form.validate()) {
77
-        this.$store.commit('report/updateFields', {
78
-          country: this.country,
79
-          city: this.city,
80
-          location: this.location
81
-        })
82
-        this.$store.commit('report/stepForward')
83
-      }
84
-    }
85
-  }
86
-}
87
-</script>
88
-
89
-<style>
90
-
91
-</style>

+ 0
- 214
components/pages/flux/Report/Step2.vue View File

@@ -1,214 +0,0 @@
1
-<template>
2
-  <div>
3
-    <v-form ref="form" @submit.prevent="goNextFluxReportPage">
4
-      <v-layout wrap justify-space-around>
5
-        <v-flex xs4>
6
-          <center>
7
-            <v-text-field
8
-              v-model="line"
9
-              :label="$t('fluxPage.report.form.label.line')"
10
-              autofocus
11
-              @keyup.enter="goNextFluxReportFormPage"
12
-            />
13
-          </center>
14
-        </v-flex>
15
-        <v-flex xs8>
16
-          <center>
17
-            <v-text-field
18
-              v-model="direction"
19
-              :label="$t('fluxPage.report.form.label.direction')"
20
-              autofocus
21
-              @keyup.enter="goNextFluxReportFormPage"
22
-            />
23
-          </center>
24
-        </v-flex>
25
-        <v-flex xs12>
26
-          <center>
27
-            {{ vehicleText }}
28
-          </center>
29
-        </v-flex>
30
-        <v-flex>
31
-          <v-btn
32
-            :outline="isSelectedVehicle(1)"
33
-            flat
34
-            block
35
-            @click="selectVehicle(1)"
36
-          >
37
-            <VehicleIcon
38
-              :id="1"
39
-              :size="2"
40
-            />
41
-          </v-btn>
42
-        </v-flex>
43
-        <v-flex>
44
-          <v-btn
45
-            :outline="isSelectedVehicle(2)"
46
-            flat
47
-            block
48
-            @click="selectVehicle(2)"
49
-          >
50
-            <VehicleIcon
51
-              :id="2"
52
-              :size="2"
53
-            />
54
-          </v-btn>
55
-        </v-flex>
56
-        <v-flex>
57
-          <v-btn
58
-            :outline="isSelectedVehicle(4)"
59
-            flat
60
-            block
61
-            @click="selectVehicle(4)"
62
-          >
63
-            <VehicleIcon
64
-              :id="4"
65
-              :size="2"
66
-            />
67
-          </v-btn>
68
-        </v-flex>
69
-        <v-flex>
70
-          <v-btn
71
-            :outline="isSelectedVehicle(8)"
72
-            flat
73
-            block
74
-            @click="selectVehicle(8)"
75
-          >
76
-            <VehicleIcon
77
-              :id="8"
78
-              :size="2"
79
-            />
80
-          </v-btn>
81
-        </v-flex>
82
-        <v-flex>
83
-          <v-btn
84
-            :outline="isSelectedVehicle(16)"
85
-            flat
86
-            block
87
-            @click="selectVehicle(16)"
88
-          >
89
-            <VehicleIcon
90
-              :id="16"
91
-              :size="2"
92
-            />
93
-          </v-btn>
94
-        </v-flex>
95
-        <v-flex>
96
-          <v-btn
97
-            :outline="isSelectedVehicle(32)"
98
-            flat
99
-            block
100
-            @click="selectVehicle(32)"
101
-          >
102
-            <VehicleIcon
103
-              :id="32"
104
-              :size="2"
105
-            />
106
-          </v-btn>
107
-        </v-flex>
108
-        <v-flex>
109
-          <v-btn
110
-            :outline="isSelectedVehicle(64)"
111
-            flat
112
-            block
113
-            @click="selectVehicle(64)"
114
-          >
115
-            <VehicleIcon
116
-              :id="64"
117
-              :size="2"
118
-            />
119
-          </v-btn>
120
-        </v-flex>
121
-        <v-flex>
122
-          <v-btn
123
-            :outline="isSelectedVehicle(128)"
124
-            flat
125
-            block
126
-            @click="selectVehicle(128)"
127
-          >
128
-            <VehicleIcon
129
-              :id="128"
130
-              :size="2"
131
-            />
132
-          </v-btn>
133
-        </v-flex>
134
-      </v-layout>
135
-    </v-form>
136
-    <v-card class="mb-5" />
137
-    <v-layout>
138
-      <v-flex class="text-xs-right">
139
-        <v-btn flat @click="goPrevFluxReportFormPage">
140
-          {{ $t('generic.form.btn.back') }}
141
-        </v-btn>
142
-        <v-btn color="#5ccbf0" class="yes" @click="goNextFluxReportFormPage">
143
-          {{ $t('generic.form.btn.next') }}
144
-        </v-btn>
145
-      </v-flex>
146
-    </v-layout>
147
-  </div>
148
-</template>
149
-
150
-<script>
151
-import VehicleIcon from '@/components/VehicleIcon.vue'
152
-
153
-export default {
154
-  components: {
155
-    VehicleIcon
156
-  },
157
-
158
-  data() {
159
-    return {
160
-      line: this.$store.state.report.form.line,
161
-      direction: this.$store.state.report.form.direction,
162
-      vehicle: this.$store.state.report.form.vehicle,
163
-      lineRules: [
164
-        v => !!v || this.$t('fluxPage.report.form.err.required')
165
-      ],
166
-      directionRules: [
167
-        v => !!v || this.$t('fluxPage.report.form.err.required')
168
-      ]
169
-    }
170
-  },
171
-
172
-  computed: {
173
-    vehicleText: function () {
174
-      switch (this.vehicle) {
175
-        case 1: { return this.$t('vehicle.bus') }
176
-        case 2: return this.$t('vehicle.train')
177
-        case 4: return this.$t('vehicle.subway')
178
-        case 8: return this.$t('vehicle.tram')
179
-        case 16: return this.$t('vehicle.lift')
180
-        case 32: return this.$t('vehicle.ship')
181
-        case 64: return this.$t('vehicle.airplane')
182
-        case 128: return this.$t('vehicle.rocket')
183
-        default: return this.$t('vehicle.none')
184
-      }
185
-    }
186
-  },
187
-
188
-  methods: {
189
-    goPrevFluxReportFormPage: function () {
190
-      this.$store.commit('report/stepBack')
191
-    },
192
-
193
-    goNextFluxReportFormPage: function () {
194
-      if (this.$refs.form.validate()) {
195
-        this.$store.commit('report/updateFields', {
196
-          line: this.line,
197
-          direction: this.direction,
198
-          vehicle: this.vehicle
199
-        })
200
-        this.$store.commit('report/stepForward')
201
-      }
202
-    },
203
-
204
-    selectVehicle: function (vId) {
205
-      this.vehicle = vId
206
-      this.goNextFluxReportFormPage()
207
-    },
208
-
209
-    isSelectedVehicle: function (vId) {
210
-      return this.vehicle === vId
211
-    }
212
-  }
213
-}
214
-</script>

+ 0
- 106
components/pages/flux/Report/Step3.vue View File

@@ -1,106 +0,0 @@
1
-<template>
2
-  <div>
3
-    <v-layout row wrap justify-space-around>
4
-      <v-flex xs12>
5
-        <center>
6
-          <v-btn-toggle v-model="isArrivalDeparture" multiple>
7
-            <v-btn>
8
-              {{ $t('fluxPage.report.form.label.arrival') }}
9
-            </v-btn>
10
-            <v-btn>
11
-              {{ $t('fluxPage.report.form.label.departure') }}
12
-            </v-btn>
13
-          </v-btn-toggle>
14
-        </center>
15
-      </v-flex>
16
-      <v-flex xs12 py-3>
17
-        <center>
18
-          <span v-if="isArrivalDeparture.includes(0) && isArrivalDeparture.includes(1)">
19
-            {{ $t('fluxPage.report.form.label.arrival') }}
20
-            &amp;
21
-            {{ $t('fluxPage.report.form.label.departure') }}
22
-          </span>
23
-          <span v-else-if="isArrivalDeparture.includes(0)">
24
-            {{ $t('fluxPage.report.form.label.arrival') }}
25
-          </span>
26
-          <span v-else-if="isArrivalDeparture.includes(1)">
27
-            {{ $t('fluxPage.report.form.label.departure') }}
28
-          </span>
29
-          <span v-else>
30
-            {{ $t('generic.missingValue') }}
31
-          </span>
32
-        </center>
33
-      </v-flex>
34
-    </v-layout>
35
-
36
-    <v-layout wrap justify-space-around>
37
-      <v-flex xs12 md6 pa-2>
38
-        <DatetimePicker
39
-          :title="$t('fluxPage.report.form.label.scheduledAt')"
40
-          :date="`${scheduledAt}`"
41
-          @datetimeSelected="scheduledAt = $event"
42
-        />
43
-      </v-flex>
44
-    </v-layout>
45
-
46
-    <v-card class="mb-5" />
47
-    <v-layout>
48
-      <v-flex class="text-xs-right">
49
-        <v-btn flat @click="goPrevFluxReportFormPage">
50
-          {{ $t('generic.form.btn.back') }}
51
-        </v-btn>
52
-        <v-btn :disabled="!isValid" color="#5ccbf0" class="yes" @click="goNextFluxReportFormPage">
53
-          {{ $t('generic.form.btn.next') }}
54
-        </v-btn>
55
-      </v-flex>
56
-    </v-layout>
57
-  </div>
58
-</template>
59
-
60
-<script>
61
-import DatetimePicker from '@/components/picker/datetime'
62
-const moment = require('moment')
63
-
64
-export default {
65
-  components: {
66
-    DatetimePicker
67
-  },
68
-
69
-  data() {
70
-    const isArrivalDeparture = []
71
-    if (this.$store.state.report.form.arrival) {
72
-      isArrivalDeparture.push(0)
73
-    }
74
-    if (this.$store.state.report.form.departure) {
75
-      isArrivalDeparture.push(1)
76
-    }
77
-    return {
78
-      scheduledAt: this.$store.state.report.form.scheduledAt ? this.$store.state.report.form.scheduledAt : `${new Date()}`,
79
-      isArrivalDeparture: isArrivalDeparture
80
-    }
81
-  },
82
-
83
-  computed: {
84
-    isValid: function () {
85
-      if (this.scheduledAt && this.isArrivalDeparture.length > 0) {
86
-        return true
87
-      }
88
-      return false
89
-    }
90
-  },
91
-
92
-  methods: {
93
-    goPrevFluxReportFormPage: function () {
94
-      this.$store.commit('report/stepBack')
95
-    },
96
-    goNextFluxReportFormPage: function () {
97
-      this.$store.commit('report/updateFields', {
98
-        scheduledAt: this.scheduledAt ? moment(this.scheduledAt).startOf('minute').toDate() : '',
99
-        arrival: this.isArrivalDeparture.includes(0),
100
-        departure: this.isArrivalDeparture.includes(1)
101
-      })
102
-      this.$store.commit('report/stepForward')
103
-    }
104
-  }
105
-}
106
-</script>

+ 0
- 120
components/pages/flux/Report/Step4.vue View File

@@ -1,120 +0,0 @@
1
-<template>
2
-  <div>
3
-    <v-layout wrap justify-space-around>
4
-      <v-flex xs12>
5
-        <v-switch
6
-          v-model="cancelled"
7
-          :label="$t('fluxPage.report.form.label.cancelled')"
8
-          color="error"
9
-        >
10
-          Cancelled
11
-        </v-switch>
12
-      </v-flex>
13
-    </v-layout>
14
-
15
-    <v-layout wrap justify-space-around :d-none="cancelled">
16
-      <v-flex xs12 md6 pa-2>
17
-        <DatetimePicker
18
-          v-model="actuallyAt"
19
-          :date="`${actuallyAt}`"
20
-          :title="$t('fluxPage.report.form.label.actuallyAt')"
21
-          @datetimeSelected="actuallyAt = $event"
22
-        />
23
-      </v-flex>
24
-    </v-layout>
25
-
26
-    <v-card class="mb-5" />
27
-    <v-layout>
28
-      <v-flex class="text-xs-right">
29
-        <v-btn flat @click="goPrevFluxReportFormPage">
30
-          {{ $t('generic.form.btn.back') }}
31
-        </v-btn>
32
-        <v-btn
33
-          :disabled="!isValid || $store.state.report.loading"
34
-          :loading="$store.state.report.loading"
35
-          color="primary"
36
-          class="yes"
37
-          @click="goNextFluxReportFormPage"
38
-        >
39
-          {{ saveBtnLabel }}
40
-        </v-btn>
41
-      </v-flex>
42
-    </v-layout>
43
-  </div>
44
-</template>
45
-
46
-<script>
47
-import DatetimePicker from '@/components/picker/datetime'
48
-
49
-const moment = require('moment')
50
-
51
-export default {
52
-  components: {
53
-    DatetimePicker
54
-  },
55
-
56
-  data() {
57
-    return {
58
-      actuallyAt: this.$store.state.report.form.actuallyAt ? this.$store.state.report.form.actuallyAt : `${new Date()}`,
59
-      cancelled: this.$store.state.report.form.cancelled
60
-    }
61
-  },
62
-
63
-  computed: {
64
-    isValid: function () {
65
-      if (this.cancelled || this.actuallyAt) {
66
-        return true
67
-      }
68
-      return false
69
-    },
70
-
71
-    saveBtnLabel: function () {
72
-      if (this.$store.state.report.form.id) {
73
-        return this.$t('fluxPage.report.form.submitExisting')
74
-      } else {
75
-        return this.$t('fluxPage.report.form.submitNew')
76
-      }
77
-    }
78
-  },
79
-
80
-  methods: {
81
-    goPrevFluxReportFormPage() {
82
-      this.$store.commit('report/stepBack')
83
-    },
84
-
85
-    goNextFluxReportFormPage() {
86
-      this.$store.commit('report/updateFields', {
87
-        actuallyAt: this.actuallyAt && !this.cancelled ? moment(this.actuallyAt).startOf('minute').toDate() : '',
88
-        cancelled: this.cancelled
89
-      })
90
-      this.$store.dispatch('report/post')
91
-        .then(() => {
92
-          this.$store.commit('report/reset')
93
-          this.$router.push(this.localePath('flux-stream'))
94
-          this.$dialog.notify.success(this.$t('generic.message.reportSaved'), {
95
-            position: 'top-left'
96
-          })
97
-        })
98
-        .catch((err) => {
99
-          this.$store.commit('report/loaded')
100
-          if (err.code === 'ECONNABORTED') {
101
-            this.$dialog.notify.warning(this.$t('generic.errors.serverUnreachable'), {
102
-              position: 'top-left',
103
-              timeout: 10000
104
-            })
105
-          } else if (err.response.status === 422) {
106
-            this.$dialog.notify.error(this.$t('generic.errors.invalidReport'), {
107
-              position: 'top-left',
108
-              timeout: 10000
109
-            })
110
-          } else {
111
-            this.$dialog.notify.error(err, {
112
-              position: 'top-left',
113
-              timeout: 10000
114
-            })
115
-          }
116
-        })
117
-    }
118
-  }
119
-}
120
-</script>

+ 0
- 91
components/pages/flux/Report/index.vue View File

@@ -1,91 +0,0 @@
1
-<template>
2
-  <v-container>
3
-    <v-layout wrap justify-space-around>
4
-      <v-flex xs12 sm10 lg8 xl6>
5
-        <v-toolbar>
6
-          <v-toolbar-title>
7
-            {{ title }}
8
-          </v-toolbar-title>
9
-          <v-spacer />
10
-          <v-btn icon @click="resetCurrentFluxForm">
11
-            <v-icon>
12
-              cancel
13
-            </v-icon>
14
-          </v-btn>
15
-        </v-toolbar>
16
-        <v-stepper v-model="$store.state.report.windowStep">
17
-          <v-stepper-header>
18
-            <v-stepper-step :complete="$store.state.report.windowStep > 1" step="1">
19
-              {{ $t('fluxPage.report.step.where') }}
20
-            </v-stepper-step>
21
-            <v-divider />
22
-            <v-stepper-step :complete="$store.state.report.windowStep > 2" step="2">
23
-              {{ $t('fluxPage.report.step.what') }}
24
-            </v-stepper-step>
25
-            <v-divider />
26
-            <v-stepper-step :complete="$store.state.report.windowStep > 3" step="3">
27
-              {{ $t('fluxPage.report.step.when') }}
28
-            </v-stepper-step>
29
-            <v-divider />
30
-            <v-stepper-step :complete="$store.state.report.windowStep > 4" step="4">
31
-              {{ $t('fluxPage.report.step.details') }}
32
-            </v-stepper-step>
33
-          </v-stepper-header>
34
-          <v-stepper-items>
35
-            <v-stepper-content step="1">
36
-              <step1 />
37
-            </v-stepper-content>
38
-            <v-stepper-content step="2">
39
-              <step2 />
40
-            </v-stepper-content>
41
-            <v-stepper-content step="3">
42
-              <step3 />
43
-            </v-stepper-content>
44
-            <v-stepper-content step="4">
45
-              <step4 />
46
-            </v-stepper-content>
47
-          </v-stepper-items>
48
-        </v-stepper>
49
-      </v-flex>
50
-    </v-layout>
51
-  </v-container>
52
-</template>
53
-
54
-<script>
55
-import Step1 from './Step1.vue'
56
-import Step2 from './Step2.vue'
57
-import Step3 from './Step3.vue'
58
-import Step4 from './Step4.vue'
59
-
60
-export default {
61
-  components: {
62
-    Step1,
63
-    Step2,
64
-    Step3,
65
-    Step4
66
-  },
67
-
68
-  computed: {
69
-    title: function () {
70
-      if (!this.$store.state.report.form.id) {
71
-        return this.$t('fluxPage.report.title.new')
72
-      } else {
73
-        return this.$t('fluxPage.report.title.edit')
74
-      }
75
-    }
76
-  },
77
-
78
-  methods: {
79
-    resetCurrentFluxForm() {
80
-      this.$store.commit('report/reset')
81
-      this.$router.push(this.localePath('flux-stream'))
82
-    }
83
-  }
84
-}
85
-</script>
86
-
87
-<style>
88
-.yes {
89
-  color: #212121 !important;
90
-}
91
-</style>

+ 346
- 0
components/pages/flux/ReportForm/Form/index.vue View File

@@ -0,0 +1,346 @@
1
+<template>
2
+  <v-form ref="reportForm">
3
+    <!-- country & city -->
4
+    <v-layout wrap justify-space-between>
5
+      <v-flex xs4>
6
+        <v-autocomplete
7
+          v-model="country"
8
+          :items="isoCountries"
9
+          :label="$t('fluxPage.report.form.label.country')"
10
+        />
11
+      </v-flex>
12
+      <v-flex xs8>
13
+        <v-combobox
14
+          v-model="city"
15
+          :items="$store.state.autoComplete.city"
16
+          :label="$t('fluxPage.report.form.label.city')"
17
+          @keyup="autoComplete('city', $event)"
18
+        />
19
+      </v-flex>
20
+    </v-layout>
21
+
22
+    <!-- vehicle -->
23
+    <small>
24
+      {{ $t('fluxPage.report.label.vehicle') }}:
25
+      {{ vehicleLabel }}
26
+    </small>
27
+    <v-layout wrap justify-space-between>
28
+      <v-flex xs12>
29
+        <v-tabs
30
+          v-model="tab"
31
+          centered
32
+          grow
33
+        >
34
+          <v-tab
35
+            v-for="t in tabs"
36
+            :key="t"
37
+            icon-and-text
38
+          >
39
+            <VehicleIcon :id="t" :size="2" />
40
+          </v-tab>
41
+          <v-tab-item
42
+            v-for="t in tabs"
43
+            :key="t"
44
+          />
45
+        </v-tabs>
46
+      </v-flex>
47
+    </v-layout>
48
+
49
+    <!-- line & direction -->
50
+    <v-layout wrap justify-space-between>
51
+      <v-flex xs4>
52
+        <v-combobox
53
+          v-model="line"
54
+          :items="$store.state.autoComplete.line"
55
+          :label="$t('fluxPage.report.form.label.line')"
56
+          @keyup="autoComplete('line', $event)"
57
+        />
58
+      </v-flex>
59
+      <v-flex xs8>
60
+        <v-combobox
61
+          v-model="direction"
62
+          :items="$store.state.autoComplete.direction"
63
+          :label="$t('fluxPage.report.form.label.direction')"
64
+          @keyup="autoComplete('direction', $event)"
65
+        />
66
+      </v-flex>
67
+    </v-layout>
68
+
69
+    <!-- location -->
70
+    <v-layout>
71
+      <v-flex xs12>
72
+        <v-combobox
73
+          v-model="location"
74
+          :items="$store.state.autoComplete.location"
75
+          :label="$t('fluxPage.report.form.label.location')"
76
+          @keyup="autoComplete('location', $event)"
77
+        />
78
+      </v-flex>
79
+    </v-layout>
80
+
81
+    <!-- times -->
82
+    <v-layout>
83
+      <v-flex xs12 pt-3>
84
+        <small>
85
+          {{ $t('fluxPage.report.label.cancelled') }}:
86
+          {{ cancelled ? $t('generic.label.yes') : $t('generic.label.no') }}
87
+        </small>
88
+        <v-btn
89
+          :flat="cancelled"
90
+          block
91
+          @click="cancelled = !cancelled"
92
+        >
93
+          {{ $t('fluxPage.report.form.label.cancelled') }}
94
+        </v-btn>
95
+      </v-flex>
96
+    </v-layout>
97
+
98
+    <v-flex v-if="!cancelled" xs12>
99
+      <v-layout row wrap justify-space-between>
100
+        <v-flex xs12>
101
+          <small>
102
+            {{ $t('fluxPage.report.label.arrivalDeparture') }}:
103
+            {{ arrival ? $t('fluxPage.report.form.label.arrival') :'' }}
104
+            {{ arrival && departure ? ' & ' : '' }}
105
+            {{ departure ? $t('fluxPage.report.form.label.departure') : '' }}
106
+          </small>
107
+        </v-flex>
108
+
109
+        <v-flex xs6>
110
+          <v-btn
111
+            :flat="arrival"
112
+            block
113
+            @click="arrival = !arrival"
114
+          >
115
+            {{ $t('fluxPage.report.form.label.arrival') }}
116
+          </v-btn>
117
+        </v-flex>
118
+        <v-flex xs6>
119
+          <v-btn
120
+            :flat="departure"
121
+            block
122
+            @click="departure = !departure"
123
+          >
124
+            {{ $t('fluxPage.report.form.label.departure') }}
125
+          </v-btn>
126
+        </v-flex>
127
+
128
+        <v-flex v-if="(arrival || departure)" xs12 py-3>
129
+          <v-layout wrap justify-space-between>
130
+            <v-flex xs12 class="text-xs-center">
131
+              <DatetimePicker
132
+                :title="$t('fluxPage.report.form.label.scheduledAt')"
133
+                :timestamp="scheduledAt"
134
+                @selected="scheduledAt = $event"
135
+              />
136
+              <v-card>
137
+                <v-flex xs12 pb-1>
138
+                  {{ scheduledAt ? scheduledAt.calendar() : '' }}
139
+                </v-flex>
140
+              </v-card>
141
+            </v-flex>
142
+            <v-flex xs12 class="text-xs-center">
143
+              <DatetimePicker
144
+                :title="$t('fluxPage.report.form.label.actuallyAt')"
145
+                :timestamp="actuallyAt"
146
+                @selected="actuallyAt = $event"
147
+              />
148
+              <v-card>
149
+                <v-flex xs12 pb-1>
150
+                  {{ actuallyAt ? actuallyAt.calendar() : '' }}
151
+                </v-flex>
152
+              </v-card>
153
+            </v-flex>
154
+          </v-layout>
155
+        </v-flex>
156
+      </v-layout>
157
+    </v-flex>
158
+
159
+    <!-- userDelay -->
160
+    <v-card v-if="(cancelled || arrival || departure)">
161
+      <v-container>
162
+        <v-layout row wrap>
163
+          <v-flex xs12>
164
+            <v-text-field
165
+              v-model="userDelay"
166
+              :label="$t('fluxPage.report.form.label.userDelay')"
167
+              type="tel"
168
+            />
169
+          </v-flex>
170
+        </v-layout>
171
+      </v-container>
172
+    </v-card>
173
+
174
+    <!-- submit button -->
175
+    <v-flex v-if="isValid" xs12>
176
+      <v-btn
177
+        class="black--text text--lighten-2"
178
+        color="primary"
179
+        block
180
+        @click="submit"
181
+      >
182
+        {{ id ? $t('fluxPage.report.form.submitExisting') : $t('fluxPage.report.form.submitNew') }}
183
+      </v-btn>
184
+    </v-flex>
185
+  </v-form>
186
+</template>
187
+
188
+<script>
189
+import { isoCountries } from '@/utils/countryCodes'
190
+import DatetimePicker from '@/components/picker/Datetime'
191
+import VehicleIcon from '@/components/VehicleIcon'
192
+
193
+const mapVehicleTabs = {
194
+  1: 0,
195
+  2: 1,
196
+  4: 2,
197
+  8: 3,
198
+  16: 4,
199
+  32: 5,
200
+  64: 6,
201
+  128: 7
202
+}
203
+
204
+export default {
205
+  components: {
206
+    DatetimePicker,
207
+    VehicleIcon
208
+  },
209
+
210
+  data() {
211
+    return {
212
+      info: true,
213
+      tab: 0,
214
+      tabs: [ 1, 2, 4, 8, 16, 32, 64, 128 ],
215
+
216
+      id: this.$store.state.report.form.id,
217
+      country: this.$store.state.report.form.country ? this.$store.state.report.form.country : this.$store.state.user.country,
218
+      city: this.$store.state.report.form.city ? this.$store.state.report.form.city : this.$store.state.user.city,
219
+      location: this.$store.state.report.form.location,
220
+      vehicle: this.$store.state.report.form.vehicle,
221
+      line: this.$store.state.report.form.line,
222
+      direction: this.$store.state.report.form.direction,
223
+      cancelled: this.$store.state.report.form.cancelled,
224
+      arrival: this.$store.state.report.form.arrival,
225
+      departure: this.$store.state.report.form.departure,
226
+      scheduledAt: this.$store.state.report.form.scheduledAt ? this.$moment(this.$store.state.report.form.scheduledAt) : this.$moment(),
227
+      actuallyAt: this.$store.state.report.form.actuallyAt ? this.$moment(this.$store.state.report.form.actuallyAt) : this.$moment(),
228
+      comment: this.$store.state.report.form.comment,
229
+      userDelay: this.$store.state.report.form.userDelay,
230
+
231
+      isoCountries: Object.keys(isoCountries).sort()
232
+    }
233
+  },
234
+
235
+  computed: {
236
+    vehicleLabel: function () {
237
+      return this.$t(`generic.vehicle.${this.tab === -1 ? '0' : this.tab === 0 ? '1' : 2 ** (this.tab)}`)
238
+    },
239
+
240
+    isValid: function () {
241
+      return (
242
+        this.country &&
243
+        this.city &&
244
+        this.location &&
245
+        this.line &&
246
+        this.direction &&
247
+          (
248
+            this.cancelled ||
249
+              (
250
+                (
251
+                  this.arrival ||
252
+                  this.departure
253
+                ) &&
254
+                this.scheduledAt &&
255
+                this.actuallyAt
256
+              )
257
+          )
258
+      )
259
+    },
260
+
261
+    cleanedForm: function () {
262
+      return {
263
+        id: this.id,
264
+        country: this.country,
265
+        city: this.city,
266
+        location: this.location,
267
+        vehicle: this.vehicle,
268
+        line: this.line,
269
+        direction: this.direction,
270
+        cancelled: this.cancelled,
271
+        arrival: this.arrival,
272
+        departure: this.departure,
273
+        scheduledAt: this.$moment(this.scheduledAt).toDate(),
274
+        actuallyAt: this.$moment(this.actuallyAt).toDate(),
275
+        comment: this.comment,
276
+        userDelay: parseInt(this.userDelay, 10)
277
+      }
278
+    }
279
+  },
280
+
281
+  watch: {
282
+    tab: function (newTab, oldTab) {
283
+      this.vehicle = newTab === -1 ? 0 : newTab === 0 ? 1 : 2 ** (newTab)
284
+    },
285
+
286
+    scheduledAt: function (newDate, oldDate) {
287
+      if (!this.userDelay) {
288
+        this.userDelay = this.timeDiff(newDate, this.actuallyAt)
289
+      }
290
+    },
291
+
292
+    actuallyAt: function (newDate, oldDate) {
293
+      if (!this.userDelay) {
294
+        this.userDelay = this.timeDiff(this.scheduledAt, newDate)
295
+      }
296
+    }
297
+  },
298
+
299
+  mounted: function () {
300
+    this.tab = mapVehicleTabs[this.vehicle]
301
+
302
+    if (this.id) {
303
+      this.$store.dispatch('stream/fetchFail', this.id)
304
+    }
305
+  },
306
+
307
+  methods: {
308
+    autoComplete: function (field, e) {
309
+      const params = {
310
+        target: field
311
+      }
312
+
313
+      for (const f of [ 'city', 'line', 'direction', 'location' ]) {
314
+        if (f === field) {
315
+          if (e.target && e.target.value) {
316
+            params[f] = e.target.value
317
+          }
318
+        } else if (this.cleanedForm[f]) {
319
+          params[f] = this.cleanedForm[f]
320
+        }
321
+      }
322
+
323
+      this.$store.dispatch('autoComplete/complete', params)
324
+    },
325
+
326
+    submit: function () {
327
+      this.$store.commit('report/updateFields', this.cleanedForm)
328
+      this.$store.dispatch('report/post')
329
+
330
+      // push the new report over to stream
331
+      this.$store.commit('stream/setFail', this.cleanedForm)
332
+      this.$router.push(this.localePath('flux-stream'))
333
+    },
334
+
335
+    timeDiff: function (a, b) {
336
+      return this.$moment.duration(this.$moment(b).startOf('minute').diff(this.$moment(a).startOf('minute'))).asMinutes()
337
+    }
338
+  }
339
+}
340
+</script>
341
+
342
+<style>
343
+.v-tabs__item {
344
+  padding: 0px;
345
+}
346
+</style>

+ 51
- 0
components/pages/flux/ReportForm/index.vue View File

@@ -0,0 +1,51 @@
1
+<template>
2
+  <v-container>
3
+    <v-layout justify-space-around>
4
+      <v-flex xs12 sm10 md8 lg6 xl4>
5
+        <v-toolbar>
6
+          <v-toolbar-title>
7
+            {{ $t('fluxPage.report.title') }}
8
+          </v-toolbar-title>
9
+          <v-spacer />
10
+          <v-btn
11
+            round
12
+            icon
13
+            @click="clickClose"
14
+          >
15
+            <v-icon>close</v-icon>
16
+          </v-btn>
17
+        </v-toolbar>
18
+        <v-card xs12 sm10 md9 lg6 xl4>
19
+          <v-container>
20
+            <v-layout justify-space-around>
21
+              <v-flex>
22
+                <Form :key="JSON.stringify($store.state.report.form)" />
23
+              </v-flex>
24
+            </v-layout>
25
+          </v-container>
26
+        </v-card>
27
+      </v-flex>
28
+    </v-layout>
29
+  </v-container>
30
+</template>
31
+
32
+<script>
33
+import Form from './Form'
34
+
35
+export default {
36
+  components: {
37
+    Form
38
+  },
39
+
40
+  methods: {
41
+    clickClose: function () {
42
+      this.$store.commit('report/reset')
43
+      return this.$router.push(this.localePath('flux-stream'))
44
+    }
45
+  }
46
+}
47
+</script>
48
+
49
+<style>
50
+
51
+</style>

+ 49
- 0
components/pages/flux/ReportNav/index.vue View File

@@ -0,0 +1,49 @@
1
+<template>
2
+  <v-layout>
3
+    <v-flex xs12>
4
+      <v-list>
5
+        <v-list-tile
6
+          v-for="(item, i) in items"
7
+          :key="i"
8
+          :to="item.to ? localePath(item.to) : ''"
9
+          router
10
+          exact
11
+          nuxt
12
+        >
13
+          <v-spacer v-if="item.divider" />
14
+          <v-list-tile-action v-else>
15
+            <v-icon>{{ item.icon }}</v-icon>
16
+          </v-list-tile-action>
17
+          <v-list-tile-content v-if="!item.spacer">
18
+            <v-list-tile-title v-text="item.title" />
19
+          </v-list-tile-content>
20
+        </v-list-tile>
21
+      </v-list>
22
+    </v-flex>
23
+  </v-layout>
24
+</template>
25
+
26
+<script>
27
+export default {
28
+  data() {
29
+    return {
30
+      items: [
31
+        {
32
+          icon: 'play_arrow',
33
+          title: this.$t('nav.stream'),
34
+          to: 'flux-stream'
35
+        },
36
+        {
37
+          icon: 'add',
38
+          title: this.$t('nav.report'),
39
+          to: 'flux-report'
40
+        }
41
+      ]
42
+    }
43
+  }
44
+}
45
+</script>
46
+
47
+<style>
48
+
49
+</style>

+ 4
- 2
components/pages/flux/Stream/Fail/index.vue View File

@@ -84,7 +84,8 @@ export default {
84 84
       cancelled: this.$store.state.stream.fail.cancelled,
85 85
       arrival: this.$store.state.stream.fail.arrival,
86 86
       departure: this.$store.state.stream.fail.departure,
87
-      comment: this.$store.state.stream.fail.comment
87
+      comment: this.$store.state.stream.fail.comment,
88
+      userDelay: this.$store.state.stream.fail.userDelay
88 89
     }
89 90
   },
90 91
 
@@ -130,8 +131,8 @@ export default {
130 131
 
131 132
   methods: {
132 133
     clickEdit: function () {
133
-      this.$router.push(this.localePath('flux-report'))
134 134
       this.$store.commit('report/updateFields', this.$store.state.stream.fail)
135
+      this.$router.push(this.localePath('flux-report'))
135 136
     },
136 137
 
137 138
     clickDelete: function () {
@@ -144,6 +145,7 @@ export default {
144 145
       }).then((res) => {
145 146
         if (res) {
146 147
           this.$store.dispatch('report/delete', this.id)
148
+          this.$store.commit('stream/setReports', this.$store.state.stream.reports.filter(report => report.id !== this.id))
147 149
           this.$store.commit('stream/setFail', null)
148 150
         }
149 151
       })

+ 24
- 6
components/pages/flux/Stream/Toolbar/index.vue View File

@@ -14,7 +14,6 @@
14 14
     <v-btn
15 15
       v-else
16 16
       :loading="this.$store.state.stream.loading"
17
-      :disabled="this.$store.state.stream.loading"
18 17
       flat
19 18
       round
20 19
       small
@@ -38,23 +37,42 @@
38 37
     >
39 38
       {{ $store.state.stream.filters.user ? $t('fluxPage.stream.title.user') : $t('fluxPage.stream.title.all') }}
40 39
     </v-toolbar-title>
40
+
41
+    <v-spacer />
42
+
43
+    <v-btn
44
+      v-if="!$store.state.stream.fail.id"
45
+      :loading="this.$store.state.stream.loading"
46
+      round
47
+      flat
48
+      icon
49
+      @click="clickAdd"
50
+    >
51
+      <v-icon>
52
+        add_alert
53
+      </v-icon>
54
+    </v-btn>
41 55
   </v-toolbar>
42 56
 </template>
43 57
 
44 58
 <script>
45 59
 export default {
46 60
   methods: {
47
-    updateStream: function () {
48
-      this.$store.dispatch('stream/fetch', {
49
-        fluxCtxField: 'scheduledAt',
50
-        pagination: this.$store.state.stream.pagination
51
-      })
61
+    clickAdd: function () {
62
+      this.$router.push(this.localePath('flux-report'))
52 63
     },
53 64
 
54 65
     toggleUserReportsOnly: function () {
55 66
       this.$store.commit('stream/setFilters', {
56 67
         user: !this.$store.state.stream.filters.user
57 68
       })
69
+      this.$store.dispatch('stream/fetchList', {
70
+        fluxCtxField: 'scheduledAt',
71
+        pagination: this.$store.state.stream.pagination
72
+      })
73
+    },
74
+
75
+    updateStream: function () {
58 76
       this.$store.dispatch('stream/fetch', {
59 77
         fluxCtxField: 'scheduledAt',
60 78
         pagination: this.$store.state.stream.pagination

+ 0
- 407
components/pages/flux/VerticalReport/index.vue View File

@@ -1,407 +0,0 @@
1
-<template>
2
-  <v-layout row wrap>
3
-    <v-flex xs12>
4
-      <v-toolbar>
5
-        <v-toolbar-title>
6
-          {{ title }}
7
-        </v-toolbar-title>
8
-      </v-toolbar>
9
-      <v-form ref="reportForm">
10
-        <v-container>
11
-          <v-layout>
12
-            <v-flex xs4 pa-1>
13
-              <v-autocomplete
14
-                v-model="country"
15
-                :items="isoCountries"
16
-                :label="$t('fluxPage.report.form.label.country')"
17
-                :value="country"
18
-                autofocus
19
-              />
20
-            </v-flex>
21
-            <v-flex xs8 pa-1>
22
-              <v-combobox
23
-                v-model="city"
24
-                :items="$store.state.autoComplete.city"
25
-                :label="$t('fluxPage.report.form.label.city')"
26
-                :value="city"
27
-                @keyup="autoComplete('city', $event)"
28
-              />
29
-            </v-flex>
30
-          </v-layout>
31
-
32
-          <v-layout>
33
-            <v-flex xs12 pa-1>
34
-              <v-combobox
35
-                v-model="location"
36
-                :items="$store.state.autoComplete.location"
37
-                :label="$t('fluxPage.report.form.label.location')"
38
-                @keyup="autoComplete('location', $event)"
39
-              />
40
-            </v-flex>
41
-          </v-layout>
42
-
43
-          <v-layout>
44
-            <v-flex xs4 pa-1>
45
-              <v-combobox
46
-                v-model="line"
47
-                :items="$store.state.autoComplete.line"
48
-                :label="$t('fluxPage.report.form.label.line')"
49
-                @keyup="autoComplete('line', $event)"
50
-              />
51
-            </v-flex>
52
-            <v-flex xs8 pa-1>
53
-              <v-combobox
54
-                v-model="direction"
55
-                :items="$store.state.autoComplete.direction"
56
-                :label="$t('fluxPage.report.form.label.direction')"
57
-                @keyup="autoComplete('direction', $event)"
58
-              />
59
-            </v-flex>
60
-          </v-layout>
61
-
62
-          <v-layout row wrap>
63
-            <v-flex>
64
-              <v-select
65
-                v-model="vehicle"
66
-                :label="$t('fluxPage.report.form.label.vehicle')"
67
-                :items="vehicles"
68
-                item-text="label"
69
-                item-value="id"
70
-              />
71
-            </v-flex>
72
-          </v-layout>
73
-
74
-          <v-layout>
75
-            <v-flex>
76
-              <DatetimePicker
77
-                :title="$t('fluxPage.report.form.label.scheduledAt')"
78
-                @datetimeSelected="scheduledAt = $event"
79
-              />
80
-            </v-flex>
81
-          </v-layout>
82
-
83
-          <v-layout>
84
-            <v-flex xs12>
85
-              <v-checkbox
86
-                v-model="cancelled"
87
-                :class="cancelledColor"
88
-                :label="$t('fluxPage.report.form.label.cancelled')"
89
-                color="primary"
90
-              >
91
-                {{ $t('generic.form.btn.cancelled') }}
92
-              </v-checkbox>
93
-            </v-flex>
94
-          </v-layout>
95
-
96
-          <v-layout v-if="!cancelled" row wrap>
97
-            <v-flex xs12>
98
-              <center>
99
-                <v-btn-toggle
100
-                  v-model="isArrivalDeparture"
101
-                  multiple
102
-                >
103
-                  <v-btn
104
-                    :class="cancelledColor"
105
-                  >
106
-                    {{ $t('fluxPage.report.form.label.arrival') }}
107
-                  </v-btn>
108
-                  <v-btn
109
-                    :class="cancelledColor"
110
-                  >
111
-                    {{ $t('fluxPage.report.form.label.departure') }}
112
-                  </v-btn>
113
-                </v-btn-toggle>
114
-              </center>
115
-            </v-flex>
116
-            <v-flex xs12 py-3>
117
-              <center>
118
-                <span v-if="isArrivalDeparture.includes(0) && isArrivalDeparture.includes(1)">
119
-                  {{ $t('fluxPage.report.form.label.arrival') }}
120
-                  &amp;
121
-                  {{ $t('fluxPage.report.form.label.departure') }}
122
-                </span>
123
-                <span v-else-if="isArrivalDeparture.includes(0)">
124
-                  {{ $t('fluxPage.report.form.label.arrival') }}
125
-                </span>
126
-                <span v-else-if="isArrivalDeparture.includes(1)">
127
-                  {{ $t('fluxPage.report.form.label.departure') }}
128
-                </span>
129
-                <span v-else class="red--text text---darken-1">
130
-                  {{ $t('generic.missingValue') }}
131
-                </span>
132
-              </center>
133
-            </v-flex>
134
-          </v-layout>
135
-
136
-          <v-layout v-if="!cancelled && isArrivalDeparture.length !== 0">
137
-            <v-flex>
138
-              <DatetimePicker
139
-                :title="$t('fluxPage.report.form.label.actuallyAt')"
140
-                :disabled="isArrivalDeparture.lenth === 0"
141
-                @datetimeSelected="actuallyAt = $event"
142
-              />
143
-            </v-flex>
144
-          </v-layout>
145
-
146
-          <v-layout>
147
-            <v-flex class="text-xs-right">
148
-              <v-btn
149
-                :disabled="$store.state.report.loading || !isValid()"
150
-                :loading="$store.state.report.loading"
151
-                color="primary"
152
-                class="yes"
153
-                block
154
-                @click="sendReport"
155
-              >
156
-                {{ saveBtnLabel }}
157
-              </v-btn>
158
-              <v-btn flat block @click="resetForm">
159
-                {{ $t('generic.form.btn.reset') }}
160
-              </v-btn>
161
-            </v-flex>
162
-          </v-layout>
163
-        </v-container>
164
-      </v-form>
165
-    </v-flex>
166
-  </v-layout>
167
-</template>
168
-
169
-<script>
170
-import DatetimePicker from '@/components/picker/datetime'
171
-import { isoCountries } from '@/utils/countryCodes'
172
-
173
-const moment = require('moment')
174
-
175
-export default {
176
-  components: {
177
-    DatetimePicker
178
-  },
179
-
180
-  data() {
181
-    return {
182
-      country: this.$store.state.user.country,
183
-      city: this.$store.state.user.city,
184
-      location: this.$store.state.report.form.location,
185
-      line: this.$store.state.report.form.line,
186
-      direction: this.$store.state.report.form.direction,
187
-      vehicle: this.$store.state.report.form.vehicle,
188
-      scheduledAt: new Date(),
189
-      actuallyAt: new Date(),
190
-      cancelled: false,
191
-      arrival: false,
192
-      departure: false,
193
-
194
-      isArrivalDeparture: [],
195
-      isoCountries: Object.keys(isoCountries),
196
-      vehicles: [
197
-        { id: 1, label: this.$t('vehicle.bus') },
198
-        { id: 2, label: this.$t('vehicle.train') },
199
-        { id: 4, label: this.$t('vehicle.subway') },
200
-        { id: 8, label: this.$t('vehicle.tram') },
201
-        { id: 16, label: this.$t('vehicle.lift') },
202
-        { id: 32, label: this.$t('vehicle.ship') },
203
-        { id: 64, label: this.$t('vehicle.airplane') },
204
-        { id: 128, label: this.$t('vehicle.rocket') }
205
-      ],
206
-
207
-      countryRules: [
208
-        v => !!v || this.$t('fluxPage.report.form.err.required'),
209
-        v => Object.keys(isoCountries).includes(v.toUpperCase()) || this.$t('unknownCountryCode')
210
-      ]
211
-    }
212
-  },
213
-
214
-  computed: {
215
-    title: function () {
216
-      if (!this.$store.state.report.form.id) {
217
-        return this.$t('fluxPage.report.title.new')
218
-      } else {
219
-        return this.$t('fluxPage.report.title.edit')
220
-      }
221
-    },
222
-
223
-    fields: function () {
224
-      const out = {
225
-        country: this.country,
226
-        city: this.city,
227
-        location: this.location,
228
-        line: this.line,
229
-        direction: this.direction,
230
-        vehicle: this.vehicle,
231
-        scheduledAt: this.scheduledAt ? moment(this.scheduledAt).startOf('minute').toDate() : '',
232
-        cancelled: this.cancelled,
233
-        actuallyAt: '',
234
-        arrival: false,
235
-        departure: false
236
-      }
237
-      if (!this.cancelled) {
238
-        out.actuallyAt = this.actuallyAt ? moment(this.actuallyAt).startOf('minute').toDate() : ''
239
-        out.arrival = [ ...this.isArrivalDeparture ].includes(0)
240
-        out.departure = [ ...this.isArrivalDeparture ].includes(1)
241
-      }
242
-      return out
243
-    },
244
-
245
-    vehicleText: function () {
246
-      switch (this.vehicle) {
247
-        case 1: { return this.$t('vehicle.bus') }
248
-        case 2: return this.$t('vehicle.train')
249
-        case 4: return this.$t('vehicle.subway')
250
-        case 8: return this.$t('vehicle.tram')
251
-        case 16: return this.$t('vehicle.lift')
252
-        case 32: return this.$t('vehicle.ship')
253
-        case 64: return this.$t('vehicle.airplane')
254
-        case 128: return this.$t('vehicle.rocket')
255
-        default: return this.$t('vehicle.none')
256
-      }
257
-    },
258
-
259
-    saveBtnLabel: function () {
260
-      if (this.$store.state.report.form.id) {
261
-        return this.$t('fluxPage.report.form.submitExisting')
262
-      } else {
263
-        return this.$t('fluxPage.report.form.submitNew')
264
-      }
265
-    },
266
-
267
-    cancelledColor: function () {
268
-      return this.isArrivalDeparture.length === 0 ? 'red--text text--darken-1' : ''
269
-    }
270
-  },
271
-
272
-  methods: {
273
-    resetForm: function () {
274
-      this.country = this.$store.state.user.country
275
-      this.city = this.$store.state.user.city
276
-      this.location = ''
277
-      this.line = ''
278
-      this.direction = ''
279
-      this.vehicle = 0
280
-      this.scheduledAt = ''
281
-      this.actuallyAt = ''
282
-      this.cancelled = false
283
-      this.arrival = false
284
-      this.departure = false
285
-      this.isArrivalDeparture = []
286
-
287
-      this.$store.commit('autoComplete/reset')
288
-    },
289
-
290
-    isValid: function () {
291
-      const fields = this.fields
292
-      if (
293
-        fields.country &&
294
-        fields.city &&
295
-        fields.location &&
296
-        fields.line &&
297
-        fields.direction &&
298
-        fields.scheduledAt
299
-      ) {
300
-        if (
301
-          fields.cancelled ||
302
-          (
303
-            (fields.arrival || fields.departure) &&
304
-            fields.actuallyAt
305
-          )
306
-        ) {
307
-          return true
308
-        }
309
-      }
310
-      return false
311
-    },
312
-
313
-    isSelectedVehicle: function (vId) {
314
-      return this.fields.vehicle === vId
315
-    },
316
-
317
-    selectVehicle: function (vId) {
318
-      this.vehicle = vId
319
-      this.goNextFluxReportFormPage()
320
-    },
321
-
322
-    sendReport: function () {
323
-      if (!this.isValid()) {
324
-        return
325
-      }
326
-
327
-      this.$store.commit('report/updateFields', this.fields)
328
-      this.$store.dispatch('report/post')
329
-        .then(() => {
330
-          this.resetForm()
331
-          this.$store.commit('report/reset')
332
-          this.$emit('reportSent')
333
-          this.$dialog.notify.success(this.$t('generic.message.reportSaved'), {
334
-            position: 'top-left'
335
-          })
336
-
337
-          if (this.$store.state.user.id) {
338
-            this.$store.dispatch('stream/user', this.$store.state.user.id)
339
-          } else {
340
-            this.$store.dispatch('stream/get')
341
-          }
342
-        })
343
-        .catch((err) => {
344
-          this.$store.commit('report/loaded')
345
-          if (err.code === 'ECONNABORTED') {
346
-            this.$dialog.notify.warning(this.$t('generic.errors.serverUnreachable'), {
347
-              position: 'top-left',
348
-              timeout: 10000
349
-            })
350
-          } else if (err.response.status === 422) {
351
-            this.$dialog.notify.error(this.$t('generic.errors.invalidReport'), {
352
-              position: 'top-left',
353
-              timeout: 10000
354
-            })
355
-          } else {
356
-            this.$dialog.notify.error(err, {
357
-              position: 'top-left',
358
-              timeout: 10000
359
-            })
360
-          }
361
-        })
362
-    },
363
-
364
-    filterCities: function (city) {
365
-      return this.$store.state.autoComplete.cities.filter(
366
-        e => `${e}`.toLowerCase().indexOf(city.toLowerCase()) === 0
367
-      )
368
-    },
369
-
370
-    filteredLocations: function (location) {
371
-      return this.$store.state.autoComplete.locations.filter(
372
-        e => `${e}`.toLowerCase().indexOf(location.toLowerCase()) === 0
373
-      )
374
-    },
375
-
376
-    filteredLines: function (line) {
377
-      return this.$store.state.autoComplete.lines.filter(
378
-        e => `${e}`.toLowerCase().indexOf(line.toLowerCase()) === 0
379
-      )
380
-    },
381
-
382
-    filteredDirections: function (direction) {
383
-      return this.$store.state.autoComplete.directions.filter(
384
-        e => `${e}`.toLowerCase().indexOf(direction.toLowerCase()) === 0
385
-      )
386
-    },
387
-
388
-    autoComplete: function (field, e) {
389
-      const params = {
390
-        target: field
391
-      }
392
-
393
-      for (const f of [ 'country', 'city', 'location', 'line', 'direction' ]) {
394
-        if (f === field) {
395
-          if (e.target && e.target.value) {
396
-            params[f] = e.target.value
397
-          }
398
-        } else if (this[f]) {
399
-          params[f] = this[f]
400
-        }
401
-      }
402
-
403
-      this.$store.dispatch('autoComplete/complete', params)
404
-    }
405
-  }
406
-}
407
-</script>

+ 105
- 0
components/picker/Datetime/index.vue View File

@@ -0,0 +1,105 @@
1
+<template>
2
+  <div>
3
+    <v-btn
4
+      block
5
+      @click="showDialog = true"
6
+    >
7
+      {{ title }}
8
+    </v-btn>
9
+
10
+    <v-dialog v-model="showDialog" width="640" persistent>
11
+      <v-card>
12
+        <v-card-actions>
13
+          <v-spacer />
14
+          <v-btn icon @click="closePicker">
15
+            <v-icon>
16
+              check
17
+            </v-icon>
18
+          </v-btn>
19
+        </v-card-actions>
20
+
21
+        <v-card-text>
22
+          <center>
23
+            <v-time-picker
24
+              v-model="selectedTime"
25
+              format="24hr"
26
+            />
27
+
28
+            <v-date-picker
29
+              v-model="selectedDate"
30
+            />
31
+          </center>
32
+        </v-card-text>
33
+
34
+        <v-card-actions>
35
+          <v-btn flat @click="setNow">
36
+            {{ $t('now') }}
37
+          </v-btn>
38
+          <v-btn block @click="closePicker">
39
+            {{ $t('next') }}
40
+          </v-btn>
41
+        </v-card-actions>
42
+      </v-card>
43
+    </v-dialog>
44
+  </div>
45
+</template>
46
+
47
+<script>
48
+export default {
49
+  props: {
50
+    title: {
51
+      type: String,
52
+      default: 'DatetimePicker'
53
+    },
54
+    timestamp: {
55
+      type: Object,
56
+      default: () => ({})
57
+    }
58
+  },
59
+
60
+  data() {
61
+    return {
62
+      selectedDate: this.timestamp ? this.$moment(this.timestamp).format('YYYY-MM-DD') : this.$moment().format('YYYY-MM-DD'),
63
+      selectedTime: this.timestamp ? this.$moment(this.timestamp).format('HH:mm') : this.$moment().format('HH:mm'),
64
+      showDialog: false
65
+    }
66
+  },
67
+
68
+  computed: {
69
+    dispayTime: function () {
70
+      if (this.selectedDate && this.selectedTime) {
71
+        return this.$moment(`${this.selectedDate} ${this.selectedTime}`, 'YYYY-MM-DD HH:mm')
72
+      }
73
+      return ''
74
+    }
75
+  },
76
+
77
+  methods: {
78
+    setDate: function (newDate) {
79
+      this.selectedDate = this.$moment(newDate).format('YYYY-MM-DD')
80
+    },
81
+    setTime: function (newTime) {
82
+      this.selectedTime = this.$moment(newTime).format('HH:mm')
83
+    },
84
+    setNow: function () {
85
+      const now = this.$moment()
86
+      this.setDate(now)
87
+      this.setTime(now)
88
+      this.emitTimestamp()
89
+    },
90
+    closePicker: function () {
91
+      this.showDialog = false
92
+      this.emitTimestamp()
93
+    },
94
+    emitTimestamp: function () {
95
+      this.$emit(
96
+        'selected', this.dispayTime
97
+      )
98
+    }
99
+  }
100
+}
101
+</script>
102
+
103
+<style>
104
+
105
+</style>

+ 0
- 140
components/picker/datetime/index.vue View File

@@ -1,140 +0,0 @@
1
-<template>
2
-  <div>
3
-    <v-card>
4
-      <v-toolbar>
5
-        {{ title }}
6
-      </v-toolbar>
7
-
8
-      <v-card-text v-if="timestamp">
9
-        <center>
10
-          {{ timestamp.calendar() }}
11
-        </center>
12
-      </v-card-text>
13
-
14
-      <v-card-actions>
15
-        <v-btn
16
-          v-if="!timestamp"
17
-          block
18
-          @click="setNow"
19
-        >
20
-          {{ $t('generic.form.btn.now') }}
21
-        </v-btn>
22
-        <v-btn
23
-          v-if="timestamp"
24
-          flat
25
-          @click="reset"
26
-        >
27
-          {{ $t('generic.form.btn.reset') }}
28
-        </v-btn>
29
-        <v-btn
30
-          v-if="timestamp"
31
-          block
32
-          @click="pickerVisible = true"
33
-        >
34
-          {{ $t('generic.form.btn.change') }}
35
-        </v-btn>
36
-      </v-card-actions>
37
-    </v-card>
38
-
39
-    <v-dialog v-model="pickerVisible" width="640" persistent>
40
-      <v-card>
41
-        <v-card-actions>
42
-          <v-spacer />
43
-          <v-btn icon @click="closePicker">
44
-            <v-icon>
45
-              check
46
-            </v-icon>
47
-          </v-btn>
48
-        </v-card-actions>
49
-
50
-        <v-card-text>
51
-          <center>
52
-            <v-date-picker
53
-              v-model="pickedDate"
54
-            />
55
-
56
-            <v-time-picker
57
-              v-model="pickedTime"
58
-              format="24hr"
59
-            />
60
-          </center>
61
-        </v-card-text>
62
-
63
-        <v-card-actions>
64
-          <v-btn flat @click="reset">
65
-            {{ $t('reset') }}
66
-          </v-btn>
67
-          <v-btn flat @click="setNow">
68
-            {{ $t('now') }}
69
-          </v-btn>
70
-          <v-btn block @click="closePicker">
71
-            {{ $t('next') }}
72
-          </v-btn>
73
-        </v-card-actions>
74
-      </v-card>
75
-    </v-dialog>
76
-  </div>
77
-</template>
78
-
79
-<script>
80
-const moment = require('moment')
81
-
82
-export default {
83
-  props: {
84
-    title: {
85
-      type: String,
86
-      default: ''
87
-    },
88
-    date: {
89
-      type: String,
90
-      default: ''
91
-    }
92
-  },
93
-
94
-  data() {
95
-    return {
96
-      pickedDate: this.date ? moment(this.date).format('YYYY-MM-DD') : '',
97
-      pickedTime: this.date ? moment(this.date).format('HH:mm') : '',
98
-      pickerVisible: false
99
-    }
100
-  },
101
-
102
-  computed: {
103
-    timestamp: function () {
104
-      if (this.pickedDate && this.pickedTime) {
105
-        return moment(`${this.pickedDate} ${this.pickedTime}`, 'YYYY-MM-DD HH:mm')
106
-      }
107
-      return ''
108
-    }
109
-  },
110
-
111
-  methods: {
112
-    setDate: function (newDate) {
113
-      this.pickedDate = moment(newDate).format('YYYY-MM-DD')
114
-    },
115
-    setTime: function (newTime) {
116
-      this.pickedTime = moment(newTime).format('HH:mm')
117
-    },
118
-    setNow: function () {
119
-      const now = moment()
120
-      this.setDate(now)
121
-      this.setTime(now)
122
-      this.emitTimestamp()
123
-    },
124
-    reset: function () {
125
-      this.pickedDate = null
126
-      this.pickedTime = null
127
-      this.emitTimestamp()
128
-    },
129
-    closePicker: function () {
130
-      this.pickerVisible = false
131
-      this.emitTimestamp()
132
-    },
133
-    emitTimestamp: function () {
134
-      this.$emit(
135
-        'datetimeSelected', this.timestamp
136
-      )
137
-    }
138
-  }
139
-}
140
-</script>

+ 25
- 7
lang/de.json View File

@@ -11,6 +11,17 @@
11 11
     "rocket": "Rakete"
12 12
   },
13 13
   "generic": {
14
+    "vehicle": {
15
+      "0": "-",
16
+      "1": "Bus",
17
+      "2": "Zug / S-Bahn",
18
+      "4": "U-Bahn",
19
+      "8": "Tram",
20
+      "16": "Aufzug",
21
+      "32": "Shiff",
22
+      "64": "Fugzeug",
23
+      "128": "Rakete"
24
+    },
14 25
     "errors": {
15 26
       "invalidReport": "Meldung ungültig! Bitte stelle sicher, dass alle Felder ausgefüllt sind.",
16 27
       "serverUnreachable": "Server nicht erreichbar, bitte versuche es einfach noch ein mal!"
@@ -40,13 +51,17 @@
40 51
       "youveAuthenticated": "Anmeldung erfolgreich.",
41 52
       "reallyDelete": "Soll die Meldung wirklich gelöscht werden?"
42 53
     },
43
-    "missingValue": "???"
54
+    "missingValue": "???",
55
+    "label": {
56
+      "yes": "Ja",
57
+      "no": "Nein"
58
+    }
44 59
   },
45 60
   "nav": {
46 61
     "home": "Home",
47 62
     "account": "Mein Konto",
48 63
     "login": "Anmelden",
49
-    "stream": "Live Ticker",
64
+    "stream": "Neuste Meldungen",
50 65
     "report": "Neue Meldung",
51 66
     "about": "Über uns",
52 67
     "privacy": "Datenschutzerklärung"
@@ -127,9 +142,11 @@
127 142
   },
128 143
   "fluxPage": {
129 144
     "report": {
130
-      "title": {
131
-        "new": "Neue Meldung",
132
-        "edit": "Editieren"
145
+      "title": "flux.fail Meldung",
146
+      "label": {
147
+        "vehicle": "Verkehrsmittel",
148
+        "cancelled": "Das Verkehrsmittel fällt aus",
149
+        "arrivalDeparture": "Es handelt sich um eine"
133 150
       },
134 151
       "step": {
135 152
         "where": "Wo",
@@ -149,7 +166,8 @@
149 166
           "actuallyAt": "Tatsächlich war es",
150 167
           "arrival": "Ankunft",
151 168
           "departure": "Abfahrt",
152
-          "cancelled": "fällt aus"
169
+          "cancelled": "fällt aus",
170
+          "userDelay": "Grund für eine zusätzliche Fahrzeit in Minuten"
153 171
         },
154 172
         "err": {
155 173
           "required": "Die ist ein Pflichtfeld."
@@ -162,7 +180,7 @@
162 180
     "stream": {
163 181
       "title": {
164 182
         "user": "Meine Meldungen",
165
-        "all": "Alle Meldungen"
183
+        "all": "Neuste Meldungen"
166 184
       },
167 185
       "deleteOptIn": {
168 186
         "title": "Meldung löschen?",

lang/us.json → lang/en.json View File

@@ -11,6 +11,17 @@
11 11
     "rocket": "Rocket"
12 12
   },
13 13
   "generic": {
14
+    "vehicle": {
15
+      "0": "-",
16
+      "1": "Bus",
17
+      "2": "Train",
18
+      "4": "Subway",
19
+      "8": "Tram",
20
+      "16": "Lift",
21
+      "32": "Ship",
22
+      "64": "Airplane",
23
+      "128": "Rocket"
24
+    },
14 25
     "errors": {
15 26
       "invalidReport": "Report invalid! Please check if you provided all required fields.",
16 27
       "serverUnreachable": "Server unreachable, please ty again!"
@@ -40,13 +51,17 @@
40 51
       "youveAuthenticated": "Login successful.",
41 52
       "reallyDelete": "Soll die Meldung wirklich gelöscht werden?"
42 53
     },
43
-    "missingValue": "???"
54
+    "missingValue": "???",
55
+    "label": {
56
+      "yes": "Yes",
57
+      "no": "No"
58
+    }
44 59
   },
45 60
   "nav": {
46 61
     "home": "Home",
47 62
     "account": "Profile",
48 63
     "login": "Login",
49
-    "stream": "Stream",
64
+    "stream": "Latest Reports",
50 65
     "report": "Create Report",
51 66
     "about": "About Us",
52 67
     "privacy": "Privacy Policy"
@@ -128,9 +143,11 @@
128 143
   },
129 144
   "fluxPage": {
130 145
     "report": {
131
-      "title": {
132
-        "new": "New Report",
133
-        "edit": "Edit Report"
146
+      "title": "Report flux.fail",
147
+      "label": {
148
+        "vehicle": "Vehicle type",
149
+        "cancelled": "The ride was cancelled",
150
+        "arrivalDeparture": "Report type"
134 151
       },
135 152
       "step": {
136 153
         "where": "Where",
@@ -150,7 +167,8 @@
150 167
           "actuallyAt": "Actually at",
151 168
           "arrival": "Arrival",
152 169
           "departure": "Departure",
153
-          "cancelled": "Cancelled"
170
+          "cancelled": "Cancelled",
171
+          "userDelay": "Cause for additional travel time in minutes"
154 172
         },
155 173
         "err": {
156 174
           "required": "field is required"
@@ -163,7 +181,7 @@
163 181
     "stream": {
164 182
       "title": {
165 183
         "user": "My Reports",
166
-        "all": "Stream"
184
+        "all": "Latest Reports"
167 185
       },
168 186
       "deleteOptIn": {
169 187
         "title": "Delete Delay?",

+ 3
- 10
layouts/default.vue View File

@@ -63,9 +63,7 @@
63 63
       fixed
64 64
       width="320px"
65 65
     >
66
-      <VerticalReport
67
-        @reportSent="rightDrawer = false"
68
-      />
66
+      <ReportNav />
69 67
     </v-navigation-drawer>
70 68
 
71 69
     <Footer
@@ -75,13 +73,13 @@
75 73
 </template>
76 74
 
77 75
 <script>
78
-import VerticalReport from '@/components/pages/flux/VerticalReport'
76
+import ReportNav from '@/components/pages/flux/ReportNav'
79 77
 import Footer from '@/components/Footer'
80 78
 
81 79
 export default {
82 80
   components: {
83 81
     Footer,
84
-    VerticalReport
82
+    ReportNav
85 83
   },
86 84
 
87 85
   data() {
@@ -108,11 +106,6 @@ export default {
108 106
           title: this.authTitle(),
109 107
           to: 'account'
110 108
         },
111
-        {
112
-          icon: 'play_arrow',
113
-          title: this.$t('nav.stream'),
114
-          to: 'flux-stream'
115
-        },
116 109
         { divider: true },
117 110
         {
118 111
           icon: 'verified_user',

+ 3
- 2
nuxt.config.js View File

@@ -52,14 +52,15 @@ module.exports = {
52 52
   modules: [
53 53
     '@nuxtjs/pwa',
54 54
     '@nuxtjs/axios',
55
+    ['@nuxtjs/moment', ['de']],
55 56
     'nuxt-webfontloader',
56 57
     ['nuxt-i18n', {
57
-      defaultLocale: 'us',
58
+      defaultLocale: 'en',
58 59
       baseUrl: APP_URL,
59 60
       lazy: true,
60 61
       langDir: 'lang/',
61 62
       locales: [
62
-        { name: 'United-States', code: 'us', iso: 'en_US', file: 'us.json' },
63
+        { name: 'English', code: 'en', iso: 'en_US', file: 'en.json' },
63 64
         { name: 'Deutsch', code: 'de', iso: 'de_DE', file: 'de.json' }
64 65
       ],
65 66
       detectBrowserLanguage: {

+ 22
- 0
package-lock.json View File

@@ -1670,6 +1670,15 @@
1670 1670
         "@nuxtjs/pwa-utils": "3.0.0-beta.16"
1671 1671
       }
1672 1672
     },
1673
+    "@nuxtjs/moment": {
1674
+      "version": "1.2.0",
1675
+      "resolved": "https://registry.npmjs.org/@nuxtjs/moment/-/moment-1.2.0.tgz",
1676
+      "integrity": "sha512-UgS7AEFx0G59umXvs23GTCUd9n/J9MqIEvw5iJ4RITnn8HJ+aXF2NCNs379nTKHMGBsgHpy6I+Pkpcsrt2+QVQ==",
1677
+      "requires": {
1678
+        "moment": "^2.24.0",
1679
+        "moment-locales-webpack-plugin": "^1.0.7"
1680
+      }
1681
+    },
1673 1682
     "@nuxtjs/proxy": {
1674 1683
       "version": "1.3.3",
1675 1684
       "resolved": "https://registry.npmjs.org/@nuxtjs/proxy/-/proxy-1.3.3.tgz",
@@ -7833,6 +7842,11 @@
7833 7842
       "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
7834 7843
       "dev": true
7835 7844
     },
7845
+    "lodash.difference": {
7846
+      "version": "4.5.0",
7847
+      "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
7848
+      "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
7849
+    },
7836 7850
     "lodash.flattendeep": {
7837 7851
       "version": "4.4.0",
7838 7852
       "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
@@ -8338,6 +8352,14 @@
8338 8352
       "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
8339 8353
       "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
8340 8354
     },
8355
+    "moment-locales-webpack-plugin": {
8356
+      "version": "1.0.7",
8357
+      "resolved": "https://registry.npmjs.org/moment-locales-webpack-plugin/-/moment-locales-webpack-plugin-1.0.7.tgz",
8358
+      "integrity": "sha512-KjYpaAhmuzGFZl6534FlZoK7QtW3vqlxd+A17W9DlgHJ5yhXANy7AZJl7iYtZpWjAfMTAWiVrQ7YDZdkFO6uRw==",
8359
+      "requires": {
8360
+        "lodash.difference": "^4.5.0"
8361
+      }
8362
+    },
8341 8363
     "move-concurrently": {
8342 8364
       "version": "1.0.1",
8343 8365
       "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",

+ 1
- 0
package.json View File

@@ -22,6 +22,7 @@
22 22
     "@fortawesome/free-solid-svg-icons": "^5.9.0",
23 23
     "@mdi/font": "^3.6.95",
24 24
     "@nuxtjs/axios": "^5.5.3",
25
+    "@nuxtjs/moment": "^1.2.0",
25 26
     "@nuxtjs/pwa": "^3.0.0-beta.16",
26 27
     "body-parser": "^1.19.0",
27 28
     "consola": "^2.7.1",

+ 3
- 3
pages/flux/report/index.vue View File

@@ -18,15 +18,15 @@
18 18
       </v-flex>
19 19
     </v-layout>
20 20
   </v-container>
21
-  <FluxReport v-else />
21
+  <ReportForm v-else />
22 22
 </template>
23 23
 
24 24
 <script>
25
-import FluxReport from '@/components/pages/flux/Report'
25
+import ReportForm from '@/components/pages/flux/ReportForm'
26 26
 
27 27
 export default {
28 28
   components: {
29
-    FluxReport
29
+    ReportForm
30 30
   }
31 31
 }
32 32
 </script>

+ 30
- 0
server/migrations/20190802173146_my-delay.js View File

@@ -0,0 +1,30 @@
1
+/** my-delay
2
+ *
3
+ * Finally, allow the user to also define their experienced delay.
4
+ *
5
+ */
6
+const moment = require('moment')
7
+
8
+exports.up = knex => knex.schema.table('flux', (t) => {
9
+  t.integer('userDelay').default(-1).notNullable()
10
+})
11
+  .then(() => knex('flux')
12
+    .select()
13
+    .then(rows => Promise.all(rows.map((row) => {
14
+      const query = knex('flux')
15
+        .where('id', row.id)
16
+
17
+      if (row.cancelled) {
18
+        return query.update({
19
+          userDelay: 0
20
+        })
21
+      }
22
+      return query.update({
23
+        userDelay: moment.duration(moment(row.actuallyAt).diff(moment(row.scheduledAt))).minutes()
24
+      })
25
+    })))
26
+  )
27
+
28
+exports.down = knex => knex.schema.table('flux', (t) => {
29
+  t.dropColumn('userDelay')
30
+})

+ 1
- 1
server/routes/flux.js View File

@@ -110,7 +110,7 @@ exports.save = [
110 110
           // New entry, save
111 111
           return db(fluxTable)
112 112
             .insert({
113
-              ...req.boindexdy,
113
+              ...req.body,
114 114
               user: req.user.id,
115 115
               createdAt: moment.utc().toDate(),
116 116
               updatedAt: moment.utc().toDate()

+ 5
- 1
server/schema/flux.json View File

@@ -69,6 +69,9 @@
69 69
     },
70 70
     "comment": {
71 71
       "type": "string"
72
+    },
73
+    "userDelay": {
74
+      "type": "integer"
72 75
     }
73 76
   },
74 77
   "required": [
@@ -80,7 +83,8 @@
80 83
     "vehicle",
81 84
     "line",
82 85
     "direction",
83
-    "scheduledAt"
86
+    "scheduledAt",
87
+    "userDelay"
84 88
   ],
85 89
   "additionalProperties": false
86 90
 }

+ 1
- 1
server/utils/paramParser.js View File

@@ -29,7 +29,7 @@ function parseSearchFilters (filters) {
29 29
       city: !!filters.city ? filters.city : null,
30 30
       line: !!filters.line ? filters.line : null,
31 31
       direction: !!filters.direction ? filters.direction : null,
32
-      location: !!filters.direction ? filters.direction : null
32
+      location: !!filters.location ? filters.location : null
33 33
     }
34 34
 
35 35
     resolve({

+ 0
- 3
store/autoComplete.js View File

@@ -83,9 +83,6 @@ export const actions = {
83 83
             commit('setLocation', res)
84 84
             break
85 85
           }
86
-          case 'scheduledAt': {
87
-            break
88
-          }
89 86
           default: {
90 87
             console.log('!!!BUG!!!', 'can not dispatch for unknown target field', targetField) // eslint-disable-line
91 88
           }

+ 31
- 33
store/report.js View File

@@ -1,7 +1,7 @@
1
-export const state = () => ({
2
-  loading: false,
3
-  windowStep: 1,
4
-  form: {
1
+const moment = require('moment')
2
+
3
+function defaultReport() {
4
+  return {
5 5
     id: '',
6 6
     country: '',
7 7
     city: '',
@@ -14,29 +14,22 @@ export const state = () => ({
14 14
     cancelled: false,
15 15
     arrival: false,
16 16
     departure: false,
17
-    comment: ''
17
+    comment: '',
18
+    userDelay: 0
18 19
   }
20
+}
21
+
22
+export const state = () => ({
23
+  loading: false,
24
+  windowStep: 1,
25
+  form: defaultReport()
19 26
 })
20 27
 
21 28
 export const mutations = {
22 29
   reset(state) {
23 30
     state.loading = false
24 31
     state.windowStep = 1
25
-    state.form = {
26
-      id: '',
27
-      country: '',
28
-      city: '',
29
-      location: '',
30
-      line: '',
31
-      direction: '',
32
-      vehicle: 0,
33
-      scheduledAt: '',
34
-      actuallyAt: '',
35
-      cancelled: false,
36
-      arrival: false,
37
-      departure: false,
38
-      comment: ''
39
-    }
32
+    state.form = defaultReport()
40 33
   },
41 34
 
42 35
   loading(state) {
@@ -56,7 +49,7 @@ export const mutations = {
56 49
   },
57 50
 
58 51
   updateFields(state, fields) {
59
-    state.form = Object.assign(state.form, fields)
52
+    state.form = { ...fields }
60 53
   },
61 54
 
62 55
   edit(state, report) {
@@ -69,7 +62,7 @@ export const actions = {
69 62
   post({ state, commit }) {
70 63
     commit('loading')
71 64
 
72
-    // create a JSON body that passes the server-side validation
65
+    // create a JSON body that passes the server-side validation...
73 66
     const fluxReport = {
74 67
       id: state.form.id,
75 68
       country: state.form.country,
@@ -83,13 +76,18 @@ export const actions = {
83 76
       cancelled: state.form.cancelled,
84 77
       arrival: state.form.arrival,
85 78
       departure: state.form.departure,
86
-      comment: state.form.comment
79
+      comment: state.form.comment,
80
+      userDelay: state.form.userDelay
87 81
     }
88 82
 
89
-    // remove all keys with empty values
90
-    Object.keys(fluxReport).forEach(key => (!fluxReport[key]) && delete fluxReport[key])
83
+    // ... by removing all keys with empty values ...
84
+    Object.keys(fluxReport).forEach(key => (fluxReport[key] === null || fluxReport[key] === undefined) && delete fluxReport[key])
85
+
86
+    // normalize timestamps to a granularity of minutes
87
+    fluxReport.scheduledAt = moment(fluxReport.scheduledAt).startOf('minute').toDate()
88
+    fluxReport.actuallyAt = moment(fluxReport.actuallyAt).startOf('minute').toDate()
91 89
 
92
-    // post request
90
+    // ... and then post request to backend
93 91
     return this.$axios.post(`/api/flux/fail/report`, fluxReport)
94 92
       .then(() => {
95 93
         return commit('reset')
@@ -101,17 +99,17 @@ export const actions = {
101 99
     return this.$axios.delete(`/api/flux/fail/report/${id}`)
102 100
       .then((res) => {
103 101
         if (res.status !== 204) {
104
-          console.log(res) // eslint-disable-line
102
+          const err = new Error('unable to delete report')
103
+          err.httpCode = res.status
104
+          throw err
105 105
         }
106
+        return res
106 107
       })
107
-      .then(() => {
108
-        commit('reset')
108
+      .then((res) => {
109 109
         return new Promise((resolve, reject) => {
110
-          resolve()
110
+          commit('reset')
111
+          resolve(res)
111 112
         })
112 113
       })
113
-      .catch((err) => {
114
-        console.log(err) // eslint-disable-line
115
-      })
116 114
   }
117 115
 }

+ 3
- 2
store/stream.js View File

@@ -11,7 +11,8 @@ function defaultFail() {
11 11
     cancelled: false,
12 12
     arrival: false,
13 13
     departure: false,
14
-    comment: ''
14
+    comment: '',
15
+    userDelay: 0
15 16
   }
16 17
 }
17 18
 
@@ -105,7 +106,7 @@ export const actions = {
105 106
           err.httpCode = 666
106 107
           throw err
107 108
         }
108
-        commit('setFail', res.data)
109
+        return commit('setFail', res.data)
109 110
       })
110 111
   }
111 112
 }

Loading…
Cancel
Save