@open-mercato/channel-imap 0.9.0 → 0.9.1-develop.7459.1.99e3accd30
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/channel_imap/__integration__/TC-CHANNEL-EMAIL-001.spec.js +16 -0
- package/dist/modules/channel_imap/__integration__/TC-CHANNEL-EMAIL-001.spec.js.map +2 -2
- package/dist/modules/channel_imap/lib/connect-form.js +66 -0
- package/dist/modules/channel_imap/lib/connect-form.js.map +7 -0
- package/dist/modules/channel_imap/widgets/injection/connect/widget.client.js +134 -290
- package/dist/modules/channel_imap/widgets/injection/connect/widget.client.js.map +2 -2
- package/package.json +8 -7
- package/src/modules/channel_imap/__integration__/TC-CHANNEL-EMAIL-001.spec.ts +17 -0
- package/src/modules/channel_imap/lib/__tests__/connect-form.test.ts +84 -0
- package/src/modules/channel_imap/lib/connect-form.ts +75 -0
- package/src/modules/channel_imap/widgets/injection/connect/widget.client.tsx +135 -295
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
[build:channel-imap] found
|
|
1
|
+
[build:channel-imap] found 34 entry points
|
|
2
2
|
[build:channel-imap] built successfully
|
|
@@ -58,5 +58,21 @@ test.describe("TC-CHANNEL-EMAIL-001: IMAP provider registration", () => {
|
|
|
58
58
|
expect(body?.fieldErrors ?? body?.error).toBeTruthy();
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
|
+
test("POST connect/credentials with an empty display name returns per-field details (#5593)", async ({ request }) => {
|
|
62
|
+
const token = await getAuthToken(request);
|
|
63
|
+
const response = await apiRequest(
|
|
64
|
+
request,
|
|
65
|
+
"POST",
|
|
66
|
+
"/api/communication_channels/channels/connect/credentials",
|
|
67
|
+
{
|
|
68
|
+
token,
|
|
69
|
+
data: { providerKey: "imap", displayName: "", credentials: {} }
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
expect(response.status()).toBe(422);
|
|
73
|
+
const body = await readJsonSafe(response);
|
|
74
|
+
expect(body?.error).toBe("Invalid request body");
|
|
75
|
+
expect(body?.details?.map((issue) => issue.path?.[0])).toEqual(["displayName"]);
|
|
76
|
+
});
|
|
61
77
|
});
|
|
62
78
|
//# sourceMappingURL=TC-CHANNEL-EMAIL-001.spec.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/channel_imap/__integration__/TC-CHANNEL-EMAIL-001.spec.ts"],
|
|
4
|
-
"sourcesContent": ["import { expect, test } from '@playwright/test'\nimport { getAuthToken } from '@open-mercato/core/helpers/integration/authFixtures'\nimport { apiRequest } from '@open-mercato/core/helpers/integration/api'\nimport { readJsonSafe } from '@open-mercato/core/helpers/integration/generalFixtures'\n\n/**\n * TC-CHANNEL-EMAIL-001 \u2014 IMAP provider visible to the hub's per-user channel API.\n *\n * After Slice 3e registers the `imap` adapter inside `@open-mercato/channel-imap`,\n * the per-user channel routes must accept `providerKey: 'imap'` as a known provider.\n * Without a live IMAP test server we exercise the routing-and-validation surface only;\n * actual credential validation against a real IMAP host is covered by the unit tests\n * in `lib/__tests__/validate-credentials.test.ts`.\n *\n * The route should NOT 404 the provider (proves registration), and should NOT 500\n * (proves the request reaches the adapter and rejects cleanly when no live server\n * is reachable).\n */\ntest.describe('TC-CHANNEL-EMAIL-001: IMAP provider registration', () => {\n test('POST connect/credentials with providerKey=imap reaches the adapter', async ({ request }) => {\n const token = await getAuthToken(request)\n const response = await apiRequest(\n request,\n 'POST',\n '/api/communication_channels/channels/connect/credentials',\n {\n token,\n data: {\n providerKey: 'imap',\n displayName: 'IMAP \u2014 integration test',\n credentials: {\n imapHost: 'invalid.test.example',\n imapPort: 993,\n imapTls: 'tls',\n imapUser: 'fake@example.test',\n imapPassword: 'wrong-password',\n smtpHost: 'invalid.test.example',\n smtpPort: 465,\n smtpTls: 'tls',\n smtpUser: 'fake@example.test',\n smtpPassword: 'wrong-password',\n fromAddress: 'fake@example.test',\n },\n },\n },\n )\n expect(response.status(), 'route should not 5xx').toBeLessThan(500)\n // 401 (no auth seeded) / 422 (validation failure surfaced via createCrudFormError) /\n // 502 ish \u2014 but never 404, which would indicate the provider isn't registered.\n expect(response.status(), 'IMAP provider should be registered').not.toBe(404)\n })\n\n test('POST connect/credentials with providerKey=imap and malformed credentials returns 422', async ({ request }) => {\n const token = await getAuthToken(request)\n const response = await apiRequest(\n request,\n 'POST',\n '/api/communication_channels/channels/connect/credentials',\n {\n token,\n data: {\n providerKey: 'imap',\n displayName: 'IMAP \u2014 malformed',\n credentials: {\n // Missing required imap/smtp fields entirely.\n fromAddress: 'not-an-email',\n },\n },\n },\n )\n expect(response.status()).toBeLessThan(500)\n expect([401, 422, 400]).toContain(response.status())\n if (response.status() === 422) {\n // The route surfaces credential-validation failures as { error, fieldErrors }\n // (see api/post/channels/connect/credentials/route.ts).\n const body = await readJsonSafe<{ error?: string; fieldErrors?: Record<string, string> }>(response)\n expect(body?.fieldErrors ?? body?.error).toBeTruthy()\n }\n })\n})\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,QAAQ,YAAY;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAe7B,KAAK,SAAS,oDAAoD,MAAM;AACtE,OAAK,sEAAsE,OAAO,EAAE,QAAQ,MAAM;AAChG,UAAM,QAAQ,MAAM,aAAa,OAAO;AACxC,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA,MAAM;AAAA,UACJ,aAAa;AAAA,UACb,aAAa;AAAA,UACb,aAAa;AAAA,YACX,UAAU;AAAA,YACV,UAAU;AAAA,YACV,SAAS;AAAA,YACT,UAAU;AAAA,YACV,cAAc;AAAA,YACd,UAAU;AAAA,YACV,UAAU;AAAA,YACV,SAAS;AAAA,YACT,UAAU;AAAA,YACV,cAAc;AAAA,YACd,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO,SAAS,OAAO,GAAG,sBAAsB,EAAE,aAAa,GAAG;AAGlE,WAAO,SAAS,OAAO,GAAG,oCAAoC,EAAE,IAAI,KAAK,GAAG;AAAA,EAC9E,CAAC;AAED,OAAK,wFAAwF,OAAO,EAAE,QAAQ,MAAM;AAClH,UAAM,QAAQ,MAAM,aAAa,OAAO;AACxC,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA,MAAM;AAAA,UACJ,aAAa;AAAA,UACb,aAAa;AAAA,UACb,aAAa;AAAA;AAAA,YAEX,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO,SAAS,OAAO,CAAC,EAAE,aAAa,GAAG;AAC1C,WAAO,CAAC,KAAK,KAAK,GAAG,CAAC,EAAE,UAAU,SAAS,OAAO,CAAC;AACnD,QAAI,SAAS,OAAO,MAAM,KAAK;AAG7B,YAAM,OAAO,MAAM,aAAuE,QAAQ;AAClG,aAAO,MAAM,eAAe,MAAM,KAAK,EAAE,WAAW;AAAA,IACtD;AAAA,EACF,CAAC;AACH,CAAC;",
|
|
4
|
+
"sourcesContent": ["import { expect, test } from '@playwright/test'\nimport { getAuthToken } from '@open-mercato/core/helpers/integration/authFixtures'\nimport { apiRequest } from '@open-mercato/core/helpers/integration/api'\nimport { readJsonSafe } from '@open-mercato/core/helpers/integration/generalFixtures'\n\n/**\n * TC-CHANNEL-EMAIL-001 \u2014 IMAP provider visible to the hub's per-user channel API.\n *\n * After Slice 3e registers the `imap` adapter inside `@open-mercato/channel-imap`,\n * the per-user channel routes must accept `providerKey: 'imap'` as a known provider.\n * Without a live IMAP test server we exercise the routing-and-validation surface only;\n * actual credential validation against a real IMAP host is covered by the unit tests\n * in `lib/__tests__/validate-credentials.test.ts`.\n *\n * The route should NOT 404 the provider (proves registration), and should NOT 500\n * (proves the request reaches the adapter and rejects cleanly when no live server\n * is reachable).\n */\ntest.describe('TC-CHANNEL-EMAIL-001: IMAP provider registration', () => {\n test('POST connect/credentials with providerKey=imap reaches the adapter', async ({ request }) => {\n const token = await getAuthToken(request)\n const response = await apiRequest(\n request,\n 'POST',\n '/api/communication_channels/channels/connect/credentials',\n {\n token,\n data: {\n providerKey: 'imap',\n displayName: 'IMAP \u2014 integration test',\n credentials: {\n imapHost: 'invalid.test.example',\n imapPort: 993,\n imapTls: 'tls',\n imapUser: 'fake@example.test',\n imapPassword: 'wrong-password',\n smtpHost: 'invalid.test.example',\n smtpPort: 465,\n smtpTls: 'tls',\n smtpUser: 'fake@example.test',\n smtpPassword: 'wrong-password',\n fromAddress: 'fake@example.test',\n },\n },\n },\n )\n expect(response.status(), 'route should not 5xx').toBeLessThan(500)\n // 401 (no auth seeded) / 422 (validation failure surfaced via createCrudFormError) /\n // 502 ish \u2014 but never 404, which would indicate the provider isn't registered.\n expect(response.status(), 'IMAP provider should be registered').not.toBe(404)\n })\n\n test('POST connect/credentials with providerKey=imap and malformed credentials returns 422', async ({ request }) => {\n const token = await getAuthToken(request)\n const response = await apiRequest(\n request,\n 'POST',\n '/api/communication_channels/channels/connect/credentials',\n {\n token,\n data: {\n providerKey: 'imap',\n displayName: 'IMAP \u2014 malformed',\n credentials: {\n // Missing required imap/smtp fields entirely.\n fromAddress: 'not-an-email',\n },\n },\n },\n )\n expect(response.status()).toBeLessThan(500)\n expect([401, 422, 400]).toContain(response.status())\n if (response.status() === 422) {\n // The route surfaces credential-validation failures as { error, fieldErrors }\n // (see api/post/channels/connect/credentials/route.ts).\n const body = await readJsonSafe<{ error?: string; fieldErrors?: Record<string, string> }>(response)\n expect(body?.fieldErrors ?? body?.error).toBeTruthy()\n }\n })\n\n test('POST connect/credentials with an empty display name returns per-field details (#5593)', async ({ request }) => {\n const token = await getAuthToken(request)\n const response = await apiRequest(\n request,\n 'POST',\n '/api/communication_channels/channels/connect/credentials',\n {\n token,\n data: { providerKey: 'imap', displayName: '', credentials: {} },\n },\n )\n expect(response.status()).toBe(422)\n const body = await readJsonSafe<{ error?: string; details?: Array<{ path?: unknown[] }> }>(response)\n expect(body?.error).toBe('Invalid request body')\n expect(body?.details?.map((issue) => issue.path?.[0])).toEqual(['displayName'])\n })\n})\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,QAAQ,YAAY;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAe7B,KAAK,SAAS,oDAAoD,MAAM;AACtE,OAAK,sEAAsE,OAAO,EAAE,QAAQ,MAAM;AAChG,UAAM,QAAQ,MAAM,aAAa,OAAO;AACxC,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA,MAAM;AAAA,UACJ,aAAa;AAAA,UACb,aAAa;AAAA,UACb,aAAa;AAAA,YACX,UAAU;AAAA,YACV,UAAU;AAAA,YACV,SAAS;AAAA,YACT,UAAU;AAAA,YACV,cAAc;AAAA,YACd,UAAU;AAAA,YACV,UAAU;AAAA,YACV,SAAS;AAAA,YACT,UAAU;AAAA,YACV,cAAc;AAAA,YACd,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO,SAAS,OAAO,GAAG,sBAAsB,EAAE,aAAa,GAAG;AAGlE,WAAO,SAAS,OAAO,GAAG,oCAAoC,EAAE,IAAI,KAAK,GAAG;AAAA,EAC9E,CAAC;AAED,OAAK,wFAAwF,OAAO,EAAE,QAAQ,MAAM;AAClH,UAAM,QAAQ,MAAM,aAAa,OAAO;AACxC,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA,MAAM;AAAA,UACJ,aAAa;AAAA,UACb,aAAa;AAAA,UACb,aAAa;AAAA;AAAA,YAEX,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO,SAAS,OAAO,CAAC,EAAE,aAAa,GAAG;AAC1C,WAAO,CAAC,KAAK,KAAK,GAAG,CAAC,EAAE,UAAU,SAAS,OAAO,CAAC;AACnD,QAAI,SAAS,OAAO,MAAM,KAAK;AAG7B,YAAM,OAAO,MAAM,aAAuE,QAAQ;AAClG,aAAO,MAAM,eAAe,MAAM,KAAK,EAAE,WAAW;AAAA,IACtD;AAAA,EACF,CAAC;AAED,OAAK,yFAAyF,OAAO,EAAE,QAAQ,MAAM;AACnH,UAAM,QAAQ,MAAM,aAAa,OAAO;AACxC,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA,MAAM,EAAE,aAAa,QAAQ,aAAa,IAAI,aAAa,CAAC,EAAE;AAAA,MAChE;AAAA,IACF;AACA,WAAO,SAAS,OAAO,CAAC,EAAE,KAAK,GAAG;AAClC,UAAM,OAAO,MAAM,aAAwE,QAAQ;AACnG,WAAO,MAAM,KAAK,EAAE,KAAK,sBAAsB;AAC/C,WAAO,MAAM,SAAS,IAAI,CAAC,UAAU,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAC;AAAA,EAChF,CAAC;AACH,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const TLS_MODES = ["tls", "starttls", "none"];
|
|
3
|
+
function createImapConnectFormSchema(t) {
|
|
4
|
+
const required = t("communication_channels.profile.connect.errors.required", "This field is required.");
|
|
5
|
+
const invalidPort = t(
|
|
6
|
+
"communication_channels.profile.connect.errors.invalidPort",
|
|
7
|
+
"Enter a port between 1 and 65535."
|
|
8
|
+
);
|
|
9
|
+
const requiredText = z.string().trim().min(1, required);
|
|
10
|
+
const optionalText = z.string().trim().optional();
|
|
11
|
+
const port = z.coerce.number(invalidPort).int(invalidPort).min(1, invalidPort).max(65535, invalidPort);
|
|
12
|
+
return z.object({
|
|
13
|
+
displayName: optionalText,
|
|
14
|
+
fromAddress: z.string().trim().min(1, required).email(t("communication_channels.profile.connect.errors.invalidEmail", "Enter a valid email address.")),
|
|
15
|
+
imapHost: requiredText,
|
|
16
|
+
imapPort: port,
|
|
17
|
+
imapTls: z.enum(TLS_MODES),
|
|
18
|
+
imapUser: requiredText,
|
|
19
|
+
imapPassword: z.string().min(1, required),
|
|
20
|
+
smtpHost: requiredText,
|
|
21
|
+
smtpPort: port,
|
|
22
|
+
smtpTls: z.enum(TLS_MODES),
|
|
23
|
+
smtpUser: optionalText,
|
|
24
|
+
smtpPassword: z.string().optional()
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const IMAP_CONNECT_INITIAL_VALUES = {
|
|
28
|
+
displayName: "",
|
|
29
|
+
fromAddress: "",
|
|
30
|
+
imapHost: "",
|
|
31
|
+
imapPort: 993,
|
|
32
|
+
imapTls: "tls",
|
|
33
|
+
imapUser: "",
|
|
34
|
+
imapPassword: "",
|
|
35
|
+
smtpHost: "",
|
|
36
|
+
smtpPort: 465,
|
|
37
|
+
smtpTls: "tls",
|
|
38
|
+
smtpUser: "",
|
|
39
|
+
smtpPassword: ""
|
|
40
|
+
};
|
|
41
|
+
function buildImapConnectRequestBody(values) {
|
|
42
|
+
return {
|
|
43
|
+
providerKey: "imap",
|
|
44
|
+
displayName: values.displayName || values.fromAddress,
|
|
45
|
+
pollIntervalSeconds: 300,
|
|
46
|
+
credentials: {
|
|
47
|
+
imapHost: values.imapHost,
|
|
48
|
+
imapPort: values.imapPort,
|
|
49
|
+
imapTls: values.imapTls,
|
|
50
|
+
imapUser: values.imapUser,
|
|
51
|
+
imapPassword: values.imapPassword,
|
|
52
|
+
smtpHost: values.smtpHost,
|
|
53
|
+
smtpPort: values.smtpPort,
|
|
54
|
+
smtpTls: values.smtpTls,
|
|
55
|
+
smtpUser: values.smtpUser || values.imapUser,
|
|
56
|
+
smtpPassword: values.smtpPassword || values.imapPassword,
|
|
57
|
+
fromAddress: values.fromAddress
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
IMAP_CONNECT_INITIAL_VALUES,
|
|
63
|
+
buildImapConnectRequestBody,
|
|
64
|
+
createImapConnectFormSchema
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=connect-form.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/channel_imap/lib/connect-form.ts"],
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\n\ntype Translate = (key: string, fallback: string) => string\n\nconst TLS_MODES = ['tls', 'starttls', 'none'] as const\n\nexport type ImapConnectTlsMode = (typeof TLS_MODES)[number]\n\nexport function createImapConnectFormSchema(t: Translate) {\n const required = t('communication_channels.profile.connect.errors.required', 'This field is required.')\n const invalidPort = t(\n 'communication_channels.profile.connect.errors.invalidPort',\n 'Enter a port between 1 and 65535.',\n )\n const requiredText = z.string().trim().min(1, required)\n const optionalText = z.string().trim().optional()\n const port = z.coerce.number(invalidPort).int(invalidPort).min(1, invalidPort).max(65535, invalidPort)\n\n return z.object({\n displayName: optionalText,\n fromAddress: z\n .string()\n .trim()\n .min(1, required)\n .email(t('communication_channels.profile.connect.errors.invalidEmail', 'Enter a valid email address.')),\n imapHost: requiredText,\n imapPort: port,\n imapTls: z.enum(TLS_MODES),\n imapUser: requiredText,\n imapPassword: z.string().min(1, required),\n smtpHost: requiredText,\n smtpPort: port,\n smtpTls: z.enum(TLS_MODES),\n smtpUser: optionalText,\n smtpPassword: z.string().optional(),\n })\n}\n\nexport type ImapConnectFormValues = z.infer<ReturnType<typeof createImapConnectFormSchema>>\n\nexport const IMAP_CONNECT_INITIAL_VALUES: Partial<ImapConnectFormValues> = {\n displayName: '',\n fromAddress: '',\n imapHost: '',\n imapPort: 993,\n imapTls: 'tls',\n imapUser: '',\n imapPassword: '',\n smtpHost: '',\n smtpPort: 465,\n smtpTls: 'tls',\n smtpUser: '',\n smtpPassword: '',\n}\n\nexport function buildImapConnectRequestBody(values: ImapConnectFormValues) {\n return {\n providerKey: 'imap',\n displayName: values.displayName || values.fromAddress,\n pollIntervalSeconds: 300,\n credentials: {\n imapHost: values.imapHost,\n imapPort: values.imapPort,\n imapTls: values.imapTls,\n imapUser: values.imapUser,\n imapPassword: values.imapPassword,\n smtpHost: values.smtpHost,\n smtpPort: values.smtpPort,\n smtpTls: values.smtpTls,\n smtpUser: values.smtpUser || values.imapUser,\n smtpPassword: values.smtpPassword || values.imapPassword,\n fromAddress: values.fromAddress,\n },\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,SAAS;AAIlB,MAAM,YAAY,CAAC,OAAO,YAAY,MAAM;AAIrC,SAAS,4BAA4B,GAAc;AACxD,QAAM,WAAW,EAAE,0DAA0D,yBAAyB;AACtG,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,EACF;AACA,QAAM,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,QAAQ;AACtD,QAAM,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAChD,QAAM,OAAO,EAAE,OAAO,OAAO,WAAW,EAAE,IAAI,WAAW,EAAE,IAAI,GAAG,WAAW,EAAE,IAAI,OAAO,WAAW;AAErG,SAAO,EAAE,OAAO;AAAA,IACd,aAAa;AAAA,IACb,aAAa,EACV,OAAO,EACP,KAAK,EACL,IAAI,GAAG,QAAQ,EACf,MAAM,EAAE,8DAA8D,8BAA8B,CAAC;AAAA,IACxG,UAAU;AAAA,IACV,UAAU;AAAA,IACV,SAAS,EAAE,KAAK,SAAS;AAAA,IACzB,UAAU;AAAA,IACV,cAAc,EAAE,OAAO,EAAE,IAAI,GAAG,QAAQ;AAAA,IACxC,UAAU;AAAA,IACV,UAAU;AAAA,IACV,SAAS,EAAE,KAAK,SAAS;AAAA,IACzB,UAAU;AAAA,IACV,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EACpC,CAAC;AACH;AAIO,MAAM,8BAA8D;AAAA,EACzE,aAAa;AAAA,EACb,aAAa;AAAA,EACb,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,cAAc;AAAA,EACd,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,cAAc;AAChB;AAEO,SAAS,4BAA4B,QAA+B;AACzE,SAAO;AAAA,IACL,aAAa;AAAA,IACb,aAAa,OAAO,eAAe,OAAO;AAAA,IAC1C,qBAAqB;AAAA,IACrB,aAAa;AAAA,MACX,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,SAAS,OAAO;AAAA,MAChB,UAAU,OAAO;AAAA,MACjB,cAAc,OAAO;AAAA,MACrB,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,SAAS,OAAO;AAAA,MAChB,UAAU,OAAO,YAAY,OAAO;AAAA,MACpC,cAAc,OAAO,gBAAgB,OAAO;AAAA,MAC5C,aAAa,OAAO;AAAA,IACtB;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -2,50 +2,30 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
|
+
import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
|
|
5
6
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
6
7
|
import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
|
|
7
8
|
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
9
|
+
import { raiseCrudError } from "@open-mercato/ui/backend/utils/serverErrors";
|
|
8
10
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
9
11
|
import {
|
|
10
12
|
Dialog,
|
|
11
13
|
DialogContent,
|
|
12
|
-
DialogFooter,
|
|
13
14
|
DialogHeader,
|
|
14
15
|
DialogTitle
|
|
15
16
|
} from "@open-mercato/ui/primitives/dialog";
|
|
16
|
-
import { Input } from "@open-mercato/ui/primitives/input";
|
|
17
|
-
import { Label } from "@open-mercato/ui/primitives/label";
|
|
18
|
-
import { PasswordInput } from "@open-mercato/ui/primitives/password-input";
|
|
19
17
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} from "@open-mercato/ui/primitives/select";
|
|
26
|
-
const INITIAL_FORM = {
|
|
27
|
-
displayName: "",
|
|
28
|
-
fromAddress: "",
|
|
29
|
-
imapHost: "",
|
|
30
|
-
imapPort: "993",
|
|
31
|
-
imapTls: "tls",
|
|
32
|
-
imapUser: "",
|
|
33
|
-
imapPassword: "",
|
|
34
|
-
smtpHost: "",
|
|
35
|
-
smtpPort: "465",
|
|
36
|
-
smtpTls: "tls",
|
|
37
|
-
smtpUser: "",
|
|
38
|
-
smtpPassword: ""
|
|
39
|
-
};
|
|
18
|
+
IMAP_CONNECT_INITIAL_VALUES,
|
|
19
|
+
buildImapConnectRequestBody,
|
|
20
|
+
createImapConnectFormSchema
|
|
21
|
+
} from "../../../lib/connect-form.js";
|
|
22
|
+
const FORM_ID = "channel-imap-connect-form";
|
|
40
23
|
function ConnectImapWidget({
|
|
41
24
|
context
|
|
42
25
|
}) {
|
|
43
26
|
const t = useT();
|
|
44
27
|
const widgetContext = context;
|
|
45
28
|
const [open, setOpen] = React.useState(false);
|
|
46
|
-
const [pending, setPending] = React.useState(false);
|
|
47
|
-
const [form, setForm] = React.useState(INITIAL_FORM);
|
|
48
|
-
const [fieldErrors, setFieldErrors] = React.useState({});
|
|
49
29
|
const { runMutation, retryLastMutation } = useGuardedMutation({
|
|
50
30
|
contextId: "channel-imap-connect",
|
|
51
31
|
blockedMessage: t("communication_channels.profile.connect.blocked", "Connection blocked by validation")
|
|
@@ -54,283 +34,147 @@ function ConnectImapWidget({
|
|
|
54
34
|
() => ({ providerKey: "imap", retryLastMutation }),
|
|
55
35
|
[retryLastMutation]
|
|
56
36
|
);
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
37
|
+
const schema = React.useMemo(() => createImapConnectFormSchema(t), [t]);
|
|
38
|
+
const fields = React.useMemo(() => {
|
|
39
|
+
const tlsOptions = [
|
|
40
|
+
{ value: "tls", label: t("communication_channels.profile.connect.tls.tls", "TLS") },
|
|
41
|
+
{ value: "starttls", label: t("communication_channels.profile.connect.tls.starttls", "STARTTLS") },
|
|
42
|
+
{ value: "none", label: t("communication_channels.profile.connect.tls.none", "None") }
|
|
43
|
+
];
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
id: "displayName",
|
|
47
|
+
type: "text",
|
|
48
|
+
label: t("communication_channels.profile.connect.fields.displayName", "Display name")
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "fromAddress",
|
|
52
|
+
type: "text",
|
|
53
|
+
label: t("communication_channels.profile.connect.fields.fromAddress", "From address"),
|
|
54
|
+
required: true
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "imapHost",
|
|
58
|
+
type: "text",
|
|
59
|
+
label: t("communication_channels.profile.connect.fields.imapHost", "IMAP host"),
|
|
60
|
+
required: true,
|
|
61
|
+
layout: "half"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "imapPort",
|
|
65
|
+
type: "number",
|
|
66
|
+
label: t("communication_channels.profile.connect.fields.imapPort", "IMAP port"),
|
|
67
|
+
required: true,
|
|
68
|
+
layout: "half"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "imapTls",
|
|
72
|
+
type: "select",
|
|
73
|
+
label: t("communication_channels.profile.connect.fields.imapTls", "IMAP security"),
|
|
74
|
+
options: tlsOptions,
|
|
75
|
+
required: true,
|
|
76
|
+
layout: "half"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "imapUser",
|
|
80
|
+
type: "text",
|
|
81
|
+
label: t("communication_channels.profile.connect.fields.imapUser", "IMAP username"),
|
|
82
|
+
required: true,
|
|
83
|
+
layout: "half"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: "imapPassword",
|
|
87
|
+
type: "password",
|
|
88
|
+
label: t("communication_channels.profile.connect.fields.imapPassword", "IMAP password"),
|
|
89
|
+
required: true
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "smtpHost",
|
|
93
|
+
type: "text",
|
|
94
|
+
label: t("communication_channels.profile.connect.fields.smtpHost", "SMTP host"),
|
|
95
|
+
required: true,
|
|
96
|
+
layout: "half"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: "smtpPort",
|
|
100
|
+
type: "number",
|
|
101
|
+
label: t("communication_channels.profile.connect.fields.smtpPort", "SMTP port"),
|
|
102
|
+
required: true,
|
|
103
|
+
layout: "half"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "smtpTls",
|
|
107
|
+
type: "select",
|
|
108
|
+
label: t("communication_channels.profile.connect.fields.smtpTls", "SMTP security"),
|
|
109
|
+
options: tlsOptions,
|
|
110
|
+
required: true,
|
|
111
|
+
layout: "half"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "smtpUser",
|
|
115
|
+
type: "text",
|
|
116
|
+
label: t("communication_channels.profile.connect.fields.smtpUser", "SMTP username"),
|
|
117
|
+
layout: "half"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: "smtpPassword",
|
|
121
|
+
type: "password",
|
|
122
|
+
label: t("communication_channels.profile.connect.fields.smtpPassword", "SMTP password")
|
|
123
|
+
}
|
|
124
|
+
];
|
|
125
|
+
}, [t]);
|
|
126
|
+
const handleSubmit = React.useCallback(
|
|
127
|
+
async (values) => {
|
|
128
|
+
const body = buildImapConnectRequestBody(values);
|
|
129
|
+
await runMutation({
|
|
75
130
|
context: mutationContext,
|
|
76
|
-
mutationPayload: { providerKey:
|
|
77
|
-
operation: () =>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
smtpPort: Number(form.smtpPort),
|
|
92
|
-
smtpTls: form.smtpTls,
|
|
93
|
-
smtpUser: form.smtpUser.trim() || form.imapUser.trim(),
|
|
94
|
-
smtpPassword: form.smtpPassword || form.imapPassword,
|
|
95
|
-
fromAddress: form.fromAddress.trim()
|
|
96
|
-
}
|
|
97
|
-
})
|
|
98
|
-
})
|
|
131
|
+
mutationPayload: { providerKey: body.providerKey, displayName: body.displayName },
|
|
132
|
+
operation: async () => {
|
|
133
|
+
const call = await apiCall("/api/communication_channels/channels/connect/credentials", {
|
|
134
|
+
method: "POST",
|
|
135
|
+
headers: { "content-type": "application/json" },
|
|
136
|
+
body: JSON.stringify(body)
|
|
137
|
+
});
|
|
138
|
+
if (!call.ok) {
|
|
139
|
+
await raiseCrudError(
|
|
140
|
+
call.response,
|
|
141
|
+
t("communication_channels.profile.connect.credentialsFailed", "Could not connect mailbox.")
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return call;
|
|
145
|
+
}
|
|
99
146
|
});
|
|
100
|
-
const body = response.result;
|
|
101
|
-
if (!response.ok) {
|
|
102
|
-
setFieldErrors(body?.fieldErrors ?? {});
|
|
103
|
-
flash(
|
|
104
|
-
body?.error ?? t("communication_channels.profile.connect.credentialsFailed", "Could not connect mailbox."),
|
|
105
|
-
"error"
|
|
106
|
-
);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
147
|
flash(t("communication_channels.profile.connect.connected", "Channel connected."), "success");
|
|
110
148
|
setOpen(false);
|
|
111
|
-
setForm(INITIAL_FORM);
|
|
112
149
|
widgetContext?.reload?.();
|
|
113
|
-
} finally {
|
|
114
|
-
setPending(false);
|
|
115
|
-
}
|
|
116
|
-
}, [form, mutationContext, pending, runMutation, t, widgetContext]);
|
|
117
|
-
const onDialogKeyDown = React.useCallback(
|
|
118
|
-
(event) => {
|
|
119
|
-
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
120
|
-
event.preventDefault();
|
|
121
|
-
void submit();
|
|
122
|
-
}
|
|
123
150
|
},
|
|
124
|
-
[
|
|
151
|
+
[mutationContext, runMutation, t, widgetContext]
|
|
125
152
|
);
|
|
153
|
+
const onDialogKeyDown = React.useCallback((event) => {
|
|
154
|
+
if (event.defaultPrevented || !(event.metaKey || event.ctrlKey) || event.key !== "Enter") return;
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
const form = document.getElementById(FORM_ID);
|
|
157
|
+
if (form instanceof HTMLFormElement) form.requestSubmit();
|
|
158
|
+
}, []);
|
|
126
159
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
127
160
|
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => setOpen(true), children: t("communication_channels.profile.connect.imap", "Connect IMAP") }),
|
|
128
161
|
/* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { onKeyDown: onDialogKeyDown, children: [
|
|
129
162
|
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t("communication_channels.profile.connect.imapTitle", "Connect IMAP mailbox") }) }),
|
|
130
|
-
/* @__PURE__ */
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
),
|
|
146
|
-
/* @__PURE__ */ jsx(
|
|
147
|
-
Field,
|
|
148
|
-
{
|
|
149
|
-
label: t("communication_channels.profile.connect.fields.fromAddress", "From address"),
|
|
150
|
-
error: fieldErrors.fromAddress,
|
|
151
|
-
children: /* @__PURE__ */ jsx(
|
|
152
|
-
Input,
|
|
153
|
-
{
|
|
154
|
-
type: "email",
|
|
155
|
-
value: form.fromAddress,
|
|
156
|
-
onChange: (event) => update("fromAddress", event.target.value),
|
|
157
|
-
"aria-invalid": Boolean(fieldErrors.fromAddress)
|
|
158
|
-
}
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
),
|
|
162
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [
|
|
163
|
-
/* @__PURE__ */ jsx(
|
|
164
|
-
Field,
|
|
165
|
-
{
|
|
166
|
-
label: t("communication_channels.profile.connect.fields.imapHost", "IMAP host"),
|
|
167
|
-
error: fieldErrors.imapHost,
|
|
168
|
-
children: /* @__PURE__ */ jsx(
|
|
169
|
-
Input,
|
|
170
|
-
{
|
|
171
|
-
value: form.imapHost,
|
|
172
|
-
onChange: (event) => update("imapHost", event.target.value),
|
|
173
|
-
"aria-invalid": Boolean(fieldErrors.imapHost)
|
|
174
|
-
}
|
|
175
|
-
)
|
|
176
|
-
}
|
|
177
|
-
),
|
|
178
|
-
/* @__PURE__ */ jsx(
|
|
179
|
-
Field,
|
|
180
|
-
{
|
|
181
|
-
label: t("communication_channels.profile.connect.fields.imapPort", "IMAP port"),
|
|
182
|
-
error: fieldErrors.imapPort,
|
|
183
|
-
children: /* @__PURE__ */ jsx(
|
|
184
|
-
Input,
|
|
185
|
-
{
|
|
186
|
-
inputMode: "numeric",
|
|
187
|
-
value: form.imapPort,
|
|
188
|
-
onChange: (event) => update("imapPort", event.target.value),
|
|
189
|
-
"aria-invalid": Boolean(fieldErrors.imapPort)
|
|
190
|
-
}
|
|
191
|
-
)
|
|
192
|
-
}
|
|
193
|
-
)
|
|
194
|
-
] }),
|
|
195
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [
|
|
196
|
-
/* @__PURE__ */ jsx(
|
|
197
|
-
Field,
|
|
198
|
-
{
|
|
199
|
-
label: t("communication_channels.profile.connect.fields.imapTls", "IMAP security"),
|
|
200
|
-
error: fieldErrors.imapTls,
|
|
201
|
-
children: /* @__PURE__ */ jsx(TlsSelect, { value: form.imapTls, onChange: (value) => update("imapTls", value) })
|
|
202
|
-
}
|
|
203
|
-
),
|
|
204
|
-
/* @__PURE__ */ jsx(
|
|
205
|
-
Field,
|
|
206
|
-
{
|
|
207
|
-
label: t("communication_channels.profile.connect.fields.imapUser", "IMAP username"),
|
|
208
|
-
error: fieldErrors.imapUser,
|
|
209
|
-
children: /* @__PURE__ */ jsx(
|
|
210
|
-
Input,
|
|
211
|
-
{
|
|
212
|
-
value: form.imapUser,
|
|
213
|
-
onChange: (event) => update("imapUser", event.target.value),
|
|
214
|
-
"aria-invalid": Boolean(fieldErrors.imapUser)
|
|
215
|
-
}
|
|
216
|
-
)
|
|
217
|
-
}
|
|
218
|
-
)
|
|
219
|
-
] }),
|
|
220
|
-
/* @__PURE__ */ jsx(
|
|
221
|
-
Field,
|
|
222
|
-
{
|
|
223
|
-
label: t("communication_channels.profile.connect.fields.imapPassword", "IMAP password"),
|
|
224
|
-
error: fieldErrors.imapPassword,
|
|
225
|
-
children: /* @__PURE__ */ jsx(
|
|
226
|
-
PasswordInput,
|
|
227
|
-
{
|
|
228
|
-
value: form.imapPassword,
|
|
229
|
-
onChange: (event) => update("imapPassword", event.target.value),
|
|
230
|
-
"aria-invalid": Boolean(fieldErrors.imapPassword)
|
|
231
|
-
}
|
|
232
|
-
)
|
|
233
|
-
}
|
|
234
|
-
),
|
|
235
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [
|
|
236
|
-
/* @__PURE__ */ jsx(
|
|
237
|
-
Field,
|
|
238
|
-
{
|
|
239
|
-
label: t("communication_channels.profile.connect.fields.smtpHost", "SMTP host"),
|
|
240
|
-
error: fieldErrors.smtpHost,
|
|
241
|
-
children: /* @__PURE__ */ jsx(
|
|
242
|
-
Input,
|
|
243
|
-
{
|
|
244
|
-
value: form.smtpHost,
|
|
245
|
-
onChange: (event) => update("smtpHost", event.target.value),
|
|
246
|
-
"aria-invalid": Boolean(fieldErrors.smtpHost)
|
|
247
|
-
}
|
|
248
|
-
)
|
|
249
|
-
}
|
|
250
|
-
),
|
|
251
|
-
/* @__PURE__ */ jsx(
|
|
252
|
-
Field,
|
|
253
|
-
{
|
|
254
|
-
label: t("communication_channels.profile.connect.fields.smtpPort", "SMTP port"),
|
|
255
|
-
error: fieldErrors.smtpPort,
|
|
256
|
-
children: /* @__PURE__ */ jsx(
|
|
257
|
-
Input,
|
|
258
|
-
{
|
|
259
|
-
inputMode: "numeric",
|
|
260
|
-
value: form.smtpPort,
|
|
261
|
-
onChange: (event) => update("smtpPort", event.target.value),
|
|
262
|
-
"aria-invalid": Boolean(fieldErrors.smtpPort)
|
|
263
|
-
}
|
|
264
|
-
)
|
|
265
|
-
}
|
|
266
|
-
)
|
|
267
|
-
] }),
|
|
268
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [
|
|
269
|
-
/* @__PURE__ */ jsx(
|
|
270
|
-
Field,
|
|
271
|
-
{
|
|
272
|
-
label: t("communication_channels.profile.connect.fields.smtpTls", "SMTP security"),
|
|
273
|
-
error: fieldErrors.smtpTls,
|
|
274
|
-
children: /* @__PURE__ */ jsx(TlsSelect, { value: form.smtpTls, onChange: (value) => update("smtpTls", value) })
|
|
275
|
-
}
|
|
276
|
-
),
|
|
277
|
-
/* @__PURE__ */ jsx(
|
|
278
|
-
Field,
|
|
279
|
-
{
|
|
280
|
-
label: t("communication_channels.profile.connect.fields.smtpUser", "SMTP username"),
|
|
281
|
-
error: fieldErrors.smtpUser,
|
|
282
|
-
children: /* @__PURE__ */ jsx(
|
|
283
|
-
Input,
|
|
284
|
-
{
|
|
285
|
-
value: form.smtpUser,
|
|
286
|
-
onChange: (event) => update("smtpUser", event.target.value),
|
|
287
|
-
"aria-invalid": Boolean(fieldErrors.smtpUser)
|
|
288
|
-
}
|
|
289
|
-
)
|
|
290
|
-
}
|
|
291
|
-
)
|
|
292
|
-
] }),
|
|
293
|
-
/* @__PURE__ */ jsx(
|
|
294
|
-
Field,
|
|
295
|
-
{
|
|
296
|
-
label: t("communication_channels.profile.connect.fields.smtpPassword", "SMTP password"),
|
|
297
|
-
error: fieldErrors.smtpPassword,
|
|
298
|
-
children: /* @__PURE__ */ jsx(
|
|
299
|
-
PasswordInput,
|
|
300
|
-
{
|
|
301
|
-
value: form.smtpPassword,
|
|
302
|
-
onChange: (event) => update("smtpPassword", event.target.value),
|
|
303
|
-
"aria-invalid": Boolean(fieldErrors.smtpPassword)
|
|
304
|
-
}
|
|
305
|
-
)
|
|
306
|
-
}
|
|
307
|
-
)
|
|
308
|
-
] }),
|
|
309
|
-
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
310
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => setOpen(false), disabled: pending, children: t("communication_channels.profile.connect.cancel", "Cancel") }),
|
|
311
|
-
/* @__PURE__ */ jsx(Button, { type: "button", onClick: () => void submit(), disabled: pending, children: pending ? t("communication_channels.profile.connect.connecting", "Connecting...") : t("communication_channels.profile.connect.save", "Connect") })
|
|
312
|
-
] })
|
|
163
|
+
open ? /* @__PURE__ */ jsx(
|
|
164
|
+
CrudForm,
|
|
165
|
+
{
|
|
166
|
+
formId: FORM_ID,
|
|
167
|
+
schema,
|
|
168
|
+
fields,
|
|
169
|
+
initialValues: IMAP_CONNECT_INITIAL_VALUES,
|
|
170
|
+
submitLabel: t("communication_channels.profile.connect.save", "Connect"),
|
|
171
|
+
onSubmit: handleSubmit,
|
|
172
|
+
embedded: true
|
|
173
|
+
}
|
|
174
|
+
) : null
|
|
313
175
|
] }) })
|
|
314
176
|
] });
|
|
315
177
|
}
|
|
316
|
-
function Field(props) {
|
|
317
|
-
return /* @__PURE__ */ jsxs("label", { className: "grid gap-1.5", children: [
|
|
318
|
-
/* @__PURE__ */ jsx(Label, { asChild: true, children: /* @__PURE__ */ jsx("span", { children: props.label }) }),
|
|
319
|
-
props.children,
|
|
320
|
-
props.error ? /* @__PURE__ */ jsx("span", { className: "text-xs text-destructive", children: props.error }) : null
|
|
321
|
-
] });
|
|
322
|
-
}
|
|
323
|
-
function TlsSelect(props) {
|
|
324
|
-
const t = useT();
|
|
325
|
-
return /* @__PURE__ */ jsxs(Select, { value: props.value, onValueChange: (value) => props.onChange(value), children: [
|
|
326
|
-
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
327
|
-
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
328
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "tls", children: t("communication_channels.profile.connect.tls.tls", "TLS") }),
|
|
329
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "starttls", children: t("communication_channels.profile.connect.tls.starttls", "STARTTLS") }),
|
|
330
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "none", children: t("communication_channels.profile.connect.tls.none", "None") })
|
|
331
|
-
] })
|
|
332
|
-
] });
|
|
333
|
-
}
|
|
334
178
|
export {
|
|
335
179
|
ConnectImapWidget as default
|
|
336
180
|
};
|