From 80cfeaac354f7f264c17b13beb29c0bbee622c0b Mon Sep 17 00:00:00 2001 From: Sean Engelhardt Date: Tue, 11 Sep 2018 16:38:23 +0200 Subject: [PATCH] Create more list views Also: - Update dependencies - Clean up proxy file --- client/package-lock.json | 203 +++++++++++++----- client/package.json | 6 +- client/proxy.conf.json | 16 +- client/src/app/shared/models/agenda/item.ts | 32 --- .../app/shared/models/mediafiles/mediafile.ts | 2 +- .../src/app/shared/models/motions/motion.ts | 2 +- client/src/app/shared/models/users/user.ts | 4 + client/src/app/shared/shared.module.ts | 7 +- .../agenda-list/agenda-list.component.html | 32 +-- .../agenda-list/agenda-list.component.ts | 38 +++- .../src/app/site/agenda/models/view-item.ts | 54 +++++ .../agenda-repository.service.spec.ts | 15 ++ .../services/agenda-repository.service.ts | 79 +++++++ .../assignment-list.component.html | 49 +++-- .../assignment-list.component.ts | 45 ++-- .../assignments/models/view-assignment.ts | 53 +++++ .../assignment-repository.service.spec.ts | 12 ++ .../services/assignment-repository.service.ts | 47 ++++ .../app/site/{ => base}/base-repository.ts | 19 +- .../app/site/{ => base}/base-view-model.ts | 4 +- client/src/app/site/base/list-view-base.ts | 63 ++++++ .../mediafile-list.component.html | 32 ++- .../mediafile-list.component.ts | 39 ++-- .../site/mediafiles/models/view-mediafile.ts | 59 +++++ .../mediafile-repository.service.spec.ts | 12 ++ .../services/mediafile-repository.service.ts | 56 +++++ .../motion-list/motion-list.component.html | 5 +- .../motion-list/motion-list.component.scss | 19 +- .../motion-list/motion-list.component.ts | 54 +---- .../app/site/motions/models/view-motion.ts | 64 ++---- .../services/motion-repository.service.ts | 2 +- .../app/site/settings/models/view-config.ts | 38 ++++ .../config-repository.service.spec.ts | 12 ++ .../services/config-repository.service.ts | 61 ++++++ .../settings-list.component.html | 31 ++- .../settings-list/settings-list.component.ts | 34 ++- client/src/app/site/users/models/view-user.ts | 68 ++++++ .../services/user-repository.service.spec.ts | 15 ++ .../users/services/user-repository.service.ts | 57 +++++ .../users/user-list/user-list.component.html | 29 ++- .../users/user-list/user-list.component.ts | 76 ++++++- client/src/styles.scss | 19 ++ 42 files changed, 1223 insertions(+), 341 deletions(-) create mode 100644 client/src/app/site/agenda/models/view-item.ts create mode 100644 client/src/app/site/agenda/services/agenda-repository.service.spec.ts create mode 100644 client/src/app/site/agenda/services/agenda-repository.service.ts create mode 100644 client/src/app/site/assignments/models/view-assignment.ts create mode 100644 client/src/app/site/assignments/services/assignment-repository.service.spec.ts create mode 100644 client/src/app/site/assignments/services/assignment-repository.service.ts rename client/src/app/site/{ => base}/base-repository.ts (88%) rename client/src/app/site/{ => base}/base-view-model.ts (74%) create mode 100644 client/src/app/site/base/list-view-base.ts create mode 100644 client/src/app/site/mediafiles/models/view-mediafile.ts create mode 100644 client/src/app/site/mediafiles/services/mediafile-repository.service.spec.ts create mode 100644 client/src/app/site/mediafiles/services/mediafile-repository.service.ts create mode 100644 client/src/app/site/settings/models/view-config.ts create mode 100644 client/src/app/site/settings/services/config-repository.service.spec.ts create mode 100644 client/src/app/site/settings/services/config-repository.service.ts create mode 100644 client/src/app/site/users/models/view-user.ts create mode 100644 client/src/app/site/users/services/user-repository.service.spec.ts create mode 100644 client/src/app/site/users/services/user-repository.service.ts diff --git a/client/package-lock.json b/client/package-lock.json index 6a1743a60..f88cb9eb2 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -106,13 +106,36 @@ } }, "@angular-devkit/schematics": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.6.8.tgz", - "integrity": "sha512-R4YqAUdo62wtrhX/5HSRGSKXNTWqfQb66ZE6m8jj6GEJNFKdNXMdxOchxr07LCiKTxfh1w6G3nGzxIsu/+D4KA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.8.1.tgz", + "integrity": "sha512-ab3xeyTpPA9JO7oXgdfbQ/+5djddvoKjFaxFFcLD5GxgDDnvJcmhgDkluJY9JZHH2oSFaW8u1G5zg8uo1HrriA==", "dev": true, "requires": { - "@angular-devkit/core": "0.6.8", - "rxjs": "^6.0.0" + "@angular-devkit/core": "0.8.1", + "rxjs": "~6.2.0" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.1.tgz", + "integrity": "sha512-0TKjF/nHb7+wwWIQ5iuQRzDIF2CphmX9ZojBIGH6PWFgQNKG0yUWqSa+PTpr5eOcGYBOa1rHLf10iyPHDmBdBw==", + "dev": true, + "requires": { + "ajv": "~6.4.0", + "chokidar": "^2.0.3", + "rxjs": "~6.2.0", + "source-map": "^0.5.6" + } + }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + } } }, "@angular/animations": { @@ -132,35 +155,71 @@ } }, "@angular/cli": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.0.8.tgz", - "integrity": "sha512-DhH1Zq5Yonthw6zh6W07fhf+9XrAZbD1fcQ0MrmbxlieCfLlTAdBqyK2LavFCKwSZkUMLF6UHM3+jiNRVZSSIg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.2.1.tgz", + "integrity": "sha512-4AO014PohYc/vbNaO6nPi/a6JqxdOHN2m0WLutgRGoBQswqSGn7aLEG1erZKRzbfq39E1a/efnNmEI3Okl3h1Q==", "dev": true, "requires": { - "@angular-devkit/architect": "0.6.8", - "@angular-devkit/core": "0.6.8", - "@angular-devkit/schematics": "0.6.8", - "@schematics/angular": "0.6.8", - "@schematics/update": "0.6.8", - "opn": "~5.3.0", - "resolve": "^1.1.7", - "rxjs": "^6.0.0", + "@angular-devkit/architect": "0.8.1", + "@angular-devkit/core": "0.8.1", + "@angular-devkit/schematics": "0.8.1", + "@schematics/angular": "0.8.1", + "@schematics/update": "0.8.1", + "json-schema-traverse": "^0.4.1", + "opn": "^5.3.0", + "rxjs": "~6.2.0", "semver": "^5.1.0", - "silent-error": "^1.0.0", "symbol-observable": "^1.2.0", "yargs-parser": "^10.0.0" }, "dependencies": { + "@angular-devkit/architect": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.8.1.tgz", + "integrity": "sha512-bf/8tg8X2y9f6wE2r48KAW2AVexfGd/rfTHRvl9+kSsFFtXVA233GNAL6Qs+wJ/G2t1NFddnE3ME2eyhJYxBwA==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.1", + "rxjs": "~6.2.0" + } + }, + "@angular-devkit/core": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.1.tgz", + "integrity": "sha512-0TKjF/nHb7+wwWIQ5iuQRzDIF2CphmX9ZojBIGH6PWFgQNKG0yUWqSa+PTpr5eOcGYBOa1rHLf10iyPHDmBdBw==", + "dev": true, + "requires": { + "ajv": "~6.4.0", + "chokidar": "^2.0.3", + "rxjs": "~6.2.0", + "source-map": "^0.5.6" + } + }, "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "yargs-parser": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.0.0.tgz", - "integrity": "sha512-+DHejWujTVYeMHLff8U96rLc4uE4Emncoftvn5AjhB1Jw1pWxLzgBUT/WYbPrHmy6YPEBTZQx5myHhVcuuu64g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", "dev": true, "requires": { "camelcase": "^4.1.0" @@ -1065,28 +1124,74 @@ "dev": true }, "@schematics/angular": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.6.8.tgz", - "integrity": "sha512-9kRphqTYG5Df/I8fvnT1zMsw0YNDPO9tl18tQZXj4am4raT7l9UCr+WkwJdlBoA5pwG6baWE9sL0iGWV/bzF/g==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.8.1.tgz", + "integrity": "sha512-AW7063IaYFIcskt+eI5k4drb/hDuY2wK3zLsW01E49WaBcRhXVIOVox7cbfwHCA6zch117WZ5xVZlbGHJ4pkMw==", "dev": true, "requires": { - "@angular-devkit/core": "0.6.8", - "@angular-devkit/schematics": "0.6.8", - "typescript": ">=2.6.2 <2.8" + "@angular-devkit/core": "0.8.1", + "@angular-devkit/schematics": "0.8.1", + "typescript": ">=2.6.2 <2.10" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.1.tgz", + "integrity": "sha512-0TKjF/nHb7+wwWIQ5iuQRzDIF2CphmX9ZojBIGH6PWFgQNKG0yUWqSa+PTpr5eOcGYBOa1rHLf10iyPHDmBdBw==", + "dev": true, + "requires": { + "ajv": "~6.4.0", + "chokidar": "^2.0.3", + "rxjs": "~6.2.0", + "source-map": "^0.5.6" + } + }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + } } }, "@schematics/update": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.6.8.tgz", - "integrity": "sha512-1Uq7LYnwL2wBwGVCgNz76QAR13ghAk+2vDDHOi+VX5+usHManxydrpoMGeX66OBPd+y5D3D2MFb+8mYHE7mygg==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.8.1.tgz", + "integrity": "sha512-G5EU8nvqAC9fX09sV+UEM9EgR+PjGwguUatOk10uvwvZvCfO+W0FVeLmSap9A6mJ+e8YOH6XFpkwLG0AMuhYrw==", "dev": true, "requires": { - "@angular-devkit/core": "0.6.8", - "@angular-devkit/schematics": "0.6.8", + "@angular-devkit/core": "0.8.1", + "@angular-devkit/schematics": "0.8.1", "npm-registry-client": "^8.5.1", - "rxjs": "^6.0.0", + "rxjs": "~6.2.0", "semver": "^5.3.0", "semver-intersect": "^1.1.2" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.1.tgz", + "integrity": "sha512-0TKjF/nHb7+wwWIQ5iuQRzDIF2CphmX9ZojBIGH6PWFgQNKG0yUWqSa+PTpr5eOcGYBOa1rHLf10iyPHDmBdBw==", + "dev": true, + "requires": { + "ajv": "~6.4.0", + "chokidar": "^2.0.3", + "rxjs": "~6.2.0", + "source-map": "^0.5.6" + } + }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + } } }, "@types/jasmine": { @@ -2068,7 +2173,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -6898,7 +7003,7 @@ }, "es6-promise": { "version": "3.0.2", - "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", "dev": true }, @@ -6910,7 +7015,7 @@ }, "readable-stream": { "version": "2.0.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", "dev": true, "requires": { @@ -8335,9 +8440,9 @@ } }, "npm-registry-client": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.5.1.tgz", - "integrity": "sha512-7rjGF2eA7hKDidGyEWmHTiKfXkbrcQAsGL/Rh4Rt3x3YNRNHhwaTzVJfW3aNvvlhg4G62VCluif0sLCb/i51Hg==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.6.0.tgz", + "integrity": "sha512-Qs6P6nnopig+Y8gbzpeN/dkt+n7IyVd8f45NTMotGk6Qo7GfBmzwYx6jRLoOOgKiMnaQfYxsuyQlD8Mc3guBhg==", "dev": true, "requires": { "concat-stream": "^1.5.2", @@ -9259,7 +9364,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -9272,7 +9377,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -9905,9 +10010,9 @@ } }, "rxjs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.2.tgz", - "integrity": "sha512-hV7criqbR0pe7EeL3O66UYVg92IR0XsA97+9y+BWTePK9SKmEI5Qd3Zj6uPnGkNzXsBywBQWTvujPl+1Kn9Zjw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", "requires": { "tslib": "^1.9.0" } @@ -10153,9 +10258,9 @@ } }, "semver-intersect": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.3.1.tgz", - "integrity": "sha1-j6hKnhAovSOeRTDRo+GB5pjYhLo=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", + "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", "dev": true, "requires": { "semver": "^5.0.0" @@ -11470,9 +11575,9 @@ "dev": true }, "typescript": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", - "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "dev": true }, "uglify-js": { diff --git a/client/package.json b/client/package.json index 9b476e202..6cd90db4f 100644 --- a/client/package.json +++ b/client/package.json @@ -34,13 +34,13 @@ "@ngx-translate/http-loader": "^3.0.1", "core-js": "^2.5.4", "ngx-mat-select-search": "^1.3.1", - "rxjs": "^6.3.2", + "rxjs": "^6.2.2", "uuid": "^3.3.2", "zone.js": "^0.8.26" }, "devDependencies": { "@angular-devkit/build-angular": "~0.6.8", - "@angular/cli": "~6.0.8", + "@angular/cli": "^6.2.1", "@angular/compiler-cli": "^6.1.7", "@angular/language-service": "^6.1.7", "@biesbjerg/ngx-translate-extract": "^2.3.4", @@ -63,6 +63,6 @@ "protractor": "^5.4.1", "ts-node": "~5.0.1", "tslint": "~5.9.1", - "typescript": "~2.7.2" + "typescript": "~2.9.2" } } diff --git a/client/proxy.conf.json b/client/proxy.conf.json index 039bc5869..2373248d9 100644 --- a/client/proxy.conf.json +++ b/client/proxy.conf.json @@ -1,25 +1,17 @@ { - "/apps/core/version": { + "/apps/": { "target": "http://localhost:8000", "secure": false }, - "/apps/users/login": { + "/media/": { "target": "http://localhost:8000", "secure": false }, - "/apps/users/logout": { + "/rest/": { "target": "http://localhost:8000", "secure": false }, - "/apps/users/whoami": { - "target": "http://localhost:8000", - "secure": false - }, - "/rest": { - "target": "http://localhost:8000", - "secure": false - }, - "/ws/site": { + "/ws/site/": { "target": "ws://localhost:8000", "secure": false, "ws": true diff --git a/client/src/app/shared/models/agenda/item.ts b/client/src/app/shared/models/agenda/item.ts index 46faac9ad..ce697738f 100644 --- a/client/src/app/shared/models/agenda/item.ts +++ b/client/src/app/shared/models/agenda/item.ts @@ -34,23 +34,6 @@ export class Item extends ProjectableBaseModel { super('agenda/item', input); } - // Note: This has to be used in the agenda repository - /*public get contentObject(): AgendaBaseModel { - const contentObject = this.DS.get(this.content_object.collection, this.content_object.id); - if (!contentObject) { - return null; - } - if (contentObject instanceof AgendaBaseModel) { - return contentObject as AgendaBaseModel; - } else { - throw new Error( - `The content object (${this.content_object.collection}, ${this.content_object.id}) of item ${ - this.id - } is not a BaseProjectableModel.` - ); - } - }*/ - public deserialize(input: any): void { Object.assign(this, input); @@ -61,26 +44,11 @@ export class Item extends ProjectableBaseModel { } } - // The repository has to check for the content object and choose which title to use. - // The code below is belongs to the repository public getTitle(): string { - /*const contentObject: AgendaBaseModel = this.contentObject; - if (contentObject) { - return contentObject.getAgendaTitle(); - } else { - return this.title; - }*/ return this.title; } - // Same here. See comment for getTitle() public getListTitle(): string { - /*const contentObject: AgendaBaseModel = this.contentObject; - if (contentObject) { - return contentObject.getAgendaTitleWithType(); - } else { - return this.title_with_type; - }*/ return this.title_with_type; } diff --git a/client/src/app/shared/models/mediafiles/mediafile.ts b/client/src/app/shared/models/mediafiles/mediafile.ts index dfe9c941f..bebde6364 100644 --- a/client/src/app/shared/models/mediafiles/mediafile.ts +++ b/client/src/app/shared/models/mediafiles/mediafile.ts @@ -29,4 +29,4 @@ export class Mediafile extends ProjectableBaseModel { } } -ProjectableBaseModel.registerCollectionElement('amediafiles/mediafile', Mediafile); +ProjectableBaseModel.registerCollectionElement('mediafiles/mediafile', Mediafile); diff --git a/client/src/app/shared/models/motions/motion.ts b/client/src/app/shared/models/motions/motion.ts index 9779caa2a..35cf26909 100644 --- a/client/src/app/shared/models/motions/motion.ts +++ b/client/src/app/shared/models/motions/motion.ts @@ -85,7 +85,7 @@ export class Motion extends AgendaBaseModel { } public getDetailStateURL(): string { - return 'TODO'; + return `/motions/${this.id}`; } public deserialize(input: any): void { diff --git a/client/src/app/shared/models/users/user.ts b/client/src/app/shared/models/users/user.ts index c49b7ae7f..208c45c89 100644 --- a/client/src/app/shared/models/users/user.ts +++ b/client/src/app/shared/models/users/user.ts @@ -48,6 +48,10 @@ export class User extends ProjectableBaseModel { return name.trim(); } + public containsGroupId(id: number): boolean { + return this.groups_id.some(groupId => groupId === id); + } + // TODO read config values for "users_sort_by" public get short_name(): string { const title = this.title.trim(); diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 1bc9bbd12..21ae88d6b 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -15,8 +15,7 @@ import { MatSnackBarModule, MatTableModule, MatPaginatorModule, - MatSortModule, - MatTabsModule + MatSortModule } from '@angular/material'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatChipsModule } from '@angular/material'; @@ -79,10 +78,10 @@ library.add(fas); MatMenuModule, MatDialogModule, MatSnackBarModule, + MatChipsModule, FontAwesomeModule, TranslateModule.forChild(), RouterModule, - MatChipsModule, NgxMatSelectSearchModule ], exports: [ @@ -106,7 +105,7 @@ library.add(fas); MatMenuModule, MatDialogModule, MatSnackBarModule, - MatTabsModule, + MatChipsModule, NgxMatSelectSearchModule, FontAwesomeModule, TranslateModule, diff --git a/client/src/app/site/agenda/agenda-list/agenda-list.component.html b/client/src/app/site/agenda/agenda-list/agenda-list.component.html index d96e3aa28..bdbbb0964 100644 --- a/client/src/app/site/agenda/agenda-list/agenda-list.component.html +++ b/client/src/app/site/agenda/agenda-list/agenda-list.component.html @@ -1,16 +1,18 @@ - - + - -
- Agenda Works -
-
- everyone should see this -
-
-
- Only permitted users should see this -
-
-
+ + + + Topic + {{item.getListTitle()}} + + + + Duration + {{item.duration}} + + + + + + diff --git a/client/src/app/site/agenda/agenda-list/agenda-list.component.ts b/client/src/app/site/agenda/agenda-list/agenda-list.component.ts index b5bb46055..75249527c 100644 --- a/client/src/app/site/agenda/agenda-list/agenda-list.component.ts +++ b/client/src/app/site/agenda/agenda-list/agenda-list.component.ts @@ -1,9 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { Title } from '@angular/platform-browser'; -import { BaseComponent } from 'app/base.component'; import { TranslateService } from '@ngx-translate/core'; -import { Item } from '../../../shared/models/agenda/item'; -import { Topic } from '../../../shared/models/topics/topic'; +import { ViewItem } from '../models/view-item'; +import { ListViewBaseComponent } from '../../base/list-view-base'; +import { AgendaRepositoryService } from '../services/agenda-repository.service'; +import { Router } from '@angular/router'; /** * List view for the agenda. @@ -15,26 +16,43 @@ import { Topic } from '../../../shared/models/topics/topic'; templateUrl: './agenda-list.component.html', styleUrls: ['./agenda-list.component.css'] }) -export class AgendaListComponent extends BaseComponent implements OnInit { +export class AgendaListComponent extends ListViewBaseComponent implements OnInit { /** * The usual constructor for components * @param titleService * @param translate */ - public constructor(titleService: Title, protected translate: TranslateService) { + public constructor( + titleService: Title, + translate: TranslateService, + private router: Router, + private repo: AgendaRepositoryService + ) { super(titleService, translate); } /** * Init function. - * Sets the title + * Sets the title, initializes the table and calls the repository. */ public ngOnInit(): void { super.setTitle('Agenda'); - // tslint:disable-next-line - const i: Item = new Item(); // Needed, that the Item.ts is loaded. Can be removed, if something else creates/uses items. - // tslint:disable-next-line - const t: Topic = new Topic(); // Needed, that the Topic.ts is loaded. Can be removed, if something else creates/uses topics. + this.initTable(); + this.repo.getViewModelListObservable().subscribe(newAgendaItem => { + this.dataSource.data = newAgendaItem; + }); + } + + /** + * Handler for click events on agenda item rows + * Links to the content object if any + */ + public selectAgendaItem(item: ViewItem): void { + if (item.contentObject) { + this.router.navigate([item.contentObject.getDetailStateURL()]); + } else { + console.error(`The selected item ${item} has no content object`); + } } /** diff --git a/client/src/app/site/agenda/models/view-item.ts b/client/src/app/site/agenda/models/view-item.ts new file mode 100644 index 000000000..cf221d8b6 --- /dev/null +++ b/client/src/app/site/agenda/models/view-item.ts @@ -0,0 +1,54 @@ +import { BaseViewModel } from '../../base/base-view-model'; +import { Item } from '../../../shared/models/agenda/item'; +import { BaseModel } from '../../../shared/models/base/base-model'; +import { AgendaBaseModel } from '../../../shared/models/base/agenda-base-model'; + +export class ViewItem extends BaseViewModel { + private _item: Item; + private _contentObject: AgendaBaseModel; + + public get item(): Item { + return this._item; + } + + public get contentObject(): AgendaBaseModel { + return this._contentObject; + } + + public get id(): number { + return this.item ? this.item.id : null; + } + + public get duration(): number { + return this.item ? this.item.duration : null; + } + + public constructor(item: Item, contentObject: AgendaBaseModel) { + super(); + this._item = item; + this._contentObject = contentObject; + } + + public getTitle(): string { + if (this.contentObject) { + return this.contentObject.getAgendaTitle(); + } else { + return this.item ? this.item.title : null; + } + } + + public getListTitle(): string { + const contentObject: AgendaBaseModel = this.contentObject; + if (contentObject) { + return contentObject.getAgendaTitleWithType(); + } else { + return this.item ? this.item.title_with_type : null; + } + } + + public updateValues(update: BaseModel): void { + if (update instanceof Item && this.id === update.id) { + this._item = update; + } + } +} diff --git a/client/src/app/site/agenda/services/agenda-repository.service.spec.ts b/client/src/app/site/agenda/services/agenda-repository.service.spec.ts new file mode 100644 index 000000000..870d49958 --- /dev/null +++ b/client/src/app/site/agenda/services/agenda-repository.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { AgendaRepositoryService } from './agenda-repository.service'; + +describe('AgendaRepositoryService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AgendaRepositoryService] + }); + }); + + it('should be created', inject([AgendaRepositoryService], (service: AgendaRepositoryService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/site/agenda/services/agenda-repository.service.ts b/client/src/app/site/agenda/services/agenda-repository.service.ts new file mode 100644 index 000000000..d49400ff0 --- /dev/null +++ b/client/src/app/site/agenda/services/agenda-repository.service.ts @@ -0,0 +1,79 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +import { BaseRepository } from '../../base/base-repository'; +import { DataStoreService } from '../../../core/services/data-store.service'; +import { Item } from '../../../shared/models/agenda/item'; +import { ViewItem } from '../models/view-item'; +import { AgendaBaseModel } from '../../../shared/models/base/agenda-base-model'; +import { BaseModel } from '../../../shared/models/base/base-model'; + +/** + * Repository service for users + * + * Documentation partially provided in {@link BaseRepository} + */ +@Injectable({ + providedIn: 'root' +}) +export class AgendaRepositoryService extends BaseRepository { + public constructor(DS: DataStoreService) { + super(DS, Item); + } + + /** + * Returns the corresponding content object to a given {@link Item} as an {@link AgendaBaseModel} + * @param agendaItem + */ + private getContentObject(agendaItem: Item): AgendaBaseModel { + const contentObject = this.DS.get( + agendaItem.content_object.collection, + agendaItem.content_object.id + ); + if (!contentObject) { + return null; + } + if (contentObject instanceof AgendaBaseModel) { + return contentObject as AgendaBaseModel; + } else { + throw new Error( + `The content object (${agendaItem.content_object.collection}, ${ + agendaItem.content_object.id + }) of item ${agendaItem.id} is not a BaseProjectableModel.` + ); + } + } + + /** + * @ignore + * + * TODO: used over not-yet-existing detail view + */ + public save(item: Item, viewUser: ViewItem): Observable { + return null; + } + + /** + * @ignore + * + * TODO: used over not-yet-existing detail view + */ + public delete(item: ViewItem): Observable { + return null; + } + + /** + * @ignore + * + * TODO: used over not-yet-existing detail view + */ + public create(item: Item, viewItem: ViewItem): Observable { + return null; + } + + public createViewModel(item: Item): ViewItem { + const contentObject = this.getContentObject(item); + + return new ViewItem(item, contentObject); + } +} diff --git a/client/src/app/site/assignments/assignment-list/assignment-list.component.html b/client/src/app/site/assignments/assignment-list/assignment-list.component.html index 76cea8273..fdf63fa7a 100644 --- a/client/src/app/site/assignments/assignment-list/assignment-list.component.html +++ b/client/src/app/site/assignments/assignment-list/assignment-list.component.html @@ -1,21 +1,32 @@ - - - + - - assignment-list works! - + + + + Title + {{assignment.getTitle()}} + + + Phase + + + {{assignment.phase}} + + + + + Candidates + + + {{assignment.candidateAmount}} + + + + + + + + + diff --git a/client/src/app/site/assignments/assignment-list/assignment-list.component.ts b/client/src/app/site/assignments/assignment-list/assignment-list.component.ts index 94852b825..63d8d251a 100644 --- a/client/src/app/site/assignments/assignment-list/assignment-list.component.ts +++ b/client/src/app/site/assignments/assignment-list/assignment-list.component.ts @@ -1,20 +1,20 @@ import { Component, OnInit } from '@angular/core'; -import { BaseComponent } from '../../../base.component'; import { TranslateService } from '@ngx-translate/core'; import { Title } from '@angular/platform-browser'; -import { Assignment } from '../../../shared/models/assignments/assignment'; +import { ViewAssignment } from '../models/view-assignment'; +import { ListViewBaseComponent } from '../../base/list-view-base'; +import { AssignmentRepositoryService } from '../services/assignment-repository.service'; /** * Listview for the assignments * - * TODO: not yet implemented */ @Component({ selector: 'os-assignment-list', templateUrl: './assignment-list.component.html', styleUrls: ['./assignment-list.component.css'] }) -export class AssignmentListComponent extends BaseComponent implements OnInit { +export class AssignmentListComponent extends ListViewBaseComponent implements OnInit { /** * Define the content of the ellipsis menu. * Give it to the HeadBar to display them. @@ -29,13 +29,27 @@ export class AssignmentListComponent extends BaseComponent implements OnInit { /** * Constructor. + * + * @param repo the repository * @param titleService * @param translate */ - public constructor(titleService: Title, protected translate: TranslateService) { + public constructor(private repo: AssignmentRepositoryService, titleService: Title, translate: TranslateService) { super(titleService, translate); } + /** + * Init function. + * Sets the title, inits the table and calls the repo. + */ + public ngOnInit(): void { + super.setTitle('Assignments'); + this.initTable(); + this.repo.getViewModelListObservable().subscribe(newAssignments => { + this.dataSource.data = newAssignments; + }); + } + /** * Click on the plus button delegated from head-bar */ @@ -44,13 +58,11 @@ export class AssignmentListComponent extends BaseComponent implements OnInit { } /** - * Init function. Sets the title. + * Select an row in the table + * @param assignment */ - public ngOnInit(): void { - super.setTitle('Assignments'); - - // tslint:disable-next-line - const a: Assignment = new Assignment(); // Needed, that the Assignment.ts is loaded. Can be removed, if something else creates/uses assignments. + public selectAssignment(assignment: ViewAssignment): void { + console.log('select assignment list: ', assignment); } /** @@ -60,15 +72,4 @@ export class AssignmentListComponent extends BaseComponent implements OnInit { public downloadAssignmentButton(): void { console.log('Hello World'); } - - /** - * handler function for clicking on items in the ellipsis menu. - * - * @param event clicked entry from ellipsis menu - */ - public onEllipsisItem(event: any): void { - if (event.action) { - this[event.action](); - } - } } diff --git a/client/src/app/site/assignments/models/view-assignment.ts b/client/src/app/site/assignments/models/view-assignment.ts new file mode 100644 index 000000000..32373a663 --- /dev/null +++ b/client/src/app/site/assignments/models/view-assignment.ts @@ -0,0 +1,53 @@ +import { BaseViewModel } from '../../base/base-view-model'; +import { Assignment } from '../../../shared/models/assignments/assignment'; +import { Tag } from '../../../shared/models/core/tag'; +import { User } from '../../../shared/models/users/user'; +import { Item } from '../../../shared/models/agenda/item'; + +export class ViewAssignment extends BaseViewModel { + private _assignment: Assignment; + private _relatedUser: User[]; + private _agendaItem: Item; + private _tags: Tag[]; + + public get assignment(): Assignment { + return this._assignment; + } + + public get candidates(): User[] { + return this._relatedUser; + } + + public get agendaItem(): Item { + return this._agendaItem; + } + + public get tags(): Tag[] { + return this._tags; + } + + /** + * unknown where the identifier to the phase is get + */ + public get phase(): number { + return this.assignment ? this.assignment.phase : null; + } + + public get candidateAmount(): number { + return this.candidates ? this.candidates.length : 0; + } + + public constructor(assignment: Assignment, relatedUser: User[], agendaItem?: Item, tags?: Tag[]) { + super(); + this._assignment = assignment; + this._relatedUser = relatedUser; + this._agendaItem = agendaItem; + this._tags = tags; + } + + public updateValues(): void {} + + public getTitle(): string { + return this.assignment ? this.assignment.title : null; + } +} diff --git a/client/src/app/site/assignments/services/assignment-repository.service.spec.ts b/client/src/app/site/assignments/services/assignment-repository.service.spec.ts new file mode 100644 index 000000000..c694cfae7 --- /dev/null +++ b/client/src/app/site/assignments/services/assignment-repository.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { AssignmentRepositoryService } from './assignment-repository.service'; + +describe('AssignmentRepositoryService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: AssignmentRepositoryService = TestBed.get(AssignmentRepositoryService); + expect(service).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/assignments/services/assignment-repository.service.ts b/client/src/app/site/assignments/services/assignment-repository.service.ts new file mode 100644 index 000000000..135b725a7 --- /dev/null +++ b/client/src/app/site/assignments/services/assignment-repository.service.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@angular/core'; +import { ViewAssignment } from '../models/view-assignment'; +import { Assignment } from '../../../shared/models/assignments/assignment'; +import { User } from '../../../shared/models/users/user'; +import { Tag } from '../../../shared/models/core/tag'; +import { Item } from '../../../shared/models/agenda/item'; +import { Observable } from 'rxjs'; +import { BaseRepository } from '../../base/base-repository'; +import { DataStoreService } from '../../../core/services/data-store.service'; + +/** + * Repository Service for Assignments. + * + * Documentation partially provided in {@link BaseRepository} + */ +@Injectable({ + providedIn: 'root' +}) +export class AssignmentRepositoryService extends BaseRepository { + /** + * Constructor for the Assignment Repository. + * + */ + public constructor(DS: DataStoreService) { + super(DS, Assignment, [User, Item, Tag]); + } + + public save(assignment: Assignment, viewAssignment: ViewAssignment): Observable { + return null; + } + + public delete(viewAssignment: ViewAssignment): Observable { + return null; + } + + public create(assignment: Assignment, viewAssignment: ViewAssignment): Observable { + return null; + } + + public createViewModel(assignment: Assignment): ViewAssignment { + const relatedUser = this.DS.getMany(User, assignment.candidateIds); + const agendaItem = this.DS.get(Item, assignment.agenda_item_id); + const tags = this.DS.getMany(Tag, assignment.tags_id); + + return new ViewAssignment(assignment, relatedUser, agendaItem, tags); + } +} diff --git a/client/src/app/site/base-repository.ts b/client/src/app/site/base/base-repository.ts similarity index 88% rename from client/src/app/site/base-repository.ts rename to client/src/app/site/base/base-repository.ts index 25d0d2998..10d1c2846 100644 --- a/client/src/app/site/base-repository.ts +++ b/client/src/app/site/base/base-repository.ts @@ -1,9 +1,9 @@ -import { OpenSlidesComponent } from '../openslides.component'; +import { OpenSlidesComponent } from '../../openslides.component'; import { BehaviorSubject, Observable } from 'rxjs'; import { BaseViewModel } from './base-view-model'; -import { BaseModel, ModelConstructor } from '../shared/models/base/base-model'; -import { CollectionStringModelMapperService } from '../core/services/collectionStringModelMapper.service'; -import { DataStoreService } from '../core/services/data-store.service'; +import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model'; +import { CollectionStringModelMapperService } from '../../core/services/collectionStringModelMapper.service'; +import { DataStoreService } from '../../core/services/data-store.service'; export abstract class BaseRepository extends OpenSlidesComponent { /** @@ -30,7 +30,7 @@ export abstract class BaseRepository, - protected depsModelCtors: ModelConstructor[] + protected depsModelCtors?: ModelConstructor[] ) { super(); @@ -47,7 +47,7 @@ export abstract class BaseRepository { return model instanceof ctor; }); @@ -92,6 +92,13 @@ export abstract class BaseRepository; + /** + * Creates a view model out of a base model. + * + * Should read all necessary objects from the datastore + * that the viewmodel needs + * @param model + */ protected abstract createViewModel(model: M): V; /** diff --git a/client/src/app/site/base-view-model.ts b/client/src/app/site/base/base-view-model.ts similarity index 74% rename from client/src/app/site/base-view-model.ts rename to client/src/app/site/base/base-view-model.ts index 7e71b7662..89ae92895 100644 --- a/client/src/app/site/base-view-model.ts +++ b/client/src/app/site/base/base-view-model.ts @@ -1,5 +1,5 @@ -import { BaseModel } from '../shared/models/base/base-model'; -import { Displayable } from '../shared/models/base/displayable'; +import { BaseModel } from '../../shared/models/base/base-model'; +import { Displayable } from '../../shared/models/base/displayable'; /** * Base class for view models. alls view models should have titles. diff --git a/client/src/app/site/base/list-view-base.ts b/client/src/app/site/base/list-view-base.ts new file mode 100644 index 000000000..b02b545bf --- /dev/null +++ b/client/src/app/site/base/list-view-base.ts @@ -0,0 +1,63 @@ +import { ViewChild } from '@angular/core'; +import { BaseComponent } from '../../base.component'; +import { Title } from '@angular/platform-browser'; +import { TranslateService } from '@ngx-translate/core'; +import { MatTableDataSource, MatTable, MatSort, MatPaginator } from '@angular/material'; +import { BaseViewModel } from './base-view-model'; + +export abstract class ListViewBaseComponent extends BaseComponent { + /** + * The data source for a table. Requires to be initialised with a BaseViewModel + */ + public dataSource: MatTableDataSource; + + /** + * The table itself + */ + @ViewChild(MatTable) + protected table: MatTable; + + /** + * Table paginator + */ + @ViewChild(MatPaginator) + protected paginator: MatPaginator; + + /** + * Sorter for a table + */ + @ViewChild(MatSort) + protected sort: MatSort; + + /** + * Constructor for list view bases + * @param titleService the title serivce + * @param translate the translate service + */ + public constructor(titleService: Title, translate: TranslateService) { + super(titleService, translate); + } + + /** + * Children need to call this in their init-function. + * Calling these three functions in the constructor of this class + * would be too early, resulting in non-paginated tables + */ + public initTable(): void { + this.dataSource = new MatTableDataSource(); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } + + /** + * handler function for clicking on items in the ellipsis menu. + * Ellipsis menu comes from the HeadBarComponent is is implemented by most ListViews + * + * @param event clicked entry from ellipsis menu + */ + public onEllipsisItem(event: any): void { + if (event.action) { + this[event.action](); + } + } +} diff --git a/client/src/app/site/mediafiles/mediafile-list/mediafile-list.component.html b/client/src/app/site/mediafiles/mediafile-list/mediafile-list.component.html index 677d6cf2e..a29b802e8 100644 --- a/client/src/app/site/mediafiles/mediafile-list/mediafile-list.component.html +++ b/client/src/app/site/mediafiles/mediafile-list/mediafile-list.component.html @@ -2,6 +2,32 @@ - - mediafile-list works! - + + + + Name + {{file.title}} + + + + + Group + + {{file.type}} +
+ {{file.size}} +
+
+ + + Download + + + + + + + +
+ + diff --git a/client/src/app/site/mediafiles/mediafile-list/mediafile-list.component.ts b/client/src/app/site/mediafiles/mediafile-list/mediafile-list.component.ts index 236226d84..81f0526a6 100644 --- a/client/src/app/site/mediafiles/mediafile-list/mediafile-list.component.ts +++ b/client/src/app/site/mediafiles/mediafile-list/mediafile-list.component.ts @@ -3,19 +3,20 @@ import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { BaseComponent } from '../../../base.component'; +import { ViewMediafile } from '../models/view-mediafile'; +import { MediafileRepositoryService } from '../services/mediafile-repository.service'; +import { ListViewBaseComponent } from '../../base/list-view-base'; /** - * Lists all the uploaded mediafiles. + * Lists all the uploaded files. * - * Not yet implemented */ @Component({ selector: 'os-mediafile-list', templateUrl: './mediafile-list.component.html', styleUrls: ['./mediafile-list.component.css'] }) -export class MediafileListComponent extends BaseComponent implements OnInit { +export class MediafileListComponent extends ListViewBaseComponent implements OnInit { /** * Define the content of the ellipsis menu. * Give it to the HeadBar to display them. @@ -31,10 +32,15 @@ export class MediafileListComponent extends BaseComponent implements OnInit { /** * Constructor * + * @param repo the repository for files * @param titleService * @param translate */ - public constructor(titleService: Title, protected translate: TranslateService) { + public constructor( + private repo: MediafileRepositoryService, + protected titleService: Title, + protected translate: TranslateService + ) { super(titleService, translate); } @@ -44,6 +50,10 @@ export class MediafileListComponent extends BaseComponent implements OnInit { */ public ngOnInit(): void { super.setTitle('Files'); + this.initTable(); + this.repo.getViewModelListObservable().subscribe(newUsers => { + this.dataSource.data = newUsers; + }); } /** @@ -64,13 +74,18 @@ export class MediafileListComponent extends BaseComponent implements OnInit { } /** - * handler function for clicking on items in the ellipsis menu. - * - * @param event clicked entry from ellipsis menu + * Clicking on a list row + * @param file the selected file */ - public onEllipsisItem(event: any): void { - if (event.action) { - this[event.action](); - } + public selectFile(file: ViewMediafile): void { + console.log('The file: ', file); + } + + /** + * Directly download a mediafile using the download button on the table + * @param file + */ + public download(file: ViewMediafile): void { + window.open(file.downloadUrl); } } diff --git a/client/src/app/site/mediafiles/models/view-mediafile.ts b/client/src/app/site/mediafiles/models/view-mediafile.ts new file mode 100644 index 000000000..f08989c17 --- /dev/null +++ b/client/src/app/site/mediafiles/models/view-mediafile.ts @@ -0,0 +1,59 @@ +import { BaseViewModel } from '../../base/base-view-model'; +import { Mediafile } from '../../../shared/models/mediafiles/mediafile'; +import { User } from '../../../shared/models/users/user'; +import { BaseModel } from '../../../shared/models/base/base-model'; + +export class ViewMediafile extends BaseViewModel { + private _mediafile: Mediafile; + private _uploader: User; + + public get mediafile(): Mediafile { + return this._mediafile; + } + + public get uploader(): User { + return this._uploader; + } + + public get title(): string { + return this.mediafile ? this.mediafile.title : null; + } + + public get size(): string { + return this.mediafile ? this.mediafile.filesize : null; + } + + public get type(): string { + return this.mediafile && this.mediafile.mediafile ? this.mediafile.mediafile.type : null; + } + + public get prefix(): string { + return this.mediafile ? this.mediafile.media_url_prefix : null; + } + + public get fileName(): string { + return this.mediafile && this.mediafile.mediafile ? this.mediafile.mediafile.name : null; + } + + public get downloadUrl(): string { + return this.mediafile && this.mediafile.mediafile ? `${this.prefix}${this.fileName}` : null; + } + + public constructor(mediafile?: Mediafile, uploader?: User) { + super(); + this._mediafile = mediafile; + this._uploader = uploader; + } + + public getTitle(): string { + return this.title; + } + + public updateValues(update: BaseModel): void { + if (update instanceof Mediafile) { + if (this.mediafile.id === update.id) { + this._mediafile = update; + } + } + } +} diff --git a/client/src/app/site/mediafiles/services/mediafile-repository.service.spec.ts b/client/src/app/site/mediafiles/services/mediafile-repository.service.spec.ts new file mode 100644 index 000000000..a65a72d1f --- /dev/null +++ b/client/src/app/site/mediafiles/services/mediafile-repository.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { MediafileRepositoryService } from './mediafile-repository.service'; + +describe('FileRepositoryService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: MediafileRepositoryService = TestBed.get(MediafileRepositoryService); + expect(service).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/mediafiles/services/mediafile-repository.service.ts b/client/src/app/site/mediafiles/services/mediafile-repository.service.ts new file mode 100644 index 000000000..09e5ab70d --- /dev/null +++ b/client/src/app/site/mediafiles/services/mediafile-repository.service.ts @@ -0,0 +1,56 @@ +import { Injectable } from '@angular/core'; + +import { BaseRepository } from '../../base/base-repository'; +import { ViewMediafile } from '../models/view-mediafile'; +import { Mediafile } from '../../../shared/models/mediafiles/mediafile'; +import { User } from '../../../shared/models/users/user'; +import { Observable } from 'rxjs'; +import { DataStoreService } from '../../../core/services/data-store.service'; + +/** + * Repository for files + */ +@Injectable({ + providedIn: 'root' +}) +export class MediafileRepositoryService extends BaseRepository { + /** + * Consturctor for the file repo + * @param DS the DataStore + */ + public constructor(DS: DataStoreService) { + super(DS, Mediafile, [User]); + } + + /** + * Saves a config value. + * + * TODO: used over not-yet-existing detail view + */ + public save(file: Mediafile, viewFile: ViewMediafile): Observable { + return null; + } + + /** + * Saves a config value. + * + * TODO: used over not-yet-existing detail view + */ + public delete(file: ViewMediafile): Observable { + return null; + } + + /** + * Saves a config value. + * + * TODO: used over not-yet-existing detail view + */ + public create(file: Mediafile, viewFile: ViewMediafile): Observable { + return null; + } + + public createViewModel(file: Mediafile): ViewMediafile { + const uploader = this.DS.get(User, file.uploader_id); + return new ViewMediafile(file, uploader); + } +} diff --git a/client/src/app/site/motions/components/motion-list/motion-list.component.html b/client/src/app/site/motions/components/motion-list/motion-list.component.html index 85902db71..5bc811104 100644 --- a/client/src/app/site/motions/components/motion-list/motion-list.component.html +++ b/client/src/app/site/motions/components/motion-list/motion-list.component.html @@ -1,4 +1,5 @@ - +
@@ -10,7 +11,7 @@
- + Identifier diff --git a/client/src/app/site/motions/components/motion-list/motion-list.component.scss b/client/src/app/site/motions/components/motion-list/motion-list.component.scss index 1c4614221..47884887f 100644 --- a/client/src/app/site/motions/components/motion-list/motion-list.component.scss +++ b/client/src/app/site/motions/components/motion-list/motion-list.component.scss @@ -15,24 +15,7 @@ line-height: normal; } -mat-table { - width: 100%; - - /** hide mat header row */ - .mat-header-row { - display: none; - } - - /** size of the mat row */ - mat-row { - height: 60px; - } - - mat-row:hover { - cursor: pointer; - background-color: rgba(0, 0, 0, 0.025); - } - +.os-listview-table { /** identifier */ .mat-column-identifier { padding-left: 10px; diff --git a/client/src/app/site/motions/components/motion-list/motion-list.component.ts b/client/src/app/site/motions/components/motion-list/motion-list.component.ts index fdc3a2946..d35b48e6a 100644 --- a/client/src/app/site/motions/components/motion-list/motion-list.component.ts +++ b/client/src/app/site/motions/components/motion-list/motion-list.component.ts @@ -1,14 +1,13 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import { Title } from '@angular/platform-browser'; -import { MatTable, MatPaginator, MatSort, MatTableDataSource } from '@angular/material'; import { TranslateService } from '@ngx-translate/core'; -import { BaseComponent } from '../../../../base.component'; import { MotionRepositoryService } from '../../services/motion-repository.service'; import { ViewMotion } from '../../models/view-motion'; import { WorkflowState } from '../../../../shared/models/motions/workflow-state'; +import { ListViewBaseComponent } from '../../../base/list-view-base'; /** * Component that displays all the motions in a Table using DataSource. @@ -18,29 +17,7 @@ import { WorkflowState } from '../../../../shared/models/motions/workflow-state' templateUrl: './motion-list.component.html', styleUrls: ['./motion-list.component.scss'] }) -export class MotionListComponent extends BaseComponent implements OnInit { - /** - * Will be processed by the mat-table - * - * Will represent the object that comes from the repository - */ - public dataSource: MatTableDataSource; - - /** - * The table itself. - */ - @ViewChild(MatTable) public table: MatTable; - - /** - * Pagination. Might be turned off to all motions at once. - */ - @ViewChild(MatPaginator) public paginator: MatPaginator; - - /** - * Sort the Table - */ - @ViewChild(MatSort) public sort: MatSort; - +export class MotionListComponent extends ListViewBaseComponent implements OnInit { /** * Use for minimal width */ @@ -78,8 +55,8 @@ export class MotionListComponent extends BaseComponent implements OnInit { * @param repo Motion Repository */ public constructor( - protected titleService: Title, - protected translate: TranslateService, + titleService: Title, + translate: TranslateService, private router: Router, private route: ActivatedRoute, private repo: MotionRepositoryService @@ -88,15 +65,13 @@ export class MotionListComponent extends BaseComponent implements OnInit { } /** - * Init function + * Init function. + * + * Sets the title, inits the table and calls the repository */ public ngOnInit(): void { super.setTitle('Motions'); - - this.dataSource = new MatTableDataSource(); - this.dataSource.paginator = this.paginator; - this.dataSource.sort = this.sort; - + this.initTable(); this.repo.getViewModelListObservable().subscribe(newMotions => { this.dataSource.data = newMotions; }); @@ -163,15 +138,4 @@ export class MotionListComponent extends BaseComponent implements OnInit { public downloadMotions(): void { console.log('Download Motions Button'); } - - /** - * handler function for clicking on items in the ellipsis menu. - * - * @param event clicked entry from ellipsis menu - */ - public onEllipsisItem(event: any): void { - if (event.action) { - this[event.action](); - } - } } diff --git a/client/src/app/site/motions/models/view-motion.ts b/client/src/app/site/motions/models/view-motion.ts index 66671ea25..c1832f5f0 100644 --- a/client/src/app/site/motions/models/view-motion.ts +++ b/client/src/app/site/motions/models/view-motion.ts @@ -4,7 +4,7 @@ import { User } from '../../../shared/models/users/user'; import { Workflow } from '../../../shared/models/motions/workflow'; import { WorkflowState } from '../../../shared/models/motions/workflow-state'; import { BaseModel } from '../../../shared/models/base/base-model'; -import { BaseViewModel } from '../../base-view-model'; +import { BaseViewModel } from '../../base/base-view-model'; import { TranslateService } from '@ngx-translate/core'; /** @@ -27,43 +27,23 @@ export class ViewMotion extends BaseViewModel { } public get id(): number { - if (this.motion) { - return this.motion.id; - } else { - return null; - } + return this.motion ? this.motion.id : null; } public get identifier(): string { - if (this.motion) { - return this.motion.identifier; - } else { - return null; - } + return this.motion ? this.motion.identifier : null; } public get title(): string { - if (this.motion) { - return this.motion.title; - } else { - return null; - } + return this.motion ? this.motion.title : null; } public get text(): string { - if (this.motion) { - return this.motion.text; - } else { - return null; - } + return this.motion ? this.motion.text : null; } public get reason(): string { - if (this.motion) { - return this.motion.reason; - } else { - return null; - } + return this.motion ? this.motion.reason : null; } public get category(): Category { @@ -71,11 +51,7 @@ export class ViewMotion extends BaseViewModel { } public get categoryId(): number { - if (this._motion && this._motion.category_id) { - return this._motion.category_id; - } else { - return null; - } + return this.motion && this.category ? this.motion.category_id : null; } public get submitters(): User[] { @@ -95,15 +71,11 @@ export class ViewMotion extends BaseViewModel { } public get stateId(): number { - if (this._motion && this._motion.state_id) { - return this._motion.state_id; - } else { - return null; - } + return this.motion && this.motion.state_id ? this.motion.state_id : null; } public get recommendationId(): number { - return this._motion.recommendation_id; + return this.motion && this.motion.recommendation_id ? this.motion.recommendation_id : null; } /** @@ -118,27 +90,15 @@ export class ViewMotion extends BaseViewModel { } public get recommendation(): WorkflowState { - if (this.recommendationId && this.workflow) { - return this.workflow.getStateById(this.recommendationId); - } else { - return null; - } + return this.recommendationId && this.workflow ? this.workflow.getStateById(this.recommendationId) : null; } public get origin(): string { - if (this.motion) { - return this.motion.origin; - } else { - return null; - } + return this.motion ? this.motion.origin : null; } public get nextStates(): WorkflowState[] { - if (this.state && this.workflow) { - return this.state.getNextStates(this.workflow); - } else { - return null; - } + return this.state && this.workflow ? this.state.getNextStates(this.workflow) : null; } public constructor( diff --git a/client/src/app/site/motions/services/motion-repository.service.ts b/client/src/app/site/motions/services/motion-repository.service.ts index d2290aba7..0c3882e6f 100644 --- a/client/src/app/site/motions/services/motion-repository.service.ts +++ b/client/src/app/site/motions/services/motion-repository.service.ts @@ -8,7 +8,7 @@ import { Workflow } from '../../../shared/models/motions/workflow'; import { WorkflowState } from '../../../shared/models/motions/workflow-state'; import { ViewMotion } from '../models/view-motion'; import { Observable } from 'rxjs'; -import { BaseRepository } from '../../base-repository'; +import { BaseRepository } from '../../base/base-repository'; import { DataStoreService } from '../../../core/services/data-store.service'; /** diff --git a/client/src/app/site/settings/models/view-config.ts b/client/src/app/site/settings/models/view-config.ts new file mode 100644 index 000000000..a531bf758 --- /dev/null +++ b/client/src/app/site/settings/models/view-config.ts @@ -0,0 +1,38 @@ +import { BaseViewModel } from '../../base/base-view-model'; +import { BaseModel } from '../../../shared/models/base/base-model'; +import { Config } from '../../../shared/models/core/config'; + +export class ViewConfig extends BaseViewModel { + private _config: Config; + + public get config(): Config { + return this._config; + } + + public get id(): number { + return this.config ? this.config.id : null; + } + + public get key(): string { + return this.config ? this.config.key : null; + } + + public get value(): Object { + return this.config ? this.config.value : null; + } + + public constructor(config: Config) { + super(); + this._config = config; + } + + public getTitle(): string { + return this.key; + } + + public updateValues(update: BaseModel): void { + if (update instanceof Config && this.id === update.id) { + this._config = update; + } + } +} diff --git a/client/src/app/site/settings/services/config-repository.service.spec.ts b/client/src/app/site/settings/services/config-repository.service.spec.ts new file mode 100644 index 000000000..eeaaf6dc6 --- /dev/null +++ b/client/src/app/site/settings/services/config-repository.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { ConfigRepositoryService } from './config-repository.service'; + +describe('SettingsRepositoryService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: ConfigRepositoryService = TestBed.get(ConfigRepositoryService); + expect(service).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/settings/services/config-repository.service.ts b/client/src/app/site/settings/services/config-repository.service.ts new file mode 100644 index 000000000..1e5958049 --- /dev/null +++ b/client/src/app/site/settings/services/config-repository.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; + +import { BaseRepository } from '../../base/base-repository'; +import { ViewConfig } from '../models/view-config'; +import { Config } from '../../../shared/models/core/config'; +import { Observable } from 'rxjs'; +import { DataStoreService } from '../../../core/services/data-store.service'; + +/** + * Repository for Configs. + * + * Documentation provided over {@link BaseRepository} + */ +@Injectable({ + providedIn: 'root' +}) +export class ConfigRepositoryService extends BaseRepository { + /** + * Constructor for ConfigRepositoryService + */ + public constructor(DS: DataStoreService) { + super(DS, Config); + } + + /** + * Saves a config value. + * + * TODO: used over not-yet-existing detail view + */ + public save(config: Config, viewConfig: ViewConfig): Observable { + return null; + } + + /** + * This particular function should never be necessary since the creation of config + * values is not planed. + * + * Function exists solely to correctly implement {@link BaseRepository} + */ + public delete(config: ViewConfig): Observable { + return null; + } + + /** + * This particular function should never be necessary since the creation of config + * values is not planed. + * + * Function exists solely to correctly implement {@link BaseRepository} + */ + public create(config: Config, viewConfig: ViewConfig): Observable { + return null; + } + + /** + * Creates a new ViewConfig of a given Config object + * @param config + */ + public createViewModel(config: Config): ViewConfig { + return new ViewConfig(config); + } +} diff --git a/client/src/app/site/settings/settings-list/settings-list.component.html b/client/src/app/site/settings/settings-list/settings-list.component.html index e98db3b23..f395ee69d 100644 --- a/client/src/app/site/settings/settings-list/settings-list.component.html +++ b/client/src/app/site/settings/settings-list/settings-list.component.html @@ -1,20 +1,19 @@ - - + - -
- - - - - Title - - + + + + Key + {{config.key}} + -

CONTENT

-
-
+ + + Value + {{config.value}} + -
-
+ + +
diff --git a/client/src/app/site/settings/settings-list/settings-list.component.ts b/client/src/app/site/settings/settings-list/settings-list.component.ts index 460a0d824..59c0ba329 100644 --- a/client/src/app/site/settings/settings-list/settings-list.component.ts +++ b/client/src/app/site/settings/settings-list/settings-list.component.ts @@ -1,41 +1,61 @@ import { Component, OnInit } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { BaseComponent } from '../../../base.component'; import { ConstantsService } from '../../../core/services/constants.service'; +import { ListViewBaseComponent } from '../../base/list-view-base'; +import { ConfigRepositoryService } from '../services/config-repository.service'; +import { ViewConfig } from '../models/view-config'; /** * List view for the global settings * - * TODO: Not yet implemented */ @Component({ selector: 'os-settings-list', templateUrl: './settings-list.component.html', styleUrls: ['./settings-list.component.css'] }) -export class SettingsListComponent extends BaseComponent implements OnInit { +export class SettingsListComponent extends ListViewBaseComponent implements OnInit { /** * The usual component constructor * @param titleService * @param translate */ public constructor( - titleService: Title, + protected titleService: Title, protected translate: TranslateService, - private constantsService: ConstantsService + private repo: ConfigRepositoryService, + private constantsService: ConstantsService, ) { super(titleService, translate); } /** - * Init function. Sets the title + * Init function. + * + * Sets the title, inits the table and calls the repo + * + * TODO: Needs the constants to be working */ public ngOnInit(): void { super.setTitle('Settings'); - + this.initTable(); this.constantsService.get('OpenSlidesConfigVariables').subscribe(data => { console.log(data); }); + this.repo.getViewModelListObservable().subscribe(newConfig => { + this.dataSource.data = newConfig; + }); + } + + /** + * Triggers when user selects a row + * @param row + * + * TODO: This prints the clicked item in the log. + * Needs the constants to be working + */ + public selectConfig(row: ViewConfig): void { + console.log('change a config: ', row.value); } } diff --git a/client/src/app/site/users/models/view-user.ts b/client/src/app/site/users/models/view-user.ts new file mode 100644 index 000000000..1eabb7864 --- /dev/null +++ b/client/src/app/site/users/models/view-user.ts @@ -0,0 +1,68 @@ +import { BaseViewModel } from '../../base/base-view-model'; +import { User } from '../../../shared/models/users/user'; +import { Group } from '../../../shared/models/users/group'; +import { BaseModel } from '../../../shared/models/base/base-model'; + +export class ViewUser extends BaseViewModel { + private _user: User; + private _groups: Group[]; + + public get user(): User { + return this._user; + } + + public get groups(): Group[] { + return this._groups; + } + + public get fullName(): string { + return this.user ? this.user.full_name : null; + } + + /** + * TODO: Make boolean, use function over view component. + */ + public get isActive(): string { + return this.user && this.user.is_active ? 'active' : 'inactive'; + } + + public get structureLevel(): string { + return this.user ? this.user.structure_level : null; + } + + public constructor(user?: User, groups?: Group[]) { + super(); + this._user = user; + this._groups = groups; + } + + public getTitle(): string { + return this.user ? this.user.toString() : null; + } + + /** + * TODO: Implement + */ + public replaceGroup(newGroup: Group): void { + console.log('replace group - not yet implemented, ', newGroup); + } + + /** + * Updates values. Triggered through observables. + * + * @param update a new User or Group + */ + public updateValues(update: BaseModel): void { + if (update instanceof User) { + if (this.user.id === update.id) { + this._user = update; + } + } else if (update instanceof Group) { + if (this.user && this.user.groups_id) { + if (this.user.containsGroupId(update.id)) { + this.replaceGroup(update); + } + } + } + } +} diff --git a/client/src/app/site/users/services/user-repository.service.spec.ts b/client/src/app/site/users/services/user-repository.service.spec.ts new file mode 100644 index 000000000..946275c6f --- /dev/null +++ b/client/src/app/site/users/services/user-repository.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { UserRepositoryService } from './user-repository.service'; + +describe('UserRepositoryService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [UserRepositoryService] + }); + }); + + it('should be created', inject([UserRepositoryService], (service: UserRepositoryService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/site/users/services/user-repository.service.ts b/client/src/app/site/users/services/user-repository.service.ts new file mode 100644 index 000000000..e79e78da0 --- /dev/null +++ b/client/src/app/site/users/services/user-repository.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@angular/core'; + +import { BaseRepository } from '../../base/base-repository'; +import { ViewUser } from '../models/view-user'; +import { User } from '../../../shared/models/users/user'; +import { Group } from '../../../shared/models/users/group'; +import { Observable } from 'rxjs'; +import { DataStoreService } from '../../../core/services/data-store.service'; + +/** + * Repository service for users + * + * Documentation partially provided in {@link BaseRepository} + */ +@Injectable({ + providedIn: 'root' +}) +export class UserRepositoryService extends BaseRepository { + /** + * Constructor calls the parent constructor + */ + public constructor(DS: DataStoreService) { + super(DS, User, [Group]); + } + + /** + * @ignore + * + * TODO: used over not-yet-existing detail view + */ + public save(user: User, viewUser: ViewUser): Observable { + return null; + } + + /** + * @ignore + * + * TODO: used over not-yet-existing detail view + */ + public delete(user: ViewUser): Observable { + return null; + } + + /** + * @ignore + * + * TODO: used over not-yet-existing detail view + */ + public create(user: User, viewFile: ViewUser): Observable { + return null; + } + + public createViewModel(user: User): ViewUser { + const groups = this.DS.getMany(Group, user.groups_id); + return new ViewUser(user, groups); + } +} diff --git a/client/src/app/site/users/user-list/user-list.component.html b/client/src/app/site/users/user-list/user-list.component.html index de6d3ed0a..831b7b6ec 100644 --- a/client/src/app/site/users/user-list/user-list.component.html +++ b/client/src/app/site/users/user-list/user-list.component.html @@ -1,6 +1,27 @@ - + - - UserList Works! - + + + + Name + {{user.fullName}} + + + + + Group + {{user.groups}} {{user.structureLevel}} + + + + Presence + {{user.isActive}} + + + + + + + diff --git a/client/src/app/site/users/user-list/user-list.component.ts b/client/src/app/site/users/user-list/user-list.component.ts index 26c0ea84f..6be181ce8 100644 --- a/client/src/app/site/users/user-list/user-list.component.ts +++ b/client/src/app/site/users/user-list/user-list.component.ts @@ -1,32 +1,98 @@ import { Component, OnInit } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { BaseComponent } from '../../../base.component'; + +import { ViewUser } from '../models/view-user'; +import { UserRepositoryService } from '../services/user-repository.service'; +import { ListViewBaseComponent } from '../../base/list-view-base'; /** * Component for the user list view. * - * TODO: Not yet implemented */ @Component({ selector: 'os-user-list', templateUrl: './user-list.component.html', styleUrls: ['./user-list.component.css'] }) -export class UserListComponent extends BaseComponent implements OnInit { +export class UserListComponent extends ListViewBaseComponent implements OnInit { + /** + * content of the ellipsis menu + */ + public userMenuList = [ + { + text: 'Groups', + icon: 'users', + action: 'toGroups' + }, + { + text: 'Import', + icon: 'download', + action: 'toGroups' + }, + { + text: 'Export', + icon: 'file-export', + action: 'toGroups' + } + ]; + /** * The usual constructor for components + * @param repo the user repository * @param titleService * @param translate */ - public constructor(titleService: Title, protected translate: TranslateService) { + public constructor( + private repo: UserRepositoryService, + protected titleService: Title, + protected translate: TranslateService + ) { super(titleService, translate); } /** - * Init function, sets the title + * Init function + * + * sets the title, inits the table and calls the repo */ public ngOnInit(): void { super.setTitle('Users'); + this.initTable(); + this.repo.getViewModelListObservable().subscribe(newUsers => { + this.dataSource.data = newUsers; + }); + } + + /** + * Navigate to import page or do it inline + * + * TODO: implement importing of users + */ + public import(): void { + console.log('click on Import'); + } + + /** + * Navigate to groups page + * TODO: implement + */ + public toGroups(): void { + console.log('to Groups'); + } + + /** + * Handles the click on a user row + * @param row selected row + */ + public selectUser(row: ViewUser): void { + console.log('clicked the row for user: ', row); + } + + /** + * Handles the click on the plus button + */ + public onPlusButton(): void { + console.log('new User'); } } diff --git a/client/src/styles.scss b/client/src/styles.scss index b02499c9c..15d6640f6 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -39,6 +39,25 @@ body { margin-right: auto; } +.os-listview-table { + width: 100%; + + /** hide mat header row */ + .mat-header-row { + display: none; + } + + /** size of the mat row */ + mat-row { + height: 60px; + } + + mat-row:hover { + cursor: pointer; + background-color: rgba(0, 0, 0, 0.025); + } +} + .card-plus-distance { margin-top: 40px; }