Compare commits

..

2 Commits

Author SHA1 Message Date
511e8ebde4 Notes printing function 2026-02-18 11:57:37 -05:00
1a82776657 Add notes API only 2026-02-18 06:15:48 -05:00
5 changed files with 174 additions and 3 deletions

View File

@@ -133,6 +133,17 @@ class LabelPrintResponse(BaseModel):
success: bool success: bool
message: str message: str
class NotesVariables(BaseModel):
animal_name: str
notes: str
class NotesPrintRequest(BaseModel):
variables: NotesVariables
class NotesPrintResponse(BaseModel):
success: bool
message: str
# Authentication Routes # Authentication Routes
@router.post("/auth/register", response_model=TokenResponse) @router.post("/auth/register", response_model=TokenResponse)
def register(user_data: UserCreate, db: Session = Depends(get_db)): def register(user_data: UserCreate, db: Session = Depends(get_db)):
@@ -575,5 +586,66 @@ def print_label(label_request: LabelPrintRequest, current_user: User = Depends(g
detail=f"Error sending label print request: {str(e)}" detail=f"Error sending label print request: {str(e)}"
) )
# Notes printing endpoint
@router.post("/notes/print", response_model=NotesPrintResponse)
def print_notes(notes_request: NotesPrintRequest, current_user: User = Depends(get_current_user)):
"""
Print notes by publishing an MQTT message
This endpoint publishes a notes print request to the MQTT broker,
which will be picked up by the label printing service.
"""
try:
# Get notes template configuration from environment
import os
template_id = os.getenv("NOTES_TEMPLATE_ID", "notes_template")
label_size = os.getenv("LABEL_SIZE", "29x90")
test_mode = os.getenv("LABEL_TEST", "false").lower() == "true"
# Capitalize text fields for better presentation
variables = notes_request.variables.dict()
variables["animal_name"] = capitalize_label_text(variables["animal_name"])
variables["notes"] = capitalize_label_text(variables["notes"])
# Convert the request to the MQTT message format
mqtt_message = {
"template_id": template_id,
"label_size": label_size,
"variables": variables,
"test": test_mode
}
# Publish to MQTT and wait for response
success, response = publish_label_print_with_response(mqtt_message, timeout=10.0)
print(f"Notes print result: success={success}, response={response}")
if success:
result = NotesPrintResponse(
success=True,
message=response.get("message", "Notes printed successfully")
)
print(f"Returning success response: {result}")
return result
else:
# Return error details from printer
# Check both 'message' and 'error' fields for error details
if response:
error_msg = response.get("message") or response.get("error", "Unknown error")
else:
error_msg = "No response from printer"
result = NotesPrintResponse(
success=False,
message=f"Print failed: {error_msg}"
)
print(f"Returning error response: {result}")
return result
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Error sending notes print request: {str(e)}"
)
# Include router with /api prefix # Include router with /api prefix
app.include_router(router) app.include_router(router)

View File

