@plutocms/supabase 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/FEATURES.md +3 -0
- package/README.md +10 -1
- package/app/components/auth/AuthSignIn.vue +56 -0
- package/app/components/migrations/MigrationsBanner.vue +26 -12
- package/app/composables/auth.ts +22 -88
- package/app/pages/admin/(auth)/confirm.vue +17 -21
- package/app/pages/admin/(auth)/forgot-password.vue +57 -64
- package/app/pages/admin/(auth)/signup.vue +104 -107
- package/app/pages/admin/(auth)/update-password.vue +75 -82
- package/app/pages/admin/setup.vue +0 -1
- package/app/plugins/pluto-extension.ts +147 -8
- package/app/utils/auth-driver.ts +110 -0
- package/i18n/locales/en.json +2 -18
- package/i18n/locales/pt-BR.json +2 -18
- package/package.json +2 -2
- package/server/utils/typegen.ts +67 -2
- package/app/components/navbar/NavbarAdmin.vue +0 -219
- package/app/components/navbar/NavbarAdminProvider.vue +0 -18
- package/app/middleware/auth.ts +0 -34
- package/app/middleware/setup-check.ts +0 -44
- package/app/pages/admin/(auth)/login.vue +0 -95
- package/app/pages/admin.vue +0 -57
- package/app/plugins/pluto-locale-sync.client.ts +0 -30
- package/app/plugins/pluto-permissions-sync.ts +0 -38
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.11.1](https://github.com/plutocms/supabase/compare/v0.11.0...v0.11.1) (2026-10-21)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **deps:** use @plutocms/pluto 0.17 ([#91](https://github.com/plutocms/supabase/issues/91)) ([7deeac5](https://github.com/plutocms/supabase/commit/7deeac5379ddaac0317fa457897190c0850e8ffc))
|
|
9
|
+
* **typegen:** keep the PostgREST version block and the app's format ([#90](https://github.com/plutocms/supabase/issues/90)) ([2518c33](https://github.com/plutocms/supabase/commit/2518c33f69fe188e69d93695819d7d3ca19932d7))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Performance Improvements
|
|
13
|
+
|
|
14
|
+
* **admin:** load the admin components on demand and start the setup check early ([#89](https://github.com/plutocms/supabase/issues/89)) ([f0d36bc](https://github.com/plutocms/supabase/commit/f0d36bc7d46f911522990653cd93ebb523c5a774))
|
|
15
|
+
|
|
16
|
+
## [0.11.0](https://github.com/plutocms/supabase/compare/v0.10.0...v0.11.0) (2026-10-20)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### ⚠ BREAKING CHANGES
|
|
20
|
+
|
|
21
|
+
* **admin:** move the admin shell to the pluto auth driver ([#83](https://github.com/plutocms/supabase/issues/83))
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* **admin:** move the admin shell to the pluto auth driver ([#83](https://github.com/plutocms/supabase/issues/83)) ([52ba78f](https://github.com/plutocms/supabase/commit/52ba78f934255e70a7b5188982bdb83f9fa5f86a))
|
|
26
|
+
|
|
3
27
|
## [0.10.0](https://github.com/plutocms/supabase/compare/v0.9.1...v0.10.0) (2026-10-18)
|
|
4
28
|
|
|
5
29
|
|
package/FEATURES.md
CHANGED
|
@@ -13,6 +13,9 @@ policies shared by every project here.
|
|
|
13
13
|
- Content adapter: a Supabase/PostgREST-backed `PlutoContentAdapter`, plugging any layer's
|
|
14
14
|
declared content type into `@plutocms/pluto`'s generic content routes.
|
|
15
15
|
@.claude/skills/content-adapter/SKILL.md
|
|
16
|
+
- Auth driver: plugs Supabase sign-in into the admin shell that pluto core owns (navbar, sign-in
|
|
17
|
+
page, route guard), and keeps the setup guard in the driver.
|
|
18
|
+
@.claude/skills/auth-driver/SKILL.md
|
|
16
19
|
- Profile language: stores each user's admin language in `profiles.locale`, written through a
|
|
17
20
|
security-definer function, and plugs it into pluto's i18n as a locale driver.
|
|
18
21
|
@.claude/skills/profile-language/SKILL.md
|
package/README.md
CHANGED
|
@@ -45,7 +45,16 @@ export default defineNuxtConfig({
|
|
|
45
45
|
|
|
46
46
|
This layer provides the following composables:
|
|
47
47
|
|
|
48
|
-
- `useAuth`:
|
|
48
|
+
- `useAuth`: Sign in, request a password reset, and update the password, with Supabase.
|
|
49
|
+
|
|
50
|
+
### Admin shell
|
|
51
|
+
|
|
52
|
+
Pluto core owns the admin shell: the `/admin` route, the sign-in page, the route guard and the
|
|
53
|
+
top navbar. This layer registers an auth driver, and core renders the shell from it. See
|
|
54
|
+
`.claude/skills/auth-driver/SKILL.md`.
|
|
55
|
+
|
|
56
|
+
An app that overrode `NavbarAdmin` or `pages/admin.vue` of an older version must move to registry
|
|
57
|
+
entries. See "Migrating an app" in that skill.
|
|
49
58
|
|
|
50
59
|
### API Routes
|
|
51
60
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The email and password form of the supabase layer. It is the `signIn`
|
|
3
|
+
// component of the auth driver. Core renders it inside the login frame
|
|
4
|
+
// (PlutoAdminAuthCard), so this file has no frame. The file lives under
|
|
5
|
+
// `app/` so Tailwind scans it.
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
/** A safe local path to open after sign-in. Core checks it. */
|
|
8
|
+
redirect: string
|
|
9
|
+
}>()
|
|
10
|
+
|
|
11
|
+
const { t } = usePlutoI18n()
|
|
12
|
+
|
|
13
|
+
const { isSubmitting, login } = useAuth({ redirect: () => props.redirect })
|
|
14
|
+
|
|
15
|
+
const form = ref({
|
|
16
|
+
email: '',
|
|
17
|
+
password: '',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
function submitForm() {
|
|
21
|
+
login(form.value)
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<form @submit.prevent="submitForm">
|
|
27
|
+
<div class="flex flex-col gap-y-6">
|
|
28
|
+
<UFormField :label="t('supabase.auth.fields.email')">
|
|
29
|
+
<UInput
|
|
30
|
+
v-model="form.email"
|
|
31
|
+
placeholder="victor@example.com"
|
|
32
|
+
class="w-full"
|
|
33
|
+
autocomplete="email"
|
|
34
|
+
/>
|
|
35
|
+
</UFormField>
|
|
36
|
+
|
|
37
|
+
<UFormField :label="t('supabase.auth.fields.password')">
|
|
38
|
+
<UInput
|
|
39
|
+
v-model="form.password"
|
|
40
|
+
placeholder="••••"
|
|
41
|
+
class="w-full"
|
|
42
|
+
type="password"
|
|
43
|
+
/>
|
|
44
|
+
</UFormField>
|
|
45
|
+
|
|
46
|
+
<UButton
|
|
47
|
+
:loading="isSubmitting"
|
|
48
|
+
type="submit"
|
|
49
|
+
icon="lucide:log-in"
|
|
50
|
+
class="w-full justify-center"
|
|
51
|
+
>
|
|
52
|
+
{{ t('supabase.auth.login.submit') }}
|
|
53
|
+
</UButton>
|
|
54
|
+
</div>
|
|
55
|
+
</form>
|
|
56
|
+
</template>
|
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
// The pending-migrations notice. It is an `adminBanners` entry of this layer,
|
|
3
|
+
// so the core admin layout renders it at the top of the admin pages.
|
|
2
4
|
// Renders nothing when there is no logged-in admin (status reads as null)
|
|
3
5
|
// or when no layer is pending. See `useMigrations` for the 401/403 handling.
|
|
6
|
+
// It also hides on the migrations page itself.
|
|
7
|
+
const route = useRoute()
|
|
4
8
|
const { status, pendingCount } = useMigrations()
|
|
5
9
|
const { t } = usePlutoI18n()
|
|
6
10
|
|
|
7
11
|
const title = computed(() =>
|
|
8
12
|
t('supabase.migrations.banner', pendingCount.value)
|
|
9
13
|
)
|
|
14
|
+
|
|
15
|
+
const isVisible = computed(
|
|
16
|
+
() =>
|
|
17
|
+
Boolean(status.value)
|
|
18
|
+
&& pendingCount.value > 0
|
|
19
|
+
&& !route.path.startsWith('/admin/migrations')
|
|
20
|
+
)
|
|
10
21
|
</script>
|
|
11
22
|
|
|
12
23
|
<template>
|
|
13
|
-
<
|
|
14
|
-
v-if="
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
<ClientOnly>
|
|
25
|
+
<Container v-if="isVisible" class="pt-4">
|
|
26
|
+
<UAlert
|
|
27
|
+
:title="title"
|
|
28
|
+
color="warning"
|
|
29
|
+
variant="subtle"
|
|
30
|
+
icon="lucide:triangle-alert"
|
|
31
|
+
>
|
|
32
|
+
<template #description>
|
|
33
|
+
<ULink to="/admin/migrations" class="underline"> {{ t('supabase.migrations.goTo') }} </ULink
|
|
34
|
+
>.
|
|
35
|
+
</template>
|
|
36
|
+
</UAlert>
|
|
37
|
+
</Container>
|
|
38
|
+
</ClientOnly>
|
|
25
39
|
</template>
|
package/app/composables/auth.ts
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
RouteLocationNormalizedGeneric,
|
|
3
|
-
RouteLocationRaw,
|
|
4
|
-
} from 'vue-router'
|
|
1
|
+
import type { MaybeRefOrGetter } from 'vue'
|
|
5
2
|
|
|
6
3
|
interface PlutoSupabaseAuthOptions {
|
|
7
|
-
|
|
4
|
+
/**
|
|
5
|
+
* A safe local path to open after a sign-in. Core checks it with
|
|
6
|
+
* `safeRedirect`. When it is empty, the sign-in opens the admin home.
|
|
7
|
+
*/
|
|
8
|
+
redirect?: MaybeRefOrGetter<string | undefined>
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
username?: string
|
|
15
|
-
is_admin?: boolean
|
|
16
|
-
[key: string]: unknown
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Admin paths that a signed-out visitor may load. Keep this list in sync
|
|
20
|
-
// with every public `/admin/*` page.
|
|
21
|
-
export const allowedUnauthenticatedPaths: RouteLocationRaw[] = [
|
|
22
|
-
'/admin/login',
|
|
23
|
-
'/admin/signup',
|
|
24
|
-
'/admin/confirm',
|
|
25
|
-
'/admin/setup',
|
|
26
|
-
'/admin/forgot-password',
|
|
27
|
-
'/admin/update-password',
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
export async function useAuth(authOptions?: PlutoSupabaseAuthOptions) {
|
|
11
|
+
// Sign-in, password reset and password update for the supabase auth pages.
|
|
12
|
+
// The session, the user and the sign-out are in the auth driver
|
|
13
|
+
// (app/plugins/pluto-extension.ts), not here.
|
|
14
|
+
export function useAuth(authOptions?: PlutoSupabaseAuthOptions) {
|
|
31
15
|
const toast = useToast()
|
|
32
16
|
// Read at setup, while the Nuxt context exists. The functions below run
|
|
33
17
|
// later, after an await, when the context is gone.
|
|
@@ -36,34 +20,7 @@ export async function useAuth(authOptions?: PlutoSupabaseAuthOptions) {
|
|
|
36
20
|
resolveAuthErrorText(error, { t, te })
|
|
37
21
|
|
|
38
22
|
const supabase = useSupabaseClient()
|
|
39
|
-
const supabaseSession = useSupabaseSession()
|
|
40
|
-
|
|
41
|
-
const supabaseUser = useSupabaseUser()
|
|
42
|
-
const userMetadata = useState<PlutoUserMetadata | null>(
|
|
43
|
-
'pluto-auth-user-metadata',
|
|
44
|
-
() => null
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
const { data: authenticatedUser } = await supabase.auth.getUser()
|
|
48
23
|
|
|
49
|
-
if (authenticatedUser.user?.user_metadata) {
|
|
50
|
-
userMetadata.value = authenticatedUser.user.user_metadata
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
watch(
|
|
54
|
-
supabaseUser,
|
|
55
|
-
(currentUser) => {
|
|
56
|
-
if (currentUser?.user_metadata) {
|
|
57
|
-
userMetadata.value = currentUser.user_metadata
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
{ immediate: true }
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
const user = computed(
|
|
64
|
-
() => supabaseUser.value?.user_metadata ?? userMetadata.value
|
|
65
|
-
)
|
|
66
|
-
const isLoggedIn = computed<boolean>(() => !!supabaseSession.value)
|
|
67
24
|
const isSubmitting = ref<boolean>(false)
|
|
68
25
|
|
|
69
26
|
interface LoginForm {
|
|
@@ -103,6 +60,17 @@ export async function useAuth(authOptions?: PlutoSupabaseAuthOptions) {
|
|
|
103
60
|
.eq('config_name', 'first_setup')
|
|
104
61
|
|
|
105
62
|
if (healthcheckError) {
|
|
63
|
+
console.error(`Error saving first setup: ${healthcheckError.message}`)
|
|
64
|
+
|
|
65
|
+
toast.add({
|
|
66
|
+
title: t('supabase.auth.toasts.loginFailed'),
|
|
67
|
+
description: authErrorText(healthcheckError),
|
|
68
|
+
icon: 'lucide:circle-x',
|
|
69
|
+
color: 'error',
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
isSubmitting.value = false
|
|
73
|
+
|
|
106
74
|
return {
|
|
107
75
|
success: false,
|
|
108
76
|
message: healthcheckError.message,
|
|
@@ -112,7 +80,7 @@ export async function useAuth(authOptions?: PlutoSupabaseAuthOptions) {
|
|
|
112
80
|
}
|
|
113
81
|
|
|
114
82
|
return navigateTo(
|
|
115
|
-
(authOptions?.
|
|
83
|
+
toValue(authOptions?.redirect) || SUPABASE_SIGN_IN_FALLBACK
|
|
116
84
|
)
|
|
117
85
|
} catch (error) {
|
|
118
86
|
if (import.meta.dev) {
|
|
@@ -125,36 +93,6 @@ export async function useAuth(authOptions?: PlutoSupabaseAuthOptions) {
|
|
|
125
93
|
}
|
|
126
94
|
}
|
|
127
95
|
|
|
128
|
-
interface LogoutOptions {
|
|
129
|
-
redirectTo?: string
|
|
130
|
-
showToast?: boolean
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async function logout(options?: LogoutOptions) {
|
|
134
|
-
await supabase.auth.signOut()
|
|
135
|
-
userMetadata.value = null
|
|
136
|
-
|
|
137
|
-
if (options?.showToast) {
|
|
138
|
-
console.warn(`User logged out`)
|
|
139
|
-
|
|
140
|
-
toast.add({
|
|
141
|
-
title: t('supabase.auth.toasts.loggedOut'),
|
|
142
|
-
icon: 'lucide:check',
|
|
143
|
-
color: 'success',
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (options?.redirectTo) {
|
|
148
|
-
return navigateTo(`/admin/login?redirect=${options.redirectTo}`)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (authOptions?.route && authOptions.route.path.startsWith('/admin')) {
|
|
152
|
-
return navigateTo('/admin/login')
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return navigateTo('/admin/login')
|
|
156
|
-
}
|
|
157
|
-
|
|
158
96
|
async function requestPasswordReset(email: string) {
|
|
159
97
|
isSubmitting.value = true
|
|
160
98
|
|
|
@@ -228,13 +166,9 @@ export async function useAuth(authOptions?: PlutoSupabaseAuthOptions) {
|
|
|
228
166
|
}
|
|
229
167
|
|
|
230
168
|
return {
|
|
231
|
-
isLoggedIn,
|
|
232
169
|
isSubmitting,
|
|
233
|
-
user,
|
|
234
170
|
login,
|
|
235
|
-
logout,
|
|
236
171
|
requestPasswordReset,
|
|
237
172
|
updatePassword,
|
|
238
|
-
allowedUnauthenticatedPaths,
|
|
239
173
|
}
|
|
240
174
|
}
|
|
@@ -11,29 +11,25 @@ useHead({
|
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
13
|
<template>
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
<div class="space-y-
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
<PlutoAdminAuthCard :title="t('supabase.auth.confirm.title')">
|
|
15
|
+
<div class="space-y-6 py-10">
|
|
16
|
+
<div class="text-center space-y-4">
|
|
17
|
+
<hgroup class="flex flex-col gap-y-1">
|
|
18
|
+
<span class="text-5xl">
|
|
19
|
+
<UIcon name="lucide:mail-check" class="inline-block" />
|
|
20
|
+
</span>
|
|
21
|
+
</hgroup>
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
<p>
|
|
24
|
+
{{ t('supabase.auth.confirm.description') }}
|
|
25
|
+
</p>
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<div class="flex items-center justify-center gap-x-4">
|
|
31
|
-
<UButton to="/admin" color="primary" icon="lucide:layout-dashboard">
|
|
32
|
-
{{ t('supabase.auth.confirm.goToDashboard') }}
|
|
33
|
-
</UButton>
|
|
34
|
-
</div>
|
|
27
|
+
<div class="flex items-center justify-center gap-x-4">
|
|
28
|
+
<UButton to="/admin" color="primary" icon="lucide:layout-dashboard">
|
|
29
|
+
{{ t('supabase.auth.confirm.goToDashboard') }}
|
|
30
|
+
</UButton>
|
|
35
31
|
</div>
|
|
36
32
|
</div>
|
|
37
|
-
</
|
|
38
|
-
</
|
|
33
|
+
</div>
|
|
34
|
+
</PlutoAdminAuthCard>
|
|
39
35
|
</template>
|
|
@@ -9,7 +9,7 @@ useHead({
|
|
|
9
9
|
title: () => t('supabase.auth.forgot.pageTitle'),
|
|
10
10
|
})
|
|
11
11
|
|
|
12
|
-
const { isSubmitting, requestPasswordReset } =
|
|
12
|
+
const { isSubmitting, requestPasswordReset } = useAuth()
|
|
13
13
|
|
|
14
14
|
const email = ref('')
|
|
15
15
|
const isEmailSent = ref(false)
|
|
@@ -24,76 +24,69 @@ async function submitForm() {
|
|
|
24
24
|
</script>
|
|
25
25
|
|
|
26
26
|
<template>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
<PlutoAdminAuthCard
|
|
28
|
+
:title="isEmailSent ? t('supabase.auth.forgot.sentTitle') : t('supabase.auth.forgot.title')"
|
|
29
|
+
:description="isEmailSent ? undefined : t('supabase.auth.forgot.description')"
|
|
30
|
+
>
|
|
31
|
+
<div v-if="isEmailSent" class="space-y-6 py-10">
|
|
32
|
+
<div class="text-center space-y-4">
|
|
33
|
+
<hgroup class="flex flex-col gap-y-1">
|
|
34
|
+
<span class="text-5xl">
|
|
35
|
+
<UIcon name="lucide:mail" class="inline-block" />
|
|
36
|
+
</span>
|
|
37
|
+
</hgroup>
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
<p>
|
|
40
|
+
<SupabaseMessageParts :message="t('supabase.auth.forgot.sentDescription')">
|
|
41
|
+
<template #email>
|
|
42
|
+
<strong>{{ email }}</strong>
|
|
43
|
+
</template>
|
|
44
|
+
</SupabaseMessageParts>
|
|
45
|
+
</p>
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</template>
|
|
44
|
-
</SupabaseMessageParts>
|
|
45
|
-
</p>
|
|
46
|
-
|
|
47
|
-
<div class="flex items-center justify-center">
|
|
48
|
-
<UButton variant="link" to="/admin/login" class="px-0">
|
|
49
|
-
{{ t('supabase.auth.backToLogin') }}
|
|
50
|
-
</UButton>
|
|
51
|
-
</div>
|
|
47
|
+
<div class="flex items-center justify-center">
|
|
48
|
+
<UButton variant="link" to="/admin/login" class="px-0">
|
|
49
|
+
{{ t('supabase.auth.backToLogin') }}
|
|
50
|
+
</UButton>
|
|
52
51
|
</div>
|
|
53
52
|
</div>
|
|
53
|
+
</div>
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<hgroup class="flex flex-col gap-y-1">
|
|
58
|
-
<h1 class="text-3xl font-bold">{{ t('supabase.auth.forgot.title') }}</h1>
|
|
59
|
-
<p class="text-sm text-muted">
|
|
60
|
-
{{ t('supabase.auth.forgot.description') }}
|
|
61
|
-
</p>
|
|
62
|
-
</hgroup>
|
|
55
|
+
<form v-else @submit.prevent="submitForm">
|
|
56
|
+
<div class="flex flex-col gap-y-6">
|
|
63
57
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
58
|
+
<UFormField :label="t('supabase.auth.fields.email')">
|
|
59
|
+
<UInput
|
|
60
|
+
v-model="email"
|
|
61
|
+
placeholder="victor@example.com"
|
|
62
|
+
type="email"
|
|
63
|
+
class="w-full"
|
|
64
|
+
autocomplete="email"
|
|
65
|
+
required
|
|
66
|
+
/>
|
|
67
|
+
</UFormField>
|
|
74
68
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
69
|
+
<div
|
|
70
|
+
class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-x-4"
|
|
71
|
+
>
|
|
72
|
+
<UFormField>
|
|
73
|
+
<UButton
|
|
74
|
+
:loading="isSubmitting"
|
|
75
|
+
type="submit"
|
|
76
|
+
icon="lucide:send"
|
|
77
|
+
class="w-full justify-center sm:w-auto"
|
|
78
|
+
>
|
|
79
|
+
{{ t('supabase.auth.forgot.submit') }}
|
|
80
|
+
</UButton>
|
|
81
|
+
</UFormField>
|
|
88
82
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
</div>
|
|
83
|
+
<UFormField>
|
|
84
|
+
<UButton variant="link" to="/admin/login" class="px-0">
|
|
85
|
+
{{ t('supabase.auth.backToLogin') }}
|
|
86
|
+
</UButton>
|
|
87
|
+
</UFormField>
|
|
95
88
|
</div>
|
|
96
|
-
</
|
|
97
|
-
</
|
|
98
|
-
</
|
|
89
|
+
</div>
|
|
90
|
+
</form>
|
|
91
|
+
</PlutoAdminAuthCard>
|
|
99
92
|
</template>
|