diff --git a/Backend/Post-Service/uploads/1734619819376.jpeg b/Backend/Post-Service/uploads/1734619819376.jpeg new file mode 100644 index 00000000..b2bd7a52 Binary files /dev/null and b/Backend/Post-Service/uploads/1734619819376.jpeg differ diff --git a/Backend/Post-Service/uploads/1734619819385.jpeg b/Backend/Post-Service/uploads/1734619819385.jpeg new file mode 100644 index 00000000..341228f5 Binary files /dev/null and b/Backend/Post-Service/uploads/1734619819385.jpeg differ diff --git a/Backend/Post-Service/uploads/1734619819388.jpeg b/Backend/Post-Service/uploads/1734619819388.jpeg new file mode 100644 index 00000000..7f1f5734 Binary files /dev/null and b/Backend/Post-Service/uploads/1734619819388.jpeg differ diff --git a/Backend/Post-Service/uploads/1734619819391.jpeg b/Backend/Post-Service/uploads/1734619819391.jpeg new file mode 100644 index 00000000..62e0bc11 Binary files /dev/null and b/Backend/Post-Service/uploads/1734619819391.jpeg differ diff --git a/Backend/Post-Service/uploads/1734619819393.jpeg b/Backend/Post-Service/uploads/1734619819393.jpeg new file mode 100644 index 00000000..edfc0bb9 Binary files /dev/null and b/Backend/Post-Service/uploads/1734619819393.jpeg differ diff --git a/Backend/Post-Service/uploads/1734643264676.jpeg b/Backend/Post-Service/uploads/1734643264676.jpeg new file mode 100644 index 00000000..9f90947d Binary files /dev/null and b/Backend/Post-Service/uploads/1734643264676.jpeg differ diff --git a/Backend/Post-Service/uploads/1734643264686.jpeg b/Backend/Post-Service/uploads/1734643264686.jpeg new file mode 100644 index 00000000..62e0bc11 Binary files /dev/null and b/Backend/Post-Service/uploads/1734643264686.jpeg differ diff --git a/Backend/Post-Service/uploads/1734643264689.jpeg b/Backend/Post-Service/uploads/1734643264689.jpeg new file mode 100644 index 00000000..341228f5 Binary files /dev/null and b/Backend/Post-Service/uploads/1734643264689.jpeg differ diff --git a/Backend/Post-Service/uploads/1734643264690.jpeg b/Backend/Post-Service/uploads/1734643264690.jpeg new file mode 100644 index 00000000..edfc0bb9 Binary files /dev/null and b/Backend/Post-Service/uploads/1734643264690.jpeg differ diff --git a/Backend/Post-Service/uploads/1734643264693.jpeg b/Backend/Post-Service/uploads/1734643264693.jpeg new file mode 100644 index 00000000..7f1f5734 Binary files /dev/null and b/Backend/Post-Service/uploads/1734643264693.jpeg differ diff --git a/Backend/Post-Service/uploads/1734643264695.jpeg b/Backend/Post-Service/uploads/1734643264695.jpeg new file mode 100644 index 00000000..b2bd7a52 Binary files /dev/null and b/Backend/Post-Service/uploads/1734643264695.jpeg differ diff --git a/Backend/Post-Service/uploads/1734643264791.jpeg b/Backend/Post-Service/uploads/1734643264791.jpeg new file mode 100644 index 00000000..446cf59d Binary files /dev/null and b/Backend/Post-Service/uploads/1734643264791.jpeg differ diff --git a/Backend/Post-Service/uploads/1734643264794.jpeg b/Backend/Post-Service/uploads/1734643264794.jpeg new file mode 100644 index 00000000..e7472085 Binary files /dev/null and b/Backend/Post-Service/uploads/1734643264794.jpeg differ diff --git a/Backend/service-notification/pom.xml b/Backend/service-notification/pom.xml index f7671151..90780d80 100644 --- a/Backend/service-notification/pom.xml +++ b/Backend/service-notification/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot @@ -11,7 +11,7 @@ com.message service-notification 0.0.1-SNAPSHOT - service-notification + service_chat Demo project for Spring Boot @@ -28,8 +28,17 @@ 17 + 2024.0.0 + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-data-jpa + org.springframework.boot spring-boot-starter-web @@ -38,7 +47,17 @@ org.springframework.boot spring-boot-starter-websocket + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.boot + spring-boot-devtools + runtime + true + com.mysql mysql-connector-j @@ -50,6 +69,17 @@ test + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + @@ -61,3 +91,4 @@ + diff --git a/Backend/service_chat/src/main/java/com/message/config/WebSocketConfig.java b/Backend/service_chat/src/main/java/com/message/config/WebSocketConfig.java index 8bd5d9d7..ce759f16 100644 --- a/Backend/service_chat/src/main/java/com/message/config/WebSocketConfig.java +++ b/Backend/service_chat/src/main/java/com/message/config/WebSocketConfig.java @@ -13,7 +13,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") - .setAllowedOrigins("http://localhost:4200", "http://localhost:8880"); + .setAllowedOrigins("http://localhost:4200", "http://localhost:8880").withSockJS(); } diff --git a/Backend/service_chat/src/main/java/com/message/controllers/MessageController.java b/Backend/service_chat/src/main/java/com/message/controllers/MessageController.java index f09e6922..de3fb144 100644 --- a/Backend/service_chat/src/main/java/com/message/controllers/MessageController.java +++ b/Backend/service_chat/src/main/java/com/message/controllers/MessageController.java @@ -10,7 +10,9 @@ import org.springframework.web.bind.annotation.*; import com.message.entities.ChatMessage; +import com.message.entities.Notification; import com.message.services.ChatMessageService; +import com.message.services.NotificationService; import java.util.List; @@ -22,15 +24,42 @@ public class MessageController { private ChatMessageService chatMessageService; @Autowired private SimpMessagingTemplate simpMessagingTemplate; + @Autowired + private NotificationService notificationService; @MessageMapping("/chat.sendMessage") @SendTo("/topic/public") public ChatMessage sendMessage( @Payload ChatMessage chatMessage ) { - return chatMessageService.saveMessage(chatMessage); + // Save the chat message + ChatMessage savedMessage = chatMessageService.saveMessage(chatMessage); + + // Create and send a notification + Notification notification = new Notification(); + notification.setSender(chatMessage.getSender()); + notification.setReceiver(chatMessage.getReceiver()); + notification.setContent(chatMessage.getContent()); + notification.setTimestamp(String.valueOf(System.currentTimeMillis())); + + // Save notification to database + notificationService.saveNotification(notification); + + // Send notification in real time + simpMessagingTemplate.convertAndSend("/topic/notifications", notification); + + return savedMessage; + + } + // Add Delete Notification Endpoint + @DeleteMapping("/notifications/delete/{id}") + @ResponseBody + public String deleteNotification(@PathVariable Long id) { + notificationService.deleteNotification(id); + return "Notification deleted successfully!"; } + @MessageMapping("/chat.addUser") @SendTo("/topic/public") public ChatMessage addUser( @@ -57,6 +86,11 @@ public List getAllMessages() { return chatMessageService.getAllMessages(); } + @GetMapping("/notifications") + @ResponseBody + public List getAllNotifications() { + return notificationService.getAllNotifications(); + } @PutMapping("/update/{id}") @ResponseBody diff --git a/Backend/service_chat/src/main/java/com/message/entities/Notification.java b/Backend/service_chat/src/main/java/com/message/entities/Notification.java new file mode 100644 index 00000000..5e130e4f --- /dev/null +++ b/Backend/service_chat/src/main/java/com/message/entities/Notification.java @@ -0,0 +1,62 @@ +package com.message.entities; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@Entity +@Table(name = "notification") +public class Notification { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String sender; + private String receiver; + private String content; + private String timestamp; + public Notification() { + super(); + } + public Notification(Long id, String sender, String receiver, String content, String timestamp) { + super(); + this.id = id; + this.sender = sender; + this.receiver = receiver; + this.content = content; + this.timestamp = timestamp; + } + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public String getSender() { + return sender; + } + public void setSender(String sender) { + this.sender = sender; + } + public String getReceiver() { + return receiver; + } + public void setReceiver(String receiver) { + this.receiver = receiver; + } + public String getContent() { + return content; + } + public void setContent(String content) { + this.content = content; + } + public String getTimestamp() { + return timestamp; + } + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + +} diff --git a/Backend/service_chat/src/main/java/com/message/repositories/NotificationRepository.java b/Backend/service_chat/src/main/java/com/message/repositories/NotificationRepository.java new file mode 100644 index 00000000..0fe5f3da --- /dev/null +++ b/Backend/service_chat/src/main/java/com/message/repositories/NotificationRepository.java @@ -0,0 +1,11 @@ +package com.message.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.message.entities.Notification; + +@Repository +public interface NotificationRepository extends JpaRepository { +} + diff --git a/Backend/service_chat/src/main/java/com/message/services/NotificationService.java b/Backend/service_chat/src/main/java/com/message/services/NotificationService.java new file mode 100644 index 00000000..cc3127f8 --- /dev/null +++ b/Backend/service_chat/src/main/java/com/message/services/NotificationService.java @@ -0,0 +1,30 @@ +package com.message.services; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.message.entities.ChatMessage; +import com.message.entities.Notification; +import com.message.repositories.NotificationRepository; + +@Service +public class NotificationService { + + @Autowired + private NotificationRepository notificationRepository; + + public Notification saveNotification(Notification notification) { + return notificationRepository.save(notification); + } + + public List getAllNotifications() { + return notificationRepository.findAll(); + } + public void deleteNotification(Long id) { + notificationRepository.deleteById(id); + } + + +} diff --git a/Backend/service_event2/demo/src/main/java/com/example/demo/repositories/EventRepository.java b/Backend/service_event2/demo/src/main/java/com/example/demo/repositories/EventRepository.java index 11a1a3e2..0f44f24e 100644 --- a/Backend/service_event2/demo/src/main/java/com/example/demo/repositories/EventRepository.java +++ b/Backend/service_event2/demo/src/main/java/com/example/demo/repositories/EventRepository.java @@ -3,7 +3,11 @@ import com.example.demo.entities.Event; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; -public interface EventRepository extends JpaRepository { +import java.util.List; -} +public interface EventRepository extends JpaRepository { + @Query("SELECT e FROM Event e WHERE e.title LIKE %?1% OR e.description LIKE %?1%") + List findByTitleOrDescriptionContaining(String pattern); +} \ No newline at end of file diff --git a/Backend/service_event2/demo/src/main/java/com/example/demo/services/EventService.java b/Backend/service_event2/demo/src/main/java/com/example/demo/services/EventService.java index 92182b1c..82baf0dd 100644 --- a/Backend/service_event2/demo/src/main/java/com/example/demo/services/EventService.java +++ b/Backend/service_event2/demo/src/main/java/com/example/demo/services/EventService.java @@ -28,6 +28,15 @@ public class EventService { private EventRepository eventRepository; @Autowired private ParticipationRepository participationRepository; + @GetMapping("/search") + public ResponseEntity> searchEvents(@RequestParam("search") String keyword) { + // Fetch events where title or description contains the given keyword + List events = eventRepository.findByTitleOrDescriptionContaining(keyword); + if (events.isEmpty()) { + return ResponseEntity.noContent().build(); // Return 204 if no events found + } + return ResponseEntity.ok(events); // Return 200 with the list of events + } @GetMapping("/GetAll") public List GetAllevents() diff --git a/Backend/service_event2/demo/target/classes/com/example/demo/repositories/EventRepository.class b/Backend/service_event2/demo/target/classes/com/example/demo/repositories/EventRepository.class index 568eab29..0588cc53 100644 Binary files a/Backend/service_event2/demo/target/classes/com/example/demo/repositories/EventRepository.class and b/Backend/service_event2/demo/target/classes/com/example/demo/repositories/EventRepository.class differ diff --git a/Backend/service_event2/demo/target/classes/com/example/demo/services/EventService.class b/Backend/service_event2/demo/target/classes/com/example/demo/services/EventService.class index 988333c8..19158bae 100644 Binary files a/Backend/service_event2/demo/target/classes/com/example/demo/services/EventService.class and b/Backend/service_event2/demo/target/classes/com/example/demo/services/EventService.class differ diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index 6537635c..7de83682 100644 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -36,6 +36,7 @@ "net": "^1.0.2", "quill": "^2.0.2", "rxjs": "~7.8.0", + "socket.io-client": "^4.8.1", "sockjs-client": "^1.6.1", "stompjs": "^2.3.3", "sweetalert2": "^11.14.5", @@ -5175,7 +5176,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true, "license": "MIT" }, "node_modules/@stomp/stompjs": { @@ -7756,11 +7756,38 @@ "node": ">=10.2.0" } }, + "node_modules/engine.io-client": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz", + "integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -13716,11 +13743,40 @@ } } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", @@ -13734,7 +13790,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -16046,7 +16101,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -16064,6 +16118,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/Frontend/package.json b/Frontend/package.json index 96a9ec3a..60470335 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -38,6 +38,7 @@ "net": "^1.0.2", "quill": "^2.0.2", "rxjs": "~7.8.0", + "socket.io-client": "^4.8.1", "sockjs-client": "^1.6.1", "stompjs": "^2.3.3", "sweetalert2": "^11.14.5", @@ -65,4 +66,4 @@ "tailwindcss": "^3.4.14", "typescript": "~5.5.2" } -} \ No newline at end of file +} diff --git a/Frontend/src/app/accueil/accueil.component.html b/Frontend/src/app/accueil/accueil.component.html index 14a53142..d113414b 100644 --- a/Frontend/src/app/accueil/accueil.component.html +++ b/Frontend/src/app/accueil/accueil.component.html @@ -145,7 +145,7 @@

Les Nouvelles Événements

Event @@ -153,8 +153,8 @@

Les Nouvelles Événements

{{ event.title }}

{{ event.price }}

- -

voir plus

+ +

voir plus

@@ -203,9 +203,9 @@
{{ comment.user.name }}
Participants
    -
  • - Avatar - {{ participant.userName }} +
  • + Avatar + {{ participant.user.name + " " + participant.user.lastName + " " + participant.price }}
@@ -237,12 +237,16 @@
Prix :
Image :
- Event Image + Event Image
diff --git a/Frontend/src/app/accueil/accueil.component.ts b/Frontend/src/app/accueil/accueil.component.ts index ee465196..ad9b98f6 100644 --- a/Frontend/src/app/accueil/accueil.component.ts +++ b/Frontend/src/app/accueil/accueil.component.ts @@ -1,6 +1,7 @@ import { UserService } from './../services/user/user.service'; import { Post } from './../models/post.model'; import { CommonModule } from '@angular/common'; +import { User } from '../models/user.model'; import { Router } from '@angular/router'; import { Component, OnInit } from '@angular/core'; import { PostService } from '../services/post/post.service'; @@ -22,10 +23,12 @@ import { ReactionService } from '../services/reaction/reaction.service'; import { tap, catchError, timestamp } from 'rxjs/operators'; import { AddpostService } from '../services/post/addpost.service'; import { ChatUsersService } from '../services/chat/chat-users.service'; - +import { EventService } from '../services/event/event.service'; +import { Participation } from '../models/Participation.model'; +import { event } from '../models/event.model'; import { of } from 'rxjs'; import Swal from 'sweetalert2'; - +import { RegistrationService } from '../services/paticipation/registration.service'; import { FormsModule } from '@angular/forms'; import SockJS from 'sockjs-client'; @@ -111,14 +114,27 @@ export class AccueilComponent implements OnInit { }); } } + getEvents(): void { + this.EventService.getEvents().subscribe({ + next: (data) => { + console.log("service",data); + + this.events = data; + }, + error: (err) => { + console.log("error : ", err); + } + }) + } + curr_users!: Participation[]; currentItem: Post | null = null; - constructor(private dialog: MatDialog,private Postservice: PostService,private Chatbotservice: ChatbotService,private commentservice: CommentService,private userservice:UserService, + constructor(private dialog: MatDialog,private EventService: EventService, private registrationService:RegistrationService,private Postservice: PostService,private Chatbotservice: ChatbotService,private commentservice: CommentService,private userservice:UserService, private ReactionService:ReactionService,private router: Router, private AddpostService: AddpostService, private ChatUsersService: ChatUsersService ) { @@ -176,6 +192,7 @@ export class AccueilComponent implements OnInit { ngOnInit(): void { this.userId = localStorage.getItem('userId') ?? ''; + this.getEvents(); console.log("userid",this.userId) this.getuser(this.userId); this.getChats(); @@ -370,159 +387,9 @@ export class AccueilComponent implements OnInit { ); } - comments = [ - { - userImage: '../../assets/accueil/lyna.jpg', - userName: ' Lyna moujehed', - content: 'Super article, dispo !', - date: '7 Février 2024' - }, - { - userImage: '../../assets/accueil/lyna.jpg', - userName: ' Lyna moujehed', - content: 'Super article, dispo !', - date: '7 Février 2024' - }, - { - userImage: '../../assets/accueil/wassim.jpg', - userName: 'Wassim saidani', - content: 'prix negociable ??.', - date: '8 Février 2024' - }, - { - userImage: '../../assets/sirineKahweji_ISETBizerte.jpg', - userName: 'syrine syrina', - content: 'Très bien expliqué !', - date: '10 Février 2024' - }, - { - userImage: '../../assets/accueil/lyna.jpg', - userName: ' Lyna moujehed', - content: 'Super article, dispo !', - date: '7 Février 2024' - }, - { - userImage: '../../assets/accueil/wassim.jpg', - userName: 'Wassim saidani', - content: 'prix negociable ??.', - date: '8 Février 2024' - }, - { - userImage: '../../assets/sirineKahweji_ISETBizerte.jpg', - userName: 'syrine syrina', - content: 'Très bien expliqué !', - date: '10 Février 2024' - }, - { - userImage: '../../assets/accueil/lyna.jpg', - userName: ' Lyna moujehed', - content: 'Super article, dispo !', - date: '7 Février 2024' - }, - { - userImage: '../../assets/accueil/wassim.jpg', - userName: 'Wassim saidani', - content: 'prix negociable ??.', - date: '8 Février 2024' - }, - { - userImage: '../../assets/sirineKahweji_ISETBizerte.jpg', - userName: 'syrine syrina', - content: 'Très bien expliqué !', - date: '10 Février 2024' - } - ]; - - events = [ - { - id:1, - image: '../../assets/accueil/event1.jpg', - title: 'Ferme de 13 ha à Ain Draham', - price: '5,400,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - }, - { - id:2, - image: '../../assets/accueil/event1.jpg', - title: 'Terrain avec vue sur la mer Bizerte CapZbib', - price: '1,200,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - }, - { - id:3, - image: '../../assets/accueil/event2.jpg', - title: 'Petit Terrain a Tunis', - price: '850,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - }, - { - id:4, - image: '../../assets/accueil/event1.jpg', - title: 'Ferme de 13 ha à Ain Draham', - price: '5,400,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - }, - { - id:5, - image: '../../assets/accueil/event2.jpg', - title: 'Ferme avec vue sur la mer', - price: '1,200,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", + - }, - { - id:6, - image: '../../assets/accueil/event3.jpg', - title: 'Appartement au centre-ville', - price: '850,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - }, - { - id:7, - image: '../../assets/accueil/event1.jpg', - title: 'Ferme de 13 ha à Ain Draham', - price: '5,400,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - }, - { - id:8, - image: '../../assets/accueil/event2.jpg', - title: 'Villa avec vue sur la mer', - price: '1,200,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - }, - { - id:9, - image: '../../assets/accueil/event3.jpg', - title: 'Appartement au centre-ville', - price: '850,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - }, - { - id:10, - image: '../../assets/accueil/event1.jpg', - title: 'Ferme de 13 ha à Ain Draham', - price: '5,400,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - }, - { - id:11, - image: '../../assets/accueil/event2.jpg', - title: 'Villa avec vue sur la mer', - price: '1,200,000 TND', - description: "Belle opportunité d'acquisition d'une ferme agricole située dans la région verdoyante de Bizerte.", - - } - ]; + events:any[]= []; showDetails = false; isModalOpen = false; @@ -534,6 +401,7 @@ openModalIA() { this.isModalOpenIA = true; } + closeModalIA() { this.isModalOpenIA = false; } @@ -570,11 +438,92 @@ formatDate(date: string): string { } - openModalDetailsEvent(eventId:number) { - this.eventDetails = this.events[eventId]; - this.isModalOpenDetailsEvent = true; - console.log("ModalDetailsEvent opened "); - } + // openModalDetailsEvent(event:any) { + // this.eventDetails = event; + // this.isModalOpenDetailsEvent = true; + // console.log("ModalDetailsEvent opened "); + // } + value = 0 ; + RegisterForEvent() { + const formData = new FormData(); + + formData.append("price", this.value.toString()); + formData.append("person_id", this.user.id.toString()); + formData.append("event_id", this.SelectedEvent.id.toString()); + + this.registrationService.register(formData).subscribe({ + next: (response) => { + this.isModalOpenDetailsEvent=false; + this.value=0; + Swal.fire({ + icon: 'success', + title: 'You registry seccfully for this event!', + showConfirmButton: false, + timer: 3000, + width: '300px', + padding: '10px', + customClass: { + title: 'swal-title', + popup: 'swal-popup' + } + }); + }, + error: (err) => { + console.error("Error during registration", err); + } + }); +} +// get_user(id:string) +// { +// this.UserService.getUser(id).subscribe({ +// next:(res) =>{ +// this.Owner = res; +// console.log(res); + +// }, +// error:(err)=>{ +// console.log("error getting user",err); + +// } +// }) +// } + + Owner!: User; + SelectedEvent: event = { + id: -1, + status: false, + description: '', + title: '', + price: 0, + owner_id: 0, + photo: '', + date_debut: '', + date_fin: '', + owner: this.Owner, + users: [], + } + openModelDetailsEvent(Ev:event):void + { + this.eventDetails = Ev; + this.SelectedEvent= Ev; + const formData = new FormData(); + formData.append("event_id",Ev.id?.toString()); + this.registrationService.getRegistratedUsers(Ev.id).subscribe({ + next:(res)=>{ + this.curr_users = res; + + + }, + error:(err)=>{ + console.log("error getting registrated users ",err); + + } + }) + + this.isModalOpenDetailsEvent= true; + + } + closeModalDetailsEvent() { this.isModalOpenDetailsEvent = false; diff --git a/Frontend/src/app/accueil/navbar/navbar.component.html b/Frontend/src/app/accueil/navbar/navbar.component.html index 931e4f6d..e92a2c4e 100644 --- a/Frontend/src/app/accueil/navbar/navbar.component.html +++ b/Frontend/src/app/accueil/navbar/navbar.component.html @@ -1,36 +1,62 @@
-
diff --git a/Frontend/src/app/accueil/navbar/navbar.component.ts b/Frontend/src/app/accueil/navbar/navbar.component.ts index 51a3411e..b31ce9a8 100644 --- a/Frontend/src/app/accueil/navbar/navbar.component.ts +++ b/Frontend/src/app/accueil/navbar/navbar.component.ts @@ -1,20 +1,25 @@ -import { Component } from '@angular/core'; +import { Component, ChangeDetectorRef , OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { RouterModule, RouterOutlet } from '@angular/router'; +import { RouterModule } from '@angular/router'; import { EventEmitter, Output } from '@angular/core'; - - - +import { io, Socket } from 'socket.io-client'; // Import Socket.IO +import SockJS from 'sockjs-client'; +import Stomp from 'stompjs'; +import { ChatUsersService } from '../../services/chat/chat-users.service'; +import { UserService } from '../../services/user/user.service'; +import { MatDialog } from '@angular/material/dialog'; +import { ChatsComponent } from '../chats/chats.component'; @Component({ selector: 'app-navbar', standalone: true, - imports: [RouterModule,RouterOutlet,CommonModule], + imports: [RouterModule, CommonModule,ChatsComponent], templateUrl: './navbar.component.html', - styleUrl: './navbar.component.css' + styleUrls: ['./navbar.component.css'], }) -export class NavbarComponent { - +export class NavbarComponent { @Output() openAssistantModal = new EventEmitter(); + + openModalIA() { this.openAssistantModal.emit(); @@ -28,34 +33,165 @@ closeModalIA() { } isNotificationModalOpen = false; -// This data simulates user interactions -notificationData = [ - { - image: '../../../assets/accueil/lyna.jpg', - name: 'Lyna Moujahed', - interaction: 'reacted', - content: '👍 on your post' - }, - { - image: '../../../assets/accueil/wassim.jpg', - name: 'Mohamed wessim saidani', - interaction: 'commented', - content: '"Great post! Keep it up!" on your post' - }, - { - image: '../../../assets/sirineKahweji_ISETBizerte.jpg', - name: 'sirine kahweji', - interaction: 'messaged', - content: 'you about your post' + // Socket connection + + + // Flag to manage Notification modal visibility + + + // Notification data + notificationData: any[] = []; + + // For real-time notification count + notificationCount = this.notificationData.length; + + constructor(private ChatUsersService: ChatUsersService,private UserService:UserService,private dialog: MatDialog,private cdr: ChangeDetectorRef) { + + } -]; + receiverId: string = ''; + users:any=null; + ngOnInit() { + // Listen for incoming notifications + this.receiverId = localStorage.getItem('userId') ?? ''; + this.getNotif(); + this.notificationCount = this.notificationData.length; + this.connectWebSocket(); + this.loadUsers(); + + } + socketClient:any= null; + connectWebSocket(): void { + // Create a new SockJS instance connected to the WebSocket endpoint + let ws = new SockJS('http://localhost:8088/ws'); // Adjust the URL as needed + this.socketClient = Stomp.over(ws); + + // Connect the WebSocket client and subscribe to the topic + this.socketClient.connect({}, (frame: any) => { + console.log('Connected to WebSocket:', frame); + + // Subscribe to the public topic (or private topic based on your requirements) + this.socketClient.subscribe('/topic/public', (message: any) => { + this.onNotifReceived(message); + }); + + }, (error: any) => { + console.error('WebSocket connection error:', error); + }); + } + + onNotifReceived(message: any): void { + const receivedMessage = JSON.parse(message.body); + if (receivedMessage.receiver === this.receiverId) { + this.notificationData.push({ + sender: receivedMessage.sender, + content: receivedMessage.content, + receiver: receivedMessage.receiver, + timestamp: receivedMessage.timestamp + }); + this.notificationCount++; + this.playNotificationSound(); + console.log('Received message:', receivedMessage); + + } + + } + playNotificationSound(): void { + const sound = new Audio('../../../assets/sound.mp3'); + sound.play(); + } + formatDate(date: string): string { + const diff = Math.floor((new Date().getTime() - new Date(date).getTime()) / 1000); + if (diff < 60) return 'il y a quelques secondes'; + if (diff < 3600) return `il y a ${Math.floor(diff / 60)} minutes`; + if (diff < 86400) return `il y a ${Math.floor(diff / 3600)} heures`; + return `il y a ${Math.floor(diff / 86400)} jours`; + } + + + -openNotificationModal() { - this.isNotificationModalOpen = true; + // Opens the Notification Modal + openNotificationModal() { + if(this.isNotificationModalOpen){ + this.isNotificationModalOpen = false; + }else{ + this.isNotificationModalOpen = true; + } + + this.notificationCount = 0; + } + deleteNotification(id: number): void { + this.ChatUsersService.deleteNotification(id).subscribe({ + next: () => { + console.log('Notification deleted successfully'); + + // Update the notification data array + this.notificationData = this.notificationData.filter(notif => String(notif.id) !== String(id)); + + + // Update the notification count + this.notificationCount = this.notificationData.length; + + // Trigger Angular's change detection manually + this.cdr.detectChanges(); + + console.log('Updated notifications:', this.notificationData); + }, + error: (err) => { + console.error('Error deleting notification:', err); + } + }); } -closeNotificationModal() { - this.isNotificationModalOpen = false; + + + + openOverlay(userId: number | undefined) { + if (userId) { + this.dialog.open(ChatsComponent, { + data: { receiverId: userId } + }); + } + } + + // Closes the Notification Modal + closeNotificationModal() { + this.isNotificationModalOpen = false; + } + + getNotif(): void { + this.ChatUsersService.getNotif().subscribe({ + next: (data) => { + console.log('Fetched notifications:', data); + + // Filter notifications for the logged-in user + this.notificationData = data.filter((notif: any) => notif.receiver === this.receiverId); + // this.notificationData.reverse(); + // Update the notification count based on the filtered notifications + this.notificationCount = this.notificationData.length; + + console.log('notificationData', this.notificationData); + }, + error: (err) => { + console.error('Error fetching notifications:', err); + }, + }); } + loadUsers(): void { + this.UserService.getUsers().subscribe({ + next: (data) => { + this.users = data; + + }, + error: (error) => { + console.error('Error fetching users:', error); + + }, + }); + } + + // Emit a message or notification to the server (for testing or sending notifications) + } diff --git a/Frontend/src/app/events/events.component.html b/Frontend/src/app/events/events.component.html index 1942dbde..2432d55c 100644 --- a/Frontend/src/app/events/events.component.html +++ b/Frontend/src/app/events/events.component.html @@ -30,7 +30,8 @@

-
@@ -216,7 +200,8 @@

Image :
class="bg-yellow-50 border border-yellow-300 text-green-900 text-xs rounded-md focus:ring-green-500 focus:border-green-500 block w-60 p-2.5 mr-5" placeholder="Enter your bid" required> - + diff --git a/Frontend/src/app/events/events.component.ts b/Frontend/src/app/events/events.component.ts index eea6e2cc..ce5d2440 100644 --- a/Frontend/src/app/events/events.component.ts +++ b/Frontend/src/app/events/events.component.ts @@ -8,6 +8,7 @@ import { User } from '../models/user.model'; import { UserService } from '../services/user/user.service'; import { Participation } from '../models/Participation.model'; import { RegistrationService } from '../services/paticipation/registration.service'; +import Swal from 'sweetalert2'; @Component({ selector: 'app-events', standalone: true, @@ -58,6 +59,30 @@ export class EventsComponent implements OnInit { } + searchQuery: string = ''; + searchUsers() { + const searchValue = this.searchQuery.trim(); + if (searchValue === '') { + this.eventService.getEvents().subscribe( + (data: any) => { + this.annonces = data; + }, + (error: any) => { + console.error('Error fetching all users:', error); + } + ); + } else { + this.eventService.search(searchValue).subscribe( + (data: any) => { + this.annonces = data; + }, + (error: any) => { + console.error('Error searching users:', error); + } + ); + } + } + get_user(id:string) { this.userService.getUser(id).subscribe({ @@ -125,7 +150,21 @@ export class EventsComponent implements OnInit { this.registrationService.register(formData).subscribe({ next: (response) => { - console.log("Registration successful", response); + this.isModalOpenDetailsEvent=false; + this.value=0; + Swal.fire({ + icon: 'success', + title: 'You registry seccfully for this event!', + showConfirmButton: false, + timer: 3000, + width: '300px', + padding: '10px', + customClass: { + title: 'swal-title', + popup: 'swal-popup' + } + }); + }, error: (err) => { console.error("Error during registration", err); diff --git a/Frontend/src/app/services/chat/chat-users.service.ts b/Frontend/src/app/services/chat/chat-users.service.ts index 31c6a537..0cdf41cb 100644 --- a/Frontend/src/app/services/chat/chat-users.service.ts +++ b/Frontend/src/app/services/chat/chat-users.service.ts @@ -12,10 +12,18 @@ export class ChatUsersService { // Get all chats getChats(): Observable { - console.log("wessim"); + return this.http.get(`${this.apiUrl}/chats/read`); } + getNotif(): Observable { + + + return this.http.get(`${this.apiUrl}/chats/notifications`); + } + deleteNotification(id: number): Observable { + return this.http.delete(`${this.apiUrl}/chats/notifications/delete/${id}`); + } sendMessage(message: { sender: number; diff --git a/Frontend/src/app/services/event/event.service.ts b/Frontend/src/app/services/event/event.service.ts index b2b444ee..bbc96347 100644 --- a/Frontend/src/app/services/event/event.service.ts +++ b/Frontend/src/app/services/event/event.service.ts @@ -34,5 +34,8 @@ export class EventService { updateEvent(id: string,item:any): Observable { return this.http.put(`${this.apiUrl}/${id}`, item); } + search(searchQuery: string): Observable { + return this.http.get(`${this.apiUrl+"/search"}?search=${searchQuery}`); + } } diff --git a/Frontend/src/assets/sound.mp3 b/Frontend/src/assets/sound.mp3 new file mode 100644 index 00000000..5792c3d9 Binary files /dev/null and b/Frontend/src/assets/sound.mp3 differ