lets add settings new to css

This commit is contained in:
2025-10-09 00:34:52 +03:00
parent a8811b94b7
commit b0e17b69e7
18 changed files with 1816 additions and 118 deletions
+152
View File
@@ -0,0 +1,152 @@
# CSS Modular Structure Guide
## Overview
This guide explains how to migrate from a monolithic CSS file to a modular CSS structure for better maintainability and organization.
## New CSS Structure
```
app/static/css/
├── base.css # Global styles, header, buttons, theme
├── login.css # Login page specific styles
├── dashboard.css # Dashboard and module cards
├── warehouse.css # Warehouse module styles
├── etichete.css # Labels/etiquette module styles (to be created)
├── quality.css # Quality module styles (to be created)
└── scan.css # Scan module styles (to be created)
```
## Implementation Strategy
### Phase 1: Setup Modular Structure ✅
- [x] Created `css/` directory
- [x] Created `base.css` with global styles
- [x] Created `login.css` for login page
- [x] Created `warehouse.css` for warehouse module
- [x] Updated `base.html` to include modular CSS
- [x] Updated `login.html` to use new structure
### Phase 2: Migration Plan (Next Steps)
1. **Extract module-specific styles from style.css:**
- Etiquette/Labels module → `etichete.css`
- Quality module → `quality.css`
- Scan module → `scan.css`
2. **Update templates to use modular CSS:**
```html
{% block head %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/module-name.css') }}">
{% endblock %}
```
3. **Clean up original style.css:**
- Remove extracted styles
- Keep only legacy/common styles temporarily
- Eventually eliminate when all modules migrated
## Template Usage Pattern
### Standard Template Structure:
```html
{% extends "base.html" %}
{% block title %}Page Title{% endblock %}
{% block head %}
<!-- Include module-specific CSS -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/module-name.css') }}">
<!-- Page-specific overrides -->
<style>
/* Only use this for page-specific customizations */
</style>
{% endblock %}
{% block content %}
<!-- Page content -->
{% endblock %}
```
## CSS Loading Order
1. `base.css` - Global styles, header, buttons, theme
2. `style.css` - Legacy styles (temporary, for backward compatibility)
3. Module-specific CSS (e.g., `warehouse.css`)
4. Inline `<style>` blocks for page-specific overrides
## Benefits of This Structure
### 1. **Maintainability**
- Easy to find and edit module-specific styles
- Reduced conflicts between different modules
- Clear separation of concerns
### 2. **Performance**
- Only load CSS needed for specific pages
- Smaller file sizes per page
- Better caching (module CSS rarely changes)
### 3. **Team Development**
- Different developers can work on different modules
- Less merge conflicts in CSS files
- Clear ownership of styles
### 4. **Scalability**
- Easy to add new modules
- Simple to deprecate old styles
- Clear migration path
## Migration Checklist
### For Each Template:
- [ ] Identify module/page type
- [ ] Extract relevant styles to module CSS file
- [ ] Update template to include module CSS
- [ ] Test styling works correctly
- [ ] Remove old styles from style.css
### Current Status:
- [x] Login page - Fully migrated
- [x] Warehouse module - Partially migrated (create_locations.html updated)
- [ ] Dashboard - CSS created, templates need updating
- [ ] Etiquette module - Needs CSS extraction
- [ ] Quality module - Needs CSS extraction
- [ ] Scan module - Needs CSS extraction
## Example: Migrating a Template
### Before:
```html
{% block head %}
<style>
.my-module-specific-class {
/* styles here */
}
</style>
{% endblock %}
```
### After:
1. Move styles to `css/module-name.css`
2. Update template:
```html
{% block head %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/module-name.css') }}">
{% endblock %}
```
## Best Practices
1. **Use semantic naming:** `warehouse.css`, `login.css`, not `page1.css`
2. **Keep base.css minimal:** Only truly global styles
3. **Avoid deep nesting:** Keep CSS selectors simple
4. **Use consistent naming:** Follow existing patterns
5. **Document changes:** Update this guide when adding new modules
## Next Steps
1. Extract etiquette module styles to `etichete.css`
2. Update all etiquette templates to use new CSS
3. Extract quality module styles to `quality.css`
4. Extract scan module styles to `scan.css`
5. Gradually remove migrated styles from `style.css`
6. Eventually remove `style.css` dependency from `base.html`
+133
View File
@@ -0,0 +1,133 @@
# Mobile-Responsive Login Page
## Overview
The login page has been enhanced with comprehensive mobile-responsive CSS to provide an optimal user experience across all device types and screen sizes.
## Mobile-Responsive Features Added
### 1. **Responsive Breakpoints**
- **Tablet (≤768px)**: Column layout, optimized logo and form sizing
- **Mobile (≤480px)**: Enhanced touch targets, better spacing
- **Small Mobile (≤320px)**: Minimal padding, compact design
- **Landscape (height ≤500px)**: Horizontal layout for landscape phones
### 2. **Layout Adaptations**
#### Desktop (>768px)
- Side-by-side logo and form layout
- Large logo (90vh height)
- Fixed form width (600px)
#### Tablet (≤768px)
- Vertical stacked layout
- Logo height reduced to 30vh
- Form width becomes responsive (100%, max 400px)
#### Mobile (≤480px)
- Optimized touch targets (44px minimum)
- Increased padding and margins
- Better visual hierarchy
- Enhanced shadows and border radius
#### Small Mobile (≤320px)
- Minimal padding to maximize space
- Compact logo (20vh height)
- Reduced font sizes where appropriate
### 3. **Touch Optimizations**
#### iOS/Safari Specific
- `font-size: 16px` on inputs prevents automatic zoom
- Proper touch target sizing (44px minimum)
#### Touch Device Enhancements
- Active states for button presses
- Optimized image rendering for high DPI screens
- Hover effects disabled on touch devices
### 4. **Accessibility Improvements**
- Proper contrast ratios maintained
- Touch targets meet accessibility guidelines
- Readable font sizes across all devices
- Smooth transitions and animations
### 5. **Performance Considerations**
- CSS-only responsive design (no JavaScript required)
- Efficient media queries
- Optimized image rendering for retina displays
## Key CSS Features
### Flexible Layout
```css
.login-page {
display: flex;
flex-direction: column; /* Mobile */
justify-content: center;
}
```
### Responsive Images
```css
.login-logo {
max-height: 25vh; /* Mobile */
max-width: 85vw;
}
```
### Touch-Friendly Inputs
```css
.form-container input {
padding: 12px;
font-size: 16px; /* Prevents iOS zoom */
min-height: 44px; /* Touch target size */
}
```
### Landscape Optimization
```css
@media screen and (max-height: 500px) and (orientation: landscape) {
.login-page {
flex-direction: row; /* Back to horizontal */
}
}
```
## Testing Recommendations
### Device Testing
- [ ] iPhone (various sizes)
- [ ] Android phones (various sizes)
- [ ] iPad/Android tablets
- [ ] Desktop browsers with responsive mode
### Orientation Testing
- [ ] Portrait mode on all devices
- [ ] Landscape mode on phones
- [ ] Landscape mode on tablets
### Browser Testing
- [ ] Safari (iOS)
- [ ] Chrome (Android/iOS)
- [ ] Firefox Mobile
- [ ] Samsung Internet
- [ ] Desktop browsers (Chrome, Firefox, Safari, Edge)
## Browser Support
- Modern browsers (ES6+ support)
- iOS Safari 12+
- Android Chrome 70+
- Desktop browsers (last 2 versions)
## Performance Impact
- **CSS Size**: Increased by ~2KB (compressed)
- **Load Time**: No impact (CSS only)
- **Rendering**: Optimized for mobile GPUs
- **Memory**: Minimal additional usage
## Future Enhancements
1. **Dark mode mobile optimizations**
2. **Progressive Web App (PWA) features**
3. **Biometric authentication UI**
4. **Loading states and animations**
5. **Error message responsive design**
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+35
View File
@@ -2382,6 +2382,41 @@ def generate_location_label_pdf():
from app.warehouse import generate_location_label_pdf from app.warehouse import generate_location_label_pdf
return generate_location_label_pdf() return generate_location_label_pdf()
@warehouse_bp.route('/update_location', methods=['POST'])
def update_location():
from app.warehouse import update_location
try:
data = request.get_json()
location_id = data.get('location_id')
location_code = data.get('location_code')
size = data.get('size')
description = data.get('description')
if not location_id or not location_code:
return jsonify({'success': False, 'error': 'Location ID and code are required'})
result = update_location(location_id, location_code, size, description)
return jsonify(result)
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
@warehouse_bp.route('/delete_location', methods=['POST'])
def delete_location():
from app.warehouse import delete_location_by_id
try:
data = request.get_json()
location_id = data.get('location_id')
if not location_id:
return jsonify({'success': False, 'error': 'Location ID is required'})
result = delete_location_by_id(location_id)
return jsonify(result)
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
# NOTE for frontend/extension developers: # NOTE for frontend/extension developers:
# To print labels, call the Chrome extension and pass the PDF URL: # To print labels, call the Chrome extension and pass the PDF URL:
+149
View File
@@ -0,0 +1,149 @@
/* Global Base Styles - Common across all pages */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f9;
}
.main-content {
width: 100vw;
max-width: 100vw;
margin: 0;
padding: 0;
background: none;
border-radius: 0;
box-shadow: none;
}
/* Header Styles */
header {
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
}
.header-content {
display: flex;
justify-content: space-between;
width: 100%;
align-items: center;
}
.left-header {
display: flex;
align-items: center;
}
.left-header .logo {
width: 60px;
height: auto;
margin-right: 10px;
}
.left-header .page-title {
font-size: 1.5em;
font-weight: bold;
}
.right-header {
display: flex;
align-items: center;
}
.right-header .user-info {
margin-right: 15px;
font-size: 1em;
color: #333;
}
.right-header .logout-button {
padding: 5px 10px;
font-size: 1em;
background-color: #007bff;
color: #fff;
text-decoration: none;
border-radius: 5px;
border: none;
cursor: pointer;
}
.right-header .logout-button:hover {
background-color: #0056b3;
color: #fff;
}
/* Common Button Styles */
.btn {
padding: 8px 16px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
font-size: 14px;
}
.btn:hover {
background-color: #0056b3;
}
.btn-success {
background-color: #28a745;
}
.btn-success:hover {
background-color: #218838;
}
.btn-danger {
background-color: #dc3545;
}
.btn-danger:hover {
background-color: #c82333;
}
.btn-info {
background-color: #17a2b8;
}
.btn-info:hover {
background-color: #138496;
}
/* Theme Toggle */
.theme-toggle {
background-color: #6c757d;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
font-size: 12px;
}
.theme-toggle:hover {
background-color: #5a6268;
}
/* Dark Mode Base Styles */
body.dark-mode {
background-color: #121212;
color: #e0e0e0;
}
body.dark-mode header {
background-color: #1e1e1e;
color: #fff;
}
body.dark-mode .user-info {
color: #ccc;
}
+67
View File
@@ -0,0 +1,67 @@
/* Dashboard and General Page Styles */
.dashboard-container {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.module-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
.module-card {
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: transform 0.2s, box-shadow 0.2s;
}
.module-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.module-card h3 {
margin: 0 0 15px 0;
color: #333;
font-size: 1.2em;
}
.module-card p {
color: #666;
margin-bottom: 15px;
line-height: 1.5;
}
.module-card a {
display: inline-block;
padding: 8px 16px;
background-color: #007bff;
color: #fff;
text-decoration: none;
border-radius: 4px;
transition: background-color 0.2s;
}
.module-card a:hover {
background-color: #0056b3;
}
/* Dark mode for dashboard */
body.dark-mode .module-card {
background: #2d2d2d;
color: #e0e0e0;
}
body.dark-mode .module-card h3 {
color: #fff;
}
body.dark-mode .module-card p {
color: #ccc;
}
+216
View File
@@ -0,0 +1,216 @@
/* Login Page Specific Styles */
.login-page {
display: flex;
align-items: center;
justify-content: space-between;
height: 100vh;
background-color: #f4f4f9;
padding: 0 20px;
}
.logo-container {
width: 100%;
text-align: center;
margin-bottom: 10px;
}
.login-logo {
max-height: 90vh;
width: auto;
}
.form-container {
width: 600px;
background: #fff;
padding: 15px 30px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
margin: 0;
align-self: center;
}
.form-container h2 {
text-align: center;
margin-bottom: 15px;
}
.form-container form {
display: flex;
flex-direction: column;
}
.form-container label {
margin-bottom: 5px;
font-weight: bold;
}
.form-container input {
margin-bottom: 10px;
padding: 8px;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 16px; /* Prevents zoom on iOS */
}
.form-container button {
padding: 10px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 16px;
}
.form-container button:hover {
background-color: #0056b3;
}
/* Hide the header only on the login page */
body.light-mode.login-page header,
body.dark-mode.login-page header {
display: none;
}
/* Mobile Responsive Design */
/* Tablet styles (768px and down) */
@media screen and (max-width: 768px) {
.login-page {
flex-direction: column;
justify-content: center;
padding: 20px 15px;
gap: 20px;
}
.logo-container {
margin-bottom: 0;
}
.login-logo {
max-height: 30vh;
max-width: 80vw;
}
.form-container {
width: 100%;
max-width: 400px;
padding: 20px 25px;
}
}
/* Mobile styles (480px and down) */
@media screen and (max-width: 480px) {
.login-page {
padding: 15px 10px;
gap: 15px;
}
.login-logo {
max-height: 25vh;
max-width: 85vw;
}
.form-container {
padding: 20px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.form-container h2 {
font-size: 1.5em;
margin-bottom: 20px;
}
.form-container input {
padding: 12px;
font-size: 16px;
border-radius: 5px;
margin-bottom: 15px;
}
.form-container button {
padding: 12px;
font-size: 16px;
border-radius: 5px;
font-weight: 600;
}
.form-container label {
font-size: 14px;
margin-bottom: 8px;
}
}
/* Small mobile styles (320px and down) */
@media screen and (max-width: 320px) {
.login-page {
padding: 10px 8px;
}
.login-logo {
max-height: 20vh;
max-width: 90vw;
}
.form-container {
padding: 15px;
width: calc(100% - 16px);
}
.form-container h2 {
font-size: 1.3em;
}
}
/* Landscape orientation adjustments */
@media screen and (max-height: 500px) and (orientation: landscape) {
.login-page {
flex-direction: row;
justify-content: space-around;
align-items: center;
gap: 20px;
}
.logo-container {
flex: 1;
margin-bottom: 0;
}
.login-logo {
max-height: 80vh;
max-width: 40vw;
}
.form-container {
flex: 1;
max-width: 350px;
align-self: center;
}
}
/* Touch device optimizations */
@media (hover: none) and (pointer: coarse) {
.form-container input {
min-height: 44px; /* Apple's recommended touch target size */
}
.form-container button {
min-height: 44px;
font-weight: 600;
}
.form-container button:active {
background-color: #004494;
transform: translateY(1px);
}
}
/* High DPI displays */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.login-logo {
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
}
}
+759
View File
@@ -0,0 +1,759 @@
/* Ensure .main-content is full width for warehouse pages */
/* Fix horizontal overflow: use 100% width, not 100vw */
.main-content {
width: 100% !important;
margin: 0 !important;
padding: 0 !important;
background: transparent !important;
border-radius: 0 !important;
box-shadow: none !important;
}
/* Normalize box-sizing for all elements */
*, *::before, *::after {
box-sizing: border-box;
}
/* WAREHOUSE PAGE CSS - FRESH START */
/* Reset and override all base styles for warehouse pages */
.main-content {
width: 100% !important;
height: calc(100vh - 60px) !important; /* Full height minus header */
margin: 0 !important;
padding: 10px !important; /* 10px margin from page edges */
background: #f4f4f9 !important; /* Match body background */
border: none !important;
box-shadow: none !important;
box-sizing: border-box !important;
display: flex !important;
flex-direction: column !important;
}
/* Main warehouse container - full width with 10px padding */
.warehouse-page-container {
width: 100%;
height: 100%;
display: flex;
gap: 10px; /* 10px between containers */
box-sizing: border-box;
}
/* The 3 main containers with 1:2:1 ratio */
.warehouse-container-1 {
flex: 1; /* 1 part */
background: #f8fafc; /* Theme card background */
color: #1e293b; /* Theme text color */
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
box-sizing: border-box;
overflow-y: auto;
align-self: flex-start;
max-height: 640px;
min-height: 320px;
}
.warehouse-container-2 {
flex: 2; /* 2 parts */
background: #f8fafc; /* Theme card background */
color: #1e293b; /* Theme text color */
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
box-sizing: border-box;
overflow-y: auto;
max-height: 640px;
}
.warehouse-container-3 {
flex: 1; /* 1 part */
background: #f8fafc; /* Theme card background */
color: #1e293b; /* Theme text color */
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
box-sizing: border-box;
overflow-y: auto;
max-height: 640px;
}
/* Form and table styles for warehouse components */
form {
display: flex;
flex-direction: column;
gap: 4px; /* Reduced gap for compactness */
}
form label {
font-weight: bold;
margin-bottom: 2px; /* Reduced margin for compactness */
}
form input, form select {
padding: 8px 12px;
border: 1px solid #cbd5e1;
border-radius: 4px;
font-size: 14px;
background: #e2e8f0; /* Theme input background */
color: #1e293b; /* Theme input text */
}
.btn {
padding: 8px 16px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
font-size: 14px;
}
.btn:hover {
background-color: #0056b3;
}
.btn-success {
background-color: #28a745;
}
.btn-info {
background-color: #17a2b8;
}
.btn-danger {
background-color: #dc3545;
}
/* Table styles */
.scan-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
font-size: 12px;
}
.scan-table th, .scan-table td {
border: 1px solid #cbd5e1; /* Theme border color */
padding: 8px;
text-align: left;
}
.scan-table th {
background-color: #e2e8f0; /* Theme input background for headers */
font-weight: bold;
color: #334155; /* Theme label color */
}
.location-row {
cursor: pointer;
transition: background-color 0.2s;
}
.location-row:hover {
background-color: #e2e8f0; /* Theme hover background */
}
.location-row.selected {
background-color: #ffb300 !important;
color: #222 !important;
font-weight: bold;
}
/* Edit/Delete card styles */
.edit-delete-card {
margin-bottom: 20px;
}
.selection-message {
color: #64748b; /* Slightly lighter theme text */
font-style: italic;
margin-bottom: 10px;
}
.selection-info {
margin-bottom: 10px;
}
.button-group {
display: flex;
gap: 10px;
margin-top: 10px;
}
/* Barcode preview */
#barcode-label-preview {
width: 8cm;
height: 4cm;
border: 2px solid #333;
background: white;
display: flex;
align-items: center;
justify-content: center;
margin: 10px auto;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
#barcode-label-preview .location-text {
font-size: 12px;
font-weight: bold;
text-align: center;
margin-bottom: 10px;
}
/* Modal styles */
.modal {
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
width: 90%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #ddd;
background-color: #f8f9fa;
}
.modal-body {
padding: 20px;
}
.close {
font-size: 24px;
font-weight: bold;
cursor: pointer;
color: #666;
}
.close:hover {
color: #333;
}
.confirm-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.confirm-table td {
padding: 8px 12px;
border-bottom: 1px solid #eee;
}
.confirm-table td:first-child {
width: 30%;
background-color: #f8f9fa;
font-weight: bold;
}
.modal-buttons {
display: flex;
gap: 10px;
justify-content: flex-end;
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #eee;
}
html, body {
width: 100% !important;
margin: 0 !important;
padding: 0 !important;
}
.scan-container {
width: 100vw !important;
margin: 0 !important;
padding: 0 !important;
display: block !important; /* Override any grid from style.css */
grid-template-columns: unset !important; /* Remove any grid columns */
gap: unset !important; /* Remove any gap */
max-width: 100vw !important; /* Override max-width from style.css */
box-shadow: none !important; /* Remove box shadow */
border-radius: 0 !important; /* Remove border radius */
}
/* Full-width container for warehouse pages - avoids scan-container restrictions */
.warehouse-full-container {
width: 100% !important;
margin: 0 !important;
padding: 0 !important;
display: block !important;
box-sizing: border-box;
background: transparent !important;
}
/* Container card with left/right margins */
.warehouse-container-card {
margin-left: 15px;
margin-right: 15px;
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
width: calc(100% - 30px);
box-sizing: border-box;
}
/* 3-column layout for warehouse create locations */
.warehouse-grid {
display: grid !important;
grid-template-columns: 1fr 2fr 1fr !important;
gap: 20px;
margin: 0;
min-height: auto;
width: 100%;
box-sizing: border-box;
}
/* Make cards equal height in grid with higher specificity */
.warehouse-grid .card {
height: fit-content;
align-self: start;
width: 100% !important;
max-width: none !important;
margin-left: 8px;
margin-right: 8px;
}
/* Responsive grid - collapse to single column on smaller screens */
@media (max-width: 1024px) {
.warehouse-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.warehouse-grid {
gap: 15px;
}
.scan-container {
padding: 15px;
}
}
.card {
background: #fff;
border-radius: 8px;
padding: 20px;
margin: 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.scan-form-card {
max-width: 600px;
margin: 0 auto 20px auto;
}
.scan-table-card {
overflow-x: auto;
}
.scan-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
font-size: 11px;
}
.scan-table th, .scan-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: center;
}
.scan-table th {
background-color: #f4f4f4;
font-weight: bold;
}
/* Location row selection styles */
.location-row {
cursor: pointer;
transition: background-color 0.2s;
}
.location-row:hover {
background-color: #f8f9fa;
}
tr.location-row.selected, .location-row.selected td {
background-color: #ffb300 !important;
color: #222 !important;
font-weight: bold;
box-shadow: 0 2px 8px rgba(255,179,0,0.12);
border-left: 4px solid #ff9800;
transition: background 0.2s, color 0.2s;
}
/* Print section styles */
.print-section {
margin-top: 24px;
padding: 16px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
}
/* Edit/Delete card styles - compact version */
.edit-delete-card {
margin-top: 16px;
padding: 12px 16px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f8f9fa;
}
.edit-delete-card h3 {
font-size: 1.05em;
margin: 0 0 8px 0;
color: #333;
}
.edit-delete-card .selection-message {
color: #666;
font-style: italic;
margin-bottom: 8px;
font-size: 0.9em;
}
.edit-delete-card .selection-info {
margin-bottom: 8px;
font-size: 0.9em;
}
.edit-delete-card .button-group {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 8px;
}
.edit-delete-card .btn {
font-size: 11px;
padding: 5px 12px;
}
/* Barcode label preview */
#barcode-label-preview {
width: 8cm;
height: 4cm;
border: 2px solid #333;
background: white;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 10px auto;
position: relative;
box-shadow: 0 2px 8px rgba(0,0,0,0.07);
}
#barcode-label-preview .location-text {
font-size: 12px;
font-weight: bold;
text-align: center;
margin-bottom: 10px;
}
#barcode-label-preview #location-barcode {
display: block;
margin: 0 auto;
max-width: 90%;
max-height: 70%;
}
#barcode-label-preview #barcode-placeholder {
text-align: center;
width: 100%;
}
/* Dark mode for warehouse */
body.dark-mode .card {
background: #2d2d2d;
color: #e0e0e0;
}
body.dark-mode .scan-table {
background-color: #1e1e1e;
color: #fff;
}
body.dark-mode .scan-table th {
background-color: #333;
}
body.dark-mode .scan-table td {
color: #ddd;
}
body.dark-mode .scan-table tr:nth-child(even) {
background-color: #2a2a2a;
}
body.dark-mode .print-section {
background-color: #333;
border-color: #555;
}
body.dark-mode .edit-delete-card {
background-color: #333;
border-color: #555;
}
body.dark-mode .edit-delete-card h3 {
color: #fff;
}
body.dark-mode .edit-delete-card .selection-message {
color: #ccc;
}
/* Modal Styles */
.modal {
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
width: 90%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #ddd;
background-color: #f8f9fa;
border-radius: 8px 8px 0 0;
}
.modal-header h3 {
margin: 0;
color: #333;
}
.close {
font-size: 24px;
font-weight: bold;
cursor: pointer;
color: #666;
line-height: 1;
}
.close:hover {
color: #333;
}
.modal-body {
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.form-group input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.modal-buttons {
display: flex;
gap: 10px;
justify-content: flex-end;
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #eee;
}
.confirm-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.confirm-table td {
padding: 8px 12px;
border-bottom: 1px solid #eee;
}
.confirm-table td:first-child {
width: 30%;
background-color: #f8f9fa;
font-weight: bold;
}
.btn-secondary {
background-color: #6c757d;
}
.btn-secondary:hover {
background-color: #5a6268;
}
/* Dark mode for modals */
body.dark-mode .modal-content {
background-color: #2d2d2d;
color: #e0e0e0;
}
body.dark-mode .modal-header {
background-color: #333;
border-bottom-color: #555;
}
body.dark-mode .modal-header h3 {
color: #fff;
}
body.dark-mode .close {
color: #ccc;
}
body.dark-mode .close:hover {
color: #fff;
}
body.dark-mode .form-group input {
background-color: #333;
border-color: #555;
color: #e0e0e0;
}
body.dark-mode .confirm-table td:first-child {
background-color: #333;
}
body.dark-mode .modal-buttons {
border-top-color: #555;
}
/* ENHANCED DARK MODE SUPPORT FOR NEW WAREHOUSE LAYOUT */
body.dark-mode .main-content {
background: #121212 !important; /* Match body dark background */
}
body.dark-mode .warehouse-container-1,
body.dark-mode .warehouse-container-2,
body.dark-mode .warehouse-container-3 {
background: #1e1e1e !important; /* Match header dark background */
color: #e0e0e0 !important; /* Match body dark text */
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
}
body.dark-mode form label {
color: #e0e0e0 !important; /* Match body dark text */
}
body.dark-mode form input,
body.dark-mode form select {
background: #2d2d2d !important; /* Slightly lighter than containers */
color: #e0e0e0 !important; /* Match body dark text */
border: 1px solid #444 !important;
}
body.dark-mode .btn {
background-color: #4299e1 !important;
}
body.dark-mode .btn:hover {
background-color: #3182ce !important;
}
body.dark-mode .btn-success {
background-color: #48bb78 !important;
}
body.dark-mode .btn-info {
background-color: #38b2ac !important;
}
body.dark-mode .btn-danger {
background-color: #f56565 !important;
}
body.dark-mode .scan-table {
background-color: #1e1e1e !important; /* Match header background */
color: #e0e0e0 !important; /* Match body dark text */
}
body.dark-mode .scan-table th {
background-color: #2d2d2d !important; /* Slightly lighter */
color: #e0e0e0 !important; /* Match body dark text */
border: 1px solid #444 !important;
}
body.dark-mode .scan-table td {
color: #e0e0e0 !important; /* Match body dark text */
border: 1px solid #444 !important;
}
body.dark-mode .location-row:hover {
background-color: #2d2d2d !important; /* Slightly lighter than table */
}
body.dark-mode .location-row.selected {
background-color: #ed8936 !important;
color: #1a202c !important;
}
body.dark-mode .selection-message {
color: #999 !important; /* Lighter gray for secondary text */
}
body.dark-mode .selection-info {
color: #e0e0e0 !important; /* Match body dark text */
}
body.dark-mode #barcode-label-preview {
background: #f8fafc !important; /* Keep white for readability */
border: 2px solid #444 !important;
}
+6 -1
View File
@@ -4,7 +4,12 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Flask App{% endblock %}</title> <title>{% block title %}Flask App{% endblock %}</title>
<!-- Base CSS for common styles -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/base.css') }}">
<!-- Legacy CSS for backward compatibility (temporarily) -->
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<!-- Page-specific CSS -->
{% block head %}{% endblock %}
</head> </head>
<body class="light-mode"> <body class="light-mode">
{% if request.endpoint != 'main.login' %} {% if request.endpoint != 'main.login' %}
@@ -38,7 +43,7 @@
</div> </div>
</header> </header>
{% endif %} {% endif %}
<div class="container"> <div class="main-content">
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>
<script src="{{ url_for('static', filename='script.js') }}"></script> <script src="{{ url_for('static', filename='script.js') }}"></script>
+236 -114
View File
@@ -2,72 +2,13 @@
{% block title %}Create Warehouse Locations{% endblock %} {% block title %}Create Warehouse Locations{% endblock %}
{% block head %} {% block head %}
<style> <link rel="stylesheet" href="{{ url_for('static', filename='css/warehouse.css') }}">
/* Barcode label preview */
#barcode-label-preview {
width: 8cm;
height: 4cm;
border: 2px solid #333;
background: white;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 10px auto;
position: relative;
box-shadow: 0 2px 8px rgba(0,0,0,0.07);
}
#barcode-label-preview .location-text {
font-size: 12px;
font-weight: bold;
text-align: center;
margin-bottom: 10px;
}
#barcode-label-preview #location-barcode {
display: block;
margin: 0 auto;
max-width: 90%;
max-height: 70%;
}
#barcode-label-preview #barcode-placeholder {
text-align: center;
width: 100%;
}
.print-section {
margin-top: 24px;
padding: 16px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
}
.location-row.selected {
background-color: #ffb300 !important; /* Accent color: Amber */
color: #222 !important;
font-weight: bold;
box-shadow: 0 2px 8px rgba(255,179,0,0.12);
border-left: 4px solid #ff9800;
transition: background 0.2s, color 0.2s;
}
.location-row {
cursor: pointer;
}
.location-row:hover {
background-color: #f8f9fa;
}
</style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="scan-container"> <div class="warehouse-page-container">
<!-- Add Warehouse Location Card --> <!-- Container 1: Add Location (1 part width) -->
<div class="card scan-form-card"> <div class="warehouse-container-1">
<h3>Add Warehouse Location</h3> <h3>Add Warehouse Location</h3>
{% if message %} {% if message %}
<div class="form-message">{{ message }}</div> <div class="form-message">{{ message }}</div>
@@ -81,7 +22,7 @@
<input type="text" name="description" maxlength="250"><br> <input type="text" name="description" maxlength="250"><br>
<button type="submit" class="btn">Add Location</button> <button type="submit" class="btn">Add Location</button>
</form> </form>
<!-- Import from CSV content moved here --> <!-- Import from CSV section -->
<div style="margin-top: 24px;"> <div style="margin-top: 24px;">
<h3 style="font-size: 1.1em;">Import Locations from CSV</h3> <h3 style="font-size: 1.1em;">Import Locations from CSV</h3>
<div style="display: flex; flex-direction: row; gap: 16px; align-items: center;"> <div style="display: flex; flex-direction: row; gap: 16px; align-items: center;">
@@ -89,11 +30,75 @@
<a href="{{ url_for('warehouse.import_locations_csv') }}" class="btn" style="padding: 4px 12px; font-size: 0.95em;">Go to Import Page</a> <a href="{{ url_for('warehouse.import_locations_csv') }}" class="btn" style="padding: 4px 12px; font-size: 0.95em;">Go to Import Page</a>
</div> </div>
</div> </div>
</div>
<!-- Print Barcode Section -->
<div class="print-section"> <!-- Container 2: Warehouse Locations Table (2 parts width) -->
<div class="warehouse-container-2">
<h3>Warehouse Locations</h3>
<div id="location-selection-hint" style="margin-bottom: 10px; font-size: 11px; color: #666; text-align: center;">
Click on a row to select a location for printing
</div>
<table class="scan-table" id="locations-table">
<thead>
<tr>
<th>ID</th>
<th>Location Code</th>
<th>Size</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for loc in locations %}
<tr class="location-row" data-location-code="{{ loc[1] }}"
data-location-id="{{ loc[0] }}"
data-location-size="{{ loc[2] or '' }}"
data-location-description="{{ loc[3] or '' }}">
<td>{{ loc[0] }}</td>
<td>{{ loc[1] }}</td>
<td>{{ loc[2] or '' }}</td>
<td>{{ loc[3] or '' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Container 3: Edit/Delete and Print (1 part width) -->
<div class="warehouse-container-3">
<div class="edit-delete-card">
<h3>Edit/Delete Selected Location</h3>
<div id="no-selection-message" class="selection-message">
Select a location from the table to edit or delete it.
</div>
<div id="selected-location-info" class="selection-info" style="display: none;">
<strong>Selected:</strong> <span id="selected-location-display"></span>
</div>
<div class="button-group">
<button id="edit-location-btn" class="btn btn-info" disabled>
✏️ Edit Selected Location
</button>
<button id="delete-location-btn" class="btn btn-danger" disabled>
🗑️ Delete Selected Location
</button>
</div>
</div>
{% if session['role'] in ['administrator', 'management'] %}
<div style="margin-top: 32px; padding: 12px; border-top: 1px solid #eee;">
<label style="font-weight:bold;">Delete location from table</label>
<div style="font-size:0.95em; margin-bottom:8px;">To delete a location, enter the ID of the location and press delete.<br>To delete 2 or multiple locations, enter the IDs separated by "," and then press delete.</div>
<form method="POST" style="display:flex; gap:8px; align-items:center;" onsubmit="return confirmDeleteLocations();">
<input type="text" name="delete_ids" placeholder="e.g. 5,7,12" style="width:160px;">
<button type="submit" name="delete_locations" value="1" class="btn" style="padding:4px 16px;">Delete Locations</button>
</form>
<script>
function confirmDeleteLocations() {
return confirm('Do you really want to delete the selected locations?');
}
</script>
</div>
{% endif %}
<hr style="margin: 32px 0 24px 0;">
<h3 style="font-size: 1.1em; margin-bottom: 12px;">Print Location Barcode</h3> <h3 style="font-size: 1.1em; margin-bottom: 12px;">Print Location Barcode</h3>
<!-- Label Preview --> <!-- Label Preview -->
<div id="barcode-label-preview" style="display: flex; align-items: center; justify-content: center;"> <div id="barcode-label-preview" style="display: flex; align-items: center; justify-content: center;">
<div style="width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;"> <div style="width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;">
@@ -104,7 +109,6 @@
<div id="barcode-placeholder" style="color: #999; font-style: italic; text-align: center;">No location selected</div> <div id="barcode-placeholder" style="color: #999; font-style: italic; text-align: center;">No location selected</div>
</div> </div>
</div> </div>
<!-- Print Controls --> <!-- Print Controls -->
<div style="text-align: center; margin-top: 15px;"> <div style="text-align: center; margin-top: 15px;">
<div style="margin-bottom: 10px;"> <div style="margin-bottom: 10px;">
@@ -121,51 +125,29 @@
</button> </button>
<div id="print-status" style="margin-top: 8px; font-size: 11px;"></div> <div id="print-status" style="margin-top: 8px; font-size: 11px;"></div>
</div> </div>
</div>
<!-- Delete Location Area -->
{% if session['role'] in ['administrator', 'management'] %}
<div style="margin-top: 32px; padding: 12px; border-top: 1px solid #eee;">
<label style="font-weight:bold;">Delete location from table</label>
<div style="font-size:0.95em; margin-bottom:8px;">To delete a location, enter the ID of the location and press delete.<br>To delete 2 or multiple locations, enter the IDs separated by "," and then press delete.</div>
<form method="POST" style="display:flex; gap:8px; align-items:center;" onsubmit="return confirmDeleteLocations();">
<input type="text" name="delete_ids" placeholder="e.g. 5,7,12" style="width:160px;">
<button type="submit" name="delete_locations" value="1" class="btn" style="padding:4px 16px;">Delete Locations</button>
</form>
<script>
function confirmDeleteLocations() {
return confirm('Do you really want to delete the selected locations?');
}
</script>
</div>
{% endif %}
</div> </div>
<!-- Locations Table Card --> </div>
<div class="card scan-table-card">
<h3>Warehouse Locations</h3> <!-- Delete Confirmation Modal -->
<div id="location-selection-hint" style="margin-bottom: 10px; font-size: 11px; color: #666; text-align: center;"> <div id="delete-modal" class="modal" style="display: none;">
Click on a row to select a location for printing <div class="modal-content">
<div class="modal-header">
<h3>Confirm Delete</h3>
<span class="close" onclick="closeDeleteModal()">&times;</span>
</div>
<div class="modal-body">
<p>Are you sure you want to delete the following location?</p>
<table class="confirm-table">
<tr><td><strong>ID:</strong></td><td id="delete-confirm-id"></td></tr>
<tr><td><strong>Location Code:</strong></td><td id="delete-confirm-code"></td></tr>
<tr><td><strong>Size:</strong></td><td id="delete-confirm-size"></td></tr>
<tr><td><strong>Description:</strong></td><td id="delete-confirm-description"></td></tr>
</table>
<div class="modal-buttons">
<button type="button" class="btn btn-secondary" onclick="closeDeleteModal()">Cancel</button>
<button type="button" class="btn btn-danger" onclick="confirmDelete()">Delete Location</button>
</div>
</div> </div>
<table class="scan-table" id="locations-table">
<thead>
<tr>
<th>ID</th>
<th>Location Code</th>
<th>Size</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for loc in locations %}
<tr class="location-row" data-location-code="{{ loc[1] }}">
<td>{{ loc[0] }}</td>
<td>{{ loc[1] }}</td>
<td>{{ loc[2] }}</td>
<td>{{ loc[3] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div> </div>
@@ -369,6 +351,13 @@ document.addEventListener('DOMContentLoaded', function() {
const locationRows = document.querySelectorAll('.location-row'); const locationRows = document.querySelectorAll('.location-row');
const locationCodeDisplay = document.getElementById('location-code-display'); const locationCodeDisplay = document.getElementById('location-code-display');
const printButton = document.getElementById('print-barcode-btn'); const printButton = document.getElementById('print-barcode-btn');
const editButton = document.getElementById('edit-location-btn');
const deleteButton = document.getElementById('delete-location-btn');
const noSelectionMessage = document.getElementById('no-selection-message');
const selectedLocationInfo = document.getElementById('selected-location-info');
const selectedLocationDisplay = document.getElementById('selected-location-display');
let selectedLocation = null;
locationRows.forEach(row => { locationRows.forEach(row => {
row.addEventListener('click', function() { row.addEventListener('click', function() {
@@ -378,22 +367,49 @@ document.addEventListener('DOMContentLoaded', function() {
// Select this row // Select this row
this.classList.add('selected'); this.classList.add('selected');
// Get location code // Get location data
selectedLocationCode = this.dataset.locationCode; selectedLocationCode = this.dataset.locationCode;
selectedLocation = {
id: this.cells[0].textContent,
code: this.cells[1].textContent,
size: this.cells[2].textContent,
description: this.cells[3].textContent
};
// Update display // Update display
locationCodeDisplay.textContent = selectedLocationCode; locationCodeDisplay.textContent = selectedLocationCode;
selectedLocationDisplay.textContent = `${selectedLocation.code} (ID: ${selectedLocation.id})`;
// Show/hide selection info
noSelectionMessage.style.display = 'none';
selectedLocationInfo.style.display = 'block';
// Generate barcode // Generate barcode
generateLocationBarcode(selectedLocationCode); generateLocationBarcode(selectedLocationCode);
// Enable print button // Enable buttons
printButton.disabled = false; printButton.disabled = false;
editButton.disabled = false;
deleteButton.disabled = false;
showNotification(`📍 Selected location: ${selectedLocationCode}`, 'info'); showNotification(`📍 Selected location: ${selectedLocationCode}`, 'info');
}); });
}); });
// Edit button functionality
editButton.addEventListener('click', function() {
if (selectedLocation) {
openEditModal(selectedLocation);
}
});
// Delete button functionality
deleteButton.addEventListener('click', function() {
if (selectedLocation) {
openDeleteModal(selectedLocation);
}
});
// Initialize QZ Tray // Initialize QZ Tray
initializeQZTray(); initializeQZTray();
}); });
@@ -519,5 +535,111 @@ async function testQZConnection() {
// Add test button functionality (for debugging - can be removed in production) // Add test button functionality (for debugging - can be removed in production)
console.log('🔧 QZ Tray test function available: testQZConnection()'); console.log('🔧 QZ Tray test function available: testQZConnection()');
// Modal Functions for Edit/Delete
function openEditModal(location) {
document.getElementById('edit-location-id').value = location.id;
document.getElementById('edit-location-code').value = location.code;
document.getElementById('edit-size').value = location.size || '';
document.getElementById('edit-description').value = location.description || '';
document.getElementById('edit-modal').style.display = 'flex';
}
function closeEditModal() {
document.getElementById('edit-modal').style.display = 'none';
}
function openDeleteModal(location) {
document.getElementById('delete-confirm-id').textContent = location.id;
document.getElementById('delete-confirm-code').textContent = location.code;
document.getElementById('delete-confirm-size').textContent = location.size || '-';
document.getElementById('delete-confirm-description').textContent = location.description || '-';
document.getElementById('delete-modal').style.display = 'flex';
}
function closeDeleteModal() {
document.getElementById('delete-modal').style.display = 'none';
}
// Handle edit form submission
document.getElementById('edit-form').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const data = {
location_id: formData.get('location_id'),
location_code: formData.get('location_code'),
size: formData.get('size'),
description: formData.get('description')
};
// Send update request
fetch('/update_location', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
if (result.success) {
showNotification('✅ Location updated successfully!', 'success');
closeEditModal();
// Reload page to show changes
setTimeout(() => window.location.reload(), 1000);
} else {
showNotification('❌ Error updating location: ' + result.error, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('❌ Error updating location: ' + error.message, 'error');
});
});
// Handle delete confirmation
function confirmDelete() {
const locationId = document.getElementById('delete-confirm-id').textContent;
// Send delete request
fetch('/delete_location', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ location_id: locationId })
})
.then(response => response.json())
.then(result => {
if (result.success) {
showNotification('✅ Location deleted successfully!', 'success');
closeDeleteModal();
// Reload page to show changes
setTimeout(() => window.location.reload(), 1000);
} else {
showNotification('❌ Error deleting location: ' + result.error, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('❌ Error deleting location: ' + error.message, 'error');
});
}
// Close modals when clicking outside
window.addEventListener('click', function(event) {
const editModal = document.getElementById('edit-modal');
const deleteModal = document.getElementById('delete-modal');
if (event.target === editModal) {
closeEditModal();
}
if (event.target === deleteModal) {
closeDeleteModal();
}
});
</script> </script>
</div>
</div>
{% endblock %} {% endblock %}
+4
View File
@@ -2,6 +2,10 @@
{% block title %}Login{% endblock %} {% block title %}Login{% endblock %}
{% block head %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/login.css') }}">
{% endblock %}
{% block content %} {% block content %}
<div class="login-page"> <div class="login-page">
<div class="logo-container"> <div class="logo-container">
+56
View File
@@ -218,3 +218,59 @@ def generate_location_label_pdf():
except Exception as e: except Exception as e:
print(f"Error generating location label PDF: {e}") print(f"Error generating location label PDF: {e}")
return jsonify({'error': str(e)}), 500 return jsonify({'error': str(e)}), 500
def update_location(location_id, location_code, size, description):
"""Update an existing warehouse location"""
try:
conn = get_db_connection()
cursor = conn.cursor()
# Check if location exists
cursor.execute("SELECT id FROM warehouse_locations WHERE id = ?", (location_id,))
if not cursor.fetchone():
conn.close()
return {"success": False, "error": "Location not found"}
# Check if location code already exists for different location
cursor.execute("SELECT id FROM warehouse_locations WHERE location_code = ? AND id != ?", (location_code, location_id))
if cursor.fetchone():
conn.close()
return {"success": False, "error": "Location code already exists"}
# Update location
cursor.execute(
"UPDATE warehouse_locations SET location_code = ?, size = ?, description = ? WHERE id = ?",
(location_code, size if size else None, description, location_id)
)
conn.commit()
conn.close()
return {"success": True, "message": "Location updated successfully"}
except Exception as e:
print(f"Error updating location: {e}")
return {"success": False, "error": str(e)}
def delete_location_by_id(location_id):
"""Delete a warehouse location by ID"""
try:
conn = get_db_connection()
cursor = conn.cursor()
# Check if location exists
cursor.execute("SELECT location_code FROM warehouse_locations WHERE id = ?", (location_id,))
location = cursor.fetchone()
if not location:
conn.close()
return {"success": False, "error": "Location not found"}
# Delete location
cursor.execute("DELETE FROM warehouse_locations WHERE id = ?", (location_id,))
conn.commit()
conn.close()
return {"success": True, "message": f"Location '{location[0]}' deleted successfully"}
except Exception as e:
print(f"Error deleting location: {e}")
return {"success": False, "error": str(e)}
+3 -3
View File
@@ -1,5 +1,5 @@
server_domain=localhost server_domain=localhost
port=3602 port=3602
database_name=recticel database_name=trasabilitate_database
username=sa username=trasabilitate
password=12345678 password=Initial01!