116 lines
3.3 KiB
TypeScript
116 lines
3.3 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import type { Database, MemberRole } from '~/types/supabase'
|
|
|
|
export type Member = Database['public']['Tables']['members']['Row']
|
|
|
|
export const useAuthStore = defineStore('auth', () => {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const supabase = useSupabaseClient() as any
|
|
const user = useSupabaseUser()
|
|
const member = ref<Member | null>(null)
|
|
const userNames = ref<Record<string, string>>({})
|
|
|
|
const role = computed<MemberRole | null>(() => member.value?.role ?? null)
|
|
|
|
function hasRequiredRole(requiredRoles: MemberRole[]): boolean {
|
|
if (!role.value) return false
|
|
return requiredRoles.includes(role.value)
|
|
}
|
|
|
|
const isAdmin = computed(() => hasRequiredRole(['admin']))
|
|
const isBoatswain = computed(() => hasRequiredRole(['admin', 'boatswain']))
|
|
|
|
const displayName = computed(() => {
|
|
if (!member.value) return user.value?.email ?? ''
|
|
const { first_name, last_name } = member.value
|
|
if (first_name || last_name) return `${first_name} ${last_name}`.trim()
|
|
return user.value?.email ?? ''
|
|
})
|
|
|
|
async function fetchMember() {
|
|
if (!user.value) { member.value = null; return }
|
|
const { data } = await supabase
|
|
.from('members')
|
|
.select('*')
|
|
.eq('user_id', user.value.id)
|
|
.single()
|
|
member.value = data
|
|
}
|
|
|
|
// Returns cached name or triggers async fetch; updates reactively when resolved.
|
|
function getUserNameById(id: string | null | undefined): string {
|
|
if (!id) return 'No User'
|
|
if (!userNames.value[id]) {
|
|
userNames.value[id] = 'Loading...'
|
|
supabase
|
|
.from('members')
|
|
.select('first_name, last_name')
|
|
.eq('user_id', id)
|
|
.single()
|
|
.then(({ data }: { data: Pick<Member, 'first_name' | 'last_name'> | null }) => {
|
|
if (data) {
|
|
userNames.value[id] = `${data.first_name} ${data.last_name}`.trim() || 'Unknown'
|
|
} else {
|
|
userNames.value[id] = 'Unknown'
|
|
}
|
|
})
|
|
}
|
|
return userNames.value[id]!
|
|
}
|
|
|
|
async function signOut() {
|
|
await supabase.auth.signOut()
|
|
member.value = null
|
|
await navigateTo('/login')
|
|
}
|
|
|
|
async function sendMagicLink(email: string) {
|
|
const { error } = await supabase.auth.signInWithOtp({
|
|
email,
|
|
options: { emailRedirectTo: window.location.origin + '/auth/callback' },
|
|
})
|
|
if (error) throw error
|
|
}
|
|
|
|
async function sendOtp(email: string) {
|
|
const { error } = await supabase.auth.signInWithOtp({ email })
|
|
if (error) throw error
|
|
}
|
|
|
|
async function verifyOtp(email: string, token: string) {
|
|
const { error } = await supabase.auth.verifyOtp({ email, token, type: 'email' })
|
|
if (error) throw error
|
|
await fetchMember()
|
|
}
|
|
|
|
async function updateName(firstName: string, lastName: string) {
|
|
if (!user.value) return
|
|
const { error } = await supabase
|
|
.from('members')
|
|
.update({ first_name: firstName, last_name: lastName })
|
|
.eq('user_id', user.value.id)
|
|
if (error) throw error
|
|
if (member.value) {
|
|
member.value.first_name = firstName
|
|
member.value.last_name = lastName
|
|
}
|
|
}
|
|
|
|
return {
|
|
user,
|
|
member,
|
|
role,
|
|
isAdmin,
|
|
isBoatswain,
|
|
displayName,
|
|
hasRequiredRole,
|
|
getUserNameById,
|
|
fetchMember,
|
|
sendMagicLink,
|
|
sendOtp,
|
|
verifyOtp,
|
|
updateName,
|
|
signOut,
|
|
}
|
|
})
|