Emmanuel Frimpong Asante
commited on
Commit
·
ebe0f8b
1
Parent(s):
781898e
update space
Browse files- .env +1 -0
- .idea/workspace.xml +10 -13
- README.md +29 -7
- backend/app/middleware/auth_middleware.py +1 -0
- backend/app/models/inventory_item.py +1 -0
- backend/app/routes/auth.py +3 -0
- backend/app/routes/chat.py +1 -0
- backend/app/routes/dashboard.py +0 -1
- backend/app/routes/farm.py +5 -4
- backend/app/routes/health.py +8 -5
- backend/app/routes/inventory.py +3 -2
- backend/app/routes/pages.py +10 -6
- backend/app/routes/task.py +1 -0
- backend/app/schemas/chat_history_schema.py +4 -1
- backend/app/schemas/inventory_item_schema.py +4 -2
- backend/app/schemas/user_schema.py +3 -1
- backend/app/services/disease_detection.py +0 -2
- backend/app/services/llama_service.py +2 -1
- backend/app/static/js/group.js +5 -5
- backend/app/static/js/health.js +3 -3
- backend/app/static/js/inventory.js +5 -6
- backend/app/static/js/tasks.js +2 -3
- backend/app/templates/auth/register.html +32 -27
- backend/app/templates/chat/index.html +259 -259
- backend/app/templates/dashboard.html +76 -76
- backend/app/templates/group/manage_group.html +44 -41
- backend/app/templates/health/log_health.html +27 -25
- backend/app/templates/health/manage_health.html +49 -47
- backend/app/templates/inventory/add_inventory.html +46 -46
- backend/app/templates/inventory/view_inventory.html +65 -62
- backend/app/templates/landing.html +4 -2
- backend/app/templates/main/base.html +16 -7
- backend/app/templates/main/partials/sidebar.html +22 -15
- backend/app/templates/todo/add_task.html +47 -47
- backend/app/templates/todo/manage_tasks.html +71 -69
- test_main.http +4 -1
.env
CHANGED
|
@@ -14,6 +14,7 @@ EMAIL_PASSWORD=your-email-password
|
|
| 14 |
EMAIL_FROM_NAME="Poultry Management System"
|
| 15 |
EMAIL_RECEIVER="default-receiver@example.com" # default receiver for general notifications
|
| 16 |
|
|
|
|
| 17 |
|
| 18 |
# TensorFlow Settings
|
| 19 |
TF_ENABLE_ONEDNN_OPTS=0
|
|
|
|
| 14 |
EMAIL_FROM_NAME="Poultry Management System"
|
| 15 |
EMAIL_RECEIVER="default-receiver@example.com" # default receiver for general notifications
|
| 16 |
|
| 17 |
+
JWT_SECRET_KEY="mypass"
|
| 18 |
|
| 19 |
# TensorFlow Settings
|
| 20 |
TF_ENABLE_ONEDNN_OPTS=0
|
.idea/workspace.xml
CHANGED
|
@@ -4,10 +4,7 @@
|
|
| 4 |
<option name="autoReloadType" value="SELECTIVE" />
|
| 5 |
</component>
|
| 6 |
<component name="ChangeListManager">
|
| 7 |
-
<list default="true" id="27c9ae1a-a6fa-4472-8bcd-a7087620894b" name="Changes" comment="update space"
|
| 8 |
-
<change beforePath="$PROJECT_DIR$/.env" beforeDir="false" afterPath="$PROJECT_DIR$/.env" afterDir="false" />
|
| 9 |
-
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
| 10 |
-
</list>
|
| 11 |
<option name="SHOW_DIALOG" value="false" />
|
| 12 |
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
| 13 |
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
@@ -128,14 +125,6 @@
|
|
| 128 |
<workItem from="1730454506390" duration="12672000" />
|
| 129 |
<workItem from="1730494385249" duration="17097000" />
|
| 130 |
</task>
|
| 131 |
-
<task id="LOCAL-00103" summary="update space">
|
| 132 |
-
<option name="closed" value="true" />
|
| 133 |
-
<created>1730470756352</created>
|
| 134 |
-
<option name="number" value="00103" />
|
| 135 |
-
<option name="presentableId" value="LOCAL-00103" />
|
| 136 |
-
<option name="project" value="LOCAL" />
|
| 137 |
-
<updated>1730470756352</updated>
|
| 138 |
-
</task>
|
| 139 |
<task id="LOCAL-00104" summary="update space">
|
| 140 |
<option name="closed" value="true" />
|
| 141 |
<created>1730471112648</created>
|
|
@@ -520,7 +509,15 @@
|
|
| 520 |
<option name="project" value="LOCAL" />
|
| 521 |
<updated>1731736727627</updated>
|
| 522 |
</task>
|
| 523 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 524 |
<servers />
|
| 525 |
</component>
|
| 526 |
<component name="TypeScriptGeneratedFilesManager">
|
|
|
|
| 4 |
<option name="autoReloadType" value="SELECTIVE" />
|
| 5 |
</component>
|
| 6 |
<component name="ChangeListManager">
|
| 7 |
+
<list default="true" id="27c9ae1a-a6fa-4472-8bcd-a7087620894b" name="Changes" comment="update space" />
|
|
|
|
|
|
|
|
|
|
| 8 |
<option name="SHOW_DIALOG" value="false" />
|
| 9 |
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
| 10 |
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
|
|
| 125 |
<workItem from="1730454506390" duration="12672000" />
|
| 126 |
<workItem from="1730494385249" duration="17097000" />
|
| 127 |
</task>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
<task id="LOCAL-00104" summary="update space">
|
| 129 |
<option name="closed" value="true" />
|
| 130 |
<created>1730471112648</created>
|
|
|
|
| 509 |
<option name="project" value="LOCAL" />
|
| 510 |
<updated>1731736727627</updated>
|
| 511 |
</task>
|
| 512 |
+
<task id="LOCAL-00152" summary="update space">
|
| 513 |
+
<option name="closed" value="true" />
|
| 514 |
+
<created>1731737347454</created>
|
| 515 |
+
<option name="number" value="00152" />
|
| 516 |
+
<option name="presentableId" value="LOCAL-00152" />
|
| 517 |
+
<option name="project" value="LOCAL" />
|
| 518 |
+
<updated>1731737347454</updated>
|
| 519 |
+
</task>
|
| 520 |
+
<option name="localTasksCounter" value="153" />
|
| 521 |
<servers />
|
| 522 |
</component>
|
| 523 |
<component name="TypeScriptGeneratedFilesManager">
|
README.md
CHANGED
|
@@ -14,11 +14,16 @@ Here’s the updated `README.md` with an improved structure and updated to-do li
|
|
| 14 |
|
| 15 |
# 🐔 Poultry Farming Assistance and Management System
|
| 16 |
|
| 17 |
-
This project combines a **Poultry Farming Assistance System** and a **Poultry Management System**, streamlining health
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
## Key Features
|
|
|
|
| 20 |
1. **User Authentication and Role-Based Access**: Secure registration and login for farmers and admins.
|
| 21 |
-
2. **Health Management with AI-Driven Disease Detection**: Fecal image analysis for common poultry diseases and
|
|
|
|
| 22 |
3. **To-Do List and Task Management**: Track daily farm tasks and share across groups.
|
| 23 |
4. **Inventory Management**: Monitor feed, medication, and supply levels with automated alerts.
|
| 24 |
5. **Data Logging and Reporting**: Real-time dashboards, export options, and health reports.
|
|
@@ -29,11 +34,14 @@ This project combines a **Poultry Farming Assistance System** and a **Poultry Ma
|
|
| 29 |
## System Overview
|
| 30 |
|
| 31 |
### Poultry Farming Assistance System
|
| 32 |
-
|
|
|
|
|
|
|
| 33 |
- **Health Monitoring**: Tracks metrics such as weight loss, mortality rate, and feed intake for proactive care.
|
| 34 |
- **Treatment Recommendations**: Offers tailored suggestions based on detected health issues.
|
| 35 |
|
| 36 |
### Poultry Management System
|
|
|
|
| 37 |
- **Task and To-Do List Management**: Allows farm admins to assign tasks and track completion across groups.
|
| 38 |
- **Inventory Tracking**: Manages inventory levels and provides alerts for low-stock items.
|
| 39 |
- **Health Records**: Logs poultry health data and tracks disease outbreaks.
|
|
@@ -42,6 +50,7 @@ This project combines a **Poultry Farming Assistance System** and a **Poultry Ma
|
|
| 42 |
---
|
| 43 |
|
| 44 |
## Use Cases
|
|
|
|
| 45 |
- **Farmers**: Access disease detection, complete tasks, view health records, and receive alerts.
|
| 46 |
- **Admins**: Create tasks, monitor group activities, manage inventory, and ensure flock health.
|
| 47 |
|
|
@@ -50,16 +59,20 @@ This project combines a **Poultry Farming Assistance System** and a **Poultry Ma
|
|
| 50 |
## 📝 To-Do List
|
| 51 |
|
| 52 |
### 1. Setup and Configuration
|
| 53 |
-
|
|
|
|
|
|
|
| 54 |
- [x] Configure MongoDB and test CRUD operations.
|
| 55 |
- [x] Establish environment variables for MongoDB, email credentials, and Hugging Face API token.
|
| 56 |
|
| 57 |
### 2. Authentication System
|
|
|
|
| 58 |
- [x] Develop user registration for farmers and admins.
|
| 59 |
- [x] Implement login/logout with JWT.
|
| 60 |
- [x] Set up password encryption (`bcrypt`) for secure storage.
|
| 61 |
|
| 62 |
### 3. Poultry Farming Assistance System
|
|
|
|
| 63 |
- [x] Integrate the poultry disease detection model (`Final_Chicken_disease_model.h5`).
|
| 64 |
- [x] Set up image preprocessing for disease detection.
|
| 65 |
- [x] Build endpoints for image upload and disease analysis.
|
|
@@ -67,48 +80,56 @@ This project combines a **Poultry Farming Assistance System** and a **Poultry Ma
|
|
| 67 |
- [x] Implement real-time health monitoring and alerts.
|
| 68 |
|
| 69 |
### 4. To-Do List Management
|
|
|
|
| 70 |
- [x] Design MongoDB schema for to-do lists.
|
| 71 |
- [x] Create routes for farmers to view and mark to-do items.
|
| 72 |
- [x] Build functionality for admins to assign tasks.
|
| 73 |
- [x] Develop an **AdminLTE 4**-based UI for managing tasks.
|
| 74 |
|
| 75 |
### 5. Data Logging and Reporting
|
|
|
|
| 76 |
- [x] Implement data logging for activities and health records.
|
| 77 |
- [x] Build reporting dashboards using **AdminLTE**.
|
| 78 |
- [x] Enable export functionality (PDF, XLS).
|
| 79 |
- [x] Integrate real-time visualization tools for health and productivity insights.
|
| 80 |
|
| 81 |
### 6. Health Management
|
|
|
|
| 82 |
- [x] Define MongoDB schema for health records.
|
| 83 |
- [x] Integrate disease detection data into health management.
|
| 84 |
- [x] Develop an **AdminLTE** dashboard for disease and health tracking.
|
| 85 |
- [x] Automate health alerts and treatment recommendations.
|
| 86 |
|
| 87 |
### 7. Inventory Management
|
|
|
|
| 88 |
- [x] Design MongoDB schema for inventory tracking.
|
| 89 |
- [x] Implement routes for adding, updating, and managing inventory items.
|
| 90 |
- [x] Create an **AdminLTE** dashboard for inventory status.
|
| 91 |
- [x] Set up email alerts for low-stock items.
|
| 92 |
|
| 93 |
### 8. Notification System
|
|
|
|
| 94 |
- [x] Configure email service for notifications (`smtplib` or `nodemailer`).
|
| 95 |
- [x] Implement notifications for:
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
- [x] Test the notification system across scenarios.
|
| 100 |
|
| 101 |
### 9. Group Management
|
|
|
|
| 102 |
- [x] Create MongoDB schema for managing user groups.
|
| 103 |
- [x] Allow admins to create groups and share tasks.
|
| 104 |
- [x] Enable farmers to join groups and track shared tasks.
|
| 105 |
|
| 106 |
### 10. Testing and Debugging
|
|
|
|
| 107 |
- [ ] Write unit tests for modules (authentication, to-do lists, health management, inventory, etc.).
|
| 108 |
- [ ] Conduct integration testing to validate end-to-end functionality.
|
| 109 |
- [ ] Debug MongoDB transactions, pytorch predictions, and AdminLTE dashboards.
|
| 110 |
|
| 111 |
### 11. Deployment
|
|
|
|
| 112 |
- [ ] Deploy the application on Hugging Face Spaces or other cloud platforms.
|
| 113 |
- [ ] Configure production settings (environment variables, security).
|
| 114 |
- [ ] Set up CI/CD pipeline for automatic testing and deployment.
|
|
@@ -116,6 +137,7 @@ This project combines a **Poultry Farming Assistance System** and a **Poultry Ma
|
|
| 116 |
---
|
| 117 |
|
| 118 |
## Future Enhancements
|
|
|
|
| 119 |
- **Mobile Application**: Develop a mobile app using `Flutter` for remote management.
|
| 120 |
- **AI-Based Inventory Forecasting**: Predict inventory needs based on usage trends.
|
| 121 |
- **Multi-Language Support**: Localize the interface for global users.
|
|
|
|
| 14 |
|
| 15 |
# 🐔 Poultry Farming Assistance and Management System
|
| 16 |
|
| 17 |
+
This project combines a **Poultry Farming Assistance System** and a **Poultry Management System**, streamlining health
|
| 18 |
+
monitoring, task management, inventory tracking, and disease detection using AI. Designed for farmers and farm admins,
|
| 19 |
+
this system enables effective poultry health management, provides real-time alerts, and generates actionable insights to
|
| 20 |
+
enhance productivity.
|
| 21 |
|
| 22 |
## Key Features
|
| 23 |
+
|
| 24 |
1. **User Authentication and Role-Based Access**: Secure registration and login for farmers and admins.
|
| 25 |
+
2. **Health Management with AI-Driven Disease Detection**: Fecal image analysis for common poultry diseases and
|
| 26 |
+
personalized treatment recommendations.
|
| 27 |
3. **To-Do List and Task Management**: Track daily farm tasks and share across groups.
|
| 28 |
4. **Inventory Management**: Monitor feed, medication, and supply levels with automated alerts.
|
| 29 |
5. **Data Logging and Reporting**: Real-time dashboards, export options, and health reports.
|
|
|
|
| 34 |
## System Overview
|
| 35 |
|
| 36 |
### Poultry Farming Assistance System
|
| 37 |
+
|
| 38 |
+
- **AI-Powered Disease Detection**: Uses pre-trained models (e.g., `Final_Chicken_disease_model.h5`) to analyze fecal
|
| 39 |
+
images and identify poultry diseases.
|
| 40 |
- **Health Monitoring**: Tracks metrics such as weight loss, mortality rate, and feed intake for proactive care.
|
| 41 |
- **Treatment Recommendations**: Offers tailored suggestions based on detected health issues.
|
| 42 |
|
| 43 |
### Poultry Management System
|
| 44 |
+
|
| 45 |
- **Task and To-Do List Management**: Allows farm admins to assign tasks and track completion across groups.
|
| 46 |
- **Inventory Tracking**: Manages inventory levels and provides alerts for low-stock items.
|
| 47 |
- **Health Records**: Logs poultry health data and tracks disease outbreaks.
|
|
|
|
| 50 |
---
|
| 51 |
|
| 52 |
## Use Cases
|
| 53 |
+
|
| 54 |
- **Farmers**: Access disease detection, complete tasks, view health records, and receive alerts.
|
| 55 |
- **Admins**: Create tasks, monitor group activities, manage inventory, and ensure flock health.
|
| 56 |
|
|
|
|
| 59 |
## 📝 To-Do List
|
| 60 |
|
| 61 |
### 1. Setup and Configuration
|
| 62 |
+
|
| 63 |
+
- [x] Set up a virtual environment and install required packages (`FastAPI`, `TensorFlow`, `MongoDB`, `transformers`,
|
| 64 |
+
etc.).
|
| 65 |
- [x] Configure MongoDB and test CRUD operations.
|
| 66 |
- [x] Establish environment variables for MongoDB, email credentials, and Hugging Face API token.
|
| 67 |
|
| 68 |
### 2. Authentication System
|
| 69 |
+
|
| 70 |
- [x] Develop user registration for farmers and admins.
|
| 71 |
- [x] Implement login/logout with JWT.
|
| 72 |
- [x] Set up password encryption (`bcrypt`) for secure storage.
|
| 73 |
|
| 74 |
### 3. Poultry Farming Assistance System
|
| 75 |
+
|
| 76 |
- [x] Integrate the poultry disease detection model (`Final_Chicken_disease_model.h5`).
|
| 77 |
- [x] Set up image preprocessing for disease detection.
|
| 78 |
- [x] Build endpoints for image upload and disease analysis.
|
|
|
|
| 80 |
- [x] Implement real-time health monitoring and alerts.
|
| 81 |
|
| 82 |
### 4. To-Do List Management
|
| 83 |
+
|
| 84 |
- [x] Design MongoDB schema for to-do lists.
|
| 85 |
- [x] Create routes for farmers to view and mark to-do items.
|
| 86 |
- [x] Build functionality for admins to assign tasks.
|
| 87 |
- [x] Develop an **AdminLTE 4**-based UI for managing tasks.
|
| 88 |
|
| 89 |
### 5. Data Logging and Reporting
|
| 90 |
+
|
| 91 |
- [x] Implement data logging for activities and health records.
|
| 92 |
- [x] Build reporting dashboards using **AdminLTE**.
|
| 93 |
- [x] Enable export functionality (PDF, XLS).
|
| 94 |
- [x] Integrate real-time visualization tools for health and productivity insights.
|
| 95 |
|
| 96 |
### 6. Health Management
|
| 97 |
+
|
| 98 |
- [x] Define MongoDB schema for health records.
|
| 99 |
- [x] Integrate disease detection data into health management.
|
| 100 |
- [x] Develop an **AdminLTE** dashboard for disease and health tracking.
|
| 101 |
- [x] Automate health alerts and treatment recommendations.
|
| 102 |
|
| 103 |
### 7. Inventory Management
|
| 104 |
+
|
| 105 |
- [x] Design MongoDB schema for inventory tracking.
|
| 106 |
- [x] Implement routes for adding, updating, and managing inventory items.
|
| 107 |
- [x] Create an **AdminLTE** dashboard for inventory status.
|
| 108 |
- [x] Set up email alerts for low-stock items.
|
| 109 |
|
| 110 |
### 8. Notification System
|
| 111 |
+
|
| 112 |
- [x] Configure email service for notifications (`smtplib` or `nodemailer`).
|
| 113 |
- [x] Implement notifications for:
|
| 114 |
+
- [x] Task completion.
|
| 115 |
+
- [x] Health alerts and recommendations.
|
| 116 |
+
- [x] Inventory updates and low stock alerts.
|
| 117 |
- [x] Test the notification system across scenarios.
|
| 118 |
|
| 119 |
### 9. Group Management
|
| 120 |
+
|
| 121 |
- [x] Create MongoDB schema for managing user groups.
|
| 122 |
- [x] Allow admins to create groups and share tasks.
|
| 123 |
- [x] Enable farmers to join groups and track shared tasks.
|
| 124 |
|
| 125 |
### 10. Testing and Debugging
|
| 126 |
+
|
| 127 |
- [ ] Write unit tests for modules (authentication, to-do lists, health management, inventory, etc.).
|
| 128 |
- [ ] Conduct integration testing to validate end-to-end functionality.
|
| 129 |
- [ ] Debug MongoDB transactions, pytorch predictions, and AdminLTE dashboards.
|
| 130 |
|
| 131 |
### 11. Deployment
|
| 132 |
+
|
| 133 |
- [ ] Deploy the application on Hugging Face Spaces or other cloud platforms.
|
| 134 |
- [ ] Configure production settings (environment variables, security).
|
| 135 |
- [ ] Set up CI/CD pipeline for automatic testing and deployment.
|
|
|
|
| 137 |
---
|
| 138 |
|
| 139 |
## Future Enhancements
|
| 140 |
+
|
| 141 |
- **Mobile Application**: Develop a mobile app using `Flutter` for remote management.
|
| 142 |
- **AI-Based Inventory Forecasting**: Predict inventory needs based on usage trends.
|
| 143 |
- **Multi-Language Support**: Localize the interface for global users.
|
backend/app/middleware/auth_middleware.py
CHANGED
|
@@ -75,6 +75,7 @@ def role_required(role: str):
|
|
| 75 |
|
| 76 |
Prompts and returns the user to the origin for access denied.
|
| 77 |
"""
|
|
|
|
| 78 |
def role_verifier(user=Depends(get_current_user)):
|
| 79 |
# Check if the user's role matches the required role
|
| 80 |
if user.get("role") != role:
|
|
|
|
| 75 |
|
| 76 |
Prompts and returns the user to the origin for access denied.
|
| 77 |
"""
|
| 78 |
+
|
| 79 |
def role_verifier(user=Depends(get_current_user)):
|
| 80 |
# Check if the user's role matches the required role
|
| 81 |
if user.get("role") != role:
|
backend/app/models/inventory_item.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from pydantic import BaseModel
|
| 2 |
from typing import Optional
|
| 3 |
|
|
|
|
| 4 |
class InventoryItem(BaseModel):
|
| 5 |
item_name: str
|
| 6 |
quantity: int
|
|
|
|
| 1 |
from pydantic import BaseModel
|
| 2 |
from typing import Optional
|
| 3 |
|
| 4 |
+
|
| 5 |
class InventoryItem(BaseModel):
|
| 6 |
item_name: str
|
| 7 |
quantity: int
|
backend/app/routes/auth.py
CHANGED
|
@@ -64,6 +64,7 @@ async def register_user(user: UserCreate):
|
|
| 64 |
logger.error("Error registering user: %s", str(e))
|
| 65 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 66 |
|
|
|
|
| 67 |
@router.post("/login")
|
| 68 |
async def login_user(login: LoginRequest, response: Response):
|
| 69 |
"""
|
|
@@ -111,6 +112,7 @@ async def login_user(login: LoginRequest, response: Response):
|
|
| 111 |
logger.error("Unexpected error during login: %s", str(e))
|
| 112 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 113 |
|
|
|
|
| 114 |
@router.post("/logout")
|
| 115 |
async def logout_user(response: Response):
|
| 116 |
"""
|
|
@@ -123,6 +125,7 @@ async def logout_user(response: Response):
|
|
| 123 |
logger.info("User logged out successfully")
|
| 124 |
return {"message": "User logged out successfully"}
|
| 125 |
|
|
|
|
| 126 |
@router.get("/me")
|
| 127 |
async def get_current_user_info(request: Request):
|
| 128 |
"""
|
|
|
|
| 64 |
logger.error("Error registering user: %s", str(e))
|
| 65 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 66 |
|
| 67 |
+
|
| 68 |
@router.post("/login")
|
| 69 |
async def login_user(login: LoginRequest, response: Response):
|
| 70 |
"""
|
|
|
|
| 112 |
logger.error("Unexpected error during login: %s", str(e))
|
| 113 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 114 |
|
| 115 |
+
|
| 116 |
@router.post("/logout")
|
| 117 |
async def logout_user(response: Response):
|
| 118 |
"""
|
|
|
|
| 125 |
logger.info("User logged out successfully")
|
| 126 |
return {"message": "User logged out successfully"}
|
| 127 |
|
| 128 |
+
|
| 129 |
@router.get("/me")
|
| 130 |
async def get_current_user_info(request: Request):
|
| 131 |
"""
|
backend/app/routes/chat.py
CHANGED
|
@@ -11,6 +11,7 @@ from bson import ObjectId
|
|
| 11 |
router = APIRouter()
|
| 12 |
db = get_database()
|
| 13 |
|
|
|
|
| 14 |
@router.post("/")
|
| 15 |
async def chat_with_ai(message: str, current_user: dict = Depends(get_current_user)):
|
| 16 |
"""
|
|
|
|
| 11 |
router = APIRouter()
|
| 12 |
db = get_database()
|
| 13 |
|
| 14 |
+
|
| 15 |
@router.post("/")
|
| 16 |
async def chat_with_ai(message: str, current_user: dict = Depends(get_current_user)):
|
| 17 |
"""
|
backend/app/routes/dashboard.py
CHANGED
|
@@ -12,7 +12,6 @@ db = get_database()
|
|
| 12 |
logger = logging.getLogger(__name__)
|
| 13 |
|
| 14 |
|
| 15 |
-
|
| 16 |
@router.get("/data")
|
| 17 |
async def get_dashboard_data(current_user: dict = Depends(get_current_user)):
|
| 18 |
"""
|
|
|
|
| 12 |
logger = logging.getLogger(__name__)
|
| 13 |
|
| 14 |
|
|
|
|
| 15 |
@router.get("/data")
|
| 16 |
async def get_dashboard_data(current_user: dict = Depends(get_current_user)):
|
| 17 |
"""
|
backend/app/routes/farm.py
CHANGED
|
@@ -14,6 +14,7 @@ router = APIRouter()
|
|
| 14 |
db = get_database()
|
| 15 |
logger = logging.getLogger(__name__)
|
| 16 |
|
|
|
|
| 17 |
@router.post("/")
|
| 18 |
async def create_group(group_name: str, current_user: dict = Depends(get_current_user)):
|
| 19 |
"""
|
|
@@ -94,7 +95,8 @@ async def get_farmers_in_group(current_user: dict = Depends(get_current_user)):
|
|
| 94 |
raise HTTPException(status_code=404, detail="Group not found or unauthorized access")
|
| 95 |
|
| 96 |
# Retrieve farmers in the group
|
| 97 |
-
farmers = list(
|
|
|
|
| 98 |
|
| 99 |
# Convert ObjectId fields to strings
|
| 100 |
for farmer in farmers:
|
|
@@ -119,7 +121,7 @@ async def remove_farmer_from_group(farmer_id: str, current_user: dict = Depends(
|
|
| 119 |
raise HTTPException(status_code=403, detail="Only admins can remove farmers from groups")
|
| 120 |
|
| 121 |
try:
|
| 122 |
-
group = db.groups.find_one({
|
| 123 |
if not group:
|
| 124 |
logger.warning("Group not found or unauthorized access by admin: %s", current_user["email"])
|
| 125 |
raise HTTPException(status_code=404, detail="Group not found or unauthorized access")
|
|
@@ -154,7 +156,7 @@ async def get_group_name(current_user: dict = Depends(get_current_user)):
|
|
| 154 |
raise HTTPException(status_code=403, detail="Only admins can view group names")
|
| 155 |
|
| 156 |
try:
|
| 157 |
-
group = db.groups.find_one({
|
| 158 |
if not group:
|
| 159 |
logger.warning("Group not found or unauthorized access attempt by admin: %s", current_user["email"])
|
| 160 |
raise HTTPException(status_code=404, detail="Group not found or unauthorized access")
|
|
@@ -165,4 +167,3 @@ async def get_group_name(current_user: dict = Depends(get_current_user)):
|
|
| 165 |
except Exception as e:
|
| 166 |
logger.error("Error retrieving group name: %s", str(e))
|
| 167 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 168 |
-
|
|
|
|
| 14 |
db = get_database()
|
| 15 |
logger = logging.getLogger(__name__)
|
| 16 |
|
| 17 |
+
|
| 18 |
@router.post("/")
|
| 19 |
async def create_group(group_name: str, current_user: dict = Depends(get_current_user)):
|
| 20 |
"""
|
|
|
|
| 95 |
raise HTTPException(status_code=404, detail="Group not found or unauthorized access")
|
| 96 |
|
| 97 |
# Retrieve farmers in the group
|
| 98 |
+
farmers = list(
|
| 99 |
+
db.users.find({"farm_id": ObjectId(current_user["farm_id"]), "role": "farmer"}, {"password_hash": 0}))
|
| 100 |
|
| 101 |
# Convert ObjectId fields to strings
|
| 102 |
for farmer in farmers:
|
|
|
|
| 121 |
raise HTTPException(status_code=403, detail="Only admins can remove farmers from groups")
|
| 122 |
|
| 123 |
try:
|
| 124 |
+
group = db.groups.find_one({"created_by": ObjectId(current_user["user_id"])})
|
| 125 |
if not group:
|
| 126 |
logger.warning("Group not found or unauthorized access by admin: %s", current_user["email"])
|
| 127 |
raise HTTPException(status_code=404, detail="Group not found or unauthorized access")
|
|
|
|
| 156 |
raise HTTPException(status_code=403, detail="Only admins can view group names")
|
| 157 |
|
| 158 |
try:
|
| 159 |
+
group = db.groups.find_one({"created_by": ObjectId(current_user["user_id"])})
|
| 160 |
if not group:
|
| 161 |
logger.warning("Group not found or unauthorized access attempt by admin: %s", current_user["email"])
|
| 162 |
raise HTTPException(status_code=404, detail="Group not found or unauthorized access")
|
|
|
|
| 167 |
except Exception as e:
|
| 168 |
logger.error("Error retrieving group name: %s", str(e))
|
| 169 |
raise HTTPException(status_code=500, detail="Internal server error")
|
|
|
backend/app/routes/health.py
CHANGED
|
@@ -10,6 +10,7 @@ from backend.app.schemas.health_record_schema import HealthRecordSchema
|
|
| 10 |
from backend.app.services.disease_detection import detect_disease
|
| 11 |
import cv2
|
| 12 |
import numpy as np
|
|
|
|
| 13 |
# Initialize router and logger
|
| 14 |
router = APIRouter()
|
| 15 |
db = get_database()
|
|
@@ -68,8 +69,8 @@ logger = logging.getLogger(__name__)
|
|
| 68 |
|
| 69 |
@router.post("/detect_and_log")
|
| 70 |
async def detect_and_log_health_record(
|
| 71 |
-
|
| 72 |
-
|
| 73 |
):
|
| 74 |
"""
|
| 75 |
Detect disease from an uploaded image and log a health record if a disease is detected.
|
|
@@ -119,6 +120,7 @@ async def detect_and_log_health_record(
|
|
| 119 |
logger.error("Error during disease detection or logging: %s", str(e))
|
| 120 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 121 |
|
|
|
|
| 122 |
@router.get("/")
|
| 123 |
async def get_health_records(current_user: dict = Depends(get_current_user)):
|
| 124 |
"""
|
|
@@ -145,11 +147,12 @@ async def get_health_records(current_user: dict = Depends(get_current_user)):
|
|
| 145 |
logger.error("Error retrieving health records: %s", str(e))
|
| 146 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 147 |
|
|
|
|
| 148 |
@router.put("/{record_id}")
|
| 149 |
async def update_health_record(
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
):
|
| 154 |
"""
|
| 155 |
Update an existing health record for the user's associated farm.
|
|
|
|
| 10 |
from backend.app.services.disease_detection import detect_disease
|
| 11 |
import cv2
|
| 12 |
import numpy as np
|
| 13 |
+
|
| 14 |
# Initialize router and logger
|
| 15 |
router = APIRouter()
|
| 16 |
db = get_database()
|
|
|
|
| 69 |
|
| 70 |
@router.post("/detect_and_log")
|
| 71 |
async def detect_and_log_health_record(
|
| 72 |
+
file: UploadFile = File(...),
|
| 73 |
+
current_user: dict = Depends(get_current_user)
|
| 74 |
):
|
| 75 |
"""
|
| 76 |
Detect disease from an uploaded image and log a health record if a disease is detected.
|
|
|
|
| 120 |
logger.error("Error during disease detection or logging: %s", str(e))
|
| 121 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 122 |
|
| 123 |
+
|
| 124 |
@router.get("/")
|
| 125 |
async def get_health_records(current_user: dict = Depends(get_current_user)):
|
| 126 |
"""
|
|
|
|
| 147 |
logger.error("Error retrieving health records: %s", str(e))
|
| 148 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 149 |
|
| 150 |
+
|
| 151 |
@router.put("/{record_id}")
|
| 152 |
async def update_health_record(
|
| 153 |
+
record_id: str,
|
| 154 |
+
updated_data: HealthRecordSchema,
|
| 155 |
+
current_user: dict = Depends(get_current_user)
|
| 156 |
):
|
| 157 |
"""
|
| 158 |
Update an existing health record for the user's associated farm.
|
backend/app/routes/inventory.py
CHANGED
|
@@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
|
|
| 16 |
|
| 17 |
@router.post("/", response_model=dict)
|
| 18 |
async def add_inventory_item(
|
| 19 |
-
|
| 20 |
):
|
| 21 |
"""
|
| 22 |
Add a new inventory item.
|
|
@@ -43,6 +43,7 @@ async def add_inventory_item(
|
|
| 43 |
logger.error("Error adding inventory item: %s", str(e))
|
| 44 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 45 |
|
|
|
|
| 46 |
@router.get("/", response_model=list[InventoryItemSchema])
|
| 47 |
async def get_inventory_items(current_user: dict = Depends(get_current_user)):
|
| 48 |
"""
|
|
@@ -106,7 +107,7 @@ async def get_inventory_item(item_id: str):
|
|
| 106 |
|
| 107 |
@router.put("/{item_id}", response_model=dict)
|
| 108 |
async def update_inventory_item(
|
| 109 |
-
|
| 110 |
):
|
| 111 |
"""
|
| 112 |
Update an existing inventory item by its unique ID.
|
|
|
|
| 16 |
|
| 17 |
@router.post("/", response_model=dict)
|
| 18 |
async def add_inventory_item(
|
| 19 |
+
item: InventoryItemCreate, current_user: dict = Depends(get_current_user)
|
| 20 |
):
|
| 21 |
"""
|
| 22 |
Add a new inventory item.
|
|
|
|
| 43 |
logger.error("Error adding inventory item: %s", str(e))
|
| 44 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 45 |
|
| 46 |
+
|
| 47 |
@router.get("/", response_model=list[InventoryItemSchema])
|
| 48 |
async def get_inventory_items(current_user: dict = Depends(get_current_user)):
|
| 49 |
"""
|
|
|
|
| 107 |
|
| 108 |
@router.put("/{item_id}", response_model=dict)
|
| 109 |
async def update_inventory_item(
|
| 110 |
+
item_id: str, item: InventoryItemUpdate, current_user: dict = Depends(get_current_user)
|
| 111 |
):
|
| 112 |
"""
|
| 113 |
Update an existing inventory item by its unique ID.
|
backend/app/routes/pages.py
CHANGED
|
@@ -122,7 +122,7 @@ async def manage_inventory_page(request: Request, current_user: dict = Depends(g
|
|
| 122 |
logger.info("Rendering inventory management page for user ID: %s", current_user["user_id"])
|
| 123 |
return templates.TemplateResponse(
|
| 124 |
"inventory/view_inventory.html", {"request": request, "title": "Manage Inventory",
|
| 125 |
-
|
| 126 |
)
|
| 127 |
|
| 128 |
|
|
@@ -144,7 +144,7 @@ async def add_health_record_page(request: Request, current_user: dict = Depends(
|
|
| 144 |
logger.info("Rendering add health record page for user ID: %s", current_user["user_id"])
|
| 145 |
return templates.TemplateResponse(
|
| 146 |
"health/log_health.html", {"request": request, "title": "Log New Health Record",
|
| 147 |
-
|
| 148 |
)
|
| 149 |
|
| 150 |
|
|
@@ -164,7 +164,9 @@ async def manage_health_records_page(request: Request, current_user: dict = Depe
|
|
| 164 |
logger.warning("Unauthorized access attempt to manage health records page.")
|
| 165 |
return RedirectResponse(url="/login")
|
| 166 |
logger.info("Rendering health records management page for user ID: %s", current_user["user_id"])
|
| 167 |
-
return templates.TemplateResponse(
|
|
|
|
|
|
|
| 168 |
|
| 169 |
|
| 170 |
@router.get("/mangroup", response_class=HTMLResponse)
|
|
@@ -186,7 +188,7 @@ async def group_management_page(request: Request, current_user: dict = Depends(g
|
|
| 186 |
logger.info("Rendering group management page for admin ID: %s", current_user["user_id"])
|
| 187 |
return templates.TemplateResponse(
|
| 188 |
"group/manage_group.html", {"request": request, "title": "Manage Group", "is_admin": True,
|
| 189 |
-
|
| 190 |
)
|
| 191 |
|
| 192 |
|
|
@@ -209,7 +211,7 @@ async def add_task_page(request: Request, current_user: dict = Depends(get_curre
|
|
| 209 |
logger.info("Rendering add task page for admin ID: %s", current_user["user_id"])
|
| 210 |
return templates.TemplateResponse(
|
| 211 |
"todo/add_task.html", {"request": request, "title": "Add Task", "is_admin": True,
|
| 212 |
-
|
| 213 |
)
|
| 214 |
|
| 215 |
|
|
@@ -235,6 +237,8 @@ async def manage_tasks_page(request: Request, current_user: dict = Depends(get_c
|
|
| 235 |
{"request": request, "title": "Manage Tasks", "is_admin": is_admin, "user_id": current_user["user_id"],
|
| 236 |
"farm_id": current_user["farm_id"], "current_user.role": current_user["role"]}
|
| 237 |
)
|
|
|
|
|
|
|
| 238 |
@router.get("/chat", response_class=HTMLResponse)
|
| 239 |
async def chat_page(request: Request, current_user: dict = Depends(get_current_user)):
|
| 240 |
"""
|
|
@@ -285,4 +289,4 @@ async def chat_history_page(request: Request, current_user: dict = Depends(get_c
|
|
| 285 |
"title": "Chat History",
|
| 286 |
"user_id": current_user["user_id"]
|
| 287 |
}
|
| 288 |
-
)
|
|
|
|
| 122 |
logger.info("Rendering inventory management page for user ID: %s", current_user["user_id"])
|
| 123 |
return templates.TemplateResponse(
|
| 124 |
"inventory/view_inventory.html", {"request": request, "title": "Manage Inventory",
|
| 125 |
+
"farm_id": current_user["farm_id"]}
|
| 126 |
)
|
| 127 |
|
| 128 |
|
|
|
|
| 144 |
logger.info("Rendering add health record page for user ID: %s", current_user["user_id"])
|
| 145 |
return templates.TemplateResponse(
|
| 146 |
"health/log_health.html", {"request": request, "title": "Log New Health Record",
|
| 147 |
+
"farm_id": current_user["farm_id"]}
|
| 148 |
)
|
| 149 |
|
| 150 |
|
|
|
|
| 164 |
logger.warning("Unauthorized access attempt to manage health records page.")
|
| 165 |
return RedirectResponse(url="/login")
|
| 166 |
logger.info("Rendering health records management page for user ID: %s", current_user["user_id"])
|
| 167 |
+
return templates.TemplateResponse("health/manage_health.html",
|
| 168 |
+
{"request": request, "title": "Manage Health Records",
|
| 169 |
+
"farm_id": current_user["farm_id"]})
|
| 170 |
|
| 171 |
|
| 172 |
@router.get("/mangroup", response_class=HTMLResponse)
|
|
|
|
| 188 |
logger.info("Rendering group management page for admin ID: %s", current_user["user_id"])
|
| 189 |
return templates.TemplateResponse(
|
| 190 |
"group/manage_group.html", {"request": request, "title": "Manage Group", "is_admin": True,
|
| 191 |
+
"farm_id": current_user["farm_id"]}
|
| 192 |
)
|
| 193 |
|
| 194 |
|
|
|
|
| 211 |
logger.info("Rendering add task page for admin ID: %s", current_user["user_id"])
|
| 212 |
return templates.TemplateResponse(
|
| 213 |
"todo/add_task.html", {"request": request, "title": "Add Task", "is_admin": True,
|
| 214 |
+
"farm_id": current_user["farm_id"], "current_user.role": current_user["role"]}
|
| 215 |
)
|
| 216 |
|
| 217 |
|
|
|
|
| 237 |
{"request": request, "title": "Manage Tasks", "is_admin": is_admin, "user_id": current_user["user_id"],
|
| 238 |
"farm_id": current_user["farm_id"], "current_user.role": current_user["role"]}
|
| 239 |
)
|
| 240 |
+
|
| 241 |
+
|
| 242 |
@router.get("/chat", response_class=HTMLResponse)
|
| 243 |
async def chat_page(request: Request, current_user: dict = Depends(get_current_user)):
|
| 244 |
"""
|
|
|
|
| 289 |
"title": "Chat History",
|
| 290 |
"user_id": current_user["user_id"]
|
| 291 |
}
|
| 292 |
+
)
|
backend/app/routes/task.py
CHANGED
|
@@ -91,6 +91,7 @@ async def get_tasks(current_user: dict = Depends(get_current_user)):
|
|
| 91 |
logger.error("Error retrieving tasks: %s", str(e))
|
| 92 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 93 |
|
|
|
|
| 94 |
@router.put("/{task_id}")
|
| 95 |
async def update_task(task_id: str, task: Task):
|
| 96 |
"""
|
|
|
|
| 91 |
logger.error("Error retrieving tasks: %s", str(e))
|
| 92 |
raise HTTPException(status_code=500, detail="Internal server error")
|
| 93 |
|
| 94 |
+
|
| 95 |
@router.put("/{task_id}")
|
| 96 |
async def update_task(task_id: str, task: Task):
|
| 97 |
"""
|
backend/app/schemas/chat_history_schema.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import List, Optional
|
|
| 3 |
from datetime import datetime, timezone
|
| 4 |
from bson import ObjectId
|
| 5 |
|
|
|
|
| 6 |
class ChatMessage(BaseModel):
|
| 7 |
"""
|
| 8 |
Represents an individual chat message in the history.
|
|
@@ -11,6 +12,7 @@ class ChatMessage(BaseModel):
|
|
| 11 |
assistant_response: str
|
| 12 |
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
| 13 |
|
|
|
|
| 14 |
class ChatHistory(BaseModel):
|
| 15 |
"""
|
| 16 |
Represents a chat session with a list of chat messages.
|
|
@@ -20,7 +22,8 @@ class ChatHistory(BaseModel):
|
|
| 20 |
session_id: ObjectId # Unique identifier for the chat session
|
| 21 |
messages: List[ChatMessage] = [] # List of messages in the chat session
|
| 22 |
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) # When the session was created
|
| 23 |
-
updated_at: datetime = Field(
|
|
|
|
| 24 |
|
| 25 |
class Config:
|
| 26 |
arbitrary_types_allowed = True
|
|
|
|
| 3 |
from datetime import datetime, timezone
|
| 4 |
from bson import ObjectId
|
| 5 |
|
| 6 |
+
|
| 7 |
class ChatMessage(BaseModel):
|
| 8 |
"""
|
| 9 |
Represents an individual chat message in the history.
|
|
|
|
| 12 |
assistant_response: str
|
| 13 |
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
| 14 |
|
| 15 |
+
|
| 16 |
class ChatHistory(BaseModel):
|
| 17 |
"""
|
| 18 |
Represents a chat session with a list of chat messages.
|
|
|
|
| 22 |
session_id: ObjectId # Unique identifier for the chat session
|
| 23 |
messages: List[ChatMessage] = [] # List of messages in the chat session
|
| 24 |
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) # When the session was created
|
| 25 |
+
updated_at: datetime = Field(
|
| 26 |
+
default_factory=lambda: datetime.now(timezone.utc)) # Last time the session was updated
|
| 27 |
|
| 28 |
class Config:
|
| 29 |
arbitrary_types_allowed = True
|
backend/app/schemas/inventory_item_schema.py
CHANGED
|
@@ -57,7 +57,8 @@ class InventoryItemCreate(BaseModel):
|
|
| 57 |
category: str
|
| 58 |
quantity: int
|
| 59 |
status: Optional[str] = "active" # Default status is "active"
|
| 60 |
-
last_updated: Optional[datetime] = Field(
|
|
|
|
| 61 |
farm_id: str # ID of the farm
|
| 62 |
|
| 63 |
|
|
@@ -79,5 +80,6 @@ class InventoryItemUpdate(BaseModel):
|
|
| 79 |
quantity: Optional[int] = None
|
| 80 |
status: Optional[str] = None
|
| 81 |
updated_by: Optional[str] = None
|
| 82 |
-
last_updated: Optional[datetime] = Field(
|
|
|
|
| 83 |
farm_id: Optional[str] = None # Optional farm ID for reassignment
|
|
|
|
| 57 |
category: str
|
| 58 |
quantity: int
|
| 59 |
status: Optional[str] = "active" # Default status is "active"
|
| 60 |
+
last_updated: Optional[datetime] = Field(
|
| 61 |
+
default_factory=lambda: datetime.now(timezone.utc)) # Automatically set the creation timestamp
|
| 62 |
farm_id: str # ID of the farm
|
| 63 |
|
| 64 |
|
|
|
|
| 80 |
quantity: Optional[int] = None
|
| 81 |
status: Optional[str] = None
|
| 82 |
updated_by: Optional[str] = None
|
| 83 |
+
last_updated: Optional[datetime] = Field(
|
| 84 |
+
default_factory=lambda: datetime.now(timezone.utc)) # Automatically set the update timestamp
|
| 85 |
farm_id: Optional[str] = None # Optional farm ID for reassignment
|
backend/app/schemas/user_schema.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
| 1 |
-
|
| 2 |
# schemas/user_schema.py
|
| 3 |
from bson import ObjectId
|
| 4 |
from pydantic import BaseModel, EmailStr, Field
|
| 5 |
from typing import Optional
|
| 6 |
|
|
|
|
| 7 |
class UserSchema(BaseModel):
|
| 8 |
id: Optional[str] = Field(None, alias="_id") # Use alias for MongoDB compatibility
|
| 9 |
username: str
|
|
@@ -16,6 +16,7 @@ class UserSchema(BaseModel):
|
|
| 16 |
arbitrary_types_allowed = True
|
| 17 |
json_encoders = {ObjectId: str} # Automatically convert ObjectId to str
|
| 18 |
|
|
|
|
| 19 |
class UserCreate(BaseModel):
|
| 20 |
username: str
|
| 21 |
email: EmailStr
|
|
@@ -23,6 +24,7 @@ class UserCreate(BaseModel):
|
|
| 23 |
role: str # User role (e.g., "admin" or "farmer")
|
| 24 |
group_name: Optional[str] # Group name for initial group creation in registration
|
| 25 |
|
|
|
|
| 26 |
class LoginRequest(BaseModel):
|
| 27 |
email: EmailStr
|
| 28 |
password: str
|
|
|
|
|
|
|
| 1 |
# schemas/user_schema.py
|
| 2 |
from bson import ObjectId
|
| 3 |
from pydantic import BaseModel, EmailStr, Field
|
| 4 |
from typing import Optional
|
| 5 |
|
| 6 |
+
|
| 7 |
class UserSchema(BaseModel):
|
| 8 |
id: Optional[str] = Field(None, alias="_id") # Use alias for MongoDB compatibility
|
| 9 |
username: str
|
|
|
|
| 16 |
arbitrary_types_allowed = True
|
| 17 |
json_encoders = {ObjectId: str} # Automatically convert ObjectId to str
|
| 18 |
|
| 19 |
+
|
| 20 |
class UserCreate(BaseModel):
|
| 21 |
username: str
|
| 22 |
email: EmailStr
|
|
|
|
| 24 |
role: str # User role (e.g., "admin" or "farmer")
|
| 25 |
group_name: Optional[str] # Group name for initial group creation in registration
|
| 26 |
|
| 27 |
+
|
| 28 |
class LoginRequest(BaseModel):
|
| 29 |
email: EmailStr
|
| 30 |
password: str
|
backend/app/services/disease_detection.py
CHANGED
|
@@ -277,5 +277,3 @@ def detect_disease(image_file):
|
|
| 277 |
# Log and return a generic error message for unexpected issues
|
| 278 |
logger.error("Unhandled error in detect_disease: %s", detection_error)
|
| 279 |
raise HTTPException(status_code=500, detail="Unexpected error during disease detection.")
|
| 280 |
-
|
| 281 |
-
|
|
|
|
| 277 |
# Log and return a generic error message for unexpected issues
|
| 278 |
logger.error("Unhandled error in detect_disease: %s", detection_error)
|
| 279 |
raise HTTPException(status_code=500, detail="Unexpected error during disease detection.")
|
|
|
|
|
|
backend/app/services/llama_service.py
CHANGED
|
@@ -85,7 +85,8 @@ def process_message(user_id: ObjectId, session_id: ObjectId, message: str) -> st
|
|
| 85 |
history = chat_history.get("messages", []) if chat_history else []
|
| 86 |
|
| 87 |
# Combine history into a single context string
|
| 88 |
-
context = "\n".join(
|
|
|
|
| 89 |
context += f"\nUser: {message}\nAssistant:"
|
| 90 |
|
| 91 |
# Tokenize the input text
|
|
|
|
| 85 |
history = chat_history.get("messages", []) if chat_history else []
|
| 86 |
|
| 87 |
# Combine history into a single context string
|
| 88 |
+
context = "\n".join(
|
| 89 |
+
[f"User: {chat['user_message']}\nAssistant: {chat['assistant_response']}" for chat in history])
|
| 90 |
context += f"\nUser: {message}\nAssistant:"
|
| 91 |
|
| 92 |
# Tokenize the input text
|
backend/app/static/js/group.js
CHANGED
|
@@ -19,7 +19,7 @@ async function loadGroupDetails() {
|
|
| 19 |
// Fetch farmers in the group
|
| 20 |
const response = await fetch(`/groups/farmers`, {
|
| 21 |
method: 'GET',
|
| 22 |
-
headers: {
|
| 23 |
});
|
| 24 |
|
| 25 |
if (!response.ok) {
|
|
@@ -52,7 +52,7 @@ async function fetchGroupName() {
|
|
| 52 |
try {
|
| 53 |
const response = await fetch(`/groups/name`, {
|
| 54 |
method: 'GET',
|
| 55 |
-
headers: {
|
| 56 |
});
|
| 57 |
|
| 58 |
if (!response.ok) {
|
|
@@ -83,8 +83,8 @@ async function addFarmer(event) {
|
|
| 83 |
try {
|
| 84 |
const response = await fetch(`/groups/add_farmer`, {
|
| 85 |
method: 'POST',
|
| 86 |
-
headers: {
|
| 87 |
-
body: JSON.stringify({
|
| 88 |
});
|
| 89 |
|
| 90 |
if (!response.ok) {
|
|
@@ -110,7 +110,7 @@ async function removeFarmer(farmerId) {
|
|
| 110 |
try {
|
| 111 |
const response = await fetch(`/groups/remove_farmer/${farmerId}`, {
|
| 112 |
method: 'DELETE',
|
| 113 |
-
headers: {
|
| 114 |
});
|
| 115 |
|
| 116 |
if (!response.ok) {
|
|
|
|
| 19 |
// Fetch farmers in the group
|
| 20 |
const response = await fetch(`/groups/farmers`, {
|
| 21 |
method: 'GET',
|
| 22 |
+
headers: {'Content-Type': 'application/json'}
|
| 23 |
});
|
| 24 |
|
| 25 |
if (!response.ok) {
|
|
|
|
| 52 |
try {
|
| 53 |
const response = await fetch(`/groups/name`, {
|
| 54 |
method: 'GET',
|
| 55 |
+
headers: {'Content-Type': 'application/json'}
|
| 56 |
});
|
| 57 |
|
| 58 |
if (!response.ok) {
|
|
|
|
| 83 |
try {
|
| 84 |
const response = await fetch(`/groups/add_farmer`, {
|
| 85 |
method: 'POST',
|
| 86 |
+
headers: {'Content-Type': 'application/json'},
|
| 87 |
+
body: JSON.stringify({username, email, password, role}),
|
| 88 |
});
|
| 89 |
|
| 90 |
if (!response.ok) {
|
|
|
|
| 110 |
try {
|
| 111 |
const response = await fetch(`/groups/remove_farmer/${farmerId}`, {
|
| 112 |
method: 'DELETE',
|
| 113 |
+
headers: {'Content-Type': 'application/json'}
|
| 114 |
});
|
| 115 |
|
| 116 |
if (!response.ok) {
|
backend/app/static/js/health.js
CHANGED
|
@@ -122,8 +122,8 @@ async function updateHealthRecord(event) {
|
|
| 122 |
try {
|
| 123 |
const response = await fetch(`/health/${id}`, {
|
| 124 |
method: 'PUT',
|
| 125 |
-
headers: {
|
| 126 |
-
body: JSON.stringify({
|
| 127 |
});
|
| 128 |
|
| 129 |
if (response.ok) {
|
|
@@ -150,7 +150,7 @@ function confirmDeleteHealthRecord(id) {
|
|
| 150 |
|
| 151 |
async function deleteHealthRecord() {
|
| 152 |
try {
|
| 153 |
-
const response = await fetch(`/health/${deleteRecordId}`, {
|
| 154 |
|
| 155 |
if (response.ok) {
|
| 156 |
alert("Health record deleted successfully!");
|
|
|
|
| 122 |
try {
|
| 123 |
const response = await fetch(`/health/${id}`, {
|
| 124 |
method: 'PUT',
|
| 125 |
+
headers: {'Content-Type': 'application/json'},
|
| 126 |
+
body: JSON.stringify({recommendation}),
|
| 127 |
});
|
| 128 |
|
| 129 |
if (response.ok) {
|
|
|
|
| 150 |
|
| 151 |
async function deleteHealthRecord() {
|
| 152 |
try {
|
| 153 |
+
const response = await fetch(`/health/${deleteRecordId}`, {method: 'DELETE'});
|
| 154 |
|
| 155 |
if (response.ok) {
|
| 156 |
alert("Health record deleted successfully!");
|
backend/app/static/js/inventory.js
CHANGED
|
@@ -34,7 +34,6 @@ function showUpdateForm(id, name, quantity, category, status) {
|
|
| 34 |
}
|
| 35 |
|
| 36 |
|
| 37 |
-
|
| 38 |
// Add Inventory Item
|
| 39 |
async function addInventoryItem(event) {
|
| 40 |
event.preventDefault();
|
|
@@ -49,7 +48,7 @@ async function addInventoryItem(event) {
|
|
| 49 |
}
|
| 50 |
if (!user_id || !farm_id) {
|
| 51 |
alert("User or farm details are missing. Please log in again.");
|
| 52 |
-
console.error("Missing user_id or farm_id:", {
|
| 53 |
return;
|
| 54 |
}
|
| 55 |
|
|
@@ -63,7 +62,7 @@ async function addInventoryItem(event) {
|
|
| 63 |
// Validate inputs
|
| 64 |
if (!item_name || isNaN(quantity) || !category) {
|
| 65 |
alert("Please provide valid inputs for all fields.");
|
| 66 |
-
console.error("Invalid inputs:", {
|
| 67 |
return;
|
| 68 |
}
|
| 69 |
|
|
@@ -75,7 +74,7 @@ async function addInventoryItem(event) {
|
|
| 75 |
'Content-Type': 'application/json',
|
| 76 |
'Authorization': `Bearer ${token}`
|
| 77 |
},
|
| 78 |
-
body: JSON.stringify({
|
| 79 |
});
|
| 80 |
|
| 81 |
if (response.ok) {
|
|
@@ -177,7 +176,7 @@ async function updateInventoryItem(event) {
|
|
| 177 |
// Validate form values
|
| 178 |
if (!id || !item_name || isNaN(quantity) || !category || !status) {
|
| 179 |
alert("Please provide valid inputs for all fields.");
|
| 180 |
-
console.error("Invalid update inputs:", {
|
| 181 |
return;
|
| 182 |
}
|
| 183 |
|
|
@@ -189,7 +188,7 @@ async function updateInventoryItem(event) {
|
|
| 189 |
'Content-Type': 'application/json',
|
| 190 |
'Authorization': `Bearer ${token}`
|
| 191 |
},
|
| 192 |
-
body: JSON.stringify({
|
| 193 |
});
|
| 194 |
|
| 195 |
if (response.ok) {
|
|
|
|
| 34 |
}
|
| 35 |
|
| 36 |
|
|
|
|
| 37 |
// Add Inventory Item
|
| 38 |
async function addInventoryItem(event) {
|
| 39 |
event.preventDefault();
|
|
|
|
| 48 |
}
|
| 49 |
if (!user_id || !farm_id) {
|
| 50 |
alert("User or farm details are missing. Please log in again.");
|
| 51 |
+
console.error("Missing user_id or farm_id:", {user_id, farm_id});
|
| 52 |
return;
|
| 53 |
}
|
| 54 |
|
|
|
|
| 62 |
// Validate inputs
|
| 63 |
if (!item_name || isNaN(quantity) || !category) {
|
| 64 |
alert("Please provide valid inputs for all fields.");
|
| 65 |
+
console.error("Invalid inputs:", {item_name, quantity, category});
|
| 66 |
return;
|
| 67 |
}
|
| 68 |
|
|
|
|
| 74 |
'Content-Type': 'application/json',
|
| 75 |
'Authorization': `Bearer ${token}`
|
| 76 |
},
|
| 77 |
+
body: JSON.stringify({item_name, quantity, category, farm_id, status, updated_by})
|
| 78 |
});
|
| 79 |
|
| 80 |
if (response.ok) {
|
|
|
|
| 176 |
// Validate form values
|
| 177 |
if (!id || !item_name || isNaN(quantity) || !category || !status) {
|
| 178 |
alert("Please provide valid inputs for all fields.");
|
| 179 |
+
console.error("Invalid update inputs:", {id, item_name, quantity, category, status});
|
| 180 |
return;
|
| 181 |
}
|
| 182 |
|
|
|
|
| 188 |
'Content-Type': 'application/json',
|
| 189 |
'Authorization': `Bearer ${token}`
|
| 190 |
},
|
| 191 |
+
body: JSON.stringify({item_name, quantity, category, status, updated_by})
|
| 192 |
});
|
| 193 |
|
| 194 |
if (response.ok) {
|
backend/app/static/js/tasks.js
CHANGED
|
@@ -53,7 +53,7 @@ async function addTask(event) {
|
|
| 53 |
try {
|
| 54 |
const response = await fetch('/tasks/', {
|
| 55 |
method: 'POST',
|
| 56 |
-
headers: {
|
| 57 |
body: JSON.stringify({
|
| 58 |
task_description: taskDescription,
|
| 59 |
assigned_to: assignedTo,
|
|
@@ -77,7 +77,6 @@ async function addTask(event) {
|
|
| 77 |
let deleteTaskId = null;
|
| 78 |
|
| 79 |
|
| 80 |
-
|
| 81 |
async function loadTasks() {
|
| 82 |
/**
|
| 83 |
* Load and display tasks for the group.
|
|
@@ -152,7 +151,7 @@ function confirmDeleteTask(id) {
|
|
| 152 |
|
| 153 |
async function deleteTask() {
|
| 154 |
try {
|
| 155 |
-
const response = await fetch(`/tasks/${deleteTaskId}`, {
|
| 156 |
if (response.ok) {
|
| 157 |
alert("Task deleted successfully!");
|
| 158 |
const modal = bootstrap.Modal.getInstance(document.getElementById('deleteTaskModal'));
|
|
|
|
| 53 |
try {
|
| 54 |
const response = await fetch('/tasks/', {
|
| 55 |
method: 'POST',
|
| 56 |
+
headers: {'Content-Type': 'application/json'},
|
| 57 |
body: JSON.stringify({
|
| 58 |
task_description: taskDescription,
|
| 59 |
assigned_to: assignedTo,
|
|
|
|
| 77 |
let deleteTaskId = null;
|
| 78 |
|
| 79 |
|
|
|
|
| 80 |
async function loadTasks() {
|
| 81 |
/**
|
| 82 |
* Load and display tasks for the group.
|
|
|
|
| 151 |
|
| 152 |
async function deleteTask() {
|
| 153 |
try {
|
| 154 |
+
const response = await fetch(`/tasks/${deleteTaskId}`, {method: 'DELETE'});
|
| 155 |
if (response.ok) {
|
| 156 |
alert("Task deleted successfully!");
|
| 157 |
const modal = bootstrap.Modal.getInstance(document.getElementById('deleteTaskModal'));
|
backend/app/templates/auth/register.html
CHANGED
|
@@ -5,9 +5,11 @@
|
|
| 5 |
<title>Register</title>
|
| 6 |
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
|
| 7 |
integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" rel="stylesheet">
|
| 8 |
-
<link crossorigin="anonymous"
|
|
|
|
| 9 |
integrity="sha256-dSokZseQNT08wYEWiz5iLI8QPlKxG+TswNRD8k35cpg=" rel="stylesheet">
|
| 10 |
-
<link crossorigin="anonymous"
|
|
|
|
| 11 |
integrity="sha256-Qsx5lrStHZyR9REqhUF8iQt73X06c8LGIUPzpOhwRrI=" rel="stylesheet">
|
| 12 |
<link href="../static/css/adminlte.css" rel="stylesheet">
|
| 13 |
</head>
|
|
@@ -20,7 +22,8 @@
|
|
| 20 |
<p class="register-box-msg">Register a new admin account</p>
|
| 21 |
<form id="registerForm" onsubmit="registerUser(event)">
|
| 22 |
<div class="input-group mb-3">
|
| 23 |
-
<input class="form-control" id="username" name="username" placeholder="Username" required
|
|
|
|
| 24 |
<div class="input-group-text"><span class="bi bi-person"></span></div>
|
| 25 |
</div>
|
| 26 |
<div class="input-group mb-3">
|
|
@@ -28,11 +31,13 @@
|
|
| 28 |
<div class="input-group-text"><span class="bi bi-envelope"></span></div>
|
| 29 |
</div>
|
| 30 |
<div class="input-group mb-3">
|
| 31 |
-
<input class="form-control" id="password" name="password" placeholder="Password" required
|
|
|
|
| 32 |
<div class="input-group-text"><span class="bi bi-lock-fill"></span></div>
|
| 33 |
</div>
|
| 34 |
<div class="input-group mb-3">
|
| 35 |
-
<input class="form-control" id="group_name" name="group_name" placeholder="Group Name (Farm Name)"
|
|
|
|
| 36 |
<div class="input-group-text"><span class="bi bi-house-door"></span></div>
|
| 37 |
</div>
|
| 38 |
<div class="row">
|
|
@@ -59,31 +64,31 @@
|
|
| 59 |
|
| 60 |
<script src="../static/js/auth.js"></script>
|
| 61 |
<script>
|
| 62 |
-
async function registerUser(event) {
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
}
|
| 82 |
-
} catch (error) {
|
| 83 |
-
console.error("Error registering admin:", error);
|
| 84 |
-
alert("An error occurred while registering.");
|
| 85 |
}
|
| 86 |
-
}
|
| 87 |
</script>
|
| 88 |
</body>
|
| 89 |
</html>
|
|
|
|
| 5 |
<title>Register</title>
|
| 6 |
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
|
| 7 |
integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" rel="stylesheet">
|
| 8 |
+
<link crossorigin="anonymous"
|
| 9 |
+
href="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/styles/overlayscrollbars.min.css"
|
| 10 |
integrity="sha256-dSokZseQNT08wYEWiz5iLI8QPlKxG+TswNRD8k35cpg=" rel="stylesheet">
|
| 11 |
+
<link crossorigin="anonymous"
|
| 12 |
+
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.min.css"
|
| 13 |
integrity="sha256-Qsx5lrStHZyR9REqhUF8iQt73X06c8LGIUPzpOhwRrI=" rel="stylesheet">
|
| 14 |
<link href="../static/css/adminlte.css" rel="stylesheet">
|
| 15 |
</head>
|
|
|
|
| 22 |
<p class="register-box-msg">Register a new admin account</p>
|
| 23 |
<form id="registerForm" onsubmit="registerUser(event)">
|
| 24 |
<div class="input-group mb-3">
|
| 25 |
+
<input class="form-control" id="username" name="username" placeholder="Username" required
|
| 26 |
+
type="text">
|
| 27 |
<div class="input-group-text"><span class="bi bi-person"></span></div>
|
| 28 |
</div>
|
| 29 |
<div class="input-group mb-3">
|
|
|
|
| 31 |
<div class="input-group-text"><span class="bi bi-envelope"></span></div>
|
| 32 |
</div>
|
| 33 |
<div class="input-group mb-3">
|
| 34 |
+
<input class="form-control" id="password" name="password" placeholder="Password" required
|
| 35 |
+
type="password">
|
| 36 |
<div class="input-group-text"><span class="bi bi-lock-fill"></span></div>
|
| 37 |
</div>
|
| 38 |
<div class="input-group mb-3">
|
| 39 |
+
<input class="form-control" id="group_name" name="group_name" placeholder="Group Name (Farm Name)"
|
| 40 |
+
required type="text">
|
| 41 |
<div class="input-group-text"><span class="bi bi-house-door"></span></div>
|
| 42 |
</div>
|
| 43 |
<div class="row">
|
|
|
|
| 64 |
|
| 65 |
<script src="../static/js/auth.js"></script>
|
| 66 |
<script>
|
| 67 |
+
async function registerUser(event) {
|
| 68 |
+
event.preventDefault();
|
| 69 |
+
const username = document.getElementById('username').value;
|
| 70 |
+
const email = document.getElementById('email').value;
|
| 71 |
+
const password = document.getElementById('password').value;
|
| 72 |
+
const group_name = document.getElementById('group_name').value;
|
| 73 |
|
| 74 |
+
try {
|
| 75 |
+
const response = await fetch('/auth/register', {
|
| 76 |
+
method: 'POST',
|
| 77 |
+
headers: {'Content-Type': 'application/json'},
|
| 78 |
+
body: JSON.stringify({username, email, password, role: 'admin', group_name})
|
| 79 |
+
});
|
| 80 |
+
if (response.ok) {
|
| 81 |
+
alert("Admin registered successfully!");
|
| 82 |
+
window.location.href = "/login";
|
| 83 |
+
} else {
|
| 84 |
+
const error = await response.json();
|
| 85 |
+
alert("Error: " + error.detail);
|
| 86 |
+
}
|
| 87 |
+
} catch (error) {
|
| 88 |
+
console.error("Error registering admin:", error);
|
| 89 |
+
alert("An error occurred while registering.");
|
| 90 |
}
|
|
|
|
|
|
|
|
|
|
| 91 |
}
|
|
|
|
| 92 |
</script>
|
| 93 |
</body>
|
| 94 |
</html>
|
backend/app/templates/chat/index.html
CHANGED
|
@@ -6,276 +6,276 @@
|
|
| 6 |
<title>AI Chat</title>
|
| 7 |
|
| 8 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
body {
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
}
|
| 20 |
-
|
| 21 |
-
.chatgpt-clone {
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
}
|
| 26 |
-
|
| 27 |
-
/* Sidebar Styling */
|
| 28 |
-
.chat-sidebar {
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
}
|
| 37 |
-
|
| 38 |
-
.chat-sidebar h2 {
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
.chat-sidebar ul {
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
}
|
| 52 |
-
|
| 53 |
-
.chat-sidebar ul li {
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
.chat-sidebar ul li:hover {
|
| 63 |
-
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
.new-chat-button {
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
.new-chat-button:hover {
|
| 78 |
-
|
| 79 |
-
}
|
| 80 |
-
|
| 81 |
-
/* Main Chat Interface */
|
| 82 |
-
.chat-main {
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
}
|
| 88 |
-
|
| 89 |
-
.chat-header {
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
}
|
| 94 |
-
|
| 95 |
-
.chat-header h1 {
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
.chat-window {
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
}
|
| 110 |
-
|
| 111 |
-
.chat-bubble {
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
}
|
| 118 |
-
|
| 119 |
-
.user-message {
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
}
|
| 125 |
-
|
| 126 |
-
.bot-response {
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
}
|
| 132 |
-
|
| 133 |
-
.chat-footer {
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
}
|
| 140 |
-
|
| 141 |
-
textarea#messageInput {
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
}
|
| 149 |
-
|
| 150 |
-
.send-button {
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
}
|
| 159 |
-
|
| 160 |
-
.send-button i {
|
| 161 |
-
|
| 162 |
-
}
|
| 163 |
-
|
| 164 |
-
.send-button:hover {
|
| 165 |
-
|
| 166 |
-
}
|
| 167 |
-
</style>
|
| 168 |
</head>
|
| 169 |
<body>
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
<textarea
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
></textarea>
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
async function fetchChatHistory() {
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
}
|
| 210 |
-
|
| 211 |
-
const history = await response.json();
|
| 212 |
-
const historyList = document.getElementById("chatHistoryList");
|
| 213 |
-
historyList.innerHTML = "";
|
| 214 |
-
|
| 215 |
-
history.forEach(chat => {
|
| 216 |
-
const listItem = document.createElement("li");
|
| 217 |
-
listItem.textContent = chat.message;
|
| 218 |
-
listItem.onclick = () => loadChat(chat);
|
| 219 |
-
historyList.appendChild(listItem);
|
| 220 |
-
});
|
| 221 |
-
} catch (error) {
|
| 222 |
-
console.error("Error fetching chat history:", error);
|
| 223 |
}
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
|
|
|
|
|
|
| 248 |
}
|
| 249 |
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
// Add a message to the chat window
|
| 254 |
-
function addMessageToChatWindow(message, className) {
|
| 255 |
-
const chatWindow = document.getElementById("chatWindow");
|
| 256 |
-
const bubble = document.createElement("div");
|
| 257 |
-
bubble.className = `chat-bubble ${className}`;
|
| 258 |
-
bubble.textContent = message;
|
| 259 |
-
chatWindow.appendChild(bubble);
|
| 260 |
-
chatWindow.scrollTop = chatWindow.scrollHeight;
|
| 261 |
-
}
|
| 262 |
-
|
| 263 |
-
// Start a new chat session
|
| 264 |
-
async function startNewChat() {
|
| 265 |
-
try {
|
| 266 |
-
await fetch("/chat/new", { method: "POST" });
|
| 267 |
const chatWindow = document.getElementById("chatWindow");
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
}
|
| 272 |
-
}
|
| 273 |
|
| 274 |
-
// Initialize
|
| 275 |
-
document.addEventListener("DOMContentLoaded", () => {
|
| 276 |
-
|
| 277 |
-
});
|
| 278 |
|
| 279 |
-
|
| 280 |
</body>
|
| 281 |
</html>
|
|
|
|
| 6 |
<title>AI Chat</title>
|
| 7 |
|
| 8 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
| 9 |
+
<style>
|
| 10 |
+
/* General Reset */
|
| 11 |
+
|
| 12 |
+
body {
|
| 13 |
+
margin: 0;
|
| 14 |
+
font-family: Arial, sans-serif;
|
| 15 |
+
background-color: #f7f8fc;
|
| 16 |
+
display: flex;
|
| 17 |
+
height: 100vh;
|
| 18 |
+
overflow: hidden;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
.chatgpt-clone {
|
| 22 |
+
display: flex;
|
| 23 |
+
width: 100%;
|
| 24 |
+
height: 100%;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
/* Sidebar Styling */
|
| 28 |
+
.chat-sidebar {
|
| 29 |
+
width: 25%;
|
| 30 |
+
background-color: #2c2c54;
|
| 31 |
+
color: white;
|
| 32 |
+
display: flex;
|
| 33 |
+
flex-direction: column;
|
| 34 |
+
padding: 20px;
|
| 35 |
+
overflow-y: auto;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
.chat-sidebar h2 {
|
| 39 |
+
margin-top: 0;
|
| 40 |
+
font-size: 20px;
|
| 41 |
+
border-bottom: 1px solid #4e4e8f;
|
| 42 |
+
padding-bottom: 10px;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
.chat-sidebar ul {
|
| 46 |
+
list-style-type: none;
|
| 47 |
+
padding: 0;
|
| 48 |
+
margin: 0;
|
| 49 |
+
flex: 1;
|
| 50 |
+
overflow-y: auto;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
.chat-sidebar ul li {
|
| 54 |
+
padding: 10px;
|
| 55 |
+
margin: 5px 0;
|
| 56 |
+
background-color: #393975;
|
| 57 |
+
border-radius: 5px;
|
| 58 |
+
cursor: pointer;
|
| 59 |
+
transition: background-color 0.3s;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
.chat-sidebar ul li:hover {
|
| 63 |
+
background-color: #5050a5;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
.new-chat-button {
|
| 67 |
+
padding: 10px;
|
| 68 |
+
background-color: #5050a5;
|
| 69 |
+
border: none;
|
| 70 |
+
color: white;
|
| 71 |
+
border-radius: 5px;
|
| 72 |
+
cursor: pointer;
|
| 73 |
+
font-size: 16px;
|
| 74 |
+
margin-top: 10px;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
.new-chat-button:hover {
|
| 78 |
+
background-color: #7070c0;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
/* Main Chat Interface */
|
| 82 |
+
.chat-main {
|
| 83 |
+
width: 75%;
|
| 84 |
+
display: flex;
|
| 85 |
+
flex-direction: column;
|
| 86 |
+
background-color: #ffffff;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
.chat-header {
|
| 90 |
+
background-color: #f7f8fc;
|
| 91 |
+
padding: 20px;
|
| 92 |
+
border-bottom: 1px solid #e2e2e2;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
.chat-header h1 {
|
| 96 |
+
margin: 0;
|
| 97 |
+
font-size: 22px;
|
| 98 |
+
color: #2c2c54;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
.chat-window {
|
| 102 |
+
flex: 1;
|
| 103 |
+
overflow-y: auto;
|
| 104 |
+
padding: 20px;
|
| 105 |
+
display: flex;
|
| 106 |
+
flex-direction: column;
|
| 107 |
+
gap: 15px;
|
| 108 |
+
background-color: #f5f5f5;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
.chat-bubble {
|
| 112 |
+
max-width: 70%;
|
| 113 |
+
padding: 15px;
|
| 114 |
+
border-radius: 15px;
|
| 115 |
+
word-wrap: break-word;
|
| 116 |
+
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
.user-message {
|
| 120 |
+
align-self: flex-end;
|
| 121 |
+
background-color: #3b82f6;
|
| 122 |
+
color: white;
|
| 123 |
+
border-bottom-right-radius: 0;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
.bot-response {
|
| 127 |
+
align-self: flex-start;
|
| 128 |
+
background-color: #e2e8f0;
|
| 129 |
+
color: #2c2c54;
|
| 130 |
+
border-bottom-left-radius: 0;
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
.chat-footer {
|
| 134 |
+
padding: 10px 20px;
|
| 135 |
+
background-color: #f7f8fc;
|
| 136 |
+
display: flex;
|
| 137 |
+
align-items: center;
|
| 138 |
+
border-top: 1px solid #e2e2e2;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
textarea#messageInput {
|
| 142 |
+
flex: 1;
|
| 143 |
+
resize: none;
|
| 144 |
+
border: 1px solid #d1d5db;
|
| 145 |
+
border-radius: 5px;
|
| 146 |
+
padding: 10px;
|
| 147 |
+
font-size: 16px;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
.send-button {
|
| 151 |
+
background-color: #3b82f6;
|
| 152 |
+
color: white;
|
| 153 |
+
border: none;
|
| 154 |
+
padding: 10px 15px;
|
| 155 |
+
margin-left: 10px;
|
| 156 |
+
border-radius: 5px;
|
| 157 |
+
cursor: pointer;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
.send-button i {
|
| 161 |
+
font-size: 18px;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
.send-button:hover {
|
| 165 |
+
background-color: #2563eb;
|
| 166 |
+
}
|
| 167 |
+
</style>
|
| 168 |
</head>
|
| 169 |
<body>
|
| 170 |
+
<div class="chatgpt-clone">
|
| 171 |
+
<!-- Sidebar for Chat History -->
|
| 172 |
+
<aside class="chat-sidebar">
|
| 173 |
+
<h2>Chat History</h2>
|
| 174 |
+
<ul id="chatHistoryList">
|
| 175 |
+
<!-- Chat history dynamically loaded here -->
|
| 176 |
+
</ul>
|
| 177 |
+
<button class="new-chat-button" onclick="startNewChat()">+ New Chat</button>
|
| 178 |
+
</aside>
|
| 179 |
+
|
| 180 |
+
<!-- Main Chat Interface -->
|
| 181 |
+
<main class="chat-main">
|
| 182 |
+
<header class="chat-header">
|
| 183 |
+
<h1>AI Assistant</h1>
|
| 184 |
+
</header>
|
| 185 |
+
|
| 186 |
+
<div class="chat-window" id="chatWindow">
|
| 187 |
+
<!-- Chat messages dynamically loaded here -->
|
| 188 |
+
</div>
|
| 189 |
+
|
| 190 |
+
<footer class="chat-footer">
|
| 191 |
<textarea
|
| 192 |
+
id="messageInput"
|
| 193 |
+
placeholder="Type your message..."
|
| 194 |
+
onkeydown="if (event.key === 'Enter' && !event.shiftKey) sendMessage(event);"
|
| 195 |
></textarea>
|
| 196 |
+
<button class="send-button" onclick="sendMessage(event)">
|
| 197 |
+
<i class="fas fa-paper-plane"></i>
|
| 198 |
+
</button>
|
| 199 |
+
</footer>
|
| 200 |
+
</main>
|
| 201 |
+
</div>
|
| 202 |
+
<script>
|
| 203 |
+
// Fetch chat history and populate the sidebar
|
| 204 |
+
async function fetchChatHistory() {
|
| 205 |
+
try {
|
| 206 |
+
const response = await fetch("/chat/history");
|
| 207 |
+
if (!response.ok) {
|
| 208 |
+
throw new Error("Failed to fetch chat history.");
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
const history = await response.json();
|
| 212 |
+
const historyList = document.getElementById("chatHistoryList");
|
| 213 |
+
historyList.innerHTML = "";
|
| 214 |
+
|
| 215 |
+
history.forEach(chat => {
|
| 216 |
+
const listItem = document.createElement("li");
|
| 217 |
+
listItem.textContent = chat.message;
|
| 218 |
+
listItem.onclick = () => loadChat(chat);
|
| 219 |
+
historyList.appendChild(listItem);
|
| 220 |
+
});
|
| 221 |
+
} catch (error) {
|
| 222 |
+
console.error("Error fetching chat history:", error);
|
| 223 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
}
|
| 225 |
+
|
| 226 |
+
// Send a new message
|
| 227 |
+
async function sendMessage(event) {
|
| 228 |
+
event.preventDefault();
|
| 229 |
+
const messageInput = document.getElementById("messageInput");
|
| 230 |
+
const message = messageInput.value.trim();
|
| 231 |
+
|
| 232 |
+
if (!message) return;
|
| 233 |
+
|
| 234 |
+
// Add user message to the chat window
|
| 235 |
+
addMessageToChatWindow(message, "user-message");
|
| 236 |
+
|
| 237 |
+
try {
|
| 238 |
+
const response = await fetch("/chat", {
|
| 239 |
+
method: "POST",
|
| 240 |
+
headers: {"Content-Type": "application/json"},
|
| 241 |
+
body: JSON.stringify({message}),
|
| 242 |
+
});
|
| 243 |
+
|
| 244 |
+
const data = await response.json();
|
| 245 |
+
addMessageToChatWindow(data.response, "bot-response");
|
| 246 |
+
} catch (error) {
|
| 247 |
+
console.error("Error sending message:", error);
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
messageInput.value = "";
|
| 251 |
}
|
| 252 |
|
| 253 |
+
// Add a message to the chat window
|
| 254 |
+
function addMessageToChatWindow(message, className) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
const chatWindow = document.getElementById("chatWindow");
|
| 256 |
+
const bubble = document.createElement("div");
|
| 257 |
+
bubble.className = `chat-bubble ${className}`;
|
| 258 |
+
bubble.textContent = message;
|
| 259 |
+
chatWindow.appendChild(bubble);
|
| 260 |
+
chatWindow.scrollTop = chatWindow.scrollHeight;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
// Start a new chat session
|
| 264 |
+
async function startNewChat() {
|
| 265 |
+
try {
|
| 266 |
+
await fetch("/chat/new", {method: "POST"});
|
| 267 |
+
const chatWindow = document.getElementById("chatWindow");
|
| 268 |
+
chatWindow.innerHTML = "";
|
| 269 |
+
} catch (error) {
|
| 270 |
+
console.error("Error starting new chat:", error);
|
| 271 |
+
}
|
| 272 |
}
|
|
|
|
| 273 |
|
| 274 |
+
// Initialize
|
| 275 |
+
document.addEventListener("DOMContentLoaded", () => {
|
| 276 |
+
fetchChatHistory();
|
| 277 |
+
});
|
| 278 |
|
| 279 |
+
</script>
|
| 280 |
</body>
|
| 281 |
</html>
|
backend/app/templates/dashboard.html
CHANGED
|
@@ -3,111 +3,111 @@
|
|
| 3 |
{% block title %}Dashboard{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
-
<div class="container-fluid">
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
<span class="info-box-icon text-bg-primary shadow-sm">
|
| 12 |
<i class="bi bi-clipboard-data"></i>
|
| 13 |
</span>
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
|
|
|
| 17 |
</div>
|
| 18 |
</div>
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
<div class="info-box">
|
| 22 |
<span class="info-box-icon text-bg-success shadow-sm">
|
| 23 |
<i class="bi bi-box-seam"></i>
|
| 24 |
</span>
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
| 28 |
</div>
|
| 29 |
</div>
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
<div class="info-box">
|
| 33 |
<span class="info-box-icon text-bg-danger shadow-sm">
|
| 34 |
<i class="bi bi-heart-pulse-fill"></i>
|
| 35 |
</span>
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
|
|
|
| 39 |
</div>
|
| 40 |
</div>
|
| 41 |
</div>
|
| 42 |
-
</div>
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
|
|
|
| 55 |
</div>
|
| 56 |
</div>
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
</
|
| 67 |
</div>
|
| 68 |
</div>
|
| 69 |
</div>
|
| 70 |
</div>
|
| 71 |
-
</div>
|
| 72 |
|
| 73 |
-
<script>
|
| 74 |
-
async function loadDashboardData() {
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
|
| 86 |
-
|
| 87 |
-
|
| 88 |
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
}
|
| 106 |
-
} catch (error) {
|
| 107 |
-
console.error("Error loading dashboard:", error);
|
| 108 |
-
}
|
| 109 |
-
}
|
| 110 |
|
| 111 |
-
document.addEventListener("DOMContentLoaded", loadDashboardData);
|
| 112 |
-
</script>
|
| 113 |
{% endblock %}
|
|
|
|
| 3 |
{% block title %}Dashboard{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
+
<div class="container-fluid">
|
| 7 |
+
<!-- Overview Statistics -->
|
| 8 |
+
<div class="row">
|
| 9 |
+
<div class="col-12 col-sm-6 col-md-3">
|
| 10 |
+
<div class="info-box">
|
| 11 |
<span class="info-box-icon text-bg-primary shadow-sm">
|
| 12 |
<i class="bi bi-clipboard-data"></i>
|
| 13 |
</span>
|
| 14 |
+
<div class="info-box-content">
|
| 15 |
+
<span class="info-box-text">Tasks</span>
|
| 16 |
+
<span class="info-box-number" id="tasksCount">0</span>
|
| 17 |
+
</div>
|
| 18 |
</div>
|
| 19 |
</div>
|
| 20 |
+
<div class="col-12 col-sm-6 col-md-3">
|
| 21 |
+
<div class="info-box">
|
|
|
|
| 22 |
<span class="info-box-icon text-bg-success shadow-sm">
|
| 23 |
<i class="bi bi-box-seam"></i>
|
| 24 |
</span>
|
| 25 |
+
<div class="info-box-content">
|
| 26 |
+
<span class="info-box-text">Inventory Items</span>
|
| 27 |
+
<span class="info-box-number" id="inventoryCount">0</span>
|
| 28 |
+
</div>
|
| 29 |
</div>
|
| 30 |
</div>
|
| 31 |
+
<div class="col-12 col-sm-6 col-md-3">
|
| 32 |
+
<div class="info-box">
|
|
|
|
| 33 |
<span class="info-box-icon text-bg-danger shadow-sm">
|
| 34 |
<i class="bi bi-heart-pulse-fill"></i>
|
| 35 |
</span>
|
| 36 |
+
<div class="info-box-content">
|
| 37 |
+
<span class="info-box-text">Health Records</span>
|
| 38 |
+
<span class="info-box-number" id="healthRecordsCount">0</span>
|
| 39 |
+
</div>
|
| 40 |
</div>
|
| 41 |
</div>
|
| 42 |
</div>
|
|
|
|
| 43 |
|
| 44 |
+
<!-- Recent Updates Section -->
|
| 45 |
+
<div class="row mt-4">
|
| 46 |
+
<div class="col-lg-6">
|
| 47 |
+
<div class="card">
|
| 48 |
+
<div class="card-header bg-primary text-white">
|
| 49 |
+
<h3 class="card-title">Recent Tasks</h3>
|
| 50 |
+
</div>
|
| 51 |
+
<div class="card-body">
|
| 52 |
+
<ul id="recentTasks" class="list-group">
|
| 53 |
+
<li class="list-group-item">No recent tasks available.</li>
|
| 54 |
+
</ul>
|
| 55 |
+
</div>
|
| 56 |
</div>
|
| 57 |
</div>
|
| 58 |
+
<div class="col-lg-6">
|
| 59 |
+
<div class="card">
|
| 60 |
+
<div class="card-header bg-success text-white">
|
| 61 |
+
<h3 class="card-title">Latest Health Records</h3>
|
| 62 |
+
</div>
|
| 63 |
+
<div class="card-body">
|
| 64 |
+
<ul id="recentHealthRecords" class="list-group">
|
| 65 |
+
<li class="list-group-item">No recent health records available.</li>
|
| 66 |
+
</ul>
|
| 67 |
+
</div>
|
| 68 |
</div>
|
| 69 |
</div>
|
| 70 |
</div>
|
| 71 |
</div>
|
|
|
|
| 72 |
|
| 73 |
+
<script>
|
| 74 |
+
async function loadDashboardData() {
|
| 75 |
+
const token = sessionStorage.getItem('access_token');
|
| 76 |
+
if (!token) {
|
| 77 |
+
window.location.href = "/login";
|
| 78 |
+
return;
|
| 79 |
+
}
|
| 80 |
|
| 81 |
+
try {
|
| 82 |
+
const response = await fetch('/dashboard/data', {
|
| 83 |
+
headers: {'Authorization': 'Bearer ' + token}
|
| 84 |
+
});
|
| 85 |
|
| 86 |
+
if (response.ok) {
|
| 87 |
+
const data = await response.json();
|
| 88 |
|
| 89 |
+
// Update Statistics
|
| 90 |
+
document.getElementById('tasksCount').innerText = data.tasksCount || 0;
|
| 91 |
+
document.getElementById('inventoryCount').innerText = data.inventoryCount || 0;
|
| 92 |
+
document.getElementById('healthRecordsCount').innerText = data.healthRecordsCount || 0;
|
| 93 |
|
| 94 |
+
// Populate Recent Tasks
|
| 95 |
+
document.getElementById('recentTasks').innerHTML = data.recentTasks.length
|
| 96 |
+
? data.recentTasks.map(task => `<li class="list-group-item">${task.description} (Due: ${new Date(task.due_date).toLocaleDateString()})</li>`).join('')
|
| 97 |
+
: '<li class="list-group-item">No recent tasks available.</li>';
|
| 98 |
|
| 99 |
+
// Populate Recent Health Records
|
| 100 |
+
document.getElementById('recentHealthRecords').innerHTML = data.recentHealthRecords.length
|
| 101 |
+
? data.recentHealthRecords.map(record => `<li class="list-group-item">${record.disease_type} (Logged: ${new Date(record.diagnosis_date).toLocaleDateString()})</li>`).join('')
|
| 102 |
+
: '<li class="list-group-item">No recent health records available.</li>';
|
| 103 |
+
} else {
|
| 104 |
+
console.error("Error fetching dashboard data:", response.statusText);
|
| 105 |
+
}
|
| 106 |
+
} catch (error) {
|
| 107 |
+
console.error("Error loading dashboard:", error);
|
| 108 |
+
}
|
| 109 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
+
document.addEventListener("DOMContentLoaded", loadDashboardData);
|
| 112 |
+
</script>
|
| 113 |
{% endblock %}
|
backend/app/templates/group/manage_group.html
CHANGED
|
@@ -3,60 +3,63 @@
|
|
| 3 |
{% block title %}Manage Group{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
-
<div class="container mt-4">
|
| 7 |
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
| 14 |
</div>
|
| 15 |
-
</div>
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
<
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
<
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
</div>
|
| 38 |
-
</div>
|
| 39 |
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
<tr>
|
| 47 |
<th>Username</th>
|
| 48 |
<th>Email</th>
|
| 49 |
<th>Actions</th>
|
| 50 |
</tr>
|
| 51 |
-
|
| 52 |
-
|
| 53 |
<!-- Farmers will be loaded here via JavaScript -->
|
| 54 |
-
|
| 55 |
-
|
|
|
|
| 56 |
</div>
|
| 57 |
</div>
|
| 58 |
-
</div>
|
| 59 |
|
| 60 |
-
<script src="../static/js/group.js"></script>
|
| 61 |
|
| 62 |
{% endblock %}
|
|
|
|
| 3 |
{% block title %}Manage Group{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
+
<div class="container mt-4">
|
| 7 |
|
| 8 |
+
<!-- Group Details Section -->
|
| 9 |
+
<div class="card mb-4" id="groupDetailsSection">
|
| 10 |
+
<div class="card-header">Group Details</div>
|
| 11 |
+
<div class="card-body">
|
| 12 |
+
<p><strong>Farm Name:</strong> <span id="farmName"></span></p>
|
| 13 |
+
<button id="addFarmerButton" class="btn btn-primary" onclick="showAddFarmerForm()">Add Farmer</button>
|
| 14 |
+
</div>
|
| 15 |
</div>
|
|
|
|
| 16 |
|
| 17 |
+
<!-- Add Farmer Form (Hidden initially) -->
|
| 18 |
+
<div class="card mb-4" id="addFarmerSection" style="display: none;">
|
| 19 |
+
<div class="card-header">Add Farmer to Group</div>
|
| 20 |
+
<div class="card-body">
|
| 21 |
+
<form id="addFarmerForm" onsubmit="addFarmer(event)">
|
| 22 |
+
<div class="form-group">
|
| 23 |
+
<label for="farmer_username">Farmer Username</label>
|
| 24 |
+
<input type="text" id="farmer_username" class="form-control" placeholder="Enter farmer username"
|
| 25 |
+
required>
|
| 26 |
+
</div>
|
| 27 |
+
<div class="form-group">
|
| 28 |
+
<label for="farmer_email">Farmer Email</label>
|
| 29 |
+
<input type="email" id="farmer_email" class="form-control" placeholder="Enter farmer email"
|
| 30 |
+
required>
|
| 31 |
+
</div>
|
| 32 |
+
<div class="form-group">
|
| 33 |
+
<label for="farmer_password">Password</label>
|
| 34 |
+
<input type="password" id="farmer_password" class="form-control"
|
| 35 |
+
placeholder="Set a password for the farmer" required>
|
| 36 |
+
</div>
|
| 37 |
+
<button type="submit" class="btn btn-success mt-2">Add Farmer</button>
|
| 38 |
+
<button type="button" class="btn btn-secondary mt-2" onclick="hideAddFarmerForm()">Cancel</button>
|
| 39 |
+
</form>
|
| 40 |
+
</div>
|
| 41 |
</div>
|
|
|
|
| 42 |
|
| 43 |
+
<!-- Farmers List -->
|
| 44 |
+
<div class="card">
|
| 45 |
+
<div class="card-header">Farmers in Group</div>
|
| 46 |
+
<div class="card-body">
|
| 47 |
+
<table class="table table-striped" id="farmersTable">
|
| 48 |
+
<thead>
|
| 49 |
<tr>
|
| 50 |
<th>Username</th>
|
| 51 |
<th>Email</th>
|
| 52 |
<th>Actions</th>
|
| 53 |
</tr>
|
| 54 |
+
</thead>
|
| 55 |
+
<tbody>
|
| 56 |
<!-- Farmers will be loaded here via JavaScript -->
|
| 57 |
+
</tbody>
|
| 58 |
+
</table>
|
| 59 |
+
</div>
|
| 60 |
</div>
|
| 61 |
</div>
|
|
|
|
| 62 |
|
| 63 |
+
<script src="../static/js/group.js"></script>
|
| 64 |
|
| 65 |
{% endblock %}
|
backend/app/templates/health/log_health.html
CHANGED
|
@@ -3,31 +3,33 @@
|
|
| 3 |
{% block title %}Log Health Record{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
-
<div class="card">
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
<
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
| 29 |
</div>
|
| 30 |
-
</div>
|
| 31 |
|
| 32 |
-
<script src="../static/js/health.js"></script>
|
| 33 |
{% endblock %}
|
|
|
|
| 3 |
{% block title %}Log Health Record{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
+
<div class="card">
|
| 7 |
+
<div class="card-body">
|
| 8 |
+
<h3 class="card-title">Log Health Record with Disease Detection</h3>
|
| 9 |
+
<form id="logHealthForm" onsubmit="detectAndLogHealthRecord(event)">
|
| 10 |
+
<!-- Image Upload -->
|
| 11 |
+
<div class="form-group">
|
| 12 |
+
<label for="image" class="form-label">Upload Image for Disease Detection</label>
|
| 13 |
+
<input type="file" id="image" class="form-control" accept="image/*" required>
|
| 14 |
+
<small class="form-text text-muted">Supported formats: JPG, PNG. Ensure the image is clear.</small>
|
| 15 |
+
</div>
|
| 16 |
+
<!-- Detected Disease Type -->
|
| 17 |
+
<div class="form-group mt-3">
|
| 18 |
+
<label for="disease_type" class="form-label">Detected Disease Type</label>
|
| 19 |
+
<input type="text" id="disease_type" class="form-control" readonly
|
| 20 |
+
placeholder="Disease will be auto-detected">
|
| 21 |
+
</div>
|
| 22 |
+
<!-- Recommendation -->
|
| 23 |
+
<div class="form-group mt-3">
|
| 24 |
+
<label for="recommendation" class="form-label">Recommendation</label>
|
| 25 |
+
<textarea id="recommendation" class="form-control" rows="4" readonly
|
| 26 |
+
placeholder="Recommendation will be auto-generated"></textarea>
|
| 27 |
+
</div>
|
| 28 |
+
<!-- Submit Button -->
|
| 29 |
+
<button type="submit" class="btn btn-dark mt-3">Log Health Record</button>
|
| 30 |
+
</form>
|
| 31 |
+
</div>
|
| 32 |
</div>
|
|
|
|
| 33 |
|
| 34 |
+
<script src="../static/js/health.js"></script>
|
| 35 |
{% endblock %}
|
backend/app/templates/health/manage_health.html
CHANGED
|
@@ -3,68 +3,70 @@
|
|
| 3 |
{% block title %}Manage Health Records{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
-
<div class="card">
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
<tr>
|
| 11 |
<th>Disease Type</th>
|
| 12 |
<th>Recommendation</th>
|
| 13 |
<th>Diagnosis Date</th>
|
| 14 |
<th>Actions</th>
|
| 15 |
</tr>
|
| 16 |
-
|
| 17 |
-
|
| 18 |
<!-- Rows will be populated by JavaScript -->
|
| 19 |
-
|
| 20 |
-
|
|
|
|
| 21 |
</div>
|
| 22 |
-
</div>
|
| 23 |
|
| 24 |
-
<!-- Update Health Record Modal -->
|
| 25 |
-
<div class="modal fade" id="updateHealthModal" tabindex="-1" aria-labelledby="updateHealthModalLabel"
|
| 26 |
-
|
| 27 |
-
<div class="modal-
|
| 28 |
-
<div class="modal-
|
| 29 |
-
<
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
<
|
| 34 |
-
<
|
| 35 |
-
|
| 36 |
-
<
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
<
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
|
|
|
|
|
|
| 45 |
</div>
|
| 46 |
</div>
|
| 47 |
</div>
|
| 48 |
-
</div>
|
| 49 |
|
| 50 |
-
<!-- Delete Confirmation Modal -->
|
| 51 |
-
<div class="modal fade" id="deleteHealthModal" tabindex="-1" aria-labelledby="deleteHealthModalLabel"
|
| 52 |
-
|
| 53 |
-
<div class="modal-
|
| 54 |
-
<div class="modal-
|
| 55 |
-
<
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
<
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
<
|
| 63 |
-
|
|
|
|
|
|
|
| 64 |
</div>
|
| 65 |
</div>
|
| 66 |
</div>
|
| 67 |
-
</div>
|
| 68 |
|
| 69 |
-
<script src="../static/js/health.js"></script>
|
| 70 |
{% endblock %}
|
|
|
|
| 3 |
{% block title %}Manage Health Records{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
+
<div class="card">
|
| 7 |
+
<div class="card-body">
|
| 8 |
+
<table class="table table-bordered table-hover" id="healthRecordsTable">
|
| 9 |
+
<thead class="table-primary">
|
| 10 |
<tr>
|
| 11 |
<th>Disease Type</th>
|
| 12 |
<th>Recommendation</th>
|
| 13 |
<th>Diagnosis Date</th>
|
| 14 |
<th>Actions</th>
|
| 15 |
</tr>
|
| 16 |
+
</thead>
|
| 17 |
+
<tbody>
|
| 18 |
<!-- Rows will be populated by JavaScript -->
|
| 19 |
+
</tbody>
|
| 20 |
+
</table>
|
| 21 |
+
</div>
|
| 22 |
</div>
|
|
|
|
| 23 |
|
| 24 |
+
<!-- Update Health Record Modal -->
|
| 25 |
+
<div class="modal fade" id="updateHealthModal" tabindex="-1" aria-labelledby="updateHealthModalLabel"
|
| 26 |
+
aria-hidden="true">
|
| 27 |
+
<div class="modal-dialog">
|
| 28 |
+
<div class="modal-content">
|
| 29 |
+
<div class="modal-header">
|
| 30 |
+
<h5 class="modal-title" id="updateHealthModalLabel">Update Health Record</h5>
|
| 31 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 32 |
+
</div>
|
| 33 |
+
<div class="modal-body">
|
| 34 |
+
<form id="updateHealthForm" onsubmit="updateHealthRecord(event)">
|
| 35 |
+
<input type="hidden" id="updateRecordId">
|
| 36 |
+
<div class="form-group">
|
| 37 |
+
<label for="updateDiseaseType">Disease Type</label>
|
| 38 |
+
<input type="text" id="updateDiseaseType" class="form-control" readonly>
|
| 39 |
+
</div>
|
| 40 |
+
<div class="form-group mt-3">
|
| 41 |
+
<label for="updateRecommendation">Recommendation</label>
|
| 42 |
+
<textarea id="updateRecommendation" class="form-control" rows="4" readonly></textarea>
|
| 43 |
+
</div>
|
| 44 |
+
<button type="submit" class="btn btn-primary mt-3">Update Record</button>
|
| 45 |
+
</form>
|
| 46 |
+
</div>
|
| 47 |
</div>
|
| 48 |
</div>
|
| 49 |
</div>
|
|
|
|
| 50 |
|
| 51 |
+
<!-- Delete Confirmation Modal -->
|
| 52 |
+
<div class="modal fade" id="deleteHealthModal" tabindex="-1" aria-labelledby="deleteHealthModalLabel"
|
| 53 |
+
aria-hidden="true">
|
| 54 |
+
<div class="modal-dialog">
|
| 55 |
+
<div class="modal-content">
|
| 56 |
+
<div class="modal-header">
|
| 57 |
+
<h5 class="modal-title" id="deleteHealthModalLabel">Delete Health Record</h5>
|
| 58 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 59 |
+
</div>
|
| 60 |
+
<div class="modal-body">
|
| 61 |
+
<p>Are you sure you want to delete this health record? This action cannot be undone.</p>
|
| 62 |
+
</div>
|
| 63 |
+
<div class="modal-footer">
|
| 64 |
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
| 65 |
+
<button type="button" class="btn btn-danger" onclick="deleteHealthRecord()">Delete</button>
|
| 66 |
+
</div>
|
| 67 |
</div>
|
| 68 |
</div>
|
| 69 |
</div>
|
|
|
|
| 70 |
|
| 71 |
+
<script src="../static/js/health.js"></script>
|
| 72 |
{% endblock %}
|
backend/app/templates/inventory/add_inventory.html
CHANGED
|
@@ -4,52 +4,52 @@
|
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
|
| 7 |
-
<div class="card">
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
|
|
|
| 51 |
</div>
|
| 52 |
-
</div>
|
| 53 |
|
| 54 |
-
<script src="../static/js/inventory.js"></script>
|
| 55 |
{% endblock %}
|
|
|
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
|
| 7 |
+
<div class="card">
|
| 8 |
+
<div class="card-body">
|
| 9 |
+
<form id="addInventoryForm" onsubmit="addInventoryItem(event)">
|
| 10 |
+
<!-- Item Name Input -->
|
| 11 |
+
<div class="form-group mt-3">
|
| 12 |
+
<label for="item_name">Item Name</label>
|
| 13 |
+
<input
|
| 14 |
+
class="form-control"
|
| 15 |
+
id="item_name"
|
| 16 |
+
placeholder="Enter item name"
|
| 17 |
+
required
|
| 18 |
+
type="text">
|
| 19 |
+
</div>
|
| 20 |
+
|
| 21 |
+
<!-- Quantity Input -->
|
| 22 |
+
<div class="form-group mt-3">
|
| 23 |
+
<label for="quantity">Quantity</label>
|
| 24 |
+
<input
|
| 25 |
+
class="form-control"
|
| 26 |
+
id="quantity"
|
| 27 |
+
placeholder="Enter quantity"
|
| 28 |
+
required
|
| 29 |
+
type="number"
|
| 30 |
+
min="1">
|
| 31 |
+
</div>
|
| 32 |
+
|
| 33 |
+
<!-- Category Selection -->
|
| 34 |
+
<div class="form-group mt-3">
|
| 35 |
+
<label for="category">Category</label>
|
| 36 |
+
<select
|
| 37 |
+
class="form-control"
|
| 38 |
+
id="category"
|
| 39 |
+
required>
|
| 40 |
+
<option value="" disabled selected>Select category</option>
|
| 41 |
+
<option value="Poultry Animal">Poultry Animal</option>
|
| 42 |
+
<option value="Feed">Feed</option>
|
| 43 |
+
<option value="Farm Tool">Farm Tool</option>
|
| 44 |
+
</select>
|
| 45 |
+
</div>
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
<!-- Submit Button -->
|
| 49 |
+
<button class="btn btn-primary mt-3" type="submit">Add Item</button>
|
| 50 |
+
</form>
|
| 51 |
+
</div>
|
| 52 |
</div>
|
|
|
|
| 53 |
|
| 54 |
+
<script src="../static/js/inventory.js"></script>
|
| 55 |
{% endblock %}
|
backend/app/templates/inventory/view_inventory.html
CHANGED
|
@@ -4,10 +4,10 @@
|
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
|
| 7 |
-
<div class="card">
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
<tr>
|
| 12 |
<th>Item Name</th>
|
| 13 |
<th>Quantity</th>
|
|
@@ -15,73 +15,76 @@
|
|
| 15 |
<th>Last Updated</th>
|
| 16 |
<th>Actions</th>
|
| 17 |
</tr>
|
| 18 |
-
|
| 19 |
-
|
| 20 |
<!-- Rows will be dynamically populated by JavaScript -->
|
| 21 |
-
|
| 22 |
-
|
|
|
|
| 23 |
</div>
|
| 24 |
-
</div>
|
| 25 |
|
| 26 |
-
<!-- Update Inventory Modal -->
|
| 27 |
-
<div class="modal fade" id="updateModal" tabindex="-1" aria-labelledby="updateModalLabel" aria-hidden="true">
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
<
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
<
|
| 48 |
-
<
|
| 49 |
-
<
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
<
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
</div>
|
| 63 |
</div>
|
| 64 |
</div>
|
| 65 |
-
</div>
|
| 66 |
|
| 67 |
-
<!-- Delete Confirmation Modal -->
|
| 68 |
-
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
|
|
|
| 81 |
</div>
|
| 82 |
</div>
|
| 83 |
</div>
|
| 84 |
-
</div>
|
| 85 |
|
| 86 |
-
<script src="../static/js/inventory.js"></script>
|
| 87 |
{% endblock %}
|
|
|
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
|
| 7 |
+
<div class="card">
|
| 8 |
+
<div class="card-body">
|
| 9 |
+
<table class="table table-striped table-bordered" id="inventoryTable">
|
| 10 |
+
<thead class="table-dark">
|
| 11 |
<tr>
|
| 12 |
<th>Item Name</th>
|
| 13 |
<th>Quantity</th>
|
|
|
|
| 15 |
<th>Last Updated</th>
|
| 16 |
<th>Actions</th>
|
| 17 |
</tr>
|
| 18 |
+
</thead>
|
| 19 |
+
<tbody>
|
| 20 |
<!-- Rows will be dynamically populated by JavaScript -->
|
| 21 |
+
</tbody>
|
| 22 |
+
</table>
|
| 23 |
+
</div>
|
| 24 |
</div>
|
|
|
|
| 25 |
|
| 26 |
+
<!-- Update Inventory Modal -->
|
| 27 |
+
<div class="modal fade" id="updateModal" tabindex="-1" aria-labelledby="updateModalLabel" aria-hidden="true">
|
| 28 |
+
<div class="modal-dialog">
|
| 29 |
+
<div class="modal-content">
|
| 30 |
+
<div class="modal-header bg-secondary text-white">
|
| 31 |
+
<h5 class="modal-title" id="updateModalLabel">Update Inventory Item</h5>
|
| 32 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 33 |
+
</div>
|
| 34 |
+
<div class="modal-body">
|
| 35 |
+
<form id="updateInventoryForm" onsubmit="updateInventoryItem(event)">
|
| 36 |
+
<input type="hidden" id="updateItemId">
|
| 37 |
+
<div class="form-group mb-3">
|
| 38 |
+
<label for="updateItemName" class="form-label">Item Name</label>
|
| 39 |
+
<input type="text" id="updateItemName" class="form-control" placeholder="Enter item name"
|
| 40 |
+
required>
|
| 41 |
+
</div>
|
| 42 |
+
<div class="form-group mb-3">
|
| 43 |
+
<label for="updateQuantity" class="form-label">Quantity</label>
|
| 44 |
+
<input type="number" id="updateQuantity" class="form-control" placeholder="Enter quantity"
|
| 45 |
+
required>
|
| 46 |
+
</div>
|
| 47 |
+
<div class="form-group mb-3">
|
| 48 |
+
<label for="updateCategory" class="form-label">Category</label>
|
| 49 |
+
<select id="updateCategory" class="form-control" required>
|
| 50 |
+
<option value="" disabled selected>Select Category</option>
|
| 51 |
+
<option value="Poultry Animal">Poultry Animal</option>
|
| 52 |
+
<option value="Feed">Feed</option>
|
| 53 |
+
<option value="Farm Tool">Farm Tool</option>
|
| 54 |
+
</select>
|
| 55 |
+
</div>
|
| 56 |
+
<div class="form-group mb-3">
|
| 57 |
+
<label for="updateStatus" class="form-label">Status</label>
|
| 58 |
+
<input type="text" id="updateStatus" class="form-control" placeholder="Enter status"
|
| 59 |
+
required>
|
| 60 |
+
</div>
|
| 61 |
+
<div class="d-grid">
|
| 62 |
+
<button type="submit" class="btn btn-dark">Update Item</button>
|
| 63 |
+
</div>
|
| 64 |
+
</form>
|
| 65 |
+
</div>
|
| 66 |
</div>
|
| 67 |
</div>
|
| 68 |
</div>
|
|
|
|
| 69 |
|
| 70 |
+
<!-- Delete Confirmation Modal -->
|
| 71 |
+
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
| 72 |
+
<div class="modal-dialog">
|
| 73 |
+
<div class="modal-content">
|
| 74 |
+
<div class="modal-header bg-danger text-white">
|
| 75 |
+
<h5 class="modal-title" id="deleteModalLabel">Delete Inventory Item</h5>
|
| 76 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 77 |
+
</div>
|
| 78 |
+
<div class="modal-body">
|
| 79 |
+
<p class="text-danger">Are you sure you want to delete this item? This action cannot be undone.</p>
|
| 80 |
+
</div>
|
| 81 |
+
<div class="modal-footer">
|
| 82 |
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
| 83 |
+
<button type="button" class="btn btn-danger" onclick="deleteInventoryItem()">Delete</button>
|
| 84 |
+
</div>
|
| 85 |
</div>
|
| 86 |
</div>
|
| 87 |
</div>
|
|
|
|
| 88 |
|
| 89 |
+
<script src="../static/js/inventory.js"></script>
|
| 90 |
{% endblock %}
|
backend/app/templates/landing.html
CHANGED
|
@@ -95,8 +95,10 @@
|
|
| 95 |
</section>
|
| 96 |
|
| 97 |
<script crossorigin="anonymous"
|
| 98 |
-
integrity="sha256-x5Mj2wYeSa9WVOK+EK8Z5rmXHFZ+MOY8r4eW8AKzpXU="
|
|
|
|
| 99 |
<script crossorigin="anonymous"
|
| 100 |
-
integrity="sha256-qq1ob4lpAizCQs1tkv5gttMXUlgpiHyvG3JcCIktRvs="
|
|
|
|
| 101 |
</body>
|
| 102 |
</html>
|
|
|
|
| 95 |
</section>
|
| 96 |
|
| 97 |
<script crossorigin="anonymous"
|
| 98 |
+
integrity="sha256-x5Mj2wYeSa9WVOK+EK8Z5rmXHFZ+MOY8r4eW8AKzpXU="
|
| 99 |
+
src="https://cdn.jsdelivr.net/npm/admin-lte@4.0.0-beta2/dist/js/adminlte.min.js"></script>
|
| 100 |
<script crossorigin="anonymous"
|
| 101 |
+
integrity="sha256-qq1ob4lpAizCQs1tkv5gttMXUlgpiHyvG3JcCIktRvs="
|
| 102 |
+
src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/all.min.js"></script>
|
| 103 |
</body>
|
| 104 |
</html>
|
backend/app/templates/main/base.html
CHANGED
|
@@ -8,13 +8,18 @@
|
|
| 8 |
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
|
| 9 |
integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" rel="stylesheet"><!--end::Fonts-->
|
| 10 |
<!--begin::Third Party Plugin(OverlayScrollbars)-->
|
| 11 |
-
<link crossorigin="anonymous"
|
|
|
|
| 12 |
integrity="sha256-dSokZseQNT08wYEWiz5iLI8QPlKxG+TswNRD8k35cpg=" rel="stylesheet">
|
| 13 |
<!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Third Party Plugin(Bootstrap Icons)-->
|
| 14 |
-
<link crossorigin="anonymous"
|
|
|
|
| 15 |
integrity="sha256-Qsx5lrStHZyR9REqhUF8iQt73X06c8LGIUPzpOhwRrI=" rel="stylesheet">
|
| 16 |
<!--end::Third Party Plugin(Bootstrap Icons)-->
|
| 17 |
-
<link rel="stylesheet"
|
|
|
|
|
|
|
|
|
|
| 18 |
</head> <!--end::Head--> <!--begin::Body-->
|
| 19 |
|
| 20 |
<body class="layout-fixed sidebar-expand-lg bg-body-tertiary">
|
|
@@ -61,17 +66,21 @@
|
|
| 61 |
|
| 62 |
<script src="../static/js/auth.js"></script>
|
| 63 |
<script crossorigin="anonymous"
|
| 64 |
-
integrity="sha256-H2VM7BKda+v2Z4+DRy69uknwxjyDRhszjXFhsL4gD3w="
|
|
|
|
| 65 |
<!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Required Plugin(popperjs for Bootstrap 5)-->
|
| 66 |
<script crossorigin="anonymous"
|
| 67 |
-
integrity="sha256-whL0tQWoY1Ku1iskqPFvmZ+CHsvmRWx/PIoEvIeWh4I="
|
|
|
|
| 68 |
<!--end::Required Plugin(popperjs for Bootstrap 5)--><!--begin::Required Plugin(Bootstrap 5)-->
|
| 69 |
<script crossorigin="anonymous"
|
| 70 |
-
integrity="sha256-YMa+wAM6QkVyz999odX7lPRxkoYAan8suedu4k2Zur8="
|
|
|
|
| 71 |
<!--end::Required Plugin(Bootstrap 5)--><!--begin::Required Plugin(AdminLTE)-->
|
| 72 |
<script src="../static/js/adminlte.js"></script> <!--end::Required Plugin(AdminLTE)-->
|
| 73 |
<!--begin::OverlayScrollbars Configure-->
|
| 74 |
-
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/all.min.js"
|
|
|
|
| 75 |
<script>
|
| 76 |
const SELECTOR_SIDEBAR_WRAPPER = ".sidebar-wrapper";
|
| 77 |
const Default = {
|
|
|
|
| 8 |
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
|
| 9 |
integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" rel="stylesheet"><!--end::Fonts-->
|
| 10 |
<!--begin::Third Party Plugin(OverlayScrollbars)-->
|
| 11 |
+
<link crossorigin="anonymous"
|
| 12 |
+
href="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/styles/overlayscrollbars.min.css"
|
| 13 |
integrity="sha256-dSokZseQNT08wYEWiz5iLI8QPlKxG+TswNRD8k35cpg=" rel="stylesheet">
|
| 14 |
<!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Third Party Plugin(Bootstrap Icons)-->
|
| 15 |
+
<link crossorigin="anonymous"
|
| 16 |
+
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.min.css"
|
| 17 |
integrity="sha256-Qsx5lrStHZyR9REqhUF8iQt73X06c8LGIUPzpOhwRrI=" rel="stylesheet">
|
| 18 |
<!--end::Third Party Plugin(Bootstrap Icons)-->
|
| 19 |
+
<link rel="stylesheet"
|
| 20 |
+
href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/css/fontawesome.min.css"
|
| 21 |
+
integrity="sha256-XfA0ppGOANs88Ds+9FqVLy3xIGzT/25K/VLmRRxE9ow=" crossorigin="anonymous">
|
| 22 |
+
<!--begin::Required Plugin(AdminLTE)-->
|
| 23 |
</head> <!--end::Head--> <!--begin::Body-->
|
| 24 |
|
| 25 |
<body class="layout-fixed sidebar-expand-lg bg-body-tertiary">
|
|
|
|
| 66 |
|
| 67 |
<script src="../static/js/auth.js"></script>
|
| 68 |
<script crossorigin="anonymous"
|
| 69 |
+
integrity="sha256-H2VM7BKda+v2Z4+DRy69uknwxjyDRhszjXFhsL4gD3w="
|
| 70 |
+
src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/browser/overlayscrollbars.browser.es6.min.js"></script>
|
| 71 |
<!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Required Plugin(popperjs for Bootstrap 5)-->
|
| 72 |
<script crossorigin="anonymous"
|
| 73 |
+
integrity="sha256-whL0tQWoY1Ku1iskqPFvmZ+CHsvmRWx/PIoEvIeWh4I="
|
| 74 |
+
src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
|
| 75 |
<!--end::Required Plugin(popperjs for Bootstrap 5)--><!--begin::Required Plugin(Bootstrap 5)-->
|
| 76 |
<script crossorigin="anonymous"
|
| 77 |
+
integrity="sha256-YMa+wAM6QkVyz999odX7lPRxkoYAan8suedu4k2Zur8="
|
| 78 |
+
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"></script>
|
| 79 |
<!--end::Required Plugin(Bootstrap 5)--><!--begin::Required Plugin(AdminLTE)-->
|
| 80 |
<script src="../static/js/adminlte.js"></script> <!--end::Required Plugin(AdminLTE)-->
|
| 81 |
<!--begin::OverlayScrollbars Configure-->
|
| 82 |
+
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/all.min.js"
|
| 83 |
+
integrity="sha256-qq1ob4lpAizCQs1tkv5gttMXUlgpiHyvG3JcCIktRvs=" crossorigin="anonymous"></script>
|
| 84 |
<script>
|
| 85 |
const SELECTOR_SIDEBAR_WRAPPER = ".sidebar-wrapper";
|
| 86 |
const Default = {
|
backend/app/templates/main/partials/sidebar.html
CHANGED
|
@@ -26,8 +26,10 @@
|
|
| 26 |
<p>Inventory<i class="nav-arrow bi bi-chevron-right"></i></p>
|
| 27 |
</a>
|
| 28 |
<ul class="nav nav-treeview">
|
| 29 |
-
<li class="nav-item"><a class="nav-link" href="/additem"><i class="far fa-circle nav-icon"></i>
|
| 30 |
-
|
|
|
|
|
|
|
| 31 |
</ul>
|
| 32 |
</li>
|
| 33 |
|
|
@@ -38,8 +40,10 @@
|
|
| 38 |
<p>Health Monitoring<i class="nav-arrow bi bi-chevron-right"></i></p>
|
| 39 |
</a>
|
| 40 |
<ul class="nav nav-treeview">
|
| 41 |
-
<li class="nav-item"><a class="nav-link" href="/addrecord"><i
|
| 42 |
-
|
|
|
|
|
|
|
| 43 |
</ul>
|
| 44 |
</li>
|
| 45 |
|
|
@@ -51,23 +55,26 @@
|
|
| 51 |
</a>
|
| 52 |
<ul class="nav nav-treeview">
|
| 53 |
{% if is_admin %}
|
| 54 |
-
|
|
|
|
| 55 |
{% endif %}
|
| 56 |
-
<li class="nav-item"><a class="nav-link" href="/tasks"><i class="far fa-circle nav-icon"></i>
|
|
|
|
| 57 |
</ul>
|
| 58 |
</li>
|
| 59 |
|
| 60 |
<!-- Group Management (Visible only to Admins) -->
|
| 61 |
{% if is_admin %}
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
|
|
|
| 71 |
{% endif %}
|
| 72 |
|
| 73 |
</ul>
|
|
|
|
| 26 |
<p>Inventory<i class="nav-arrow bi bi-chevron-right"></i></p>
|
| 27 |
</a>
|
| 28 |
<ul class="nav nav-treeview">
|
| 29 |
+
<li class="nav-item"><a class="nav-link" href="/additem"><i class="far fa-circle nav-icon"></i>
|
| 30 |
+
Add Inventory</a></li>
|
| 31 |
+
<li class="nav-item"><a class="nav-link" href="/items"><i class="far fa-circle nav-icon"></i>
|
| 32 |
+
Manage Inventory</a></li>
|
| 33 |
</ul>
|
| 34 |
</li>
|
| 35 |
|
|
|
|
| 40 |
<p>Health Monitoring<i class="nav-arrow bi bi-chevron-right"></i></p>
|
| 41 |
</a>
|
| 42 |
<ul class="nav nav-treeview">
|
| 43 |
+
<li class="nav-item"><a class="nav-link" href="/addrecord"><i
|
| 44 |
+
class="far fa-circle nav-icon"></i> Add Health Record</a></li>
|
| 45 |
+
<li class="nav-item"><a class="nav-link" href="/hrecords"><i class="far fa-circle nav-icon"></i>
|
| 46 |
+
Manage Health Records</a></li>
|
| 47 |
</ul>
|
| 48 |
</li>
|
| 49 |
|
|
|
|
| 55 |
</a>
|
| 56 |
<ul class="nav nav-treeview">
|
| 57 |
{% if is_admin %}
|
| 58 |
+
<li class="nav-item"><a class="nav-link" href="/addtask"><i
|
| 59 |
+
class="far fa-circle nav-icon"></i> Add Task</a></li>
|
| 60 |
{% endif %}
|
| 61 |
+
<li class="nav-item"><a class="nav-link" href="/tasks"><i class="far fa-circle nav-icon"></i>
|
| 62 |
+
Manage Tasks</a></li>
|
| 63 |
</ul>
|
| 64 |
</li>
|
| 65 |
|
| 66 |
<!-- Group Management (Visible only to Admins) -->
|
| 67 |
{% if is_admin %}
|
| 68 |
+
<li class="nav-item">
|
| 69 |
+
<a class="nav-link" href="#">
|
| 70 |
+
<i class="nav-icon fas fa-users"></i>
|
| 71 |
+
<p>Group Management<i class="nav-arrow bi bi-chevron-right"></i></p>
|
| 72 |
+
</a>
|
| 73 |
+
<ul class="nav nav-treeview">
|
| 74 |
+
<li class="nav-item"><a class="nav-link" href="/mangroup"><i
|
| 75 |
+
class="far fa-circle nav-icon"></i> Manage Groups</a></li>
|
| 76 |
+
</ul>
|
| 77 |
+
</li>
|
| 78 |
{% endif %}
|
| 79 |
|
| 80 |
</ul>
|
backend/app/templates/todo/add_task.html
CHANGED
|
@@ -3,57 +3,57 @@
|
|
| 3 |
{% block title %}Add Task{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
-
<div class="container mt-4">
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
| 50 |
</div>
|
| 51 |
</div>
|
| 52 |
-
</div>
|
| 53 |
|
| 54 |
-
<script src="../static/js/tasks.js"></script>
|
| 55 |
-
<script>
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
</script>
|
| 59 |
{% endblock %}
|
|
|
|
| 3 |
{% block title %}Add Task{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
+
<div class="container mt-4">
|
| 7 |
+
<div class="card">
|
| 8 |
+
<div class="card-body">
|
| 9 |
+
<form id="addTaskForm" onsubmit="addTask(event)">
|
| 10 |
+
<!-- Task Description -->
|
| 11 |
+
<div class="form-group">
|
| 12 |
+
<label for="task_description">Task Description</label>
|
| 13 |
+
<input
|
| 14 |
+
type="text"
|
| 15 |
+
id="task_description"
|
| 16 |
+
class="form-control"
|
| 17 |
+
placeholder="Enter task description"
|
| 18 |
+
required
|
| 19 |
+
>
|
| 20 |
+
</div>
|
| 21 |
|
| 22 |
+
<!-- Assign To -->
|
| 23 |
+
<div class="form-group mt-3">
|
| 24 |
+
<label for="assigned_to">Assign To (Optional)</label>
|
| 25 |
+
<select
|
| 26 |
+
id="assigned_to"
|
| 27 |
+
class="form-control">
|
| 28 |
+
<option value="">No Assignment</option>
|
| 29 |
+
</select>
|
| 30 |
+
</div>
|
| 31 |
|
| 32 |
+
<!-- Due Date -->
|
| 33 |
+
<div class="form-group mt-3">
|
| 34 |
+
<label for="due_date">Due Date</label>
|
| 35 |
+
<input
|
| 36 |
+
type="datetime-local"
|
| 37 |
+
id="due_date"
|
| 38 |
+
class="form-control"
|
| 39 |
+
required
|
| 40 |
+
>
|
| 41 |
+
</div>
|
| 42 |
|
| 43 |
+
<!-- Submit Button -->
|
| 44 |
+
<button
|
| 45 |
+
type="submit"
|
| 46 |
+
class="btn btn-primary mt-3">
|
| 47 |
+
Add Task
|
| 48 |
+
</button>
|
| 49 |
+
</form>
|
| 50 |
+
</div>
|
| 51 |
</div>
|
| 52 |
</div>
|
|
|
|
| 53 |
|
| 54 |
+
<script src="../static/js/tasks.js"></script>
|
| 55 |
+
<script>
|
| 56 |
+
// Initialize farmers when the page loads
|
| 57 |
+
document.addEventListener('DOMContentLoaded', fetchFarmers);
|
| 58 |
+
</script>
|
| 59 |
{% endblock %}
|
backend/app/templates/todo/manage_tasks.html
CHANGED
|
@@ -3,13 +3,13 @@
|
|
| 3 |
{% block title %}Manage Tasks{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
-
<div class="container mt-4">
|
| 7 |
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
<tr>
|
| 14 |
<th>Description</th>
|
| 15 |
<th>Assigned To</th>
|
|
@@ -17,81 +17,83 @@
|
|
| 17 |
<th>Due Date</th>
|
| 18 |
<th>Actions</th>
|
| 19 |
</tr>
|
| 20 |
-
|
| 21 |
-
|
| 22 |
<!-- Tasks will be dynamically loaded here -->
|
| 23 |
-
|
| 24 |
-
|
|
|
|
| 25 |
</div>
|
| 26 |
</div>
|
| 27 |
-
</div>
|
| 28 |
|
| 29 |
-
<!-- Update Task Modal -->
|
| 30 |
-
<div class="modal fade" id="updateTaskModal" tabindex="-1" aria-labelledby="updateTaskModalLabel"
|
| 31 |
-
|
| 32 |
-
<div class="modal-
|
| 33 |
-
<div class="modal-
|
| 34 |
-
<
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
<
|
| 39 |
-
<
|
| 40 |
-
|
| 41 |
-
<
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
<
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
<
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
<
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
<
|
| 58 |
-
|
| 59 |
-
<
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
|
|
|
|
|
|
| 67 |
</div>
|
| 68 |
</div>
|
| 69 |
</div>
|
| 70 |
-
</div>
|
| 71 |
|
| 72 |
|
| 73 |
-
<!-- Delete Task Modal -->
|
| 74 |
-
<div class="modal fade" id="deleteTaskModal" tabindex="-1" aria-labelledby="deleteTaskModalLabel"
|
| 75 |
-
|
| 76 |
-
<div class="modal-
|
| 77 |
-
<div class="modal-
|
| 78 |
-
<
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
<
|
| 86 |
-
|
|
|
|
|
|
|
| 87 |
</div>
|
| 88 |
</div>
|
| 89 |
</div>
|
| 90 |
-
</div>
|
| 91 |
|
| 92 |
-
<script>
|
| 93 |
-
|
| 94 |
|
| 95 |
-
</script>
|
| 96 |
-
<script src="../static/js/tasks.js"></script>
|
| 97 |
{% endblock %}
|
|
|
|
| 3 |
{% block title %}Manage Tasks{% endblock %}
|
| 4 |
|
| 5 |
{% block content %}
|
| 6 |
+
<div class="container mt-4">
|
| 7 |
|
| 8 |
+
<!-- Task Table -->
|
| 9 |
+
<div class="card">
|
| 10 |
+
<div class="card-body">
|
| 11 |
+
<table class="table table-striped" id="tasksTable">
|
| 12 |
+
<thead>
|
| 13 |
<tr>
|
| 14 |
<th>Description</th>
|
| 15 |
<th>Assigned To</th>
|
|
|
|
| 17 |
<th>Due Date</th>
|
| 18 |
<th>Actions</th>
|
| 19 |
</tr>
|
| 20 |
+
</thead>
|
| 21 |
+
<tbody>
|
| 22 |
<!-- Tasks will be dynamically loaded here -->
|
| 23 |
+
</tbody>
|
| 24 |
+
</table>
|
| 25 |
+
</div>
|
| 26 |
</div>
|
| 27 |
</div>
|
|
|
|
| 28 |
|
| 29 |
+
<!-- Update Task Modal -->
|
| 30 |
+
<div class="modal fade" id="updateTaskModal" tabindex="-1" aria-labelledby="updateTaskModalLabel"
|
| 31 |
+
aria-hidden="true">
|
| 32 |
+
<div class="modal-dialog">
|
| 33 |
+
<div class="modal-content">
|
| 34 |
+
<div class="modal-header">
|
| 35 |
+
<h5 class="modal-title" id="updateTaskModalLabel">Update Task</h5>
|
| 36 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 37 |
+
</div>
|
| 38 |
+
<div class="modal-body">
|
| 39 |
+
<form id="updateTaskForm" onsubmit="updateTask(event)">
|
| 40 |
+
<input type="hidden" id="updateTaskId">
|
| 41 |
+
<div class="form-group">
|
| 42 |
+
<label for="updateTaskDescription">Task Description</label>
|
| 43 |
+
<input type="text" id="updateTaskDescription" class="form-control" required>
|
| 44 |
+
</div>
|
| 45 |
+
<div class="form-group mt-3">
|
| 46 |
+
<label for="updateAssignedTo">Assigned To</label>
|
| 47 |
+
<input type="text" id="updateAssignedTo" class="form-control" required>
|
| 48 |
+
</div>
|
| 49 |
+
<div class="form-group mt-3">
|
| 50 |
+
<label for="updateCreatedAt">Created At</label>
|
| 51 |
+
<input type="datetime-local" id="updateCreatedAt" class="form-control" required>
|
| 52 |
+
</div>
|
| 53 |
+
<div class="form-group mt-3">
|
| 54 |
+
<label for="updateDueDate">Due Date</label>
|
| 55 |
+
<input type="datetime-local" id="updateDueDate" class="form-control" required>
|
| 56 |
+
</div>
|
| 57 |
+
<div class="form-group mt-3">
|
| 58 |
+
<label for="updateStatus">Status</label>
|
| 59 |
+
<select id="updateStatus" class="form-control" required>
|
| 60 |
+
<option value="pending">Pending</option>
|
| 61 |
+
<option value="in-progress">In Progress</option>
|
| 62 |
+
<option value="completed">Completed</option>
|
| 63 |
+
<option value="overdue">Overdue</option>
|
| 64 |
+
</select>
|
| 65 |
+
</div>
|
| 66 |
+
<button type="submit" class="btn btn-primary mt-3">Update Task</button>
|
| 67 |
+
</form>
|
| 68 |
+
</div>
|
| 69 |
</div>
|
| 70 |
</div>
|
| 71 |
</div>
|
|
|
|
| 72 |
|
| 73 |
|
| 74 |
+
<!-- Delete Task Modal -->
|
| 75 |
+
<div class="modal fade" id="deleteTaskModal" tabindex="-1" aria-labelledby="deleteTaskModalLabel"
|
| 76 |
+
aria-hidden="true">
|
| 77 |
+
<div class="modal-dialog">
|
| 78 |
+
<div class="modal-content">
|
| 79 |
+
<div class="modal-header">
|
| 80 |
+
<h5 class="modal-title" id="deleteTaskModalLabel">Delete Task</h5>
|
| 81 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 82 |
+
</div>
|
| 83 |
+
<div class="modal-body">
|
| 84 |
+
Are you sure you want to delete this task?
|
| 85 |
+
</div>
|
| 86 |
+
<div class="modal-footer">
|
| 87 |
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
| 88 |
+
<button type="button" class="btn btn-danger" onclick="deleteTask()">Delete</button>
|
| 89 |
+
</div>
|
| 90 |
</div>
|
| 91 |
</div>
|
| 92 |
</div>
|
|
|
|
| 93 |
|
| 94 |
+
<script>
|
| 95 |
+
// Pass the user's role and ID dynamically to the JavaScript
|
| 96 |
|
| 97 |
+
</script>
|
| 98 |
+
<script src="../static/js/tasks.js"></script>
|
| 99 |
{% endblock %}
|
test_main.http
CHANGED
|
@@ -35,7 +35,10 @@ Content-Type: application/json
|
|
| 35 |
|
| 36 |
{
|
| 37 |
"group_name": "Test Farm Group",
|
| 38 |
-
"farmers": [
|
|
|
|
|
|
|
|
|
|
| 39 |
"created_by": "admin_id"
|
| 40 |
}
|
| 41 |
|
|
|
|
| 35 |
|
| 36 |
{
|
| 37 |
"group_name": "Test Farm Group",
|
| 38 |
+
"farmers": [
|
| 39 |
+
"farmer_id_1",
|
| 40 |
+
"farmer_id_2"
|
| 41 |
+
],
|
| 42 |
"created_by": "admin_id"
|
| 43 |
}
|
| 44 |
|