signalk-webhook-bridge 2.0.1 → 2.1.0

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to Webhook Bridge will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
6
  and this project uses [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [2.1.0] - 2026-09-13
9
+
10
+ ### Added
11
+
12
+ - Live **Current Payload Preview** in the plugin configuration page.
13
+ - Preview uses the current live Signal K values and shows the exact JSON structure that will be sent to the receiving webhook.
14
+ - Preview updates automatically when Signal K paths, webhook field names or output units are changed.
15
+ - Preview supports structured Signal K values such as `navigation.position`.
16
+
17
+ ### Changed
18
+
19
+ - Payload documentation in the configuration interface is now complemented by a live example using the current configuration.
20
+
8
21
  ## [2.0.1] - 2026-09-13
9
22
 
10
23
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-webhook-bridge",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Send selected Signal K data paths to an external webhook with unit conversion, persistent queueing and automatic retry.",
5
5
  "main": "plugin/index.js",
6
6
  "scripts": {
package/plugin/index.js CHANGED
@@ -406,6 +406,29 @@ module.exports = function (app) {
406
406
  });
407
407
  }
408
408
  });
409
+
410
+ router.post("/preview", (req, res) => {
411
+ try {
412
+ const previewSettings = {
413
+ paths: Array.isArray(req.body?.paths) ? req.body.paths : [],
414
+ };
415
+
416
+ const snapshot = buildSnapshot(previewSettings);
417
+
418
+ res.status(200).json({
419
+ timestamp: new Date().toISOString(),
420
+ ...snapshot,
421
+ });
422
+ } catch (error) {
423
+ app.error(
424
+ `Unable to generate webhook preview: ${error.message}`,
425
+ );
426
+
427
+ res.status(500).json({
428
+ error: "Unable to generate webhook preview",
429
+ });
430
+ }
431
+ });
409
432
  };
410
433
 
411
434
  /**
@@ -1,2 +1,2 @@
1
1
  /*! For license information please see 651.main.js.LICENSE.txt */
