feat(admin/members): mark-as-Slack-invited button + date display
Replaces the placeholder Slack-invite handler with a call to the new PATCH /api/admin/members/:id/slack-status endpoint. Status labels are reworded to match reality (no Slack API call from this app): - Pending → Not yet invited - Invited → Invited <slackInvitedAt> - Action button copy → 'Mark as Slack invited' - Removes slackInviteStatus reads from the member detail page (the remaining repo-wide sweep lands in the cleanup task).
This commit is contained in:
parent
0981596ea2
commit
c2999810c6
2 changed files with 95 additions and 22 deletions
|
|
@ -39,11 +39,11 @@
|
|||
<form class="edit-form" @submit.prevent="submitEdit">
|
||||
<div class="field">
|
||||
<label>Name</label>
|
||||
<input v-model="form.name" type="text" required />
|
||||
<input v-model="form.name" type="text" required >
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Email</label>
|
||||
<input v-model="form.email" type="email" required />
|
||||
<input v-model="form.email" type="email" required >
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Circle</label>
|
||||
|
|
@ -106,8 +106,19 @@
|
|||
</div>
|
||||
<div class="meta-row">
|
||||
<dt>Slack invite</dt>
|
||||
<dd :class="member.slackInvited ? 'status-ok' : 'status-dim'">
|
||||
{{ member.slackInvited ? "Invited" : "Pending" }}
|
||||
<dd v-if="member.slackInvited" class="status-ok">
|
||||
Invited {{ formatDate(member.slackInvitedAt) }}
|
||||
</dd>
|
||||
<dd v-else class="meta-action">
|
||||
<span class="status-dim">Not yet invited</span>
|
||||
<button
|
||||
type="button"
|
||||
class="link-btn"
|
||||
:disabled="markingSlackInvited"
|
||||
@click="markSlackInvited"
|
||||
>
|
||||
{{ markingSlackInvited ? "Marking…" : "Mark as Slack invited" }}
|
||||
</button>
|
||||
</dd>
|
||||
</div>
|
||||
<div v-if="member.helcimCustomerId" class="meta-row">
|
||||
|
|
@ -155,12 +166,6 @@
|
|||
{{ member.onboarding?.completedAt ? formatDate(member.onboarding.completedAt) : 'In progress' }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<dt>Slack status</dt>
|
||||
<dd :class="slackStatusClass">
|
||||
{{ member.slackInviteStatus || 'none' }}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
|
|
@ -356,12 +361,31 @@ const hasBoardEngaged = computed(() => {
|
|||
)
|
||||
})
|
||||
|
||||
const slackStatusClass = computed(() => {
|
||||
const status = member.value?.slackInviteStatus
|
||||
if (status === 'joined') return 'status-ok'
|
||||
if (status === 'invited') return 'status-dim'
|
||||
return 'status-dim'
|
||||
})
|
||||
const markingSlackInvited = ref(false)
|
||||
|
||||
async function markSlackInvited() {
|
||||
if (!member.value || markingSlackInvited.value) return
|
||||
markingSlackInvited.value = true
|
||||
try {
|
||||
const res = await $fetch(
|
||||
`/api/admin/members/${route.params.id}/slack-status`,
|
||||
{
|
||||
method: "PATCH",
|
||||
body: { slackInvited: true },
|
||||
},
|
||||
)
|
||||
member.value = { ...member.value, ...res.member }
|
||||
toast.add({ title: "Marked as Slack invited", color: "success" })
|
||||
} catch (err) {
|
||||
toast.add({
|
||||
title: "Failed to mark Slack invited",
|
||||
description: err.data?.statusMessage || err.message,
|
||||
color: "error",
|
||||
})
|
||||
} finally {
|
||||
markingSlackInvited.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Activity log
|
||||
const activityEntries = ref([])
|
||||
|
|
@ -553,6 +577,32 @@ onMounted(loadActivity)
|
|||
word-break: break-all;
|
||||
}
|
||||
|
||||
.meta-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.link-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--candle);
|
||||
cursor: pointer;
|
||||
font-family: "Commit Mono", monospace;
|
||||
font-size: 11px;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.link-btn:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.link-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: "Commit Mono", monospace;
|
||||
font-size: 11px;
|
||||
|
|
|
|||
|
|
@ -124,8 +124,11 @@
|
|||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span :class="member.slackInvited ? 'status-ok' : 'status-dim'">
|
||||
{{ member.slackInvited ? "Invited" : "Pending" }}
|
||||
<span v-if="member.slackInvited" class="status-ok">
|
||||
Invited {{ formatDate(member.slackInvitedAt) }}
|
||||
</span>
|
||||
<span v-else class="status-dim">
|
||||
Not yet invited
|
||||
</span>
|
||||
</td>
|
||||
<td class="col-mono col-date">
|
||||
|
|
@ -135,8 +138,12 @@
|
|||
<NuxtLink :to="`/admin/members/${member._id}`" class="link-btn" @click.stop
|
||||
>View</NuxtLink
|
||||
>
|
||||
<button class="link-btn" @click.stop="sendSlackInvite(member)">
|
||||
Slack
|
||||
<button
|
||||
v-if="!member.slackInvited"
|
||||
class="link-btn"
|
||||
@click.stop="markSlackInvited(member)"
|
||||
>
|
||||
Mark as Slack invited
|
||||
</button>
|
||||
<button class="link-btn" @click.stop="editMember(member)">Edit</button>
|
||||
</td>
|
||||
|
|
@ -829,8 +836,24 @@ const submitInvites = async () => {
|
|||
};
|
||||
|
||||
// --- Existing actions ---
|
||||
const sendSlackInvite = (member) => {
|
||||
console.log("Send Slack invite to:", member.email);
|
||||
const markSlackInvited = async (member) => {
|
||||
try {
|
||||
const res = await $fetch(
|
||||
`/api/admin/members/${member._id}/slack-status`,
|
||||
{
|
||||
method: "PATCH",
|
||||
body: { slackInvited: true },
|
||||
},
|
||||
);
|
||||
Object.assign(member, res.member);
|
||||
toast.add({ title: "Marked as Slack invited", color: "success" });
|
||||
} catch (err) {
|
||||
toast.add({
|
||||
title: "Failed to mark Slack invited",
|
||||
description: err.data?.statusMessage || err.message,
|
||||
color: "error",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// --- Edit Member ---
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue