Compare commits

...

10 Commits

Author SHA1 Message Date
cfe68a276f latestt changes 2025-08-19 14:03:53 +05:30
911933074b backend url is supported to https 2024-09-29 09:23:18 +05:30
9712439261 route hasing added.. 2024-09-29 09:04:54 +05:30
7d701d3747 slight chnages in dockerfile 2024-09-29 08:56:23 +05:30
d8be4e7ca1 prod url is added 2024-09-29 01:09:46 +05:30
279c6f9c1a db config changed 2024-09-29 00:40:39 +05:30
f41a1043c9 docker config done 2024-09-28 23:42:55 +05:30
e206968993 lot of changes on someday 2024-09-28 20:56:56 +05:30
bd5119fc3c first commit 2024-09-22 10:21:52 +05:30
9c6c3abc32 stable after blogs 2024-08-31 19:39:59 +05:30
276 changed files with 29172 additions and 7720 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

26
docker-compose.yml Normal file
View 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

View 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"]

View File

@ -15,8 +15,11 @@
<description>Demo project for Spring Boot</description>
<properties>
<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.name>angular-${project.artifactId}</docker.image.name>
@ -29,6 +32,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
@ -100,11 +109,11 @@
<version>1.12.75</version>
</dependency>
<dependency>
<!-- <dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -158,6 +167,12 @@
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
@ -195,11 +210,7 @@
</path>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>
</plugin>
@ -246,6 +257,9 @@
</configuration>
</plugin>
</plugins>
</build>

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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 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";
}

View File

@ -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();
}
}

View File

@ -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());
}
}

View File

@ -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
// ...
}

View File

@ -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);
}
}

View File

@ -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
}
}
}

View File

@ -1,5 +1,6 @@
package net.shyshkin.study.fullstack.supportportal.backend.controller;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.shyshkin.study.fullstack.supportportal.backend.constant.SecurityConstants;
@ -32,6 +33,13 @@ import static org.springframework.http.HttpStatus.OK;
@RequiredArgsConstructor
public class UserResource {
@Data
public static class UserDTO{
String username;
String password;
}
private final UserService userService;
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider;
@ -47,7 +55,8 @@ public class UserResource {
}
@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());
User byUsername = userService.findByUsername(user.getUsername());

View File

@ -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;
}
}

View File

@ -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.
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,7 @@
package net.shyshkin.study.fullstack.supportportal.backend.domain;
public enum WorkingStatus {
ACTIVE,
ON_LEAVE,
RETIRED
}

View File

@ -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
// ...
}

View File

@ -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
}

View File

@ -0,0 +1,8 @@
package net.shyshkin.study.fullstack.supportportal.backend.exception.domain;
public class ProfessorNotFoundException extends RuntimeException {
public ProfessorNotFoundException(String message) {
super(message);
}
}

View File

@ -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();
}
}

View File

@ -7,7 +7,9 @@ import org.mapstruct.Mapping;
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 {
@Mapping(target = "isNotLocked", source = "notLocked")

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -18,7 +18,6 @@ public class EmailService {
private final Environment environment;
public void sendNewPasswordEmail(String firstName, String password, String email) {
// Create a Simple MailMessage.
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
@ -35,7 +34,20 @@ public class EmailService {
message.setSubject(EMAIL_SUBJECT);
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!
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());
}
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -35,3 +35,5 @@ public interface UserService extends UserDetailsService {
byte[] getDefaultProfileImage(UUID userId);
}

View File

@ -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.exception.domain.*;
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 org.apache.commons.lang3.RandomStringUtils;
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";
private final UserRepository userRepository;
private final ProfessorRepository professorRepository;
private final PasswordEncoder passwordEncoder;
private final LoginAttemptService loginAttemptService;
private final EmailService emailService;
@ -264,10 +266,14 @@ public class UserServiceImpl implements UserService {
@Override
public byte[] getDefaultProfileImage(UUID userId) {
if (!userRepository.existsByUserId(userId)) {
if (!userRepository.existsByUserId(userId) && !professorRepository.existsByProfessorId(userId)) {
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"
RequestEntity<Void> requestEntity = RequestEntity
.get("/{userId}", userId)

View File

@ -4,25 +4,34 @@ server:
# whitelabel:
# enabled: false
spring:
mail:
host: smtp.gmail.com
host: mail.techzoos.in
port: 587
username: ${PORTAL_MAIL_USERNAME:fake.user@gmail.com}
password: ${PORTAL_MAIL_PASSWORD:fake_password}
username: ${PORTAL_MAIL_USERNAME:govardhan@techzoos.in}
password: ${PORTAL_MAIL_PASSWORD:123456}
properties:
mail:
transport:
protocol: smtp
smtp:
auth: true
auth: false
starttls:
enable: true
ssl:
enable: false
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
url: jdbc:mysql://mysql:3306/support-portal
username: ENC(criE3etnc/EVZbizNgNdmj+8F0BYC3bSVBK1VT/xJ7WMoNvSfdEGsqWfCpaX5lEWvXLOO8pzgjdB5zIOBcTikw==)
password: ENC(OTG4nZfio2dHHxV0Ey/Nmb4XeEfaD1YMsRVQxOwF59Q1JSBZPUKLWXORJXPz2RysKRngcdk2SgioAMw166DoqA==)
jpa:
hibernate:
ddl-auto: update
@ -39,87 +48,100 @@ spring:
web:
resources:
add-mappings: false
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:
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:
secret: ENC(EfWSJqncgjSJ0g/tMzLoO9PlrjmpQf8Eb+q51SUXlh3AzwMHJyTF1gV0VpuNEQkNb9Lsw62xOBnxDNe73BsPDQ==)
secret: custom_text
# secret: ${random.value} #Does not work - every time generates new value
jasypt:
encryptor:
password: ${JASYPT_PASSWORD}
algorithm: PBEWITHHMACSHA512ANDAES_256
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
# jasypt:
# encryptor:
# # password: ${JASYPT_PASSWORD}
# password: custom_text
# algorithm: PBEWITHHMACSHA512ANDAES_256
# iv-generator-classname: org.jasypt.iv.RandomIvGenerator
---
spring:
config:
activate:
on-profile: local
datasource:
url: jdbc:mysql://localhost:23306/support-portal
jpa:
show-sql: true
logging:
level:
net.shyshkin: debug
# spring:
# config:
# activate:
# on-profile: local
# datasource:
# url: jdbc:mysql://210.18.189.94:8098/demo
# username: youruser
# password: youruserpassword
# jpa:
# show-sql: true
# logging:
# level:
# net.shyshkin: debug
---
spring:
config:
activate:
on-profile: aws-local
datasource:
url: jdbc:mysql://localhost:3306/support_portal
username: support_portal_user
password: Supp0rt_Porta!_P@ssword
mail:
host: email-smtp.eu-north-1.amazonaws.com
port: 587
username: AKIAVW7XGDOWFHHCELIH
password: BJyWOWS1xWYR35MRCFn3BuuQ6vY+k7DRsdAvOfqDs/Fk
# spring:
# config:
# activate:
# on-profile: aws-local
# datasource:
# url: jdbc:mysql://210.18.189.94:8098/demo
# username: youruser
# password: youruserpassword
# mail:
# host: email-smtp.eu-north-1.amazonaws.com
# port: 587
# 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
app:
email:
from: d.art.shishkin@gmail.com
carbon-copy: d.art.shishkin@gmail.com
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
server:
port: 5000
logging:
level:
net.shyshkin: debug
# app:
# email:
# from: d.art.shishkin@gmail.com
# carbon-copy: d.art.shishkin@gmail.com
# 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
# server:
# port: 5000
# logging:
# level:
# net.shyshkin: debug
---
spring:
config:
activate:
on-profile: aws-rds
datasource:
url: jdbc:mysql://portal-db.coaum9neetxc.eu-north-1.rds.amazonaws.com:3306/support_portal
username: ENC(MPap/iQmyyLSeulVzLLq4nQ5dcwMyJ1cbW+bW7MOU4pN7CHQULbaDn8/5VszOP9F)
password: ENC(nC0PV+0wPW+73o2uOh4Zg7EA34vdwZKpkPD4CIKvjDDXQ+dGXjykTuHUl3jlxkRC/00IpFurk/UJ9hTpZ6QqGA==)
mail:
host: email-smtp.eu-north-1.amazonaws.com
port: 587
username: ENC(CgaSXOMqTmswes1PgAYp3ICcoIVVXyKUlDR1Se963Vja02cBIor/2884e2OEFKW4XhBClTbuZCVdHK0vRRNqYg==)
password: ENC(GA8XsfU8vmat/7A8qEhrVz0Y47THxNT8jQ29wSg035fozwW7m+fKhJMQd4tgxL9dPfOzSXYzkffL0fG1AihWiHl99H9iBeXndDSvOhskvh4=)
# spring:
# config:
# activate:
# on-profile: aws-rds
# datasource:
# url: jdbc:mysql://210.18.189.94:8098/demo
# username: youruser
# password: youruserpassword
# mail:
# host: email-smtp.eu-north-1.amazonaws.com
# port: 587
# 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
app:
email:
from: d.art.shishkin@gmail.com
carbon-copy: d.art.shishkin@gmail.com
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
server:
port: 5000
logging:
level:
net.shyshkin: debug
# app:
# email:
# from: d.art.shishkin@gmail.com
# carbon-copy: d.art.shishkin@gmail.com
# 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
# server:
# port: 5000
# logging:
# level:
# net.shyshkin: debug
#####
#
@ -127,37 +149,37 @@ logging:
#
#####
server.ssl:
enabled: true # Enable HTTPS support (only accept HTTPS requests)
key-alias: securedPortal # Alias that identifies the key in the key store
key-store: classpath:securedPortal-keystore.p12 # Keystore location
key-store-password: ENC(nqDHyVFmySdbaCOZfj4EiQLRYyLSPLRLq/OzncqlsFIuWvh8caiOapAb+zrKR1+A) # Keystore password
key-store-type: PKCS12 # Keystore format
# server.ssl:
# enabled: true # Enable HTTPS support (only accept HTTPS requests)
# key-alias: securedPortal # Alias that identifies the key in the key store
# key-store: classpath:securedPortal-keystore.p12 # Keystore location
# key-store-password: custom_text
# key-store-type: PKCS12 # Keystore format
---
spring:
config:
activate:
on-profile: image-s3
app:
amazon-s3:
bucket-name: portal-user-profile-images
# ---
# spring:
# config:
# activate:
# on-profile: image-s3
# app:
# amazon-s3:
# bucket-name: portal-user-profile-images
---
spring:
config:
activate:
on-profile: image-s3-localstack
app:
amazon-s3:
bucket-name: portal-user-profile-images
config:
aws:
region: eu-north-1
s3:
url: http://127.0.0.1:4566
bucket-name: portal-user-profile-images
access-key: localstack
secret-key: localstack
# ---
# spring:
# config:
# activate:
# on-profile: image-s3-localstack
# app:
# amazon-s3:
# bucket-name: portal-user-profile-images
# config:
# aws:
# region: eu-north-1
# s3:
# url: http://127.0.0.1:4566
# bucket-name: portal-user-profile-images
# access-key: localstack
# secret-key: localstack

View File

@ -114,6 +114,9 @@ class UserResourceTest extends BaseUserTest {
//when
ResponseEntity<User> responseEntity = restTemplate.postForEntity("/user/register", fakeUser, User.class);
//then
log.debug("Response Entity: {}", responseEntity);
assertThat(responseEntity.getStatusCode()).isEqualTo(OK);
@ -132,6 +135,7 @@ class UserResourceTest extends BaseUserTest {
user = registeredUser;
}
@Test
@Order(40)
void registerUser_usernameExists() {

View 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;"]

View File

@ -31,8 +31,14 @@
],
"styles": [
"src/styles.css"
],
"scripts": []
"scripts": [
]
},
"configurations": {
"production": {
@ -115,7 +121,8 @@
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
"src/assets",
"src/assets/assets2"
],
"styles": [
"src/styles.css"

View File

@ -0,0 +1,7 @@
[[ -s $HOME/.nvm/nvm.sh ]] && . $HOME/.nvm/nvm.sh # This loads NVM
nvm use 12

File diff suppressed because it is too large Load Diff

View File

@ -18,17 +18,25 @@
"@angular/platform-browser": "~12.2.0",
"@angular/platform-browser-dynamic": "~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",
"ng-particles": "^2.1.11",
"ngx-owl-carousel-o": "^5.0.0",
"ngx-typed-js": "^2.0.2",
"rxjs": "~6.6.0",
"subsink": "^1.0.2",
"tslib": "^2.3.0",
"tsparticles": "^1.18.11",
"xlsx": "^0.18.5",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.2.5",
"@angular-devkit/build-angular": "^12.2.18",
"@angular/cli": "~12.2.5",
"@angular/compiler-cli": "~12.2.0",
"@babel/core": "^7.25.2",
"@types/jasmine": "~3.8.0",
"@types/node": "^12.11.1",
"jasmine-core": "~3.8.0",

View File

@ -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 { }

View 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 { }

View File

@ -14,7 +14,7 @@ describe('routes', () => {
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]});
});

View File

@ -1,41 +1,100 @@
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 { 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';
export const routes: Routes = [
{path: 'login', component: LoginComponent},
{path: 'register', component: RegisterComponent},
{path: 'user/management', component: UserComponent, canActivate: [AuthenticationGuard]},
// {
// path: '',
// loadChildren: () =>
// import('./pages/pages.module').then((m) => m.PagesModule),
// },
{
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: 'dashboard',
loadChildren: () =>
import('./admin/admin.module').then((m) => m.AdminModule),
},
{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({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
exports: [RouterModule],
})
export class AppRoutingModule {
}
export class AppRoutingModule {}

View File

@ -6,5 +6,5 @@ import {Component} from '@angular/core';
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'support-portal-frontend';
title = 'cnc - Admin';
}

View File

@ -1,5 +1,7 @@
import {NgModule} from '@angular/core';
import {Compiler, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import { HashLocationStrategy, LocationStrategy } from '@angular/common';
import {AppComponent} from './app.component';
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 {AuthInterceptor} from "./interceptor/auth.interceptor";
import {AuthenticationGuard} from "./guard/authentication.guard";
import {NotificationModule} from "./notification/notification.module";
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} from "@angular/forms";
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';
@ -20,30 +21,58 @@ 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 { PagesModule } from './pages/pages.module';
@NgModule({
declarations: [
AppComponent,
LoginComponent,
RegisterComponent,
UserComponent,
ManagementComponent,
UsersComponent,
SettingsComponent,
ProfileComponent,
UsersTableComponent,
UserViewComponent,
UserEditComponent
// LoginComponent,
// RegisterComponent,
// UserComponent,
// ManagementComponent,
// UsersComponent,
// SettingsComponent,
// ProfileComponent,
// UsersTableComponent,
// UserViewComponent,
// UserEditComponent,
// ProfessorComponent,
// MenuComponent,
// HomeComponent,
// BlogComponent,
// EventComponent,
// EventFormComponent
],
imports: [
BrowserModule,
HttpClientModule,
NotificationModule,
AppRoutingModule,
FormsModule
// FormsModule,
// ReactiveFormsModule,
// AngularEditorModule,
// PagesModule,
],
providers: [AuthenticationGuard, AuthenticationService, UserService,
{provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true}
// providers:[],
providers: [Compiler,AuthenticationGuard, AuthenticationService, UserService,BlogService,
{provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
{provide: LocationStrategy, useClass: HashLocationStrategy}
],
bootstrap: [AppComponent]
})

View File

@ -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;
}

View File

@ -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">&#10003;</span> <!-- Check mark for posted -->
<span *ngIf="!blog.posted" class="text-danger">&#10007;</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>

View File

@ -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();
});
});

View 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: ''
});
}
}

View File

@ -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>

View File

@ -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();
});
});

View File

@ -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']));
}
}
}
}

View File

@ -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>

View File

@ -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();
});
});

View File

@ -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());
}
}
}

View File

@ -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 */
}

View File

@ -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>

View File

@ -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();
});
});

View File

@ -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 {
}
}

View File

@ -1,6 +1,6 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import {Router} from "@angular/router";
import {AuthenticationService} from "../../service/authentication.service";
import {NotificationService} from "../../service/notification.service";
import {NotificationType} from "../../notification/notification-type";
import {Subscription} from "rxjs";
@ -8,6 +8,8 @@ import {UserLogin} from "../../dto/user-login";
import {HttpErrorResponse, HttpResponse} from "@angular/common/http";
import {User} from "../../model/user";
import {HeaderType} from "../../enum/header-type.enum";
import { AuthenticationService } from 'src/app/service/authentication.service';
;
@Component({
selector: 'app-login',
@ -26,7 +28,8 @@ export class LoginComponent implements OnInit, OnDestroy {
ngOnInit(): void {
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");
}
}
@ -43,7 +46,7 @@ export class LoginComponent implements OnInit, OnDestroy {
this.authenticationService.addUserToLocalStorage(response.body!);
this.router.navigateByUrl('/management/users');
this.router.navigateByUrl('/dashboard/home');
this.showLoading = false;
},
(errorResponse: HttpErrorResponse) => {

View File

@ -1,7 +1,9 @@
import {Component, OnInit} from '@angular/core';
import {Role} from "../../enum/role.enum";
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 {BehaviorSubject} from "rxjs";

View File

@ -1,4 +1,7 @@
<!-- user profile -->
<app-menu></app-menu>
<div class="tab-pane fade show active" id="profile">
<div class="container">
<div class="row flex-lg-nowrap">

View File

@ -1,13 +1,16 @@
import {Component, OnInit} from '@angular/core';
import {AuthenticationService} from "../../../service/authentication.service";
import {User} from "../../../model/user";
import {FileUploadStatus} from "../../../model/file-upload.status";
import {NotificationType} from "../../../notification/notification-type";
import {HttpErrorResponse, HttpEvent, HttpEventType} from "@angular/common/http";
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 {Router} from "@angular/router";
import { AuthenticationService } from 'src/app/service/authentication.service';
;
@Component({
selector: 'app-profile',
@ -74,7 +77,7 @@ export class ProfileComponent implements OnInit {
onLogOut() {
this.authenticationService.logout();
this.router.navigate(['/login']);
this.router.navigate(['/dashboard/login']);
this.notificationService.notify(NotificationType.SUCCESS, 'You have been successfully logged out');
}

View File

@ -1,4 +1,9 @@
<!-- change password -->
<app-menu></app-menu>
<div class="container">
<div *ngIf="isAdmin" class="tab-pane fade show active" id="reset-password">
<form #resetPasswordForm="ngForm" (ngSubmit)="onResetPassword(resetPasswordForm)">
<fieldset>
@ -18,3 +23,5 @@
</form>
</div>
</div>

View File

@ -1,11 +1,14 @@
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 {CustomHttpResponse} from "../../../dto/custom-http-response";
import {NotificationType} from "../../../notification/notification-type";
import {HttpErrorResponse} from "@angular/common/http";
import {SubSink} from "subsink";
import {UserService} from "../../../service/user.service";
import { UserService } from 'src/app/service/user.service';
import {NotificationService} from "../../../service/notification.service";
@Component({
@ -53,4 +56,5 @@ export class SettingsComponent implements OnInit, OnDestroy {
}
);
}
}

View File

@ -2,7 +2,8 @@ import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from "@angular/router";
import {User} from "../../../model/user";
import {Observable} from "rxjs";
import {UserService} from "../../../service/user.service";
import { UserService } from 'src/app/service/user.service';
@Injectable({
providedIn: 'root'

View File

@ -1,7 +1,8 @@
import {Component, OnInit} from '@angular/core';
import {UserService} from "../../../../service/user.service";
import {User} from "../../../../model/user";
import {ActivatedRoute, Router} from "@angular/router";
import { UserService } from 'src/app/service/user.service';
@Component({
selector: 'app-user-view',

View File

@ -3,7 +3,8 @@ import {User} from "../../../../model/user";
import {NotificationType} from "../../../../notification/notification-type";
import {HttpErrorResponse} from "@angular/common/http";
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 {AuthenticationService} from "../../../../service/authentication.service";
import {ActivatedRoute, Router} from "@angular/router";

View File

@ -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;
}

View File

@ -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>

View File

@ -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();
});
});

View File

@ -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);
}
}

View File

@ -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">&times;</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">&times;</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">&times;</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>

View File

@ -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();
});
});

