Skip to content

Commit 31bdffd

Browse files
authored
fix(ui): feedback on team/invitation (NangoHQ#2545)
## Describe your changes Fixes https://linear.app/nango/issue/SAM-12/design-feedback Just a pass after we discussed with design and product - Fix: Button ring when focused - Fix: Input ring when focused - Fix: Button added empty faded variant (= cancel button) - Fix: Button add disabled display - Fix: Toast make the box fit the content - Fix some wordings and display stuff
1 parent 13c837c commit 31bdffd

File tree

12 files changed

+88
-77
lines changed

12 files changed

+88
-77
lines changed

packages/webapp/src/components/ui/button/Button.tsx

+34-26
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,38 @@ import { cn } from '../../../utils/utils';
66

77
export type ButtonVariants = VariantProps<typeof buttonStyles>['variant'];
88

9-
export const buttonStyles = cva('disabled:pointer-events-none disabled:opacity-50 rounded text-sm', {
10-
variants: {
11-
variant: {
12-
primary: 'bg-white text-black hover:bg-gray-300',
13-
secondary: 'bg-[#282828] text-white hover:bg-gray-800',
14-
success: 'bg-green-700 text-white hover:bg-green-500',
15-
danger: 'bg-red-base text-white hover:bg-red-500',
16-
zombie: 'bg-transparent text-white hover:bg-active-gray',
17-
zombieGray: 'bg-transparent text-white hover:bg-hover-gray border border-active-gray',
18-
yellow: 'bg-yellow-500 text-white hover:bg-yellow-400',
19-
black: 'bg-black text-white hover:bg-hover-gray',
20-
active: 'bg-active-gray text-white',
21-
hover: 'hover:bg-hover-gray text-white',
22-
zinc: 'bg-active-gray hover:bg-neutral-800 text-gray-400 border border-neutral-700',
23-
icon: 'bg-transparent text-text-light-gray hover:text-white focus:text-white'
9+
export const buttonStyles = cva(
10+
'rounded text-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none',
11+
{
12+
variants: {
13+
variant: {
14+
primary: 'bg-white text-black hover:bg-gray-300 disabled:bg-pure-black disabled:text-text-light-gray',
15+
secondary: 'bg-[#282828] text-white hover:bg-gray-800',
16+
success: 'bg-green-700 text-white hover:bg-green-500',
17+
danger: 'bg-red-base text-white hover:bg-red-500',
18+
zombie: 'bg-transparent text-white hover:bg-active-gray',
19+
zombieGray: 'bg-transparent text-white hover:bg-hover-gray border border-active-gray',
20+
yellow: 'bg-yellow-500 text-white hover:bg-yellow-400',
21+
black: 'bg-black text-white hover:bg-hover-gray',
22+
active: 'bg-active-gray text-white',
23+
hover: 'hover:bg-hover-gray text-white',
24+
zinc: 'bg-active-gray hover:bg-neutral-800 text-gray-400 border border-neutral-700',
25+
icon: 'bg-transparent text-text-light-gray hover:text-white focus:text-white',
26+
emptyFaded: 'border border-text-light-gray text-text-light-gray hover:text-white focus:text-white'
27+
},
28+
size: {
29+
xs: 'h-8 py-1 px-3',
30+
sm: 'h-9 px-2',
31+
md: 'h-9 py-2 px-4',
32+
lg: 'h-11 px-4'
33+
}
2434
},
25-
size: {
26-
xs: 'h-8 py-1 px-3',
27-
sm: 'h-9 px-2 ',
28-
md: 'h-9 py-2 px-4',
29-
lg: 'h-11 px-4'
35+
defaultVariants: {
36+
variant: 'primary',
37+
size: 'md'
3038
}
31-
},
32-
defaultVariants: {
33-
variant: 'primary',
34-
size: 'md'
3539
}
36-
});
40+
);
3741

3842
interface ExtraProps {
3943
isLoading?: boolean;
@@ -47,7 +51,11 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button({ size
4751
}
4852

4953
return (
50-
<button ref={ref} className={cn(buttonStyles({ className, variant, size }), 'relative flex gap-2 items-center', isLoading && 'opacity-0')} {...props}>
54+
<button
55+
ref={ref}
56+
className={cn(buttonStyles({ className, variant, size }), 'relative flex gap-2 items-center justify-center', isLoading && 'opacity-0')}
57+
{...props}
58+
>
5159
{children}
5260
{isLoading && <Loader className="animate-spin flex inset-x-0 h-full" />}
5361
</button>

packages/webapp/src/components/ui/input/Input.tsx

+2-8
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,13 @@ const Input = forwardRef<
4242
VariantProps<typeof wrapperStyles>
4343
>(({ className, type, before, after, inputSize, variant, ...props }, ref) => {
4444
return (
45-
<div
46-
className={cn(
47-
'relative flex items-center bg-transparent w-full rounded border text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
48-
wrapperStyles({ variant }),
49-
className
50-
)}
51-
>
45+
<div className={cn('relative flex items-center bg-transparent w-full rounded border text-sm', wrapperStyles({ variant }), className)}>
5246
{before && <div className="absolute text-text-light-gray px-2">{before}</div>}
5347
<input
5448
type={type}
5549
ref={ref}
5650
className={cn(
57-
'bg-transparent border-0 h-full w-full text-white file:border-0 file:bg-transparent file:text-sm file:font-medium outline-none disabled:text-text-light-gray disabled:cursor-not-allowed',
51+
'bg-transparent border-0 h-full w-full rounded text-white focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-1 file:border-0 file:bg-transparent file:text-sm file:font-medium outline-none disabled:text-text-light-gray disabled:cursor-not-allowed',
5852
inputStyles({ inputSize }),
5953
before && 'pl-8',
6054
after && 'pr-8'

packages/webapp/src/components/ui/toast/Toast.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ const ToastViewport = forwardRef<React.ElementRef<typeof ToastPrimitives.Viewpor
1111
({ className, ...props }, ref) => (
1212
<ToastPrimitives.Viewport
1313
ref={ref}
14-
className={cn('fixed bottom-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 items-center pointer-events-none', className)}
14+
className={cn('fixed bottom-10 z-[100] flex max-h-screen w-full flex-col-reverse gap-2 p-4 items-center pointer-events-none', className)}
1515
{...props}
1616
/>
1717
)
1818
);
1919
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
2020

2121
const toastVariants = cva(
22-
'max-w-[420px] pointer-events-all group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-1 px-3 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
22+
'max-w-[500px] pointer-events-all group pointer-events-auto relative flex items-center justify-between space-x-4 overflow-hidden rounded-md border p-1 px-3 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
2323
{
2424
variants: {
2525
variant: {
@@ -62,13 +62,13 @@ const ToastClose = forwardRef<React.ElementRef<typeof ToastPrimitives.Close>, Re
6262
<ToastPrimitives.Close
6363
ref={ref}
6464
className={cn(
65-
'absolute right-2 top-0.5 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
65+
'absolute right-2 top-[3px] rounded-md p-1 text-foreground/50 hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
6666
className
6767
)}
6868
toast-close=""
6969
{...props}
7070
>
71-
<Cross1Icon className="h-4 w-4" />
71+
<Cross1Icon className="h-3.5 w-3.5" />
7272
</ToastPrimitives.Close>
7373
)
7474
);

packages/webapp/src/components/ui/toast/Toaster.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from './Toast';
22
import { useToast } from '../../../hooks/useToast';
3-
import { CheckCircledIcon, CrossCircledIcon } from '@radix-ui/react-icons';
3+
import { CheckIcon, Cross1Icon } from '@radix-ui/react-icons';
44

55
export function Toaster() {
66
const { toasts } = useToast();
@@ -10,16 +10,16 @@ export function Toaster() {
1010
{toasts.map(function ({ id, title, description, action, ...props }) {
1111
return (
1212
<Toast key={id} {...props}>
13-
<div className="flex gap-2 items-center">
13+
<div className="flex gap-2 items-center mr-2">
1414
<div>
1515
{props.variant === 'success' && (
16-
<div className="bg-green-base bg-opacity-35 rounded-full">
17-
<CheckCircledIcon />
16+
<div className="bg-green-base text-green-dark rounded-full border-2 border-green-base-35">
17+
<CheckIcon className="w-2.5 h-2.5" />
1818
</div>
1919
)}
2020
{props.variant === 'error' && (
2121
<div className="bg-red-base bg-opacity-35 rounded-full">
22-
<CrossCircledIcon />
22+
<Cross1Icon />
2323
</div>
2424
)}
2525
</div>

packages/webapp/src/hooks/useToast.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { ToastActionElement, ToastProps } from '../components/ui/toast/Toas
33

44
// Copy pasted from shadcn
55

6-
const TOAST_LIMIT = 1;
6+
const TOAST_LIMIT = 3;
77
const TOAST_REMOVE_DELAY = 1000000;
88

99
type ToasterToast = ToastProps & {

packages/webapp/src/pages/Account/InviteSignup.tsx

+22-14
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,21 @@ export const InviteSignup: React.FC = () => {
6363
return (
6464
<DefaultLayout>
6565
<div className="flex flex-col justify-center">
66-
<div className="flex flex-col justify-center w-80 mt-4">
66+
<div className="flex flex-col justify-center items-center w-80 mt-4 gap-8">
6767
{error.error.code === 'not_found' ? (
68-
<div className="flex flex-col items-center gap-4">
69-
<Info color={'blue'} classNames="text-xs" size={20}>
70-
This invitation does not exists or is expired
71-
</Info>
72-
<Link to={'/signup'}>
73-
<Button>Back to signup</Button>
74-
</Link>
75-
</div>
68+
<>
69+
<div>
70+
<h2 className="text-3xl font-semibold text-white text-center">Invitation Error</h2>
71+
<div className="text-text-light-gray text-sm">This invitation no longer exists or is expired.</div>
72+
</div>
73+
<div className="w-full">
74+
<Link to={'/signup'}>
75+
<Button className="w-full" size={'lg'}>
76+
Back to signup
77+
</Button>
78+
</Link>
79+
</div>
80+
</>
7681
) : (
7782
<Info color={'red'} classNames="text-xs" size={20}>
7883
An error occurred, refresh your page or reach out to the support.{' '}
@@ -96,14 +101,17 @@ export const InviteSignup: React.FC = () => {
96101
return (
97102
<DefaultLayout>
98103
<div className="flex flex-col justify-center">
99-
<div className="flex flex-col justify-center mt-4 gap-4">
100-
<Info size={20} color="orange" classNames="text-xs">
101-
This invitation was sent to a different email. Please logout and use the correct account
102-
</Info>
104+
<div className="flex flex-col justify-center items-center w-80 mt-4 gap-8">
105+
<div>
106+
<h2 className="text-3xl font-semibold text-white text-center">Invitation Error</h2>
107+
<div className="text-text-light-gray text-sm text-center">
108+
This invitation was sent to a different email. Please logout and use the correct account
109+
</div>
110+
</div>
103111

104112
<div className="flex gap-2 justify-center">
105113
<Link to="/">
106-
<Button variant={'zinc'}>Back to home</Button>
114+
<Button variant={'emptyFaded'}>Back to home</Button>
107115
</Link>
108116
<Button onClick={async () => await signout()}>
109117
<ExitIcon className="h-5 w-5 mr-2" />

packages/webapp/src/pages/Account/components/SignupForm.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export const SignupForm: React.FC<{ invitation?: ApiInvitation; token?: string }
115115
<Button
116116
type="submit"
117117
size={'lg'}
118-
className="justify-center"
118+
className="justify-center disabled:bg-dark-700"
119119
disabled={!name || !email || !password || !passwordStrength}
120120
isLoading={loading}
121121
>

packages/webapp/src/pages/Team/components/Actions.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const InvitationAction: React.FC<{ invitation: ApiInvitation }> = ({ invi
9090
const update = await apiDeleteInvite(env, { email: invitation.email });
9191

9292
if (!update || update.res.status === 200) {
93-
toast({ title: `${invitation.email}'s invitation has successfully been revoked`, variant: 'success' });
93+
toast({ title: `${invitation.email}'s invitation has been revoked`, variant: 'success' });
9494
setOpen(false);
9595
void mutate();
9696
}

packages/webapp/src/pages/Team/components/AddTeamMember.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ export const AddTeamMember: React.FC<{ team: ApiTeam }> = ({ team }) => {
6969
const update = await apiPostInvite(env, { emails: emails.map((email) => email.value).filter(Boolean) });
7070

7171
if (update) {
72-
if (update.res.status === 200) {
73-
toast({ title: `Added successfully to ${team.name}'s team`, variant: 'success' });
72+
if (update.res.status === 200 && 'data' in update.json) {
73+
toast({ title: `${update.json.data.invited.length} new members have successfully been added to ${team.name}'s team`, variant: 'success' });
7474
setOpen(false);
7575
setEmails([{ value: '' }]);
7676
void mutate();

packages/webapp/src/pages/Team/components/Info.tsx

+4-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { useRef, useState } from 'react';
22
import { Input } from '../../../components/ui/input/Input';
33
import { apiPutTeam, useTeam } from '../../../hooks/useTeam';
44
import { useStore } from '../../../store';
5-
import { CopyButton } from '../../../components/ui/button/CopyButton';
65
import Button from '../../../components/ui/button/Button';
76
import { Pencil1Icon } from '@radix-ui/react-icons';
87
import { useToast } from '../../../hooks/useToast';
@@ -43,7 +42,6 @@ export const TeamInfo: React.FC = () => {
4342
disabled={!edit}
4443
after={
4544
<div className="flex gap-1 items-center">
46-
<CopyButton text={name} />
4745
{!edit && (
4846
<Tooltip delayDuration={0}>
4947
<TooltipTrigger asChild>
@@ -67,18 +65,18 @@ export const TeamInfo: React.FC = () => {
6765
}
6866
/>
6967
{edit && (
70-
<div className="flex justify-end gap-1 items-center">
68+
<div className="flex justify-end gap-2 items-center">
7169
<Button
72-
size={'sm'}
73-
variant={'zinc'}
70+
size={'md'}
71+
variant={'emptyFaded'}
7472
onClick={() => {
7573
setName(team.name);
7674
setEdit(false);
7775
}}
7876
>
7977
Cancel
8078
</Button>
81-
<Button size={'sm'} onClick={onSave}>
79+
<Button size={'md'} onClick={onSave}>
8280
Save
8381
</Button>
8482
</div>

packages/webapp/src/pages/User/Settings.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const UserSettings: React.FC = () => {
2222
const update = await apiPatchUser({ name });
2323

2424
if (!update || update.res.status === 200) {
25-
toast({ title: 'Profile updated successfully', variant: 'success' });
25+
toast({ title: 'You have successfully updated your profile', variant: 'success' });
2626
setEdit(false);
2727
void mutate();
2828
}
@@ -66,7 +66,7 @@ export const UserSettings: React.FC = () => {
6666
<h3 className="font-semibold text-sm text-white">Display Name</h3>
6767
<Input
6868
ref={ref}
69-
variant={'flat'}
69+
variant={edit ? 'border' : 'flat'}
7070
inputSize={'lg'}
7171
value={name}
7272
onChange={(e) => setName(e.target.value)}
@@ -96,18 +96,18 @@ export const UserSettings: React.FC = () => {
9696
}
9797
/>
9898
{edit && (
99-
<div className="flex justify-end gap-1 items-center">
99+
<div className="flex justify-end gap-2 items-center">
100100
<Button
101-
size={'sm'}
102-
variant={'zinc'}
101+
size={'md'}
102+
variant={'emptyFaded'}
103103
onClick={() => {
104104
setName(user!.name);
105105
setEdit(false);
106106
}}
107107
>
108108
Cancel
109109
</Button>
110-
<Button size={'sm'} onClick={onSave}>
110+
<Button size={'md'} onClick={onSave}>
111111
Save
112112
</Button>
113113
</div>

packages/webapp/tailwind.config.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module.exports = {
99
extend: {
1010
colors: {
1111
'bg-black': '#0E1014',
12-
'pure-black': '#05050a',
12+
'pure-black': '#05050A',
1313
'active-gray': '#161720',
1414
'hover-gray': '#1D1F28',
1515
'text-light-gray': '#A9A9A9',
@@ -25,12 +25,14 @@ module.exports = {
2525
white: '#FFFFFF',
2626

2727
// From Figma
28+
'green-light': '#EDFBD8',
29+
'green-base-35': 'rgba(132, 214, 90, 0.35)',
2830
'green-base': '#84D65A',
2931
'green-dark': '#2B641E',
3032
'blue-base': '#509AF8',
3133
'red-base': '#EF665B',
3234
'red-dark': '#71192F',
33-
'red-base-35': 'rgba(57, 58, 55, 0.16)',
35+
'red-base-35': 'rgba(239, 102, 91, 0.35)',
3436
'yellow-base': '#F7C752',
3537
'yellow-base-35': 'rgba(247, 199, 82, 0.35)',
3638
'alert-red': '#EF665B',
@@ -44,7 +46,8 @@ module.exports = {
4446
'dark-700': '#18181B',
4547
'dark-800': '#09090B',
4648
'border-gray-400': '#323439',
47-
'text-light': '#A9ACB3'
49+
'text-light': '#A9ACB3',
50+
ring: '#A9ACB3'
4851
},
4952
width: {
5053
largebox: '1200px',

0 commit comments

Comments
 (0)