feat: Add bulk admit pending members functionality
- Add "Admit Pending" button to admit all pending members at once - Implement confirmation dialog with options to add members to contacts - Hide button when no pending members exist - Pause auto-refresh during dialog interaction for better UX - Add notification constants for dialog text and actions - Support both "Admit and Add to Contacts" and "Admit Only" workflows - Include comprehensive error handling and success feedback The dialog shows pending member count and allows users to choose whether to add admitted members to their contacts list, streamlining the admission process for organizers.
This commit is contained in:
@@ -54,6 +54,15 @@
|
||||
Refresh
|
||||
<span class="text-xs">({{ countdownTimer }}s)</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="getPendingMembersCount() > 0"
|
||||
class="text-sm bg-gradient-to-b from-blue-100 to-blue-200 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.2)] text-blue-800 px-3 py-1.5 rounded-md"
|
||||
@click="showAdmitAllPendingDialog"
|
||||
>
|
||||
<font-awesome icon="circle-plus" class="text-blue-500" />
|
||||
Admit Pending
|
||||
</button>
|
||||
</div>
|
||||
<ul
|
||||
v-if="membersToShow().length > 0"
|
||||
@@ -86,8 +95,8 @@
|
||||
/>
|
||||
<font-awesome
|
||||
v-if="!member.member.admitted"
|
||||
icon="spinner"
|
||||
class="fa-fw fa-spin-pulse text-slate-400"
|
||||
icon="hourglass-half"
|
||||
class="fa-fw text-slate-400"
|
||||
/>
|
||||
{{ member.name || unnamedMember }}
|
||||
</h3>
|
||||
@@ -205,6 +214,7 @@ import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||
import {
|
||||
NOTIFY_ADD_CONTACT_FIRST,
|
||||
NOTIFY_CONTINUE_WITHOUT_ADDING,
|
||||
NOTIFY_ADMIT_ALL_PENDING,
|
||||
} from "@/constants/notifications";
|
||||
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||
import SetBulkVisibilityDialog from "./SetBulkVisibilityDialog.vue";
|
||||
@@ -744,6 +754,114 @@ export default class MembersList extends Vue {
|
||||
this.startAutoRefresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of pending (non-admitted) members
|
||||
*/
|
||||
getPendingMembersCount(): number {
|
||||
return this.decryptedMembers.filter(
|
||||
(member) => !member.member.admitted && member.did !== this.activeDid,
|
||||
).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of pending members
|
||||
*/
|
||||
getPendingMembers(): DecryptedMember[] {
|
||||
return this.decryptedMembers.filter(
|
||||
(member) => !member.member.admitted && member.did !== this.activeDid,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the admit all pending members dialog
|
||||
*/
|
||||
showAdmitAllPendingDialog() {
|
||||
const pendingCount = this.getPendingMembersCount();
|
||||
|
||||
if (pendingCount === 0) {
|
||||
this.notify.info(
|
||||
"There are no pending members to admit.",
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pause auto-refresh when dialog opens
|
||||
this.stopAutoRefresh();
|
||||
|
||||
const dialogText = NOTIFY_ADMIT_ALL_PENDING.text.replace(
|
||||
"{count}",
|
||||
pendingCount.toString(),
|
||||
);
|
||||
|
||||
this.$notify(
|
||||
{
|
||||
group: "modal",
|
||||
type: "confirm",
|
||||
title: NOTIFY_ADMIT_ALL_PENDING.title,
|
||||
text: dialogText,
|
||||
yesText: NOTIFY_ADMIT_ALL_PENDING.yesText,
|
||||
noText: NOTIFY_ADMIT_ALL_PENDING.noText,
|
||||
onYes: async () => {
|
||||
await this.admitAllPendingMembers(true); // true = add to contacts
|
||||
// Resume auto-refresh after action
|
||||
this.startAutoRefresh();
|
||||
},
|
||||
onNo: async () => {
|
||||
await this.admitAllPendingMembers(false); // false = don't add to contacts
|
||||
// Resume auto-refresh after action
|
||||
this.startAutoRefresh();
|
||||
},
|
||||
onCancel: async () => {
|
||||
// Do nothing - user cancelled
|
||||
// Resume auto-refresh after cancellation
|
||||
this.startAutoRefresh();
|
||||
},
|
||||
},
|
||||
TIMEOUTS.MODAL,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Admit all pending members with optional contact addition
|
||||
*/
|
||||
async admitAllPendingMembers(addToContacts: boolean) {
|
||||
const pendingMembers = this.getPendingMembers();
|
||||
|
||||
if (pendingMembers.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Process each pending member
|
||||
for (const member of pendingMembers) {
|
||||
// Add to contacts if requested and not already a contact
|
||||
if (addToContacts && !this.getContactFor(member.did)) {
|
||||
await this.addAsContact(member);
|
||||
}
|
||||
|
||||
// Admit the member
|
||||
await this.toggleAdmission(member);
|
||||
}
|
||||
|
||||
// Show success message
|
||||
const contactMessage = addToContacts ? " and added to your contacts" : "";
|
||||
this.notify.success(
|
||||
`All ${pendingMembers.length} pending members have been admitted${contactMessage}.`,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
} catch (error) {
|
||||
this.$logAndConsole(
|
||||
"Error admitting all pending members: " + errorStringForLog(error),
|
||||
true,
|
||||
);
|
||||
this.notify.error(
|
||||
"Failed to admit some members. Please try again.",
|
||||
TIMEOUTS.LONG,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
beforeDestroy() {
|
||||
this.stopAutoRefresh();
|
||||
}
|
||||
|
||||
@@ -471,6 +471,14 @@ export const NOTIFY_CONTINUE_WITHOUT_ADDING = {
|
||||
noText: "No, Cancel",
|
||||
};
|
||||
|
||||
// Used in: MembersList.vue (complex modal for admitting all pending members)
|
||||
export const NOTIFY_ADMIT_ALL_PENDING = {
|
||||
title: "Admit All Pending Members",
|
||||
text: "You are about to admit {count} pending member/s. Would you also like to add them to your Contacts list?",
|
||||
yesText: "Admit and Add to Contacts",
|
||||
noText: "Admit Only",
|
||||
};
|
||||
|
||||
// HelpNotificationsView.vue specific constants
|
||||
// Used in: HelpNotificationsView.vue (sendTestWebPushMessage method - not subscribed error)
|
||||
export const NOTIFY_PUSH_NOT_SUBSCRIBED = {
|
||||
|
||||
@@ -60,6 +60,7 @@ import {
|
||||
faHand,
|
||||
faHandHoldingDollar,
|
||||
faHandHoldingHeart,
|
||||
faHourglassHalf,
|
||||
faHouseChimney,
|
||||
faImage,
|
||||
faImagePortrait,
|
||||
@@ -156,6 +157,7 @@ library.add(
|
||||
faHand,
|
||||
faHandHoldingDollar,
|
||||
faHandHoldingHeart,
|
||||
faHourglassHalf,
|
||||
faHouseChimney,
|
||||
faImage,
|
||||
faImagePortrait,
|
||||
|
||||
Reference in New Issue
Block a user