2
- "use strict";(self.webpackChunksignalk_webhook_bridge=self.webpackChunksignalk_webhook_bridge||[]).push([[651],{651(e,t,a){a.r(t),a.d(t,{default:()=>s});var r=a.cw(function(e,t){var a=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function i(e,t,r){var i=null;if(void 0!==r&&(i=""+r),void 0!==t.key&&(i=""+t.key),"key"in t)for(var n in r={},t)"key"!==n&&(r[n]=t[n]);else r=t;return t=r.ref,{$$typeof:a,type:e,key:i,ref:void 0!==t?t:null,props:r}}t.Fragment=r,t.jsx=i,t.jsxs=i}),i=a.cw(function(e,t){e.exports=r()}),n=a(231);function s({configuration:e={},save:t}){const[a,i]=(0,n.useState)(e.webhookUrl||""),[s,l]=(0,n.useState)(e.authKey||""),[o,d]=(0,n.useState)(e.sendFreq||10),[h,u]=(0,n.useState)(Array.isArray(e.paths)?e.paths:[]),[c,g]=(0,n.useState)([]),[m,p]=(0,n.useState)(!0),[y,v]=(0,n.useState)(""),[b,x]=(0,n.useState)(null),[j,f]=(0,n.useState)(null),[k,w]=(0,n.useState)(""),[S,C]=(0,n.useState)(!1);(0,n.useEffect)(()=>{fetch("/plugins/signalk-webhook-bridge/paths").then(e=>{if(!e.ok)throw new Error(`HTTP ${e.status}`);return e.json()}).then(e=>{g(Array.isArray(e.paths)?e.paths:[]),p(!1)}).catch(e=>{console.error("Unable to load Signal K paths:",e),v("Unable to load available Signal K paths."),p(!1)})},[]),(0,n.useEffect)(()=>{let e=!0;const t=async()=>{try{const t=await fetch("/plugins/signalk-webhook-bridge/status");if(!t.ok)throw new Error(`HTTP ${t.status}`);const a=await t.json();e&&(f(a),w(""))}catch(t){console.error("Unable to load webhook status:",t),e&&w("Unable to load webhook status.")}};t();const a=setInterval(t,5e3);return()=>{e=!1,clearInterval(a)}},[]);const B=(e,t,a)=>{u(r=>r.map((r,i)=>i===e?{...r,[t]:a}:r))};return(0,r().jsxs)("div",{style:{padding:"1rem",maxWidth:"900px"},children:[(0,r().jsx)("h2",{children:"Webhook Bridge"}),(0,r().jsx)("div",{style:{marginBottom:"1.25rem",maxWidth:"750px",lineHeight:"1.5",opacity:.8},children:"Send selected Signal K data to an external webhook at a regular interval. Choose the data paths to include, give each value a webhook field name, and select the units you want to send. If delivery is unavailable, updates are stored locally and automatically sent in order when the connection returns."}),(0,r().jsxs)("div",{style:{marginBottom:"1.5rem",padding:"1rem",borderRadius:"6px",transition:"background 0.2s ease, border 0.2s ease",...(e=>{switch(e){case"queued":case"waiting":return{background:"rgba(255, 152, 0, 0.10)",border:"1px solid rgba(255, 152, 0, 0.40)"};case"error":return{background:"rgba(244, 67, 54, 0.10)",border:"1px solid rgba(244, 67, 54, 0.40)"};default:return{background:"rgba(33, 150, 243, 0.08)",border:"1px solid rgba(33, 150, 243, 0.35)"}}})(j?.deliveryState)},children:[(0,r().jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",gap:"1rem",marginBottom:"0.75rem"},children:[(0,r().jsxs)("div",{children:[(0,r().jsx)("h3",{style:{margin:0},children:"Status"}),(0,r().jsx)("div",{style:{marginTop:"0.25rem",fontSize:"0.9rem",opacity:.75},children:"Current delivery and queue status."})]}),(0,r().jsx)("button",{type:"button",onClick:async()=>{C(!0);try{const e=await fetch("/plugins/signalk-webhook-bridge/retry",{method:"POST"});if(!e.ok)throw new Error(`HTTP ${e.status}`);const t=await e.json();f(t),w("")}catch(e){console.error("Unable to retry webhook delivery:",e),w("Unable to retry webhook delivery.")}finally{C(!1)}},disabled:S,style:{padding:"0.5rem 0.8rem",cursor:S?"default":"pointer",opacity:S?.7:1},children:S?"Retrying...":"Retry Now"})]}),k&&(0,r().jsx)("div",{style:{marginBottom:"0.75rem",padding:"0.65rem 0.8rem",border:"1px solid rgba(244, 67, 54, 0.45)",borderRadius:"6px",background:"rgba(244, 67, 54, 0.12)"},children:k}),!j&&!k&&(0,r().jsx)("div",{style:{opacity:.75},children:"Loading status..."}),j&&(0,r().jsxs)("div",{style:{display:"grid",gridTemplateColumns:"180px 1fr",gap:"0.5rem 1rem"},children:[(0,r().jsx)("div",{style:{fontWeight:"600"},children:"Delivery"}),(0,r().jsx)("div",{children:"connected"===j.deliveryState?"Connected":"delivering"===j.deliveryState?"Delivering":"waiting"===j.deliveryState?"Waiting to retry":"queued"===j.deliveryState?"Queued":"error"===j.deliveryState?"Error":"Idle"}),(0,r().jsx)("div",{style:{fontWeight:"600"},children:"Queue"}),(0,r().jsxs)("div",{children:[j.queueCount," ",1===j.queueCount?"entry":"entries"," ","waiting"]}),(0,r().jsx)("div",{style:{fontWeight:"600"},children:"Last capture"}),(0,r().jsx)("div",{children:j.lastCapture?new Date(j.lastCapture).toLocaleString():"Not yet"}),(0,r().jsx)("div",{style:{fontWeight:"600"},children:"Last delivery"}),(0,r().jsx)("div",{children:j.lastDelivery?new Date(j.lastDelivery).toLocaleString():"Not yet"}),j.lastError&&(0,r().jsxs)(r().Fragment,{children:[(0,r().jsx)("div",{style:{fontWeight:"600"},children:"Last error"}),(0,r().jsx)("div",{style:{wordBreak:"break-word"},children:j.lastError})]})]})]}),(0,r().jsxs)("div",{style:{marginBottom:"1rem"},children:[(0,r().jsx)("label",{htmlFor:"webhookUrl",style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Webhook URL"}),(0,r().jsx)("input",{id:"webhookUrl",type:"url",value:a,onChange:e=>i(e.target.value),placeholder:"https://example.com/webhook",style:{width:"100%",padding:"0.5rem"}})]}),(0,r().jsxs)("div",{style:{marginBottom:"1rem"},children:[(0,r().jsx)("label",{htmlFor:"authKey",style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Authentication Key"}),(0,r().jsx)("input",{id:"authKey",type:"text",value:s,onChange:e=>l(e.target.value),placeholder:"Optional",style:{width:"100%",padding:"0.5rem"}})]}),(0,r().jsxs)("div",{style:{marginBottom:"1.5rem"},children:[(0,r().jsx)("label",{htmlFor:"sendFreq",style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Send Interval"}),(0,r().jsx)("input",{id:"sendFreq",type:"number",min:"1",value:o,onChange:e=>d(e.target.value),style:{width:"150px",padding:"0.5rem"}}),(0,r().jsx)("div",{style:{marginTop:"0.35rem",fontSize:"0.9rem",opacity:.75},children:"Minutes between webhook updates."})]}),(0,r().jsx)("hr",{style:{margin:"1.5rem 0"}}),(0,r().jsx)("div",{style:{marginBottom:"1rem"},children:(0,r().jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",gap:"1rem"},children:[(0,r().jsxs)("div",{children:[(0,r().jsx)("h3",{style:{marginBottom:"0.25rem"},children:"Signal K Data Paths"}),(0,r().jsx)("div",{style:{fontSize:"0.9rem",opacity:.75},children:"Choose the Signal K values to include in each webhook update."})]}),(0,r().jsx)("button",{type:"button",onClick:()=>{u(e=>[...e,{path:"",fieldName:"",units:"native"}])},style:{padding:"0.5rem 0.8rem",cursor:"pointer"},children:"Add Path"})]})}),0===h.length&&(0,r().jsx)("div",{style:{padding:"1rem",border:"1px solid rgba(128,128,128,0.35)",borderRadius:"6px",marginBottom:"1rem",opacity:.8},children:"No Signal K paths added yet."}),h.map((e,t)=>{const a=c.find(t=>t.path===e.path),i=function(e){switch(e){case"m/s":return[{value:"native",label:"Native (m/s)"},{value:"knots",label:"Knots"},{value:"kmh",label:"km/h"},{value:"mph",label:"mph"}];case"rad":return[{value:"native",label:"Native (rad)"},{value:"degrees",label:"Degrees"}];case"m":return[{value:"native",label:"Native (m)"},{value:"metres",label:"Metres"},{value:"feet",label:"Feet"}];case"K":return[{value:"native",label:"Native (K)"},{value:"celsius",label:"Celsius"},{value:"fahrenheit",label:"Fahrenheit"}];case"Pa":return[{value:"native",label:"Native (Pa)"},{value:"hpa",label:"hPa"},{value:"mbar",label:"mbar"}];default:return[{value:"native",label:e?`Native (${e})`:"Native"}]}}(a?.units);return(0,r().jsx)("div",{style:{border:"1px solid rgba(128,128,128,0.35)",borderRadius:"6px",padding:"1rem",marginBottom:"1rem"},children:(0,r().jsxs)("div",{style:{display:"grid",gridTemplateColumns:"minmax(220px, 2fr) minmax(180px, 1.2fr) minmax(140px, 1fr) auto",gap:"0.75rem",alignItems:"end"},children:[(0,r().jsxs)("div",{children:[(0,r().jsx)("label",{style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Signal K Path"}),(0,r().jsx)("input",{type:"text",list:`signalk-paths-${t}`,value:e.path,onChange:e=>B(t,"path",e.target.value),placeholder:m?"Loading Signal K paths...":"Search Signal K paths...",disabled:m,style:{width:"100%",padding:"0.5rem"}}),(0,r().jsx)("datalist",{id:`signalk-paths-${t}`,children:c.map(e=>(0,r().jsx)("option",{value:e.path},e.path))}),y&&(0,r().jsx)("div",{style:{marginTop:"0.35rem",fontSize:"0.85rem"},children:y})]}),(0,r().jsxs)("div",{children:[(0,r().jsx)("label",{style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Webhook Field Name"}),(0,r().jsx)("input",{type:"text",value:e.fieldName,onChange:e=>B(t,"fieldName",e.target.value),placeholder:"speed",style:{width:"100%",padding:"0.5rem"}})]}),(0,r().jsxs)("div",{children:[(0,r().jsx)("label",{style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Output Units"}),(0,r().jsx)("select",{value:i.some(t=>t.value===e.units)?e.units:"native",onChange:e=>B(t,"units",e.target.value),style:{width:"100%",padding:"0.5rem"},children:i.map(e=>(0,r().jsx)("option",{value:e.value,children:e.label},e.value))})]}),(0,r().jsx)("button",{type:"button",onClick:()=>{return e=t,void u(t=>t.filter((t,a)=>a!==e));var e},style:{padding:"0.5rem 0.75rem",cursor:"pointer"},children:"Remove"})]})},t)}),(0,r().jsxs)("details",{style:{marginTop:"1.5rem",marginBottom:"1.5rem"},children:[(0,r().jsx)("summary",{style:{cursor:"pointer",fontWeight:"600"},children:"What does Webhook Bridge send?"}),(0,r().jsxs)("div",{style:{marginTop:"1rem",lineHeight:"1.5"},children:[(0,r().jsxs)("p",{children:["Webhook Bridge sends an HTTP POST request containing a JSON object. Each configured Signal K path is sent using the ",(0,r().jsx)("strong",{children:"Webhook Field Name"})," you specify above, with values converted to your selected output units."]}),(0,r().jsxs)("p",{children:["Each request also contains the time the update was captured and a ",(0,r().jsx)("code",{children:"units"})," object describing the units used."]}),(0,r().jsx)("pre",{style:{padding:"1rem",overflowX:"auto",borderRadius:"4px",background:"rgba(128,128,128,0.10)"},children:'{\n "timestamp": "2026-09-13T10:15:00.000Z",\n "speed": 5.64,\n "depth": 12.8,\n "units": {\n "speed": "kn",\n "depth": "m"\n }\n}'}),(0,r().jsxs)("p",{children:["Your webhook should return an HTTP"," ",(0,r().jsx)("strong",{children:"2xx response"})," when the data has been successfully received. If delivery fails, the update remains in the local queue and will be sent again later in its original order."]}),(0,r().jsxs)("p",{style:{marginBottom:0},children:["If an Authentication Key is configured, it is sent as a"," ",(0,r().jsx)("strong",{children:"Bearer token"})," in the"," ",(0,r().jsx)("code",{children:"Authorization"})," header."]})]})]}),(0,r().jsx)("button",{type:"button",onClick:async()=>{x("saving");try{await t({...e,webhookUrl:a,authKey:s,sendFreq:Number(o),paths:h}),x("saved"),setTimeout(()=>{x(null)},4e3)}catch(e){console.error("Unable to save configuration:",e),x("error")}},disabled:"saving"===b,style:{padding:"0.55rem 1rem",cursor:"saving"===b?"default":"pointer",marginTop:"0.5rem",opacity:"saving"===b?.7:1},children:"saving"===b?"Saving...":"Save Configuration"}),"saved"===b&&(0,r().jsx)("div",{style:{marginTop:"0.75rem",padding:"0.65rem 0.8rem",border:"1px solid rgba(76, 175, 80, 0.45)",borderRadius:"6px",background:"rgba(76, 175, 80, 0.12)"},children:"Configuration saved successfully."}),"error"===b&&(0,r().jsx)("div",{style:{marginTop:"0.75rem",padding:"0.65rem 0.8rem",border:"1px solid rgba(244, 67, 54, 0.45)",borderRadius:"6px",background:"rgba(244, 67, 54, 0.12)"},children:"Unable to save configuration. Please try again."})]})}i()}}]);
2
+ "use strict";(self.webpackChunksignalk_webhook_bridge=self.webpackChunksignalk_webhook_bridge||[]).push([[651],{651(e,t,r){r.r(t),r.d(t,{default:()=>l});var a=r.cw(function(e,t){var r=Symbol.for("react.transitional.element"),a=Symbol.for("react.fragment");function i(e,t,a){var i=null;if(void 0!==a&&(i=""+a),void 0!==t.key&&(i=""+t.key),"key"in t)for(var n in a={},t)"key"!==n&&(a[n]=t[n]);else a=t;return t=a.ref,{$$typeof:r,type:e,key:i,ref:void 0!==t?t:null,props:a}}t.Fragment=a,t.jsx=i,t.jsxs=i}),i=r.cw(function(e,t){e.exports=a()}),n=r(231);function l({configuration:e={},save:t}){const[r,i]=(0,n.useState)(e.webhookUrl||""),[l,s]=(0,n.useState)(e.authKey||""),[o,d]=(0,n.useState)(e.sendFreq||10),[u,h]=(0,n.useState)(Array.isArray(e.paths)?e.paths:[]),[c,g]=(0,n.useState)([]),[m,p]=(0,n.useState)(!0),[v,y]=(0,n.useState)(""),[b,x]=(0,n.useState)(null),[f,j]=(0,n.useState)(null),[k,w]=(0,n.useState)(""),[S,C]=(0,n.useState)(!1),[T,B]=(0,n.useState)(null),[K,N]=(0,n.useState)(!1),[W,U]=(0,n.useState)("");(0,n.useEffect)(()=>{fetch("/plugins/signalk-webhook-bridge/paths").then(e=>{if(!e.ok)throw new Error(`HTTP ${e.status}`);return e.json()}).then(e=>{g(Array.isArray(e.paths)?e.paths:[]),p(!1)}).catch(e=>{console.error("Unable to load Signal K paths:",e),y("Unable to load available Signal K paths."),p(!1)})},[]),(0,n.useEffect)(()=>{let e=!0;const t=async()=>{try{const t=await fetch("/plugins/signalk-webhook-bridge/status");if(!t.ok)throw new Error(`HTTP ${t.status}`);const r=await t.json();e&&(j(r),w(""))}catch(t){console.error("Unable to load webhook status:",t),e&&w("Unable to load webhook status.")}};t();const r=setInterval(t,5e3);return()=>{e=!1,clearInterval(r)}},[]),(0,n.useEffect)(()=>{const e=u.filter(e=>e&&e.path);if(0===e.length)return B(null),U(""),void N(!1);const t=setTimeout(()=>{N(!0),U(""),fetch("/plugins/signalk-webhook-bridge/preview",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paths:e})}).then(async e=>{if(!e.ok){const t=await e.text();throw new Error(t||`HTTP ${e.status}`)}return e.json()}).then(e=>{B(e),N(!1)}).catch(e=>{console.error("Unable to generate webhook preview:",e),U("Unable to generate the payload preview."),N(!1)})},350);return()=>clearTimeout(t)},[u]);const P=(e,t,r)=>{h(a=>a.map((a,i)=>i===e?{...a,[t]:r}:a))};return(0,a().jsxs)("div",{style:{padding:"1rem",maxWidth:"900px"},children:[(0,a().jsx)("h2",{children:"Webhook Bridge"}),(0,a().jsx)("div",{style:{marginBottom:"1.25rem",maxWidth:"750px",lineHeight:"1.5",opacity:.8},children:"Send selected Signal K data to an external webhook at a regular interval. Choose the data paths to include, give each value a webhook field name, and select the units you want to send. If delivery is unavailable, updates are stored locally and automatically sent in order when the connection returns."}),(0,a().jsxs)("div",{style:{marginBottom:"1.5rem",padding:"1rem",borderRadius:"6px",transition:"background 0.2s ease, border 0.2s ease",...(e=>{switch(e){case"queued":case"waiting":return{background:"rgba(255, 152, 0, 0.10)",border:"1px solid rgba(255, 152, 0, 0.40)"};case"error":return{background:"rgba(244, 67, 54, 0.10)",border:"1px solid rgba(244, 67, 54, 0.40)"};default:return{background:"rgba(33, 150, 243, 0.08)",border:"1px solid rgba(33, 150, 243, 0.35)"}}})(f?.deliveryState)},children:[(0,a().jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",gap:"1rem",marginBottom:"0.75rem"},children:[(0,a().jsxs)("div",{children:[(0,a().jsx)("h3",{style:{margin:0},children:"Status"}),(0,a().jsx)("div",{style:{marginTop:"0.25rem",fontSize:"0.9rem",opacity:.75},children:"Current delivery and queue status."})]}),(0,a().jsx)("button",{type:"button",onClick:async()=>{C(!0);try{const e=await fetch("/plugins/signalk-webhook-bridge/retry",{method:"POST"});if(!e.ok)throw new Error(`HTTP ${e.status}`);const t=await e.json();j(t),w("")}catch(e){console.error("Unable to retry webhook delivery:",e),w("Unable to retry webhook delivery.")}finally{C(!1)}},disabled:S,style:{padding:"0.5rem 0.8rem",cursor:S?"default":"pointer",opacity:S?.7:1},children:S?"Retrying...":"Retry Now"})]}),k&&(0,a().jsx)("div",{style:{marginBottom:"0.75rem",padding:"0.65rem 0.8rem",border:"1px solid rgba(244, 67, 54, 0.45)",borderRadius:"6px",background:"rgba(244, 67, 54, 0.12)"},children:k}),!f&&!k&&(0,a().jsx)("div",{style:{opacity:.75},children:"Loading status..."}),f&&(0,a().jsxs)("div",{style:{display:"grid",gridTemplateColumns:"180px 1fr",gap:"0.5rem 1rem"},children:[(0,a().jsx)("div",{style:{fontWeight:"600"},children:"Delivery"}),(0,a().jsx)("div",{children:"connected"===f.deliveryState?"Connected":"delivering"===f.deliveryState?"Delivering":"waiting"===f.deliveryState?"Waiting to retry":"queued"===f.deliveryState?"Queued":"error"===f.deliveryState?"Error":"Idle"}),(0,a().jsx)("div",{style:{fontWeight:"600"},children:"Queue"}),(0,a().jsxs)("div",{children:[f.queueCount," ",1===f.queueCount?"entry":"entries"," ","waiting"]}),(0,a().jsx)("div",{style:{fontWeight:"600"},children:"Last capture"}),(0,a().jsx)("div",{children:f.lastCapture?new Date(f.lastCapture).toLocaleString():"Not yet"}),(0,a().jsx)("div",{style:{fontWeight:"600"},children:"Last delivery"}),(0,a().jsx)("div",{children:f.lastDelivery?new Date(f.lastDelivery).toLocaleString():"Not yet"}),f.lastError&&(0,a().jsxs)(a().Fragment,{children:[(0,a().jsx)("div",{style:{fontWeight:"600"},children:"Last error"}),(0,a().jsx)("div",{style:{wordBreak:"break-word"},children:f.lastError})]})]})]}),(0,a().jsxs)("div",{style:{marginBottom:"1rem"},children:[(0,a().jsx)("label",{htmlFor:"webhookUrl",style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Webhook URL"}),(0,a().jsx)("input",{id:"webhookUrl",type:"url",value:r,onChange:e=>i(e.target.value),placeholder:"https://example.com/webhook",style:{width:"100%",padding:"0.5rem"}})]}),(0,a().jsxs)("div",{style:{marginBottom:"1rem"},children:[(0,a().jsx)("label",{htmlFor:"authKey",style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Authentication Key"}),(0,a().jsx)("input",{id:"authKey",type:"text",value:l,onChange:e=>s(e.target.value),placeholder:"Optional",style:{width:"100%",padding:"0.5rem"}})]}),(0,a().jsxs)("div",{style:{marginBottom:"1.5rem"},children:[(0,a().jsx)("label",{htmlFor:"sendFreq",style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Send Interval"}),(0,a().jsx)("input",{id:"sendFreq",type:"number",min:"1",value:o,onChange:e=>d(e.target.value),style:{width:"150px",padding:"0.5rem"}}),(0,a().jsx)("div",{style:{marginTop:"0.35rem",fontSize:"0.9rem",opacity:.75},children:"Minutes between webhook updates."})]}),(0,a().jsx)("hr",{style:{margin:"1.5rem 0"}}),(0,a().jsx)("div",{style:{marginBottom:"1rem"},children:(0,a().jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",gap:"1rem"},children:[(0,a().jsxs)("div",{children:[(0,a().jsx)("h3",{style:{marginBottom:"0.25rem"},children:"Signal K Data Paths"}),(0,a().jsx)("div",{style:{fontSize:"0.9rem",opacity:.75},children:"Choose the Signal K values to include in each webhook update."})]}),(0,a().jsx)("button",{type:"button",onClick:()=>{h(e=>[...e,{path:"",fieldName:"",units:"native"}])},style:{padding:"0.5rem 0.8rem",cursor:"pointer"},children:"Add Path"})]})}),0===u.length&&(0,a().jsx)("div",{style:{padding:"1rem",border:"1px solid rgba(128,128,128,0.35)",borderRadius:"6px",marginBottom:"1rem",opacity:.8},children:"No Signal K paths added yet."}),u.map((e,t)=>{const r=c.find(t=>t.path===e.path),i=function(e){switch(e){case"m/s":return[{value:"native",label:"Native (m/s)"},{value:"knots",label:"Knots"},{value:"kmh",label:"km/h"},{value:"mph",label:"mph"}];case"rad":return[{value:"native",label:"Native (rad)"},{value:"degrees",label:"Degrees"}];case"m":return[{value:"native",label:"Native (m)"},{value:"metres",label:"Metres"},{value:"feet",label:"Feet"}];case"K":return[{value:"native",label:"Native (K)"},{value:"celsius",label:"Celsius"},{value:"fahrenheit",label:"Fahrenheit"}];case"Pa":return[{value:"native",label:"Native (Pa)"},{value:"hpa",label:"hPa"},{value:"mbar",label:"mbar"}];default:return[{value:"native",label:e?`Native (${e})`:"Native"}]}}(r?.units);return(0,a().jsx)("div",{style:{border:"1px solid rgba(128,128,128,0.35)",borderRadius:"6px",padding:"1rem",marginBottom:"1rem"},children:(0,a().jsxs)("div",{style:{display:"grid",gridTemplateColumns:"minmax(220px, 2fr) minmax(180px, 1.2fr) minmax(140px, 1fr) auto",gap:"0.75rem",alignItems:"end"},children:[(0,a().jsxs)("div",{children:[(0,a().jsx)("label",{style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Signal K Path"}),(0,a().jsx)("input",{type:"text",list:`signalk-paths-${t}`,value:e.path,onChange:e=>P(t,"path",e.target.value),placeholder:m?"Loading Signal K paths...":"Search Signal K paths...",disabled:m,style:{width:"100%",padding:"0.5rem"}}),(0,a().jsx)("datalist",{id:`signalk-paths-${t}`,children:c.map(e=>(0,a().jsx)("option",{value:e.path},e.path))}),v&&(0,a().jsx)("div",{style:{marginTop:"0.35rem",fontSize:"0.85rem"},children:v})]}),(0,a().jsxs)("div",{children:[(0,a().jsx)("label",{style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Webhook Field Name"}),(0,a().jsx)("input",{type:"text",value:e.fieldName,onChange:e=>P(t,"fieldName",e.target.value),placeholder:"speed",style:{width:"100%",padding:"0.5rem"}})]}),(0,a().jsxs)("div",{children:[(0,a().jsx)("label",{style:{display:"block",fontWeight:"600",marginBottom:"0.35rem"},children:"Output Units"}),(0,a().jsx)("select",{value:i.some(t=>t.value===e.units)?e.units:"native",onChange:e=>P(t,"units",e.target.value),style:{width:"100%",padding:"0.5rem"},children:i.map(e=>(0,a().jsx)("option",{value:e.value,children:e.label},e.value))})]}),(0,a().jsx)("button",{type:"button",onClick:()=>{return e=t,void h(t=>t.filter((t,r)=>r!==e));var e},style:{padding:"0.5rem 0.75rem",cursor:"pointer"},children:"Remove"})]})},t)}),(0,a().jsxs)("div",{style:{marginTop:"1.5rem",marginBottom:"1.5rem",border:"1px solid rgba(128,128,128,0.35)",borderRadius:"6px",overflow:"hidden"},children:[(0,a().jsxs)("div",{style:{padding:"0.9rem 1rem",borderBottom:"1px solid rgba(128,128,128,0.25)"},children:[(0,a().jsx)("div",{style:{fontWeight:"600",fontSize:"1.05rem"},children:"Current Payload Preview"}),(0,a().jsx)("div",{style:{marginTop:"0.25rem",fontSize:"0.9rem",opacity:.75},children:"This is the JSON structure your webhook will receive using the paths, field names and units currently configured above. Values shown are the current live Signal K values."})]}),(0,a().jsxs)("div",{style:{padding:"1rem"},children:[0===u.filter(e=>e&&e.path).length&&(0,a().jsx)("div",{style:{opacity:.7},children:"Add a Signal K path to see the outgoing payload."}),K&&!T&&(0,a().jsx)("div",{style:{opacity:.7},children:"Loading current Signal K values..."}),W&&(0,a().jsx)("div",{style:{padding:"0.75rem",borderRadius:"4px",background:"rgba(244, 67, 54, 0.10)",border:"1px solid rgba(244, 67, 54, 0.35)"},children:W}),T&&(0,a().jsxs)(a().Fragment,{children:[(0,a().jsx)("pre",{style:{margin:0,padding:"1rem",overflowX:"auto",borderRadius:"4px",background:"rgba(128,128,128,0.10)",fontSize:"0.9rem",lineHeight:"1.5"},children:JSON.stringify(T,null,2)}),K&&(0,a().jsx)("div",{style:{marginTop:"0.5rem",fontSize:"0.85rem",opacity:.6},children:"Updating preview..."})]})]})]}),(0,a().jsx)("button",{type:"button",onClick:async()=>{x("saving");try{await t({...e,webhookUrl:r,authKey:l,sendFreq:Number(o),paths:u}),x("saved"),setTimeout(()=>{x(null)},4e3)}catch(e){console.error("Unable to save configuration:",e),x("error")}},disabled:"saving"===b,style:{padding:"0.55rem 1rem",cursor:"saving"===b?"default":"pointer",marginTop:"0.5rem",opacity:"saving"===b?.7:1},children:"saving"===b?"Saving...":"Save Configuration"}),"saved"===b&&(0,a().jsx)("div",{style:{marginTop:"0.75rem",padding:"0.65rem 0.8rem",border:"1px solid rgba(76, 175, 80, 0.45)",borderRadius:"6px",background:"rgba(76, 175, 80, 0.12)"},children:"Configuration saved successfully."}),"error"===b&&(0,a().jsx)("div",{style:{marginTop:"0.75rem",padding:"0.65rem 0.8rem",border:"1px solid rgba(244, 67, 54, 0.45)",borderRadius:"6px",background:"rgba(244, 67, 54, 0.12)"},children:"Unable to save configuration. Please try again."})]})}i()}}]);