Teams integration: webhook messaging + configuración en Settings + plugin-http

- @tauri-apps/plugin-http instalado (npm + Cargo.toml + capabilities)
- src-tauri/src/main.rs: registrado tauri_plugin_http::init()
- services/teams.ts: servicio para enviar mensajes Teams via webhook
  soporta Tauri plugin y browser fetch con fallback automático
  incluye notifyHUCreated, notifyBlockerLogged, sendTestMessage
- SettingsView: nueva sección Teams con input de webhook URL + botón test
- ChartAreaInteractive.vue: removido import no usado (@unovis/vue)
This commit is contained in:
2026-05-29 23:55:37 -05:00
parent bf81b8e04b
commit b21214d1f1
13 changed files with 6919 additions and 15 deletions
+3
View File
@@ -12,6 +12,7 @@
"@tanstack/vue-table": "^8.21.3",
"@tauri-apps/api": "^2.11.0",
"@tauri-apps/cli": "^2.11.2",
"@tauri-apps/plugin-http": "^2.5.9",
"@vueuse/core": "^14.3.0",
"ag-grid-community": "^35.3.0",
"ag-grid-vue3": "^35.3.0",
@@ -406,6 +407,8 @@
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.11.2", "", { "os": "win32", "cpu": "x64" }, "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA=="],
"@tauri-apps/plugin-http": ["@tauri-apps/plugin-http@2.5.9", "", { "dependencies": { "@tauri-apps/api": "^2.11.0" } }, "sha512-lCiY0+vs4HvIUSvZrBs8TC3TiCB0MOPRmiUjTq4prW7SlcJE2jdLeT6KBsJrT9Tlplufl7W1pY6SFAO3gCWxDA=="],
"@ts-morph/common": ["@ts-morph/common@0.28.1", "", { "dependencies": { "minimatch": "^10.0.1", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.14" } }, "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g=="],
"@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="],
+5884
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -18,6 +18,7 @@
"@tanstack/vue-table": "^8.21.3",
"@tauri-apps/api": "^2.11.0",
"@tauri-apps/cli": "^2.11.2",
"@tauri-apps/plugin-http": "^2.5.9",
"@vueuse/core": "^14.3.0",
"ag-grid-community": "^35.3.0",
"ag-grid-vue3": "^35.3.0",
+405 -13
View File
@@ -71,6 +71,7 @@ dependencies = [
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-http",
"tauri-plugin-opener",
]
@@ -621,6 +622,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chrono"
version = "0.4.44"
@@ -690,10 +697,29 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "cookie_store"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
dependencies = [
"cookie",
"document-features",
"idna",
"log",
"publicsuffix",
"serde",
"serde_derive",
"serde_json",
"time",
"url",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -860,6 +886,12 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "data-url"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]]
name = "dbus"
version = "0.9.11"
@@ -1000,6 +1032,15 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "document-features"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
dependencies = [
"litrs",
]
[[package]]
name = "dom_query"
version = "0.27.0"
@@ -1092,6 +1133,15 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]]
name = "endi"
version = "1.1.1"
@@ -1497,8 +1547,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
@@ -1508,9 +1560,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 5.3.0",
"wasip2",
"wasm-bindgen",
]
[[package]]
@@ -1693,6 +1747,25 @@ dependencies = [
"tracing",
]
[[package]]
name = "h2"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http 1.4.1",
"indexmap 2.14.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1859,7 +1932,7 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"h2",
"h2 0.3.27",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
@@ -1883,6 +1956,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-core",
"h2 0.4.14",
"http 1.4.1",
"http-body 1.0.1",
"httparse",
@@ -1903,14 +1977,30 @@ dependencies = [
"http 0.2.12",
"hyper 0.14.32",
"log",
"rustls",
"rustls 0.22.4",
"rustls-native-certs",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tokio-rustls 0.25.0",
"webpki-roots 0.26.11",
]
[[package]]
name = "hyper-rustls"
version = "0.27.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
dependencies = [
"http 1.4.1",
"hyper 1.9.0",
"hyper-util",
"rustls 0.23.40",
"tokio",
"tokio-rustls 0.26.4",
"tower-service",
"webpki-roots 1.0.7",
]
[[package]]
name = "hyper-timeout"
version = "0.4.1"
@@ -1941,9 +2031,11 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"socket2 0.6.3",
"system-configuration",
"tokio",
"tower-service",
"tracing",
"windows-registry",
]
[[package]]
@@ -2394,7 +2486,7 @@ dependencies = [
"futures",
"http 0.2.12",
"hyper 0.14.32",
"hyper-rustls",
"hyper-rustls 0.25.0",
"libsql-hrana",
"libsql-sqlite3-parser",
"libsql-sys",
@@ -2529,6 +2621,12 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -2544,6 +2642,12 @@ version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
[[package]]
name = "lru-slab"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "markup5ever"
version = "0.38.0"
@@ -2830,6 +2934,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
dependencies = [
"bitflags 2.11.1",
"block2",
"libc",
"objc2",
"objc2-core-foundation",
]
@@ -3061,7 +3166,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared 0.11.3",
"rand",
"rand 0.8.6",
]
[[package]]
@@ -3327,6 +3432,22 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "psl-types"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
[[package]]
name = "publicsuffix"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
dependencies = [
"idna",
"psl-types",
]
[[package]]
name = "quick-xml"
version = "0.39.4"
@@ -3336,6 +3457,61 @@ dependencies = [
"memchr",
]
[[package]]
name = "quinn"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash 2.1.2",
"rustls 0.23.40",
"socket2 0.6.3",
"thiserror 2.0.18",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
dependencies = [
"bytes",
"getrandom 0.3.4",
"lru-slab",
"rand 0.9.4",
"ring",
"rustc-hash 2.1.2",
"rustls 0.23.40",
"rustls-pki-types",
"slab",
"thiserror 2.0.18",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2 0.6.3",
"tracing",
"windows-sys 0.59.0",
]
[[package]]
name = "quote"
version = "1.0.45"
@@ -3364,8 +3540,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.5",
]
[[package]]
@@ -3375,7 +3561,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.5",
]
[[package]]
@@ -3387,6 +3583,15 @@ dependencies = [
"getrandom 0.2.17",
]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "raw-window-handle"
version = "0.6.2"
@@ -3473,6 +3678,49 @@ version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "reqwest"
version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64 0.22.1",
"bytes",
"cookie",
"cookie_store",
"encoding_rs",
"futures-core",
"h2 0.4.14",
"http 1.4.1",
"http-body 1.0.1",
"http-body-util",
"hyper 1.9.0",
"hyper-rustls 0.27.9",
"hyper-util",
"js-sys",
"log",
"mime",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls 0.23.40",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 1.0.2",
"tokio",
"tokio-rustls 0.26.4",
"tower 0.5.3",
"tower-http 0.6.11",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots 1.0.7",
]
[[package]]
name = "reqwest"
version = "0.13.4"
@@ -3577,7 +3825,21 @@ dependencies = [
"log",
"ring",
"rustls-pki-types",
"rustls-webpki",
"rustls-webpki 0.102.8",
"subtle",
"zeroize",
]
[[package]]
name = "rustls"
version = "0.23.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki 0.103.13",
"subtle",
"zeroize",
]
@@ -3610,6 +3872,7 @@ version = "1.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
dependencies = [
"web-time",
"zeroize",
]
@@ -3624,12 +3887,29 @@ dependencies = [
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.103.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "same-file"
version = "1.0.6"
@@ -3852,6 +4132,18 @@ dependencies = [
"serde_core",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_with"
version = "3.20.0"
@@ -4134,6 +4426,27 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "system-configuration"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
dependencies = [
"bitflags 2.11.1",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "system-deps"
version = "6.2.2"
@@ -4234,7 +4547,7 @@ dependencies = [
"percent-encoding",
"plist",
"raw-window-handle",
"reqwest",
"reqwest 0.13.4",
"serde",
"serde_json",
"serde_repr",
@@ -4333,6 +4646,54 @@ dependencies = [
"walkdir",
]
[[package]]
name = "tauri-plugin-fs"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371"
dependencies = [
"anyhow",
"dunce",
"glob",
"log",
"objc2-foundation",
"percent-encoding",
"schemars 0.8.22",
"serde",
"serde_json",
"serde_repr",
"tauri",
"tauri-plugin",
"tauri-utils",
"thiserror 2.0.18",
"toml 1.1.2+spec-1.1.0",
"url",
]
[[package]]
name = "tauri-plugin-http"
version = "2.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067"
dependencies = [
"bytes",
"cookie_store",
"data-url",
"http 1.4.1",
"regex",
"reqwest 0.12.28",
"schemars 0.8.22",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"tauri-plugin-fs",
"thiserror 2.0.18",
"tokio",
"url",
"urlpattern",
]
[[package]]
name = "tauri-plugin-opener"
version = "2.5.4"
@@ -4618,11 +4979,21 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
dependencies = [
"rustls",
"rustls 0.22.4",
"rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
dependencies = [
"rustls 0.23.40",
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.18"
@@ -4778,7 +5149,7 @@ dependencies = [
"axum",
"base64 0.21.7",
"bytes",
"h2",
"h2 0.3.27",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.32",
@@ -4825,7 +5196,7 @@ dependencies = [
"indexmap 1.9.3",
"pin-project",
"pin-project-lite",
"rand",
"rand 0.8.6",
"slab",
"tokio",
"tokio-util",
@@ -5292,6 +5663,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "web_atoms"
version = "0.2.4"
@@ -5563,6 +5944,17 @@ dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
dependencies = [
"windows-link 0.2.1",
"windows-result 0.4.1",
"windows-strings 0.5.1",
]
[[package]]
name = "windows-result"
version = "0.3.4"
+1
View File
@@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
tauri-plugin-http = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
libsql = "0.9"
+10
View File
@@ -0,0 +1,10 @@
{
"identifier": "default",
"description": "Default capabilities",
"windows": ["main"],
"permissions": [
"core:default",
"opener:default",
"http:default"
]
}
File diff suppressed because one or more lines are too long
+204
View File
@@ -134,6 +134,144 @@
"description": "Reference a permission or permission set by identifier and extends its scope.",
"type": "object",
"allOf": [
{
"if": {
"properties": {
"identifier": {
"anyOf": [
{
"description": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`",
"type": "string",
"const": "http:default",
"markdownDescription": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`"
},
{
"description": "Enables the fetch command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch",
"markdownDescription": "Enables the fetch command without any pre-configured scope."
},
{
"description": "Enables the fetch_cancel command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-cancel",
"markdownDescription": "Enables the fetch_cancel command without any pre-configured scope."
},
{
"description": "Enables the fetch_cancel_body command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-cancel-body",
"markdownDescription": "Enables the fetch_cancel_body command without any pre-configured scope."
},
{
"description": "Enables the fetch_read_body command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-read-body",
"markdownDescription": "Enables the fetch_read_body command without any pre-configured scope."
},
{
"description": "Enables the fetch_send command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-send",
"markdownDescription": "Enables the fetch_send command without any pre-configured scope."
},
{
"description": "Denies the fetch command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch",
"markdownDescription": "Denies the fetch command without any pre-configured scope."
},
{
"description": "Denies the fetch_cancel command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-cancel",
"markdownDescription": "Denies the fetch_cancel command without any pre-configured scope."
},
{
"description": "Denies the fetch_cancel_body command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-cancel-body",
"markdownDescription": "Denies the fetch_cancel_body command without any pre-configured scope."
},
{
"description": "Denies the fetch_read_body command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-read-body",
"markdownDescription": "Denies the fetch_read_body command without any pre-configured scope."
},
{
"description": "Denies the fetch_send command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-send",
"markdownDescription": "Denies the fetch_send command without any pre-configured scope."
}
]
}
}
},
"then": {
"properties": {
"allow": {
"items": {
"title": "HttpScopeEntry",
"description": "HTTP scope entry.",
"anyOf": [
{
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
},
{
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
}
]
}
},
"deny": {
"items": {
"title": "HttpScopeEntry",
"description": "HTTP scope entry.",
"anyOf": [
{
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
},
{
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
}
]
}
}
}
},
"properties": {
"identifier": {
"description": "Identifier of the permission or permission set.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
}
}
},
{
"if": {
"properties": {
@@ -2360,6 +2498,72 @@
"const": "core:window:deny-unminimize",
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
},
{
"description": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`",
"type": "string",
"const": "http:default",
"markdownDescription": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`"
},
{
"description": "Enables the fetch command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch",
"markdownDescription": "Enables the fetch command without any pre-configured scope."
},
{
"description": "Enables the fetch_cancel command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-cancel",
"markdownDescription": "Enables the fetch_cancel command without any pre-configured scope."
},
{
"description": "Enables the fetch_cancel_body command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-cancel-body",
"markdownDescription": "Enables the fetch_cancel_body command without any pre-configured scope."
},
{
"description": "Enables the fetch_read_body command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-read-body",
"markdownDescription": "Enables the fetch_read_body command without any pre-configured scope."
},
{
"description": "Enables the fetch_send command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-send",
"markdownDescription": "Enables the fetch_send command without any pre-configured scope."
},
{
"description": "Denies the fetch command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch",
"markdownDescription": "Denies the fetch command without any pre-configured scope."
},
{
"description": "Denies the fetch_cancel command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-cancel",
"markdownDescription": "Denies the fetch_cancel command without any pre-configured scope."
},
{
"description": "Denies the fetch_cancel_body command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-cancel-body",
"markdownDescription": "Denies the fetch_cancel_body command without any pre-configured scope."
},
{
"description": "Denies the fetch_read_body command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-read-body",
"markdownDescription": "Denies the fetch_read_body command without any pre-configured scope."
},
{
"description": "Denies the fetch_send command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-send",
"markdownDescription": "Denies the fetch_send command without any pre-configured scope."
},
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
"type": "string",
+204
View File
@@ -134,6 +134,144 @@
"description": "Reference a permission or permission set by identifier and extends its scope.",
"type": "object",
"allOf": [
{
"if": {
"properties": {
"identifier": {
"anyOf": [
{
"description": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`",
"type": "string",
"const": "http:default",
"markdownDescription": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`"
},
{
"description": "Enables the fetch command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch",
"markdownDescription": "Enables the fetch command without any pre-configured scope."
},
{
"description": "Enables the fetch_cancel command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-cancel",
"markdownDescription": "Enables the fetch_cancel command without any pre-configured scope."
},
{
"description": "Enables the fetch_cancel_body command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-cancel-body",
"markdownDescription": "Enables the fetch_cancel_body command without any pre-configured scope."
},
{
"description": "Enables the fetch_read_body command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-read-body",
"markdownDescription": "Enables the fetch_read_body command without any pre-configured scope."
},
{
"description": "Enables the fetch_send command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-send",
"markdownDescription": "Enables the fetch_send command without any pre-configured scope."
},
{
"description": "Denies the fetch command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch",
"markdownDescription": "Denies the fetch command without any pre-configured scope."
},
{
"description": "Denies the fetch_cancel command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-cancel",
"markdownDescription": "Denies the fetch_cancel command without any pre-configured scope."
},
{
"description": "Denies the fetch_cancel_body command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-cancel-body",
"markdownDescription": "Denies the fetch_cancel_body command without any pre-configured scope."
},
{
"description": "Denies the fetch_read_body command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-read-body",
"markdownDescription": "Denies the fetch_read_body command without any pre-configured scope."
},
{
"description": "Denies the fetch_send command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-send",
"markdownDescription": "Denies the fetch_send command without any pre-configured scope."
}
]
}
}
},
"then": {
"properties": {
"allow": {
"items": {
"title": "HttpScopeEntry",
"description": "HTTP scope entry.",
"anyOf": [
{
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
},
{
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
}
]
}
},
"deny": {
"items": {
"title": "HttpScopeEntry",
"description": "HTTP scope entry.",
"anyOf": [
{
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
},
{
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
}
]
}
}
}
},
"properties": {
"identifier": {
"description": "Identifier of the permission or permission set.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
}
}
},
{
"if": {
"properties": {
@@ -2360,6 +2498,72 @@
"const": "core:window:deny-unminimize",
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
},
{
"description": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`",
"type": "string",
"const": "http:default",
"markdownDescription": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`"
},
{
"description": "Enables the fetch command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch",
"markdownDescription": "Enables the fetch command without any pre-configured scope."
},
{
"description": "Enables the fetch_cancel command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-cancel",
"markdownDescription": "Enables the fetch_cancel command without any pre-configured scope."
},
{
"description": "Enables the fetch_cancel_body command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-cancel-body",
"markdownDescription": "Enables the fetch_cancel_body command without any pre-configured scope."
},
{
"description": "Enables the fetch_read_body command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-read-body",
"markdownDescription": "Enables the fetch_read_body command without any pre-configured scope."
},
{
"description": "Enables the fetch_send command without any pre-configured scope.",
"type": "string",
"const": "http:allow-fetch-send",
"markdownDescription": "Enables the fetch_send command without any pre-configured scope."
},
{
"description": "Denies the fetch command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch",
"markdownDescription": "Denies the fetch command without any pre-configured scope."
},
{
"description": "Denies the fetch_cancel command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-cancel",
"markdownDescription": "Denies the fetch_cancel command without any pre-configured scope."
},
{
"description": "Denies the fetch_cancel_body command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-cancel-body",
"markdownDescription": "Denies the fetch_cancel_body command without any pre-configured scope."
},
{
"description": "Denies the fetch_read_body command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-read-body",
"markdownDescription": "Denies the fetch_read_body command without any pre-configured scope."
},
{
"description": "Denies the fetch_send command without any pre-configured scope.",
"type": "string",
"const": "http:deny-fetch-send",
"markdownDescription": "Denies the fetch_send command without any pre-configured scope."
},
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
"type": "string",
+1
View File
@@ -18,6 +18,7 @@ fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_http::init())
.manage(Mutex::new(db_path))
.invoke_handler(tauri::generate_handler![
commands::get_projects,
@@ -3,7 +3,6 @@ import { ref, computed } from "vue"
import type { ChartConfig } from "@/components/ui/chart"
// import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { VisArea, VisAxis, VisLine, VisXYContainer } from "@unovis/vue"
import {
Card,
CardContent,
+138
View File
@@ -0,0 +1,138 @@
import { storage } from '@/services/storage'
const STORAGE_KEY = 'teams_webhook_url'
export function getWebhookUrl(): string {
return storage.get(STORAGE_KEY) || ''
}
export function setWebhookUrl(url: string): void {
storage.set(STORAGE_KEY, url)
}
export function hasWebhook(): boolean {
return !!getWebhookUrl()
}
interface TeamsMessage {
title?: string
text: string
themeColor?: string
sections?: {
activityTitle?: string
activitySubtitle?: string
facts?: { name: string; value: string }[]
text?: string
}[]
}
/**
* Envía un mensaje a Teams via webhook.
* Formato: https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook
* Soporta tanto Tauri (plugin-http) como browser (fetch).
*/
export async function sendToTeams(message: TeamsMessage): Promise<boolean> {
const webhookUrl = getWebhookUrl()
if (!webhookUrl) {
console.warn('[Teams] No hay webhook configurado')
return false
}
const payload = {
'@type': 'MessageCard',
'@context': 'http://schema.org/extensions',
summary: message.title || message.text.slice(0, 50),
title: message.title,
text: message.text,
themeColor: message.themeColor || '0078D4',
sections: message.sections || [],
}
try {
// Intentar usar Tauri plugin HTTP primero
if (typeof window !== 'undefined' && (window as any).__TAURI_INTERNALS__) {
const { fetch } = await import('@tauri-apps/plugin-http')
const res = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
if (!res.ok) {
console.error(`[Teams] Error HTTP ${res.status}`)
return false
}
console.log('[Teams] Mensaje enviado via Tauri plugin')
return true
}
// Fallback browser fetch
const res = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
if (!res.ok) {
console.error(`[Teams] Error HTTP ${res.status}`)
return false
}
console.log('[Teams] Mensaje enviado via browser fetch')
return true
} catch (e: any) {
console.error('[Teams] Error al enviar mensaje:', e)
return false
}
}
/**
* Envía una notificación de HU creada.
*/
export async function notifyHUCreated(projectName: string, huTitle: string, huUrl?: string): Promise<boolean> {
return sendToTeams({
title: '🆕 Nueva HU creada',
text: `**Proyecto:** ${projectName}`,
themeColor: '0078D4',
sections: [{
facts: [
{ name: 'HU', value: huTitle },
{ name: 'Proyecto', value: projectName },
{ name: 'URL', value: huUrl || window.location.href },
],
}],
})
}
/**
* Envía una notificación de bloqueo/impedimento.
*/
export async function notifyBlockerLogged(
projectName: string,
huTitle: string,
category: string,
description: string,
hoursLost: number,
): Promise<boolean> {
return sendToTeams({
title: '⛔ Bloqueo registrado',
text: `**Proyecto:** ${projectName}`,
themeColor: 'FF4444',
sections: [{
facts: [
{ name: 'HU', value: huTitle },
{ name: 'Categoría', value: category },
{ name: 'Descripción', value: description },
{ name: 'Horas perdidas', value: `${hoursLost}h` },
],
}],
})
}
/**
* Envía un mensaje de prueba.
*/
export async function sendTestMessage(): Promise<boolean> {
return sendToTeams({
title: '🔔 Prueba de integración',
text: 'Este es un mensaje de prueba desde **Alpha**.\n\nSi ves esto, la integración con Teams funciona correctamente.',
themeColor: '00FF00',
})
}
+67
View File
@@ -16,6 +16,7 @@ import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import { getWebhookUrl, setWebhookUrl, sendTestMessage } from '@/services/teams'
import {
Brain,
Key,
@@ -26,6 +27,7 @@ import {
LogOut,
Sparkles,
ChevronRight,
MessageSquare,
} from 'lucide-vue-next'
const { t } = useI18n()
@@ -68,6 +70,33 @@ function markDirty(key: string) {
onMounted(loadPrompts)
// Teams
const teamsUrl = ref(getWebhookUrl())
const teamsSending = ref(false)
const teamsStatus = ref<'idle' | 'ok' | 'error'>('idle')
const teamsStatusMsg = ref('')
function teamsUrlChanged() {
setWebhookUrl(teamsUrl.value)
teamsStatus.value = 'idle'
}
async function testTeams() {
teamsSending.value = true
teamsStatus.value = 'idle'
teamsStatusMsg.value = ''
try {
const ok = await sendTestMessage()
teamsStatus.value = ok ? 'ok' : 'error'
teamsStatusMsg.value = ok ? 'Mensaje enviado a Teams' : 'Error al enviar. Verifica la URL del webhook.'
} catch (e: any) {
teamsStatus.value = 'error'
teamsStatusMsg.value = e.message
} finally {
teamsSending.value = false
}
}
const newKey = ref('')
const keySaved = ref(false)
@@ -283,6 +312,44 @@ const tierColors: Record<string, string> = {
</CardContent>
</Card>
<!-- Teams -->
<Card id="settings-teams">
<CardHeader>
<CardTitle class="text-sm font-medium flex items-center gap-2">
<MessageSquare class="size-4" />
Microsoft Teams
</CardTitle>
<CardDescription class="text-xs">
Configurá un webhook para recibir notificaciones de Alpha en Teams.
</CardDescription>
</CardHeader>
<CardContent class="space-y-3">
<div class="space-y-1.5">
<Label class="text-xs font-medium">URL del Webhook</Label>
<Input
v-model="teamsUrl"
placeholder="https://...office.com/webhookb2/..."
class="text-xs font-mono"
@input="teamsUrlChanged"
/>
<p class="text-[10px] text-muted-foreground">
Creá un webhook en Teams: Canal ... Conectores Webhook entrante
</p>
</div>
<div v-if="teamsUrl" class="flex items-center gap-2">
<Button size="sm" variant="outline" class="text-xs" :disabled="teamsSending" @click="testTeams">
{{ teamsSending ? 'Enviando...' : 'Enviar prueba' }}
</Button>
<span v-if="teamsStatus === 'ok'" class="text-xs text-green-600 flex items-center gap-1">
<CheckCircle2 class="size-3" /> {{ teamsStatusMsg }}
</span>
<span v-if="teamsStatus === 'error'" class="text-xs text-red-600 flex items-center gap-1">
<XCircle class="size-3" /> {{ teamsStatusMsg }}
</span>
</div>
</CardContent>
</Card>
<!-- Prompts -->
<Card id="settings-prompts" class="border-dashed">
<CardHeader>