@nocobase/portal-template-default 1.0.15 → 1.0.17

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.
Files changed (153) hide show
  1. package/AGENTS.md +8 -0
  2. package/README.MD +4 -0
  3. package/dist/assets/{LineDraw-iG-ADes-.js → LineDraw-BBB5hwuJ.js} +1 -1
  4. package/dist/assets/{SymbolDraw-DXoSw1tM.js → SymbolDraw-CKS3uUb_.js} +1 -1
  5. package/dist/assets/{VisualMapping-C_ee4Eht.js → VisualMapping-BCwaW3TD.js} +1 -1
  6. package/dist/assets/auth-method-demo-BdCCvEEz.js +1 -0
  7. package/dist/assets/{auto-login-provider-5Uxza9Ms.js → auto-login-provider-DO5Il6Dp.js} +2 -2
  8. package/dist/assets/auto-login-provider-TQ08B6-i.js +1 -0
  9. package/dist/assets/auto-redirect-provider-ButXp0A3.js +1 -0
  10. package/dist/assets/auto-redirect-provider-DnGGP2Vn.js +1 -0
  11. package/dist/assets/auto-redirect-provider-ja-vFPaF.js +1 -0
  12. package/dist/assets/calendar-C7xnRgOT.js +1 -0
  13. package/dist/assets/{cas-sign-in-button-CyqG1QME.js → cas-sign-in-button-BZCa4lR6.js} +1 -1
  14. package/dist/assets/{components-D-ptYjX_.js → components-_g-GjQeV.js} +3 -3
  15. package/dist/assets/{configuration-gate-C08mkkiX.js → configuration-gate-B4XVz7tS.js} +1 -1
  16. package/dist/assets/{createClipPathFromCoordSys-wj34-b1B.js → createClipPathFromCoordSys-z9Kmh-tu.js} +1 -1
  17. package/dist/assets/createSeriesDataSimply-CPqysF0K.js +1 -0
  18. package/dist/assets/{customGraphicKeyframeAnimation-DWrhrHL_.js → customGraphicKeyframeAnimation-DMU5crAu.js} +1 -1
  19. package/dist/assets/demo-BPlae2bp.js +1 -0
  20. package/dist/assets/demo-Bj_Y0RrW.js +1 -0
  21. package/dist/assets/demo-CKPAQOib.js +1 -0
  22. package/dist/assets/demo-CXRTPvIx.js +1 -0
  23. package/dist/assets/demo-DJuVDu1n.js +1 -0
  24. package/dist/assets/demo-DUmWU7ik.js +1 -0
  25. package/dist/assets/demo-NhIzFRE1.js +1 -0
  26. package/dist/assets/{dingtalk-sign-in-button-DSWsSruv.js → dingtalk-sign-in-button-DMPgE5kT.js} +1 -1
  27. package/dist/assets/{echarts-preview-DxHNfq1C.js → echarts-preview-R96Pbqmz.js} +1 -1
  28. package/dist/assets/{echarts-runtime-D8kXn6ME.js → echarts-runtime-Ce_z2eX_.js} +3 -3
  29. package/dist/assets/{echarts-runtime-advanced-QY8v95qK.js → echarts-runtime-advanced-Dr80KHJR.js} +1 -1
  30. package/dist/assets/{echarts-runtime-common-D-yqAK-r.js → echarts-runtime-common-DB95Yfc1.js} +1 -1
  31. package/dist/assets/{echarts-runtime-components-P4-i8jPU.js → echarts-runtime-components-DCJEaTJk.js} +1 -1
  32. package/dist/assets/{echarts-runtime-hierarchy-ByQZ-DbP.js → echarts-runtime-hierarchy-CezgZs9O.js} +1 -1
  33. package/dist/assets/{floating-BFVEyPEp.js → floating-O9WmiDXk.js} +2 -2
  34. package/dist/assets/index-BBKV6O5d.css +1 -0
  35. package/dist/assets/{index-Btk35Nx-.js → index-CPzehPGe.js} +3 -3
  36. package/dist/assets/index-Cc6awvdg.js +1064 -0
  37. package/dist/assets/index-ChA0z9ke.js +38 -0
  38. package/dist/assets/{index-um5hdKdZ.js → index-DgB7uTMs.js} +2 -2
  39. package/dist/assets/{index-CwNxd66J.js → index-DroUAK80.js} +1 -1
  40. package/dist/assets/index-WKvygTBp.js +59 -0
  41. package/dist/assets/{labelHelper-DQqRy3a_.js → labelHelper-Dk5jkiD-.js} +1 -1
  42. package/dist/assets/ldap-sign-in-form-CKN4l7qM.js +1 -0
  43. package/dist/assets/{oidc-sign-in-button-CQdRcR_p.js → oidc-sign-in-button-BIRonrpC.js} +1 -1
  44. package/dist/assets/{page-context-CnQ4GYM7.js → page-context-DAiW_dId.js} +3 -3
  45. package/dist/assets/prompt-card-rGZqpqkF.js +1 -0
  46. package/dist/assets/prompt-output-B4h3uLWf.js +1 -0
  47. package/dist/assets/saml-sign-in-button-B4ugaLIb.js +1 -0
  48. package/dist/assets/{sectorHelper-DN_Vv6OL.js → sectorHelper-BuxrrJrA.js} +1 -1
  49. package/dist/assets/shortcut-BhKXxJLs.js +47 -0
  50. package/dist/assets/{sms-sign-in-form-DTX9GV2I.js → sms-sign-in-form-DKpxRDdZ.js} +1 -1
  51. package/dist/assets/{static.browser-jI1psK0d.js → static.browser-kzzpH0Xt.js} +8 -8
  52. package/dist/assets/switch-BdVxPIVU.js +1 -0
  53. package/dist/assets/{tool-cards-DFttLAfo.js → tool-cards-CpczIke-.js} +3 -3
  54. package/dist/assets/{use-authenticator-sign-in-CRGjzXju.js → use-authenticator-sign-in-CIj210ry.js} +1 -1
  55. package/dist/assets/use-dingtalk-sign-in-BW4kkXD3.js +1 -0
  56. package/dist/assets/{use-wecom-sign-in-C9j8wfCD.js → use-wecom-sign-in-dIqMtcN9.js} +1 -1
  57. package/dist/assets/wecom-sign-in-button-Dn_2zDab.js +1 -0
  58. package/dist/index.html +2 -2
  59. package/dist/index.raw.html +2 -2
  60. package/package.json +9 -1
  61. package/src/App.tsx +5 -2
  62. package/src/app/extension.ts +2 -0
  63. package/src/app/extensions.tsx +18 -6
  64. package/src/app/route-access-guard.tsx +22 -0
  65. package/src/app/route-runtime.ts +173 -0
  66. package/src/app/routes.tsx +2 -2
  67. package/src/components/app-shell/header.tsx +56 -1
  68. package/src/components/app-shell/sidebar.tsx +14 -2
  69. package/src/components/auth/sign-up-form.tsx +287 -59
  70. package/src/components/auth/types.ts +23 -1
  71. package/src/extensions/nocobase-acl/README.md +28 -14
  72. package/src/extensions/nocobase-acl/demo/index.tsx +87 -15
  73. package/src/extensions/nocobase-acl/demo/prompt-generator.tsx +1 -0
  74. package/src/extensions/nocobase-mail/README.md +74 -0
  75. package/src/extensions/nocobase-mail/components/index.ts +79 -0
  76. package/src/extensions/nocobase-mail/components/mail-api.ts +423 -0
  77. package/src/extensions/nocobase-mail/components/mail-attachment-list.tsx +67 -0
  78. package/src/extensions/nocobase-mail/components/mail-compose-attachments.tsx +166 -0
  79. package/src/extensions/nocobase-mail/components/mail-compose.tsx +1071 -0
  80. package/src/extensions/nocobase-mail/components/mail-detail.tsx +653 -0
  81. package/src/extensions/nocobase-mail/components/mail-draft-autosave.ts +26 -0
  82. package/src/extensions/nocobase-mail/components/mail-empty.tsx +48 -0
  83. package/src/extensions/nocobase-mail/components/mail-filters.tsx +277 -0
  84. package/src/extensions/nocobase-mail/components/mail-inbox.tsx +544 -0
  85. package/src/extensions/nocobase-mail/components/mail-inline-images.ts +69 -0
  86. package/src/extensions/nocobase-mail/components/mail-label-badge.tsx +25 -0
  87. package/src/extensions/nocobase-mail/components/mail-label-manager.tsx +271 -0
  88. package/src/extensions/nocobase-mail/components/mail-labels-editor.tsx +267 -0
  89. package/src/extensions/nocobase-mail/components/mail-mass-tracking.tsx +271 -0
  90. package/src/extensions/nocobase-mail/components/mail-note-editor.tsx +99 -0
  91. package/src/extensions/nocobase-mail/components/mail-recipient-input.tsx +260 -0
  92. package/src/extensions/nocobase-mail/components/mail-reply-quote.ts +28 -0
  93. package/src/extensions/nocobase-mail/components/mail-rich-editor.tsx +287 -0
  94. package/src/extensions/nocobase-mail/components/mail-send-actions.tsx +238 -0
  95. package/src/extensions/nocobase-mail/components/mail-senders.ts +61 -0
  96. package/src/extensions/nocobase-mail/components/mail-settings-drawer.tsx +708 -0
  97. package/src/extensions/nocobase-mail/components/mail-showcase-layout.tsx +64 -0
  98. package/src/extensions/nocobase-mail/components/mail-signature-manager.tsx +258 -0
  99. package/src/extensions/nocobase-mail/components/mail-signatures.ts +25 -0
  100. package/src/extensions/nocobase-mail/components/mail-table.tsx +476 -0
  101. package/src/extensions/nocobase-mail/components/mail-template-manager.tsx +204 -0
  102. package/src/extensions/nocobase-mail/components/mail-toolbar.tsx +155 -0
  103. package/src/extensions/nocobase-mail/components/mail-unread.tsx +111 -0
  104. package/src/extensions/nocobase-mail/components/types.ts +297 -0
  105. package/src/extensions/nocobase-mail/components/use-mail-compose.tsx +262 -0
  106. package/src/extensions/nocobase-mail/components/use-mail-mass-messages.ts +54 -0
  107. package/src/extensions/nocobase-mail/components/use-mail-messages.ts +113 -0
  108. package/src/extensions/nocobase-mail/components/use-mail-signatures.ts +122 -0
  109. package/src/extensions/nocobase-mail/components/use-mail-templates.ts +54 -0
  110. package/src/extensions/nocobase-mail/extension.tsx +105 -0
  111. package/src/extensions/nocobase-mail/index.ts +1 -0
  112. package/src/extensions/nocobase-mail/mail-demo-pages.tsx +503 -0
  113. package/src/extensions/nocobase-mail/mail-pages.tsx +201 -0
  114. package/src/extensions/nocobase-mail/tests/mail-quote-html-regression.mjs +51 -0
  115. package/src/extensions/nocobase-mail/tests/mail-regression.mjs +649 -0
  116. package/src/extensions/nocobase-users-example/app-routes.tsx +121 -0
  117. package/src/extensions/nocobase-users-example/extension.tsx +2 -100
  118. package/src/extensions/nocobase-users-example/index.ts +1 -0
  119. package/src/lib/nocobase/acl/menu.ts +17 -3
  120. package/src/lib/nocobase/acl/types.ts +4 -0
  121. package/src/locales/en-US.ts +2 -1
  122. package/src/locales/zh-CN.ts +2 -1
  123. package/src/providers/runtime-config.ts +18 -0
  124. package/src/routes.tsx +10 -0
  125. package/src/vite-env.d.ts +3 -0
  126. package/vite.config.ts +8 -0
  127. package/dist/assets/auth-method-demo-DaOCuvsq.js +0 -1
  128. package/dist/assets/auto-login-provider-DyaPGK2D.js +0 -1
  129. package/dist/assets/auto-redirect-provider-CfwRvYOI.js +0 -1
  130. package/dist/assets/auto-redirect-provider-Cr3jmYts.js +0 -1
  131. package/dist/assets/auto-redirect-provider-QkJQRBbG.js +0 -1
  132. package/dist/assets/calendar-DjDRfD5t.js +0 -1
  133. package/dist/assets/card-DuQBOS6A.js +0 -1
  134. package/dist/assets/createSeriesDataSimply-D0A6SAVQ.js +0 -1
  135. package/dist/assets/demo-BYWHeKUe.js +0 -1
  136. package/dist/assets/demo-CPWoNtx8.js +0 -1
  137. package/dist/assets/demo-CyvJk5zV.js +0 -1
  138. package/dist/assets/demo-D4OiKplc.js +0 -1
  139. package/dist/assets/demo-DBueJg0m.js +0 -1
  140. package/dist/assets/demo-DkIhyiRK.js +0 -1
  141. package/dist/assets/demo-tpTjO0sC.js +0 -1
  142. package/dist/assets/index-BgCbGDMc.js +0 -43
  143. package/dist/assets/index-MB-jfQld.js +0 -686
  144. package/dist/assets/index-NH3aqqQz.css +0 -1
  145. package/dist/assets/index-gFtCXH3o.js +0 -58
  146. package/dist/assets/ldap-sign-in-form--6Gt3yGq.js +0 -1
  147. package/dist/assets/prompt-card-B2RS4nt2.js +0 -1
  148. package/dist/assets/prompt-output-DZHRM2ft.js +0 -1
  149. package/dist/assets/saml-sign-in-button-BHL5a0JV.js +0 -1
  150. package/dist/assets/shortcut-C5x5LQWU.js +0 -47
  151. package/dist/assets/switch-CRL8PmRI.js +0 -1
  152. package/dist/assets/use-dingtalk-sign-in-8WYzJ2jf.js +0 -1
  153. package/dist/assets/wecom-sign-in-button-mIS1zGSU.js +0 -1
