diff --git a/README.md b/README.md index 0583566..db16a0e 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,29 @@ parties agree with me. ![VoteLog Screenshot](screenshot.png) (those are not my votes, at least not all of them...) -## Setup +## Build Docker images + +Build in your native architecture as follows. + +```bash +docker -t votelog . +``` -### Build and run Development WebDAV server +Build an aarch64 image, run the following (may need some dependencies). +```bash +docker buildx build --platform linux/arm64 -t votelog . ``` + +## Setup + +### WebDAV server + +Build and run Development server with username "cat" and password "dog". Add +`VUE_APP_WEBDAV_URI="http://localhost:8080"` into `.env.local` to make this the +default value on the login page. + +```bash docker build -t webdav-dev dev/webdav/ docker-compose -f dev/webdav/docker-compose.yml up -d ``` @@ -45,3 +63,8 @@ npm run build ``` npm run lint ``` + +# Assets + + - [Beth Ellen Font](https://fonts.google.com/specimen/Beth+Ellen) is used for some Icons (e.g. 2x.svg) + - [Unifraktur Cook](https://fonts.google.com/specimen/UnifrakturCook) used fon some Icons (e.g. nein.svg) diff --git a/package-lock.json b/package-lock.json index 4bd4eb8..ff1479c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "VoteLog", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "VoteLog", - "version": "0.1.0", + "version": "0.1.1", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.1", diff --git a/src/assets/4x.svg b/src/assets/4x.svg index fd0fe40..97d6c4c 100644 --- a/src/assets/4x.svg +++ b/src/assets/4x.svg @@ -7,7 +7,7 @@ viewBox="0 0 85.195831 58.994766" version="1.1" id="svg32163" - inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" + inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)" sodipodi:docname="4x.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" @@ -28,14 +28,16 @@ fit-margin-right="0" fit-margin-bottom="0" inkscape:zoom="1.28" - inkscape:cx="216.79688" + inkscape:cx="217.1875" inkscape:cy="234.76562" - inkscape:window-width="2554" - inkscape:window-height="1375" + inkscape:window-width="2129" + inkscape:window-height="1133" inkscape:window-x="0" - inkscape:window-y="32" - inkscape:window-maximized="1" - inkscape:current-layer="text3459" /> + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="text3459" + inkscape:showpageshadow="2" + inkscape:deskcolor="#d1d1d1" /> - 4x + transform="scale(0.95150393,1.0509678)" + aria-label="4x" /> + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="text113379" + inkscape:showpageshadow="2" + inkscape:deskcolor="#d1d1d1" /> + style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect35482);display:inline;fill:#000000;fill-opacity:1;stroke:none" /> + style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect3734);display:inline;fill:#000000;fill-opacity:1;stroke:none" /> - - - + style="font-size:42.3333px;font-family:UnifrakturCook;-inkscape-font-specification:UnifrakturCook;stroke-width:0.0700042" + d="m 72.408889,98.922427 2.418455,-3.017902 6.593908,4.774895 -0.620117,2.43913 -0.268717,6.65592 q 0,0.062 0,0.12403 0,1.01286 0.620116,1.67431 0.620117,0.64079 1.405598,0.97152 l -4.134111,5.04362 q -1.529621,-1.1989 -2.356444,-2.12907 -0.806151,-0.93018 -0.806151,-3.82406 0,-0.2067 0,-0.43408 l 0.144693,-7.64811 q 0,-0.86816 -2.645831,-2.89388 l -0.826822,1.30225 -0.392741,7.81347 q 0,0.062 0,0.12403 0,1.01286 0.620117,1.67431 0.620117,0.64079 1.426268,0.97152 l -4.134111,5.04362 -4.361488,-3.53467 q 1.178222,-1.42627 1.178222,-2.85254 l 0.165364,-9.38443 q 0,-0.99219 -1.529621,-1.984379 l 3.121254,-3.927406 z m 21.600732,18.665523 -9.901197,-3.94808 q 0.558105,-3.10058 0.558105,-6.44922 0,-3.3693 -0.640787,-7.02799 l 7.131342,-4.196123 v -0.06201 l 0.04134,0.02067 h 0.02067 l 8.040846,5.767083 -8.371575,8.80566 q 0.847493,1.01286 1.88102,1.11621 1.033528,0.10336 1.694986,0.10336 0.682129,0 1.591633,-0.062 0.930175,-0.0827 2.273761,-0.12403 -1.095539,1.52962 -2.170408,3.03857 -1.074869,1.50895 -2.149738,3.01791 z m -3.431313,-16.10237 q -1.198892,0 -1.198892,3.26595 0,1.86035 0.620117,3.7207 0,0.18603 0.06201,0.31006 l 3.32796,-3.514 q 0.3514,-0.37207 0.3514,-0.74414 0,-0.37207 -0.37207,-0.95085 -0.3514,-0.57877 -0.72347,-0.95084 -1.302245,-1.13688 -2.067055,-1.13688 z m 9.694492,-1.653649 3.12126,-3.927406 4.6302,2.170409 q -0.24805,0.847493 -0.53743,1.653645 -0.28939,0.806151 -0.28939,1.653641 l -0.28939,8.39225 q 0,0.10336 0,0.18604 0,0.97152 0.62012,1.6123 0.62011,0.64079 1.42627,0.97152 l -4.13412,5.04362 -4.38215,-3.53467 q 1.19889,-1.42627 1.19889,-2.85254 l 0.14469,-9.38443 q 0,-0.99219 -1.50895,-1.984379 z m 0.62012,-4.113441 1.65364,-8.16487 6.13916,2.459797 z m 16.8465,3.203937 2.41846,-3.017902 6.5939,4.774895 -0.62011,2.43913 -0.26872,6.65592 q 0,0.062 0,0.12403 0,1.01286 0.62012,1.67431 0.62011,0.64079 1.40559,0.97152 l -4.13411,5.04362 q -1.52962,-1.1989 -2.35644,-2.12907 -0.80615,-0.93018 -0.80615,-3.82406 0,-0.2067 0,-0.43408 l 0.14469,-7.64811 q 0,-0.86816 -2.64583,-2.89388 l -0.82682,1.30225 -0.39274,7.81347 q 0,0.062 0,0.12403 0,1.01286 0.62011,1.67431 0.62012,0.64079 1.42627,0.97152 l -4.13411,5.04362 -4.36149,-3.53467 q 1.17823,-1.42627 1.17823,-2.85254 l 0.16536,-9.38443 q 0,-0.99219 -1.52962,-1.984379 l 3.12125,-3.927406 z" + id="text1" + aria-label="nein" /> diff --git a/src/assets/ja.svg b/src/assets/yes.svg similarity index 62% rename from src/assets/ja.svg rename to src/assets/yes.svg index e1852df..468b861 100644 --- a/src/assets/ja.svg +++ b/src/assets/yes.svg @@ -7,7 +7,7 @@ viewBox="0 0 85.195831 58.994766" version="1.1" id="svg32163" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" + inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)" sodipodi:docname="ja.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" @@ -28,14 +28,16 @@ fit-margin-right="0" fit-margin-bottom="0" inkscape:zoom="2" - inkscape:cx="165.75" - inkscape:cy="27.25" - inkscape:window-width="2554" - inkscape:window-height="1375" + inkscape:cx="173" + inkscape:cy="139.25" + inkscape:window-width="2129" + inkscape:window-height="1133" inkscape:window-x="0" - inkscape:window-y="32" - inkscape:window-maximized="1" - inkscape:current-layer="layer3" /> + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="text113379" + inkscape:showpageshadow="2" + inkscape:deskcolor="#d1d1d1" /> + style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect35482);display:inline;fill:#000000;fill-opacity:1;stroke:none" /> + style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect3734);display:inline;fill:#000000;fill-opacity:1;stroke:none" /> - + style="font-size:42.3333px;font-family:UnifrakturCook;-inkscape-font-specification:UnifrakturCook;stroke-width:0.0700042" + d="m 83.178245,99.831941 3.121254,-3.927406 4.630205,2.170408 q -0.227376,0.847493 -0.496094,1.653645 -0.248046,0.806152 -0.39274,2.170412 -0.144694,1.34358 -0.144694,3.20393 0,1.83968 -0.04134,3.61735 -0.04134,1.77767 -0.992187,5.84977 -0.950846,4.0721 -4.113441,11.82356 -0.661458,-0.64079 -2.191079,-1.01286 1.343586,-4.85758 1.674315,-9.9012 0.330729,-5.04361 0.413411,-10.06656 l 0.06201,-3.59668 q 0,-0.99218 -1.529621,-1.984369 z m 0.620116,-4.113441 1.653645,-8.16487 6.180497,2.459796 z m 8.826328,6.82128 8.061521,-6.552563 9.13638,4.278803 q -1.6123,1.4056 -1.63297,3.1626 l -0.0827,6.84195 q 0,0.7028 0.72347,1.59164 0.74414,0.88883 1.40559,1.42626 l -4.17545,4.29948 -3.20393,-3.28662 q -1.15756,0.93018 -2.33578,1.71566 -1.17822,0.76481 -2.108395,1.6123 -3.100583,-1.15755 -5.787756,-3.57601 0.764811,-0.97151 0.888834,-2.83186 l -0.06201,-6.24251 q 0,-0.0827 0,-0.16536 0,-1.21957 -0.826822,-2.27377 z m 7.090001,-1.59163 q -1.198892,1.79834 -1.198892,3.20394 v 5.06428 q 0,1.5503 0.558105,2.21175 0.475423,0.68213 0.868163,1.21957 0.413414,0.53743 1.302244,0.78548 l 1.69499,-1.21957 v -9.50845 z" + id="text1" + aria-label="ja" /> diff --git a/src/components/AnswerCard.vue b/src/components/AnswerCard.vue new file mode 100644 index 0000000..e8e3930 --- /dev/null +++ b/src/components/AnswerCard.vue @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/src/components/App.vue b/src/components/App.vue index 45e100a..286809e 100644 --- a/src/components/App.vue +++ b/src/components/App.vue @@ -1,30 +1,35 @@ \ No newline at end of file diff --git a/src/components/Index.vue b/src/components/Index.vue index 9b03117..d90ccc1 100644 --- a/src/components/Index.vue +++ b/src/components/Index.vue @@ -4,18 +4,14 @@ diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue index 6a98508..5c895dd 100644 --- a/src/components/LoginForm.vue +++ b/src/components/LoginForm.vue @@ -7,26 +7,12 @@ Anmeldung fehlgeschlagen
-
- server: {{ defaultWebDav }} 
- - use different server - -
- -
+
@@ -66,12 +52,10 @@ export default { data: function () { return { - webDav: undefined, - defaultWebDav: process.env.VUE_APP_WEBDAV_URI, + webDav: process.env.VUE_APP_WEBDAV_URI, userName: undefined, password: undefined, loginFailed: false, - customWebDav: false, }; }, computed: { @@ -82,7 +66,7 @@ export default { methods: { async login() { const payload = { - webDav: this.customWebDav ? this.webDav : this.defaultWebDav, + webDav: this.webDav, userName: this.userName, password: this.password, }; diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index c6ad0e6..399a09c 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -4,12 +4,12 @@ -
-
-

Deine Stimme

-
-
- - Bearbeiten - +
+

+ Deine Stimme + + + +

-
Stimme:
-
- - - - +
Stimme:
+
+
-
Gewichtung:
+
Gewichtung:
0
-
{{ userVote?.importance || 1 }}x
+
+ +
-
Begründung:
-
+
Begründung:
+
{{ userVote?.reasoning }} @@ -110,15 +95,19 @@ + + diff --git a/src/components/SvgIcon.vue b/src/components/SvgIcon.vue deleted file mode 100644 index 40f84c6..0000000 --- a/src/components/SvgIcon.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/src/components/VotesTable.vue b/src/components/VotesTable.vue index 5d46dff..00934a9 100644 --- a/src/components/VotesTable.vue +++ b/src/components/VotesTable.vue @@ -51,33 +51,15 @@
-
- -
-
-
+
{ - if (!subject.categories) { - return false; - } - for (var i = 0; i < subject.categories.length; i++) { - let subject_str = subject.categories[i].join('###'); - if (subject_str.startsWith(this.filter)) { - return true; - } - } - return false; - }); - }, loggedIn() { return this.$store.getters.isLoggedIn(); }, - orderedSubjects() { - return [...this.subjects].sort((a, b) => { - if (a.date.getTime() == b.date.getTime()) { - return a.name > b.name; - } - a.date < b.date; - }); - }, votesChanged() { return this.$store.state.votesChanged; }, @@ -177,29 +116,9 @@ export default { } return subjects; }, - toggleCategory() { - if (this.category) { - this.$router.push({ name: 'votesTable' }); - } else { - this.$router.push({ name: 'votesTableCategory' }); - } - }, - formatCategory(category) { - let arr = category.split('###'); - let prefix = '\xa0'.repeat((arr.length - 1) * 5); - return prefix + arr[arr.length - 1]; - }, userVote(subject_id) { return this.$store.getters.getUserVote(subject_id); }, - edit(subject) { - this.editSubject = subject; - this.editUserVote = this.userVote(subject.id); - }, - finishEdit() { - this.editSubject = undefined; - this.editUserVote = undefined; - }, }, }; diff --git a/src/components/VotesTableSubject.vue b/src/components/VotesTableSubject.vue index 83339cb..e669e26 100644 --- a/src/components/VotesTableSubject.vue +++ b/src/components/VotesTableSubject.vue @@ -112,10 +112,10 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import { agreementValue, Answer } from '../Answer.js'; import Novote from '@/assets/novote.svg'; -import Ja from '@/assets/ja.svg'; +import Ja from '@/assets/yes.svg'; import Abstention from '@/assets/abstention.svg'; import ImportanceSymbol from '@/components/ImportanceSymbol.vue'; -import Nein from '@/assets/nein.svg'; +import Nein from '@/assets/no.svg'; import T0x from '@/assets/0x.svg'; import T1x from '@/assets/1x.svg'; import T2x from '@/assets/2x.svg'; @@ -162,15 +162,14 @@ export default { }, methods: { showDetails() { - if (this.loggedIn) { - this.$router.push({ - name: 'showSubject', - params: { - term_hash: this.term_hash, - subject_id: this.subject.id, - }, - }); - } + console.log('showDatails'); + this.$router.push({ + name: 'showSubject', + params: { + term_hash: this.term_hash, + subject_id: this.subject.id, + }, + }); }, classAgreement(userVote, otherVote) { if (otherVote == undefined || userVote == undefined) { diff --git a/src/icons.js b/src/icons.js index f408162..de9b5a9 100644 --- a/src/icons.js +++ b/src/icons.js @@ -5,6 +5,7 @@ import { faCircle, faEye, faCog, + faCircleInfo, faTimes, faQuestion, faHandshake, @@ -24,6 +25,9 @@ import { faAngleDown, faAngleUp, faAngleDoubleUp, + faRightToBracket, + faRightFromBracket, + faPenToSquare, } from '@fortawesome/free-solid-svg-icons'; library.add(faDiceThree); @@ -31,6 +35,7 @@ library.add(faCheck); library.add(faCircle); library.add(faEye); library.add(faCog); +library.add(faCircleInfo); library.add(faTimes); library.add(faQuestion); library.add(faHandshake); @@ -50,3 +55,6 @@ library.add(faGripLinesVertical); library.add(faAngleDown); library.add(faAngleUp); library.add(faAngleDoubleUp); +library.add(faRightToBracket); +library.add(faRightFromBracket); +library.add(faPenToSquare); diff --git a/src/router.js b/src/router.js index ae03872..09eced3 100644 --- a/src/router.js +++ b/src/router.js @@ -11,8 +11,14 @@ import store from '@/store/'; const routes = [ { - path: '/:term_hash', - name: 'home', + path: '/login', + name: 'login', + props: true, + component: Login, + }, + { + path: '/terms/:term_hash', + name: 'term', props: true, component: App, children: [ @@ -29,16 +35,13 @@ const routes = [ component: VotesTableCategories, }, { - path: ':subject_id', + path: 'votes/:subject_id', name: 'showSubject', props: true, component: ShowSubject, - meta: { - requiresAuth: true, - }, }, { - path: ':subject_id/edit', + path: 'votes/:subject_id/edit', name: 'editSubject', props: true, component: EditSubject, @@ -49,13 +52,17 @@ const routes = [ ], }, { - path: '/login', - name: 'login', - component: Login, - }, - { + // redirect to default Term path: '/:pathMatch(.*)*', - component: NotFound, + redirect: (to) => { + const defaultTermHash = store.getters.getDefaultTerm().hash; + return { + name: 'votesTable', + params: { + term_hash: defaultTermHash, + }, + }; + }, }, ]; @@ -64,28 +71,12 @@ const router = new createRouter({ routes, }); -let defaultTerm = () => { - const now = new Date(); - let terms = store.getters.getTerms(); - terms.sort((a, b) => a.start_date > b.start_date); - let i = 0; - let currentTerm = terms[i]; - while (i + 1 < terms.length && currentTerm.end_date < now) - currentTerm = terms[++i]; - return currentTerm?.hash; -}; - -router.beforeEach((to, from, next) => { - if (to.path == '/') { - next('/' + defaultTerm()); - } else { - next(); - } -}); - +// Login redirection router.beforeEach((to, from, next) => { store.dispatch('init').then(() => { - if ( + if (to.name == 'login' && store.getters.isLoggedIn()) { + next({ name: from.name }); + } else if ( to.matched.some((record) => record.meta.requiresAuth) && !store.getters.isLoggedIn() ) { diff --git a/src/store/getters.js b/src/store/getters.js index b631f39..9cebc34 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -6,20 +6,19 @@ const getters = { return term?.subjects.find((subject) => subject.id == subject_id); }, getTerms: (state) => () => state.terms, - getTerm: (state) => (term_hash) => { - let thash = term_hash; - if (!thash) { - // FIXME: compute defaultTermHash - thash = '2019_23'; - } - return state.terms.find((term) => term.hash == thash); + getTerm: (state) => (termHash) => { + const term = state.terms.find((term) => term.hash == termHash); + return term; + }, + getTermHash: (state) => () => state.termHash, + getDefaultTerm: (state) => () => { + return state.terms.sort((a, b) => a.id < b.id)[0]; }, getNextTermHash: (state) => (term_id) => state.terms.find((term) => term.id == term_id + 1)?.hash, getPrevTermHash: (state) => (term_id) => state.terms.find((term) => term.id == term_id - 1)?.hash, getConnection: (state) => () => state.connection, - getTermHash: (state) => () => state.votes[2].hash, getUserVotes: (state) => () => state.userVotes, getUserVote: (state) => (subjectId) => state.userVotes?.find((vote) => vote.id == subjectId), diff --git a/src/store/index.js b/src/store/index.js index 20267b1..a6e96f2 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -17,6 +17,7 @@ const store = createStore({ userVotes: undefined, unsavedChanges: false, terms: terms, + termHash: terms.sort((a, b) => a.hash < b.hash)[0].hash, }; }, getters: getters, diff --git a/src/store/mutations.js b/src/store/mutations.js index 2694ea2..cfe6f9e 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -23,9 +23,6 @@ const mutations = { state.userVotes = [...state.userVotes, vote]; state.unsavedChanges = true; }, - // SET_PERIOD(state, i){ - // state.period.setFullYear(state.period.getFullYear() + i); - // }, DELETE_VOTE(state, index) { state.userVotes.splice(index, 1); state.unsavedChanges = true; @@ -34,6 +31,7 @@ const mutations = { state.client = undefined; state.userVotes = undefined; state.connection = undefined; + state.unsavedChanges = false; }, };