├── 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 |
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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------