mirror of https://github.com/buster-so/buster.git
invite modal update
This commit is contained in:
parent
f99f2bddab
commit
898562b965
|
@ -11,6 +11,7 @@ import { useInviteModalStore } from '@/context/BusterAppLayout';
|
||||||
import { InvitePeopleModal } from '@/components/features/modal/InvitePeopleModal';
|
import { InvitePeopleModal } from '@/components/features/modal/InvitePeopleModal';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
|
import { Plus } from '@/components/ui/icons';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const userOrganization = useUserConfigContextSelector((x) => x.userOrganizations);
|
const userOrganization = useUserConfigContextSelector((x) => x.userOrganizations);
|
||||||
|
@ -43,7 +44,9 @@ export default function Page() {
|
||||||
setSearchText={handleSearchChange}
|
setSearchText={handleSearchChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button onClick={() => onToggleInviteModal(true)}>Invite people</Button>
|
<Button prefix={<Plus />} onClick={() => onToggleInviteModal(true)}>
|
||||||
|
Invite people
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ export const LoginForm: React.FC<{}> = ({}) => {
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
errorFallback(error);
|
errorFallback(error);
|
||||||
}
|
}
|
||||||
setLoading('google');
|
setLoading(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSignInWithGithub = useMemoizedFn(async () => {
|
const onSignInWithGithub = useMemoizedFn(async () => {
|
||||||
|
@ -78,7 +78,7 @@ export const LoginForm: React.FC<{}> = ({}) => {
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
errorFallback(error);
|
errorFallback(error);
|
||||||
}
|
}
|
||||||
setLoading('github');
|
setLoading(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSignInWithAzure = useMemoizedFn(async () => {
|
const onSignInWithAzure = useMemoizedFn(async () => {
|
||||||
|
@ -182,6 +182,10 @@ const LoginOptions: React.FC<{
|
||||||
Object.keys(Cookies.get()).forEach((cookieName) => {
|
Object.keys(Cookies.get()).forEach((cookieName) => {
|
||||||
Cookies.remove(cookieName);
|
Cookies.remove(cookieName);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//also clear local storage
|
||||||
|
localStorage.clear();
|
||||||
|
sessionStorage.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmitClickPreflight = useMemoizedFn(async (d: { email: string; password: string }) => {
|
const onSubmitClickPreflight = useMemoizedFn(async (d: { email: string; password: string }) => {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { InputTagInput } from '@/components/ui/inputs/InputTagInput';
|
||||||
import { useInviteUser } from '@/api/buster_rest/users';
|
import { useInviteUser } from '@/api/buster_rest/users';
|
||||||
import { validate } from 'email-validator';
|
import { validate } from 'email-validator';
|
||||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||||
|
import uniq from 'lodash/uniq';
|
||||||
|
|
||||||
export const InvitePeopleModal: React.FC<{
|
export const InvitePeopleModal: React.FC<{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
@ -12,10 +13,12 @@ export const InvitePeopleModal: React.FC<{
|
||||||
}> = React.memo(({ open, onClose }) => {
|
}> = React.memo(({ open, onClose }) => {
|
||||||
const [emails, setEmails] = React.useState<string[]>([]);
|
const [emails, setEmails] = React.useState<string[]>([]);
|
||||||
const { mutateAsync: inviteUsers, isPending: inviting } = useInviteUser();
|
const { mutateAsync: inviteUsers, isPending: inviting } = useInviteUser();
|
||||||
|
const [inputText, setInputText] = React.useState<string>('');
|
||||||
const { openErrorMessage } = useBusterNotifications();
|
const { openErrorMessage } = useBusterNotifications();
|
||||||
|
|
||||||
const handleInvite = useMemoizedFn(async () => {
|
const handleInvite = useMemoizedFn(async () => {
|
||||||
await inviteUsers({ emails });
|
const allEmails = uniq([...emails, inputText].filter((email) => !!email && validate(email)));
|
||||||
|
await inviteUsers({ emails: allEmails });
|
||||||
onClose();
|
onClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -26,22 +29,27 @@ export const InvitePeopleModal: React.FC<{
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const isInputTextValidEmail = useMemo(() => {
|
||||||
|
return validate(inputText);
|
||||||
|
}, [inputText]);
|
||||||
|
|
||||||
const memoizedFooter = useMemo(() => {
|
const memoizedFooter = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
primaryButton: {
|
primaryButton: {
|
||||||
text: 'Send invites',
|
text: 'Send invites',
|
||||||
onClick: handleInvite,
|
onClick: handleInvite,
|
||||||
loading: inviting,
|
loading: inviting,
|
||||||
disabled: emails.length === 0
|
disabled: inputText.length ? !isInputTextValidEmail : emails.length === 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [inviting, emails.length]);
|
}, [inviting, isInputTextValidEmail, emails.length, inputText.length]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppModal open={open} onClose={onClose} header={memoizedHeader} footer={memoizedFooter}>
|
<AppModal open={open} onClose={onClose} header={memoizedHeader} footer={memoizedFooter}>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<InputTagInput
|
<InputTagInput
|
||||||
tags={emails}
|
tags={emails}
|
||||||
|
onChangeText={setInputText}
|
||||||
onTagAdd={(v) => {
|
onTagAdd={(v) => {
|
||||||
if (validate(v)) {
|
if (validate(v)) {
|
||||||
setEmails([...emails, v]);
|
setEmails([...emails, v]);
|
||||||
|
|
|
@ -11,6 +11,7 @@ export interface TagInputProps extends VariantProps<typeof inputVariants> {
|
||||||
tags: string[];
|
tags: string[];
|
||||||
onTagAdd?: (tag: string) => void;
|
onTagAdd?: (tag: string) => void;
|
||||||
onTagRemove?: (index: number) => void;
|
onTagRemove?: (index: number) => void;
|
||||||
|
onChangeText?: (text: string) => void;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
maxTags?: number;
|
maxTags?: number;
|
||||||
|
@ -26,6 +27,7 @@ const InputTagInput = React.forwardRef<HTMLInputElement, TagInputProps>(
|
||||||
tags = [],
|
tags = [],
|
||||||
onTagAdd,
|
onTagAdd,
|
||||||
onTagRemove,
|
onTagRemove,
|
||||||
|
onChangeText,
|
||||||
placeholder,
|
placeholder,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
maxTags,
|
maxTags,
|
||||||
|
@ -72,6 +74,7 @@ const InputTagInput = React.forwardRef<HTMLInputElement, TagInputProps>(
|
||||||
} else {
|
} else {
|
||||||
setInputValue(value);
|
setInputValue(value);
|
||||||
}
|
}
|
||||||
|
onChangeText?.(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Focus the container when clicked
|
// Focus the container when clicked
|
||||||
|
|
Loading…
Reference in New Issue