Initial project scaffolding
This commit is contained in:
104
app/app.vue
Normal file
104
app/app.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<IonApp>
|
||||
<IonMenu content-id="main-content" menu-id="main-menu">
|
||||
<IonHeader>
|
||||
<IonToolbar color="primary">
|
||||
<IonTitle>OYS Borrow a Boat</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent>
|
||||
<IonList lines="none">
|
||||
<!-- All authenticated users -->
|
||||
<IonItem button router-link="/" router-direction="root" @click="closeMenu">
|
||||
<IonIcon slot="start" :icon="homeOutline" />
|
||||
<IonLabel>Home</IonLabel>
|
||||
</IonItem>
|
||||
<IonItem button router-link="/schedule" router-direction="root" @click="closeMenu">
|
||||
<IonIcon slot="start" :icon="calendarOutline" />
|
||||
<IonLabel>Schedule</IonLabel>
|
||||
</IonItem>
|
||||
<IonItem button router-link="/boat" router-direction="root" @click="closeMenu">
|
||||
<IonIcon slot="start" :icon="boatOutline" />
|
||||
<IonLabel>Boats</IonLabel>
|
||||
</IonItem>
|
||||
<IonItem button router-link="/reference" router-direction="root" @click="closeMenu">
|
||||
<IonIcon slot="start" :icon="bookOutline" />
|
||||
<IonLabel>Reference</IonLabel>
|
||||
</IonItem>
|
||||
<IonItem button router-link="/profile" router-direction="root" @click="closeMenu">
|
||||
<IonIcon slot="start" :icon="personOutline" />
|
||||
<IonLabel>Profile</IonLabel>
|
||||
</IonItem>
|
||||
|
||||
<!-- Boatswain + Admin: schedule management -->
|
||||
<template v-if="authStore.isBoatswain">
|
||||
<IonItemDivider>
|
||||
<IonLabel>Management</IonLabel>
|
||||
</IonItemDivider>
|
||||
<IonItem button router-link="/schedule/manage" router-direction="root" @click="closeMenu">
|
||||
<IonIcon slot="start" :icon="calendarNumberOutline" />
|
||||
<IonLabel>Manage Schedule</IonLabel>
|
||||
</IonItem>
|
||||
</template>
|
||||
|
||||
<!-- Admin only -->
|
||||
<template v-if="authStore.isAdmin">
|
||||
<IonItem button router-link="/admin/user" router-direction="root" @click="closeMenu">
|
||||
<IonIcon slot="start" :icon="peopleOutline" />
|
||||
<IonLabel>Users</IonLabel>
|
||||
</IonItem>
|
||||
<IonItem button router-link="/admin/boat" router-direction="root" @click="closeMenu">
|
||||
<IonIcon slot="start" :icon="constructOutline" />
|
||||
<IonLabel>Manage Boats</IonLabel>
|
||||
</IonItem>
|
||||
</template>
|
||||
|
||||
<!-- Sign out -->
|
||||
<IonItemDivider />
|
||||
<IonItem button @click="signOut">
|
||||
<IonIcon slot="start" :icon="logOutOutline" />
|
||||
<IonLabel>Sign Out</IonLabel>
|
||||
<IonNote slot="end">{{ authStore.displayName }}</IonNote>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
</IonContent>
|
||||
</IonMenu>
|
||||
|
||||
<IonRouterOutlet id="main-content" />
|
||||
</IonApp>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
IonApp, IonMenu, IonHeader, IonToolbar, IonTitle,
|
||||
IonContent, IonList, IonItem, IonItemDivider, IonIcon, IonLabel, IonNote,
|
||||
IonRouterOutlet, menuController,
|
||||
} from '@ionic/vue'
|
||||
import {
|
||||
homeOutline, calendarOutline, boatOutline, personOutline,
|
||||
bookOutline, calendarNumberOutline, peopleOutline, constructOutline, logOutOutline,
|
||||
} from 'ionicons/icons'
|
||||
import { useAuthStore } from '~/stores/auth'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
async function closeMenu() {
|
||||
await menuController.close('main-menu')
|
||||
}
|
||||
|
||||
async function signOut() {
|
||||
await closeMenu()
|
||||
await authStore.signOut()
|
||||
}
|
||||
|
||||
// Fetch member profile on app load (populates role for menu visibility)
|
||||
onMounted(() => {
|
||||
if (authStore.user) authStore.fetchMember()
|
||||
})
|
||||
|
||||
// Re-fetch when user changes (e.g., after login)
|
||||
watch(() => authStore.user, (u) => {
|
||||
if (u) authStore.fetchMember()
|
||||
else authStore.member = null
|
||||
})
|
||||
</script>
|
||||
15
app/composables/useIonToast.ts
Normal file
15
app/composables/useIonToast.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { toastController } from '@ionic/vue'
|
||||
|
||||
export function useIonToast() {
|
||||
async function showToast(message: string, color: 'success' | 'danger' | 'warning' = 'success') {
|
||||
const toast = await toastController.create({
|
||||
message,
|
||||
duration: 2000,
|
||||
color,
|
||||
position: 'bottom',
|
||||
})
|
||||
await toast.present()
|
||||
}
|
||||
|
||||
return { showToast }
|
||||
}
|
||||
10
app/middleware/auth.ts
Normal file
10
app/middleware/auth.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export default defineNuxtRouteMiddleware((to) => {
|
||||
const user = useSupabaseUser()
|
||||
|
||||
const publicRoutes = ['/login', '/signup', '/auth/callback']
|
||||
if (publicRoutes.includes(to.path)) return
|
||||
|
||||
if (!user.value) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
})
|
||||
13
app/pages/auth/callback.vue
Normal file
13
app/pages/auth/callback.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<IonPage>
|
||||
<IonContent class="ion-padding ion-text-center">
|
||||
<p>Signing you in...</p>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IonPage, IonContent } from '@ionic/vue'
|
||||
|
||||
definePageMeta({ layout: false })
|
||||
</script>
|
||||
22
app/pages/index.vue
Normal file
22
app/pages/index.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar color="primary">
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>Home</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent class="ion-padding">
|
||||
<h2>Welcome to OYS Borrow a Boat</h2>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
IonPage, IonHeader, IonToolbar, IonTitle, IonContent,
|
||||
IonButtons, IonMenuButton,
|
||||
} from '@ionic/vue'
|
||||
</script>
|
||||
18
app/pages/login.vue
Normal file
18
app/pages/login.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar color="primary">
|
||||
<IonTitle>Sign In</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent class="ion-padding">
|
||||
<!-- TODO: Auth form -->
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/vue'
|
||||
|
||||
definePageMeta({ layout: false })
|
||||
</script>
|
||||
7
app/plugins/ionic.client.ts
Normal file
7
app/plugins/ionic.client.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { IonicVue } from '@ionic/vue'
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
nuxtApp.vueApp.use(IonicVue, {
|
||||
mode: 'md', // Use Material Design style on all platforms for consistency
|
||||
})
|
||||
})
|
||||
115
app/stores/auth.ts
Normal file
115
app/stores/auth.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
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,
|
||||
}
|
||||
})
|
||||
23
app/stores/boat.ts
Normal file
23
app/stores/boat.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
// TODO: Replace with generated Supabase types after `npx supabase gen types typescript`
|
||||
interface Boat {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
active: boolean
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export const useBoatStore = defineStore('boat', () => {
|
||||
const supabase = useSupabaseClient()
|
||||
const boats = ref<Map<string, Boat>>(new Map())
|
||||
|
||||
async function fetchBoats() {
|
||||
const { data, error } = await supabase.from('boats').select('*').order('name')
|
||||
if (error) throw error
|
||||
boats.value = new Map((data as Boat[]).map((b) => [b.id, b]))
|
||||
}
|
||||
|
||||
return { boats, fetchBoats }
|
||||
})
|
||||
360
app/types/supabase.ts
Normal file
360
app/types/supabase.ts
Normal file
@@ -0,0 +1,360 @@
|
||||
export type Json =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| { [key: string]: Json | undefined }
|
||||
| Json[]
|
||||
|
||||
// Domain types
|
||||
export type MemberRole = 'member' | 'skipper' | 'admin' | 'boatswain' | 'volunteer' | 'instructor'
|
||||
export type ReservationStatus = 'pending' | 'tentative' | 'confirmed'
|
||||
export interface Defect {
|
||||
type: string
|
||||
severity: string
|
||||
description: string
|
||||
detail?: string
|
||||
}
|
||||
// time_tuples shape: [[startHHMM, endHHMM], ...] e.g. [["08:00","12:00"]]
|
||||
export type TimeTuple = [string, string]
|
||||
|
||||
export type Database = {
|
||||
__InternalSupabase: {
|
||||
PostgrestVersion: "14.4"
|
||||
}
|
||||
public: {
|
||||
Tables: {
|
||||
boats: {
|
||||
Row: {
|
||||
id: string
|
||||
name: string
|
||||
display_name: string | null
|
||||
class: string | null
|
||||
year: number | null
|
||||
img_src: string | null
|
||||
icon_src: string | null
|
||||
booking_available: boolean
|
||||
required_certs: string[]
|
||||
max_passengers: number
|
||||
defects: Defect[]
|
||||
created_at: string
|
||||
}
|
||||
Insert: {
|
||||
id?: string
|
||||
name: string
|
||||
display_name?: string | null
|
||||
class?: string | null
|
||||
year?: number | null
|
||||
img_src?: string | null
|
||||
icon_src?: string | null
|
||||
booking_available?: boolean
|
||||
required_certs?: string[]
|
||||
max_passengers?: number
|
||||
defects?: Defect[]
|
||||
created_at?: string
|
||||
}
|
||||
Update: {
|
||||
id?: string
|
||||
name?: string
|
||||
display_name?: string | null
|
||||
class?: string | null
|
||||
year?: number | null
|
||||
img_src?: string | null
|
||||
icon_src?: string | null
|
||||
booking_available?: boolean
|
||||
required_certs?: string[]
|
||||
max_passengers?: number
|
||||
defects?: Defect[]
|
||||
created_at?: string
|
||||
}
|
||||
}
|
||||
members: {
|
||||
Row: {
|
||||
id: string
|
||||
user_id: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
email: string
|
||||
slack_id: string | null
|
||||
certifications: string[]
|
||||
role: MemberRole
|
||||
created_at: string
|
||||
}
|
||||
Insert: {
|
||||
id?: string
|
||||
user_id: string
|
||||
first_name?: string
|
||||
last_name?: string
|
||||
email: string
|
||||
slack_id?: string | null
|
||||
certifications?: string[]
|
||||
role?: MemberRole
|
||||
created_at?: string
|
||||
}
|
||||
Update: {
|
||||
id?: string
|
||||
user_id?: string
|
||||
first_name?: string
|
||||
last_name?: string
|
||||
email?: string
|
||||
slack_id?: string | null
|
||||
certifications?: string[]
|
||||
role?: MemberRole
|
||||
created_at?: string
|
||||
}
|
||||
}
|
||||
interval_templates: {
|
||||
Row: {
|
||||
id: string
|
||||
name: string
|
||||
time_tuples: TimeTuple[]
|
||||
created_at: string
|
||||
}
|
||||
Insert: {
|
||||
id?: string
|
||||
name: string
|
||||
time_tuples?: TimeTuple[]
|
||||
created_at?: string
|
||||
}
|
||||
Update: {
|
||||
id?: string
|
||||
name?: string
|
||||
time_tuples?: TimeTuple[]
|
||||
created_at?: string
|
||||
}
|
||||
}
|
||||
intervals: {
|
||||
Row: {
|
||||
id: string
|
||||
boat_id: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
user_id: string | null
|
||||
created_at: string
|
||||
}
|
||||
Insert: {
|
||||
id?: string
|
||||
boat_id: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
user_id?: string | null
|
||||
created_at?: string
|
||||
}
|
||||
Update: {
|
||||
id?: string
|
||||
boat_id?: string
|
||||
start_time?: string
|
||||
end_time?: string
|
||||
user_id?: string | null
|
||||
created_at?: string
|
||||
}
|
||||
}
|
||||
reservations: {
|
||||
Row: {
|
||||
id: string
|
||||
boat_id: string
|
||||
user_id: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
status: ReservationStatus
|
||||
reason: string
|
||||
comment: string
|
||||
member_ids: string[]
|
||||
guest_ids: string[]
|
||||
created_at: string
|
||||
}
|
||||
Insert: {
|
||||
id?: string
|
||||
boat_id: string
|
||||
user_id: string
|
||||
start_time: string
|
||||
end_time: string
|
||||
status?: ReservationStatus
|
||||
reason?: string
|
||||
comment?: string
|
||||
member_ids?: string[]
|
||||
guest_ids?: string[]
|
||||
created_at?: string
|
||||
}
|
||||
Update: {
|
||||
id?: string
|
||||
boat_id?: string
|
||||
user_id?: string
|
||||
start_time?: string
|
||||
end_time?: string
|
||||
status?: ReservationStatus
|
||||
reason?: string
|
||||
comment?: string
|
||||
member_ids?: string[]
|
||||
guest_ids?: string[]
|
||||
created_at?: string
|
||||
}
|
||||
}
|
||||
reference_docs: {
|
||||
Row: {
|
||||
id: string
|
||||
title: string
|
||||
category: string
|
||||
tags: string[]
|
||||
subtitle: string | null
|
||||
content: string
|
||||
created_at: string
|
||||
}
|
||||
Insert: {
|
||||
id?: string
|
||||
title: string
|
||||
category: string
|
||||
tags?: string[]
|
||||
subtitle?: string | null
|
||||
content: string
|
||||
created_at?: string
|
||||
}
|
||||
Update: {
|
||||
id?: string
|
||||
title?: string
|
||||
category?: string
|
||||
tags?: string[]
|
||||
subtitle?: string | null
|
||||
content?: string
|
||||
created_at?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Enums: {
|
||||
member_role: MemberRole
|
||||
reservation_status: ReservationStatus
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
|
||||
|
||||
type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, "public">]
|
||||
|
||||
export type Tables<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
||||
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
||||
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends {
|
||||
Row: infer R
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] &
|
||||
DefaultSchema["Views"])
|
||||
? (DefaultSchema["Tables"] &
|
||||
DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends {
|
||||
Row: infer R
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: never
|
||||
|
||||
export type TablesInsert<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema["Tables"]
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||
Insert: infer I
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
||||
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
||||
Insert: infer I
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: never
|
||||
|
||||
export type TablesUpdate<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema["Tables"]
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||
Update: infer U
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
||||
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
||||
Update: infer U
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: never
|
||||
|
||||
export type Enums<
|
||||
DefaultSchemaEnumNameOrOptions extends
|
||||
| keyof DefaultSchema["Enums"]
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
EnumName extends DefaultSchemaEnumNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"]
|
||||
: never = never,
|
||||
> = DefaultSchemaEnumNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName]
|
||||
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"]
|
||||
? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions]
|
||||
: never
|
||||
|
||||
export type CompositeTypes<
|
||||
PublicCompositeTypeNameOrOptions extends
|
||||
| keyof DefaultSchema["CompositeTypes"]
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
|
||||
: never = never,
|
||||
> = PublicCompositeTypeNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
|
||||
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
|
||||
? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
|
||||
: never
|
||||
|
||||
export const Constants = {
|
||||
public: {
|
||||
Enums: {},
|
||||
},
|
||||
} as const
|
||||
Reference in New Issue
Block a user