commit b3e725456824c815d91b0f019a23bfa5d1db603d Author: Flo Date: Sat Aug 24 21:54:19 2024 +0000 first intermediate version diff --git a/README.md b/README.md new file mode 100644 index 0000000..682a0df --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# template + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.2.6. + +## Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. diff --git a/angular.json b/angular.json new file mode 100644 index 0000000..b8acc35 --- /dev/null +++ b/angular.json @@ -0,0 +1,125 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "template": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss", + "skipTests": true + }, + "@schematics/angular:class": { + "skipTests": true + }, + "@schematics/angular:directive": { + "skipTests": true + }, + "@schematics/angular:guard": { + "skipTests": true + }, + "@schematics/angular:module": { + }, + "@schematics/angular:pipe": { + "skipTests": true + }, + "@schematics/angular:service": { + "skipTests": true + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "assets": [ + "src/assets", + "src/favicon.ico" + ], + "styles": [ + "@angular/material/prebuilt-themes/deeppurple-amber.css", + "src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "800kb", + "maximumError": "2mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "template:build:production" + }, + "development": { + "browserTarget": "template:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "template:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "assets": [ + "src/assets", + "src/favicon.ico" + ], + "styles": [ + "@angular/material/prebuilt-themes/deeppurple-amber.scss", + "src/styles.scss" + ], + "scripts": [] + } + } + } + } + }, + "cli": { + "analytics": false + } +} diff --git a/bin/script/init b/bin/script/init new file mode 100755 index 0000000..8f28c30 --- /dev/null +++ b/bin/script/init @@ -0,0 +1,43 @@ +#!/bin/bash + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +PROJECT_DIR=$(realpath $SCRIPT_DIR/../../) +ENV_DIR=$(realpath $PROJECT_DIR/../../../) + +EXIT=0 + +# Check docker-compose.yml file +if [ ! -f "$PROJECT_DIR/docker/docker-compose.yml" ] +then + cp "$PROJECT_DIR/docker/docker-compose.yml.dist" "$PROJECT_DIR/docker/docker-compose.yml" + EXIT=1 +fi + +# Check docker-compose-mac.yml file +if [ ! -f "$PROJECT_DIR/docker/docker-compose-mac.yml" ] +then + cp "$PROJECT_DIR/docker/docker-compose-mac.yml.dist" "$PROJECT_DIR/docker/docker-compose-mac.yml" + EXIT=1 +fi + + +if [ $EXIT -eq 1 ] +then + echo "docker-compose or env files created, please change variables and call init again" + exit 1 +fi + +# Source key-scripts +source $ENV_DIR/bin/drun +source $ENV_DIR/bin/dexec + +# Build and start docker containers +dexec template-frontend build +dexec template-frontend up -d + +# Install node Packages +drun template-frontend npm install +drun template-frontend npm install -g @angular/cli + +# Initial build of website +drun template-frontend npm run build \ No newline at end of file diff --git a/docker/docker-compose-mac.yml.dist b/docker/docker-compose-mac.yml.dist new file mode 100644 index 0000000..07d9979 --- /dev/null +++ b/docker/docker-compose-mac.yml.dist @@ -0,0 +1,31 @@ +networks: + template: + external: true + +services: + template-frontend-app: + image: template-frontend-app + networks: + - template + volumes: + - /Users/flo/dev/frontend/template/:/var/www/ + build: + context: ./../ + dockerfile: ./docker/npm/dockerfile + tty: true + + template-frontend-nginx: + image: template-frontend-nginx + networks: + - template + volumes: + - /Users/flo/dev/frontend/template/:/var/www/html:z + build: + context: ./../ + dockerfile: ./docker/nginx/dockerfile + labels: + - "traefik.http.routers.frontend.rule=Host(`template.local`)" + - "traefik.http.routers.frontend.entrypoints=websecure" + - "traefik.http.routers.frontend.tls.certresolver=le" + depends_on: + - template-frontend-app \ No newline at end of file diff --git a/docker/docker-compose.yml.dist b/docker/docker-compose.yml.dist new file mode 100644 index 0000000..5dd392e --- /dev/null +++ b/docker/docker-compose.yml.dist @@ -0,0 +1,31 @@ +networks: + template: + external: true + +services: + template-frontend-app: + image: template-frontend-app + networks: + - template + volumes: + - ./../:/var/www/html + build: + context: ./../ + dockerfile: ./docker/npm/dockerfile + tty: true + + template-frontend-nginx: + image: template-frontend-nginx + networks: + - template + volumes: + - ./../:/var/www/html + build: + context: ./../ + dockerfile: ./docker/nginx/dockerfile + labels: + - "traefik.http.routers.frontend.rule=Host(`template.local`)" + - "traefik.http.routers.frontend.entrypoints=websecure" + - "traefik.http.routers.frontend.tls.certresolver=le" + depends_on: + - template-frontend-app \ No newline at end of file diff --git a/docker/nginx/config/nginx.conf b/docker/nginx/config/nginx.conf new file mode 100644 index 0000000..ffef825 --- /dev/null +++ b/docker/nginx/config/nginx.conf @@ -0,0 +1,27 @@ +upstream host-backend-nginx { + server template-backend-nginx:80; +} + + +server{ + listen 80 default_server; + + client_max_body_size 10000M; + + root /var/www/html/dist; + index index.html; + + location / { + try_files $uri$args $uri$args/ /index.html; + } + + location /api/ { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + + proxy_pass http://host-backend-nginx; + } +} \ No newline at end of file diff --git a/docker/nginx/dockerfile b/docker/nginx/dockerfile new file mode 100644 index 0000000..f2aba1c --- /dev/null +++ b/docker/nginx/dockerfile @@ -0,0 +1,5 @@ +FROM nginx:alpine + +COPY docker/nginx/config/nginx.conf /etc/nginx/conf.d/default.conf + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/npm/dockerfile b/docker/npm/dockerfile new file mode 100644 index 0000000..d6f8389 --- /dev/null +++ b/docker/npm/dockerfile @@ -0,0 +1,12 @@ +FROM node:20-slim + +WORKDIR /var/www/html + +# confirm installation +RUN node -v +RUN npm -v + +# use newest npm version +RUN npm install npm + +CMD ["/bin/bash"] \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..24e8935 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "template", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/animations": "^15.2.10", + "@angular/cdk": "^15.2.9", + "@angular/common": "^15.2.0", + "@angular/compiler": "^15.2.0", + "@angular/core": "^15.2.0", + "@angular/forms": "^15.2.0", + "@angular/material": "^15.2.9", + "@angular/platform-browser": "^15.2.0", + "@angular/platform-browser-dynamic": "^15.2.0", + "@angular/router": "^15.2.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.12.0" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^15.2.6", + "@angular/cli": "~15.2.6", + "@angular/compiler-cli": "^15.2.0", + "@types/jasmine": "~4.3.0", + "jasmine-core": "~4.5.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.0.0", + "tailwindcss": "^3.4.3", + "typescript": "~4.9.4" + } +} diff --git a/src/app/app.component.html b/src/app/app.component.html new file mode 100644 index 0000000..04cd27d --- /dev/null +++ b/src/app/app.component.html @@ -0,0 +1,2 @@ + + diff --git a/src/app/app.component.scss b/src/app/app.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/app.component.ts b/src/app/app.component.ts new file mode 100644 index 0000000..fe8da85 --- /dev/null +++ b/src/app/app.component.ts @@ -0,0 +1,9 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'] +}) +export class AppComponent { +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts new file mode 100644 index 0000000..bdc234c --- /dev/null +++ b/src/app/app.module.ts @@ -0,0 +1,44 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { HttpClientModule } from '@angular/common/http'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +import { AppComponent } from './app.component'; +import { RouterModule, Routes } from '@angular/router'; +import { SharedModule } from './shared/shared.module'; +import { NavigationComponent } from './component/navigation/navigation.component'; +import { HomeComponent } from './component/home/home.component'; + +const routes: Routes = [ + { path: 'home', component: HomeComponent }, + { + path: 'video', + loadChildren: () => + import('./feature/video/video.module').then((m) => m.VideoModule), + }, + { + path: 'analyze', + loadChildren: () => + import('./feature/analyze/analyze.module').then((m) => m.AnalyzeModule), + }, + { + path: 'tag', + loadChildren: () => + import('./feature/tag/tag.module').then((m) => m.TagModule), + }, + { path: '', redirectTo: 'home', pathMatch: 'full' }, +]; + +@NgModule({ + declarations: [AppComponent, NavigationComponent, HomeComponent], + imports: [ + BrowserModule, + BrowserAnimationsModule, + HttpClientModule, + RouterModule.forRoot(routes), + SharedModule, + ], + providers: [], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/src/app/app.service.ts b/src/app/app.service.ts new file mode 100644 index 0000000..e65248d --- /dev/null +++ b/src/app/app.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class AppService { + + private username: string|null; + + constructor() { + this.username = null; + } + + public getUsername(): string|null { + return this.username; + } + + public setUsername(name: string|null): void { + this.username = name; + } +} diff --git a/src/app/core/components/home/home.component.html b/src/app/core/components/home/home.component.html new file mode 100644 index 0000000..db3a4e3 --- /dev/null +++ b/src/app/core/components/home/home.component.html @@ -0,0 +1,9 @@ +
+ +
+ + diff --git a/src/app/core/components/home/home.component.scss b/src/app/core/components/home/home.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/core/components/home/home.component.ts b/src/app/core/components/home/home.component.ts new file mode 100644 index 0000000..f67627a --- /dev/null +++ b/src/app/core/components/home/home.component.ts @@ -0,0 +1,42 @@ +import { Component } from '@angular/core'; +import { TagListEntry } from 'src/app/model/TagListEntry'; +import { VideoListEntry } from 'src/app/model/VideoListEntry'; +import { RequestService } from 'src/app/request.service'; +import { OnReadListModel } from 'src/app/shared/components/video-list/video-list.component'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'], +}) +export class HomeComponent { + total: number = 0; + videos: VideoListEntry[] = []; + tags: TagListEntry[] = []; + + constructor(public requestService: RequestService) { + this.readTagList(); + } + + readList(model: OnReadListModel): void { + this.requestService.post( + 'video-list/read-list', + { + query: model.query, + page: model.page, + perPage: model.perPage, + sortBy: model.sortBy, + }, + (response: any) => { + this.videos = response.items; + this.total = response.total; + } + ); + } + + readTagList(): void { + this.requestService.post('tag-list/read-list', {}, (response: any) => { + this.tags = response.items; + }); + } +} diff --git a/src/app/core/components/navigation/navigation.component.html b/src/app/core/components/navigation/navigation.component.html new file mode 100644 index 0000000..c7d7d94 --- /dev/null +++ b/src/app/core/components/navigation/navigation.component.html @@ -0,0 +1,15 @@ + +

