@tuturuuu/ui 0.5.0 → 0.6.0

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 (88) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/package.json +41 -34
  3. package/src/components/ui/currency-input.tsx +65 -23
  4. package/src/components/ui/custom/__tests__/sidebar-context.test.tsx +64 -0
  5. package/src/components/ui/custom/__tests__/sidebar-remote-behavior-bridge.test.tsx +109 -0
  6. package/src/components/ui/custom/combobox.test.tsx +141 -0
  7. package/src/components/ui/custom/combobox.tsx +105 -36
  8. package/src/components/ui/custom/settings/task-settings.tsx +50 -0
  9. package/src/components/ui/custom/settings/task-sound-settings.test.tsx +21 -1
  10. package/src/components/ui/custom/sidebar-context.tsx +68 -6
  11. package/src/components/ui/custom/sidebar-remote-behavior-bridge.tsx +21 -2
  12. package/src/components/ui/finance/finance-layout.tsx +2 -4
  13. package/src/components/ui/finance/shared/balance-mode-toggle.tsx +35 -0
  14. package/src/components/ui/finance/shared/finance-layout-controls.tsx +43 -0
  15. package/src/components/ui/finance/shared/quick-actions.tsx +14 -6
  16. package/src/components/ui/finance/shared/use-finance-balance-mode.ts +72 -0
  17. package/src/components/ui/finance/shared/wallet-balance-mode.test.ts +66 -0
  18. package/src/components/ui/finance/shared/wallet-balance-mode.ts +42 -0
  19. package/src/components/ui/finance/transactions/form-types.ts +23 -0
  20. package/src/components/ui/finance/transactions/form.tsx +81 -22
  21. package/src/components/ui/finance/transactions/wallet-filter.tsx +21 -2
  22. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-adjustment-dialog.tsx +73 -26
  23. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-history-dialog.tsx +617 -0
  24. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-panel.tsx +2 -1
  25. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-sections.tsx +4 -4
  26. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoints.test.tsx +298 -34
  27. package/src/components/ui/finance/wallets/checkpoints/wallet-total-check-dialog.tsx +219 -46
  28. package/src/components/ui/finance/wallets/columns-rendering.test.tsx +125 -0
  29. package/src/components/ui/finance/wallets/columns.test.ts +56 -0
  30. package/src/components/ui/finance/wallets/columns.tsx +196 -43
  31. package/src/components/ui/finance/wallets/form.test.tsx +79 -14
  32. package/src/components/ui/finance/wallets/form.tsx +41 -197
  33. package/src/components/ui/finance/wallets/query-invalidation.ts +1 -0
  34. package/src/components/ui/finance/wallets/wallet-basics-fields.tsx +141 -0
  35. package/src/components/ui/finance/wallets/wallet-credit-fields.tsx +136 -0
  36. package/src/components/ui/finance/wallets/walletId/credit-wallet-summary.tsx +143 -68
  37. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.test.tsx +105 -0
  38. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +120 -16
  39. package/src/components/ui/finance/wallets/walletId/wallet-details-amount.test.tsx +64 -0
  40. package/src/components/ui/finance/wallets/walletId/wallet-details-amount.tsx +226 -6
  41. package/src/components/ui/finance/wallets/walletId/wallet-details-page.test.tsx +64 -2
  42. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +42 -35
  43. package/src/components/ui/finance/wallets/wallets-data-table.test.tsx +171 -0
  44. package/src/components/ui/finance/wallets/wallets-data-table.tsx +132 -29
  45. package/src/components/ui/finance/wallets/wallets-page.test.tsx +111 -37
  46. package/src/components/ui/finance/wallets/wallets-page.tsx +38 -78
  47. package/src/components/ui/storefront/accent-button.tsx +33 -0
  48. package/src/components/ui/storefront/cart-summary.tsx +140 -0
  49. package/src/components/ui/storefront/empty-listings.tsx +32 -0
  50. package/src/components/ui/storefront/hero-panel.tsx +70 -0
  51. package/src/components/ui/storefront/image-panel.tsx +40 -0
  52. package/src/components/ui/storefront/index.ts +12 -0
  53. package/src/components/ui/storefront/listing-card.tsx +129 -0
  54. package/src/components/ui/storefront/storefront-surface.test.tsx +85 -0
  55. package/src/components/ui/storefront/storefront-surface.tsx +235 -0
  56. package/src/components/ui/storefront/types.ts +99 -0
  57. package/src/components/ui/storefront/utils.ts +90 -0
  58. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.test.ts +134 -0
  59. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +127 -0
  60. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +17 -42
  61. package/src/components/ui/tu-do/boards/boardId/timeline-board-open-task.test.tsx +164 -0
  62. package/src/components/ui/tu-do/boards/boardId/timeline-board.tsx +25 -16
  63. package/src/components/ui/tu-do/hooks/useTaskDialog.ts +15 -1
  64. package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +2 -0
  65. package/src/components/ui/tu-do/my-tasks/use-task-context-actions.ts +114 -7
  66. package/src/components/ui/tu-do/providers/__tests__/task-dialog-provider.test.tsx +217 -5
  67. package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +180 -35
  68. package/src/components/ui/tu-do/shared/__tests__/task-dialog-manager.test.tsx +222 -26
  69. package/src/components/ui/tu-do/shared/board-client.tsx +1 -3
  70. package/src/components/ui/tu-do/shared/list-view-context-menu.test.tsx +55 -2
  71. package/src/components/ui/tu-do/shared/list-view.tsx +23 -16
  72. package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +93 -76
  73. package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +11 -0
  74. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +128 -1
  75. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +104 -69
  76. package/src/components/ui/tu-do/shared/task-edit-dialog/components/smart-task-suggestions-panel.test.tsx +129 -0
  77. package/src/components/ui/tu-do/shared/task-edit-dialog/components/smart-task-suggestions-panel.tsx +358 -0
  78. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +1 -1
  79. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +6 -2
  80. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.test.tsx +17 -1
  81. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.tsx +151 -111
  82. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-form-reset.ts +18 -2
  83. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-realtime-sync.ts +1 -2
  84. package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.tsx +5 -3
  85. package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +584 -53
  86. package/src/hooks/useBoardRealtime.ts +54 -1
  87. package/src/hooks/useBoardRealtimeEventHandler.ts +169 -4
  88. package/src/hooks/useTaskUserRealtime.ts +338 -0