@@ -0,0 +1,155 @@
1
+ import {
2
+ MailOpen,
3
+ MailX,
4
+ RefreshCw,
5
+ Search,
6
+ Trash2,
7
+ Undo2,
8
+ X,
9
+ } from "lucide-react";
10
+ import type { ReactNode } from "react";
11
+ import { Button } from "@/components/ui/button";
12
+ import { Input } from "@/components/ui/input";
13
+ import { cn } from "@/lib/utils";
14
+
15
+ export function MailToolbar({
16
+ search,
17
+ onSearchChange,
18
+ selectedCount,
19
+ syncing,
20
+ onSync,
21
+ onMarkRead,
22
+ onMarkUnread,
23
+ actionMode = "normal",
24
+ onRestore,
25
+ onDeleteForever,
26
+ onTrash,
27
+ onClearSelection,
28
+ actions,
29
+ className,
30
+ }: {
31
+ search: string;
32
+ onSearchChange: (value: string) => void;
33
+ selectedCount: number;
34
+ syncing: boolean;
35
+ onSync: () => void;
36
+ onMarkRead: () => void;
37
+ onMarkUnread: () => void;
38
+ actionMode?:
39
+ | "normal"
40
+ | "trash"
41
+ | "draft"
42
+ | "providerDraft"
43
+ | "scheduled"
44
+ | "mixed";
45
+ onRestore: () => void;
46
+ onDeleteForever: () => void;
47
+ onTrash: () => void;
48
+ onClearSelection: () => void;
49
+ actions?: ReactNode;
50
+ className?: string;
51
+ }) {
52
+ const hasSelection = selectedCount > 0;
53
+
54
+ return (
55
+ <div className={cn("flex items-center gap-2", className)}>
56
+ <div className="relative w-full max-w-sm">
57
+ <Search className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" />
58
+ <Input
59
+ value={search}
60
+ onChange={(e) => onSearchChange(e.target.value)}
61
+ placeholder="Search messages…"
62
+ className="h-8 pl-8 text-sm"
63
+ />
64
+ {search && (
65
+ <button
66
+ type="button"
67
+ onClick={() => onSearchChange("")}
68
+ className="absolute top-1/2 right-2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
69
+ >
70
+ <X className="size-3.5" />
71
+ </button>
72
+ )}
73
+ </div>
74
+
75
+ {hasSelection && (
76
+ <div className="flex items-center gap-1">
77
+ <span className="mr-1 text-xs tabular-nums text-muted-foreground">
78
+ {selectedCount} selected
79
+ </span>
80
+ {!['draft', 'providerDraft', 'scheduled', 'mixed'].includes(actionMode) && (
81
+ <>
82
+ <Button variant="ghost" size="icon-sm" title="Mark as read" onClick={onMarkRead}>
83
+ <MailOpen />
84
+ </Button>
85
+ <Button variant="ghost" size="icon-sm" title="Mark as unread" onClick={onMarkUnread}>
86
+ <MailX />
87
+ </Button>
88
+ </>
89
+ )}
90
+ {actionMode === "trash" ? (
91
+ <>
92
+ <Button variant="ghost" size="icon-sm" title="Put back" onClick={onRestore}>
93
+ <Undo2 />
94
+ </Button>
95
+ <Button
96
+ variant="ghost"
97
+ size="icon-sm"
98
+ title="Permanently delete"
99
+ className="text-destructive hover:text-destructive"
100
+ onClick={onDeleteForever}
101
+ >
102
+ <Trash2 />
103
+ </Button>
104
+ </>
105
+ ) : actionMode === "draft" ? (
106
+ <Button
107
+ variant="ghost"
108
+ size="icon-sm"
109
+ title="Delete drafts"
110
+ className="text-destructive hover:text-destructive"
111
+ onClick={onDeleteForever}
112
+ >
113
+ <Trash2 />
114
+ </Button>
115
+ ) : actionMode === "normal" ? (
116
+ <Button
117
+ variant="ghost"
118
+ size="icon-sm"
119
+ title="Move to trash"
120
+ className="text-destructive hover:text-destructive"
121
+ onClick={onTrash}
122
+ >
123
+ <Trash2 />
124
+ </Button>
125
+ ) : (
126
+ <span className="text-xs text-muted-foreground">
127
+ {actionMode === "scheduled"
128
+ ? "Open a scheduled message to cancel it"
129
+ : actionMode === "providerDraft"
130
+ ? "Edit provider drafts in the original mailbox"
131
+ : "Select messages from one folder state"}
132
+ </span>
133
+ )}
134
+ <Button variant="ghost" size="xs" onClick={onClearSelection}>
135
+ Clear
136
+ </Button>
137
+ </div>
138
+ )}
139
+
140
+ <div className="ml-auto flex items-center gap-2">
141
+ <Button
142
+ variant="outline"
143
+ size="sm"
144
+ title="Sync mailbox"
145
+ onClick={onSync}
146
+ disabled={syncing}
147
+ >
148
+ <RefreshCw className={cn(syncing && "animate-spin")} />
149
+ Refresh
150
+ </Button>
151
+ {actions}
152
+ </div>
153
+ </div>
154
+ );
155
+ }
@@ -0,0 +1,111 @@
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ type PropsWithChildren,
10
+ type ReactNode,
11
+ } from "react";
12
+ import { Mail } from "lucide-react";
13
+ import { mailApi } from "./mail-api";
14
+ import { cn } from "@/lib/utils";
15
+
16
+ interface MailUnreadContextValue {
17
+ count: number;
18
+ refresh: () => void;
19
+ }
20
+
21
+ const MailUnreadContext = createContext<MailUnreadContextValue>({
22
+ count: 0,
23
+ refresh: () => undefined,
24
+ });
25
+
26
+ export interface MailUnreadProviderProps extends PropsWithChildren {
27
+ pollIntervalMs?: number;
28
+ }
29
+
30
+ export function MailUnreadProvider({
31
+ children,
32
+ pollIntervalMs = 60_000,
33
+ }: MailUnreadProviderProps) {
34
+ const [count, setCount] = useState(0);
35
+ const inFlight = useRef<Promise<void> | undefined>(undefined);
36
+
37
+ const refresh = useCallback(() => {
38
+ if (inFlight.current) return;
39
+ inFlight.current = mailApi
40
+ .unreadCount()
41
+ .then(setCount)
42
+ .catch(() => undefined)
43
+ .finally(() => {
44
+ inFlight.current = undefined;
45
+ });
46
+ }, []);
47
+
48
+ useEffect(() => {
49
+ refresh();
50
+ const interval = window.setInterval(refresh, pollIntervalMs);
51
+ const handleFocus = () => refresh();
52
+ const handleVisibility = () => {
53
+ if (document.visibilityState === "visible") refresh();
54
+ };
55
+ window.addEventListener("focus", handleFocus);
56
+ document.addEventListener("visibilitychange", handleVisibility);
57
+ return () => {
58
+ window.clearInterval(interval);
59
+ window.removeEventListener("focus", handleFocus);
60
+ document.removeEventListener("visibilitychange", handleVisibility);
61
+ };
62
+ }, [pollIntervalMs, refresh]);
63
+
64
+ const value = useMemo(() => ({ count, refresh }), [count, refresh]);
65
+ return <MailUnreadContext.Provider value={value}>{children}</MailUnreadContext.Provider>;
66
+ }
67
+
68
+ export function useMailUnread() {
69
+ return useContext(MailUnreadContext);
70
+ }
71
+
72
+ export interface MailUnreadIndicatorProps {
73
+ icon?: ReactNode;
74
+ label?: string;
75
+ showZero?: boolean;
76
+ max?: number;
77
+ className?: string;
78
+ }
79
+
80
+ export function MailUnreadIndicator({
81
+ icon = <Mail />,
82
+ label,
83
+ showZero = false,
84
+ max = 99,
85
+ className,
86
+ }: MailUnreadIndicatorProps) {
87
+ const { count } = useMailUnread();
88
+ const visible = showZero || count > 0;
89
+ const displayCount = count > max ? `${max}+` : String(count);
90
+
91
+ return (
92
+ <span
93
+ className={cn("inline-flex items-center gap-2", className)}
94
+ aria-label={`${count} unread ${count === 1 ? "message" : "messages"}`}
95
+ >
96
+ <span className="relative inline-flex">
97
+ {icon}
98
+ {visible && (
99
+ <span className="absolute -top-2 -right-2 flex min-w-4 items-center justify-center rounded-full bg-destructive px-1 text-[9px] font-semibold leading-4 text-destructive-foreground ring-2 ring-sidebar">
100
+ {displayCount}
101
+ </span>
102
+ )}
103
+ </span>
104
+ {label && <span>{label}</span>}
105
+ </span>
106
+ );
107
+ }
108
+
109
+ export function MailUnreadIcon() {
110
+ return <MailUnreadIndicator />;
111
+ }
@@ -0,0 +1,297 @@
1
+ export enum MailBoxType {
2
+ IN = "in",
3
+ OUT = "out",
4
+ DRAFT = "draft",
5
+ TRASH = "trash",
6
+ SPAM = "spam",
7
+ ARCHIVE = "archive",
8
+ MASS = "mass",
9
+ SCHEDULED = "scheduled",
10
+ }
11
+
12
+ export interface MailUser {
13
+ name?: string;
14
+ address: string;
15
+ }
16
+
17
+ export interface MailAttachment {
18
+ filename: string;
19
+ mimeType: string;
20
+ attachmentId: string;
21
+ contentId?: string;
22
+ size?: number;
23
+ originalname?: string;
24
+ path?: string;
25
+ encoding?: string;
26
+ mimetype?: string;
27
+ }
28
+
29
+ export interface MailUploadedAttachment {
30
+ originalname: string;
31
+ filename: string;
32
+ path: string;
33
+ size: number | string;
34
+ encoding: string;
35
+ mimetype: string;
36
+ /** Microsoft sender compatibility; Google continues to use `mimetype`. */
37
+ mimeType?: string;
38
+ }
39
+
40
+ export interface MailLabel {
41
+ id: number;
42
+ label: string;
43
+ color: string;
44
+ description?: string;
45
+ }
46
+
47
+ export const LABEL_COLOR_OPTIONS = [
48
+ "default",
49
+ "red",
50
+ "orange",
51
+ "yellow",
52
+ "green",
53
+ "blue",
54
+ "purple",
55
+ ] as const;
56
+
57
+ export const LABEL_BADGE_CLASSES: Record<string, string> = {
58
+ default: "bg-muted text-muted-foreground",
59
+ blue: "bg-blue-500/15 text-blue-600 dark:text-blue-400",
60
+ green: "bg-green-500/15 text-green-600 dark:text-green-400",
61
+ red: "bg-red-500/15 text-red-600 dark:text-red-400",
62
+ orange: "bg-orange-500/15 text-orange-600 dark:text-orange-400",
63
+ yellow: "bg-yellow-500/15 text-yellow-600 dark:text-yellow-400",
64
+ gold: "bg-amber-500/15 text-amber-600 dark:text-amber-400",
65
+ cyan: "bg-cyan-500/15 text-cyan-600 dark:text-cyan-400",
66
+ purple: "bg-purple-500/15 text-purple-600 dark:text-purple-400",
67
+ magenta: "bg-pink-500/15 text-pink-600 dark:text-pink-400",
68
+ };
69
+
70
+ export const LABEL_SWATCH_CLASSES: Record<string, string> = {
71
+ default: "bg-muted-foreground/40",
72
+ blue: "bg-blue-500",
73
+ green: "bg-green-500",
74
+ red: "bg-red-500",
75
+ orange: "bg-orange-500",
76
+ yellow: "bg-yellow-500",
77
+ gold: "bg-amber-500",
78
+ cyan: "bg-cyan-500",
79
+ purple: "bg-purple-500",
80
+ magenta: "bg-pink-500",
81
+ };
82
+
83
+ export interface MailNote {
84
+ id: number;
85
+ note: string;
86
+ mailMessageId: number;
87
+ }
88
+
89
+ export interface MailMessage {
90
+ id: number;
91
+ email: string;
92
+ mailId: string;
93
+ rawId: string;
94
+ boxType: MailBoxType | "";
95
+ originalBoxType?: MailBoxType | "";
96
+ isRead: boolean;
97
+ isDraft: boolean;
98
+ from: string;
99
+ identityEmail?: string;
100
+ fromUser?: MailUser;
101
+ to: string;
102
+ toUsers?: MailUser[];
103
+ cc: string;
104
+ ccUsers?: MailUser[];
105
+ bcc: string;
106
+ bccUsers?: MailUser[];
107
+ subject: string;
108
+ replyTo?: string;
109
+ conversationId?: string;
110
+ relatedMessageIds?: string[];
111
+ relatedMessagesIsRead?: boolean;
112
+ relatedMessageLatestDate?: string;
113
+ date: string;
114
+ bodyText: string;
115
+ bodyHtml: string;
116
+ attachments: MailAttachment[];
117
+ labels?: MailLabel[];
118
+ isTodo?: boolean;
119
+ note?: MailNote[];
120
+ children?: MailMessage[];
121
+ childrenMessages?: MailMessage[];
122
+ scheduleSendAt?: string;
123
+ userId?: number;
124
+ user?: {
125
+ id: number;
126
+ nickname?: string;
127
+ email?: string;
128
+ };
129
+ createdAt?: string;
130
+ updatedAt?: string;
131
+ }
132
+
133
+ export function isLocalMailDraft(
134
+ message: Pick<MailMessage, "isDraft" | "mailId" | "rawId">
135
+ ) {
136
+ return Boolean(message.isDraft && !message.mailId && !message.rawId);
137
+ }
138
+
139
+ export interface MailAccount {
140
+ id: number;
141
+ type: string;
142
+ email: string;
143
+ userId: number;
144
+ settingId: number;
145
+ config?: Record<string, unknown> & {
146
+ signatures?: MailAccountSignature[];
147
+ };
148
+ identities?: MailIdentity[];
149
+ }
150
+
151
+ export interface MailIdentity {
152
+ id: number;
153
+ email: string;
154
+ accountId: number;
155
+ userId: number;
156
+ isPrimary?: boolean;
157
+ name?: string;
158
+ verificationStatus?: string;
159
+ }
160
+
161
+ export interface MailUserRecord {
162
+ id: number;
163
+ nickname?: string;
164
+ email?: string;
165
+ username?: string;
166
+ }
167
+
168
+ export interface MailRecipientOption {
169
+ email: string;
170
+ name?: string;
171
+ description?: string;
172
+ }
173
+
174
+ export interface MailAccountSignature {
175
+ id: string;
176
+ name: string;
177
+ content: string;
178
+ default?: boolean;
179
+ }
180
+
181
+ export interface MailSignature {
182
+ id: string;
183
+ name: string;
184
+ content: string;
185
+ isDefault?: boolean;
186
+ }
187
+
188
+ export interface MailTemplate {
189
+ id: number | string;
190
+ name: string;
191
+ content: string;
192
+ /** Kept for callers migrating from the old local template format. */
193
+ subject?: string;
194
+ }
195
+
196
+ export interface MailSendPayload {
197
+ id?: number;
198
+ from: string;
199
+ accountEmail?: string;
200
+ identityEmail?: string;
201
+ to: string[];
202
+ cc?: string[];
203
+ subject: string;
204
+ body: string;
205
+ attachments?: MailUploadedAttachment[];
206
+ replyTo?: string;
207
+ isDraft?: boolean;
208
+ scheduleSendAt?: string;
209
+ }
210
+
211
+ export enum MailMassMessageStatus {
212
+ PENDING = "pending",
213
+ SENDING = "sending",
214
+ SENT = "sent",
215
+ FAILED = "failed",
216
+ CANCELED = "canceled",
217
+ SOME_SENT = "some_sent",
218
+ }
219
+
220
+ export interface MailMassMessage {
221
+ id: number;
222
+ parentId?: number | null;
223
+ interval: number;
224
+ message: MailSendPayload;
225
+ from: string;
226
+ to: string;
227
+ sendAt?: string | null;
228
+ status: MailMassMessageStatus;
229
+ result?: unknown;
230
+ children?: MailMassMessage[];
231
+ createdAt?: string;
232
+ updatedAt?: string;
233
+ }
234
+
235
+ export interface MailMassListResponse {
236
+ rows: MailMassMessage[];
237
+ count: number;
238
+ }
239
+
240
+ export interface MailMassSendSettings {
241
+ /** Delay between two recipients, in milliseconds. */
242
+ interval?: number;
243
+ }
244
+
245
+ export type MailScope = "all" | "personal";
246
+
247
+ export interface MailListParams {
248
+ page?: number;
249
+ pageSize?: number;
250
+ boxType?: MailBoxType;
251
+ isRead?: boolean;
252
+ search?: string;
253
+ labelId?: number;
254
+ sort?: string;
255
+ scope?: MailScope;
256
+ userId?: number;
257
+ filter?: Record<string, unknown>;
258
+ }
259
+
260
+ export interface MailListResponse {
261
+ rows: MailMessage[];
262
+ count: number;
263
+ }
264
+
265
+ export type MailColumnId =
266
+ | "from"
267
+ | "to"
268
+ | "subject"
269
+ | "date"
270
+ | "boxType"
271
+ | "isRead"
272
+ | "email"
273
+ | "user"
274
+ | "labels"
275
+ | "attachments";
276
+
277
+ export const MAIL_COLUMN_LABELS: Record<MailColumnId, string> = {
278
+ from: "From",
279
+ to: "To",
280
+ subject: "Subject",
281
+ date: "Date",
282
+ boxType: "Folder",
283
+ isRead: "Status",
284
+ email: "Mailbox",
285
+ user: "Owner",
286
+ labels: "Labels",
287
+ attachments: "Attachments",
288
+ };
289
+
290
+ export const DEFAULT_MAIL_COLUMNS: MailColumnId[] = [
291
+ "from",
292
+ "subject",
293
+ "boxType",
294
+ "date",
295
+ "isRead",
296
+ "labels",
297
+ ];