fixtureqa 0.4.4__tar.gz → 0.4.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fixtureqa-0.4.4/fixtureqa.egg-info → fixtureqa-0.4.6}/PKG-INFO +1 -1
- fixtureqa-0.4.4/fixture/static/assets/index-DTaHBswn.js → fixtureqa-0.4.6/fixture/static/assets/index-FGgej6RF.js +1 -1
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/static/index.html +1 -1
- {fixtureqa-0.4.4 → fixtureqa-0.4.6/fixtureqa.egg-info}/PKG-INFO +1 -1
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixtureqa.egg-info/SOURCES.txt +1 -1
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/pyproject.toml +1 -1
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/LICENSE +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/README.md +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/__init__.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/__main__.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/__init__.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/app.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/connection_manager.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/deps.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/__init__.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/admin.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/auth.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/branding.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/custom_tags.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/fix_spec.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/messages.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/perf.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/scenarios.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/sessions.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/setup.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/spec_overlay.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/templates.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/routers/ws.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/api/schemas.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/config/__init__.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/__init__.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/atomic_io.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/auth.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/config_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/custom_tag_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/db_migrations.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/events.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/exec_csv_writer.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/fix_application.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/fix_builder.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/fix_parser.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/fix_spec_parser.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/fix_tags.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/fix_time.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/housekeeping.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/inbound.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/json_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/message_log.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/message_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/models.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/perf_engine.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/perf_models.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/perf_payload.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/perf_stats.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/perf_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/perf_writer.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/scenario_runner.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/scenario_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/session.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/session_manager.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/spec_overlay_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/template_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/user_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/core/venue_responses.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/fix_specs/FIX42.xml +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/fix_specs/FIX44.xml +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/server.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/static/assets/ag-grid-_QKprVdm.js +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/static/assets/index-BwQf-cei.css +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/static/assets/index-CyNOPa0n.js +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/static/assets/react-vendor-2eF0YfZT.js +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/static/favicon.svg +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixture/ui/__init__.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixtureqa.egg-info/dependency_links.txt +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixtureqa.egg-info/entry_points.txt +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixtureqa.egg-info/requires.txt +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/fixtureqa.egg-info/top_level.txt +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/setup.cfg +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_atomic_io.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_auth.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_config_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_connection_manager.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_db_migrations.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_fix_builder.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_health.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_inbound.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_inbound_validation.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_message_store.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_perf_api.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_perf_engine.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_perf_models.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_perf_payload.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_perf_rehydrate.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_scenarios.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_session_lifecycle.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_session_manager_concurrency.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_sessions.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_templates.py +0 -0
- {fixtureqa-0.4.4 → fixtureqa-0.4.6}/tests/test_ws.py +0 -0
|
@@ -99,4 +99,4 @@ Please change the parent <Route path="${b}"> to <Route path="${b==="/"?"*":`${b}
|
|
|
99
99
|
]
|
|
100
100
|
}
|
|
101
101
|
]
|
|
102
|
-
}`;function Kz({onClose:e}){const[t,r]=h.useState(""),[n,a]=h.useState(""),[i,o]=h.useState(!1);h.useEffect(()=>{G.specOverlay.get().then(l=>r(JSON.stringify(l,null,2))).catch(console.error)},[]);async function s(){a("");let l;try{l=JSON.parse(t)}catch{a("Invalid JSON");return}o(!0);try{await G.specOverlay.set(l),e()}catch(u){a(u instanceof Error?u.message:"Save failed")}finally{o(!1)}}return c.jsx("div",{className:"modal-overlay",onClick:e,children:c.jsxs("div",{className:"modal",style:{width:660,maxHeight:"85vh",display:"flex",flexDirection:"column"},onClick:l=>l.stopPropagation(),children:[c.jsxs("div",{className:"modal-header",children:[c.jsx("span",{children:"Spec Overlay — Custom Message Types"}),c.jsx("button",{className:"btn-icon",onClick:e,children:"✕"})]}),c.jsx("div",{style:{padding:"10px 16px 4px",fontSize:12,color:"var(--text2)"},children:"Define custom / proprietary message types (e.g. 35=U1). Fields reference standard tag numbers; use Custom Tags for non-standard tag names. Field names and types are resolved from the spec at runtime."}),c.jsx("div",{style:{flex:1,display:"flex",flexDirection:"column",padding:"8px 16px",minHeight:0},children:c.jsx("textarea",{style:{flex:1,minHeight:300,fontFamily:"var(--font)",fontSize:12,background:"var(--bg3)",color:"var(--text)",border:"1px solid var(--border)",borderRadius:4,padding:"8px 10px",resize:"none"},value:t,onChange:l=>r(l.target.value),spellCheck:!1,placeholder:Zz})}),c.jsxs("div",{style:{padding:"0 16px 8px",fontSize:11,color:"var(--text2)"},children:["Schema: ",c.jsx("code",{children:'{ "messages": [{ "msg_type", "name", "category", "fields": [{ "tag", "required" }] }] }'})]}),n&&c.jsx("div",{className:"tpl-error",style:{margin:"0 16px 8px"},children:n}),c.jsxs("div",{className:"modal-footer",children:[c.jsx("button",{className:"btn btn-ghost",onClick:e,children:"Cancel"}),c.jsx("button",{className:"btn btn-primary",onClick:s,disabled:i,children:i?"Saving…":"Save overlay"})]})]})})}const Iw="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgdmlld0JveD0iMCAwIDY4MCAxNjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgCgogIDxwb2x5Z29uIHBvaW50cz0iMTg4LDI4IDIzOCwyOCAyNTgsNzggMjM4LDEyOCAxODgsMTI4IiBmaWxsPSIjMWEzYTVjIiBzdHlsZT0iZmlsbDpyZ2IoMjYsIDU4LCA5Mik7c3Ryb2tlOm5vbmU7Y29sb3I6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7b3BhY2l0eToxO2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGNpcmNsZSBjeD0iMjA3IiBjeT0iNzgiIHI9IjEwIiBmaWxsPSIjMDBiNGM4IiBzdHlsZT0iZmlsbDpyZ2IoMCwgMTgwLCAyMDApO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxjaXJjbGUgY3g9IjIwNyIgY3k9Ijc4IiByPSI2IiBmaWxsPSIjMWEzYTVjIiBzdHlsZT0iZmlsbDpyZ2IoMjYsIDU4LCA5Mik7c3Ryb2tlOm5vbmU7Y29sb3I6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7b3BhY2l0eToxO2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGxpbmUgeDE9IjIyMCIgeTE9IjY1IiB4Mj0iMjQ4IiB5Mj0iNjUiIHN0cm9rZT0iIzAwYjRjOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0eWxlPSJmaWxsOnJnYigwLCAwLCAwKTtzdHJva2U6cmdiKDAsIDE4MCwgMjAwKTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjRweDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7b3BhY2l0eToxO2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGxpbmUgeDE9IjIyMCIgeTE9Ijc4IiB4Mj0iMjUyIiB5Mj0iNzgiIHN0cm9rZT0iIzAwYjRjOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9IjAuNyIgc3R5bGU9ImZpbGw6cmdiKDAsIDAsIDApO3N0cm9rZTpyZ2IoMCwgMTgwLCAyMDApO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6NHB4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjAuNztmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxsaW5lIHgxPSIyMjAiIHkxPSI5MSIgeDI9IjI0NCIgeTI9IjkxIiBzdHJva2U9IiMwMGI0YzgiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBvcGFjaXR5PSIwLjQ1IiBzdHlsZT0iZmlsbDpyZ2IoMCwgMCwgMCk7c3Ryb2tlOnJnYigwLCAxODAsIDIwMCk7Y29sb3I6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZS13aWR0aDo0cHg7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MC40NTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgoKICA8dGV4dCB4PSIyNzIiIHk9IjEwNSIgZm9udC1mYW1pbHk9InZhcigtLWZvbnQtbW9ubykiIGZvbnQtc2l6ZT0iNjYiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMwMGI0YzgiIGxldHRlci1zcGFjaW5nPSItMSIgc3R5bGU9ImZpbGw6cmdiKDAsIDE4MCwgMjAwKTtzdHJva2U6bm9uZTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjE7Zm9udC1mYW1pbHk6dWktbW9ub3NwYWNlLCBtb25vc3BhY2U7Zm9udC1zaXplOjY2cHg7Zm9udC13ZWlnaHQ6NzAwO3RleHQtYW5jaG9yOnN0YXJ0O2RvbWluYW50LWJhc2VsaW5lOmF1dG8iPkZJWDwvdGV4dD4KICA8dGV4dCB4PSIzODYiIHk9IjEwNSIgZm9udC1mYW1pbHk9InZhcigtLWZvbnQtc2FucykiIGZvbnQtc2l6ZT0iNTAiIGZvbnQtd2VpZ2h0PSIzMDAiIGxldHRlci1zcGFjaW5nPSItMSIgc3R5bGU9ImZpbGw6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6NTBweDtmb250LXdlaWdodDozMDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byI+dHVyZTwvdGV4dD4KPC9zdmc+",kw="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgdmlld0JveD0iMCAwIDY4MCAxNjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CgogIDxwb2x5Z29uIHBvaW50cz0iMTg4LDI4IDIzOCwyOCAyNTgsNzggMjM4LDEyOCAxODgsMTI4IiBmaWxsPSIjMDBiNGM4IiBzdHlsZT0iZmlsbDpyZ2IoMCwgMTgwLCAyMDApO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxjaXJjbGUgY3g9IjIwNyIgY3k9Ijc4IiByPSIxMCIgZmlsbD0iIzFhM2E1YyIgc3R5bGU9ImZpbGw6cmdiKDI2LCA1OCwgOTIpO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxjaXJjbGUgY3g9IjIwNyIgY3k9Ijc4IiByPSI2IiBmaWxsPSIjMDBiNGM4IiBzdHlsZT0iZmlsbDpyZ2IoMCwgMTgwLCAyMDApO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxsaW5lIHgxPSIyMjAiIHkxPSI2NSIgeDI9IjI0OCIgeTI9IjY1IiBzdHJva2U9IiMxYTNhNWMiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHlsZT0iZmlsbDpyZ2IoMCwgMCwgMCk7c3Ryb2tlOnJnYigyNiwgNTgsIDkyKTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjRweDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7b3BhY2l0eToxO2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGxpbmUgeDE9IjIyMCIgeTE9Ijc4IiB4Mj0iMjUyIiB5Mj0iNzgiIHN0cm9rZT0iIzFhM2E1YyIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9IjAuNyIgc3R5bGU9ImZpbGw6cmdiKDAsIDAsIDApO3N0cm9rZTpyZ2IoMjYsIDU4LCA5Mik7Y29sb3I6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZS13aWR0aDo0cHg7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MC43O2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGxpbmUgeDE9IjIyMCIgeTE9IjkxIiB4Mj0iMjQ0IiB5Mj0iOTEiIHN0cm9rZT0iIzFhM2E1YyIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9IjAuNDUiIHN0eWxlPSJmaWxsOnJnYigwLCAwLCAwKTtzdHJva2U6cmdiKDI2LCA1OCwgOTIpO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6NHB4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjAuNDU7Zm9udC1mYW1pbHk6JnF1b3Q7QW50aHJvcGljIFNhbnMmcXVvdDssIC1hcHBsZS1zeXN0ZW0sIEJsaW5rTWFjU3lzdGVtRm9udCwgJnF1b3Q7U2Vnb2UgVUkmcXVvdDssIHNhbnMtc2VyaWY7Zm9udC1zaXplOjE2cHg7Zm9udC13ZWlnaHQ6NDAwO3RleHQtYW5jaG9yOnN0YXJ0O2RvbWluYW50LWJhc2VsaW5lOmF1dG8iLz4KCiAgPHRleHQgeD0iMjcyIiB5PSIxMDUiIGZvbnQtZmFtaWx5PSJ2YXIoLS1mb250LW1vbm8pIiBmb250LXNpemU9IjY2IiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMWEzYTVjIiBsZXR0ZXItc3BhY2luZz0iLTEiIHN0eWxlPSJmaWxsOnJnYigyNiwgNTgsIDkyKTtzdHJva2U6bm9uZTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjE7Zm9udC1mYW1pbHk6dWktbW9ub3NwYWNlLCBtb25vc3BhY2U7Zm9udC1zaXplOjY2cHg7Zm9udC13ZWlnaHQ6NzAwO3RleHQtYW5jaG9yOnN0YXJ0O2RvbWluYW50LWJhc2VsaW5lOmF1dG8iPkZJWDwvdGV4dD4KICA8dGV4dCB4PSIzODYiIHk9IjEwNSIgZm9udC1mYW1pbHk9InZhcigtLWZvbnQtc2FucykiIGZvbnQtc2l6ZT0iNTAiIGZvbnQtd2VpZ2h0PSIzMDAiIGZpbGw9IiMwMGI0YzgiIGxldHRlci1zcGFjaW5nPSItMSIgc3R5bGU9ImZpbGw6cmdiKDAsIDE4MCwgMjAwKTtzdHJva2U6bm9uZTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjE7Zm9udC1mYW1pbHk6JnF1b3Q7QW50aHJvcGljIFNhbnMmcXVvdDssIHNhbnMtc2VyaWY7Zm9udC1zaXplOjUwcHg7Zm9udC13ZWlnaHQ6MzAwO3RleHQtYW5jaG9yOnN0YXJ0O2RvbWluYW50LWJhc2VsaW5lOmF1dG8iPnR1cmU8L3RleHQ+Cjwvc3ZnPg==";function Us(){const e=X(r=>r.theme),t=X(r=>r.branding);return e==="dark"?(t==null?void 0:t.logo_dark)||Iw:(t==null?void 0:t.logo_light)||kw}function qz(){const e=X(g=>g.setSessions),t=X(g=>g.theme),r=X(g=>g.toggleTheme),n=X(g=>g.user),a=X(g=>g.logout),i=X(g=>g.openTabs),o=X(g=>g.activeTabId),s=X(g=>g.branding),l=Us(),u=s!=null&&s.prefix?`${s.prefix} FIXture`:"FIXture",[f,d]=h.useState(!1),[m,p]=h.useState(!1),[v,y]=h.useState(!1);return Sv(),h.useEffect(()=>{document.documentElement.setAttribute("data-theme",t)},[t]),h.useEffect(()=>{G.sessions.list().then(e).catch(console.error)},[]),h.useEffect(()=>{if(!n){n1();return}G.customTags.list().then(jv).catch(console.error)},[n]),c.jsxs("div",{className:"app",children:[c.jsxs("header",{className:"app-header",children:[c.jsx("img",{src:l,alt:u,className:"app-logo"}),c.jsxs("div",{className:"header-right",children:[n&&c.jsx("span",{className:"header-user",children:n.username}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>d(!0),children:"Scenarios"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p(!0),children:"Custom Tags"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>y(!0),children:"Spec Overlay"}),(n==null?void 0:n.role)==="platform_admin"&&c.jsx(qr,{to:"/perf",className:"btn btn-sm btn-ghost",title:"Performance testing",children:"Perf"}),(n==null?void 0:n.role)==="platform_admin"&&c.jsx(qr,{to:"/admin/users",className:"btn-icon",title:"Admin",children:"⚙"}),c.jsx("button",{className:"btn-icon theme-toggle",onClick:r,title:"Toggle theme",children:t==="dark"?"☀":"🌙"}),c.jsx("button",{className:"btn-icon",onClick:a,title:"Sign out",children:"⎋"})]})]}),c.jsxs("div",{className:"app-body",children:[c.jsx(g1,{}),c.jsx("div",{className:"tab-area",children:i.length===0?c.jsx("div",{className:"tab-area-empty",children:"Select a session to open it."}):c.jsxs(c.Fragment,{children:[c.jsx(Az,{}),c.jsx("div",{className:"tab-panels",children:i.map(g=>c.jsx("div",{className:`tab-panel${g===o?" active":""}`,children:c.jsx(Cz,{sessionId:g})},g))})]})})]}),f&&c.jsx(Bz,{onClose:()=>d(!1)}),m&&c.jsx(Gz,{onClose:()=>p(!1)}),v&&c.jsx(Kz,{onClose:()=>y(!1)})]})}function Yz(){const[e,t]=h.useState(""),[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState(!1),[l,u]=h.useState(!1),{login:f}=X(),d=Us(),m=an();h.useEffect(()=>{fetch("/api/auth/register/status").then(v=>v.json()).then(v=>u(v.enabled)).catch(()=>{})},[]);async function p(v){v.preventDefault(),i(""),s(!0);try{const y=await fetch("/api/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:e,password:r})});if(!y.ok){const b=await y.json().catch(()=>({}));throw new Error(b.detail??"Login failed")}const g=await y.json();f(g.access_token,g.user),m("/",{replace:!0})}catch(y){i(y.message)}finally{s(!1)}}return c.jsx("div",{className:"auth-page",children:c.jsxs("div",{className:"auth-card",children:[c.jsx("img",{src:d,alt:"FIXture",className:"auth-logo"}),c.jsx("h2",{children:"Sign in"}),c.jsxs("form",{onSubmit:p,className:"auth-form",children:[c.jsxs("label",{children:["Username",c.jsx("input",{type:"text",value:e,onChange:v=>t(v.target.value),autoComplete:"username",required:!0})]}),c.jsxs("label",{children:["Password",c.jsx("input",{type:"password",value:r,onChange:v=>n(v.target.value),autoComplete:"current-password",required:!0})]}),a&&c.jsx("p",{className:"auth-error",children:a}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:o,children:o?"Signing in…":"Sign in"})]}),l&&c.jsxs("p",{className:"auth-hint",style:{marginTop:12},children:["No account? ",c.jsx(qr,{to:"/register",children:"Register"})]})]})})}function Xz(){const[e,t]=h.useState(""),[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState(!1),l=Us(),u=an();async function f(d){d.preventDefault(),i(""),s(!0);try{const m=await fetch("/api/setup/create-admin",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:e,password:r})});if(!m.ok){const p=await m.json().catch(()=>({}));throw new Error(p.detail??"Setup failed")}u("/login",{replace:!0})}catch(m){i(m.message)}finally{s(!1)}}return c.jsx("div",{className:"auth-page",children:c.jsxs("div",{className:"auth-card",children:[c.jsx("img",{src:l,alt:"FIXture",className:"auth-logo"}),c.jsx("h2",{children:"Create admin account"}),c.jsx("p",{className:"auth-hint",children:"First-time setup — create the platform admin."}),c.jsxs("form",{onSubmit:f,className:"auth-form",children:[c.jsxs("label",{children:["Username",c.jsx("input",{type:"text",value:e,onChange:d=>t(d.target.value),autoComplete:"username",required:!0})]}),c.jsxs("label",{children:["Password",c.jsx("input",{type:"password",value:r,onChange:d=>n(d.target.value),autoComplete:"new-password",minLength:8,required:!0})]}),a&&c.jsx("p",{className:"auth-error",children:a}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:o,children:o?"Creating…":"Create admin"})]})]})})}function Vz(){const[e,t]=h.useState(""),[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState(!1),[l,u]=h.useState(null),{login:f,theme:d}=X(),m=an();h.useEffect(()=>{fetch("/api/auth/register/status").then(y=>y.json()).then(u).catch(()=>u({enabled:!1,max_users:0,current_users:0}))},[]);async function p(y){y.preventDefault(),i(""),s(!0);try{const g=await fetch("/api/auth/register",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:e,password:r})});if(!g.ok){const x=await g.json().catch(()=>({}));throw new Error(x.detail??"Registration failed")}const b=await g.json();f(b.access_token,b.user),m("/",{replace:!0})}catch(g){i(g.message)}finally{s(!1)}}const v=l&&l.current_users>=l.max_users;return c.jsx("div",{className:"auth-page",children:c.jsxs("div",{className:"auth-card",children:[c.jsx("img",{src:d==="dark"?Iw:kw,alt:"FIXture",className:"auth-logo"}),c.jsx("h2",{children:"Create account"}),l===null&&c.jsx("p",{className:"auth-hint",children:"Checking registration status…"}),l&&!l.enabled&&c.jsx("p",{className:"auth-error",children:"Registration is currently closed. Contact an administrator."}),l&&l.enabled&&v&&c.jsx("p",{className:"auth-error",children:"Registration limit reached. Contact an administrator."}),l&&l.enabled&&!v&&c.jsxs("form",{onSubmit:p,className:"auth-form",children:[c.jsxs("label",{children:["Username",c.jsx("input",{type:"text",value:e,onChange:y=>t(y.target.value),autoComplete:"username",required:!0})]}),c.jsxs("label",{children:["Password",c.jsx("input",{type:"password",value:r,onChange:y=>n(y.target.value),autoComplete:"new-password",minLength:8,required:!0})]}),a&&c.jsx("p",{className:"auth-error",children:a}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:o,children:o?"Creating account…":"Create account"})]}),c.jsxs("p",{className:"auth-hint",style:{marginTop:12},children:["Already have an account? ",c.jsx(qr,{to:"/login",children:"Sign in"})]})]})})}const Jz=["accent","bg","bg2","bg3","border","text","text2"];function Tw(e){let t=document.getElementById("branding-style");t||(t=document.createElement("style"),t.id="branding-style",document.head.appendChild(t));const r=Jz.filter(n=>e[n]).map(n=>`--${n}: ${e[n]};`).join(" ");if(r?t.textContent=`:root { ${r} } [data-theme="light"] { ${r} }`:t.textContent="",e.favicon){let n=document.querySelector('link[rel="icon"]');n||(n=document.createElement("link"),n.rel="icon",document.head.appendChild(n)),n.href=e.favicon}document.title=e.prefix?`${e.prefix} FIXture`:"FIXture"}function Sl(e){return new Promise((t,r)=>{const n=new FileReader;n.onload=()=>t(n.result),n.onerror=r,n.readAsDataURL(e)})}const Mw=["user","user_admin","platform_admin"];function Qz(e){return new Date(e*1e3).toLocaleString()}function eW({onClose:e,onCreated:t}){const[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState("user"),[l,u]=h.useState(""),[f,d]=h.useState(!1);async function m(p){p.preventDefault(),u(""),d(!0);try{const v=await G.admin.users.create({username:r,password:a,role:o});t(v),e()}catch(v){u(v.message)}finally{d(!1)}}return c.jsx("div",{className:"modal-overlay",onClick:e,children:c.jsxs("div",{className:"modal",onClick:p=>p.stopPropagation(),children:[c.jsxs("div",{className:"modal-header",children:[c.jsx("span",{children:"Create User"}),c.jsx("button",{className:"btn-icon",onClick:e,children:"✕"})]}),c.jsxs("form",{onSubmit:m,children:[c.jsxs("div",{className:"form-grid",children:[c.jsx("label",{children:"Username"}),c.jsx("input",{value:r,onChange:p=>n(p.target.value),required:!0}),c.jsx("label",{children:"Password"}),c.jsx("input",{type:"password",value:a,onChange:p=>i(p.target.value),minLength:8,required:!0}),c.jsx("label",{children:"Role"}),c.jsx("select",{value:o,onChange:p=>s(p.target.value),children:Mw.map(p=>c.jsx("option",{value:p,children:p},p))})]}),l&&c.jsx("div",{className:"error-msg",children:l}),c.jsxs("div",{className:"modal-footer",children:[c.jsx("button",{type:"button",className:"btn btn-secondary",onClick:e,children:"Cancel"}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:f,children:f?"Creating…":"Create"})]})]})]})})}function tW({user:e,onClose:t}){const[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState(!1),[l,u]=h.useState(!1);async function f(d){d.preventDefault(),i(""),s(!0);try{await G.admin.users.update(e.uid,{password:r}),u(!0)}catch(m){i(m.message)}finally{s(!1)}}return c.jsx("div",{className:"modal-overlay",onClick:t,children:c.jsxs("div",{className:"modal",onClick:d=>d.stopPropagation(),children:[c.jsxs("div",{className:"modal-header",children:[c.jsxs("span",{children:["Reset Password — ",e.username]}),c.jsx("button",{className:"btn-icon",onClick:t,children:"✕"})]}),l?c.jsxs("div",{style:{padding:16},children:[c.jsx("p",{style:{color:"var(--green)"},children:"Password updated successfully."}),c.jsx("div",{className:"modal-footer",children:c.jsx("button",{className:"btn btn-primary",onClick:t,children:"Close"})})]}):c.jsxs("form",{onSubmit:f,children:[c.jsxs("div",{className:"form-grid",children:[c.jsx("label",{children:"New Password"}),c.jsx("input",{type:"password",value:r,onChange:d=>n(d.target.value),minLength:8,required:!0})]}),a&&c.jsx("div",{className:"error-msg",children:a}),c.jsxs("div",{className:"modal-footer",children:[c.jsx("button",{type:"button",className:"btn btn-secondary",onClick:t,children:"Cancel"}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:o,children:o?"Saving…":"Save"})]})]})]})})}function rW(){const e=X(T=>T.user),t=an(),[r,n]=h.useState([]),[a,i]=h.useState(null),[o,s]=h.useState(null),[l,u]=h.useState(null),[f,d]=h.useState(!1),[m,p]=h.useState(null),[v,y]=h.useState(!1),{setBranding:g}=X(),[b,x]=h.useState(!1),[w,S]=h.useState(null),[_,j]=h.useState("");async function O(){try{const[T,R,z,W]=await Promise.all([G.admin.users.list(),G.admin.settings.get(),G.admin.housekeeping.get(),G.admin.branding.get()]);n(T),i(R),s(z),p(W)}catch(T){j(T.message)}}h.useEffect(()=>{O()},[]);async function N(T){try{const R=await G.admin.users.update(T.uid,{is_active:!T.is_active});n(z=>z.map(W=>W.uid===T.uid?R:W))}catch(R){j(R.message)}}async function A(T,R){try{const z=await G.admin.users.update(T.uid,{role:R});n(W=>W.map(V=>V.uid===T.uid?z:V))}catch(z){j(z.message)}}async function k(T){if(confirm(`Delete user "${T.username}"? This cannot be undone.`))try{await G.admin.users.delete(T.uid),n(R=>R.filter(z=>z.uid!==T.uid))}catch(R){j(R.message)}}async function M(){if(a)try{const T=await G.admin.settings.update({registration_enabled:!a.enabled});i(T)}catch(T){j(T.message)}}async function P(T){const R=parseInt(T,10);if(!(isNaN(R)||R<1))try{const z=await G.admin.settings.update({max_users:R});i(z)}catch(z){j(z.message)}}async function H(){if(o)try{const T=await G.admin.housekeeping.update({enabled:!o.enabled});s(T)}catch(T){j(T.message)}}async function L(T,R){const z=parseInt(R,10);if(!(isNaN(z)||z<0))try{const W=await G.admin.housekeeping.update({[T]:z});s(W)}catch(W){j(W.message)}}async function U(){d(!0),u(null);try{const T=await G.admin.housekeeping.runNow();u(`Done — deleted ${T.msgs_deleted} messages, ${T.logs_deleted} log files`)}catch(T){j(T.message)}finally{d(!1)}}return c.jsxs("div",{className:"admin-page",children:[c.jsxs("div",{className:"admin-header",children:[c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12},children:[c.jsx("button",{className:"btn-icon",onClick:()=>t("/"),title:"Back",children:"←"}),c.jsx("h1",{children:"User Management"})]}),c.jsx("button",{className:"btn btn-primary btn-sm",onClick:()=>x(!0),children:"+ Create User"})]}),_&&c.jsx("div",{className:"error-msg",style:{margin:"0 0 12px"},children:_}),a&&c.jsxs("div",{className:"admin-settings-bar",children:[c.jsx("span",{className:"admin-settings-label",children:"Self-registration:"}),c.jsx("button",{className:`btn btn-sm ${a.enabled?"btn-stop":"btn-start"}`,onClick:M,children:a.enabled?"Enabled — click to disable":"Disabled — click to enable"}),c.jsx("span",{className:"admin-settings-label",style:{marginLeft:16},children:"Max users:"}),c.jsx("input",{type:"number",className:"admin-max-users-input",defaultValue:a.max_users,min:1,onBlur:T=>P(T.target.value)}),c.jsxs("span",{className:"admin-settings-label",style:{color:"var(--text2)"},children:["(",a.current_users," / ",a.max_users," registered)"]})]}),o&&c.jsxs("div",{className:"admin-settings-bar",children:[c.jsx("span",{className:"admin-settings-label",children:"Housekeeping:"}),c.jsx("button",{className:`btn btn-sm ${o.enabled?"btn-stop":"btn-start"}`,onClick:H,children:o.enabled?"Auto — click to disable":"Manual only — click to enable"}),c.jsx("span",{className:"admin-settings-label",style:{marginLeft:16},children:"Keep messages (days):"}),c.jsx("input",{type:"number",className:"admin-max-users-input",defaultValue:o.msg_retention_days,min:0,title:"0 = keep forever",onBlur:T=>L("msg_retention_days",T.target.value)}),c.jsx("span",{className:"admin-settings-label",style:{marginLeft:16},children:"Keep logs (days):"}),c.jsx("input",{type:"number",className:"admin-max-users-input",defaultValue:o.log_retention_days,min:0,title:"0 = keep forever",onBlur:T=>L("log_retention_days",T.target.value)}),c.jsx("button",{className:"btn btn-sm btn-ghost",style:{marginLeft:16},onClick:U,disabled:f,children:f?"Running…":"Run Now"}),l&&c.jsx("span",{style:{marginLeft:8,color:"var(--green)",fontSize:12},children:l})]}),m&&c.jsxs("div",{className:"admin-settings-bar",style:{flexDirection:"column",alignItems:"flex-start",gap:10},children:[c.jsx("span",{className:"admin-settings-label",style:{fontWeight:700},children:"Branding"}),c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[c.jsx("span",{className:"admin-settings-label",children:"Brand prefix:"}),c.jsx("input",{className:"admin-max-users-input",style:{width:160},value:m.prefix,placeholder:"e.g. Acme (shown as Acme FIXture)",onChange:T=>p({...m,prefix:T.target.value})}),c.jsx("span",{className:"admin-settings-label",style:{marginLeft:16},children:"Accent colour:"}),c.jsx("input",{type:"color",value:m.accent||"#388bfd",onChange:T=>p({...m,accent:T.target.value}),title:"Primary accent colour"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,accent:""}),title:"Reset to default blue",children:"Reset"})]}),c.jsx("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[["bg","Page background","#0d1117"],["bg2","Panel background","#161b22"],["bg3","Input background","#21262d"],["border","Border colour","#30363d"],["text","Primary text","#e6edf3"],["text2","Secondary text","#8b949e"]].map(([T,R,z])=>c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:4},children:[c.jsxs("span",{className:"admin-settings-label",children:[R,":"]}),c.jsx("input",{type:"color",value:m[T]||z,onChange:W=>p({...m,[T]:W.target.value}),title:R}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,[T]:""}),title:"Reset to default",children:"↺"})]},T))}),c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[c.jsx("span",{className:"admin-settings-label",children:"Logo (dark theme):"}),m.logo_dark&&c.jsx("img",{src:m.logo_dark,style:{height:28,background:"#161b22",borderRadius:4,padding:"2px 6px"},alt:"dark logo preview"}),c.jsx("input",{type:"file",accept:"image/*",onChange:async T=>{var z;const R=(z=T.target.files)==null?void 0:z[0];R&&p({...m,logo_dark:await Sl(R)})}}),m.logo_dark&&c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,logo_dark:""}),children:"Clear"})]}),c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[c.jsx("span",{className:"admin-settings-label",children:"Logo (light theme):"}),m.logo_light&&c.jsx("img",{src:m.logo_light,style:{height:28,background:"#f0f2f5",borderRadius:4,padding:"2px 6px"},alt:"light logo preview"}),c.jsx("input",{type:"file",accept:"image/*",onChange:async T=>{var z;const R=(z=T.target.files)==null?void 0:z[0];R&&p({...m,logo_light:await Sl(R)})}}),m.logo_light&&c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,logo_light:""}),children:"Clear"})]}),c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[c.jsx("span",{className:"admin-settings-label",children:"Favicon:"}),m.favicon&&c.jsx("img",{src:m.favicon,style:{height:20,width:20},alt:"favicon preview"}),c.jsx("input",{type:"file",accept:"image/png,image/svg+xml,image/x-icon",onChange:async T=>{var z;const R=(z=T.target.files)==null?void 0:z[0];R&&p({...m,favicon:await Sl(R)})}}),m.favicon&&c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,favicon:""}),children:"Clear"})]}),c.jsx("button",{className:"btn btn-sm btn-primary",disabled:v,onClick:async()=>{y(!0);try{const T=await G.admin.branding.update(m);p(T),g(T),Tw(T)}catch(T){j(T.message)}finally{y(!1)}},children:v?"Saving…":"Save Branding"})]}),c.jsxs("table",{className:"admin-table",children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Username"}),c.jsx("th",{children:"Role"}),c.jsx("th",{children:"Status"}),c.jsx("th",{children:"Created"}),c.jsx("th",{children:"Actions"})]})}),c.jsx("tbody",{children:r.map(T=>c.jsxs("tr",{className:T.is_active?"":"admin-row-inactive",children:[c.jsxs("td",{children:[T.username,T.uid===(e==null?void 0:e.uid)&&c.jsx("span",{className:"admin-you-badge",children:" (you)"})]}),c.jsx("td",{children:c.jsx("select",{value:T.role,onChange:R=>A(T,R.target.value),className:"admin-role-select",disabled:T.uid===(e==null?void 0:e.uid),children:Mw.map(R=>c.jsx("option",{value:R,children:R},R))})}),c.jsx("td",{children:c.jsx("span",{style:{color:T.is_active?"var(--green)":"var(--text2)"},children:T.is_active?"Active":"Inactive"})}),c.jsx("td",{style:{color:"var(--text2)",fontSize:11},children:Qz(T.created_at)}),c.jsx("td",{children:c.jsxs("div",{className:"admin-row-actions",children:[c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>N(T),disabled:T.uid===(e==null?void 0:e.uid),title:T.is_active?"Deactivate":"Activate",children:T.is_active?"Deactivate":"Activate"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>S(T),title:"Reset password",children:"Reset PW"}),c.jsx("button",{className:"btn btn-sm btn-danger",onClick:()=>k(T),disabled:T.uid===(e==null?void 0:e.uid),title:"Delete user",children:"✕"})]})})]},T.uid))})]}),b&&c.jsx(eW,{onClose:()=>x(!1),onCreated:T=>n(R=>[...R,T])}),w&&c.jsx(tW,{user:w,onClose:()=>S(null)})]})}const Up={name:"smoke",mode:"loopback",client_session_id:null,venue_session_id:null,correlation_tag:376,exec_id_tag:25116,rate:{orders_per_window:50,order_window_ms:100,fills_per_window:50,fill_window_ms:100,fill_ratio:1,allow_burst:!1,max_burst_multiplier:2},test:{duration:30,max_orders:0,scenario_timeout_ms:3e4,max_pending:1e5,on_saturation:"pause",output:"results",record_execs:!1},payload:{variables:{},order:{symbols:["AAPL","MSFT","GOOGL"],side:"alternate",ord_type:"limit",quantity_min:100,quantity_max:1e3,price_min:99,price_max:101,time_in_force:"0"},fill:{fill_type:"full",send_ack:!0,fills_per_order:1,partial_fill_pct_min:50,partial_fill_pct_max:99,price_variance_ticks:0},order_template_id:null,exec_template_id:null,auto_expiry:!0,expiry_offset:1,expiry_unit:"days",gen_rules:{}}},nW=["loopback","manager","client","venue"];function Hp(e,t){return t?Math.round(e/t*1e3):0}function aW({onStarted:e}){var mt,Cr;const t=X(C=>C.sessions),[r,n]=h.useState(Up),[a,i]=h.useState(Up.payload.order.symbols.join(", ")),[o,s]=h.useState(!1),[l,u]=h.useState(""),[f,d]=h.useState(!1),[m,p]=h.useState([]),[v,y]=h.useState(""),[g,b]=h.useState([]),x=Object.values(t),w=r.mode==="loopback"||r.mode==="manager"||r.mode==="client",S=r.mode==="loopback"||r.mode==="manager"||r.mode==="venue",_=r.client_session_id?t[r.client_session_id]:void 0,j=r.venue_session_id?t[r.venue_session_id]:void 0,O=!w||(_==null?void 0:_.status)==="LOGGED_ON",N=!S||(j==null?void 0:j.status)==="LOGGED_ON",A=(!w||!!r.client_session_id)&&(!S||!!r.venue_session_id),k=(()=>{if(!r.name.trim())return"Name is required";const C=r.rate;if(C.max_burst_multiplier<1)return"Max burst multiplier must be ≥ 1";if(r.test.scenario_timeout_ms<1)return"Order timeout must be > 0";if(r.test.max_pending<1)return"Max pending must be > 0";if(w){if(C.orders_per_window<1||C.order_window_ms<1)return"Order rate must be > 0";if(a.split(",").map(J=>J.trim()).filter(Boolean).length===0)return"At least one symbol is required";const I=r.payload.order;if(I.quantity_min<1||I.quantity_max<1||I.quantity_min>I.quantity_max)return"Check quantity min/max";if(I.price_min<=0||I.price_max<=0||I.price_min>I.price_max)return"Check price min/max"}if(S){if(C.fills_per_window<1||C.fill_window_ms<1)return"Fill rate must be > 0";if(r.payload.fill.fills_per_order<1)return"Fills per order must be ≥ 1";if(C.fill_ratio<0||C.fill_ratio>1)return"Fill ratio must be between 0 and 1"}return null})(),M=A&&O&&N&&!k&&!f;async function P(){try{p(await G.perf.configs.list())}catch{}}h.useEffect(()=>{P()},[]),h.useEffect(()=>{G.templates.list().then(b).catch(()=>{})},[]);const H=g.filter(C=>C.msg_type==="D"),L=g.filter(C=>C.msg_type==="8"),U=!!r.payload.order_template_id,T=!!r.payload.exec_template_id,R=((mt=H.find(C=>C.id===r.payload.order_template_id))==null?void 0:mt.name)??"selected",z=((Cr=L.find(C=>C.id===r.payload.exec_template_id))==null?void 0:Cr.name)??"selected",W=C=>n(I=>({...I,...C})),V=C=>n(I=>({...I,rate:{...I.rate,...C}})),se=C=>n(I=>({...I,test:{...I.test,...C}})),Q=C=>n(I=>({...I,payload:{...I.payload,order:{...I.payload.order,...C}}})),ye=C=>n(I=>({...I,payload:{...I.payload,fill:{...I.payload.fill,...C}}})),de=C=>n(I=>({...I,payload:{...I.payload,...C}})),[Ne,$]=h.useState([]),ae=C=>{$(C),de({gen_rules:Object.fromEntries(C.filter(I=>I.tag.trim()).map(I=>[I.tag.trim(),I.value]))})},ne=(C,I)=>ae(Ne.map((J,he)=>he===C?{...J,...I}:J));function B(){const C=a.split(",").map(I=>I.trim()).filter(Boolean);return{...r,client_session_id:w?r.client_session_id:null,venue_session_id:S?r.venue_session_id:null,payload:{...r.payload,order:{...r.payload.order,symbols:C}}}}async function F(){u(""),d(!0);try{const{run_id:C}=await G.perf.runs.create(B());e(C)}catch(C){u(C.message??"Failed to start run")}finally{d(!1)}}async function ee(){u("");try{await G.perf.configs.save(B()),await P()}catch(C){u(C.message??"Failed to save config")}}async function D(C){if(y(C),!!C){u("");try{const I=await G.perf.configs.get(C);n(I),i(I.payload.order.symbols.join(", ")),$(Object.entries(I.payload.gen_rules??{}).map(([J,he])=>({tag:J,value:he})))}catch(I){u(I.message??"Failed to load config")}}}async function Te(){if(v&&window.confirm("Delete this saved config?"))try{await G.perf.configs.delete(v),y(""),await P()}catch(C){u(C.message??"Failed to delete config")}}const oe=C=>{const I=t[C];return I?`${I.display_name||`${I.sender_comp_id} → ${I.target_comp_id}`} (${I.status})`:C},ze=x.filter(C=>C.session_role!=="venue"),We=x.filter(C=>C.session_role!=="client");return c.jsxs("div",{className:"perf-form",children:[l&&c.jsx("div",{className:"error-msg",children:l}),c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Saved configs"}),c.jsxs("div",{className:"perf-field-row",children:[c.jsxs("select",{value:v,onChange:C=>D(C.target.value),children:[c.jsx("option",{value:"",children:"(load a saved config…)"}),m.map(C=>c.jsxs("option",{value:C.config_id,children:[C.name," — ",C.mode]},C.config_id))]}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:ee,children:"Save current"}),c.jsx("button",{className:"btn btn-sm btn-ghost btn-danger-ghost",onClick:Te,disabled:!v,children:"Delete"})]})]}),c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Run"}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Name"}),c.jsx("input",{value:r.name,onChange:C=>W({name:C.target.value})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Mode"}),c.jsx("select",{value:r.mode,onChange:C=>W({mode:C.target.value}),children:nW.map(C=>c.jsx("option",{value:C,children:C},C))})]}),w&&c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Client session"}),c.jsxs("select",{value:r.client_session_id??"",onChange:C=>W({client_session_id:C.target.value||null}),children:[c.jsx("option",{value:"",children:"(select…)"}),ze.map(C=>c.jsx("option",{value:C.session_id,children:oe(C.session_id)},C.session_id))]}),_&&c.jsx(Gp,{status:_.status})]}),S&&c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Venue session"}),c.jsxs("select",{value:r.venue_session_id??"",onChange:C=>W({venue_session_id:C.target.value||null}),children:[c.jsx("option",{value:"",children:"(select…)"}),We.map(C=>c.jsx("option",{value:C.session_id,children:oe(C.session_id)},C.session_id))]}),j&&c.jsx(Gp,{status:j.status})]})]}),c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Rate"}),c.jsxs("div",{className:"perf-field-grid",children:[w&&c.jsxs(c.Fragment,{children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Orders / window"}),c.jsx("input",{type:"number",min:1,value:r.rate.orders_per_window,onChange:C=>V({orders_per_window:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Order window (ms)"}),c.jsx("input",{type:"number",min:1,value:r.rate.order_window_ms,onChange:C=>V({order_window_ms:Number(C.target.value)})})]})]}),S&&c.jsxs(c.Fragment,{children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fills / window"}),c.jsx("input",{type:"number",min:1,value:r.rate.fills_per_window,onChange:C=>V({fills_per_window:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fill window (ms)"}),c.jsx("input",{type:"number",min:1,value:r.rate.fill_window_ms,onChange:C=>V({fill_window_ms:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fill ratio"}),c.jsx("input",{type:"number",min:0,max:1,step:.05,value:r.rate.fill_ratio,onChange:C=>V({fill_ratio:Number(C.target.value)})})]})]})]}),c.jsxs("div",{className:"perf-hint",children:[w&&c.jsxs(c.Fragment,{children:["≈ ",Hp(r.rate.orders_per_window,r.rate.order_window_ms).toLocaleString()," orders/s"]}),w&&S&&" · ",S&&c.jsxs(c.Fragment,{children:["≈ ",Hp(r.rate.fills_per_window,r.rate.fill_window_ms).toLocaleString()," fills/s"]})," ","Smaller windows smooth the injection."]})]}),c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Test"}),c.jsxs("div",{className:"perf-field-grid",children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Duration (s, 0 = no time limit)"}),c.jsx("input",{type:"number",min:0,value:r.test.duration,onChange:C=>se({duration:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Max orders (0 = no count limit)"}),c.jsx("input",{type:"number",min:0,value:r.test.max_orders,onChange:C=>se({max_orders:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Order timeout (ms)"}),c.jsx("input",{type:"number",min:1,step:1e3,value:r.test.scenario_timeout_ms,onChange:C=>se({scenario_timeout_ms:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"On saturation"}),c.jsxs("select",{value:r.test.on_saturation,onChange:C=>se({on_saturation:C.target.value}),children:[c.jsx("option",{value:"pause",children:"pause (preserves latency accuracy)"}),c.jsx("option",{value:"drop_oldest",children:"drop_oldest"})]})]})]}),w&&c.jsxs("label",{className:"checkbox-label",children:[c.jsx("input",{type:"checkbox",checked:r.test.record_execs,onChange:C=>se({record_execs:C.target.checked})}),"Record per-exec detail — one CSV row per ack/fill with latency + inter-report gap (downloads as csv.gz after the run)"]}),c.jsxs("div",{className:"perf-hint",children:["An order not fully filled within the order timeout is counted ",c.jsx("strong",{children:"Lost"}),". Raise it if rate-limited fills are being flagged lost on full-fill runs."]})]}),w&&c.jsxs("div",{className:`perf-form-section ${U?"perf-section-layered":""}`,children:[c.jsxs("div",{className:"perf-section-title",children:["Order payload",U&&c.jsx("span",{className:"perf-layer-badge",children:"values for template"})]}),U&&c.jsxs("div",{className:"perf-hint perf-layer-note",children:["Order template ",c.jsx("strong",{children:R})," defines the message shape. These fields supply the values it references via ",c.jsx("code",{children:"{symbol}"})," ",c.jsx("code",{children:"{side}"})," ",c.jsx("code",{children:"{qty}"})," ",c.jsx("code",{children:"{price}"})," tokens. Value generators (",c.jsx("code",{children:"uuid()"}),", ",c.jsx("code",{children:"random_str(n)"}),", ",c.jsx("code",{children:"random_int(a,b)"}),","," ",c.jsx("code",{children:"seq(width)"}),", ",c.jsx("code",{children:"timestamp()"}),", ",c.jsx("code",{children:"date()"}),") are evaluated fresh per order and mix with text as ",c.jsx("code",{children:"ORD-{seq(8)}"}),"; ClOrdID(11) is always made unique per order. ",c.jsx("strong",{children:"Order type"})," is taken from the template."]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Symbols (comma-separated)"}),c.jsx("input",{value:a,onChange:C=>i(C.target.value)})]}),c.jsxs("div",{className:"perf-field-grid",children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Side"}),c.jsxs("select",{value:r.payload.order.side,onChange:C=>Q({side:C.target.value}),children:[c.jsx("option",{value:"alternate",children:"alternate"}),c.jsx("option",{value:"fixed_buy",children:"fixed_buy"}),c.jsx("option",{value:"fixed_sell",children:"fixed_sell"}),c.jsx("option",{value:"random",children:"random"})]})]}),c.jsxs("label",{className:`perf-field ${U?"perf-field-overridden":""}`,children:[c.jsxs("span",{children:["Order type ",U&&c.jsx("em",{className:"perf-field-tag",children:"from template"})]}),c.jsxs("select",{value:r.payload.order.ord_type,disabled:U,onChange:C=>Q({ord_type:C.target.value}),children:[c.jsx("option",{value:"limit",children:"limit"}),c.jsx("option",{value:"market",children:"market"})]})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Qty min"}),c.jsx("input",{type:"number",min:1,value:r.payload.order.quantity_min,onChange:C=>Q({quantity_min:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Qty max"}),c.jsx("input",{type:"number",min:1,value:r.payload.order.quantity_max,onChange:C=>Q({quantity_max:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Price min"}),c.jsx("input",{type:"number",min:0,step:.01,value:r.payload.order.price_min,onChange:C=>Q({price_min:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Price max"}),c.jsx("input",{type:"number",min:0,step:.01,value:r.payload.order.price_max,onChange:C=>Q({price_max:Number(C.target.value)})})]})]})]}),w&&c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Send options"}),c.jsxs("div",{className:"perf-field-row",children:[c.jsxs("label",{className:"checkbox-label",children:[c.jsx("input",{type:"checkbox",checked:r.payload.auto_expiry??!0,onChange:C=>de({auto_expiry:C.target.checked})}),"Auto expiry (tags 126 / 432, only if present)"]}),(r.payload.auto_expiry??!0)&&c.jsxs(c.Fragment,{children:[c.jsx("input",{type:"number",min:0,style:{width:64},value:r.payload.expiry_offset??1,onChange:C=>de({expiry_offset:Number(C.target.value)})}),c.jsxs("select",{value:r.payload.expiry_unit??"days",onChange:C=>de({expiry_unit:C.target.value}),children:[c.jsx("option",{value:"minutes",children:"minutes"}),c.jsx("option",{value:"hours",children:"hours"}),c.jsx("option",{value:"days",children:"days"})]}),c.jsx("span",{children:"into the future"})]})]}),Ne.map((C,I)=>c.jsxs("div",{className:"perf-field-row",children:[c.jsx("input",{style:{width:72},placeholder:"tag",value:C.tag,onChange:J=>ne(I,{tag:J.target.value.replace(/[^0-9]/g,"")})}),c.jsx("span",{children:"="}),c.jsx("input",{style:{flex:1},placeholder:"uuid() / seq(8) / ORD-{seq(8)} / literal",value:C.value,onChange:J=>ne(I,{value:J.target.value})}),c.jsx("button",{className:"btn btn-sm btn-ghost btn-danger-ghost",onClick:()=>ae(Ne.filter((J,he)=>he!==I)),children:"✕"})]},I)),c.jsx("div",{className:"perf-field-row",children:c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>ae([...Ne,{tag:"",value:""}]),children:"+ Add tag rule"})}),c.jsxs("div",{className:"perf-hint",children:["Rules set the tag on every order/scenario message (insert or replace). Generators: ",c.jsx("code",{children:"uuid()"})," · ",c.jsx("code",{children:"random_str(n)"})," · ",c.jsx("code",{children:"random_int(a,b)"})," ·"," ",c.jsx("code",{children:"seq(width[,start])"})," · ",c.jsx("code",{children:"timestamp()"})," · ",c.jsx("code",{children:"date()"})," — mix with text as ",c.jsx("code",{children:"ORD-{seq(8)}"}),". ClOrdID(11) always stays unique per order."]})]}),S&&c.jsxs("div",{className:`perf-form-section ${T?"perf-section-layered":""}`,children:[c.jsxs("div",{className:"perf-section-title",children:["Fill payload",T&&c.jsx("span",{className:"perf-layer-badge",children:"behavior for template"})]}),T&&c.jsxs("div",{className:"perf-hint perf-layer-note",children:["Exec template ",c.jsx("strong",{children:z})," reshapes each report (custom tags). These still control fill ",c.jsx("strong",{children:"behavior"})," — how many reports, the fill ratio, and the ack — while the engine sets the standard exec tags."]}),c.jsxs("div",{className:"perf-field-grid",children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fill type"}),c.jsxs("select",{value:r.payload.fill.fill_type,onChange:C=>ye({fill_type:C.target.value}),children:[c.jsx("option",{value:"full",children:"full"}),c.jsx("option",{value:"partial",children:"partial"}),c.jsx("option",{value:"random",children:"random"})]})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fills per order"}),c.jsx("input",{type:"number",min:1,value:r.payload.fill.fills_per_order,onChange:C=>ye({fills_per_order:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field perf-field-check",children:[c.jsx("input",{type:"checkbox",checked:r.payload.fill.send_ack,onChange:C=>ye({send_ack:C.target.checked})}),c.jsx("span",{children:"Send ack before fills (35=8|150=0|39=0)"})]}),r.payload.fill.fill_type!=="full"&&c.jsxs(c.Fragment,{children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Partial % min"}),c.jsx("input",{type:"number",min:1,max:100,value:r.payload.fill.partial_fill_pct_min,onChange:C=>ye({partial_fill_pct_min:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Partial % max"}),c.jsx("input",{type:"number",min:1,max:100,value:r.payload.fill.partial_fill_pct_max,onChange:C=>ye({partial_fill_pct_max:Number(C.target.value)})})]})]})]})]}),(w||S)&&c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Templates (optional)"}),c.jsxs("div",{className:"perf-field-grid",children:[w&&c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Order template"}),c.jsxs("select",{value:r.payload.order_template_id??"",onChange:C=>de({order_template_id:C.target.value||null}),children:[c.jsx("option",{value:"",children:"— built-in —"}),H.map(C=>c.jsx("option",{value:C.id,children:C.name},C.id))]})]}),S&&c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Exec report template"}),c.jsxs("select",{value:r.payload.exec_template_id??"",onChange:C=>de({exec_template_id:C.target.value||null}),children:[c.jsx("option",{value:"",children:"— built-in —"}),L.map(C=>c.jsx("option",{value:C.id,children:C.name},C.id))]})]})]}),c.jsx("div",{className:"perf-hint",children:"Optional FIX templates from your Templates library shape the order / execution report (custom & venue-specific tags). Standard tags (correlation, OrderQty, CumQty, ExecType…) are always set by the engine. Leave as built-in for the default messages."})]}),c.jsxs("div",{className:"perf-form-section",children:[c.jsxs("button",{className:"btn btn-sm btn-ghost",onClick:()=>s(C=>!C),children:[o?"▾":"▸"," Advanced"]}),o&&c.jsxs("div",{className:"perf-field-grid",style:{marginTop:8},children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Correlation tag"}),c.jsx("input",{type:"number",min:1,value:r.correlation_tag,onChange:C=>W({correlation_tag:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Exec ID tag"}),c.jsx("input",{type:"number",min:1,value:r.exec_id_tag,onChange:C=>W({exec_id_tag:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Max pending"}),c.jsx("input",{type:"number",min:1,value:r.test.max_pending,onChange:C=>se({max_pending:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field perf-field-check",children:[c.jsx("input",{type:"checkbox",checked:r.rate.allow_burst,onChange:C=>V({allow_burst:C.target.checked})}),c.jsx("span",{children:"Allow burst"})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Max burst multiplier"}),c.jsx("input",{type:"number",min:1,step:.5,value:r.rate.max_burst_multiplier,onChange:C=>V({max_burst_multiplier:Number(C.target.value)})})]})]})]}),c.jsxs("div",{className:"perf-form-actions",children:[k?c.jsx("span",{className:"perf-not-ready",children:k}):A&&!(O&&N)?c.jsxs("span",{className:"perf-not-ready",children:["Selected session",w&&S?"s":""," must be LOGGED_ON before starting."]}):null,c.jsx("button",{className:"btn btn-primary",onClick:F,disabled:!M,title:A?k||(O&&N?void 0:"Session not logged on yet"):"Select the required session(s)",children:f?"Starting…":"▶ Start run"})]})]})}function Gp({status:e}){const t=e==="LOGGED_ON";return c.jsxs("span",{className:`perf-status-chip ${t?"ok":"warn"}`,children:[t?"● ":"○ ",e]})}const Zp=600,iW=5e3,oW=new Set(["completed","stopped","error"]);function sW(e){const[t,r]=h.useState(null),[n,a]=h.useState([]),[i,o]=h.useState(!1);return h.useEffect(()=>{if(r(null),a([]),o(!1),!e)return;let s=!1,l=null,u,f=0,d=!1;async function m(){if(s||d||!e)return;let v;try{({ticket:v}=await G.perf.runs.ticket(e))}catch{p();return}if(s)return;const g=`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/api/perf/runs/${e}/live?ticket=${v}`;l=new WebSocket(g),l.onopen=()=>{s||(f=0,o(!0))},l.onmessage=b=>{let x;try{x=JSON.parse(b.data)}catch{return}r(x),a(w=>{var j,O,N,A,k,M,P,H;const S={t:x.elapsed_s,ops:((j=x.client)==null?void 0:j.ops_live)??0,fps:((O=x.venue)==null?void 0:O.fps_live)??0,tps:(((N=x.client)==null?void 0:N.ops_live)??0)+(((A=x.venue)==null?void 0:A.fps_live)??0),p50:(((k=x.response_latency)==null?void 0:k.p50_us)??0)/1e3,p99:(((M=x.response_latency)==null?void 0:M.p99_us)??0)/1e3,fc50:(((P=x.fill_completion_latency)==null?void 0:P.p50_us)??0)/1e3,fc99:(((H=x.fill_completion_latency)==null?void 0:H.p99_us)??0)/1e3},_=[...w,S];return _.length>Zp?_.slice(-Zp):_}),oW.has(x.status)&&(d=!0,l==null||l.close())},l.onclose=()=>{s||(o(!1),d||p())},l.onerror=()=>{l==null||l.close()}}function p(){if(s||d)return;f+=1;const v=Math.min(iW,500*2**Math.min(f,4));u=setTimeout(m,v)}return m(),()=>{s=!0,clearTimeout(u),l==null||l.close()}},[e]),{latest:t,series:n,connected:i}}const Dw="img-export-exclude";async function lW(e,t,r){const{toPng:n,toJpeg:a}=await Cv(async()=>{const{toPng:u,toJpeg:f}=await import("./index-CyNOPa0n.js");return{toPng:u,toJpeg:f}},[]),i=getComputedStyle(document.body).backgroundColor||"#111",s=await(r==="png"?n:a)(e,{backgroundColor:i,pixelRatio:2,quality:.95,filter:u=>!(u instanceof HTMLElement&&u.classList.contains(Dw))}),l=document.createElement("a");l.href=s,l.download=t,l.click()}function cW(e,t){const r=new Date().toISOString().slice(0,19).replace(/[:T]/g,"-");return`${e}-${r}.${t}`}const _l=new Set(["completed","stopped","error"]);function wn(e){return e?(e/1e3).toFixed(3):"—"}function Rw(e,t){return t<=0?"—":(e/t).toFixed(2)}function Kp({title:e,sub:t,stats:r}){return c.jsxs("div",{className:"perf-lat-card",children:[c.jsxs("div",{className:"perf-lat-title",children:[e,t&&c.jsx("span",{className:"perf-lat-sub",children:t})]}),c.jsx("div",{className:"perf-lat-grid",children:["p50_us","p95_us","p99_us","max_us"].map(n=>c.jsxs("div",{className:"perf-lat-cell",children:[c.jsx("span",{className:"perf-lat-val",children:wn(r[n])}),c.jsxs("span",{className:"perf-lat-lbl",children:[n.replace("_us","")," ms"]})]},n))})]})}function uW({runId:e}){var x;const{latest:t,series:r,connected:n}=sW(e),[a,i]=h.useState(null),[o,s]=h.useState(!1),l=h.useRef(null);h.useEffect(()=>{let w=!0;i(null);let S;const _=async()=>{try{const j=await G.perf.runs.get(e);if(!w)return;i(j),_l.has(j.status)&&clearInterval(S)}catch{}};return S=setInterval(_,2e3),_(),()=>{w=!1,clearInterval(S)}},[e]);const u=t?_l.has(t.status):a?_l.has(a.status):!1,f=!u;async function d(){try{await G.perf.runs.stop(e)}catch{}}async function m(w){if(!(!l.current||o)){s(!0);try{const S=w==="png"?"png":"jpg";await lW(l.current,cW(`perf-${e.slice(0,8)}`,S),w)}catch(S){console.error("image export failed",S)}finally{s(!1)}}}const p=t==null?void 0:t.client,v=t==null?void 0:t.venue,y=t==null?void 0:t.errors,g=(y==null?void 0:y.saturated)||(t==null?void 0:t.status)==="saturated",b=[];return p&&b.push({label:"ops/s",value:Math.round(p.ops_live).toLocaleString()},{label:"orders sent",value:p.orders_sent.toLocaleString()},{label:"pending",value:p.pending.toLocaleString()},{label:"dropped",value:p.dropped.toLocaleString()}),v&&b.push({label:"fills/s",value:Math.round(v.fps_live).toLocaleString()},{label:"fills",value:v.fills_sent.toLocaleString()},{label:"fills/order",value:Rw(v.fills_sent,v.orders_received-v.unfilled)},{label:"fill ratio",value:v.fill_ratio.toFixed(3)},{label:"recv",value:v.orders_received.toLocaleString()},{label:"acks",value:v.acks_sent.toLocaleString()},{label:"unfilled",value:v.unfilled.toLocaleString()}),y&&b.push({label:"lost",value:y.lost_timeout.toLocaleString()},{label:"rejected",value:y.rejected.toLocaleString()}),c.jsxs("div",{className:"perf-dashboard",ref:l,children:[c.jsxs("div",{className:"perf-dash-header",children:[c.jsxs("div",{className:"perf-dash-status",children:[c.jsx("span",{className:`badge perf-state-${(t==null?void 0:t.status)??(a==null?void 0:a.status)??"pending"}`,children:(t==null?void 0:t.status)??(a==null?void 0:a.status)??"pending"}),t&&c.jsxs("span",{className:"perf-dash-elapsed",children:[Math.round(t.elapsed_s),"s",t.duration_s?` / ${t.duration_s}s`:""," · ",t.mode]}),c.jsx("span",{className:`perf-conn ${n?"on":"off"}`,children:n?"● live":"○ offline"})]}),c.jsxs("div",{className:`perf-dash-actions ${Dw}`,children:[f&&c.jsx("button",{className:"btn btn-sm btn-danger",onClick:d,children:"■ Stop"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>G.perf.runs.export(e,"messages"),children:"Export CSV"}),u&&c.jsx("button",{className:"btn btn-sm btn-ghost",title:"Per-exec detail (requires record_execs)",onClick:()=>G.perf.runs.export(e,"execs"),children:"Execs CSV"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>G.perf.runs.export(e,"both"),children:"Export ZIP"}),c.jsx("button",{className:"btn btn-sm btn-ghost",disabled:o,onClick:()=>m("png"),children:o?"…":"PNG"}),c.jsx("button",{className:"btn btn-sm btn-ghost",disabled:o,onClick:()=>m("jpeg"),children:o?"…":"JPG"})]})]}),g&&c.jsxs("div",{className:"perf-saturated-banner",children:["⚠ SATURATED — injection paused to protect latency measurement.",p&&c.jsxs(c.Fragment,{children:[" Pending ",p.pending.toLocaleString(),", dropped ",p.dropped.toLocaleString(),"."]})]}),(x=a==null?void 0:a.warnings)==null?void 0:x.map((w,S)=>c.jsxs("div",{className:"perf-warning-banner",children:["⚠ ",w]},S)),!t&&!u&&c.jsx("div",{className:"loading-msg",children:"Waiting for first snapshot…"}),b.length>0&&c.jsx("div",{className:"stat-cards",children:b.map(({label:w,value:S})=>c.jsxs("div",{className:"stat-card",children:[c.jsx("span",{className:"stat-card-value",children:S}),c.jsx("span",{className:"stat-card-label",children:w})]},w))}),t&&c.jsxs("div",{className:"perf-lat-row",children:[c.jsx(Kp,{title:"Response latency",sub:"order-2-ack",stats:t.response_latency}),c.jsx(Kp,{title:"Fill-completion latency",sub:"order-2-last-fill",stats:t.fill_completion_latency})]}),r.length>1&&c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"analysis-section-title",children:"Throughput"}),c.jsx("div",{className:"analysis-chart-wrap",children:c.jsx(Pn,{width:"100%",height:200,children:c.jsxs(Ii,{data:r,margin:{top:4,right:16,bottom:4,left:8},children:[c.jsx(Gr,{strokeDasharray:"3 3",stroke:"var(--border)"}),c.jsx(Zr,{dataKey:"t",tick:{fontSize:11,fill:"var(--text2)"},tickFormatter:w=>`${Math.round(w)}s`}),c.jsx(Kr,{tick:{fontSize:11,fill:"var(--text2)"},allowDecimals:!1}),c.jsx(En,{contentStyle:{background:"var(--bg3)",border:"1px solid var(--border)",fontSize:12},labelStyle:{color:"var(--text)"},labelFormatter:w=>`${Math.round(Number(w))}s`}),c.jsx(pa,{wrapperStyle:{fontSize:11}}),c.jsx(_t,{type:"monotone",dataKey:"ops",name:"orders/s",stroke:"#00b4c8",dot:!1,isAnimationActive:!1}),c.jsx(_t,{type:"monotone",dataKey:"fps",name:"fills/s",stroke:"#a78bfa",dot:!1,isAnimationActive:!1}),c.jsx(_t,{type:"monotone",dataKey:"tps",name:"total msgs/s",stroke:"#fbbf24",dot:!1,isAnimationActive:!1})]})})}),c.jsx("div",{className:"analysis-section-title",children:"Response latency (ms)"}),c.jsx("div",{className:"analysis-chart-wrap",children:c.jsx(Pn,{width:"100%",height:200,children:c.jsxs(Ii,{data:r,margin:{top:4,right:16,bottom:4,left:8},children:[c.jsx(Gr,{strokeDasharray:"3 3",stroke:"var(--border)"}),c.jsx(Zr,{dataKey:"t",tick:{fontSize:11,fill:"var(--text2)"},tickFormatter:w=>`${Math.round(w)}s`}),c.jsx(Kr,{tick:{fontSize:11,fill:"var(--text2)"}}),c.jsx(En,{contentStyle:{background:"var(--bg3)",border:"1px solid var(--border)",fontSize:12},labelStyle:{color:"var(--text)"},labelFormatter:w=>`${Math.round(Number(w))}s`}),c.jsx(pa,{wrapperStyle:{fontSize:11}}),c.jsx(_t,{type:"monotone",dataKey:"p50",name:"p50",stroke:"#4ade80",dot:!1,isAnimationActive:!1}),c.jsx(_t,{type:"monotone",dataKey:"p99",name:"p99",stroke:"#f87171",dot:!1,isAnimationActive:!1})]})})}),c.jsx("div",{className:"analysis-section-title",children:"Fill-completion latency (ms)"}),c.jsx("div",{className:"analysis-chart-wrap",children:c.jsx(Pn,{width:"100%",height:200,children:c.jsxs(Ii,{data:r,margin:{top:4,right:16,bottom:4,left:8},children:[c.jsx(Gr,{strokeDasharray:"3 3",stroke:"var(--border)"}),c.jsx(Zr,{dataKey:"t",tick:{fontSize:11,fill:"var(--text2)"},tickFormatter:w=>`${Math.round(w)}s`}),c.jsx(Kr,{tick:{fontSize:11,fill:"var(--text2)"}}),c.jsx(En,{contentStyle:{background:"var(--bg3)",border:"1px solid var(--border)",fontSize:12},labelStyle:{color:"var(--text)"},labelFormatter:w=>`${Math.round(Number(w))}s`}),c.jsx(pa,{wrapperStyle:{fontSize:11}}),c.jsx(_t,{type:"monotone",dataKey:"fc50",name:"p50",stroke:"#4ade80",dot:!1,isAnimationActive:!1}),c.jsx(_t,{type:"monotone",dataKey:"fc99",name:"p99",stroke:"#f87171",dot:!1,isAnimationActive:!1})]})})})]}),u&&(a==null?void 0:a.summary)&&c.jsx(dW,{status:a})]})}function dW({status:e}){const t=e.summary,r=[["Orders sent",t.orders_sent.toLocaleString()],["Acks sent",t.acks_sent.toLocaleString()],["Fills sent",t.fills_sent.toLocaleString()],["Responses",t.responses.toLocaleString()],["Completions (fully filled)",t.completions.toLocaleString()],["Fills per order (completed)",Rw(t.fills_sent,t.completions)],["Fill ratio (completed)",t.fill_ratio.toFixed(3)],["Lost / dropped / rejected",`${t.lost_timeout} / ${t.dropped} / ${t.rejected}`],["Response p50 / p99 (ms)",`${wn(t.response_latency.p50_us)} / ${wn(t.response_latency.p99_us)}`],["Response max (ms)",wn(t.response_latency.max_us)],["Fill-completion p50 / p99 (ms)",`${wn(t.fill_completion_latency.p50_us)} / ${wn(t.fill_completion_latency.p99_us)}`]];return c.jsxs("div",{className:"perf-summary-card",children:[c.jsxs("div",{className:"perf-section-title",children:["Run summary — ",e.status]}),c.jsx("table",{className:"perf-summary-table",children:c.jsx("tbody",{children:r.map(([n,a])=>c.jsxs("tr",{children:[c.jsx("td",{children:n}),c.jsx("td",{className:"mono",children:a})]},n))})})]})}const fW=new Set(["completed","stopped","error"]);function hW(e){if(!e)return"—";try{return new Date(e*1e3).toLocaleTimeString()}catch{return"—"}}function mW({activeRunId:e,onSelect:t,refreshKey:r}){const[n,a]=h.useState([]);h.useEffect(()=>{let o=!0;const s=()=>G.perf.runs.list().then(u=>{o&&a(u)}).catch(()=>{});s();const l=setInterval(s,2e3);return()=>{o=!1,clearInterval(l)}},[r]);async function i(o,s){o.stopPropagation();try{await G.perf.runs.stop(s)}catch{}}return n.length===0?c.jsx("div",{className:"perf-runlist-empty",children:"No runs yet. Configure and start one."}):c.jsxs("table",{className:"perf-runlist",children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Name"}),c.jsx("th",{children:"Mode"}),c.jsx("th",{children:"Status"}),c.jsx("th",{children:"Started"}),c.jsx("th",{children:"Elapsed"}),c.jsx("th",{children:"Orders"}),c.jsx("th",{children:"Fill"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:n.map(o=>c.jsxs("tr",{className:o.run_id===e?"perf-run-active":"",onClick:()=>t(o.run_id),children:[c.jsx("td",{children:o.name}),c.jsx("td",{children:o.mode}),c.jsx("td",{children:c.jsx("span",{className:`badge perf-state-${o.status}`,children:o.status})}),c.jsx("td",{children:hW(o.started_at)}),c.jsxs("td",{className:"mono",children:[Math.round(o.elapsed_s),"s"]}),c.jsx("td",{className:"mono",children:o.summary?o.summary.orders_sent.toLocaleString():"—"}),c.jsx("td",{className:"mono",children:o.summary?o.summary.fill_ratio.toFixed(2):"—"}),c.jsx("td",{className:"perf-runlist-actions",children:!fW.has(o.status)&&c.jsx("button",{className:"btn btn-sm btn-danger",onClick:s=>i(s,o.run_id),children:"Stop"})})]},o.run_id))})]})}const Ol=200,_n=(e,t,r=120)=>({field:e,headerName:t,width:r,type:"numericColumn",valueFormatter:n=>n.value==null?"":Number(n.value).toLocaleString()}),Pc=(e,t)=>({field:e,headerName:t,width:150,type:"numericColumn",valueFormatter:r=>r.value==null?"":`${Math.round(Number(r.value)).toLocaleString()}`}),pW=[{field:"clordid",headerName:"ClOrdID",width:150},{field:"corr_id",headerName:"Corr ID",width:150},{field:"msg_type",headerName:"Type",width:80},{field:"symbol",headerName:"Symbol",width:90},{field:"side",headerName:"Side",width:70},_n("qty","Qty",90),_n("price","Price",100),Pc("response_latency_us","Resp µs"),Pc("fill_latency_us","Fill µs"),_n("fill_qty","Fill qty",100),_n("fill_price","Fill price",110),{field:"status",headerName:"Status",width:110,cellStyle:e=>e.value==="lost"||e.value==="dropped"?{color:"#facc15",fontWeight:"bold"}:void 0}],vW=[{field:"scenario_name",headerName:"Scenario",flex:1,minWidth:160},Pc("latency_us","Latency µs"),_n("msg_count","Msgs",90),_n("fill_ratio","Fill ratio",110),{field:"status",headerName:"Status",width:110,cellStyle:e=>e.value==="lost"||e.value==="dropped"?{color:"#facc15",fontWeight:"bold"}:void 0}];function gW({runId:e}){const t=X(g=>g.theme),[r,n]=h.useState("messages"),[a,i]=h.useState(0),[o,s]=h.useState([]),[l,u]=h.useState(0),[f,d]=h.useState(!1);h.useEffect(()=>{i(0)},[r,e]),h.useEffect(()=>{let g=!0;return d(!0),(r==="messages"?G.perf.runs.messages:G.perf.runs.scenarios)(e,{limit:Ol,offset:a*Ol}).then(x=>{g&&(s(x.items),u(x.total))}).catch(()=>{g&&(s([]),u(0))}).finally(()=>{g&&d(!1)}),()=>{g=!1}},[e,r,a]);const m=h.useMemo(()=>r==="messages"?pW:vW,[r]),p=h.useMemo(()=>({sortable:!0,filter:!0,resizable:!0}),[]),v=Math.max(0,Math.ceil(l/Ol)-1),y=t==="dark"?"ag-theme-quartz-dark":"ag-theme-quartz";return c.jsxs("div",{className:"perf-results",children:[c.jsxs("div",{className:"perf-results-toolbar",children:[c.jsxs("div",{className:"perf-results-tabs",children:[c.jsx("button",{className:`btn btn-sm ${r==="messages"?"btn-primary":"btn-ghost"}`,onClick:()=>n("messages"),children:"Messages"}),c.jsx("button",{className:`btn btn-sm ${r==="scenarios"?"btn-primary":"btn-ghost"}`,onClick:()=>n("scenarios"),children:"Scenarios"})]}),c.jsxs("div",{className:"perf-results-pager",children:[c.jsxs("span",{children:[l.toLocaleString()," rows"]}),c.jsx("button",{className:"btn btn-sm btn-ghost",disabled:a<=0||f,onClick:()=>i(g=>Math.max(0,g-1)),children:"‹ Prev"}),c.jsxs("span",{children:["Page ",a+1," / ",v+1]}),c.jsx("button",{className:"btn btn-sm btn-ghost",disabled:a>=v||f,onClick:()=>i(g=>Math.min(v,g+1)),children:"Next ›"})]})]}),c.jsx("div",{className:`${y} perf-results-grid`,children:c.jsx(Nc,{rowData:o,columnDefs:m,defaultColDef:p,animateRows:!1,suppressCellFocus:!0})})]})}function yW(){const{theme:e,toggleTheme:t,branding:r,setSessions:n}=X(),a=Us(),i=r!=null&&r.prefix?`${r.prefix} FIXture`:"FIXture";Sv(),h.useEffect(()=>{G.sessions.list().then(n).catch(()=>{})},[]);const[o,s]=h.useState("configure"),[l,u]=h.useState(null),[f,d]=h.useState(0);function m(y){u(y),d(g=>g+1),s("live")}function p(y){u(y),s("live")}const v=[{id:"configure",label:"Configure"},{id:"live",label:"Live",disabled:!l},{id:"results",label:"Results",disabled:!l},{id:"history",label:"History"}];return c.jsxs("div",{className:"app perf-page",children:[c.jsxs("header",{className:"app-header",children:[c.jsx("img",{src:a,alt:i,className:"app-logo"}),c.jsx("div",{className:"perf-page-title",children:"Performance Testing"}),c.jsxs("div",{className:"header-right",children:[c.jsx(qr,{to:"/",className:"btn btn-sm btn-ghost",children:"← Sessions"}),c.jsx("button",{className:"btn-icon theme-toggle",onClick:t,title:"Toggle theme",children:e==="dark"?"☀":"🌙"})]})]}),c.jsx("div",{className:"perf-tabbar",children:v.map(y=>c.jsx("button",{className:`perf-tab${o===y.id?" active":""}`,disabled:y.disabled,onClick:()=>s(y.id),children:y.label},y.id))}),c.jsxs("div",{className:"perf-body",children:[o==="configure"&&c.jsx("div",{className:"perf-pane perf-pane-form",children:c.jsx(aW,{onStarted:m})}),o==="live"&&l&&c.jsx("div",{className:"perf-pane",children:c.jsx(uW,{runId:l},l)}),o==="results"&&l&&c.jsx("div",{className:"perf-pane",children:c.jsx(gW,{runId:l},l)}),o==="history"&&c.jsx("div",{className:"perf-pane",children:c.jsx(mW,{activeRunId:l,onSelect:p,refreshKey:f})})]})]})}function bW(){return X(t=>t.token)?c.jsx(uv,{}):c.jsx(Mi,{to:"/login",replace:!0})}function xW(){const e=X(t=>t.user);return e?e.role!=="platform_admin"?c.jsx(Mi,{to:"/",replace:!0}):c.jsx(uv,{}):c.jsx(Mi,{to:"/login",replace:!0})}function wW(){const e=X(t=>t.setBranding);h.useEffect(()=>{fetch("/api/branding").then(t=>t.json()).then(t=>{e(t),Tw(t)}).catch(()=>{})},[])}function jW(){const e=an(),t=X(r=>r.theme);return wW(),h.useEffect(()=>{document.documentElement.setAttribute("data-theme",t)},[t]),h.useEffect(()=>{fetch("/api/setup/status").then(r=>r.json()).then(r=>{r.setup_required&&e("/setup",{replace:!0})}).catch(()=>{})},[]),c.jsxs(Zj,{children:[c.jsx(Lt,{path:"/setup",element:c.jsx(Xz,{})}),c.jsx(Lt,{path:"/login",element:c.jsx(Yz,{})}),c.jsx(Lt,{path:"/register",element:c.jsx(Vz,{})}),c.jsxs(Lt,{element:c.jsx(bW,{}),children:[c.jsx(Lt,{path:"/",element:c.jsx(qz,{})}),c.jsxs(Lt,{element:c.jsx(xW,{}),children:[c.jsx(Lt,{path:"/admin/users",element:c.jsx(rW,{})}),c.jsx(Lt,{path:"/perf",element:c.jsx(yW,{})})]})]}),c.jsx(Lt,{path:"*",element:c.jsx(Mi,{to:"/",replace:!0})})]})}Pl.createRoot(document.getElementById("root")).render(c.jsx(qp.StrictMode,{children:c.jsx(vS,{children:c.jsx(jW,{})})}));
|
|
102
|
+
}`;function Kz({onClose:e}){const[t,r]=h.useState(""),[n,a]=h.useState(""),[i,o]=h.useState(!1);h.useEffect(()=>{G.specOverlay.get().then(l=>r(JSON.stringify(l,null,2))).catch(console.error)},[]);async function s(){a("");let l;try{l=JSON.parse(t)}catch{a("Invalid JSON");return}o(!0);try{await G.specOverlay.set(l),e()}catch(u){a(u instanceof Error?u.message:"Save failed")}finally{o(!1)}}return c.jsx("div",{className:"modal-overlay",onClick:e,children:c.jsxs("div",{className:"modal",style:{width:660,maxHeight:"85vh",display:"flex",flexDirection:"column"},onClick:l=>l.stopPropagation(),children:[c.jsxs("div",{className:"modal-header",children:[c.jsx("span",{children:"Spec Overlay — Custom Message Types"}),c.jsx("button",{className:"btn-icon",onClick:e,children:"✕"})]}),c.jsx("div",{style:{padding:"10px 16px 4px",fontSize:12,color:"var(--text2)"},children:"Define custom / proprietary message types (e.g. 35=U1). Fields reference standard tag numbers; use Custom Tags for non-standard tag names. Field names and types are resolved from the spec at runtime."}),c.jsx("div",{style:{flex:1,display:"flex",flexDirection:"column",padding:"8px 16px",minHeight:0},children:c.jsx("textarea",{style:{flex:1,minHeight:300,fontFamily:"var(--font)",fontSize:12,background:"var(--bg3)",color:"var(--text)",border:"1px solid var(--border)",borderRadius:4,padding:"8px 10px",resize:"none"},value:t,onChange:l=>r(l.target.value),spellCheck:!1,placeholder:Zz})}),c.jsxs("div",{style:{padding:"0 16px 8px",fontSize:11,color:"var(--text2)"},children:["Schema: ",c.jsx("code",{children:'{ "messages": [{ "msg_type", "name", "category", "fields": [{ "tag", "required" }] }] }'})]}),n&&c.jsx("div",{className:"tpl-error",style:{margin:"0 16px 8px"},children:n}),c.jsxs("div",{className:"modal-footer",children:[c.jsx("button",{className:"btn btn-ghost",onClick:e,children:"Cancel"}),c.jsx("button",{className:"btn btn-primary",onClick:s,disabled:i,children:i?"Saving…":"Save overlay"})]})]})})}const Iw="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgdmlld0JveD0iMCAwIDY4MCAxNjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgCgogIDxwb2x5Z29uIHBvaW50cz0iMTg4LDI4IDIzOCwyOCAyNTgsNzggMjM4LDEyOCAxODgsMTI4IiBmaWxsPSIjMWEzYTVjIiBzdHlsZT0iZmlsbDpyZ2IoMjYsIDU4LCA5Mik7c3Ryb2tlOm5vbmU7Y29sb3I6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7b3BhY2l0eToxO2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGNpcmNsZSBjeD0iMjA3IiBjeT0iNzgiIHI9IjEwIiBmaWxsPSIjMDBiNGM4IiBzdHlsZT0iZmlsbDpyZ2IoMCwgMTgwLCAyMDApO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxjaXJjbGUgY3g9IjIwNyIgY3k9Ijc4IiByPSI2IiBmaWxsPSIjMWEzYTVjIiBzdHlsZT0iZmlsbDpyZ2IoMjYsIDU4LCA5Mik7c3Ryb2tlOm5vbmU7Y29sb3I6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7b3BhY2l0eToxO2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGxpbmUgeDE9IjIyMCIgeTE9IjY1IiB4Mj0iMjQ4IiB5Mj0iNjUiIHN0cm9rZT0iIzAwYjRjOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0eWxlPSJmaWxsOnJnYigwLCAwLCAwKTtzdHJva2U6cmdiKDAsIDE4MCwgMjAwKTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjRweDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7b3BhY2l0eToxO2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGxpbmUgeDE9IjIyMCIgeTE9Ijc4IiB4Mj0iMjUyIiB5Mj0iNzgiIHN0cm9rZT0iIzAwYjRjOCIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9IjAuNyIgc3R5bGU9ImZpbGw6cmdiKDAsIDAsIDApO3N0cm9rZTpyZ2IoMCwgMTgwLCAyMDApO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6NHB4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjAuNztmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxsaW5lIHgxPSIyMjAiIHkxPSI5MSIgeDI9IjI0NCIgeTI9IjkxIiBzdHJva2U9IiMwMGI0YzgiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBvcGFjaXR5PSIwLjQ1IiBzdHlsZT0iZmlsbDpyZ2IoMCwgMCwgMCk7c3Ryb2tlOnJnYigwLCAxODAsIDIwMCk7Y29sb3I6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZS13aWR0aDo0cHg7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MC40NTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgoKICA8dGV4dCB4PSIyNzIiIHk9IjEwNSIgZm9udC1mYW1pbHk9InZhcigtLWZvbnQtbW9ubykiIGZvbnQtc2l6ZT0iNjYiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMwMGI0YzgiIGxldHRlci1zcGFjaW5nPSItMSIgc3R5bGU9ImZpbGw6cmdiKDAsIDE4MCwgMjAwKTtzdHJva2U6bm9uZTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjE7Zm9udC1mYW1pbHk6dWktbW9ub3NwYWNlLCBtb25vc3BhY2U7Zm9udC1zaXplOjY2cHg7Zm9udC13ZWlnaHQ6NzAwO3RleHQtYW5jaG9yOnN0YXJ0O2RvbWluYW50LWJhc2VsaW5lOmF1dG8iPkZJWDwvdGV4dD4KICA8dGV4dCB4PSIzODYiIHk9IjEwNSIgZm9udC1mYW1pbHk9InZhcigtLWZvbnQtc2FucykiIGZvbnQtc2l6ZT0iNTAiIGZvbnQtd2VpZ2h0PSIzMDAiIGxldHRlci1zcGFjaW5nPSItMSIgc3R5bGU9ImZpbGw6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6NTBweDtmb250LXdlaWdodDozMDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byI+dHVyZTwvdGV4dD4KPC9zdmc+",kw="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgdmlld0JveD0iMCAwIDY4MCAxNjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CgogIDxwb2x5Z29uIHBvaW50cz0iMTg4LDI4IDIzOCwyOCAyNTgsNzggMjM4LDEyOCAxODgsMTI4IiBmaWxsPSIjMDBiNGM4IiBzdHlsZT0iZmlsbDpyZ2IoMCwgMTgwLCAyMDApO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxjaXJjbGUgY3g9IjIwNyIgY3k9Ijc4IiByPSIxMCIgZmlsbD0iIzFhM2E1YyIgc3R5bGU9ImZpbGw6cmdiKDI2LCA1OCwgOTIpO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxjaXJjbGUgY3g9IjIwNyIgY3k9Ijc4IiByPSI2IiBmaWxsPSIjMDBiNGM4IiBzdHlsZT0iZmlsbDpyZ2IoMCwgMTgwLCAyMDApO3N0cm9rZTpub25lO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MTtmb250LWZhbWlseTomcXVvdDtBbnRocm9waWMgU2FucyZxdW90OywgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAmcXVvdDtTZWdvZSBVSSZxdW90Oywgc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDtmb250LXdlaWdodDo0MDA7dGV4dC1hbmNob3I6c3RhcnQ7ZG9taW5hbnQtYmFzZWxpbmU6YXV0byIvPgogIDxsaW5lIHgxPSIyMjAiIHkxPSI2NSIgeDI9IjI0OCIgeTI9IjY1IiBzdHJva2U9IiMxYTNhNWMiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHlsZT0iZmlsbDpyZ2IoMCwgMCwgMCk7c3Ryb2tlOnJnYigyNiwgNTgsIDkyKTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjRweDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46bWl0ZXI7b3BhY2l0eToxO2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGxpbmUgeDE9IjIyMCIgeTE9Ijc4IiB4Mj0iMjUyIiB5Mj0iNzgiIHN0cm9rZT0iIzFhM2E1YyIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9IjAuNyIgc3R5bGU9ImZpbGw6cmdiKDAsIDAsIDApO3N0cm9rZTpyZ2IoMjYsIDU4LCA5Mik7Y29sb3I6cmdiKDI1NSwgMjU1LCAyNTUpO3N0cm9rZS13aWR0aDo0cHg7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO29wYWNpdHk6MC43O2ZvbnQtZmFtaWx5OiZxdW90O0FudGhyb3BpYyBTYW5zJnF1b3Q7LCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICZxdW90O1NlZ29lIFVJJnF1b3Q7LCBzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0LWFuY2hvcjpzdGFydDtkb21pbmFudC1iYXNlbGluZTphdXRvIi8+CiAgPGxpbmUgeDE9IjIyMCIgeTE9IjkxIiB4Mj0iMjQ0IiB5Mj0iOTEiIHN0cm9rZT0iIzFhM2E1YyIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9IjAuNDUiIHN0eWxlPSJmaWxsOnJnYigwLCAwLCAwKTtzdHJva2U6cmdiKDI2LCA1OCwgOTIpO2NvbG9yOnJnYigyNTUsIDI1NSwgMjU1KTtzdHJva2Utd2lkdGg6NHB4O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjAuNDU7Zm9udC1mYW1pbHk6JnF1b3Q7QW50aHJvcGljIFNhbnMmcXVvdDssIC1hcHBsZS1zeXN0ZW0sIEJsaW5rTWFjU3lzdGVtRm9udCwgJnF1b3Q7U2Vnb2UgVUkmcXVvdDssIHNhbnMtc2VyaWY7Zm9udC1zaXplOjE2cHg7Zm9udC13ZWlnaHQ6NDAwO3RleHQtYW5jaG9yOnN0YXJ0O2RvbWluYW50LWJhc2VsaW5lOmF1dG8iLz4KCiAgPHRleHQgeD0iMjcyIiB5PSIxMDUiIGZvbnQtZmFtaWx5PSJ2YXIoLS1mb250LW1vbm8pIiBmb250LXNpemU9IjY2IiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMWEzYTVjIiBsZXR0ZXItc3BhY2luZz0iLTEiIHN0eWxlPSJmaWxsOnJnYigyNiwgNTgsIDkyKTtzdHJva2U6bm9uZTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjE7Zm9udC1mYW1pbHk6dWktbW9ub3NwYWNlLCBtb25vc3BhY2U7Zm9udC1zaXplOjY2cHg7Zm9udC13ZWlnaHQ6NzAwO3RleHQtYW5jaG9yOnN0YXJ0O2RvbWluYW50LWJhc2VsaW5lOmF1dG8iPkZJWDwvdGV4dD4KICA8dGV4dCB4PSIzODYiIHk9IjEwNSIgZm9udC1mYW1pbHk9InZhcigtLWZvbnQtc2FucykiIGZvbnQtc2l6ZT0iNTAiIGZvbnQtd2VpZ2h0PSIzMDAiIGZpbGw9IiMwMGI0YzgiIGxldHRlci1zcGFjaW5nPSItMSIgc3R5bGU9ImZpbGw6cmdiKDAsIDE4MCwgMjAwKTtzdHJva2U6bm9uZTtjb2xvcjpyZ2IoMjU1LCAyNTUsIDI1NSk7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtvcGFjaXR5OjE7Zm9udC1mYW1pbHk6JnF1b3Q7QW50aHJvcGljIFNhbnMmcXVvdDssIHNhbnMtc2VyaWY7Zm9udC1zaXplOjUwcHg7Zm9udC13ZWlnaHQ6MzAwO3RleHQtYW5jaG9yOnN0YXJ0O2RvbWluYW50LWJhc2VsaW5lOmF1dG8iPnR1cmU8L3RleHQ+Cjwvc3ZnPg==";function Us(){const e=X(r=>r.theme),t=X(r=>r.branding);return e==="dark"?(t==null?void 0:t.logo_dark)||Iw:(t==null?void 0:t.logo_light)||kw}function qz(){const e=X(g=>g.setSessions),t=X(g=>g.theme),r=X(g=>g.toggleTheme),n=X(g=>g.user),a=X(g=>g.logout),i=X(g=>g.openTabs),o=X(g=>g.activeTabId),s=X(g=>g.branding),l=Us(),u=s!=null&&s.prefix?`${s.prefix} FIXture`:"FIXture",[f,d]=h.useState(!1),[m,p]=h.useState(!1),[v,y]=h.useState(!1);return Sv(),h.useEffect(()=>{document.documentElement.setAttribute("data-theme",t)},[t]),h.useEffect(()=>{G.sessions.list().then(e).catch(console.error)},[]),h.useEffect(()=>{if(!n){n1();return}G.customTags.list().then(jv).catch(console.error)},[n]),c.jsxs("div",{className:"app",children:[c.jsxs("header",{className:"app-header",children:[c.jsx("img",{src:l,alt:u,className:"app-logo"}),c.jsxs("div",{className:"header-right",children:[n&&c.jsx("span",{className:"header-user",children:n.username}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>d(!0),children:"Scenarios"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p(!0),children:"Custom Tags"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>y(!0),children:"Spec Overlay"}),(n==null?void 0:n.role)==="platform_admin"&&c.jsx(qr,{to:"/perf",className:"btn btn-sm btn-ghost",title:"Performance testing",children:"Perf"}),(n==null?void 0:n.role)==="platform_admin"&&c.jsx(qr,{to:"/admin/users",className:"btn-icon",title:"Admin",children:"⚙"}),c.jsx("button",{className:"btn-icon theme-toggle",onClick:r,title:"Toggle theme",children:t==="dark"?"☀":"🌙"}),c.jsx("button",{className:"btn-icon",onClick:a,title:"Sign out",children:"⎋"})]})]}),c.jsxs("div",{className:"app-body",children:[c.jsx(g1,{}),c.jsx("div",{className:"tab-area",children:i.length===0?c.jsx("div",{className:"tab-area-empty",children:"Select a session to open it."}):c.jsxs(c.Fragment,{children:[c.jsx(Az,{}),c.jsx("div",{className:"tab-panels",children:i.map(g=>c.jsx("div",{className:`tab-panel${g===o?" active":""}`,children:c.jsx(Cz,{sessionId:g})},g))})]})})]}),f&&c.jsx(Bz,{onClose:()=>d(!1)}),m&&c.jsx(Gz,{onClose:()=>p(!1)}),v&&c.jsx(Kz,{onClose:()=>y(!1)})]})}function Yz(){const[e,t]=h.useState(""),[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState(!1),[l,u]=h.useState(!1),{login:f}=X(),d=Us(),m=an();h.useEffect(()=>{fetch("/api/auth/register/status").then(v=>v.json()).then(v=>u(v.enabled)).catch(()=>{})},[]);async function p(v){v.preventDefault(),i(""),s(!0);try{const y=await fetch("/api/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:e,password:r})});if(!y.ok){const b=await y.json().catch(()=>({}));throw new Error(b.detail??"Login failed")}const g=await y.json();f(g.access_token,g.user),m("/",{replace:!0})}catch(y){i(y.message)}finally{s(!1)}}return c.jsx("div",{className:"auth-page",children:c.jsxs("div",{className:"auth-card",children:[c.jsx("img",{src:d,alt:"FIXture",className:"auth-logo"}),c.jsx("h2",{children:"Sign in"}),c.jsxs("form",{onSubmit:p,className:"auth-form",children:[c.jsxs("label",{children:["Username",c.jsx("input",{type:"text",value:e,onChange:v=>t(v.target.value),autoComplete:"username",required:!0})]}),c.jsxs("label",{children:["Password",c.jsx("input",{type:"password",value:r,onChange:v=>n(v.target.value),autoComplete:"current-password",required:!0})]}),a&&c.jsx("p",{className:"auth-error",children:a}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:o,children:o?"Signing in…":"Sign in"})]}),l&&c.jsxs("p",{className:"auth-hint",style:{marginTop:12},children:["No account? ",c.jsx(qr,{to:"/register",children:"Register"})]})]})})}function Xz(){const[e,t]=h.useState(""),[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState(!1),l=Us(),u=an();async function f(d){d.preventDefault(),i(""),s(!0);try{const m=await fetch("/api/setup/create-admin",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:e,password:r})});if(!m.ok){const p=await m.json().catch(()=>({}));throw new Error(p.detail??"Setup failed")}u("/login",{replace:!0})}catch(m){i(m.message)}finally{s(!1)}}return c.jsx("div",{className:"auth-page",children:c.jsxs("div",{className:"auth-card",children:[c.jsx("img",{src:l,alt:"FIXture",className:"auth-logo"}),c.jsx("h2",{children:"Create admin account"}),c.jsx("p",{className:"auth-hint",children:"First-time setup — create the platform admin."}),c.jsxs("form",{onSubmit:f,className:"auth-form",children:[c.jsxs("label",{children:["Username",c.jsx("input",{type:"text",value:e,onChange:d=>t(d.target.value),autoComplete:"username",required:!0})]}),c.jsxs("label",{children:["Password",c.jsx("input",{type:"password",value:r,onChange:d=>n(d.target.value),autoComplete:"new-password",minLength:8,required:!0})]}),a&&c.jsx("p",{className:"auth-error",children:a}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:o,children:o?"Creating…":"Create admin"})]})]})})}function Vz(){const[e,t]=h.useState(""),[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState(!1),[l,u]=h.useState(null),{login:f,theme:d}=X(),m=an();h.useEffect(()=>{fetch("/api/auth/register/status").then(y=>y.json()).then(u).catch(()=>u({enabled:!1,max_users:0,current_users:0}))},[]);async function p(y){y.preventDefault(),i(""),s(!0);try{const g=await fetch("/api/auth/register",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:e,password:r})});if(!g.ok){const x=await g.json().catch(()=>({}));throw new Error(x.detail??"Registration failed")}const b=await g.json();f(b.access_token,b.user),m("/",{replace:!0})}catch(g){i(g.message)}finally{s(!1)}}const v=l&&l.current_users>=l.max_users;return c.jsx("div",{className:"auth-page",children:c.jsxs("div",{className:"auth-card",children:[c.jsx("img",{src:d==="dark"?Iw:kw,alt:"FIXture",className:"auth-logo"}),c.jsx("h2",{children:"Create account"}),l===null&&c.jsx("p",{className:"auth-hint",children:"Checking registration status…"}),l&&!l.enabled&&c.jsx("p",{className:"auth-error",children:"Registration is currently closed. Contact an administrator."}),l&&l.enabled&&v&&c.jsx("p",{className:"auth-error",children:"Registration limit reached. Contact an administrator."}),l&&l.enabled&&!v&&c.jsxs("form",{onSubmit:p,className:"auth-form",children:[c.jsxs("label",{children:["Username",c.jsx("input",{type:"text",value:e,onChange:y=>t(y.target.value),autoComplete:"username",required:!0})]}),c.jsxs("label",{children:["Password",c.jsx("input",{type:"password",value:r,onChange:y=>n(y.target.value),autoComplete:"new-password",minLength:8,required:!0})]}),a&&c.jsx("p",{className:"auth-error",children:a}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:o,children:o?"Creating account…":"Create account"})]}),c.jsxs("p",{className:"auth-hint",style:{marginTop:12},children:["Already have an account? ",c.jsx(qr,{to:"/login",children:"Sign in"})]})]})})}const Jz=["accent","bg","bg2","bg3","border","text","text2"];function Tw(e){let t=document.getElementById("branding-style");t||(t=document.createElement("style"),t.id="branding-style",document.head.appendChild(t));const r=Jz.filter(n=>e[n]).map(n=>`--${n}: ${e[n]};`).join(" ");if(r?t.textContent=`:root { ${r} } [data-theme="light"] { ${r} }`:t.textContent="",e.favicon){let n=document.querySelector('link[rel="icon"]');n||(n=document.createElement("link"),n.rel="icon",document.head.appendChild(n)),n.href=e.favicon}document.title=e.prefix?`${e.prefix} FIXture`:"FIXture"}function Sl(e){return new Promise((t,r)=>{const n=new FileReader;n.onload=()=>t(n.result),n.onerror=r,n.readAsDataURL(e)})}const Mw=["user","user_admin","platform_admin"];function Qz(e){return new Date(e*1e3).toLocaleString()}function eW({onClose:e,onCreated:t}){const[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState("user"),[l,u]=h.useState(""),[f,d]=h.useState(!1);async function m(p){p.preventDefault(),u(""),d(!0);try{const v=await G.admin.users.create({username:r,password:a,role:o});t(v),e()}catch(v){u(v.message)}finally{d(!1)}}return c.jsx("div",{className:"modal-overlay",onClick:e,children:c.jsxs("div",{className:"modal",onClick:p=>p.stopPropagation(),children:[c.jsxs("div",{className:"modal-header",children:[c.jsx("span",{children:"Create User"}),c.jsx("button",{className:"btn-icon",onClick:e,children:"✕"})]}),c.jsxs("form",{onSubmit:m,children:[c.jsxs("div",{className:"form-grid",children:[c.jsx("label",{children:"Username"}),c.jsx("input",{value:r,onChange:p=>n(p.target.value),required:!0}),c.jsx("label",{children:"Password"}),c.jsx("input",{type:"password",value:a,onChange:p=>i(p.target.value),minLength:8,required:!0}),c.jsx("label",{children:"Role"}),c.jsx("select",{value:o,onChange:p=>s(p.target.value),children:Mw.map(p=>c.jsx("option",{value:p,children:p},p))})]}),l&&c.jsx("div",{className:"error-msg",children:l}),c.jsxs("div",{className:"modal-footer",children:[c.jsx("button",{type:"button",className:"btn btn-secondary",onClick:e,children:"Cancel"}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:f,children:f?"Creating…":"Create"})]})]})]})})}function tW({user:e,onClose:t}){const[r,n]=h.useState(""),[a,i]=h.useState(""),[o,s]=h.useState(!1),[l,u]=h.useState(!1);async function f(d){d.preventDefault(),i(""),s(!0);try{await G.admin.users.update(e.uid,{password:r}),u(!0)}catch(m){i(m.message)}finally{s(!1)}}return c.jsx("div",{className:"modal-overlay",onClick:t,children:c.jsxs("div",{className:"modal",onClick:d=>d.stopPropagation(),children:[c.jsxs("div",{className:"modal-header",children:[c.jsxs("span",{children:["Reset Password — ",e.username]}),c.jsx("button",{className:"btn-icon",onClick:t,children:"✕"})]}),l?c.jsxs("div",{style:{padding:16},children:[c.jsx("p",{style:{color:"var(--green)"},children:"Password updated successfully."}),c.jsx("div",{className:"modal-footer",children:c.jsx("button",{className:"btn btn-primary",onClick:t,children:"Close"})})]}):c.jsxs("form",{onSubmit:f,children:[c.jsxs("div",{className:"form-grid",children:[c.jsx("label",{children:"New Password"}),c.jsx("input",{type:"password",value:r,onChange:d=>n(d.target.value),minLength:8,required:!0})]}),a&&c.jsx("div",{className:"error-msg",children:a}),c.jsxs("div",{className:"modal-footer",children:[c.jsx("button",{type:"button",className:"btn btn-secondary",onClick:t,children:"Cancel"}),c.jsx("button",{type:"submit",className:"btn btn-primary",disabled:o,children:o?"Saving…":"Save"})]})]})]})})}function rW(){const e=X(T=>T.user),t=an(),[r,n]=h.useState([]),[a,i]=h.useState(null),[o,s]=h.useState(null),[l,u]=h.useState(null),[f,d]=h.useState(!1),[m,p]=h.useState(null),[v,y]=h.useState(!1),{setBranding:g}=X(),[b,x]=h.useState(!1),[w,S]=h.useState(null),[_,j]=h.useState("");async function O(){try{const[T,R,z,W]=await Promise.all([G.admin.users.list(),G.admin.settings.get(),G.admin.housekeeping.get(),G.admin.branding.get()]);n(T),i(R),s(z),p(W)}catch(T){j(T.message)}}h.useEffect(()=>{O()},[]);async function N(T){try{const R=await G.admin.users.update(T.uid,{is_active:!T.is_active});n(z=>z.map(W=>W.uid===T.uid?R:W))}catch(R){j(R.message)}}async function A(T,R){try{const z=await G.admin.users.update(T.uid,{role:R});n(W=>W.map(V=>V.uid===T.uid?z:V))}catch(z){j(z.message)}}async function k(T){if(confirm(`Delete user "${T.username}"? This cannot be undone.`))try{await G.admin.users.delete(T.uid),n(R=>R.filter(z=>z.uid!==T.uid))}catch(R){j(R.message)}}async function M(){if(a)try{const T=await G.admin.settings.update({registration_enabled:!a.enabled});i(T)}catch(T){j(T.message)}}async function P(T){const R=parseInt(T,10);if(!(isNaN(R)||R<1))try{const z=await G.admin.settings.update({max_users:R});i(z)}catch(z){j(z.message)}}async function H(){if(o)try{const T=await G.admin.housekeeping.update({enabled:!o.enabled});s(T)}catch(T){j(T.message)}}async function L(T,R){const z=parseInt(R,10);if(!(isNaN(z)||z<0))try{const W=await G.admin.housekeeping.update({[T]:z});s(W)}catch(W){j(W.message)}}async function U(){d(!0),u(null);try{const T=await G.admin.housekeeping.runNow();u(`Done — deleted ${T.msgs_deleted} messages, ${T.logs_deleted} log files`)}catch(T){j(T.message)}finally{d(!1)}}return c.jsxs("div",{className:"admin-page",children:[c.jsxs("div",{className:"admin-header",children:[c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12},children:[c.jsx("button",{className:"btn-icon",onClick:()=>t("/"),title:"Back",children:"←"}),c.jsx("h1",{children:"User Management"})]}),c.jsx("button",{className:"btn btn-primary btn-sm",onClick:()=>x(!0),children:"+ Create User"})]}),_&&c.jsx("div",{className:"error-msg",style:{margin:"0 0 12px"},children:_}),a&&c.jsxs("div",{className:"admin-settings-bar",children:[c.jsx("span",{className:"admin-settings-label",children:"Self-registration:"}),c.jsx("button",{className:`btn btn-sm ${a.enabled?"btn-stop":"btn-start"}`,onClick:M,children:a.enabled?"Enabled — click to disable":"Disabled — click to enable"}),c.jsx("span",{className:"admin-settings-label",style:{marginLeft:16},children:"Max users:"}),c.jsx("input",{type:"number",className:"admin-max-users-input",defaultValue:a.max_users,min:1,onBlur:T=>P(T.target.value)}),c.jsxs("span",{className:"admin-settings-label",style:{color:"var(--text2)"},children:["(",a.current_users," / ",a.max_users," registered)"]})]}),o&&c.jsxs("div",{className:"admin-settings-bar",children:[c.jsx("span",{className:"admin-settings-label",children:"Housekeeping:"}),c.jsx("button",{className:`btn btn-sm ${o.enabled?"btn-stop":"btn-start"}`,onClick:H,children:o.enabled?"Auto — click to disable":"Manual only — click to enable"}),c.jsx("span",{className:"admin-settings-label",style:{marginLeft:16},children:"Keep messages (days):"}),c.jsx("input",{type:"number",className:"admin-max-users-input",defaultValue:o.msg_retention_days,min:0,title:"0 = keep forever",onBlur:T=>L("msg_retention_days",T.target.value)}),c.jsx("span",{className:"admin-settings-label",style:{marginLeft:16},children:"Keep logs (days):"}),c.jsx("input",{type:"number",className:"admin-max-users-input",defaultValue:o.log_retention_days,min:0,title:"0 = keep forever",onBlur:T=>L("log_retention_days",T.target.value)}),c.jsx("button",{className:"btn btn-sm btn-ghost",style:{marginLeft:16},onClick:U,disabled:f,children:f?"Running…":"Run Now"}),l&&c.jsx("span",{style:{marginLeft:8,color:"var(--green)",fontSize:12},children:l})]}),m&&c.jsxs("div",{className:"admin-settings-bar",style:{flexDirection:"column",alignItems:"flex-start",gap:10},children:[c.jsx("span",{className:"admin-settings-label",style:{fontWeight:700},children:"Branding"}),c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[c.jsx("span",{className:"admin-settings-label",children:"Brand prefix:"}),c.jsx("input",{className:"admin-max-users-input",style:{width:160},value:m.prefix,placeholder:"e.g. Acme (shown as Acme FIXture)",onChange:T=>p({...m,prefix:T.target.value})}),c.jsx("span",{className:"admin-settings-label",style:{marginLeft:16},children:"Accent colour:"}),c.jsx("input",{type:"color",value:m.accent||"#388bfd",onChange:T=>p({...m,accent:T.target.value}),title:"Primary accent colour"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,accent:""}),title:"Reset to default blue",children:"Reset"})]}),c.jsx("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[["bg","Page background","#0d1117"],["bg2","Panel background","#161b22"],["bg3","Input background","#21262d"],["border","Border colour","#30363d"],["text","Primary text","#e6edf3"],["text2","Secondary text","#8b949e"]].map(([T,R,z])=>c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:4},children:[c.jsxs("span",{className:"admin-settings-label",children:[R,":"]}),c.jsx("input",{type:"color",value:m[T]||z,onChange:W=>p({...m,[T]:W.target.value}),title:R}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,[T]:""}),title:"Reset to default",children:"↺"})]},T))}),c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[c.jsx("span",{className:"admin-settings-label",children:"Logo (dark theme):"}),m.logo_dark&&c.jsx("img",{src:m.logo_dark,style:{height:28,background:"#161b22",borderRadius:4,padding:"2px 6px"},alt:"dark logo preview"}),c.jsx("input",{type:"file",accept:"image/*",onChange:async T=>{var z;const R=(z=T.target.files)==null?void 0:z[0];R&&p({...m,logo_dark:await Sl(R)})}}),m.logo_dark&&c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,logo_dark:""}),children:"Clear"})]}),c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[c.jsx("span",{className:"admin-settings-label",children:"Logo (light theme):"}),m.logo_light&&c.jsx("img",{src:m.logo_light,style:{height:28,background:"#f0f2f5",borderRadius:4,padding:"2px 6px"},alt:"light logo preview"}),c.jsx("input",{type:"file",accept:"image/*",onChange:async T=>{var z;const R=(z=T.target.files)==null?void 0:z[0];R&&p({...m,logo_light:await Sl(R)})}}),m.logo_light&&c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,logo_light:""}),children:"Clear"})]}),c.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,flexWrap:"wrap"},children:[c.jsx("span",{className:"admin-settings-label",children:"Favicon:"}),m.favicon&&c.jsx("img",{src:m.favicon,style:{height:20,width:20},alt:"favicon preview"}),c.jsx("input",{type:"file",accept:"image/png,image/svg+xml,image/x-icon",onChange:async T=>{var z;const R=(z=T.target.files)==null?void 0:z[0];R&&p({...m,favicon:await Sl(R)})}}),m.favicon&&c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>p({...m,favicon:""}),children:"Clear"})]}),c.jsx("button",{className:"btn btn-sm btn-primary",disabled:v,onClick:async()=>{y(!0);try{const T=await G.admin.branding.update(m);p(T),g(T),Tw(T)}catch(T){j(T.message)}finally{y(!1)}},children:v?"Saving…":"Save Branding"})]}),c.jsxs("table",{className:"admin-table",children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Username"}),c.jsx("th",{children:"Role"}),c.jsx("th",{children:"Status"}),c.jsx("th",{children:"Created"}),c.jsx("th",{children:"Actions"})]})}),c.jsx("tbody",{children:r.map(T=>c.jsxs("tr",{className:T.is_active?"":"admin-row-inactive",children:[c.jsxs("td",{children:[T.username,T.uid===(e==null?void 0:e.uid)&&c.jsx("span",{className:"admin-you-badge",children:" (you)"})]}),c.jsx("td",{children:c.jsx("select",{value:T.role,onChange:R=>A(T,R.target.value),className:"admin-role-select",disabled:T.uid===(e==null?void 0:e.uid),children:Mw.map(R=>c.jsx("option",{value:R,children:R},R))})}),c.jsx("td",{children:c.jsx("span",{style:{color:T.is_active?"var(--green)":"var(--text2)"},children:T.is_active?"Active":"Inactive"})}),c.jsx("td",{style:{color:"var(--text2)",fontSize:11},children:Qz(T.created_at)}),c.jsx("td",{children:c.jsxs("div",{className:"admin-row-actions",children:[c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>N(T),disabled:T.uid===(e==null?void 0:e.uid),title:T.is_active?"Deactivate":"Activate",children:T.is_active?"Deactivate":"Activate"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>S(T),title:"Reset password",children:"Reset PW"}),c.jsx("button",{className:"btn btn-sm btn-danger",onClick:()=>k(T),disabled:T.uid===(e==null?void 0:e.uid),title:"Delete user",children:"✕"})]})})]},T.uid))})]}),b&&c.jsx(eW,{onClose:()=>x(!1),onCreated:T=>n(R=>[...R,T])}),w&&c.jsx(tW,{user:w,onClose:()=>S(null)})]})}const Up={name:"smoke",mode:"loopback",client_session_id:null,venue_session_id:null,correlation_tag:376,exec_id_tag:25116,rate:{orders_per_window:50,order_window_ms:100,fills_per_window:50,fill_window_ms:100,fill_ratio:1,allow_burst:!1,max_burst_multiplier:2},test:{duration:30,max_orders:0,scenario_timeout_ms:3e4,max_pending:1e5,on_saturation:"pause",output:"results",record_execs:!1},payload:{variables:{},order:{symbols:["AAPL","MSFT","GOOGL"],side:"alternate",ord_type:"limit",quantity_min:100,quantity_max:1e3,price_min:99,price_max:101,time_in_force:"0"},fill:{fill_type:"full",send_ack:!0,fills_per_order:1,partial_fill_pct_min:50,partial_fill_pct_max:99,price_variance_ticks:0},order_template_id:null,exec_template_id:null,auto_expiry:!0,expiry_offset:1,expiry_unit:"days",gen_rules:{}}},nW=["loopback","manager","client","venue"];function Hp(e,t){return t?Math.round(e/t*1e3):0}function aW({onStarted:e}){var mt,Cr;const t=X(C=>C.sessions),[r,n]=h.useState(Up),[a,i]=h.useState(Up.payload.order.symbols.join(", ")),[o,s]=h.useState(!1),[l,u]=h.useState(""),[f,d]=h.useState(!1),[m,p]=h.useState([]),[v,y]=h.useState(""),[g,b]=h.useState([]),x=Object.values(t),w=r.mode==="loopback"||r.mode==="manager"||r.mode==="client",S=r.mode==="loopback"||r.mode==="manager"||r.mode==="venue",_=r.client_session_id?t[r.client_session_id]:void 0,j=r.venue_session_id?t[r.venue_session_id]:void 0,O=!w||(_==null?void 0:_.status)==="LOGGED_ON",N=!S||(j==null?void 0:j.status)==="LOGGED_ON",A=(!w||!!r.client_session_id)&&(!S||!!r.venue_session_id),k=(()=>{if(!r.name.trim())return"Name is required";const C=r.rate;if(C.max_burst_multiplier<1)return"Max burst multiplier must be ≥ 1";if(r.test.scenario_timeout_ms<1)return"Order timeout must be > 0";if(r.test.max_pending<1)return"Max pending must be > 0";if(w){if(C.orders_per_window<1||C.order_window_ms<1)return"Order rate must be > 0";if(a.split(",").map(J=>J.trim()).filter(Boolean).length===0)return"At least one symbol is required";const I=r.payload.order;if(I.quantity_min<1||I.quantity_max<1||I.quantity_min>I.quantity_max)return"Check quantity min/max";if(I.price_min<=0||I.price_max<=0||I.price_min>I.price_max)return"Check price min/max"}if(S){if(C.fills_per_window<1||C.fill_window_ms<1)return"Fill rate must be > 0";if(r.payload.fill.fills_per_order<1)return"Fills per order must be ≥ 1";if(C.fill_ratio<0||C.fill_ratio>1)return"Fill ratio must be between 0 and 1"}return null})(),M=A&&O&&N&&!k&&!f;async function P(){try{p(await G.perf.configs.list())}catch{}}h.useEffect(()=>{P()},[]),h.useEffect(()=>{G.templates.list().then(b).catch(()=>{})},[]);const H=g.filter(C=>C.msg_type==="D"),L=g.filter(C=>C.msg_type==="8"),U=!!r.payload.order_template_id,T=!!r.payload.exec_template_id,R=((mt=H.find(C=>C.id===r.payload.order_template_id))==null?void 0:mt.name)??"selected",z=((Cr=L.find(C=>C.id===r.payload.exec_template_id))==null?void 0:Cr.name)??"selected",W=C=>n(I=>({...I,...C})),V=C=>n(I=>({...I,rate:{...I.rate,...C}})),se=C=>n(I=>({...I,test:{...I.test,...C}})),Q=C=>n(I=>({...I,payload:{...I.payload,order:{...I.payload.order,...C}}})),ye=C=>n(I=>({...I,payload:{...I.payload,fill:{...I.payload.fill,...C}}})),de=C=>n(I=>({...I,payload:{...I.payload,...C}})),[Ne,$]=h.useState([]),ae=C=>{$(C),de({gen_rules:Object.fromEntries(C.filter(I=>I.tag.trim()).map(I=>[I.tag.trim(),I.value]))})},ne=(C,I)=>ae(Ne.map((J,he)=>he===C?{...J,...I}:J));function B(){const C=a.split(",").map(I=>I.trim()).filter(Boolean);return{...r,client_session_id:w?r.client_session_id:null,venue_session_id:S?r.venue_session_id:null,payload:{...r.payload,order:{...r.payload.order,symbols:C}}}}async function F(){u(""),d(!0);try{const{run_id:C}=await G.perf.runs.create(B());e(C)}catch(C){u(C.message??"Failed to start run")}finally{d(!1)}}async function ee(){u("");try{await G.perf.configs.save(B()),await P()}catch(C){u(C.message??"Failed to save config")}}async function D(C){if(y(C),!!C){u("");try{const I=await G.perf.configs.get(C);n(I),i(I.payload.order.symbols.join(", ")),$(Object.entries(I.payload.gen_rules??{}).map(([J,he])=>({tag:J,value:he})))}catch(I){u(I.message??"Failed to load config")}}}async function Te(){if(v&&window.confirm("Delete this saved config?"))try{await G.perf.configs.delete(v),y(""),await P()}catch(C){u(C.message??"Failed to delete config")}}const oe=C=>{const I=t[C];return I?`${I.display_name||`${I.sender_comp_id} → ${I.target_comp_id}`} (${I.status})`:C},ze=x.filter(C=>C.session_role!=="venue"),We=x.filter(C=>C.session_role!=="client");return c.jsxs("div",{className:"perf-form",children:[l&&c.jsx("div",{className:"error-msg",children:l}),c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Saved configs"}),c.jsxs("div",{className:"perf-field-row",children:[c.jsxs("select",{value:v,onChange:C=>D(C.target.value),children:[c.jsx("option",{value:"",children:"(load a saved config…)"}),m.map(C=>c.jsxs("option",{value:C.config_id,children:[C.name," — ",C.mode]},C.config_id))]}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:ee,children:"Save current"}),c.jsx("button",{className:"btn btn-sm btn-ghost btn-danger-ghost",onClick:Te,disabled:!v,children:"Delete"})]})]}),c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Run"}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Name"}),c.jsx("input",{value:r.name,onChange:C=>W({name:C.target.value})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Mode"}),c.jsx("select",{value:r.mode,onChange:C=>W({mode:C.target.value}),children:nW.map(C=>c.jsx("option",{value:C,children:C},C))})]}),w&&c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Client session"}),c.jsxs("select",{value:r.client_session_id??"",onChange:C=>W({client_session_id:C.target.value||null}),children:[c.jsx("option",{value:"",children:"(select…)"}),ze.map(C=>c.jsx("option",{value:C.session_id,children:oe(C.session_id)},C.session_id))]}),_&&c.jsx(Gp,{status:_.status})]}),S&&c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Venue session"}),c.jsxs("select",{value:r.venue_session_id??"",onChange:C=>W({venue_session_id:C.target.value||null}),children:[c.jsx("option",{value:"",children:"(select…)"}),We.map(C=>c.jsx("option",{value:C.session_id,children:oe(C.session_id)},C.session_id))]}),j&&c.jsx(Gp,{status:j.status})]})]}),c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Rate"}),c.jsxs("div",{className:"perf-field-grid",children:[w&&c.jsxs(c.Fragment,{children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Orders / window"}),c.jsx("input",{type:"number",min:1,value:r.rate.orders_per_window,onChange:C=>V({orders_per_window:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Order window (ms)"}),c.jsx("input",{type:"number",min:1,value:r.rate.order_window_ms,onChange:C=>V({order_window_ms:Number(C.target.value)})})]})]}),S&&c.jsxs(c.Fragment,{children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fills / window"}),c.jsx("input",{type:"number",min:1,value:r.rate.fills_per_window,onChange:C=>V({fills_per_window:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fill window (ms)"}),c.jsx("input",{type:"number",min:1,value:r.rate.fill_window_ms,onChange:C=>V({fill_window_ms:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fill ratio"}),c.jsx("input",{type:"number",min:0,max:1,step:.05,value:r.rate.fill_ratio,onChange:C=>V({fill_ratio:Number(C.target.value)})})]})]})]}),c.jsxs("div",{className:"perf-hint",children:[w&&c.jsxs(c.Fragment,{children:["≈ ",Hp(r.rate.orders_per_window,r.rate.order_window_ms).toLocaleString()," orders/s"]}),w&&S&&" · ",S&&c.jsxs(c.Fragment,{children:["≈ ",Hp(r.rate.fills_per_window,r.rate.fill_window_ms).toLocaleString()," fills/s"]})," ","Smaller windows smooth the injection."]})]}),c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Test"}),c.jsxs("div",{className:"perf-field-grid",children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Duration (s, 0 = no time limit)"}),c.jsx("input",{type:"number",min:0,value:r.test.duration,onChange:C=>se({duration:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Max orders (0 = no count limit)"}),c.jsx("input",{type:"number",min:0,value:r.test.max_orders,onChange:C=>se({max_orders:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Order timeout (ms)"}),c.jsx("input",{type:"number",min:1,step:1e3,value:r.test.scenario_timeout_ms,onChange:C=>se({scenario_timeout_ms:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"On saturation"}),c.jsxs("select",{value:r.test.on_saturation,onChange:C=>se({on_saturation:C.target.value}),children:[c.jsx("option",{value:"pause",children:"pause (preserves latency accuracy)"}),c.jsx("option",{value:"drop_oldest",children:"drop_oldest"})]})]})]}),w&&c.jsxs("label",{className:"checkbox-label",children:[c.jsx("input",{type:"checkbox",checked:r.test.record_execs,onChange:C=>se({record_execs:C.target.checked})}),"Record per-exec detail — one CSV row per ack/fill with latency + inter-report gap (downloads as csv.gz after the run)"]}),c.jsxs("div",{className:"perf-hint",children:["An order not fully filled within the order timeout is counted ",c.jsx("strong",{children:"Lost"}),". Raise it if rate-limited fills are being flagged lost on full-fill runs."]})]}),w&&c.jsxs("div",{className:`perf-form-section ${U?"perf-section-layered":""}`,children:[c.jsxs("div",{className:"perf-section-title",children:["Order payload",U&&c.jsx("span",{className:"perf-layer-badge",children:"values for template"})]}),U&&c.jsxs("div",{className:"perf-hint perf-layer-note",children:["Order template ",c.jsx("strong",{children:R})," defines the message shape. These fields supply the values it references via ",c.jsx("code",{children:"{symbol}"})," ",c.jsx("code",{children:"{side}"})," ",c.jsx("code",{children:"{qty}"})," ",c.jsx("code",{children:"{price}"})," tokens. Value generators (",c.jsx("code",{children:"uuid()"}),", ",c.jsx("code",{children:"random_str(n)"}),", ",c.jsx("code",{children:"random_int(a,b)"}),","," ",c.jsx("code",{children:"seq(width)"}),", ",c.jsx("code",{children:"timestamp()"}),", ",c.jsx("code",{children:"date()"}),") are evaluated fresh per order and mix with text as ",c.jsx("code",{children:"ORD-{seq(8)}"}),"; ClOrdID(11) is always made unique per order. ",c.jsx("strong",{children:"Order type"})," is taken from the template."]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Symbols (comma-separated)"}),c.jsx("input",{value:a,onChange:C=>i(C.target.value)})]}),c.jsxs("div",{className:"perf-field-grid",children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Side"}),c.jsxs("select",{value:r.payload.order.side,onChange:C=>Q({side:C.target.value}),children:[c.jsx("option",{value:"alternate",children:"alternate"}),c.jsx("option",{value:"fixed_buy",children:"fixed_buy"}),c.jsx("option",{value:"fixed_sell",children:"fixed_sell"}),c.jsx("option",{value:"random",children:"random"})]})]}),c.jsxs("label",{className:`perf-field ${U?"perf-field-overridden":""}`,children:[c.jsxs("span",{children:["Order type ",U&&c.jsx("em",{className:"perf-field-tag",children:"from template"})]}),c.jsxs("select",{value:r.payload.order.ord_type,disabled:U,onChange:C=>Q({ord_type:C.target.value}),children:[c.jsx("option",{value:"limit",children:"limit"}),c.jsx("option",{value:"market",children:"market"})]})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Qty min"}),c.jsx("input",{type:"number",min:1,value:r.payload.order.quantity_min,onChange:C=>Q({quantity_min:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Qty max"}),c.jsx("input",{type:"number",min:1,value:r.payload.order.quantity_max,onChange:C=>Q({quantity_max:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Price min"}),c.jsx("input",{type:"number",min:0,step:.01,value:r.payload.order.price_min,onChange:C=>Q({price_min:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Price max"}),c.jsx("input",{type:"number",min:0,step:.01,value:r.payload.order.price_max,onChange:C=>Q({price_max:Number(C.target.value)})})]})]})]}),w&&c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Send options"}),c.jsxs("div",{className:"perf-field-row",children:[c.jsxs("label",{className:"checkbox-label",children:[c.jsx("input",{type:"checkbox",checked:r.payload.auto_expiry??!0,onChange:C=>de({auto_expiry:C.target.checked})}),"Auto expiry (tags 126 / 432, only if present)"]}),(r.payload.auto_expiry??!0)&&c.jsxs(c.Fragment,{children:[c.jsx("input",{type:"number",min:0,style:{width:64},value:r.payload.expiry_offset??1,onChange:C=>de({expiry_offset:Number(C.target.value)})}),c.jsxs("select",{value:r.payload.expiry_unit??"days",onChange:C=>de({expiry_unit:C.target.value}),children:[c.jsx("option",{value:"minutes",children:"minutes"}),c.jsx("option",{value:"hours",children:"hours"}),c.jsx("option",{value:"days",children:"days"})]}),c.jsx("span",{children:"into the future"})]})]}),Ne.map((C,I)=>c.jsxs("div",{className:"perf-field-row",children:[c.jsx("input",{style:{width:72},placeholder:"tag",value:C.tag,onChange:J=>ne(I,{tag:J.target.value.replace(/[^0-9]/g,"")})}),c.jsx("span",{children:"="}),c.jsx("input",{style:{flex:1},placeholder:"uuid() / seq(8) / ORD-{seq(8)} / literal",value:C.value,onChange:J=>ne(I,{value:J.target.value})}),c.jsx("button",{className:"btn btn-sm btn-ghost btn-danger-ghost",onClick:()=>ae(Ne.filter((J,he)=>he!==I)),children:"✕"})]},I)),c.jsx("div",{className:"perf-field-row",children:c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>ae([...Ne,{tag:"",value:""}]),children:"+ Add tag rule"})}),c.jsxs("div",{className:"perf-hint",children:["Rules set the tag on every order/scenario message (insert or replace). Generators: ",c.jsx("code",{children:"uuid()"})," · ",c.jsx("code",{children:"random_str(n)"})," · ",c.jsx("code",{children:"random_int(a,b)"})," ·"," ",c.jsx("code",{children:"seq(width[,start])"})," · ",c.jsx("code",{children:"timestamp()"})," · ",c.jsx("code",{children:"date()"})," — mix with text as ",c.jsx("code",{children:"ORD-{seq(8)}"}),". ClOrdID(11) always stays unique per order."]})]}),S&&c.jsxs("div",{className:`perf-form-section ${T?"perf-section-layered":""}`,children:[c.jsxs("div",{className:"perf-section-title",children:["Fill payload",T&&c.jsx("span",{className:"perf-layer-badge",children:"behavior for template"})]}),T&&c.jsxs("div",{className:"perf-hint perf-layer-note",children:["Exec template ",c.jsx("strong",{children:z})," reshapes each report (custom tags). These still control fill ",c.jsx("strong",{children:"behavior"})," — how many reports, the fill ratio, and the ack — while the engine sets the standard exec tags."]}),c.jsxs("div",{className:"perf-field-grid",children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fill type"}),c.jsxs("select",{value:r.payload.fill.fill_type,onChange:C=>ye({fill_type:C.target.value}),children:[c.jsx("option",{value:"full",children:"full"}),c.jsx("option",{value:"partial",children:"partial"}),c.jsx("option",{value:"random",children:"random"})]})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Fills per order"}),c.jsx("input",{type:"number",min:1,value:r.payload.fill.fills_per_order,onChange:C=>ye({fills_per_order:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field perf-field-check",children:[c.jsx("input",{type:"checkbox",checked:r.payload.fill.send_ack,onChange:C=>ye({send_ack:C.target.checked})}),c.jsx("span",{children:"Send ack before fills (35=8|150=0|39=0)"})]}),r.payload.fill.fill_type!=="full"&&c.jsxs(c.Fragment,{children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Partial % min"}),c.jsx("input",{type:"number",min:1,max:100,value:r.payload.fill.partial_fill_pct_min,onChange:C=>ye({partial_fill_pct_min:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Partial % max"}),c.jsx("input",{type:"number",min:1,max:100,value:r.payload.fill.partial_fill_pct_max,onChange:C=>ye({partial_fill_pct_max:Number(C.target.value)})})]})]})]})]}),(w||S)&&c.jsxs("div",{className:"perf-form-section",children:[c.jsx("div",{className:"perf-section-title",children:"Templates (optional)"}),c.jsxs("div",{className:"perf-field-grid",children:[w&&c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Order template"}),c.jsxs("select",{value:r.payload.order_template_id??"",onChange:C=>de({order_template_id:C.target.value||null}),children:[c.jsx("option",{value:"",children:"— built-in —"}),H.map(C=>c.jsx("option",{value:C.id,children:C.name},C.id))]})]}),S&&c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Exec report template"}),c.jsxs("select",{value:r.payload.exec_template_id??"",onChange:C=>de({exec_template_id:C.target.value||null}),children:[c.jsx("option",{value:"",children:"— built-in —"}),L.map(C=>c.jsx("option",{value:C.id,children:C.name},C.id))]})]})]}),c.jsx("div",{className:"perf-hint",children:"Optional FIX templates from your Templates library shape the order / execution report (custom & venue-specific tags). Standard tags (correlation, OrderQty, CumQty, ExecType…) are always set by the engine. Leave as built-in for the default messages."})]}),c.jsxs("div",{className:"perf-form-section",children:[c.jsxs("button",{className:"btn btn-sm btn-ghost",onClick:()=>s(C=>!C),children:[o?"▾":"▸"," Advanced"]}),o&&c.jsxs("div",{className:"perf-field-grid",style:{marginTop:8},children:[c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Correlation tag"}),c.jsx("input",{type:"number",min:1,value:r.correlation_tag,onChange:C=>W({correlation_tag:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Exec ID tag"}),c.jsx("input",{type:"number",min:1,value:r.exec_id_tag,onChange:C=>W({exec_id_tag:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Max pending"}),c.jsx("input",{type:"number",min:1,value:r.test.max_pending,onChange:C=>se({max_pending:Number(C.target.value)})})]}),c.jsxs("label",{className:"perf-field perf-field-check",children:[c.jsx("input",{type:"checkbox",checked:r.rate.allow_burst,onChange:C=>V({allow_burst:C.target.checked})}),c.jsx("span",{children:"Allow burst"})]}),c.jsxs("label",{className:"perf-field",children:[c.jsx("span",{children:"Max burst multiplier"}),c.jsx("input",{type:"number",min:1,step:.5,value:r.rate.max_burst_multiplier,onChange:C=>V({max_burst_multiplier:Number(C.target.value)})})]})]})]}),c.jsxs("div",{className:"perf-form-actions",children:[k?c.jsx("span",{className:"perf-not-ready",children:k}):A&&!(O&&N)?c.jsxs("span",{className:"perf-not-ready",children:["Selected session",w&&S?"s":""," must be LOGGED_ON before starting."]}):null,c.jsx("button",{className:"btn btn-primary",onClick:F,disabled:!M,title:A?k||(O&&N?void 0:"Session not logged on yet"):"Select the required session(s)",children:f?"Starting…":"▶ Start run"})]})]})}function Gp({status:e}){const t=e==="LOGGED_ON";return c.jsxs("span",{className:`perf-status-chip ${t?"ok":"warn"}`,children:[t?"● ":"○ ",e]})}const Zp=600,iW=5e3,oW=new Set(["completed","stopped","error"]);function sW(e){const[t,r]=h.useState(null),[n,a]=h.useState([]),[i,o]=h.useState(!1);return h.useEffect(()=>{if(r(null),a([]),o(!1),!e)return;let s=!1,l=null,u,f=0,d=!1;async function m(){if(s||d||!e)return;let v;try{({ticket:v}=await G.perf.runs.ticket(e))}catch{p();return}if(s)return;const g=`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/api/perf/runs/${e}/live?ticket=${v}`;l=new WebSocket(g),l.onopen=()=>{s||(f=0,o(!0))},l.onmessage=b=>{let x;try{x=JSON.parse(b.data)}catch{return}r(x),a(w=>{var j,O,N,A,k,M,P,H;const S={t:x.elapsed_s,ops:((j=x.client)==null?void 0:j.ops_live)??0,fps:((O=x.venue)==null?void 0:O.fps_live)??0,tps:(((N=x.client)==null?void 0:N.ops_live)??0)+(((A=x.venue)==null?void 0:A.fps_live)??0),p50:(((k=x.response_latency)==null?void 0:k.p50_us)??0)/1e3,p99:(((M=x.response_latency)==null?void 0:M.p99_us)??0)/1e3,fc50:(((P=x.fill_completion_latency)==null?void 0:P.p50_us)??0)/1e3,fc99:(((H=x.fill_completion_latency)==null?void 0:H.p99_us)??0)/1e3},_=[...w,S];return _.length>Zp?_.slice(-Zp):_}),oW.has(x.status)&&(d=!0,l==null||l.close())},l.onclose=()=>{s||(o(!1),d||p())},l.onerror=()=>{l==null||l.close()}}function p(){if(s||d)return;f+=1;const v=Math.min(iW,500*2**Math.min(f,4));u=setTimeout(m,v)}return m(),()=>{s=!0,clearTimeout(u),l==null||l.close()}},[e]),{latest:t,series:n,connected:i}}const Dw="img-export-exclude";async function lW(e,t,r){const{toPng:n,toJpeg:a}=await Cv(async()=>{const{toPng:u,toJpeg:f}=await import("./index-CyNOPa0n.js");return{toPng:u,toJpeg:f}},[]),i=getComputedStyle(document.body).backgroundColor||"#111",s=await(r==="png"?n:a)(e,{backgroundColor:i,pixelRatio:2,quality:.95,filter:u=>!(u instanceof HTMLElement&&u.classList.contains(Dw))}),l=document.createElement("a");l.href=s,l.download=t,l.click()}function cW(e,t){const r=new Date().toISOString().slice(0,19).replace(/[:T]/g,"-");return`${e}-${r}.${t}`}const _l=new Set(["completed","stopped","error"]);function wn(e){return e?(e/1e3).toFixed(3):"—"}function Rw(e,t){return t<=0?"—":(e/t).toFixed(2)}function Kp({title:e,sub:t,stats:r}){return c.jsxs("div",{className:"perf-lat-card",children:[c.jsxs("div",{className:"perf-lat-title",children:[e,t&&c.jsx("span",{className:"perf-lat-sub",children:t})]}),c.jsx("div",{className:"perf-lat-grid",children:["p50_us","p95_us","p99_us","max_us"].map(n=>c.jsxs("div",{className:"perf-lat-cell",children:[c.jsx("span",{className:"perf-lat-val",children:wn(r[n])}),c.jsxs("span",{className:"perf-lat-lbl",children:[n.replace("_us","")," ms"]})]},n))})]})}function uW({runId:e}){var w;const{latest:t,series:r,connected:n}=sW(e),[a,i]=h.useState(null),[o,s]=h.useState(!1),l=h.useRef(null);h.useEffect(()=>{let S=!0;i(null);let _;const j=async()=>{try{const O=await G.perf.runs.get(e);if(!S)return;i(O),_l.has(O.status)&&clearInterval(_)}catch{}};return _=setInterval(j,2e3),j(),()=>{S=!1,clearInterval(_)}},[e]);const u=t?_l.has(t.status):a?_l.has(a.status):!1,f=!u;async function d(){try{await G.perf.runs.stop(e)}catch{}}async function m(S){if(!(!l.current||o)){s(!0);try{const _=S==="png"?"png":"jpg";await lW(l.current,cW(`perf-${e.slice(0,8)}`,_),S)}catch(_){console.error("image export failed",_)}finally{s(!1)}}}const p=t==null?void 0:t.client,v=t==null?void 0:t.venue,y=t==null?void 0:t.errors,g=(y==null?void 0:y.saturated)||(t==null?void 0:t.status)==="saturated",b=t==null?void 0:t.rate_config,x=[];return p&&(x.push({label:"orders/s",value:Math.round(p.ops_live).toLocaleString()}),(b==null?void 0:b.orders_per_window)!=null&&(b==null?void 0:b.order_window_ms)!=null&&x.push({label:"order rate (cfg)",value:`${b.orders_per_window.toLocaleString()} / ${b.order_window_ms}ms`}),x.push({label:"orders sent",value:p.orders_sent.toLocaleString()},{label:"pending",value:p.pending.toLocaleString()},{label:"dropped",value:p.dropped.toLocaleString()})),v&&(x.push({label:"fills/s",value:Math.round(v.fps_live).toLocaleString()}),(b==null?void 0:b.fills_per_window)!=null&&(b==null?void 0:b.fill_window_ms)!=null&&x.push({label:"fill rate (cfg)",value:`${b.fills_per_window.toLocaleString()} / ${b.fill_window_ms}ms`}),x.push({label:"fills",value:v.fills_sent.toLocaleString()},{label:"fills/order",value:Rw(v.fills_sent,v.orders_received-v.unfilled)},{label:"fill ratio",value:v.fill_ratio.toFixed(3)},{label:"recv",value:v.orders_received.toLocaleString()},{label:"acks",value:v.acks_sent.toLocaleString()},{label:"unfilled",value:v.unfilled.toLocaleString()})),y&&x.push({label:"lost",value:y.lost_timeout.toLocaleString()},{label:"rejected",value:y.rejected.toLocaleString()}),c.jsxs("div",{className:"perf-dashboard",ref:l,children:[c.jsxs("div",{className:"perf-dash-header",children:[c.jsxs("div",{className:"perf-dash-status",children:[c.jsx("span",{className:`badge perf-state-${(t==null?void 0:t.status)??(a==null?void 0:a.status)??"pending"}`,children:(t==null?void 0:t.status)??(a==null?void 0:a.status)??"pending"}),t&&c.jsxs("span",{className:"perf-dash-elapsed",children:[Math.round(t.elapsed_s),"s",t.duration_s?` / ${t.duration_s}s`:""," · ",t.mode]}),c.jsx("span",{className:`perf-conn ${n?"on":"off"}`,children:n?"● live":"○ offline"})]}),c.jsxs("div",{className:`perf-dash-actions ${Dw}`,children:[f&&c.jsx("button",{className:"btn btn-sm btn-danger",onClick:d,children:"■ Stop"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>G.perf.runs.export(e,"messages"),children:"Export CSV"}),u&&c.jsx("button",{className:"btn btn-sm btn-ghost",title:"Per-exec detail (requires record_execs)",onClick:()=>G.perf.runs.export(e,"execs"),children:"Execs CSV"}),c.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>G.perf.runs.export(e,"both"),children:"Export ZIP"}),c.jsx("button",{className:"btn btn-sm btn-ghost",disabled:o,onClick:()=>m("png"),children:o?"…":"PNG"}),c.jsx("button",{className:"btn btn-sm btn-ghost",disabled:o,onClick:()=>m("jpeg"),children:o?"…":"JPG"})]})]}),g&&c.jsxs("div",{className:"perf-saturated-banner",children:["⚠ SATURATED — injection paused to protect latency measurement.",p&&c.jsxs(c.Fragment,{children:[" Pending ",p.pending.toLocaleString(),", dropped ",p.dropped.toLocaleString(),"."]})]}),(w=a==null?void 0:a.warnings)==null?void 0:w.map((S,_)=>c.jsxs("div",{className:"perf-warning-banner",children:["⚠ ",S]},_)),!t&&!u&&c.jsx("div",{className:"loading-msg",children:"Waiting for first snapshot…"}),x.length>0&&c.jsx("div",{className:"stat-cards",children:x.map(({label:S,value:_})=>c.jsxs("div",{className:"stat-card",children:[c.jsx("span",{className:"stat-card-value",children:_}),c.jsx("span",{className:"stat-card-label",children:S})]},S))}),t&&c.jsxs("div",{className:"perf-lat-row",children:[c.jsx(Kp,{title:"Response latency",sub:"order-2-ack",stats:t.response_latency}),c.jsx(Kp,{title:"Fill-completion latency",sub:"order-2-last-fill",stats:t.fill_completion_latency})]}),r.length>1&&c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"analysis-section-title",children:"Throughput"}),c.jsx("div",{className:"analysis-chart-wrap",children:c.jsx(Pn,{width:"100%",height:200,children:c.jsxs(Ii,{data:r,margin:{top:4,right:16,bottom:4,left:8},children:[c.jsx(Gr,{strokeDasharray:"3 3",stroke:"var(--border)"}),c.jsx(Zr,{dataKey:"t",tick:{fontSize:11,fill:"var(--text2)"},tickFormatter:S=>`${Math.round(S)}s`}),c.jsx(Kr,{tick:{fontSize:11,fill:"var(--text2)"},allowDecimals:!1}),c.jsx(En,{contentStyle:{background:"var(--bg3)",border:"1px solid var(--border)",fontSize:12},labelStyle:{color:"var(--text)"},labelFormatter:S=>`${Math.round(Number(S))}s`}),c.jsx(pa,{wrapperStyle:{fontSize:11}}),c.jsx(_t,{type:"monotone",dataKey:"ops",name:"orders/s",stroke:"#00b4c8",dot:!1,isAnimationActive:!1}),c.jsx(_t,{type:"monotone",dataKey:"fps",name:"fills/s",stroke:"#a78bfa",dot:!1,isAnimationActive:!1}),c.jsx(_t,{type:"monotone",dataKey:"tps",name:"total msgs/s",stroke:"#fbbf24",dot:!1,isAnimationActive:!1})]})})}),c.jsx("div",{className:"analysis-section-title",children:"Response latency (ms)"}),c.jsx("div",{className:"analysis-chart-wrap",children:c.jsx(Pn,{width:"100%",height:200,children:c.jsxs(Ii,{data:r,margin:{top:4,right:16,bottom:4,left:8},children:[c.jsx(Gr,{strokeDasharray:"3 3",stroke:"var(--border)"}),c.jsx(Zr,{dataKey:"t",tick:{fontSize:11,fill:"var(--text2)"},tickFormatter:S=>`${Math.round(S)}s`}),c.jsx(Kr,{tick:{fontSize:11,fill:"var(--text2)"}}),c.jsx(En,{contentStyle:{background:"var(--bg3)",border:"1px solid var(--border)",fontSize:12},labelStyle:{color:"var(--text)"},labelFormatter:S=>`${Math.round(Number(S))}s`}),c.jsx(pa,{wrapperStyle:{fontSize:11}}),c.jsx(_t,{type:"monotone",dataKey:"p50",name:"p50",stroke:"#4ade80",dot:!1,isAnimationActive:!1}),c.jsx(_t,{type:"monotone",dataKey:"p99",name:"p99",stroke:"#f87171",dot:!1,isAnimationActive:!1})]})})}),c.jsx("div",{className:"analysis-section-title",children:"Fill-completion latency (ms)"}),c.jsx("div",{className:"analysis-chart-wrap",children:c.jsx(Pn,{width:"100%",height:200,children:c.jsxs(Ii,{data:r,margin:{top:4,right:16,bottom:4,left:8},children:[c.jsx(Gr,{strokeDasharray:"3 3",stroke:"var(--border)"}),c.jsx(Zr,{dataKey:"t",tick:{fontSize:11,fill:"var(--text2)"},tickFormatter:S=>`${Math.round(S)}s`}),c.jsx(Kr,{tick:{fontSize:11,fill:"var(--text2)"}}),c.jsx(En,{contentStyle:{background:"var(--bg3)",border:"1px solid var(--border)",fontSize:12},labelStyle:{color:"var(--text)"},labelFormatter:S=>`${Math.round(Number(S))}s`}),c.jsx(pa,{wrapperStyle:{fontSize:11}}),c.jsx(_t,{type:"monotone",dataKey:"fc50",name:"p50",stroke:"#4ade80",dot:!1,isAnimationActive:!1}),c.jsx(_t,{type:"monotone",dataKey:"fc99",name:"p99",stroke:"#f87171",dot:!1,isAnimationActive:!1})]})})})]}),u&&(a==null?void 0:a.summary)&&c.jsx(dW,{status:a})]})}function dW({status:e}){const t=e.summary,r=[["Orders sent",t.orders_sent.toLocaleString()],["Acks sent",t.acks_sent.toLocaleString()],["Fills sent",t.fills_sent.toLocaleString()],["Responses",t.responses.toLocaleString()],["Completions (fully filled)",t.completions.toLocaleString()],["Fills per order (completed)",Rw(t.fills_sent,t.completions)],["Fill ratio (completed)",t.fill_ratio.toFixed(3)],["Lost / dropped / rejected",`${t.lost_timeout} / ${t.dropped} / ${t.rejected}`],["Response p50 / p99 (ms)",`${wn(t.response_latency.p50_us)} / ${wn(t.response_latency.p99_us)}`],["Response max (ms)",wn(t.response_latency.max_us)],["Fill-completion p50 / p99 (ms)",`${wn(t.fill_completion_latency.p50_us)} / ${wn(t.fill_completion_latency.p99_us)}`]];return c.jsxs("div",{className:"perf-summary-card",children:[c.jsxs("div",{className:"perf-section-title",children:["Run summary — ",e.status]}),c.jsx("table",{className:"perf-summary-table",children:c.jsx("tbody",{children:r.map(([n,a])=>c.jsxs("tr",{children:[c.jsx("td",{children:n}),c.jsx("td",{className:"mono",children:a})]},n))})})]})}const fW=new Set(["completed","stopped","error"]);function hW(e){if(!e)return"—";try{return new Date(e*1e3).toLocaleTimeString()}catch{return"—"}}function mW({activeRunId:e,onSelect:t,refreshKey:r}){const[n,a]=h.useState([]);h.useEffect(()=>{let o=!0;const s=()=>G.perf.runs.list().then(u=>{o&&a(u)}).catch(()=>{});s();const l=setInterval(s,2e3);return()=>{o=!1,clearInterval(l)}},[r]);async function i(o,s){o.stopPropagation();try{await G.perf.runs.stop(s)}catch{}}return n.length===0?c.jsx("div",{className:"perf-runlist-empty",children:"No runs yet. Configure and start one."}):c.jsxs("table",{className:"perf-runlist",children:[c.jsx("thead",{children:c.jsxs("tr",{children:[c.jsx("th",{children:"Name"}),c.jsx("th",{children:"Mode"}),c.jsx("th",{children:"Status"}),c.jsx("th",{children:"Started"}),c.jsx("th",{children:"Elapsed"}),c.jsx("th",{children:"Orders"}),c.jsx("th",{children:"Fill"}),c.jsx("th",{})]})}),c.jsx("tbody",{children:n.map(o=>c.jsxs("tr",{className:o.run_id===e?"perf-run-active":"",onClick:()=>t(o.run_id),children:[c.jsx("td",{children:o.name}),c.jsx("td",{children:o.mode}),c.jsx("td",{children:c.jsx("span",{className:`badge perf-state-${o.status}`,children:o.status})}),c.jsx("td",{children:hW(o.started_at)}),c.jsxs("td",{className:"mono",children:[Math.round(o.elapsed_s),"s"]}),c.jsx("td",{className:"mono",children:o.summary?o.summary.orders_sent.toLocaleString():"—"}),c.jsx("td",{className:"mono",children:o.summary?o.summary.fill_ratio.toFixed(2):"—"}),c.jsx("td",{className:"perf-runlist-actions",children:!fW.has(o.status)&&c.jsx("button",{className:"btn btn-sm btn-danger",onClick:s=>i(s,o.run_id),children:"Stop"})})]},o.run_id))})]})}const Ol=200,_n=(e,t,r=120)=>({field:e,headerName:t,width:r,type:"numericColumn",valueFormatter:n=>n.value==null?"":Number(n.value).toLocaleString()}),Pc=(e,t)=>({field:e,headerName:t,width:150,type:"numericColumn",valueFormatter:r=>r.value==null?"":`${Math.round(Number(r.value)).toLocaleString()}`}),pW=[{field:"clordid",headerName:"ClOrdID",width:150},{field:"corr_id",headerName:"Corr ID",width:150},{field:"msg_type",headerName:"Type",width:80},{field:"symbol",headerName:"Symbol",width:90},{field:"side",headerName:"Side",width:70},_n("qty","Qty",90),_n("price","Price",100),Pc("response_latency_us","Resp µs"),Pc("fill_latency_us","Fill µs"),_n("fill_qty","Fill qty",100),_n("fill_price","Fill price",110),{field:"status",headerName:"Status",width:110,cellStyle:e=>e.value==="lost"||e.value==="dropped"?{color:"#facc15",fontWeight:"bold"}:void 0}],vW=[{field:"scenario_name",headerName:"Scenario",flex:1,minWidth:160},Pc("latency_us","Latency µs"),_n("msg_count","Msgs",90),_n("fill_ratio","Fill ratio",110),{field:"status",headerName:"Status",width:110,cellStyle:e=>e.value==="lost"||e.value==="dropped"?{color:"#facc15",fontWeight:"bold"}:void 0}];function gW({runId:e}){const t=X(g=>g.theme),[r,n]=h.useState("messages"),[a,i]=h.useState(0),[o,s]=h.useState([]),[l,u]=h.useState(0),[f,d]=h.useState(!1);h.useEffect(()=>{i(0)},[r,e]),h.useEffect(()=>{let g=!0;return d(!0),(r==="messages"?G.perf.runs.messages:G.perf.runs.scenarios)(e,{limit:Ol,offset:a*Ol}).then(x=>{g&&(s(x.items),u(x.total))}).catch(()=>{g&&(s([]),u(0))}).finally(()=>{g&&d(!1)}),()=>{g=!1}},[e,r,a]);const m=h.useMemo(()=>r==="messages"?pW:vW,[r]),p=h.useMemo(()=>({sortable:!0,filter:!0,resizable:!0}),[]),v=Math.max(0,Math.ceil(l/Ol)-1),y=t==="dark"?"ag-theme-quartz-dark":"ag-theme-quartz";return c.jsxs("div",{className:"perf-results",children:[c.jsxs("div",{className:"perf-results-toolbar",children:[c.jsxs("div",{className:"perf-results-tabs",children:[c.jsx("button",{className:`btn btn-sm ${r==="messages"?"btn-primary":"btn-ghost"}`,onClick:()=>n("messages"),children:"Messages"}),c.jsx("button",{className:`btn btn-sm ${r==="scenarios"?"btn-primary":"btn-ghost"}`,onClick:()=>n("scenarios"),children:"Scenarios"})]}),c.jsxs("div",{className:"perf-results-pager",children:[c.jsxs("span",{children:[l.toLocaleString()," rows"]}),c.jsx("button",{className:"btn btn-sm btn-ghost",disabled:a<=0||f,onClick:()=>i(g=>Math.max(0,g-1)),children:"‹ Prev"}),c.jsxs("span",{children:["Page ",a+1," / ",v+1]}),c.jsx("button",{className:"btn btn-sm btn-ghost",disabled:a>=v||f,onClick:()=>i(g=>Math.min(v,g+1)),children:"Next ›"})]})]}),c.jsx("div",{className:`${y} perf-results-grid`,children:c.jsx(Nc,{rowData:o,columnDefs:m,defaultColDef:p,animateRows:!1,suppressCellFocus:!0})})]})}function yW(){const{theme:e,toggleTheme:t,branding:r,setSessions:n}=X(),a=Us(),i=r!=null&&r.prefix?`${r.prefix} FIXture`:"FIXture";Sv(),h.useEffect(()=>{G.sessions.list().then(n).catch(()=>{})},[]);const[o,s]=h.useState("configure"),[l,u]=h.useState(null),[f,d]=h.useState(0);function m(y){u(y),d(g=>g+1),s("live")}function p(y){u(y),s("live")}const v=[{id:"configure",label:"Configure"},{id:"live",label:"Live",disabled:!l},{id:"results",label:"Results",disabled:!l},{id:"history",label:"History"}];return c.jsxs("div",{className:"app perf-page",children:[c.jsxs("header",{className:"app-header",children:[c.jsx("img",{src:a,alt:i,className:"app-logo"}),c.jsx("div",{className:"perf-page-title",children:"Performance Testing"}),c.jsxs("div",{className:"header-right",children:[c.jsx(qr,{to:"/",className:"btn btn-sm btn-ghost",children:"← Sessions"}),c.jsx("button",{className:"btn-icon theme-toggle",onClick:t,title:"Toggle theme",children:e==="dark"?"☀":"🌙"})]})]}),c.jsx("div",{className:"perf-tabbar",children:v.map(y=>c.jsx("button",{className:`perf-tab${o===y.id?" active":""}`,disabled:y.disabled,onClick:()=>s(y.id),children:y.label},y.id))}),c.jsxs("div",{className:"perf-body",children:[o==="configure"&&c.jsx("div",{className:"perf-pane perf-pane-form",children:c.jsx(aW,{onStarted:m})}),o==="live"&&l&&c.jsx("div",{className:"perf-pane",children:c.jsx(uW,{runId:l},l)}),o==="results"&&l&&c.jsx("div",{className:"perf-pane",children:c.jsx(gW,{runId:l},l)}),o==="history"&&c.jsx("div",{className:"perf-pane",children:c.jsx(mW,{activeRunId:l,onSelect:p,refreshKey:f})})]})]})}function bW(){return X(t=>t.token)?c.jsx(uv,{}):c.jsx(Mi,{to:"/login",replace:!0})}function xW(){const e=X(t=>t.user);return e?e.role!=="platform_admin"?c.jsx(Mi,{to:"/",replace:!0}):c.jsx(uv,{}):c.jsx(Mi,{to:"/login",replace:!0})}function wW(){const e=X(t=>t.setBranding);h.useEffect(()=>{fetch("/api/branding").then(t=>t.json()).then(t=>{e(t),Tw(t)}).catch(()=>{})},[])}function jW(){const e=an(),t=X(r=>r.theme);return wW(),h.useEffect(()=>{document.documentElement.setAttribute("data-theme",t)},[t]),h.useEffect(()=>{fetch("/api/setup/status").then(r=>r.json()).then(r=>{r.setup_required&&e("/setup",{replace:!0})}).catch(()=>{})},[]),c.jsxs(Zj,{children:[c.jsx(Lt,{path:"/setup",element:c.jsx(Xz,{})}),c.jsx(Lt,{path:"/login",element:c.jsx(Yz,{})}),c.jsx(Lt,{path:"/register",element:c.jsx(Vz,{})}),c.jsxs(Lt,{element:c.jsx(bW,{}),children:[c.jsx(Lt,{path:"/",element:c.jsx(qz,{})}),c.jsxs(Lt,{element:c.jsx(xW,{}),children:[c.jsx(Lt,{path:"/admin/users",element:c.jsx(rW,{})}),c.jsx(Lt,{path:"/perf",element:c.jsx(yW,{})})]})]}),c.jsx(Lt,{path:"*",element:c.jsx(Mi,{to:"/",replace:!0})})]})}Pl.createRoot(document.getElementById("root")).render(c.jsx(qp.StrictMode,{children:c.jsx(vS,{children:c.jsx(jW,{})})}));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<title>FIXture</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-FGgej6RF.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/ag-grid-_QKprVdm.js">
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/assets/index-BwQf-cei.css">
|
|
11
11
|
</head>
|
|
@@ -65,7 +65,7 @@ fixture/static/index.html
|
|
|
65
65
|
fixture/static/assets/ag-grid-_QKprVdm.js
|
|
66
66
|
fixture/static/assets/index-BwQf-cei.css
|
|
67
67
|
fixture/static/assets/index-CyNOPa0n.js
|
|
68
|
-
fixture/static/assets/index-
|
|
68
|
+
fixture/static/assets/index-FGgej6RF.js
|
|
69
69
|
fixture/static/assets/react-vendor-2eF0YfZT.js
|
|
70
70
|
fixture/ui/__init__.py
|
|
71
71
|
fixtureqa.egg-info/PKG-INFO
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|