@tuturuuu/ui 0.2.0 → 0.3.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/package.json +79 -67
  3. package/src/components/ui/__tests__/avatar.test.tsx +8 -5
  4. package/src/components/ui/calendar-app/components/calendar-connections-compact.tsx +414 -0
  5. package/src/components/ui/calendar-app/components/calendar-connections-manager.tsx +5 -1
  6. package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +529 -0
  7. package/src/components/ui/calendar-app/components/calendar-connections-unified.tsx +26 -1429
  8. package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +711 -0
  9. package/src/components/ui/chart.test.tsx +29 -0
  10. package/src/components/ui/chart.tsx +12 -3
  11. package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +396 -2
  12. package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +36 -8
  13. package/src/components/ui/chat/chat-agent-details-setup-panel.tsx +14 -0
  14. package/src/components/ui/chat/chat-agent-details-sidebar.test.tsx +5 -0
  15. package/src/components/ui/chat/chat-agent-details-sidebar.tsx +21 -7
  16. package/src/components/ui/chat/chat-agent-details-utils.test.ts +73 -0
  17. package/src/components/ui/chat/chat-agent-details-utils.tsx +100 -26
  18. package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +517 -0
  19. package/src/components/ui/chat/chat-workspace.tsx +31 -1
  20. package/src/components/ui/chat/hooks-messages.test.tsx +45 -1
  21. package/src/components/ui/chat/hooks-messages.ts +1 -1
  22. package/src/components/ui/chat/hooks-realtime.ts +13 -16
  23. package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +24 -1
  24. package/src/components/ui/custom/__tests__/tuturuuu-logo.test.ts +12 -3
  25. package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +39 -0
  26. package/src/components/ui/custom/common-footer.tsx +16 -1
  27. package/src/components/ui/custom/production-indicator.tsx +1 -1
  28. package/src/components/ui/custom/settings/sidebar-settings.tsx +1 -1
  29. package/src/components/ui/custom/settings/task-settings.tsx +18 -0
  30. package/src/components/ui/custom/settings-dialog-shell.tsx +38 -23
  31. package/src/components/ui/custom/sidebar-context-compile-graph.test.ts +60 -0
  32. package/src/components/ui/custom/sidebar-context.tsx +61 -61
  33. package/src/components/ui/custom/sidebar-remote-behavior-bridge.tsx +123 -0
  34. package/src/components/ui/custom/tuturuuu-logo-urls.ts +6 -0
  35. package/src/components/ui/custom/tuturuuu-logo.tsx +25 -7
  36. package/src/components/ui/custom/workspace-select-helpers.ts +20 -0
  37. package/src/components/ui/custom/workspace-select.tsx +33 -12
  38. package/src/components/ui/finance/invoices/components/invoice-checkout-summary.tsx +7 -1
  39. package/src/components/ui/finance/invoices/components/invoice-payment-settings.tsx +3 -0
  40. package/src/components/ui/finance/invoices/components/invoice-products-permission-warning.tsx +58 -0
  41. package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +12 -20
  42. package/src/components/ui/finance/invoices/hooks/use-subscription-auto-selection.ts +10 -9
  43. package/src/components/ui/finance/invoices/hooks/use-subscription-invoice-content.ts +10 -5
  44. package/src/components/ui/finance/invoices/hooks.ts +75 -20
  45. package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +137 -0
  46. package/src/components/ui/finance/invoices/new-invoice-page.tsx +86 -37
  47. package/src/components/ui/finance/invoices/product-selection.test.tsx +8 -26
  48. package/src/components/ui/finance/invoices/product-selection.tsx +2 -10
  49. package/src/components/ui/finance/invoices/standard-invoice.tsx +88 -26
  50. package/src/components/ui/finance/invoices/subscription-invoice.tsx +154 -46
  51. package/src/components/ui/finance/invoices/utils.test.ts +50 -0
  52. package/src/components/ui/finance/invoices/utils.ts +75 -17
  53. package/src/components/ui/finance/shared/finance-display-amount.tsx +3 -1
  54. package/src/components/ui/finance/shared/finance-permission-warning-dialog.test.tsx +34 -0
  55. package/src/components/ui/finance/shared/finance-permission-warning-dialog.tsx +157 -0
  56. package/src/components/ui/finance/transactions/form-basic-tab.tsx +8 -0
  57. package/src/components/ui/finance/transactions/form-more-tab.tsx +8 -0
  58. package/src/components/ui/finance/transactions/form-types.ts +2 -0
  59. package/src/components/ui/finance/transactions/form.test.tsx +43 -0
  60. package/src/components/ui/finance/transactions/form.tsx +60 -0
  61. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +27 -0
  62. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +13 -1
  63. package/src/components/ui/finance/transactions/transactions-infinite-page.tsx +4 -0
  64. package/src/components/ui/finance/transactions/transactions-page.tsx +23 -1
  65. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
  66. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +5 -0
  67. package/src/components/ui/legacy/calendar/calendar-content.tsx +9 -1
  68. package/src/components/ui/legacy/calendar/event-modal.tsx +146 -2
  69. package/src/components/ui/legacy/calendar/event-preview-popover.tsx +200 -0
  70. package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +76 -0
  71. package/src/components/ui/legacy/calendar/smart-calendar.tsx +13 -1
  72. package/src/components/ui/legacy/meet/page.test.ts +180 -0
  73. package/src/components/ui/legacy/meet/page.tsx +87 -39
  74. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +79 -25
  75. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-external-workspaces.test.tsx +392 -0
  76. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.test.tsx +57 -0
  77. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.tsx +106 -0
  78. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +106 -161
  79. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-assignees.ts +96 -150
  80. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-labels.ts +63 -79
  81. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-projects.ts +64 -83
  82. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +115 -155
  83. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-utils.ts +319 -2
  84. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operations.ts +8 -1
  85. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +63 -37
  86. package/src/components/ui/tu-do/boards/boardId/kanban/kanban-column-collapse.ts +16 -0
  87. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +46 -0
  88. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +5 -3
  89. package/src/components/ui/tu-do/boards/boardId/kanban.tsx +19 -7
  90. package/src/components/ui/tu-do/boards/boardId/menus/__tests__/task-menus.test.tsx +181 -2
  91. package/src/components/ui/tu-do/boards/boardId/menus/index.ts +1 -0
  92. package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-menu.tsx +463 -0
  93. package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-utils.ts +109 -0
  94. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +4 -0
  95. package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardCheckbox.tsx +6 -3
  96. package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardDates.tsx +26 -9
  97. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-checkbox-style.ts +39 -0
  98. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.test.ts +43 -0
  99. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +33 -0
  100. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.test.ts +31 -0
  101. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.ts +9 -0
  102. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.test.tsx +124 -0
  103. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.tsx +88 -0
  104. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +151 -76
  105. package/src/components/ui/tu-do/boards/boardId/task-card/task-scheduling-badge.tsx +174 -0
  106. package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +34 -13
  107. package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +54 -1
  108. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +158 -0
  109. package/src/components/ui/tu-do/shared/__tests__/task-dialog-manager.test.tsx +5 -2
  110. package/src/components/ui/tu-do/shared/board-client.tsx +12 -2
  111. package/src/components/ui/tu-do/shared/board-views.tsx +195 -328
  112. package/src/components/ui/tu-do/shared/list-view.tsx +18 -8
  113. package/src/components/ui/tu-do/shared/task-due-date-visibility.test.ts +72 -0
  114. package/src/components/ui/tu-do/shared/task-due-date-visibility.ts +38 -0
  115. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +6 -3
  116. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.ts +2 -2
  117. package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +33 -0
  118. package/src/hooks/__tests__/use-calendar-readonly.test.tsx +74 -3
  119. package/src/hooks/__tests__/use-task-actions.test.tsx +118 -0
  120. package/src/hooks/__tests__/use-user-config.test.tsx +65 -0
  121. package/src/hooks/__tests__/use-workspace-presence.test.tsx +1 -1
  122. package/src/hooks/use-calendar-sync.tsx +22 -277
  123. package/src/hooks/use-calendar.tsx +95 -525
  124. package/src/hooks/use-semantic-task-search.ts +10 -33
  125. package/src/hooks/use-task-actions.ts +43 -117
  126. package/src/hooks/use-user-config.ts +1 -1
  127. package/src/hooks/use-workspace-config.ts +6 -2
  128. package/src/hooks/use-workspace-presence.ts +1 -1
  129. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-bar.tsx +0 -94
