├── README.md ├── Screen Recording 2025-05-13 000343.mp4 ├── backend ├── Dockerfile ├── docker-compose.yml ├── pom.xml ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── expiryalert │ │ │ ├── ExpiryAlertApplication.java │ │ │ ├── config │ │ │ └── OpenApiConfig.java │ │ │ ├── controller │ │ │ ├── ItemController.java │ │ │ ├── StorageLocationController.java │ │ │ └── TestController.java │ │ │ ├── model │ │ │ ├── Item.java │ │ │ └── StorageLocation.java │ │ │ ├── repository │ │ │ ├── ItemRepository.java │ │ │ └── StorageLocationRepository.java │ │ │ └── service │ │ │ ├── ItemService.java │ │ │ └── NotificationService.java │ │ └── resources │ │ ├── application.properties │ │ └── static │ │ ├── simple.html │ │ ├── test.html │ │ └── test2.html └── target │ ├── classes │ ├── application.properties │ ├── com │ │ └── expiryalert │ │ │ ├── ExpiryAlertApplication.class │ │ │ ├── config │ │ │ └── OpenApiConfig.class │ │ │ ├── controller │ │ │ ├── ItemController.class │ │ │ ├── StorageLocationController.class │ │ │ └── TestController.class │ │ │ ├── model │ │ │ ├── Item.class │ │ │ └── StorageLocation.class │ │ │ ├── repository │ │ │ ├── ItemRepository.class │ │ │ └── StorageLocationRepository.class │ │ │ └── service │ │ │ ├── ItemService.class │ │ │ └── NotificationService.class │ └── static │ │ ├── simple.html │ │ ├── test.html │ │ └── test2.html │ └── maven-status │ └── maven-compiler-plugin │ └── compile │ └── default-compile │ ├── createdFiles.lst │ └── inputFiles.lst ├── expiryalertsystem.mp4 └── frontend ├── .gitignore ├── package-lock.json ├── package.json ├── public └── index.html └── src ├── App.js ├── index.js ├── shelfLife.js └── styles.css /README.md: -------------------------------------------------------------------------------- 1 |

🕒 Spring Boot Expiry Alert System

2 |

A web-based system to track, update, and manage item expiry data efficiently.

3 | 4 | --- 5 | 6 | ## 🔧 Features 7 | 8 | - ✅ Add, update, and delete items with expiry dates 9 | - ❌ Mark expired or wasted items 10 | - 📋 View all active and wasted items 11 | - 💾 MySQL database integration 12 | - 🔗 RESTful API endpoints 13 | - 🌐 CORS enabled for frontend access 14 | - ⚠️ Error handling and input validation 15 | 16 | 17 | ## 🔌 API Endpoints 18 | 19 | | Method | Endpoint | Description | 20 | |--------|-------------------------|--------------------------| 21 | | GET | `/api/items` | Get all active items | 22 | | GET | `/api/items/wasted` | Get all wasted items | 23 | | POST | `/api/items` | Add a new item | 24 | | PUT | `/api/items/{id}` | Update item | 25 | | DELETE | `/api/items/{id}` | Delete item | 26 | | POST | `/api/items/{id}/waste` | Mark item as wasted | 27 | 28 | --- 29 | 30 | ## ⚙️ Technologies Used 31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | --- 46 | 47 | ## 🚀 How to Run the Project 48 | 49 | # Step 1: Navigate to the backend folder 50 | cd backend 51 | 52 | # Step 2: Run the Spring Boot application 53 | mvn spring-boot:run 54 | 55 | # Step 3: Open the frontend folder in your browser 56 | # Example: 57 | open ../frontend/index.html 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Screen Recording 2025-05-13 000343.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/Screen Recording 2025-05-13 000343.mp4 -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM eclipse-temurin:21-jdk-alpine 2 | WORKDIR /app 3 | COPY target/*.jar app.jar 4 | EXPOSE 8080 5 | ENTRYPOINT ["java", "-jar", "app.jar"] -------------------------------------------------------------------------------- /backend/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | app: 5 | build: . 6 | ports: 7 | - "8080:8080" 8 | environment: 9 | - SPRING_PROFILES_ACTIVE=prod 10 | - SPRING_DATASOURCE_URL=jdbc:h2:mem:expirydb 11 | - SPRING_DATASOURCE_DRIVERCLASSNAME=org.h2.Driver 12 | - SPRING_DATASOURCE_USERNAME=sa 13 | - SPRING_DATASOURCE_PASSWORD= 14 | - SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.H2Dialect 15 | - SPRING_H2_CONSOLE_ENABLED=true 16 | volumes: 17 | - ./target:/app/target -------------------------------------------------------------------------------- /backend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 3.2.0 11 | 12 | 13 | 14 | com.expiryalert 15 | expiry-alert 16 | 0.0.1-SNAPSHOT 17 | expiry-alert 18 | Expiry Alert System 19 | 20 | 21 | 21 22 | 21 23 | 21 24 | UTF-8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-jpa 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-mail 39 | 40 | 41 | com.h2database 42 | h2 43 | runtime 44 | 45 | 46 | org.projectlombok 47 | lombok 48 | true 49 | 50 | 51 | org.springdoc 52 | springdoc-openapi-starter-webmvc-ui 53 | 2.3.0 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-maven-plugin 67 | 68 | 69 | 70 | org.projectlombok 71 | lombok 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/ExpiryAlertApplication.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | @SpringBootApplication 8 | @EnableScheduling 9 | public class ExpiryAlertApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(ExpiryAlertApplication.class, args); 12 | } 13 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/config/OpenApiConfig.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.config; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.oas.models.info.Info; 5 | import io.swagger.v3.oas.models.info.Contact; 6 | import io.swagger.v3.oas.models.info.License; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | public class OpenApiConfig { 12 | 13 | @Bean 14 | public OpenAPI customOpenAPI() { 15 | return new OpenAPI() 16 | .info(new Info() 17 | .title("Expiry Alert API") 18 | .version("1.0") 19 | .description("API for managing product expiry dates and alerts") 20 | .contact(new Contact() 21 | .name("Expiry Alert Team") 22 | .email("support@expiryalert.com")) 23 | .license(new License() 24 | .name("Apache 2.0") 25 | .url("http://www.apache.org/licenses/LICENSE-2.0.html"))); 26 | } 27 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/controller/ItemController.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.controller; 2 | 3 | import com.expiryalert.model.Item; 4 | import com.expiryalert.service.ItemService; 5 | import io.swagger.v3.oas.annotations.Operation; 6 | import io.swagger.v3.oas.annotations.Parameter; 7 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 8 | import io.swagger.v3.oas.annotations.tags.Tag; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.web.bind.annotation.*; 12 | import java.util.List; 13 | 14 | @RestController 15 | @RequestMapping("/api/items") 16 | @CrossOrigin(origins = "http://localhost:3000") 17 | @Tag(name = "Item Management", description = "APIs for managing items and their expiry dates") 18 | public class ItemController { 19 | 20 | @Autowired 21 | private ItemService itemService; 22 | 23 | @Operation(summary = "Get all items", description = "Retrieves a list of all items in the system") 24 | @ApiResponse(responseCode = "200", description = "Successfully retrieved all items") 25 | @GetMapping 26 | public List getAllItems() { 27 | return itemService.getAllItems(); 28 | } 29 | 30 | @Operation(summary = "Create new item", description = "Creates a new item with the provided details") 31 | @ApiResponse(responseCode = "200", description = "Item created successfully") 32 | @PostMapping 33 | public Item createItem(@RequestBody Item item) { 34 | return itemService.saveItem(item); 35 | } 36 | 37 | @Operation(summary = "Update item", description = "Updates an existing item by ID") 38 | @ApiResponse(responseCode = "200", description = "Item updated successfully") 39 | @PutMapping("/{id}") 40 | public Item updateItem( 41 | @Parameter(description = "ID of the item to update") @PathVariable Long id, 42 | @RequestBody Item item) { 43 | item.setId(id); 44 | return itemService.saveItem(item); 45 | } 46 | 47 | @Operation(summary = "Delete item", description = "Deletes an item by ID") 48 | @ApiResponse(responseCode = "200", description = "Item deleted successfully") 49 | @DeleteMapping("/{id}") 50 | public ResponseEntity deleteItem( 51 | @Parameter(description = "ID of the item to delete") @PathVariable Long id) { 52 | itemService.deleteItem(id); 53 | return ResponseEntity.ok().build(); 54 | } 55 | 56 | @Operation(summary = "Mark item as wasted", description = "Marks an item as wasted and records the waste date") 57 | @ApiResponse(responseCode = "200", description = "Item marked as wasted successfully") 58 | @PostMapping("/{id}/waste") 59 | public Item markAsWasted( 60 | @Parameter(description = "ID of the item to mark as wasted") @PathVariable Long id) { 61 | return itemService.markAsWasted(id); 62 | } 63 | 64 | @Operation(summary = "Get wasted items", description = "Retrieves a list of all wasted items") 65 | @ApiResponse(responseCode = "200", description = "Successfully retrieved wasted items") 66 | @GetMapping("/wasted") 67 | public List getWastedItems() { 68 | return itemService.getWastedItems(); 69 | } 70 | 71 | @Operation(summary = "Get items by location", description = "Retrieves all items in a specific storage location") 72 | @ApiResponse(responseCode = "200", description = "Successfully retrieved items by location") 73 | @GetMapping("/location/{locationId}") 74 | public List getItemsByLocation( 75 | @Parameter(description = "ID of the storage location") @PathVariable Long locationId) { 76 | return itemService.getItemsByLocation(locationId); 77 | } 78 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/controller/StorageLocationController.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.controller; 2 | 3 | import com.expiryalert.model.StorageLocation; 4 | import com.expiryalert.repository.StorageLocationRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.*; 7 | import java.util.List; 8 | 9 | @RestController 10 | @RequestMapping("/api/locations") 11 | @CrossOrigin(origins = "http://localhost:3000") 12 | public class StorageLocationController { 13 | 14 | @Autowired 15 | private StorageLocationRepository locationRepository; 16 | 17 | @GetMapping 18 | public List getAllLocations() { 19 | return locationRepository.findAll(); 20 | } 21 | 22 | @PostMapping 23 | public StorageLocation createLocation(@RequestBody StorageLocation location) { 24 | return locationRepository.save(location); 25 | } 26 | 27 | @PutMapping("/{id}") 28 | public StorageLocation updateLocation(@PathVariable Long id, @RequestBody StorageLocation location) { 29 | location.setId(id); 30 | return locationRepository.save(location); 31 | } 32 | 33 | @DeleteMapping("/{id}") 34 | public void deleteLocation(@PathVariable Long id) { 35 | locationRepository.deleteById(id); 36 | } 37 | 38 | @GetMapping("/search") 39 | public List searchLocations(@RequestParam String name) { 40 | return locationRepository.findByNameContainingIgnoreCase(name); 41 | } 42 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/controller/TestController.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.controller; 2 | 3 | import com.expiryalert.model.Item; 4 | import com.expiryalert.service.ItemService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.mail.SimpleMailMessage; 7 | import org.springframework.mail.javamail.JavaMailSender; 8 | import org.springframework.web.bind.annotation.*; 9 | import java.time.LocalDate; 10 | 11 | @RestController 12 | @RequestMapping("/api/test") 13 | public class TestController { 14 | 15 | @Autowired 16 | private JavaMailSender emailSender; 17 | 18 | @Autowired 19 | private ItemService itemService; 20 | 21 | @GetMapping("/send-email") 22 | public String sendTestEmail() { 23 | try { 24 | SimpleMailMessage message = new SimpleMailMessage(); 25 | message.setTo("rmkoushika3115@gmail.com"); 26 | message.setSubject("Test Email from Expiry Alert"); 27 | message.setText("This is a test email from your Expiry Alert application. If you receive this, the email configuration is working correctly!"); 28 | 29 | emailSender.send(message); 30 | return "Test email sent successfully!"; 31 | } catch (Exception e) { 32 | return "Error sending email: " + e.getMessage(); 33 | } 34 | } 35 | 36 | @PostMapping("/add-item") 37 | public String addCustomItem(@RequestParam String name, @RequestParam String expiryDate) { 38 | try { 39 | Item item = new Item(); 40 | item.setName(name); 41 | item.setExpiry(LocalDate.parse(expiryDate)); 42 | itemService.saveItem(item); 43 | 44 | // Calculate days until expiry 45 | long daysUntilExpiry = java.time.temporal.ChronoUnit.DAYS.between(LocalDate.now(), item.getExpiry()); 46 | 47 | String response = String.format(""" 48 | Item added successfully! 49 | Name: %s 50 | Expiry Date: %s 51 | Days until expiry: %d 52 | 53 | %s 54 | """, 55 | name, 56 | expiryDate, 57 | daysUntilExpiry, 58 | daysUntilExpiry <= 3 ? "You will receive an alert for this item!" : "No alert will be sent (expires in more than 3 days)" 59 | ); 60 | 61 | return response; 62 | } catch (Exception e) { 63 | return "Error adding item: " + e.getMessage(); 64 | } 65 | } 66 | 67 | @PostMapping("/add-test-items") 68 | public String addTestItems() { 69 | try { 70 | // Items expiring today (URGENT) 71 | Item item1 = new Item(); 72 | item1.setName("Milk"); 73 | item1.setExpiry(LocalDate.now()); 74 | itemService.saveItem(item1); 75 | 76 | Item item2 = new Item(); 77 | item2.setName("Yogurt"); 78 | item2.setExpiry(LocalDate.now()); 79 | itemService.saveItem(item2); 80 | 81 | // Items expiring in 2 days (WARNING) 82 | Item item3 = new Item(); 83 | item3.setName("Bread"); 84 | item3.setExpiry(LocalDate.now().plusDays(2)); 85 | itemService.saveItem(item3); 86 | 87 | Item item4 = new Item(); 88 | item4.setName("Eggs"); 89 | item4.setExpiry(LocalDate.now().plusDays(2)); 90 | itemService.saveItem(item4); 91 | 92 | // Items expiring in 5 days (No alert) 93 | Item item5 = new Item(); 94 | item5.setName("Cheese"); 95 | item5.setExpiry(LocalDate.now().plusDays(5)); 96 | itemService.saveItem(item5); 97 | 98 | Item item6 = new Item(); 99 | item6.setName("Butter"); 100 | item6.setExpiry(LocalDate.now().plusDays(5)); 101 | itemService.saveItem(item6); 102 | 103 | return "Test items added successfully! You should receive alerts for:\n" + 104 | "- Milk (expiring today)\n" + 105 | "- Yogurt (expiring today)\n" + 106 | "- Bread (expiring in 2 days)\n" + 107 | "- Eggs (expiring in 2 days)\n\n" + 108 | "No alerts for:\n" + 109 | "- Cheese (expiring in 5 days)\n" + 110 | "- Butter (expiring in 5 days)"; 111 | } catch (Exception e) { 112 | return "Error adding test items: " + e.getMessage(); 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/model/Item.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.model; 2 | 3 | import lombok.Data; 4 | import jakarta.persistence.*; 5 | import java.time.LocalDate; 6 | 7 | @Data 8 | @Entity 9 | @Table(name = "items") 10 | public class Item { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | private Long id; 14 | 15 | @Column(nullable = false) 16 | private String name; 17 | 18 | @Column(nullable = false) 19 | private LocalDate expiry; 20 | 21 | @ManyToOne 22 | @JoinColumn(name = "location_id") 23 | private StorageLocation location; 24 | 25 | private boolean wasted; 26 | 27 | private LocalDate wastedDate; 28 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/model/StorageLocation.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.model; 2 | 3 | import lombok.Data; 4 | import jakarta.persistence.*; 5 | import java.util.List; 6 | 7 | @Data 8 | @Entity 9 | @Table(name = "storage_locations") 10 | public class StorageLocation { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | private Long id; 14 | 15 | @Column(nullable = false) 16 | private String name; 17 | 18 | private String description; 19 | 20 | private String temperature; 21 | 22 | @OneToMany(mappedBy = "location") 23 | private List items; 24 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/repository/ItemRepository.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.repository; 2 | 3 | import com.expiryalert.model.Item; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import java.time.LocalDate; 6 | import java.util.List; 7 | 8 | public interface ItemRepository extends JpaRepository { 9 | List findByExpiryBeforeAndWastedFalse(LocalDate date); 10 | List findByWastedTrue(); 11 | List findByLocationId(Long locationId); 12 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/repository/StorageLocationRepository.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.repository; 2 | 3 | import com.expiryalert.model.StorageLocation; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import java.util.List; 6 | 7 | public interface StorageLocationRepository extends JpaRepository { 8 | List findByNameContainingIgnoreCase(String name); 9 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/service/ItemService.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.service; 2 | 3 | import com.expiryalert.model.Item; 4 | import com.expiryalert.repository.ItemRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import java.time.LocalDate; 8 | import java.util.List; 9 | 10 | @Service 11 | public class ItemService { 12 | 13 | @Autowired 14 | private ItemRepository itemRepository; 15 | 16 | public List findItemsExpiringBefore(LocalDate date) { 17 | return itemRepository.findByExpiryBeforeAndWastedFalse(date); 18 | } 19 | 20 | public List getAllItems() { 21 | return itemRepository.findAll(); 22 | } 23 | 24 | public Item saveItem(Item item) { 25 | return itemRepository.save(item); 26 | } 27 | 28 | public void deleteItem(Long id) { 29 | itemRepository.deleteById(id); 30 | } 31 | 32 | public Item markAsWasted(Long id) { 33 | Item item = itemRepository.findById(id) 34 | .orElseThrow(() -> new RuntimeException("Item not found")); 35 | 36 | item.setWasted(true); 37 | item.setWastedDate(LocalDate.now()); 38 | return itemRepository.save(item); 39 | } 40 | 41 | public List getWastedItems() { 42 | return itemRepository.findByWastedTrue(); 43 | } 44 | 45 | public List getItemsByLocation(Long locationId) { 46 | return itemRepository.findByLocationId(locationId); 47 | } 48 | 49 | public Item createItem(Item item) { 50 | return itemRepository.save(item); 51 | } 52 | 53 | public Item updateItem(Long id, Item itemDetails) { 54 | Item item = itemRepository.findById(id) 55 | .orElseThrow(() -> new RuntimeException("Item not found")); 56 | 57 | item.setName(itemDetails.getName()); 58 | item.setExpiry(itemDetails.getExpiry()); 59 | 60 | return itemRepository.save(item); 61 | } 62 | 63 | public String getItemStatus(LocalDate expiry) { 64 | LocalDate today = LocalDate.now(); 65 | long diff = java.time.temporal.ChronoUnit.DAYS.between(today, expiry); 66 | 67 | if (diff < 0) return "expired"; 68 | if (diff <= 3) return "near"; 69 | return "safe"; 70 | } 71 | } -------------------------------------------------------------------------------- /backend/src/main/java/com/expiryalert/service/NotificationService.java: -------------------------------------------------------------------------------- 1 | package com.expiryalert.service; 2 | 3 | import com.expiryalert.model.Item; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.mail.SimpleMailMessage; 6 | import org.springframework.mail.javamail.JavaMailSender; 7 | import org.springframework.scheduling.annotation.Scheduled; 8 | import org.springframework.stereotype.Service; 9 | import java.time.LocalDate; 10 | import java.time.temporal.ChronoUnit; 11 | import java.util.List; 12 | 13 | @Service 14 | public class NotificationService { 15 | 16 | @Autowired 17 | private JavaMailSender emailSender; 18 | 19 | @Autowired 20 | private ItemService itemService; 21 | 22 | @Scheduled(cron = "0 * * * * ?") 23 | public void checkExpiringItems() { 24 | System.out.println("Checking for expiring items..."); 25 | LocalDate threeDaysFromNow = LocalDate.now().plusDays(3); 26 | List expiringItems = itemService.findItemsExpiringBefore(threeDaysFromNow); 27 | 28 | for (Item item : expiringItems) { 29 | sendExpiryNotification(item); 30 | } 31 | } 32 | 33 | private void sendExpiryNotification(Item item) { 34 | SimpleMailMessage message = new SimpleMailMessage(); 35 | message.setTo("rmkoushika3115@gmail.com"); 36 | message.setSubject("⚠️ EXPIRY ALERT: " + item.getName()); 37 | 38 | // Calculate days until expiry 39 | long daysUntilExpiry = ChronoUnit.DAYS.between(LocalDate.now(), item.getExpiry()); 40 | String urgencyLevel = daysUntilExpiry == 0 ? "URGENT" : "WARNING"; 41 | 42 | // Create alert-style message 43 | String alertMessage = String.format(""" 44 | ⚠️ EXPIRY ALERT ⚠️ 45 | =================== 46 | 47 | Product: %s 48 | Expiry Date: %s 49 | Days Remaining: %d 50 | Status: %s 51 | 52 | ACTION REQUIRED: 53 | Please check this item immediately! 54 | 55 | Location: %s 56 | 57 | This is an automated alert from your Expiry Alert System. 58 | =================== 59 | """, 60 | item.getName(), 61 | item.getExpiry(), 62 | daysUntilExpiry, 63 | urgencyLevel, 64 | item.getLocation() != null ? item.getLocation().getName() : "Not specified" 65 | ); 66 | 67 | message.setText(alertMessage); 68 | emailSender.send(message); 69 | System.out.println("Alert sent for item: " + item.getName()); 70 | } 71 | } -------------------------------------------------------------------------------- /backend/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Database Configuration 2 | spring.datasource.url=jdbc:h2:mem:expiryalertdb 3 | spring.datasource.driverClassName=org.h2.Driver 4 | spring.datasource.username=sa 5 | spring.datasource.password= 6 | spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 7 | spring.h2.console.enabled=true 8 | 9 | # JPA Configuration 10 | spring.jpa.hibernate.ddl-auto=update 11 | spring.jpa.show-sql=true 12 | 13 | # Email Configuration 14 | spring.mail.host=smtp.gmail.com 15 | spring.mail.port=587 16 | spring.mail.username=rmkoushika3115@gmail.com 17 | spring.mail.password=ktaphsluisuptgxk 18 | spring.mail.properties.mail.smtp.auth=true 19 | spring.mail.properties.mail.smtp.starttls.enable=true 20 | 21 | # Server Configuration 22 | server.port=8080 -------------------------------------------------------------------------------- /backend/src/main/resources/static/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple Test 5 | 14 | 15 | 16 |