+ Template +

+
+ + diff --git a/src/app/core/components/navigation/navigation.component.scss b/src/app/core/components/navigation/navigation.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/core/components/navigation/navigation.component.ts b/src/app/core/components/navigation/navigation.component.ts new file mode 100644 index 0000000..b89583a --- /dev/null +++ b/src/app/core/components/navigation/navigation.component.ts @@ -0,0 +1,28 @@ +import { Component } from '@angular/core'; + +interface Link { + routerLink: string; + caption: string; +} + +@Component({ + selector: 'app-navigation', + templateUrl: './navigation.component.html', + styleUrls: ['./navigation.component.scss'], +}) +export class NavigationComponent { + links: Link[] = [ + { + routerLink: '/video/upload', + caption: 'Upload', + }, + { + routerLink: '/analyze', + caption: 'Analyse', + }, + { + routerLink: '/tag/list', + caption: 'Tags', + }, + ]; +} diff --git a/src/app/request.service.ts b/src/app/request.service.ts new file mode 100644 index 0000000..38ec3fa --- /dev/null +++ b/src/app/request.service.ts @@ -0,0 +1,105 @@ +import { HttpClient } from '@angular/common/http'; +import { Router } from '@angular/router'; +import { Injectable } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Injectable({ + providedIn: 'root' +}) +export class RequestService { + + constructor( + private http: HttpClient, + private router: Router, + private snackBar: MatSnackBar + ) { + } + + post(apiPath: string, body: any, fct: Function) + { + let url = this.obtainUrl(apiPath); + let observable = this.http.post(url, body).subscribe( + (answer:any) => { + fct(answer); + }, + (error:any) => { + this.handleError(error); + } + ); + } + + postFiles(apiPath: string, files: File[], fct: Function) { + if (!files || files.length === 0) { + throw 'Need to select at least one file'; + } + + const formData = new FormData(); + + for (let fileIndex = 0; fileIndex < files.length; fileIndex++) { + formData.append('file' + fileIndex, files[fileIndex]); + } + + let url = this.obtainUrl(apiPath); + let observable = this.http.post(url, formData).subscribe( + (answer: any) => { + fct(answer); + }, + (error: any) => { + this.handleError(error); + } + ); + } + + get(apiPath: string, body: any, fct: Function) + { + let url = this.obtainUrl(apiPath); + let observable = this.http.get(url, body).subscribe( + (answer:any) => { + fct(answer); + }, + (error:any) => { + this.handleError(error); + } + ); + } + + private obtainUrl(apiPath: string): string { + let hostString = window.location.host; + let protocol = window.location.protocol; + return protocol + '//' + hostString + '/api/' + apiPath; + } + + private handleError(answer: any): void { + + if (answer.status == 401) { + console.log('Deine Sitzung konnte nicht gefunden werden'); + this.router.navigate(['/auth']); + return; + } + + if (answer.status == 403) { + this.showSnackBar('Du bist nicht für diese Aktion autorisiert', 'Ok'); + return; + } + + try { + let errorObject = answer.error.error; + + if (errorObject.hasOwnProperty('message')) { + throw errorObject.message.toString(); + } + + if (errorObject.hasOwnProperty('code')) { + throw errorObject.code.toString(); + } + } catch(error:any) { + this.showSnackBar(error.toString(), 'Ok'); + } + } + + private showSnackBar(message: string, action?: string) { + this.snackBar.open(message.toString(), action, { + duration: 3000, + }); + } +} diff --git a/src/app/shared/components/card/card.component.html b/src/app/shared/components/card/card.component.html new file mode 100644 index 0000000..02ae685 --- /dev/null +++ b/src/app/shared/components/card/card.component.html @@ -0,0 +1,31 @@ +
+ + +
+
"{{ content }}"
+
+ +
+
+
diff --git a/src/app/shared/components/card/card.component.scss b/src/app/shared/components/card/card.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/card/card.component.ts b/src/app/shared/components/card/card.component.ts new file mode 100644 index 0000000..cde549d --- /dev/null +++ b/src/app/shared/components/card/card.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'shared-card', + templateUrl: './card.component.html', + styleUrls: ['./card.component.scss'], +}) +export class CardComponent { + @Input() header: string | null = null; + @Input() icon: string | null = null; + @Input() subHeader: string | null = null; + @Input() content: string | null = null; +} diff --git a/src/app/shared/components/form/form.component.html b/src/app/shared/components/form/form.component.html new file mode 100644 index 0000000..f500cd5 --- /dev/null +++ b/src/app/shared/components/form/form.component.html @@ -0,0 +1,21 @@ +
+ {{ caption }} +
+ +
+
+ +
+
+
+ +
+
diff --git a/src/app/shared/components/form/form.component.scss b/src/app/shared/components/form/form.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/form/form.component.ts b/src/app/shared/components/form/form.component.ts new file mode 100644 index 0000000..7a0e704 --- /dev/null +++ b/src/app/shared/components/form/form.component.ts @@ -0,0 +1,12 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'shared-form', + templateUrl: './form.component.html', + styleUrls: ['./form.component.scss'] +}) +export class FormComponent { + @Output() submit = new EventEmitter(); + @Input() caption: string|null = null; + +} diff --git a/src/app/shared/components/paginator/paginator.component.html b/src/app/shared/components/paginator/paginator.component.html new file mode 100644 index 0000000..8961ffe --- /dev/null +++ b/src/app/shared/components/paginator/paginator.component.html @@ -0,0 +1,69 @@ + diff --git a/src/app/shared/components/paginator/paginator.component.scss b/src/app/shared/components/paginator/paginator.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/paginator/paginator.component.ts b/src/app/shared/components/paginator/paginator.component.ts new file mode 100644 index 0000000..93b4529 --- /dev/null +++ b/src/app/shared/components/paginator/paginator.component.ts @@ -0,0 +1,80 @@ +import { + Component, + EventEmitter, + Input, + OnChanges, + Output, + SimpleChanges, +} from '@angular/core'; + +export interface PaginatorItem { + page: number | null; +} + +@Component({ + selector: 'shared-paginator', + templateUrl: './paginator.component.html', + styleUrls: ['./paginator.component.scss'], +}) +export class PaginatorComponent implements OnChanges { + @Output() pageChange = new EventEmitter(); + @Input() page: number = 1; + @Input() perPage: number = 10; + @Input() total: number | null = null; + items: PaginatorItem[] = []; + + ngOnChanges(changes: SimpleChanges): void { + this.calculateItems(); + } + + protected setActivePage(newPage: number | null) { + if (newPage === null) { + return; + } + if (newPage === -1) { + newPage = this.page - 1; + } + if (newPage === -2) { + newPage = this.page + 1; + } + + if (newPage <= 0 || newPage > this.getMaxPages()) return; + + this.page = newPage; + this.pageChange.emit(newPage); + this.calculateItems(); + } + + private calculateItems(): void { + this.items = []; + + let first = 1; + let last = this.getMaxPages(); + let rangeFirst = this.page - 1; + let rangeLast = this.page + 1; + + if (this.page === 1) { + rangeFirst = 1; + rangeLast = last < 3 ? last : 3; + } + + if (this.page === last) { + rangeFirst = Math.max(1, last - 2); + rangeLast = last; + } + + if (rangeFirst > 1) this.items.push({ page: first }); + if (rangeFirst > 2) this.items.push({ page: null }); + + for (let index = rangeFirst; index <= rangeLast; index++) { + this.items.push({ page: index }); + } + + if (rangeLast < last - 1) this.items.push({ page: null }); + if (rangeLast < last) this.items.push({ page: last }); + } + + private getMaxPages(): number { + return Math.ceil((this.total ?? 0) / this.perPage); + } +} diff --git a/src/app/shared/components/tab-control/tab-control.component.html b/src/app/shared/components/tab-control/tab-control.component.html new file mode 100644 index 0000000..f6b074f --- /dev/null +++ b/src/app/shared/components/tab-control/tab-control.component.html @@ -0,0 +1,20 @@ +
+
    +
  • + +
  • +
