@tuturuuu/ui 0.19.0 → 0.20.1
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 +46 -0
- package/biome.json +1 -1
- package/package.json +62 -50
- package/src/components/ui/badge.tsx +2 -0
- package/src/components/ui/button.tsx +2 -0
- package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +48 -4
- package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +22 -0
- package/src/components/ui/custom/animated-slot-text.tsx +20 -0
- package/src/components/ui/custom/common-footer.tsx +262 -237
- package/src/components/ui/custom/language-dropdown-item.tsx +3 -10
- package/src/components/ui/custom/language-toggle.test.tsx +30 -0
- package/src/components/ui/custom/language-toggle.tsx +11 -10
- package/src/components/ui/custom/locale-preference.ts +32 -0
- package/src/components/ui/custom/settings/appearance-settings.tsx +5 -13
- package/src/components/ui/custom/settings-dialog-shell.tsx +40 -9
- package/src/components/ui/custom/structure.tsx +12 -0
- package/src/components/ui/custom/system-language-dropdown-item.tsx +3 -6
- package/src/components/ui/custom/workspace-access/adapters.test.ts +10 -1
- package/src/components/ui/custom/workspace-access/adapters.ts +36 -4
- package/src/components/ui/custom/workspace-access/member-filter-utils.test.ts +14 -0
- package/src/components/ui/custom/workspace-access/member-filter-utils.ts +8 -0
- package/src/components/ui/custom/workspace-access/types.ts +20 -0
- package/src/components/ui/custom/workspace-access/workspace-access-context.test.tsx +111 -0
- package/src/components/ui/custom/workspace-access/workspace-access-default-role-card.tsx +15 -5
- package/src/components/ui/custom/workspace-access/workspace-access-invite-dialog.tsx +155 -48
- package/src/components/ui/custom/workspace-access/workspace-access-member-profile-dialog.tsx +92 -0
- package/src/components/ui/custom/workspace-access/workspace-access-member-row.tsx +171 -76
- package/src/components/ui/custom/workspace-access/workspace-access-members.tsx +11 -2
- package/src/components/ui/custom/workspace-access/workspace-access-page-header.tsx +11 -11
- package/src/components/ui/custom/workspace-access/workspace-access-page.tsx +180 -22
- package/src/components/ui/custom/workspace-access/workspace-access-people-filters.tsx +29 -15
- package/src/components/ui/custom/workspace-access/workspace-access-permission-checklist.tsx +56 -10
- package/src/components/ui/custom/workspace-access/workspace-access-permission-preview.test.ts +33 -0
- package/src/components/ui/custom/workspace-access/workspace-access-permission-preview.tsx +24 -1
- package/src/components/ui/custom/workspace-access/workspace-access-responsive.test.ts +64 -0
- package/src/components/ui/custom/workspace-access/workspace-access-role-editor-dialog.tsx +28 -16
- package/src/components/ui/custom/workspace-access/workspace-access-roles.tsx +35 -11
- package/src/components/ui/custom/workspace-access/workspace-access-tabs-toolbar.tsx +23 -11
- package/src/components/ui/custom/workspace-select-helpers.ts +5 -3
- package/src/components/ui/custom/workspace-select.tsx +8 -2
- package/src/components/ui/finance/shared/charts/monthly-total-chart-client.tsx +1 -1
- package/src/components/ui/finance/shared/charts/monthly-total-chart.tsx +1 -1
- package/src/components/ui/storefront/cart-summary.tsx +12 -2
- package/src/components/ui/storefront/storefront-surface.test.tsx +21 -0
- package/src/components/ui/storefront/storefront-surface.tsx +6 -0
- package/src/components/ui/text-editor/__tests__/collaboration-binding.test.tsx +161 -0
- package/src/components/ui/text-editor/editor.tsx +267 -235
- package/src/globals.css +166 -0
- package/src/hooks/__tests__/use-workspace-identity-mutation.test.tsx +181 -0
- package/src/hooks/use-workspace-identity-mutation.ts +136 -0
- package/vendor/xlsx/LICENSE +201 -0
- package/vendor/xlsx/types/index.d.ts +1065 -0
- package/vendor/xlsx/xlsx.js +28103 -0
- package/vendor/xlsx/xlsx.mjs +28228 -0
- package/vendor/xlsx-0.20.3.tgz +0 -0
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
CreditCard,
|
|
5
|
+
MailPlus,
|
|
6
|
+
ShieldCheck,
|
|
7
|
+
TriangleAlert,
|
|
8
|
+
UserPlus,
|
|
9
|
+
} from '@tuturuuu/icons';
|
|
5
10
|
import { Badge } from '@tuturuuu/ui/badge';
|
|
6
11
|
import { Button } from '@tuturuuu/ui/button';
|
|
12
|
+
import { Checkbox } from '@tuturuuu/ui/checkbox';
|
|
7
13
|
import {
|
|
8
14
|
Dialog,
|
|
9
15
|
DialogContent,
|
|
10
16
|
DialogDescription,
|
|
17
|
+
DialogFooter,
|
|
11
18
|
DialogHeader,
|
|
12
19
|
DialogTitle,
|
|
13
20
|
} from '@tuturuuu/ui/dialog';
|
|
21
|
+
import { Label } from '@tuturuuu/ui/label';
|
|
14
22
|
import {
|
|
15
23
|
Select,
|
|
16
24
|
SelectContent,
|
|
@@ -23,22 +31,32 @@ import { useTranslations } from 'next-intl';
|
|
|
23
31
|
import { parseInviteEmails } from './member-filter-utils';
|
|
24
32
|
|
|
25
33
|
type Props = {
|
|
34
|
+
accessPreset: 'guest' | 'member' | 'pos_operator';
|
|
35
|
+
canManageRoles: boolean;
|
|
36
|
+
confirmDefaultAdminMigration: boolean;
|
|
37
|
+
defaultAdminEnabled: boolean;
|
|
26
38
|
emails: string;
|
|
27
39
|
isSubmitting: boolean;
|
|
28
|
-
|
|
40
|
+
joinedMemberCount: number;
|
|
41
|
+
onAccessPresetChange: (value: 'guest' | 'member' | 'pos_operator') => void;
|
|
42
|
+
onConfirmDefaultAdminMigrationChange: (value: boolean) => void;
|
|
29
43
|
onEmailsChange: (value: string) => void;
|
|
30
|
-
onMemberTypeChange: (value: WorkspaceDefaultPermissionMemberType) => void;
|
|
31
44
|
onOpenChange: (open: boolean) => void;
|
|
32
45
|
onSubmit: () => void;
|
|
33
46
|
open: boolean;
|
|
34
47
|
};
|
|
35
48
|
|
|
36
49
|
export function WorkspaceAccessInviteDialog({
|
|
50
|
+
accessPreset,
|
|
51
|
+
canManageRoles,
|
|
52
|
+
confirmDefaultAdminMigration,
|
|
53
|
+
defaultAdminEnabled,
|
|
37
54
|
emails,
|
|
38
55
|
isSubmitting,
|
|
39
|
-
|
|
56
|
+
joinedMemberCount,
|
|
57
|
+
onAccessPresetChange,
|
|
58
|
+
onConfirmDefaultAdminMigrationChange,
|
|
40
59
|
onEmailsChange,
|
|
41
|
-
onMemberTypeChange,
|
|
42
60
|
onOpenChange,
|
|
43
61
|
onSubmit,
|
|
44
62
|
open,
|
|
@@ -48,54 +66,143 @@ export function WorkspaceAccessInviteDialog({
|
|
|
48
66
|
|
|
49
67
|
return (
|
|
50
68
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
51
|
-
<DialogContent className="sm:max-w-xl">
|
|
52
|
-
<DialogHeader className="
|
|
53
|
-
<div className="flex
|
|
54
|
-
<
|
|
69
|
+
<DialogContent className="flex max-h-[calc(100dvh-1rem)] w-[calc(100%-1rem)] max-w-none flex-col gap-0 overflow-hidden rounded-b-none p-0 max-sm:top-auto max-sm:bottom-0 max-sm:left-0 max-sm:translate-x-0 max-sm:translate-y-0 sm:max-h-[min(88dvh,52rem)] sm:max-w-xl sm:rounded-lg">
|
|
70
|
+
<DialogHeader className="shrink-0 gap-0 border-b p-4 pr-12 text-left sm:p-6 sm:pr-12">
|
|
71
|
+
<div className="flex items-start gap-3">
|
|
72
|
+
<div className="flex size-10 shrink-0 items-center justify-center rounded-xl border border-dynamic-blue/25 bg-dynamic-blue/10 text-dynamic-blue">
|
|
73
|
+
<UserPlus className="size-4" />
|
|
74
|
+
</div>
|
|
75
|
+
<div className="min-w-0 space-y-1">
|
|
76
|
+
<DialogTitle>{t('ws-members.invite_member')}</DialogTitle>
|
|
77
|
+
<DialogDescription className="leading-5">
|
|
78
|
+
{t('ws-members.invite_dialog_description')}
|
|
79
|
+
</DialogDescription>
|
|
80
|
+
</div>
|
|
55
81
|
</div>
|
|
56
|
-
<DialogTitle>{t('ws-members.invite_member')}</DialogTitle>
|
|
57
|
-
<DialogDescription>
|
|
58
|
-
{t('ws-members.invite_dialog_description')}
|
|
59
|
-
</DialogDescription>
|
|
60
82
|
</DialogHeader>
|
|
61
83
|
|
|
62
|
-
<div className="space-y-4">
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
>
|
|
76
|
-
<SelectTrigger className="w-full">
|
|
77
|
-
<SelectValue />
|
|
78
|
-
</SelectTrigger>
|
|
79
|
-
<SelectContent>
|
|
80
|
-
<SelectItem value="MEMBER">
|
|
81
|
-
{t('ws-members.invite_membership_member')}
|
|
82
|
-
</SelectItem>
|
|
83
|
-
<SelectItem value="GUEST">
|
|
84
|
-
{t('ws-members.invite_membership_guest')}
|
|
85
|
-
</SelectItem>
|
|
86
|
-
</SelectContent>
|
|
87
|
-
</Select>
|
|
84
|
+
<div className="min-h-0 flex-1 space-y-4 overflow-y-auto overscroll-contain p-4 sm:p-6">
|
|
85
|
+
<div className="grid gap-2">
|
|
86
|
+
<Label htmlFor="workspace-access-invite-emails">
|
|
87
|
+
{t('ws-members.invite_member')}
|
|
88
|
+
</Label>
|
|
89
|
+
<Textarea
|
|
90
|
+
id="workspace-access-invite-emails"
|
|
91
|
+
rows={5}
|
|
92
|
+
value={emails}
|
|
93
|
+
onChange={(event) => onEmailsChange(event.target.value)}
|
|
94
|
+
placeholder={'one@example.com\ntwo@example.com'}
|
|
95
|
+
className="min-h-28 resize-y"
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
88
98
|
|
|
89
|
-
<div className="
|
|
90
|
-
<
|
|
91
|
-
{
|
|
92
|
-
</
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
{
|
|
96
|
-
|
|
99
|
+
<div className="grid gap-2">
|
|
100
|
+
<Label htmlFor="workspace-access-invite-membership">
|
|
101
|
+
{t('ws-members.invite_membership_label')}
|
|
102
|
+
</Label>
|
|
103
|
+
<Select
|
|
104
|
+
value={accessPreset}
|
|
105
|
+
onValueChange={(value) =>
|
|
106
|
+
onAccessPresetChange(
|
|
107
|
+
value as 'guest' | 'member' | 'pos_operator'
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
>
|
|
111
|
+
<SelectTrigger
|
|
112
|
+
id="workspace-access-invite-membership"
|
|
113
|
+
className="w-full"
|
|
114
|
+
>
|
|
115
|
+
<SelectValue />
|
|
116
|
+
</SelectTrigger>
|
|
117
|
+
<SelectContent>
|
|
118
|
+
<SelectItem value="member">
|
|
119
|
+
{t('ws-members.invite_membership_member')}
|
|
120
|
+
</SelectItem>
|
|
121
|
+
<SelectItem value="guest">
|
|
122
|
+
{t('ws-members.invite_membership_guest')}
|
|
123
|
+
</SelectItem>
|
|
124
|
+
<SelectItem value="pos_operator" disabled={!canManageRoles}>
|
|
125
|
+
{t('ws-members.invite_membership_pos_operator')}
|
|
126
|
+
</SelectItem>
|
|
127
|
+
</SelectContent>
|
|
128
|
+
</Select>
|
|
97
129
|
</div>
|
|
130
|
+
|
|
131
|
+
{accessPreset === 'pos_operator' ? (
|
|
132
|
+
<div className="space-y-3 rounded-xl border border-dynamic-blue/25 bg-dynamic-blue/5 p-3.5 sm:p-4">
|
|
133
|
+
<div className="flex items-start gap-3">
|
|
134
|
+
<div className="mt-0.5 rounded-lg border border-dynamic-blue/20 bg-background p-2 text-dynamic-blue">
|
|
135
|
+
<CreditCard className="h-4 w-4" />
|
|
136
|
+
</div>
|
|
137
|
+
<div className="min-w-0 space-y-1">
|
|
138
|
+
<p className="font-medium text-sm">
|
|
139
|
+
{t('ws-members.pos_operator_title')}
|
|
140
|
+
</p>
|
|
141
|
+
<p className="text-muted-foreground text-sm leading-5">
|
|
142
|
+
{t('ws-members.pos_operator_description')}
|
|
143
|
+
</p>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<div className="grid gap-2 sm:grid-cols-2">
|
|
148
|
+
{defaultAdminEnabled ? (
|
|
149
|
+
<div className="flex items-center gap-2 rounded-lg border bg-background/70 p-3 text-sm">
|
|
150
|
+
<ShieldCheck className="h-4 w-4 text-dynamic-green" />
|
|
151
|
+
<span>
|
|
152
|
+
{joinedMemberCount}{' '}
|
|
153
|
+
{t('ws-members.pos_operator_existing_members')}
|
|
154
|
+
</span>
|
|
155
|
+
</div>
|
|
156
|
+
) : null}
|
|
157
|
+
<div className="flex items-center gap-2 rounded-lg border bg-background/70 p-3 text-sm">
|
|
158
|
+
<CreditCard className="h-4 w-4 text-dynamic-blue" />
|
|
159
|
+
<span>{t('ws-members.pos_operator_only_permission')}</span>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
{defaultAdminEnabled ? (
|
|
164
|
+
<div className="flex items-start gap-2 rounded-lg border border-dynamic-orange/25 bg-dynamic-orange/5 p-3 text-sm">
|
|
165
|
+
<TriangleAlert className="mt-0.5 h-4 w-4 shrink-0 text-dynamic-orange" />
|
|
166
|
+
<p className="text-muted-foreground leading-5">
|
|
167
|
+
{t('ws-members.pos_operator_admin_migration_note')}
|
|
168
|
+
</p>
|
|
169
|
+
</div>
|
|
170
|
+
) : null}
|
|
171
|
+
|
|
172
|
+
<label className="flex cursor-pointer items-start gap-3 rounded-lg border bg-background p-3">
|
|
173
|
+
<Checkbox
|
|
174
|
+
checked={confirmDefaultAdminMigration}
|
|
175
|
+
className="mt-0.5"
|
|
176
|
+
onCheckedChange={(checked) =>
|
|
177
|
+
onConfirmDefaultAdminMigrationChange(checked === true)
|
|
178
|
+
}
|
|
179
|
+
/>
|
|
180
|
+
<span className="text-sm leading-5">
|
|
181
|
+
{t('ws-members.pos_operator_confirmation')}
|
|
182
|
+
</span>
|
|
183
|
+
</label>
|
|
184
|
+
</div>
|
|
185
|
+
) : null}
|
|
98
186
|
</div>
|
|
187
|
+
|
|
188
|
+
<DialogFooter className="grid shrink-0 grid-cols-[auto_1fr] items-center gap-3 border-t bg-muted/20 p-3 sm:flex sm:p-4">
|
|
189
|
+
<Badge variant="secondary" className="rounded-full">
|
|
190
|
+
{count} {t('ws-members.pending_invitations').toLowerCase()}
|
|
191
|
+
</Badge>
|
|
192
|
+
<Button
|
|
193
|
+
className="min-w-0"
|
|
194
|
+
disabled={
|
|
195
|
+
isSubmitting ||
|
|
196
|
+
count === 0 ||
|
|
197
|
+
(accessPreset === 'pos_operator' &&
|
|
198
|
+
(!canManageRoles || !confirmDefaultAdminMigration))
|
|
199
|
+
}
|
|
200
|
+
onClick={onSubmit}
|
|
201
|
+
>
|
|
202
|
+
<MailPlus className="mr-2 size-4" />
|
|
203
|
+
<span className="truncate">{t('ws-members.invite_submit')}</span>
|
|
204
|
+
</Button>
|
|
205
|
+
</DialogFooter>
|
|
99
206
|
</DialogContent>
|
|
100
207
|
</Dialog>
|
|
101
208
|
);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Loader2 } from '@tuturuuu/icons';
|
|
4
|
+
import type { InternalApiEnhancedWorkspaceMember } from '@tuturuuu/types';
|
|
5
|
+
import { Button } from '@tuturuuu/ui/button';
|
|
6
|
+
import {
|
|
7
|
+
Dialog,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogDescription,
|
|
10
|
+
DialogFooter,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
} from '@tuturuuu/ui/dialog';
|
|
14
|
+
import { Input } from '@tuturuuu/ui/input';
|
|
15
|
+
import { Label } from '@tuturuuu/ui/label';
|
|
16
|
+
import { MAX_NAME_LENGTH } from '@tuturuuu/utils/constants';
|
|
17
|
+
import { useTranslations } from 'next-intl';
|
|
18
|
+
import { type FormEvent, useState } from 'react';
|
|
19
|
+
|
|
20
|
+
type Props = {
|
|
21
|
+
isSubmitting: boolean;
|
|
22
|
+
member: InternalApiEnhancedWorkspaceMember;
|
|
23
|
+
onOpenChange: (open: boolean) => void;
|
|
24
|
+
onSubmit: (displayName: string | null) => void;
|
|
25
|
+
open: boolean;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function WorkspaceAccessMemberProfileDialog({
|
|
29
|
+
isSubmitting,
|
|
30
|
+
member,
|
|
31
|
+
onOpenChange,
|
|
32
|
+
onSubmit,
|
|
33
|
+
open,
|
|
34
|
+
}: Props) {
|
|
35
|
+
const t = useTranslations();
|
|
36
|
+
const [displayName, setDisplayName] = useState(
|
|
37
|
+
member.workspace_profile_display_name ?? member.display_name ?? ''
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const submit = (event: FormEvent<HTMLFormElement>) => {
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
onSubmit(displayName.trim() || null);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
47
|
+
<DialogContent className="sm:max-w-md">
|
|
48
|
+
<DialogHeader>
|
|
49
|
+
<DialogTitle>{t('ws-members.profile_display_name')}</DialogTitle>
|
|
50
|
+
<DialogDescription>
|
|
51
|
+
{t('ws-members.profile_display_name_description')}
|
|
52
|
+
</DialogDescription>
|
|
53
|
+
</DialogHeader>
|
|
54
|
+
|
|
55
|
+
<form className="space-y-4" onSubmit={submit}>
|
|
56
|
+
<div className="space-y-2">
|
|
57
|
+
<Label htmlFor="workspace-member-profile-display-name">
|
|
58
|
+
{t('ws-members.profile_display_name')}
|
|
59
|
+
</Label>
|
|
60
|
+
<Input
|
|
61
|
+
id="workspace-member-profile-display-name"
|
|
62
|
+
autoComplete="off"
|
|
63
|
+
autoFocus
|
|
64
|
+
disabled={isSubmitting}
|
|
65
|
+
maxLength={MAX_NAME_LENGTH}
|
|
66
|
+
onChange={(event) => setDisplayName(event.target.value)}
|
|
67
|
+
placeholder={t('ws-members.profile_display_name_placeholder')}
|
|
68
|
+
value={displayName}
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<DialogFooter>
|
|
73
|
+
<Button
|
|
74
|
+
disabled={isSubmitting}
|
|
75
|
+
onClick={() => onOpenChange(false)}
|
|
76
|
+
type="button"
|
|
77
|
+
variant="outline"
|
|
78
|
+
>
|
|
79
|
+
{t('common.cancel')}
|
|
80
|
+
</Button>
|
|
81
|
+
<Button disabled={isSubmitting} type="submit">
|
|
82
|
+
{isSubmitting ? (
|
|
83
|
+
<Loader2 className="size-4 animate-spin" />
|
|
84
|
+
) : null}
|
|
85
|
+
{t('ws-members.save_profile_display_name')}
|
|
86
|
+
</Button>
|
|
87
|
+
</DialogFooter>
|
|
88
|
+
</form>
|
|
89
|
+
</DialogContent>
|
|
90
|
+
</Dialog>
|
|
91
|
+
);
|
|
92
|
+
}
|