@burdenoff/microfe-channel 2026.624.3 → 2026.625.2
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/dist/channel/modules/channels/pages/ChannelDashboardPage.js +134 -134
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +287 -277
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +140 -131
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js +169 -169
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.js +204 -209
- package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +161 -155
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js +141 -148
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/CreateChannelPage.js +27 -24
- package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/CreateConnectionPage.js +161 -156
- package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +177 -192
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditChannelPage.js +130 -122
- package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditConnectionPage.js +137 -132
- package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/SendMessagePage.js +87 -84
- package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js +185 -183
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -7,8 +7,9 @@ import { useI18n as s } from "@burdenoff/fe-libs/shared/providers/shell/I18nProv
|
|
|
7
7
|
import { AlertCircle as c, ArrowLeft as l, Loader2 as u, Save as d, Shield as f } from "lucide-react";
|
|
8
8
|
import { useNavigate as p, useSearchParams as m } from "react-router-dom";
|
|
9
9
|
import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
10
|
+
import { GlassCard as v } from "@burdenoff/fe-libs/ui";
|
|
10
11
|
//#region src/channel/modules/channels/pages/CreateConnectionPage.tsx
|
|
11
|
-
var
|
|
12
|
+
var y = [
|
|
12
13
|
{
|
|
13
14
|
value: "OAUTH2",
|
|
14
15
|
label: "OAuth 2.0",
|
|
@@ -35,11 +36,11 @@ var v = [
|
|
|
35
36
|
help: "Mail server credentials"
|
|
36
37
|
}
|
|
37
38
|
];
|
|
38
|
-
function
|
|
39
|
-
let
|
|
40
|
-
let n =
|
|
39
|
+
function b() {
|
|
40
|
+
let b = p(), [x] = m(), { organizationId: S } = e(), { channels: C, isLoading: w } = n(), { t: T } = s(), E = (e, t) => {
|
|
41
|
+
let n = T(e);
|
|
41
42
|
return n === e ? t : n;
|
|
42
|
-
}, [
|
|
43
|
+
}, [D, O] = o(x.get("channelId") ?? ""), { createConnection: k } = t(D), [A, j] = o("OAUTH2"), [M, N] = o(""), [P, F] = o(""), [I, L] = o(""), [R, z] = o(""), [B, V] = o(!1), [H, U] = o(null), W = a(() => C.find((e) => e.id === D) ?? null, [C, D]);
|
|
43
44
|
return /* @__PURE__ */ g(r, {
|
|
44
45
|
permission: i.CHANNEL_CONNECTION_CREATE,
|
|
45
46
|
children: /* @__PURE__ */ _("div", {
|
|
@@ -47,184 +48,188 @@ function y() {
|
|
|
47
48
|
children: [
|
|
48
49
|
/* @__PURE__ */ _("button", {
|
|
49
50
|
type: "button",
|
|
50
|
-
onClick: () =>
|
|
51
|
+
onClick: () => b(-1),
|
|
51
52
|
className: "flex items-center gap-2 text-fg-muted hover:text-fg mb-6",
|
|
52
|
-
children: [/* @__PURE__ */ g(l, { className: "size-4 " }),
|
|
53
|
+
children: [/* @__PURE__ */ g(l, { className: "size-4 " }), E("common.back", "Back")]
|
|
53
54
|
}),
|
|
54
55
|
/* @__PURE__ */ _("div", {
|
|
55
56
|
className: "flex items-center gap-3 mb-6",
|
|
56
57
|
children: [/* @__PURE__ */ g(f, { className: "size-7 text-accent-blue" }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h1", {
|
|
57
58
|
className: "text-2xl font-bold text-fg",
|
|
58
|
-
children:
|
|
59
|
+
children: E("connection.create.title", "Create Connection")
|
|
59
60
|
}), /* @__PURE__ */ g("p", {
|
|
60
61
|
className: "text-sm text-fg-muted",
|
|
61
|
-
children:
|
|
62
|
+
children: E("connection.create.subtitle", "Authenticate a channel against its platform provider.")
|
|
62
63
|
})] })]
|
|
63
64
|
}),
|
|
64
|
-
|
|
65
|
+
H && /* @__PURE__ */ _("div", {
|
|
65
66
|
className: "bg-status-error-bg-subtle border border-status-error-bg-subtle rounded-lg p-4 mb-6 flex gap-3",
|
|
66
67
|
children: [/* @__PURE__ */ g(c, { className: "size-5 text-status-error-text shrink-0" }), /* @__PURE__ */ g("p", {
|
|
67
68
|
className: "text-sm text-status-error-text",
|
|
68
|
-
children:
|
|
69
|
+
children: H
|
|
69
70
|
})]
|
|
70
71
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
72
|
+
/* @__PURE__ */ g(v, {
|
|
73
|
+
treatment: "plain",
|
|
74
|
+
className: "border-border-seam p-6",
|
|
75
|
+
children: /* @__PURE__ */ _("form", {
|
|
76
|
+
onSubmit: async (e) => {
|
|
77
|
+
if (e.preventDefault(), U(null), !D) {
|
|
78
|
+
U(E("connection.validation.channelRequired", "Pick a channel to connect"));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!S) {
|
|
82
|
+
U(E("connection.validation.organizationRequired", "Organization context missing"));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let t;
|
|
86
|
+
if (R.trim()) try {
|
|
87
|
+
t = JSON.parse(R);
|
|
88
|
+
} catch {
|
|
89
|
+
U(E("connection.validation.credentialsJson", "Credentials must be valid JSON"));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
V(!0);
|
|
93
|
+
try {
|
|
94
|
+
let e = await k({
|
|
95
|
+
authType: A,
|
|
96
|
+
authCode: M.trim() || void 0,
|
|
97
|
+
apiKey: P.trim() || void 0,
|
|
98
|
+
redirectUri: I.trim() || void 0,
|
|
99
|
+
credentials: t
|
|
100
|
+
});
|
|
101
|
+
e ? b(`../${e.id}`) : U(E("connection.createFailed", "Failed to create connection"));
|
|
102
|
+
} catch (e) {
|
|
103
|
+
U(e instanceof Error ? e.message : E("connection.createFailed", "Failed to create connection"));
|
|
104
|
+
} finally {
|
|
105
|
+
V(!1);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
className: "space-y-6",
|
|
109
|
+
children: [
|
|
110
|
+
/* @__PURE__ */ _("div", { children: [
|
|
111
|
+
/* @__PURE__ */ _("label", {
|
|
112
|
+
className: "block text-sm font-medium text-fg mb-2",
|
|
113
|
+
children: [E("connection.field.channel", "Channel"), " *"]
|
|
114
|
+
}),
|
|
115
|
+
/* @__PURE__ */ _("select", {
|
|
116
|
+
value: D,
|
|
117
|
+
onChange: (e) => O(e.target.value),
|
|
118
|
+
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg",
|
|
119
|
+
required: !0,
|
|
120
|
+
disabled: w,
|
|
121
|
+
children: [/* @__PURE__ */ g("option", {
|
|
122
|
+
value: "",
|
|
123
|
+
children: w ? E("common.loading", "Loading...") : E("connection.field.channelPlaceholder", "Select a channel")
|
|
124
|
+
}), C.map((e) => /* @__PURE__ */ _("option", {
|
|
125
|
+
value: e.id,
|
|
126
|
+
children: [
|
|
127
|
+
e.name,
|
|
128
|
+
" (",
|
|
129
|
+
e.platform,
|
|
130
|
+
")"
|
|
131
|
+
]
|
|
132
|
+
}, e.id))]
|
|
133
|
+
}),
|
|
134
|
+
W && /* @__PURE__ */ _("p", {
|
|
135
|
+
className: "mt-1 text-xs text-fg-muted",
|
|
136
|
+
children: [E("connection.field.channelHint", "Adapter: "), W.adapterType ?? "—"]
|
|
137
|
+
})
|
|
138
|
+
] }),
|
|
139
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ _("label", {
|
|
108
140
|
className: "block text-sm font-medium text-fg mb-2",
|
|
109
|
-
children: [
|
|
110
|
-
}),
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
onChange: (e) => D(e.target.value),
|
|
141
|
+
children: [E("connection.field.authType", "Authentication Type"), " *"]
|
|
142
|
+
}), /* @__PURE__ */ g("select", {
|
|
143
|
+
value: A,
|
|
144
|
+
onChange: (e) => j(e.target.value),
|
|
114
145
|
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg",
|
|
115
146
|
required: !0,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
value: "",
|
|
119
|
-
children: C ? T("common.loading", "Loading...") : T("connection.field.channelPlaceholder", "Select a channel")
|
|
120
|
-
}), S.map((e) => /* @__PURE__ */ _("option", {
|
|
121
|
-
value: e.id,
|
|
147
|
+
children: y.map((e) => /* @__PURE__ */ _("option", {
|
|
148
|
+
value: e.value,
|
|
122
149
|
children: [
|
|
123
|
-
e.
|
|
124
|
-
"
|
|
125
|
-
e.
|
|
126
|
-
")"
|
|
150
|
+
e.label,
|
|
151
|
+
": ",
|
|
152
|
+
e.help
|
|
127
153
|
]
|
|
128
|
-
}, e.
|
|
129
|
-
}),
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
value: e.value,
|
|
145
|
-
children: [
|
|
146
|
-
e.label,
|
|
147
|
-
": ",
|
|
148
|
-
e.help
|
|
149
|
-
]
|
|
150
|
-
}, e.value))
|
|
151
|
-
})] }),
|
|
152
|
-
k === "OAUTH2" && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
153
|
-
htmlFor: "create-conn-auth-code",
|
|
154
|
-
className: "block text-sm font-medium text-fg mb-2",
|
|
155
|
-
children: T("connection.field.authCode", "OAuth Authorization Code")
|
|
156
|
-
}), /* @__PURE__ */ g("input", {
|
|
157
|
-
id: "create-conn-auth-code",
|
|
158
|
-
"aria-label": T("connection.field.authCode", "OAuth Authorization Code"),
|
|
159
|
-
type: "text",
|
|
160
|
-
value: j,
|
|
161
|
-
onChange: (e) => M(e.target.value),
|
|
162
|
-
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg",
|
|
163
|
-
autoComplete: "off"
|
|
164
|
-
})] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
165
|
-
htmlFor: "create-conn-redirect-uri",
|
|
166
|
-
className: "block text-sm font-medium text-fg mb-2",
|
|
167
|
-
children: T("connection.field.redirectUri", "Redirect URI")
|
|
168
|
-
}), /* @__PURE__ */ g("input", {
|
|
169
|
-
id: "create-conn-redirect-uri",
|
|
170
|
-
"aria-label": T("connection.field.redirectUri", "Redirect URI"),
|
|
171
|
-
type: "url",
|
|
172
|
-
value: F,
|
|
173
|
-
onChange: (e) => I(e.target.value),
|
|
174
|
-
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg"
|
|
175
|
-
})] })] }),
|
|
176
|
-
(k === "API_KEY" || k === "BOT_TOKEN") && /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
177
|
-
htmlFor: "create-conn-api-key",
|
|
178
|
-
className: "block text-sm font-medium text-fg mb-2",
|
|
179
|
-
children: T("connection.field.apiKey", "API Key / Token")
|
|
180
|
-
}), /* @__PURE__ */ g("input", {
|
|
181
|
-
id: "create-conn-api-key",
|
|
182
|
-
"aria-label": T("connection.field.apiKey", "API Key / Token"),
|
|
183
|
-
type: "password",
|
|
184
|
-
value: N,
|
|
185
|
-
onChange: (e) => P(e.target.value),
|
|
186
|
-
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg",
|
|
187
|
-
autoComplete: "new-password"
|
|
188
|
-
})] }),
|
|
189
|
-
/* @__PURE__ */ _("div", { children: [
|
|
190
|
-
/* @__PURE__ */ g("label", {
|
|
154
|
+
}, e.value))
|
|
155
|
+
})] }),
|
|
156
|
+
A === "OAUTH2" && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
157
|
+
htmlFor: "create-conn-auth-code",
|
|
158
|
+
className: "block text-sm font-medium text-fg mb-2",
|
|
159
|
+
children: E("connection.field.authCode", "OAuth Authorization Code")
|
|
160
|
+
}), /* @__PURE__ */ g("input", {
|
|
161
|
+
id: "create-conn-auth-code",
|
|
162
|
+
"aria-label": E("connection.field.authCode", "OAuth Authorization Code"),
|
|
163
|
+
type: "text",
|
|
164
|
+
value: M,
|
|
165
|
+
onChange: (e) => N(e.target.value),
|
|
166
|
+
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg",
|
|
167
|
+
autoComplete: "off"
|
|
168
|
+
})] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
169
|
+
htmlFor: "create-conn-redirect-uri",
|
|
191
170
|
className: "block text-sm font-medium text-fg mb-2",
|
|
192
|
-
children:
|
|
193
|
-
}),
|
|
194
|
-
|
|
195
|
-
"aria-label": "
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
className: "
|
|
204
|
-
children:
|
|
171
|
+
children: E("connection.field.redirectUri", "Redirect URI")
|
|
172
|
+
}), /* @__PURE__ */ g("input", {
|
|
173
|
+
id: "create-conn-redirect-uri",
|
|
174
|
+
"aria-label": E("connection.field.redirectUri", "Redirect URI"),
|
|
175
|
+
type: "url",
|
|
176
|
+
value: I,
|
|
177
|
+
onChange: (e) => L(e.target.value),
|
|
178
|
+
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg"
|
|
179
|
+
})] })] }),
|
|
180
|
+
(A === "API_KEY" || A === "BOT_TOKEN") && /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
181
|
+
htmlFor: "create-conn-api-key",
|
|
182
|
+
className: "block text-sm font-medium text-fg mb-2",
|
|
183
|
+
children: E("connection.field.apiKey", "API Key / Token")
|
|
184
|
+
}), /* @__PURE__ */ g("input", {
|
|
185
|
+
id: "create-conn-api-key",
|
|
186
|
+
"aria-label": E("connection.field.apiKey", "API Key / Token"),
|
|
187
|
+
type: "password",
|
|
188
|
+
value: P,
|
|
189
|
+
onChange: (e) => F(e.target.value),
|
|
190
|
+
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg",
|
|
191
|
+
autoComplete: "new-password"
|
|
192
|
+
})] }),
|
|
193
|
+
/* @__PURE__ */ _("div", { children: [
|
|
194
|
+
/* @__PURE__ */ g("label", {
|
|
195
|
+
className: "block text-sm font-medium text-fg mb-2",
|
|
196
|
+
children: E("connection.field.credentials", "Additional Credentials (JSON)")
|
|
197
|
+
}),
|
|
198
|
+
/* @__PURE__ */ g("textarea", {
|
|
199
|
+
"aria-label": "{\"smtpHost\": \"smtp.example.com\"}",
|
|
200
|
+
value: R,
|
|
201
|
+
onChange: (e) => z(e.target.value),
|
|
202
|
+
rows: 6,
|
|
203
|
+
className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md font-mono text-sm text-fg",
|
|
204
|
+
placeholder: "{\"smtpHost\": \"smtp.example.com\"}"
|
|
205
|
+
}),
|
|
206
|
+
/* @__PURE__ */ g("p", {
|
|
207
|
+
className: "mt-1 text-xs text-fg-muted",
|
|
208
|
+
children: E("connection.field.credentialsHint", "Encrypted at rest with AES-256-GCM. Never logged in plaintext.")
|
|
209
|
+
})
|
|
210
|
+
] }),
|
|
211
|
+
/* @__PURE__ */ _("div", {
|
|
212
|
+
className: "flex gap-3 pt-2",
|
|
213
|
+
children: [/* @__PURE__ */ _("button", {
|
|
214
|
+
type: "submit",
|
|
215
|
+
disabled: B || !D,
|
|
216
|
+
className: "flex items-center gap-2 px-4 py-2 bg-accent-blue text-fg-on-accent rounded-md disabled:opacity-50",
|
|
217
|
+
children: [B ? /* @__PURE__ */ g(u, { className: "size-4 animate-spin" }) : /* @__PURE__ */ g(d, { className: "size-4 " }), E("connection.create.submit", "Create Connection")]
|
|
218
|
+
}), /* @__PURE__ */ g("button", {
|
|
219
|
+
type: "button",
|
|
220
|
+
onClick: () => b(-1),
|
|
221
|
+
className: "px-4 py-2 text-fg border border-border-subtle rounded-md",
|
|
222
|
+
children: E("common.cancel", "Cancel")
|
|
223
|
+
})]
|
|
205
224
|
})
|
|
206
|
-
]
|
|
207
|
-
|
|
208
|
-
className: "flex gap-3 pt-2",
|
|
209
|
-
children: [/* @__PURE__ */ _("button", {
|
|
210
|
-
type: "submit",
|
|
211
|
-
disabled: z || !E,
|
|
212
|
-
className: "flex items-center gap-2 px-4 py-2 bg-accent-blue text-fg-on-accent rounded-md disabled:opacity-50",
|
|
213
|
-
children: [z ? /* @__PURE__ */ g(u, { className: "size-4 animate-spin" }) : /* @__PURE__ */ g(d, { className: "size-4 " }), T("connection.create.submit", "Create Connection")]
|
|
214
|
-
}), /* @__PURE__ */ g("button", {
|
|
215
|
-
type: "button",
|
|
216
|
-
onClick: () => y(-1),
|
|
217
|
-
className: "px-4 py-2 text-fg border border-border-subtle rounded-md",
|
|
218
|
-
children: T("common.cancel", "Cancel")
|
|
219
|
-
})]
|
|
220
|
-
})
|
|
221
|
-
]
|
|
225
|
+
]
|
|
226
|
+
})
|
|
222
227
|
})
|
|
223
228
|
]
|
|
224
229
|
})
|
|
225
230
|
});
|
|
226
231
|
}
|
|
227
232
|
//#endregion
|
|
228
|
-
export {
|
|
233
|
+
export { b as CreateConnectionPage };
|
|
229
234
|
|
|
230
235
|
//# sourceMappingURL=CreateConnectionPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateConnectionPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/CreateConnectionPage.tsx"],"sourcesContent":["/**\n * Create Connection Page\n *\n * Standalone page mounted at /channels/connections/create.\n * User picks a target channel, then provides auth details.\n *\n * Form fields match CreateConnectionInput exactly (less channelId which is\n * resolved from the selector).\n */\n\nimport { useMemo, useState } from 'react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Loader2, AlertCircle, Save, Shield } from 'lucide-react';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport { useChannel, useChannels, type ChannelAuthType } from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\n\nconst AUTH_TYPES: { value: ChannelAuthType; label: string; help: string }[] = [\n { value: 'OAUTH2', label: 'OAuth 2.0', help: 'Authorization code flow' },\n { value: 'API_KEY', label: 'API Key', help: 'Static API key' },\n { value: 'BOT_TOKEN', label: 'Bot Token', help: 'Slack/Discord/Teams bot token' },\n { value: 'WEBHOOK', label: 'Webhook', help: 'Shared secret for HMAC verification' },\n { value: 'SMTP_IMAP', label: 'SMTP / IMAP', help: 'Mail server credentials' },\n];\n\nexport function CreateConnectionPage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { organizationId } = useChannelContext();\n const { channels, isLoading: channelsLoading } = useChannels();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const initialChannelId = searchParams.get('channelId') ?? '';\n const [selectedChannelId, setSelectedChannelId] = useState<string>(initialChannelId);\n const { createConnection } = useChannel(selectedChannelId);\n\n const [authType, setAuthType] = useState<ChannelAuthType>('OAUTH2');\n const [authCode, setAuthCode] = useState('');\n const [apiKey, setApiKey] = useState('');\n const [redirectUri, setRedirectUri] = useState('');\n const [credentials, setCredentials] = useState('');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const selectedChannel = useMemo(\n () => channels.find((c) => c.id === selectedChannelId) ?? null,\n [channels, selectedChannelId]\n );\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!selectedChannelId) {\n setError(tr('connection.validation.channelRequired', 'Pick a channel to connect'));\n return;\n }\n if (!organizationId) {\n setError(tr('connection.validation.organizationRequired', 'Organization context missing'));\n return;\n }\n\n let parsedCredentials: Record<string, unknown> | undefined;\n if (credentials.trim()) {\n try {\n parsedCredentials = JSON.parse(credentials);\n } catch {\n setError(tr('connection.validation.credentialsJson', 'Credentials must be valid JSON'));\n return;\n }\n }\n\n setIsSubmitting(true);\n try {\n const result = await createConnection({\n authType,\n authCode: authCode.trim() || undefined,\n apiKey: apiKey.trim() || undefined,\n redirectUri: redirectUri.trim() || undefined,\n credentials: parsedCredentials,\n });\n if (result) {\n navigate(`../${result.id}`);\n } else {\n setError(tr('connection.createFailed', 'Failed to create connection'));\n }\n } catch (err: unknown) {\n setError(\n err instanceof Error\n ? err.message\n : tr('connection.createFailed', 'Failed to create connection')\n );\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_CREATE}>\n <div className=\"container mx-auto p-6 max-w-2xl\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"flex items-center gap-2 text-fg-muted hover:text-fg mb-6\"\n >\n <ArrowLeft className=\"size-4 \" />\n {tr('common.back', 'Back')}\n </button>\n\n <div className=\"flex items-center gap-3 mb-6\">\n <Shield className=\"size-7 text-accent-blue\" />\n <div>\n <h1 className=\"text-2xl font-bold text-fg\">\n {tr('connection.create.title', 'Create Connection')}\n </h1>\n <p className=\"text-sm text-fg-muted\">\n {tr(\n 'connection.create.subtitle',\n 'Authenticate a channel against its platform provider.'\n )}\n </p>\n </div>\n </div>\n\n {error && (\n <div className=\"bg-status-error-bg-subtle border border-status-error-bg-subtle rounded-lg p-4 mb-6 flex gap-3\">\n <AlertCircle className=\"size-5 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">{error}</p>\n </div>\n )}\n\n <form\n onSubmit={handleSubmit}\n className=\"space-y-6 bg-bg-elevated rounded-lg p-6 border border-border-subtle\"\n >\n <div>\n <label className=\"block text-sm font-medium text-fg mb-2\">\n {tr('connection.field.channel', 'Channel')} *\n </label>\n <select\n value={selectedChannelId}\n onChange={(e) => setSelectedChannelId(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n required\n disabled={channelsLoading}\n >\n <option value=\"\">\n {channelsLoading\n ? tr('common.loading', 'Loading...')\n : tr('connection.field.channelPlaceholder', 'Select a channel')}\n </option>\n {channels.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name} ({c.platform})\n </option>\n ))}\n </select>\n {selectedChannel && (\n <p className=\"mt-1 text-xs text-fg-muted\">\n {tr('connection.field.channelHint', 'Adapter: ')}\n {selectedChannel.adapterType ?? '—'}\n </p>\n )}\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-fg mb-2\">\n {tr('connection.field.authType', 'Authentication Type')} *\n </label>\n <select\n value={authType}\n onChange={(e) => setAuthType(e.target.value as ChannelAuthType)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n required\n >\n {AUTH_TYPES.map((a) => (\n <option key={a.value} value={a.value}>\n {a.label}: {a.help}\n </option>\n ))}\n </select>\n </div>\n\n {authType === 'OAUTH2' && (\n <>\n <div>\n <label\n htmlFor=\"create-conn-auth-code\"\n className=\"block text-sm font-medium text-fg mb-2\"\n >\n {tr('connection.field.authCode', 'OAuth Authorization Code')}\n </label>\n <input\n id=\"create-conn-auth-code\"\n aria-label={tr('connection.field.authCode', 'OAuth Authorization Code')}\n type=\"text\"\n value={authCode}\n onChange={(e) => setAuthCode(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n autoComplete=\"off\"\n />\n </div>\n <div>\n <label\n htmlFor=\"create-conn-redirect-uri\"\n className=\"block text-sm font-medium text-fg mb-2\"\n >\n {tr('connection.field.redirectUri', 'Redirect URI')}\n </label>\n <input\n id=\"create-conn-redirect-uri\"\n aria-label={tr('connection.field.redirectUri', 'Redirect URI')}\n type=\"url\"\n value={redirectUri}\n onChange={(e) => setRedirectUri(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n />\n </div>\n </>\n )}\n\n {(authType === 'API_KEY' || authType === 'BOT_TOKEN') && (\n <div>\n <label\n htmlFor=\"create-conn-api-key\"\n className=\"block text-sm font-medium text-fg mb-2\"\n >\n {tr('connection.field.apiKey', 'API Key / Token')}\n </label>\n <input\n id=\"create-conn-api-key\"\n aria-label={tr('connection.field.apiKey', 'API Key / Token')}\n type=\"password\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n autoComplete=\"new-password\"\n />\n </div>\n )}\n\n <div>\n <label className=\"block text-sm font-medium text-fg mb-2\">\n {tr('connection.field.credentials', 'Additional Credentials (JSON)')}\n </label>\n <textarea\n aria-label='{\"smtpHost\": \"smtp.example.com\"}'\n value={credentials}\n onChange={(e) => setCredentials(e.target.value)}\n rows={6}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md font-mono text-sm text-fg\"\n placeholder='{\"smtpHost\": \"smtp.example.com\"}'\n />\n <p className=\"mt-1 text-xs text-fg-muted\">\n {tr(\n 'connection.field.credentialsHint',\n 'Encrypted at rest with AES-256-GCM. Never logged in plaintext.'\n )}\n </p>\n </div>\n\n <div className=\"flex gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={isSubmitting || !selectedChannelId}\n className=\"flex items-center gap-2 px-4 py-2 bg-accent-blue text-fg-on-accent rounded-md disabled:opacity-50\"\n >\n {isSubmitting ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Save className=\"size-4 \" />\n )}\n {tr('connection.create.submit', 'Create Connection')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"px-4 py-2 text-fg border border-border-subtle rounded-md\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </form>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateConnectionPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/CreateConnectionPage.tsx"],"sourcesContent":["/**\n * Create Connection Page\n *\n * Standalone page mounted at /channels/connections/create.\n * User picks a target channel, then provides auth details.\n *\n * Form fields match CreateConnectionInput exactly (less channelId which is\n * resolved from the selector).\n */\n\nimport { useMemo, useState } from 'react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Loader2, AlertCircle, Save, Shield } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport { useChannel, useChannels, type ChannelAuthType } from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\n\nconst AUTH_TYPES: { value: ChannelAuthType; label: string; help: string }[] = [\n { value: 'OAUTH2', label: 'OAuth 2.0', help: 'Authorization code flow' },\n { value: 'API_KEY', label: 'API Key', help: 'Static API key' },\n { value: 'BOT_TOKEN', label: 'Bot Token', help: 'Slack/Discord/Teams bot token' },\n { value: 'WEBHOOK', label: 'Webhook', help: 'Shared secret for HMAC verification' },\n { value: 'SMTP_IMAP', label: 'SMTP / IMAP', help: 'Mail server credentials' },\n];\n\nexport function CreateConnectionPage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { organizationId } = useChannelContext();\n const { channels, isLoading: channelsLoading } = useChannels();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const initialChannelId = searchParams.get('channelId') ?? '';\n const [selectedChannelId, setSelectedChannelId] = useState<string>(initialChannelId);\n const { createConnection } = useChannel(selectedChannelId);\n\n const [authType, setAuthType] = useState<ChannelAuthType>('OAUTH2');\n const [authCode, setAuthCode] = useState('');\n const [apiKey, setApiKey] = useState('');\n const [redirectUri, setRedirectUri] = useState('');\n const [credentials, setCredentials] = useState('');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const selectedChannel = useMemo(\n () => channels.find((c) => c.id === selectedChannelId) ?? null,\n [channels, selectedChannelId]\n );\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!selectedChannelId) {\n setError(tr('connection.validation.channelRequired', 'Pick a channel to connect'));\n return;\n }\n if (!organizationId) {\n setError(tr('connection.validation.organizationRequired', 'Organization context missing'));\n return;\n }\n\n let parsedCredentials: Record<string, unknown> | undefined;\n if (credentials.trim()) {\n try {\n parsedCredentials = JSON.parse(credentials);\n } catch {\n setError(tr('connection.validation.credentialsJson', 'Credentials must be valid JSON'));\n return;\n }\n }\n\n setIsSubmitting(true);\n try {\n const result = await createConnection({\n authType,\n authCode: authCode.trim() || undefined,\n apiKey: apiKey.trim() || undefined,\n redirectUri: redirectUri.trim() || undefined,\n credentials: parsedCredentials,\n });\n if (result) {\n navigate(`../${result.id}`);\n } else {\n setError(tr('connection.createFailed', 'Failed to create connection'));\n }\n } catch (err: unknown) {\n setError(\n err instanceof Error\n ? err.message\n : tr('connection.createFailed', 'Failed to create connection')\n );\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_CREATE}>\n <div className=\"container mx-auto p-6 max-w-2xl\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"flex items-center gap-2 text-fg-muted hover:text-fg mb-6\"\n >\n <ArrowLeft className=\"size-4 \" />\n {tr('common.back', 'Back')}\n </button>\n\n <div className=\"flex items-center gap-3 mb-6\">\n <Shield className=\"size-7 text-accent-blue\" />\n <div>\n <h1 className=\"text-2xl font-bold text-fg\">\n {tr('connection.create.title', 'Create Connection')}\n </h1>\n <p className=\"text-sm text-fg-muted\">\n {tr(\n 'connection.create.subtitle',\n 'Authenticate a channel against its platform provider.'\n )}\n </p>\n </div>\n </div>\n\n {error && (\n <div className=\"bg-status-error-bg-subtle border border-status-error-bg-subtle rounded-lg p-4 mb-6 flex gap-3\">\n <AlertCircle className=\"size-5 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">{error}</p>\n </div>\n )}\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-6\">\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n <div>\n <label className=\"block text-sm font-medium text-fg mb-2\">\n {tr('connection.field.channel', 'Channel')} *\n </label>\n <select\n value={selectedChannelId}\n onChange={(e) => setSelectedChannelId(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n required\n disabled={channelsLoading}\n >\n <option value=\"\">\n {channelsLoading\n ? tr('common.loading', 'Loading...')\n : tr('connection.field.channelPlaceholder', 'Select a channel')}\n </option>\n {channels.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name} ({c.platform})\n </option>\n ))}\n </select>\n {selectedChannel && (\n <p className=\"mt-1 text-xs text-fg-muted\">\n {tr('connection.field.channelHint', 'Adapter: ')}\n {selectedChannel.adapterType ?? '—'}\n </p>\n )}\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-fg mb-2\">\n {tr('connection.field.authType', 'Authentication Type')} *\n </label>\n <select\n value={authType}\n onChange={(e) => setAuthType(e.target.value as ChannelAuthType)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n required\n >\n {AUTH_TYPES.map((a) => (\n <option key={a.value} value={a.value}>\n {a.label}: {a.help}\n </option>\n ))}\n </select>\n </div>\n\n {authType === 'OAUTH2' && (\n <>\n <div>\n <label\n htmlFor=\"create-conn-auth-code\"\n className=\"block text-sm font-medium text-fg mb-2\"\n >\n {tr('connection.field.authCode', 'OAuth Authorization Code')}\n </label>\n <input\n id=\"create-conn-auth-code\"\n aria-label={tr('connection.field.authCode', 'OAuth Authorization Code')}\n type=\"text\"\n value={authCode}\n onChange={(e) => setAuthCode(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n autoComplete=\"off\"\n />\n </div>\n <div>\n <label\n htmlFor=\"create-conn-redirect-uri\"\n className=\"block text-sm font-medium text-fg mb-2\"\n >\n {tr('connection.field.redirectUri', 'Redirect URI')}\n </label>\n <input\n id=\"create-conn-redirect-uri\"\n aria-label={tr('connection.field.redirectUri', 'Redirect URI')}\n type=\"url\"\n value={redirectUri}\n onChange={(e) => setRedirectUri(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n />\n </div>\n </>\n )}\n\n {(authType === 'API_KEY' || authType === 'BOT_TOKEN') && (\n <div>\n <label\n htmlFor=\"create-conn-api-key\"\n className=\"block text-sm font-medium text-fg mb-2\"\n >\n {tr('connection.field.apiKey', 'API Key / Token')}\n </label>\n <input\n id=\"create-conn-api-key\"\n aria-label={tr('connection.field.apiKey', 'API Key / Token')}\n type=\"password\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg\"\n autoComplete=\"new-password\"\n />\n </div>\n )}\n\n <div>\n <label className=\"block text-sm font-medium text-fg mb-2\">\n {tr('connection.field.credentials', 'Additional Credentials (JSON)')}\n </label>\n <textarea\n aria-label='{\"smtpHost\": \"smtp.example.com\"}'\n value={credentials}\n onChange={(e) => setCredentials(e.target.value)}\n rows={6}\n className=\"w-full px-3 py-2 bg-bg border border-border-subtle rounded-md font-mono text-sm text-fg\"\n placeholder='{\"smtpHost\": \"smtp.example.com\"}'\n />\n <p className=\"mt-1 text-xs text-fg-muted\">\n {tr(\n 'connection.field.credentialsHint',\n 'Encrypted at rest with AES-256-GCM. Never logged in plaintext.'\n )}\n </p>\n </div>\n\n <div className=\"flex gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={isSubmitting || !selectedChannelId}\n className=\"flex items-center gap-2 px-4 py-2 bg-accent-blue text-fg-on-accent rounded-md disabled:opacity-50\"\n >\n {isSubmitting ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Save className=\"size-4 \" />\n )}\n {tr('connection.create.submit', 'Create Connection')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"px-4 py-2 text-fg border border-border-subtle rounded-md\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </form>\n </GlassCard>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|