Added ability to edit records
This commit is contained in:
parent
a2473bd06d
commit
3ad1041a7b
13 changed files with 352 additions and 99 deletions
|
@ -193,11 +193,28 @@ function updateEndRecord($user_id, $params)
|
||||||
// Get start instance to calculate duration
|
// Get start instance to calculate duration
|
||||||
$start_time = getTimeRecord($user_id, $record_id)["start_time"];
|
$start_time = getTimeRecord($user_id, $record_id)["start_time"];
|
||||||
|
|
||||||
$data = [
|
$record = [
|
||||||
|
"record_id" => $record_id,
|
||||||
"end_time" => $params->get("end_time"),
|
"end_time" => $params->get("end_time"),
|
||||||
"duration" => calcDuration($start_time, $params->get("end_time"))
|
"duration" => calcDuration($start_time, $params->get("end_time"))
|
||||||
];
|
];
|
||||||
|
|
||||||
|
updateRecord($user_id, $record);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRecord($user_id, $record)
|
||||||
|
{
|
||||||
|
$record_id = $record["record_id"];
|
||||||
|
|
||||||
|
// Update given parameters
|
||||||
|
$data = [];
|
||||||
|
$props = ["end_time", "start_time", "duration", "project_id"];
|
||||||
|
foreach ($props as $p) {
|
||||||
|
if (array_key_exists ($p, $record)) {
|
||||||
|
$data[$p] = $record[$p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$db = new DbOperations();
|
$db = new DbOperations();
|
||||||
$db->update("time_records", $data);
|
$db->update("time_records", $data);
|
||||||
$db->where("user_id", Comparison::EQUAL, $user_id);
|
$db->where("user_id", Comparison::EQUAL, $user_id);
|
||||||
|
|
29
public/api/updateRecord.php
Normal file
29
public/api/updateRecord.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once(__DIR__ . "/services/apiBranch.inc.php");
|
||||||
|
require_once(__DIR__ . "/services/responses.inc.php");
|
||||||
|
require_once(__DIR__ . "/services/jugglDbApi.inc.php");
|
||||||
|
|
||||||
|
class UpdateRecordBranch extends ApiBranch
|
||||||
|
{
|
||||||
|
function get(ParamCleaner $params)
|
||||||
|
{
|
||||||
|
respondStatus(405);
|
||||||
|
}
|
||||||
|
|
||||||
|
function post(ParamCleaner $params)
|
||||||
|
{
|
||||||
|
$user_id = $params->get("user_id");
|
||||||
|
|
||||||
|
if ($params->exists(["record"]) == false) {
|
||||||
|
respondStatus(400, "Missing parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRecord($user_id, $params->get("record"));
|
||||||
|
|
||||||
|
respondStatus(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$branch = new UpdateRecordBranch();
|
||||||
|
$branch->execute();
|
|
@ -28,40 +28,75 @@
|
||||||
<b-form-timepicker
|
<b-form-timepicker
|
||||||
id="starttime"
|
id="starttime"
|
||||||
v-model="times.start.time"
|
v-model="times.start.time"
|
||||||
|
show-seconds
|
||||||
required
|
required
|
||||||
placeholder="Choose a start time"
|
placeholder="Choose a start time"
|
||||||
dark
|
dark
|
||||||
>
|
>
|
||||||
</b-form-timepicker>
|
</b-form-timepicker>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
|
<b-form-checkbox id="running" v-model="times.finished" required dark>
|
||||||
|
Finished
|
||||||
|
</b-form-checkbox>
|
||||||
<b-form-group id="enddate-group" label="End date" label-for="enddate">
|
<b-form-group id="enddate-group" label="End date" label-for="enddate">
|
||||||
<b-form-datepicker
|
<b-form-datepicker
|
||||||
id="enddate"
|
id="enddate"
|
||||||
v-model="times.end.date"
|
v-model="times.end.date"
|
||||||
required
|
:required="times.finished"
|
||||||
|
:disabled="!times.finished"
|
||||||
placeholder="Choose an end date"
|
placeholder="Choose an end date"
|
||||||
:min="times.start.date"
|
:min="times.start.date"
|
||||||
dark
|
dark
|
||||||
>
|
>
|
||||||
</b-form-datepicker>
|
</b-form-datepicker>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
|
<b-form-group id="endtime-group" label="End time" label-for="endtime">
|
||||||
|
<b-form-timepicker
|
||||||
|
id="endtime"
|
||||||
|
v-model="times.end.time"
|
||||||
|
:required="times.finished"
|
||||||
|
:disabled="!times.finished"
|
||||||
|
show-seconds
|
||||||
|
placeholder="Choose an end time"
|
||||||
|
dark
|
||||||
|
>
|
||||||
|
</b-form-timepicker>
|
||||||
|
</b-form-group>
|
||||||
|
<b-form-group id="duration-group" label="Duration [seconds]" label-for="duration">
|
||||||
|
<b-form-input
|
||||||
|
id="duration"
|
||||||
|
v-model="times.duration"
|
||||||
|
type="number"
|
||||||
|
:required="times.finished"
|
||||||
|
:disabled="!times.finished"
|
||||||
|
placeholder="Choose a duration"
|
||||||
|
min="0"
|
||||||
|
dark
|
||||||
|
>
|
||||||
|
</b-form-input>
|
||||||
|
</b-form-group>
|
||||||
<b-form-invalid-feedback :state="!failed">
|
<b-form-invalid-feedback :state="!failed">
|
||||||
Something went wrong.
|
Something went wrong.
|
||||||
</b-form-invalid-feedback>
|
</b-form-invalid-feedback>
|
||||||
<b-button variant="primary" type="submit" class="right" :disabled="working">
|
<b-button variant="primary" type="submit" class="right" :disabled="working">
|
||||||
<b-spinner v-if="working" small />
|
<b-spinner v-if="working" small />
|
||||||
Save
|
{{ saveBtnText }}
|
||||||
|
</b-button>
|
||||||
|
<b-button variant="outline-secondary" @click="() => cancel()" class="left">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<b-button
|
||||||
|
class="left"
|
||||||
|
@click="() => deleteRecord()"
|
||||||
|
variant="outline-danger"
|
||||||
|
>
|
||||||
|
<b-icon class="icon-btn" icon="trash" />
|
||||||
</b-button>
|
</b-button>
|
||||||
<b-card class="mt-3" header="Form Data Result">
|
|
||||||
<pre class="m-0">{{ record }}</pre>
|
|
||||||
</b-card>
|
|
||||||
<b-card class="mt-3" header="Form Data Result">
|
|
||||||
<pre class="m-0">{{ times }}</pre>
|
|
||||||
</b-card>
|
|
||||||
</b-form>
|
</b-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { helperService } from "@/services/helper.service.js";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -75,14 +110,50 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
times: {
|
times: {
|
||||||
start: {},
|
start: { date: undefined, time: undefined },
|
||||||
end: {}
|
end: { date: undefined, time: undefined },
|
||||||
|
finished: false,
|
||||||
|
duration: 0
|
||||||
},
|
},
|
||||||
selectableProjects: [],
|
selectableProjects: [],
|
||||||
failed: false,
|
failed: false,
|
||||||
working: false
|
working: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
resultRecord: function() {
|
||||||
|
var resultRecord = {
|
||||||
|
record_id: this.record.record_id,
|
||||||
|
user_id: this.record.user_id,
|
||||||
|
project_id: this.record.project_id,
|
||||||
|
running: !this.times.finished,
|
||||||
|
start_time: this.times.start.date + " " + this.times.start.time,
|
||||||
|
end_time: this.times.end.date + " " + this.times.end.time,
|
||||||
|
duration: this.times.duration,
|
||||||
|
start_device_id: this.record.start_device_id // TODO: Remove at some time
|
||||||
|
};
|
||||||
|
|
||||||
|
if (resultRecord.running) {
|
||||||
|
resultRecord.end_time = null;
|
||||||
|
resultRecord.duration = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultRecord;
|
||||||
|
},
|
||||||
|
saveBtnText: function() {
|
||||||
|
var result = "Save";
|
||||||
|
|
||||||
|
if (this.resultRecord.running !== this.record.running) {
|
||||||
|
if (this.resultRecord.running) {
|
||||||
|
result += " and track";
|
||||||
|
} else {
|
||||||
|
result += " and finish";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
/**
|
||||||
* Submits the form. Assupmtion: Form is valid, based on required flags.
|
* Submits the form. Assupmtion: Form is valid, based on required flags.
|
||||||
|
@ -92,7 +163,21 @@ export default {
|
||||||
this.failed = false;
|
this.failed = false;
|
||||||
this.working = true;
|
this.working = true;
|
||||||
|
|
||||||
|
store.dispatch("updateRecord", this.resultRecord).then(() => {
|
||||||
|
this.working = false;
|
||||||
|
this.$emit("submit");
|
||||||
|
}).catch(() => {
|
||||||
|
this.failed = true;
|
||||||
|
this.working = false;
|
||||||
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
},
|
||||||
|
cancel: function() {
|
||||||
|
this.$emit("cancel");
|
||||||
|
},
|
||||||
|
deleteRecord: function() {
|
||||||
|
store.dispatch("removeRecord", this.record.record_id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: function() {
|
created: function() {
|
||||||
|
@ -104,6 +189,9 @@ export default {
|
||||||
this.selectableProjects = projects;
|
this.selectableProjects = projects;
|
||||||
|
|
||||||
// Load record times
|
// Load record times
|
||||||
|
this.times.finished = !this.record.running;
|
||||||
|
this.times.duration = Math.round(this.record.duration);
|
||||||
|
|
||||||
let dateAndTime = /([0-9-]*)\s([0-9:]*)/;
|
let dateAndTime = /([0-9-]*)\s([0-9:]*)/;
|
||||||
var startMatch = String(this.record.start_time).match(dateAndTime);
|
var startMatch = String(this.record.start_time).match(dateAndTime);
|
||||||
this.times.start.date = startMatch[1];
|
this.times.start.date = startMatch[1];
|
||||||
|
@ -115,7 +203,7 @@ export default {
|
||||||
this.times.end.time = endMatch[2];
|
this.times.end.time = endMatch[2];
|
||||||
} else {
|
} else {
|
||||||
this.times.end.date = new Date().toISOString();
|
this.times.end.date = new Date().toISOString();
|
||||||
this.times.end.time = new Date().toTimeString();
|
this.times.end.time = helperService.toISOTime(new Date());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -124,7 +212,9 @@ export default {
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
.left
|
.left
|
||||||
float: left !important
|
float: left !important
|
||||||
|
margin-right: 1rem
|
||||||
|
|
||||||
.right
|
.right
|
||||||
float: right !important
|
float: right !important
|
||||||
|
margin-left: 1rem
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -27,25 +27,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #cell(details)="data">
|
<template #cell(details)="data">
|
||||||
<b-button
|
<b-button size="sm" @click="data.toggleDetails" variant="outline-dark">
|
||||||
size="sm"
|
|
||||||
@click="() => detailsRecord(data.item.record_id)"
|
|
||||||
variant="outline-dark"
|
|
||||||
>
|
|
||||||
<b-icon class="icon-btn" icon="gear" />
|
<b-icon class="icon-btn" icon="gear" />
|
||||||
</b-button>
|
</b-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #cell(abort)="data">
|
|
||||||
<b-button
|
|
||||||
size="sm"
|
|
||||||
@click="() => abortRecord(data.item.record_id)"
|
|
||||||
variant="outline-primary"
|
|
||||||
>
|
|
||||||
<b-icon class="icon-btn" icon="x" />
|
|
||||||
</b-button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #cell(stop)="data">
|
<template #cell(stop)="data">
|
||||||
<b-button
|
<b-button
|
||||||
size="sm"
|
size="sm"
|
||||||
|
@ -55,15 +41,29 @@
|
||||||
<b-icon class="icon-btn" icon="check" />
|
<b-icon class="icon-btn" icon="check" />
|
||||||
</b-button>
|
</b-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #row-details="data">
|
||||||
|
<b-card>
|
||||||
|
<FormRecordDetails
|
||||||
|
:record="data.item"
|
||||||
|
@cancel="data.toggleDetails"
|
||||||
|
@submit="data.toggleDetails"
|
||||||
|
/>
|
||||||
|
</b-card>
|
||||||
|
</template>
|
||||||
</b-table>
|
</b-table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import FormRecordDetails from "@/components/forms/FormRecordDetails";
|
||||||
import { helperService } from "@/services/helper.service.js";
|
import { helperService } from "@/services/helper.service.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "JugglRecordsList",
|
name: "JugglRecordsList",
|
||||||
|
components: {
|
||||||
|
FormRecordDetails
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
records: {
|
records: {
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -72,7 +72,7 @@ export default {
|
||||||
sortDesc: {
|
sortDesc: {
|
||||||
required: false,
|
required: false,
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: true
|
||||||
},
|
},
|
||||||
running: {
|
running: {
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -102,10 +102,6 @@ export default {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
runningFields: [
|
runningFields: [
|
||||||
{
|
|
||||||
key: "abort",
|
|
||||||
label: "Abort"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: "stop",
|
key: "stop",
|
||||||
label: "Stop"
|
label: "Stop"
|
||||||
|
@ -141,12 +137,8 @@ export default {
|
||||||
stopRecord: function(id) {
|
stopRecord: function(id) {
|
||||||
store.dispatch("endRecord", id);
|
store.dispatch("endRecord", id);
|
||||||
},
|
},
|
||||||
abortRecord: function(id) {
|
|
||||||
store.dispatch("removeRecord", id);
|
|
||||||
},
|
|
||||||
detailsRecord: function(id) {
|
detailsRecord: function(id) {
|
||||||
return id;
|
this.$router.push("/record/" + id);
|
||||||
// this.$router.push("/record/" + id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@ import VueRouter from "vue-router";
|
||||||
import store from "../store";
|
import store from "../store";
|
||||||
import Login from "../views/Login.vue";
|
import Login from "../views/Login.vue";
|
||||||
import Home from "../views/Home.vue";
|
import Home from "../views/Home.vue";
|
||||||
|
import History from "../views/History.vue";
|
||||||
import RecordDetails from "../views/RecordDetails.vue";
|
import RecordDetails from "../views/RecordDetails.vue";
|
||||||
|
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
|
@ -17,6 +18,12 @@ const routes = [
|
||||||
name: "Login",
|
name: "Login",
|
||||||
component: Login
|
component: Login
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/history",
|
||||||
|
name: "History",
|
||||||
|
component: History,
|
||||||
|
beforeEnter: requireAuth
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/home",
|
path: "/home",
|
||||||
name: "Home",
|
name: "Home",
|
||||||
|
@ -49,6 +56,8 @@ const router = new VueRouter({
|
||||||
* Checks authentication before proceeding
|
* Checks authentication before proceeding
|
||||||
*/
|
*/
|
||||||
function requireAuth(to, from, next) {
|
function requireAuth(to, from, next) {
|
||||||
|
store.dispatch("loadSavedLogin");
|
||||||
|
|
||||||
if (!store.getters.isLoggedIn) {
|
if (!store.getters.isLoggedIn) {
|
||||||
next({
|
next({
|
||||||
path: "/login",
|
path: "/login",
|
||||||
|
|
|
@ -10,11 +10,11 @@ import store from "@/store";
|
||||||
* Returns promises.
|
* Returns promises.
|
||||||
*/
|
*/
|
||||||
export const apiService = {
|
export const apiService = {
|
||||||
get(resource, options) {
|
get(resource, options = {}) {
|
||||||
return this.getApi().get(resource, options);
|
return this.getApi().get(resource, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
post(resource, json, options) {
|
post(resource, json = {}, options = {}) {
|
||||||
return this.getApi().post(
|
return this.getApi().post(
|
||||||
resource,
|
resource,
|
||||||
{ ...this.getDefaultJson(), ...json },
|
{ ...this.getDefaultJson(), ...json },
|
||||||
|
@ -22,7 +22,7 @@ export const apiService = {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
put(resource, json, options) {
|
put(resource, json = {}, options = {}) {
|
||||||
return this.getApi().put(
|
return this.getApi().put(
|
||||||
resource,
|
resource,
|
||||||
{ ...this.getDefaultJson(), ...json },
|
{ ...this.getDefaultJson(), ...json },
|
||||||
|
|
|
@ -136,5 +136,15 @@ export const helperService = {
|
||||||
timestamp = days + ":" + timestamp;
|
timestamp = days + ":" + timestamp;
|
||||||
}
|
}
|
||||||
return timestamp;
|
return timestamp;
|
||||||
|
},
|
||||||
|
|
||||||
|
toISOTime(dateTime) {
|
||||||
|
return (
|
||||||
|
dateTime.getHours() +
|
||||||
|
":" +
|
||||||
|
dateTime.getMinutes() +
|
||||||
|
":" +
|
||||||
|
dateTime.getSeconds()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,14 +54,14 @@ export const jugglService = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getRecords({ limit = undefined, finished = undefined }) {
|
getRecords(options = {}) {
|
||||||
var payload = {};
|
var payload = {};
|
||||||
|
|
||||||
if (limit !== undefined && limit > 0) {
|
if (options.limit !== undefined && options.limit > 0) {
|
||||||
payload.limit = limit;
|
payload.limit = options.limit;
|
||||||
}
|
}
|
||||||
if (finished !== undefined) {
|
if (options.finished !== undefined) {
|
||||||
payload.finished = finished;
|
payload.finished = options.finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
return apiService.post("/getRecords.php", payload).then(r => {
|
return apiService.post("/getRecords.php", payload).then(r => {
|
||||||
|
@ -72,6 +72,19 @@ export const jugglService = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateRecord(record) {
|
||||||
|
var payload = {
|
||||||
|
record: record
|
||||||
|
};
|
||||||
|
|
||||||
|
return apiService.post("/updateRecord.php", payload).then(r => {
|
||||||
|
return {
|
||||||
|
data: processRecords(r.data),
|
||||||
|
msg: ""
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getRunningRecords() {
|
getRunningRecords() {
|
||||||
return apiService.post("/getRunningRecords.php").then(r => {
|
return apiService.post("/getRunningRecords.php").then(r => {
|
||||||
return {
|
return {
|
||||||
|
@ -105,7 +118,7 @@ export const jugglService = {
|
||||||
})
|
})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
return {
|
return {
|
||||||
data: r.data,
|
data: processRecords(r.data),
|
||||||
msg: ""
|
msg: ""
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -120,7 +133,7 @@ export const jugglService = {
|
||||||
})
|
})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
return {
|
return {
|
||||||
data: r.data,
|
data: processRecords(r.data),
|
||||||
msg: ""
|
msg: ""
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import Vuex from "vuex";
|
import Vuex from "vuex";
|
||||||
import { juggl } from "./modules/juggl";
|
import { juggl } from "./modules/juggl";
|
||||||
import createPersistedState from "vuex-persistedstate";
|
// TODO: Remove dependency from packages
|
||||||
|
// import createPersistedState from "vuex-persistedstate";
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
|
@ -9,9 +10,9 @@ export default new Vuex.Store({
|
||||||
modules: {
|
modules: {
|
||||||
juggl
|
juggl
|
||||||
},
|
},
|
||||||
plugins: [
|
// plugins: [
|
||||||
createPersistedState({
|
// createPersistedState({
|
||||||
storage: window.sessionStorage
|
// storage: window.localStorage
|
||||||
})
|
// })
|
||||||
]
|
// ]
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,15 +22,6 @@ export const juggl = {
|
||||||
setRecords(state, records) {
|
setRecords(state, records) {
|
||||||
state.records = records;
|
state.records = records;
|
||||||
},
|
},
|
||||||
usingFinishedRecords(state, using) {
|
|
||||||
state.usingFinishedRecords = using;
|
|
||||||
},
|
|
||||||
usingRunningRecords(state, using) {
|
|
||||||
state.usingRunningRecords = using;
|
|
||||||
},
|
|
||||||
usingProjects(state, using) {
|
|
||||||
state.usingProjects = using;
|
|
||||||
},
|
|
||||||
setRecordsLimit(state, limit) {
|
setRecordsLimit(state, limit) {
|
||||||
state.recordsLimit = limit;
|
state.recordsLimit = limit;
|
||||||
},
|
},
|
||||||
|
@ -103,13 +94,17 @@ export const juggl = {
|
||||||
return Object.values(getters.records).find(
|
return Object.values(getters.records).find(
|
||||||
record => record.record_id === id
|
record => record.record_id === id
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
getRecordsExceptId: (state, getters) => id => {
|
||||||
|
return Object.values(getters.records).filter(
|
||||||
|
record => record.record_id !== id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
loadProjects({ commit }) {
|
loadProjects({ commit }) {
|
||||||
return jugglService.getProjects().then(r => {
|
return jugglService.getProjects().then(r => {
|
||||||
commit("setProjects", r.data.projects);
|
commit("setProjects", r.data.projects);
|
||||||
commit("usingProjects", true);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadUser({ commit }) {
|
loadUser({ commit }) {
|
||||||
|
@ -124,13 +119,14 @@ export const juggl = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadRecords({ commit, state }, { limit, finished }) {
|
loadRecords({ commit, state }, { limit, finished }) {
|
||||||
|
if (limit !== undefined) {
|
||||||
commit("setRecordsLimit", limit);
|
commit("setRecordsLimit", limit);
|
||||||
return jugglService
|
}
|
||||||
.getRecords({ limit: state.recordsLimit, finished: finished })
|
|
||||||
.then(r => {
|
var payload = { limit: state.recordsLimit, finished: finished };
|
||||||
|
|
||||||
|
return jugglService.getRecords(payload).then(r => {
|
||||||
commit("setRecords", r.data.records);
|
commit("setRecords", r.data.records);
|
||||||
commit("usingFinishedRecords", true);
|
|
||||||
commit("usingRunningRecords", true);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadRunningRecords({ commit, getters }) {
|
loadRunningRecords({ commit, getters }) {
|
||||||
|
@ -140,7 +136,6 @@ export const juggl = {
|
||||||
...r.data.records
|
...r.data.records
|
||||||
};
|
};
|
||||||
commit("setRecords", allRecords);
|
commit("setRecords", allRecords);
|
||||||
commit("usingRunningRecords", true);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
login({ commit, getters }, { userId, apiKey }) {
|
login({ commit, getters }, { userId, apiKey }) {
|
||||||
|
@ -169,14 +164,20 @@ export const juggl = {
|
||||||
commit("setUser", undefined);
|
commit("setUser", undefined);
|
||||||
commit("logout");
|
commit("logout");
|
||||||
},
|
},
|
||||||
endRecord(context, recordId) {
|
endRecord({ getters }, recordId) {
|
||||||
|
if (recordId === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return jugglService
|
return jugglService
|
||||||
.endRecord(recordId)
|
.endRecord(recordId)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.dispatch("updateState");
|
// TODO: Return ended record from API
|
||||||
|
var record = getters.getRecordById(recordId);
|
||||||
|
record.running = false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -187,45 +188,40 @@ export const juggl = {
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.dispatch("updateState");
|
this.dispatch("loadProjects");
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
startRecord(context, projectId) {
|
startRecord(context, projectId) {
|
||||||
|
if (projectId === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return jugglService
|
return jugglService
|
||||||
.startRecord(projectId)
|
.startRecord(projectId)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.dispatch("updateState");
|
this.dispatch("loadRunningRecords");
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
removeRecord(context, recordId) {
|
removeRecord({ commit, getters }, recordId) {
|
||||||
|
if (recordId === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return jugglService
|
return jugglService
|
||||||
.removeRecord(recordId)
|
.removeRecord(recordId)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.dispatch("updateState");
|
commit("setRecords", getters.getRecordsExceptId(recordId));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateState({ state }) {
|
|
||||||
if (state.usingProjects) {
|
|
||||||
this.dispatch("loadProjects");
|
|
||||||
}
|
|
||||||
if (state.usingRunningRecords && state.usingFinishedRecords) {
|
|
||||||
this.dispatch("loadRecords");
|
|
||||||
} else if (state.usingRunningRecords) {
|
|
||||||
this.dispatch("loadRunningRecords");
|
|
||||||
}
|
|
||||||
if (state.user === undefined) {
|
|
||||||
this.dispatch("loadUser");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
loadSavedLogin({ commit }) {
|
loadSavedLogin({ commit }) {
|
||||||
var userId = localStorage.getItem("userId");
|
var userId = localStorage.getItem("userId");
|
||||||
var apiKey = localStorage.getItem("apiKey");
|
var apiKey = localStorage.getItem("apiKey");
|
||||||
|
@ -235,6 +231,27 @@ export const juggl = {
|
||||||
}
|
}
|
||||||
|
|
||||||
commit("login", { apiKey: apiKey, userId: userId });
|
commit("login", { apiKey: apiKey, userId: userId });
|
||||||
|
},
|
||||||
|
removeLocalRecords({ commit }) {
|
||||||
|
commit("setRecords", []);
|
||||||
|
},
|
||||||
|
updateRecord({ commit, getters }, record) {
|
||||||
|
if (record.record_id === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jugglService
|
||||||
|
.updateRecord(record)
|
||||||
|
.catch(() => {
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// TODO: Return updated record from API
|
||||||
|
var records = getters.getRecordsExceptId(record.record_id);
|
||||||
|
records.push(record);
|
||||||
|
commit("setRecords", records);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,10 +9,11 @@ $secondary: $grey
|
||||||
$red: $primary
|
$red: $primary
|
||||||
|
|
||||||
$background-primary: #222
|
$background-primary: #222
|
||||||
$background-secondary: #000
|
$background-secondary: #191919
|
||||||
// $background-gradient: linear-gradient(165deg, $background-primary 65%, $background-secondary 100%)
|
// $background-gradient: linear-gradient(165deg, $background-primary 65%, $background-secondary 100%)
|
||||||
|
|
||||||
$font-primary: $white
|
$font-primary: $white
|
||||||
|
$font-inactive: $grey
|
||||||
$font-secondary: $primary
|
$font-secondary: $primary
|
||||||
$font-inverted: $black
|
$font-inverted: $black
|
||||||
$font-link: $secondary
|
$font-link: $secondary
|
||||||
|
@ -54,6 +55,9 @@ body
|
||||||
a
|
a
|
||||||
color: $font-link !important
|
color: $font-link !important
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color: $background-primary !important
|
||||||
|
|
||||||
.b-dropdown, .dropdown
|
.b-dropdown, .dropdown
|
||||||
border-radius: 0
|
border-radius: 0
|
||||||
background: #0000
|
background: #0000
|
||||||
|
@ -110,9 +114,9 @@ header.b-calendar-grid-caption, .b-calendar-grid-weekdays *, header.b-calendar-h
|
||||||
.b-form-datepicker label:not(.text-muted), .b-form-timepicker label:not(.text-muted)
|
.b-form-datepicker label:not(.text-muted), .b-form-timepicker label:not(.text-muted)
|
||||||
color: $font-primary !important
|
color: $font-primary !important
|
||||||
|
|
||||||
input:disabled
|
input:disabled, .b-form-timepicker[aria-disabled=true], .b-form-datepicker[aria-disabled=true]
|
||||||
color: $font-secondary !important
|
color: $font-inactive !important
|
||||||
border-color: $grey !important
|
border-color: $background-primary !important
|
||||||
|
|
||||||
input
|
input
|
||||||
color: $font-primary !important
|
color: $font-primary !important
|
||||||
|
@ -123,6 +127,13 @@ select
|
||||||
background-color: $background-primary !important
|
background-color: $background-primary !important
|
||||||
color: $font-primary !important
|
color: $font-primary !important
|
||||||
|
|
||||||
|
div.card
|
||||||
|
background-color: $background-secondary
|
||||||
|
border-color: $white
|
||||||
|
|
||||||
|
pre
|
||||||
|
color: $font-primary !important
|
||||||
|
|
||||||
|
|
||||||
// Import Bootstrap and BootstrapVue source SCSS files
|
// Import Bootstrap and BootstrapVue source SCSS files
|
||||||
@import '~bootstrap/scss/bootstrap.scss'
|
@import '~bootstrap/scss/bootstrap.scss'
|
||||||
|
|
57
src/views/History.vue
Normal file
57
src/views/History.vue
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<template>
|
||||||
|
<LayoutNavbarPrivate>
|
||||||
|
<section>
|
||||||
|
<h2 class="center">History</h2>
|
||||||
|
<div class="center">
|
||||||
|
<b-spinner v-if="working"></b-spinner>
|
||||||
|
</div>
|
||||||
|
<JugglRecordsList :records="finishedRecords" v-if="!working" />
|
||||||
|
</section>
|
||||||
|
</LayoutNavbarPrivate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import LayoutNavbarPrivate from "@/components/layout/LayoutNavbarPrivate";
|
||||||
|
import JugglRecordsList from "@/components/juggl/JugglRecordsList";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Home",
|
||||||
|
data: () => {
|
||||||
|
return {
|
||||||
|
working: true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
LayoutNavbarPrivate,
|
||||||
|
JugglRecordsList
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
finishedRecords: () => {
|
||||||
|
return store.getters.finishedRecords;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created: function() {
|
||||||
|
store.dispatch("loadProjects");
|
||||||
|
store
|
||||||
|
.dispatch("loadRecords", { limit: 0, finished: true })
|
||||||
|
.then(() => {
|
||||||
|
this.working = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.working = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
beforeDestroy: function() {
|
||||||
|
store.dispatch("removeLocalRecords");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="sass">
|
||||||
|
.center
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
section
|
||||||
|
margin-bottom: 4rem
|
||||||
|
</style>
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<LayoutNavbarPrivate>
|
<LayoutNavbarPrivate>
|
||||||
<section v-if="runningRecords.length > 0">
|
<section v-if="runningRecords.length > 0">
|
||||||
<h2 class="center">Tracking</h2>
|
<h2 class="center bold">Tracking</h2>
|
||||||
<JugglRecordsList :records="runningRecords" running />
|
<JugglRecordsList :records="runningRecords" running />
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2 class="center">Projects</h2>
|
<h2 class="center bold">Projects</h2>
|
||||||
<div v-if="finishedProjects.length > 0">
|
<div v-if="finishedProjects.length > 0">
|
||||||
<JugglProjectsPanel :projects="finishedProjects" />
|
<JugglProjectsPanel :projects="finishedProjects" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,8 +14,13 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section v-if="finishedRecords.length > 0">
|
<section v-if="finishedRecords.length > 0">
|
||||||
<h2 class="center">Finished</h2>
|
<h2 class="center bold">History</h2>
|
||||||
<JugglRecordsList :records="finishedRecords" />
|
<JugglRecordsList :records="finishedRecords" />
|
||||||
|
<div class="center">
|
||||||
|
<b-button to="/history" variant="outline-secondary" center
|
||||||
|
>Show all</b-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</LayoutNavbarPrivate>
|
</LayoutNavbarPrivate>
|
||||||
</template>
|
</template>
|
||||||
|
@ -49,7 +54,7 @@ export default {
|
||||||
created: () => {
|
created: () => {
|
||||||
store.dispatch("loadProjects");
|
store.dispatch("loadProjects");
|
||||||
store.dispatch("loadRunningRecords");
|
store.dispatch("loadRunningRecords");
|
||||||
store.dispatch("loadRecords", { limit: 25, finished: true });
|
store.dispatch("loadRecords", { limit: 10, finished: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -57,6 +62,8 @@ export default {
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
.center
|
.center
|
||||||
text-align: center
|
text-align: center
|
||||||
|
|
||||||
|
.bold
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
|
|
||||||
section
|
section
|
||||||
|
|
Loading…
Reference in a new issue