@@ -172,14 +172,17 @@ function setupEventListeners() {
const dispenseForm = document.getElementById('dispenseForm'); const dispenseForm = document.getElementById('dispenseForm');
const prescribeForm = document.getElementById('prescribeForm'); const prescribeForm = document.getElementById('prescribeForm');
const editForm = document.getElementById('editForm'); const editForm = document.getElementById('editForm');
const printNotesForm = document.getElementById('printNotesForm');
const addModal = document.getElementById('addModal'); const addModal = document.getElementById('addModal');
const addVariantModal = document.getElementById('addVariantModal'); const addVariantModal = document.getElementById('addVariantModal');
const editVariantModal = document.getElementById('editVariantModal'); const editVariantModal = document.getElementById('editVariantModal');
const dispenseModal = document.getElementById('dispenseModal'); const dispenseModal = document.getElementById('dispenseModal');
const prescribeModal = document.getElementById('prescribeModal'); const prescribeModal = document.getElementById('prescribeModal');
const editModal = document.getElementById('editModal'); const editModal = document.getElementById('editModal');
const printNotesModal = document.getElementById('printNotesModal');
const addDrugBtn = document.getElementById('addDrugBtn'); const addDrugBtn = document.getElementById('addDrugBtn');
const dispenseBtn = document.getElementById('dispenseBtn'); const dispenseBtn = document.getElementById('dispenseBtn');
const printNotesBtn = document.getElementById('printNotesBtn');
const cancelAddBtn = document.getElementById('cancelAddBtn'); const cancelAddBtn = document.getElementById('cancelAddBtn');
const cancelVariantBtn = document.getElementById('cancelVariantBtn'); const cancelVariantBtn = document.getElementById('cancelVariantBtn');
const cancelEditVariantBtn = document.getElementById('cancelEditVariantBtn'); const cancelEditVariantBtn = document.getElementById('cancelEditVariantBtn');
@@ -202,8 +205,10 @@ function setupEventListeners() {
if (dispenseForm) dispenseForm.addEventListener('submit', handleDispenseDrug); if (dispenseForm) dispenseForm.addEventListener('submit', handleDispenseDrug);
if (prescribeForm) prescribeForm.addEventListener('submit', handlePrescribeDrug); if (prescribeForm) prescribeForm.addEventListener('submit', handlePrescribeDrug);
if (editForm) editForm.addEventListener('submit', handleEditDrug); if (editForm) editForm.addEventListener('submit', handleEditDrug);
if (printNotesForm) printNotesForm.addEventListener('submit', handlePrintNotes);
if (addDrugBtn) addDrugBtn.addEventListener('click', () => openModal(addModal)); if (addDrugBtn) addDrugBtn.addEventListener('click', () => openModal(addModal));
if (printNotesBtn) printNotesBtn.addEventListener('click', () => openModal(printNotesModal));
if (dispenseBtn) dispenseBtn.addEventListener('click', () => { if (dispenseBtn) dispenseBtn.addEventListener('click', () => {
updateDispenseDrugSelect(); updateDispenseDrugSelect();
openModal(dispenseModal); openModal(dispenseModal);
@@ -216,6 +221,9 @@ function setupEventListeners() {
if (cancelPrescribeBtn) cancelPrescribeBtn.addEventListener('click', () => closeModal(prescribeModal)); if (cancelPrescribeBtn) cancelPrescribeBtn.addEventListener('click', () => closeModal(prescribeModal));
if (cancelEditBtn) cancelEditBtn.addEventListener('click', closeEditModal); if (cancelEditBtn) cancelEditBtn.addEventListener('click', closeEditModal);
const cancelPrintNotesBtn = document.getElementById('cancelPrintNotesBtn');
if (cancelPrintNotesBtn) cancelPrintNotesBtn.addEventListener('click', () => closeModal(printNotesModal));
const closeHistoryBtn = document.getElementById('closeHistoryBtn'); const closeHistoryBtn = document.getElementById('closeHistoryBtn');
if (closeHistoryBtn) closeHistoryBtn.addEventListener('click', () => closeModal(document.getElementById('historyModal'))); if (closeHistoryBtn) closeHistoryBtn.addEventListener('click', () => closeModal(document.getElementById('historyModal')));
@@ -770,6 +778,63 @@ async function handlePrescribeDrug(e) {
} }
} }
// Handle print notes form submission
async function handlePrintNotes(e) {
e.preventDefault();
const animalName = document.getElementById('notesAnimalName').value.trim();
const notes = document.getElementById('notesContent').value.trim();
if (!animalName || !notes) {
showToast('Please fill in all required fields', 'warning');
return;
}
try {
// Send notes to print endpoint
const notesData = {
variables: {
animal_name: animalName,
notes: notes
}
};
const response = await apiCall('/notes/print', {
method: 'POST',
body: JSON.stringify(notesData)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Notes printing request failed');
}
const result = await response.json();
console.log('Notes print result:', result);
if (!result.success) {
// Printing failed
const isError = result.message && (
result.message.includes('not found') ||
result.message.includes('error') ||
result.message.includes('failed')
);
const toastType = isError ? 'error' : 'warning';
showToast(result.message, toastType, 5000);
return;
}
// Printing succeeded
showToast('Notes printed successfully!', 'success');
document.getElementById('printNotesForm').reset();
closeModal(document.getElementById('printNotesModal'));
} catch (error) {
console.error('Error printing notes:', error);
showToast('Failed to print notes: ' + error.message, 'error');
}
}
// Delete variant // Delete variant
async function deleteVariant(variantId) { async function deleteVariant(variantId) {
if (!confirm('Are you sure you want to delete this variant?')) return; if (!confirm('Are you sure you want to delete this variant?')) return;

View File

@@ -55,6 +55,7 @@
<div class="section-header"> <div class="section-header">
<h2>Current Inventory</h2> <h2>Current Inventory</h2>
<div class="header-actions"> <div class="header-actions">
<button id="printNotesBtn" class="btn btn-primary btn-small">📝 Print Notes</button>
<button id="addDrugBtn" class="btn btn-primary btn-small"> Add Drug</button> <button id="addDrugBtn" class="btn btn-primary btn-small"> Add Drug</button>
</div> </div>
<div class="filters"> <div class="filters">
@@ -429,6 +430,30 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Print Notes Modal -->
<div id="printNotesModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2>Print Notes</h2>
<form id="printNotesForm" novalidate>
<div class="form-group">
<label for="notesAnimalName">Animal Name/ID *</label>
<input type="text" id="notesAnimalName" required>
</div>
<div class="form-group">
<label for="notesContent">Notes *</label>
<textarea id="notesContent" rows="6" placeholder="Enter notes to print..." required></textarea>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Print Notes</button>
<button type="button" class="btn btn-secondary" id="cancelPrintNotesBtn">Cancel</button>
</div>
</form>
</div>
</div>
</div> </div>
<script src="app.js"></script> <script src="app.js"></script>

View File

@@ -252,7 +252,9 @@ label {
input[type="text"], input[type="text"],
input[type="number"], input[type="number"],
input[type="password"], input[type="password"],
select { input[type="date"],
select,
textarea {
width: 100%; width: 100%;
padding: 12px; padding: 12px;
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
@@ -262,10 +264,17 @@ select {
transition: border-color 0.2s; transition: border-color 0.2s;
} }
textarea {
resize: vertical;
min-height: 100px;
}
input[type="text"]:focus, input[type="text"]:focus,
input[type="number"]:focus, input[type="number"]:focus,
input[type="password"]:focus, input[type="password"]:focus,
select:focus { input[type="date"]:focus,
select:focus,
textarea:focus {
outline: none; outline: none;
border-color: var(--secondary-color); border-color: var(--secondary-color);
box-shadow: 0 0 5px rgba(52, 152, 219, 0.3); box-shadow: 0 0 5px rgba(52, 152, 219, 0.3);

View File

@@ -12,7 +12,7 @@ password_file /mosquitto/config/pwfile
# Logging # Logging
log_dest stdout log_dest stdout
log_timestamp true log_timestamp true
log_type all #log_type all
# Performance # Performance
max_connections -1 max_connections -1