@tuturuuu/ui 0.7.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 +88 -0
- package/biome.json +1 -1
- package/package.json +75 -73
- 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/currency-input.test.tsx +43 -0
- package/src/components/ui/currency-input.tsx +1 -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-access/workspace-access-default-role-card.tsx +60 -35
- package/src/components/ui/custom/workspace-access/workspace-access-member-row.tsx +176 -167
- package/src/components/ui/custom/workspace-access/workspace-access-members.tsx +16 -10
- package/src/components/ui/custom/workspace-access/workspace-access-page-header.tsx +75 -36
- package/src/components/ui/custom/workspace-access/workspace-access-page.tsx +39 -42
- package/src/components/ui/custom/workspace-access/workspace-access-people-filters.tsx +1 -1
- package/src/components/ui/custom/workspace-access/workspace-access-roles.tsx +113 -91
- package/src/components/ui/custom/workspace-access/workspace-access-tabs-toolbar.tsx +73 -32
- 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 +3 -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-card.tsx +21 -9
- 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/money-input.test.tsx +64 -0
- package/src/components/ui/money-input.tsx +63 -0
- 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 +104 -80
- package/src/components/ui/storefront/checkout-overlay.tsx +26 -0
- package/src/components/ui/storefront/hero-panel.tsx +2 -8
- package/src/components/ui/storefront/image-panel.tsx +6 -0
- package/src/components/ui/storefront/index.ts +11 -0
- package/src/components/ui/storefront/listing-card.tsx +84 -22
- package/src/components/ui/storefront/merch-sections.tsx +70 -0
- package/src/components/ui/storefront/product-detail.tsx +289 -0
- package/src/components/ui/storefront/product-dialog.tsx +72 -0
- package/src/components/ui/storefront/storefront-surface.test.tsx +221 -3
- package/src/components/ui/storefront/storefront-surface.tsx +288 -153
- package/src/components/ui/storefront/types.ts +27 -1
- package/src/components/ui/storefront/utils.ts +117 -27
- package/src/components/ui/text-editor/__tests__/content-migration.test.ts +32 -0
- package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
- package/src/components/ui/text-editor/__tests__/image-extension.test.ts +69 -1
- package/src/components/ui/text-editor/__tests__/video-extension.test.ts +47 -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/content-migration.ts +41 -18
- package/src/components/ui/text-editor/editor.tsx +69 -14
- package/src/components/ui/text-editor/extensions.ts +9 -3
- package/src/components/ui/text-editor/highlight-extension.ts +22 -0
- package/src/components/ui/text-editor/image-extension.ts +40 -18
- package/src/components/ui/text-editor/tool-bar.tsx +9 -16
- package/src/components/ui/text-editor/video-extension.ts +11 -2
- 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/__tests__/workspace-projects-client-page.test.tsx +70 -1
- 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-external-retry.test.tsx +127 -0
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +113 -46
- 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 +51 -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.test.ts +63 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +127 -38
- 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 +410 -4
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +106 -14
- 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 +186 -0
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +59 -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/boardId/timeline/timeline-display.ts +9 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +8 -16
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +5 -25
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.test.ts +36 -1
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.ts +51 -2
- package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
- package/src/components/ui/tu-do/boards/workspace-projects-client-page.tsx +13 -3
- 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 +237 -3
- 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 +465 -937
- 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 +596 -82
- package/src/components/ui/tu-do/shared/cursor-overlay-multi-wrapper.tsx +53 -12
- 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-dialog-presentation.test.ts +53 -0
- package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +19 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +57 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +136 -111
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +3 -1
- 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 +44 -15
- 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/__tests__/useBoardRealtime.test.tsx +2 -2
- package/src/hooks/__tests__/useCursorTracking.test.tsx +212 -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/useBoardRealtime.ts +6 -3
- package/src/hooks/useBoardRealtime.types.ts +11 -0
- package/src/hooks/useBoardRealtimeEventHandler.ts +11 -0
- package/src/hooks/useCursorTracking.ts +91 -27
- package/src/hooks/useTaskUserRealtime.ts +5 -3
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ShieldCheck, Users } from '@tuturuuu/icons';
|
|
4
|
-
import { Badge } from '@tuturuuu/ui/badge';
|
|
3
|
+
import { ShieldCheck, UserCheck, UserPlus, Users } from '@tuturuuu/icons';
|
|
5
4
|
import { useTranslations } from 'next-intl';
|
|
5
|
+
import type { ReactNode } from 'react';
|
|
6
6
|
import type { WorkspaceAccessContext, WorkspaceAccessMode } from './types';
|
|
7
7
|
|
|
8
8
|
type Props = {
|
|
@@ -13,6 +13,13 @@ type Props = {
|
|
|
13
13
|
totalCount: number;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
type Stat = {
|
|
17
|
+
accent: string;
|
|
18
|
+
icon: ReactNode;
|
|
19
|
+
label: string;
|
|
20
|
+
value: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
16
23
|
export function WorkspaceAccessPageHeader({
|
|
17
24
|
context,
|
|
18
25
|
invitedCount,
|
|
@@ -22,49 +29,81 @@ export function WorkspaceAccessPageHeader({
|
|
|
22
29
|
}: Props) {
|
|
23
30
|
const t = useTranslations() as (key: string) => string;
|
|
24
31
|
|
|
32
|
+
const stats: Stat[] = [
|
|
33
|
+
{
|
|
34
|
+
accent: 'text-dynamic-blue',
|
|
35
|
+
icon: <Users className="h-4 w-4" />,
|
|
36
|
+
label: t('common.total'),
|
|
37
|
+
value: totalCount,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
accent: 'text-dynamic-green',
|
|
41
|
+
icon: <UserCheck className="h-4 w-4" />,
|
|
42
|
+
label: t('ws-members.active_members'),
|
|
43
|
+
value: joinedCount,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
accent: 'text-dynamic-orange',
|
|
47
|
+
icon: <UserPlus className="h-4 w-4" />,
|
|
48
|
+
label: t('ws-members.pending_invitations'),
|
|
49
|
+
value: invitedCount,
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
25
53
|
return (
|
|
26
|
-
<section className="
|
|
27
|
-
<div className="flex flex-col gap-6 lg:flex-row lg:items-start lg:justify-between">
|
|
28
|
-
<div className="min-w-0">
|
|
29
|
-
<div className="flex items-center
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<div className="
|
|
34
|
-
<h1 className="
|
|
54
|
+
<section className="rounded-xl border border-border bg-background">
|
|
55
|
+
<div className="flex flex-col gap-6 p-6 lg:flex-row lg:items-start lg:justify-between">
|
|
56
|
+
<div className="flex min-w-0 gap-4">
|
|
57
|
+
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border border-dynamic-blue/30 bg-dynamic-blue/10 text-dynamic-blue">
|
|
58
|
+
<Users className="h-5 w-5" />
|
|
59
|
+
</div>
|
|
60
|
+
<div className="min-w-0">
|
|
61
|
+
<div className="flex items-center gap-2">
|
|
62
|
+
<h1 className="truncate font-bold text-2xl tracking-tight">
|
|
35
63
|
{mode === 'cms'
|
|
36
64
|
? t('external-projects.settings.members_title')
|
|
37
65
|
: t('workspace-settings-layout.members')}
|
|
38
66
|
</h1>
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</p>
|
|
44
|
-
) : null}
|
|
67
|
+
<span className="inline-flex items-center gap-1 rounded-full border border-dynamic-purple/30 bg-dynamic-purple/10 px-2 py-0.5 font-medium text-dynamic-purple text-xs">
|
|
68
|
+
<ShieldCheck className="h-3 w-3" />
|
|
69
|
+
{t('ws-roles.plural')}
|
|
70
|
+
</span>
|
|
45
71
|
</div>
|
|
72
|
+
<p className="mt-1.5 max-w-2xl text-muted-foreground text-sm leading-6">
|
|
73
|
+
{mode === 'cms'
|
|
74
|
+
? t('external-projects.settings.members_description')
|
|
75
|
+
: t('ws-members.description')}
|
|
76
|
+
</p>
|
|
77
|
+
{context.boundProjectName ? (
|
|
78
|
+
<p className="mt-2 inline-flex items-center gap-1.5 rounded-md border border-border bg-foreground/[0.03] px-2 py-1 text-muted-foreground text-xs">
|
|
79
|
+
{t('external-projects.settings.bound_project_label')}:
|
|
80
|
+
<span className="font-semibold text-foreground">
|
|
81
|
+
{context.boundProjectName}
|
|
82
|
+
</span>
|
|
83
|
+
</p>
|
|
84
|
+
) : null}
|
|
46
85
|
</div>
|
|
47
|
-
<p className="mt-4 max-w-3xl text-muted-foreground text-sm leading-6">
|
|
48
|
-
{mode === 'cms'
|
|
49
|
-
? t('external-projects.settings.members_description')
|
|
50
|
-
: t('ws-members.description')}
|
|
51
|
-
</p>
|
|
52
86
|
</div>
|
|
53
87
|
|
|
54
|
-
<div className="
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
88
|
+
<div className="grid shrink-0 grid-cols-3 divide-x divide-border overflow-hidden rounded-lg border border-border bg-foreground/[0.02]">
|
|
89
|
+
{stats.map((stat) => (
|
|
90
|
+
<div
|
|
91
|
+
key={stat.label}
|
|
92
|
+
className="flex flex-col gap-1 px-4 py-3 text-center"
|
|
93
|
+
>
|
|
94
|
+
<span
|
|
95
|
+
className={`inline-flex items-center justify-center gap-1.5 ${stat.accent}`}
|
|
96
|
+
>
|
|
97
|
+
{stat.icon}
|
|
98
|
+
<span className="font-bold text-foreground text-xl tabular-nums">
|
|
99
|
+
{stat.value}
|
|
100
|
+
</span>
|
|
101
|
+
</span>
|
|
102
|
+
<span className="text-muted-foreground text-xs">
|
|
103
|
+
{stat.label}
|
|
104
|
+
</span>
|
|
105
|
+
</div>
|
|
106
|
+
))}
|
|
68
107
|
</div>
|
|
69
108
|
</div>
|
|
70
109
|
</section>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
4
|
-
import {
|
|
4
|
+
import { TriangleAlert } from '@tuturuuu/icons';
|
|
5
5
|
import type { SupabaseUser } from '@tuturuuu/supabase/next/user';
|
|
6
6
|
import type { WorkspaceDefaultPermissionMemberType } from '@tuturuuu/types';
|
|
7
7
|
import { Skeleton } from '@tuturuuu/ui/skeleton';
|
|
@@ -222,10 +222,10 @@ export function WorkspaceAccessPage({
|
|
|
222
222
|
|
|
223
223
|
if (contextQuery.isPending) {
|
|
224
224
|
return (
|
|
225
|
-
<div className="space-y-
|
|
226
|
-
<Skeleton className="h-
|
|
227
|
-
<Skeleton className="h-
|
|
228
|
-
<Skeleton className="h-96 rounded-
|
|
225
|
+
<div className="space-y-6">
|
|
226
|
+
<Skeleton className="h-32 rounded-xl" />
|
|
227
|
+
<Skeleton className="h-10 rounded-xl" />
|
|
228
|
+
<Skeleton className="h-96 rounded-xl" />
|
|
229
229
|
</div>
|
|
230
230
|
);
|
|
231
231
|
}
|
|
@@ -255,42 +255,39 @@ export function WorkspaceAccessPage({
|
|
|
255
255
|
search={search}
|
|
256
256
|
/>
|
|
257
257
|
|
|
258
|
-
<TabsContent value="people" className="mt-4">
|
|
259
|
-
<
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
/>
|
|
258
|
+
<TabsContent value="people" className="mt-6 space-y-4">
|
|
259
|
+
<WorkspaceAccessPeopleFilters
|
|
260
|
+
filterOptions={filterOptions}
|
|
261
|
+
filters={filters}
|
|
262
|
+
labels={labels}
|
|
263
|
+
onFiltersChange={setFilters}
|
|
264
|
+
onStatusChange={setStatus}
|
|
265
|
+
status={status}
|
|
266
|
+
/>
|
|
268
267
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
/>
|
|
290
|
-
</div>
|
|
268
|
+
<WorkspaceAccessMembers
|
|
269
|
+
canManageMembers={canManageMembers}
|
|
270
|
+
canManageRoles={canManageRoles}
|
|
271
|
+
isLoading={membersQuery.isPending}
|
|
272
|
+
isMutating={
|
|
273
|
+
removeMemberMutation.isPending || roleMembershipMutation.isPending
|
|
274
|
+
}
|
|
275
|
+
labels={labels}
|
|
276
|
+
members={visibleMembers}
|
|
277
|
+
onAssignRole={(payload) =>
|
|
278
|
+
roleMembershipMutation.mutate({ ...payload, action: 'add' })
|
|
279
|
+
}
|
|
280
|
+
onRemoveMember={(payload) => removeMemberMutation.mutate(payload)}
|
|
281
|
+
onRemoveRole={(payload) =>
|
|
282
|
+
roleMembershipMutation.mutate({ ...payload, action: 'remove' })
|
|
283
|
+
}
|
|
284
|
+
roles={roles}
|
|
285
|
+
searchTerm={search}
|
|
286
|
+
status={status}
|
|
287
|
+
/>
|
|
291
288
|
</TabsContent>
|
|
292
289
|
|
|
293
|
-
<TabsContent value="roles" className="mt-
|
|
290
|
+
<TabsContent value="roles" className="mt-6">
|
|
294
291
|
<WorkspaceAccessRoles
|
|
295
292
|
canManageRoles={canManageRoles}
|
|
296
293
|
isLoading={rolesQuery.isPending}
|
|
@@ -309,7 +306,7 @@ export function WorkspaceAccessPage({
|
|
|
309
306
|
/>
|
|
310
307
|
</TabsContent>
|
|
311
308
|
|
|
312
|
-
<TabsContent value="defaults-member" className="mt-
|
|
309
|
+
<TabsContent value="defaults-member" className="mt-6">
|
|
313
310
|
<WorkspaceAccessDefaultRoleCard
|
|
314
311
|
canManageRoles={canManageRoles}
|
|
315
312
|
isLoading={memberDefaultQuery.isPending}
|
|
@@ -327,7 +324,7 @@ export function WorkspaceAccessPage({
|
|
|
327
324
|
/>
|
|
328
325
|
</TabsContent>
|
|
329
326
|
|
|
330
|
-
<TabsContent value="defaults-guest" className="mt-
|
|
327
|
+
<TabsContent value="defaults-guest" className="mt-6">
|
|
331
328
|
<WorkspaceAccessDefaultRoleCard
|
|
332
329
|
canManageRoles={canManageRoles}
|
|
333
330
|
isLoading={guestDefaultQuery.isPending}
|
|
@@ -378,8 +375,8 @@ export function WorkspaceAccessPage({
|
|
|
378
375
|
) : null}
|
|
379
376
|
|
|
380
377
|
{contextQuery.isError ? (
|
|
381
|
-
<div className="rounded-
|
|
382
|
-
<
|
|
378
|
+
<div className="flex items-center gap-2 rounded-xl border border-dynamic-red/20 bg-dynamic-red/5 p-4 text-dynamic-red text-sm">
|
|
379
|
+
<TriangleAlert className="h-4 w-4 shrink-0" />
|
|
383
380
|
{t('common.error')}
|
|
384
381
|
</div>
|
|
385
382
|
) : null}
|
|
@@ -52,7 +52,7 @@ export function WorkspaceAccessPeopleFilters({
|
|
|
52
52
|
filters.permissionIds.length + filters.roleIds.length;
|
|
53
53
|
|
|
54
54
|
return (
|
|
55
|
-
<div className="
|
|
55
|
+
<div className="flex flex-col gap-3 rounded-xl border border-border bg-foreground/[0.02] p-3 sm:flex-row sm:items-center sm:justify-between">
|
|
56
56
|
<div className="flex flex-wrap items-center gap-2">
|
|
57
57
|
<div className="mr-1 flex items-center gap-2 text-muted-foreground text-sm">
|
|
58
58
|
<ListFilter className="h-4 w-4" />
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Pencil, Plus, Trash2 } from '@tuturuuu/icons';
|
|
3
|
+
import { Pencil, Plus, ShieldCheck, Trash2, Users } from '@tuturuuu/icons';
|
|
4
4
|
import type { InternalApiEnhancedWorkspaceMember } from '@tuturuuu/types';
|
|
5
5
|
import { Badge } from '@tuturuuu/ui/badge';
|
|
6
6
|
import { Button } from '@tuturuuu/ui/button';
|
|
7
|
-
import {
|
|
8
|
-
Card,
|
|
9
|
-
CardContent,
|
|
10
|
-
CardDescription,
|
|
11
|
-
CardHeader,
|
|
12
|
-
CardTitle,
|
|
13
|
-
} from '@tuturuuu/ui/card';
|
|
14
7
|
import { Skeleton } from '@tuturuuu/ui/skeleton';
|
|
15
8
|
import { useTranslations } from 'next-intl';
|
|
16
9
|
import { getMemberDisplayName } from './member-filter-utils';
|
|
@@ -56,37 +49,36 @@ export function WorkspaceAccessRoles({
|
|
|
56
49
|
|
|
57
50
|
return (
|
|
58
51
|
<div className="space-y-4">
|
|
59
|
-
<
|
|
60
|
-
<
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
</CardHeader>
|
|
74
|
-
</Card>
|
|
52
|
+
<div className="flex flex-wrap items-start justify-between gap-4">
|
|
53
|
+
<div>
|
|
54
|
+
<h2 className="font-semibold text-lg">{labels.accessLevelsLabel}</h2>
|
|
55
|
+
<p className="text-muted-foreground text-sm">
|
|
56
|
+
{t('ws-roles.description')}
|
|
57
|
+
</p>
|
|
58
|
+
</div>
|
|
59
|
+
{canManageRoles ? (
|
|
60
|
+
<Button onClick={onCreateRole}>
|
|
61
|
+
<Plus className="mr-2 h-4 w-4" />
|
|
62
|
+
{t('ws-roles.create')}
|
|
63
|
+
</Button>
|
|
64
|
+
) : null}
|
|
65
|
+
</div>
|
|
75
66
|
|
|
76
67
|
{isLoading ? (
|
|
77
68
|
<div className="space-y-3">
|
|
78
|
-
<Skeleton className="h-36 rounded-
|
|
79
|
-
<Skeleton className="h-36 rounded-
|
|
69
|
+
<Skeleton className="h-36 rounded-xl" />
|
|
70
|
+
<Skeleton className="h-36 rounded-xl" />
|
|
80
71
|
</div>
|
|
81
72
|
) : roles.length === 0 ? (
|
|
82
|
-
<
|
|
83
|
-
<
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
73
|
+
<div className="flex min-h-44 flex-col items-center justify-center gap-2 rounded-xl border border-border border-dashed p-8 text-center">
|
|
74
|
+
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-dynamic-purple/10 text-dynamic-purple">
|
|
75
|
+
<ShieldCheck className="h-6 w-6" />
|
|
76
|
+
</div>
|
|
77
|
+
<div className="font-medium">{labels.rolesEmptyTitle}</div>
|
|
78
|
+
<div className="max-w-sm text-muted-foreground text-sm">
|
|
79
|
+
{labels.rolesEmptyDescription}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
90
82
|
) : (
|
|
91
83
|
<div className="grid gap-3">
|
|
92
84
|
{roles.map((role) => {
|
|
@@ -94,74 +86,104 @@ export function WorkspaceAccessRoles({
|
|
|
94
86
|
role.members && role.members.length > 0
|
|
95
87
|
? role.members
|
|
96
88
|
: assignedMembersForRole(role.id, members);
|
|
89
|
+
const enabled = enabledPermissionCount(role);
|
|
90
|
+
const pct =
|
|
91
|
+
permissionCount > 0
|
|
92
|
+
? Math.round((enabled / permissionCount) * 100)
|
|
93
|
+
: 0;
|
|
97
94
|
|
|
98
95
|
return (
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
</span>
|
|
108
|
-
<span>
|
|
109
|
-
{t('ws-roles.permissions')}:{' '}
|
|
110
|
-
{enabledPermissionCount(role)}/{permissionCount}
|
|
111
|
-
</span>
|
|
112
|
-
</div>
|
|
96
|
+
<div
|
|
97
|
+
key={role.id}
|
|
98
|
+
className="rounded-xl border border-border bg-background p-5 transition-colors hover:border-foreground/20"
|
|
99
|
+
>
|
|
100
|
+
<div className="flex flex-wrap items-start justify-between gap-4">
|
|
101
|
+
<div className="flex min-w-0 gap-3">
|
|
102
|
+
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border border-dynamic-purple/30 bg-dynamic-purple/10 text-dynamic-purple">
|
|
103
|
+
<ShieldCheck className="h-5 w-5" />
|
|
113
104
|
</div>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
>
|
|
121
|
-
<Pencil className="mr-2 h-4 w-4" />
|
|
122
|
-
{t('common.edit')}
|
|
123
|
-
</Button>
|
|
124
|
-
<Button
|
|
105
|
+
<div className="min-w-0">
|
|
106
|
+
<div className="flex items-center gap-2">
|
|
107
|
+
<span className="truncate font-semibold text-base">
|
|
108
|
+
{role.name}
|
|
109
|
+
</span>
|
|
110
|
+
<Badge
|
|
125
111
|
variant="outline"
|
|
126
|
-
|
|
112
|
+
className="h-5 gap-1 px-1.5 text-xs"
|
|
127
113
|
>
|
|
128
|
-
<
|
|
129
|
-
{
|
|
130
|
-
</
|
|
114
|
+
<Users className="h-3 w-3" />
|
|
115
|
+
{assignedMembers.length}
|
|
116
|
+
</Badge>
|
|
131
117
|
</div>
|
|
132
|
-
|
|
118
|
+
<div className="mt-2 max-w-xs">
|
|
119
|
+
<div className="flex items-center justify-between text-muted-foreground text-xs">
|
|
120
|
+
<span>{t('ws-roles.permissions')}</span>
|
|
121
|
+
<span className="tabular-nums">
|
|
122
|
+
{enabled}/{permissionCount}
|
|
123
|
+
</span>
|
|
124
|
+
</div>
|
|
125
|
+
<div className="mt-1 h-1.5 overflow-hidden rounded-full bg-foreground/10">
|
|
126
|
+
<div
|
|
127
|
+
className="h-full rounded-full bg-dynamic-purple"
|
|
128
|
+
style={{ width: `${pct}%` }}
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
133
|
</div>
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
{canManageRoles ? (
|
|
136
|
+
<div className="flex flex-wrap gap-2">
|
|
137
|
+
<Button
|
|
138
|
+
variant="outline"
|
|
139
|
+
size="sm"
|
|
140
|
+
onClick={() => onEditRole(role)}
|
|
141
|
+
>
|
|
142
|
+
<Pencil className="mr-2 h-4 w-4" />
|
|
143
|
+
{t('common.edit')}
|
|
144
|
+
</Button>
|
|
145
|
+
<Button
|
|
146
|
+
variant="outline"
|
|
147
|
+
size="sm"
|
|
148
|
+
onClick={() => onDeleteRole(role)}
|
|
149
|
+
>
|
|
150
|
+
<Trash2 className="mr-2 h-4 w-4" />
|
|
151
|
+
{t('common.delete')}
|
|
152
|
+
</Button>
|
|
153
|
+
</div>
|
|
154
|
+
) : null}
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div className="mt-4 flex flex-wrap gap-2">
|
|
158
|
+
<WorkspaceAccessPermissionPreview
|
|
159
|
+
emptyLabel={t('ws-members.no_permissions')}
|
|
160
|
+
permissionTitles={permissionTitles}
|
|
161
|
+
role={role}
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
142
164
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
{assignedMembers.length > 0 ? (
|
|
166
|
+
<div className="mt-3 flex flex-wrap gap-1.5 border-border border-t pt-3">
|
|
167
|
+
{assignedMembers.slice(0, 6).map((member) => (
|
|
168
|
+
<Badge
|
|
169
|
+
key={`${role.id}-${member.id}`}
|
|
170
|
+
variant="secondary"
|
|
171
|
+
className="rounded-full text-xs"
|
|
172
|
+
>
|
|
173
|
+
{getMemberDisplayName(
|
|
174
|
+
member as InternalApiEnhancedWorkspaceMember,
|
|
175
|
+
t('common.unknown')
|
|
176
|
+
)}
|
|
147
177
|
</Badge>
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
>
|
|
155
|
-
{getMemberDisplayName(
|
|
156
|
-
member as InternalApiEnhancedWorkspaceMember,
|
|
157
|
-
t('common.unknown')
|
|
158
|
-
)}
|
|
159
|
-
</Badge>
|
|
160
|
-
))
|
|
161
|
-
)}
|
|
178
|
+
))}
|
|
179
|
+
{assignedMembers.length > 6 ? (
|
|
180
|
+
<Badge variant="outline" className="rounded-full text-xs">
|
|
181
|
+
+{assignedMembers.length - 6}
|
|
182
|
+
</Badge>
|
|
183
|
+
) : null}
|
|
162
184
|
</div>
|
|
163
|
-
|
|
164
|
-
</
|
|
185
|
+
) : null}
|
|
186
|
+
</div>
|
|
165
187
|
);
|
|
166
188
|
})}
|
|
167
189
|
</div>
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
KeyRound,
|
|
5
|
+
Search,
|
|
6
|
+
ShieldCheck,
|
|
7
|
+
ShieldUser,
|
|
8
|
+
UserPlus,
|
|
9
|
+
Users,
|
|
10
|
+
} from '@tuturuuu/icons';
|
|
4
11
|
import { Button } from '@tuturuuu/ui/button';
|
|
5
12
|
import { Input } from '@tuturuuu/ui/input';
|
|
6
13
|
import { TabsList, TabsTrigger } from '@tuturuuu/ui/tabs';
|
|
7
14
|
import { useTranslations } from 'next-intl';
|
|
15
|
+
import type { ReactNode } from 'react';
|
|
8
16
|
import type { WorkspaceAccessTab } from './types';
|
|
9
17
|
|
|
10
18
|
type Props = {
|
|
@@ -18,6 +26,9 @@ type Props = {
|
|
|
18
26
|
search: string;
|
|
19
27
|
};
|
|
20
28
|
|
|
29
|
+
const TAB_TRIGGER_CLASS =
|
|
30
|
+
'rounded-none border-transparent border-b-2 bg-transparent px-1 pt-1 pb-3 text-muted-foreground shadow-none transition-colors hover:text-foreground data-[state=active]:border-dynamic-blue data-[state=active]:bg-transparent data-[state=active]:text-foreground data-[state=active]:shadow-none';
|
|
31
|
+
|
|
21
32
|
export function WorkspaceAccessTabsToolbar({
|
|
22
33
|
activeTab,
|
|
23
34
|
accessLevelsLabel,
|
|
@@ -30,41 +41,71 @@ export function WorkspaceAccessTabsToolbar({
|
|
|
30
41
|
}: Props) {
|
|
31
42
|
const t = useTranslations() as (key: string) => string;
|
|
32
43
|
|
|
44
|
+
const tabs: Array<{
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
icon: ReactNode;
|
|
47
|
+
label: string;
|
|
48
|
+
value: WorkspaceAccessTab;
|
|
49
|
+
}> = [
|
|
50
|
+
{
|
|
51
|
+
icon: <Users className="h-4 w-4" />,
|
|
52
|
+
label: t('ws-roles.members'),
|
|
53
|
+
value: 'people',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
disabled: !canManageRoles,
|
|
57
|
+
icon: <ShieldCheck className="h-4 w-4" />,
|
|
58
|
+
label: accessLevelsLabel,
|
|
59
|
+
value: 'roles',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
disabled: !canManageRoles,
|
|
63
|
+
icon: <ShieldUser className="h-4 w-4" />,
|
|
64
|
+
label: t('ws-roles.member_defaults_tab'),
|
|
65
|
+
value: 'defaults-member',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
disabled: !canManageRoles,
|
|
69
|
+
icon: <KeyRound className="h-4 w-4" />,
|
|
70
|
+
label: t('ws-roles.guest_defaults_tab'),
|
|
71
|
+
value: 'defaults-guest',
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
|
|
33
75
|
return (
|
|
34
|
-
<div className="
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
<TabsTrigger
|
|
38
|
-
|
|
39
|
-
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{t('ws-roles.guest_defaults_tab')}
|
|
76
|
+
<div className="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
|
|
77
|
+
<TabsList className="h-auto w-full justify-start gap-5 overflow-x-auto rounded-none border-border border-b bg-transparent p-0 lg:w-auto">
|
|
78
|
+
{tabs.map((tab) => (
|
|
79
|
+
<TabsTrigger
|
|
80
|
+
key={tab.value}
|
|
81
|
+
value={tab.value}
|
|
82
|
+
disabled={tab.disabled}
|
|
83
|
+
className={TAB_TRIGGER_CLASS}
|
|
84
|
+
>
|
|
85
|
+
{tab.icon}
|
|
86
|
+
{tab.label}
|
|
46
87
|
</TabsTrigger>
|
|
47
|
-
|
|
88
|
+
))}
|
|
89
|
+
</TabsList>
|
|
48
90
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
</div>
|
|
59
|
-
{activeTab === 'people' ? (
|
|
60
|
-
<Button disabled={!canInvite} onClick={onInviteClick}>
|
|
61
|
-
<UserPlus className="mr-2 h-4 w-4" />
|
|
62
|
-
{disableInvite
|
|
63
|
-
? t('ws-members.invite_member_disabled')
|
|
64
|
-
: t('ws-members.invite_member')}
|
|
65
|
-
</Button>
|
|
66
|
-
) : null}
|
|
91
|
+
<div className="flex w-full shrink-0 flex-col gap-2 sm:flex-row lg:w-auto">
|
|
92
|
+
<div className="relative min-w-0 sm:min-w-[280px]">
|
|
93
|
+
<Search className="pointer-events-none absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
94
|
+
<Input
|
|
95
|
+
value={search}
|
|
96
|
+
onChange={(event) => onSearchChange(event.target.value)}
|
|
97
|
+
placeholder={t('common.search')}
|
|
98
|
+
className="pl-9"
|
|
99
|
+
/>
|
|
67
100
|
</div>
|
|
101
|
+
{activeTab === 'people' ? (
|
|
102
|
+
<Button disabled={!canInvite} onClick={onInviteClick}>
|
|
103
|
+
<UserPlus className="mr-2 h-4 w-4" />
|
|
104
|
+
{disableInvite
|
|
105
|
+
? t('ws-members.invite_member_disabled')
|
|
106
|
+
: t('ws-members.invite_member')}
|
|
107
|
+
</Button>
|
|
108
|
+
) : null}
|
|
68
109
|
</div>
|
|
69
110
|
</div>
|
|
70
111
|
);
|