{"app":{"id":25,"summary":"Production Planning","versions":[8856,8857,8858,8861,8862],"created_by":"tristan795","created_at":"2026-02-17T10:40:43.532Z","votes":0,"approved":true,"apps":[],"app_type":"raw","external_embed_url":"https://app.windmill.dev/public/windmill-labs/f58e92fcd1b760f5ff379156f2cd0d41","value":{"runnables":{"get_steps":{"type":"inline","inlineScript":{"lock":"{\n  \"dependencies\": {\n    \"windmill-client\": \"latest\"\n  }\n}\n//bun.lock\n{\n  \"lockfileVersion\": 1,\n  \"workspaces\": {\n    \"\": {\n      \"dependencies\": {\n        \"windmill-client\": \"latest\",\n      },\n    },\n  },\n  \"packages\": {\n    \"windmill-client\": [\"windmill-client@1.624.0\", \"\", {}, \"sha512-Y/Y3/7wIe7gyiwtvbme/+wO5fdEqXaskTxHum+Iz8A6qwU66KFohfx/WBpuyXThC6StwFIq1xEX0wOqTqM42AA==\"],\n  }\n}\n","content":"import * as wmill from 'windmill-client';\n\nexport async function main() {\n  try {\n    console.log('get_steps: Starting...');\n    const sql = wmill.datatable();\n    console.log('get_steps: Got datatable connection');\n    const steps = await sql`\n      SELECT step_number, name, theoretical_duration_min\n      FROM assemblyline.steps\n      ORDER BY step_number\n    `.fetch();\n    console.log('get_steps: Query complete, rows:', steps?.length);\n    return steps;\n  } catch (error: any) {\n    console.error('get_steps ERROR:', error?.message || error);\n    throw error;\n  }\n}\n","language":"bun"}},"get_orders":{"type":"inline","inlineScript":{"lock":"{\n  \"dependencies\": {\n    \"windmill-client\": \"latest\"\n  }\n}\n//bun.lock\n{\n  \"lockfileVersion\": 1,\n  \"workspaces\": {\n    \"\": {\n      \"dependencies\": {\n        \"windmill-client\": \"latest\",\n      },\n    },\n  },\n  \"packages\": {\n    \"windmill-client\": [\"windmill-client@1.624.0\", \"\", {}, \"sha512-Y/Y3/7wIe7gyiwtvbme/+wO5fdEqXaskTxHum+Iz8A6qwU66KFohfx/WBpuyXThC6StwFIq1xEX0wOqTqM42AA==\"],\n  }\n}\n","content":"import * as wmill from 'windmill-client';\n\nexport async function main(status?: string) {\n  try {\n    console.log('get_orders: Starting with status:', status);\n    const sql = wmill.datatable();\n    console.log('get_orders: Got datatable connection');\n\n    let result;\n    if (status && status !== 'all') {\n      result = await sql`\n        SELECT f.*,\n               COALESCE(s.name, 'Non démarré') as current_step_name\n        FROM assemblyline.orders f\n        LEFT JOIN assemblyline.steps s ON s.step_number = f.current_step\n        WHERE f.status = ${status}\n        ORDER BY\n          CASE f.priority\n            WHEN 'urgent' THEN 1\n            WHEN 'high' THEN 2\n            WHEN 'normal' THEN 3\n            ELSE 4\n          END,\n          f.due_date ASC NULLS LAST\n      `.fetch();\n    } else {\n      result = await sql`\n        SELECT f.*,\n               COALESCE(s.name, 'Non démarré') as current_step_name\n        FROM assemblyline.orders f\n        LEFT JOIN assemblyline.steps s ON s.step_number = f.current_step\n        ORDER BY\n          CASE f.priority\n            WHEN 'urgent' THEN 1\n            WHEN 'high' THEN 2\n            WHEN 'normal' THEN 3\n            ELSE 4\n          END,\n          f.due_date ASC NULLS LAST\n      `.fetch();\n    }\n    console.log('get_orders: Query complete, rows:', result?.length);\n    return result;\n  } catch (error: any) {\n    console.error('get_orders ERROR:', error?.message || error);\n    throw error;\n  }\n}\n","language":"bun"}},"init_steps":{"type":"inline","inlineScript":{"lock":"{\n  \"dependencies\": {\n    \"windmill-client\": \"latest\"\n  }\n}\n//bun.lock\n{\n  \"lockfileVersion\": 1,\n  \"workspaces\": {\n    \"\": {\n      \"dependencies\": {\n        \"windmill-client\": \"latest\",\n      },\n    },\n  },\n  \"packages\": {\n    \"windmill-client\": [\"windmill-client@1.624.0\", \"\", {}, \"sha512-Y/Y3/7wIe7gyiwtvbme/+wO5fdEqXaskTxHum+Iz8A6qwU66KFohfx/WBpuyXThC6StwFIq1xEX0wOqTqM42AA==\"],\n  }\n}\n","content":"import * as wmill from 'windmill-client';\n\nexport async function main() {\n  const sql = wmill.datatable();\n\n  // Define the 7 manufacturing steps\n  const steps = [\n    { step_number: 1, name: 'Order Received', theoretical_duration_min: 30 },\n    { step_number: 2, name: 'Materials Ready', theoretical_duration_min: 45 },\n    { step_number: 3, name: 'Production', theoretical_duration_min: 60 },\n    { step_number: 4, name: 'Assembly', theoretical_duration_min: 90 },\n    { step_number: 5, name: 'Quality Check', theoretical_duration_min: 30 },\n    { step_number: 6, name: 'Packaging', theoretical_duration_min: 45 },\n    { step_number: 7, name: 'Ready to Ship', theoretical_duration_min: 20 }\n  ];\n\n  // Clear existing steps and insert new ones\n  await sql`DELETE FROM assemblyline.steps`;\n\n  for (const step of steps) {\n    await sql`\n      INSERT INTO assemblyline.steps (step_number, name, theoretical_duration_min)\n      VALUES (${step.step_number}, ${step.name}, ${step.theoretical_duration_min})\n    `;\n  }\n\n  return { success: true, message: 'Steps initialized', steps_count: steps.length };\n}\n","language":"bun"}},"start_step":{"type":"inline","inlineScript":{"lock":"{\n  \"dependencies\": {\n    \"windmill-client\": \"latest\"\n  }\n}\n//bun.lock\n{\n  \"lockfileVersion\": 1,\n  \"workspaces\": {\n    \"\": {\n      \"dependencies\": {\n        \"windmill-client\": \"latest\",\n      },\n    },\n  },\n  \"packages\": {\n    \"windmill-client\": [\"windmill-client@1.624.0\", \"\", {}, \"sha512-Y/Y3/7wIe7gyiwtvbme/+wO5fdEqXaskTxHum+Iz8A6qwU66KFohfx/WBpuyXThC6StwFIq1xEX0wOqTqM42AA==\"],\n  }\n}\n","content":"import * as wmill from 'windmill-client';\n\nexport async function main(\n  order_id: string,\n  step_number: number,\n  operator_id: string\n) {\n  const sql = wmill.datatable();\n\n  // Validate step number\n  if (step_number < 1 || step_number > 7) {\n    throw new Error('Step number must be between 1 and 7');\n  }\n\n  // Check if previous step is completed (except for step 1)\n  if (step_number > 1) {\n    const prevStep = await sql`\n      SELECT status FROM assemblyline.step_logs\n      WHERE order_id = ${order_id} AND step_number = ${step_number - 1}\n    `.fetchOne();\n\n    if (prevStep?.status !== 'completed') {\n      throw new Error(`Step ${step_number - 1} must be completed first`);\n    }\n  }\n\n  // Start the step\n  await sql`\n    UPDATE assemblyline.step_logs\n    SET status = 'in_progress',\n        operator_id = ${operator_id},\n        started_at = NOW()\n    WHERE order_id = ${order_id} AND step_number = ${step_number}\n  `.execute();\n\n  // Update order status and current step\n  await sql`\n    UPDATE assemblyline.orders\n    SET status = 'in_progress', current_step = ${step_number}\n    WHERE id = ${order_id}\n  `.execute();\n\n  return { success: true, message: `Step ${step_number} started` };\n}\n","language":"bun"}},"create_order":{"type":"inline","inlineScript":{"lock":"{\n  \"dependencies\": {\n    \"windmill-client\": \"latest\"\n  }\n}\n//bun.lock\n{\n  \"lockfileVersion\": 1,\n  \"workspaces\": {\n    \"\": {\n      \"dependencies\": {\n        \"windmill-client\": \"latest\",\n      },\n    },\n  },\n  \"packages\": {\n    \"windmill-client\": [\"windmill-client@1.624.0\", \"\", {}, \"sha512-Y/Y3/7wIe7gyiwtvbme/+wO5fdEqXaskTxHum+Iz8A6qwU66KFohfx/WBpuyXThC6StwFIq1xEX0wOqTqM42AA==\"],\n  }\n}\n","content":"import * as wmill from 'windmill-client';\n\nexport async function main(\n  id: string,\n  client_name: string,\n  product_description?: string,\n  quantity?: number,\n  priority?: string,\n  due_date?: string\n) {\n  const sql = wmill.datatable();\n\n  // Create order\n  const dueDateValue = due_date ? new Date(due_date) : null;\n  await sql`\n    INSERT INTO assemblyline.orders (id, client_name, product_description, quantity, priority, due_date)\n    VALUES (${id}, ${client_name}, ${product_description || null}, ${quantity || 1}, ${priority || 'normal'}, ${dueDateValue}::date)\n  `.execute();\n\n  // Initialize all 7 step logs for this order\n  for (let step = 1; step <= 7; step++) {\n    await sql`\n      INSERT INTO assemblyline.step_logs (order_id, step_number, status)\n      VALUES (${id}, ${step}, 'pending')\n    `.execute();\n  }\n\n  return { success: true, order_id: id };\n}\n","language":"bun"}},"complete_step":{"type":"inline","inlineScript":{"lock":"{\n  \"dependencies\": {\n    \"windmill-client\": \"latest\"\n  }\n}\n//bun.lock\n{\n  \"lockfileVersion\": 1,\n  \"workspaces\": {\n    \"\": {\n      \"dependencies\": {\n        \"windmill-client\": \"latest\",\n      },\n    },\n  },\n  \"packages\": {\n    \"windmill-client\": [\"windmill-client@1.624.0\", \"\", {}, \"sha512-Y/Y3/7wIe7gyiwtvbme/+wO5fdEqXaskTxHum+Iz8A6qwU66KFohfx/WBpuyXThC6StwFIq1xEX0wOqTqM42AA==\"],\n  }\n}\n","content":"import * as wmill from 'windmill-client';\n\nexport async function main(\n  order_id: string,\n  step_number: number,\n  notes?: string\n) {\n  const sql = wmill.datatable();\n\n  // Get current step info\n  const stepLog = await sql`\n    SELECT * FROM assemblyline.step_logs\n    WHERE order_id = ${order_id} AND step_number = ${step_number}\n  `.fetchOne();\n\n  if (!stepLog) {\n    throw new Error('Step not found');\n  }\n\n  if (stepLog.status !== 'in_progress') {\n    throw new Error('Step must be in progress to complete');\n  }\n\n  // Calculate actual duration\n  const startTime = new Date(stepLog.started_at).getTime();\n  const endTime = Date.now();\n  const durationMin = Math.round((endTime - startTime) / 60000);\n\n  // Complete the step\n  await sql`\n    UPDATE assemblyline.step_logs\n    SET status = 'completed',\n        completed_at = NOW(),\n        actual_duration_min = ${durationMin},\n        notes = ${notes || null}\n    WHERE order_id = ${order_id} AND step_number = ${step_number}\n  `.execute();\n\n  // Check if this was the last step\n  if (step_number === 7) {\n    await sql`\n      UPDATE assemblyline.orders\n      SET status = 'completed', completed_at = NOW()\n      WHERE id = ${order_id}\n    `.execute();\n  }\n\n  // Get theoretical duration for comparison\n  const stepRef = await sql`\n    SELECT theoretical_duration_min FROM assemblyline.steps\n    WHERE step_number = ${step_number}\n  `.fetchOne();\n\n  const theoreticalMin = stepRef?.theoretical_duration_min || 15;\n  const isDelayed = durationMin > theoreticalMin;\n\n  return {\n    success: true,\n    actual_duration_min: durationMin,\n    theoretical_duration_min: theoreticalMin,\n    is_delayed: isDelayed,\n    delay_min: isDelayed ? durationMin - theoreticalMin : 0\n  };\n}\n","language":"bun"}},"get_order_progress":{"type":"inline","inlineScript":{"lock":"{\n  \"dependencies\": {\n    \"windmill-client\": \"latest\"\n  }\n}\n//bun.lock\n{\n  \"lockfileVersion\": 1,\n  \"workspaces\": {\n    \"\": {\n      \"dependencies\": {\n        \"windmill-client\": \"latest\",\n      },\n    },\n  },\n  \"packages\": {\n    \"windmill-client\": [\"windmill-client@1.624.0\", \"\", {}, \"sha512-Y/Y3/7wIe7gyiwtvbme/+wO5fdEqXaskTxHum+Iz8A6qwU66KFohfx/WBpuyXThC6StwFIq1xEX0wOqTqM42AA==\"],\n  }\n}\n","content":"import * as wmill from 'windmill-client';\n\nexport async function main(order_id: string) {\n  const sql = wmill.datatable();\n\n  // Get order info\n  const order = await sql`\n    SELECT * FROM assemblyline.orders WHERE id = ${order_id}\n  `.fetchOne();\n\n  if (!order) {\n    throw new Error('Order not found');\n  }\n\n  // Get all steps with their logs (if any)\n  const steps = await sql`\n    SELECT\n      s.step_number,\n      s.name as step_name,\n      s.theoretical_duration_min,\n      COALESCE(sl.status, 'pending') as status,\n      sl.operator_id,\n      sl.started_at,\n      sl.completed_at,\n      sl.actual_duration_min,\n      sl.notes\n    FROM assemblyline.steps s\n    LEFT JOIN assemblyline.step_logs sl ON sl.step_number = s.step_number AND sl.order_id = ${order_id}\n    ORDER BY s.step_number\n  `.fetch();\n\n  // Calculate progress\n  const completedSteps = steps.filter((s: any) => s.status === 'completed').length;\n  const progressPercent = Math.round((completedSteps / 7) * 100);\n\n  // Calculate total time\n  const totalActual = steps.reduce((sum: number, s: any) => sum + (s.actual_duration_min || 0), 0);\n  const totalTheoretical = steps.reduce((sum: number, s: any) => sum + s.theoretical_duration_min, 0);\n\n  return {\n    order,\n    steps,\n    progress: {\n      completed_steps: completedSteps,\n      total_steps: 7,\n      percent: progressPercent,\n      total_actual_min: totalActual,\n      total_theoretical_min: totalTheoretical,\n      efficiency: totalActual > 0 ? Math.round((totalTheoretical / totalActual) * 100) : null\n    }\n  };\n}\n","language":"bun"}},"get_dashboard_stats":{"type":"inline","inlineScript":{"lock":"{\n  \"dependencies\": {\n    \"windmill-client\": \"latest\"\n  }\n}\n//bun.lock\n{\n  \"lockfileVersion\": 1,\n  \"workspaces\": {\n    \"\": {\n      \"dependencies\": {\n        \"windmill-client\": \"latest\",\n      },\n    },\n  },\n  \"packages\": {\n    \"windmill-client\": [\"windmill-client@1.624.0\", \"\", {}, \"sha512-Y/Y3/7wIe7gyiwtvbme/+wO5fdEqXaskTxHum+Iz8A6qwU66KFohfx/WBpuyXThC6StwFIq1xEX0wOqTqM42AA==\"],\n  }\n}\n","content":"import * as wmill from 'windmill-client';\n\nexport async function main() {\n  const sql = wmill.datatable();\n\n  // Get order counts by status\n  const statusCounts = await sql`\n    SELECT status, COUNT(*) as count\n    FROM assemblyline.orders\n    GROUP BY status\n  `.fetch();\n\n  // Get urgent/high priority orders in progress\n  const priorityFolders = await sql`\n    SELECT f.id, f.client_name, f.priority, f.due_date,\n           s.name as current_step_name, f.current_step\n    FROM assemblyline.orders f\n    LEFT JOIN assemblyline.steps s ON s.step_number = f.current_step\n    WHERE f.status = 'in_progress'\n      AND f.priority IN ('urgent', 'high')\n    ORDER BY\n      CASE f.priority WHEN 'urgent' THEN 1 ELSE 2 END,\n      f.due_date ASC\n    LIMIT 5\n  `.fetch();\n\n  // Get step performance (avg actual vs theoretical)\n  const stepPerformance = await sql`\n    SELECT s.step_number, s.name, s.theoretical_duration_min,\n           ROUND(AVG(sl.actual_duration_min)) as avg_actual_min,\n           COUNT(sl.id) FILTER (WHERE sl.status = 'completed') as completed_count\n    FROM assemblyline.steps s\n    LEFT JOIN assemblyline.step_logs sl ON sl.step_number = s.step_number\n      AND sl.status = 'completed'\n    GROUP BY s.step_number, s.name, s.theoretical_duration_min\n    ORDER BY s.step_number\n  `.fetch();\n\n  // Get orders due today or overdue\n  const dueSoon = await sql`\n    SELECT COUNT(*) as count\n    FROM assemblyline.orders\n    WHERE status NOT IN ('completed', 'cancelled')\n      AND due_date <= CURRENT_DATE\n  `.fetchOne();\n\n  return {\n    status_counts: statusCounts,\n    priority_orders: priorityFolders,\n    step_performance: stepPerformance,\n    overdue_count: dueSoon?.count || 0\n  };\n}\n","language":"bun"}}},"files":{"/App.tsx":"import React, { useState, useEffect, useMemo } from 'react'\nimport { backend } from './wmill'\nimport './index.css'\n\ntype Order = {\n  id: string;\n  client_name: string;\n  product_description: string;\n  quantity: number;\n  priority: string;\n  due_date: string;\n  status: string;\n  current_step: number;\n  current_step_name: string;\n  created_at: string;\n};\n\ntype Step = {\n  step_number: number;\n  name: string;\n  theoretical_duration_min: number;\n};\n\nconst PRIORITY_CONFIG: Record<string, { color: string; bg: string; label: string }> = {\n  urgent: { color: '#dc2626', bg: '#fef2f2', label: 'Urgent' },\n  high: { color: '#ea580c', bg: '#fff7ed', label: 'High' },\n  normal: { color: '#2563eb', bg: '#eff6ff', label: 'Normal' },\n  low: { color: '#6b7280', bg: '#f9fafb', label: 'Low' }\n};\n\nconst STATUS_CONFIG: Record<string, { color: string; bg: string; label: string }> = {\n  pending: { color: '#6b7280', bg: '#f3f4f6', label: 'Pending' },\n  in_progress: { color: '#d97706', bg: '#fef3c7', label: 'In Progress' },\n  completed: { color: '#059669', bg: '#d1fae5', label: 'Completed' },\n  cancelled: { color: '#dc2626', bg: '#fef2f2', label: 'Cancelled' }\n};\n\nconst App = () => {\n  const [allOrders, setAllOrders] = useState<Order[]>([]);\n  const [steps, setSteps] = useState<Step[]>([]);\n  const [loading, setLoading] = useState(false);\n  const [selectedOrder, setSelectedOrder] = useState<string | null>(null);\n  const [allProgress, setAllProgress] = useState<Record<string, any>>({});\n  const [showNewForm, setShowNewForm] = useState(false);\n  const [filterStatus, setFilterStatus] = useState('all');\n\n  // Modal states\n  const [showStartModal, setShowStartModal] = useState(false);\n  const [showCompleteModal, setShowCompleteModal] = useState(false);\n  const [modalStepNumber, setModalStepNumber] = useState<number | null>(null);\n  const [operatorId, setOperatorId] = useState('');\n  const [completionNotes, setCompletionNotes] = useState('');\n\n  const [newOrder, setNewOrder] = useState({\n    id: '',\n    client_name: '',\n    product_description: '',\n    quantity: 1,\n    priority: 'normal',\n    due_date: ''\n  });\n\n  // Filter orders client-side based on selected status\n  const filteredOrders = useMemo(() => {\n    if (filterStatus === 'all') {\n      return allOrders;\n    }\n    return allOrders.filter(order => order.status === filterStatus);\n  }, [allOrders, filterStatus]);\n\n  const orderProgress = selectedOrder ? allProgress[selectedOrder] ?? null : null;\n\n  async function loadData() {\n    setLoading(true);\n    try {\n      const [ordersData, stepsData] = await Promise.all([\n        backend.get_orders({ status: 'all' }),\n        backend.get_steps({})\n      ]);\n      const ordersArray = Array.isArray(ordersData) ? ordersData : [];\n      const stepsArray = Array.isArray(stepsData) ? stepsData : [];\n      setAllOrders(ordersArray as Order[]);\n      setSteps(stepsArray as Step[]);\n\n      // Load progress for all orders in parallel\n      const orders = ordersArray as Order[];\n      const progressResults = await Promise.all(\n        orders.map(o => backend.get_order_progress({ order_id: o.id }).catch(() => null))\n      );\n      const progressMap: Record<string, any> = {};\n      orders.forEach((o, i) => {\n        if (progressResults[i]) progressMap[o.id] = progressResults[i];\n      });\n      setAllProgress(progressMap);\n    } catch (e) {\n      console.error('Error loading data:', e);\n    }\n    setLoading(false);\n  }\n\n  // Load data on mount\n  useEffect(() => {\n    loadData();\n  }, []);\n\n  async function handleCreateOrder(e: React.FormEvent) {\n    e.preventDefault();\n    try {\n      const result = await backend.create_order({\n        id: newOrder.id,\n        client_name: newOrder.client_name,\n        product_description: newOrder.product_description || undefined,\n        quantity: newOrder.quantity,\n        priority: newOrder.priority,\n        due_date: newOrder.due_date || undefined\n      });\n      if (result && typeof result === 'object' && 'error' in result) {\n        alert('Error: ' + JSON.stringify(result));\n        return;\n      }\n      setShowNewForm(false);\n      setNewOrder({ id: '', client_name: '', product_description: '', quantity: 1, priority: 'normal', due_date: '' });\n      loadData();\n    } catch (e: any) {\n      alert('Error creating order: ' + (e?.message || JSON.stringify(e)));\n    }\n  }\n\n  function openStartModal(stepNumber: number) {\n    setModalStepNumber(stepNumber);\n    setOperatorId('');\n    setShowStartModal(true);\n  }\n\n  function openCompleteModal(stepNumber: number) {\n    setModalStepNumber(stepNumber);\n    setCompletionNotes('');\n    setShowCompleteModal(true);\n  }\n\n  async function handleStartStep() {\n    if (!selectedOrder || !modalStepNumber || !operatorId.trim()) return;\n\n    try {\n      await backend.start_step({ order_id: selectedOrder, step_number: modalStepNumber, operator_id: operatorId.trim() });\n      setShowStartModal(false);\n      setOperatorId('');\n      loadData();\n      loadOrderProgress(selectedOrder);\n    } catch (e: any) {\n      alert('Error: ' + e.message);\n    }\n  }\n\n  async function handleCompleteStep() {\n    if (!selectedOrder || !modalStepNumber) return;\n\n    try {\n      await backend.complete_step({\n        order_id: selectedOrder,\n        step_number: modalStepNumber,\n        notes: completionNotes.trim() || undefined\n      });\n      setShowCompleteModal(false);\n      setCompletionNotes('');\n      loadData();\n      loadOrderProgress(selectedOrder);\n    } catch (e: any) {\n      alert('Error: ' + e.message);\n    }\n  }\n\n  function loadOrderProgress(orderId: string) {\n    setSelectedOrder(orderId);\n  }\n\n  return (\n    <div style={{\n      minHeight: '100vh',\n      background: 'linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%)',\n      padding: '32px',\n      fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n    }}>\n      <div style={{ maxWidth: '1400px', margin: '0 auto' }}>\n        {/* Header */}\n        <div style={{\n          display: 'flex',\n          justifyContent: 'space-between',\n          alignItems: 'center',\n          marginBottom: '32px'\n        }}>\n          <div>\n            <h1 style={{\n              fontSize: '28px',\n              fontWeight: '700',\n              color: '#0f172a',\n              margin: 0,\n              letterSpacing: '-0.025em'\n            }}>\n              Production Planning\n            </h1>\n            <p style={{ color: '#64748b', margin: '4px 0 0 0', fontSize: '14px' }}>\n              Order and manufacturing tracking\n            </p>\n          </div>\n          <div style={{\n            display: 'flex',\n            alignItems: 'center',\n            gap: '8px',\n            padding: '8px 16px',\n            background: 'white',\n            borderRadius: '12px',\n            boxShadow: '0 1px 3px rgba(0,0,0,0.08)'\n          }}>\n            <div style={{\n              width: '8px',\n              height: '8px',\n              borderRadius: '50%',\n              background: '#22c55e',\n              animation: 'pulse 2s infinite'\n            }} />\n            <span style={{ fontSize: '13px', color: '#64748b', fontWeight: '500' }}>Demo App</span>\n          </div>\n        </div>\n\n        {/* Controls Bar */}\n        <div style={{\n          display: 'flex',\n          gap: '12px',\n          marginBottom: '24px',\n          flexWrap: 'wrap',\n          alignItems: 'center'\n        }}>\n          <button\n            onClick={() => setShowNewForm(!showNewForm)}\n            style={{\n              padding: '12px 24px',\n              background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',\n              color: 'white',\n              border: 'none',\n              borderRadius: '12px',\n              cursor: 'pointer',\n              fontWeight: '600',\n              fontSize: '14px',\n              boxShadow: '0 4px 12px rgba(59, 130, 246, 0.35)',\n              transition: 'all 0.2s ease',\n              display: 'flex',\n              alignItems: 'center',\n              gap: '8px'\n            }}\n          >\n            <svg width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" viewBox=\"0 0 24 24\">\n              <path d=\"M12 5v14M5 12h14\" strokeLinecap=\"round\"/>\n            </svg>\n            New Order\n          </button>\n\n          <div style={{\n            display: 'flex',\n            background: 'white',\n            borderRadius: '12px',\n            padding: '4px',\n            boxShadow: '0 1px 3px rgba(0,0,0,0.08)'\n          }}>\n            {[\n              { value: 'all', label: 'All' },\n              { value: 'pending', label: 'Pending' },\n              { value: 'in_progress', label: 'In Progress' },\n              { value: 'completed', label: 'Completed' }\n            ].map(opt => (\n              <button\n                key={opt.value}\n                onClick={() => setFilterStatus(opt.value)}\n                style={{\n                  padding: '8px 16px',\n                  background: filterStatus === opt.value ? '#f1f5f9' : 'transparent',\n                  border: 'none',\n                  borderRadius: '8px',\n                  cursor: 'pointer',\n                  fontWeight: filterStatus === opt.value ? '600' : '500',\n                  fontSize: '13px',\n                  color: filterStatus === opt.value ? '#0f172a' : '#64748b',\n                  transition: 'all 0.15s ease'\n                }}\n              >\n                {opt.label}\n              </button>\n            ))}\n          </div>\n\n          <div style={{ marginLeft: 'auto', fontSize: '13px', color: '#64748b' }}>\n            {filteredOrders.length} order{filteredOrders.length !== 1 ? 's' : ''}\n          </div>\n        </div>\n\n        {/* New Order Form */}\n        {showNewForm && (\n          <div style={{\n            background: 'white',\n            borderRadius: '16px',\n            padding: '24px',\n            marginBottom: '24px',\n            boxShadow: '0 4px 20px rgba(0,0,0,0.08)',\n            border: '1px solid #e2e8f0'\n          }}>\n            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>\n              <h3 style={{ margin: 0, fontSize: '16px', fontWeight: '600', color: '#0f172a' }}>\n                Create New Order\n              </h3>\n              <button\n                onClick={() => setShowNewForm(false)}\n                style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '4px' }}\n              >\n                <svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"#94a3b8\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n                  <path d=\"M6 18L18 6M6 6l12 12\" strokeLinecap=\"round\"/>\n                </svg>\n              </button>\n            </div>\n            <form onSubmit={handleCreateOrder}>\n              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '16px' }}>\n                <div>\n                  <label style={{ display: 'block', fontSize: '12px', fontWeight: '600', color: '#475569', marginBottom: '6px', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n                    Order # *\n                  </label>\n                  <input\n                    placeholder=\"e.g. FLD-2024-001\"\n                    value={newOrder.id}\n                    onChange={(e) => setNewOrder({ ...newOrder, id: e.target.value })}\n                    required\n                    style={{\n                      width: '100%',\n                      padding: '12px 14px',\n                      borderRadius: '10px',\n                      border: '1px solid #e2e8f0',\n                      fontSize: '14px',\n                      transition: 'all 0.15s ease',\n                      outline: 'none',\n                      boxSizing: 'border-box'\n                    }}\n                  />\n                </div>\n                <div>\n                  <label style={{ display: 'block', fontSize: '12px', fontWeight: '600', color: '#475569', marginBottom: '6px', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n                    Client *\n                  </label>\n                  <input\n                    placeholder=\"Client name\"\n                    value={newOrder.client_name}\n                    onChange={(e) => setNewOrder({ ...newOrder, client_name: e.target.value })}\n                    required\n                    style={{\n                      width: '100%',\n                      padding: '12px 14px',\n                      borderRadius: '10px',\n                      border: '1px solid #e2e8f0',\n                      fontSize: '14px',\n                      outline: 'none',\n                      boxSizing: 'border-box'\n                    }}\n                  />\n                </div>\n                <div>\n                  <label style={{ display: 'block', fontSize: '12px', fontWeight: '600', color: '#475569', marginBottom: '6px', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n                    Description\n                  </label>\n                  <input\n                    placeholder=\"Product description\"\n                    value={newOrder.product_description}\n                    onChange={(e) => setNewOrder({ ...newOrder, product_description: e.target.value })}\n                    style={{\n                      width: '100%',\n                      padding: '12px 14px',\n                      borderRadius: '10px',\n                      border: '1px solid #e2e8f0',\n                      fontSize: '14px',\n                      outline: 'none',\n                      boxSizing: 'border-box'\n                    }}\n                  />\n                </div>\n                <div>\n                  <label style={{ display: 'block', fontSize: '12px', fontWeight: '600', color: '#475569', marginBottom: '6px', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n                    Quantity\n                  </label>\n                  <input\n                    type=\"number\"\n                    value={newOrder.quantity}\n                    onChange={(e) => setNewOrder({ ...newOrder, quantity: parseInt(e.target.value) || 1 })}\n                    min={1}\n                    style={{\n                      width: '100%',\n                      padding: '12px 14px',\n                      borderRadius: '10px',\n                      border: '1px solid #e2e8f0',\n                      fontSize: '14px',\n                      outline: 'none',\n                      boxSizing: 'border-box'\n                    }}\n                  />\n                </div>\n                <div>\n                  <label style={{ display: 'block', fontSize: '12px', fontWeight: '600', color: '#475569', marginBottom: '6px', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n                    Priority\n                  </label>\n                  <select\n                    value={newOrder.priority}\n                    onChange={(e) => setNewOrder({ ...newOrder, priority: e.target.value })}\n                    style={{\n                      width: '100%',\n                      padding: '12px 14px',\n                      borderRadius: '10px',\n                      border: '1px solid #e2e8f0',\n                      fontSize: '14px',\n                      outline: 'none',\n                      boxSizing: 'border-box',\n                      background: 'white',\n                      cursor: 'pointer'\n                    }}\n                  >\n                    <option value=\"low\">Low</option>\n                    <option value=\"normal\">Normal</option>\n                    <option value=\"high\">High</option>\n                    <option value=\"urgent\">Urgent</option>\n                  </select>\n                </div>\n                <div>\n                  <label style={{ display: 'block', fontSize: '12px', fontWeight: '600', color: '#475569', marginBottom: '6px', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n                    Due Date\n                  </label>\n                  <input\n                    type=\"date\"\n                    value={newOrder.due_date}\n                    onChange={(e) => setNewOrder({ ...newOrder, due_date: e.target.value })}\n                    style={{\n                      width: '100%',\n                      padding: '12px 14px',\n                      borderRadius: '10px',\n                      border: '1px solid #e2e8f0',\n                      fontSize: '14px',\n                      outline: 'none',\n                      boxSizing: 'border-box'\n                    }}\n                  />\n                </div>\n              </div>\n              <div style={{ marginTop: '20px', display: 'flex', gap: '12px' }}>\n                <button\n                  type=\"submit\"\n                  style={{\n                    padding: '12px 28px',\n                    background: 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)',\n                    color: 'white',\n                    border: 'none',\n                    borderRadius: '10px',\n                    cursor: 'pointer',\n                    fontWeight: '600',\n                    fontSize: '14px',\n                    boxShadow: '0 4px 12px rgba(34, 197, 94, 0.35)'\n                  }}\n                >\n                  Create Order\n                </button>\n                <button\n                  type=\"button\"\n                  onClick={() => setShowNewForm(false)}\n                  style={{\n                    padding: '12px 28px',\n                    background: '#f1f5f9',\n                    color: '#475569',\n                    border: 'none',\n                    borderRadius: '10px',\n                    cursor: 'pointer',\n                    fontWeight: '600',\n                    fontSize: '14px'\n                  }}\n                >\n                  Cancel\n                </button>\n              </div>\n            </form>\n          </div>\n        )}\n\n        {/* Main Content Grid */}\n        <div style={{\n          display: 'grid',\n          gridTemplateColumns: selectedOrder ? '1fr 1fr' : '1fr',\n          gap: '24px',\n          alignItems: 'start'\n        }}>\n          {/* Orders List */}\n          <div>\n            {loading ? (\n              <div style={{\n                background: 'white',\n                borderRadius: '16px',\n                padding: '60px 24px',\n                textAlign: 'center',\n                boxShadow: '0 1px 3px rgba(0,0,0,0.08)'\n              }}>\n                <div style={{\n                  width: '40px',\n                  height: '40px',\n                  border: '3px solid #e2e8f0',\n                  borderTopColor: '#3b82f6',\n                  borderRadius: '50%',\n                  margin: '0 auto 16px',\n                  animation: 'spin 1s linear infinite'\n                }} />\n                <p style={{ color: '#64748b', margin: 0 }}>Loading...</p>\n              </div>\n            ) : filteredOrders.length === 0 ? (\n              <div style={{\n                background: 'white',\n                borderRadius: '16px',\n                padding: '60px 24px',\n                textAlign: 'center',\n                boxShadow: '0 1px 3px rgba(0,0,0,0.08)'\n              }}>\n                <div style={{\n                  width: '64px',\n                  height: '64px',\n                  background: '#f1f5f9',\n                  borderRadius: '16px',\n                  display: 'flex',\n                  alignItems: 'center',\n                  justifyContent: 'center',\n                  margin: '0 auto 16px'\n                }}>\n                  <svg width=\"28\" height=\"28\" fill=\"none\" stroke=\"#94a3b8\" strokeWidth=\"1.5\" viewBox=\"0 0 24 24\">\n                    <path d=\"M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n                  </svg>\n                </div>\n                <p style={{ color: '#64748b', margin: 0, fontSize: '15px' }}>No orders found</p>\n                <p style={{ color: '#94a3b8', margin: '8px 0 0', fontSize: '13px' }}>\n                  {filterStatus !== 'all' ? 'Try selecting a different status filter or ' : ''}Create your first order\n                </p>\n              </div>\n            ) : (\n              <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>\n                {filteredOrders.map((order) => {\n                  const priorityConf = PRIORITY_CONFIG[order.priority] || PRIORITY_CONFIG.normal;\n                  const statusConf = STATUS_CONFIG[order.status] || STATUS_CONFIG.pending;\n                  const isSelected = selectedOrder === order.id;\n\n                  return (\n                    <div\n                      key={order.id}\n                      onClick={() => loadOrderProgress(order.id)}\n                      style={{\n                        padding: '20px',\n                        background: isSelected ? '#f8fafc' : 'white',\n                        border: isSelected ? '2px solid #3b82f6' : '1px solid #e2e8f0',\n                        borderRadius: '14px',\n                        cursor: 'pointer',\n                        transition: 'all 0.2s ease',\n                        boxShadow: isSelected ? '0 4px 20px rgba(59, 130, 246, 0.15)' : '0 1px 3px rgba(0,0,0,0.05)'\n                      }}\n                    >\n                      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '12px' }}>\n                        <div>\n                          <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>\n                            <span style={{\n                              fontSize: '16px',\n                              fontWeight: '700',\n                              color: '#0f172a'\n                            }}>\n                              {order.id}\n                            </span>\n                            <span style={{\n                              padding: '3px 8px',\n                              borderRadius: '6px',\n                              fontSize: '11px',\n                              fontWeight: '600',\n                              background: priorityConf.bg,\n                              color: priorityConf.color,\n                              textTransform: 'uppercase',\n                              letterSpacing: '0.03em'\n                            }}>\n                              {priorityConf.label}\n                            </span>\n                          </div>\n                          <div style={{ color: '#475569', marginTop: '4px', fontSize: '14px', fontWeight: '500' }}>\n                            {order.client_name}\n                          </div>\n                        </div>\n                        <span style={{\n                          padding: '6px 12px',\n                          borderRadius: '8px',\n                          fontSize: '12px',\n                          fontWeight: '600',\n                          background: statusConf.bg,\n                          color: statusConf.color\n                        }}>\n                          {statusConf.label}\n                        </span>\n                      </div>\n\n                      {/* Mini Progress */}\n                      <div style={{ marginBottom: '12px' }}>\n                        <div style={{ display: 'flex', gap: '4px' }}>\n                          {[1, 2, 3, 4, 5, 6, 7].map(step => (\n                            <div\n                              key={step}\n                              style={{\n                                flex: 1,\n                                height: '4px',\n                                borderRadius: '2px',\n                                background: step <= order.current_step\n                                  ? (order.status === 'completed' ? '#22c55e' : '#3b82f6')\n                                  : '#e2e8f0'\n                              }}\n                            />\n                          ))}\n                        </div>\n                      </div>\n\n                      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n                        <span style={{ fontSize: '13px', color: '#64748b' }}>\n                          {order.current_step === 0 ? 'Not started' : `Step ${order.current_step}/7 - ${order.current_step_name}`}\n                        </span>\n                        {order.due_date && (\n                          <span style={{\n                            fontSize: '12px',\n                            color: new Date(order.due_date) < new Date() && order.status !== 'completed' ? '#dc2626' : '#64748b',\n                            display: 'flex',\n                            alignItems: 'center',\n                            gap: '4px'\n                          }}>\n                            <svg width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n                              <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/>\n                              <line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/>\n                              <line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/>\n                            </svg>\n                            {new Date(order.due_date).toLocaleDateString('en-US')}\n                          </span>\n                        )}\n                      </div>\n                    </div>\n                  );\n                })}\n              </div>\n            )}\n          </div>\n\n          {/* Order Detail Panel */}\n          {selectedOrder && orderProgress && (\n            <div style={{\n              background: 'white',\n              borderRadius: '16px',\n              boxShadow: '0 4px 20px rgba(0,0,0,0.08)',\n              border: '1px solid #e2e8f0',\n              overflow: 'hidden',\n              position: 'sticky',\n              top: '32px'\n            }}>\n              {/* Header */}\n              <div style={{\n                padding: '20px 24px',\n                borderBottom: '1px solid #e2e8f0',\n                display: 'flex',\n                justifyContent: 'space-between',\n                alignItems: 'center',\n                background: 'linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%)'\n              }}>\n                <div>\n                  <h2 style={{ margin: 0, fontSize: '18px', fontWeight: '700', color: '#0f172a' }}>\n                    {orderProgress.order.id}\n                  </h2>\n                  <p style={{ margin: '4px 0 0', fontSize: '14px', color: '#64748b' }}>\n                    {orderProgress.order.client_name}\n                  </p>\n                </div>\n                <button\n                  onClick={() => setSelectedOrder(null)}\n                  style={{\n                    background: 'white',\n                    border: '1px solid #e2e8f0',\n                    borderRadius: '8px',\n                    padding: '8px',\n                    cursor: 'pointer',\n                    display: 'flex',\n                    alignItems: 'center',\n                    justifyContent: 'center'\n                  }}\n                >\n                  <svg width=\"16\" height=\"16\" fill=\"none\" stroke=\"#64748b\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n                    <path d=\"M6 18L18 6M6 6l12 12\" strokeLinecap=\"round\"/>\n                  </svg>\n                </button>\n              </div>\n\n              {/* Progress Circle */}\n              <div style={{ padding: '24px', textAlign: 'center', borderBottom: '1px solid #e2e8f0' }}>\n                <div style={{ position: 'relative', width: '120px', height: '120px', margin: '0 auto' }}>\n                  <svg width=\"120\" height=\"120\" style={{ transform: 'rotate(-90deg)' }}>\n                    <circle cx=\"60\" cy=\"60\" r=\"52\" fill=\"none\" stroke=\"#e2e8f0\" strokeWidth=\"12\"/>\n                    <circle\n                      cx=\"60\" cy=\"60\" r=\"52\" fill=\"none\"\n                      stroke={orderProgress.progress.percent === 100 ? '#22c55e' : '#3b82f6'}\n                      strokeWidth=\"12\"\n                      strokeDasharray={`${(orderProgress.progress.percent / 100) * 327} 327`}\n                      strokeLinecap=\"round\"\n                      style={{ transition: 'stroke-dasharray 0.5s ease' }}\n                    />\n                  </svg>\n                  <div style={{\n                    position: 'absolute',\n                    top: '50%',\n                    left: '50%',\n                    transform: 'translate(-50%, -50%)',\n                    textAlign: 'center'\n                  }}>\n                    <div style={{ fontSize: '28px', fontWeight: '700', color: '#0f172a' }}>\n                      {orderProgress.progress.percent}%\n                    </div>\n                    <div style={{ fontSize: '12px', color: '#64748b' }}>\n                      {orderProgress.progress.completed_steps}/7 steps\n                    </div>\n                  </div>\n                </div>\n              </div>\n\n              {/* Steps List */}\n              <div style={{ padding: '24px' }}>\n                <h3 style={{\n                  margin: '0 0 20px',\n                  fontSize: '11px',\n                  fontWeight: '600',\n                  color: '#94a3b8',\n                  textTransform: 'uppercase',\n                  letterSpacing: '0.1em'\n                }}>\n                  Steps\n                </h3>\n                <div style={{ display: 'flex', flexDirection: 'column', gap: '2px' }}>\n                  {orderProgress.steps.map((step: any, index: number) => {\n                    const isCompleted = step.status === 'completed';\n                    const isInProgress = step.status === 'in_progress';\n                    const isPending = step.status === 'pending';\n                    const canStart = isPending && (index === 0 || orderProgress.steps[index - 1]?.status === 'completed');\n\n                    return (\n                      <div\n                        key={step.step_number}\n                        style={{\n                          padding: '16px 0',\n                          borderBottom: index < orderProgress.steps.length - 1 ? '1px solid #f1f5f9' : 'none'\n                        }}\n                      >\n                        <div style={{ display: 'flex', alignItems: 'flex-start', gap: '16px' }}>\n                          {/* Step Number */}\n                          <div style={{\n                            width: '24px',\n                            height: '24px',\n                            borderRadius: '50%',\n                            background: isCompleted ? '#0f172a' : isInProgress ? '#0f172a' : '#e2e8f0',\n                            display: 'flex',\n                            alignItems: 'center',\n                            justifyContent: 'center',\n                            flexShrink: 0,\n                            marginTop: '2px'\n                          }}>\n                            {isCompleted ? (\n                              <svg width=\"12\" height=\"12\" fill=\"none\" stroke=\"white\" strokeWidth=\"2.5\" viewBox=\"0 0 24 24\">\n                                <path d=\"M5 13l4 4L19 7\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n                              </svg>\n                            ) : (\n                              <span style={{ fontSize: '11px', fontWeight: '600', color: isInProgress ? 'white' : '#94a3b8' }}>\n                                {step.step_number}\n                              </span>\n                            )}\n                          </div>\n\n                          {/* Step Content */}\n                          <div style={{ flex: 1, minWidth: 0 }}>\n                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '12px' }}>\n                              <span style={{\n                                fontSize: '14px',\n                                fontWeight: isInProgress ? '600' : '500',\n                                color: isPending ? '#94a3b8' : '#0f172a'\n                              }}>\n                                {step.step_name}\n                              </span>\n\n                              {/* Action Button */}\n                              {canStart && (\n                                <button\n                                  onClick={(e) => { e.stopPropagation(); openStartModal(step.step_number); }}\n                                  style={{\n                                    padding: '6px 12px',\n                                    background: '#0f172a',\n                                    color: 'white',\n                                    border: 'none',\n                                    borderRadius: '6px',\n                                    cursor: 'pointer',\n                                    fontWeight: '500',\n                                    fontSize: '12px'\n                                  }}\n                                >\n                                  Start\n                                </button>\n                              )}\n                              {isInProgress && (\n                                <button\n                                  onClick={(e) => { e.stopPropagation(); openCompleteModal(step.step_number); }}\n                                  style={{\n                                    padding: '6px 12px',\n                                    background: '#0f172a',\n                                    color: 'white',\n                                    border: 'none',\n                                    borderRadius: '6px',\n                                    cursor: 'pointer',\n                                    fontWeight: '500',\n                                    fontSize: '12px'\n                                  }}\n                                >\n                                  Done\n                                </button>\n                              )}\n                            </div>\n\n                            {/* Meta info */}\n                            {(isCompleted || isInProgress) && (step.operator_id || step.actual_duration_min != null) && (\n                              <div style={{\n                                marginTop: '6px',\n                                fontSize: '12px',\n                                color: '#64748b',\n                                display: 'flex',\n                                gap: '12px'\n                              }}>\n                                {step.operator_id && (\n                                  <span>{step.operator_id}</span>\n                                )}\n                                {isCompleted && step.actual_duration_min != null && (\n                                  <span>\n                                    {step.actual_duration_min} min\n                                    {step.actual_duration_min > step.theoretical_duration_min && (\n                                      <span style={{ color: '#f59e0b' }}> (+{step.actual_duration_min - step.theoretical_duration_min})</span>\n                                    )}\n                                  </span>\n                                )}\n                              </div>\n                            )}\n\n                            {/* Notes */}\n                            {isCompleted && step.notes && (\n                              <div style={{\n                                marginTop: '8px',\n                                fontSize: '12px',\n                                color: '#64748b',\n                                fontStyle: 'italic'\n                              }}>\n                                {step.notes}\n                              </div>\n                            )}\n                          </div>\n                        </div>\n                      </div>\n                    );\n                  })}\n                </div>\n              </div>\n\n              {/* Stats */}\n              {orderProgress.progress.total_actual_min > 0 && (\n                <div style={{\n                  padding: '20px 24px',\n                  borderTop: '1px solid #e2e8f0',\n                  background: '#f8fafc'\n                }}>\n                  <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px', textAlign: 'center' }}>\n                    <div>\n                      <div style={{ fontSize: '11px', fontWeight: '600', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n                        Actual Time\n                      </div>\n                      <div style={{ fontSize: '20px', fontWeight: '700', color: '#0f172a', marginTop: '4px' }}>\n                        {orderProgress.progress.total_actual_min}<span style={{ fontSize: '12px', fontWeight: '500', color: '#64748b' }}>min</span>\n                      </div>\n                    </div>\n                    <div>\n                      <div style={{ fontSize: '11px', fontWeight: '600', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n                        Expected\n                      </div>\n                      <div style={{ fontSize: '20px', fontWeight: '700', color: '#0f172a', marginTop: '4px' }}>\n                        {orderProgress.progress.total_theoretical_min}<span style={{ fontSize: '12px', fontWeight: '500', color: '#64748b' }}>min</span>\n                      </div>\n                    </div>\n                    <div>\n                      <div style={{ fontSize: '11px', fontWeight: '600', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n                        Efficiency\n                      </div>\n                      <div style={{\n                        fontSize: '20px',\n                        fontWeight: '700',\n                        marginTop: '4px',\n                        color: orderProgress.progress.efficiency >= 100 ? '#22c55e' : orderProgress.progress.efficiency >= 80 ? '#f59e0b' : '#ef4444'\n                      }}>\n                        {orderProgress.progress.efficiency || '—'}<span style={{ fontSize: '12px', fontWeight: '500' }}>%</span>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              )}\n            </div>\n          )}\n        </div>\n      </div>\n\n      {/* Start Step Modal */}\n      {showStartModal && (\n        <div style={{\n          position: 'fixed',\n          inset: 0,\n          background: 'rgba(0, 0, 0, 0.5)',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n          zIndex: 1000,\n          backdropFilter: 'blur(4px)'\n        }}>\n          <div style={{\n            background: 'white',\n            borderRadius: '20px',\n            padding: '32px',\n            width: '100%',\n            maxWidth: '400px',\n            boxShadow: '0 25px 50px rgba(0, 0, 0, 0.25)',\n            animation: 'modalIn 0.2s ease-out'\n          }}>\n            <div style={{ textAlign: 'center', marginBottom: '24px' }}>\n              <div style={{\n                width: '56px',\n                height: '56px',\n                background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',\n                borderRadius: '16px',\n                display: 'flex',\n                alignItems: 'center',\n                justifyContent: 'center',\n                margin: '0 auto 16px'\n              }}>\n                <svg width=\"28\" height=\"28\" fill=\"none\" stroke=\"white\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n                  <path d=\"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n                  <path d=\"M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n                </svg>\n              </div>\n              <h3 style={{ margin: 0, fontSize: '20px', fontWeight: '700', color: '#0f172a' }}>\n                Start Step {modalStepNumber}\n              </h3>\n              <p style={{ margin: '8px 0 0', fontSize: '14px', color: '#64748b' }}>\n                Enter operator ID to begin this step\n              </p>\n            </div>\n\n            <div style={{ marginBottom: '24px' }}>\n              <label style={{\n                display: 'block',\n                fontSize: '12px',\n                fontWeight: '600',\n                color: '#475569',\n                marginBottom: '8px',\n                textTransform: 'uppercase',\n                letterSpacing: '0.05em'\n              }}>\n                Operator ID *\n              </label>\n              <input\n                type=\"text\"\n                value={operatorId}\n                onChange={(e) => setOperatorId(e.target.value)}\n                placeholder=\"Enter your ID\"\n                autoFocus\n                style={{\n                  width: '100%',\n                  padding: '14px 16px',\n                  borderRadius: '12px',\n                  border: '2px solid #e2e8f0',\n                  fontSize: '16px',\n                  outline: 'none',\n                  boxSizing: 'border-box',\n                  transition: 'border-color 0.15s ease'\n                }}\n                onKeyDown={(e) => e.key === 'Enter' && operatorId.trim() && handleStartStep()}\n              />\n            </div>\n\n            <div style={{ display: 'flex', gap: '12px' }}>\n              <button\n                onClick={() => setShowStartModal(false)}\n                style={{\n                  flex: 1,\n                  padding: '14px 24px',\n                  background: '#f1f5f9',\n                  color: '#475569',\n                  border: 'none',\n                  borderRadius: '12px',\n                  cursor: 'pointer',\n                  fontWeight: '600',\n                  fontSize: '14px'\n                }}\n              >\n                Cancel\n              </button>\n              <button\n                onClick={handleStartStep}\n                disabled={!operatorId.trim()}\n                style={{\n                  flex: 1,\n                  padding: '14px 24px',\n                  background: operatorId.trim() ? 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)' : '#e2e8f0',\n                  color: operatorId.trim() ? 'white' : '#94a3b8',\n                  border: 'none',\n                  borderRadius: '12px',\n                  cursor: operatorId.trim() ? 'pointer' : 'not-allowed',\n                  fontWeight: '600',\n                  fontSize: '14px',\n                  boxShadow: operatorId.trim() ? '0 4px 12px rgba(59, 130, 246, 0.35)' : 'none'\n                }}\n              >\n                Start Step\n              </button>\n            </div>\n          </div>\n        </div>\n      )}\n\n      {/* Complete Step Modal */}\n      {showCompleteModal && (\n        <div style={{\n          position: 'fixed',\n          inset: 0,\n          background: 'rgba(0, 0, 0, 0.5)',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n          zIndex: 1000,\n          backdropFilter: 'blur(4px)'\n        }}>\n          <div style={{\n            background: 'white',\n            borderRadius: '20px',\n            padding: '32px',\n            width: '100%',\n            maxWidth: '400px',\n            boxShadow: '0 25px 50px rgba(0, 0, 0, 0.25)',\n            animation: 'modalIn 0.2s ease-out'\n          }}>\n            <div style={{ textAlign: 'center', marginBottom: '24px' }}>\n              <div style={{\n                width: '56px',\n                height: '56px',\n                background: 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)',\n                borderRadius: '16px',\n                display: 'flex',\n                alignItems: 'center',\n                justifyContent: 'center',\n                margin: '0 auto 16px'\n              }}>\n                <svg width=\"28\" height=\"28\" fill=\"none\" stroke=\"white\" strokeWidth=\"2.5\" viewBox=\"0 0 24 24\">\n                  <path d=\"M5 13l4 4L19 7\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n                </svg>\n              </div>\n              <h3 style={{ margin: 0, fontSize: '20px', fontWeight: '700', color: '#0f172a' }}>\n                Complete Step {modalStepNumber}\n              </h3>\n              <p style={{ margin: '8px 0 0', fontSize: '14px', color: '#64748b' }}>\n                Add any notes before completing\n              </p>\n            </div>\n\n            <div style={{ marginBottom: '24px' }}>\n              <label style={{\n                display: 'block',\n                fontSize: '12px',\n                fontWeight: '600',\n                color: '#475569',\n                marginBottom: '8px',\n                textTransform: 'uppercase',\n                letterSpacing: '0.05em'\n              }}>\n                Notes (optional)\n              </label>\n              <textarea\n                value={completionNotes}\n                onChange={(e) => setCompletionNotes(e.target.value)}\n                placeholder=\"Any issues, delays, or comments...\"\n                rows={3}\n                autoFocus\n                style={{\n                  width: '100%',\n                  padding: '14px 16px',\n                  borderRadius: '12px',\n                  border: '2px solid #e2e8f0',\n                  fontSize: '14px',\n                  outline: 'none',\n                  boxSizing: 'border-box',\n                  resize: 'none',\n                  fontFamily: 'inherit',\n                  transition: 'border-color 0.15s ease'\n                }}\n              />\n            </div>\n\n            <div style={{ display: 'flex', gap: '12px' }}>\n              <button\n                onClick={() => setShowCompleteModal(false)}\n                style={{\n                  flex: 1,\n                  padding: '14px 24px',\n                  background: '#f1f5f9',\n                  color: '#475569',\n                  border: 'none',\n                  borderRadius: '12px',\n                  cursor: 'pointer',\n                  fontWeight: '600',\n                  fontSize: '14px'\n                }}\n              >\n                Cancel\n              </button>\n              <button\n                onClick={handleCompleteStep}\n                style={{\n                  flex: 1,\n                  padding: '14px 24px',\n                  background: 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)',\n                  color: 'white',\n                  border: 'none',\n                  borderRadius: '12px',\n                  cursor: 'pointer',\n                  fontWeight: '600',\n                  fontSize: '14px',\n                  boxShadow: '0 4px 12px rgba(34, 197, 94, 0.35)'\n                }}\n              >\n                Complete Step\n              </button>\n            </div>\n          </div>\n        </div>\n      )}\n\n      {/* CSS Animations */}\n      <style>{`\n        @keyframes spin {\n          to { transform: rotate(360deg); }\n        }\n        @keyframes pulse {\n          0%, 100% { opacity: 1; }\n          50% { opacity: 0.5; }\n        }\n        @keyframes modalIn {\n          from {\n            opacity: 0;\n            transform: scale(0.95) translateY(-10px);\n          }\n          to {\n            opacity: 1;\n            transform: scale(1) translateY(0);\n          }\n        }\n        input:focus, select:focus, textarea:focus {\n          border-color: #3b82f6 !important;\n          box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15) !important;\n        }\n        button:hover {\n          transform: translateY(-1px);\n        }\n        button:active {\n          transform: translateY(0);\n        }\n      `}</style>\n    </div>\n  );\n};\n\nexport default App;\n","/index.css":".myclass {\n    border: 1px solid gray;\n    padding: 2px;\n}\n","/index.tsx":"import React from 'react'\n\nimport { createRoot } from 'react-dom/client'\nimport App from './App'\n\nconst root = createRoot(document.getElementById('root')!);\nroot.render(<App/>);\n","/package.json":"{\n    \"dependencies\": {\n        \"react\": \"19.0.0\",\n        \"react-dom\": \"19.0.0\",\n        \"windmill-client\": \"^1\"\n    },\n    \"devDependencies\": {\n        \"@types/react-dom\": \"^19.0.0\",\n        \"@types/react\": \"^19.0.0\"\n    }\n}\n"},"data":{"schema":"assemblyline","tables":["main/assemblyline:steps","main/assemblyline:orders","main/assemblyline:step_logs"],"datatable":"main"}},"description":"A production planner app for managing assembly line operations. Track each step of item production, assign operators to tasks, and monitor progress in real-time. Built with Windmill scripts for database operations.","vcreated_at":"2026-02-25T09:19:39.298Z","vcreated_by":"hugo989","comments":[]}}