Add new components for AI and tutorials, update routing, and enhance file upload functionality

This commit is contained in:
liosha84
2025-08-04 10:38:46 +03:00
parent 18c6906637
commit 483d029952
206 changed files with 2362 additions and 1199 deletions
-12
View File
@@ -1,12 +0,0 @@
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering, withRoutes } from '@angular/ssr';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(withRoutes(serverRoutes))
]
};
export const config = mergeApplicationConfig(appConfig, serverConfig);
-4
View File
@@ -14,10 +14,6 @@ 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';
import {authInterceptorProviders} from './helpers/auth.interceptor';
export const appConfig: ApplicationConfig = {
+1
View File
@@ -1 +1,2 @@
<app-spinner></app-spinner>
<router-outlet></router-outlet>
+7 -6
View File
@@ -7,15 +7,16 @@ import { BrowserAnimationsModule }
import { MatIconModule } from '@angular/material/icon';
import {HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {MainComponent} from './main-module/main.component/main.component';
import {MainComponent} from './modules/main-module/main.component/main.component';
import {authInterceptorProviders} from './helpers/auth.interceptor';
import {AdminComponent} from './admin-module/admin.component/admin.component';
import {AdminWelcomeComponent} from './admin-module/admin-welcome.component/admin-welcome.component';
import {SettingsComponent} from './admin-module/settings.component/settings.component';
import {SystemComponent} from './admin-module/system.component/system.component';
import {AdminComponent} from './modules/admin-module/admin.component/admin.component';
import {AdminWelcomeComponent} from './modules/admin-module/admin-welcome.component/admin-welcome.component';
import {SettingsComponent} from './modules/admin-module/settings.component/settings.component';
import {SystemComponent} from './modules/admin-module/system.component/system.component';
import {AppRoutingModule} from './app.routes';
import {App} from './app';
import {CustomHttpInterceptor} from './helpers/custom-http-interceptor';
import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
@NgModule({
@@ -32,7 +33,7 @@ import {CustomHttpInterceptor} from './helpers/custom-http-interceptor';
AdminWelcomeComponent,
SettingsComponent,
SystemComponent,
AngularMarkdownEditorModule.forRoot({ iconlibrary: 'fa' })
],
providers: [
authInterceptorProviders,provideHttpClient(withInterceptorsFromDi()),{
@@ -1,8 +0,0 @@
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
{
path: '**',
renderMode: RenderMode.Prerender
}
];
+3 -2
View File
@@ -1,16 +1,17 @@
import {RouterModule, Routes} from '@angular/router';
import {NgModule} from '@angular/core';
import {environment} from '../environments/environment';
export const routes: Routes = [
{
path: '',
redirectTo: 'main/generate-image',
redirectTo: environment.default_page,
pathMatch: 'full'
},
{
path: 'main',
loadChildren: () =>
import('./main-module/main.module').then((m) => m.MainModule),
import('./modules/main-module/main.module').then((m) => m.MainModule),
}
];
@NgModule({
+6 -3
View File
@@ -1,13 +1,16 @@
import { Component } from '@angular/core';
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {SpinnerComponent} from './components/spinner/spinner.component';
@Component({
selector: 'app-root',
templateUrl: './app.html',
imports: [
RouterOutlet
RouterOutlet,
SpinnerComponent
],
styleUrl: './app.scss'
styleUrl: './app.scss',
schemas:[CUSTOM_ELEMENTS_SCHEMA]
})
export class App {
protected title = 'jambotron-ui';
@@ -1,74 +0,0 @@
import {ChangeDetectionStrategy, Component, EventEmitter, Inject, inject, Output, signal} from '@angular/core';
import {MatButtonModule} from "@angular/material/button";
import {
MAT_DIALOG_DATA,
MatDialogActions,
MatDialogClose,
MatDialogContent,
MatDialogRef,
MatDialogTitle
} from "@angular/material/dialog";
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 {DialogLoginData} from '../../main-module/main.component/main.component';
import {MatSnackBar} from '@angular/material/snack-bar';
@Component({
selector: 'app-dialog',
imports: [MatButtonModule, MatLabel, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatFormField, MatInput, FormsModule, MatIcon, MatSuffix],
templateUrl: './dialog.component.html',
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:DialogLoginData) {
}
openLoginFailedSnackBar(errorMessage : string = "Login failed.") {
this._snackBar.open(errorMessage , "", {
duration: this.durationInSeconds * 1000,
});
}
onNoClick() {
this.dialogRef.close();
}
clickEvent(event: MouseEvent) {
this.hide.set(!this.hide());
event.stopPropagation();
}
login() {
this.loginClicked.emit(this.data);
}
signup() {
this.signupClicked.emit();
}
}
@@ -5,7 +5,7 @@ import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/m
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
import {AsyncPipe} from '@angular/common';
import {Image} from '../../models/image';
import {ZhipuaiImageService} from '../../services/zhipuai-image.service';
import {ZhipuaiImageService} from '../../modules/ai-module/zhipuai-image.service';
import {MatProgressSpinner} from '@angular/material/progress-spinner';
import {SpinnerService} from '../../services/spinner.service';
@@ -50,11 +50,6 @@ export class GenerateImageComponent implements OnInit {
ngOnInit(): void {
const img = this.renderer.selectRootElement('img');
this.viewer = new Viewer(img, {
inline: true,
});
this.viewer.zoomTo(1);
}
@@ -77,11 +72,4 @@ export class GenerateImageComponent implements OnInit {
this.viewer.update();
})
}
zoomPlus(){
this.viewer.zoomTo(5);
}
zoomMinus(){
this.viewer.zoomTo(-5);
}
}
@@ -1,50 +0,0 @@
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p><p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p><p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<p>dfsdgdsgsdg</p>
<!--
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title>Add tutorial</mat-card-title>
</mat-card-header>
<mat-card-content *ngIf="!submitted">
<mat-form-field class="example-full-width">
<mat-label>Title</mat-label>
<input matInput [(ngModel)]="query">
</mat-form-field>
&lt;!&ndash; <kendo-editor [(ngModel)]="tutorial.description" ></kendo-editor>&ndash;&gt;
</mat-card-content>
<mat-card-actions>
<button matButton (click)="generateImage()" >Save</button>
<div>
<h4>Tutorial was submitted successfully!</h4>
</div>
</mat-card-actions>
</mat-card>
<div>
<angular-image-viewer [src]="images" [(config)]="config" [(index)]="imageIndexOne"
[screenHeightOccupied]='0' (customImageEvent)="handleEvent($event)" >
</angular-image-viewer>
</div>
-->
@@ -1,76 +0,0 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
import {AngularImageViewerModule, CustomImageEvent, ImageViewerConfig} from '@hreimer/angular-image-viewer';
import {ZhipuaiImageService} from '../../services/zhipuai-image.service';
import {MatButton} from '@angular/material/button';
import {FormsModule} from '@angular/forms';
import {CommonModule} from '@angular/common';
import {BrowserModule} from '@angular/platform-browser';
import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
import {Image} from '../../models/image';
@Component({
selector: 'app-home.component',
imports: [
AngularImageViewerModule,
FormsModule
],
templateUrl: './home.component.html',
styleUrl: './home.component.scss',
schemas :[CUSTOM_ELEMENTS_SCHEMA]
})
export class HomeComponent {
query : string = '';
images = [];
image: Image = {
url: ''
};
imageIndexOne = 0;
config: ImageViewerConfig = { customBtns: [{ name: 'print', icon: {
classes: 'fas fa-paperclip',
text: 'link'
} }, { name: 'link', icon: {
classes: 'fas fa-paperclip',
text: 'link'
} }] };
submitted: boolean = false;
constructor(private zhipuaiImageService: ZhipuaiImageService) {
}
handleEvent(event: CustomImageEvent) {
console.log(`${event.name} has been click on img ${event.imageIndex + 1}`);
switch (event.name) {
case 'print':
console.log('run print logic');
break;
}
}
generateImage(){
this.zhipuaiImageService.generate(this.query).subscribe(
data => {
this.image = data;
// @ts-ignore
this.images = [this.image.url];
console.log(data);
},
error => {
console.log(error);
}
)
}
}
@@ -1,75 +0,0 @@
<div class="col-md-12">
<div class="card card-container">
<img
id="profile-img"
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
class="profile-img-card"
/>
<form
*ngIf="!isLoggedIn"
name="form"
(ngSubmit)="f.form.valid && onSubmit()"
#f="ngForm"
novalidate
>
<div class="form-group">
<label for="username">Username</label>
<input
type="text"
class="form-control"
id="username"
[(ngModel)]="form.username"
required
#username="ngModel"
/>
<div
class="alert alert-danger"
role="alert"
*ngIf="username.errors && f.submitted"
>
Username is required!
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
class="form-control"
id="password"
[(ngModel)]="form.password"
required
minlength="6"
#password="ngModel"
/>
<div
class="alert alert-danger"
role="alert"
*ngIf="password.errors && f.submitted"
>
<div *ngIf="password.errors['required']">Password is required</div>
<div *ngIf="password.errors['minlength']">
Password must be at least 6 characters
</div>
</div>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block">
Login
</button>
</div>
<div class="form-group">
<div
class="alert alert-danger"
role="alert"
*ngIf="f.submitted && isLoginFailed"
>
Login failed: {{ errorMessage }}
</div>
</div>
</form>
<div class="alert alert-success" *ngIf="isLoggedIn">
Logged in as {{ roles }}.
</div>
</div>
</div>
@@ -1 +0,0 @@
@@ -1,60 +0,0 @@
import { Component, OnInit } from '@angular/core';
import {FormsModule} from "@angular/forms";
import {NgIf} from "@angular/common";
import {AuthService} from '../../services/auth.service';
import {TokenStorageService} from '../../services/token-storage.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
imports: [
FormsModule,
NgIf
],
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
form: any = {
username: null,
password: null
};
isLoggedIn = false;
isLoginFailed = false;
errorMessage = '';
roles: string[] = [];
constructor(private authService: AuthService, private tokenStorage: TokenStorageService) { }
ngOnInit(): void {
if (this.tokenStorage.getToken()) {
this.isLoggedIn = true;
this.roles = this.tokenStorage.getUser().roles;
}
}
onSubmit(): void {
const { username, password } = this.form;
this.authService.login(username, password).subscribe(
data => {
this.tokenStorage.saveToken(data.accessToken);
this.tokenStorage.saveUser(data);
this.isLoginFailed = false;
this.isLoggedIn = true;
this.roles = this.tokenStorage.getUser().roles;
this.reloadPage();
},
err => {
this.errorMessage = err.error.message;
this.isLoginFailed = true;
}
);
}
reloadPage(): void {
window.location.reload();
}
}
@@ -1,26 +0,0 @@
<div class="container" *ngIf="currentUser; else loggedOut">
<header class="jumbotron">
<h3>
<strong>{{ currentUser.username }}</strong> Profile
</h3>
</header>
<p>
<strong>Token:</strong>
{{ currentUser.accessToken.substring(0, 20) }} ...
{{ currentUser.accessToken.substr(currentUser.accessToken.length - 20) }}
</p>
<p>
<strong>Email:</strong>
{{ currentUser.email }}
</p>
<strong>Roles:</strong>
<ul>
<li *ngFor="let role of currentUser.roles">
{{ role }}
</li>
</ul>
</div>
<ng-template #loggedOut>
Please login.
</ng-template>
@@ -1,23 +0,0 @@
import { Component, OnInit } from '@angular/core';
import {TokenStorageService} from '../../services/token-storage.service';
import {NgForOf, NgIf} from '@angular/common';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
imports: [
NgForOf,
NgIf
],
styleUrls: ['./profile.component.scss']
})
export class ProfileComponent implements OnInit {
currentUser: any;
constructor(private token: TokenStorageService) { }
ngOnInit(): void {
this.currentUser = this.token.getUser();
}
}
@@ -1,89 +0,0 @@
<div class="col-md-12">
<div id="register">
<img id="foto"
id="profile-img"
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
class="profile-img-card"
/>
<form
*ngIf="!isSuccessful"
name="form"
(ngSubmit)="f.form.valid && onSubmit()"
#f="ngForm"
novalidate
>
<div class="form-group">
<label for="username">Username</label>
<input
type="text"
class="form-control"
id="username"
[(ngModel)]="form.username"
[ngModelOptions]="{standalone: true}"
required
minlength="3"
maxlength="20"
#username="ngModel"
/>
<div class="alert-danger" *ngIf="username.errors && f.submitted">
<div *ngIf="username.errors['required']">Username is required</div>
<div *ngIf="username.errors['minlength']">
Username must be at least 3 characters
</div>
<div *ngIf="username.errors['maxlength']">
Username must be at most 20 characters
</div>
</div>
</div>
<div class="form-group">
<label for="email">Email</label>
<input
type="email"
class="form-control"
id="email"
[(ngModel)]="form.email"
[ngModelOptions]="{standalone: true}"
required
email
#email="ngModel"
/>
<div class="alert-danger" *ngIf="email.errors && f.submitted">
<div *ngIf="email.errors['required']">Email is required</div>
<div *ngIf="email.errors['email']">
Email must be a valid email address
</div>
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
class="form-control"
id="password"
[(ngModel)]="form.password"
[ngModelOptions]="{standalone: true}"
required
minlength="6"
#password="ngModel"
/>
<div class="alert-danger" *ngIf="password.errors && f.submitted">
<div *ngIf="password.errors['required']">Password is required</div>
<div *ngIf="password.errors['minlength']">
Password must be at least 6 characters
</div>
</div>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block">Sign Up</button>
</div>
<div class="alert alert-warning" *ngIf="f.submitted && isSignUpFailed">
Signup failed!<br />{{ errorMessage }}
</div>
</form>
<div class="alert alert-success" *ngIf="isSuccessful">
Your registration is successful!
</div>
</div>
</div>
@@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RegisterComponent } from './register.component';
describe('RegisterComponent', () => {
let component: RegisterComponent;
let fixture: ComponentFixture<RegisterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RegisterComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(RegisterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,46 +0,0 @@
import { Component, OnInit } from '@angular/core';
import {AuthService} from '../../services/auth.service';
import {FormsModule} from '@angular/forms';
import {NgIf} from '@angular/common';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
imports: [
FormsModule,
NgIf
],
styleUrls: ['./register.component.scss']
})
export class RegisterComponent implements OnInit {
form: any = {
username: null,
email: null,
password: null
};
isSuccessful = false;
isSignUpFailed = false;
errorMessage = '';
constructor(private authService: AuthService) { }
ngOnInit(): void {
}
onSubmit(): void {
const { username, email, password } = this.form;
this.authService.register(username, email, password).subscribe(
data => {
console.log(data);
this.isSuccessful = true;
this.isSignUpFailed = false;
},
err => {
this.errorMessage = err.error.message;
this.isSignUpFailed = true;
}
);
}
}
@@ -1,6 +1,6 @@
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';
import {authInterceptorProviders} from '../../helpers/auth.interceptor';
@@ -26,9 +26,9 @@ import {CustomHttpInterceptor} from '../../helpers/custom-http-interceptor';
})
export class TutorialsComponent {
tutorials?: Tutorial[];
/*
constructor(private tutorialService: TutorialService) {
this.retrieveTutorials();
// this.retrieveTutorials();
}
// ngOnInit(): void {
@@ -45,5 +45,5 @@ export class TutorialsComponent {
console.log(error);
}
);
}
}*/
}
@@ -1,8 +0,0 @@
import { ColumnResizeDirective } from './column-resize.directive';
describe('ColumnResizeDirective', () => {
it('should create an instance', () => {
const directive = new ColumnResizeDirective();
expect(directive).toBeTruthy();
});
});
@@ -1,114 +0,0 @@
import {
Directive,
ElementRef,
OnDestroy,
OnInit,
Renderer2,
NgZone,
Input,
} from '@angular/core';
import { Subject, fromEvent, takeUntil } from 'rxjs';
@Directive({
selector: '[appColumnResize]',
})
export class ColumnResizeDirective implements OnInit, OnDestroy {
@Input() resizableTable: HTMLElement | null = null;
private isResizing = false;
private startX!: number;
private startWidth!: number;
private column: HTMLElement;
private table: HTMLElement | null = null;
private resizer!: HTMLElement;
private destroy$ = new Subject<void>();
constructor(
private el: ElementRef,
private renderer: Renderer2,
private zone: NgZone
) {
this.column = this.el.nativeElement;
}
ngOnInit() {
this.table = this.resizableTable || this.findParentTable(this.column);
if (!this.table) {
console.error(
'Parent table not found. Make sure the directive is applied to a th element within a table.'
);
return;
}
this.createResizer();
this.initializeResizeListener();
}
private createResizer() {
this.resizer = this.renderer.createElement('div');
this.renderer.addClass(this.resizer, 'column-resizer');
this.renderer.setStyle(this.resizer, 'position', 'absolute');
this.renderer.setStyle(this.resizer, 'right', '0');
this.renderer.setStyle(this.resizer, 'top', '0');
this.renderer.setStyle(this.resizer, 'height', '100%');
this.renderer.setStyle(this.resizer, 'width', '5px');
this.renderer.setStyle(this.resizer, 'cursor', 'col-resize');
this.renderer.appendChild(this.column, this.resizer);
}
private initializeResizeListener() {
this.zone.runOutsideAngular(() => {
fromEvent(this.resizer, 'mousedown')
.pipe(takeUntil(this.destroy$))
.subscribe((e: Event) => this.onMouseDown(e as MouseEvent));
fromEvent(document, 'mousemove')
.pipe(takeUntil(this.destroy$))
.subscribe((e: Event) => this.onMouseMove(e as MouseEvent));
fromEvent(document, 'mouseup')
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.onMouseUp());
});
}
private onMouseDown(e: MouseEvent) {
e.preventDefault();
this.isResizing = true;
this.startX = e.pageX;
this.startWidth = this.column.offsetWidth;
this.renderer.addClass(this.column, 'resizing');
if (this.table) {
this.renderer.addClass(this.table, 'resizing');
}
}
private onMouseMove(e: MouseEvent) {
if (!this.isResizing) return;
const width = this.startWidth + (e.pageX - this.startX);
this.renderer.setStyle(this.column, 'width', `${width}px`);
}
private onMouseUp() {
if (!this.isResizing) return;
this.isResizing = false;
this.renderer.removeClass(this.column, 'resizing');
if (this.table) {
this.renderer.removeClass(this.table, 'resizing');
}
}
private findParentTable(element: HTMLElement): HTMLElement | null {
while (element && element.tagName !== 'TABLE') {
element = element.parentElement as HTMLElement;
if (!element) return null;
}
return element;
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
+8
View File
@@ -2,6 +2,14 @@ import {environment} from '../environments/environment';
export class GlobalConstants {
public static readonly TITLE = (() => {
return environment.title;
})();
public static readonly DEFAULT_PAGE = (() => {
return environment.default_page;
})();
public static readonly API_URL = (() => {
// ... calculate the value and return it
@@ -0,0 +1,7 @@
import { FileInfo } from './file-info';
describe('FileInfo', () => {
it('should create an instance', () => {
expect(new FileInfo()).toBeTruthy();
});
});
+4
View File
@@ -0,0 +1,4 @@
export class FileInfo {
name: string | undefined;
url: string | undefined;
}
+1 -1
View File
@@ -1,5 +1,5 @@
export class Image {
url?:string;
b64Json?:boolean;
b64string?:string;
}
@@ -8,6 +8,8 @@ export class Tutorial {
modified?: Date;
tobepublished?: boolean;
isEdit?: boolean;
titleimage?: string;
body?: string;
}
@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import {Observable} from 'rxjs';
import {HttpClient} from '@angular/common/http';
import {User} from '../models/user.model';
import {GlobalConstants} from '../global-constants';
import {User} from '../../models/user.model';
import {GlobalConstants} from '../../global-constants';
@Injectable({
providedIn: 'root'
@@ -11,11 +11,8 @@ import {
MatHeaderRowDef,
MatRow, MatRowDef, MatTable
} from '@angular/material/table';
import {MatIconButton} from '@angular/material/button';
import {MatList, MatListItem} from '@angular/material/list';
import {Bean} from '../../models/Bean';
import {SystemService} from '../../services/system.service';
import {NameValueItem} from '../../models/name-value-item';
import {SystemService} from '../../../services/system.service';
import {NameValueItem} from '../../../models/name-value-item';
@Component({
selector: 'app-settings.component',
@@ -14,8 +14,8 @@ import {
} from '@angular/material/table';
import {MatList, MatListItem} from '@angular/material/list';
import {MatIconButton} from '@angular/material/button';
import {SystemService} from '../../services/system.service';
import {Bean} from '../../models/Bean';
import {SystemService} from '../../../services/system.service';
import {Bean} from '../../../models/Bean';
import {MatIcon} from '@angular/material/icon';
@Component({
@@ -1,8 +1,8 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA, model} from '@angular/core';
import {UserService} from '../../services/user.service';
import {RolesService} from '../../services/roles.service';
import {Role} from '../../models/role';
import {User} from '../../models/user.model';
import {UserService} from '../../../services/user.service';
import {RolesService} from '../../../services/roles.service';
import {Role} from '../../../models/role';
import {User} from '../../../models/user.model';
import {
MatCell,
MatCellDef, MatColumnDef,
@@ -0,0 +1 @@
<router-outlet></router-outlet>
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component';
import { AiMainComponent } from './ai-main.component';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
describe('AiMainComponent', () => {
let component: AiMainComponent;
let fixture: ComponentFixture<AiMainComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HomeComponent]
imports: [AiMainComponent]
})
.compileComponents();
fixture = TestBed.createComponent(HomeComponent);
fixture = TestBed.createComponent(AiMainComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import {RouterOutlet} from '@angular/router';
@Component({
selector: 'app-ai-main.component',
imports: [
RouterOutlet
],
templateUrl: './ai-main.component.html',
styleUrl: './ai-main.component.scss'
})
export class AiMainComponent {
}
@@ -0,0 +1 @@
<p>ai-welcome.component works!</p>
@@ -1,20 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
import { AiWelcomeComponent } from './ai-welcome.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
describe('AiWelcomeComponent', () => {
let component: AiWelcomeComponent;
let fixture: ComponentFixture<AiWelcomeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LoginComponent ]
imports: [AiWelcomeComponent]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
fixture = TestBed.createComponent(AiWelcomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-ai-welcome.component',
imports: [],
templateUrl: './ai-welcome.component.html',
styleUrl: './ai-welcome.component.scss'
})
export class AiWelcomeComponent {
}
@@ -0,0 +1,21 @@
import {NgModule} from '@angular/core';
import {moderatorRouting} from '../moderator-module/moderator.routing';
import {ModeratorComponent} from '../moderator-module/moderator.component/moderator.component';
import {CommonModule} from '@angular/common';
import {AiWelcomeComponent} from './ai-welcome.component/ai-welcome.component';
import {AiMainComponent} from './ai-main.component/ai-main.component';
import {aiRouting} from './ai.routing';
import {MatDialogModule} from "@angular/material/dialog";
@NgModule({
declarations: [],
imports: [
aiRouting,
AiMainComponent,
CommonModule,
MatDialogModule
]
})
export class AiModule { }
@@ -0,0 +1,22 @@
import {RouterModule, Routes} from '@angular/router';
import {AiMainComponent} from './ai-main.component/ai-main.component';
const AI_ROUTES: Routes = [
{
path: '',
component: AiMainComponent,
children: [
{
path: 'ai-welcome',
loadComponent: () => import('../ai-module/ai-welcome.component/ai-welcome.component').then((c) => c.AiWelcomeComponent),
},
{
path: 'generate-image',
loadComponent: () => import('../ai-module/generate-image.component/generate-image.component').then((c) => c.GenerateImageComponent)
}
]
}
];
export const aiRouting = RouterModule.forChild(AI_ROUTES);
@@ -0,0 +1,49 @@
<div class="generate-image-view">
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title>Generate image</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-form-field class="example-full-width">
<mat-label>Query</mat-label>
<input matInput required
[(ngModel)]="query">
</mat-form-field>
</mat-card-content>
<mat-card-actions>
@if (spinnerService.visibility | async) {
<mat-spinner [diameter]="25" ></mat-spinner>
} @else {
<button matButton (click)="generateImage()" >Generate</button>
}
</mat-card-actions>
</mat-card>
@for (image of images; track image) {
<mat-card>
<mat-card-header>
<div class="image-container">
<img src="{{image.url}}"
alt="" width="100%" height="100%">
<mat-toolbar class="image-toolbar">
<mat-toolbar-row>
<span class="menu-spacer"></span>
<!-- <button matMiniFab matTooltip="Dawnload image" aria-label="Download" (click)="downloadImage(image.url)">
<mat-icon>download</mat-icon>
</button>-->
@if (isLoggedIn) {
<button matMiniFab matTooltip="Save to server" aria-label="Save" (click)="save(image.url)" >
<mat-icon>upload_file</mat-icon>
</button>
}
</mat-toolbar-row>
</mat-toolbar>
</div>
</mat-card-header>
<mat-card-content>
</mat-card-content>
</mat-card>
}
</div>
@@ -0,0 +1,42 @@
.generate-image-view {
margin-right: 12px;
padding: 20px 22px 18px 22px;
margin-top: 81px;
margin-left: 12px;
background-color: #e7edf3c2;
backdrop-filter: blur(8px);
}
.image-container{
width: 100%;
height: 100%;
}
.image-container .image-toolbar
{
background-color: rgba(153,153,153,0);
color: white;
position: absolute;
left : 50%;
top: 5%;
transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
padding: 0px 2px;
border: none;
border-radius: 5px;
}
.image-container .menu-spacer {
flex: 1 1 auto;
}
.image-container .image-file-name {
padding-left: 10px;
}
.mat-mdc-mini-fab{
margin: 5px;
}
.example-full-width {
width: 100%;
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GenerateImageComponent } from './generate-image.component';
describe('GenerateImageComponent', () => {
let component: GenerateImageComponent;
let fixture: ComponentFixture<GenerateImageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [GenerateImageComponent]
})
.compileComponents();
fixture = TestBed.createComponent(GenerateImageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,126 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA, Inject, inject, Renderer2} from '@angular/core';
import {AsyncPipe, DOCUMENT, NgOptimizedImage} from "@angular/common";
import {FormsModule} from "@angular/forms";
import {MatButton, MatMiniFabButton} from "@angular/material/button";
import {
MatCard,
MatCardActions,
MatCardContent,
MatCardHeader,
MatCardImage,
MatCardXlImage
} from "@angular/material/card";
import {MatFormField, MatInput, MatLabel} from "@angular/material/input";
import {Image} from '../../../models/image';
import {ZhipuaiImageService} from '../zhipuai-image.service';
import {SpinnerService} from '../../../services/spinner.service';
import {MatProgressSpinner} from '@angular/material/progress-spinner';
import {FileUploadService} from '../../../services/file-upload-api.service';
import {Observable, Subscription} from 'rxjs';
import {HttpClient, HttpResponse} from '@angular/common/http';
import {MatToolbar, MatToolbarRow} from '@angular/material/toolbar';
import {MatIcon} from '@angular/material/icon';
import {MatTooltip} from '@angular/material/tooltip';
import {AuthService} from '../../../services/auth.service';
import {TokenStorageService} from '../../../services/token-storage.service';
import {FileInfo} from '../../../models/file-info';
import {UserApiService} from '../../user-module/user-api.service';
import {MatProgressBar} from '@angular/material/progress-bar';
@Component({
selector: 'app-generate-image.component',
imports: [
AsyncPipe,
FormsModule,
MatButton,
MatCard,
MatCardActions,
MatCardContent,
MatCardHeader,
MatFormField,
MatInput,
MatLabel,
MatProgressSpinner,
NgOptimizedImage,
MatCardXlImage,
MatCardImage,
MatToolbarRow,
MatToolbar,
MatIcon,
MatMiniFabButton,
MatTooltip,
MatProgressBar
],
templateUrl: './generate-image.component.html',
styleUrl: './generate-image.component.scss',
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class GenerateImageComponent {
isSpinnerVisible = false;
query : string = '';
images: Image[] = [];
image: Image = {
url: ''
};
currentFile?: File;
message = '';
isLoggedIn = false;
private storageService: TokenStorageService = inject(TokenStorageService);
constructor(
private zhipuaiImageService: ZhipuaiImageService,
public spinnerService: SpinnerService,
private uploadService: FileUploadService,
private userApiService:UserApiService,
) {
this.isLoggedIn = this.storageService.isLoggedIn();
}
generateImage(){
this.zhipuaiImageService.generate(this.query).subscribe(
data => {
this.image = data;
this.images.unshift(this.image);
// this.output.file = data.;
console.log(data);
},
error => {
console.log(error);
}
)
}
save(url: string | undefined) {
let requestUrl:string = url?url:"";
console.log(requestUrl);
this.userApiService.saveZhipuaiImage(requestUrl).subscribe(data =>{
console.log(data);
},error =>{
console.log(error);
});
}
}
@@ -1,10 +1,10 @@
import { Injectable } from '@angular/core';
import {Observable} from 'rxjs';
import {Tutorial} from '../models/tutorial.model';
import {Tutorial} from '../../models/tutorial.model';
import {HttpClient} from '@angular/common/http';
import {Image} from '../models/image';
import {SpinnerService} from './spinner.service';
import {GlobalConstants} from '../global-constants';
import {Image} from '../../models/image';
import {SpinnerService} from '../../services/spinner.service';
import {GlobalConstants} from '../../global-constants';
@@ -1,20 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProfileComponent } from './profile.component';
import { DialogLoginComponent } from './dialog-login.component';
describe('ProfileComponent', () => {
let component: ProfileComponent;
let fixture: ComponentFixture<ProfileComponent>;
describe('DialogLoginComponent', () => {
let component: DialogLoginComponent;
let fixture: ComponentFixture<DialogLoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ProfileComponent ]
imports: [DialogLoginComponent]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ProfileComponent);
fixture = TestBed.createComponent(DialogLoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@@ -0,0 +1,86 @@
import {
ChangeDetectionStrategy,
Component,
CUSTOM_ELEMENTS_SCHEMA,
EventEmitter, Inject,
inject,
Output,
signal
} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {MatButton, MatIconButton} 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 {DialogLoginData} from '../main.component';
@Component({
selector: 'app-dialog-login.component',
imports: [
FormsModule,
MatButton,
MatDialogActions,
MatDialogClose,
MatDialogContent,
MatDialogTitle,
MatFormField,
MatIcon,
MatIconButton,
MatInput,
MatLabel,
MatSuffix
],
templateUrl: './dialog-login.component.html',
styleUrl: './dialog-login.component.scss',
schemas: [CUSTOM_ELEMENTS_SCHEMA],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DialogLoginComponent {
readonly dialogRef = inject(MatDialogRef<DialogLoginComponent>);
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:DialogLoginData) {
}
openLoginFailedSnackBar(errorMessage : string = "Login failed.") {
this._snackBar.open(errorMessage , "", {
duration: this.durationInSeconds * 1000,
});
}
onNoClick() {
this.dialogRef.close();
}
clickEvent(event: MouseEvent) {
this.hide.set(!this.hide());
event.stopPropagation();
}
login() {
this.loginClicked.emit(this.data);
}
signup() {
this.signupClicked.emit();
}
}
@@ -22,7 +22,7 @@ import {
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 {DialogSignupData} from '../main.component';
import {ErrorStateMatcher} from '@angular/material/core';
@Component({
@@ -1,15 +1,13 @@
<mat-toolbar class="fixed-top">
<button mat-raised-button routerLink="generate-image">
<button mat-button routerLink="">
{{title}}
</button>
<button mat-raised-button routerLink="./ai/generate-image">
Generate Image
</button>
<button mat-raised-button routerLink="tutorials">
<button mat-raised-button routerLink="./tutorials/tutorials-all">
Tutorials
</button>
@if (isLoggedIn) {
<button mat-raised-button routerLink="add-tutorial" >
Add tutorial
</button>
}
<span class="menu-spacer"></span>
@@ -58,6 +56,8 @@
</mat-menu>
}
</mat-toolbar>
<div class="router_outlet">
<router-outlet/>
</div>
@@ -16,7 +16,11 @@ mat-toolbar{
}
.mat-mdc-raised-button:not(:disabled){
color: rgba(24, 255, 255, 0.96);
background-color: rgba(24,255,255,0.04);
background-color: rgba(24, 255, 255, 0);
}
.mat-mdc-button:not(:disabled){
color: rgba(24, 255, 255, 0.96);
background-color: rgba(24, 255, 255, 0);
}
.mat-mdc-mini-fab{
margin: 5px;
@@ -1,35 +1,30 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
//import {NavigationComponent} from '../navigation/navigation.component';
import {Router, RouterLink, RouterOutlet} from '@angular/router';
import {MatToolbar} from '@angular/material/toolbar';
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';
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 {TokenStorageService} from '../../../services/token-storage.service';
import {AuthService} from '../../../services/auth.service';
import {EventBusService} from '../../../_shared/event-bus.service';
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';
import {HttpClient} from '@angular/common/http';
import { environment } from '../../../environments/environment';
import { environment } from '../../../../environments/environment';
import {DialogLoginComponent} from './dialog-login.component/dialog-login.component';
import {DialogSignupComponent} from './dialog-signup.component/dialog-signup.component';
import {GlobalConstants} from '../../../global-constants';
@Component({
selector: 'app-main.component',
imports: [
MatToolbar,
MatButton,
RouterLink,
RouterOutlet,
CommonModule,
MatIcon,
@@ -39,13 +34,14 @@ import { environment } from '../../../environments/environment';
MatMenuItem,
MatTooltip,
MatLabel
],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
templateUrl: './main.component.html',
styleUrl: './main.component.scss'
})
export class MainComponent implements OnInit{
title = GlobalConstants.TITLE;
// public props
navCollapsed: boolean = false;
navCollapsedMob: boolean = false;
@@ -76,8 +72,6 @@ export class MainComponent implements OnInit{
}
ngOnInit(): void {
this.refreshToolbar();
this.eventBusSub = this.eventBusService.on('logout', () => {
@@ -140,7 +134,7 @@ export class MainComponent implements OnInit{
this.storageService.clean();
//window.location.reload();
this.router.navigate([environment.default_page]).then(() => {
this.router.navigate([GlobalConstants.DEFAULT_PAGE]).then(() => {
window.location.reload();
});
//window.location.reload();
@@ -153,7 +147,7 @@ export class MainComponent implements OnInit{
openLoginDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
let dialogLoginRef = this.dialog.open(DialogComponent, {
let dialogLoginRef = this.dialog.open(DialogLoginComponent, {
width: '350px',
enterAnimationDuration,
exitAnimationDuration,
@@ -187,7 +181,7 @@ export class MainComponent implements OnInit{
dialogLoginRef.close()
this.router.navigate(['main/tutorials']);
this.router.navigate([GlobalConstants.DEFAULT_PAGE]);
},
err => {
dialogLoginRef.componentInstance.openLoginFailedSnackBar(err.error.message);
@@ -2,14 +2,9 @@ import {RouterModule} from '@angular/router';
import {MainComponent} from './main.component/main.component';
import {mainRouting} from './main.routing';
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
import {CustomHttpInterceptor} from '../helpers/custom-http-interceptor';
import {AngularImageViewerModule} from '@hreimer/angular-image-viewer';
import {BrowserModule} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {authInterceptorProviders} from '../helpers/auth.interceptor';
import {CustomHttpInterceptor} from '../../helpers/custom-http-interceptor';
import {authInterceptorProviders} from '../../helpers/auth.interceptor';
@NgModule({
declarations: [
@@ -17,10 +12,7 @@ import {authInterceptorProviders} from '../helpers/auth.interceptor';
],
imports: [
mainRouting,
MainComponent,
MainComponent
],
exports: [RouterModule],
providers: [authInterceptorProviders,provideHttpClient(withInterceptorsFromDi()),{
@@ -1,16 +1,21 @@
import {RouterModule, Routes} from '@angular/router';
import {HomeComponent} from '../components/home.component/home.component';
import {MainComponent} from './main.component/main.component';
import {TutorialsComponent} from '../components/tutorials.component/tutorials.component';
import {RegisterComponent} from '../components/register/register.component';
import {ProfileComponent} from '../components/profile/profile.component';
import {GenerateImageComponent} from '../components/generate-image.component/generate-image.component';
const MAIN_ROUTES: Routes =[
{
path: '',
component: MainComponent,
children: [
{
path: 'tutorials',
loadChildren: () =>
import('../tutorials-module/tutorials.module').then((m) => m.TutorialsModule),
},
{
path: 'ai',
loadChildren: () =>
import('../ai-module/ai.module').then((m) => m.AiModule),
},
{
path: 'admin',
loadChildren: () =>
@@ -25,26 +30,6 @@ const MAIN_ROUTES: Routes =[
path: 'moderator',
loadChildren: () =>
import('../moderator-module/moderator.module').then((m) => m.ModeratorModule),
},
{
path: 'home',
component: HomeComponent
},
{
path: 'generate-image',
component: GenerateImageComponent
},
{
path: 'tutorials',
component: TutorialsComponent
},
{
path: 'register',
component : RegisterComponent
},
{
path: 'profile',
component : ProfileComponent
}
],
}
@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {Tutorial} from '../models/tutorial.model';
import {GlobalConstants} from '../global-constants';
import {Tutorial} from '../../models/tutorial.model';
import {GlobalConstants} from '../../global-constants';
@Injectable({
providedIn: 'root'
@@ -1,5 +1,5 @@
import {RouterModule, Routes} from '@angular/router';
import {UserComponent} from '../user-module/user.component/user.component';
import {ModeratorComponent} from './moderator.component/moderator.component';
const MODERATOR_ROUTES: Routes = [

Some files were not shown because too many files have changed in this diff Show More