Fix asset list filters: MAKE→manufacturer, populate customers, expand search, fix assigned_to type
This commit is contained in:
Binary file not shown.
Binary file not shown.
+56
-1
@@ -311,6 +311,61 @@ def get_machine_id(rec):
|
|||||||
return str(mid).strip()
|
return str(mid).strip()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def _get_category_for_manufacturer(manufacturer: str) -> str:
|
||||||
|
"""Map manufacturer to asset category for new imports."""
|
||||||
|
if not manufacturer:
|
||||||
|
return "Other"
|
||||||
|
m = manufacturer.strip().upper()
|
||||||
|
|
||||||
|
# Vending Machine
|
||||||
|
for prefix in ["DIXIE NARCO", "NATIONAL", "ROYAL", "VENDO", "AUTOMATIC PRODUCTS",
|
||||||
|
"CRANE", "USI", "WITTERN", "AMS", "ROWE", "SEAGA", "LEKTRO-VEND",
|
||||||
|
"ROYAL VENDORS"]:
|
||||||
|
if m.startswith(prefix):
|
||||||
|
return "Vending Machine"
|
||||||
|
|
||||||
|
# Coffee Brewer
|
||||||
|
for prefix in ["FLAVIA", "KEURIG", "NEWCO", "BUNN", "CAFECTION", "NESPRESSO",
|
||||||
|
"CURTIS", "BRAVILOR", "SMUCKER", "BLOOMFIELD", "STARBUCKS",
|
||||||
|
"AQUACAFE", "SEGAFREDO", "SIMONELLI", "WITTENBORG", "KREA",
|
||||||
|
"TOTAL ONE",
|
||||||
|
"DEJONG", "DE JONG", "DE JOHN"]:
|
||||||
|
if m.startswith(prefix):
|
||||||
|
return "Coffee Brewer"
|
||||||
|
|
||||||
|
# Cooler / Refrigerator
|
||||||
|
for prefix in ["MINUS FORTY", "IMBERA", "ALPINE", "COOLSERV", "VITRIFRIGO",
|
||||||
|
"KOOLTEK", "MTN", "CELSIUS", "NESTLE", "COLD SNAP", "MONSTER",
|
||||||
|
"COKE", "PEPSI", "INTERPURE", "EVERPURE"]:
|
||||||
|
if m.startswith(prefix):
|
||||||
|
return "Cooler"
|
||||||
|
|
||||||
|
# Water Dispenser
|
||||||
|
for prefix in ["WATERLOGIC", "OASIS", "ECOSTREAM", "ECHOSTREAM", "ION",
|
||||||
|
"PURLOGIX", "PURELOGIX", "AVALON", "WELLSYS", "MARCO",
|
||||||
|
"SC WATER"]:
|
||||||
|
if m.startswith(prefix):
|
||||||
|
return "Water Dispenser"
|
||||||
|
|
||||||
|
# Smart Market Kiosk
|
||||||
|
for prefix in ["365 RETAIL MARKETS", "PANOPTYC", "INTUITIVO", "SMART MARKET",
|
||||||
|
"MICRO-MARKET", "OPTCONNECT", "INSTANT SYSTEMS"]:
|
||||||
|
if m.startswith(prefix):
|
||||||
|
return "Smart Market Kiosk"
|
||||||
|
|
||||||
|
# Ice Machine
|
||||||
|
for prefix in ["FOLLETT", "HOSHIZAKI", "EVERSYS"]:
|
||||||
|
if m.startswith(prefix):
|
||||||
|
return "Ice Machine"
|
||||||
|
|
||||||
|
# Beverage Dispenser
|
||||||
|
for prefix in ["BEVI"]:
|
||||||
|
if m.startswith(prefix):
|
||||||
|
return "Beverage Dispenser"
|
||||||
|
|
||||||
|
return "Other"
|
||||||
|
|
||||||
|
|
||||||
def map_record_to_asset(rec):
|
def map_record_to_asset(rec):
|
||||||
"""
|
"""
|
||||||
Map a merged record's fields to the assets table columns.
|
Map a merged record's fields to the assets table columns.
|
||||||
@@ -321,7 +376,7 @@ def map_record_to_asset(rec):
|
|||||||
name = rec.get("msfs_name") or rec.get("canteen_name") or ""
|
name = rec.get("msfs_name") or rec.get("canteen_name") or ""
|
||||||
|
|
||||||
# Category / status
|
# Category / status
|
||||||
category = rec.get("canteen_category") or "Other"
|
category = rec.get("canteen_category") or _get_category_for_manufacturer(rec.get("msfs_manufacturer"))
|
||||||
status = rec.get("canteen_status") or "active"
|
status = rec.get("canteen_status") or "active"
|
||||||
|
|
||||||
# Make / model / serial
|
# Make / model / serial
|
||||||
|
|||||||
@@ -924,10 +924,10 @@ def list_categories():
|
|||||||
def list_makes():
|
def list_makes():
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
rows = conn.execute(
|
rows = conn.execute(
|
||||||
"SELECT DISTINCT make FROM assets WHERE make IS NOT NULL AND make != '' ORDER BY make"
|
"SELECT DISTINCT manufacturer FROM assets WHERE manufacturer IS NOT NULL AND manufacturer != '' ORDER BY manufacturer"
|
||||||
).fetchall()
|
).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
return [r["make"] for r in rows]
|
return [r["manufacturer"] for r in rows]
|
||||||
|
|
||||||
|
|
||||||
# ─── Task 4: POST /api/assets ──────────────────────────────────────────────
|
# ─── Task 4: POST /api/assets ──────────────────────────────────────────────
|
||||||
@@ -1062,7 +1062,7 @@ def list_assets(
|
|||||||
model: Optional[str] = Query(None),
|
model: Optional[str] = Query(None),
|
||||||
customer_id: Optional[int] = Query(None),
|
customer_id: Optional[int] = Query(None),
|
||||||
location_id: Optional[int] = Query(None),
|
location_id: Optional[int] = Query(None),
|
||||||
assigned_to: Optional[int] = Query(None),
|
assigned_to: Optional[str] = Query(None, description="Technician username to filter by"),
|
||||||
disney_park: Optional[str] = Query(None),
|
disney_park: Optional[str] = Query(None),
|
||||||
disney_filter: Optional[str] = Query(None),
|
disney_filter: Optional[str] = Query(None),
|
||||||
disney_group: Optional[str] = Query(None, description="Grouped park categories: park, resort, office, springs, other"),
|
disney_group: Optional[str] = Query(None, description="Grouped park categories: park, resort, office, springs, other"),
|
||||||
@@ -1084,7 +1084,7 @@ def list_assets(
|
|||||||
conditions.append("status = ?")
|
conditions.append("status = ?")
|
||||||
params.append(status)
|
params.append(status)
|
||||||
if make:
|
if make:
|
||||||
conditions.append("make = ?")
|
conditions.append("manufacturer = ?")
|
||||||
params.append(make)
|
params.append(make)
|
||||||
if disney_park:
|
if disney_park:
|
||||||
conditions.append("disney_park = ?")
|
conditions.append("disney_park = ?")
|
||||||
@@ -1116,12 +1116,12 @@ def list_assets(
|
|||||||
conditions.append("location_id = ?")
|
conditions.append("location_id = ?")
|
||||||
params.append(location_id)
|
params.append(location_id)
|
||||||
if assigned_to is not None:
|
if assigned_to is not None:
|
||||||
conditions.append("assigned_to = ?")
|
conditions.append("u.username = ?")
|
||||||
params.append(assigned_to)
|
params.append(assigned_to)
|
||||||
if q:
|
if q:
|
||||||
conditions.append("(a.name LIKE ? OR a.machine_id LIKE ? OR a.serial_number LIKE ? OR a.description LIKE ? OR a.company LIKE ?)")
|
conditions.append("(a.name LIKE ? OR a.machine_id LIKE ? OR a.serial_number LIKE ? OR a.description LIKE ? OR a.company LIKE ? OR a.place LIKE ? OR a.customer_name LIKE ? OR a.address LIKE ?)")
|
||||||
like = f"%{q}%"
|
like = f"%{q}%"
|
||||||
params.extend([like, like, like, like, like])
|
params.extend([like, like, like, like, like, like, like, like])
|
||||||
if no_dex_days is not None:
|
if no_dex_days is not None:
|
||||||
conditions.append("(dex_report_date IS NULL OR REPLACE(dex_report_date, 'T', ' ') < datetime('now', '-' || ? || ' days'))")
|
conditions.append("(dex_report_date IS NULL OR REPLACE(dex_report_date, 'T', ' ') < datetime('now', '-' || ? || ' days'))")
|
||||||
params.append(no_dex_days)
|
params.append(no_dex_days)
|
||||||
@@ -1147,7 +1147,7 @@ def list_assets(
|
|||||||
conditions.append("1=0")
|
conditions.append("1=0")
|
||||||
|
|
||||||
where = " AND ".join(conditions)
|
where = " AND ".join(conditions)
|
||||||
from_clause = "FROM assets a LEFT JOIN customers c ON a.customer_id = c.id"
|
from_clause = "FROM assets a LEFT JOIN customers c ON a.customer_id = c.id LEFT JOIN users u ON a.assigned_to = u.id"
|
||||||
sql = f"SELECT a.*, COALESCE(c.name, a.customer_name) AS customer_name {from_clause}"
|
sql = f"SELECT a.*, COALESCE(c.name, a.customer_name) AS customer_name {from_clause}"
|
||||||
if where:
|
if where:
|
||||||
sql += f" WHERE {where}"
|
sql += f" WHERE {where}"
|
||||||
@@ -1170,10 +1170,23 @@ def list_assets(
|
|||||||
@app.get("/api/assets/search")
|
@app.get("/api/assets/search")
|
||||||
def search_by_machine_id(machine_id: str = Query(...)):
|
def search_by_machine_id(machine_id: str = Query(...)):
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
|
# Exact match first
|
||||||
row = conn.execute(
|
row = conn.execute(
|
||||||
"SELECT * FROM assets WHERE machine_id = ? OR connect_id = ? OR serial_number = ?",
|
"SELECT * FROM assets WHERE machine_id = ? OR connect_id = ? OR serial_number = ?",
|
||||||
(machine_id, machine_id, machine_id),
|
(machine_id, machine_id, machine_id),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
|
|
||||||
|
# If not found and value is all-numeric >= 5 chars, try LIKE suffix match
|
||||||
|
# on connect_id and equipment_id (for partial OCR reads of Connect IDs)
|
||||||
|
if row is None and len(machine_id) >= 5 and machine_id.isdigit():
|
||||||
|
row = conn.execute(
|
||||||
|
"""SELECT * FROM assets
|
||||||
|
WHERE replace(replace(replace(replace(upper(connect_id), '-', ''), '.', ''), ' ', ''), '/', '') LIKE ?
|
||||||
|
OR replace(replace(replace(replace(upper(equipment_id), '-', ''), '.', ''), ' ', ''), '/', '') LIKE ?
|
||||||
|
""",
|
||||||
|
(f"%{machine_id}", f"%{machine_id}"),
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
if row is None:
|
if row is None:
|
||||||
raise HTTPException(status_code=404, detail="Asset not found")
|
raise HTTPException(status_code=404, detail="Asset not found")
|
||||||
@@ -1452,8 +1465,12 @@ def update_asset(asset_id: int, body: AssetUpdate):
|
|||||||
updates["assigned_to"] = body.assigned_to
|
updates["assigned_to"] = body.assigned_to
|
||||||
if body.latitude is not None:
|
if body.latitude is not None:
|
||||||
updates["latitude"] = body.latitude
|
updates["latitude"] = body.latitude
|
||||||
|
elif body.latitude is None and "latitude" in body.model_fields_set:
|
||||||
|
updates["latitude"] = None
|
||||||
if body.longitude is not None:
|
if body.longitude is not None:
|
||||||
updates["longitude"] = body.longitude
|
updates["longitude"] = body.longitude
|
||||||
|
elif body.longitude is None and "longitude" in body.model_fields_set:
|
||||||
|
updates["longitude"] = None
|
||||||
if body.geofence_radius_meters is not None:
|
if body.geofence_radius_meters is not None:
|
||||||
updates["geofence_radius_meters"] = body.geofence_radius_meters
|
updates["geofence_radius_meters"] = body.geofence_radius_meters
|
||||||
if body.is_disney is not None:
|
if body.is_disney is not None:
|
||||||
@@ -3066,18 +3083,47 @@ def _solve_tsp(points, origin_idx=0):
|
|||||||
@app.get("/api/workorders/search")
|
@app.get("/api/workorders/search")
|
||||||
async def workorders_search(
|
async def workorders_search(
|
||||||
q: str = Query("", description="Search term"),
|
q: str = Query("", description="Search term"),
|
||||||
|
status: str = Query("", description="Filter by status label (e.g. 'Posted', 'Completed', 'In Progress', 'Scheduled', 'On Hold')"),
|
||||||
|
date_from: str = Query("", description="Filter by created date >= (ISO format, e.g. 2026-01-01)"),
|
||||||
|
date_to: str = Query("", description="Filter by created date <= (ISO format, e.g. 2026-06-30)"),
|
||||||
limit: int = Query(50, ge=1, le=200),
|
limit: int = Query(50, ge=1, le=200),
|
||||||
offset: int = Query(0, ge=0),
|
offset: int = Query(0, ge=0),
|
||||||
):
|
):
|
||||||
"""Search work orders by name, account, or city."""
|
"""Search work orders by name, account, or city, with optional status and date filters."""
|
||||||
conn = _get_extraction_db()
|
conn = _get_extraction_db()
|
||||||
if not conn:
|
if not conn:
|
||||||
raise HTTPException(503, "Database not available — sync extraction DB missing")
|
raise HTTPException(503, "Database not available — sync extraction DB missing")
|
||||||
try:
|
try:
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
like = f"%{q}%"
|
params: list = []
|
||||||
|
where_clauses: list = []
|
||||||
|
|
||||||
|
# Search term
|
||||||
|
like = f"%{q}%" if q else None
|
||||||
|
if like:
|
||||||
|
where_clauses.append("(w.msdyn_name LIKE ? OR a.name LIKE ? OR a.address1_city LIKE ?)")
|
||||||
|
params.extend([like, like, like])
|
||||||
|
|
||||||
|
# Status filter — map label back to code
|
||||||
|
if status:
|
||||||
|
status_label_map = {v: k for k, v in STATUS_LABELS.items()}
|
||||||
|
code = status_label_map.get(status)
|
||||||
|
if code:
|
||||||
|
where_clauses.append("w.msdyn_systemstatus = ?")
|
||||||
|
params.append(int(code))
|
||||||
|
|
||||||
|
# Date range
|
||||||
|
if date_from:
|
||||||
|
where_clauses.append("w.createdon >= ?")
|
||||||
|
params.append(date_from)
|
||||||
|
if date_to:
|
||||||
|
where_clauses.append("w.createdon <= ?")
|
||||||
|
params.append(date_to + "T23:59:59Z" if "T" not in date_to else date_to)
|
||||||
|
|
||||||
|
where_sql = " AND ".join(where_clauses) if where_clauses else "1=1"
|
||||||
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
f"""
|
||||||
SELECT
|
SELECT
|
||||||
w.msdyn_workorderid, w.msdyn_name,
|
w.msdyn_workorderid, w.msdyn_name,
|
||||||
w."msdyn_serviceaccount!name" AS account_name,
|
w."msdyn_serviceaccount!name" AS account_name,
|
||||||
@@ -3086,29 +3132,26 @@ async def workorders_search(
|
|||||||
w.hsl_onsiteduration,
|
w.hsl_onsiteduration,
|
||||||
w."msdyn_priority!name" AS priority,
|
w."msdyn_priority!name" AS priority,
|
||||||
w.msdyn_systemstatus,
|
w.msdyn_systemstatus,
|
||||||
|
w.createdon,
|
||||||
a.address1_line1, a.address1_city, a.address1_stateorprovince, a.address1_postalcode,
|
a.address1_line1, a.address1_city, a.address1_stateorprovince, a.address1_postalcode,
|
||||||
a.address1_latitude, a.address1_longitude
|
a.address1_latitude, a.address1_longitude
|
||||||
FROM msdyn_workorder w
|
FROM msdyn_workorder w
|
||||||
LEFT JOIN account a ON w."msdyn_serviceaccount!id" = a.accountid
|
LEFT JOIN account a ON w."msdyn_serviceaccount!id" = a.accountid
|
||||||
WHERE w.msdyn_name LIKE ?
|
WHERE {where_sql}
|
||||||
OR a.name LIKE ?
|
ORDER BY w.createdon DESC
|
||||||
OR a.address1_city LIKE ?
|
|
||||||
ORDER BY w.msdyn_name
|
|
||||||
LIMIT ? OFFSET ?
|
LIMIT ? OFFSET ?
|
||||||
""",
|
""",
|
||||||
(like, like, like, limit, offset),
|
(*params, limit, offset),
|
||||||
)
|
)
|
||||||
rows = cur.fetchall()
|
rows = cur.fetchall()
|
||||||
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
f"""
|
||||||
SELECT COUNT(*) FROM msdyn_workorder w
|
SELECT COUNT(*) FROM msdyn_workorder w
|
||||||
LEFT JOIN account a ON w."msdyn_serviceaccount!id" = a.accountid
|
LEFT JOIN account a ON w."msdyn_serviceaccount!id" = a.accountid
|
||||||
WHERE w.msdyn_name LIKE ?
|
WHERE {where_sql}
|
||||||
OR a.name LIKE ?
|
|
||||||
OR a.address1_city LIKE ?
|
|
||||||
""",
|
""",
|
||||||
(like, like, like),
|
params,
|
||||||
)
|
)
|
||||||
total = cur.fetchone()[0]
|
total = cur.fetchone()[0]
|
||||||
|
|
||||||
@@ -3123,6 +3166,7 @@ async def workorders_search(
|
|||||||
"status": _status_label(r["msdyn_systemstatus"]),
|
"status": _status_label(r["msdyn_systemstatus"]),
|
||||||
"priority": r["priority"],
|
"priority": r["priority"],
|
||||||
"onsite_duration": r["hsl_onsiteduration"],
|
"onsite_duration": r["hsl_onsiteduration"],
|
||||||
|
"created_on": r["createdon"],
|
||||||
"address": {
|
"address": {
|
||||||
"line1": r["address1_line1"],
|
"line1": r["address1_line1"],
|
||||||
"city": r["address1_city"],
|
"city": r["address1_city"],
|
||||||
|
|||||||
@@ -3897,6 +3897,13 @@
|
|||||||
'Markets': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 512 512"><path fill="currentColor" d="M256.18 21c-23.242 0-46.577 3.01-63.186 8.54c-8.304 2.763-14.868 6.196-18.808 9.558c-3.94 3.36-5.167 5.956-5.186 8.96L168.943 57H41v14h430V57H342.967l.033-9.033c.01-3.002-1.17-5.55-5.057-8.895s-10.407-6.773-18.677-9.535C302.724 24.014 279.42 21 256.18 21M38.277 89l-10.443 94h80.9l20.243-86.36L122.81 183h81.07l17.114-86.68l-3.096 86.68h75.75l-4.634-86.518L307.694 183h81.497l-6.167-86.36L403.266 183h80.9l-10.443-94zM25.834 201l-.51 4.588C39.822 226.445 52.968 235 64 235c11.32 0 24.852-8.89 39.8-30.96l.714-3.04zm95.687 0l-.32 4.498C135.753 226.495 148.935 235 160 235c11.293 0 24.788-8.85 39.693-30.803l.63-3.197zm95.736 0l-.156 4.352C231.69 226.455 244.908 235 256 235c11.08 0 24.28-8.525 38.85-29.576l-.237-4.424h-77.357zm94.324 0l.674 3.12c15 22.192 28.503 31.044 39.773 31.013c11.03-.03 24.212-8.62 38.772-29.637l-.32-4.496zm95.906 0l.713 3.04C423.147 226.11 436.68 235 448 235c11.032 0 24.178-8.555 38.676-29.412l-.51-4.588h-78.68zM112 223.31C97.313 242.11 81.492 253 64 253c-13.972 0-26.884-6.906-39-19.264V487h318V279h114v208h30V233.736C474.884 246.094 461.972 253 448 253c-17.49 0-33.31-10.888-47.996-29.684c-14.664 18.808-30.432 29.77-47.926 29.817c-17.508.048-33.352-10.87-48.092-29.807C289.303 242.116 273.486 253 256 253c-17.492 0-33.313-10.89-48-29.69c-14.687 18.8-30.508 29.69-48 29.69s-33.313-10.89-48-29.69M55 279h258v178H55zm18 18v142h222V297zm288 0v71.064l78 .573V297zM88 312h128c-108.235 8-116.31 24-128 113.11zm273 74.066v13.998l78 .573v-14.002zm0 32V487h78v-68.365z"/></svg>', color: '#7c3aed' },
|
'Markets': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 512 512"><path fill="currentColor" d="M256.18 21c-23.242 0-46.577 3.01-63.186 8.54c-8.304 2.763-14.868 6.196-18.808 9.558c-3.94 3.36-5.167 5.956-5.186 8.96L168.943 57H41v14h430V57H342.967l.033-9.033c.01-3.002-1.17-5.55-5.057-8.895s-10.407-6.773-18.677-9.535C302.724 24.014 279.42 21 256.18 21M38.277 89l-10.443 94h80.9l20.243-86.36L122.81 183h81.07l17.114-86.68l-3.096 86.68h75.75l-4.634-86.518L307.694 183h81.497l-6.167-86.36L403.266 183h80.9l-10.443-94zM25.834 201l-.51 4.588C39.822 226.445 52.968 235 64 235c11.32 0 24.852-8.89 39.8-30.96l.714-3.04zm95.687 0l-.32 4.498C135.753 226.495 148.935 235 160 235c11.293 0 24.788-8.85 39.693-30.803l.63-3.197zm95.736 0l-.156 4.352C231.69 226.455 244.908 235 256 235c11.08 0 24.28-8.525 38.85-29.576l-.237-4.424h-77.357zm94.324 0l.674 3.12c15 22.192 28.503 31.044 39.773 31.013c11.03-.03 24.212-8.62 38.772-29.637l-.32-4.496zm95.906 0l.713 3.04C423.147 226.11 436.68 235 448 235c11.032 0 24.178-8.555 38.676-29.412l-.51-4.588h-78.68zM112 223.31C97.313 242.11 81.492 253 64 253c-13.972 0-26.884-6.906-39-19.264V487h318V279h114v208h30V233.736C474.884 246.094 461.972 253 448 253c-17.49 0-33.31-10.888-47.996-29.684c-14.664 18.808-30.432 29.77-47.926 29.817c-17.508.048-33.352-10.87-48.092-29.807C289.303 242.116 273.486 253 256 253c-17.492 0-33.313-10.89-48-29.69c-14.687 18.8-30.508 29.69-48 29.69s-33.313-10.89-48-29.69M55 279h258v178H55zm18 18v142h222V297zm288 0v71.064l78 .573V297zM88 312h128c-108.235 8-116.31 24-128 113.11zm273 74.066v13.998l78 .573v-14.002zm0 32V487h78v-68.365z"/></svg>', color: '#7c3aed' },
|
||||||
'Coffee': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 512 512"><path fill="currentColor" d="M144 32S94.11 69.4 96 96c1.604 22.57 44.375 25.665 48 48c1.91 11.772-16 32-16 32s48-25.373 48-48s-42.8-25.978-48-48c-3.875-16.414 16-48 16-48m80 0s-49.89 37.4-48 64c1.604 22.57 44.375 25.665 48 48c1.91 11.772-16 32-16 32s48-25.373 48-48s-42.8-25.978-48-48c-3.875-16.414 16-48 16-48m80 0s-49.89 37.4-48 64c1.604 22.57 44.375 25.665 48 48c1.91 11.772-16 32-16 32s48-25.373 48-48s-42.8-25.978-48-48c-3.875-16.414 16-48 16-48M73.293 201c1.43 63.948 18.943 179.432 74.707 238h152c55.764-58.568 73.278-174.052 74.707-238zm319.598.445c-.186 9.152-.652 19.252-1.472 30.057C419.312 235.162 441 259.142 441 288c0 31.374-25.626 57-57 57a56.7 56.7 0 0 1-12.764-1.465c-2.912 9.62-6.176 19.165-9.84 28.51A87.5 87.5 0 0 0 384 375c48.155 0 87-38.845 87-87c0-45.153-34.153-82.12-78.11-86.555zM42.763 457c1.507 5.193 3.854 11.2 6.955 16.37c2.637 4.394 5.69 8.207 8.428 10.58C60.882 486.32 63 487 64 487h320c1 0 3.118-.678 5.855-3.05c2.738-2.373 5.79-6.186 8.428-10.58c3.1-5.17 5.448-11.177 6.955-16.37z"/></svg>', color: '#a16207' },
|
'Coffee': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 512 512"><path fill="currentColor" d="M144 32S94.11 69.4 96 96c1.604 22.57 44.375 25.665 48 48c1.91 11.772-16 32-16 32s48-25.373 48-48s-42.8-25.978-48-48c-3.875-16.414 16-48 16-48m80 0s-49.89 37.4-48 64c1.604 22.57 44.375 25.665 48 48c1.91 11.772-16 32-16 32s48-25.373 48-48s-42.8-25.978-48-48c-3.875-16.414 16-48 16-48m80 0s-49.89 37.4-48 64c1.604 22.57 44.375 25.665 48 48c1.91 11.772-16 32-16 32s48-25.373 48-48s-42.8-25.978-48-48c-3.875-16.414 16-48 16-48M73.293 201c1.43 63.948 18.943 179.432 74.707 238h152c55.764-58.568 73.278-174.052 74.707-238zm319.598.445c-.186 9.152-.652 19.252-1.472 30.057C419.312 235.162 441 259.142 441 288c0 31.374-25.626 57-57 57a56.7 56.7 0 0 1-12.764-1.465c-2.912 9.62-6.176 19.165-9.84 28.51A87.5 87.5 0 0 0 384 375c48.155 0 87-38.845 87-87c0-45.153-34.153-82.12-78.11-86.555zM42.763 457c1.507 5.193 3.854 11.2 6.955 16.37c2.637 4.394 5.69 8.207 8.428 10.58C60.882 486.32 63 487 64 487h320c1 0 3.118-.678 5.855-3.05c2.738-2.373 5.79-6.186 8.428-10.58c3.1-5.17 5.448-11.177 6.955-16.37z"/></svg>', color: '#a16207' },
|
||||||
'Other': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 512 512"><path fill="currentColor" d="M247 16v89h9c14.9 0 23 11.7 23.8 23.6c.4 6-1.2 11.5-4.7 15.4c-3.5 4-9.1 7-19.1 7c-5 0-11.1-2.8-15.6-7.4c-4.6-4.5-7.4-10.6-7.4-15.6h-18c0 11 5.2 20.9 12.6 28.4c7.5 7.4 17.4 12.6 28.4 12.6c14 0 25.4-5 32.5-13c7.2-8.1 10-18.6 9.3-28.6c-1.2-17.5-13.4-35.18-32.8-39.42V16zm-47.9 140.5L61.34 247h32.7l114.86-75.5zm113.8 0l-9.8 15L418 247h32.6zM41 265v222h430V265zm38 23h18v176H79zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18z"/></svg>', color: '#6b7280' },
|
'Other': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 512 512"><path fill="currentColor" d="M247 16v89h9c14.9 0 23 11.7 23.8 23.6c.4 6-1.2 11.5-4.7 15.4c-3.5 4-9.1 7-19.1 7c-5 0-11.1-2.8-15.6-7.4c-4.6-4.5-7.4-10.6-7.4-15.6h-18c0 11 5.2 20.9 12.6 28.4c7.5 7.4 17.4 12.6 28.4 12.6c14 0 25.4-5 32.5-13c7.2-8.1 10-18.6 9.3-28.6c-1.2-17.5-13.4-35.18-32.8-39.42V16zm-47.9 140.5L61.34 247h32.7l114.86-75.5zm113.8 0l-9.8 15L418 247h32.6zM41 265v222h430V265zm38 23h18v176H79zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18zm48 0h18v176h-18z"/></svg>', color: '#6b7280' },
|
||||||
|
'Vending Machine': { icon: '<svg width="24" height="24" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M100 29v454h312V29zm18 18h210v338H118zm226 16h50v66h-50zm18 18v30h14V81zm-203 7v23h-16v18h160v-18h-64V80l-16 6-6 25h-26V88zm194 55h32v18h-32zm-90 10.8c-5.4 0-10.1 3-12.8 6.7-2.9 4.9-4.5 9.6-4.1 14.5H143v18h160v-18h-23.1c.3-5.1-1.6-11-4.1-14.5-2.7-3.7-7.4-6.7-12.8-6.7zm90 21.2h32v18h-32zm-194 41v23h-16v18h160v-18h-24v-23h-24v23h-32v-31h-16v31h-32v-15zm8 61.4c-6.1 0-10.8 3.9-13.3 8-3.2 6.1-4 11.5-3.5 17.6H143v18h160v-18h-64v-23h-32v23h-23.2c.6-6-.7-13-3.5-17.6-2.5-4.1-7.2-8-13.3-8zM353 351h32v18h-32zm-235 52h210v62H118zm18 18v26h174v-26z"/></svg>', color: '#7c3aed' },
|
||||||
|
'Coffee Brewer': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.89 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM2 21h18v-2H2v2z"/></svg>', color: '#a16207' },
|
||||||
|
'Smart Market Kiosk': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M21 4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7v2H8v2h8v-2h-2v-2h7c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H3V6h18v12z"/></svg>', color: '#0891b2' },
|
||||||
|
'Cooler': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M18 2.01L6 2c-1.1 0-2 .89-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.11-.9-1.99-2-1.99zM18 20H6v-9.02h12V20zm0-11H6V4h12v5zM8 5h2v3H8V5zm0 7h2v5H8v-5z"/></svg>', color: '#0284c7' },
|
||||||
|
'Water Dispenser': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M12 4C9.11 7.27 5 12.17 5 16c0 3.87 3.13 7 7 7s7-3.13 7-7c0-3.83-4.09-8.73-7-12z"/></svg>', color: '#2563eb' },
|
||||||
|
'Beverage Dispenser': { icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M3 2l2.01 18.23C5.13 21.23 5.97 22 7 22h10c1.03 0 1.87-.77 1.99-1.77L21 2H3zm9 17c-1.66 0-3-1.34-3-3 0-2 3-5.4 3-5.4s3 3.4 3 5.4c0 1.66-1.34 3-3 3zm6.33-11H5.67l-.32-3h13.3l-.32 3z"/></svg>', color: '#0ea5e9' },
|
||||||
|
'Ice Machine': { icon: '<svg width="24" height="24" viewBox="0 0 39.259 39.26" xmlns="http://www.w3.org/2000/svg"><g><g><path fill="currentColor" d="M35.336,0.307C35.153,0.111,34.898,0,34.629,0h-30C4.362,0,4.106,0.111,3.924,0.307C3.742,0.503,3.648,0.765,3.667,1.032l2.574,37.329c0.035,0.506,0.456,0.898,0.963,0.898h24.852c0.507,0,0.928-0.393,0.963-0.898l2.574-37.329C35.612,0.765,35.518,0.502,35.336,0.307z M31.456,32.979H7.805L5.664,1.931h27.932L31.456,32.979z"/><path fill="currentColor" d="M9.381,31.697h20.499l1.635-22.043h-2.539c-0.008-0.217-0.115-0.427-0.318-0.541l-6.463-3.608c-0.15-0.084-0.326-0.104-0.488-0.057c-0.164,0.047-0.305,0.157-0.388,0.305l-2.179,3.901H7.746L9.381,31.697z M27.467,9.924l-2.981,5.339l-5.339-2.982l2.982-5.339L27.467,9.924z M27.45,23.803l-2.9,5.383l-5.383-2.898l2.899-5.383L27.45,23.803z M11.57,16.651l5.8-1.935l1.936,5.802l-5.8,1.935L11.57,16.651z"/></g></g></svg>', color: '#0891b2' },
|
||||||
};
|
};
|
||||||
const CATEGORY_UNMAPPED_ICON = '❓';
|
const CATEGORY_UNMAPPED_ICON = '❓';
|
||||||
const CATEGORY_UNMAPPED_COLOR = '#b91c1c';
|
const CATEGORY_UNMAPPED_COLOR = '#b91c1c';
|
||||||
|
|||||||
Reference in New Issue
Block a user