refactor invitation handling: remove email parameter from createInviteLink and update UI to use invite link

This commit is contained in:
jableader 2025-12-13 15:35:30 +11:00
parent 4edeb6dfe4
commit e873bbc3f8
4 changed files with 19 additions and 73 deletions

View file

@ -14,21 +14,13 @@ export async function acceptInvitation(token: string): Promise<Household> {
return { id: h.id, name: h.name, slug: h.slug } return { id: h.id, name: h.name, slug: h.slug }
} }
export async function sendInvitation(householdSlug: string, email: string): Promise<void> { export async function createInviteLink(householdSlug: string): Promise<string> {
const res = await api.POST('/api/v1/households/{householdSlug}/invitations', { const { data, error, response } = await api.POST('/api/v1/households/{householdSlug}/invitations', { params: { path: { householdSlug } } })
params: { path: { householdSlug } },
body: { email },
})
if (!res.response.ok) throw new Error(`${res.response.status} ${res.response.statusText || 'HTTP error'}`)
}
export async function createInviteLink(householdSlug: string, email: string): Promise<string> { if (!response.ok) {
const res = await api.POST('/api/v1/households/{householdSlug}/invitations', { throw new Error(`${response.status} ${response.statusText || 'HTTP error'}${error ? `: ${String(error)}` : ''}`)
params: { path: { householdSlug } }, }
body: { email }, const inviteLink = data?.invite_link
})
if (!res.response.ok) throw new Error(`${res.response.status} ${res.response.statusText || 'HTTP error'}`)
const inviteLink = res.data?.invite_link
if (typeof inviteLink !== 'string') throw new Error('Invalid response: missing invite_link') if (typeof inviteLink !== 'string') throw new Error('Invalid response: missing invite_link')
return inviteLink return inviteLink
} }

View file

@ -462,11 +462,6 @@ export interface components {
/** Name */ /** Name */
name: string; name: string;
}; };
/** CreateInvitationBody */
CreateInvitationBody: {
/** Email */
email: string;
};
/** CurrentShoppingList */ /** CurrentShoppingList */
CurrentShoppingList: { CurrentShoppingList: {
/** Outstandingitems */ /** Outstandingitems */
@ -1341,11 +1336,7 @@ export interface operations {
}; };
cookie?: never; cookie?: never;
}; };
requestBody: { requestBody?: never;
content: {
"application/json": components["schemas"]["CreateInvitationBody"];
};
};
responses: { responses: {
/** @description Successful Response */ /** @description Successful Response */
200: { 200: {

View file

@ -3,29 +3,14 @@
<h1>Household Members</h1> <h1>Household Members</h1>
<section> <section>
<h2>Invite a member</h2> <h2>Invite a member</h2>
<form @submit.prevent="onInvite"> <p class="muted">Share an invite link with the person you want to add.</p>
<label for="email">Email</label> <button
<input type="button"
id="email" :disabled="submitting"
v-model="email" @click="onCopyInviteLink"
type="email" >
required Copy Invite Link
autocomplete="email" </button>
>
<button
type="submit"
:disabled="submitting"
>
Send Invitation
</button>
<button
type="button"
:disabled="submitting"
@click="onCopyInviteLink"
>
Copy Invite Link
</button>
</form>
<p <p
v-if="message" v-if="message"
class="message" class="message"
@ -62,12 +47,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from 'vue' import { ref, onMounted, computed } from 'vue'
import { sendInvitation, createInviteLink } from '@/api/invitations' import { createInviteLink } from '@/api/invitations'
import { listMembers, type Member } from '@/api/households' import { listMembers, type Member } from '@/api/households'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useAlert } from '@/composables/useAlert' import { useAlert } from '@/composables/useAlert'
const email = ref('')
const submitting = ref(false) const submitting = ref(false)
const message = ref('') const message = ref('')
const error = ref('') const error = ref('')
@ -84,23 +68,6 @@ onMounted(async () => {
} }
}) })
async function onInvite() {
message.value = ''
error.value = ''
submitting.value = true
try {
const slug = householdSlug.value
if (!slug) throw new Error('No household selected')
await sendInvitation(slug, email.value.trim())
message.value = 'Invitation sent.'
email.value = ''
} catch (e) {
error.value = e instanceof Error ? e.message : 'Failed to send invitation'
} finally {
submitting.value = false
}
}
async function onCopyInviteLink() { async function onCopyInviteLink() {
message.value = '' message.value = ''
error.value = '' error.value = ''
@ -108,15 +75,11 @@ async function onCopyInviteLink() {
try { try {
const slug = householdSlug.value const slug = householdSlug.value
if (!slug) throw new Error('No household selected') if (!slug) throw new Error('No household selected')
const emailValue = email.value.trim() const inviteLink = await createInviteLink(slug)
if (!emailValue) throw new Error('Please enter an email address')
const inviteLink = await createInviteLink(slug, emailValue)
await navigator.clipboard.writeText(inviteLink) await navigator.clipboard.writeText(inviteLink)
showAlert({ type: 'success', heading: 'Success', message: 'Invite link copied to clipboard!' }) showAlert({ type: 'success', heading: 'Success', message: 'Invite link copied to clipboard!' })
scheduleAutoDismiss(3000) scheduleAutoDismiss(3000)
email.value = ''
} catch (e) { } catch (e) {
error.value = e instanceof Error ? e.message : 'Failed to copy invite link' error.value = e instanceof Error ? e.message : 'Failed to copy invite link'
} finally { } finally {

View file

@ -17,7 +17,7 @@
</section> </section>
<section> <section>
<h2>Join an existing household</h2> <h2>Join an existing household</h2>
<p>To join a household, ask an existing member to send an invitation to your email address.</p> <p>To join a household, ask an existing member to share an invitation link with you.</p>
</section> </section>
</div> </div>
</template> </template>