@tuturuuu/ui 0.8.0 → 0.9.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.
- package/CHANGELOG.md +40 -0
- package/biome.json +1 -1
- package/package.json +73 -71
- package/src/components/ui/accordion.tsx +1 -1
- package/src/components/ui/breadcrumb.tsx +1 -1
- package/src/components/ui/calendar-app/calendar-page-shell.tsx +4 -0
- package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +239 -33
- package/src/components/ui/calendar-app/components/load-smart-scheduling-tasks.tsx +143 -0
- package/src/components/ui/calendar-app/components/priority-view.tsx +10 -3
- package/src/components/ui/calendar-app/components/tasks-sidebar.tsx +4 -116
- package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +67 -2
- package/src/components/ui/calendar.tsx +1 -1
- package/src/components/ui/carousel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-external-thread-panel.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-external-thread-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-setup-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-sidebar.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-sidebar.tsx +2 -2
- package/src/components/ui/chat/chat-agent-details-utils.test.ts +1 -1
- package/src/components/ui/chat/chat-agent-details-utils.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +2 -2
- package/src/components/ui/checkbox.tsx +1 -1
- package/src/components/ui/color-picker.tsx +1 -1
- package/src/components/ui/command.tsx +1 -1
- package/src/components/ui/context-menu.tsx +5 -1
- package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +3 -0
- package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +19 -0
- package/src/components/ui/custom/combobox.test.tsx +195 -0
- package/src/components/ui/custom/combobox.tsx +273 -156
- package/src/components/ui/custom/education/modules/youtube/delete-link-button.tsx +5 -13
- package/src/components/ui/custom/facebook-mockup/facebook-mockup.tsx +7 -1
- package/src/components/ui/custom/facebook-mockup/form.tsx +1 -1
- package/src/components/ui/custom/facebook-mockup/image-upload-field.tsx +1 -1
- package/src/components/ui/custom/facebook-mockup/preview.tsx +1 -1
- package/src/components/ui/custom/settings-dialog-shell.tsx +2 -1
- package/src/components/ui/custom/theme-toggle.tsx +1 -1
- package/src/components/ui/custom/workspace-select.tsx +8 -3
- package/src/components/ui/dialog.test.tsx +52 -0
- package/src/components/ui/dialog.tsx +6 -2
- package/src/components/ui/dropdown-menu.tsx +5 -1
- package/src/components/ui/finance/debts/debt-loan-form.tsx +12 -5
- package/src/components/ui/finance/debts/debt-loan-summary.tsx +3 -2
- package/src/components/ui/finance/debts/debts-page.test.tsx +54 -5
- package/src/components/ui/finance/debts/debts-page.tsx +15 -2
- package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +3 -5
- package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +25 -5
- package/src/components/ui/finance/invoices/new-invoice-page.tsx +7 -2
- package/src/components/ui/finance/invoices/standard-invoice.tsx +4 -2
- package/src/components/ui/finance/invoices/subscription-invoice.tsx +4 -2
- package/src/components/ui/finance/invoices/utils.ts +3 -1
- package/src/components/ui/finance/transactions/form-content-dialog.tsx +3 -0
- package/src/components/ui/finance/transactions/form-types.ts +1 -0
- package/src/components/ui/finance/transactions/form.tsx +2 -0
- package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +2 -0
- package/src/components/ui/finance/transactions/period-charts/category-breakdown-dialog.tsx +1 -1
- package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +1 -4
- package/src/components/ui/finance/transactions/transactions-create-summary.tsx +3 -0
- package/src/components/ui/finance/transactions/transactions-page.tsx +4 -1
- package/src/components/ui/finance/wallets/form.test.tsx +51 -3
- package/src/components/ui/finance/wallets/form.tsx +15 -4
- package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
- package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +4 -2
- package/src/components/ui/finance/wallets/wallets-data-table.tsx +1 -0
- package/src/components/ui/finance/wallets/wallets-page.tsx +5 -2
- package/src/components/ui/input-otp.tsx +1 -1
- package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +28 -39
- package/src/components/ui/legacy/calendar/calendar-cell.tsx +2 -0
- package/src/components/ui/legacy/calendar/calendar-content.tsx +10 -6
- package/src/components/ui/legacy/calendar/calendar-header.tsx +23 -3
- package/src/components/ui/legacy/calendar/calendar-loading-skeleton.tsx +135 -0
- package/src/components/ui/legacy/calendar/calendar-matrix.tsx +175 -237
- package/src/components/ui/legacy/calendar/event-card.test.tsx +177 -0
- package/src/components/ui/legacy/calendar/event-card.tsx +220 -131
- package/src/components/ui/legacy/calendar/event-modal.tsx +17 -17
- package/src/components/ui/legacy/calendar/event-provider-display.tsx +69 -0
- package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +86 -4
- package/src/components/ui/legacy/calendar/smart-calendar.tsx +32 -2
- package/src/components/ui/legacy/meet/create-plan-dialog.tsx +19 -10
- package/src/components/ui/navigation-menu.tsx +1 -1
- package/src/components/ui/pagination.tsx +1 -1
- package/src/components/ui/radio-group.tsx +1 -1
- package/src/components/ui/select.tsx +5 -1
- package/src/components/ui/sheet.tsx +1 -1
- package/src/components/ui/sidebar.tsx +1 -1
- package/src/components/ui/storefront/cart-popover.tsx +61 -0
- package/src/components/ui/storefront/cart-summary-parts.tsx +290 -0
- package/src/components/ui/storefront/cart-summary.tsx +93 -154
- package/src/components/ui/storefront/checkout-overlay.tsx +4 -5
- package/src/components/ui/storefront/listing-card.tsx +1 -1
- package/src/components/ui/storefront/merch-sections.tsx +70 -0
- package/src/components/ui/storefront/product-detail.tsx +1 -1
- package/src/components/ui/storefront/storefront-surface.test.tsx +106 -11
- package/src/components/ui/storefront/storefront-surface.tsx +101 -166
- package/src/components/ui/storefront/types.ts +4 -0
- package/src/components/ui/storefront/utils.ts +6 -0
- package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
- package/src/components/ui/text-editor/background-color-extension.ts +62 -0
- package/src/components/ui/text-editor/color-controls.tsx +284 -0
- package/src/components/ui/text-editor/editor.tsx +69 -14
- package/src/components/ui/text-editor/extensions.ts +8 -2
- package/src/components/ui/text-editor/highlight-extension.ts +22 -0
- package/src/components/ui/text-editor/tool-bar.tsx +9 -16
- package/src/components/ui/toast.tsx +1 -1
- package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +270 -0
- package/src/components/ui/tu-do/boards/board-public-link-section.tsx +231 -0
- package/src/components/ui/tu-do/boards/board-share-dialog.tsx +222 -109
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +112 -43
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +2 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +5 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +3 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +50 -2
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +17 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +4 -1
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +38 -9
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +2 -8
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +47 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +81 -30
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +380 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +204 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +61 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +54 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +251 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +27 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +58 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +238 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +143 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +65 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +234 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +397 -2
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +103 -13
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +443 -19
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +94 -32
- package/src/components/ui/tu-do/boards/boardId/kanban.tsx +213 -106
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +26 -4
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +5 -2
- package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +3 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +3 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +191 -28
- package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +152 -0
- package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +555 -545
- package/src/components/ui/tu-do/boards/boardId/task-list.tsx +7 -0
- package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
- package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +10 -12
- package/src/components/ui/tu-do/drafts/drafts-page.tsx +33 -16
- package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +56 -88
- package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +26 -2
- package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +55 -8
- package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +1 -4
- package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +25 -0
- package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +341 -38
- package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +253 -0
- package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +203 -2
- package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +17 -0
- package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
- package/src/components/ui/tu-do/shared/board-client.tsx +2 -7
- package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
- package/src/components/ui/tu-do/shared/board-header.tsx +464 -975
- package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
- package/src/components/ui/tu-do/shared/board-switcher.tsx +209 -217
- package/src/components/ui/tu-do/shared/board-views.tsx +587 -75
- package/src/components/ui/tu-do/shared/list-view.tsx +227 -1
- package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +142 -94
- package/src/components/ui/tu-do/shared/special-task-list-pins.ts +51 -0
- package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +28 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +3 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +91 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +123 -78
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +7 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +8 -3
- package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +2 -1
- package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
- package/src/declarations.d.ts +1 -0
- package/src/hooks/__tests__/use-calendar-readonly.test.tsx +322 -2
- package/src/hooks/__tests__/use-calendar-sync.test.tsx +446 -0
- package/src/hooks/use-calendar-sync.tsx +247 -243
- package/src/hooks/use-calendar.tsx +323 -138
- package/src/hooks/use-task-actions.ts +24 -0
- package/src/hooks/use-user-workspace-config.ts +75 -0
- package/src/hooks/use-workspace-currency.ts +8 -3
- package/src/hooks/useBoardRealtimeEventHandler.ts +11 -0
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
4
|
-
import { Loader2, Share2, Trash2 } from '@tuturuuu/icons';
|
|
4
|
+
import { Loader2, Share2, Trash2, Users } from '@tuturuuu/icons';
|
|
5
5
|
import {
|
|
6
6
|
createWorkspaceTaskBoardShare,
|
|
7
7
|
deleteWorkspaceTaskBoardShare,
|
|
8
8
|
listWorkspaceTaskBoardShares,
|
|
9
|
+
listWorkspaceTaskBoardViewableMembers,
|
|
9
10
|
updateWorkspaceTaskBoardShare,
|
|
10
11
|
type WorkspaceTaskBoardShare,
|
|
11
12
|
type WorkspaceTaskBoardSharePermission,
|
|
13
|
+
type WorkspaceTaskBoardViewableMember,
|
|
12
14
|
} from '@tuturuuu/internal-api/tasks';
|
|
13
15
|
import type { WorkspaceTaskBoard } from '@tuturuuu/types';
|
|
14
16
|
import { Avatar, AvatarFallback, AvatarImage } from '@tuturuuu/ui/avatar';
|
|
15
17
|
import { Badge } from '@tuturuuu/ui/badge';
|
|
16
18
|
import { Button } from '@tuturuuu/ui/button';
|
|
19
|
+
import { Combobox } from '@tuturuuu/ui/custom/combobox';
|
|
17
20
|
import {
|
|
18
21
|
Dialog,
|
|
19
22
|
DialogContent,
|
|
@@ -23,17 +26,12 @@ import {
|
|
|
23
26
|
DialogTitle,
|
|
24
27
|
} from '@tuturuuu/ui/dialog';
|
|
25
28
|
import { Input } from '@tuturuuu/ui/input';
|
|
26
|
-
import {
|
|
27
|
-
Select,
|
|
28
|
-
SelectContent,
|
|
29
|
-
SelectItem,
|
|
30
|
-
SelectTrigger,
|
|
31
|
-
SelectValue,
|
|
32
|
-
} from '@tuturuuu/ui/select';
|
|
33
29
|
import { getInitials } from '@tuturuuu/utils/name-helper';
|
|
34
30
|
import { useTranslations } from 'next-intl';
|
|
35
|
-
import { useState } from 'react';
|
|
31
|
+
import { useRef, useState } from 'react';
|
|
36
32
|
import { toast } from 'sonner';
|
|
33
|
+
import { BoardPublicLinkSection } from './board-public-link-section';
|
|
34
|
+
import { ShareSection } from './share-section';
|
|
37
35
|
|
|
38
36
|
interface BoardShareDialogProps {
|
|
39
37
|
board: Pick<WorkspaceTaskBoard, 'id' | 'name'>;
|
|
@@ -52,6 +50,15 @@ function shareDisplayName(share: WorkspaceTaskBoardShare) {
|
|
|
52
50
|
);
|
|
53
51
|
}
|
|
54
52
|
|
|
53
|
+
function viewableMemberDisplayName(member: WorkspaceTaskBoardViewableMember) {
|
|
54
|
+
return (
|
|
55
|
+
member.display_name ||
|
|
56
|
+
(member.handle ? `@${member.handle}` : null) ||
|
|
57
|
+
member.email ||
|
|
58
|
+
member.user_id
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
export function BoardShareDialog({
|
|
56
63
|
board,
|
|
57
64
|
onOpenChange,
|
|
@@ -63,6 +70,9 @@ export function BoardShareDialog({
|
|
|
63
70
|
const [email, setEmail] = useState('');
|
|
64
71
|
const [permission, setPermission] =
|
|
65
72
|
useState<WorkspaceTaskBoardSharePermission>('view');
|
|
73
|
+
const [membersOpen, setMembersOpen] = useState(false);
|
|
74
|
+
const [guestsOpen, setGuestsOpen] = useState(false);
|
|
75
|
+
const initialFocusRef = useRef<HTMLDivElement>(null);
|
|
66
76
|
|
|
67
77
|
const queryKey = ['task-board-shares', wsId, board.id] as const;
|
|
68
78
|
const sharesQuery = useQuery({
|
|
@@ -70,6 +80,12 @@ export function BoardShareDialog({
|
|
|
70
80
|
queryFn: () => listWorkspaceTaskBoardShares(wsId, board.id),
|
|
71
81
|
enabled: open,
|
|
72
82
|
});
|
|
83
|
+
const viewableMembersQuery = useQuery({
|
|
84
|
+
queryKey: ['task-board-viewable-members', wsId, board.id] as const,
|
|
85
|
+
queryFn: () => listWorkspaceTaskBoardViewableMembers(wsId, board.id),
|
|
86
|
+
enabled: open && membersOpen,
|
|
87
|
+
staleTime: 60_000,
|
|
88
|
+
});
|
|
73
89
|
|
|
74
90
|
const createMutation = useMutation({
|
|
75
91
|
mutationFn: () =>
|
|
@@ -121,16 +137,59 @@ export function BoardShareDialog({
|
|
|
121
137
|
});
|
|
122
138
|
|
|
123
139
|
const shares = sharesQuery.data?.shares ?? [];
|
|
140
|
+
const membersCount = viewableMembersQuery.data?.members.length;
|
|
141
|
+
const membersStatusBadge = viewableMembersQuery.isLoading ? (
|
|
142
|
+
<Badge variant="secondary" className="gap-1 px-2 py-0.5 text-[10px]">
|
|
143
|
+
<Loader2 className="h-3 w-3 animate-spin" />
|
|
144
|
+
{t('common.loading')}
|
|
145
|
+
</Badge>
|
|
146
|
+
) : typeof membersCount === 'number' ? (
|
|
147
|
+
<Badge variant="secondary" className="px-2 py-0.5 text-[10px]">
|
|
148
|
+
{membersCount}
|
|
149
|
+
</Badge>
|
|
150
|
+
) : (
|
|
151
|
+
<Badge variant="outline" className="px-2 py-0.5 text-[10px]">
|
|
152
|
+
{t('common.workspace')}
|
|
153
|
+
</Badge>
|
|
154
|
+
);
|
|
155
|
+
const guestsStatusBadge = sharesQuery.isLoading ? (
|
|
156
|
+
<Badge variant="secondary" className="gap-1 px-2 py-0.5 text-[10px]">
|
|
157
|
+
<Loader2 className="h-3 w-3 animate-spin" />
|
|
158
|
+
{t('common.loading')}
|
|
159
|
+
</Badge>
|
|
160
|
+
) : shares.length ? (
|
|
161
|
+
<Badge variant="secondary" className="px-2 py-0.5 text-[10px]">
|
|
162
|
+
{shares.length}
|
|
163
|
+
</Badge>
|
|
164
|
+
) : (
|
|
165
|
+
<Badge variant="outline" className="px-2 py-0.5 text-[10px]">
|
|
166
|
+
{t('common.none')}
|
|
167
|
+
</Badge>
|
|
168
|
+
);
|
|
124
169
|
const canSubmit =
|
|
125
170
|
email.trim().length > 0 &&
|
|
126
171
|
!createMutation.isPending &&
|
|
127
172
|
!sharesQuery.isLoading;
|
|
173
|
+
const permissionOptions = [
|
|
174
|
+
{
|
|
175
|
+
value: 'view',
|
|
176
|
+
label: t('ws-task-boards.share.permission.view'),
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
value: 'edit',
|
|
180
|
+
label: t('ws-task-boards.share.permission.edit'),
|
|
181
|
+
},
|
|
182
|
+
];
|
|
128
183
|
|
|
129
184
|
return (
|
|
130
185
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
131
186
|
<DialogContent
|
|
132
|
-
className="sm:max-w-xl"
|
|
187
|
+
className="max-h-[min(88dvh,720px)] overflow-y-auto sm:max-w-xl"
|
|
133
188
|
onClick={(e) => e.stopPropagation()}
|
|
189
|
+
onOpenAutoFocus={(event) => {
|
|
190
|
+
event.preventDefault();
|
|
191
|
+
initialFocusRef.current?.focus();
|
|
192
|
+
}}
|
|
134
193
|
>
|
|
135
194
|
<DialogHeader>
|
|
136
195
|
<DialogTitle className="flex items-center gap-2">
|
|
@@ -139,134 +198,188 @@ export function BoardShareDialog({
|
|
|
139
198
|
name: board.name || t('common.untitled'),
|
|
140
199
|
})}
|
|
141
200
|
</DialogTitle>
|
|
142
|
-
<DialogDescription>
|
|
143
|
-
{t('ws-task-boards.share.
|
|
201
|
+
<DialogDescription className="sr-only">
|
|
202
|
+
{t('ws-task-boards.share.title', {
|
|
203
|
+
name: board.name || t('common.untitled'),
|
|
204
|
+
})}
|
|
144
205
|
</DialogDescription>
|
|
145
206
|
</DialogHeader>
|
|
207
|
+
<div ref={initialFocusRef} tabIndex={-1} className="sr-only" />
|
|
146
208
|
|
|
147
|
-
<div className="space-y-
|
|
148
|
-
<
|
|
149
|
-
{t('ws-task-boards.share.guest_scope')}
|
|
150
|
-
</div>
|
|
209
|
+
<div className="space-y-2">
|
|
210
|
+
<BoardPublicLinkSection boardId={board.id} open={open} wsId={wsId} />
|
|
151
211
|
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
setPermission(value as WorkspaceTaskBoardSharePermission)
|
|
163
|
-
}
|
|
164
|
-
>
|
|
165
|
-
<SelectTrigger>
|
|
166
|
-
<SelectValue />
|
|
167
|
-
</SelectTrigger>
|
|
168
|
-
<SelectContent>
|
|
169
|
-
<SelectItem value="view">
|
|
170
|
-
{t('ws-task-boards.share.permission.view')}
|
|
171
|
-
</SelectItem>
|
|
172
|
-
<SelectItem value="edit">
|
|
173
|
-
{t('ws-task-boards.share.permission.edit')}
|
|
174
|
-
</SelectItem>
|
|
175
|
-
</SelectContent>
|
|
176
|
-
</Select>
|
|
177
|
-
<Button
|
|
178
|
-
type="button"
|
|
179
|
-
onClick={() => createMutation.mutate()}
|
|
180
|
-
disabled={!canSubmit}
|
|
181
|
-
>
|
|
182
|
-
{createMutation.isPending ? (
|
|
212
|
+
<ShareSection
|
|
213
|
+
open={membersOpen}
|
|
214
|
+
onOpenChange={setMembersOpen}
|
|
215
|
+
title={t('ws-task-boards.share.workspace_members.title')}
|
|
216
|
+
tooltip={t('ws-task-boards.share.workspace_members.tooltip')}
|
|
217
|
+
icon={<Users className="h-4 w-4 text-muted-foreground" />}
|
|
218
|
+
statusBadge={membersStatusBadge}
|
|
219
|
+
>
|
|
220
|
+
{viewableMembersQuery.isLoading ? (
|
|
221
|
+
<div className="flex items-center gap-2 text-muted-foreground text-sm">
|
|
183
222
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
184
|
-
) : (
|
|
185
|
-
t('common.share')
|
|
186
|
-
)}
|
|
187
|
-
</Button>
|
|
188
|
-
</div>
|
|
189
|
-
|
|
190
|
-
<div className="space-y-2">
|
|
191
|
-
<div className="font-medium text-sm">
|
|
192
|
-
{t('ws-task-boards.share.shared_with')}
|
|
193
|
-
</div>
|
|
194
|
-
{sharesQuery.isLoading ? (
|
|
195
|
-
<div className="rounded-md border border-dashed p-4 text-muted-foreground text-sm">
|
|
196
223
|
{t('common.loading')}
|
|
197
224
|
</div>
|
|
198
|
-
) :
|
|
199
|
-
<div className="
|
|
200
|
-
{t('ws-task-boards.share.empty')}
|
|
225
|
+
) : (viewableMembersQuery.data?.members ?? []).length === 0 ? (
|
|
226
|
+
<div className="text-muted-foreground text-sm">
|
|
227
|
+
{t('ws-task-boards.share.workspace_members.empty')}
|
|
201
228
|
</div>
|
|
202
229
|
) : (
|
|
203
230
|
<div className="divide-y rounded-md border">
|
|
204
|
-
{
|
|
231
|
+
{viewableMembersQuery.data?.members.map((member) => (
|
|
205
232
|
<div
|
|
206
|
-
key={
|
|
233
|
+
key={member.user_id}
|
|
207
234
|
className="flex flex-col gap-3 p-3 sm:flex-row sm:items-center"
|
|
208
235
|
>
|
|
209
236
|
<div className="flex min-w-0 flex-1 items-center gap-3">
|
|
210
237
|
<Avatar className="h-8 w-8">
|
|
211
|
-
<AvatarImage
|
|
212
|
-
src={share.user?.avatar_url ?? undefined}
|
|
213
|
-
/>
|
|
238
|
+
<AvatarImage src={member.avatar_url ?? undefined} />
|
|
214
239
|
<AvatarFallback>
|
|
215
|
-
{getInitials(
|
|
240
|
+
{getInitials(viewableMemberDisplayName(member))}
|
|
216
241
|
</AvatarFallback>
|
|
217
242
|
</Avatar>
|
|
218
243
|
<div className="min-w-0">
|
|
219
244
|
<div className="truncate font-medium text-sm">
|
|
220
|
-
{
|
|
245
|
+
{viewableMemberDisplayName(member)}
|
|
221
246
|
</div>
|
|
222
247
|
<div className="truncate text-muted-foreground text-xs">
|
|
223
|
-
{
|
|
248
|
+
{member.email || member.user_id}
|
|
224
249
|
</div>
|
|
225
250
|
</div>
|
|
226
251
|
</div>
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
>
|
|
242
|
-
<SelectTrigger className="w-28">
|
|
243
|
-
<SelectValue />
|
|
244
|
-
</SelectTrigger>
|
|
245
|
-
<SelectContent>
|
|
246
|
-
<SelectItem value="view">
|
|
247
|
-
{t('ws-task-boards.share.permission.view')}
|
|
248
|
-
</SelectItem>
|
|
249
|
-
<SelectItem value="edit">
|
|
250
|
-
{t('ws-task-boards.share.permission.edit')}
|
|
251
|
-
</SelectItem>
|
|
252
|
-
</SelectContent>
|
|
253
|
-
</Select>
|
|
254
|
-
|
|
255
|
-
<Button
|
|
256
|
-
type="button"
|
|
257
|
-
variant="ghost"
|
|
258
|
-
size="icon"
|
|
259
|
-
onClick={() => deleteMutation.mutate(share.id)}
|
|
260
|
-
disabled={deleteMutation.isPending}
|
|
261
|
-
aria-label={t('common.remove')}
|
|
262
|
-
>
|
|
263
|
-
<Trash2 className="h-4 w-4" />
|
|
264
|
-
</Button>
|
|
252
|
+
<div className="flex flex-wrap items-center gap-1.5">
|
|
253
|
+
{member.is_creator && (
|
|
254
|
+
<Badge variant="secondary">
|
|
255
|
+
{t('ws-task-boards.share.workspace_members.creator')}
|
|
256
|
+
</Badge>
|
|
257
|
+
)}
|
|
258
|
+
{member.roles.slice(0, 2).map((role) => (
|
|
259
|
+
<Badge key={role.id} variant="outline">
|
|
260
|
+
{role.name}
|
|
261
|
+
</Badge>
|
|
262
|
+
))}
|
|
263
|
+
<Badge variant="outline">
|
|
264
|
+
{t('ws-task-boards.share.workspace_members.badge')}
|
|
265
|
+
</Badge>
|
|
266
|
+
</div>
|
|
265
267
|
</div>
|
|
266
268
|
))}
|
|
267
269
|
</div>
|
|
268
270
|
)}
|
|
269
|
-
</
|
|
271
|
+
</ShareSection>
|
|
272
|
+
|
|
273
|
+
<ShareSection
|
|
274
|
+
open={guestsOpen}
|
|
275
|
+
onOpenChange={setGuestsOpen}
|
|
276
|
+
title={t('ws-task-boards.share.guests.title')}
|
|
277
|
+
tooltip={t('ws-task-boards.share.guests.tooltip')}
|
|
278
|
+
icon={<Users className="h-4 w-4 text-muted-foreground" />}
|
|
279
|
+
statusBadge={guestsStatusBadge}
|
|
280
|
+
>
|
|
281
|
+
<div className="space-y-3">
|
|
282
|
+
<div className="grid gap-2 sm:grid-cols-[1fr_8rem_auto]">
|
|
283
|
+
<Input
|
|
284
|
+
type="email"
|
|
285
|
+
value={email}
|
|
286
|
+
onChange={(event) => setEmail(event.target.value)}
|
|
287
|
+
placeholder={t('ws-task-boards.share.email_placeholder')}
|
|
288
|
+
/>
|
|
289
|
+
<Combobox
|
|
290
|
+
mode="single"
|
|
291
|
+
options={permissionOptions}
|
|
292
|
+
selected={permission}
|
|
293
|
+
onChange={(value) =>
|
|
294
|
+
setPermission(value as WorkspaceTaskBoardSharePermission)
|
|
295
|
+
}
|
|
296
|
+
placeholder={t('ws-task-boards.share.permission.view')}
|
|
297
|
+
searchPlaceholder={t('common.search_members')}
|
|
298
|
+
className="[&_button]:h-9"
|
|
299
|
+
/>
|
|
300
|
+
<Button
|
|
301
|
+
type="button"
|
|
302
|
+
onClick={() => createMutation.mutate()}
|
|
303
|
+
disabled={!canSubmit}
|
|
304
|
+
>
|
|
305
|
+
{createMutation.isPending ? (
|
|
306
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
307
|
+
) : (
|
|
308
|
+
t('common.share')
|
|
309
|
+
)}
|
|
310
|
+
</Button>
|
|
311
|
+
</div>
|
|
312
|
+
|
|
313
|
+
{sharesQuery.isLoading ? (
|
|
314
|
+
<div className="rounded-md border border-dashed p-4 text-muted-foreground text-sm">
|
|
315
|
+
{t('common.loading')}
|
|
316
|
+
</div>
|
|
317
|
+
) : shares.length === 0 ? (
|
|
318
|
+
<div className="rounded-md border border-dashed p-4 text-muted-foreground text-sm">
|
|
319
|
+
{t('ws-task-boards.share.empty')}
|
|
320
|
+
</div>
|
|
321
|
+
) : (
|
|
322
|
+
<div className="divide-y rounded-md border">
|
|
323
|
+
{shares.map((share) => (
|
|
324
|
+
<div
|
|
325
|
+
key={share.id}
|
|
326
|
+
className="flex flex-col gap-3 p-3 sm:flex-row sm:items-center"
|
|
327
|
+
>
|
|
328
|
+
<div className="flex min-w-0 flex-1 items-center gap-3">
|
|
329
|
+
<Avatar className="h-8 w-8">
|
|
330
|
+
<AvatarImage
|
|
331
|
+
src={share.user?.avatar_url ?? undefined}
|
|
332
|
+
/>
|
|
333
|
+
<AvatarFallback>
|
|
334
|
+
{getInitials(shareDisplayName(share))}
|
|
335
|
+
</AvatarFallback>
|
|
336
|
+
</Avatar>
|
|
337
|
+
<div className="min-w-0">
|
|
338
|
+
<div className="truncate font-medium text-sm">
|
|
339
|
+
{shareDisplayName(share)}
|
|
340
|
+
</div>
|
|
341
|
+
<div className="truncate text-muted-foreground text-xs">
|
|
342
|
+
{share.email || share.user_id}
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<Badge variant="outline" className="w-fit">
|
|
348
|
+
{t('common.guest_access')}
|
|
349
|
+
</Badge>
|
|
350
|
+
|
|
351
|
+
<Combobox
|
|
352
|
+
mode="single"
|
|
353
|
+
options={permissionOptions}
|
|
354
|
+
selected={share.permission}
|
|
355
|
+
onChange={(value) =>
|
|
356
|
+
updateMutation.mutate({
|
|
357
|
+
shareId: share.id,
|
|
358
|
+
nextPermission:
|
|
359
|
+
value as WorkspaceTaskBoardSharePermission,
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
placeholder={t('ws-task-boards.share.permission.view')}
|
|
363
|
+
searchPlaceholder={t('common.search_members')}
|
|
364
|
+
className="w-28 [&_button]:h-9"
|
|
365
|
+
/>
|
|
366
|
+
|
|
367
|
+
<Button
|
|
368
|
+
type="button"
|
|
369
|
+
variant="ghost"
|
|
370
|
+
size="icon"
|
|
371
|
+
onClick={() => deleteMutation.mutate(share.id)}
|
|
372
|
+
disabled={deleteMutation.isPending}
|
|
373
|
+
aria-label={t('common.remove')}
|
|
374
|
+
>
|
|
375
|
+
<Trash2 className="h-4 w-4" />
|
|
376
|
+
</Button>
|
|
377
|
+
</div>
|
|
378
|
+
))}
|
|
379
|
+
</div>
|
|
380
|
+
)}
|
|
381
|
+
</div>
|
|
382
|
+
</ShareSection>
|
|
270
383
|
</div>
|
|
271
384
|
|
|
272
385
|
<DialogFooter>
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
Filter,
|
|
12
12
|
GripVertical,
|
|
13
13
|
Loader2,
|
|
14
|
+
Pin,
|
|
15
|
+
PinOff,
|
|
14
16
|
RotateCcw,
|
|
15
17
|
} from '@tuturuuu/icons';
|
|
16
18
|
import type { ExternalTaskSortBy } from '@tuturuuu/internal-api/tasks';
|
|
@@ -174,6 +176,9 @@ interface BoardColumnProps {
|
|
|
174
176
|
wsId: string;
|
|
175
177
|
onExternalTasksCollapsedChange?: (collapsed: boolean) => void;
|
|
176
178
|
onTaskListCollapsedChange?: (listId: string, collapsed: boolean) => void;
|
|
179
|
+
specialPinned?: boolean;
|
|
180
|
+
onSpecialPinnedChange?: (pinned: boolean) => void;
|
|
181
|
+
readOnly?: boolean;
|
|
177
182
|
}
|
|
178
183
|
|
|
179
184
|
export function BoardColumn({
|
|
@@ -200,6 +205,9 @@ export function BoardColumn({
|
|
|
200
205
|
wsId,
|
|
201
206
|
onExternalTasksCollapsedChange,
|
|
202
207
|
onTaskListCollapsedChange,
|
|
208
|
+
specialPinned = false,
|
|
209
|
+
onSpecialPinnedChange,
|
|
210
|
+
readOnly = false,
|
|
203
211
|
}: BoardColumnProps) {
|
|
204
212
|
const t = useTranslations('common');
|
|
205
213
|
const tTasks = useTranslations('ws-tasks');
|
|
@@ -210,7 +218,9 @@ export function BoardColumn({
|
|
|
210
218
|
const isExternalCollapsed =
|
|
211
219
|
isExternalStaging && column.is_external_collapsed === true;
|
|
212
220
|
const listState = pagination[column.id];
|
|
213
|
-
const isInitialLoad =
|
|
221
|
+
const isInitialLoad = readOnly
|
|
222
|
+
? false
|
|
223
|
+
: !listState || listState.isInitialLoad;
|
|
214
224
|
const [externalIncludeDocuments, setExternalIncludeDocuments] =
|
|
215
225
|
useState(false);
|
|
216
226
|
const [externalIncludeDoneClosed, setExternalIncludeDoneClosed] =
|
|
@@ -376,7 +386,7 @@ export function BoardColumn({
|
|
|
376
386
|
isDragging,
|
|
377
387
|
} = useSortable({
|
|
378
388
|
id: column.id,
|
|
379
|
-
disabled: isExternalStaging,
|
|
389
|
+
disabled: readOnly || isExternalStaging,
|
|
380
390
|
data: {
|
|
381
391
|
type: 'Column',
|
|
382
392
|
column: {
|
|
@@ -446,6 +456,9 @@ export function BoardColumn({
|
|
|
446
456
|
: visibleTasks.length;
|
|
447
457
|
const externalFilterCount =
|
|
448
458
|
(externalIncludeDocuments ? 1 : 0) + (externalIncludeDoneClosed ? 1 : 0);
|
|
459
|
+
const pinListLabel = specialPinned
|
|
460
|
+
? tTasks('unpin_task_list', { name: translateListName(column.name) })
|
|
461
|
+
: tTasks('pin_task_list', { name: translateListName(column.name) });
|
|
449
462
|
|
|
450
463
|
// Memoize drag handle for performance
|
|
451
464
|
const DragHandle = useMemo(
|
|
@@ -501,6 +514,8 @@ export function BoardColumn({
|
|
|
501
514
|
<Card
|
|
502
515
|
ref={composedRef}
|
|
503
516
|
style={style}
|
|
517
|
+
data-kanban-column-id={column.id}
|
|
518
|
+
data-kanban-real-column={isExternalStaging ? undefined : 'true'}
|
|
504
519
|
className={cn(
|
|
505
520
|
'group flex h-full w-14 shrink-0 snap-start flex-col items-center rounded-xl border border-dashed transition-all duration-200',
|
|
506
521
|
'touch-none select-none overflow-hidden hover:shadow-md',
|
|
@@ -550,6 +565,8 @@ export function BoardColumn({
|
|
|
550
565
|
<Card
|
|
551
566
|
ref={composedRef}
|
|
552
567
|
style={style}
|
|
568
|
+
data-kanban-column-id={column.id}
|
|
569
|
+
data-kanban-real-column={isExternalStaging ? undefined : 'true'}
|
|
553
570
|
className={cn(
|
|
554
571
|
'group flex h-full w-[var(--kanban-column-width)] shrink-0 snap-start flex-col rounded-xl transition-all duration-200 last:snap-end',
|
|
555
572
|
'touch-none select-none',
|
|
@@ -566,7 +583,7 @@ export function BoardColumn({
|
|
|
566
583
|
)}
|
|
567
584
|
>
|
|
568
585
|
<div className="flex items-center gap-2 rounded-t-xl border-b p-3">
|
|
569
|
-
{!isExternalStaging && DragHandle}
|
|
586
|
+
{!readOnly && !isExternalStaging && DragHandle}
|
|
570
587
|
<div className="flex flex-1 items-center gap-2">
|
|
571
588
|
<span className="text-sm">{statusIcon}</span>
|
|
572
589
|
<h3
|
|
@@ -577,9 +594,9 @@ export function BoardColumn({
|
|
|
577
594
|
: 'cursor-pointer hover:underline'
|
|
578
595
|
)}
|
|
579
596
|
onClick={() => {
|
|
580
|
-
if (!isExternalStaging) setIsEditOpen(true);
|
|
597
|
+
if (!readOnly && !isExternalStaging) setIsEditOpen(true);
|
|
581
598
|
}}
|
|
582
|
-
title={isExternalStaging ? undefined : t('edit_list')}
|
|
599
|
+
title={readOnly || isExternalStaging ? undefined : t('edit_list')}
|
|
583
600
|
>
|
|
584
601
|
{translateListName(column.name)}
|
|
585
602
|
</h3>
|
|
@@ -705,53 +722,104 @@ export function BoardColumn({
|
|
|
705
722
|
</DropdownMenuRadioGroup>
|
|
706
723
|
</DropdownMenuContent>
|
|
707
724
|
</DropdownMenu>
|
|
708
|
-
|
|
709
|
-
type="button"
|
|
710
|
-
variant="ghost"
|
|
711
|
-
size="xs"
|
|
712
|
-
className="h-7 w-7 p-0 text-dynamic-cyan hover:bg-dynamic-cyan/10"
|
|
713
|
-
title={tTasks('collapse_external_tasks')}
|
|
714
|
-
aria-label={tTasks('collapse_external_tasks')}
|
|
715
|
-
onClick={() => onExternalTasksCollapsedChange?.(true)}
|
|
716
|
-
>
|
|
717
|
-
<ChevronLeft className="h-3.5 w-3.5" />
|
|
718
|
-
</Button>
|
|
719
|
-
</>
|
|
720
|
-
) : (
|
|
721
|
-
<>
|
|
722
|
-
{isClosedCollapsed || column.status === 'closed' ? (
|
|
725
|
+
{onSpecialPinnedChange ? (
|
|
723
726
|
<Button
|
|
724
727
|
type="button"
|
|
725
728
|
variant="ghost"
|
|
726
729
|
size="xs"
|
|
727
730
|
className={cn(
|
|
728
|
-
'h-7 w-7 p-0 hover:bg-
|
|
729
|
-
|
|
731
|
+
'h-7 w-7 p-0 text-dynamic-cyan hover:bg-dynamic-cyan/10',
|
|
732
|
+
specialPinned && 'bg-dynamic-cyan/10'
|
|
730
733
|
)}
|
|
731
|
-
title={
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
734
|
+
title={pinListLabel}
|
|
735
|
+
aria-label={pinListLabel}
|
|
736
|
+
onClick={() => onSpecialPinnedChange(!specialPinned)}
|
|
737
|
+
>
|
|
738
|
+
{specialPinned ? (
|
|
739
|
+
<PinOff className="h-3.5 w-3.5" />
|
|
740
|
+
) : (
|
|
741
|
+
<Pin className="h-3.5 w-3.5" />
|
|
742
|
+
)}
|
|
743
|
+
</Button>
|
|
744
|
+
) : null}
|
|
745
|
+
{!specialPinned && (
|
|
746
|
+
<Button
|
|
747
|
+
type="button"
|
|
748
|
+
variant="ghost"
|
|
749
|
+
size="xs"
|
|
750
|
+
className="h-7 w-7 p-0 text-dynamic-cyan hover:bg-dynamic-cyan/10"
|
|
751
|
+
title={tTasks('collapse_external_tasks')}
|
|
752
|
+
aria-label={tTasks('collapse_external_tasks')}
|
|
753
|
+
onClick={() => onExternalTasksCollapsedChange?.(true)}
|
|
738
754
|
>
|
|
739
755
|
<ChevronLeft className="h-3.5 w-3.5" />
|
|
740
756
|
</Button>
|
|
757
|
+
)}
|
|
758
|
+
</>
|
|
759
|
+
) : (
|
|
760
|
+
<>
|
|
761
|
+
{isClosedCollapsed || column.status === 'closed' ? (
|
|
762
|
+
<>
|
|
763
|
+
{onSpecialPinnedChange ? (
|
|
764
|
+
<Button
|
|
765
|
+
type="button"
|
|
766
|
+
variant="ghost"
|
|
767
|
+
size="xs"
|
|
768
|
+
className={cn(
|
|
769
|
+
'h-7 w-7 p-0 hover:bg-muted/40',
|
|
770
|
+
getListTextColorClass(column.color as SupportedColor),
|
|
771
|
+
specialPinned && 'bg-muted/40'
|
|
772
|
+
)}
|
|
773
|
+
title={pinListLabel}
|
|
774
|
+
aria-label={pinListLabel}
|
|
775
|
+
onClick={() => onSpecialPinnedChange(!specialPinned)}
|
|
776
|
+
>
|
|
777
|
+
{specialPinned ? (
|
|
778
|
+
<PinOff className="h-3.5 w-3.5" />
|
|
779
|
+
) : (
|
|
780
|
+
<Pin className="h-3.5 w-3.5" />
|
|
781
|
+
)}
|
|
782
|
+
</Button>
|
|
783
|
+
) : null}
|
|
784
|
+
{!specialPinned && (
|
|
785
|
+
<Button
|
|
786
|
+
type="button"
|
|
787
|
+
variant="ghost"
|
|
788
|
+
size="xs"
|
|
789
|
+
className={cn(
|
|
790
|
+
'h-7 w-7 p-0 hover:bg-muted/40',
|
|
791
|
+
getListTextColorClass(column.color as SupportedColor)
|
|
792
|
+
)}
|
|
793
|
+
title={tTasks('collapse_task_list', {
|
|
794
|
+
name: translateListName(column.name),
|
|
795
|
+
})}
|
|
796
|
+
aria-label={tTasks('collapse_task_list', {
|
|
797
|
+
name: translateListName(column.name),
|
|
798
|
+
})}
|
|
799
|
+
onClick={() =>
|
|
800
|
+
onTaskListCollapsedChange?.(column.id, true)
|
|
801
|
+
}
|
|
802
|
+
>
|
|
803
|
+
<ChevronLeft className="h-3.5 w-3.5" />
|
|
804
|
+
</Button>
|
|
805
|
+
)}
|
|
806
|
+
</>
|
|
741
807
|
) : null}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
808
|
+
{!readOnly && (
|
|
809
|
+
<ListActions
|
|
810
|
+
listId={column.id}
|
|
811
|
+
listName={column.name}
|
|
812
|
+
listStatus={column.status}
|
|
813
|
+
listColor={column.color as SupportedColor}
|
|
814
|
+
tasks={tasks}
|
|
815
|
+
boardId={boardId}
|
|
816
|
+
wsId={wsId}
|
|
817
|
+
onUpdate={handleUpdate}
|
|
818
|
+
onSelectAll={handleSelectAll}
|
|
819
|
+
isEditOpen={isEditOpen}
|
|
820
|
+
onEditOpenChange={setIsEditOpen}
|
|
821
|
+
/>
|
|
822
|
+
)}
|
|
755
823
|
</>
|
|
756
824
|
)}
|
|
757
825
|
</div>
|
|
@@ -783,10 +851,11 @@ export function BoardColumn({
|
|
|
783
851
|
onLoadMore={handleLoadMore}
|
|
784
852
|
hasMore={listState?.hasMore ?? false}
|
|
785
853
|
isLoadingMore={listState?.isLoading ?? false}
|
|
854
|
+
readOnly={readOnly}
|
|
786
855
|
/>
|
|
787
856
|
)}
|
|
788
857
|
|
|
789
|
-
{!isExternalStaging && (
|
|
858
|
+
{!readOnly && !isExternalStaging && (
|
|
790
859
|
<div className="rounded-b-xl border-t p-3 backdrop-blur-sm">
|
|
791
860
|
<Button
|
|
792
861
|
variant="ghost"
|