Add new components for AI and tutorials, update routing, and enhance file upload functionality
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<p>ai-models.component works!</p>
|
||||
+23
@@ -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 {
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<h1 mat-dialog-title>Hi </h1>
|
||||
<div mat-dialog-content >
|
||||
<mat-dialog-content class="mat-typography">
|
||||
<!-- <mat-nav-list>-->
|
||||
@for (fileInfo of fileInfos; track fileInfo){
|
||||
<a mat-list-item (click)="select(fileInfo)">
|
||||
<img style="width: 120px; height: 120px" src="{{fileInfo.url}}" alt="">
|
||||
<span class="entry">
|
||||
|
||||
<!-- @if (!isCollapsed) {-->
|
||||
<span >{{fileInfo.name}}</span>
|
||||
<!-- }-->
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
|
||||
<!-- </mat-nav-list>-->
|
||||
</mat-dialog-content>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="upload()">Upload</button>
|
||||
<span class="menu-spacer"></span>
|
||||
<button mat-button mat-dialog-close (click)="onNoClick()">No Thanks</button>
|
||||
<!-- <button mat-button cdkFocusInitial (click)="select()">Ok</button>-->
|
||||
</div>
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
.menu-spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.mat-dialog-content{
|
||||
min-height: 300px;
|
||||
min-width: 300px;
|
||||
|
||||
height: 75%;
|
||||
width: 75%;
|
||||
}
|
||||
.mdc-dialog--open .mat-mdc-dialog-inner-container
|
||||
{
|
||||
opacity: 1;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.mat-mdc-dialog-container {
|
||||
width: 600px;
|
||||
height: 500px;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
max-height: inherit;
|
||||
min-height: inherit;
|
||||
min-width: inherit;
|
||||
max-width: inherit;
|
||||
outline: 0;
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DialogSelectImageComponent } from './dialog-select-image.component';
|
||||
|
||||
describe('DialogSelectImageComponent', () => {
|
||||
let component: DialogSelectImageComponent;
|
||||
let fixture: ComponentFixture<DialogSelectImageComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DialogSelectImageComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DialogSelectImageComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import {Component, EventEmitter, inject, Output} from '@angular/core';
|
||||
import {
|
||||
MatDialogActions,
|
||||
MatDialogClose,
|
||||
MatDialogContent,
|
||||
MatDialogRef,
|
||||
MatDialogTitle
|
||||
} from '@angular/material/dialog';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
import {MatListItem, MatNavList} from '@angular/material/list';
|
||||
import {RouterLink} from '@angular/router';
|
||||
import {FileInfo} from '../../../models/file-info';
|
||||
import {UserApiService} from '../user-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dialog-select-image.component',
|
||||
imports: [
|
||||
MatDialogContent,
|
||||
MatDialogTitle,
|
||||
MatButton,
|
||||
MatDialogActions,
|
||||
MatDialogClose,
|
||||
|
||||
MatListItem,
|
||||
MatNavList,
|
||||
|
||||
],
|
||||
templateUrl: './dialog-select-image.component.html',
|
||||
styleUrl: './dialog-select-image.component.scss'
|
||||
})
|
||||
export class DialogSelectImageComponent {
|
||||
fileInfos?: FileInfo[] = [];
|
||||
|
||||
@Output() uploadClicked = new EventEmitter<any>();
|
||||
@Output() selectClicked = new EventEmitter<any>();
|
||||
|
||||
readonly dialogRef = inject(MatDialogRef<DialogSelectImageComponent>);
|
||||
|
||||
constructor(private userApiService: UserApiService) {
|
||||
this.userApiService.getImages().subscribe(data => {
|
||||
this.fileInfos = data;
|
||||
console.log(data);
|
||||
});
|
||||
}
|
||||
upload() {
|
||||
|
||||
this.uploadClicked.emit();
|
||||
|
||||
}
|
||||
|
||||
onNoClick() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
select(fileInfo:FileInfo) {
|
||||
this.selectClicked.emit(fileInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<p>images.component works!</p>
|
||||
|
||||
<div class="generate-image-view">
|
||||
|
||||
|
||||
@for (fileInfo of fileInfos; track fileInfo) {
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<div class="image-container">
|
||||
<img src="{{fileInfo.url}}"
|
||||
alt="" width="100%" height="100%">
|
||||
<mat-toolbar class="image-toolbar">
|
||||
<mat-toolbar-row>
|
||||
<label class="image-file-name">{{fileInfo.name}}</label>
|
||||
<span class="menu-spacer"></span>
|
||||
<button matMiniFab matTooltip="Dawnload image" aria-label="Download" (click)="downloadImage(fileInfo.url)">
|
||||
<mat-icon>download</mat-icon>
|
||||
</button>
|
||||
|
||||
</mat-toolbar-row>
|
||||
</mat-toolbar>
|
||||
</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
.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;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ImagesComponent } from './images.component';
|
||||
|
||||
describe('ImagesComponent', () => {
|
||||
let component: ImagesComponent;
|
||||
let fixture: ComponentFixture<ImagesComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ImagesComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ImagesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject} from '@angular/core';
|
||||
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
|
||||
import {MatMiniFabButton} from '@angular/material/button';
|
||||
import {MatToolbar, MatToolbarRow} from '@angular/material/toolbar';
|
||||
import {MatTooltip} from '@angular/material/tooltip';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {FileInfo} from '../../../models/file-info';
|
||||
import {UserApiService} from '../user-api.service';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'app-images.component',
|
||||
imports: [
|
||||
MatCard,
|
||||
MatCardContent,
|
||||
MatCardHeader,
|
||||
MatIcon,
|
||||
MatMiniFabButton,
|
||||
MatToolbar,
|
||||
MatToolbarRow,
|
||||
MatTooltip
|
||||
],
|
||||
templateUrl: './images.component.html',
|
||||
styleUrl: './images.component.scss',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class ImagesComponent {
|
||||
fileInfos?: FileInfo[] = [];
|
||||
|
||||
|
||||
constructor(private userApiService: UserApiService) {
|
||||
this.userApiService.getImages().subscribe(data =>{
|
||||
this.fileInfos = data;
|
||||
console.log(data);
|
||||
});
|
||||
}
|
||||
|
||||
downloadImage(url: string | undefined) {
|
||||
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<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="images">
|
||||
<span class="entry">
|
||||
<mat-icon>imagesmode</mat-icon>
|
||||
@if (!isCollapsed) {
|
||||
<span >Images</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
</mat-nav-list>
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
.entry{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding:0.75rem;
|
||||
color: rgba(24, 255, 255, 0.96);
|
||||
|
||||
}
|
||||
|
||||
a.mdc-list-item
|
||||
{
|
||||
|
||||
background-color: rgba(24,255,255,0.04);
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SideBarUserComponent } from './side-bar-user.component';
|
||||
|
||||
describe('SideBarUserComponent', () => {
|
||||
let component: SideBarUserComponent;
|
||||
let fixture: ComponentFixture<SideBarUserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SideBarUserComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SideBarUserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {MatListItem, MatNavList} from '@angular/material/list';
|
||||
import {RouterLink} from '@angular/router';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
|
||||
@Component({
|
||||
selector: 'app-side-bar-user',
|
||||
imports: [
|
||||
MatIcon,
|
||||
MatListItem,
|
||||
MatNavList,
|
||||
RouterLink
|
||||
],
|
||||
templateUrl: './side-bar-user.component.html',
|
||||
styleUrl: './side-bar-user.component.scss',
|
||||
schemas:[CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class SideBarUserComponent {
|
||||
isCollapsed = false;
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Add tutorial</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content >
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Title</mat-label>
|
||||
|
||||
<input matInput required
|
||||
[(ngModel)]="tutorial.title">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Use title image</mat-label>
|
||||
<img style="width: 120px; height: 120px" src="{{tutorial.titleimage}}" alt="">
|
||||
<input matInput
|
||||
[(ngModel)]="tutorial.titleimage">
|
||||
<button mat-raised-button color="primary" (click)="openSelectImageDialog('100ms', '5ms')">Select image</button>
|
||||
|
||||
</mat-form-field>
|
||||
<div class="example-full-width">
|
||||
<mat-label>Description</mat-label>
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Markdown text">
|
||||
<div class="markdown-editor-container">
|
||||
<div class="markdown-editor">
|
||||
<form novalidate>
|
||||
<angular-markdown-editor
|
||||
textareaId="editor1"
|
||||
[options]="editorOptions"
|
||||
name="markdownText"
|
||||
[(ngModel)]="tutorial.description"
|
||||
(onFullscreenExit)="hidePreview()"
|
||||
>
|
||||
</angular-markdown-editor>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<markdown class="variable-binding" [data]="tutorial.description"></markdown>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
<div class="example-full-width">
|
||||
<mat-label>Body</mat-label>
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Markdown text">
|
||||
<div class="markdown-editor-container">
|
||||
<div class="markdown-editor">
|
||||
<form novalidate>
|
||||
<angular-markdown-editor
|
||||
textareaId="editor2"
|
||||
[options]="editorOptions"
|
||||
name="markdownText"
|
||||
[(ngModel)]="tutorial.body"
|
||||
(onFullscreenExit)="hidePreview()"
|
||||
>
|
||||
</angular-markdown-editor>
|
||||
</form>
|
||||
</div>
|
||||
<markdown class="variable-binding" [data]="tutorial.body"></markdown>
|
||||
</div>
|
||||
|
||||
</mat-tab>
|
||||
<mat-tab label="Result">
|
||||
<markdown class="preview" [data]="tutorial.body"></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>
|
||||
</div>
|
||||
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
@if (submitted){
|
||||
<div >
|
||||
<h4>Tutorial was submitted successfully!</h4>
|
||||
<button matButton (click)="newTutorial()">Add new tutorial</button>
|
||||
</div>
|
||||
} @else {
|
||||
<button matButton (click)="saveTutorial()" >Save</button>
|
||||
}
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
.submit-form {
|
||||
max-width: 400px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.markdown-editor{
|
||||
max-width: 300px;
|
||||
}
|
||||
.markdown-editor-container{
|
||||
display: flex;
|
||||
}
|
||||
mat-card{
|
||||
margin: 20px;
|
||||
}
|
||||
mat-card-title{
|
||||
color: cyan;
|
||||
}
|
||||
|
||||
.example-form {
|
||||
min-width: 150px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.example-full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.variable-binding,
|
||||
.variable-textarea {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.variable-textarea {
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.07);
|
||||
min-height: 420px;
|
||||
padding: 8px;
|
||||
transition: all 300ms ease-out;
|
||||
}
|
||||
|
||||
.variable-textarea:hover {
|
||||
box-shadow: 0 6px 12px 3px rgba(0,0,0,.09),
|
||||
0 2px 3px 1px rgba(0,0,0,.06);
|
||||
}
|
||||
|
||||
.variable-binding {
|
||||
display: block;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.preview {
|
||||
/* display: block;
|
||||
float: right;*/
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TutorialAddComponent } from './tutorial-add.component';
|
||||
|
||||
describe('TutorialAddComponent', () => {
|
||||
let component: TutorialAddComponent;
|
||||
let fixture: ComponentFixture<TutorialAddComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TutorialAddComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TutorialAddComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
|
||||
import {UserApiService} from '../user-api.service';
|
||||
import {TokenStorageService} from '../../../services/token-storage.service';
|
||||
import {EventBusService} from '../../../_shared/event-bus.service';
|
||||
import {Router} from '@angular/router';
|
||||
import {AuthService} from '../../../services/auth.service';
|
||||
import {Tutorial} from '../../../models/tutorial.model';
|
||||
import {MarkdownComponent, MarkdownService} from 'ngx-markdown';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
|
||||
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
|
||||
import {MatTab, MatTabGroup} from '@angular/material/tabs';
|
||||
import {FormBuilder, FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {AngularMarkdownEditorModule, EditorInstance, EditorOption} from 'angular-markdown-editor';
|
||||
import {DialogLoginComponent} from '../../main-module/main.component/dialog-login.component/dialog-login.component';
|
||||
import {GlobalConstants} from '../../../global-constants';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {DialogSelectImageComponent} from '../dialog-select-image.component/dialog-select-image.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tutorial-add.component',
|
||||
imports: [
|
||||
MarkdownComponent,
|
||||
MatButton,
|
||||
MatCard,
|
||||
MatCardActions,
|
||||
MatCardContent,
|
||||
MatCardHeader,
|
||||
MatFormField,
|
||||
MatInput,
|
||||
MatLabel,
|
||||
MatTab,
|
||||
MatTabGroup,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
AngularMarkdownEditorModule
|
||||
],
|
||||
templateUrl: './tutorial-add.component.html',
|
||||
styleUrl: './tutorial-add.component.scss',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class TutorialAddComponent implements OnInit{
|
||||
|
||||
tutorial: Tutorial = new Tutorial();
|
||||
submitted = false;
|
||||
bsEditorInstance!: EditorInstance;
|
||||
markdownText = '';
|
||||
showEditor = true;
|
||||
tutorialForm!: FormGroup;
|
||||
editorOptions!: EditorOption;
|
||||
|
||||
readonly dialog = inject(MatDialog);
|
||||
|
||||
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 fb: FormBuilder,
|
||||
private markdownService: MarkdownService,
|
||||
private userApiService: UserApiService,
|
||||
private storageService: TokenStorageService,
|
||||
private eventBusService: EventBusService,
|
||||
private router: Router,
|
||||
private authService: AuthService
|
||||
) {
|
||||
|
||||
this.tutorial.description = this.markdown;
|
||||
this.tutorial.body = this.markdown;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.editorOptions = {
|
||||
autofocus: false,
|
||||
iconlibrary: 'fa',
|
||||
savable: false,
|
||||
onFullscreenExit: (e) => this.hidePreview(),
|
||||
onShow: (e) => this.bsEditorInstance = e,
|
||||
parser: (val) => this.parse(val)
|
||||
};
|
||||
this.buildForm(this.tutorial.description);
|
||||
|
||||
}
|
||||
|
||||
buildForm(markdownText: string | undefined) {
|
||||
this.tutorialForm = this.fb.group({
|
||||
body: [markdownText],
|
||||
isPreview: [true]
|
||||
});
|
||||
}
|
||||
selectImage() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
/** highlight all code found, needs to be wrapped in timer to work properly */
|
||||
highlight() {
|
||||
setTimeout(() => {
|
||||
this.markdownService.highlight();
|
||||
});
|
||||
}
|
||||
|
||||
hidePreview() {
|
||||
if (this.bsEditorInstance && this.bsEditorInstance.hidePreview) {
|
||||
this.bsEditorInstance.hidePreview();
|
||||
}
|
||||
}
|
||||
|
||||
showFullScreen(isFullScreen: boolean) {
|
||||
if (this.bsEditorInstance && this.bsEditorInstance.setFullscreen) {
|
||||
this.bsEditorInstance.showPreview();
|
||||
this.bsEditorInstance.setFullscreen(isFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
parse(inputValue: string) {
|
||||
const markedOutput = this.markdownService.parse(inputValue.trim());
|
||||
this.highlight();
|
||||
|
||||
return markedOutput;
|
||||
}
|
||||
|
||||
onFormChanges(): void {
|
||||
this.tutorialForm.valueChanges.subscribe(formData => {
|
||||
if (formData) {
|
||||
this.markdownText = formData.body;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveTutorial(): void {
|
||||
const data = {
|
||||
title: this.tutorial.title,
|
||||
description: this.tutorial.description,
|
||||
body: this.tutorial.body,
|
||||
published: this.tutorial.published,
|
||||
titleimage: this.tutorial.titleimage,
|
||||
created: new Date(),
|
||||
modified: new Date(),
|
||||
tobepublished: false,
|
||||
isEdit: false,
|
||||
isAdmin: false,
|
||||
isSuperAdmin: false,
|
||||
isUser: false,
|
||||
};
|
||||
|
||||
this.userApiService.create(data)
|
||||
.subscribe(
|
||||
response => {
|
||||
console.log(response);
|
||||
this.submitted = true;
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
newTutorial(): void {
|
||||
this.submitted = false;
|
||||
this.tutorial = {
|
||||
title: '',
|
||||
description: '',
|
||||
published: false
|
||||
};
|
||||
}
|
||||
|
||||
openSelectImageDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
|
||||
|
||||
let dialogSelectRef = this.dialog.open(DialogSelectImageComponent, {
|
||||
height: '500px',
|
||||
width: '600px',
|
||||
enterAnimationDuration,
|
||||
exitAnimationDuration,
|
||||
// data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
|
||||
});
|
||||
dialogSelectRef.componentInstance.uploadClicked.subscribe(result => {
|
||||
dialogSelectRef.close();
|
||||
//this.openSignupDialog(enterAnimationDuration, exitAnimationDuration);
|
||||
})
|
||||
|
||||
const dialogSelectSubscription = dialogSelectRef.componentInstance.selectClicked
|
||||
.subscribe(result => {
|
||||
console.log('Got the data!', result);
|
||||
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
this.tutorial.titleimage = result.url;
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Edit tutorial</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content >
|
||||
<mat-form-field class="example-full-width">
|
||||
<mat-label>Title</mat-label>
|
||||
<input matInput required
|
||||
[(ngModel)]="tutorial.title">
|
||||
</mat-form-field>
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Markdown text">
|
||||
<textarea class="variable-textarea" [(ngModel)]="tutorial.description"></textarea>
|
||||
<markdown class="variable-binding" [data]="tutorial.description"></markdown>
|
||||
</mat-tab>
|
||||
<mat-tab label="Result">
|
||||
<markdown class="preview" [data]="tutorial.description"></markdown>
|
||||
</mat-tab>
|
||||
<mat-tab label="Example">
|
||||
<textarea class="variable-textarea" [(ngModel)]="markdown"></textarea>
|
||||
<markdown class="variable-binding" [data]="markdown"></markdown>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
|
||||
<button matButton (click)="updateTutorial()">Save</button>
|
||||
@if (hasError){
|
||||
<mat-error>
|
||||
{{errorMessage}}
|
||||
</mat-error>
|
||||
}
|
||||
@if(submitted) {
|
||||
<h4>Tutorial was submitted successfully!</h4>
|
||||
<button matButton routerLink="../tutorial-add">Add new tutorial</button>
|
||||
<button matButton routerLink="../tutorials-list">Back to list</button>
|
||||
}
|
||||
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
.submit-form {
|
||||
max-width: 400px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
mat-card{
|
||||
//margin: 20px;
|
||||
}
|
||||
mat-card-title{
|
||||
color: cyan;
|
||||
}
|
||||
|
||||
.example-form {
|
||||
min-width: 150px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.example-full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.variable-binding,
|
||||
.variable-textarea {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.variable-textarea {
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.07);
|
||||
min-height: 420px;
|
||||
padding: 8px;
|
||||
transition: all 300ms ease-out;
|
||||
}
|
||||
|
||||
.variable-textarea:hover {
|
||||
box-shadow: 0 6px 12px 3px rgba(0,0,0,.09),
|
||||
0 2px 3px 1px rgba(0,0,0,.06);
|
||||
}
|
||||
|
||||
.variable-binding {
|
||||
display: block;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.preview {
|
||||
/* display: block;
|
||||
float: right;*/
|
||||
}
|
||||
|
||||
.mat-mdc-card-outlined {
|
||||
background-color: var(--mat-card-outlined-container-color, var(--mat-sys-surface));
|
||||
border-radius:0;
|
||||
border-width: var(--mat-card-outlined-outline-width, 1px);
|
||||
border-color: var(--mat-card-outlined-outline-color, var(--mat-sys-outline-variant));
|
||||
box-shadow: var(--mat-card-outlined-container-elevation, var(--mat-sys-level0));
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TutorialEditComponent } from './tutorial-edit.component';
|
||||
|
||||
describe('TutorialEditComponent', () => {
|
||||
let component: TutorialEditComponent;
|
||||
let fixture: ComponentFixture<TutorialEditComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TutorialEditComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TutorialEditComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {MarkdownComponent} from 'ngx-markdown';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
|
||||
import {MatError, MatFormField, MatInput, MatLabel} from '@angular/material/input';
|
||||
import {MatTab, MatTabGroup} from '@angular/material/tabs';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {Tutorial} from '../../../models/tutorial.model';
|
||||
import {UserApiService} from '../user-api.service';
|
||||
import {ActivatedRoute, RouterLink} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tutorial-edit.component',
|
||||
imports: [
|
||||
MarkdownComponent,
|
||||
MatButton,
|
||||
MatCard,
|
||||
MatCardActions,
|
||||
MatCardContent,
|
||||
MatCardHeader,
|
||||
MatFormField,
|
||||
MatInput,
|
||||
MatLabel,
|
||||
MatTab,
|
||||
MatTabGroup,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
MatFormField,
|
||||
RouterLink,
|
||||
MatError
|
||||
],
|
||||
templateUrl: './tutorial-edit.component.html',
|
||||
styleUrl: './tutorial-edit.component.scss',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class TutorialEditComponent {
|
||||
tutorial: Tutorial = new Tutorial();
|
||||
submitted = false;
|
||||
hasError = false;
|
||||
errorMessage = '';
|
||||
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`;
|
||||
private id: string | null | undefined;
|
||||
|
||||
constructor(private userApiService: UserApiService, private route: ActivatedRoute) {
|
||||
|
||||
this.route.queryParams
|
||||
.subscribe(params => {
|
||||
console.log(params);
|
||||
this.id = params['id'];
|
||||
console.log(this.id);
|
||||
});
|
||||
|
||||
this.userApiService.getTutorial(this.id).subscribe(
|
||||
data=>{
|
||||
this.tutorial = data;
|
||||
}
|
||||
);
|
||||
}
|
||||
updateTutorial(): void {
|
||||
this.userApiService.update(this.id, this.tutorial)
|
||||
.subscribe(
|
||||
response => {
|
||||
console.log(response);
|
||||
this.submitted = true;
|
||||
this.hasError = false;
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
|
||||
this.errorMessage = error.error.message;
|
||||
|
||||
this.hasError = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
<article class="table-header">
|
||||
<button
|
||||
class="button-remove-rows"
|
||||
mat-button
|
||||
(click)="removeSelectedRows()"
|
||||
>
|
||||
Remove Rows
|
||||
</button>
|
||||
<button mat-raised-button routerLink="../tutorial-add">Add tutorial</button>
|
||||
|
||||
</article>
|
||||
<table mat-table [dataSource]="dataSource">
|
||||
@for (column of columnsSchema; track column){
|
||||
<ng-container [matColumnDef]="column.key">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
@switch (column.key) {
|
||||
@case ('isSelected') {
|
||||
<mat-checkbox
|
||||
(change)="selectAll($event)"
|
||||
[checked]="isAllSelected()"
|
||||
[indeterminate]="!isAllSelected() && isAnySelected()"
|
||||
></mat-checkbox>
|
||||
}
|
||||
@default {
|
||||
{{ column.label }}
|
||||
}
|
||||
}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<!-- @if (element.isEdit) {-->
|
||||
@switch (column.type) {
|
||||
@case ('isSelected') {
|
||||
<mat-checkbox
|
||||
(change)="element.isSelected = $event.checked"
|
||||
[checked]="element.isSelected"
|
||||
></mat-checkbox>
|
||||
}
|
||||
@case('isEdit') {
|
||||
<div class="btn-edit" >
|
||||
<button mat-button routerLink='../tutorial-edit' [queryParams]="{id:element.id}" (click)="element.isEdit = !element.isEdit">
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
class="button-remove"
|
||||
(click)="removeRow(element.id)"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@case ('boolean') {
|
||||
<mat-slide-toggle
|
||||
class="example-margin"
|
||||
[checked]="element[column.key]"
|
||||
[disabled]="column.key !== 'tobepublished'"
|
||||
(change)="publish(element, $event.checked)"
|
||||
>
|
||||
|
||||
</mat-slide-toggle>
|
||||
}
|
||||
@case ('datetime') {
|
||||
{{ element[column.key] | date: 'medium' }}
|
||||
}
|
||||
@default {
|
||||
{{ element[column.key] }}
|
||||
}
|
||||
}
|
||||
<!-- }-->
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
}
|
||||
<!--<ng-container [matColumnDef]="col.key" *ngFor="let col of columnsSchema">-->
|
||||
<!--<th mat-header-cell *matHeaderCellDef [ngSwitch]="col.key">
|
||||
<span *ngSwitchCase="'isSelected'">
|
||||
<mat-checkbox
|
||||
(change)="selectAll($event)"
|
||||
[checked]="isAllSelected()"
|
||||
[indeterminate]="!isAllSelected() && isAnySelected()"
|
||||
></mat-checkbox>
|
||||
</span>
|
||||
<span *ngSwitchDefault>{{ col.label }}</span>
|
||||
</th>-->
|
||||
<!-- <mat-form-field-->
|
||||
<!--<td mat-cell *matCellDef="let element">
|
||||
<div [ngSwitch]="col.type" *ngIf="!element.isEdit">
|
||||
<ng-container *ngSwitchCase="'isSelected'">
|
||||
<mat-checkbox
|
||||
(change)="element.isSelected = $event.checked"
|
||||
[checked]="element.isSelected"
|
||||
></mat-checkbox>
|
||||
</ng-container>
|
||||
<div class="btn-edit" *ngSwitchCase="'isEdit'">
|
||||
<button mat-button (click)="element.isEdit = !element.isEdit">
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
class="button-remove"
|
||||
(click)="removeRow(element.id)"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
<span *ngSwitchCase="'date'">
|
||||
{{ element[col.key] | date: 'mediumDate' }}
|
||||
</span>
|
||||
<span *ngSwitchDefault>
|
||||
{{ element[col.key] }}
|
||||
</span>
|
||||
</div>
|
||||
<div [ngSwitch]="col.type" *ngIf="element.isEdit">
|
||||
<div *ngSwitchCase="'isSelected'"></div>
|
||||
<div class="btn-edit" *ngSwitchCase="'isEdit'">
|
||||
<button
|
||||
mat-button
|
||||
(click)="editRow(element)"
|
||||
[disabled]="disableSubmit(element.id)"
|
||||
>
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
class="form-input"
|
||||
*ngSwitchCase="'date'"
|
||||
appearance="fill"
|
||||
>
|
||||
<mat-label>Choose a date</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[matDatepicker]="picker"
|
||||
[(ngModel)]="element[col.key]"
|
||||
/>
|
||||
<mat-datepicker-toggle
|
||||
matSuffix
|
||||
[for]="picker"
|
||||
></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>-->
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
.table-header {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
text-align: right;
|
||||
margin-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.spacer{
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
+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();
|
||||
});
|
||||
});
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit} from '@angular/core';
|
||||
import {Tutorial} from '../../../models/tutorial.model';
|
||||
import {UserApiService} from '../user-api.service';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
import {TokenStorageService} from '../../../services/token-storage.service';
|
||||
import {EventBusService} from '../../../_shared/event-bus.service';
|
||||
import {EventData} from '../../../_shared/event.class';
|
||||
import {Router, RouterLink} from '@angular/router';
|
||||
import {AuthService} from '../../../services/auth.service';
|
||||
import {FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {
|
||||
MatCell,
|
||||
MatCellDef,
|
||||
MatColumnDef, MatHeaderCell, MatHeaderCellDef,
|
||||
MatHeaderRow,
|
||||
MatHeaderRowDef,
|
||||
MatRow,
|
||||
MatRowDef,
|
||||
MatTable,
|
||||
MatTableDataSource
|
||||
} from '@angular/material/table';
|
||||
import {DatePipe} from '@angular/common';
|
||||
import {MatCheckbox} from '@angular/material/checkbox';
|
||||
import {MatSlideToggle} from '@angular/material/slide-toggle';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-tutorials-list.component',
|
||||
imports: [
|
||||
MatButton,
|
||||
RouterLink,
|
||||
ReactiveFormsModule,
|
||||
MatTable,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
FormsModule,
|
||||
MatColumnDef,
|
||||
MatHeaderCell,
|
||||
MatHeaderCellDef,
|
||||
MatCellDef,
|
||||
MatCheckbox,
|
||||
DatePipe,
|
||||
MatCell,
|
||||
MatSlideToggle
|
||||
],
|
||||
schemas:[CUSTOM_ELEMENTS_SCHEMA],
|
||||
templateUrl: './tutorials-list.component.html',
|
||||
styleUrl: './tutorials-list.component.scss'
|
||||
})
|
||||
export class TutorialsListComponent implements OnInit {
|
||||
//tutorials?: Tutorial[];
|
||||
|
||||
displayedColumns: string[] = TutorialColumns.map((col) => col.key)
|
||||
columnsSchema: any = TutorialColumns
|
||||
dataSource = new MatTableDataSource<Tutorial>()
|
||||
|
||||
|
||||
constructor(private userApiService: UserApiService,
|
||||
private storageService: TokenStorageService,
|
||||
private eventBusService: EventBusService,
|
||||
private router: Router,
|
||||
private authService: AuthService
|
||||
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.eventBusService.on('logout', () => {
|
||||
this.logout();
|
||||
})
|
||||
this.getTutorials();
|
||||
}
|
||||
|
||||
getTutorials(): void {
|
||||
this.userApiService.getUserAllTutorials()
|
||||
.subscribe(( data:Tutorial[] ) => {
|
||||
this.dataSource.data = data;
|
||||
console.log(data);
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
if (
|
||||
(
|
||||
error.status === 401
|
||||
|
||||
)
|
||||
&& this.storageService.isLoggedIn()
|
||||
) {
|
||||
this.eventBusService.emit(new EventData('logout', null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
selectAll(event: any) {
|
||||
this.dataSource.data = this.dataSource.data.map((item) => ({
|
||||
...item,
|
||||
isSelected: event.checked
|
||||
}));
|
||||
}
|
||||
|
||||
isAllSelected() {
|
||||
return this.dataSource.data.every((item) => item.isSelected)
|
||||
}
|
||||
|
||||
isAnySelected() {
|
||||
return this.dataSource.data.some((item) => item.isSelected)
|
||||
}
|
||||
|
||||
removeSelectedRows() {
|
||||
const selectedTutorials = this.dataSource.data.filter((u: Tutorial) => u.isSelected)
|
||||
/*this.dialog
|
||||
.open(ConfirmDialogComponent)
|
||||
.afterClosed()
|
||||
.subscribe((confirm) => {*/
|
||||
// if (confirm) {
|
||||
this.userApiService.deleteTutorials(selectedTutorials).subscribe(() => {
|
||||
this.dataSource.data = this.dataSource.data.filter(
|
||||
(u: Tutorial) => !u.isSelected
|
||||
)
|
||||
})
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
removeRow(id: number) {
|
||||
this.userApiService.deleteTutorial(id).subscribe(() => {
|
||||
this.dataSource.data = this.dataSource.data.filter(
|
||||
(u: Tutorial) => u.id !== id,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
publish(element: any, checked: boolean){
|
||||
element.tobepublished = checked;
|
||||
this.userApiService.update(element.id, element).subscribe(
|
||||
response => {
|
||||
console.log(response);
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.authService.logout().subscribe({
|
||||
next: res => {
|
||||
console.log(res);
|
||||
this.storageService.clean();
|
||||
|
||||
//window.location.reload();
|
||||
this.router.navigate(['main/generate-image']).then(() => {
|
||||
//window.location.reload();
|
||||
})
|
||||
|
||||
},
|
||||
error: err => {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export const TutorialColumns = [
|
||||
{
|
||||
key: 'isSelected',
|
||||
type: 'isSelected',
|
||||
label: '',
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
type: 'text',
|
||||
label: 'Title',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'published',
|
||||
type: 'boolean',
|
||||
label: 'Is Published',
|
||||
},
|
||||
{
|
||||
key: 'created',
|
||||
type: 'datetime',
|
||||
label: 'Created Date',
|
||||
required: true,
|
||||
|
||||
},
|
||||
{
|
||||
key: 'modified',
|
||||
type: 'datetime',
|
||||
label: 'Modified Date',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
key: 'tobepublished',
|
||||
type: 'boolean',
|
||||
label: 'To be published',
|
||||
required: true
|
||||
|
||||
},
|
||||
{
|
||||
key: 'isEdit',
|
||||
type: 'isEdit',
|
||||
label: '',
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserApiService } from './user-api.service';
|
||||
|
||||
describe('UserApiService', () => {
|
||||
let service: UserApiService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(UserApiService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {forkJoin, Observable} from 'rxjs';
|
||||
import {Tutorial} from '../../models/tutorial.model';
|
||||
import {HttpClient, HttpHeaders} from '@angular/common/http';
|
||||
import {GlobalConstants} from '../../global-constants';
|
||||
import {FileInfo} from '../../models/file-info';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserApiService {
|
||||
baseUrl = `${GlobalConstants.API_URL}/user`;
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
|
||||
}
|
||||
|
||||
saveZhipuaiImage(url: string): Observable<any> {
|
||||
return this.http.post(`${this.baseUrl}/saveZhipuAiImage`, {imageUrl:url});
|
||||
}
|
||||
|
||||
getImages(): Observable<FileInfo[]> {
|
||||
return this.http.get<FileInfo[]>(`${this.baseUrl}/getImages`);
|
||||
}
|
||||
|
||||
getUserAllTutorials(): Observable<Tutorial[]> {
|
||||
return this.http.get<Tutorial[]>(`${this.baseUrl}/tutorials`);
|
||||
}
|
||||
|
||||
getTutorial(id: string | null | undefined): Observable<Tutorial> {
|
||||
return this.http.get<Tutorial>(`${this.baseUrl}/tutorial-get/${id}`);
|
||||
}
|
||||
|
||||
create(data: any): Observable<any> {
|
||||
return this.http.post(`${this.baseUrl}/tutorial-add`, data);
|
||||
}
|
||||
|
||||
update(id: any,data: any): Observable<any> {
|
||||
return this.http.put(`${this.baseUrl}/tutorial-update/${id}`, data);
|
||||
}
|
||||
|
||||
deleteTutorials(tutorials: Tutorial[]): Observable<Tutorial[]> {
|
||||
return forkJoin(
|
||||
tutorials.map((tutorial) =>
|
||||
this.http.delete<Tutorial>(`${this.baseUrl}/tutorials/${tutorial.id}`)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
deleteTutorial(id: number): Observable<Tutorial> {
|
||||
return this.http.delete<Tutorial>(`${this.baseUrl}/tutorials/${id}`);
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -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();
|
||||
});
|
||||
});
|
||||
+11
@@ -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,9 @@
|
||||
<app-side-bar-user
|
||||
class="pc-sidebar"
|
||||
>
|
||||
</app-side-bar-user>
|
||||
|
||||
<div class="pc-container">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//---------------
|
||||
.pc-sidebar{
|
||||
top: 65px;
|
||||
overflow-y: auto;
|
||||
background-color: rgba(153, 153, 153, 0.16);
|
||||
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
.pc-container{
|
||||
top: 0px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
@@ -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,44 @@
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit, ViewChild} from '@angular/core';
|
||||
import {RouterOutlet} from '@angular/router';
|
||||
import {MatSidenav} from '@angular/material/sidenav';
|
||||
import {BreakpointObserver} from '@angular/cdk/layout';
|
||||
import {SideBarUserComponent} from '../side-bar-user.component/side-bar-user.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user.component',
|
||||
imports: [
|
||||
RouterOutlet,
|
||||
SideBarUserComponent
|
||||
],
|
||||
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,18 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {userRouting} from './user.routing';
|
||||
import {UserComponent} from './user.component/user.component';
|
||||
import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
userRouting,
|
||||
UserComponent,
|
||||
CommonModule,
|
||||
AngularMarkdownEditorModule
|
||||
]
|
||||
})
|
||||
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,39 @@
|
||||
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: 'tutorial-add',
|
||||
loadComponent: () => import('../user-module/tutorial-add.component/tutorial-add.component').then((c) => c.TutorialAddComponent)
|
||||
},
|
||||
{
|
||||
path: 'tutorial-edit',
|
||||
loadComponent: () => import('../user-module/tutorial-edit.component/tutorial-edit.component').then((c) => c.TutorialEditComponent)
|
||||
},
|
||||
{
|
||||
path: 'ai-models',
|
||||
loadComponent: () => import('../user-module/ai-models.component/ai-models.component').then((c) => c.AiModelsComponent)
|
||||
},
|
||||
{
|
||||
path: 'images',
|
||||
loadComponent: () => import('../user-module/images.component/images.component').then((c) => c.ImagesComponent)
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export const userRouting = RouterModule.forChild(USER_ROUTES);
|
||||
Reference in New Issue
Block a user