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">
|
<form class="edit-form" @submit.prevent="submitEdit">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Name</label>
|
<label>Name</label>
|
||||||
<input v-model="form.name" type="text" required />
|
<input v-model="form.name" type="text" required >
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Email</label>
|
<label>Email</label>
|
||||||
<input v-model="form.email" type="email" required />
|
<input v-model="form.email" type="email" required >
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Circle</label>
|
<label>Circle</label>
|
||||||
|
|
@ -106,8 +106,19 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="meta-row">
|
<div class="meta-row">
|
||||||
<dt>Slack invite</dt>
|
<dt>Slack invite</dt>
|
||||||
<dd :class="member.slackInvited ? 'status-ok' : 'status-dim'">
|
<dd v-if="member.slackInvited" class="status-ok">
|
||||||
{{ member.slackInvited ? "Invited" : "Pending" }}
|
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>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="member.helcimCustomerId" class="meta-row">
|
<div v-if="member.helcimCustomerId" class="meta-row">
|
||||||
|
|
@ -155,12 +166,6 @@
|
||||||
{{ member.onboarding?.completedAt ? formatDate(member.onboarding.completedAt) : 'In progress' }}
|
{{ member.onboarding?.completedAt ? formatDate(member.onboarding.completedAt) : 'In progress' }}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta-row">
|
|
||||||
<dt>Slack status</dt>
|
|
||||||
<dd :class="slackStatusClass">
|
|
||||||
{{ member.slackInviteStatus || 'none' }}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
</dl>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
@ -356,12 +361,31 @@ const hasBoardEngaged = computed(() => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const slackStatusClass = computed(() => {
|
const markingSlackInvited = ref(false)
|
||||||
const status = member.value?.slackInviteStatus
|
|
||||||
if (status === 'joined') return 'status-ok'
|
async function markSlackInvited() {
|
||||||
if (status === 'invited') return 'status-dim'
|
if (!member.value || markingSlackInvited.value) return
|
||||||
return 'status-dim'
|
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
|
// Activity log
|
||||||
const activityEntries = ref([])
|
const activityEntries = ref([])
|
||||||
|
|
@ -553,6 +577,32 @@ onMounted(loadActivity)
|
||||||
word-break: break-all;
|
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 {
|
.mono {
|
||||||
font-family: "Commit Mono", monospace;
|
font-family: "Commit Mono", monospace;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,11 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span :class="member.slackInvited ? 'status-ok' : 'status-dim'">
|
<span v-if="member.slackInvited" class="status-ok">
|
||||||
{{ member.slackInvited ? "Invited" : "Pending" }}
|
Invited {{ formatDate(member.slackInvitedAt) }}
|
||||||
|
</span>
|
||||||
|
<span v-else class="status-dim">
|
||||||
|
Not yet invited
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="col-mono col-date">
|
<td class="col-mono col-date">
|
||||||
|
|
@ -135,8 +138,12 @@
|
||||||
<NuxtLink :to="`/admin/members/${member._id}`" class="link-btn" @click.stop
|
<NuxtLink :to="`/admin/members/${member._id}`" class="link-btn" @click.stop
|
||||||
>View</NuxtLink
|
>View</NuxtLink
|
||||||
>
|
>
|
||||||
<button class="link-btn" @click.stop="sendSlackInvite(member)">
|
<button
|
||||||
Slack
|
v-if="!member.slackInvited"
|
||||||
|
class="link-btn"
|
||||||
|
@click.stop="markSlackInvited(member)"
|
||||||
|
>
|
||||||
|
Mark as Slack invited
|
||||||
</button>
|
</button>
|
||||||
<button class="link-btn" @click.stop="editMember(member)">Edit</button>
|
<button class="link-btn" @click.stop="editMember(member)">Edit</button>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -829,8 +836,24 @@ const submitInvites = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Existing actions ---
|
// --- Existing actions ---
|
||||||
const sendSlackInvite = (member) => {
|
const markSlackInvited = async (member) => {
|
||||||
console.log("Send Slack invite to:", member.email);
|
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 ---
|
// --- Edit Member ---
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue