Update with new components
This commit is contained in:
@ -11,13 +11,15 @@
|
||||
<div *ngIf="isShowForm" class="mb-4">
|
||||
<form [formGroup]="blogForm" (ngSubmit)="saveBlog()">
|
||||
<div class="container">
|
||||
<button type="submit" class="btn btn-primary mx-2">{{ editing ? 'Update Blog' : 'Create Blog' }}</button>
|
||||
<button type="submit" class="btn btn-primary mx-2">{{ editing ? 'Update Blog' : 'Create Blog'
|
||||
}}</button>
|
||||
<button type="button" class="btn btn-secondary ml-2" (click)="resetForm()">Cancel</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group m-2 ">
|
||||
<label class="text-primary" for="title">Title</label>
|
||||
<input type="text" id="title" class="form-control" formControlName="title" placeholder="Enter blog title">
|
||||
<input type="text" id="title" class="form-control" formControlName="title"
|
||||
placeholder="Enter blog title">
|
||||
<div *ngIf="blogForm?.get('title')?.invalid && blogForm.get('title')?.touched" class="text-danger">
|
||||
Title is required.
|
||||
</div>
|
||||
@ -26,34 +28,62 @@
|
||||
<div class="form-group m-2">
|
||||
<label class="text-primary" for="professors">Professors</label>
|
||||
<select id="professors" formControlName="professors" class="form-control" multiple>
|
||||
<option *ngFor="let professor of allProfessors" [value]="professor.id">
|
||||
{{ professor.firstName }}
|
||||
</option>
|
||||
<option *ngFor="let professor of allProfessors" [value]="professor.id">
|
||||
{{ professor.firstName }}
|
||||
</option>
|
||||
</select>
|
||||
<div *ngIf="blogForm.get('professors')?.invalid && blogForm.get('professors')?.touched" class="text-danger mt-1">
|
||||
At least one professor must be selected.
|
||||
<div *ngIf="blogForm.get('professors')?.invalid && blogForm.get('professors')?.touched"
|
||||
class="text-danger mt-1">
|
||||
At least one professor must be selected.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group m-2">
|
||||
</div>
|
||||
|
||||
<div class="form-group m-2">
|
||||
<label class="text-primary" for="tags">Tags</label>
|
||||
<input type="text" id="tags" class="form-control" formControlName="tags" placeholder="Enter tags separated by commas">
|
||||
<input type="text" id="tags" class="form-control" formControlName="tags"
|
||||
placeholder="Enter tags separated by commas">
|
||||
<div *ngIf="blogForm.get('tags')?.invalid && blogForm.get('tags')?.touched" class="text-danger mt-1">
|
||||
Tags are required.
|
||||
Tags are required.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group m-2">
|
||||
<input type="checkbox" class="mx-2" id="posted" formControlName="posted">
|
||||
<label class="text-primary" for="posted">Posted</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Add this section after the tags field and before the posted checkbox -->
|
||||
<div class="form-group m-2">
|
||||
<label class="text-primary" for="image">Blog Image</label>
|
||||
<input type="file" id="image" class="form-control" accept="image/*" (change)="onImageSelected($event)">
|
||||
|
||||
<!-- Image Preview -->
|
||||
<div *ngIf="imagePreviewUrl" class="mt-3">
|
||||
<img [src]="imagePreviewUrl" alt="Image preview" class="img-thumbnail"
|
||||
style="max-width: 300px; max-height: 200px;">
|
||||
<button type="button" class="btn btn-sm btn-danger ml-2"
|
||||
(click)="imagePreviewUrl = null; selectedImage = null;">
|
||||
Remove Image
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Upload Progress -->
|
||||
<div *ngIf="uploadingImage" class="mt-2">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 100%">
|
||||
Uploading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group m-2">
|
||||
<input type="checkbox" class="mx-2" id="posted" formControlName="posted">
|
||||
<label class="text-primary" for="posted">Posted</label>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="form-group m-2">
|
||||
<label class="text-primary m-1" for="content">Content</label>
|
||||
<angular-editor [config]="editorConfig" [placeholder]="'Enter text here...'" formControlName="content"></angular-editor>
|
||||
<div *ngIf="blogForm.get('content')?.invalid && blogForm.get('content')?.touched" class="text-danger mt-1">
|
||||
<angular-editor [config]="editorConfig" [placeholder]="'Enter text here...'"
|
||||
formControlName="content"></angular-editor>
|
||||
<div *ngIf="blogForm.get('content')?.invalid && blogForm.get('content')?.touched"
|
||||
class="text-danger mt-1">
|
||||
Content is required.
|
||||
</div>
|
||||
</div>
|
||||
@ -69,50 +99,53 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Blog List -->
|
||||
<div *ngIf="!isShowForm">
|
||||
<div *ngIf="blogs.length > 0">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<!-- <th>Content</th> -->
|
||||
<th>Authors</th>
|
||||
<th>Tags</th>
|
||||
<th>Posted</th> <!-- New column for posted status -->
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let blog of blogs">
|
||||
<td>{{ blog.title }}</td>
|
||||
<!-- <td>{{ blog.content | slice:0:50 }}...</td> -->
|
||||
<td>
|
||||
<span *ngFor="let professor of blog.professors">{{ professor.firstName }}<br></span>
|
||||
</td>
|
||||
<td>
|
||||
<span *ngFor="let tag of blog.tags">{{ tag }}<br></span>
|
||||
</td>
|
||||
<td>
|
||||
<span *ngIf="blog.posted" class="text-success">✓</span> <!-- Check mark for posted -->
|
||||
<span *ngIf="!blog.posted" class="text-danger">✗</span> <!-- Cross mark for not posted -->
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-info btn-sm mr-2" (click)="editBlog(blog)"> <!-- Added margin-right for spacing -->
|
||||
Edit
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm" (click)="deleteBlog(blog)">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div *ngIf="blogs.length === 0" class="alert alert-info">
|
||||
No blogs available. Please create a new blog.
|
||||
</div>
|
||||
</div>
|
||||
<!-- Blog List -->
|
||||
<div *ngIf="!isShowForm">
|
||||
<div *ngIf="blogs.length > 0">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<!-- <th>Content</th> -->
|
||||
<th>Authors</th>
|
||||
<th>Tags</th>
|
||||
<th>Posted</th> <!-- New column for posted status -->
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let blog of blogs">
|
||||
<td>{{ blog.title }}</td>
|
||||
<!-- <td>{{ blog.content | slice:0:50 }}...</td> -->
|
||||
<td>
|
||||
<span *ngFor="let professor of blog.professors">{{ professor.firstName }}<br></span>
|
||||
</td>
|
||||
<td>
|
||||
<span *ngFor="let tag of blog.tags">{{ tag }}<br></span>
|
||||
</td>
|
||||
<td>
|
||||
<span *ngIf="blog.posted" class="text-success">✓</span>
|
||||
<!-- Check mark for posted -->
|
||||
<span *ngIf="!blog.posted" class="text-danger">✗</span>
|
||||
<!-- Cross mark for not posted -->
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-info btn-sm mr-2" (click)="editBlog(blog)">
|
||||
<!-- Added margin-right for spacing -->
|
||||
Edit
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm" (click)="deleteBlog(blog)">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="blogs.length === 0" class="alert alert-info">
|
||||
No blogs available. Please create a new blog.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -2,7 +2,6 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AngularEditorConfig } from '@josipv/angular-editor-k2';
|
||||
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||
;
|
||||
import { BlogService } from 'src/app/service/blog.service';
|
||||
import { ProfessorService } from 'src/app/service/professor.service';
|
||||
|
||||
@ -18,9 +17,14 @@ export class BlogComponent implements OnInit {
|
||||
blogForm: FormGroup;
|
||||
editing: boolean = false;
|
||||
loggedInUser: any;
|
||||
currentBlog: any = null; // Holds the blog being edited
|
||||
isShowForm = false; // Controls visibility of form vs. table
|
||||
currentBlog: any = null;
|
||||
isShowForm = false;
|
||||
content = '';
|
||||
|
||||
// Image upload properties
|
||||
selectedImage: File | null = null;
|
||||
imagePreviewUrl: string | null = null;
|
||||
uploadingImage: boolean = false;
|
||||
|
||||
constructor(
|
||||
private blogService: BlogService,
|
||||
@ -32,9 +36,9 @@ export class BlogComponent implements OnInit {
|
||||
this.blogForm = this.fb.group({
|
||||
title: ['', Validators.required],
|
||||
content: ['', Validators.required],
|
||||
professors: [[], Validators.required], // To hold selected professor IDs
|
||||
tags: ['', Validators.required], // To hold tags as a comma-separated string
|
||||
posted: [true], // Initialize checkbox with default value false
|
||||
professors: [[], Validators.required],
|
||||
tags: ['', Validators.required],
|
||||
posted: [true],
|
||||
});
|
||||
}
|
||||
|
||||
@ -53,9 +57,6 @@ export class BlogComponent implements OnInit {
|
||||
defaultParagraphSeparator: '',
|
||||
defaultFontName: '',
|
||||
defaultFontSize: '',
|
||||
// headers: [{
|
||||
|
||||
// }],
|
||||
fonts: [
|
||||
{ class: 'arial', name: 'Arial' },
|
||||
{ class: 'times-new-roman', name: 'Times New Roman' },
|
||||
@ -77,111 +78,236 @@ export class BlogComponent implements OnInit {
|
||||
tag: 'h1',
|
||||
},
|
||||
],
|
||||
// uploadUrl: 'v1/image',
|
||||
// upload: (file: File) => { ... }
|
||||
// uploadWithCredentials: false,
|
||||
sanitize: true,
|
||||
toolbarPosition: 'top',
|
||||
toolbarHiddenButtons: [['bold', 'italic'], ['fontSize']],
|
||||
};
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loggedInUser = this.authService.getUserFromLocalStorage();
|
||||
this.professorService
|
||||
.getAllProfessors()
|
||||
.subscribe((res) => (this.allProfessors = res.content));
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loggedInUser = this.authService.getUserFromLocalStorage();
|
||||
this.professorService
|
||||
.getAllProfessors()
|
||||
.subscribe((res) => (this.allProfessors = res.content));
|
||||
this.loadBlogs();
|
||||
|
||||
// Subscribe to form value changes to update content for preview
|
||||
this.blogForm.get('content')?.valueChanges.subscribe((value) => {
|
||||
this.content = value;
|
||||
});
|
||||
}
|
||||
|
||||
this.loadBlogs();
|
||||
// Subscribe to form value changes to update content for preview
|
||||
this.blogForm.get('content')?.valueChanges.subscribe((value) => {
|
||||
this.content = value;
|
||||
});
|
||||
}
|
||||
showForm() {
|
||||
this.isShowForm = true;
|
||||
this.resetForm();
|
||||
}
|
||||
|
||||
showForm() {
|
||||
this.isShowForm = true;
|
||||
this.resetForm(); // Ensure form is reset when showing
|
||||
showTable() {
|
||||
this.isShowForm = false;
|
||||
this.resetForm();
|
||||
}
|
||||
|
||||
loadBlogs() {
|
||||
this.blogService.getBlogs().subscribe(data => {
|
||||
this.blogs = data;
|
||||
});
|
||||
}
|
||||
|
||||
createBlog() {
|
||||
this.showForm();
|
||||
this.blogForm.reset();
|
||||
this.selectedBlog = null;
|
||||
this.editing = false;
|
||||
}
|
||||
|
||||
editBlog(blog: any) {
|
||||
this.selectedBlog = blog;
|
||||
|
||||
this.blogForm.patchValue({
|
||||
title: blog.title,
|
||||
content: blog.content,
|
||||
posted: blog.posted,
|
||||
professors: blog.professors.map((prof: any) => prof.id),
|
||||
tags: blog.tags.join(', ')
|
||||
});
|
||||
|
||||
// Set image preview if exists
|
||||
if (blog.imageUrl) {
|
||||
this.imagePreviewUrl = blog.imageUrl;
|
||||
}
|
||||
|
||||
showTable() {
|
||||
this.isShowForm = false;
|
||||
this.resetForm(); // Ensure form is reset when switching back
|
||||
}
|
||||
|
||||
loadBlogs() {
|
||||
this.blogService.getBlogs().subscribe(data => {
|
||||
this.blogs = data;
|
||||
});
|
||||
}
|
||||
|
||||
createBlog() {
|
||||
this.showForm(); // Show form to create a new blog
|
||||
this.blogForm.reset();
|
||||
this.selectedBlog = null;
|
||||
this.editing = false;
|
||||
}
|
||||
|
||||
editBlog(blog: any) {
|
||||
|
||||
this.selectedBlog = blog;
|
||||
|
||||
this.blogForm.patchValue({
|
||||
title: blog.title,
|
||||
content: blog.content,
|
||||
posted: blog.posted,
|
||||
professors: blog.professors.map((prof: any) => prof.id), // Assuming professors are an array of objects
|
||||
tags: blog.tags.join(', ') // Convert tags array back to comma-separated string
|
||||
});
|
||||
this.editing = true;
|
||||
this.isShowForm = true;
|
||||
|
||||
this.editing = true;
|
||||
this.isShowForm = true;
|
||||
}
|
||||
|
||||
// Image upload methods
|
||||
onImageSelected(event: any) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
// Validate file type
|
||||
if (!file.type.startsWith('image/')) {
|
||||
alert('Please select a valid image file.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file size (10MB max)
|
||||
const maxSize = 10 * 1024 * 1024; // 10MB in bytes
|
||||
if (file.size > maxSize) {
|
||||
alert('File size must be less than 10MB.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedImage = file;
|
||||
|
||||
// Create preview URL
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e: any) => {
|
||||
this.imagePreviewUrl = e.target.result;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
saveBlog() {
|
||||
if (this.blogForm.valid) {
|
||||
const blogData = this.blogForm.value;
|
||||
|
||||
// Convert tags to array and professors to array of IDs
|
||||
blogData.tags = blogData.tags.split(',').map((tag: string) => tag.trim());
|
||||
blogData.professors = blogData.professors || [];
|
||||
|
||||
blogData.author = this.loggedInUser._id; // Associate logged-in user with the blog
|
||||
|
||||
if (this.editing && this.selectedBlog) {
|
||||
this.blogService.updateBlog(this.selectedBlog.id, blogData).subscribe(() => {
|
||||
this.loadBlogs();
|
||||
this.resetForm();
|
||||
this.isShowForm = false; // Hide form after update
|
||||
});
|
||||
} else {
|
||||
this.blogService.createBlog(blogData).subscribe(() => {
|
||||
this.loadBlogs();
|
||||
this.resetForm();
|
||||
this.isShowForm = false; // Hide form after creation
|
||||
});
|
||||
}
|
||||
|
||||
removeImage() {
|
||||
this.selectedImage = null;
|
||||
this.imagePreviewUrl = null;
|
||||
|
||||
// Clear the file input
|
||||
const fileInput = document.getElementById('image') as HTMLInputElement;
|
||||
if (fileInput) {
|
||||
fileInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
async uploadImage(): Promise<string | null> {
|
||||
if (!this.selectedImage) return null;
|
||||
|
||||
this.uploadingImage = true;
|
||||
try {
|
||||
const response = await this.blogService.uploadImage(this.selectedImage).toPromise();
|
||||
return response.url;
|
||||
} catch (error) {
|
||||
console.error('Error uploading image:', error);
|
||||
alert('Failed to upload image. Please try again.');
|
||||
return null;
|
||||
} finally {
|
||||
this.uploadingImage = false;
|
||||
}
|
||||
}
|
||||
|
||||
async saveBlog() {
|
||||
if (this.blogForm.valid) {
|
||||
let imageUrl = null;
|
||||
|
||||
// Upload image if selected
|
||||
if (this.selectedImage) {
|
||||
imageUrl = await this.uploadImage();
|
||||
if (!imageUrl) {
|
||||
return; // Stop if image upload failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deleteBlog(blog: any) {
|
||||
if (confirm('Are you sure you want to delete this blog?')) {
|
||||
this.blogService.deleteBlog(blog.id).subscribe(() => {
|
||||
this.loadBlogs();
|
||||
});
|
||||
|
||||
const blogData = this.blogForm.value;
|
||||
|
||||
// Convert tags to array and professors to array of IDs
|
||||
blogData.tags = blogData.tags.split(',').map((tag: string) => tag.trim());
|
||||
blogData.professors = blogData.professors || [];
|
||||
|
||||
// Add image URL if uploaded, or keep existing image for updates
|
||||
if (imageUrl) {
|
||||
blogData.imageUrl = imageUrl;
|
||||
} else if (this.editing && this.selectedBlog && this.selectedBlog.imageUrl && this.imagePreviewUrl) {
|
||||
// Keep existing image URL if editing and no new image selected
|
||||
blogData.imageUrl = this.selectedBlog.imageUrl;
|
||||
}
|
||||
|
||||
blogData.author = this.loggedInUser._id;
|
||||
|
||||
if (this.editing && this.selectedBlog) {
|
||||
this.blogService.updateBlog(this.selectedBlog.id, blogData).subscribe({
|
||||
next: () => {
|
||||
this.loadBlogs();
|
||||
this.resetForm();
|
||||
this.isShowForm = false;
|
||||
alert('Blog updated successfully!');
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error updating blog:', error);
|
||||
alert('Failed to update blog. Please try again.');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.blogService.createBlog(blogData).subscribe({
|
||||
next: () => {
|
||||
this.loadBlogs();
|
||||
this.resetForm();
|
||||
this.isShowForm = false;
|
||||
alert('Blog created successfully!');
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error creating blog:', error);
|
||||
alert('Failed to create blog. Please try again.');
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Mark all fields as touched to show validation errors
|
||||
Object.keys(this.blogForm.controls).forEach(key => {
|
||||
this.blogForm.get(key)?.markAsTouched();
|
||||
});
|
||||
alert('Please fill in all required fields.');
|
||||
}
|
||||
|
||||
resetForm() {
|
||||
this.blogForm.reset();
|
||||
this.selectedBlog = null;
|
||||
this.editing = false;
|
||||
this.blogForm.patchValue({
|
||||
professors: [],
|
||||
tags: ''
|
||||
}
|
||||
|
||||
deleteBlog(blog: any) {
|
||||
if (confirm('Are you sure you want to delete this blog?')) {
|
||||
this.blogService.deleteBlog(blog.id).subscribe({
|
||||
next: () => {
|
||||
this.loadBlogs();
|
||||
alert('Blog deleted successfully!');
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error deleting blog:', error);
|
||||
alert('Failed to delete blog. Please try again.');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resetForm() {
|
||||
this.blogForm.reset();
|
||||
this.selectedBlog = null;
|
||||
this.editing = false;
|
||||
this.selectedImage = null;
|
||||
this.imagePreviewUrl = null;
|
||||
|
||||
this.blogForm.patchValue({
|
||||
professors: [],
|
||||
tags: '',
|
||||
posted: true
|
||||
});
|
||||
|
||||
// Clear the file input
|
||||
const fileInput = document.getElementById('image') as HTMLInputElement;
|
||||
if (fileInput) {
|
||||
fileInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Utility method to check if form field has error
|
||||
hasError(fieldName: string): boolean {
|
||||
const field = this.blogForm.get(fieldName);
|
||||
return !!(field && field.invalid && field.touched);
|
||||
}
|
||||
|
||||
// Utility method to get error message for a field
|
||||
getErrorMessage(fieldName: string): string {
|
||||
const field = this.blogForm.get(fieldName);
|
||||
if (field && field.errors && field.touched) {
|
||||
if (field.errors['required']) {
|
||||
return `${fieldName} is required.`;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user