View File

@ -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;
}
}

View File

@ -50,6 +50,7 @@
*ngIf="emailInput.invalid && emailInput.touched">Please enter an email.</span>
<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">
<i class="fas fa-spinner fa-spin" *ngIf="showLoading"></i>&nbsp;&nbsp;
<span *ngIf="showLoading">{{showLoading ? 'Loading...' : 'Register'}}</span>

View File

@ -1,7 +1,9 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import {User} from "../../model/user";
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 {Subscription} from "rxjs";
import {HttpErrorResponse} from "@angular/common/http";
@ -24,7 +26,7 @@ export class RegisterComponent implements OnInit, OnDestroy {
ngOnInit(): void {
if (this.authenticationService.isUserLoggedIn()) {
this.router.navigateByUrl("/user/management");
this.router.navigateByUrl("/dashboard/userManagement");
this.notificationService.notify(NotificationType.INFO, "You are already logged in");
}
}
@ -37,7 +39,7 @@ export class RegisterComponent implements OnInit, OnDestroy {
.subscribe(user => {
this.notificationService.notify(NotificationType.SUCCESS, `A new account was created for ${user.firstName}.
Please check your email for password to log in`);
this.router.navigateByUrl('/login');
this.router.navigateByUrl('/dashboard/login');
this.showLoading = false;
},
(errorResponse: HttpErrorResponse) => {

View File

@ -1,5 +1,9 @@
<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>
<div class="col-md-4">
@ -11,13 +15,17 @@
</div>
<!-- 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="nav nav-pills">
<a class="nav-item nav-link active ml-1" (click)="changeTitle('Users')" data-bs-toggle="tab" href="#users">
<i class="fa fa-users"></i>
Users
</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-->
<!-- 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.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">
<i class="fa fa-cogs"></i>
Settings
@ -433,6 +441,7 @@
</label>
</div>
</div>
<fieldset class="form-group">
<div class="form-check">
<label class="form-check-label">

View File

@ -4,7 +4,9 @@ import {UserComponent} from './user.component';
import {HttpClient} from "@angular/common/http";
import {Router} from "@angular/router";
import {NotificationService} from "../../service/notification.service";
import {AuthenticationService} from "../../service/authentication.service";
import { AuthenticationService } from 'src/app/service/authentication.service';
;
describe('UserComponent', () => {
let component: UserComponent;

View File

@ -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 {User} from "../../model/user";
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 {NgForm} from "@angular/forms";
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 {FileUploadStatus} from "../../model/file-upload.status";
import {Role} from "../../enum/role.enum";
@ -23,6 +27,7 @@ export class UserComponent implements OnInit, OnDestroy {
private titleSubject = new BehaviorSubject<string>('Users');
public titleAction$ = this.titleSubject.asObservable();
public users: User[] = [];
public loggedInUser: User;
@ -55,6 +60,11 @@ export class UserComponent implements OnInit, OnDestroy {
this.titleSubject.next(title);
}
handleTitleChange(title: string): void {
this.titleSubject.next(title);
}
public getUsers(showNotification: boolean) {
this.refreshing = true;
@ -228,7 +238,7 @@ export class UserComponent implements OnInit, OnDestroy {
onLogOut() {
this.authenticationService.logout();
this.router.navigate(['/login']);
this.router.navigate(['/dashboard/login']);
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 {
return this.loggedInUser.role === Role.ADMIN || this.loggedInUser.role === Role.SUPER_ADMIN;
}

View 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();
});
});

View 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;
}

View File

@ -0,0 +1,6 @@
export enum WorkingStatus {
ACTIVE = 'ACTIVE',
ON_LEAVE = 'ON_LEAVE',
RETIRED = 'RETIRED'
}

View 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

View File

@ -2,7 +2,7 @@ import {TestBed} from '@angular/core/testing';
import {AuthenticationGuard} from './authentication.guard';
import {HttpClient} from "@angular/common/http";
import {NotificationModule} from "../notification/notification.module";
import {NotificationModule} from "../../notification/notification.module";
import {RouterTestingModule} from "@angular/router/testing";
describe('AuthenticationGuard', () => {

View File

@ -1,9 +1,10 @@
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
import {Observable} from 'rxjs';
import {AuthenticationService} from "../service/authentication.service";
import {NotificationService} from "../service/notification.service";
import {NotificationType} from "../notification/notification-type";
import {AuthenticationService} from "../service/authentication.service";
// import { AuthenticationService } from '../service/authentication.service';
@Injectable({
providedIn: 'root'
@ -27,7 +28,7 @@ export class AuthenticationGuard implements CanActivate {
if (this.authenticationService.isUserLoggedIn())
return true;
else {
this.router.navigate(['/login']);
this.router.navigate(['/dashboard/login']);
this.notificationService.notify(NotificationType.ERROR, `You need to log in to access this page`);

View File

@ -2,7 +2,7 @@ import {Injectable} from '@angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs';
import {environment} from "../../environments/environment";
import {AuthenticationService} from "../service/authentication.service";
import {AuthenticationService} from '../service/authentication.service' ;
@Injectable()
export class AuthInterceptor implements HttpInterceptor {

View 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
}

View File

@ -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>

View File

@ -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();
});
});

View File

@ -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);
}
});
}
}

View File

@ -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>

View File

@ -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