diff --git a/public/api/addTagToRecord.php b/public/api/addTagToRecord.php index c035013..12f8ea4 100644 --- a/public/api/addTagToRecord.php +++ b/public/api/addTagToRecord.php @@ -13,6 +13,7 @@ class AddTagToRecordBranch extends ApiBranch function post(ParamCleaner $params) { + // !! Security issue: Could combined arbitrary tag and record, not restricted by user $record_id = $params->get("record_id"); $tag_id = $params->get("tag_id"); diff --git a/public/api/removeTagFromRecord.php b/public/api/removeTagFromRecord.php index 43bf099..b16c4c6 100644 --- a/public/api/removeTagFromRecord.php +++ b/public/api/removeTagFromRecord.php @@ -13,6 +13,7 @@ class RemoveTagFromRecordBranch extends ApiBranch function post(ParamCleaner $params) { + // !! Security issue: Could combined arbitrary tag and record, not restricted by user $record_id = $params->get("record_id"); $tag_id = $params->get("tag_id"); diff --git a/public/api/services/jsonBuilder.inc.php b/public/api/services/jsonBuilder.inc.php index e729006..044c122 100644 --- a/public/api/services/jsonBuilder.inc.php +++ b/public/api/services/jsonBuilder.inc.php @@ -27,6 +27,8 @@ class JsonBuilder "duration" => "", "user_id" => "", "project_id" => "", + "running" => "", + "tags" => "", "start_device_id" => "" ); diff --git a/src/components/forms/FormTagAdd.vue b/src/components/forms/FormTagAdd.vue new file mode 100644 index 0000000..a1ea9bc --- /dev/null +++ b/src/components/forms/FormTagAdd.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/src/components/juggl/JugglRecordsList.vue b/src/components/juggl/JugglRecordsList.vue index f74ca32..9584a83 100644 --- a/src/components/juggl/JugglRecordsList.vue +++ b/src/components/juggl/JugglRecordsList.vue @@ -26,8 +26,16 @@ {{ getDurationTimestamp(data.item.duration) }} + + @@ -55,14 +63,16 @@ + + diff --git a/src/services/juggl.service.js b/src/services/juggl.service.js index a3114fb..a111243 100644 --- a/src/services/juggl.service.js +++ b/src/services/juggl.service.js @@ -28,6 +28,15 @@ export const jugglService = { }); }, + getTags() { + return apiService.post("/getRecordTags.php").then(r => { + return { + data: r.data, + msg: "" + }; + }); + }, + getRecord(recordId) { return apiService .post("/getRecord.php", { @@ -109,6 +118,47 @@ export const jugglService = { }); }, + addTag(name) { + return apiService + .post("/addRecordTag.php", { + tag_name: name + }) + .then(r => { + return { + data: r.data, + msg: "" + }; + }); + }, + + addTagToRecord(tagId, recordId) { + return apiService + .post("/addTagToRecord.php", { + tag_id: tagId, + record_id: recordId + }) + .then(r => { + return { + data: r.data, + msg: "" + }; + }); + }, + + removeTagFromRecord(tagId, recordId) { + return apiService + .post("/removeTagFromRecord.php", { + tag_id: tagId, + record_id: recordId + }) + .then(r => { + return { + data: r.data, + msg: "" + }; + }); + }, + startRecord(projectId, startTime = null) { if (startTime == null) startTime = new Date(); return apiService @@ -142,11 +192,11 @@ export const jugglService = { function processRecords(data) { Object.values(data.records).forEach(rec => { - rec.running = rec.end_time === null; - if (rec.running) { rec.duration = helperService.calcDurationInSeconds(rec.start_time); } + + rec.tags = Object.values(rec.tags); }); return data; } diff --git a/src/store/modules/juggl.js b/src/store/modules/juggl.js index dbf06fd..1cfa4e6 100644 --- a/src/store/modules/juggl.js +++ b/src/store/modules/juggl.js @@ -3,8 +3,9 @@ import { jugglService } from "@/services/juggl.service.js"; export const juggl = { state: { apiUrl: "https://juggl.giller.dev/api", - projects: {}, - records: {}, + projects: [], + records: [], + tags: [], user: undefined, auth: undefined, recordsLimit: 0 @@ -16,6 +17,9 @@ export const juggl = { setRecords(state, records) { state.records = records; }, + setTags(state, tags) { + state.tags = tags; + }, setRecordsLimit(state, limit) { state.recordsLimit = limit; }, @@ -46,6 +50,7 @@ export const juggl = { isLoggedIn: state => !!state.auth, records: state => state.records, projects: state => state.projects, + tags: state => state.tags, projectIds: state => { var projectIds = []; Object.values(state.projects).forEach(project => { @@ -84,6 +89,9 @@ export const juggl = { project => project.project_id === id ); }, + getTagById: (state, getters) => id => { + return Object.values(getters.tags).find(tag => tag.record_tag_id === id); + }, getRecordById: (state, getters) => id => { return Object.values(getters.records).find( record => record.record_id === id @@ -101,6 +109,11 @@ export const juggl = { commit("setProjects", r.data.projects); }); }, + loadTags({ commit }) { + return jugglService.getTags().then(r => { + commit("setTags", r.data.record_tags); + }); + }, loadUser({ commit }) { return jugglService .getUser() @@ -196,6 +209,39 @@ export const juggl = { return true; }); }, + addTag(context, { name }) { + return jugglService + .addTag(name) + .catch(() => { + return false; + }) + .then(() => { + this.dispatch("loadTags"); + return true; + }); + }, + addTagToRecord(context, { tagId, recordId }) { + return jugglService + .addTagToRecord(tagId, recordId) + .catch(() => { + return false; + }) + .then(() => { + // TODO: Manualy add tag + return true; + }); + }, + removeTagFromRecord(context, { tagId, recordId }) { + return jugglService + .removeTagFromRecord(tagId, recordId) + .catch(() => { + return false; + }) + .then(() => { + // TODO: Manualy remove tag + return true; + }); + }, startRecord(context, projectId) { if (projectId === undefined) { return false; diff --git a/src/style/theme.sass b/src/style/theme.sass index adae8a3..6d42c18 100644 --- a/src/style/theme.sass +++ b/src/style/theme.sass @@ -62,9 +62,12 @@ a border-radius: 0 background: #0000 -.dropdown-menu - background-color: $background-primary !important - border: 1px solid $primary !important +.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^="bottom"] > .arrow::after + border-bottom-color: $white !important + +.dropdown-menu, .b-popover + background-color: $background-secondary !important + border: 1px solid $white !important color: $primary .b-time *, @@ -134,6 +137,9 @@ div.card pre color: $font-primary !important +hr + border-color: $white !important + // Import Bootstrap and BootstrapVue source SCSS files @import '~bootstrap/scss/bootstrap.scss' diff --git a/src/views/History.vue b/src/views/History.vue index 6a4c164..784757c 100644 --- a/src/views/History.vue +++ b/src/views/History.vue @@ -1,9 +1,8 @@