@@ -1,16 +1,20 @@
1
1
  import { Input } from '@tuturuuu/ui/input';
2
+ import { Textarea } from '@tuturuuu/ui/textarea';
2
3
  import { MAX_TASK_NAME_LENGTH } from '@tuturuuu/utils/constants';
3
4
  import { useTranslations } from 'next-intl';
5
+ import { useLayoutEffect, useRef } from 'react';
4
6
  import {
5
7
  getNormalizedCursorPosition,
6
8
  normalizeLiveTextReplacements,
7
9
  normalizeTextReplacements,
8
10
  } from '../../../../text-editor/text-replacements';
9
11
 
12
+ type TaskTitleControlElement = HTMLInputElement | HTMLTextAreaElement;
13
+
10
14
  interface TaskNameInputProps {
11
15
  name: string;
12
16
  isCreateMode: boolean;
13
- titleInputRef: React.RefObject<HTMLInputElement | null>;
17
+ titleInputRef: React.RefObject<TaskTitleControlElement | null>;
14
18
  editorRef: React.RefObject<HTMLDivElement | null>;
15
19
  lastCursorPositionRef: React.RefObject<number | null>;
16
20
  targetEditorCursorRef: React.MutableRefObject<number | null>;
@@ -38,6 +42,20 @@ export function TaskNameInput({
38
42
  }: TaskNameInputProps) {
39
43
  const t = useTranslations('ws-task-boards.dialog');
40
44
  const isCompact = variant === 'compact';
45
+ const hasPlacedInitialCaretRef = useRef(false);
46
+
47
+ useLayoutEffect(() => {
48
+ if (hasPlacedInitialCaretRef.current || disabled) return;
49
+
50
+ const titleInput = titleInputRef.current;
51
+ if (!titleInput) return;
52
+
53
+ hasPlacedInitialCaretRef.current = true;
54
+ const endPosition = titleInput.value.length;
55
+
56
+ titleInput.focus();
57
+ titleInput.setSelectionRange(endPosition, endPosition);
58
+ }, [disabled, titleInputRef]);
41
59
 
42
60
  const focusDescriptionEditor = () => {
43
61
  if (isCompact) return;
@@ -53,124 +71,146 @@ export function TaskNameInput({
53
71
  }, 0);
54
72
  };
55
73
 
74
+ const handleChange = (e: React.ChangeEvent<TaskTitleControlElement>) => {
75
+ const rawValue = e.target.value;
76
+ const normalizedValue = normalizeLiveTextReplacements(rawValue);
77
+
78
+ if (rawValue !== normalizedValue) {
79
+ const rawCursorPosition = e.target.selectionStart ?? rawValue.length;
80
+ const nextCursorPosition = getNormalizedCursorPosition(
81
+ rawValue,
82
+ rawCursorPosition,
83
+ normalizeLiveTextReplacements
84
+ );
85
+
86
+ requestAnimationFrame(() => {
87
+ titleInputRef.current?.setSelectionRange(
88
+ nextCursorPosition,
89
+ nextCursorPosition
90
+ );
91
+ });
92
+ }
93
+
94
+ setName(normalizedValue);
95
+ // Trigger debounced save while typing (in edit mode)
96
+ if (!isCreateMode && normalizedValue.trim()) {
97
+ updateName(normalizedValue);
98
+ }
99
+ };
100
+
101
+ const handleBlur = (e: React.FocusEvent<TaskTitleControlElement>) => {
102
+ const normalizedValue = normalizeTextReplacements(e.target.value);
103
+
104
+ if (normalizedValue !== e.target.value) {
105
+ setName(normalizedValue);
106
+ if (!isCreateMode && normalizedValue.trim()) {
107
+ updateName(normalizedValue);
108
+ }
109
+ }
110
+
111
+ // Flush pending save immediately when user clicks away (in edit mode)
112
+ if (!isCreateMode && normalizedValue.trim()) {
113
+ flushNameUpdate();
114
+ }
115
+ };
116
+
117
+ const handleKeyDown = (e: React.KeyboardEvent<TaskTitleControlElement>) => {
118
+ // Enter key moves to description
119
+ if (
120
+ e.key === 'Enter' &&
121
+ !e.altKey &&
122
+ !e.ctrlKey &&
123
+ !e.metaKey &&
124
+ !e.shiftKey
125
+ ) {
126
+ if (e.nativeEvent.isComposing || e.keyCode === 229) {
127
+ return;
128
+ }
129
+
130
+ e.preventDefault();
131
+ e.stopPropagation();
132
+ // Flush pending save immediately when pressing Enter (in edit mode)
133
+ if (!isCreateMode && e.currentTarget.value.trim()) {
134
+ flushNameUpdate();
135
+ }
136
+ if (isCompact) {
137
+ onSubmit?.();
138
+ return;
139
+ }
140
+ focusDescriptionEditor();
141
+ }
142
+
143
+ if (!isCompact && e.key === 'ArrowDown') {
144
+ e.preventDefault();
145
+ const input = e.currentTarget;
146
+ const cursorPosition = input.selectionStart ?? 0;
147
+
148
+ // Store cursor position for smart navigation
149
+ lastCursorPositionRef.current = cursorPosition;
150
+ targetEditorCursorRef.current = cursorPosition;
151
+
152
+ // Focus the editor - cursor positioning will be handled by the editor via prop
153
+ const editorElement = editorRef.current?.querySelector(
154
+ '.ProseMirror'
155
+ ) as HTMLElement;
156
+ if (editorElement) {
157
+ editorElement.focus();
158
+ }
159
+ }
160
+
161
+ // Right arrow at end of title moves to description
162
+ if (!isCompact && e.key === 'ArrowRight') {
163
+ const input = e.currentTarget;
164
+ const cursorPosition = input.selectionStart ?? 0;
165
+ const textLength = input.value.length;
166
+
167
+ // Only move if cursor is at the end
168
+ if (cursorPosition === textLength) {
169
+ e.preventDefault();
170
+ const editorElement = editorRef.current?.querySelector(
171
+ '.ProseMirror'
172
+ ) as HTMLElement;
173
+ if (editorElement) {
174
+ editorElement.focus();
175
+ }
176
+ }
177
+ }
178
+ };
179
+
180
+ if (isCompact) {
181
+ return (
182
+ <div className="group">
183
+ <Textarea
184
+ ref={titleInputRef as React.RefObject<HTMLTextAreaElement | null>}
185
+ data-task-name-input
186
+ disabled={disabled}
187
+ value={name}
188
+ maxLength={MAX_TASK_NAME_LENGTH}
189
+ rows={1}
190
+ onChange={handleChange}
191
+ onBlur={handleBlur}
192
+ onKeyDown={handleKeyDown}
193
+ placeholder={t('task_name_placeholder')}
194
+ className="max-h-32 min-h-11 resize-none overflow-y-auto border-0 bg-transparent px-0 py-0 font-semibold text-base text-foreground leading-tight shadow-none transition-colors placeholder:text-muted-foreground/40 focus-visible:outline-0 focus-visible:ring-0 disabled:opacity-100 md:text-lg"
195
+ autoFocus
196
+ />
197
+ </div>
198
+ );
199
+ }
200
+
56
201
  return (
57
202
  <div className="group">
58
203
  <Input
59
- ref={titleInputRef}
204
+ ref={titleInputRef as React.RefObject<HTMLInputElement | null>}
60
205
  data-task-name-input
61
206
  disabled={disabled}
62
207
  value={name}
63
208
  maxLength={MAX_TASK_NAME_LENGTH}
64
- onChange={(e) => {
65
- const rawValue = e.target.value;
66
- const normalizedValue = normalizeLiveTextReplacements(rawValue);
67
-
68
- if (rawValue !== normalizedValue) {
69
- const rawCursorPosition =
70
- e.target.selectionStart ?? rawValue.length;
71
- const nextCursorPosition = getNormalizedCursorPosition(
72
- rawValue,
73
- rawCursorPosition,
74
- normalizeLiveTextReplacements
75
- );
76
-
77
- requestAnimationFrame(() => {
78
- titleInputRef.current?.setSelectionRange(
79
- nextCursorPosition,
80
- nextCursorPosition
81
- );
82
- });
83
- }
84
-
85
- setName(normalizedValue);
86
- // Trigger debounced save while typing (in edit mode)
87
- if (!isCreateMode && normalizedValue.trim()) {
88
- updateName(normalizedValue);
89
- }
90
- }}
91
- onBlur={(e) => {
92
- const normalizedValue = normalizeTextReplacements(e.target.value);
93
-
94
- if (normalizedValue !== e.target.value) {
95
- setName(normalizedValue);
96
- if (!isCreateMode && normalizedValue.trim()) {
97
- updateName(normalizedValue);
98
- }
99
- }
100
-
101
- // Flush pending save immediately when user clicks away (in edit mode)
102
- if (!isCreateMode && normalizedValue.trim()) {
103
- flushNameUpdate();
104
- }
105
- }}
106
- onKeyDown={(e) => {
107
- // Enter key moves to description
108
- if (
109
- e.key === 'Enter' &&
110
- !e.altKey &&
111
- !e.ctrlKey &&
112
- !e.metaKey &&
113
- !e.shiftKey
114
- ) {
115
- if (e.nativeEvent.isComposing || e.keyCode === 229) {
116
- return;
117
- }
118
-
119
- e.preventDefault();
120
- e.stopPropagation();
121
- // Flush pending save immediately when pressing Enter (in edit mode)
122
- if (!isCreateMode && e.currentTarget.value.trim()) {
123
- flushNameUpdate();
124
- }
125
- if (isCompact) {
126
- onSubmit?.();
127
- return;
128
- }
129
- focusDescriptionEditor();
130
- }
131
-
132
- if (!isCompact && e.key === 'ArrowDown') {
133
- e.preventDefault();
134
- const input = e.currentTarget;
135
- const cursorPosition = input.selectionStart ?? 0;
136
-
137
- // Store cursor position for smart navigation
138
- lastCursorPositionRef.current = cursorPosition;
139
- targetEditorCursorRef.current = cursorPosition;
140
-
141
- // Focus the editor - cursor positioning will be handled by the editor via prop
142
- const editorElement = editorRef.current?.querySelector(
143
- '.ProseMirror'
144
- ) as HTMLElement;
145
- if (editorElement) {
146
- editorElement.focus();
147
- }
148
- }
149
-
150
- // Right arrow at end of title moves to description
151
- if (!isCompact && e.key === 'ArrowRight') {
152
- const input = e.currentTarget;
153
- const cursorPosition = input.selectionStart ?? 0;
154
- const textLength = input.value.length;
155
-
156
- // Only move if cursor is at the end
157
- if (cursorPosition === textLength) {
158
- e.preventDefault();
159
- const editorElement = editorRef.current?.querySelector(
160
- '.ProseMirror'
161
- ) as HTMLElement;
162
- if (editorElement) {
163
- editorElement.focus();
164
- }
165
- }
166
- }
167
- }}
209
+ onChange={handleChange}
210
+ onBlur={handleBlur}
211
+ onKeyDown={handleKeyDown}
168
212
  placeholder={t('task_name_placeholder')}
169
- className={
170
- isCompact
171
- ? 'h-11 border-0 bg-transparent px-0 font-semibold text-base text-foreground leading-tight shadow-none transition-colors placeholder:text-muted-foreground/40 focus-visible:outline-0 focus-visible:ring-0 disabled:opacity-100 md:text-lg'
172
- : 'h-auto border-0 bg-transparent px-4 pt-4 pb-2 font-bold text-2xl text-foreground leading-tight tracking-tight shadow-none transition-colors placeholder:text-muted-foreground/30 focus-visible:outline-0 focus-visible:ring-0 disabled:opacity-100 md:px-8 md:pt-4 md:pb-2 md:text-2xl'
173
- }
213
+ className="h-auto border-0 bg-transparent px-4 pt-4 pb-2 font-bold text-2xl text-foreground leading-tight tracking-tight shadow-none transition-colors placeholder:text-muted-foreground/30 focus-visible:outline-0 focus-visible:ring-0 disabled:opacity-100 md:px-8 md:pt-4 md:pb-2 md:text-2xl"
174
214
  autoFocus
175
215
  />
176
216
  </div>
@@ -30,6 +30,8 @@ export interface UseTaskFormResetProps {
30
30
  isCreateMode: boolean;
31
31
  task?: Task;
32
32
  filters?: TaskFilters;
33
+ taskHydrationVersion?: number;
34
+ preserveNameOnHydration?: boolean;
33
35
 
34
36
  // State setters - using React dispatch types for compatibility
35
37
  setName: React.Dispatch<React.SetStateAction<string>>;
@@ -55,6 +57,8 @@ export function useTaskFormReset({
55
57
  isCreateMode,
56
58
  task,
57
59
  filters,
60
+ taskHydrationVersion = 0,
61
+ preserveNameOnHydration = false,
58
62
  setName,
59
63
  setDescription,
60
64
  setPriority,
@@ -67,12 +71,15 @@ export function useTaskFormReset({
67
71
  setSelectedProjects,
68
72
  }: UseTaskFormResetProps): void {
69
73
  const previousTaskIdRef = useRef<string | null>(null);
74
+ const previousTaskHydrationVersionRef = useRef<number>(taskHydrationVersion);
70
75
  const previousIsOpenRef = useRef<boolean>(false);
71
76
  const isMountedRef = useRef(true);
72
77
 
73
78
  // Reset form when task changes or dialog opens
74
79
  useEffect(() => {
75
80
  const taskIdChanged = previousTaskIdRef.current !== task?.id;
81
+ const taskHydrationVersionChanged =
82
+ previousTaskHydrationVersionRef.current !== taskHydrationVersion;
76
83
  const justOpened = isOpen && !previousIsOpenRef.current;
77
84
  previousIsOpenRef.current = isOpen;
78
85
 
@@ -88,8 +95,14 @@ export function useTaskFormReset({
88
95
  // In edit mode, reset whenever the dialog opens or the task changes.
89
96
  // We don't reset on close (see below) so we must reset on every open
90
97
  // to ensure the form reflects the latest DB state, even for the same task.
91
- if (isOpen && !isCreateMode && (taskIdChanged || justOpened)) {
92
- setName(task?.name || '');
98
+ if (
99
+ isOpen &&
100
+ !isCreateMode &&
101
+ (taskIdChanged || taskHydrationVersionChanged || justOpened)
102
+ ) {
103
+ if (!(taskHydrationVersionChanged && preserveNameOnHydration)) {
104
+ setName(task?.name || '');
105
+ }
93
106
  setDescription(getDescriptionContent(task?.description));
94
107
  setPriority(task?.priority || null);
95
108
  setStartDate(task?.start_date ? new Date(task?.start_date) : undefined);
@@ -100,6 +113,7 @@ export function useTaskFormReset({
100
113
  setSelectedAssignees(task?.assignees || []);
101
114
  setSelectedProjects(task?.projects || []);
102
115
  if (task?.id) previousTaskIdRef.current = task.id;
116
+ previousTaskHydrationVersionRef.current = taskHydrationVersion;
103
117
  } else if (
104
118
  isOpen &&
105
119
  (isCreateMode || task?.id === 'new') &&
@@ -122,6 +136,8 @@ export function useTaskFormReset({
122
136
  isCreateMode,
123
137
  isOpen,
124
138
  task,
139
+ taskHydrationVersion,
140
+ preserveNameOnHydration,
125
141
  filters,
126
142
  setName,
127
143
  setDescription,
@@ -66,7 +66,6 @@ export function useTaskRealtimeSync({
66
66
  isCreateMode,
67
67
  isOpen,
68
68
  realtimeEnabled = true,
69
- isPersonalWorkspace = false,
70
69
  name,
71
70
  priority,
72
71
  startDate,
@@ -262,7 +261,7 @@ export function useTaskRealtimeSync({
262
261
  );
263
262
 
264
263
  const { broadcast } = useBoardRealtime(boardId, {
265
- enabled: realtimeActive && !isPersonalWorkspace,
264
+ enabled: realtimeActive,
266
265
  onTaskChange: handleTaskChange,
267
266
  onTaskRelationsChange: handleTaskRelationsChange,
268
267
  });
@@ -47,6 +47,7 @@ interface TaskDialogActionsProps {
47
47
  onNavigateBack?: () => void;
48
48
  onOpenShareDialog?: () => void;
49
49
  disabled?: boolean;
50
+ controlsDisabled?: boolean;
50
51
  }
51
52
 
52
53
  export function TaskDialogActions({
@@ -64,6 +65,7 @@ export function TaskDialogActions({
64
65
  onNavigateBack,
65
66
  onOpenShareDialog,
66
67
  disabled = false,
68
+ controlsDisabled = false,
67
69
  }: TaskDialogActionsProps) {
68
70
  const t = useTranslations();
69
71
  const tasksHref = useTasksHref();
@@ -75,7 +77,7 @@ export function TaskDialogActions({
75
77
  return (
76
78
  <>
77
79
  {/* Share button - only in edit mode */}
78
- {!isCreateMode && taskId && onOpenShareDialog && (
80
+ {!isCreateMode && taskId && onOpenShareDialog && !controlsDisabled && (
79
81
  <Tooltip>
80
82
  <TooltipTrigger asChild>
81
83
  <Button
@@ -95,7 +97,7 @@ export function TaskDialogActions({
95
97
  )}
96
98
 
97
99
  {/* More options menu - only in edit mode */}
98
- {!isCreateMode && taskId && !disabled && (
100
+ {!isCreateMode && taskId && !disabled && !controlsDisabled && (
99
101
  <DropdownMenu open={isMoreMenuOpen} onOpenChange={setIsMoreMenuOpen}>
100
102
  <DropdownMenuTrigger asChild>
101
103
  <Button
@@ -164,7 +166,7 @@ export function TaskDialogActions({
164
166
  )}
165
167
 
166
168
  {/* Back to related task button - only in create mode with pending relationship */}
167
- {showBackButton && (
169
+ {showBackButton && !controlsDisabled && (
168
170
  <Tooltip>
169
171
  <TooltipTrigger asChild>
170
172
  <Button