@@ -0,0 +1,26 @@
|
||||
name: Docker Compose Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Install docker-compose (if needed)
|
||||
run: sudo apt-get install docker-compose
|
||||
|
||||
- name: Build and push Docker images
|
||||
run: |
|
||||
cd src/Docker
|
||||
docker-compose build
|
||||
@@ -0,0 +1,67 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
|
||||
|
||||
name: Java CI with Gradle
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 24
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '24'
|
||||
distribution: 'temurin'
|
||||
|
||||
# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
|
||||
# See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
|
||||
|
||||
- name: Build with Gradle Wrapper
|
||||
run: ./gradlew build
|
||||
|
||||
# NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html).
|
||||
# If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version.
|
||||
#
|
||||
# - name: Setup Gradle
|
||||
# uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
|
||||
# with:
|
||||
# gradle-version: '8.9'
|
||||
#
|
||||
# - name: Build with Gradle 8.9
|
||||
# run: gradle build
|
||||
|
||||
dependency-submission:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 24
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '24'
|
||||
distribution: 'temurin'
|
||||
|
||||
# Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies.
|
||||
# See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#FROM openjdk:24 AS BUILD_IMAGE
|
||||
#ENV APP_HOME=/jambotron
|
||||
#RUN mkdir -p $APP_HOME/src/main/java
|
||||
#WORKDIR $APP_HOME
|
||||
#COPY ./build.gradle ./gradlew ./gradlew.bat $APP_HOME/
|
||||
#COPY gradle $APP_HOME/gradle
|
||||
#COPY ./src/ $APP_HOME/src/
|
||||
#RUN ./gradlew clean build
|
||||
|
||||
FROM openjdk:24
|
||||
WORKDIR /jambotron/
|
||||
COPY '/build/libs/jambotron-0.0.1-SNAPSHOT.jar' '/app/jambotron.jar'
|
||||
#COPY --from=BUILD_IMAGE '/jambotron/build/libs/jambotron-0.0.1-SNAPSHOT.jar' '/app/jambotron.jar'
|
||||
EXPOSE 8080
|
||||
CMD ["java","-jar","/app/jambotron.jar"]
|
||||
Generated
+1511
-1
File diff suppressed because it is too large
Load Diff
@@ -29,8 +29,10 @@
|
||||
"@primeng/themes": "^19.1.3",
|
||||
"bootstrap": "5.3.6",
|
||||
"express": "^5.1.0",
|
||||
"ngx-markdown": "^20.0.0",
|
||||
"ngx-scrollbar": "18.0.0",
|
||||
"primeng": "^19.1.3",
|
||||
"prismjs": "^1.30.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"viewerjs": "^1.11.7",
|
||||
|
||||
@@ -12,6 +12,12 @@ import {AppModule} from './app.module';
|
||||
import {providePrimeNG} from 'primeng/config';
|
||||
import Aura from '@primeng/themes/aura';
|
||||
import {provideAnimations} from '@angular/platform-browser/animations';
|
||||
import {provideMarkdown} from 'ngx-markdown';
|
||||
|
||||
import 'prismjs';
|
||||
import 'prismjs/components/prism-typescript.min.js';
|
||||
import 'prismjs/plugins/line-numbers/prism-line-numbers.js';
|
||||
import 'prismjs/plugins/line-highlight/prism-line-highlight.js';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
@@ -19,7 +25,7 @@ export const appConfig: ApplicationConfig = {
|
||||
provideAnimations(),
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(routes), provideClientHydration(withEventReplay()),
|
||||
|
||||
provideMarkdown(),
|
||||
importProvidersFrom(AppModule),
|
||||
providePrimeNG({
|
||||
theme: {
|
||||
|
||||
@@ -16,9 +16,7 @@ import {SystemComponent} from './admin-module/system.component/system.component'
|
||||
import {AppRoutingModule} from './app.routes';
|
||||
import {App} from './app';
|
||||
import {CustomHttpInterceptor} from './helpers/custom-http-interceptor';
|
||||
import {AngularImageViewerModule} from '@hreimer/angular-image-viewer';
|
||||
import {MainModule} from './main-module/main.module';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
||||
@@ -38,14 +38,26 @@
|
||||
<mat-card-header>
|
||||
<mat-card-title>Add tutorial</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content *ngIf="!submitted">
|
||||
<mat-card-content >
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Title</mat-label>
|
||||
<input matInput required
|
||||
[(ngModel)]="tutorial.title">
|
||||
</mat-form-field>
|
||||
<!-- <kendo-editor [(ngModel)]="tutorial.description" ></kendo-editor>-->
|
||||
<textarea matInput required [(ngModel)]="tutorial.description"></textarea>
|
||||
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Markdown text">
|
||||
<textarea class="variable-textarea" [(ngModel)]="tutorial.description"></textarea>
|
||||
<markdown class="variable-binding" [data]="tutorial.description"></markdown>
|
||||
</mat-tab>
|
||||
<mat-tab label="Result">
|
||||
<markdown class="preview" [data]="tutorial.description"></markdown>
|
||||
</mat-tab>
|
||||
<mat-tab label="Example">
|
||||
<textarea class="variable-textarea" [(ngModel)]="markdown"></textarea>
|
||||
<markdown class="variable-binding" [data]="markdown"></markdown>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button matButton (click)="saveTutorial()" *ngIf="!submitted">Save</button>
|
||||
|
||||
@@ -19,3 +19,34 @@ mat-card-title{
|
||||
.example-full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.variable-binding,
|
||||
.variable-textarea {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.variable-textarea {
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.07);
|
||||
min-height: 420px;
|
||||
padding: 8px;
|
||||
transition: all 300ms ease-out;
|
||||
}
|
||||
|
||||
.variable-textarea:hover {
|
||||
box-shadow: 0 6px 12px 3px rgba(0,0,0,.09),
|
||||
0 2px 3px 1px rgba(0,0,0,.06);
|
||||
}
|
||||
|
||||
.variable-binding {
|
||||
display: block;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.preview {
|
||||
/* display: block;
|
||||
float: right;*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import {FormsModule} from '@angular/forms';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
|
||||
import {NgIf} from '@angular/common';
|
||||
import {MatTab, MatTabGroup} from '@angular/material/tabs';
|
||||
import {MarkdownComponent} from 'ngx-markdown';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-tutorial',
|
||||
@@ -24,6 +26,9 @@ import {NgIf} from '@angular/common';
|
||||
MatInput,
|
||||
NgIf,
|
||||
MatLabel,
|
||||
MatTabGroup,
|
||||
MatTab,
|
||||
MarkdownComponent,
|
||||
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
@@ -35,9 +40,30 @@ export class AddTutorialComponent implements OnInit {
|
||||
description: '',
|
||||
published: false
|
||||
};
|
||||
submitted = false;
|
||||
submitted = false
|
||||
|
||||
constructor(private tutorialService: TutorialService) { }
|
||||
markdown = `## Markdown __rulez__!
|
||||
---
|
||||
|
||||
### Syntax highlight
|
||||
\`\`\`typescript
|
||||
const language = 'typescript';
|
||||
\`\`\`
|
||||
|
||||
### Lists
|
||||
1. Ordered list
|
||||
2. Another bullet point
|
||||
- Unordered list
|
||||
- Another unordered bullet
|
||||
|
||||
### Blockquote
|
||||
> Blockquote to the max`;
|
||||
|
||||
|
||||
|
||||
constructor(private tutorialService: TutorialService) {
|
||||
this.tutorial.description = this.markdown;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<h1 mat-dialog-title>Hi </h1>
|
||||
<form [formGroup]="signUpForm">
|
||||
<div mat-dialog-content>
|
||||
<p>User name</p>
|
||||
<mat-form-field class="fillField">
|
||||
<input matInput
|
||||
formControlName="username"
|
||||
[(ngModel)]="data.username"
|
||||
minlength="3"
|
||||
>
|
||||
</mat-form-field>
|
||||
<p>Email</p>
|
||||
<mat-form-field class="fillField">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput type="email"
|
||||
formControlName="email"
|
||||
|
||||
placeholder="Ex. pat@example.com"
|
||||
[(ngModel)]="data.email"
|
||||
(blur)="updateErrorMessage()"
|
||||
required
|
||||
>
|
||||
@if (emailFormControl.invalid) {
|
||||
<mat-error>{{errorMessage()}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<p>Password</p>
|
||||
<mat-form-field class="fillField">
|
||||
<mat-label>Enter your password</mat-label>
|
||||
<input matInput
|
||||
|
||||
formControlName = "password"
|
||||
[type]="hide() ? 'password' : 'text'"
|
||||
[(ngModel)]="data.password"
|
||||
minlength="6"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
mat-icon-button
|
||||
matSuffix
|
||||
(click)="clickEvent($event)"
|
||||
[attr.aria-label]="'Hide password'"
|
||||
[attr.aria-pressed]="hide()"
|
||||
>
|
||||
<mat-icon>{{hide() ? 'visibility_off' : 'visibility'}}</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
<p>Confirm password</p>
|
||||
<mat-form-field class="fillField">
|
||||
<mat-label>Confirm your password</mat-label>
|
||||
<input matInput
|
||||
|
||||
formControlName = "confirmPassword"
|
||||
[type]="hide() ? 'password' : 'text'"
|
||||
[(ngModel)]="data.confirmPassword"
|
||||
/>
|
||||
@if (signUpForm.get("confirmPassword")?.value !== signUpForm.get("password")?.value) {
|
||||
<mat-error>Passwords do not match</mat-error>
|
||||
}
|
||||
<button
|
||||
mat-icon-button
|
||||
matSuffix
|
||||
(click)="clickEvent($event)"
|
||||
[attr.aria-label]="'Hide password'"
|
||||
[attr.aria-pressed]="hide()"
|
||||
>
|
||||
<mat-icon>{{hide() ? 'visibility_off' : 'visibility'}}</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="login()">Login</button>
|
||||
<span class="menu-spacer"></span>
|
||||
<button mat-button mat-dialog-close (click)="onNoClick()">No Thanks</button>
|
||||
<button mat-button cdkFocusInitial (click)="signup()">Ok</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,7 @@
|
||||
.menu-spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.fillField{
|
||||
width: 100%;
|
||||
};
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DialogSignupComponent } from './dialog-signup.component';
|
||||
|
||||
describe('DialogSignupComponent', () => {
|
||||
let component: DialogSignupComponent;
|
||||
let fixture: ComponentFixture<DialogSignupComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DialogSignupComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DialogSignupComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, EventEmitter, Inject, inject, OnInit, Output, signal} from '@angular/core';
|
||||
import {
|
||||
AbstractControl,
|
||||
FormBuilder,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
FormGroupDirective,
|
||||
FormsModule,
|
||||
NgForm,
|
||||
ReactiveFormsModule, ValidationErrors, ValidatorFn,
|
||||
Validators
|
||||
} from '@angular/forms';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialogActions,
|
||||
MatDialogClose,
|
||||
MatDialogContent,
|
||||
MatDialogRef,
|
||||
MatDialogTitle
|
||||
} from '@angular/material/dialog';
|
||||
import {MatFormField, MatInput, MatLabel, MatSuffix} from '@angular/material/input';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {DialogSignupData} from '../../main-module/main.component/main.component';
|
||||
import {ErrorStateMatcher} from '@angular/material/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dialog-signup.component',
|
||||
imports: [
|
||||
FormsModule,
|
||||
MatButtonModule,
|
||||
MatDialogActions,
|
||||
MatDialogClose,
|
||||
MatDialogContent,
|
||||
MatDialogTitle,
|
||||
MatFormField,
|
||||
MatIcon,
|
||||
MatInput,
|
||||
MatLabel,
|
||||
MatSuffix,
|
||||
MatFormField,
|
||||
ReactiveFormsModule,
|
||||
|
||||
],
|
||||
templateUrl: './dialog-signup.component.html',
|
||||
styleUrl: './dialog-signup.component.scss',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class DialogSignupComponent implements OnInit {
|
||||
readonly dialogRef = inject(MatDialogRef<DialogSignupComponent>);
|
||||
hide = signal(true);
|
||||
|
||||
@Output() signupClicked = new EventEmitter<any>();
|
||||
@Output() loginClicked = new EventEmitter<any>();
|
||||
|
||||
private _snackBar = inject(MatSnackBar);
|
||||
|
||||
durationInSeconds = 5;
|
||||
|
||||
emailFormControl = new FormControl('', [Validators.required, Validators.email]);
|
||||
|
||||
matcher = new MyErrorStateMatcher();
|
||||
|
||||
errorMessage = signal('');
|
||||
|
||||
signUpForm = new FormGroup({
|
||||
username: new FormControl('', [Validators.required]),
|
||||
email: new FormControl('', [Validators.required, Validators.email]),
|
||||
password: new FormControl('', [Validators.required]),
|
||||
confirmPassword: new FormControl('', [Validators.required, this.validateSamePassword]),
|
||||
});
|
||||
minPw = 8;
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public data:DialogSignupData,
|
||||
private formBuilder: FormBuilder) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
|
||||
updateErrorMessage() {
|
||||
if (this.emailFormControl.hasError('required')) {
|
||||
this.errorMessage.set('You must enter a value');
|
||||
} else if (this.emailFormControl.hasError('email')) {
|
||||
this.errorMessage.set('Not a valid email');
|
||||
} else {
|
||||
this.errorMessage.set('');
|
||||
}
|
||||
}
|
||||
|
||||
clickEvent(event: MouseEvent) {
|
||||
this.hide.set(!this.hide());
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
onNoClick() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
openSignupFailedSnackBar(errorMessage : string = "Sign up failed.") {
|
||||
this._snackBar.open(errorMessage , "", {
|
||||
duration: this.durationInSeconds * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
openSignupSuccessSnackBar(message : string = "User registered successfully!") {
|
||||
this._snackBar.open(message, "", {
|
||||
duration: this.durationInSeconds * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
signup() {
|
||||
if (!this.signUpForm.invalid) {
|
||||
this.signupClicked.emit(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
login() {
|
||||
this.loginClicked.emit();
|
||||
}
|
||||
|
||||
private validateSamePassword(control: AbstractControl): ValidationErrors | null {
|
||||
const password = control.parent?.get('password');
|
||||
const confirmPassword = control.parent?.get('confirmPassword');
|
||||
return password?.value == confirmPassword?.value ? null : { 'notSame': true };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Error when invalid control is dirty, touched, or submitted. */
|
||||
export class MyErrorStateMatcher implements ErrorStateMatcher {
|
||||
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||
const isSubmitted = form && form.submitted;
|
||||
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@
|
||||
</div>
|
||||
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="signup()">Sign Up</button>
|
||||
<span class="menu-spacer"></span>
|
||||
<button mat-button mat-dialog-close (click)="onNoClick()">No Thanks</button>
|
||||
<button mat-button [mat-dialog-close]="data" cdkFocusInitial>Ok</button>
|
||||
<button mat-button cdkFocusInitial (click)="login()">Ok</button>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
.menu-spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.fillField{
|
||||
width: 100%;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ChangeDetectionStrategy, Component, Inject, inject, signal} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, Component, EventEmitter, Inject, inject, Output, signal} from '@angular/core';
|
||||
import {MatButtonModule} from "@angular/material/button";
|
||||
import {
|
||||
MAT_DIALOG_DATA,
|
||||
@@ -14,7 +14,8 @@ import {MatFormField, MatLabel, MatSuffix} from "@angular/material/form-field";
|
||||
import {MatInput} from "@angular/material/input";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {MatIcon} from "@angular/material/icon";
|
||||
import {DialogData} from '../../main-module/main.component/main.component';
|
||||
import {DialogLoginData} from '../../main-module/main.component/main.component';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
|
||||
|
||||
|
||||
@@ -22,16 +23,32 @@ import {DialogData} from '../../main-module/main.component/main.component';
|
||||
@Component({
|
||||
selector: 'app-dialog',
|
||||
imports: [MatButtonModule, MatLabel, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatFormField, MatInput, FormsModule, MatIcon, MatSuffix],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
||||
templateUrl: './dialog.component.html',
|
||||
styleUrl: './dialog.component.scss'
|
||||
styleUrl: './dialog.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DialogComponent {
|
||||
readonly dialogRef = inject(MatDialogRef<DialogComponent>);
|
||||
hide = signal(true);
|
||||
|
||||
@Output() loginClicked = new EventEmitter<any>();
|
||||
@Output() signupClicked = new EventEmitter<any>();
|
||||
|
||||
private _snackBar = inject(MatSnackBar);
|
||||
durationInSeconds = 5;
|
||||
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public data:DialogData) {}
|
||||
@Inject(MAT_DIALOG_DATA) public data:DialogLoginData) {
|
||||
|
||||
}
|
||||
|
||||
openLoginFailedSnackBar(errorMessage : string = "Login failed.") {
|
||||
this._snackBar.open(errorMessage , "", {
|
||||
duration: this.durationInSeconds * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
onNoClick() {
|
||||
this.dialogRef.close();
|
||||
@@ -42,6 +59,15 @@ export class DialogComponent {
|
||||
this.hide.set(!this.hide());
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
login() {
|
||||
this.loginClicked.emit(this.data);
|
||||
|
||||
}
|
||||
|
||||
signup() {
|
||||
this.signupClicked.emit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<div class="navbar-wrapper">
|
||||
<div class="m-header">
|
||||
<mat-label>Jambotron</mat-label>
|
||||
<a href="javascript:" class="b-brand">
|
||||
<img src="assets/images/logo-dark.svg" alt="theme-logo" class="logo logo-dark logo-lg" />
|
||||
</a>
|
||||
</div>
|
||||
<!-- <app-nav-content (NavCollapsedMob)="navCollapseMob()" class="scroll-div w-100 compact"></app-nav-content>-->
|
||||
<mat-nav-list>
|
||||
<a mat-list-item routerLink="user-welcome">
|
||||
<span class="entry">
|
||||
<mat-icon>house</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >Dashboard</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
<a mat-list-item routerLink="tutorials-list">
|
||||
<span class="entry">
|
||||
<mat-icon>newspaper</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >Tutorials</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
<a mat-list-item routerLink="ai-models">
|
||||
<span class="entry">
|
||||
<mat-icon>house</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >AI tools</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
</mat-nav-list>
|
||||
<div class="example-action-buttons">
|
||||
<button matButton (click)="accordion().openAll()">Expand All</button>
|
||||
<button matButton (click)="accordion().closeAll()">Collapse All</button>
|
||||
<!-- #docregion multi -->
|
||||
<mat-accordion class="example-headers-align" multi>
|
||||
<!-- #enddocregion multi -->
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<a mat-list-item routerLink="user-welcome">
|
||||
<span class="entry">
|
||||
<mat-icon>house</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >Dashboard</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
</mat-panel-title>
|
||||
|
||||
</mat-expansion-panel-header>
|
||||
<mat-nav-list>
|
||||
<a mat-list-item routerLink="user-welcome">
|
||||
<span class="entry">
|
||||
<mat-icon>house</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >Dashboard</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
<a mat-list-item routerLink="tutorials-list">
|
||||
<span class="entry">
|
||||
<mat-icon>newspaper</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >Tutorials</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
<a mat-list-item routerLink="ai-models">
|
||||
<span class="entry">
|
||||
<mat-icon>house</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >AI tools</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
</mat-nav-list>
|
||||
</mat-expansion-panel>
|
||||
<!-- #docregion disabled -->
|
||||
<mat-expansion-panel disabled>
|
||||
<!-- #enddocregion disabled -->
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title> Destination </mat-panel-title>
|
||||
<mat-panel-description>
|
||||
Type the country name
|
||||
<mat-icon>map</mat-icon>
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Country</mat-label>
|
||||
<input matInput />
|
||||
</mat-form-field>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title> Day of the trip </mat-panel-title>
|
||||
<mat-panel-description>
|
||||
Inform the date you wish to travel
|
||||
<mat-icon>date_range</mat-icon>
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Date</mat-label>
|
||||
<input matInput [matDatepicker]="picker" (focus)="picker.open()" readonly />
|
||||
</mat-form-field>
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
.entry{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding:0.75rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.example-action-buttons {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-expansion-panel-header-description {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.example-headers-align .mat-mdc-form-field + .mat-mdc-form-field {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SideBarComponent } from './side-bar.component';
|
||||
|
||||
describe('SideBarComponent', () => {
|
||||
let component: SideBarComponent;
|
||||
let fixture: ComponentFixture<SideBarComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SideBarComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SideBarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import {ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, viewChild} from '@angular/core';
|
||||
import {MatTree, MatTreeNode} from '@angular/material/tree';
|
||||
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
|
||||
import {MatDivider, MatListItem, MatNavList} from '@angular/material/list';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {RouterLink} from '@angular/router';
|
||||
import {
|
||||
MatAccordion, MatExpansionModule,
|
||||
MatExpansionPanel,
|
||||
MatExpansionPanelDescription,
|
||||
MatExpansionPanelTitle
|
||||
} from '@angular/material/expansion';
|
||||
import {MatDatepicker, MatDatepickerInput} from '@angular/material/datepicker';
|
||||
import {provideNativeDateAdapter} from '@angular/material/core';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
|
||||
@Component({
|
||||
selector: 'app-side-bar',
|
||||
imports: [
|
||||
MatTree,
|
||||
MatTreeNode,
|
||||
MatLabel,
|
||||
MatNavList,
|
||||
MatListItem,
|
||||
MatIcon,
|
||||
RouterLink,
|
||||
MatExpansionPanel,
|
||||
MatExpansionPanelTitle,
|
||||
MatExpansionPanelDescription,
|
||||
MatFormField,
|
||||
MatInput,
|
||||
MatDatepickerInput,
|
||||
MatDatepicker,
|
||||
MatDivider,
|
||||
MatAccordion,
|
||||
MatButton,
|
||||
MatExpansionModule
|
||||
],
|
||||
templateUrl: './side-bar.component.html',
|
||||
styleUrl: './side-bar.component.scss',
|
||||
schemas:[CUSTOM_ELEMENTS_SCHEMA],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [provideNativeDateAdapter()],
|
||||
})
|
||||
|
||||
|
||||
|
||||
export class SideBarComponent {
|
||||
isCollapsed = false;
|
||||
|
||||
accordion = viewChild.required(MatAccordion);
|
||||
}
|
||||
|
||||
const TREE_DATA: TreeNode[] = [
|
||||
{
|
||||
name: 'Fruit',
|
||||
children: [{name: 'Apple'}, {name: 'Banana'}, {name: 'Fruit loops'}],
|
||||
},
|
||||
{
|
||||
name: 'Vegetables',
|
||||
children: [
|
||||
{
|
||||
name: 'Green',
|
||||
children: [{name: 'Broccoli'}, {name: 'Brussels sprouts'}],
|
||||
},
|
||||
{
|
||||
name: 'Orange',
|
||||
children: [{name: 'Pumpkins'}, {name: 'Carrots'}],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
interface TreeNode {
|
||||
name: string;
|
||||
children?: TreeNode[];
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{tutorial.title}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content [innerHTML]="tutorial.description">
|
||||
<mat-card-content >
|
||||
<markdown class="variable-binding" [data]="tutorial.description"></markdown>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@ import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {Tutorial} from '../../models/tutorial.model';
|
||||
import {TutorialService} from '../../services/tutorial.service';
|
||||
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
|
||||
import {MarkdownComponent} from 'ngx-markdown';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tutorials.component',
|
||||
imports: [
|
||||
MatCard,
|
||||
MatCardContent,
|
||||
MatCardHeader
|
||||
MatCardHeader,
|
||||
MarkdownComponent
|
||||
],
|
||||
templateUrl: './tutorials.component.html',
|
||||
styleUrl: './tutorials.component.scss',
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
<!--<p>main.component works!</p>-->
|
||||
|
||||
<mat-toolbar class="fixed-top">
|
||||
<button mat-raised-button routerLink="home">
|
||||
<!-- <img ngSrc="jambotron_logo.svg" height="40" width="135">-->
|
||||
My App
|
||||
</button>
|
||||
<span class="menu-spacer"></span>
|
||||
<button mat-raised-button routerLink="generate-image">
|
||||
Generate Image
|
||||
</button>
|
||||
@@ -17,54 +10,46 @@
|
||||
Add tutorial
|
||||
</button>
|
||||
}
|
||||
<span class="menu-spacer"></span>
|
||||
@if (showAdminBoard) {
|
||||
<button mat-raised-button routerLink="admin/welcome">
|
||||
<!-- <mat-icon>manage_accounts</mat-icon>-->
|
||||
Admin Bord
|
||||
</button>
|
||||
}
|
||||
|
||||
@if (showAdminBoard) {
|
||||
<button mat-raised-button routerLink="admin/system" >
|
||||
<!-- <mat-icon>settings_applications</mat-icon>-->
|
||||
System
|
||||
</button>
|
||||
}
|
||||
|
||||
<span class="menu-spacer"></span>
|
||||
@if (isLoggedIn) {
|
||||
<button mat-raised-button routerLink="register">
|
||||
<!-- <mat-icon>app_registration</mat-icon>-->
|
||||
Register
|
||||
</button>
|
||||
}
|
||||
|
||||
@if (!isLoggedIn) {
|
||||
<button mat-raised-button (click)="openDialog('100ms', '5ms')" >
|
||||
<!-- <i antIcon type="bell" theme="outline"></i>-->
|
||||
<!-- <mat-icon>login</mat-icon>-->
|
||||
<!-- <i class="d- f-20" antIcon theme="outline" type="login">Login</i>-->
|
||||
</button>
|
||||
}
|
||||
@if (isLoggedIn) {
|
||||
<button mat-raised-button routerLink="profile" >
|
||||
<!-- <mat-icon>account_circle</mat-icon>-->
|
||||
{{ username }}
|
||||
</button>
|
||||
<button mat-raised-button (click)="logout()">
|
||||
|
||||
<!-- <i class="d-flex f-20" antIcon theme="outline" type="logout"></i>-->
|
||||
Logout
|
||||
<button matMiniFab matTooltip="Sign Up" aria-label="Sign Up" (click)="openSignupDialog('100ms', '5ms')">
|
||||
<mat-icon>person_add</mat-icon>
|
||||
</button>
|
||||
<button matMiniFab matTooltip="Login" aria-label="Login" (click)="openLoginDialog('100ms', '5ms')" >
|
||||
<mat-icon>login</mat-icon>
|
||||
</button>
|
||||
}@else {
|
||||
<button matButton="elevated" aria-label="Account" [matMenuTriggerFor]="menu">
|
||||
<mat-icon>account_circle</mat-icon>
|
||||
<mat-label>{{username}}</mat-label>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
@if (showAdminBoard) {
|
||||
<button mat-menu-item routerLink="admin/welcome">
|
||||
<mat-icon>admin_panel_settings</mat-icon>
|
||||
Admin Panel
|
||||
</button>
|
||||
}
|
||||
|
||||
<button mat-menu-item routerLink="user/user-welcome">
|
||||
<mat-icon>space_dashboard</mat-icon>
|
||||
User tools
|
||||
</button>
|
||||
<button mat-menu-item routerLink="profile">
|
||||
<mat-icon>person</mat-icon>
|
||||
Profile
|
||||
</button>
|
||||
|
||||
<button mat-menu-item (click)="logout()">
|
||||
<mat-icon>logout</mat-icon>
|
||||
Logout
|
||||
</button>
|
||||
</mat-menu>
|
||||
}
|
||||
|
||||
|
||||
</mat-toolbar>
|
||||
|
||||
|
||||
<!--<app-nav-bar (NavCollapsedMob)="navMobClick()" (NavCollapse)="this.navCollapsed = !this.navCollapsed" />-->
|
||||
<div class="router_outlet_padding">
|
||||
<div class="router_outlet">
|
||||
<router-outlet/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -12,9 +12,18 @@ mat-toolbar{
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 1000; // Ensure toolbar stays above other content
|
||||
height: 65px;
|
||||
}
|
||||
.mat-mdc-raised-button:not(:disabled){
|
||||
color: rgba(24, 255, 255, 0.96);
|
||||
background-color: rgba(24,255,255,0.04);
|
||||
}
|
||||
.mat-mdc-mini-fab{
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.router_outlet{
|
||||
margin-top: 65px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
|
||||
|
||||
import {Router, RouterLink, RouterOutlet} from '@angular/router';
|
||||
import {MatToolbar} from '@angular/material/toolbar';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
import {MatButton, MatFabButton, MatMiniFabButton} from '@angular/material/button';
|
||||
import {IconDirective, IconService} from '@ant-design/icons-angular';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {Subscription} from 'rxjs';
|
||||
@@ -11,16 +11,13 @@ import {TokenStorageService} from '../../services/token-storage.service';
|
||||
import {AuthService} from '../../services/auth.service';
|
||||
import {EventBusService} from '../../_shared/event-bus.service';
|
||||
import {DialogComponent} from '../../components/dialog/dialog.component';
|
||||
import {
|
||||
ArrowRightOutline, BellOutline,
|
||||
CheckCircleOutline, CommentOutline, EditOutline,
|
||||
GiftOutline, GithubOutline, LockOutline, LogoutOutline,
|
||||
MessageOutline,
|
||||
PhoneOutline, ProfileOutline, QuestionCircleOutline,
|
||||
SettingOutline, UnorderedListOutline, UserOutline, WalletOutline
|
||||
} from '@ant-design/icons-angular/icons';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu';
|
||||
import {MatTooltip} from '@angular/material/tooltip';
|
||||
import {DialogSignupComponent} from '../../components/dialog-signup.component/dialog-signup.component';
|
||||
import {MatLabel} from '@angular/material/input';
|
||||
|
||||
@Component({
|
||||
selector: 'app-main.component',
|
||||
@@ -31,7 +28,15 @@ import {CommonModule} from '@angular/common';
|
||||
RouterLink,
|
||||
IconDirective,
|
||||
RouterOutlet,
|
||||
CommonModule
|
||||
CommonModule,
|
||||
MatIcon,
|
||||
MatMiniFabButton,
|
||||
MatMenuTrigger,
|
||||
MatMenu,
|
||||
MatMenuItem,
|
||||
MatTooltip,
|
||||
MatLabel,
|
||||
MatFabButton
|
||||
|
||||
],
|
||||
schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
|
||||
@@ -45,7 +50,9 @@ export class MainComponent implements OnInit{
|
||||
|
||||
readonly dialog = inject(MatDialog);
|
||||
//private authService = new AuthService(provideHttpClient())
|
||||
public dialogData : DialogData = {password: "", username: ""};
|
||||
public dialogLoginData : DialogLoginData = {password: "", username: ""};
|
||||
public dialogSignupData : DialogSignupData = {password: "", username: "", email: "", confirmPassword: ""};
|
||||
|
||||
|
||||
private roles: string[] = [];
|
||||
isLoggedIn = false;
|
||||
@@ -54,38 +61,14 @@ export class MainComponent implements OnInit{
|
||||
username?: string;
|
||||
|
||||
eventBusSub?: Subscription;
|
||||
private errorMessage: any;
|
||||
private isLoginFailed: boolean = false;
|
||||
|
||||
|
||||
private storageService: TokenStorageService = inject(TokenStorageService);
|
||||
private authService: AuthService = inject(AuthService);
|
||||
private eventBusService: EventBusService = inject(EventBusService);
|
||||
private iconService = inject(IconService);
|
||||
|
||||
|
||||
|
||||
constructor(private router: Router) {
|
||||
this.iconService.addIcon(
|
||||
...[
|
||||
CheckCircleOutline,
|
||||
GiftOutline,
|
||||
MessageOutline,
|
||||
SettingOutline,
|
||||
PhoneOutline,
|
||||
LogoutOutline,
|
||||
UserOutline,
|
||||
EditOutline,
|
||||
ProfileOutline,
|
||||
QuestionCircleOutline,
|
||||
LockOutline,
|
||||
CommentOutline,
|
||||
UnorderedListOutline,
|
||||
ArrowRightOutline,
|
||||
BellOutline,
|
||||
GithubOutline,
|
||||
WalletOutline
|
||||
]
|
||||
);
|
||||
|
||||
}
|
||||
goToHome() {
|
||||
this.router.navigate(['main/home']);
|
||||
@@ -161,49 +144,99 @@ export class MainComponent implements OnInit{
|
||||
});
|
||||
}
|
||||
|
||||
openDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
|
||||
openLoginDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
|
||||
|
||||
let dialogRef = this.dialog.open(DialogComponent, {
|
||||
let dialogLoginRef = this.dialog.open(DialogComponent, {
|
||||
width: '350px',
|
||||
enterAnimationDuration,
|
||||
exitAnimationDuration,
|
||||
data: {username: this.dialogData.username, password: this.dialogData.password}
|
||||
data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
|
||||
});
|
||||
dialogLoginRef.componentInstance.signupClicked.subscribe(result => {
|
||||
dialogLoginRef.close();
|
||||
this.openSignupDialog(enterAnimationDuration, exitAnimationDuration);
|
||||
})
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
console.log('The dialog was closed');
|
||||
if(result== null){
|
||||
return;
|
||||
}
|
||||
this.dialogData = result;
|
||||
const dialogSubmitSubscription = dialogLoginRef.componentInstance.loginClicked
|
||||
.subscribe(result => {
|
||||
console.log('Got the data!', result);
|
||||
|
||||
this.authService.login(this.dialogData.username, this.dialogData.password).subscribe(
|
||||
data => {
|
||||
this.storageService.saveToken(data.accessToken);
|
||||
this.storageService.saveUser(data);
|
||||
|
||||
this.isLoginFailed = false;
|
||||
this.isLoggedIn = true;
|
||||
let user = this.storageService.getUser();
|
||||
this.roles = user.roles;
|
||||
//this.username = user.username;
|
||||
//this.reloadPage();
|
||||
this.refreshToolbar();
|
||||
},
|
||||
err => {
|
||||
this.errorMessage = err.error.message;
|
||||
this.isLoginFailed = true;
|
||||
if(result== null){
|
||||
return;
|
||||
}
|
||||
);
|
||||
this.dialogLoginData = result;
|
||||
|
||||
this.authService.login(this.dialogLoginData.username, this.dialogLoginData.password).subscribe(
|
||||
data => {
|
||||
this.storageService.saveToken(data.accessToken);
|
||||
this.storageService.saveUser(data);
|
||||
|
||||
this.isLoggedIn = true;
|
||||
let user = this.storageService.getUser();
|
||||
this.roles = user.roles;
|
||||
|
||||
this.refreshToolbar();
|
||||
dialogLoginRef.close()
|
||||
this.router.navigate(['main/user/user-welcome']);
|
||||
},
|
||||
err => {
|
||||
dialogLoginRef.componentInstance.openLoginFailedSnackBar(err.error.message);
|
||||
}
|
||||
);
|
||||
|
||||
// do something here with the data
|
||||
dialogSubmitSubscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
|
||||
openSignupDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
|
||||
|
||||
let dialogSignupRef = this.dialog.open(DialogSignupComponent, {
|
||||
width: '350px',
|
||||
enterAnimationDuration,
|
||||
exitAnimationDuration,
|
||||
data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
|
||||
});
|
||||
|
||||
}
|
||||
reloadPage(): void {
|
||||
window.location.reload();
|
||||
}
|
||||
const dialogloginSubscription = dialogSignupRef.componentInstance.loginClicked.subscribe(result => {
|
||||
dialogSignupRef.close();
|
||||
this.openLoginDialog(enterAnimationDuration, exitAnimationDuration);
|
||||
})
|
||||
|
||||
const dialogSubmitSubscription = dialogSignupRef.componentInstance.signupClicked
|
||||
.subscribe(result => {
|
||||
console.log('Got the data!', result);
|
||||
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
this.dialogSignupData = result;
|
||||
|
||||
this.authService.register(this.dialogSignupData.username, this.dialogSignupData.email, this.dialogSignupData.password).subscribe(
|
||||
data => {
|
||||
console.log(data);
|
||||
dialogSignupRef.componentInstance.openSignupSuccessSnackBar(data.message)
|
||||
dialogSignupRef.close();
|
||||
},
|
||||
err => {
|
||||
dialogSignupRef.componentInstance.openSignupFailedSnackBar(err.error.message);
|
||||
}
|
||||
);
|
||||
|
||||
// do something here with the data
|
||||
dialogloginSubscription.unsubscribe();
|
||||
dialogSubmitSubscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
}
|
||||
export interface DialogData {
|
||||
export interface DialogLoginData {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface DialogSignupData {
|
||||
email: string;
|
||||
username: string;
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,11 @@ const MAIN_ROUTES: Routes =[
|
||||
loadChildren: () =>
|
||||
import('../admin-module/admin.module').then((m) => m.AdminModule),
|
||||
},
|
||||
{
|
||||
path: 'user',
|
||||
loadChildren: () =>
|
||||
import('../user-module/user.module').then((m) => m.UserModule),
|
||||
},
|
||||
{
|
||||
path: 'home',
|
||||
component: HomeComponent
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<p>ai-models.component works!</p>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AiModelsComponent } from './ai-models.component';
|
||||
|
||||
describe('AiModelsComponent', () => {
|
||||
let component: AiModelsComponent;
|
||||
let fixture: ComponentFixture<AiModelsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AiModelsComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AiModelsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-ai-models.component',
|
||||
imports: [],
|
||||
templateUrl: './ai-models.component.html',
|
||||
styleUrl: './ai-models.component.scss'
|
||||
})
|
||||
export class AiModelsComponent {
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<p>tutorials-list.component works!</p>
|
||||
<mat-list role="list">
|
||||
<mat-list-item role="listitem">Item 1</mat-list-item>
|
||||
<mat-list-item role="listitem">Item 2</mat-list-item>
|
||||
<mat-list-item role="listitem">Item 3</mat-list-item>
|
||||
</mat-list>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TutorialsListComponent } from './tutorials-list.component';
|
||||
|
||||
describe('TutorialsListComponent', () => {
|
||||
let component: TutorialsListComponent;
|
||||
let fixture: ComponentFixture<TutorialsListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TutorialsListComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TutorialsListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {MatList, MatListItem} from '@angular/material/list';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tutorials-list.component',
|
||||
imports: [
|
||||
MatList,
|
||||
MatListItem
|
||||
],
|
||||
templateUrl: './tutorials-list.component.html',
|
||||
styleUrl: './tutorials-list.component.scss'
|
||||
})
|
||||
export class TutorialsListComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<p>user-welcome.component works!</p>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserWelcomeComponent } from './user-welcome.component';
|
||||
|
||||
describe('UserWelcomeComponent', () => {
|
||||
let component: UserWelcomeComponent;
|
||||
let fixture: ComponentFixture<UserWelcomeComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserWelcomeComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserWelcomeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-welcome.component',
|
||||
imports: [],
|
||||
templateUrl: './user-welcome.component.html',
|
||||
styleUrl: './user-welcome.component.scss'
|
||||
})
|
||||
export class UserWelcomeComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<app-side-bar
|
||||
class="pc-sidebar"
|
||||
>
|
||||
|
||||
</app-side-bar>
|
||||
|
||||
<div class="pc-container">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<!--<mat-toolbar color="primary">-->
|
||||
<!-- <button mat-icon-button aria-label="Menu icon" (click)="toggleMenu()">-->
|
||||
<!-- <mat-icon>menu</mat-icon>-->
|
||||
<!-- </button>-->
|
||||
<!-- <h1>Responsive Material Sidenavigation</h1>-->
|
||||
<!--</mat-toolbar>-->
|
||||
<!--<mat-sidenav-container autosize="true">-->
|
||||
<!-- <mat-sidenav [ngClass]="!isCollapsed ? 'expanded' : ''" [mode]="isMobile ? 'over' : 'side'" [opened]="isMobile ? 'false' : 'true'">-->
|
||||
<!-- <mat-nav-list>-->
|
||||
<!-- <a mat-list-item>-->
|
||||
<!-- <span class="entry">-->
|
||||
<!-- <mat-icon>house</mat-icon>-->
|
||||
<!-- @if (!isCollapsed) {-->
|
||||
<!-- <span >Dashboard</span>-->
|
||||
<!-- }-->
|
||||
<!-- </span>-->
|
||||
<!-- </a>-->
|
||||
<!-- </mat-nav-list>-->
|
||||
<!-- </mat-sidenav>-->
|
||||
<!-- <mat-sidenav-content>-->
|
||||
<!-- Content-->
|
||||
<!-- <router-outlet></router-outlet>-->
|
||||
<!-- </mat-sidenav-content>-->
|
||||
<!--</mat-sidenav-container>-->
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
mat-toolbar{
|
||||
position:fixed;
|
||||
top:0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
mat-sidenav-container {
|
||||
height:100%;
|
||||
}
|
||||
|
||||
// Move the content down so that it won't be hidden by the toolbar
|
||||
mat-sidenav {
|
||||
padding-top: 3.5rem;
|
||||
transition: width 0.3s ease;
|
||||
@media screen and (min-width: 600px) {
|
||||
padding-top: 4rem;
|
||||
}
|
||||
|
||||
.entry{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding:0.75rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Move the content down so that it won't be hidden by the toolbar
|
||||
mat-sidenav-content{
|
||||
padding-top: 3.5rem;
|
||||
@media screen and (min-width: 600px) {
|
||||
padding-top: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.expanded {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
//---------------
|
||||
.pc-sidebar{
|
||||
top: 65px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.pc-container{
|
||||
top: 0px;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserComponent } from './user.component';
|
||||
|
||||
describe('UserComponent', () => {
|
||||
let component: UserComponent;
|
||||
let fixture: ComponentFixture<UserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit, ViewChild} from '@angular/core';
|
||||
import {RouterOutlet} from '@angular/router';
|
||||
import {SideBarComponent} from '../../components/side-bar.component/side-bar.component';
|
||||
import {MatSidenav, MatSidenavContainer} from '@angular/material/sidenav';
|
||||
import {NgClass} from '@angular/common';
|
||||
import {MatListItem, MatNavList} from '@angular/material/list';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {BreakpointObserver} from '@angular/cdk/layout';
|
||||
import {MatToolbar} from '@angular/material/toolbar';
|
||||
import {MatIconButton} from '@angular/material/button';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user.component',
|
||||
imports: [
|
||||
RouterOutlet,
|
||||
SideBarComponent,
|
||||
MatSidenavContainer,
|
||||
NgClass,
|
||||
MatNavList,
|
||||
MatSidenav,
|
||||
MatListItem,
|
||||
MatIcon,
|
||||
MatToolbar,
|
||||
MatIconButton
|
||||
],
|
||||
templateUrl: './user.component.html',
|
||||
styleUrl: './user.component.scss',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class UserComponent implements OnInit {
|
||||
@ViewChild(MatSidenav)
|
||||
sidenav!: MatSidenav;
|
||||
isMobile= true;
|
||||
isCollapsed = true;
|
||||
|
||||
|
||||
constructor(private observer: BreakpointObserver) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.observer.observe(['(max-width: 800px)']).subscribe((screenSize) => {
|
||||
this.isMobile = screenSize.matches;
|
||||
});
|
||||
}
|
||||
toggleMenu() {
|
||||
if(this.isMobile){
|
||||
this.sidenav.toggle();
|
||||
this.isCollapsed = false;
|
||||
} else {
|
||||
this.sidenav.open();
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {userRouting} from './user.routing';
|
||||
import {UserComponent} from './user.component/user.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
userRouting,
|
||||
UserComponent,
|
||||
CommonModule
|
||||
]
|
||||
})
|
||||
export class UserModule { }
|
||||
@@ -0,0 +1,7 @@
|
||||
import { UserRouting } from './user.routing';
|
||||
|
||||
describe('UserRouting', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new UserRouting()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
|
||||
import {UserComponent} from './user.component/user.component';
|
||||
|
||||
const USER_ROUTES: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: UserComponent,
|
||||
|
||||
children: [
|
||||
{
|
||||
path: 'user-welcome',
|
||||
loadComponent: () => import('../user-module/user-welcome.component/user-welcome.component').then((c) => c.UserWelcomeComponent),
|
||||
},
|
||||
{
|
||||
path: 'tutorials-list',
|
||||
loadComponent: () => import('../user-module/tutorials-list.component/tutorials-list.component').then((c) => c.TutorialsListComponent)
|
||||
},
|
||||
{
|
||||
path: 'ai-models',
|
||||
loadComponent: () => import('../user-module/ai-models.component/ai-models.component').then((c) => c.AiModelsComponent)
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export const userRouting = RouterModule.forChild(USER_ROUTES);
|
||||
@@ -38,6 +38,18 @@ File: style.css
|
||||
|
||||
@import 'scss/themes/preset-style.scss';
|
||||
|
||||
//markdown styles
|
||||
@import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
@import 'prismjs/themes/prism-okaidia.css';
|
||||
@import 'prismjs/plugins/line-numbers/prism-line-numbers.css';
|
||||
@import 'prismjs/plugins/line-highlight/prism-line-highlight.css';
|
||||
blockquote {
|
||||
border-left: 3px solid rgba(0,0,0,0.21);
|
||||
padding-left: 12px;
|
||||
color: rgba(0,0,0,0.54);
|
||||
}
|
||||
//--------------------------
|
||||
//
|
||||
html, body { height: 100%; }
|
||||
body {
|
||||
background-image: url('../public/Firefly_jambo tron 118290.jpg');
|
||||
|
||||
@@ -13,8 +13,8 @@ services:
|
||||
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres_jambotron:5432/jambotronDB
|
||||
SPRING_DATASOURCE_USERNAME: admin
|
||||
SPRING_DATASOURCE_PASSWORD: postgrespw
|
||||
SPRING_FLYWAY_BASELINE-ON-MIGRATE: true
|
||||
SPRING_FLYWAY_VALIDATE-ON-MIGRATE: true
|
||||
SPRING_FLYWAY_BASELINE-ON-MIGRATE: "true"
|
||||
SPRING_FLYWAY_VALIDATE-ON-MIGRATE: "true"
|
||||
|
||||
SPRING_FLYWAY_USER: admin
|
||||
SPRING_FLYWAY_PASSWORD: postgrespw
|
||||
|
||||
Reference in New Issue
Block a user