Add user module routing, integrate markdown support, and enhance dialog functionality
This commit is contained in:
Generated
+1511
-1
File diff suppressed because it is too large
Load Diff
@@ -29,8 +29,10 @@
|
|||||||
"@primeng/themes": "^19.1.3",
|
"@primeng/themes": "^19.1.3",
|
||||||
"bootstrap": "5.3.6",
|
"bootstrap": "5.3.6",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
|
"ngx-markdown": "^20.0.0",
|
||||||
"ngx-scrollbar": "18.0.0",
|
"ngx-scrollbar": "18.0.0",
|
||||||
"primeng": "^19.1.3",
|
"primeng": "^19.1.3",
|
||||||
|
"prismjs": "^1.30.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"viewerjs": "^1.11.7",
|
"viewerjs": "^1.11.7",
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ import {AppModule} from './app.module';
|
|||||||
import {providePrimeNG} from 'primeng/config';
|
import {providePrimeNG} from 'primeng/config';
|
||||||
import Aura from '@primeng/themes/aura';
|
import Aura from '@primeng/themes/aura';
|
||||||
import {provideAnimations} from '@angular/platform-browser/animations';
|
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 = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
@@ -19,7 +25,7 @@ export const appConfig: ApplicationConfig = {
|
|||||||
provideAnimations(),
|
provideAnimations(),
|
||||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||||
provideRouter(routes), provideClientHydration(withEventReplay()),
|
provideRouter(routes), provideClientHydration(withEventReplay()),
|
||||||
|
provideMarkdown(),
|
||||||
importProvidersFrom(AppModule),
|
importProvidersFrom(AppModule),
|
||||||
providePrimeNG({
|
providePrimeNG({
|
||||||
theme: {
|
theme: {
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ import {SystemComponent} from './admin-module/system.component/system.component'
|
|||||||
import {AppRoutingModule} from './app.routes';
|
import {AppRoutingModule} from './app.routes';
|
||||||
import {App} from './app';
|
import {App} from './app';
|
||||||
import {CustomHttpInterceptor} from './helpers/custom-http-interceptor';
|
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({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
|||||||
@@ -38,14 +38,26 @@
|
|||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>Add tutorial</mat-card-title>
|
<mat-card-title>Add tutorial</mat-card-title>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content *ngIf="!submitted">
|
<mat-card-content >
|
||||||
<mat-form-field class="example-full-width">
|
<mat-form-field class="example-full-width">
|
||||||
<mat-label>Title</mat-label>
|
<mat-label>Title</mat-label>
|
||||||
<input matInput required
|
<input matInput required
|
||||||
[(ngModel)]="tutorial.title">
|
[(ngModel)]="tutorial.title">
|
||||||
</mat-form-field>
|
</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-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button matButton (click)="saveTutorial()" *ngIf="!submitted">Save</button>
|
<button matButton (click)="saveTutorial()" *ngIf="!submitted">Save</button>
|
||||||
|
|||||||
@@ -19,3 +19,34 @@ mat-card-title{
|
|||||||
.example-full-width {
|
.example-full-width {
|
||||||
width: 100%;
|
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 {MatButton} from '@angular/material/button';
|
||||||
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
|
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
|
||||||
import {NgIf} from '@angular/common';
|
import {NgIf} from '@angular/common';
|
||||||
|
import {MatTab, MatTabGroup} from '@angular/material/tabs';
|
||||||
|
import {MarkdownComponent} from 'ngx-markdown';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-add-tutorial',
|
selector: 'app-add-tutorial',
|
||||||
@@ -24,6 +26,9 @@ import {NgIf} from '@angular/common';
|
|||||||
MatInput,
|
MatInput,
|
||||||
NgIf,
|
NgIf,
|
||||||
MatLabel,
|
MatLabel,
|
||||||
|
MatTabGroup,
|
||||||
|
MatTab,
|
||||||
|
MarkdownComponent,
|
||||||
|
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
@@ -35,9 +40,30 @@ export class AddTutorialComponent implements OnInit {
|
|||||||
description: '',
|
description: '',
|
||||||
published: false
|
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 {
|
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>
|
||||||
|
|
||||||
<div mat-dialog-actions>
|
<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 (click)="onNoClick()">No Thanks</button>
|
||||||
<button mat-button [mat-dialog-close]="data" cdkFocusInitial>Ok</button>
|
<button mat-button cdkFocusInitial (click)="login()">Ok</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
.menu-spacer {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
.fillField{
|
.fillField{
|
||||||
width: 100%;
|
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 {MatButtonModule} from "@angular/material/button";
|
||||||
import {
|
import {
|
||||||
MAT_DIALOG_DATA,
|
MAT_DIALOG_DATA,
|
||||||
@@ -14,7 +14,8 @@ import {MatFormField, MatLabel, MatSuffix} from "@angular/material/form-field";
|
|||||||
import {MatInput} from "@angular/material/input";
|
import {MatInput} from "@angular/material/input";
|
||||||
import {FormsModule} from "@angular/forms";
|
import {FormsModule} from "@angular/forms";
|
||||||
import {MatIcon} from "@angular/material/icon";
|
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({
|
@Component({
|
||||||
selector: 'app-dialog',
|
selector: 'app-dialog',
|
||||||
imports: [MatButtonModule, MatLabel, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatFormField, MatInput, FormsModule, MatIcon, MatSuffix],
|
imports: [MatButtonModule, MatLabel, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatFormField, MatInput, FormsModule, MatIcon, MatSuffix],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
templateUrl: './dialog.component.html',
|
templateUrl: './dialog.component.html',
|
||||||
styleUrl: './dialog.component.scss'
|
styleUrl: './dialog.component.scss',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class DialogComponent {
|
export class DialogComponent {
|
||||||
readonly dialogRef = inject(MatDialogRef<DialogComponent>);
|
readonly dialogRef = inject(MatDialogRef<DialogComponent>);
|
||||||
hide = signal(true);
|
hide = signal(true);
|
||||||
|
|
||||||
|
@Output() loginClicked = new EventEmitter<any>();
|
||||||
|
@Output() signupClicked = new EventEmitter<any>();
|
||||||
|
|
||||||
|
private _snackBar = inject(MatSnackBar);
|
||||||
|
durationInSeconds = 5;
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
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() {
|
onNoClick() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
@@ -42,6 +59,15 @@ export class DialogComponent {
|
|||||||
this.hide.set(!this.hide());
|
this.hide.set(!this.hide());
|
||||||
event.stopPropagation();
|
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-header>
|
||||||
<mat-card-title>{{tutorial.title}}</mat-card-title>
|
<mat-card-title>{{tutorial.title}}</mat-card-title>
|
||||||
</mat-card-header>
|
</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-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,15 @@ import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
|||||||
import {Tutorial} from '../../models/tutorial.model';
|
import {Tutorial} from '../../models/tutorial.model';
|
||||||
import {TutorialService} from '../../services/tutorial.service';
|
import {TutorialService} from '../../services/tutorial.service';
|
||||||
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
|
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
|
||||||
|
import {MarkdownComponent} from 'ngx-markdown';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tutorials.component',
|
selector: 'app-tutorials.component',
|
||||||
imports: [
|
imports: [
|
||||||
MatCard,
|
MatCard,
|
||||||
MatCardContent,
|
MatCardContent,
|
||||||
MatCardHeader
|
MatCardHeader,
|
||||||
|
MarkdownComponent
|
||||||
],
|
],
|
||||||
templateUrl: './tutorials.component.html',
|
templateUrl: './tutorials.component.html',
|
||||||
styleUrl: './tutorials.component.scss',
|
styleUrl: './tutorials.component.scss',
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
<!--<p>main.component works!</p>-->
|
|
||||||
|
|
||||||
<mat-toolbar class="fixed-top">
|
<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">
|
<button mat-raised-button routerLink="generate-image">
|
||||||
Generate Image
|
Generate Image
|
||||||
</button>
|
</button>
|
||||||
@@ -17,54 +10,46 @@
|
|||||||
Add tutorial
|
Add tutorial
|
||||||
</button>
|
</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>
|
<span class="menu-spacer"></span>
|
||||||
@if (isLoggedIn) {
|
|
||||||
<button mat-raised-button routerLink="register">
|
|
||||||
<!-- <mat-icon>app_registration</mat-icon>-->
|
|
||||||
Register
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
@if (!isLoggedIn) {
|
@if (!isLoggedIn) {
|
||||||
<button mat-raised-button (click)="openDialog('100ms', '5ms')" >
|
<button matMiniFab matTooltip="Sign Up" aria-label="Sign Up" (click)="openSignupDialog('100ms', '5ms')">
|
||||||
<!-- <i antIcon type="bell" theme="outline"></i>-->
|
<mat-icon>person_add</mat-icon>
|
||||||
<!-- <mat-icon>login</mat-icon>-->
|
</button>
|
||||||
<!-- <i class="d- f-20" antIcon theme="outline" type="login">Login</i>-->
|
<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>
|
||||||
}
|
}
|
||||||
@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>-->
|
<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
|
Logout
|
||||||
</button>
|
</button>
|
||||||
|
</mat-menu>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
|
<div class="router_outlet">
|
||||||
|
|
||||||
<!--<app-nav-bar (NavCollapsedMob)="navMobClick()" (NavCollapse)="this.navCollapsed = !this.navCollapsed" />-->
|
|
||||||
<div class="router_outlet_padding">
|
|
||||||
<router-outlet/>
|
<router-outlet/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,18 @@ mat-toolbar{
|
|||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 1000; // Ensure toolbar stays above other content
|
z-index: 1000; // Ensure toolbar stays above other content
|
||||||
|
height: 65px;
|
||||||
}
|
}
|
||||||
.mat-mdc-raised-button:not(:disabled){
|
.mat-mdc-raised-button:not(:disabled){
|
||||||
color: rgba(24, 255, 255, 0.96);
|
color: rgba(24, 255, 255, 0.96);
|
||||||
background-color: rgba(24,255,255,0.04);
|
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 {Router, RouterLink, RouterOutlet} from '@angular/router';
|
||||||
import {MatToolbar} from '@angular/material/toolbar';
|
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 {IconDirective, IconService} from '@ant-design/icons-angular';
|
||||||
import {MatDialog} from '@angular/material/dialog';
|
import {MatDialog} from '@angular/material/dialog';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
@@ -11,16 +11,13 @@ import {TokenStorageService} from '../../services/token-storage.service';
|
|||||||
import {AuthService} from '../../services/auth.service';
|
import {AuthService} from '../../services/auth.service';
|
||||||
import {EventBusService} from '../../_shared/event-bus.service';
|
import {EventBusService} from '../../_shared/event-bus.service';
|
||||||
import {DialogComponent} from '../../components/dialog/dialog.component';
|
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 {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({
|
@Component({
|
||||||
selector: 'app-main.component',
|
selector: 'app-main.component',
|
||||||
@@ -31,7 +28,15 @@ import {CommonModule} from '@angular/common';
|
|||||||
RouterLink,
|
RouterLink,
|
||||||
IconDirective,
|
IconDirective,
|
||||||
RouterOutlet,
|
RouterOutlet,
|
||||||
CommonModule
|
CommonModule,
|
||||||
|
MatIcon,
|
||||||
|
MatMiniFabButton,
|
||||||
|
MatMenuTrigger,
|
||||||
|
MatMenu,
|
||||||
|
MatMenuItem,
|
||||||
|
MatTooltip,
|
||||||
|
MatLabel,
|
||||||
|
MatFabButton
|
||||||
|
|
||||||
],
|
],
|
||||||
schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
|
schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
|
||||||
@@ -45,7 +50,9 @@ export class MainComponent implements OnInit{
|
|||||||
|
|
||||||
readonly dialog = inject(MatDialog);
|
readonly dialog = inject(MatDialog);
|
||||||
//private authService = new AuthService(provideHttpClient())
|
//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[] = [];
|
private roles: string[] = [];
|
||||||
isLoggedIn = false;
|
isLoggedIn = false;
|
||||||
@@ -54,38 +61,14 @@ export class MainComponent implements OnInit{
|
|||||||
username?: string;
|
username?: string;
|
||||||
|
|
||||||
eventBusSub?: Subscription;
|
eventBusSub?: Subscription;
|
||||||
private errorMessage: any;
|
|
||||||
private isLoginFailed: boolean = false;
|
|
||||||
|
|
||||||
private storageService: TokenStorageService = inject(TokenStorageService);
|
private storageService: TokenStorageService = inject(TokenStorageService);
|
||||||
private authService: AuthService = inject(AuthService);
|
private authService: AuthService = inject(AuthService);
|
||||||
private eventBusService: EventBusService = inject(EventBusService);
|
private eventBusService: EventBusService = inject(EventBusService);
|
||||||
private iconService = inject(IconService);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constructor(private router: Router) {
|
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() {
|
goToHome() {
|
||||||
this.router.navigate(['main/home']);
|
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',
|
width: '350px',
|
||||||
enterAnimationDuration,
|
enterAnimationDuration,
|
||||||
exitAnimationDuration,
|
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);
|
||||||
|
})
|
||||||
|
|
||||||
|
const dialogSubmitSubscription = dialogLoginRef.componentInstance.loginClicked
|
||||||
|
.subscribe(result => {
|
||||||
|
console.log('Got the data!', result);
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
|
||||||
console.log('The dialog was closed');
|
|
||||||
if(result== null){
|
if(result== null){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.dialogData = result;
|
this.dialogLoginData = result;
|
||||||
|
|
||||||
this.authService.login(this.dialogData.username, this.dialogData.password).subscribe(
|
this.authService.login(this.dialogLoginData.username, this.dialogLoginData.password).subscribe(
|
||||||
data => {
|
data => {
|
||||||
this.storageService.saveToken(data.accessToken);
|
this.storageService.saveToken(data.accessToken);
|
||||||
this.storageService.saveUser(data);
|
this.storageService.saveUser(data);
|
||||||
|
|
||||||
this.isLoginFailed = false;
|
|
||||||
this.isLoggedIn = true;
|
this.isLoggedIn = true;
|
||||||
let user = this.storageService.getUser();
|
let user = this.storageService.getUser();
|
||||||
this.roles = user.roles;
|
this.roles = user.roles;
|
||||||
//this.username = user.username;
|
|
||||||
//this.reloadPage();
|
|
||||||
this.refreshToolbar();
|
this.refreshToolbar();
|
||||||
|
dialogLoginRef.close()
|
||||||
|
this.router.navigate(['main/user/user-welcome']);
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
this.errorMessage = err.error.message;
|
dialogLoginRef.componentInstance.openLoginFailedSnackBar(err.error.message);
|
||||||
this.isLoginFailed = true;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 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}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
const dialogloginSubscription = dialogSignupRef.componentInstance.loginClicked.subscribe(result => {
|
||||||
reloadPage(): void {
|
dialogSignupRef.close();
|
||||||
window.location.reload();
|
this.openLoginDialog(enterAnimationDuration, exitAnimationDuration);
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const dialogSubmitSubscription = dialogSignupRef.componentInstance.signupClicked
|
||||||
|
.subscribe(result => {
|
||||||
|
console.log('Got the data!', result);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
export interface DialogData {
|
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 DialogLoginData {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DialogSignupData {
|
||||||
|
email: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
confirmPassword: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ const MAIN_ROUTES: Routes =[
|
|||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('../admin-module/admin.module').then((m) => m.AdminModule),
|
import('../admin-module/admin.module').then((m) => m.AdminModule),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'user',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('../user-module/user.module').then((m) => m.UserModule),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'home',
|
path: 'home',
|
||||||
component: HomeComponent
|
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';
|
@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%; }
|
html, body { height: 100%; }
|
||||||
body {
|
body {
|
||||||
background-image: url('../public/Firefly_jambo tron 118290.jpg');
|
background-image: url('../public/Firefly_jambo tron 118290.jpg');
|
||||||
|
|||||||
Reference in New Issue
Block a user