@@ -155,7 +155,10 @@ export function ChatAgentDetailsSidebar({
155
155
  if (!open) return null;
156
156
 
157
157
  return (
158
- <aside className="hidden w-96 min-w-0 shrink-0 overflow-hidden border-l bg-background md:flex md:flex-col">
158
+ <aside
159
+ className="hidden w-96 min-w-0 shrink-0 overflow-hidden border-l bg-background md:flex md:flex-col"
160
+ data-chat-agent-details-sidebar="true"
161
+ >
159
162
  <Tabs
160
163
  className="min-h-0 flex-1 gap-0"
161
164
  defaultValue="setup"
@@ -192,7 +195,7 @@ export function ChatAgentDetailsSidebar({
192
195
  </TabsList>
193
196
  </div>
194
197
 
195
- <ScrollArea className="min-h-0 flex-1">
198
+ <ScrollArea className="[&_[data-radix-scroll-area-viewport]>div]:!block [&_[data-radix-scroll-area-viewport]>div]:!min-w-0 min-h-0 min-w-0 flex-1 overflow-hidden [&_[data-radix-scroll-area-viewport]>div]:w-full [&_[data-radix-scroll-area-viewport]>div]:max-w-full">
196
199
  {renderContent({
197
200
  agentsError: agentsQuery.error,
198
201
  agentsLoading: agentsQuery.isLoading,
@@ -263,7 +266,7 @@ function renderContent({
263
266
 
264
267
  if (agentsLoading) {
265
268
  return (
266
- <div className="flex items-center justify-center p-6 text-muted-foreground text-sm">
269
+ <div className="flex min-w-0 items-center justify-center p-6 text-muted-foreground text-sm">
267
270
  <LoaderCircle className="mr-2 size-4 animate-spin" />
268
271
  {t('loading_ai_settings')}
269
272
  </div>
@@ -285,7 +288,10 @@ function renderContent({
285
288
 
286
289
  return (
287
290
  <>
288
- <TabsContent className="m-0 p-3" value={'setup' satisfies AgentTab}>
291
+ <TabsContent
292
+ className="m-0 min-w-0 max-w-full overflow-hidden p-3"
293
+ value={'setup' satisfies AgentTab}
294
+ >
289
295
  <AgentSetupForm
290
296
  agent={agent}
291
297
  channel={channel}
@@ -293,13 +299,18 @@ function renderContent({
293
299
  onSubmit={onSave}
294
300
  />
295
301
  </TabsContent>
296
- <TabsContent className="m-0 p-3" value={'operations' satisfies AgentTab}>
302
+ <TabsContent
303
+ className="m-0 min-w-0 max-w-full overflow-hidden p-3"
304
+ value={'operations' satisfies AgentTab}
305
+ >
297
306
  <AgentOperationsPanel
307
+ agentId={agent.id}
298
308
  channel={channel}
299
309
  isPending={isPending}
300
310
  onCopySecret={onCopySecret}
301
311
  onDeploy={onDeploy}
302
312
  onPause={onPause}
313
+ onRefresh={onRefresh}
303
314
  onRotateSecret={onRotateSecret}
304
315
  onTest={onTest}
305
316
  secretPreview={secretPreview}
@@ -307,7 +318,10 @@ function renderContent({
307
318
  />
308
319
  </TabsContent>
309
320
  {tabs.includes('thread') ? (
310
- <TabsContent className="m-0 p-3" value={'thread' satisfies AgentTab}>
321
+ <TabsContent
322
+ className="m-0 min-w-0 max-w-full overflow-hidden p-3"
323
+ value={'thread' satisfies AgentTab}
324
+ >
311
325
  <AgentExternalThreadPanel metadata={metadata} onRefresh={onRefresh} />
312
326
  </TabsContent>
313
327
  ) : null}
@@ -323,7 +337,7 @@ function SidebarNotice({
323
337
  secondary?: string | null;
324
338
  }) {
325
339
  return (
326
- <div className="space-y-2 p-4 text-sm">
340
+ <div className="min-w-0 space-y-2 overflow-hidden p-4 text-sm">
327
341
  <p className="text-muted-foreground">{message}</p>
328
342
  {secondary ? (
329
343
  <p className="break-words rounded-md border bg-muted/20 p-2 text-muted-foreground text-xs">
@@ -5,6 +5,7 @@ import { describe, expect, it } from 'vitest';
5
5
  import {
6
6
  buildAgentPayload,
7
7
  readAgentConversationMetadata,
8
+ secretNamesForChannel,
8
9
  } from './chat-agent-details-utils';
9
10
 
10
11
  function conversation(metadata: Record<string, unknown>): ChatConversation {
@@ -158,4 +159,76 @@ describe('buildAgentPayload', () => {
158
159
  name: 'Updated name',
159
160
  });
160
161
  });
162
+
163
+ it('preserves personal Zalo channel mode and credentials when saving setup edits', () => {
164
+ const agent: AiAgentDefinition = {
165
+ channels: [
166
+ {
167
+ adapter: 'zalo',
168
+ autoRespond: true,
169
+ displayName: 'Personal Zalo',
170
+ enabled: true,
171
+ externalChannelId: 'zalo-thread',
172
+ historySyncEnabled: true,
173
+ id: 'zalo-personal',
174
+ lastDeployedAt: null,
175
+ lastError: null,
176
+ lastEventAt: null,
177
+ mentionRoleIds: [],
178
+ secrets: [
179
+ {
180
+ configured: true,
181
+ lastFour: 'json',
182
+ name: 'personalCookieJson',
183
+ },
184
+ ],
185
+ status: 'deployed',
186
+ webhookUrl: null,
187
+ workspaceId: 'workspace-1',
188
+ zaloAccountMode: 'personal',
189
+ zaloPersonalOwnId: 'own-1',
190
+ },
191
+ ],
192
+ createdAt: null,
193
+ enabled: true,
194
+ id: 'agent-1',
195
+ instructions: 'Old instructions',
196
+ modelId: 'old-model',
197
+ name: 'Old name',
198
+ temperature: null,
199
+ tools: [],
200
+ updatedAt: null,
201
+ };
202
+ const formData = new FormData();
203
+ formData.set('agentEnabled', 'on');
204
+ formData.set('name', 'Updated name');
205
+ formData.set('modelId', 'updated-model');
206
+ formData.set('instructions', 'Updated instructions');
207
+ formData.set('channelDisplayName', 'Personal Zalo');
208
+ formData.set('channelEnabled', 'on');
209
+ formData.set('channelAutoRespond', 'on');
210
+ formData.set('channelHistorySyncEnabled', 'on');
211
+ formData.set('externalChannelId', 'zalo-thread');
212
+ formData.set('workspaceId', 'workspace-1');
213
+ formData.set('zaloPersonalOwnId', 'own-1');
214
+
215
+ expect(secretNamesForChannel(agent.channels[0]!)).toEqual([
216
+ 'personalCookieJson',
217
+ 'personalImei',
218
+ 'personalUserAgent',
219
+ ]);
220
+ expect(
221
+ buildAgentPayload(formData, agent, agent.channels[0]!)
222
+ ).toMatchObject({
223
+ channels: [
224
+ {
225
+ adapter: 'zalo',
226
+ id: 'zalo-personal',
227
+ zaloAccountMode: 'personal',
228
+ zaloOfficialAccountId: null,
229
+ zaloPersonalOwnId: 'own-1',
230
+ },
231
+ ],
232
+ });
233
+ });
161
234
  });
@@ -78,10 +78,10 @@ export function PanelSection({
78
78
  title: string;
79
79
  }) {
80
80
  return (
81
- <section className="space-y-3 rounded-md border bg-muted/10 p-3">
82
- <h3 className="flex items-center gap-2 font-medium text-sm">
83
- {icon}
84
- {title}
81
+ <section className="min-w-0 space-y-3 overflow-hidden rounded-md border bg-muted/10 p-3">
82
+ <h3 className="flex min-w-0 items-center gap-2 font-medium text-sm">
83
+ <span className="shrink-0">{icon}</span>
84
+ <span className="min-w-0 truncate">{title}</span>
85
85
  </h3>
86
86
  {children}
87
87
  </section>
@@ -108,7 +108,7 @@ export function Field({
108
108
  export function KeyValue({ label, value }: { label: string; value: string }) {
109
109
  return (
110
110
  <div className="flex min-w-0 items-center justify-between gap-3 text-sm">
111
- <span className="text-muted-foreground">{label}</span>
111
+ <span className="shrink-0 text-muted-foreground">{label}</span>
112
112
  <span className="min-w-0 truncate font-medium">{value}</span>
113
113
  </div>
114
114
  );
@@ -169,12 +169,61 @@ export function secretNamesForChannel(channel: AiAgentChannelConfig) {
169
169
  const required =
170
170
  channel.adapter === 'discord'
171
171
  ? ['applicationId', 'publicKey', 'botToken']
172
- : ['botToken', 'webhookSecret'];
172
+ : isPersonalZaloChannel(channel)
173
+ ? ['personalCookieJson', 'personalImei', 'personalUserAgent']
174
+ : ['botToken', 'webhookSecret'];
173
175
  return [
174
176
  ...new Set([...required, ...channel.secrets.map((item) => item.name)]),
175
177
  ];
176
178
  }
177
179
 
180
+ export function isPersonalZaloChannel(channel: AiAgentChannelConfig) {
181
+ return channel.adapter === 'zalo' && channel.zaloAccountMode === 'personal';
182
+ }
183
+
184
+ const ZALO_PERSONAL_ERROR_KEYS = {
185
+ ai_agent_zalo_personal_channel_required:
186
+ 'agent_zalo_personal_channel_required',
187
+ zalo_personal_cookie_json_invalid: 'agent_zalo_personal_cookie_json_invalid',
188
+ zalo_personal_credentials_missing: 'agent_zalo_personal_credentials_missing',
189
+ zalo_personal_feature_disabled: 'agent_zalo_personal_feature_disabled',
190
+ zalo_personal_phone_sync_timed_out: 'agent_zalo_personal_sync_phone_failed',
191
+ zalo_personal_phone_sync_no_payload:
192
+ 'agent_zalo_personal_sync_phone_no_payload',
193
+ zalo_personal_phone_sync_waiting_for_phone:
194
+ 'agent_zalo_personal_sync_phone_waiting',
195
+ zalo_personal_web_sync_browser_unavailable:
196
+ 'agent_zalo_personal_sync_web_browser_unavailable',
197
+ zalo_personal_web_sync_failed: 'agent_zalo_personal_sync_web_failed',
198
+ zalo_personal_web_sync_login_required:
199
+ 'agent_zalo_personal_sync_web_login_required',
200
+ zalo_personal_web_sync_timed_out: 'agent_zalo_personal_sync_web_timed_out',
201
+ zalo_personal_web_sync_waiting_for_phone:
202
+ 'agent_zalo_personal_sync_phone_waiting',
203
+ zalo_personal_qr_aborted: 'agent_zalo_personal_qr_aborted',
204
+ zalo_personal_qr_credentials_missing:
205
+ 'agent_zalo_personal_qr_credentials_missing',
206
+ zalo_personal_qr_declined: 'agent_zalo_personal_qr_declined',
207
+ zalo_personal_qr_expired: 'agent_zalo_personal_qr_expired',
208
+ } as const;
209
+
210
+ export function formatZaloPersonalError(
211
+ error: string | null | undefined,
212
+ t: ReturnType<typeof useTranslations>
213
+ ) {
214
+ if (!error) return null;
215
+
216
+ const key =
217
+ ZALO_PERSONAL_ERROR_KEYS[error as keyof typeof ZALO_PERSONAL_ERROR_KEYS];
218
+ if (key) return t(key);
219
+
220
+ if (error.startsWith('zalo_personal_qr_')) {
221
+ return t('agent_zalo_personal_qr_failed');
222
+ }
223
+
224
+ return error;
225
+ }
226
+
178
227
  export async function copyToClipboard(
179
228
  value: string,
180
229
  labels: { error: string; success: string }
@@ -212,17 +261,30 @@ function buildSelectedChannelPayload(
212
261
  String(formData.get('workspaceId') ?? '').trim() || channel.workspaceId,
213
262
  };
214
263
 
215
- return channel.adapter === 'discord'
216
- ? {
217
- ...base,
218
- discordGuildId:
219
- String(formData.get('discordGuildId') ?? '').trim() || null,
220
- }
221
- : {
222
- ...base,
223
- zaloOfficialAccountId:
224
- String(formData.get('zaloOfficialAccountId') ?? '').trim() || null,
225
- };
264
+ if (channel.adapter === 'discord') {
265
+ return {
266
+ ...base,
267
+ discordGuildId:
268
+ String(formData.get('discordGuildId') ?? '').trim() || null,
269
+ };
270
+ }
271
+
272
+ const zaloAccountMode = channel.zaloAccountMode ?? 'official';
273
+
274
+ return {
275
+ ...base,
276
+ zaloAccountMode,
277
+ zaloOfficialAccountId:
278
+ zaloAccountMode === 'official'
279
+ ? String(formData.get('zaloOfficialAccountId') ?? '').trim() || null
280
+ : null,
281
+ zaloPersonalOwnId:
282
+ zaloAccountMode === 'personal'
283
+ ? String(formData.get('zaloPersonalOwnId') ?? '').trim() ||
284
+ channel.zaloPersonalOwnId ||
285
+ null
286
+ : null,
287
+ };
226
288
  }
227
289
 
228
290
  function buildExistingChannelPayload(
@@ -241,15 +303,27 @@ function buildExistingChannelPayload(
241
303
  workspaceId: channel.workspaceId,
242
304
  };
243
305
 
244
- return channel.adapter === 'discord'
245
- ? {
246
- ...base,
247
- discordGuildId: channel.discordGuildId ?? null,
248
- }
249
- : {
250
- ...base,
251
- zaloOfficialAccountId: channel.zaloOfficialAccountId ?? null,
252
- };
306
+ if (channel.adapter === 'discord') {
307
+ return {
308
+ ...base,
309
+ discordGuildId: channel.discordGuildId ?? null,
310
+ };
311
+ }
312
+
313
+ const zaloAccountMode = channel.zaloAccountMode ?? 'official';
314
+
315
+ return {
316
+ ...base,
317
+ zaloAccountMode,
318
+ zaloOfficialAccountId:
319
+ zaloAccountMode === 'official'
320
+ ? (channel.zaloOfficialAccountId ?? null)
321
+ : null,
322
+ zaloPersonalOwnId:
323
+ zaloAccountMode === 'personal'
324
+ ? (channel.zaloPersonalOwnId ?? null)
325
+ : null,
326
+ };
253
327
  }
254
328
 
255
329
  function readSecretPayload(formData: FormData) {