@open-mercato/shared 0.4.5-develop-5191db4ef3 → 0.4.5-develop-033a719bf2
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/lib/email/send.js +3 -2
- package/dist/lib/email/send.js.map +2 -2
- package/dist/lib/redis/connection.js +2 -0
- package/dist/lib/redis/connection.js.map +2 -2
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/modules/messages/types.js +1 -0
- package/dist/modules/messages/types.js.map +7 -0
- package/package.json +1 -1
- package/src/lib/email/__tests__/send.test.ts +27 -0
- package/src/lib/email/send.ts +7 -1
- package/src/lib/redis/connection.ts +2 -0
- package/src/modules/messages/types.ts +169 -0
package/dist/lib/email/send.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Resend } from "resend";
|
|
2
2
|
import { parseBooleanWithDefault } from "../boolean.js";
|
|
3
|
-
async function sendEmail({ to, subject, react, from, replyTo }) {
|
|
3
|
+
async function sendEmail({ to, subject, react, from, replyTo, attachments }) {
|
|
4
4
|
const emailDisabled = parseBooleanWithDefault(process.env.OM_DISABLE_EMAIL_DELIVERY, false) || parseBooleanWithDefault(process.env.OM_TEST_MODE, false);
|
|
5
5
|
if (emailDisabled) return;
|
|
6
6
|
const apiKey = process.env.RESEND_API_KEY;
|
|
@@ -12,7 +12,8 @@ async function sendEmail({ to, subject, react, from, replyTo }) {
|
|
|
12
12
|
subject,
|
|
13
13
|
from: fromAddr,
|
|
14
14
|
react,
|
|
15
|
-
...replyTo ? { reply_to: replyTo } : {}
|
|
15
|
+
...replyTo ? { reply_to: replyTo } : {},
|
|
16
|
+
...attachments?.length ? { attachments } : {}
|
|
16
17
|
};
|
|
17
18
|
const result = await resend.emails.send(payload);
|
|
18
19
|
const errorMessage = typeof result?.error === "string" ? result.error : typeof result?.error?.message === "string" ? result.error.message : null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/email/send.ts"],
|
|
4
|
-
"sourcesContent": ["import { Resend } from 'resend'\nimport React from 'react'\nimport { parseBooleanWithDefault } from '../boolean'\n\nexport type SendEmailOptions = {\n to: string\n subject: string\n react: React.ReactElement\n from?: string\n replyTo?: string\n}\n\nexport async function sendEmail({ to, subject, react, from, replyTo }: SendEmailOptions) {\n const emailDisabled =\n parseBooleanWithDefault(process.env.OM_DISABLE_EMAIL_DELIVERY, false) ||\n parseBooleanWithDefault(process.env.OM_TEST_MODE, false)\n if (emailDisabled) return\n\n const apiKey = process.env.RESEND_API_KEY\n if (!apiKey) throw new Error('RESEND_API_KEY is not set')\n const resend = new Resend(apiKey)\n const fromAddr = from || process.env.EMAIL_FROM || 'no-reply@localhost'\n const payload = {\n to,\n subject,\n from: fromAddr,\n react,\n ...(replyTo ? { reply_to: replyTo } : {}),\n }\n const result = await resend.emails.send(payload)\n const errorMessage =\n typeof (result as any)?.error === 'string'\n ? (result as any).error\n : typeof (result as any)?.error?.message === 'string'\n ? (result as any).error.message\n : null\n if (errorMessage) {\n throw new Error(`RESEND_SEND_FAILED: ${errorMessage}`)\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,cAAc;AAEvB,SAAS,+BAA+B;
|
|
4
|
+
"sourcesContent": ["import { Resend } from 'resend'\nimport React from 'react'\nimport { parseBooleanWithDefault } from '../boolean'\n\nexport type SendEmailOptions = {\n to: string\n subject: string\n react: React.ReactElement\n from?: string\n replyTo?: string\n attachments?: Array<{\n filename: string\n content: string\n contentType?: string\n }>\n}\n\nexport async function sendEmail({ to, subject, react, from, replyTo, attachments }: SendEmailOptions) {\n const emailDisabled =\n parseBooleanWithDefault(process.env.OM_DISABLE_EMAIL_DELIVERY, false) ||\n parseBooleanWithDefault(process.env.OM_TEST_MODE, false)\n if (emailDisabled) return\n\n const apiKey = process.env.RESEND_API_KEY\n if (!apiKey) throw new Error('RESEND_API_KEY is not set')\n const resend = new Resend(apiKey)\n const fromAddr = from || process.env.EMAIL_FROM || 'no-reply@localhost'\n const payload = {\n to,\n subject,\n from: fromAddr,\n react,\n ...(replyTo ? { reply_to: replyTo } : {}),\n ...(attachments?.length ? { attachments } : {}),\n }\n const result = await resend.emails.send(payload)\n const errorMessage =\n typeof (result as any)?.error === 'string'\n ? (result as any).error\n : typeof (result as any)?.error?.message === 'string'\n ? (result as any).error.message\n : null\n if (errorMessage) {\n throw new Error(`RESEND_SEND_FAILED: ${errorMessage}`)\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,cAAc;AAEvB,SAAS,+BAA+B;AAexC,eAAsB,UAAU,EAAE,IAAI,SAAS,OAAO,MAAM,SAAS,YAAY,GAAqB;AACpG,QAAM,gBACJ,wBAAwB,QAAQ,IAAI,2BAA2B,KAAK,KACpE,wBAAwB,QAAQ,IAAI,cAAc,KAAK;AACzD,MAAI,cAAe;AAEnB,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,2BAA2B;AACxD,QAAM,SAAS,IAAI,OAAO,MAAM;AAChC,QAAM,WAAW,QAAQ,QAAQ,IAAI,cAAc;AACnD,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,GAAI,UAAU,EAAE,UAAU,QAAQ,IAAI,CAAC;AAAA,IACvC,GAAI,aAAa,SAAS,EAAE,YAAY,IAAI,CAAC;AAAA,EAC/C;AACA,QAAM,SAAS,MAAM,OAAO,OAAO,KAAK,OAAO;AAC/C,QAAM,eACJ,OAAQ,QAAgB,UAAU,WAC7B,OAAe,QAChB,OAAQ,QAAgB,OAAO,YAAY,WACxC,OAAe,MAAM,UACtB;AACR,MAAI,cAAc;AAChB,UAAM,IAAI,MAAM,uBAAuB,YAAY,EAAE;AAAA,EACvD;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -18,6 +18,8 @@ function parseRedisUrl(url) {
|
|
|
18
18
|
db
|
|
19
19
|
};
|
|
20
20
|
} catch {
|
|
21
|
+
const safeUrl = url.replace(/\/\/[^:]*:[^@]*@/, "//<redacted>@");
|
|
22
|
+
console.warn(`[redis] Failed to parse URL "${safeUrl}", falling back to localhost:6379`);
|
|
21
23
|
return { host: "localhost", port: 6379 };
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/redis/connection.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Shared Redis connection utilities.\n *\n * Every package that needs a Redis URL or parsed connection options\n * should import from here instead of reading env vars directly.\n *\n * The `prefix` parameter lets each subsystem define its own override:\n * getRedisUrl('QUEUE') \u2192 QUEUE_REDIS_URL > REDIS_URL > localhost\n * getRedisUrl('CACHE') \u2192 CACHE_REDIS_URL > REDIS_URL > localhost\n * getRedisUrl() \u2192 REDIS_URL > localhost\n */\n\nexport type ParsedRedisConnection = {\n host: string\n port: number\n password?: string\n db?: number\n}\n\n/**\n * Resolve a Redis URL from environment variables.\n *\n * Priority: <PREFIX>_REDIS_URL \u2192 REDIS_URL \u2192 redis://localhost:6379\n */\nexport function getRedisUrl(prefix?: string): string {\n if (prefix) {\n const prefixed = process.env[`${prefix}_REDIS_URL`]\n if (prefixed) return prefixed\n }\n return process.env.REDIS_URL || 'redis://localhost:6379'\n}\n\n/**\n * Parse a redis:// URL into a {host, port, password, db} object\n * suitable for BullMQ / ioredis structured connection options.\n */\nexport function parseRedisUrl(url: string): ParsedRedisConnection {\n try {\n const parsed = new URL(url)\n const dbStr = parsed.pathname ? parsed.pathname.slice(1) : ''\n const dbParsed = dbStr !== '' ? parseInt(dbStr, 10) : NaN\n const db = Number.isNaN(dbParsed) ? undefined : dbParsed\n return {\n host: parsed.hostname || 'localhost',\n port: parseInt(parsed.port, 10) || 6379,\n password: parsed.password || undefined,\n db,\n }\n } catch {\n return { host: 'localhost', port: 6379 }\n }\n}\n\n/**\n * Convenience: resolve the URL from env and parse it in one step.\n */\nexport function resolveRedisConnection(prefix?: string): ParsedRedisConnection & { url: string } {\n const url = getRedisUrl(prefix)\n return { url, ...parseRedisUrl(url) }\n}\n"],
|
|
5
|
-
"mappings": "AAwBO,SAAS,YAAY,QAAyB;AACnD,MAAI,QAAQ;AACV,UAAM,WAAW,QAAQ,IAAI,GAAG,MAAM,YAAY;AAClD,QAAI,SAAU,QAAO;AAAA,EACvB;AACA,SAAO,QAAQ,IAAI,aAAa;AAClC;AAMO,SAAS,cAAc,KAAoC;AAChE,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,UAAM,QAAQ,OAAO,WAAW,OAAO,SAAS,MAAM,CAAC,IAAI;AAC3D,UAAM,WAAW,UAAU,KAAK,SAAS,OAAO,EAAE,IAAI;AACtD,UAAM,KAAK,OAAO,MAAM,QAAQ,IAAI,SAAY;AAChD,WAAO;AAAA,MACL,MAAM,OAAO,YAAY;AAAA,MACzB,MAAM,SAAS,OAAO,MAAM,EAAE,KAAK;AAAA,MACnC,UAAU,OAAO,YAAY;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,MAAM,aAAa,MAAM,KAAK;AAAA,EACzC;AACF;AAKO,SAAS,uBAAuB,QAA0D;AAC/F,QAAM,MAAM,YAAY,MAAM;AAC9B,SAAO,EAAE,KAAK,GAAG,cAAc,GAAG,EAAE;AACtC;",
|
|
4
|
+
"sourcesContent": ["/**\n * Shared Redis connection utilities.\n *\n * Every package that needs a Redis URL or parsed connection options\n * should import from here instead of reading env vars directly.\n *\n * The `prefix` parameter lets each subsystem define its own override:\n * getRedisUrl('QUEUE') \u2192 QUEUE_REDIS_URL > REDIS_URL > localhost\n * getRedisUrl('CACHE') \u2192 CACHE_REDIS_URL > REDIS_URL > localhost\n * getRedisUrl() \u2192 REDIS_URL > localhost\n */\n\nexport type ParsedRedisConnection = {\n host: string\n port: number\n password?: string\n db?: number\n}\n\n/**\n * Resolve a Redis URL from environment variables.\n *\n * Priority: <PREFIX>_REDIS_URL \u2192 REDIS_URL \u2192 redis://localhost:6379\n */\nexport function getRedisUrl(prefix?: string): string {\n if (prefix) {\n const prefixed = process.env[`${prefix}_REDIS_URL`]\n if (prefixed) return prefixed\n }\n return process.env.REDIS_URL || 'redis://localhost:6379'\n}\n\n/**\n * Parse a redis:// URL into a {host, port, password, db} object\n * suitable for BullMQ / ioredis structured connection options.\n */\nexport function parseRedisUrl(url: string): ParsedRedisConnection {\n try {\n const parsed = new URL(url)\n const dbStr = parsed.pathname ? parsed.pathname.slice(1) : ''\n const dbParsed = dbStr !== '' ? parseInt(dbStr, 10) : NaN\n const db = Number.isNaN(dbParsed) ? undefined : dbParsed\n return {\n host: parsed.hostname || 'localhost',\n port: parseInt(parsed.port, 10) || 6379,\n password: parsed.password || undefined,\n db,\n }\n } catch {\n const safeUrl = url.replace(/\\/\\/[^:]*:[^@]*@/, '//<redacted>@')\n console.warn(`[redis] Failed to parse URL \"${safeUrl}\", falling back to localhost:6379`)\n return { host: 'localhost', port: 6379 }\n }\n}\n\n/**\n * Convenience: resolve the URL from env and parse it in one step.\n */\nexport function resolveRedisConnection(prefix?: string): ParsedRedisConnection & { url: string } {\n const url = getRedisUrl(prefix)\n return { url, ...parseRedisUrl(url) }\n}\n"],
|
|
5
|
+
"mappings": "AAwBO,SAAS,YAAY,QAAyB;AACnD,MAAI,QAAQ;AACV,UAAM,WAAW,QAAQ,IAAI,GAAG,MAAM,YAAY;AAClD,QAAI,SAAU,QAAO;AAAA,EACvB;AACA,SAAO,QAAQ,IAAI,aAAa;AAClC;AAMO,SAAS,cAAc,KAAoC;AAChE,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,UAAM,QAAQ,OAAO,WAAW,OAAO,SAAS,MAAM,CAAC,IAAI;AAC3D,UAAM,WAAW,UAAU,KAAK,SAAS,OAAO,EAAE,IAAI;AACtD,UAAM,KAAK,OAAO,MAAM,QAAQ,IAAI,SAAY;AAChD,WAAO;AAAA,MACL,MAAM,OAAO,YAAY;AAAA,MACzB,MAAM,SAAS,OAAO,MAAM,EAAE,KAAK;AAAA,MACnC,UAAU,OAAO,YAAY;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,QAAQ;AACN,UAAM,UAAU,IAAI,QAAQ,oBAAoB,eAAe;AAC/D,YAAQ,KAAK,gCAAgC,OAAO,mCAAmC;AACvF,WAAO,EAAE,MAAM,aAAa,MAAM,KAAK;AAAA,EACzC;AACF;AAKO,SAAS,uBAAuB,QAA0D;AAC/F,QAAM,MAAM,YAAY,MAAM;AAC9B,SAAO,EAAE,KAAK,GAAG,cAAc,GAAG,EAAE;AACtC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/version.js
CHANGED
package/dist/lib/version.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/version.ts"],
|
|
4
|
-
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.5-develop-
|
|
4
|
+
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.5-develop-033a719bf2'\nexport const appVersion = APP_VERSION\n"],
|
|
5
5
|
"mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
package/package.json
CHANGED
|
@@ -61,6 +61,33 @@ describe('sendEmail', () => {
|
|
|
61
61
|
expect(payload.reply_to).toBeUndefined()
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
+
it('passes attachments to Resend payload when provided', async () => {
|
|
65
|
+
await sendEmail({
|
|
66
|
+
to: 'user@example.com',
|
|
67
|
+
subject: 'Hello',
|
|
68
|
+
react: React.createElement('div', null, 'Hi'),
|
|
69
|
+
attachments: [
|
|
70
|
+
{
|
|
71
|
+
filename: 'invoice.pdf',
|
|
72
|
+
content: 'dGVzdA==',
|
|
73
|
+
contentType: 'application/pdf',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
expect(sendMock).toHaveBeenCalledWith(
|
|
79
|
+
expect.objectContaining({
|
|
80
|
+
attachments: [
|
|
81
|
+
{
|
|
82
|
+
filename: 'invoice.pdf',
|
|
83
|
+
content: 'dGVzdA==',
|
|
84
|
+
contentType: 'application/pdf',
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
})
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
|
|
64
91
|
it('throws when Resend returns an error', async () => {
|
|
65
92
|
sendMock.mockResolvedValueOnce({ error: { message: 'invalid domain' } })
|
|
66
93
|
|
package/src/lib/email/send.ts
CHANGED
|
@@ -8,9 +8,14 @@ export type SendEmailOptions = {
|
|
|
8
8
|
react: React.ReactElement
|
|
9
9
|
from?: string
|
|
10
10
|
replyTo?: string
|
|
11
|
+
attachments?: Array<{
|
|
12
|
+
filename: string
|
|
13
|
+
content: string
|
|
14
|
+
contentType?: string
|
|
15
|
+
}>
|
|
11
16
|
}
|
|
12
17
|
|
|
13
|
-
export async function sendEmail({ to, subject, react, from, replyTo }: SendEmailOptions) {
|
|
18
|
+
export async function sendEmail({ to, subject, react, from, replyTo, attachments }: SendEmailOptions) {
|
|
14
19
|
const emailDisabled =
|
|
15
20
|
parseBooleanWithDefault(process.env.OM_DISABLE_EMAIL_DELIVERY, false) ||
|
|
16
21
|
parseBooleanWithDefault(process.env.OM_TEST_MODE, false)
|
|
@@ -26,6 +31,7 @@ export async function sendEmail({ to, subject, react, from, replyTo }: SendEmail
|
|
|
26
31
|
from: fromAddr,
|
|
27
32
|
react,
|
|
28
33
|
...(replyTo ? { reply_to: replyTo } : {}),
|
|
34
|
+
...(attachments?.length ? { attachments } : {}),
|
|
29
35
|
}
|
|
30
36
|
const result = await resend.emails.send(payload)
|
|
31
37
|
const errorMessage =
|
|
@@ -47,6 +47,8 @@ export function parseRedisUrl(url: string): ParsedRedisConnection {
|
|
|
47
47
|
db,
|
|
48
48
|
}
|
|
49
49
|
} catch {
|
|
50
|
+
const safeUrl = url.replace(/\/\/[^:]*:[^@]*@/, '//<redacted>@')
|
|
51
|
+
console.warn(`[redis] Failed to parse URL "${safeUrl}", falling back to localhost:6379`)
|
|
50
52
|
return { host: 'localhost', port: 6379 }
|
|
51
53
|
}
|
|
52
54
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { ComponentType } from 'react'
|
|
2
|
+
|
|
3
|
+
export type MessageAction = {
|
|
4
|
+
id: string
|
|
5
|
+
label: string
|
|
6
|
+
labelKey?: string
|
|
7
|
+
variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost'
|
|
8
|
+
icon?: string
|
|
9
|
+
commandId?: string
|
|
10
|
+
href?: string
|
|
11
|
+
isTerminal?: boolean
|
|
12
|
+
confirmRequired?: boolean
|
|
13
|
+
confirmMessage?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type MessageActionData = {
|
|
17
|
+
actions: MessageAction[]
|
|
18
|
+
primaryActionId?: string
|
|
19
|
+
expiresAt?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type MessageListItemProps = {
|
|
23
|
+
message: {
|
|
24
|
+
id: string
|
|
25
|
+
type: string
|
|
26
|
+
typeLabel?: string
|
|
27
|
+
subject: string
|
|
28
|
+
body: string
|
|
29
|
+
bodyFormat: 'text' | 'markdown'
|
|
30
|
+
priority: 'low' | 'normal' | 'high' | 'urgent'
|
|
31
|
+
sentAt: Date | null
|
|
32
|
+
senderName?: string
|
|
33
|
+
senderAvatar?: string
|
|
34
|
+
hasObjects: boolean
|
|
35
|
+
objectCount?: number
|
|
36
|
+
hasAttachments: boolean
|
|
37
|
+
attachmentCount?: number
|
|
38
|
+
recipientCount?: number
|
|
39
|
+
hasActions: boolean
|
|
40
|
+
actionTaken?: string | null
|
|
41
|
+
unread: boolean
|
|
42
|
+
}
|
|
43
|
+
onClick: () => void
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type MessageContentProps = {
|
|
47
|
+
message: {
|
|
48
|
+
id: string
|
|
49
|
+
type: string
|
|
50
|
+
subject: string
|
|
51
|
+
body: string
|
|
52
|
+
bodyFormat: 'text' | 'markdown'
|
|
53
|
+
priority: 'low' | 'normal' | 'high' | 'urgent'
|
|
54
|
+
sentAt: Date | null
|
|
55
|
+
senderName?: string
|
|
56
|
+
senderAvatar?: string
|
|
57
|
+
senderUserId: string
|
|
58
|
+
actionData?: MessageActionData | null
|
|
59
|
+
actionTaken?: string | null
|
|
60
|
+
actionTakenAt?: Date | null
|
|
61
|
+
actionTakenByName?: string
|
|
62
|
+
}
|
|
63
|
+
objects: Array<{
|
|
64
|
+
id: string
|
|
65
|
+
entityModule: string
|
|
66
|
+
entityType: string
|
|
67
|
+
entityId: string
|
|
68
|
+
actionRequired: boolean
|
|
69
|
+
snapshot?: Record<string, unknown>
|
|
70
|
+
}>
|
|
71
|
+
attachments: Array<{
|
|
72
|
+
id: string
|
|
73
|
+
fileName: string
|
|
74
|
+
fileSize: number
|
|
75
|
+
mimeType: string
|
|
76
|
+
url: string
|
|
77
|
+
}>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type MessageActionsProps = {
|
|
81
|
+
message: {
|
|
82
|
+
id: string
|
|
83
|
+
type: string
|
|
84
|
+
actionData?: MessageActionData | null
|
|
85
|
+
actionTaken?: string | null
|
|
86
|
+
}
|
|
87
|
+
onExecuteAction: (actionId: string, payload?: Record<string, unknown>) => Promise<void>
|
|
88
|
+
isExecuting: boolean
|
|
89
|
+
executingActionId?: string | null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type MessageTypeDefinition = {
|
|
93
|
+
type: string
|
|
94
|
+
module: string
|
|
95
|
+
labelKey: string
|
|
96
|
+
icon: string
|
|
97
|
+
color?: string
|
|
98
|
+
ui?: {
|
|
99
|
+
listItemComponent?: string
|
|
100
|
+
contentComponent?: string
|
|
101
|
+
actionsComponent?: string
|
|
102
|
+
}
|
|
103
|
+
ListItemComponent?: ComponentType<MessageListItemProps>
|
|
104
|
+
ContentComponent?: ComponentType<MessageContentProps>
|
|
105
|
+
ActionsComponent?: ComponentType<MessageActionsProps>
|
|
106
|
+
defaultActions?: MessageAction[]
|
|
107
|
+
allowReply?: boolean
|
|
108
|
+
allowForward?: boolean
|
|
109
|
+
actionsExpireAfterHours?: number
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type MessageObjectAction = {
|
|
113
|
+
id: string
|
|
114
|
+
labelKey: string
|
|
115
|
+
variant?: 'default' | 'secondary' | 'destructive' | 'outline'
|
|
116
|
+
icon?: string
|
|
117
|
+
commandId?: string
|
|
118
|
+
href?: string
|
|
119
|
+
isTerminal?: boolean
|
|
120
|
+
confirmRequired?: boolean
|
|
121
|
+
confirmMessage?: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type ObjectPreviewProps = {
|
|
125
|
+
entityId: string
|
|
126
|
+
entityModule: string
|
|
127
|
+
entityType: string
|
|
128
|
+
snapshot?: Record<string, unknown>
|
|
129
|
+
previewData?: ObjectPreviewData
|
|
130
|
+
actionRequired?: boolean
|
|
131
|
+
actionType?: string
|
|
132
|
+
actionLabel?: string
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export type ObjectDetailProps = ObjectPreviewProps & {
|
|
136
|
+
onAction: (actionId: string, payload?: Record<string, unknown>) => Promise<void>
|
|
137
|
+
actions: MessageObjectAction[]
|
|
138
|
+
actionTaken?: string | null
|
|
139
|
+
actionTakenAt?: Date | null
|
|
140
|
+
actionTakenByUserId?: string | null
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export type ObjectPreviewData = {
|
|
144
|
+
title: string
|
|
145
|
+
subtitle?: string
|
|
146
|
+
status?: string
|
|
147
|
+
statusColor?: string
|
|
148
|
+
metadata?: Record<string, string>
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type LoadContext = {
|
|
152
|
+
tenantId: string
|
|
153
|
+
organizationId?: string | null
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export type MessageObjectTypeDefinition = {
|
|
157
|
+
module: string
|
|
158
|
+
entityType: string
|
|
159
|
+
labelKey: string
|
|
160
|
+
icon: string
|
|
161
|
+
messageTypes?: string[]
|
|
162
|
+
entityId?: string
|
|
163
|
+
optionLabelField?: string
|
|
164
|
+
optionSubtitleField?: string
|
|
165
|
+
PreviewComponent?: ComponentType<ObjectPreviewProps>
|
|
166
|
+
DetailComponent?: ComponentType<ObjectDetailProps>
|
|
167
|
+
actions: MessageObjectAction[]
|
|
168
|
+
loadPreview?: (entityId: string, ctx: LoadContext) => Promise<ObjectPreviewData>
|
|
169
|
+
}
|