Initial commit: Ballet Production Suite ERP/CRM foundation
This commit is contained in:
141
frontend/app.py
Normal file
141
frontend/app.py
Normal file
@@ -0,0 +1,141 @@
|
||||
from nicegui import ui
|
||||
import api_client
|
||||
from pages.logistics import render_logistics_page
|
||||
|
||||
# Define the layout
|
||||
@ui.page('/')
|
||||
def main_page():
|
||||
with ui.header().classes('items-center justify-between'):
|
||||
ui.label('Suite de Producción de Ballet').classes('text-2xl font-bold')
|
||||
with ui.row():
|
||||
ui.button('Cerrar Sesión', icon='logout', on_click=lambda: ui.notify('Sesión cerrada'))
|
||||
|
||||
with ui.left_drawer(value=True).classes('bg-slate-100'):
|
||||
with ui.column().classes('w-full gap-2 p-4'):
|
||||
ui.button('Panel de Control', icon='dashboard', on_click=lambda: content.set_content('Dashboard')).classes('w-full justify-start')
|
||||
ui.button('Productos', icon='inventory_2', on_click=lambda: content.set_content('Productos')).classes('w-full justify-start')
|
||||
ui.button('CRM y Pedidos', icon='people', on_click=lambda: content.set_content('CRM')).classes('w-full justify-start')
|
||||
ui.button('Producción', icon='factory', on_click=lambda: content.set_content('Producción')).classes('w-full justify-start')
|
||||
ui.button('Inventario', icon='category', on_click=lambda: content.set_content('Inventario')).classes('w-full justify-start')
|
||||
ui.button('Logística', icon='local_shipping', on_click=lambda: content.set_content('Logística')).classes('w-full justify-start')
|
||||
|
||||
with ui.column().classes('w-full p-8') as main_content:
|
||||
ui.label('Bienvenido al Panel de Control de Ballet Atelier').classes('text-3xl')
|
||||
ui.markdown('''
|
||||
Gestione su negocio de ropa de ballet de manera eficiente:
|
||||
- **Productos**: Seguimiento de sus más de 100 modelos y materiales.
|
||||
- **CRM**: Gestione clientes y sus medidas únicas.
|
||||
- **Producción**: Supervise los pedidos a través del flujo del taller.
|
||||
- **Inventario**: Seguimiento de stock y alertas en tiempo real.
|
||||
''')
|
||||
|
||||
class ContentManager:
|
||||
def set_content(self, page_name):
|
||||
main_content.clear()
|
||||
with main_content:
|
||||
ui.label(f'Página de {page_name}').classes('text-3xl mb-4')
|
||||
if page_name == 'Dashboard':
|
||||
ui.label('Aquí aparecerán las estadísticas resumidas.')
|
||||
elif page_name == 'Inventario':
|
||||
render_inventory_page()
|
||||
elif page_name == 'CRM':
|
||||
render_crm_page()
|
||||
elif page_name == 'Producción':
|
||||
render_production_page()
|
||||
elif page_name == 'Logística':
|
||||
render_logistics_page()
|
||||
elif page_name == 'Productos':
|
||||
render_products_page()
|
||||
else:
|
||||
ui.label(f'Contenido para {page_name} próximamente...').classes('text-xl')
|
||||
|
||||
content = ContentManager()
|
||||
|
||||
def render_crm_page():
|
||||
ui.label('Gestión de Clientes (CRM)').classes('text-xl mb-4')
|
||||
|
||||
with ui.row().classes('w-full items-start gap-4'):
|
||||
# Client List
|
||||
with ui.card().classes('flex-1'):
|
||||
ui.label('Clientes').classes('font-bold mb-2')
|
||||
columns = [
|
||||
{'name': 'name', 'label': 'Nombre', 'field': 'name', 'align': 'left'},
|
||||
{'name': 'phone', 'label': 'Teléfono', 'field': 'phone'},
|
||||
]
|
||||
rows = [
|
||||
{'name': 'Maria Garcia', 'phone': '600-000-000'},
|
||||
{'name': 'Elena Rodriguez', 'phone': '611-111-111'},
|
||||
]
|
||||
ui.table(columns=columns, rows=rows, row_key='name').classes('w-full')
|
||||
|
||||
# New Client Form
|
||||
with ui.card().classes('w-64'):
|
||||
ui.label('Nuevo Cliente').classes('font-bold')
|
||||
ui.input('Nombre Completo')
|
||||
ui.input('Teléfono')
|
||||
ui.textarea('Medidas').classes('w-full')
|
||||
ui.button('Guardar Cliente', on_click=lambda: ui.notify('Cliente guardado'))
|
||||
|
||||
def render_production_page():
|
||||
ui.label('Flujo de Producción (Kanban)').classes('text-xl mb-4')
|
||||
|
||||
with ui.row().classes('w-full mb-4'):
|
||||
ui.button('Generar Hoja de Corte', icon='content_cut', on_click=lambda: ui.notify('Hoja de corte generada para todos los pedidos pendientes'))
|
||||
|
||||
with ui.row().classes('w-full justify-between gap-4'):
|
||||
for status_label in ['Corte', 'Confección', 'Terminado']:
|
||||
with ui.column().classes('bg-slate-50 p-4 rounded-lg flex-1 min-h-[400px] border border-slate-200'):
|
||||
ui.label(status_label).classes('font-bold text-lg mb-4 text-slate-600 uppercase tracking-wider')
|
||||
|
||||
# Sample Card
|
||||
with ui.card().classes('w-full mb-2 cursor-pointer hover:shadow-md transition-shadow'):
|
||||
ui.label('Pedido #1001').classes('font-bold')
|
||||
ui.label('Cliente: Maria Garcia')
|
||||
ui.label('Estado: En Proceso').classes('text-xs text-blue-500')
|
||||
with ui.row().classes('justify-end w-full'):
|
||||
ui.button(icon='arrow_forward', variant='text').classes('p-0')
|
||||
|
||||
def render_products_page():
|
||||
ui.label('Catálogo de Productos').classes('text-xl mb-4')
|
||||
with ui.grid(columns=3).classes('w-full gap-4'):
|
||||
# In a real app, this would fetch from the API
|
||||
products = [
|
||||
{'name': 'Tutú Profesional', 'sku': 'TUTU-001', 'total_cost': 65.50},
|
||||
{'name': 'Maillot Clásico', 'sku': 'LEO-002', 'total_cost': 28.00},
|
||||
{'name': 'Falda de Ballet', 'sku': 'SKT-003', 'total_cost': 15.20},
|
||||
]
|
||||
for p in products:
|
||||
with ui.card():
|
||||
ui.label(p['name']).classes('font-bold')
|
||||
ui.label(f"SKU: {p['sku']}").classes('text-xs text-slate-500')
|
||||
ui.label(f"Coste Total: {p['total_cost']}€").classes('text-lg font-semibold text-primary')
|
||||
ui.button('Editar Materiales', variant='text')
|
||||
|
||||
def render_inventory_page():
|
||||
ui.label('Inventario de Materiales').classes('text-xl mb-2')
|
||||
|
||||
# Simple form to add material
|
||||
with ui.card().classes('w-full mb-4'):
|
||||
ui.label('Añadir Nuevo Material').classes('font-bold')
|
||||
with ui.row():
|
||||
name = ui.input('Nombre')
|
||||
unit = ui.input('Unidad (ej. metros)')
|
||||
stock = ui.number('Stock Inicial', value=0)
|
||||
cost = ui.number('Coste por Unidad', value=0.0)
|
||||
ui.button('Añadir Material', on_click=lambda: ui.notify(f'Añadido {name.value}'))
|
||||
|
||||
# Placeholder for table
|
||||
ui.label('Niveles de Stock').classes('font-bold')
|
||||
columns = [
|
||||
{'name': 'name', 'label': 'Nombre', 'field': 'name', 'required': True, 'align': 'left'},
|
||||
{'name': 'unit', 'label': 'Unidad', 'field': 'unit'},
|
||||
{'name': 'stock', 'label': 'Stock', 'field': 'stock'},
|
||||
{'name': 'cost', 'label': 'Coste', 'field': 'cost'},
|
||||
]
|
||||
rows = [
|
||||
{'name': 'Satén Rosa', 'unit': 'metros', 'stock': 50, 'cost': 12.5},
|
||||
{'name': 'Tul Blanco', 'unit': 'metros', 'stock': 120, 'cost': 5.0},
|
||||
]
|
||||
ui.table(columns=columns, rows=rows, row_key='name')
|
||||
|
||||
ui.run(title="Suite de Producción de Ballet", port=8080)
|
||||
Reference in New Issue
Block a user