If you can see this with a red background, static files are working!

17 | 18 | -------------------------------------------------------------------------------- /backend/src/main/resources/static/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Expiry Alert System 5 | 141 | 142 | 143 |
144 |

Expiry Alert System

145 | 146 |
147 | 148 | 149 |
150 | 151 |
152 |

Add Custom Item

153 |
154 | 155 | 156 |
157 |
158 | 159 | 160 |
161 | 162 |
163 | 164 |
165 |
166 | 167 | 215 | 216 | -------------------------------------------------------------------------------- /backend/src/main/resources/static/test2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Expiry Alert System - Test 2 5 | 141 | 142 | 143 |
144 |

Expiry Alert System - Test 2

145 | 146 |
147 | 148 | 149 |
150 | 151 |
152 |

Add Custom Item

153 |
154 | 155 | 156 |
157 |
158 | 159 | 160 |
161 | 162 |
163 | 164 |
165 |
166 | 167 | 215 | 216 | -------------------------------------------------------------------------------- /backend/target/classes/application.properties: -------------------------------------------------------------------------------- 1 | # Database Configuration 2 | spring.datasource.url=jdbc:h2:mem:expiryalertdb 3 | spring.datasource.driverClassName=org.h2.Driver 4 | spring.datasource.username=sa 5 | spring.datasource.password= 6 | spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 7 | spring.h2.console.enabled=true 8 | 9 | # JPA Configuration 10 | spring.jpa.hibernate.ddl-auto=update 11 | spring.jpa.show-sql=true 12 | 13 | # Email Configuration 14 | spring.mail.host=smtp.gmail.com 15 | spring.mail.port=587 16 | spring.mail.username=rmkoushika3115@gmail.com 17 | spring.mail.password=ktaphsluisuptgxk 18 | spring.mail.properties.mail.smtp.auth=true 19 | spring.mail.properties.mail.smtp.starttls.enable=true 20 | 21 | # Server Configuration 22 | server.port=8080 -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/ExpiryAlertApplication.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/ExpiryAlertApplication.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/config/OpenApiConfig.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/config/OpenApiConfig.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/controller/ItemController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/controller/ItemController.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/controller/StorageLocationController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/controller/StorageLocationController.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/controller/TestController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/controller/TestController.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/model/Item.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/model/Item.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/model/StorageLocation.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/model/StorageLocation.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/repository/ItemRepository.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/repository/ItemRepository.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/repository/StorageLocationRepository.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/repository/StorageLocationRepository.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/service/ItemService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/service/ItemService.class -------------------------------------------------------------------------------- /backend/target/classes/com/expiryalert/service/NotificationService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/backend/target/classes/com/expiryalert/service/NotificationService.class -------------------------------------------------------------------------------- /backend/target/classes/static/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple Test 5 | 14 | 15 | 16 |

