improvement: loading state added to buttons who interact with backend.

This commit is contained in:
2026-07-17 10:58:04 +03:30
parent 68fc9d5d6d
commit d2ad1fd7b6
23 changed files with 240 additions and 144 deletions

View File

@@ -597,39 +597,37 @@ export function StaffPage() {
variant="outline"
size="sm"
isLoading={copyingInviteMembershipId === lastInviteInfo.membershipId}
onClick={() => {
onClick={async () => {
const member = members.find((item) => item.id === lastInviteInfo.membershipId);
if (member) {
void copyStaffInviteLink(member);
await copyStaffInviteLink(member);
return;
}
void (async () => {
setCopyingInviteMembershipId(lastInviteInfo.membershipId);
toast.setError('');
try {
const res = await staffApi.getInvitationLink(lastInviteInfo.membershipId);
if (currentOrganization?.id) {
const nextLinks = {
...pendingInviteLinks,
[lastInviteInfo.membershipId]: {
membershipId: lastInviteInfo.membershipId,
email: lastInviteInfo.email,
invitationUrl: res.data.invitationUrl,
},
};
setPendingInviteLinks(nextLinks);
writeStoredInviteLinks(currentOrganization.id, nextLinks);
}
setLastInviteInfo({ ...lastInviteInfo, invitationUrl: res.data.invitationUrl });
await navigator.clipboard.writeText(res.data.invitationUrl);
setCopiedInviteMembershipId(lastInviteInfo.membershipId);
setTimeout(() => setCopiedInviteMembershipId(null), 1500);
} catch (e) {
toast.showError(getUserFacingError(e, tErrors, t('errorCopyInvite')));
} finally {
setCopyingInviteMembershipId(null);
setCopyingInviteMembershipId(lastInviteInfo.membershipId);
toast.setError('');
try {
const res = await staffApi.getInvitationLink(lastInviteInfo.membershipId);
if (currentOrganization?.id) {
const nextLinks = {
...pendingInviteLinks,
[lastInviteInfo.membershipId]: {
membershipId: lastInviteInfo.membershipId,
email: lastInviteInfo.email,
invitationUrl: res.data.invitationUrl,
},
};
setPendingInviteLinks(nextLinks);
writeStoredInviteLinks(currentOrganization.id, nextLinks);
}
})();
setLastInviteInfo({ ...lastInviteInfo, invitationUrl: res.data.invitationUrl });
await navigator.clipboard.writeText(res.data.invitationUrl);
setCopiedInviteMembershipId(lastInviteInfo.membershipId);
setTimeout(() => setCopiedInviteMembershipId(null), 1500);
} catch (e) {
toast.showError(getUserFacingError(e, tErrors, t('errorCopyInvite')));
} finally {
setCopyingInviteMembershipId(null);
}
}}
>
{copiedInviteMembershipId === lastInviteInfo.membershipId
@@ -661,7 +659,7 @@ export function StaffPage() {
canShareInviteLink={canShareStaffInviteLink}
canEnable={canEnableStaff}
canDisable={canDisableStaff}
onCopyInviteLink={(member) => void copyStaffInviteLink(member)}
onCopyInviteLink={(member) => copyStaffInviteLink(member)}
onEnable={setEnableTarget}
onDisable={setDisableTarget}
onEdit={openEdit}
@@ -747,7 +745,7 @@ export function StaffPage() {
type="button"
className="p-2 rounded-md text-text-secondary hover:bg-background-card/80 hover:text-text-primary disabled:opacity-50"
disabled={copyingInviteMembershipId === m.id}
onClick={() => void copyStaffInviteLink(m)}
onClick={() => copyStaffInviteLink(m)}
aria-label={t('copyInviteLink')}
title={t('copyInviteLinkTitle')}
>
@@ -930,7 +928,7 @@ export function StaffPage() {
type="button"
isLoading={inviteLoading}
disabled={!inviteEmail.trim() || !inviteName.trim()}
onClick={() => void submitInvite(false)}
onClick={() => submitInvite(false)}
>
{t('sendInvite')}
</Button>
@@ -941,7 +939,7 @@ export function StaffPage() {
type="button"
variant="outline"
isLoading={inviteLoading}
onClick={() => void submitInvite(false)}
onClick={() => submitInvite(false)}
>
{t('skipForNow')}
</Button>
@@ -949,7 +947,7 @@ export function StaffPage() {
type="button"
isLoading={inviteLoading}
disabled={Boolean(inviteHoursValidationError)}
onClick={() => void submitInvite(true)}
onClick={() => submitInvite(true)}
>
{t('sendInvite')}
</Button>
@@ -1004,7 +1002,7 @@ export function StaffPage() {
variant="primary"
isLoading={enablingMembershipId === enableTarget.id}
disabled={Boolean(enablingMembershipId) || !hasAvailableSeat}
onClick={() => void confirmEnableMember()}
onClick={() => confirmEnableMember()}
>
{t('enableMemberButton')}
</Button>
@@ -1054,7 +1052,7 @@ export function StaffPage() {
variant="danger"
isLoading={disablingMembershipId === disableTarget.id}
disabled={Boolean(disablingMembershipId)}
onClick={() => void confirmDisableMember()}
onClick={() => confirmDisableMember()}
>
{t('disableMemberButton')}
</Button>
@@ -1136,7 +1134,7 @@ export function StaffPage() {
{tCommon('next')}
</Button>
) : (
<Button type="button" isLoading={editLoading} onClick={() => void submitEdit()}>
<Button type="button" isLoading={editLoading} onClick={() => submitEdit()}>
{tCommon('save')}
</Button>
)
@@ -1145,7 +1143,7 @@ export function StaffPage() {
type="button"
isLoading={editLoading}
disabled={Boolean(editHoursValidationError)}
onClick={() => void submitEdit()}
onClick={() => submitEdit()}
>
{tCommon('save')}
</Button>