HouseholdSettings.vue — fixed template, added proper imports, typed members, and rendered current members list.

This commit is contained in:
jableader 2025-11-01 14:49:57 +11:00
parent b75ece2fc8
commit 6d95764516
4 changed files with 79 additions and 2 deletions

View file

@ -165,6 +165,7 @@ Progress Log (Nov 1, 2025)
- Added `useAuth.createAccount` with state update and tests for it; implemented `CreateAccount.vue` with form and navigation. - Added `useAuth.createAccount` with state update and tests for it; implemented `CreateAccount.vue` with form and navigation.
- Implemented Invitation Accept flow: added `src/api/invitations.ts` with `acceptInvitation` (temporary raw fetch), `InvitationAccept.vue` reads token and redirects to household; added `tests/invitations.api.test.ts`. - Implemented Invitation Accept flow: added `src/api/invitations.ts` with `acceptInvitation` (temporary raw fetch), `InvitationAccept.vue` reads token and redirects to household; added `tests/invitations.api.test.ts`.
- Next: Implement Household Settings (invite members form), then remove legacy Person UI. - Next: Implement Household Settings (invite members form), then remove legacy Person UI.
- Added a Settings link to `HouseholdSwitcher.vue` to surface the `household-settings` route for easier discovery.
--- ---

18
src/api/households.ts Normal file
View file

@ -0,0 +1,18 @@
import { api, fetchApi } from '@/api/client'
import type { components, paths } from '@/api/types'
export type Member = components['schemas']['User']
// Prefer typed endpoint if exists, fallback to raw fetch for now
export async function listMembers(): Promise<Member[]> {
// Try typed path if openapi exposes it
const hasTyped: boolean = Boolean((api as unknown as { GET?: unknown }).GET)
if (hasTyped) {
// Our OpenAPI file doesn't specify this route yet, so default to raw fetch
}
const resp = await fetchApi('/api/v1/households/members', { method: 'GET' })
if (!resp.ok) throw new Error(`${resp.status} ${resp.statusText || 'HTTP error'}`)
const data = await resp.json().catch(() => null)
const arr = Array.isArray(data) ? data : []
return arr.filter((m): m is Member => typeof m === 'object' && m !== null && typeof (m as { id: unknown }).id === 'number')
}

View file

@ -35,7 +35,18 @@
<section> <section>
<h2>Current members</h2> <h2>Current members</h2>
<p class="muted"> <ul v-if="members.length > 0">
<li
v-for="m in members"
:key="m.id"
>
{{ m.displayName }} <small>({{ m.email }})</small>
</li>
</ul>
<p
v-else
class="muted"
>
Listing members will be added once the backend endpoint is available. Listing members will be added once the backend endpoint is available.
</p> </p>
</section> </section>
@ -43,13 +54,23 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, onMounted } from 'vue'
import { sendInvitation } from '@/api/invitations' import { sendInvitation } from '@/api/invitations'
import { listMembers, type Member } from '@/api/households'
const email = ref('') const email = ref('')
const submitting = ref(false) const submitting = ref(false)
const message = ref('') const message = ref('')
const error = ref('') const error = ref('')
const members = ref<Member[]>([])
onMounted(async () => {
try {
members.value = await listMembers()
} catch (_e) {
// ignore; backend may not support this yet
}
})
async function onInvite() { async function onInvite() {
message.value = '' message.value = ''

View file

@ -0,0 +1,37 @@
import { describe, it, expect } from 'vitest'
import { server, http, HttpResponse } from './test-setup'
import { loginWithPassword } from '@/api/auth'
import { setHouseholdSlugProvider } from '@/api/client'
import { listMembers } from '@/api/households'
describe('households api (list members)', () => {
it('GETs members with Authorization and X-Household-Slug headers', async () => {
// Simulate login token
server.use(
http.post('*/api/v1/auth/login', () =>
HttpResponse.json({ accessToken: 'tokLM', tokenType: 'bearer', user: { id: 3, email: 'u@e', displayName: 'User' } })
)
)
await loginWithPassword('u@e', 'pw')
// Provide a household slug for header injection
setHouseholdSlugProvider(() => 'the-smiths')
server.use(
http.get('*/api/v1/households/members', ({ request }) => {
const auth = request.headers.get('authorization')
expect(auth?.toLowerCase()).toBe('bearer toklm')
const slug = request.headers.get('x-household-slug')
expect(slug).toBe('the-smiths')
return HttpResponse.json([
{ id: 10, email: 'a@example.com', displayName: 'Alice' },
{ id: 11, email: 'b@example.com', displayName: 'Bob' },
])
})
)
const members = await listMembers()
expect(members.length).toBe(2)
expect(members[0]?.displayName).toBe('Alice')
})
})