If you can see this with a red background, static files are working!

17 | 18 | -------------------------------------------------------------------------------- /backend/target/classes/static/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Expiry Alert System 5 | 141 | 142 | 143 |
144 |

Expiry Alert System

145 | 146 |
147 | 148 | 149 |
150 | 151 |
152 |

Add Custom Item

153 |
154 | 155 | 156 |
157 |
158 | 159 | 160 |
161 | 162 |
163 | 164 |
165 |
166 | 167 | 215 | 216 | -------------------------------------------------------------------------------- /backend/target/classes/static/test2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Expiry Alert System - Test 2 5 | 141 | 142 | 143 |
144 |

Expiry Alert System - Test 2

145 | 146 |
147 | 148 | 149 |
150 | 151 |
152 |

Add Custom Item

153 |
154 | 155 | 156 |
157 |
158 | 159 | 160 |
161 | 162 |
163 | 164 |
165 |
166 | 167 | 215 | 216 | -------------------------------------------------------------------------------- /backend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst: -------------------------------------------------------------------------------- 1 | com\expiryalert\controller\TestController.class 2 | com\expiryalert\config\OpenApiConfig.class 3 | com\expiryalert\controller\StorageLocationController.class 4 | com\expiryalert\repository\StorageLocationRepository.class 5 | com\expiryalert\model\StorageLocation.class 6 | com\expiryalert\service\NotificationService.class 7 | com\expiryalert\ExpiryAlertApplication.class 8 | com\expiryalert\model\Item.class 9 | com\expiryalert\service\ItemService.class 10 | com\expiryalert\repository\ItemRepository.class 11 | com\expiryalert\controller\ItemController.class 12 | -------------------------------------------------------------------------------- /backend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst: -------------------------------------------------------------------------------- 1 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\controller\StorageLocationController.java 2 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\model\Item.java 3 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\repository\ItemRepository.java 4 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\controller\ItemController.java 5 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\config\OpenApiConfig.java 6 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\model\StorageLocation.java 7 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\service\NotificationService.java 8 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\controller\TestController.java 9 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\repository\StorageLocationRepository.java 10 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\service\ItemService.java 11 | C:\Users\preth\OneDrive\Documents\ExpiryAlertProject\backend\src\main\java\com\expiryalert\ExpiryAlertApplication.java 12 | -------------------------------------------------------------------------------- /expiryalertsystem.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koushika31/SpringBoot-ExpiryAlertSystem/26f027d4dcbb21f893ce793fa10f942de2a8f959/expiryalertsystem.mp4 -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expiryalert", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^18.2.0", 7 | "react-dom": "^18.2.0", 8 | "react-scripts": "5.0.1" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "browserslist": { 17 | "production": [ 18 | ">0.2%", 19 | "not dead", 20 | "not op_mini all" 21 | ], 22 | "development": [ 23 | "last 1 chrome version", 24 | "last 1 firefox version", 25 | "last 1 safari version" 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ExpiryAlert 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import './styles.css'; 3 | 4 | function App() { 5 | const [items, setItems] = useState([]); 6 | const [newItem, setNewItem] = useState({ name: '', expiry: '' }); 7 | const [message, setMessage] = useState(''); 8 | 9 | useEffect(() => { 10 | fetchItems(); 11 | }, []); 12 | 13 | const fetchItems = async () => { 14 | try { 15 | const response = await fetch('http://localhost:8080/api/items'); 16 | const data = await response.json(); 17 | setItems(data); 18 | } catch (error) { 19 | setMessage('Error fetching items: ' + error.message); 20 | } 21 | }; 22 | 23 | const handleSubmit = async (e) => { 24 | e.preventDefault(); 25 | try { 26 | const response = await fetch('http://localhost:8080/api/items', { 27 | method: 'POST', 28 | headers: { 29 | 'Content-Type': 'application/json', 30 | }, 31 | body: JSON.stringify(newItem), 32 | }); 33 | 34 | if (response.ok) { 35 | setMessage('Item added successfully!'); 36 | setNewItem({ name: '', expiry: '' }); 37 | fetchItems(); 38 | } else { 39 | setMessage('Error adding item'); 40 | } 41 | } catch (error) { 42 | setMessage('Error: ' + error.message); 43 | } 44 | }; 45 | 46 | const getExpiryStatus = (expiryDate) => { 47 | const today = new Date(); 48 | const expiry = new Date(expiryDate); 49 | const diffTime = expiry - today; 50 | const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); 51 | 52 | if (diffDays < 0) return 'expired'; 53 | if (diffDays <= 3) return 'warning'; 54 | if (diffDays <= 7) return 'soon'; 55 | return 'ok'; 56 | }; 57 | 58 | const getExpiryClass = (status) => { 59 | switch (status) { 60 | case 'expired': return 'expiry-warning'; 61 | case 'warning': return 'expiry-warning'; 62 | case 'soon': return 'expiry-soon'; 63 | default: return 'expiry-ok'; 64 | } 65 | }; 66 | 67 | return ( 68 |
69 |

Expiry Alert System

70 | 71 |
72 |
73 | 74 | setNewItem({ ...newItem, name: e.target.value })} 79 | placeholder="Enter item name" 80 | required 81 | /> 82 |
83 |
84 | 85 | setNewItem({ ...newItem, expiry: e.target.value })} 90 | required 91 | /> 92 |
93 | 94 |
95 | 96 | {message && ( 97 |
98 | {message} 99 |
100 | )} 101 | 102 |
103 |

Your Items

104 | {items.map((item) => { 105 | const status = getExpiryStatus(item.expiry); 106 | const statusClass = getExpiryClass(status); 107 | return ( 108 |
109 |
{item.name}
110 |
111 | Expires: {new Date(item.expiry).toLocaleDateString()} 112 | {status === 'expired' && ' (Expired)'} 113 | {status === 'warning' && ' (Expiring soon!)'} 114 | {status === 'soon' && ' (Expiring in a week)'} 115 |
116 |
117 | ); 118 | })} 119 |
120 |
121 | ); 122 | } 123 | 124 | export default App; 125 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | import './styles.css'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render(); -------------------------------------------------------------------------------- /frontend/src/shelfLife.js: -------------------------------------------------------------------------------- 1 | const shelfLife = { 2 | "milk": 7, 3 | "bread": 5, 4 | "eggs": 21, 5 | "yogurt": 10, 6 | "cheese": 14, 7 | "vegetables": 5, 8 | "fruits": 5, 9 | "juice": 10 10 | }; 11 | 12 | export default shelfLife; -------------------------------------------------------------------------------- /frontend/src/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 3 | margin: 0; 4 | padding: 0; 5 | min-height: 100vh; 6 | background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); 7 | background-size: 400% 400%; 8 | animation: gradient 15s ease infinite; 9 | } 10 | 11 | @keyframes gradient { 12 | 0% { 13 | background-position: 0% 50%; 14 | } 15 | 50% { 16 | background-position: 100% 50%; 17 | } 18 | 100% { 19 | background-position: 0% 50%; 20 | } 21 | } 22 | 23 | .container { 24 | background: rgba(255, 255, 255, 0.9); 25 | padding: 2rem; 26 | border-radius: 15px; 27 | box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); 28 | backdrop-filter: blur(4px); 29 | width: 80%; 30 | max-width: 600px; 31 | margin: 2rem auto; 32 | } 33 | 34 | h1 { 35 | color: #2c3e50; 36 | text-align: center; 37 | margin-bottom: 2rem; 38 | font-size: 2.5rem; 39 | text-shadow: 2px 2px 4px rgba(0,0,0,0.1); 40 | } 41 | 42 | .button-group { 43 | display: flex; 44 | flex-direction: column; 45 | gap: 1rem; 46 | } 47 | 48 | button { 49 | padding: 1rem 2rem; 50 | border: none; 51 | border-radius: 8px; 52 | background: linear-gradient(45deg, #2196F3, #00BCD4); 53 | color: white; 54 | font-size: 1.1rem; 55 | cursor: pointer; 56 | transition: transform 0.3s ease, box-shadow 0.3s ease; 57 | text-transform: uppercase; 58 | letter-spacing: 1px; 59 | } 60 | 61 | button:hover { 62 | transform: translateY(-3px); 63 | box-shadow: 0 5px 15px rgba(0,0,0,0.3); 64 | } 65 | 66 | button:active { 67 | transform: translateY(0); 68 | } 69 | 70 | .form-group { 71 | margin-bottom: 1rem; 72 | } 73 | 74 | label { 75 | display: block; 76 | margin-bottom: 0.5rem; 77 | color: #2c3e50; 78 | font-weight: bold; 79 | } 80 | 81 | input { 82 | width: 100%; 83 | padding: 0.8rem; 84 | border: 2px solid #e0e0e0; 85 | border-radius: 6px; 86 | font-size: 1rem; 87 | transition: border-color 0.3s ease; 88 | } 89 | 90 | input:focus { 91 | outline: none; 92 | border-color: #2196F3; 93 | } 94 | 95 | .item-list { 96 | margin-top: 2rem; 97 | padding: 1.5rem; 98 | border-radius: 8px; 99 | background: rgba(255, 255, 255, 0.8); 100 | border: 1px solid rgba(255, 255, 255, 0.18); 101 | } 102 | 103 | .item-card { 104 | background: white; 105 | padding: 1rem; 106 | margin-bottom: 1rem; 107 | border-radius: 8px; 108 | box-shadow: 0 2px 8px rgba(0,0,0,0.1); 109 | transition: transform 0.3s ease; 110 | } 111 | 112 | .item-card:hover { 113 | transform: translateY(-2px); 114 | } 115 | 116 | .item-name { 117 | font-size: 1.2rem; 118 | font-weight: bold; 119 | color: #2c3e50; 120 | margin-bottom: 0.5rem; 121 | } 122 | 123 | .item-expiry { 124 | color: #666; 125 | font-size: 0.9rem; 126 | } 127 | 128 | .expiry-warning { 129 | color: #e74c3c; 130 | font-weight: bold; 131 | } 132 | 133 | .expiry-soon { 134 | color: #f39c12; 135 | font-weight: bold; 136 | } 137 | 138 | .expiry-ok { 139 | color: #27ae60; 140 | font-weight: bold; 141 | } 142 | 143 | .alert { 144 | padding: 1rem; 145 | margin-top: 1rem; 146 | border-radius: 6px; 147 | } 148 | 149 | .alert-success { 150 | background-color: #d4edda; 151 | color: #155724; 152 | border: 1px solid #c3e6cb; 153 | } 154 | 155 | .alert-error { 156 | background-color: #f8d7da; 157 | color: #721c24; 158 | border: 1px solid #f5c6cb; 159 | } 160 | 161 | .dark { 162 | background-color: #121212; 163 | color: #fff; 164 | } 165 | 166 | .dark .container { 167 | background-color: #1f1f1f; 168 | } 169 | 170 | .header { 171 | display: flex; 172 | justify-content: space-between; 173 | align-items: center; 174 | } 175 | 176 | .input-group { 177 | display: flex; 178 | gap: 10px; 179 | margin: 10px 0; 180 | } 181 | 182 | .input-group input, .input-group button { 183 | flex: 1; 184 | padding: 10px; 185 | font-size: 16px; 186 | } 187 | 188 | .filters { 189 | display: flex; 190 | justify-content: space-around; 191 | margin: 10px 0; 192 | } 193 | 194 | .filters button { 195 | padding: 8px 12px; 196 | font-size: 14px; 197 | border: none; 198 | background-color: #007bff; 199 | color: white; 200 | border-radius: 6px; 201 | cursor: pointer; 202 | } 203 | 204 | .filters button:hover { 205 | background-color: #0056b3; 206 | } 207 | 208 | .active-filter { 209 | background-color: #17a2b8 !important; 210 | } 211 | 212 | .item-list, .wasted-list { 213 | list-style: none; 214 | padding: 0; 215 | } 216 | 217 | .item { 218 | display: flex; 219 | justify-content: space-between; 220 | align-items: center; 221 | margin: 6px 0; 222 | padding: 10px; 223 | border-radius: 6px; 224 | } 225 | 226 | .item-info { 227 | flex: 1; 228 | } 229 | 230 | .item-actions button { 231 | margin-left: 5px; 232 | padding: 6px 10px; 233 | font-size: 14px; 234 | cursor: pointer; 235 | } 236 | 237 | .safe { background-color: #d4edda; } 238 | .near { background-color: #fff3cd; } 239 | .expired { background-color: #f8d7da; } 240 | .wasted-list li { 241 | background: #e2e3e5; 242 | margin: 4px 0; 243 | padding: 8px; 244 | border-radius: 6px; 245 | } 246 | 247 | .dark .input-group input, 248 | .dark .input-group button, 249 | .dark .filters button { 250 | background-color: #333; 251 | color: #fff; 252 | } 253 | 254 | .dark .item-actions button { 255 | background-color: #555; 256 | color: white; 257 | } 258 | 259 | .dark .item { 260 | border: 1px solid #444; 261 | } 262 | --------------------------------------------------------------------------------