Compare commits
10 Commits
7b153f4cf6
...
cfe68a276f
| Author | SHA1 | Date | |
|---|---|---|---|
| cfe68a276f | |||
| 911933074b | |||
| 9712439261 | |||
| 7d701d3747 | |||
| d8be4e7ca1 | |||
| 279c6f9c1a | |||
| f41a1043c9 | |||
| e206968993 | |||
| bd5119fc3c | |||
| 9c6c3abc32 |
26
docker-compose.yml
Normal file
26
docker-compose.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
version: '3.1'
|
||||||
|
|
||||||
|
services:
|
||||||
|
cmc-new-backend:
|
||||||
|
build:
|
||||||
|
context: ./support-portal-backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8070:8080"
|
||||||
|
environment:
|
||||||
|
- DATABASE_HOST=mysql-common-mysql-1
|
||||||
|
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql-common-mysql-1:3306/demo
|
||||||
|
- SPRING_DATASOURCE_USERNAME=youruser
|
||||||
|
- SPRING_DATASOURCE_PASSWORD=youruserpassword
|
||||||
|
networks:
|
||||||
|
- cmc-forntend
|
||||||
|
- mysql-common_mynetwork
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db-data: {}
|
||||||
|
|
||||||
|
networks:
|
||||||
|
cmc-forntend: {}
|
||||||
|
mysql-common_mynetwork:
|
||||||
|
external: true
|
||||||
15
support-portal-backend/Dockerfile
Normal file
15
support-portal-backend/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
FROM --platform=$BUILDPLATFORM maven:3.8.5-eclipse-temurin-17 AS builder
|
||||||
|
WORKDIR /workdir/server
|
||||||
|
COPY pom.xml /workdir/server/pom.xml
|
||||||
|
RUN mvn dependency:go-offline
|
||||||
|
|
||||||
|
COPY src /workdir/server/src
|
||||||
|
RUN mvn package -Dmaven.test.skip=true
|
||||||
|
RUN ls -la target/
|
||||||
|
|
||||||
|
FROM eclipse-temurin:17-jre-focal
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
VOLUME /tmp
|
||||||
|
COPY --from=builder /workdir/server/target/*.jar /app/app.jar
|
||||||
|
ENTRYPOINT ["java","-jar","/app/app.jar"]
|
||||||
@ -15,8 +15,11 @@
|
|||||||
<description>Demo project for Spring Boot</description>
|
<description>Demo project for Spring Boot</description>
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>11</java.version>
|
<java.version>11</java.version>
|
||||||
|
<maven.compiler.source>11</maven.compiler.source>
|
||||||
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
|
|
||||||
<mapstruct.version>1.4.2.Final</mapstruct.version>
|
|
||||||
|
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||||
|
|
||||||
<docker.image.prefix>artarkatesoft</docker.image.prefix>
|
<docker.image.prefix>artarkatesoft</docker.image.prefix>
|
||||||
<docker.image.name>angular-${project.artifactId}</docker.image.name>
|
<docker.image.name>angular-${project.artifactId}</docker.image.name>
|
||||||
@ -29,6 +32,12 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- <dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||||
|
</dependency> -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-security</artifactId>
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
@ -100,11 +109,11 @@
|
|||||||
<version>1.12.75</version>
|
<version>1.12.75</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<!-- <dependency>
|
||||||
<groupId>com.github.ulisesbocchio</groupId>
|
<groupId>com.github.ulisesbocchio</groupId>
|
||||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||||
<version>3.0.3</version>
|
<version>3.0.3</version>
|
||||||
</dependency>
|
</dependency> -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@ -158,6 +167,12 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
@ -195,11 +210,7 @@
|
|||||||
</path>
|
</path>
|
||||||
|
|
||||||
</annotationProcessorPaths>
|
</annotationProcessorPaths>
|
||||||
<compilerArgs>
|
|
||||||
<compilerArg>
|
|
||||||
-Amapstruct.defaultComponentModel=spring
|
|
||||||
</compilerArg>
|
|
||||||
</compilerArgs>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
@ -246,6 +257,9 @@
|
|||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.config;
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.User;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.repository.UserRepository;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static net.shyshkin.study.fullstack.supportportal.backend.constant.Authority.*;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class DataInitializer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CommandLineRunner init(UserRepository userRepository, PasswordEncoder passwordEncoder) {
|
||||||
|
return args -> {
|
||||||
|
System.out.println("Running DataInitializer...");
|
||||||
|
|
||||||
|
// Initialize users
|
||||||
|
createUserIfNotExists(userRepository, passwordEncoder, "admin", "adminpassword", "admin@example.com", "Admin", "User", "ROLE_ADMIN");
|
||||||
|
createUserIfNotExists(userRepository, passwordEncoder, "hr", "hrpassword", "hr@example.com", "HR", "User", "ROLE_HR");
|
||||||
|
createUserIfNotExists(userRepository, passwordEncoder, "manager", "managerpassword", "manager@example.com", "Manager", "User", "ROLE_MANAGER");
|
||||||
|
createUserIfNotExists(userRepository, passwordEncoder, "user", "userpassword", "user@example.com", "Regular", "User", "ROLE_USER");
|
||||||
|
createUserIfNotExists(userRepository, passwordEncoder, "superadmin", "superadminpassword", "superadmin@example.com", "Super", "Admin", "ROLE_SUPER_ADMIN");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createUserIfNotExists(UserRepository userRepository, PasswordEncoder passwordEncoder,
|
||||||
|
String username, String password, String email,
|
||||||
|
String firstName, String lastName, String role) {
|
||||||
|
if (userRepository.findByUsername(username).isEmpty()) {
|
||||||
|
String encodedPassword = passwordEncoder.encode(password);
|
||||||
|
|
||||||
|
User user = User.builder()
|
||||||
|
.email(email)
|
||||||
|
.firstName(firstName)
|
||||||
|
.lastName(lastName)
|
||||||
|
.username(username)
|
||||||
|
.password(encodedPassword)
|
||||||
|
.userId(UUID.randomUUID())
|
||||||
|
.isActive(true)
|
||||||
|
.isNotLocked(true)
|
||||||
|
.role(role)
|
||||||
|
.authorities(getAuthoritiesByRole(role))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
userRepository.save(user);
|
||||||
|
System.out.println(role + " user created successfully.");
|
||||||
|
} else {
|
||||||
|
System.out.println(role + " user already exists.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getAuthoritiesByRole(String role) {
|
||||||
|
switch (role) {
|
||||||
|
case "ROLE_ADMIN":
|
||||||
|
return ADMIN_AUTHORITIES;
|
||||||
|
case "ROLE_HR":
|
||||||
|
return HR_AUTHORITIES;
|
||||||
|
case "ROLE_MANAGER":
|
||||||
|
return MANAGER_AUTHORITIES;
|
||||||
|
case "ROLE_USER":
|
||||||
|
return USER_AUTHORITIES;
|
||||||
|
case "ROLE_SUPER_ADMIN":
|
||||||
|
return SUPER_ADMIN_AUTHORITIES;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid role: " + role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
// package com.example.tamilnadureservoir.config;
|
||||||
|
|
||||||
|
|
||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.config;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RestTemplateConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RestTemplate restTemplate() {
|
||||||
|
return new RestTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -14,4 +14,10 @@ public class FileConstant {
|
|||||||
public static final String NOT_AN_IMAGE_FILE = " is not an image file. Please upload an image file";
|
public static final String NOT_AN_IMAGE_FILE = " is not an image file. Please upload an image file";
|
||||||
public static final String TEMP_PROFILE_IMAGE_BASE_URL = "https://robohash.org/";
|
public static final String TEMP_PROFILE_IMAGE_BASE_URL = "https://robohash.org/";
|
||||||
|
|
||||||
|
// public static final String PROFESSOR_IMAGE_PATH = "/professor/image/";
|
||||||
|
// public static final String PROFESSOR_FOLDER = System.getProperty("user.home") + "/supportportal/professor/";
|
||||||
|
// public static final String DEFAULT_PROFESSOR_IMAGE_URI_PATTERN = "/professor/%s/profile-image";
|
||||||
|
// public static final String PROFESSOR_IMAGE_FILENAME = "avatar.jpg";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Event;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.repository.EventRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/events")
|
||||||
|
public class EventController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EventRepository eventRepository;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<List<Event>> getAllEvents() {
|
||||||
|
List<Event> events = eventRepository.findAll();
|
||||||
|
return new ResponseEntity<>(events, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseEntity<Event> getEventById(@PathVariable Long id) {
|
||||||
|
Optional<Event> event = eventRepository.findById(id);
|
||||||
|
return event.map(ResponseEntity::ok)
|
||||||
|
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<Event> createEvent(@RequestBody Event event) {
|
||||||
|
Event savedEvent = eventRepository.save(event);
|
||||||
|
return new ResponseEntity<>(savedEvent, HttpStatus.CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ResponseEntity<Event> updateEvent(@PathVariable Long id, @RequestBody Event event) {
|
||||||
|
if (!eventRepository.existsById(id)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
event.setId(id);
|
||||||
|
Event updatedEvent = eventRepository.save(event);
|
||||||
|
return new ResponseEntity<>(updatedEvent, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseEntity<Void> deleteEvent(@PathVariable Long id) {
|
||||||
|
if (!eventRepository.existsById(id)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
eventRepository.deleteById(id);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,142 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Post;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.PostDto;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.repository.PostRepository;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.repository.ProfessorRepository;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/posts")
|
||||||
|
public class PostController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PostRepository postRepository;
|
||||||
|
|
||||||
|
|
||||||
|
// Get all posts where isPosted is true
|
||||||
|
@GetMapping("/posted")
|
||||||
|
public ResponseEntity<List<Post>> getAllPostedPosts() {
|
||||||
|
try {
|
||||||
|
List<Post> posts = postRepository.findAllByIsPostedTrue();
|
||||||
|
return ResponseEntity.ok(posts);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all unique tags with count
|
||||||
|
@GetMapping("/tags/count")
|
||||||
|
public ResponseEntity<Map<String, Long>> getTagsWithCount() {
|
||||||
|
try {
|
||||||
|
List<Object[]> tagCounts = postRepository.findTagsWithCount();
|
||||||
|
Map<String, Long> tagCountMap = new HashMap<>();
|
||||||
|
for (Object[] tagCount : tagCounts) {
|
||||||
|
tagCountMap.put((String) tagCount[0], (Long) tagCount[1]);
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(tagCountMap);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all posts associated with a specific tag where isPosted is true
|
||||||
|
@GetMapping("/tag/{tag}")
|
||||||
|
public ResponseEntity<List<Post>> getPostsByTag(@PathVariable String tag) {
|
||||||
|
try {
|
||||||
|
List<Post> posts = postRepository.findAllByTagAndIsPostedTrue(tag);
|
||||||
|
return ResponseEntity.ok(posts);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all posts
|
||||||
|
@GetMapping
|
||||||
|
public List<Post> getAllPosts() {
|
||||||
|
return postRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a single post by ID
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseEntity<Post> getPostById(@PathVariable Long id) {
|
||||||
|
return postRepository.findById(id)
|
||||||
|
.map(post -> ResponseEntity.ok(post))
|
||||||
|
.orElse(ResponseEntity.notFound().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProfessorRepository professorRepository;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<?> createPost(@RequestBody PostDto postDto) {
|
||||||
|
Post post = new Post();
|
||||||
|
post.setTitle(postDto.getTitle());
|
||||||
|
post.setContent(postDto.getContent());
|
||||||
|
post.setPosted(postDto.isPosted());
|
||||||
|
|
||||||
|
// Fetch professors from IDs, filter out null IDs
|
||||||
|
List<Long> validProfessorIds = postDto.getProfessors().stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<Professor> professors = professorRepository.findAllById(validProfessorIds);
|
||||||
|
post.setProfessors(professors);
|
||||||
|
|
||||||
|
// Set tags
|
||||||
|
post.setTags(postDto.getTags());
|
||||||
|
|
||||||
|
// Save the post
|
||||||
|
postRepository.save(post);
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ResponseEntity<?> updatePost(@PathVariable Long id, @RequestBody PostDto postDto) {
|
||||||
|
Post post = postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Post not found"));
|
||||||
|
|
||||||
|
post.setTitle(postDto.getTitle());
|
||||||
|
post.setContent(postDto.getContent());
|
||||||
|
post.setPosted(postDto.isPosted());
|
||||||
|
|
||||||
|
// Fetch professors from IDs, filter out null IDs
|
||||||
|
List<Long> validProfessorIds = postDto.getProfessors().stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<Professor> professors = professorRepository.findAllById(validProfessorIds);
|
||||||
|
post.setProfessors(professors);
|
||||||
|
|
||||||
|
// Set tags
|
||||||
|
post.setTags(postDto.getTags());
|
||||||
|
|
||||||
|
// Save the updated post
|
||||||
|
postRepository.save(post);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseEntity<Void> deletePost(@PathVariable Long id) {
|
||||||
|
return postRepository.findById(id)
|
||||||
|
.map(post -> {
|
||||||
|
postRepository.delete(post);
|
||||||
|
return ResponseEntity.noContent().<Void>build(); // Explicitly specify the type parameter
|
||||||
|
})
|
||||||
|
.orElse(ResponseEntity.notFound().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class PostDTO
|
||||||
|
{
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@NotEmpty(message = "At least one professor must be selected.")
|
||||||
|
private List<Long> professors;
|
||||||
|
|
||||||
|
private List<String> tags;
|
||||||
|
private boolean posted;
|
||||||
|
|
||||||
|
// Getters and setters
|
||||||
|
// ...
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.HttpResponse;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.ProfessorDto;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.service.ProfessorService;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.springframework.http.HttpStatus.OK;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("professor")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ProfessorResource {
|
||||||
|
|
||||||
|
private final ProfessorService professorService;
|
||||||
|
|
||||||
|
@GetMapping("home")
|
||||||
|
public String showProfessor() {
|
||||||
|
return "Application works";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("register")
|
||||||
|
public Professor register(@RequestBody Professor professor) {
|
||||||
|
return professorService.register(professor.getFirstName(), professor.getLastName(), professor.getEmail(), professor.getDepartment(), professor.getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("add")
|
||||||
|
public ResponseEntity<Professor> addNewProfessor(@Valid ProfessorDto professorDto) {
|
||||||
|
log.debug("Professor DTO: {}", professorDto);
|
||||||
|
Professor professor = professorService.addNewProfessor(professorDto);
|
||||||
|
return ResponseEntity.ok(professor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PutMapping("{professorId}")
|
||||||
|
public Professor updateProfessor(@PathVariable UUID professorId, @Valid ProfessorDto professorDto) {
|
||||||
|
log.debug("Professor DTO: {}", professorDto);
|
||||||
|
return professorService.updateProfessor(professorId, professorDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("{professorId}")
|
||||||
|
public Professor findProfessorById(@PathVariable UUID professorId) {
|
||||||
|
return professorService.findByProfessorId(professorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("email/{email}")
|
||||||
|
public Professor findProfessorByEmail(@PathVariable String email) {
|
||||||
|
return professorService.findByEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public Page<Professor> getAllProfessors(Pageable pageable) {
|
||||||
|
return professorService.findAll(pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("{professorId}")
|
||||||
|
public HttpResponse deleteProfessor(@PathVariable UUID professorId) {
|
||||||
|
professorService.deleteProfessor(professorId);
|
||||||
|
return HttpResponse.builder()
|
||||||
|
.httpStatusCode(OK.value())
|
||||||
|
.httpStatus(OK)
|
||||||
|
.reason(OK.getReasonPhrase())
|
||||||
|
.message("Professor deleted successfully")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("{professorId}/profile-image")
|
||||||
|
public Professor updateProfileImage(@PathVariable UUID professorId, @RequestParam MultipartFile profileImage) {
|
||||||
|
return professorService.updateProfileImage(professorId, profileImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(path = "{professorId}/profile-image/{filename}", produces = MediaType.IMAGE_JPEG_VALUE)
|
||||||
|
public byte[] getProfileImageByProfessorId(@PathVariable UUID professorId, @PathVariable String filename) {
|
||||||
|
return professorService.getImageByProfessorId(professorId, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(path = "{professorId}/profile-image", produces = MediaType.IMAGE_JPEG_VALUE)
|
||||||
|
public byte[] getDefaultProfileImage(@PathVariable UUID professorId) {
|
||||||
|
return professorService.getDefaultProfileImage(professorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,198 @@
|
|||||||
|
// package com.example.tamilnadureservoir.controller;
|
||||||
|
|
||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.transform.dom.DOMResult;
|
||||||
|
import javax.xml.transform.dom.DOMSource;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PatchMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.ConferenceData;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.repository.ConferenceDataRepository;
|
||||||
|
|
||||||
|
|
||||||
|
@CrossOrigin(origins = "*", maxAge = 3600)
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/soap")
|
||||||
|
public class SoapController {
|
||||||
|
|
||||||
|
private final RestTemplate restTemplate;
|
||||||
|
private final ConferenceDataRepository conferenceDataRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SoapController(RestTemplate restTemplate, ConferenceDataRepository conferenceDataRepository) {
|
||||||
|
this.restTemplate = restTemplate;
|
||||||
|
this.conferenceDataRepository = conferenceDataRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/insertConferenceData")
|
||||||
|
public ResponseEntity<String> insertConferenceData(@RequestBody ConferenceData conferenceData) {
|
||||||
|
try {
|
||||||
|
// Save the conferenceData object to the database
|
||||||
|
ConferenceData savedConferenceData = conferenceDataRepository.save(conferenceData);
|
||||||
|
return new ResponseEntity<>("ConferenceData inserted with ID: " + savedConferenceData.getId(),
|
||||||
|
HttpStatus.CREATED);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new ResponseEntity<>("Failed to insert ConferenceData: " + e.getMessage(),
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/getAllConferenceData")
|
||||||
|
public ResponseEntity<List<ConferenceData>> getAllConferenceData() {
|
||||||
|
List<ConferenceData> conferenceDataList = conferenceDataRepository.findAll();
|
||||||
|
return new ResponseEntity<>(conferenceDataList, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/updateConferenceData/{id}")
|
||||||
|
public ResponseEntity<ConferenceData> updateConferenceData(@PathVariable Long id,
|
||||||
|
@RequestBody ConferenceData updatedData) {
|
||||||
|
// Implement the logic to update conference data by ID
|
||||||
|
Optional<ConferenceData> existingData = conferenceDataRepository.findById(id);
|
||||||
|
|
||||||
|
if (existingData.isPresent()) {
|
||||||
|
ConferenceData dataToUpdate = existingData.get();
|
||||||
|
// Update the fields of dataToUpdate with values from updatedData
|
||||||
|
// e.g., dataToUpdate.setName(updatedData.getName());
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Save the updated data
|
||||||
|
conferenceDataRepository.save(dataToUpdate);
|
||||||
|
|
||||||
|
return new ResponseEntity<>(dataToUpdate, HttpStatus.OK);
|
||||||
|
} else {
|
||||||
|
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/partialUpdateConferenceData/{id}")
|
||||||
|
public ResponseEntity<ConferenceData> partialUpdateConferenceData(@PathVariable Long id,
|
||||||
|
@RequestBody Map<String, Object> updates) {
|
||||||
|
// Implement the logic to partially update conference data by ID
|
||||||
|
Optional<ConferenceData> existingData = conferenceDataRepository.findById(id);
|
||||||
|
|
||||||
|
if (existingData.isPresent()) {
|
||||||
|
ConferenceData dataToUpdate = existingData.get();
|
||||||
|
|
||||||
|
// Apply updates from the request body to dataToUpdate
|
||||||
|
for (Map.Entry<String, Object> entry : updates.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
|
||||||
|
// Update specific fields based on the key-value pairs
|
||||||
|
// e.g., if (key.equals("name")) dataToUpdate.setName((String) value);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the partially updated data
|
||||||
|
conferenceDataRepository.save(dataToUpdate);
|
||||||
|
|
||||||
|
return new ResponseEntity<>(dataToUpdate, HttpStatus.OK);
|
||||||
|
} else {
|
||||||
|
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/deleteConferenceData/{id}")
|
||||||
|
public ResponseEntity<Void> deleteConferenceData(@PathVariable Long id) {
|
||||||
|
// Implement the logic to delete conference data by ID
|
||||||
|
conferenceDataRepository.deleteById(id);
|
||||||
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/getConferenceDataByPhone/{phone}")
|
||||||
|
public ResponseEntity<Object> getConferenceDataByPhone(@PathVariable("phone") String phone) {
|
||||||
|
Optional<ConferenceData> conferenceDataOptional = conferenceDataRepository.findByPhone(phone);
|
||||||
|
|
||||||
|
if (conferenceDataOptional.isPresent()) {
|
||||||
|
ConferenceData conferenceData = conferenceDataOptional.get();
|
||||||
|
return new ResponseEntity<>(conferenceData, HttpStatus.OK);
|
||||||
|
} else {
|
||||||
|
return new ResponseEntity<>("ConferenceData not found for phone: " + phone, HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/callWebService", consumes = "application/soap+xml", produces = MediaType.APPLICATION_XML_VALUE)
|
||||||
|
public String callWebService(
|
||||||
|
@RequestBody String soapRequest,
|
||||||
|
@RequestHeader("Content-Type") String contentType,
|
||||||
|
@RequestHeader("SOAPAction") String soapAction) {
|
||||||
|
|
||||||
|
// Log or use the 'Content-Type' and 'SOAPAction' headers as needed
|
||||||
|
System.out.println("Content-Type: " + contentType);
|
||||||
|
System.out.println("SOAPAction: " + soapAction);
|
||||||
|
|
||||||
|
// Specify the SOAP action for your ASMX web service
|
||||||
|
String soapActionValue = soapAction;
|
||||||
|
String url = "https://clin.cmcvellore.ac.in/newconference/ConferencePay.asmx";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create a DocumentBuilder to parse the SOAP request string
|
||||||
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
|
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||||
|
Document requestDoc = builder.parse(new InputSource(new StringReader(soapRequest)));
|
||||||
|
|
||||||
|
// Create a DOMSource from the parsed SOAP request
|
||||||
|
DOMSource requestSource = new DOMSource(requestDoc);
|
||||||
|
|
||||||
|
// Create a DOMResult to capture the response
|
||||||
|
DOMResult responseResult = new DOMResult();
|
||||||
|
|
||||||
|
// Set the Content-Type header to specify the SOAP format
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.TEXT_XML);
|
||||||
|
|
||||||
|
// Set the SOAPAction header to specify the SOAP action
|
||||||
|
headers.set("SOAPAction", soapActionValue);
|
||||||
|
|
||||||
|
// Create a HttpEntity with the headers
|
||||||
|
HttpEntity<DOMSource> httpEntity = new HttpEntity<>(requestSource, headers);
|
||||||
|
|
||||||
|
// Send the SOAP request to the external ASMX web service
|
||||||
|
ResponseEntity<String> responseEntity = restTemplate.exchange(
|
||||||
|
url,
|
||||||
|
HttpMethod.POST,
|
||||||
|
httpEntity,
|
||||||
|
String.class);
|
||||||
|
|
||||||
|
// Extract the response XML from the ResponseEntity
|
||||||
|
String responseXml = responseEntity.getBody();
|
||||||
|
|
||||||
|
// Handle the SOAP response as needed
|
||||||
|
return responseXml;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Handle exceptions
|
||||||
|
e.printStackTrace(); // You can log the exception details
|
||||||
|
return null; // Return an appropriate response or handle differently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.shyshkin.study.fullstack.supportportal.backend.constant.SecurityConstants;
|
import net.shyshkin.study.fullstack.supportportal.backend.constant.SecurityConstants;
|
||||||
@ -32,6 +33,13 @@ import static org.springframework.http.HttpStatus.OK;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserResource {
|
public class UserResource {
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class UserDTO{
|
||||||
|
String username;
|
||||||
|
String password;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final AuthenticationManager authenticationManager;
|
private final AuthenticationManager authenticationManager;
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
@ -47,7 +55,8 @@ public class UserResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("login")
|
@PostMapping("login")
|
||||||
public ResponseEntity<User> login(@RequestBody User user) {
|
public ResponseEntity<User> login(@RequestBody UserDTO user) {
|
||||||
|
// public ResponseEntity<User> login(@RequestBody User user) {
|
||||||
|
|
||||||
authenticate(user.getUsername(), user.getPassword());
|
authenticate(user.getUsername(), user.getPassword());
|
||||||
User byUsername = userService.findByUsername(user.getUsername());
|
User byUsername = userService.findByUsername(user.getUsername());
|
||||||
|
|||||||
@ -0,0 +1,45 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@MappedSuperclass
|
||||||
|
public abstract class BaseEntity {
|
||||||
|
|
||||||
|
@Column(nullable = false, updatable = false)
|
||||||
|
private LocalDateTime createdDate;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private LocalDateTime updatedDate;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private boolean isDeleted = false;
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
createdDate = LocalDateTime.now();
|
||||||
|
updatedDate = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
updatedDate = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreatedDate() {
|
||||||
|
return createdDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpdatedDate() {
|
||||||
|
return updatedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeleted() {
|
||||||
|
return isDeleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleted(boolean deleted) {
|
||||||
|
isDeleted = deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
public class ConferenceData {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
// Request fields
|
||||||
|
private String conferencecode;
|
||||||
|
private String conferenceyear;
|
||||||
|
private String bankname;
|
||||||
|
private String remoteip;
|
||||||
|
private String regno;
|
||||||
|
private String candidatename;
|
||||||
|
private String nameinreceipt;
|
||||||
|
private String address1;
|
||||||
|
private String address2;
|
||||||
|
private String city;
|
||||||
|
private String state;
|
||||||
|
private String country;
|
||||||
|
private String pincode;
|
||||||
|
private String phone;
|
||||||
|
private String mobile;
|
||||||
|
private String email;
|
||||||
|
private String foodtype;
|
||||||
|
private String participanttype;
|
||||||
|
private String practicetype;
|
||||||
|
private String accompanymembers;
|
||||||
|
private String paymentamount;
|
||||||
|
private String ToWards;
|
||||||
|
private String Allow80G;
|
||||||
|
private String PanCardNo;
|
||||||
|
private String hasgst;
|
||||||
|
private String GSTReg;
|
||||||
|
private String gstnumber;
|
||||||
|
private String gstmobileno;
|
||||||
|
private String gstemailid;
|
||||||
|
private String inputcaption1;
|
||||||
|
private String inputvalue1;
|
||||||
|
private String inputcaption2;
|
||||||
|
private String inputvalue2;
|
||||||
|
private String inputcaption3;
|
||||||
|
private String inputvalue3;
|
||||||
|
private String inputcaption4;
|
||||||
|
private String inputvalue4;
|
||||||
|
private String inputcaption5;
|
||||||
|
private String inputvalue5;
|
||||||
|
|
||||||
|
// Response fields
|
||||||
|
private String responseTransid;
|
||||||
|
private String responseResultCode;
|
||||||
|
private String responseResult;
|
||||||
|
private String responseURL;
|
||||||
|
|
||||||
|
// Constructors, getters, and setters
|
||||||
|
|
||||||
|
// You can generate getters and setters for each field using your IDE or
|
||||||
|
// manually.
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Entity
|
||||||
|
@Table(name = "events")
|
||||||
|
public class Event {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(nullable = false, unique = true)
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String year;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String subject;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String subTitle;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String date;
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@CollectionTable(name = "venues", joinColumns = @JoinColumn(name = "event_id"))
|
||||||
|
private List<Venue> venue;
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@CollectionTable(name = "highlights", joinColumns = @JoinColumn(name = "event_id"))
|
||||||
|
private List<String> highlights;
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@CollectionTable(name = "organisers", joinColumns = @JoinColumn(name = "event_id"))
|
||||||
|
private List<String> organisers;
|
||||||
|
|
||||||
|
// @ElementCollection
|
||||||
|
// @CollectionTable(name = "fees", joinColumns = @JoinColumn(name = "event_id"))
|
||||||
|
// private List<Fee> fee;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Boolean isActive;
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(
|
||||||
|
name = "event_professors",
|
||||||
|
joinColumns = @JoinColumn(name = "event_id"),
|
||||||
|
inverseJoinColumns = @JoinColumn(name = "professor_id")
|
||||||
|
)
|
||||||
|
private List<Professor> professors;
|
||||||
|
|
||||||
|
// Assuming you have these classes defined as well
|
||||||
|
@Embeddable
|
||||||
|
public static class Venue {
|
||||||
|
private String title;
|
||||||
|
private String date;
|
||||||
|
private String address;
|
||||||
|
private String info;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public static class Fee {
|
||||||
|
private String desc;
|
||||||
|
private Integer cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@ToString
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class Post extends BaseEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Column(columnDefinition = "LONGTEXT", nullable = false)
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(
|
||||||
|
name = "post_professors",
|
||||||
|
joinColumns = @JoinColumn(name = "post_id"),
|
||||||
|
inverseJoinColumns = @JoinColumn(name = "professor_id")
|
||||||
|
)
|
||||||
|
private List<Professor> professors;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private boolean isPosted;
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@CollectionTable(name = "post_tags", joinColumns = @JoinColumn(name = "post_id"))
|
||||||
|
@Column(name = "tag")
|
||||||
|
private List<String> tags;
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.*;
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||||
|
@Builder
|
||||||
|
public class Professor implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -4372214856545239049L;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
// @EqualsAndHashCode.Include
|
||||||
|
// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Type(type = "org.hibernate.type.UUIDCharType")
|
||||||
|
@Column(length = 36, columnDefinition = "varchar(36)", updatable = false, nullable = false)
|
||||||
|
private UUID professorId;
|
||||||
|
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private String email;
|
||||||
|
private String department;
|
||||||
|
private String position;
|
||||||
|
private String officeLocation;
|
||||||
|
private LocalDateTime joinDate;
|
||||||
|
private String profileImageUrl;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private WorkingStatus status; // Use enum to track detailed working status
|
||||||
|
|
||||||
|
|
||||||
|
@ManyToMany(mappedBy = "professors")
|
||||||
|
@JsonIgnore
|
||||||
|
private List<Post> posts;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||||
|
|
||||||
|
public enum WorkingStatus {
|
||||||
|
ACTIVE,
|
||||||
|
ON_LEAVE,
|
||||||
|
RETIRED
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
|
||||||
|
|
||||||
|
public class PostDto {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@NotEmpty(message = "At least one professor must be selected.")
|
||||||
|
private List<Long> professors;
|
||||||
|
|
||||||
|
private List<String> tags;
|
||||||
|
private boolean posted;
|
||||||
|
|
||||||
|
// Getters and setters
|
||||||
|
// ...
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Role;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.WorkingStatus;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Email;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class ProfessorDto {
|
||||||
|
|
||||||
|
@NotEmpty(message = "Should not be empty")
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private String email;
|
||||||
|
private String department;
|
||||||
|
private String position;
|
||||||
|
private String officeLocation;
|
||||||
|
private WorkingStatus status;
|
||||||
|
private LocalDateTime joinDate;
|
||||||
|
private MultipartFile profileImage; // Optional field for profile image URL
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.exception.domain;
|
||||||
|
|
||||||
|
public class ProfessorNotFoundException extends RuntimeException {
|
||||||
|
public ProfessorNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.mapper;
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.ProfessorDto;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.WorkingStatus;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Named;
|
||||||
|
|
||||||
|
@Mapper(componentModel = "spring")
|
||||||
|
public interface ProfessorMapper {
|
||||||
|
|
||||||
|
// @Mapping(target = "professorId", ignore = true) // Auto-generated
|
||||||
|
@Mapping(target = "joinDate", expression = "java(java.time.LocalDateTime.now())") // Default value
|
||||||
|
@Mapping(target = "status", source = "status", qualifiedByName = "stringToWorkingStatus")
|
||||||
|
Professor toEntity(ProfessorDto professorDto);
|
||||||
|
|
||||||
|
@Mapping(target = "profileImage", ignore = true) // Ignore profileImage mapping
|
||||||
|
@Mapping(target = "status", source = "status", qualifiedByName = "workingStatusToString")
|
||||||
|
ProfessorDto toDto(Professor professor);
|
||||||
|
|
||||||
|
@Named("stringToWorkingStatus")
|
||||||
|
default WorkingStatus stringToWorkingStatus(String status) {
|
||||||
|
return status == null ? null : WorkingStatus.valueOf(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named("workingStatusToString")
|
||||||
|
default String workingStatusToString(WorkingStatus status) {
|
||||||
|
return status == null ? null : status.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -7,7 +7,9 @@ import org.mapstruct.Mapping;
|
|||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Mapper(imports = {LocalDateTime.class})
|
|
||||||
|
@Mapper(componentModel = "spring") // This is crucial for Spring integration
|
||||||
|
// @Mapper(componentModel = "spring",imports = {LocalDateTime.class})
|
||||||
public interface UserMapper {
|
public interface UserMapper {
|
||||||
|
|
||||||
@Mapping(target = "isNotLocked", source = "notLocked")
|
@Mapping(target = "isNotLocked", source = "notLocked")
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.mapper;
|
||||||
|
|
||||||
|
// import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
@XmlRootElement(name = "YourSoapRequest")
|
||||||
|
public class YourSoapRequest {
|
||||||
|
private String conferencecode;
|
||||||
|
private String conferenceyear;
|
||||||
|
private String bankname;
|
||||||
|
private String remoteip;
|
||||||
|
private String regno;
|
||||||
|
private String candidatename;
|
||||||
|
private String nameinreceipt;
|
||||||
|
private String address1;
|
||||||
|
private String address2;
|
||||||
|
private String city;
|
||||||
|
private String state;
|
||||||
|
private String country;
|
||||||
|
private String pincode;
|
||||||
|
private String phone;
|
||||||
|
private String mobile;
|
||||||
|
private String email;
|
||||||
|
private String foodtype;
|
||||||
|
private String participanttype;
|
||||||
|
private String practicetype;
|
||||||
|
private String accompanymembers;
|
||||||
|
private String paymentamount;
|
||||||
|
private String ToWards;
|
||||||
|
private String Allow80G;
|
||||||
|
private String PanCardNo;
|
||||||
|
private String hasgst;
|
||||||
|
private String GSTReg;
|
||||||
|
private String gstnumber;
|
||||||
|
private String gstmobileno;
|
||||||
|
private String gstemailid;
|
||||||
|
private String inputcaption1;
|
||||||
|
private String inputvalue1;
|
||||||
|
private String inputcaption2;
|
||||||
|
private String inputvalue2;
|
||||||
|
private String inputcaption3;
|
||||||
|
private String inputvalue3;
|
||||||
|
private String inputcaption4;
|
||||||
|
private String inputvalue4;
|
||||||
|
private String inputcaption5;
|
||||||
|
private String inputvalue5;
|
||||||
|
|
||||||
|
// Add getters and setters for each property
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.ConferenceData;
|
||||||
|
|
||||||
|
// import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||||
|
|
||||||
|
|
||||||
|
// @RepositoryRestResource(path = "conferences")
|
||||||
|
public interface ConferenceDataRepository extends JpaRepository<ConferenceData, Long> {
|
||||||
|
Optional<ConferenceData> findByPhone(String phone); // Change 'phone' to your actual field name
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Event;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface EventRepository extends JpaRepository<Event, Long> {
|
||||||
|
// Custom query methods can be added here if needed
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Post;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface PostRepository extends JpaRepository<Post, Long> {
|
||||||
|
|
||||||
|
// Additional query methods can be defined here if needed
|
||||||
|
|
||||||
|
// 1. Find all posts where isPosted is true
|
||||||
|
List<Post> findAllByIsPostedTrue();
|
||||||
|
|
||||||
|
// 3. Find all posts associated with a specific tag where isPosted is true
|
||||||
|
@Query("SELECT p FROM Post p JOIN p.tags t WHERE t = :tag AND p.isPosted = true")
|
||||||
|
List<Post> findAllByTagAndIsPostedTrue(@Param("tag") String tag);
|
||||||
|
|
||||||
|
// Custom query to count unique tags
|
||||||
|
@Query("SELECT t, COUNT(t) FROM Post p JOIN p.tags t GROUP BY t")
|
||||||
|
List<Object[]> findTagsWithCount();
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
// import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||||
|
|
||||||
|
// @RepositoryRestResource(collectionResourceRel = "professors", path = "professors")
|
||||||
|
public interface ProfessorRepository extends JpaRepository<Professor, Long> {
|
||||||
|
|
||||||
|
@Query("SELECT p FROM Professor p WHERE p.email = :email")
|
||||||
|
Optional<Professor> findByEmail(@Param("email") String email);
|
||||||
|
|
||||||
|
@Query("SELECT CASE WHEN COUNT(p) > 0 THEN TRUE ELSE FALSE END FROM Professor p WHERE p.email = :email")
|
||||||
|
Boolean existsByEmail(@Param("email") String email);
|
||||||
|
|
||||||
|
Boolean existsByProfessorId(UUID professorId);
|
||||||
|
|
||||||
|
@Query("SELECT p FROM Professor p WHERE p.professorId = :professorId")
|
||||||
|
Optional<Professor> findByProfessorId(@Param("professorId") UUID professorId);
|
||||||
|
}
|
||||||
@ -18,7 +18,6 @@ public class EmailService {
|
|||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
|
|
||||||
public void sendNewPasswordEmail(String firstName, String password, String email) {
|
public void sendNewPasswordEmail(String firstName, String password, String email) {
|
||||||
|
|
||||||
// Create a Simple MailMessage.
|
// Create a Simple MailMessage.
|
||||||
SimpleMailMessage message = new SimpleMailMessage();
|
SimpleMailMessage message = new SimpleMailMessage();
|
||||||
message.setTo(email);
|
message.setTo(email);
|
||||||
@ -35,7 +34,20 @@ public class EmailService {
|
|||||||
message.setSubject(EMAIL_SUBJECT);
|
message.setSubject(EMAIL_SUBJECT);
|
||||||
message.setText("Hello " + firstName + "!\n\nYour new account password is: " + password + "\n\nThe Support Team");
|
message.setText("Hello " + firstName + "!\n\nYour new account password is: " + password + "\n\nThe Support Team");
|
||||||
|
|
||||||
|
// Log the email details before sending
|
||||||
|
log.info("Preparing to send email:");
|
||||||
|
log.info("From: {}", message.getFrom());
|
||||||
|
log.info("To: {}", String.join(", ", message.getTo()));
|
||||||
|
log.info("Cc: {}", String.join(", ", message.getCc()));
|
||||||
|
log.info("Subject: {}", message.getSubject());
|
||||||
|
log.info("Text: {}", message.getText());
|
||||||
|
|
||||||
|
try {
|
||||||
// Send Message!
|
// Send Message!
|
||||||
this.emailSender.send(message);
|
this.emailSender.send(message);
|
||||||
|
log.info("Email successfully sent to {}", email);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to send email to {}. Error: {}", email, e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.service;
|
||||||
|
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.ProfessorDto;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface ProfessorService {
|
||||||
|
|
||||||
|
Professor register(String firstName, String lastName, String email, String department, String position);
|
||||||
|
|
||||||
|
Page<Professor> findAll(Pageable pageable);
|
||||||
|
|
||||||
|
Professor findByEmail(String email);
|
||||||
|
|
||||||
|
Professor findByProfessorId(UUID professorId);
|
||||||
|
|
||||||
|
Professor addNewProfessor(ProfessorDto professorDto);
|
||||||
|
|
||||||
|
Professor updateProfessor(UUID professorId, ProfessorDto professorDto);
|
||||||
|
|
||||||
|
void deleteProfessor(UUID professorId);
|
||||||
|
|
||||||
|
Professor updateProfileImage(UUID professorId, MultipartFile profileImage);
|
||||||
|
|
||||||
|
byte[] getImageByProfessorId(UUID professorId, String filename);
|
||||||
|
|
||||||
|
byte[] getDefaultProfileImage(UUID professorId);
|
||||||
|
}
|
||||||
@ -0,0 +1,237 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.service;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.ProfessorDto;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.EmailExistsException;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.NotAnImageFileException;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.ProfessorNotFoundException;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.mapper.ProfessorMapper;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.repository.ProfessorRepository;
|
||||||
|
|
||||||
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.http.RequestEntity;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||||
|
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
|
import static net.shyshkin.study.fullstack.supportportal.backend.constant.FileConstant.*;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.springframework.http.MediaType.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ProfessorServiceImpl implements ProfessorService {
|
||||||
|
|
||||||
|
public static final String EMAIL_NOT_FOUND_MSG = "Professor with email `%s` not found";
|
||||||
|
public static final String EMAIL_EXISTS_MSG = "Professor with email `%s` is already registered";
|
||||||
|
public static final String PROFESSOR_NOT_FOUND_MSG = "Professor not found";
|
||||||
|
|
||||||
|
private final ProfessorRepository professorRepository;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
private final EmailService emailService;
|
||||||
|
private final ProfessorMapper professorMapper;
|
||||||
|
private final ProfileImageService profileImageService;
|
||||||
|
private final RestTemplateBuilder restTemplateBuilder;
|
||||||
|
|
||||||
|
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
void init() {
|
||||||
|
restTemplate = restTemplateBuilder
|
||||||
|
.rootUri(TEMP_PROFILE_IMAGE_BASE_URL)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Professor register(String firstName, String lastName, String email, String department, String position) {
|
||||||
|
ProfessorDto professorDto = ProfessorDto.builder()
|
||||||
|
.firstName(firstName)
|
||||||
|
.lastName(lastName)
|
||||||
|
.email(email)
|
||||||
|
.department(department)
|
||||||
|
.position(position)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return addNewProfessor(professorDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateDefaultProfileImageUrl(UUID professorId) {
|
||||||
|
return ServletUriComponentsBuilder.fromCurrentContextPath()
|
||||||
|
.path(String.format(DEFAULT_USER_IMAGE_URI_PATTERN, professorId))
|
||||||
|
.toUriString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateProfileImageUrl(UUID professorId) {
|
||||||
|
return ServletUriComponentsBuilder.fromCurrentContextPath()
|
||||||
|
.path(String.format(DEFAULT_USER_IMAGE_URI_PATTERN, professorId))
|
||||||
|
.pathSegment(USER_IMAGE_FILENAME)
|
||||||
|
.toUriString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Professor> findAll(Pageable pageable) {
|
||||||
|
return professorRepository.findAll(pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Professor findByEmail(String email) {
|
||||||
|
return professorRepository
|
||||||
|
.findByEmail(email)
|
||||||
|
.orElseThrow(() -> new EmailExistsException(String.format(EMAIL_NOT_FOUND_MSG, email)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Professor findByProfessorId(UUID professorId) {
|
||||||
|
return professorRepository
|
||||||
|
.findByProfessorId(professorId)
|
||||||
|
.orElseThrow(() -> new ProfessorNotFoundException(PROFESSOR_NOT_FOUND_MSG));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void saveProfileImage(Professor professor, MultipartFile profileImage) {
|
||||||
|
if (profileImage == null) return;
|
||||||
|
|
||||||
|
if (!List.of(IMAGE_JPEG_VALUE, IMAGE_GIF_VALUE, IMAGE_PNG_VALUE).contains(profileImage.getContentType())) {
|
||||||
|
throw new NotAnImageFileException(profileImage.getOriginalFilename() + " is not an image file. Please upload an image");
|
||||||
|
}
|
||||||
|
|
||||||
|
String imageUrl = profileImageService.persistProfileImage(professor.getProfessorId(), profileImage, USER_IMAGE_FILENAME);
|
||||||
|
|
||||||
|
if (imageUrl == null)
|
||||||
|
imageUrl = generateProfileImageUrl(professor.getProfessorId());
|
||||||
|
|
||||||
|
professor.setProfileImageUrl(imageUrl);
|
||||||
|
professorRepository.save(professor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearProfessorStorage(Professor professor) {
|
||||||
|
profileImageService.clearUserStorage(professor.getProfessorId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private UUID generateUuid() {
|
||||||
|
return UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Professor addNewProfessor(ProfessorDto professorDto) {
|
||||||
|
|
||||||
|
validateNewEmail(professorDto.getEmail());
|
||||||
|
|
||||||
|
Professor professor = professorMapper.toEntity(professorDto);
|
||||||
|
|
||||||
|
// Set a unique identifier for the professor
|
||||||
|
professor.setProfessorId(generateUuid());
|
||||||
|
// professor.setProfessorId(UUID.randomUUID()); // Set a unique identifier for the professor
|
||||||
|
professor.setJoinDate(LocalDateTime.now()); // Set join date if not provided
|
||||||
|
professor.setProfileImageUrl(generateDefaultProfileImageUrl(professor.getProfessorId()));
|
||||||
|
|
||||||
|
professorRepository.save(professor);
|
||||||
|
|
||||||
|
// saveProfileImage(professor, professorDto.getProfileImage());
|
||||||
|
|
||||||
|
return professor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Professor updateProfessor(UUID professorId, ProfessorDto professorDto) {
|
||||||
|
|
||||||
|
Professor professor = professorRepository.findByProfessorId(professorId)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Professor not found with id: " + professorId));
|
||||||
|
|
||||||
|
validateUpdateEmail(professorId,professorDto.getEmail());
|
||||||
|
|
||||||
|
professor.setFirstName(professorDto.getFirstName());
|
||||||
|
professor.setLastName(professorDto.getLastName());
|
||||||
|
professor.setEmail(professorDto.getEmail());
|
||||||
|
professor.setDepartment(professorDto.getDepartment());
|
||||||
|
professor.setPosition(professorDto.getPosition());
|
||||||
|
professor.setOfficeLocation(professorDto.getOfficeLocation());
|
||||||
|
professor.setStatus(professorDto.getStatus());
|
||||||
|
professor.setJoinDate(professorDto.getJoinDate()); // Update join date if provided
|
||||||
|
|
||||||
|
professorRepository.save(professor);
|
||||||
|
|
||||||
|
saveProfileImage(professor, professorDto.getProfileImage());
|
||||||
|
|
||||||
|
return professor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteProfessor(UUID professorId) {
|
||||||
|
Professor professorToBeDeleted = professorRepository
|
||||||
|
.findByProfessorId(professorId)
|
||||||
|
.orElseThrow(() -> new ProfessorNotFoundException(PROFESSOR_NOT_FOUND_MSG));
|
||||||
|
|
||||||
|
clearProfessorStorage(professorToBeDeleted);
|
||||||
|
professorRepository.delete(professorToBeDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Professor updateProfileImage(UUID professorId, MultipartFile profileImage) {
|
||||||
|
Professor professor = findByProfessorId(professorId);
|
||||||
|
saveProfileImage(professor, profileImage);
|
||||||
|
return professor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getImageByProfessorId(UUID professorId, String filename) {
|
||||||
|
if (!professorRepository.existsByProfessorId(professorId)) {
|
||||||
|
throw new ProfessorNotFoundException(PROFESSOR_NOT_FOUND_MSG);
|
||||||
|
}
|
||||||
|
return profileImageService.retrieveProfileImage(professorId, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getDefaultProfileImage(UUID professorId) {
|
||||||
|
if (!professorRepository.existsByProfessorId(professorId)) {
|
||||||
|
throw new ProfessorNotFoundException(PROFESSOR_NOT_FOUND_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestEntity<Void> requestEntity = RequestEntity
|
||||||
|
.get("/{professorId}", professorId)
|
||||||
|
.accept(IMAGE_JPEG)
|
||||||
|
.build();
|
||||||
|
var responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<byte[]>() {});
|
||||||
|
return responseEntity.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateNewEmail(String email) {
|
||||||
|
if (professorRepository.existsByEmail(email)) {
|
||||||
|
throw new EmailExistsException(String.format(EMAIL_EXISTS_MSG, email));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Professor validateUpdateEmail(UUID professorId, String email) {
|
||||||
|
Objects.requireNonNull(professorId);
|
||||||
|
|
||||||
|
Professor currentProfessor = findByProfessorId(professorId);
|
||||||
|
|
||||||
|
if (!Objects.equals(currentProfessor.getEmail(), email) && professorRepository.existsByEmail(email)) {
|
||||||
|
throw new EmailExistsException(String.format(EMAIL_EXISTS_MSG, email));
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentProfessor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -35,3 +35,5 @@ public interface UserService extends UserDetailsService {
|
|||||||
|
|
||||||
byte[] getDefaultProfileImage(UUID userId);
|
byte[] getDefaultProfileImage(UUID userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import net.shyshkin.study.fullstack.supportportal.backend.domain.UserPrincipal;
|
|||||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.UserDto;
|
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.UserDto;
|
||||||
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.*;
|
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.*;
|
||||||
import net.shyshkin.study.fullstack.supportportal.backend.mapper.UserMapper;
|
import net.shyshkin.study.fullstack.supportportal.backend.mapper.UserMapper;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.repository.ProfessorRepository;
|
||||||
import net.shyshkin.study.fullstack.supportportal.backend.repository.UserRepository;
|
import net.shyshkin.study.fullstack.supportportal.backend.repository.UserRepository;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
@ -45,6 +46,7 @@ public class UserServiceImpl implements UserService {
|
|||||||
public static final String EMAIL_EXISTS_MSG = "User with email `%s` is already registered";
|
public static final String EMAIL_EXISTS_MSG = "User with email `%s` is already registered";
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
private final ProfessorRepository professorRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final LoginAttemptService loginAttemptService;
|
private final LoginAttemptService loginAttemptService;
|
||||||
private final EmailService emailService;
|
private final EmailService emailService;
|
||||||
@ -264,10 +266,14 @@ public class UserServiceImpl implements UserService {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] getDefaultProfileImage(UUID userId) {
|
public byte[] getDefaultProfileImage(UUID userId) {
|
||||||
|
|
||||||
if (!userRepository.existsByUserId(userId)) {
|
if (!userRepository.existsByUserId(userId) && !professorRepository.existsByProfessorId(userId)) {
|
||||||
throw new UserNotFoundException(USER_NOT_FOUND_MSG);
|
throw new UserNotFoundException(USER_NOT_FOUND_MSG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (!professorRepository.existsByProfessorId(userId)) {
|
||||||
|
// throw new UserNotFoundException(USER_NOT_FOUND_MSG);
|
||||||
|
// }
|
||||||
|
|
||||||
// "https://robohash.org/11951691-d373-4126-bef2-84d157a6546b"
|
// "https://robohash.org/11951691-d373-4126-bef2-84d157a6546b"
|
||||||
RequestEntity<Void> requestEntity = RequestEntity
|
RequestEntity<Void> requestEntity = RequestEntity
|
||||||
.get("/{userId}", userId)
|
.get("/{userId}", userId)
|
||||||
|
|||||||
@ -4,25 +4,34 @@ server:
|
|||||||
# whitelabel:
|
# whitelabel:
|
||||||
# enabled: false
|
# enabled: false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
mail:
|
mail:
|
||||||
host: smtp.gmail.com
|
host: mail.techzoos.in
|
||||||
port: 587
|
port: 587
|
||||||
username: ${PORTAL_MAIL_USERNAME:fake.user@gmail.com}
|
username: ${PORTAL_MAIL_USERNAME:govardhan@techzoos.in}
|
||||||
password: ${PORTAL_MAIL_PASSWORD:fake_password}
|
password: ${PORTAL_MAIL_PASSWORD:123456}
|
||||||
properties:
|
properties:
|
||||||
mail:
|
mail:
|
||||||
transport:
|
transport:
|
||||||
protocol: smtp
|
protocol: smtp
|
||||||
smtp:
|
smtp:
|
||||||
auth: true
|
auth: false
|
||||||
starttls:
|
starttls:
|
||||||
enable: true
|
enable: true
|
||||||
|
ssl:
|
||||||
|
enable: false
|
||||||
datasource:
|
datasource:
|
||||||
|
# url: jdbc:mysql://mysql:3306/demo
|
||||||
|
url: jdbc:mysql://210.18.189.94:8098/demo
|
||||||
|
# url: jdbc:mysql://${MYSQL_HOST:db}:8098/demo
|
||||||
|
username: youruser
|
||||||
|
password: youruserpassword
|
||||||
|
# url: ${SPRING_DATASOURCE_URL}
|
||||||
|
# username: ${SPRING_DATASOURCE_USERNAME}
|
||||||
|
# password: ${SPRING_DATASOURCE_PASSWORD}
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://mysql:3306/support-portal
|
|
||||||
username: ENC(criE3etnc/EVZbizNgNdmj+8F0BYC3bSVBK1VT/xJ7WMoNvSfdEGsqWfCpaX5lEWvXLOO8pzgjdB5zIOBcTikw==)
|
|
||||||
password: ENC(OTG4nZfio2dHHxV0Ey/Nmb4XeEfaD1YMsRVQxOwF59Q1JSBZPUKLWXORJXPz2RysKRngcdk2SgioAMw166DoqA==)
|
|
||||||
jpa:
|
jpa:
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: update
|
ddl-auto: update
|
||||||
@ -39,87 +48,100 @@ spring:
|
|||||||
web:
|
web:
|
||||||
resources:
|
resources:
|
||||||
add-mappings: false
|
add-mappings: false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app:
|
app:
|
||||||
public-urls: /user/login,/user/register,/user/*/profile-image/**
|
public-urls: /user/login,/user/register,/user/*/profile-image/**,/professors,/professors/**,/api/posts,/api/posts/*,/professor,/professor/*,/api/events,/api/events/*
|
||||||
cors:
|
cors:
|
||||||
allowed-origins: http://localhost:4200,https://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://portal.shyshkin.net
|
allowed-origins: http://localhost:4200,https://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://portal.shyshkin.net,*
|
||||||
jwt:
|
jwt:
|
||||||
secret: ENC(EfWSJqncgjSJ0g/tMzLoO9PlrjmpQf8Eb+q51SUXlh3AzwMHJyTF1gV0VpuNEQkNb9Lsw62xOBnxDNe73BsPDQ==)
|
secret: custom_text
|
||||||
|
|
||||||
|
|
||||||
# secret: ${random.value} #Does not work - every time generates new value
|
# secret: ${random.value} #Does not work - every time generates new value
|
||||||
jasypt:
|
# jasypt:
|
||||||
encryptor:
|
# encryptor:
|
||||||
password: ${JASYPT_PASSWORD}
|
# # password: ${JASYPT_PASSWORD}
|
||||||
algorithm: PBEWITHHMACSHA512ANDAES_256
|
# password: custom_text
|
||||||
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
|
# algorithm: PBEWITHHMACSHA512ANDAES_256
|
||||||
|
# iv-generator-classname: org.jasypt.iv.RandomIvGenerator
|
||||||
|
|
||||||
---
|
---
|
||||||
spring:
|
# spring:
|
||||||
config:
|
# config:
|
||||||
activate:
|
# activate:
|
||||||
on-profile: local
|
# on-profile: local
|
||||||
datasource:
|
# datasource:
|
||||||
url: jdbc:mysql://localhost:23306/support-portal
|
# url: jdbc:mysql://210.18.189.94:8098/demo
|
||||||
jpa:
|
# username: youruser
|
||||||
show-sql: true
|
# password: youruserpassword
|
||||||
logging:
|
|
||||||
level:
|
# jpa:
|
||||||
net.shyshkin: debug
|
# show-sql: true
|
||||||
|
# logging:
|
||||||
|
# level:
|
||||||
|
# net.shyshkin: debug
|
||||||
|
|
||||||
---
|
---
|
||||||
spring:
|
# spring:
|
||||||
config:
|
# config:
|
||||||
activate:
|
# activate:
|
||||||
on-profile: aws-local
|
# on-profile: aws-local
|
||||||
datasource:
|
# datasource:
|
||||||
url: jdbc:mysql://localhost:3306/support_portal
|
# url: jdbc:mysql://210.18.189.94:8098/demo
|
||||||
username: support_portal_user
|
# username: youruser
|
||||||
password: Supp0rt_Porta!_P@ssword
|
# password: youruserpassword
|
||||||
mail:
|
|
||||||
host: email-smtp.eu-north-1.amazonaws.com
|
# mail:
|
||||||
port: 587
|
# host: email-smtp.eu-north-1.amazonaws.com
|
||||||
username: AKIAVW7XGDOWFHHCELIH
|
# port: 587
|
||||||
password: BJyWOWS1xWYR35MRCFn3BuuQ6vY+k7DRsdAvOfqDs/Fk
|
# username: AKIAVW7XGDOWFHHCELIH
|
||||||
|
# password: BJyWOWS1xWYR35MRCFn3BuuQ6vY+k7DRsdAvOfqDs/Fk
|
||||||
|
|
||||||
# we want to test (1) from localhost, (2) from S3 bucket Static Web Site, (3) from our EC2 instance
|
# we want to test (1) from localhost, (2) from S3 bucket Static Web Site, (3) from our EC2 instance
|
||||||
app:
|
# app:
|
||||||
email:
|
# email:
|
||||||
from: d.art.shishkin@gmail.com
|
# from: d.art.shishkin@gmail.com
|
||||||
carbon-copy: d.art.shishkin@gmail.com
|
# carbon-copy: d.art.shishkin@gmail.com
|
||||||
cors:
|
# cors:
|
||||||
allowed-origins: http://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://support-portal.shyshkin.net,http://portal.shyshkin.net
|
# allowed-origins: http://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://support-portal.shyshkin.net,http://portal.shyshkin.net
|
||||||
server:
|
# server:
|
||||||
port: 5000
|
# port: 5000
|
||||||
logging:
|
# logging:
|
||||||
level:
|
# level:
|
||||||
net.shyshkin: debug
|
# net.shyshkin: debug
|
||||||
|
|
||||||
---
|
---
|
||||||
spring:
|
# spring:
|
||||||
config:
|
# config:
|
||||||
activate:
|
# activate:
|
||||||
on-profile: aws-rds
|
# on-profile: aws-rds
|
||||||
datasource:
|
# datasource:
|
||||||
url: jdbc:mysql://portal-db.coaum9neetxc.eu-north-1.rds.amazonaws.com:3306/support_portal
|
# url: jdbc:mysql://210.18.189.94:8098/demo
|
||||||
username: ENC(MPap/iQmyyLSeulVzLLq4nQ5dcwMyJ1cbW+bW7MOU4pN7CHQULbaDn8/5VszOP9F)
|
# username: youruser
|
||||||
password: ENC(nC0PV+0wPW+73o2uOh4Zg7EA34vdwZKpkPD4CIKvjDDXQ+dGXjykTuHUl3jlxkRC/00IpFurk/UJ9hTpZ6QqGA==)
|
# password: youruserpassword
|
||||||
mail:
|
|
||||||
host: email-smtp.eu-north-1.amazonaws.com
|
# mail:
|
||||||
port: 587
|
# host: email-smtp.eu-north-1.amazonaws.com
|
||||||
username: ENC(CgaSXOMqTmswes1PgAYp3ICcoIVVXyKUlDR1Se963Vja02cBIor/2884e2OEFKW4XhBClTbuZCVdHK0vRRNqYg==)
|
# port: 587
|
||||||
password: ENC(GA8XsfU8vmat/7A8qEhrVz0Y47THxNT8jQ29wSg035fozwW7m+fKhJMQd4tgxL9dPfOzSXYzkffL0fG1AihWiHl99H9iBeXndDSvOhskvh4=)
|
# username: custom_text
|
||||||
|
# password: custom_text
|
||||||
|
|
||||||
# we want to test (1) from localhost, (2) from S3 bucket Static Web Site, (3) from our EC2 instance
|
# we want to test (1) from localhost, (2) from S3 bucket Static Web Site, (3) from our EC2 instance
|
||||||
app:
|
# app:
|
||||||
email:
|
# email:
|
||||||
from: d.art.shishkin@gmail.com
|
# from: d.art.shishkin@gmail.com
|
||||||
carbon-copy: d.art.shishkin@gmail.com
|
# carbon-copy: d.art.shishkin@gmail.com
|
||||||
cors:
|
# cors:
|
||||||
allowed-origins: http://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://support-portal.shyshkin.net,http://portal.shyshkin.net
|
# allowed-origins: http://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://support-portal.shyshkin.net,http://portal.shyshkin.net
|
||||||
server:
|
# server:
|
||||||
port: 5000
|
# port: 5000
|
||||||
logging:
|
# logging:
|
||||||
level:
|
# level:
|
||||||
net.shyshkin: debug
|
# net.shyshkin: debug
|
||||||
|
|
||||||
#####
|
#####
|
||||||
#
|
#
|
||||||
@ -127,37 +149,37 @@ logging:
|
|||||||
#
|
#
|
||||||
#####
|
#####
|
||||||
|
|
||||||
server.ssl:
|
# server.ssl:
|
||||||
enabled: true # Enable HTTPS support (only accept HTTPS requests)
|
# enabled: true # Enable HTTPS support (only accept HTTPS requests)
|
||||||
key-alias: securedPortal # Alias that identifies the key in the key store
|
# key-alias: securedPortal # Alias that identifies the key in the key store
|
||||||
key-store: classpath:securedPortal-keystore.p12 # Keystore location
|
# key-store: classpath:securedPortal-keystore.p12 # Keystore location
|
||||||
key-store-password: ENC(nqDHyVFmySdbaCOZfj4EiQLRYyLSPLRLq/OzncqlsFIuWvh8caiOapAb+zrKR1+A) # Keystore password
|
# key-store-password: custom_text
|
||||||
key-store-type: PKCS12 # Keystore format
|
# key-store-type: PKCS12 # Keystore format
|
||||||
|
|
||||||
---
|
# ---
|
||||||
spring:
|
# spring:
|
||||||
config:
|
# config:
|
||||||
activate:
|
# activate:
|
||||||
on-profile: image-s3
|
# on-profile: image-s3
|
||||||
app:
|
# app:
|
||||||
amazon-s3:
|
# amazon-s3:
|
||||||
bucket-name: portal-user-profile-images
|
# bucket-name: portal-user-profile-images
|
||||||
|
|
||||||
---
|
# ---
|
||||||
spring:
|
# spring:
|
||||||
config:
|
# config:
|
||||||
activate:
|
# activate:
|
||||||
on-profile: image-s3-localstack
|
# on-profile: image-s3-localstack
|
||||||
app:
|
# app:
|
||||||
amazon-s3:
|
# amazon-s3:
|
||||||
bucket-name: portal-user-profile-images
|
# bucket-name: portal-user-profile-images
|
||||||
config:
|
# config:
|
||||||
aws:
|
# aws:
|
||||||
region: eu-north-1
|
# region: eu-north-1
|
||||||
s3:
|
# s3:
|
||||||
url: http://127.0.0.1:4566
|
# url: http://127.0.0.1:4566
|
||||||
bucket-name: portal-user-profile-images
|
# bucket-name: portal-user-profile-images
|
||||||
access-key: localstack
|
# access-key: localstack
|
||||||
secret-key: localstack
|
# secret-key: localstack
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -114,6 +114,9 @@ class UserResourceTest extends BaseUserTest {
|
|||||||
//when
|
//when
|
||||||
ResponseEntity<User> responseEntity = restTemplate.postForEntity("/user/register", fakeUser, User.class);
|
ResponseEntity<User> responseEntity = restTemplate.postForEntity("/user/register", fakeUser, User.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//then
|
//then
|
||||||
log.debug("Response Entity: {}", responseEntity);
|
log.debug("Response Entity: {}", responseEntity);
|
||||||
assertThat(responseEntity.getStatusCode()).isEqualTo(OK);
|
assertThat(responseEntity.getStatusCode()).isEqualTo(OK);
|
||||||
@ -132,6 +135,7 @@ class UserResourceTest extends BaseUserTest {
|
|||||||
user = registeredUser;
|
user = registeredUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(40)
|
@Order(40)
|
||||||
void registerUser_usernameExists() {
|
void registerUser_usernameExists() {
|
||||||
|
|||||||
25
support-portal-frontend/Dockerfile
Normal file
25
support-portal-frontend/Dockerfile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
|
||||||
|
### STAGE 1: Build ###
|
||||||
|
FROM node:12.22.12 AS build
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm install --save --legacy-peer-deps
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build --prod
|
||||||
|
|
||||||
|
|
||||||
|
### STAGE 2: Run ###
|
||||||
|
FROM nginx:1.17.1-alpine
|
||||||
|
# COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY --from=build /usr/src/app/dist/support-portal-frontend /usr/share/nginx/html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Expose port 80 to the Docker host, so we can access it
|
||||||
|
# from the outside.
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
ENTRYPOINT ["nginx","-g","daemon off;"]
|
||||||
@ -31,8 +31,14 @@
|
|||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [
|
||||||
|
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@ -115,7 +121,8 @@
|
|||||||
"karmaConfig": "karma.conf.js",
|
"karmaConfig": "karma.conf.js",
|
||||||
"assets": [
|
"assets": [
|
||||||
"src/favicon.ico",
|
"src/favicon.ico",
|
||||||
"src/assets"
|
"src/assets",
|
||||||
|
"src/assets/assets2"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
|
|||||||
7
support-portal-frontend/mac_config.txt
Normal file
7
support-portal-frontend/mac_config.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
[[ -s $HOME/.nvm/nvm.sh ]] && . $HOME/.nvm/nvm.sh # This loads NVM
|
||||||
|
|
||||||
|
|
||||||
|
nvm use 12
|
||||||
|
|
||||||
16463
support-portal-frontend/package-lock.json
generated
16463
support-portal-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -18,17 +18,25 @@
|
|||||||
"@angular/platform-browser": "~12.2.0",
|
"@angular/platform-browser": "~12.2.0",
|
||||||
"@angular/platform-browser-dynamic": "~12.2.0",
|
"@angular/platform-browser-dynamic": "~12.2.0",
|
||||||
"@angular/router": "~12.2.0",
|
"@angular/router": "~12.2.0",
|
||||||
"@auth0/angular-jwt": "^5.0.2",
|
"@auth0/angular-jwt": "^3.0.1",
|
||||||
|
"@josipv/angular-editor-k2": "^2.20.0",
|
||||||
|
"@nicky-lenaers/ngx-scroll-to": "^9.0.0",
|
||||||
"angular-notifier": "^9.1.0",
|
"angular-notifier": "^9.1.0",
|
||||||
|
"ng-particles": "^2.1.11",
|
||||||
|
"ngx-owl-carousel-o": "^5.0.0",
|
||||||
|
"ngx-typed-js": "^2.0.2",
|
||||||
"rxjs": "~6.6.0",
|
"rxjs": "~6.6.0",
|
||||||
"subsink": "^1.0.2",
|
"subsink": "^1.0.2",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
|
"tsparticles": "^1.18.11",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "~12.2.5",
|
"@angular-devkit/build-angular": "^12.2.18",
|
||||||
"@angular/cli": "~12.2.5",
|
"@angular/cli": "~12.2.5",
|
||||||
"@angular/compiler-cli": "~12.2.0",
|
"@angular/compiler-cli": "~12.2.0",
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
"@types/jasmine": "~3.8.0",
|
"@types/jasmine": "~3.8.0",
|
||||||
"@types/node": "^12.11.1",
|
"@types/node": "^12.11.1",
|
||||||
"jasmine-core": "~3.8.0",
|
"jasmine-core": "~3.8.0",
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {RouterModule, Routes} from "@angular/router";
|
||||||
|
|
||||||
|
import {LoginComponent} from "../component/login/login.component";
|
||||||
|
import {RegisterComponent} from "../component/register/register.component";
|
||||||
|
import {UserComponent} from "../component/user/user.component";
|
||||||
|
import {AuthenticationGuard} from "../guard/authentication.guard";
|
||||||
|
import {ManagementComponent} from "../component/management/management.component";
|
||||||
|
import {UsersComponent} from "../component/management/users/users.component";
|
||||||
|
import {SettingsComponent} from "../component/management/settings/settings.component";
|
||||||
|
import {ProfileComponent} from "../component/management/profile/profile.component";
|
||||||
|
import {UserEditComponent} from "../component/management/users/user-edit/user-edit.component";
|
||||||
|
import {UserViewComponent} from "../component/management/users/user-view/user-view.component";
|
||||||
|
import {UserResolver} from "../component/management/users/user-resolver.service";
|
||||||
|
import { ProfessorComponent } from '../component/professor/professor.component';
|
||||||
|
import { HomeComponent } from '../component/home/home.component';
|
||||||
|
import { EventComponent } from '../component/event/event.component';
|
||||||
|
import { BlogComponent } from '../component/blog/blog.component';
|
||||||
|
import { EventFormComponent } from '../component/event-form/event-form.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: '', component: HomeComponent },
|
||||||
|
|
||||||
|
{ path: 'login', component: LoginComponent },
|
||||||
|
{ path: 'home', component: HomeComponent },
|
||||||
|
{ path: 'register', component: RegisterComponent },
|
||||||
|
{ path: 'settings', component: SettingsComponent, canActivate: [AuthenticationGuard] },
|
||||||
|
{ path: 'profile', component: ProfileComponent, canActivate: [AuthenticationGuard] },
|
||||||
|
{ path: 'events', component: EventComponent, canActivate: [AuthenticationGuard] },
|
||||||
|
{ path: 'eventForm', component: EventFormComponent, canActivate: [AuthenticationGuard] },
|
||||||
|
{ path: 'eventForm/:id', component: EventFormComponent, canActivate: [AuthenticationGuard] },
|
||||||
|
{ path: 'blogs', component: BlogComponent, canActivate: [AuthenticationGuard] },
|
||||||
|
{ path: 'userManagement', component: UserComponent, canActivate: [AuthenticationGuard] },
|
||||||
|
{ path: 'professorManagement', component: ProfessorComponent, canActivate: [AuthenticationGuard] },
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'login',
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class AdminRoutingModule { }
|
||||||
82
support-portal-frontend/src/app/admin/admin.module.ts
Normal file
82
support-portal-frontend/src/app/admin/admin.module.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
|
|
||||||
|
// import {AppComponent} from '../app.component';
|
||||||
|
import {HTTP_INTERCEPTORS, HttpClientModule} from "@angular/common/http";
|
||||||
|
import {AuthenticationService} from "../service/authentication.service";
|
||||||
|
import {UserService} from "../service/user.service";
|
||||||
|
import {AuthInterceptor} from "../interceptor/auth.interceptor";
|
||||||
|
import {AuthenticationGuard} from "../guard/authentication.guard";
|
||||||
|
import {LoginComponent} from '../component/login/login.component';
|
||||||
|
import {RegisterComponent} from '../component/register/register.component';
|
||||||
|
import {UserComponent} from '../component/user/user.component';
|
||||||
|
// import {AppRoutingModule} from '../app-routing.module';
|
||||||
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
|
import {ManagementComponent} from '../component/management/management.component';
|
||||||
|
import {UsersComponent} from '../component/management/users/users.component';
|
||||||
|
import {SettingsComponent} from '../component/management/settings/settings.component';
|
||||||
|
import {ProfileComponent} from '../component/management/profile/profile.component';
|
||||||
|
import {UsersTableComponent} from '../component/management/users/users-table/users-table.component';
|
||||||
|
import {UserViewComponent} from '../component/management/users/user-view/user-view.component';
|
||||||
|
import {UserEditComponent} from '../component/management/users/user-edit/user-edit.component';
|
||||||
|
import { ProfessorComponent } from '../component/professor/professor.component';
|
||||||
|
import { MenuComponent } from '../component/menu/menu.component';
|
||||||
|
import { HomeComponent } from '../component/home/home.component';
|
||||||
|
import { BlogComponent } from '../component/blog/blog.component';
|
||||||
|
import { EventComponent } from '../component/event/event.component';
|
||||||
|
import { BlogService } from '../service/blog.service';
|
||||||
|
import { AngularEditorModule } from '@josipv/angular-editor-k2';
|
||||||
|
import { NotificationModule } from '../notification/notification.module';
|
||||||
|
import { EventFormComponent } from '../component/event-form/event-form.component';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { AdminRoutingModule } from './admin-routing.module';
|
||||||
|
// import { PagesModule } from '../pages/pages.module';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
// AppComponent,
|
||||||
|
LoginComponent,
|
||||||
|
RegisterComponent,
|
||||||
|
UserComponent,
|
||||||
|
ManagementComponent,
|
||||||
|
UsersComponent,
|
||||||
|
SettingsComponent,
|
||||||
|
ProfileComponent,
|
||||||
|
UsersTableComponent,
|
||||||
|
UserViewComponent,
|
||||||
|
UserEditComponent,
|
||||||
|
ProfessorComponent,
|
||||||
|
MenuComponent,
|
||||||
|
HomeComponent,
|
||||||
|
BlogComponent,
|
||||||
|
EventComponent,
|
||||||
|
EventFormComponent
|
||||||
|
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
// BrowserModule,
|
||||||
|
// HttpClientModule,
|
||||||
|
NotificationModule,
|
||||||
|
// AppRoutingModule,
|
||||||
|
AdminRoutingModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
AngularEditorModule,
|
||||||
|
|
||||||
|
// PagesModule,
|
||||||
|
|
||||||
|
],
|
||||||
|
// providers: [AuthenticationGuard, AuthenticationService, UserService,BlogService,
|
||||||
|
// {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true}
|
||||||
|
// ],
|
||||||
|
// bootstrap: [AppComponent]
|
||||||
|
})
|
||||||
|
export class AdminModule { }
|
||||||
|
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ describe('routes', () => {
|
|||||||
expect(routes).toContain({path: "register", component: RegisterComponent});
|
expect(routes).toContain({path: "register", component: RegisterComponent});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain a route for /user/management', () => {
|
it('should contain a route for /dashboard/userManagement', () => {
|
||||||
expect(routes).toContain({path: 'user/management', component: UserComponent, canActivate: [AuthenticationGuard]});
|
expect(routes).toContain({path: 'user/management', component: UserComponent, canActivate: [AuthenticationGuard]});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,41 +1,100 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import {RouterModule, Routes} from "@angular/router";
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import {LoginComponent} from "./component/login/login.component";
|
import { LoginComponent } from './component/login/login.component';
|
||||||
import {RegisterComponent} from "./component/register/register.component";
|
import { RegisterComponent } from './component/register/register.component';
|
||||||
import {UserComponent} from "./component/user/user.component";
|
import { UserComponent } from './component/user/user.component';
|
||||||
import {AuthenticationGuard} from "./guard/authentication.guard";
|
import { AuthenticationGuard } from './guard/authentication.guard';
|
||||||
import {ManagementComponent} from "./component/management/management.component";
|
import { ManagementComponent } from './component/management/management.component';
|
||||||
import {UsersComponent} from "./component/management/users/users.component";
|
import { UsersComponent } from './component/management/users/users.component';
|
||||||
import {SettingsComponent} from "./component/management/settings/settings.component";
|
import { SettingsComponent } from './component/management/settings/settings.component';
|
||||||
import {ProfileComponent} from "./component/management/profile/profile.component";
|
import { ProfileComponent } from './component/management/profile/profile.component';
|
||||||
import {UserEditComponent} from "./component/management/users/user-edit/user-edit.component";
|
import { UserEditComponent } from './component/management/users/user-edit/user-edit.component';
|
||||||
import {UserViewComponent} from "./component/management/users/user-view/user-view.component";
|
import { UserViewComponent } from './component/management/users/user-view/user-view.component';
|
||||||
import {UserResolver} from "./component/management/users/user-resolver.service";
|
import { UserResolver } from './component/management/users/user-resolver.service';
|
||||||
|
import { ProfessorComponent } from './component/professor/professor.component';
|
||||||
|
import { HomeComponent } from './component/home/home.component';
|
||||||
|
import { EventComponent } from './component/event/event.component';
|
||||||
|
import { BlogComponent } from './component/blog/blog.component';
|
||||||
|
import { EventFormComponent } from './component/event-form/event-form.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{path: 'login', component: LoginComponent},
|
// {
|
||||||
{path: 'register', component: RegisterComponent},
|
// path: '',
|
||||||
{path: 'user/management', component: UserComponent, canActivate: [AuthenticationGuard]},
|
// loadChildren: () =>
|
||||||
|
// import('./pages/pages.module').then((m) => m.PagesModule),
|
||||||
|
// },
|
||||||
|
|
||||||
{
|
{
|
||||||
path: 'management', component: ManagementComponent, canActivate: [AuthenticationGuard],
|
path: 'dashboard',
|
||||||
children: [
|
loadChildren: () =>
|
||||||
{path: 'settings', component: SettingsComponent},
|
import('./admin/admin.module').then((m) => m.AdminModule),
|
||||||
{path: 'profile', component: ProfileComponent},
|
|
||||||
{
|
|
||||||
path: 'users', component: UsersComponent,
|
|
||||||
children: [
|
|
||||||
{path: ':id/view', component: UserViewComponent, resolve: {user: UserResolver}},
|
|
||||||
{path: ':id/edit', component: UserEditComponent}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{path: '', redirectTo: '/login', pathMatch: 'full'}
|
|
||||||
|
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
|
||||||
|
|
||||||
|
// { path: 'home', component: HomeComponent },
|
||||||
|
// { path: 'login', component: LoginComponent },
|
||||||
|
// { path: 'register', component: RegisterComponent },
|
||||||
|
// {
|
||||||
|
// path: 'settings',
|
||||||
|
// component: SettingsComponent,
|
||||||
|
// canActivate: [AuthenticationGuard],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// path: 'profile',
|
||||||
|
// component: ProfileComponent,
|
||||||
|
// canActivate: [AuthenticationGuard],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// path: 'events',
|
||||||
|
// component: EventComponent,
|
||||||
|
// canActivate: [AuthenticationGuard],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// path: 'eventForm',
|
||||||
|
// component: EventFormComponent,
|
||||||
|
// canActivate: [AuthenticationGuard],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// path: 'eventForm/:id',
|
||||||
|
// component: EventFormComponent,
|
||||||
|
// canActivate: [AuthenticationGuard],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// path: 'blogs',
|
||||||
|
// component: BlogComponent,
|
||||||
|
// canActivate: [AuthenticationGuard],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// path: 'user/management',
|
||||||
|
// component: UserComponent,
|
||||||
|
// canActivate: [AuthenticationGuard],
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// path: 'professor/management',
|
||||||
|
// component: ProfessorComponent,
|
||||||
|
// canActivate: [AuthenticationGuard],
|
||||||
|
// },
|
||||||
|
|
||||||
|
// {
|
||||||
|
// path: 'management', component: ManagementComponent, canActivate: [AuthenticationGuard],
|
||||||
|
// children: [
|
||||||
|
// {path: 'settings', component: SettingsComponent},
|
||||||
|
// {path: 'profile', component: ProfileComponent},
|
||||||
|
// {
|
||||||
|
// path: 'users', component: UsersComponent,
|
||||||
|
// children: [
|
||||||
|
// {path: ':id/view', component: UserViewComponent, resolve: {user: UserResolver}},
|
||||||
|
// {path: ':id/edit', component: UserEditComponent}
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
// { path: '', redirectTo: '/login', pathMatch: 'full' },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class AppRoutingModule {
|
export class AppRoutingModule {}
|
||||||
}
|
|
||||||
|
|||||||
@ -6,5 +6,5 @@ import {Component} from '@angular/core';
|
|||||||
styleUrls: ['./app.component.css']
|
styleUrls: ['./app.component.css']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'support-portal-frontend';
|
title = 'cnc - Admin';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import {Compiler, NgModule} from '@angular/core';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
|
import { HashLocationStrategy, LocationStrategy } from '@angular/common';
|
||||||
|
|
||||||
|
|
||||||
import {AppComponent} from './app.component';
|
import {AppComponent} from './app.component';
|
||||||
import {HTTP_INTERCEPTORS, HttpClientModule} from "@angular/common/http";
|
import {HTTP_INTERCEPTORS, HttpClientModule} from "@angular/common/http";
|
||||||
@ -7,12 +9,11 @@ import {AuthenticationService} from "./service/authentication.service";
|
|||||||
import {UserService} from "./service/user.service";
|
import {UserService} from "./service/user.service";
|
||||||
import {AuthInterceptor} from "./interceptor/auth.interceptor";
|
import {AuthInterceptor} from "./interceptor/auth.interceptor";
|
||||||
import {AuthenticationGuard} from "./guard/authentication.guard";
|
import {AuthenticationGuard} from "./guard/authentication.guard";
|
||||||
import {NotificationModule} from "./notification/notification.module";
|
|
||||||
import {LoginComponent} from './component/login/login.component';
|
import {LoginComponent} from './component/login/login.component';
|
||||||
import {RegisterComponent} from './component/register/register.component';
|
import {RegisterComponent} from './component/register/register.component';
|
||||||
import {UserComponent} from './component/user/user.component';
|
import {UserComponent} from './component/user/user.component';
|
||||||
import {AppRoutingModule} from './app-routing.module';
|
import {AppRoutingModule} from './app-routing.module';
|
||||||
import {FormsModule} from "@angular/forms";
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
import {ManagementComponent} from './component/management/management.component';
|
import {ManagementComponent} from './component/management/management.component';
|
||||||
import {UsersComponent} from './component/management/users/users.component';
|
import {UsersComponent} from './component/management/users/users.component';
|
||||||
import {SettingsComponent} from './component/management/settings/settings.component';
|
import {SettingsComponent} from './component/management/settings/settings.component';
|
||||||
@ -20,30 +21,58 @@ import {ProfileComponent} from './component/management/profile/profile.component
|
|||||||
import {UsersTableComponent} from './component/management/users/users-table/users-table.component';
|
import {UsersTableComponent} from './component/management/users/users-table/users-table.component';
|
||||||
import {UserViewComponent} from './component/management/users/user-view/user-view.component';
|
import {UserViewComponent} from './component/management/users/user-view/user-view.component';
|
||||||
import {UserEditComponent} from './component/management/users/user-edit/user-edit.component';
|
import {UserEditComponent} from './component/management/users/user-edit/user-edit.component';
|
||||||
|
import { ProfessorComponent } from './component/professor/professor.component';
|
||||||
|
import { MenuComponent } from './component/menu/menu.component';
|
||||||
|
import { HomeComponent } from './component/home/home.component';
|
||||||
|
import { BlogComponent } from './component/blog/blog.component';
|
||||||
|
import { EventComponent } from './component/event/event.component';
|
||||||
|
import { BlogService } from './service/blog.service';
|
||||||
|
import { AngularEditorModule } from '@josipv/angular-editor-k2';
|
||||||
|
import { NotificationModule } from './notification/notification.module';
|
||||||
|
import { EventFormComponent } from './component/event-form/event-form.component';
|
||||||
|
// import { PagesModule } from './pages/pages.module';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
LoginComponent,
|
// LoginComponent,
|
||||||
RegisterComponent,
|
// RegisterComponent,
|
||||||
UserComponent,
|
// UserComponent,
|
||||||
ManagementComponent,
|
// ManagementComponent,
|
||||||
UsersComponent,
|
// UsersComponent,
|
||||||
SettingsComponent,
|
// SettingsComponent,
|
||||||
ProfileComponent,
|
// ProfileComponent,
|
||||||
UsersTableComponent,
|
// UsersTableComponent,
|
||||||
UserViewComponent,
|
// UserViewComponent,
|
||||||
UserEditComponent
|
// UserEditComponent,
|
||||||
|
// ProfessorComponent,
|
||||||
|
// MenuComponent,
|
||||||
|
// HomeComponent,
|
||||||
|
// BlogComponent,
|
||||||
|
// EventComponent,
|
||||||
|
// EventFormComponent
|
||||||
|
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
NotificationModule,
|
NotificationModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
FormsModule
|
// FormsModule,
|
||||||
|
// ReactiveFormsModule,
|
||||||
|
// AngularEditorModule,
|
||||||
|
// PagesModule,
|
||||||
|
|
||||||
],
|
],
|
||||||
providers: [AuthenticationGuard, AuthenticationService, UserService,
|
// providers:[],
|
||||||
{provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true}
|
providers: [Compiler,AuthenticationGuard, AuthenticationService, UserService,BlogService,
|
||||||
|
{provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
|
||||||
|
{provide: LocationStrategy, useClass: HashLocationStrategy}
|
||||||
|
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
/* Add these styles to your component's CSS file or a global stylesheet */
|
||||||
|
|
||||||
|
.blog-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-list {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 20px; /* Adjust spacing as needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-form {
|
||||||
|
flex: 2; /* Adjust this if you want the form to be wider or narrower */
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-form form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.result-container {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-container h4 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
<app-menu></app-menu>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<div class="d-flex justify-content-between mb-3">
|
||||||
|
<!-- Button to Toggle to Blog Form -->
|
||||||
|
<button *ngIf="!isShowForm" class="btn btn-primary" (click)="showForm()">New Blog</button>
|
||||||
|
<!-- Button to Toggle to Blog List -->
|
||||||
|
<button *ngIf="isShowForm" class="btn btn-secondary" (click)="showTable()">Back to List</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Blog Form -->
|
||||||
|
<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="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">
|
||||||
|
<div *ngIf="blogForm?.get('title')?.invalid && blogForm.get('title')?.touched" class="text-danger">
|
||||||
|
Title is required.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</select>
|
||||||
|
<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">
|
||||||
|
<label class="text-primary" for="tags">Tags</label>
|
||||||
|
<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.
|
||||||
|
</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">
|
||||||
|
Content is required.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result-container mt-4">
|
||||||
|
<h4 class="text-primary">Preview</h4>
|
||||||
|
<div [innerHTML]="blogForm.get('content')?.value || ''"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</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>
|
||||||
|
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { BlogComponent } from './blog.component';
|
||||||
|
|
||||||
|
describe('BlogComponent', () => {
|
||||||
|
let component: BlogComponent;
|
||||||
|
let fixture: ComponentFixture<BlogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ BlogComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BlogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
187
support-portal-frontend/src/app/component/blog/blog.component.ts
Normal file
187
support-portal-frontend/src/app/component/blog/blog.component.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-blog',
|
||||||
|
templateUrl: './blog.component.html',
|
||||||
|
styleUrls: ['./blog.component.css'],
|
||||||
|
})
|
||||||
|
export class BlogComponent implements OnInit {
|
||||||
|
blogs: any[] = [];
|
||||||
|
allProfessors: any[] = [];
|
||||||
|
selectedBlog: any = null;
|
||||||
|
blogForm: FormGroup;
|
||||||
|
editing: boolean = false;
|
||||||
|
loggedInUser: any;
|
||||||
|
currentBlog: any = null; // Holds the blog being edited
|
||||||
|
isShowForm = false; // Controls visibility of form vs. table
|
||||||
|
content = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private blogService: BlogService,
|
||||||
|
private authService: AuthenticationService,
|
||||||
|
private professorService: ProfessorService,
|
||||||
|
private fb: FormBuilder
|
||||||
|
) {
|
||||||
|
// Initialize form with form controls
|
||||||
|
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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
editorConfig: AngularEditorConfig = {
|
||||||
|
editable: true,
|
||||||
|
spellcheck: true,
|
||||||
|
height: '10',
|
||||||
|
minHeight: '0',
|
||||||
|
maxHeight: 'auto',
|
||||||
|
width: 'auto',
|
||||||
|
minWidth: '0',
|
||||||
|
translate: 'yes',
|
||||||
|
enableToolbar: true,
|
||||||
|
showToolbar: true,
|
||||||
|
placeholder: 'Enter text here...',
|
||||||
|
defaultParagraphSeparator: '',
|
||||||
|
defaultFontName: '',
|
||||||
|
defaultFontSize: '',
|
||||||
|
// headers: [{
|
||||||
|
|
||||||
|
// }],
|
||||||
|
fonts: [
|
||||||
|
{ class: 'arial', name: 'Arial' },
|
||||||
|
{ class: 'times-new-roman', name: 'Times New Roman' },
|
||||||
|
{ class: 'calibri', name: 'Calibri' },
|
||||||
|
{ class: 'comic-sans-ms', name: 'Comic Sans MS' },
|
||||||
|
],
|
||||||
|
customClasses: [
|
||||||
|
{
|
||||||
|
name: 'quote',
|
||||||
|
class: 'quote',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'redText',
|
||||||
|
class: 'redText',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'titleText',
|
||||||
|
class: 'titleText',
|
||||||
|
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));
|
||||||
|
|
||||||
|
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(); // Ensure form is reset when showing
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteBlog(blog: any) {
|
||||||
|
if (confirm('Are you sure you want to delete this blog?')) {
|
||||||
|
this.blogService.deleteBlog(blog.id).subscribe(() => {
|
||||||
|
this.loadBlogs();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
resetForm() {
|
||||||
|
this.blogForm.reset();
|
||||||
|
this.selectedBlog = null;
|
||||||
|
this.editing = false;
|
||||||
|
this.blogForm.patchValue({
|
||||||
|
professors: [],
|
||||||
|
tags: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,145 @@
|
|||||||
|
<app-menu></app-menu>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<form [formGroup]="eventForm" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="code" class="form-label text-primary">Code</label>
|
||||||
|
<input id="code" formControlName="code" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="year" class="form-label text-primary">Year</label>
|
||||||
|
<input id="year" formControlName="year" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="subject" class="form-label text-primary">Subject</label>
|
||||||
|
<input id="subject" formControlName="subject" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="title" class="form-label text-primary">Title</label>
|
||||||
|
<input id="title" formControlName="title" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="subTitle" class="form-label text-primary">Subtitle</label>
|
||||||
|
<input id="subTitle" formControlName="subTitle" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="date" class="form-label text-primary">Date</label>
|
||||||
|
<input id="date" formControlName="date" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="phone" class="form-label text-primary">Phone</label>
|
||||||
|
<input id="phone" formControlName="phone" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="email" class="form-label text-primary">Email</label>
|
||||||
|
<input id="email" formControlName="email" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="isActive" class="form-label text-primary">Active</label>
|
||||||
|
<input id="isActive" type="checkbox" formControlName="isActive" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label class="form-label text-primary">Venues</label>
|
||||||
|
<div formArrayName="venues">
|
||||||
|
<div *ngFor="let venue of venues.controls; let i = index" class="card mb-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div [formGroupName]="i">
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-12">
|
||||||
|
<input formControlName="title" placeholder="Title" class="form-control" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-6">
|
||||||
|
<input formControlName="date" placeholder="Date" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<input formControlName="address" placeholder="Address" class="form-control" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-12">
|
||||||
|
<input formControlName="info" placeholder="Info" class="form-control" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm float-end" (click)="removeVenue(i)">
|
||||||
|
<!-- <i class="bi bi-trash"></i> -->X
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary" (click)="addVenue()">
|
||||||
|
<i class="bi bi-plus"></i> Add Venue
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label class="form-label text-primary">Highlights</label>
|
||||||
|
<div formArrayName="highlights">
|
||||||
|
<div *ngFor="let highlight of highlights.controls; let i = index" class="card mb-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<input [formControlName]="i" class="form-control" placeholder="Highlight" />
|
||||||
|
<button type="button" class="btn btn-danger btn-sm float-end mt-2" (click)="removeHighlight(i)">
|
||||||
|
<!-- <i class="bi bi-trash"></i> -->X
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary" (click)="addHighlight()">
|
||||||
|
<i class="bi bi-plus"></i> Add Highlight
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label class="form-label text-primary">Organisers</label>
|
||||||
|
<div formArrayName="organisers">
|
||||||
|
<div *ngFor="let organiser of organisers.controls; let i = index" class="card mb-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<input [formControlName]="i" class="form-control" placeholder="Organiser" />
|
||||||
|
<button type="button" class="btn btn-danger btn-sm float-end mt-2" (click)="removeOrganiser(i)">
|
||||||
|
<!-- <i class="bi bi-trash"></i> -->X
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary" (click)="addOrganiser()">
|
||||||
|
<i class="bi bi-plus"></i> Add Organiser
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label class="form-label text-primary">Fees</label>
|
||||||
|
<div formArrayName="fees">
|
||||||
|
<div *ngFor="let fee of fees.controls; let i = index" class="card mb-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div [formGroupName]="i">
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-8">
|
||||||
|
<input formControlName="desc" placeholder="Description" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<input formControlName="cost" placeholder="Cost" type="number" class="form-control" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm float-end mt-2" (click)="removeFee(i)">
|
||||||
|
<!-- <i class="bi bi-trash"></i> -->X
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary" (click)="addFee()">
|
||||||
|
<i class="bi bi-plus"></i> Add Fee
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-success">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EventFormComponent } from './event-form.component';
|
||||||
|
|
||||||
|
describe('EventFormComponent', () => {
|
||||||
|
let component: EventFormComponent;
|
||||||
|
let fixture: ComponentFixture<EventFormComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ EventFormComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(EventFormComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { EventService } from 'src/app/service/event.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-event-form',
|
||||||
|
templateUrl: './event-form.component.html',
|
||||||
|
styleUrls: ['./event-form.component.css']
|
||||||
|
})
|
||||||
|
export class EventFormComponent implements OnInit {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
eventForm: FormGroup;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private eventService: EventService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router
|
||||||
|
|
||||||
|
) {}
|
||||||
|
eventId: number | null;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.eventForm = this.fb.group({
|
||||||
|
code: ['', Validators.required],
|
||||||
|
year: ['', Validators.required],
|
||||||
|
subject: ['', Validators.required],
|
||||||
|
title: ['', Validators.required],
|
||||||
|
subTitle: [''],
|
||||||
|
date: ['', Validators.required],
|
||||||
|
venues: this.fb.array([]),
|
||||||
|
highlights: this.fb.array([]),
|
||||||
|
organisers: this.fb.array([]),
|
||||||
|
fees: this.fb.array([]),
|
||||||
|
phone: ['', Validators.required],
|
||||||
|
email: ['', [Validators.required, Validators.email]],
|
||||||
|
isActive: [true]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route.paramMap.subscribe(params => {
|
||||||
|
const idParam = params.get('id');
|
||||||
|
this.eventId = idParam ? +idParam : null;
|
||||||
|
|
||||||
|
if (this.eventId !== null) {
|
||||||
|
this.loadEvent(this.eventId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadEvent(id: number): void {
|
||||||
|
this.eventService.getEvent(id).subscribe(event => {
|
||||||
|
this.eventForm.patchValue(event);
|
||||||
|
this.setArrayValues('venues', event.venues);
|
||||||
|
this.setArrayValues('highlights', event.highlights);
|
||||||
|
this.setArrayValues('organisers', event.organisers);
|
||||||
|
this.setArrayValues('fees', event.fees);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setArrayValues(controlName: string, values: any[]): void {
|
||||||
|
const array = this.eventForm.get(controlName) as FormArray;
|
||||||
|
values.forEach(value => array.push(this.fb.group(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
get venues(): FormArray {
|
||||||
|
return this.eventForm.get('venues') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
get highlights(): FormArray {
|
||||||
|
return this.eventForm.get('highlights') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
get organisers(): FormArray {
|
||||||
|
return this.eventForm.get('organisers') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fees(): FormArray {
|
||||||
|
return this.eventForm.get('fees') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
addVenue() {
|
||||||
|
this.venues.push(this.fb.group({
|
||||||
|
title: [''],
|
||||||
|
date: [''],
|
||||||
|
address: [''],
|
||||||
|
info: ['']
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeVenue(index: number) {
|
||||||
|
this.venues.removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
addHighlight() {
|
||||||
|
this.highlights.push(this.fb.control(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeHighlight(index: number) {
|
||||||
|
this.highlights.removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
addOrganiser() {
|
||||||
|
this.organisers.push(this.fb.control(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeOrganiser(index: number) {
|
||||||
|
this.organisers.removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
addFee() {
|
||||||
|
this.fees.push(this.fb.group({
|
||||||
|
desc: [''],
|
||||||
|
cost: ['']
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeFee(index: number) {
|
||||||
|
this.fees.removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(): void {
|
||||||
|
if (this.eventForm.valid) {
|
||||||
|
if (this.eventId) {
|
||||||
|
this.eventService.updateEvent(this.eventId, this.eventForm.value).subscribe(() => this.router.navigate(['/events']));
|
||||||
|
} else {
|
||||||
|
this.eventService.createEvent(this.eventForm.value).subscribe(() => this.router.navigate(['/events']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
<app-menu></app-menu>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h2 class="mb-4">Events</h2>
|
||||||
|
<div class="mb-3">
|
||||||
|
<a routerLink="/dashboard/eventForm" class="btn btn-primary">
|
||||||
|
<i class="bi bi-plus"></i> Add New Event
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Code</th>
|
||||||
|
<th>Year</th>
|
||||||
|
<th>Subject</th>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>Subtitle</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Phone</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Active</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let event of events">
|
||||||
|
<td>{{ event.code }}</td>
|
||||||
|
<td>{{ event.year }}</td>
|
||||||
|
<td>{{ event.subject }}</td>
|
||||||
|
<td>{{ event.title }}</td>
|
||||||
|
<td>{{ event.subTitle }}</td>
|
||||||
|
<td>{{ event.date }}</td>
|
||||||
|
<td>{{ event.phone }}</td>
|
||||||
|
<td>{{ event.email }}</td>
|
||||||
|
<td>{{ event.isActive ? 'Yes' : 'No' }}</td>
|
||||||
|
<td>
|
||||||
|
<a [routerLink]="['/eventForm', event.id]" class="btn btn-warning btn-sm">
|
||||||
|
<i class="bi bi-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
<button (click)="deleteEvent(event.id)" class="btn btn-danger btn-sm">
|
||||||
|
<i class="bi bi-trash"></i> Delete
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EventComponent } from './event.component';
|
||||||
|
|
||||||
|
describe('EventComponent', () => {
|
||||||
|
let component: EventComponent;
|
||||||
|
let fixture: ComponentFixture<EventComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ EventComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(EventComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { EventService } from 'src/app/service/event.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-event',
|
||||||
|
templateUrl: './event.component.html',
|
||||||
|
styleUrls: ['./event.component.css']
|
||||||
|
})
|
||||||
|
export class EventComponent implements OnInit {
|
||||||
|
events: any[] = []; // Define the type according to your model
|
||||||
|
|
||||||
|
constructor(private eventService: EventService, private router: Router) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.loadEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadEvents(): void {
|
||||||
|
this.eventService.getEvents().subscribe(events => this.events = events);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteEvent(id: number): void {
|
||||||
|
if (confirm('Are you sure you want to delete this event?')) {
|
||||||
|
// this.eventService.deleteEvent(id).subscribe(() => this.loadEvents());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
/* This ensures that the container takes at least the full viewport height */
|
||||||
|
.min-vh-100 {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optional: Add more custom styles if needed */
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #007bff; /* Bootstrap primary color or customize */
|
||||||
|
font-size: 2.5rem; /* Adjust size as needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
color: #6c757d; /* Bootstrap secondary color or customize */
|
||||||
|
font-size: 1.25rem; /* Adjust size as needed */
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
<app-menu ></app-menu>
|
||||||
|
|
||||||
|
<div class="container d-flex justify-content-center align-items-center min-vh-100">
|
||||||
|
<div class="text-center">
|
||||||
|
<h1>Welcome, CMC!</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { HomeComponent } from './home.component';
|
||||||
|
|
||||||
|
describe('HomeComponent', () => {
|
||||||
|
let component: HomeComponent;
|
||||||
|
let fixture: ComponentFixture<HomeComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ HomeComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(HomeComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-home',
|
||||||
|
templateUrl: './home.component.html',
|
||||||
|
styleUrls: ['./home.component.css']
|
||||||
|
})
|
||||||
|
export class HomeComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {AuthenticationService} from "../../service/authentication.service";
|
|
||||||
import {NotificationService} from "../../service/notification.service";
|
import {NotificationService} from "../../service/notification.service";
|
||||||
import {NotificationType} from "../../notification/notification-type";
|
import {NotificationType} from "../../notification/notification-type";
|
||||||
import {Subscription} from "rxjs";
|
import {Subscription} from "rxjs";
|
||||||
@ -8,6 +8,8 @@ import {UserLogin} from "../../dto/user-login";
|
|||||||
import {HttpErrorResponse, HttpResponse} from "@angular/common/http";
|
import {HttpErrorResponse, HttpResponse} from "@angular/common/http";
|
||||||
import {User} from "../../model/user";
|
import {User} from "../../model/user";
|
||||||
import {HeaderType} from "../../enum/header-type.enum";
|
import {HeaderType} from "../../enum/header-type.enum";
|
||||||
|
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||||
|
;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-login',
|
selector: 'app-login',
|
||||||
@ -26,7 +28,8 @@ export class LoginComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (this.authenticationService.isUserLoggedIn()) {
|
if (this.authenticationService.isUserLoggedIn()) {
|
||||||
this.router.navigate(["/management", "users"]);
|
this.router.navigate(["/dashboard", "userManagement"]);
|
||||||
|
// this.router.navigate(["/management", "users"]);
|
||||||
this.notificationService.notify(NotificationType.INFO, "You are already logged in");
|
this.notificationService.notify(NotificationType.INFO, "You are already logged in");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,7 +46,7 @@ export class LoginComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.authenticationService.addUserToLocalStorage(response.body!);
|
this.authenticationService.addUserToLocalStorage(response.body!);
|
||||||
|
|
||||||
this.router.navigateByUrl('/management/users');
|
this.router.navigateByUrl('/dashboard/home');
|
||||||
this.showLoading = false;
|
this.showLoading = false;
|
||||||
},
|
},
|
||||||
(errorResponse: HttpErrorResponse) => {
|
(errorResponse: HttpErrorResponse) => {
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {Role} from "../../enum/role.enum";
|
import {Role} from "../../enum/role.enum";
|
||||||
import {User} from "../../model/user";
|
import {User} from "../../model/user";
|
||||||
import {AuthenticationService} from "../../service/authentication.service";
|
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||||
|
;
|
||||||
|
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {BehaviorSubject} from "rxjs";
|
import {BehaviorSubject} from "rxjs";
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
<!-- user profile -->
|
<!-- user profile -->
|
||||||
|
|
||||||
|
<app-menu></app-menu>
|
||||||
|
|
||||||
<div class="tab-pane fade show active" id="profile">
|
<div class="tab-pane fade show active" id="profile">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row flex-lg-nowrap">
|
<div class="row flex-lg-nowrap">
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {AuthenticationService} from "../../../service/authentication.service";
|
|
||||||
import {User} from "../../../model/user";
|
import {User} from "../../../model/user";
|
||||||
import {FileUploadStatus} from "../../../model/file-upload.status";
|
import {FileUploadStatus} from "../../../model/file-upload.status";
|
||||||
import {NotificationType} from "../../../notification/notification-type";
|
import {NotificationType} from "../../../notification/notification-type";
|
||||||
import {HttpErrorResponse, HttpEvent, HttpEventType} from "@angular/common/http";
|
import {HttpErrorResponse, HttpEvent, HttpEventType} from "@angular/common/http";
|
||||||
import {SubSink} from "subsink";
|
import {SubSink} from "subsink";
|
||||||
import {UserService} from "../../../service/user.service";
|
import { UserService } from 'src/app/service/user.service';
|
||||||
|
|
||||||
import {NotificationService} from "../../../service/notification.service";
|
import {NotificationService} from "../../../service/notification.service";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
|
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||||
|
;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-profile',
|
selector: 'app-profile',
|
||||||
@ -74,7 +77,7 @@ export class ProfileComponent implements OnInit {
|
|||||||
|
|
||||||
onLogOut() {
|
onLogOut() {
|
||||||
this.authenticationService.logout();
|
this.authenticationService.logout();
|
||||||
this.router.navigate(['/login']);
|
this.router.navigate(['/dashboard/login']);
|
||||||
this.notificationService.notify(NotificationType.SUCCESS, 'You have been successfully logged out');
|
this.notificationService.notify(NotificationType.SUCCESS, 'You have been successfully logged out');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
<!-- change password -->
|
<!-- change password -->
|
||||||
|
|
||||||
|
<app-menu></app-menu>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
<div *ngIf="isAdmin" class="tab-pane fade show active" id="reset-password">
|
<div *ngIf="isAdmin" class="tab-pane fade show active" id="reset-password">
|
||||||
<form #resetPasswordForm="ngForm" (ngSubmit)="onResetPassword(resetPasswordForm)">
|
<form #resetPasswordForm="ngForm" (ngSubmit)="onResetPassword(resetPasswordForm)">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@ -18,3 +23,5 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
import {AuthenticationService} from "../../../service/authentication.service";
|
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||||
|
;
|
||||||
|
|
||||||
import {NgForm} from "@angular/forms";
|
import {NgForm} from "@angular/forms";
|
||||||
import {CustomHttpResponse} from "../../../dto/custom-http-response";
|
import {CustomHttpResponse} from "../../../dto/custom-http-response";
|
||||||
import {NotificationType} from "../../../notification/notification-type";
|
import {NotificationType} from "../../../notification/notification-type";
|
||||||
import {HttpErrorResponse} from "@angular/common/http";
|
import {HttpErrorResponse} from "@angular/common/http";
|
||||||
import {SubSink} from "subsink";
|
import {SubSink} from "subsink";
|
||||||
import {UserService} from "../../../service/user.service";
|
import { UserService } from 'src/app/service/user.service';
|
||||||
|
|
||||||
import {NotificationService} from "../../../service/notification.service";
|
import {NotificationService} from "../../../service/notification.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -53,4 +56,5 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,8 @@ import {Injectable} from '@angular/core';
|
|||||||
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from "@angular/router";
|
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from "@angular/router";
|
||||||
import {User} from "../../../model/user";
|
import {User} from "../../../model/user";
|
||||||
import {Observable} from "rxjs";
|
import {Observable} from "rxjs";
|
||||||
import {UserService} from "../../../service/user.service";
|
import { UserService } from 'src/app/service/user.service';
|
||||||
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {UserService} from "../../../../service/user.service";
|
|
||||||
import {User} from "../../../../model/user";
|
import {User} from "../../../../model/user";
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
import { UserService } from 'src/app/service/user.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-view',
|
selector: 'app-user-view',
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import {User} from "../../../../model/user";
|
|||||||
import {NotificationType} from "../../../../notification/notification-type";
|
import {NotificationType} from "../../../../notification/notification-type";
|
||||||
import {HttpErrorResponse} from "@angular/common/http";
|
import {HttpErrorResponse} from "@angular/common/http";
|
||||||
import {SubSink} from "subsink";
|
import {SubSink} from "subsink";
|
||||||
import {UserService} from "../../../../service/user.service";
|
import { UserService } from 'src/app/service/user.service';
|
||||||
|
|
||||||
import {NotificationService} from "../../../../service/notification.service";
|
import {NotificationService} from "../../../../service/notification.service";
|
||||||
import {AuthenticationService} from "../../../../service/authentication.service";
|
import {AuthenticationService} from "../../../../service/authentication.service";
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
.navbar {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-pills .nav-link {
|
||||||
|
border-radius: 0;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-pills .nav-link.active {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.move-right {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="row mb-2 mt-2 text-center">
|
||||||
|
<div class="col-md-4"></div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h5>Admin Dashboard</h5>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Navbar -->
|
||||||
|
<nav class="navbar navbar-expand-md breadcrumb">
|
||||||
|
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||||
|
<div class="nav nav-pills">
|
||||||
|
<a class="nav-item nav-link ml-1" routerLink="/dashboard/home" routerLinkActive="active" (click)="changeTitle('Home')">
|
||||||
|
<i class="fa fa-home"></i>
|
||||||
|
Home
|
||||||
|
</a>
|
||||||
|
<a class="nav-item nav-link ml-1" routerLink="/dashboard/userManagement" routerLinkActive="active" (click)="changeTitle('Users')">
|
||||||
|
<i class="fa fa-users"></i>
|
||||||
|
Users
|
||||||
|
</a>
|
||||||
|
<a class="nav-item nav-link ml-1" routerLink="/dashboard/professorManagement" routerLinkActive="active" (click)="changeTitle('Professors')">
|
||||||
|
<i class="fa fa-chalkboard-teacher"></i>
|
||||||
|
Professors
|
||||||
|
</a>
|
||||||
|
<a class="nav-item nav-link ml-1" routerLink="/dashboard/blogs" routerLinkActive="active" (click)="changeTitle('Professors')">
|
||||||
|
<i class="fa fa-chalkboard-teacher"></i>
|
||||||
|
Blogs
|
||||||
|
</a>
|
||||||
|
<a class="nav-item nav-link ml-1" routerLink="/dashboard/events" routerLinkActive="active" (click)="changeTitle('Professors')">
|
||||||
|
<i class="fa fa-chalkboard-teacher"></i>
|
||||||
|
Events
|
||||||
|
</a>
|
||||||
|
<a class="nav-item nav-link ml-3" routerLink="/dashboard/settings" routerLinkActive="active" (click)="changeTitle('Settings')">
|
||||||
|
<i class="fa fa-cogs"></i>
|
||||||
|
Settings
|
||||||
|
</a>
|
||||||
|
<a class="nav-item nav-link move-right mr-3" routerLink="/dashboard/profile" routerLinkActive="active" (click)="changeTitle('Profile')">
|
||||||
|
Welcome, {{ loggedInUser.firstName }} {{ loggedInUser.lastName }}
|
||||||
|
<i class="fa fa-user"></i>
|
||||||
|
</a>
|
||||||
|
<!-- <a class="nav-item nav-link ml-1" (click)="logout()">
|
||||||
|
<i class="fa fa-sign-out-alt"></i>
|
||||||
|
Logout
|
||||||
|
</a> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MenuComponent } from './menu.component';
|
||||||
|
|
||||||
|
describe('MenuComponent', () => {
|
||||||
|
let component: MenuComponent;
|
||||||
|
let fixture: ComponentFixture<MenuComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ MenuComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MenuComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||||
|
;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-menu',
|
||||||
|
templateUrl: './menu.component.html',
|
||||||
|
styleUrls: ['./menu.component.css']
|
||||||
|
})
|
||||||
|
export class MenuComponent implements OnInit {
|
||||||
|
titleAction$: Observable<string> | undefined;
|
||||||
|
titleChanged = new EventEmitter<string>();
|
||||||
|
|
||||||
|
loggedInUser: any = {};
|
||||||
|
|
||||||
|
constructor(private authenticationService: AuthenticationService) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Fetch user details from authentication service
|
||||||
|
this.loggedInUser = this.authenticationService.getUserFromLocalStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
logout(){
|
||||||
|
this.authenticationService.logout()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
changeTitle(title: string): void {
|
||||||
|
this.titleChanged.emit(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,566 @@
|
|||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<app-menu ></app-menu>
|
||||||
|
|
||||||
|
<div *ngIf="false" class="row mb-2 mt-2 text-center">
|
||||||
|
<div class="col-md-4"></div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h5>Professor Management Portal</h5>
|
||||||
|
<small *ngIf="titleAction$ | async as title">{{ title }}</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Navbar -->
|
||||||
|
<nav *ngIf="false" class="navbar navbar-expand-md breadcrumb">
|
||||||
|
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||||
|
<div class="nav nav-pills">
|
||||||
|
<a
|
||||||
|
class="nav-item nav-link active ml-1"
|
||||||
|
(click)="changeTitle('Professors')"
|
||||||
|
data-bs-toggle="tab"
|
||||||
|
href="#professors"
|
||||||
|
>
|
||||||
|
<i class="fa fa-chalkboard-teacher"></i>
|
||||||
|
Professors
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="nav-item nav-link ml-3"
|
||||||
|
(click)="changeTitle('Settings')"
|
||||||
|
data-bs-toggle="tab"
|
||||||
|
href="#settings"
|
||||||
|
>
|
||||||
|
<i class="fa fa-cogs"></i>
|
||||||
|
Settings
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="nav-item nav-link move-right mr-3"
|
||||||
|
(click)="changeTitle('Profile')"
|
||||||
|
data-bs-toggle="tab"
|
||||||
|
href="#profile"
|
||||||
|
>
|
||||||
|
Welcome, {{ loggedInUser.firstName }} {{ loggedInUser.lastName }}
|
||||||
|
<i class="fa fa-user"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div class="tab-content mt-3" id="myTabContent">
|
||||||
|
<!-- Professor Table -->
|
||||||
|
<div class="tab-pane fade show active" id="professors">
|
||||||
|
<div class="mb-3 float-end">
|
||||||
|
<div class="btn-group mr-2">
|
||||||
|
<form class="form-inline my-2 my-lg-0 justify-content-center">
|
||||||
|
<input
|
||||||
|
name="searchTerm"
|
||||||
|
#searchTerm="ngModel"
|
||||||
|
class="form-control mr-sm-2"
|
||||||
|
type="search"
|
||||||
|
placeholder="Search professors..."
|
||||||
|
ngModel
|
||||||
|
(ngModelChange)="searchProfessors(searchTerm.value)"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
<button
|
||||||
|
*ngIf="isManager"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-info"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#addProfessorModal"
|
||||||
|
>
|
||||||
|
<i class="fa fa-plus"></i> New Professor
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-info"
|
||||||
|
(click)="getProfessors(true)"
|
||||||
|
>
|
||||||
|
<i class="fas fa-sync" [ngClass]="{ 'fa-spin': refreshing }"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead class="table-borderless">
|
||||||
|
<tr class="text-center">
|
||||||
|
<th>Photo</th>
|
||||||
|
<th>Professor ID</th>
|
||||||
|
<th>First Name</th>
|
||||||
|
<th>Last Name</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="text-center" *ngFor="let professor of professors">
|
||||||
|
<td (click)="onSelectProfessor(professor)">
|
||||||
|
<img
|
||||||
|
height="40"
|
||||||
|
width="40"
|
||||||
|
src="{{ professor?.profileImageUrl }}"
|
||||||
|
class="rounded-circle img-fluid img-thumbnail"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td (click)="onSelectProfessor(professor)">
|
||||||
|
{{ professor?.professorId }}
|
||||||
|
</td>
|
||||||
|
<td (click)="onSelectProfessor(professor)">
|
||||||
|
{{ professor?.firstName }}
|
||||||
|
</td>
|
||||||
|
<td (click)="onSelectProfessor(professor)">
|
||||||
|
{{ professor?.lastName }}
|
||||||
|
</td>
|
||||||
|
<td (click)="onSelectProfessor(professor)">
|
||||||
|
{{ professor?.email }}
|
||||||
|
</td>
|
||||||
|
<td (click)="onSelectProfessor(professor)">
|
||||||
|
<span
|
||||||
|
class="badge"
|
||||||
|
[ngClass]="{
|
||||||
|
'bg-success': professor?.status === WorkingStatus.ACTIVE,
|
||||||
|
'bg-warning': professor?.status === WorkingStatus.ON_LEAVE,
|
||||||
|
'bg-danger': professor?.status === WorkingStatus.RETIRED
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ professor?.status }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-info"
|
||||||
|
(click)="onEditProfessor(professor)"
|
||||||
|
>
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
*ngIf="isAdmin"
|
||||||
|
class="btn btn-outline-danger"
|
||||||
|
(click)="onDeleteProfessor(professor)"
|
||||||
|
>
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
[hidden]="true"
|
||||||
|
type="button"
|
||||||
|
id="openProfessorInfo"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#viewProfessorModal"
|
||||||
|
></button>
|
||||||
|
<button
|
||||||
|
[hidden]="true"
|
||||||
|
type="button"
|
||||||
|
id="openProfessorEdit"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#editProfessorModal"
|
||||||
|
></button>
|
||||||
|
|
||||||
|
<!-- Settings -->
|
||||||
|
<div class="tab-pane fade" id="settings">
|
||||||
|
<p>Settings content goes here...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Profile -->
|
||||||
|
<div class="tab-pane fade" id="profile">
|
||||||
|
<!-- Profile content goes here... -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Modals -->
|
||||||
|
<!-- Add Professor Modal -->
|
||||||
|
<div
|
||||||
|
*ngIf="isManager"
|
||||||
|
class="modal fade"
|
||||||
|
id="addProfessorModal"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-labelledby="addProfessorModalLabel"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Add New Professor</h5>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="close"
|
||||||
|
data-bs-dismiss="modal"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form
|
||||||
|
#newProfessorForm="ngForm"
|
||||||
|
(ngSubmit)="onAddNewProfessor(newProfessorForm)"
|
||||||
|
>
|
||||||
|
<!-- Form Fields for Adding New Professor -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="firstName">First Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="firstName"
|
||||||
|
name="firstName"
|
||||||
|
class="form-control"
|
||||||
|
ngModel
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="lastName">Last Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="lastName"
|
||||||
|
name="lastName"
|
||||||
|
class="form-control"
|
||||||
|
ngModel
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
class="form-control"
|
||||||
|
ngModel
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="status">Status</label>
|
||||||
|
<select
|
||||||
|
id="status"
|
||||||
|
name="status"
|
||||||
|
class="form-control"
|
||||||
|
ngModel
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option [value]="WorkingStatus.ACTIVE">Active</option>
|
||||||
|
<option [value]="WorkingStatus.ON_LEAVE">On Leave</option>
|
||||||
|
<option [value]="WorkingStatus.RETIRED">Retired</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="form-group">
|
||||||
|
<label for="profileImageUrl">Profile Image URL</label>
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
id="profileImageUrl"
|
||||||
|
name="profileImageUrl"
|
||||||
|
class="form-control"
|
||||||
|
ngModel
|
||||||
|
/>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div class="input-group mb-2">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text">Profile Picture </span>
|
||||||
|
</div>
|
||||||
|
<div class="custom-file">
|
||||||
|
<input type="file" accept="image/*" name="profileImage"
|
||||||
|
(change)="onProfileImageChange($any($event).target.files)"
|
||||||
|
class="custom-file-input">
|
||||||
|
<label class="custom-file-label">
|
||||||
|
<span>{{profileImageFileName ? profileImageFileName : 'Choose File'}}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary"
|
||||||
|
[disabled]="newProfessorForm.invalid"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Edit Professor Modal -->
|
||||||
|
<div
|
||||||
|
class="modal fade"
|
||||||
|
id="editProfessorModal"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-labelledby="editProfessorModalLabel"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Edit Professor</h5>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="close"
|
||||||
|
data-bs-dismiss="modal"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form
|
||||||
|
#editProfessorForm="ngForm"
|
||||||
|
(ngSubmit)="onUpdateProfessor(editProfessorForm)"
|
||||||
|
>
|
||||||
|
<!-- Form Fields for Editing Professor -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="editFirstName">First Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="editFirstName"
|
||||||
|
name="firstName"
|
||||||
|
class="form-control"
|
||||||
|
[(ngModel)]="selectedProfessor.firstName"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="editLastName">Last Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="editLastName"
|
||||||
|
name="lastName"
|
||||||
|
class="form-control"
|
||||||
|
[(ngModel)]="selectedProfessor.lastName"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="editEmail">Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="editEmail"
|
||||||
|
name="email"
|
||||||
|
class="form-control"
|
||||||
|
[(ngModel)]="selectedProfessor.email"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="editStatus">Status</label>
|
||||||
|
<select
|
||||||
|
id="editStatus"
|
||||||
|
name="status"
|
||||||
|
class="form-control"
|
||||||
|
[(ngModel)]="selectedProfessor.status"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option [value]="WorkingStatus.ACTIVE">Active</option>
|
||||||
|
<option [value]="WorkingStatus.ON_LEAVE">On Leave</option>
|
||||||
|
<option [value]="WorkingStatus.RETIRED">Retired</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="form-group">
|
||||||
|
<label for="editProfileImageUrl">Profile Image URL</label>
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
id="editProfileImageUrl"
|
||||||
|
name="profileImageUrl"
|
||||||
|
class="form-control"
|
||||||
|
[(ngModel)]="selectedProfessor.profileImageUrl"
|
||||||
|
/>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div class="input-group mb-2">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text">Profile Picture </span>
|
||||||
|
</div>
|
||||||
|
<div class="custom-file">
|
||||||
|
<input type="file" accept="image/*" name="profileImage"
|
||||||
|
(change)="onProfileImageChange($any($event).target.files)"
|
||||||
|
class="custom-file-input" [disabled]="!isManager">
|
||||||
|
<label class="custom-file-label">
|
||||||
|
<span>{{profileImageFileName ? profileImageFileName : 'Choose File'}}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary"
|
||||||
|
[disabled]="editProfessorForm.invalid"
|
||||||
|
>
|
||||||
|
Save changes
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- profile image change form -->
|
||||||
|
<form enctype="multipart/form-data" style="display:none;">
|
||||||
|
<input type="file"
|
||||||
|
(change)="onProfileImageChange($any($event).target.files); onUpdateProfileImage()"
|
||||||
|
name="profile-image-input" id="profile-image-input" placeholder="file" accept="image/*"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal User Info -->
|
||||||
|
<div
|
||||||
|
*ngIf="selectedProfessor"
|
||||||
|
class="modal fade bd-example-modal-lg"
|
||||||
|
id="viewProfessorModal"
|
||||||
|
tabindex="-1"
|
||||||
|
role="dialog"
|
||||||
|
aria-labelledby=""
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title text-center" id="exampleModalLongTitle">
|
||||||
|
{{ selectedProfessor.firstName || "First Name" }}
|
||||||
|
{{ selectedProfessor.lastName || "Last Name" }}
|
||||||
|
</h5>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="close"
|
||||||
|
data-bs-dismiss="modal"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-sm-auto">
|
||||||
|
<div class="mx-auto" style="width: 120px">
|
||||||
|
<div
|
||||||
|
class="d-flex justify-content-center align-items-center rounded"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="rounded"
|
||||||
|
height="120"
|
||||||
|
width="120"
|
||||||
|
[src]="
|
||||||
|
selectedProfessor.profileImageUrl ||
|
||||||
|
'default-image-url'
|
||||||
|
"
|
||||||
|
alt="{{ selectedProfessor.firstName || 'Professor' }}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="col d-flex flex-column flex-sm-row justify-content-between"
|
||||||
|
>
|
||||||
|
<div class="text-center text-sm-left mb-sm-0">
|
||||||
|
<h6 class="pt-sm-2 pb-1 mb-0 text-nowrap">
|
||||||
|
{{ selectedProfessor.firstName || "First Name" }}
|
||||||
|
{{ selectedProfessor.lastName || "Last Name" }}
|
||||||
|
</h6>
|
||||||
|
<p class="mb-1">
|
||||||
|
{{ selectedProfessor.email || "Email not available" }}
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
Status:
|
||||||
|
<span
|
||||||
|
class="badge"
|
||||||
|
[ngClass]="{
|
||||||
|
'bg-success': selectedProfessor.status === 'ACTIVE',
|
||||||
|
'bg-warning':
|
||||||
|
selectedProfessor.status === 'ON_LEAVE',
|
||||||
|
'bg-danger': selectedProfessor.status === 'RETIRED'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
selectedProfessor.status === "ACTIVE"
|
||||||
|
? "Active"
|
||||||
|
: selectedProfessor.status === "ON_LEAVE"
|
||||||
|
? "On Leave"
|
||||||
|
: "Retired"
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
*ngIf="selectedProfessor.joinDate"
|
||||||
|
class="text-muted"
|
||||||
|
>
|
||||||
|
<small
|
||||||
|
>Joined
|
||||||
|
{{
|
||||||
|
selectedProfessor.joinDate | date : "medium"
|
||||||
|
}}</small
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center text-sm-right">
|
||||||
|
<div class="text-muted">
|
||||||
|
<small
|
||||||
|
>Department:
|
||||||
|
{{
|
||||||
|
selectedProfessor.department || "Not provided"
|
||||||
|
}}</small
|
||||||
|
><br />
|
||||||
|
<small
|
||||||
|
>Position:
|
||||||
|
{{
|
||||||
|
selectedProfessor.position || "Not provided"
|
||||||
|
}}</small
|
||||||
|
><br />
|
||||||
|
<small
|
||||||
|
>Office Location:
|
||||||
|
{{
|
||||||
|
selectedProfessor.officeLocation || "Not provided"
|
||||||
|
}}</small
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<i class="fa fa-id-badge float-end"></i
|
||||||
|
>{{ selectedProfessor.professorId || "ID not available" }}
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<i class="fa fa-envelope float-end"></i
|
||||||
|
>{{ selectedProfessor.email || "Email not available" }}
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<i class="fas fa-shield-alt float-end"></i
|
||||||
|
>{{ selectedProfessor.status || "Status not available" }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
data-bs-dismiss="modal"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ProfessorComponent } from './professor.component';
|
||||||
|
|
||||||
|
describe('ProfessorComponent', () => {
|
||||||
|
let component: ProfessorComponent;
|
||||||
|
let fixture: ComponentFixture<ProfessorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ ProfessorComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProfessorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,264 @@
|
|||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { Professor } from '../../model/Professor';
|
||||||
|
import { NotificationService } from '../../service/notification.service';
|
||||||
|
import { NotificationType } from '../../notification/notification-type';
|
||||||
|
import { HttpErrorResponse, HttpEvent, HttpEventType } from '@angular/common/http';
|
||||||
|
import { NgForm } from '@angular/forms';
|
||||||
|
import { CustomHttpResponse } from '../../dto/custom-http-response';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { FileUploadStatus } from 'src/app/model/file-upload.status';
|
||||||
|
import { SubSink } from 'subsink';
|
||||||
|
import { WorkingStatus } from "../../enum/WorkingStatus";
|
||||||
|
import { User } from 'src/app/model/user';
|
||||||
|
;
|
||||||
|
import { Role } from 'src/app/enum/role.enum';
|
||||||
|
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||||
|
import { ProfessorService } from 'src/app/service/professor.service';
|
||||||
|
;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-professor',
|
||||||
|
templateUrl: './professor.component.html',
|
||||||
|
styleUrls: ['./professor.component.css']
|
||||||
|
})
|
||||||
|
export class ProfessorComponent implements OnInit, OnDestroy {
|
||||||
|
WorkingStatus = WorkingStatus; // Declare enum here
|
||||||
|
|
||||||
|
private titleSubject = new BehaviorSubject<string>('Professors');
|
||||||
|
public titleAction$ = this.titleSubject.asObservable();
|
||||||
|
public loggedInUser: User;
|
||||||
|
|
||||||
|
|
||||||
|
public professors: Professor[] = [];
|
||||||
|
public loggedInProfessor: Professor;
|
||||||
|
public refreshing: boolean;
|
||||||
|
private subs = new SubSink();
|
||||||
|
selectedProfessor: Professor = {
|
||||||
|
professorId: '', // Initialize with empty string or appropriate default
|
||||||
|
firstName: '', // Initialize with empty string or appropriate default
|
||||||
|
lastName: '', // Initialize with empty string or appropriate default
|
||||||
|
email: '', // Initialize with empty string or appropriate default
|
||||||
|
profileImageUrl: '', // Optional property, can be initialized with empty string or `null`
|
||||||
|
status: WorkingStatus.ACTIVE, // Initialize with a default value from your enum
|
||||||
|
department: '', // Initialize with empty string or appropriate default
|
||||||
|
position: '', // Initialize with empty string or appropriate default
|
||||||
|
officeLocation: '', // Initialize with empty string or appropriate default
|
||||||
|
joinDate: new Date() // Initialize with the current date or an appropriate default
|
||||||
|
};
|
||||||
|
public profileImageFileName: string | null;
|
||||||
|
public profileImage: File | null;
|
||||||
|
// public editProfessor: Professor = new Professor();
|
||||||
|
public fileUploadStatus: FileUploadStatus = new FileUploadStatus();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private professorService: ProfessorService,
|
||||||
|
private notificationService: NotificationService,
|
||||||
|
private router: Router,
|
||||||
|
private authenticationService: AuthenticationService,
|
||||||
|
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
this.getProfessors(true);
|
||||||
|
this.loggedInUser = this.authenticationService.getUserFromLocalStorage();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTitleChange(title: string): void {
|
||||||
|
this.titleSubject.next(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public changeTitle(title: string): void {
|
||||||
|
this.titleSubject.next(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getProfessors(showNotification: boolean): void {
|
||||||
|
this.refreshing = true;
|
||||||
|
this.subs.sink = this.professorService.getAllProfessors().subscribe(
|
||||||
|
professorsPage => {
|
||||||
|
this.professors = professorsPage.content;
|
||||||
|
this.professorService.addProfessorsToLocalStorage(this.professors);
|
||||||
|
if (showNotification) {
|
||||||
|
this.notificationService.notify(NotificationType.SUCCESS, `${this.professors.length} professors loaded successfully`);
|
||||||
|
}
|
||||||
|
this.refreshing = false;
|
||||||
|
},
|
||||||
|
(errorResponse: HttpErrorResponse) => {
|
||||||
|
this.sendErrorNotification(errorResponse.error.message);
|
||||||
|
this.refreshing = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSelectProfessor(selectedProfessor: Professor): void {
|
||||||
|
this.selectedProfessor = selectedProfessor;
|
||||||
|
this.clickButton('openProfessorInfo');
|
||||||
|
}
|
||||||
|
|
||||||
|
public onProfileImageChange(fileList: FileList): void {
|
||||||
|
this.profileImageFileName = fileList[0].name;
|
||||||
|
this.profileImage = fileList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendErrorNotification(message: string): void {
|
||||||
|
this.sendNotification(NotificationType.ERROR, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendNotification(type: NotificationType, message: string): void {
|
||||||
|
this.notificationService.notify(type, message ? message : 'An error occurred. Please try again');
|
||||||
|
}
|
||||||
|
|
||||||
|
public onAddNewProfessor(professorForm: NgForm): void {
|
||||||
|
const formData = this.professorService.createProfessorFormData(professorForm.value, this.profileImage);
|
||||||
|
this.subs.sink = this.professorService.addProfessor(formData).subscribe(
|
||||||
|
(professor: Professor) => {
|
||||||
|
this.clickButton('new-professor-close');
|
||||||
|
this.getProfessors(false);
|
||||||
|
this.invalidateVariables();
|
||||||
|
professorForm.reset();
|
||||||
|
this.notificationService.notify(NotificationType.SUCCESS, `Professor ${professor.firstName} added successfully`);
|
||||||
|
},
|
||||||
|
(errorResponse: HttpErrorResponse) => {
|
||||||
|
this.sendErrorNotification(errorResponse.error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private invalidateVariables(): void {
|
||||||
|
this.profileImage = null;
|
||||||
|
this.profileImageFileName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveNewProfessor(): void {
|
||||||
|
this.clickButton('new-professor-save');
|
||||||
|
}
|
||||||
|
|
||||||
|
public searchProfessors(searchTerm: string): void {
|
||||||
|
if (!searchTerm) {
|
||||||
|
this.professors = this.professorService.getProfessorsFromLocalStorage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchProfessors: Professor[] = [];
|
||||||
|
searchTerm = searchTerm.toLowerCase();
|
||||||
|
for (const professor of this.professorService.getProfessorsFromLocalStorage()) {
|
||||||
|
if (
|
||||||
|
professor.firstName.toLowerCase().includes(searchTerm) ||
|
||||||
|
professor.lastName.toLowerCase().includes(searchTerm) ||
|
||||||
|
professor.email.toLowerCase().includes(searchTerm) ||
|
||||||
|
professor.department.toLowerCase().includes(searchTerm)
|
||||||
|
) {
|
||||||
|
matchProfessors.push(professor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.professors = matchProfessors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private clickButton(buttonId: string): void {
|
||||||
|
document.getElementById(buttonId)?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onEditProfessor(professor: Professor): void {
|
||||||
|
// this.editProfessor = professor;
|
||||||
|
this.selectedProfessor = professor;
|
||||||
|
this.clickButton('openProfessorEdit');
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateProfessor(form: NgForm) {
|
||||||
|
if (form.invalid) {
|
||||||
|
// Handle form validation errors if needed
|
||||||
|
this.notificationService.notify(NotificationType.ERROR, 'Please fill out all required fields.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = this.professorService.createProfessorFormData(this.selectedProfessor, this.profileImage);
|
||||||
|
|
||||||
|
this.subs.add(this.professorService.updateProfessor(this.selectedProfessor.professorId, formData).subscribe(
|
||||||
|
(professor: Professor) => {
|
||||||
|
this.clickButton('closeEditProfessorButton');
|
||||||
|
this.getProfessors(false);
|
||||||
|
this.invalidateVariables();
|
||||||
|
this.notificationService.notify(NotificationType.SUCCESS, `Professor ${professor.firstName} updated successfully`);
|
||||||
|
},
|
||||||
|
(errorResponse: HttpErrorResponse) => {
|
||||||
|
this.sendErrorNotification(errorResponse.error.message);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public onDeleteProfessor(professor: Professor): void {
|
||||||
|
this.subs.sink = this.professorService.deleteProfessor(professor.professorId).subscribe(
|
||||||
|
(response: CustomHttpResponse) => {
|
||||||
|
this.getProfessors(false);
|
||||||
|
this.invalidateVariables();
|
||||||
|
this.notificationService.notify(NotificationType.SUCCESS, response.message);
|
||||||
|
},
|
||||||
|
(errorResponse: HttpErrorResponse) => {
|
||||||
|
this.sendErrorNotification(errorResponse.error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateProfileImage(): void {
|
||||||
|
this.clickButton('profile-image-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
public onUpdateProfileImage(): void {
|
||||||
|
if (!this.profileImage) return;
|
||||||
|
this.refreshing = true;
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("profileImage", this.profileImage);
|
||||||
|
let professor = this.professorService.getSelectedProfessor();
|
||||||
|
this.subs.sink = this.professorService.updateProfileImage(professor.professorId, formData).subscribe(
|
||||||
|
(event: HttpEvent<any>) => {
|
||||||
|
this.reportUploadProgress(event);
|
||||||
|
},
|
||||||
|
(errorResponse: HttpErrorResponse) => {
|
||||||
|
this.sendErrorNotification(errorResponse.error.message);
|
||||||
|
this.refreshing = false;
|
||||||
|
this.fileUploadStatus.status = 'error';
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.refreshing = false;
|
||||||
|
this.getProfessors(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private reportUploadProgress(event: HttpEvent<any>): void {
|
||||||
|
switch (event.type) {
|
||||||
|
case HttpEventType.UploadProgress:
|
||||||
|
this.fileUploadStatus.percentage = Math.round(100 * event.loaded / event.total!);
|
||||||
|
this.fileUploadStatus.status = 'progress';
|
||||||
|
break;
|
||||||
|
case HttpEventType.Response:
|
||||||
|
if (event.status === 200) {
|
||||||
|
// For browser to fetch image when updating (because name left the same)
|
||||||
|
this.loggedInProfessor.profileImageUrl = `${event.body.profileImageUrl}?time=${new Date().getTime()}`;
|
||||||
|
this.notificationService.notify(NotificationType.SUCCESS, `${event.body.firstName}'s image updated successfully`);
|
||||||
|
this.fileUploadStatus.status = 'done';
|
||||||
|
} else {
|
||||||
|
this.sendErrorNotification('Unable to upload image. Please try again');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.fileUploadStatus.status = 'default';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isAdmin(): boolean {
|
||||||
|
return this.loggedInUser.role === Role.ADMIN || this.loggedInUser.role === Role.SUPER_ADMIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isManager(): boolean {
|
||||||
|
return this.isAdmin || this.loggedInUser.role === Role.MANAGER;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -50,6 +50,7 @@
|
|||||||
*ngIf="emailInput.invalid && emailInput.touched">Please enter an email.</span>
|
*ngIf="emailInput.invalid && emailInput.touched">Please enter an email.</span>
|
||||||
|
|
||||||
<div class="d-flex justify-content-center mt-3 login_container">
|
<div class="d-flex justify-content-center mt-3 login_container">
|
||||||
|
{{registerForm.invalid || showLoading}}
|
||||||
<button type="submit" [disabled]="registerForm.invalid || showLoading" name="button" class="btn login_btn">
|
<button type="submit" [disabled]="registerForm.invalid || showLoading" name="button" class="btn login_btn">
|
||||||
<i class="fas fa-spinner fa-spin" *ngIf="showLoading"></i>
|
<i class="fas fa-spinner fa-spin" *ngIf="showLoading"></i>
|
||||||
<span *ngIf="showLoading">{{showLoading ? 'Loading...' : 'Register'}}</span>
|
<span *ngIf="showLoading">{{showLoading ? 'Loading...' : 'Register'}}</span>
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
import {User} from "../../model/user";
|
import {User} from "../../model/user";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {AuthenticationService} from "../../service/authentication.service";
|
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||||
|
;
|
||||||
|
|
||||||
import {NotificationService} from "../../service/notification.service";
|
import {NotificationService} from "../../service/notification.service";
|
||||||
import {Subscription} from "rxjs";
|
import {Subscription} from "rxjs";
|
||||||
import {HttpErrorResponse} from "@angular/common/http";
|
import {HttpErrorResponse} from "@angular/common/http";
|
||||||
@ -24,7 +26,7 @@ export class RegisterComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (this.authenticationService.isUserLoggedIn()) {
|
if (this.authenticationService.isUserLoggedIn()) {
|
||||||
this.router.navigateByUrl("/user/management");
|
this.router.navigateByUrl("/dashboard/userManagement");
|
||||||
this.notificationService.notify(NotificationType.INFO, "You are already logged in");
|
this.notificationService.notify(NotificationType.INFO, "You are already logged in");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,7 +39,7 @@ export class RegisterComponent implements OnInit, OnDestroy {
|
|||||||
.subscribe(user => {
|
.subscribe(user => {
|
||||||
this.notificationService.notify(NotificationType.SUCCESS, `A new account was created for ${user.firstName}.
|
this.notificationService.notify(NotificationType.SUCCESS, `A new account was created for ${user.firstName}.
|
||||||
Please check your email for password to log in`);
|
Please check your email for password to log in`);
|
||||||
this.router.navigateByUrl('/login');
|
this.router.navigateByUrl('/dashboard/login');
|
||||||
this.showLoading = false;
|
this.showLoading = false;
|
||||||
},
|
},
|
||||||
(errorResponse: HttpErrorResponse) => {
|
(errorResponse: HttpErrorResponse) => {
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-2 mt-2 text-center">
|
|
||||||
|
<app-menu></app-menu>
|
||||||
|
|
||||||
|
|
||||||
|
<div *ngIf="false" class="row mb-2 mt-2 text-center">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
@ -11,13 +15,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- nav bar -->
|
<!-- nav bar -->
|
||||||
<nav class="navbar navbar-expand-md breadcrumb">
|
<nav *ngIf="false" class="navbar navbar-expand-md breadcrumb">
|
||||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||||
<div class="nav nav-pills">
|
<div class="nav nav-pills">
|
||||||
<a class="nav-item nav-link active ml-1" (click)="changeTitle('Users')" data-bs-toggle="tab" href="#users">
|
<a class="nav-item nav-link active ml-1" (click)="changeTitle('Users')" data-bs-toggle="tab" href="#users">
|
||||||
<i class="fa fa-users"></i>
|
<i class="fa fa-users"></i>
|
||||||
Users
|
Users
|
||||||
</a>
|
</a>
|
||||||
|
<a class="nav-item nav-link active ml-1" (click)="changeTitle('Professors')" [routerLink]="['/dashboard/professorManagement']" >
|
||||||
|
<i class="fa fa-users"></i>
|
||||||
|
Professors
|
||||||
|
</a>
|
||||||
|
|
||||||
<!-- Possible attacks-->
|
<!-- Possible attacks-->
|
||||||
<!-- document.getElementsByClassName('nav-item nav-link ml-3')[0].click()-->
|
<!-- document.getElementsByClassName('nav-item nav-link ml-3')[0].click()-->
|
||||||
@ -26,7 +34,7 @@
|
|||||||
<!-- document.getElementsByClassName('nav-item nav-link ml-3')[0].hidden=false-->
|
<!-- document.getElementsByClassName('nav-item nav-link ml-3')[0].hidden=false-->
|
||||||
<!-- document.getElementById('reset-password').hidden=false-->
|
<!-- document.getElementById('reset-password').hidden=false-->
|
||||||
|
|
||||||
<a *ngIf="isAdmin" class="nav-item nav-link ml-3" (click)="changeTitle('Settings')" data-bs-toggle="tab"
|
<a class="nav-item nav-link ml-3" (click)="changeTitle('Settings')" data-bs-toggle="tab"
|
||||||
href="#reset-password">
|
href="#reset-password">
|
||||||
<i class="fa fa-cogs"></i>
|
<i class="fa fa-cogs"></i>
|
||||||
Settings
|
Settings
|
||||||
@ -433,6 +441,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset class="form-group">
|
<fieldset class="form-group">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<label class="form-check-label">
|
<label class="form-check-label">
|
||||||
|
|||||||
@ -4,7 +4,9 @@ import {UserComponent} from './user.component';
|
|||||||
import {HttpClient} from "@angular/common/http";
|
import {HttpClient} from "@angular/common/http";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {NotificationService} from "../../service/notification.service";
|
import {NotificationService} from "../../service/notification.service";
|
||||||
import {AuthenticationService} from "../../service/authentication.service";
|
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
describe('UserComponent', () => {
|
describe('UserComponent', () => {
|
||||||
let component: UserComponent;
|
let component: UserComponent;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||||
import {BehaviorSubject} from "rxjs";
|
import {BehaviorSubject} from "rxjs";
|
||||||
import {User} from "../../model/user";
|
import {User} from "../../model/user";
|
||||||
import {UserService} from "../../service/user.service";
|
import {UserService} from "../../service/user.service";
|
||||||
@ -7,7 +7,11 @@ import {NotificationType} from "../../notification/notification-type";
|
|||||||
import {HttpErrorResponse, HttpEvent, HttpEventType} from "@angular/common/http";
|
import {HttpErrorResponse, HttpEvent, HttpEventType} from "@angular/common/http";
|
||||||
import {NgForm} from "@angular/forms";
|
import {NgForm} from "@angular/forms";
|
||||||
import {CustomHttpResponse} from "../../dto/custom-http-response";
|
import {CustomHttpResponse} from "../../dto/custom-http-response";
|
||||||
import {AuthenticationService} from "../../service/authentication.service";
|
import { AuthenticationService } from 'src/app/service/authentication.service';
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {FileUploadStatus} from "../../model/file-upload.status";
|
import {FileUploadStatus} from "../../model/file-upload.status";
|
||||||
import {Role} from "../../enum/role.enum";
|
import {Role} from "../../enum/role.enum";
|
||||||
@ -23,6 +27,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
private titleSubject = new BehaviorSubject<string>('Users');
|
private titleSubject = new BehaviorSubject<string>('Users');
|
||||||
public titleAction$ = this.titleSubject.asObservable();
|
public titleAction$ = this.titleSubject.asObservable();
|
||||||
|
|
||||||
|
|
||||||
public users: User[] = [];
|
public users: User[] = [];
|
||||||
|
|
||||||
public loggedInUser: User;
|
public loggedInUser: User;
|
||||||
@ -55,6 +60,11 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
this.titleSubject.next(title);
|
this.titleSubject.next(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleTitleChange(title: string): void {
|
||||||
|
this.titleSubject.next(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public getUsers(showNotification: boolean) {
|
public getUsers(showNotification: boolean) {
|
||||||
this.refreshing = true;
|
this.refreshing = true;
|
||||||
|
|
||||||
@ -228,7 +238,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
onLogOut() {
|
onLogOut() {
|
||||||
this.authenticationService.logout();
|
this.authenticationService.logout();
|
||||||
this.router.navigate(['/login']);
|
this.router.navigate(['/dashboard/login']);
|
||||||
this.sendNotification(NotificationType.SUCCESS, 'You have been successfully logged out');
|
this.sendNotification(NotificationType.SUCCESS, 'You have been successfully logged out');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,6 +292,20 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ViewChild('addUserModal') addUserModal!: ElementRef;
|
||||||
|
|
||||||
|
openAddUserModal() {
|
||||||
|
console.log("clicked")
|
||||||
|
if (this.addUserModal) {
|
||||||
|
|
||||||
|
// const modalInstance = new Modal(this.addUserModal.nativeElement);
|
||||||
|
// modalInstance.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public get isAdmin(): boolean {
|
public get isAdmin(): boolean {
|
||||||
return this.loggedInUser.role === Role.ADMIN || this.loggedInUser.role === Role.SUPER_ADMIN;
|
return this.loggedInUser.role === Role.ADMIN || this.loggedInUser.role === Role.SUPER_ADMIN;
|
||||||
}
|
}
|
||||||
|
|||||||
16
support-portal-frontend/src/app/data.service.spec.ts
Normal file
16
support-portal-frontend/src/app/data.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DataService } from './data.service';
|
||||||
|
|
||||||
|
describe('DataService', () => {
|
||||||
|
let service: DataService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(DataService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
389
support-portal-frontend/src/app/data.service.ts
Normal file
389
support-portal-frontend/src/app/data.service.ts
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { HttpClient, HttpHeaders } from "@angular/common/http";
|
||||||
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: "root",
|
||||||
|
})
|
||||||
|
export class DataService {
|
||||||
|
private url = "https://api.wrdpwd.com/soap/callWebService";
|
||||||
|
// private url =
|
||||||
|
// "https://clin.CMCVellore.ac.in/newconference/ConferencePay.asmx";
|
||||||
|
prod_cred = {
|
||||||
|
userName: "UMRCETS",
|
||||||
|
password: "us8FaGH5",
|
||||||
|
program: "TSURCME",
|
||||||
|
};
|
||||||
|
|
||||||
|
private test_url =
|
||||||
|
"https://clin.CMCVellore.ac.in/TestConference/ConferencePay.asmx";
|
||||||
|
test_cred = {
|
||||||
|
userName: "UMRESTC",
|
||||||
|
password: "zEVjHc9Y",
|
||||||
|
program: "TSURCME",
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
|
// Method to send the SOAP request
|
||||||
|
sendSOAPRequestForStatus(formData: any): Observable<any> {
|
||||||
|
const headers = new HttpHeaders({
|
||||||
|
"Content-Type": "application/soap+xml; charset=utf-8",
|
||||||
|
SOAPAction: "http://www.cmch-vellore.edu/CONFONLINEPAYSTATUS",
|
||||||
|
});
|
||||||
|
|
||||||
|
const soapBody = this.generateSOAPBodyForStatus(this.prod_cred, formData);
|
||||||
|
|
||||||
|
console.log(soapBody);
|
||||||
|
|
||||||
|
return this.httpClient.post(this.url, soapBody, {
|
||||||
|
headers,
|
||||||
|
responseType: "text",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Method to send the SOAP request
|
||||||
|
sendSOAPRequest(formData: any): Observable<any> {
|
||||||
|
const headers = new HttpHeaders({
|
||||||
|
"Content-Type": "application/soap+xml; charset=utf-8",
|
||||||
|
SOAPAction: "http://www.cmch-vellore.edu/NEWCONFONLINEPAYSAVE",
|
||||||
|
});
|
||||||
|
|
||||||
|
const soapBody = this.generateSOAPBody(this.prod_cred, formData);
|
||||||
|
|
||||||
|
console.log(soapBody);
|
||||||
|
|
||||||
|
return this.httpClient.post(this.url, soapBody, {
|
||||||
|
headers,
|
||||||
|
responseType: "text",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the SOAP body from form data
|
||||||
|
private generateSOAPBodyForStatus(userDetails: any, formData: any): string {
|
||||||
|
|
||||||
|
const soapXML = `
|
||||||
|
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/" xmlns:www="http://www.cmch-vellore.edu/">
|
||||||
|
<x:Header>
|
||||||
|
<www:UserDetails>
|
||||||
|
<www:userName>${userDetails.userName}</www:userName>
|
||||||
|
<www:password>${userDetails.password}</www:password>
|
||||||
|
<www:program>${userDetails.program}</www:program>
|
||||||
|
</www:UserDetails>
|
||||||
|
</x:Header>
|
||||||
|
<x:Body>
|
||||||
|
<www:CONFONLINEPAYSTATUS>
|
||||||
|
${this.generateFieldsXML(formData)}
|
||||||
|
</www:CONFONLINEPAYSTATUS>
|
||||||
|
</x:Body>
|
||||||
|
</x:Envelope>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return soapXML;
|
||||||
|
}
|
||||||
|
// Generate the SOAP body from form data
|
||||||
|
private generateSOAPBody(userDetails: any, formData: any): string {
|
||||||
|
const soapXML = `
|
||||||
|
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/" xmlns:www="http://www.cmch-vellore.edu/">
|
||||||
|
<x:Header>
|
||||||
|
<www:UserDetails>
|
||||||
|
<www:userName>${userDetails.userName}</www:userName>
|
||||||
|
<www:password>${userDetails.password}</www:password>
|
||||||
|
<www:program>${userDetails.program}</www:program>
|
||||||
|
</www:UserDetails>
|
||||||
|
</x:Header>
|
||||||
|
<x:Body>
|
||||||
|
<www:NEWCONFONLINEPAYSAVE>
|
||||||
|
${this.generateFieldsXML(formData)}
|
||||||
|
</www:NEWCONFONLINEPAYSAVE>
|
||||||
|
</x:Body>
|
||||||
|
</x:Envelope>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return soapXML;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the fields in SOAP XML format from form data
|
||||||
|
private generateFieldsXML(formData: any): string {
|
||||||
|
let fieldsXML = "";
|
||||||
|
|
||||||
|
// Iterate through form data and generate XML for each field
|
||||||
|
for (const key in formData) {
|
||||||
|
if (formData.hasOwnProperty(key)) {
|
||||||
|
fieldsXML += `<www:${key}>${formData[key]}</www:${key}>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldsXML;
|
||||||
|
}
|
||||||
|
|
||||||
|
events = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
code: "TSURCME",
|
||||||
|
year: "2023",
|
||||||
|
subject: "CME on ",
|
||||||
|
title: " Advances in Chest Trauma Management ",
|
||||||
|
subTitle : "ACTraM 2023",
|
||||||
|
date: "13 & 14, October - 2023",
|
||||||
|
venue: [
|
||||||
|
{
|
||||||
|
title: "Symposium (Hybrid/ In person) ",
|
||||||
|
date: "13.10.2023",
|
||||||
|
address: "Conference Hall 7th floor, A-Block, CMC Vellore Ranipet Campus",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cadaveric Workshop ",
|
||||||
|
date: "14.10.2023",
|
||||||
|
address: "Antomy Dissection Hall, CMC Vellore Bagayam Campus",
|
||||||
|
info: "Limited seats & in person only",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
highlights: [
|
||||||
|
"Keynote lectures by eminent national and international faculty",
|
||||||
|
"Sessions oriented towards understanding the intricacies of multidisciplinary care that is needed in the management of chest trauma patients. ",
|
||||||
|
"Challenging cases discussions ",
|
||||||
|
"Specific focus on intercostal drainage, minimally invasive chest surgery in trauma, thoracic rib fixation, pain relief medications in trauma and critical care management.",
|
||||||
|
"Hands-on cadaveric training on the basic principles of surgical rib fixation",
|
||||||
|
],
|
||||||
|
orgnisers: [
|
||||||
|
"Dr. Sukria Nayak - Organising Chairperson",
|
||||||
|
"Dr. Joses Dany James- Convener ",
|
||||||
|
"Dr. Vijayan P -Convener",
|
||||||
|
"Dr. Srujan Lam Sharma -Convener",
|
||||||
|
"Ms. Nithya.A- Manager",
|
||||||
|
"Mr. Prabhu - Manager",
|
||||||
|
],
|
||||||
|
|
||||||
|
fee: [
|
||||||
|
{ desc: "Online (ISTAC Members only) - ₹500", cost: 500 },
|
||||||
|
{ desc: "Symposium (In-person) - ₹1000", cost: 1000 },
|
||||||
|
{ desc: "Workshop (Only) - ₹3500", cost: 3500 },
|
||||||
|
{ desc: "Symposium & Workshop ₹4500", cost: 5310 },
|
||||||
|
{ desc: "* (+18% GST)", cost: 5310 },
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
phone: "04172 – 224627 ",
|
||||||
|
email: "traumasurg.academic@cmcvellore.ac.in",
|
||||||
|
isActive: true,
|
||||||
|
doctors: [
|
||||||
|
{
|
||||||
|
name: "Dr. Amit Gupta",
|
||||||
|
prof: "Professor",
|
||||||
|
at: "Division of Trauma Surgery JPNATC, AIIMS New Delhi",
|
||||||
|
id: 3,
|
||||||
|
image: "3.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Subodh Kumar",
|
||||||
|
prof: "Professor",
|
||||||
|
at: "Division of Trauma Surgery, JPNATC, AIIMS New Delhi",
|
||||||
|
id: 2,
|
||||||
|
image: "2.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
name: "Dr. Kajal Jain ",
|
||||||
|
prof: "Professor",
|
||||||
|
at: "Trauma Anaesthesia PGIMER, Chandigarh.",
|
||||||
|
id: 1,
|
||||||
|
image: "1.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Krishnan Raghavendran ",
|
||||||
|
prof: "Professor",
|
||||||
|
at: " Division of Trauma and Acute Care Surgery, University Hospital, Ann Arbor Hospital Michigan",
|
||||||
|
id: 4,
|
||||||
|
image: "4.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Balasubramoniam",
|
||||||
|
at: "Consultant Thoracic Surgeon, Yashoda group of Hospitals, Hyderabad",
|
||||||
|
id: 5,
|
||||||
|
image: "5.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Niladri Banerjee",
|
||||||
|
prof: "Assistant Professor",
|
||||||
|
at: "Department of Surgery, AIIMS Jodhpur",
|
||||||
|
id: 8,
|
||||||
|
image: "8.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Sukria Nayak",
|
||||||
|
prof: "Professor & Head",
|
||||||
|
at: " Department of Trauma Surgery, CMC Vellore",
|
||||||
|
id: 6,
|
||||||
|
image: "6.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Ekta Rai",
|
||||||
|
prof: "Professor ",
|
||||||
|
at: "Department of Anesthesia,CMC Vellore",
|
||||||
|
id: 16,
|
||||||
|
image: "16.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr.Madhu Andrew Philip",
|
||||||
|
at: "Prof & Head, Department of Cardiothoracic Surgery,CMC Vellore",
|
||||||
|
id: 17,
|
||||||
|
image: "17.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Balasubramani",
|
||||||
|
prof: "Professor ",
|
||||||
|
at: "Department of Surgical ICU, CMC Vellore",
|
||||||
|
id: 18,
|
||||||
|
image: "18.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Susheel Sudheesh",
|
||||||
|
prof: "Assistant Professor",
|
||||||
|
at: "Department of Anaesthesia, CMC Vellore",
|
||||||
|
id: 10,
|
||||||
|
image: "10.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Srujan Lam Sharma",
|
||||||
|
prof: "Assistant Professor",
|
||||||
|
at: "Department of Trauma Surgery, CMC Vellore",
|
||||||
|
id: 12,
|
||||||
|
image: "12.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Vinay M Rao ",
|
||||||
|
prof: "Associate Professor",
|
||||||
|
at: "Department of Cardiothoracic, Surgery CMC Vellore",
|
||||||
|
id: 7,
|
||||||
|
image: "7.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Santhosh R Benjamin",
|
||||||
|
prof: "Associate Professor",
|
||||||
|
at: "Department of Cardiothoracic, Surgery,CMC Vellore",
|
||||||
|
id: 9,
|
||||||
|
image: "9.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Kirthi Sathyakumar ",
|
||||||
|
prof: "Associate Professor",
|
||||||
|
at: "Emergency Radiology, CMC Vellore",
|
||||||
|
id: 11,
|
||||||
|
image: "11.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Joses Dany James",
|
||||||
|
prof: "Assistant Professor",
|
||||||
|
at: "Department of Trauma Surgery, CMC Vellore",
|
||||||
|
id: 14,
|
||||||
|
image: "14.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Vijayan P",
|
||||||
|
prof: "Associate Professor",
|
||||||
|
at: "Department of Trauma Surgery, CMC Vellore",
|
||||||
|
id: 13,
|
||||||
|
image: "13.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Dr. Vignesh Kumar",
|
||||||
|
prof: "Associate Professor",
|
||||||
|
at: "Department of General Surgery, PIMS, Puducherry",
|
||||||
|
id: 15,
|
||||||
|
image: "15.jpg",
|
||||||
|
},
|
||||||
|
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
getAllConferenceData(): Observable<any> {
|
||||||
|
const url = "https://api.wrdpwd.com/soap/getAllConferenceData";
|
||||||
|
return this.httpClient.get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a method to send data via POST request
|
||||||
|
insertConferenceData(data: any): Observable<any> {
|
||||||
|
const url = "https://api.wrdpwd.com/soap/insertConferenceData";
|
||||||
|
// Define headers if needed (adjust as necessary)
|
||||||
|
const headers = new HttpHeaders({
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send the POST request
|
||||||
|
return this.httpClient.post(url, data, { headers });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a method to send data via POST request
|
||||||
|
updateConferenceData(id, data: any): Observable<any> {
|
||||||
|
const url = "https://api.wrdpwd.com/soap/updateConferenceData/" + id;
|
||||||
|
// Define headers if needed (adjust as necessary)
|
||||||
|
const headers = new HttpHeaders({
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send the POST request
|
||||||
|
return this.httpClient.put(url, data, { headers });
|
||||||
|
}
|
||||||
|
|
||||||
|
partialUpdateConferenceData(id, data: any): Observable<any> {
|
||||||
|
const url = "https://api.wrdpwd.com/soap/partialUpdateConferenceData/" + id;
|
||||||
|
// Define headers if needed (adjust as necessary)
|
||||||
|
const headers = new HttpHeaders({
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send the POST request
|
||||||
|
return this.httpClient.patch(url, data, { headers });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getConferenceDataByRegno(id){
|
||||||
|
const url = "https://api.wrdpwd.com/soap/getConferenceDataByRegno/" + id;
|
||||||
|
// Define headers if needed (adjust as necessary)
|
||||||
|
const headers = new HttpHeaders({
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send the POST request
|
||||||
|
return this.httpClient.get(url, { headers });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getConferenceDataByPhone(id){
|
||||||
|
const url = "https://api.wrdpwd.com/soap/getConferenceDataByPhone/" + id;
|
||||||
|
// Define headers if needed (adjust as necessary)
|
||||||
|
const headers = new HttpHeaders({
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send the POST request
|
||||||
|
return this.httpClient.get(url, { headers });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create a method to send data via POST request
|
||||||
|
deleteConferenceData(id): Observable<any> {
|
||||||
|
const url = "https://api.wrdpwd.com/soap/deleteConferenceData/" + id;
|
||||||
|
// Define headers if needed (adjust as necessary)
|
||||||
|
const headers = new HttpHeaders({
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send the POST request
|
||||||
|
return this.httpClient.delete(url, { headers });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PaymentInfo {
|
||||||
|
Registration: string;
|
||||||
|
Transid: string;
|
||||||
|
ResultCode: string;
|
||||||
|
Result: string;
|
||||||
|
URL: string;
|
||||||
|
}
|
||||||
6
support-portal-frontend/src/app/enum/WorkingStatus.ts
Normal file
6
support-portal-frontend/src/app/enum/WorkingStatus.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
export enum WorkingStatus {
|
||||||
|
ACTIVE = 'ACTIVE',
|
||||||
|
ON_LEAVE = 'ON_LEAVE',
|
||||||
|
RETIRED = 'RETIRED'
|
||||||
|
}
|
||||||
36
support-portal-frontend/src/app/generate.yml
Normal file
36
support-portal-frontend/src/app/generate.yml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
ng generate module pages/xyz --routing
|
||||||
|
ng generate component pages/xyz/header
|
||||||
|
ng generate component pages/xyz/navbar
|
||||||
|
ng generate component pages/xyz/mobile-nav
|
||||||
|
ng generate component pages/xyz/dropdown-menu
|
||||||
|
ng generate component pages/xyz/logo
|
||||||
|
ng generate component pages/xyz/sidebar-modal
|
||||||
|
ng generate component pages/xyz/responsive-menu-toggle
|
||||||
|
ng generate component pages/xyz/home
|
||||||
|
ng generate component pages/xyz/home-two
|
||||||
|
ng generate component pages/xyz/home-three
|
||||||
|
ng generate component pages/xyz/home-four
|
||||||
|
ng generate component pages/xyz/home-five
|
||||||
|
ng generate component pages/xyz/graduate-admission
|
||||||
|
ng generate component pages/xyz/campus-life
|
||||||
|
ng generate component pages/xyz/alumni
|
||||||
|
ng generate component pages/xyz/academics
|
||||||
|
ng generate component pages/xyz/academics-details
|
||||||
|
ng generate component pages/xyz/latest-news
|
||||||
|
ng generate component pages/xyz/news-details
|
||||||
|
ng generate component pages/xyz/faq
|
||||||
|
ng generate component pages/xyz/login
|
||||||
|
ng generate component pages/xyz/register
|
||||||
|
ng generate component pages/xyz/recover-password
|
||||||
|
ng generate component pages/xyz/privacy-policy
|
||||||
|
ng generate component pages/xyz/terms-conditions
|
||||||
|
ng generate component pages/xyz/coming-soon
|
||||||
|
ng generate component pages/xyz/not-found
|
||||||
|
ng generate component pages/xyz/courses
|
||||||
|
ng generate component pages/xyz/courses-details
|
||||||
|
ng generate component pages/xyz/health-care
|
||||||
|
ng generate component pages/xyz/health-care-details
|
||||||
|
ng generate component pages/xyz/events
|
||||||
|
ng generate component pages/xyz/events-details
|
||||||
|
ng generate component pages/xyz/contact-us
|
||||||
@ -2,7 +2,7 @@ import {TestBed} from '@angular/core/testing';
|
|||||||
|
|
||||||
import {AuthenticationGuard} from './authentication.guard';
|
import {AuthenticationGuard} from './authentication.guard';
|
||||||
import {HttpClient} from "@angular/common/http";
|
import {HttpClient} from "@angular/common/http";
|
||||||
import {NotificationModule} from "../notification/notification.module";
|
import {NotificationModule} from "../../notification/notification.module";
|
||||||
import {RouterTestingModule} from "@angular/router/testing";
|
import {RouterTestingModule} from "@angular/router/testing";
|
||||||
|
|
||||||
describe('AuthenticationGuard', () => {
|
describe('AuthenticationGuard', () => {
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
|
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {AuthenticationService} from "../service/authentication.service";
|
|
||||||
import {NotificationService} from "../service/notification.service";
|
import {NotificationService} from "../service/notification.service";
|
||||||
import {NotificationType} from "../notification/notification-type";
|
import {NotificationType} from "../notification/notification-type";
|
||||||
|
import {AuthenticationService} from "../service/authentication.service";
|
||||||
|
// import { AuthenticationService } from '../service/authentication.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -27,7 +28,7 @@ export class AuthenticationGuard implements CanActivate {
|
|||||||
if (this.authenticationService.isUserLoggedIn())
|
if (this.authenticationService.isUserLoggedIn())
|
||||||
return true;
|
return true;
|
||||||
else {
|
else {
|
||||||
this.router.navigate(['/login']);
|
this.router.navigate(['/dashboard/login']);
|
||||||
|
|
||||||
this.notificationService.notify(NotificationType.ERROR, `You need to log in to access this page`);
|
this.notificationService.notify(NotificationType.ERROR, `You need to log in to access this page`);
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {Injectable} from '@angular/core';
|
|||||||
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {environment} from "../../environments/environment";
|
import {environment} from "../../environments/environment";
|
||||||
import {AuthenticationService} from "../service/authentication.service";
|
import {AuthenticationService} from '../service/authentication.service' ;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthInterceptor implements HttpInterceptor {
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
|
|||||||
16
support-portal-frontend/src/app/model/Professor.ts
Normal file
16
support-portal-frontend/src/app/model/Professor.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { WorkingStatus } from "../enum/WorkingStatus";
|
||||||
|
|
||||||
|
export class Professor {
|
||||||
|
// id : number;
|
||||||
|
professorId: string; // UUID
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
email: string;
|
||||||
|
department: string;
|
||||||
|
position: string;
|
||||||
|
officeLocation: string;
|
||||||
|
status: WorkingStatus; // Assuming status is represented as a string in the DTO
|
||||||
|
joinDate: Date; // LocalDateTime as a string
|
||||||
|
profileImageUrl?: string; // Optional, URL to the profile image
|
||||||
|
// profileImage?: File; // Optional, used for uploading profile images
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
<div class="container mt-4">
|
||||||
|
<h2 class="mb-4">Detail Info</h2>
|
||||||
|
<div *ngIf="rowData" class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="-title">Candidate Information</h5>
|
||||||
|
<!-- Display the labels based on their corresponding inputcaptionX values -->
|
||||||
|
|
||||||
|
<!-- Add the remaining fields here using the same pattern -->
|
||||||
|
<p class="card-text"><strong>ID:</strong> {{ rowData.id || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Conference Code:</strong> {{ rowData.conferencecode || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Conference Year:</strong> {{ rowData.conferenceyear || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Bank Name:</strong> {{ rowData.bankname || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Remote IP:</strong> {{ rowData.remoteip || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Registration Number:</strong> {{ rowData.regno || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Candidate Name:</strong> {{ rowData.candidatename || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Name in Receipt:</strong> {{ rowData.nameinreceipt || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Address 1:</strong> {{ rowData.address1 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Address 2:</strong> {{ rowData.address2 || "-NA-"}}</p>
|
||||||
|
|
||||||
|
<p class="card-text"><strong>Country:</strong> {{ rowData.country || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Pincode:</strong> {{ rowData.pincode || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Phone:</strong> {{ rowData.phone || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Mobile:</strong> {{ rowData.mobile || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Email:</strong> {{ rowData.email || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Food Type:</strong> {{ rowData.foodtype || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Participant Type:</strong> {{ rowData.participanttype || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Practice Type:</strong> {{ rowData.practicetype || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Accompany Members:</strong> {{ rowData.accompanymembers || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>External or CMC Staff: </strong>: {{ rowData.inputvalue5 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Payment Amount:</strong> {{ rowData.paymentamount || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Has GST:</strong> {{ rowData.hasgst || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>GST Number:</strong> {{ rowData.gstnumber || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>GST Mobile Number:</strong> {{ rowData.gstmobileno || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>GST Email ID:</strong> {{ rowData.gstemailid || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>To Wards:</strong> {{ rowData.toWards || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Allow 80G:</strong> {{ rowData.allow80G || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Pan Card Number:</strong> {{ rowData.panCardNo || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>GST Registration:</strong> {{ rowData.gstreg || "-NA-"}}</p>
|
||||||
|
<!-- Add any additional fields as needed -->
|
||||||
|
|
||||||
|
|
||||||
|
<p class="card-text"><strong>CMC MISSION:</strong> {{ rowData.city || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>MISSION ID:</strong> {{ rowData.state || "-NA-"}}</p>
|
||||||
|
|
||||||
|
<p class="card-text"><strong>designation: </strong>: {{ rowData.inputvalue1 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>placeOfWork: </strong>: {{ rowData.inputcaption2 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>medicalCollegeName: </strong>: {{ rowData.inputvalue2 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>hospitalName: </strong>: {{ rowData.inputcaption3 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>istacMember: </strong>: {{ rowData.inputvalue3 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>istacMemberID: </strong>: {{ rowData.inputcaption4 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>registrationType: </strong>: {{ rowData.inputvalue4 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>submitCase: </strong>: {{ rowData.inputcaption5 || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>isPaymentDone: </strong>: {{ rowData.inputcaption1 || "-NA-"}}</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p class="card-text"><strong>Response Transaction ID:</strong> {{ rowData.responseTransid || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Response Result Code:</strong> {{ rowData.responseResultCode || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Response Result:</strong> {{ rowData.responseResult || "-NA-"}}</p>
|
||||||
|
<p class="card-text"><strong>Response URL:</strong> {{ rowData.responseURL || "-NA-"}}</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CandidateDetailsComponent } from './candidate-details.component';
|
||||||
|
|
||||||
|
describe('CandidateDetailsComponent', () => {
|
||||||
|
let component: CandidateDetailsComponent;
|
||||||
|
let fixture: ComponentFixture<CandidateDetailsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ CandidateDetailsComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CandidateDetailsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-candidate-details',
|
||||||
|
templateUrl: './candidate-details.component.html',
|
||||||
|
styleUrls: ['./candidate-details.component.scss']
|
||||||
|
})
|
||||||
|
export class CandidateDetailsComponent implements OnInit {
|
||||||
|
|
||||||
|
rowData: any; // Data for the selected row
|
||||||
|
|
||||||
|
constructor(private route: ActivatedRoute) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
// Retrieve the data object from the route's state
|
||||||
|
this.route.paramMap.subscribe(params => {
|
||||||
|
const dataString = params.get('data');
|
||||||
|
if (dataString) {
|
||||||
|
this.rowData = JSON.parse(dataString);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,147 @@
|
|||||||
|
<div class="row p-3">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header">Registration Info</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form [formGroup]="detailForm" (ngSubmit)="detailFormSubmit()">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="phone">Mobile Number</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="phone"
|
||||||
|
formControlName="phone"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" *ngIf="obj.id">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Conference Information</h5>
|
||||||
|
<hr />
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<td>{{ obj.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Conference Code</th>
|
||||||
|
<td>{{ obj.conferencecode }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Conference Year</th>
|
||||||
|
<td>{{ obj.conferenceyear }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Bank Name</th>
|
||||||
|
<td>{{ obj.bankname }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Registration Number</th>
|
||||||
|
<td>{{ obj.regno }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Response Transaction ID</th>
|
||||||
|
<td>{{ obj.responseTransid }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Name </th>
|
||||||
|
<td>{{ obj.nameinreceipt }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Address</th>
|
||||||
|
<td>{{ obj.address1 }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Phone</th>
|
||||||
|
<td>{{ obj.phone }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Payment Status</th>
|
||||||
|
<td>{{ obj.inputcaption1 }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">Transaction Status</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="regno">Reg No</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="regno"
|
||||||
|
formControlName="regno"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="transid">Transaction ID</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="transid"
|
||||||
|
formControlName="transid"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="conference">Conference</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="conference"
|
||||||
|
formControlName="conference"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="confyear">Conference Year</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="confyear"
|
||||||
|
formControlName="confyear"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="bankname">Bank Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="bankname"
|
||||||
|
formControlName="bankname"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mt-3" *ngIf="status.Transid">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Transaction Details</h5>
|
||||||
|
<hr />
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<!-- <tr>
|
||||||
|
<th>Property</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr> -->
|
||||||
|
<tr *ngFor="let property of status | keyvalue">
|
||||||
|
<th>{{ property.key }}</th>
|
||||||
|
<td>{{ property.value }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CheckStatusComponent } from './check-status.component';
|
||||||
|
|
||||||
|
describe('CheckStatusComponent', () => {
|
||||||
|
let component: CheckStatusComponent;
|
||||||
|
let fixture: ComponentFixture<CheckStatusComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ CheckStatusComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CheckStatusComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user