Files
Remote-Terminal-for-MeshCore/frontend/prebuilt/assets/RepeaterDashboard-DjXZo1ON.js
2026-03-15 18:07:30 -07:00

4 lines
28 KiB
JavaScript

const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/NeighborsMiniMap-BfUArOef.js","assets/index-DhrMLy6_.js","assets/index-Kkb-Bio9.css","assets/leaflet-lJm4twjh.js","assets/leaflet-Dgihpmma.css","assets/Popup-DkSClDwo.js"])))=>i.map(i=>d[i]);
import{r as a,k as $,t as q,j as e,I as ne,B as z,l as F,D as K,E as de,F as ue,_ as me,G as fe,H as xe,J as he,R as pe,K as ge,M as be,N as Q,O as je,P as ve}from"./index-DhrMLy6_.js";import{S as X}from"./separator-DS1eMu86.js";const ee=3,Ne=2e3,ye=20,E={loading:!1,attempt:0,error:null,fetched_at:null};function te(){return{status:{...E},nodeInfo:{...E},neighbors:{...E},acl:{...E},radioSettings:{...E},advertIntervals:{...E},ownerInfo:{...E},lppTelemetry:{...E}}}function se(){return{status:null,nodeInfo:null,neighbors:null,acl:null,radioSettings:null,advertIntervals:null,ownerInfo:null,lppTelemetry:null}}const H=new Map;function we(t){switch(t){case"timeout":return"Login confirmation not heard";case"error":return"Login not confirmed";default:return"Repeater login not confirmed"}}function oe(t){return{...t}}function le(t){return{status:{...t.status,loading:!1},nodeInfo:{...t.nodeInfo,loading:!1},neighbors:{...t.neighbors,loading:!1},acl:{...t.acl,loading:!1},radioSettings:{...t.radioSettings,loading:!1},advertIntervals:{...t.advertIntervals,loading:!1},ownerInfo:{...t.ownerInfo,loading:!1},lppTelemetry:{...t.lppTelemetry,loading:!1}}}function ae(t){return t.map(s=>({...s}))}function _e(t){if(!t)return null;const s=H.get(t);return s?(H.delete(t),H.set(t,s),{loggedIn:s.loggedIn,loginError:s.loginError,paneData:oe(s.paneData),paneStates:le(s.paneStates),consoleHistory:ae(s.consoleHistory)}):null}function Ce(t,s){if(H.delete(t),H.set(t,{loggedIn:s.loggedIn,loginError:s.loginError,paneData:oe(s.paneData),paneStates:le(s.paneStates),consoleHistory:ae(s.consoleHistory)}),H.size>ye){const r=H.keys().next().value;r&&H.delete(r)}}function Re(t,s){switch(s){case"status":return $.repeaterStatus(t);case"nodeInfo":return $.repeaterNodeInfo(t);case"neighbors":return $.repeaterNeighbors(t);case"acl":return $.repeaterAcl(t);case"radioSettings":return $.repeaterRadioSettings(t);case"advertIntervals":return $.repeaterAdvertIntervals(t);case"ownerInfo":return $.repeaterOwnerInfo(t);case"lppTelemetry":return $.repeaterLppTelemetry(t)}}function Le(t){const s=t&&t.type==="contact"?t.id:null,r=_e(s),[o,l]=a.useState((r==null?void 0:r.loggedIn)??!1),[n,h]=a.useState(!1),[i,c]=a.useState((r==null?void 0:r.loginError)??null),[j,D]=a.useState((r==null?void 0:r.paneData)??se),[v,R]=a.useState((r==null?void 0:r.paneStates)??te),w=a.useRef((r==null?void 0:r.paneData)??se()),N=a.useRef((r==null?void 0:r.paneStates)??te()),[A,f]=a.useState((r==null?void 0:r.consoleHistory)??[]),[T,P]=a.useState(!1),u=a.useRef((t==null?void 0:t.id)??null),m=a.useRef(!0);a.useEffect(()=>{u.current=s},[s]),a.useEffect(()=>(m.current=!0,()=>{m.current=!1}),[]),a.useEffect(()=>{s&&Ce(s,{loggedIn:o,loginError:i,paneData:j,paneStates:v,consoleHistory:A})},[A,s,o,i,j,v]),a.useEffect(()=>{w.current=j},[j]),a.useEffect(()=>{N.current=v},[v]);const _=a.useCallback(()=>!t||t.type!=="contact"?null:t.id,[t]),L=a.useCallback(async p=>{const b=_();if(!b)return;const y=b;h(!0),c(null);try{const d=await $.repeaterLogin(b,p);if(u.current!==y)return;if(l(!0),!d.authenticated){const g=d.message??"Repeater login was not confirmed";c(g),q.error(we(d.status),{description:g})}}catch(d){if(u.current!==y)return;const g=d instanceof Error?d.message:"Login failed";l(!0),c(g),q.error("Login request failed",{description:`${g}. The dashboard is still available, but repeater operations may fail until a login succeeds.`})}finally{u.current===y&&h(!1)}},[_]),W=a.useCallback(async()=>{await L("")},[L]),S=a.useCallback(async p=>{const b=_();if(!b)return;const y=b;if(p==="neighbors"){const d=N.current.nodeInfo,g=w.current.nodeInfo;if((d.error!==null||d.fetched_at==null&&g==null)&&(await S("nodeInfo"),!m.current||u.current!==y))return}for(let d=1;d<=ee;d++){if(!m.current||u.current!==y)return;const g={loading:!0,attempt:d,error:null,fetched_at:N.current[p].fetched_at??null};N.current={...N.current,[p]:g},R(C=>({...C,[p]:g}));try{const C=await Re(b,p);if(!m.current||u.current!==y)return;w.current={...w.current,[p]:C};const O={loading:!1,attempt:d,error:null,fetched_at:Date.now()};N.current={...N.current,[p]:O},D(M=>({...M,[p]:C})),R(M=>({...M,[p]:O}));return}catch(C){if(!m.current||u.current!==y)return;const O=C instanceof Error?C.message:"Request failed";if(d===ee){const M={loading:!1,attempt:d,error:O,fetched_at:N.current[p].fetched_at??null};N.current={...N.current,[p]:M},R(ce=>({...ce,[p]:M})),q.error(`Failed to fetch ${p}`,{description:O})}else await new Promise(M=>setTimeout(M,Ne))}}},[_]),I=a.useCallback(async()=>{const p=["status","nodeInfo","neighbors","radioSettings","acl","advertIntervals","ownerInfo","lppTelemetry"];for(const b of p)await S(b)},[S]),k=a.useCallback(async p=>{const b=_();if(!b)return;const y=b,d=Math.floor(Date.now()/1e3);f(g=>[...g,{command:p,response:"",timestamp:d,outgoing:!0}]),P(!0);try{const g=await $.sendRepeaterCommand(b,p);if(u.current!==y)return;f(C=>[...C,{command:p,response:g.response,timestamp:g.sender_timestamp??d,outgoing:!1}])}catch(g){if(u.current!==y)return;const C=g instanceof Error?g.message:"Command failed";f(O=>[...O,{command:p,response:`Error: ${C}`,timestamp:d,outgoing:!1}])}finally{u.current===y&&P(!1)}},[_]),U=a.useCallback(async()=>{await k("advert.zerohop")},[k]),V=a.useCallback(async()=>{await k("advert")},[k]),J=a.useCallback(async()=>{await k("reboot")},[k]),Y=a.useCallback(async()=>{const p=Math.floor(Date.now()/1e3);await k(`time ${p}`)},[k]);return{loggedIn:o,loginLoading:n,loginError:i,paneData:j,paneStates:v,consoleHistory:A,consoleLoading:T,login:L,loginAsGuest:W,refreshPane:S,loadAll:I,sendConsoleCommand:k,sendZeroHopAdvert:U,sendFloodAdvert:V,rebootRepeater:J,syncClock:Y}}function Se({repeaterName:t,loading:s,error:r,onLogin:o,onLoginAsGuest:l}){const[n,h]=a.useState(""),i=a.useCallback(async c=>{c.preventDefault(),!s&&await o(n.trim())},[n,s,o]);return e.jsx("div",{className:"flex-1 flex items-center justify-center p-4",children:e.jsxs("div",{className:"w-full max-w-sm space-y-6",children:[e.jsxs("div",{className:"text-center space-y-1",children:[e.jsx("h2",{className:"text-lg font-semibold",children:t}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Log in to access repeater dashboard"})]}),e.jsxs("form",{onSubmit:i,className:"space-y-4",autoComplete:"off",children:[e.jsx(ne,{type:"password",autoComplete:"off",name:"repeater-password","data-lpignore":"true","data-1p-ignore":"true","data-bwignore":"true",value:n,onChange:c=>h(c.target.value),placeholder:"Repeater password...","aria-label":"Repeater password",disabled:s,autoFocus:!0}),r&&e.jsx("p",{className:"text-sm text-destructive text-center",role:"alert",children:r}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(z,{type:"submit",disabled:s,className:"w-full",children:s?"Logging in...":"Login with Password"}),e.jsx(z,{type:"button",variant:"outline",disabled:s,className:"w-full",onClick:l,children:"Login as Guest / ACLs"})]})]})]})})}function ie({className:t}){return e.jsx("svg",{className:t,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"})})}function Z(t){if(t<60)return`${t}s`;const s=Math.floor(t/86400),r=Math.floor(t%86400/3600),o=Math.floor(t%3600/60);return s>0?r>0&&o>0?`${s}d${r}h${o}m`:r>0?`${s}d${r}h`:o>0?`${s}d${o}m`:`${s}d`:r>0?o>0?`${r}h${o}m`:`${r}h`:`${o}m`}function ke(t){let s;const r=t.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?\s*-\s*(\d{1,2})\/(\d{1,2})\/(\d{4})/);if(r){const[,j,D,v,R,w,N]=r;s=new Date(Date.UTC(+N,+w-1,+R,+j,+D,+(v??0)))}else s=new Date(t.replace(" ","T")+(t.includes("Z")||t.includes("UTC")?"":"Z"));if(isNaN(s.getTime()))return{text:"(invalid)",isLarge:!1};const o=Math.abs(Date.now()-s.getTime()),l=Math.floor(o/1e3);if(l>=86400)return{text:">24 hours!",isLarge:!0};const n=Math.floor(l/3600),h=Math.floor(l%3600/60),i=l%60,c=[];return n>0&&c.push(`${n}h`),h>0&&c.push(`${h}m`),c.push(`${i}s`),{text:c.join(""),isLarge:!1}}function re(t){if(t==null)return"—";const s=t.trim();return s==="0"?"<disabled>":`${s}h`}function De(t){const s=Math.max(0,Math.floor((Date.now()-t)/1e3));if(s<60)return"Just now";const r=Math.floor(s/60);if(r<60)return`${r} minute${r===1?"":"s"} ago`;const o=Math.floor(r/60);return`${o} hour${o===1?"":"s"} ago`}function Ie(t){return new Date(t).toLocaleTimeString([],{hour:"numeric",minute:"2-digit",second:"2-digit"})}function G({title:t,state:s,onRefresh:r,disabled:o,children:l,className:n,contentClassName:h}){const i=s.fetched_at??null;return e.jsxs("div",{className:F("border border-border rounded-lg overflow-hidden",n),children:[e.jsxs("div",{className:"flex items-center justify-between px-3 py-2 bg-muted/50 border-b border-border",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("h3",{className:"text-sm font-medium",children:t}),i&&e.jsxs("p",{className:"text-[11px] text-muted-foreground",title:new Date(i).toLocaleString(),children:["Fetched ",Ie(i)," (",De(i),")"]})]}),r&&e.jsx("button",{type:"button",onClick:r,disabled:o||s.loading,className:F("p-1 rounded transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",o||s.loading?"text-muted-foreground":"text-success hover:bg-accent hover:text-success"),title:"Refresh","aria-label":`Refresh ${t}`,children:e.jsx(ie,{className:F("w-3.5 h-3.5",s.loading&&"animate-spin [animation-direction:reverse]")})})]}),s.error&&e.jsx("div",{className:"px-3 py-1.5 text-xs text-destructive bg-destructive/5 border-b border-border",children:s.error}),e.jsx("div",{className:F("p-3",h),children:s.loading?e.jsxs("p",{className:"text-sm text-muted-foreground italic",children:["Fetching",s.attempt>1?` (attempt ${s.attempt}/3)`:"","..."]}):l})]})}function B(){return e.jsx("p",{className:"text-sm text-muted-foreground italic",children:"<not fetched>"})}function x({label:t,value:s}){return e.jsxs("div",{className:"flex justify-between items-center text-sm py-0.5",children:[e.jsx("span",{className:"text-muted-foreground",children:t}),e.jsx("span",{className:"font-medium text-right",children:s})]})}const $e={temperature:"°C",humidity:"%",barometer:"hPa",voltage:"V",current:"mA",luminosity:"lux",altitude:"m",power:"W",distance:"mm",energy:"kWh",direction:"°",concentration:"ppm",colour:""};function Ae(t){return t.charAt(0).toUpperCase()+t.slice(1).replace(/_/g," ")}function Te({sensor:t}){const s=Ae(t.type_name);if(typeof t.value=="object"&&t.value!==null)return e.jsxs("div",{className:"py-0.5",children:[e.jsx("span",{className:"text-sm text-muted-foreground",children:s}),e.jsx("div",{className:"pl-3",children:Object.entries(t.value).map(([l,n])=>e.jsx(x,{label:l.charAt(0).toUpperCase()+l.slice(1),value:typeof n=="number"?n.toFixed(2):String(n)},l))})]});const r=$e[t.type_name]??"",o=typeof t.value=="number"?`${t.value%1===0?t.value:t.value.toFixed(2)}${r?` ${r}`:""}`:String(t.value);return e.jsx(x,{label:s,value:o})}function Pe({data:t,state:s,onRefresh:r,disabled:o}){return e.jsx(G,{title:"Telemetry",state:s,onRefresh:r,disabled:o,children:t?e.jsxs("div",{className:"space-y-2",children:[e.jsx(x,{label:"Battery",value:`${t.battery_volts.toFixed(3)}V`}),e.jsx(x,{label:"Uptime",value:Z(t.uptime_seconds)}),e.jsx(x,{label:"TX Airtime",value:Z(t.airtime_seconds)}),e.jsx(x,{label:"RX Airtime",value:Z(t.rx_airtime_seconds)}),e.jsx(X,{className:"my-1"}),e.jsx(x,{label:"Noise Floor",value:`${t.noise_floor_dbm} dBm`}),e.jsx(x,{label:"Last RSSI",value:`${t.last_rssi_dbm} dBm`}),e.jsx(x,{label:"Last SNR",value:`${t.last_snr_db.toFixed(1)} dB`}),e.jsx(X,{className:"my-1"}),e.jsx(x,{label:"Packets",value:`${t.packets_received.toLocaleString()} rx / ${t.packets_sent.toLocaleString()} tx`}),e.jsx(x,{label:"Flood",value:`${t.recv_flood.toLocaleString()} rx / ${t.sent_flood.toLocaleString()} tx`}),e.jsx(x,{label:"Direct",value:`${t.recv_direct.toLocaleString()} rx / ${t.sent_direct.toLocaleString()} tx`}),e.jsx(x,{label:"Duplicates",value:`${t.flood_dups.toLocaleString()} flood / ${t.direct_dups.toLocaleString()} direct`}),e.jsx(X,{className:"my-1"}),e.jsx(x,{label:"TX Queue",value:t.tx_queue_len}),e.jsx(x,{label:"Debug Flags",value:t.full_events})]}):e.jsx(B,{})})}const Fe=a.lazy(()=>me(()=>import("./NeighborsMiniMap-BfUArOef.js"),__vite__mapDeps([0,1,2,3,4,5])).then(t=>({default:t.NeighborsMiniMap})));function Me({data:t,state:s,onRefresh:r,disabled:o,contacts:l,nodeInfo:n,nodeInfoState:h,repeaterName:i}){const c=a.useMemo(()=>{const f=(n==null?void 0:n.lat)!=null?parseFloat(n.lat):null;return Number.isFinite(f)?f:null},[n==null?void 0:n.lat]),j=a.useMemo(()=>{const f=(n==null?void 0:n.lon)!=null?parseFloat(n.lon):null;return Number.isFinite(f)?f:null},[n==null?void 0:n.lon]),D=(n==null?void 0:n.name)||i,v=K(c,j),R=!v&&(h.error!==null||h.fetched_at!=null||n!==null),{neighborsWithCoords:w,sorted:N,hasDistances:A}=a.useMemo(()=>{if(!t)return{neighborsWithCoords:[],sorted:[],hasDistances:!1};const f=[],T=[];let P=!1;for(const u of t.neighbors){const m=l.find(S=>S.public_key.startsWith(u.pubkey_prefix)),_=(m==null?void 0:m.lat)??null,L=(m==null?void 0:m.lon)??null;let W=null;if(v&&K(_,L)){const S=ue(c,j,_,L);S!=null&&(W=de(S),P=!0)}T.push({...u,distance:W}),K(_,L)&&f.push({...u,lat:_,lon:L})}return T.sort((u,m)=>m.snr-u.snr),{neighborsWithCoords:f,sorted:T,hasDistances:P}},[l,t,v,c,j]);return e.jsx(G,{title:"Neighbors",state:s,onRefresh:r,disabled:o,className:"flex flex-col",contentClassName:"flex-1 flex flex-col",children:t?N.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No neighbors reported"}):e.jsxs("div",{className:"flex-1 flex flex-col gap-2",children:[e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"text-left text-muted-foreground text-xs",children:[e.jsx("th",{className:"pb-1 font-medium",children:"Name"}),e.jsx("th",{className:"pb-1 font-medium text-right",children:"SNR"}),A&&e.jsx("th",{className:"pb-1 font-medium text-right",children:"Dist"}),e.jsx("th",{className:"pb-1 font-medium text-right",children:"Last Heard"})]})}),e.jsx("tbody",{children:N.map((f,T)=>{const P=f.distance,u=f.snr>=0?`+${f.snr.toFixed(1)}`:f.snr.toFixed(1),m=f.snr>=6?"text-success":f.snr>=0?"text-warning":"text-destructive";return e.jsxs("tr",{className:"border-t border-border/50",children:[e.jsx("td",{className:"py-1",children:f.name||f.pubkey_prefix}),e.jsxs("td",{className:F("py-1 text-right font-mono",m),children:[u," dB"]}),A&&e.jsx("td",{className:"py-1 text-right text-muted-foreground font-mono",children:P??"—"}),e.jsxs("td",{className:"py-1 text-right text-muted-foreground",children:[Z(f.last_heard_seconds)," ago"]})]},T)})})]})}),v&&(w.length>0||v)?e.jsx(a.Suspense,{fallback:e.jsx("div",{className:"h-48 flex items-center justify-center text-xs text-muted-foreground",children:"Loading map..."}),children:e.jsx(Fe,{neighbors:w,radioLat:c,radioLon:j,radioName:D},w.map(f=>f.pubkey_prefix).join(","))}):R?e.jsx("div",{className:"rounded border border-border/70 bg-muted/20 px-3 py-2 text-xs text-muted-foreground",children:"GPS info failed to fetch; map and distance data not available. This may be due to missing or zero-zero GPS data on the repeater, or due to transient fetch failure. Try refreshing."}):null]}):e.jsx(B,{})})}function Ee({data:t,state:s,onRefresh:r,disabled:o}){const l={0:"bg-muted text-muted-foreground",1:"bg-info/10 text-info",2:"bg-success/10 text-success",3:"bg-warning/10 text-warning"};return e.jsx(G,{title:"ACL",state:s,onRefresh:r,disabled:o,children:t?t.acl.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No ACL entries"}):e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"text-left text-muted-foreground text-xs",children:[e.jsx("th",{className:"pb-1 font-medium",children:"Name"}),e.jsx("th",{className:"pb-1 font-medium text-right",children:"Permission"})]})}),e.jsx("tbody",{children:t.acl.map((n,h)=>e.jsxs("tr",{className:"border-t border-border/50",children:[e.jsx("td",{className:"py-1",children:n.name||n.pubkey_prefix}),e.jsx("td",{className:"py-1 text-right",children:e.jsx("span",{className:F("text-xs px-1.5 py-0.5 rounded",l[n.permission]??"bg-muted text-muted-foreground"),children:n.permission_name})})]},h))})]}):e.jsx(B,{})})}function He({data:t,state:s,onRefresh:r,disabled:o}){const l=a.useMemo(()=>t!=null&&t.clock_utc?ke(t.clock_utc):null,[t==null?void 0:t.clock_utc]);return e.jsx(G,{title:"Node Info",state:s,onRefresh:r,disabled:o,children:t?e.jsxs("div",{children:[e.jsx(x,{label:"Name",value:t.name??"—"}),e.jsx(x,{label:"Lat / Lon",value:t.lat!=null||t.lon!=null?`${t.lat??"—"}, ${t.lon??"—"}`:"—"}),e.jsxs("div",{className:"flex justify-between text-sm py-0.5",children:[e.jsx("span",{className:"text-muted-foreground",children:"Clock (UTC)"}),e.jsxs("span",{children:[t.clock_utc??"—",l&&e.jsxs("span",{className:F("ml-2 text-xs",l.isLarge?"text-destructive":"text-muted-foreground"),children:["(drift: ",l.text,")"]})]})]})]}):e.jsx(B,{})})}function ze(t){if(t==null)return{display:"—",raw:null};const s=t.trim(),r=s.split(",").map(w=>w.trim());if(r.length!==4)return{display:s||"—",raw:s||null};const[o,l,n,h]=r,i=Number.parseFloat(o),c=Number.parseFloat(l),j=Number.parseInt(n,10),D=Number.parseInt(h,10);if(![i,c,j,D].every(Number.isFinite))return{display:s||"—",raw:s||null};const v=Number(i.toFixed(3)).toString(),R=Number(c.toFixed(3)).toString();return{display:`${v} MHz, BW ${R} kHz, SF${j}, CR${D}`,raw:s}}function Be({data:t,state:s,onRefresh:r,disabled:o,advertData:l,advertState:n,onRefreshAdvert:h}){const i=ze((t==null?void 0:t.radio)??null);return e.jsxs(G,{title:"Radio Settings",state:s,onRefresh:r,disabled:o,children:[t?e.jsxs("div",{children:[e.jsx(x,{label:"Firmware",value:t.firmware_version??"—"}),e.jsx(x,{label:"Radio",value:e.jsx("span",{title:i.raw??void 0,children:i.display})}),e.jsx(x,{label:"TX Power",value:t.tx_power!=null?`${t.tx_power} dBm`:"—"}),e.jsx(x,{label:"Airtime Factor",value:t.airtime_factor??"—"}),e.jsx(x,{label:"Repeat Mode",value:t.repeat_enabled??"—"}),e.jsx(x,{label:"Max Flood Hops",value:t.flood_max??"—"})]}):e.jsx(B,{}),e.jsx(X,{className:"my-2"}),e.jsxs("div",{className:"flex items-center justify-between mb-1",children:[e.jsx("span",{className:"text-xs font-medium text-muted-foreground",children:"Advert Intervals"}),e.jsx("button",{type:"button",onClick:h,disabled:o||n.loading,className:F("p-1 rounded transition-colors disabled:opacity-50",o||n.loading?"text-muted-foreground":"text-success hover:bg-accent hover:text-success"),title:"Refresh Advert Intervals","aria-label":"Refresh Advert Intervals",children:e.jsx(ie,{className:F("w-3 h-3",n.loading&&"animate-spin [animation-direction:reverse]")})})]}),n.error&&e.jsx("p",{className:"text-xs text-destructive mb-1",children:n.error}),n.loading?e.jsxs("p",{className:"text-sm text-muted-foreground italic",children:["Fetching",n.attempt>1?` (attempt ${n.attempt}/3)`:"","..."]}):l?e.jsxs("div",{children:[e.jsx(x,{label:"Local Advert",value:re(l.advert_interval)}),e.jsx(x,{label:"Flood Advert",value:re(l.flood_advert_interval)})]}):e.jsx(B,{})]})}function Oe({data:t,state:s,onRefresh:r,disabled:o}){return e.jsx(G,{title:"LPP Sensors",state:s,onRefresh:r,disabled:o,children:t?t.sensors.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No sensor data available"}):e.jsx("div",{className:"space-y-0.5",children:t.sensors.map((l,n)=>e.jsx(Te,{sensor:l},n))}):e.jsx(B,{})})}function Ge({data:t,state:s,onRefresh:r,disabled:o}){return e.jsx(G,{title:"Owner Info",state:s,onRefresh:r,disabled:o,children:t?e.jsxs("div",{className:"break-all",children:[e.jsx(x,{label:"Owner Info",value:t.owner_info??"—"}),e.jsx(x,{label:"Guest Password",value:t.guest_password??"—"})]}):e.jsx(B,{})})}function We({onSendZeroHopAdvert:t,onSendFloodAdvert:s,onSyncClock:r,onReboot:o,consoleLoading:l}){const[n,h]=a.useState(!1),i=a.useCallback(()=>{if(!n){h(!0);return}h(!1),o()},[n,o]);return a.useEffect(()=>{if(!n)return;const c=setTimeout(()=>h(!1),3e3);return()=>clearTimeout(c)},[n]),e.jsxs("div",{className:"border border-border rounded-lg overflow-hidden",children:[e.jsx("div",{className:"px-3 py-2 bg-muted/50 border-b border-border",children:e.jsx("h3",{className:"text-sm font-medium",children:"Actions"})}),e.jsxs("div",{className:"p-3 flex flex-wrap gap-2",children:[e.jsx(z,{variant:"outline",size:"sm",onClick:t,disabled:l,children:"Zero Hop Advert"}),e.jsx(z,{variant:"destructive",size:"sm",onClick:s,disabled:l,children:"Flood Advert"}),e.jsx(z,{variant:"outline",size:"sm",onClick:r,disabled:l,children:"Sync Clock"}),e.jsx(z,{variant:n?"destructive":"outline",size:"sm",onClick:i,disabled:l,children:n?"Confirm Reboot":"Reboot"})]})]})}function qe({history:t,loading:s,onSend:r}){const[o,l]=a.useState(""),n=a.useRef(null);a.useEffect(()=>{n.current&&(n.current.scrollTop=n.current.scrollHeight)},[t]);const h=a.useCallback(async i=>{i.preventDefault();const c=o.trim();!c||s||(l(""),await r(c))},[o,s,r]);return e.jsxs("div",{className:"border border-border rounded-lg overflow-hidden col-span-full",children:[e.jsx("div",{className:"px-3 py-2 bg-muted/50 border-b border-border",children:e.jsx("h3",{className:"text-sm font-medium",children:"Console"})}),e.jsxs("div",{ref:n,className:"h-48 overflow-y-auto p-3 font-mono text-xs bg-console-bg/50 text-console space-y-1",children:[t.length===0&&e.jsx("p",{className:"text-muted-foreground italic",children:"Type a CLI command below..."}),t.map((i,c)=>i.outgoing?e.jsxs("div",{className:"text-console-command",children:["> ",i.command]},c):e.jsx("div",{className:"text-console/80 whitespace-pre-wrap",children:i.response},c)),s&&e.jsx("div",{className:"text-muted-foreground animate-pulse",children:"..."})]}),e.jsxs("form",{onSubmit:h,className:"flex gap-2 p-2 border-t border-border",children:[e.jsx(ne,{type:"text",autoComplete:"off",name:"console-input",value:o,onChange:i=>l(i.target.value),placeholder:"CLI command...","aria-label":"Console command",disabled:s,className:"flex-1 font-mono text-sm"}),e.jsx(z,{type:"submit",size:"sm",disabled:s||!o.trim(),children:"Send"})]})]})}function Ue({conversation:t,contacts:s,favorites:r,notificationsSupported:o,notificationsEnabled:l,notificationsPermission:n,radioLat:h,radioLon:i,radioName:c,onTrace:j,onPathDiscovery:D,onToggleNotifications:v,onToggleFavorite:R,onDeleteContact:w}){const[N,A]=a.useState(!1),{loggedIn:f,loginLoading:T,loginError:P,paneData:u,paneStates:m,consoleHistory:_,consoleLoading:L,login:W,loginAsGuest:S,refreshPane:I,loadAll:k,sendConsoleCommand:U,sendZeroHopAdvert:V,sendFloodAdvert:J,rebootRepeater:Y,syncClock:p}=Le(t),b=s.find(g=>g.public_key===t.id),y=fe(r,"contact",t.id),d=Object.values(m).some(g=>g.loading);return e.jsxs("div",{className:"flex-1 flex flex-col min-h-0",children:[e.jsxs("header",{className:"flex justify-between items-start px-4 py-2.5 border-b border-border gap-2",children:[e.jsx("span",{className:"flex min-w-0 flex-1 flex-col",children:e.jsxs("span",{className:"flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5",children:[e.jsxs("span",{className:"flex min-w-0 flex-1 items-baseline gap-2",children:[e.jsx("span",{className:"min-w-0 flex-shrink truncate font-semibold text-base",children:t.name}),e.jsx("span",{className:"min-w-0 flex-1 truncate font-mono text-[11px] text-muted-foreground transition-colors hover:text-primary",role:"button",tabIndex:0,onKeyDown:xe,onClick:()=>{navigator.clipboard.writeText(t.id),q.success("Contact key copied!")},title:"Click to copy",children:t.id})]}),b&&e.jsx("span",{className:"min-w-0 flex-none text-[11px] text-muted-foreground max-sm:basis-full",children:e.jsx(he,{contact:b,ourLat:h,ourLon:i})})]})}),e.jsxs("div",{className:"flex items-center gap-0.5 flex-shrink-0",children:[f&&e.jsx(z,{variant:"outline",size:"sm",onClick:k,disabled:d,className:"h-7 px-2 text-[11px] leading-none border-success text-success hover:bg-success/10 hover:text-success sm:h-8 sm:px-3 sm:text-xs",children:d?"Loading...":"Load All"}),b&&e.jsx("button",{className:"p-1 rounded hover:bg-accent text-lg leading-none transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",onClick:()=>A(!0),title:"Path Discovery. Send a routed probe and inspect the forward and return paths","aria-label":"Path Discovery",children:e.jsx(pe,{className:"h-4 w-4 text-muted-foreground","aria-hidden":"true"})}),e.jsx("button",{className:"p-1 rounded hover:bg-accent text-lg leading-none transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",onClick:j,title:"Direct Trace","aria-label":"Direct Trace",children:e.jsx(ge,{className:"h-4 w-4 text-muted-foreground"})}),o&&e.jsxs("button",{className:"flex items-center gap-1 rounded px-1 py-1 hover:bg-accent text-lg leading-none transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",onClick:v,title:l?"Disable desktop notifications for this conversation":n==="denied"?"Notifications blocked by the browser":"Enable desktop notifications for this conversation","aria-label":l?"Disable notifications for this conversation":"Enable notifications for this conversation",children:[e.jsx(be,{className:`h-4 w-4 ${l?"text-status-connected":"text-muted-foreground"}`,fill:l?"currentColor":"none","aria-hidden":"true"}),l&&e.jsx("span",{className:"hidden md:inline text-[11px] font-medium text-status-connected",children:"Notifications On"})]}),e.jsx("button",{className:"p-1 rounded hover:bg-accent text-lg leading-none transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",onClick:()=>R("contact",t.id),title:y?"Remove from favorites. Favorite contacts stay loaded on the radio for ACK support.":"Add to favorites. Favorite contacts stay loaded on the radio for ACK support.","aria-label":y?"Remove from favorites":"Add to favorites",children:y?e.jsx(Q,{className:"h-4 w-4 fill-current text-favorite","aria-hidden":"true"}):e.jsx(Q,{className:"h-4 w-4 text-muted-foreground","aria-hidden":"true"})}),e.jsx("button",{className:"p-1 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive text-lg leading-none transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",onClick:()=>w(t.id),title:"Delete","aria-label":"Delete",children:e.jsx(je,{className:"h-4 w-4","aria-hidden":"true"})})]}),b&&e.jsx(ve,{open:N,onClose:()=>A(!1),contact:b,contacts:s,radioName:c,onDiscover:D})]}),e.jsx("div",{className:"flex-1 overflow-y-auto p-4",children:f?e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx(He,{data:u.nodeInfo,state:m.nodeInfo,onRefresh:()=>I("nodeInfo"),disabled:d}),e.jsx(Pe,{data:u.status,state:m.status,onRefresh:()=>I("status"),disabled:d}),e.jsx(Be,{data:u.radioSettings,state:m.radioSettings,onRefresh:()=>I("radioSettings"),disabled:d,advertData:u.advertIntervals,advertState:m.advertIntervals,onRefreshAdvert:()=>I("advertIntervals")}),e.jsx(Oe,{data:u.lppTelemetry,state:m.lppTelemetry,onRefresh:()=>I("lppTelemetry"),disabled:d})]}),e.jsx("div",{className:"flex flex-col gap-4",children:e.jsx(Me,{data:u.neighbors,state:m.neighbors,onRefresh:()=>I("neighbors"),disabled:d,contacts:s,nodeInfo:u.nodeInfo,nodeInfoState:m.nodeInfo,repeaterName:t.name})})]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsx(Ee,{data:u.acl,state:m.acl,onRefresh:()=>I("acl"),disabled:d}),e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx(Ge,{data:u.ownerInfo,state:m.ownerInfo,onRefresh:()=>I("ownerInfo"),disabled:d}),e.jsx(We,{onSendZeroHopAdvert:V,onSendFloodAdvert:J,onSyncClock:p,onReboot:Y,consoleLoading:L})]})]}),e.jsx(qe,{history:_,loading:L,onSend:U})]}):e.jsx(Se,{repeaterName:t.name,loading:T,error:P,onLogin:W,onLoginAsGuest:S})})]})}export{Ue as RepeaterDashboard,ke as formatClockDrift,Z as formatDuration};
//# sourceMappingURL=RepeaterDashboard-DjXZo1ON.js.map