mirror of
https://github.com/Paris-est-Ludique/ForceOrange.git
synced 2025-09-10 22:26:27 +02:00
✨ User can signup / signin (WIP)
This commit is contained in:
committed by
ChatonDeAru (Romain)
parent
c35de52aec
commit
37b2238b84
@@ -1,6 +1,10 @@
|
||||
# Nuxt 3 Minimal Starter
|
||||
[](https://app.netlify.com/sites/force-orange/deploys)
|
||||
|
||||
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||
# Nuxt
|
||||
|
||||
Look at the
|
||||
[Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to
|
||||
learn more.
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -72,4 +76,6 @@ yarn preview
|
||||
bun run preview
|
||||
```
|
||||
|
||||
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
||||
Check out the
|
||||
[deployment documentation](https://nuxt.com/docs/getting-started/deployment) for
|
||||
more information.
|
||||
|
42
modules/app/components/FOHeader.vue
Normal file
42
modules/app/components/FOHeader.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import type { Database } from '@pel/supabase/types'
|
||||
|
||||
defineOptions({
|
||||
name: 'FOHeader',
|
||||
})
|
||||
|
||||
const links = [{
|
||||
label: 'Les actus',
|
||||
to: '/'
|
||||
}, {
|
||||
label: 'Rejoindre FO',
|
||||
to: '/join'
|
||||
}]
|
||||
|
||||
const user = useSupabaseUser()
|
||||
const { auth } = useSupabaseClient<Database>()
|
||||
|
||||
const signOut = async () => {
|
||||
const { error } = await auth.signOut()
|
||||
if (error) console.log(error)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UHeader :links="links" class="bg-white dark:bg-gray-900 rounded-xl shadow-lg mx-4 mt-4">
|
||||
<template #left>
|
||||
<!-- <p class="font-logo text-orange-500 stroke-5 stroke-black-500 text-hlogo">Force Orange</p> -->
|
||||
<NuxtImg src="/assets/img/logo-fo.svg" alt="Force Orange" />
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<UColorModeButton />
|
||||
{{ user?.user_metadata.firstname }}
|
||||
<UButton v-if="user" @click="signOut" variant="soft">Se déconnecter</UButton>
|
||||
<UButton v-else to="/signin" variant="soft">Se connecter</UButton>
|
||||
</template>
|
||||
</UHeader>
|
||||
|
||||
<UNotification v-if="user && !user?.email_confirmed_at" title="N'oubliez pas de confirmer votre adresse de courriel !"
|
||||
:id="1" :timeout="0" />
|
||||
</template>
|
41
modules/app/components/PasswordStrength.vue
Normal file
41
modules/app/components/PasswordStrength.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: 'PasswordStrength',
|
||||
})
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string | undefined
|
||||
}>()
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const data = useVModel(props, 'modelValue', emit)
|
||||
|
||||
const { strength } = usePasswordStrength(data)
|
||||
const color = computed(() => {
|
||||
if (strength.value.score === 1) return 'red'
|
||||
if (strength.value.score === 2) return 'orange'
|
||||
if (strength.value.score === 3) return 'yellow'
|
||||
if (strength.value.score === 4) return 'green'
|
||||
return 'blue'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UMeter :value="strength.score" :max="4" :color="color">
|
||||
<template #label>
|
||||
<p class="text-sm font-thin">
|
||||
<template v-if="strength.score === 1">
|
||||
Le mot de passe est faible
|
||||
</template>
|
||||
<template v-else-if="strength.score === 2">
|
||||
Le mot de passe est moyen
|
||||
</template>
|
||||
<template v-else-if="strength.score === 3">
|
||||
Le mot de passe est bon
|
||||
</template>
|
||||
<template v-else-if="strength.score === 4">
|
||||
Le mot de passe est fort
|
||||
</template>
|
||||
</p>
|
||||
</template>
|
||||
</UMeter>
|
||||
</template>
|
42
modules/app/composables/passwordStrength.ts
Normal file
42
modules/app/composables/passwordStrength.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { zxcvbn, zxcvbnOptions, type ZxcvbnResult } from '@zxcvbn-ts/core'
|
||||
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
|
||||
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-fr'
|
||||
|
||||
export const usePasswordStrength = (password: Ref<string | undefined>) => {
|
||||
|
||||
// 0 # too guessable: risky password. (guesses < 10 ^ 3)
|
||||
// 1 # very guessable: protection from throttled online attacks. (guesses < 10 ^ 6)
|
||||
// 2 # somewhat guessable: protection from unthrottled online attacks. (guesses < 10 ^ 8)
|
||||
// 3 # safely unguessable: moderate protection from offline slow - hash scenario. (guesses < 10 ^ 10)
|
||||
// 4 # very unguessable: strong protection from offline slow - hash scenario. (guesses >= 10 ^ 10)
|
||||
|
||||
const strength = ref<Partial<ZxcvbnResult>>({
|
||||
score: 0,
|
||||
})
|
||||
|
||||
const options = {
|
||||
translations: zxcvbnEnPackage.translations,
|
||||
graphs: zxcvbnCommonPackage.adjacencyGraphs,
|
||||
dictionary: {
|
||||
...zxcvbnCommonPackage.dictionary,
|
||||
...zxcvbnEnPackage.dictionary,
|
||||
},
|
||||
}
|
||||
|
||||
zxcvbnOptions.setOptions(options)
|
||||
|
||||
watchDebounced(password, (newPassword) => {
|
||||
if (!newPassword) {
|
||||
strength.value = {
|
||||
score: 0,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
strength.value = zxcvbn(newPassword.trim())
|
||||
}, { immediate: true, debounce: 500 })
|
||||
|
||||
return {
|
||||
strength,
|
||||
}
|
||||
}
|
@@ -1,30 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
useHead({
|
||||
bodyAttrs: {
|
||||
class: 'bg-[#F9DD75F2]'
|
||||
class: 'bg-orange-200 dark:bg-orange-900 dark:bg-opacity-30'
|
||||
}
|
||||
})
|
||||
const links = [{
|
||||
label: 'Les actus',
|
||||
to: '/'
|
||||
}, {
|
||||
label: 'Rejoindre FO',
|
||||
to: '/join'
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UHeader :links="links" class="rounded-xl shadow-lg bg-white mx-4 mt-4">
|
||||
<template #left>
|
||||
<!-- <p class="font-logo text-orange-500 stroke-5 stroke-black-500 text-hlogo">Force Orange</p> -->
|
||||
<NuxtImg src="/assets/img/logo-fo.svg" alt="Force Orange" />
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<UColorModeButton />
|
||||
<UButton to="/signin" variant="soft">Se connecter</UButton>
|
||||
</template>
|
||||
</UHeader>
|
||||
<FOHeader />
|
||||
|
||||
<UMain class="m-4">
|
||||
<slot />
|
||||
|
7
modules/app/middleware/auth.ts
Normal file
7
modules/app/middleware/auth.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export default defineNuxtRouteMiddleware((to, _from) => {
|
||||
const user = useSupabaseUser()
|
||||
|
||||
if (!user.value) {
|
||||
return navigateTo('/signin')
|
||||
}
|
||||
})
|
@@ -1,16 +1,16 @@
|
||||
[build.environment]
|
||||
NODE_VERSION = "21"
|
||||
NODE_VERSION = "21"
|
||||
|
||||
[build]
|
||||
publish = "dist"
|
||||
command = "pnpm run build"
|
||||
publish = ".output"
|
||||
command = "pnpm run build"
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
|
||||
[[headers]]
|
||||
for = "/*"
|
||||
[headers.values]
|
||||
Access-Control-Allow-Origin = "*"
|
||||
for = "/*"
|
||||
[headers.values]
|
||||
Access-Control-Allow-Origin = "*"
|
||||
|
@@ -33,7 +33,7 @@ export default defineNuxtConfig({
|
||||
redirectOptions: {
|
||||
login: '/signin',
|
||||
callback: '/signin/confirm',
|
||||
exclude: ['/*'],
|
||||
exclude: ['/signin/*', '/join', '/public/*'],
|
||||
},
|
||||
cookieName: 'fo-cookies',
|
||||
cookieOptions: {
|
||||
|
@@ -13,6 +13,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@pel/shared": "workspace:*",
|
||||
"@pel/supabase": "workspace:*",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"@zxcvbn-ts/language-common": "^3.0.4",
|
||||
"@zxcvbn-ts/language-fr": "^3.0.2",
|
||||
"nuxt": "^3.12.4",
|
||||
"vue": "latest"
|
||||
},
|
||||
|
@@ -1,14 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { object, string, boolean, type InferType } from 'yup'
|
||||
import type { Database } from '@pel/supabase/types'
|
||||
import type { FormSubmitEvent } from '#ui/types'
|
||||
|
||||
definePageMeta({
|
||||
name: 'Join',
|
||||
})
|
||||
|
||||
const user = useSupabaseUser()
|
||||
const { auth } = useSupabaseClient<Database>()
|
||||
|
||||
const schema = object({
|
||||
mail: string().email('Invalid email').required('Required'),
|
||||
password: string().required('Required'),
|
||||
password: string().min(6).required('Required'),
|
||||
firstname: string().required('Required'),
|
||||
lastname: string().required('Required'),
|
||||
isAdult: boolean().required('Required'),
|
||||
@@ -16,8 +20,6 @@ const schema = object({
|
||||
|
||||
type Schema = InferType<typeof schema>
|
||||
|
||||
const strenght = ref(0)
|
||||
|
||||
const state = reactive({
|
||||
mail: undefined,
|
||||
password: undefined,
|
||||
@@ -28,7 +30,25 @@ const state = reactive({
|
||||
|
||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
// Do something with event.data
|
||||
const formSubmit = event.data
|
||||
console.log(event.data)
|
||||
|
||||
const { data, error } = await auth.signUp({
|
||||
email: formSubmit.mail,
|
||||
password: formSubmit.password,
|
||||
options: {
|
||||
data: {
|
||||
firstname: formSubmit.firstname,
|
||||
lastname: formSubmit.lastname,
|
||||
is_adult: formSubmit.isAdult,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// TODO
|
||||
// [x] create member in database if user logged in
|
||||
|
||||
if (error) console.log(error)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -40,27 +60,21 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
</p>
|
||||
|
||||
<UForm :schema="schema" :state="state" @submit="onSubmit">
|
||||
<UFormGroup label="Adresse courriel">
|
||||
<UInput placeholder="Adresse courriel" name="mail" type="mail" v-model="state.mail" />
|
||||
<UFormGroup label="Adresse courriel" eager-validation>
|
||||
<UInput placeholder="Adresse courriel" name="mail" type="email" v-model="state.mail" />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Password">
|
||||
<UFormGroup label="Password" eager-validation>
|
||||
<UInput placeholder="Password" type="password" v-model="state.password" />
|
||||
</UFormGroup>
|
||||
<UMeter :value="strenght" :max="100">
|
||||
<template #label="{ percent }">
|
||||
<p class="text-sm font-thin">
|
||||
le mot de passe est fort
|
||||
</p>
|
||||
</template>
|
||||
</UMeter>
|
||||
<PasswordStrength v-model="state.password" />
|
||||
|
||||
<div class="flex md:flex-row flex-col gap-4">
|
||||
<UFormGroup label="Prénom" class="flex-1">
|
||||
<UFormGroup label="Prénom" class="flex-1" eager-validation>
|
||||
<UInput placeholder="Prénom" name="firstname" v-model="state.firstname" />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Nom de famille" class="flex-1">
|
||||
<UFormGroup label="Nom de famille" class="flex-1" eager-validation>
|
||||
<UInput placeholder="Nom de famille" name="lastname" v-model="state.lastname" />
|
||||
</UFormGroup>
|
||||
</div>
|
||||
|
27
modules/app/pages/signin/confirm.vue
Normal file
27
modules/app/pages/signin/confirm.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import type { Database } from '@pel/supabase/types'
|
||||
|
||||
const user = useSupabaseUser()
|
||||
const { auth } = useSupabaseClient<Database>()
|
||||
|
||||
watch(user, () => {
|
||||
if (user.value)
|
||||
return navigateTo('/')
|
||||
}, { immediate: true })
|
||||
|
||||
onMounted(async () => {
|
||||
const token_hash = window.location.hash.replace('#', '')
|
||||
const type = 'signup'
|
||||
|
||||
const { error } = await auth.verifyOtp({ token_hash, type })
|
||||
if (error) console.log(error)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<p class="u-text-black">
|
||||
Redirecting...
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
@@ -1,12 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import { object, string, type InferType } from 'yup'
|
||||
import type { Database } from '@pel/supabase/types'
|
||||
import type { FormSubmitEvent } from '#ui/types'
|
||||
|
||||
definePageMeta({
|
||||
name: 'Signin',
|
||||
})
|
||||
|
||||
const user = useSupabaseUser()
|
||||
const { auth } = useSupabaseClient<Database>()
|
||||
|
||||
const schema = object({
|
||||
mail: string().email('Invalid email').required('Required'),
|
||||
password: string().required('Required'),
|
||||
})
|
||||
|
||||
type Schema = InferType<typeof schema>
|
||||
|
||||
const state = reactive({
|
||||
mail: undefined,
|
||||
password: undefined,
|
||||
})
|
||||
|
||||
async function onSignin(event: FormSubmitEvent<Schema>) {
|
||||
const formSubmit = event.data
|
||||
|
||||
const { data, error } = await auth.signInWithPassword({
|
||||
email: formSubmit.mail,
|
||||
password: formSubmit.password
|
||||
})
|
||||
|
||||
// TODO: add possibility to signin with magic link -> signInWithOtp
|
||||
// const { data, error } = await auth.signInWithOtp({
|
||||
// email: 'example@email.com',
|
||||
// options: {
|
||||
// // set this to false if you do not want the user to be automatically signed up
|
||||
// shouldCreateUser: false,
|
||||
// emailRedirectTo: 'https://example.com/welcome',
|
||||
// },
|
||||
// })
|
||||
|
||||
// TODO confirm signin
|
||||
|
||||
if (error) console.log(error)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -14,7 +51,7 @@ const state = reactive({
|
||||
<UCard>
|
||||
<h1 class="text-2xl uppercase">Qui êtes-vous ?</h1>
|
||||
|
||||
<UForm :state="state">
|
||||
<UForm :state="state" @submit="onSignin">
|
||||
<UFormGroup label="Adresse courriel">
|
||||
<UInput name="mail" v-model="state.mail" />
|
||||
</UFormGroup>
|
||||
|
7
modules/supabase/README.md
Normal file
7
modules/supabase/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## Generate types from live database
|
||||
|
||||
`supabase gen types --lang=typescript --project-id YourProjectId > types/database.types.ts`
|
||||
|
||||
## Generate types when using local environment
|
||||
|
||||
`supabase gen types --lang=typescript --local > types/database.types.ts`
|
14
modules/supabase/package.json
Normal file
14
modules/supabase/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@pel/supabase",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.9.0",
|
||||
"main": "./type.ts",
|
||||
"files": [
|
||||
"types.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"up": "taze major -I"
|
||||
}
|
||||
}
|
187
modules/supabase/types.ts
Normal file
187
modules/supabase/types.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
// Generated types from supabase admin cli
|
||||
|
||||
export type Json =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| { [key: string]: Json | undefined }
|
||||
| Json[]
|
||||
|
||||
export type Database = {
|
||||
public: {
|
||||
Tables: {
|
||||
profiles: {
|
||||
Row: {
|
||||
birthday: string | null
|
||||
candidacy_message: string | null
|
||||
created_at: string
|
||||
description: string | null
|
||||
discord_avatar_url: string | null
|
||||
discord_id: string | null
|
||||
displayname: string | null
|
||||
firstname: string | null
|
||||
id: string
|
||||
is_adult: boolean
|
||||
is_member: boolean
|
||||
is_validated: boolean
|
||||
last_validation_date: string | null
|
||||
lastname: string | null
|
||||
mail: string
|
||||
pel_editions: number[]
|
||||
phone_number: number | null
|
||||
picture_url: string | null
|
||||
updated_at: string | null
|
||||
validation_comment: string | null
|
||||
}
|
||||
Insert: {
|
||||
birthday?: string | null
|
||||
candidacy_message?: string | null
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
discord_avatar_url?: string | null
|
||||
discord_id?: string | null
|
||||
displayname?: string | null
|
||||
firstname?: string | null
|
||||
id?: string
|
||||
is_adult?: boolean
|
||||
is_member?: boolean
|
||||
is_validated?: boolean
|
||||
last_validation_date?: string | null
|
||||
lastname?: string | null
|
||||
mail: string
|
||||
pel_editions?: number[]
|
||||
phone_number?: number | null
|
||||
picture_url?: string | null
|
||||
updated_at?: string | null
|
||||
validation_comment?: string | null
|
||||
}
|
||||
Update: {
|
||||
birthday?: string | null
|
||||
candidacy_message?: string | null
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
discord_avatar_url?: string | null
|
||||
discord_id?: string | null
|
||||
displayname?: string | null
|
||||
firstname?: string | null
|
||||
id?: string
|
||||
is_adult?: boolean
|
||||
is_member?: boolean
|
||||
is_validated?: boolean
|
||||
last_validation_date?: string | null
|
||||
lastname?: string | null
|
||||
mail?: string
|
||||
pel_editions?: number[]
|
||||
phone_number?: number | null
|
||||
picture_url?: string | null
|
||||
updated_at?: string | null
|
||||
validation_comment?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "profiles_id_fkey"
|
||||
columns: ["id"]
|
||||
isOneToOne: true
|
||||
referencedRelation: "users"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Enums: {
|
||||
[_ in never]: never
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type PublicSchema = Database[Extract<keyof Database, "public">]
|
||||
|
||||
export type Tables<
|
||||
PublicTableNameOrOptions extends
|
||||
| keyof (PublicSchema["Tables"] & PublicSchema["Views"])
|
||||
| { schema: keyof Database },
|
||||
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
|
||||
? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
|
||||
Database[PublicTableNameOrOptions["schema"]]["Views"])
|
||||
: never = never,
|
||||
> = PublicTableNameOrOptions extends { schema: keyof Database }
|
||||
? (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
|
||||
Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends {
|
||||
Row: infer R
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] &
|
||||
PublicSchema["Views"])
|
||||
? (PublicSchema["Tables"] &
|
||||
PublicSchema["Views"])[PublicTableNameOrOptions] extends {
|
||||
Row: infer R
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: never
|
||||
|
||||
export type TablesInsert<
|
||||
PublicTableNameOrOptions extends
|
||||
| keyof PublicSchema["Tables"]
|
||||
| { schema: keyof Database },
|
||||
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
|
||||
? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"]
|
||||
: never = never,
|
||||
> = PublicTableNameOrOptions extends { schema: keyof Database }
|
||||
? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||
Insert: infer I
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
|
||||
? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
|
||||
Insert: infer I
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: never
|
||||
|
||||
export type TablesUpdate<
|
||||
PublicTableNameOrOptions extends
|
||||
| keyof PublicSchema["Tables"]
|
||||
| { schema: keyof Database },
|
||||
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
|
||||
? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"]
|
||||
: never = never,
|
||||
> = PublicTableNameOrOptions extends { schema: keyof Database }
|
||||
? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||
Update: infer U
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
|
||||
? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
|
||||
Update: infer U
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: never
|
||||
|
||||
export type Enums<
|
||||
PublicEnumNameOrOptions extends
|
||||
| keyof PublicSchema["Enums"]
|
||||
| { schema: keyof Database },
|
||||
EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database }
|
||||
? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"]
|
||||
: never = never,
|
||||
> = PublicEnumNameOrOptions extends { schema: keyof Database }
|
||||
? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName]
|
||||
: PublicEnumNameOrOptions extends keyof PublicSchema["Enums"]
|
||||
? PublicSchema["Enums"][PublicEnumNameOrOptions]
|
||||
: never
|
Reference in New Issue
Block a user