+
+ +
+ +
diff --git a/src/app/shared/components/tab-control/tab-control.component.scss b/src/app/shared/components/tab-control/tab-control.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/tab-control/tab-control.component.ts b/src/app/shared/components/tab-control/tab-control.component.ts new file mode 100644 index 0000000..a4a0552 --- /dev/null +++ b/src/app/shared/components/tab-control/tab-control.component.ts @@ -0,0 +1,30 @@ +import { Component, ContentChildren, EventEmitter, Input, Output, QueryList } from '@angular/core'; +import { TabItem } from '../../models/TabItem'; + +@Component({ + selector: 'shared-tab-control', + templateUrl: './tab-control.component.html', + styleUrls: ['./tab-control.component.scss'] +}) +export class TabControlComponent { + @Input() public tabs: TabItem[] = []; + @Input() public activeTabId: string = ''; + @Output() public activeTabIdChange = new EventEmitter(); + + @ContentChildren('tabContent') tabContents!: QueryList; + + public activeIndex = 0; + + public ngOnChanges() { + this.setActiveTab(null); + } + + public setActiveTab(tab: TabItem|null) { + if (tab !== null) { + this.activeTabId = tab.id; + this.activeTabIdChange.emit(this.activeTabId); + } + + this.activeIndex = this.tabs.findIndex((i) => i.id === this.activeTabId); + } +} diff --git a/src/app/shared/components/table/table.component.html b/src/app/shared/components/table/table.component.html new file mode 100644 index 0000000..f94ab58 --- /dev/null +++ b/src/app/shared/components/table/table.component.html @@ -0,0 +1,54 @@ + + + + + + + + + + + +
+
+
+ +
+
+
+ +
+
+ {{ column.columnContent }} +
+
+
+
+
diff --git a/src/app/shared/components/table/table.component.scss b/src/app/shared/components/table/table.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/table/table.component.ts b/src/app/shared/components/table/table.component.ts new file mode 100644 index 0000000..9e426a2 --- /dev/null +++ b/src/app/shared/components/table/table.component.ts @@ -0,0 +1,31 @@ +import { Component, Input } from '@angular/core'; + +export interface ColumnDefinition { + header: string; + columnContent?: string | undefined; + columnFunction?: Function | undefined; + routerLink?: Function | undefined; + headerCss?: string | undefined; + columnCss?: string | undefined; +} + +@Component({ + selector: 'shared-table', + templateUrl: './table.component.html', + styleUrls: ['./table.component.scss'], +}) +export class TableComponent { + @Input() foreground: string = 'text-white'; + @Input() background: string = 'bg-zinc-800'; + @Input() backgroundColumn: string = 'bg-zinc-800'; + @Input() backgroundColumnAlternate: string = 'bg-zinc-700'; + @Input() border: string = 'border-zinc-600'; + + @Input() items: any[] = []; + @Input() columns: ColumnDefinition[] = []; + + isBoolean(obje: any): boolean { + console.log(obje); + return typeof obje === 'boolean'; + } +} diff --git a/src/app/shared/models/TabItem.ts b/src/app/shared/models/TabItem.ts new file mode 100644 index 0000000..6200d6d --- /dev/null +++ b/src/app/shared/models/TabItem.ts @@ -0,0 +1,4 @@ +export interface TabItem { + id: string; + title: string; +} \ No newline at end of file diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts new file mode 100644 index 0000000..922416b --- /dev/null +++ b/src/app/shared/shared.module.ts @@ -0,0 +1,115 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { CommonModule } from '@angular/common'; +import { FormComponent } from './components/form/form.component'; +import { CardComponent } from './components/card/card.component'; +import { PaginatorComponent } from './components/paginator/paginator.component'; +import { TabControlComponent } from './components/tab-control/tab-control.component'; +import { VideoPresenterComponent } from './components/video-presenter/video-presenter.component'; +import { ImagePresenterComponent } from './components/image-presenter/image-presenter.component'; +import { TableComponent } from './components/table/table.component'; +import { FormsModule } from '@angular/forms'; +import { MatInputModule } from '@angular/material/input'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatListModule } from '@angular/material/list'; +import { MatIconModule } from '@angular/material/icon'; +import { MatGridListModule } from '@angular/material/grid-list'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatTableModule } from '@angular/material/table'; +import { MatChipsModule } from '@angular/material/chips'; +import { VideoListComponent } from './components/video-list/video-list.component'; +import { VideoCardComponent } from './components/video-card/video-card.component'; +import { TagCardComponent } from './components/tag-card/tag-card.component'; +import { TagSelectorComponent } from './components/tag-selector/tag-selector.component'; + +@NgModule({ + declarations: [ + ImagePresenterComponent, + VideoPresenterComponent, + CardComponent, + TabControlComponent, + PaginatorComponent, + FormComponent, + TableComponent, + VideoListComponent, + VideoCardComponent, + TagCardComponent, + TagSelectorComponent, + ], + exports: [ + ImagePresenterComponent, + VideoPresenterComponent, + CardComponent, + TabControlComponent, + PaginatorComponent, + FormComponent, + TableComponent, + VideoListComponent, + VideoCardComponent, + TagSelectorComponent, + TagCardComponent, + + MatSlideToggleModule, + MatCardModule, + MatButtonModule, + MatIconModule, + MatGridListModule, + MatFormFieldModule, + FormsModule, + MatExpansionModule, + MatMenuModule, + MatListModule, + MatToolbarModule, + MatSidenavModule, + MatInputModule, + MatSelectModule, + MatDividerModule, + MatDialogModule, + MatSnackBarModule, + MatPaginatorModule, + MatCheckboxModule, + MatTabsModule, + MatTableModule, + MatChipsModule, + ], + imports: [ + RouterModule, + CommonModule, + MatSlideToggleModule, + MatCardModule, + MatButtonModule, + MatIconModule, + MatGridListModule, + MatFormFieldModule, + FormsModule, + MatExpansionModule, + MatMenuModule, + MatListModule, + MatToolbarModule, + MatSidenavModule, + MatInputModule, + MatSelectModule, + MatDividerModule, + MatDialogModule, + MatSnackBarModule, + MatPaginatorModule, + MatCheckboxModule, + MatTabsModule, + MatTableModule, + MatChipsModule, + ], +}) +export class SharedModule {} diff --git a/src/assets/icon.ico b/src/assets/icon.ico new file mode 100644 index 0000000..c909290 Binary files /dev/null and b/src/assets/icon.ico differ diff --git a/src/favicon.ico b/src/favicon.ico new file mode 100644 index 0000000..e7ff278 Binary files /dev/null and b/src/favicon.ico differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..e9efc5c --- /dev/null +++ b/src/index.html @@ -0,0 +1,15 @@ + + + + + Template + + + + + + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..c58dc05 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,7 @@ +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; + + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/src/styles.scss b/src/styles.scss new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/src/styles.scss @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..5397866 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./src/**/*.{html,ts}"], + theme: { + extend: {}, + }, + plugins: [], +} + diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..374cc9d --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ed966d4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,33 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "useDefineForClassFields": false, + "lib": [ + "ES2022", + "dom" + ] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000..be7e9da --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +}