@plutocms/supabase 0.29.0 → 0.31.0
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 +19 -0
- package/README.md +11 -111
- package/app/components/EnvPersistWarning.vue +11 -9
- package/app/components/SupabaseLogo.vue +47 -0
- package/app/components/backend/SupabaseBackendDetails.vue +1 -1
- package/app/components/mail/AuthEmailsPanel.vue +3 -2
- package/app/pages/admin/migrations.vue +21 -19
- package/app/pages/admin/roles.vue +117 -119
- package/app/pages/admin/setup.vue +160 -271
- package/app/pages/admin/users.vue +191 -189
- package/app/plugins/pluto-extension.ts +2 -1
- package/i18n/locales/en.json +17 -19
- package/i18n/locales/pt-BR.json +17 -19
- package/modules/internal/supabase-types-shim.ts +27 -0
- package/modules/pluto-migrations.ts +17 -10
- package/package.json +3 -2
- package/app/layouts/setup.vue +0 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.31.0](https://github.com/plutocms/supabase/compare/v0.30.0...v0.31.0) (2026-11-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* admin UI round 5: tooltips, a playground site, new tab links; core 0.68 ([#302](https://github.com/plutocms/supabase/issues/302)) ([441d322](https://github.com/plutocms/supabase/commit/441d322a52cf31712cd7510b0876ca885698fda7))
|
|
9
|
+
|
|
10
|
+
## [0.30.0](https://github.com/plutocms/supabase/compare/v0.29.0...v0.30.0) (2026-11-10)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* the post editor look of core, a line icon for the backend badge, shop translations ([#293](https://github.com/plutocms/supabase/issues/293)) ([96f582b](https://github.com/plutocms/supabase/commit/96f582b261524fa9511d1f32451ae8a801dcc3b1))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* declare the MIT license of supabase and supabase-shop; short READMEs ([#289](https://github.com/plutocms/supabase/issues/289)) ([d9b0745](https://github.com/plutocms/supabase/commit/d9b07459d1315bc75e8188535c8db0d2f9212399))
|
|
21
|
+
|
|
3
22
|
## [0.29.0](https://github.com/plutocms/supabase/compare/v0.28.0...v0.29.0) (2026-11-08)
|
|
4
23
|
|
|
5
24
|
|
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Supabase Layer for Pluto
|
|
2
2
|
|
|
3
|
-
This layer
|
|
3
|
+
This layer makes Supabase the backend of Pluto: the database, the sign-in, the users, and the roles.
|
|
4
|
+
|
|
5
|
+
**Docs:** [Supabase](https://pluto.vulmix.dev/docs/supabase)
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
7
9
|
> [!NOTE]
|
|
8
|
-
> Make sure you have a Supabase project set up. You
|
|
10
|
+
> Make sure you have a Supabase project set up. You need the URL and the publishable key of the project. You can find these in your [Supabase dashboard](https://supabase.com/dashboard).
|
|
9
11
|
|
|
10
12
|
1. Create a `.env` file in the root of your project and add the following environment variables:
|
|
11
13
|
|
|
@@ -30,117 +32,15 @@ export default defineNuxtConfig({
|
|
|
30
32
|
})
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
### Composables
|
|
36
|
-
|
|
37
|
-
This layer provides the following composables:
|
|
38
|
-
|
|
39
|
-
- `useAuth`: Sign in, request a password reset, and update the password, with Supabase.
|
|
40
|
-
|
|
41
|
-
### Admin shell
|
|
42
|
-
|
|
43
|
-
Pluto core owns the admin shell: the `/admin` route, the sign-in page, the route guard and the
|
|
44
|
-
top navbar. This layer registers an auth driver, and core renders the shell from it. See
|
|
45
|
-
`.claude/skills/auth-driver/SKILL.md`.
|
|
46
|
-
|
|
47
|
-
An app that overrode `NavbarAdmin` or `pages/admin.vue` of an older version must move to registry
|
|
48
|
-
entries. See "Migrating an app" in that skill.
|
|
49
|
-
|
|
50
|
-
### Invite users (access API)
|
|
51
|
-
|
|
52
|
-
An admin invites a user by email and gives the user one or more roles. The routes are under
|
|
53
|
-
`/api/_pluto/supabase/access/**`. Every write needs an admin. This needs the migration
|
|
54
|
-
`008_users_and_roles_admin.sql`, which the admin applies from `/admin/migrations`.
|
|
55
|
-
|
|
56
|
-
Invites need no setup. Check one thing: in the Supabase dashboard (Authentication, then URL
|
|
57
|
-
Configuration), the **Site URL** is the URL of your site. Supabase accepts every link back to that
|
|
58
|
-
host, so `/admin/accept-invite` needs no entry in the Redirect URLs. Add a Redirect URL only for
|
|
59
|
-
another host (for example a preview deploy, or `www.` next to the bare domain).
|
|
60
|
-
|
|
61
|
-
How the invite email goes out:
|
|
62
|
-
|
|
63
|
-
- **Without the service role key** (the default): the server sends a passwordless sign-in link
|
|
64
|
-
with the public key. Supabase creates the account and uses its sign-up or magic link email
|
|
65
|
-
template. Public sign-up must be on (the Supabase default). Supabase sends one email per
|
|
66
|
-
address every 60 seconds.
|
|
67
|
-
- **With the service role key** (optional): set `NUXT_SUPABASE_SECRET_KEY` (the Supabase secret
|
|
68
|
-
key) in the environment of the **running server**. Never put it in the build environment. The
|
|
69
|
-
server then uses Supabase's invite API and the "Invite user" email template. This also works
|
|
70
|
-
when public sign-up is off.
|
|
71
|
-
|
|
72
|
-
Optional: a custom invite email template (Authentication, then Email Templates, then Invite
|
|
73
|
-
user) makes the link work in every browser, because the page then reads a `token_hash` and not
|
|
74
|
-
tokens in the URL hash:
|
|
75
|
-
|
|
76
|
-
```
|
|
77
|
-
{{ .SiteURL }}/admin/accept-invite?token_hash={{ .TokenHash }}&type=invite
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
The built-in Supabase mailer sends very few emails, and only to members of your Supabase
|
|
81
|
-
organization. To invite any other address, set up custom SMTP (Project Settings, then
|
|
82
|
-
Authentication, then SMTP). A production site needs it anyway, for every auth email. Without it,
|
|
83
|
-
an invite to another address answers 400 with the hint `emailNotAuthorized`.
|
|
84
|
-
|
|
85
|
-
The invited user opens the link, sets a password on `/admin/accept-invite`, and goes to the
|
|
86
|
-
admin. An invite to an email that already has an account adds the roles and sends no email.
|
|
87
|
-
|
|
88
|
-
#### Invites through Pluto mail (optional)
|
|
89
|
-
|
|
90
|
-
With the service role key **and** a working Pluto mail driver, an invite does not use Supabase's
|
|
91
|
-
mailer. The server makes a one-time link with `auth.admin.generateLink`, and Pluto mail sends the
|
|
92
|
-
email. This lifts the team-only address rule and the Supabase email rate limit for invites. Set
|
|
93
|
-
the mail variables in the environment of the **running server** (Resend is the first driver):
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
NUXT_PLUTO_MAIL_RESEND_API_KEY=re_...
|
|
97
|
-
NUXT_PLUTO_MAIL_FROM="Your Site <hello@your-domain.com>"
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
`Settings > Email` in the admin guides the setup (account, key, domain, test email). The
|
|
101
|
-
`mail` skill of `@plutocms/core` lists every `NUXT_PLUTO_MAIL_*` variable. If the send fails,
|
|
102
|
-
the user and the roles stay, and the admin gets the invite link to send by hand.
|
|
103
|
-
|
|
104
|
-
With the key, an admin can also copy the invite link (Users, then the row menu, or the invite
|
|
105
|
-
form). The link works once and expires. Sign-up, password reset and magic link emails still
|
|
106
|
-
come from Supabase. The Email tab has a guide to send them through Resend too.
|
|
107
|
-
|
|
108
|
-
### API Routes
|
|
109
|
-
|
|
110
|
-
The layer includes API routes for interacting with Supabase services, such as authentication and settings.
|
|
111
|
-
Every route lives under `/api/_pluto/supabase/`. Core blocks a cross-site write to `/api/_pluto/**`, so
|
|
112
|
-
a page of another site cannot call them. A failure is an HTTP error with a translation key, never a
|
|
113
|
-
`200` with `success: false`. These routes serve the admin. They are not a public API.
|
|
114
|
-
|
|
115
|
-
### Public content API
|
|
116
|
-
|
|
117
|
-
The public content of the Supabase layers is read through the public API of core,
|
|
118
|
-
`GET /api/pluto/v1/content/<type>` (and `/<id or slug>`). See
|
|
119
|
-
[the public API](https://pluto.vulmix.dev/docs/build/public-api). The routes are anonymous. This layer
|
|
120
|
-
reads for them with an anon client that has no user session, so Row Level Security applies as for
|
|
121
|
-
an anonymous visitor, and the answer is the same for everyone. A CDN can cache it. The adapter
|
|
122
|
-
never uses the service role key for a read. The adapter also supports the `filters` of a public
|
|
123
|
-
query, and puts the created, updated and published-at columns on each item, so a public list can
|
|
124
|
-
sort by date.
|
|
125
|
-
|
|
126
|
-
- GET and PUT `/api/_pluto/supabase/settings` (PUT needs `settings:manage`; it answers `{ message, data }`)
|
|
127
|
-
- POST `/api/_pluto/supabase/auth/signup`
|
|
128
|
-
- GET `/api/_pluto/supabase/migrations`, POST `/api/_pluto/supabase/migrations/run` (admin)
|
|
129
|
-
- GET `/api/_pluto/supabase/me/capabilities`
|
|
130
|
-
- GET and PUT `/api/_pluto/supabase/me/locale`
|
|
131
|
-
- GET `/api/_pluto/supabase/setup` (the setup state), POST `/api/_pluto/supabase/setup` (runs the first setup)
|
|
132
|
-
- GET `/api/_pluto/supabase/access/users`, GET `/api/_pluto/supabase/access/roles` (permission `users:read`)
|
|
133
|
-
- POST `/api/_pluto/supabase/access/users/invite`, POST `/api/_pluto/supabase/access/users/:id/resend` (admin; `locale` is the
|
|
134
|
-
optional language of the email)
|
|
135
|
-
- POST `/api/_pluto/supabase/access/users/invite-link` (admin; needs the service role key; returns a one-time link
|
|
136
|
-
and sends no email)
|
|
137
|
-
- GET `/api/_pluto/supabase/access/capabilities` (admin; says whether the service key and Pluto mail are set)
|
|
138
|
-
- PUT and DELETE `/api/_pluto/supabase/access/users/:id/roles` (admin; DELETE revokes the access)
|
|
139
|
-
- POST `/api/_pluto/supabase/access/roles`, PUT and DELETE `/api/_pluto/supabase/access/roles/:key` (admin)
|
|
35
|
+
## What it adds
|
|
140
36
|
|
|
141
|
-
|
|
37
|
+
- **Content in Postgres:** a content adapter for the content types of every layer, with history, trash, scheduled publish, and a server copy of unsaved changes. See [Content adapter](https://pluto.vulmix.dev/docs/supabase/content-adapter).
|
|
38
|
+
- **Sign-in:** email and password with Supabase Auth, and the auth driver of the admin. See [Auth](https://pluto.vulmix.dev/docs/supabase/auth).
|
|
39
|
+
- **Users and roles:** invite users by email, and give roles that hold capabilities. See [Users and roles](https://pluto.vulmix.dev/docs/supabase/users-and-roles) and [Invites and email](https://pluto.vulmix.dev/docs/supabase/invites-and-email).
|
|
40
|
+
- **Media:** the media library of core stores its files in Supabase Storage.
|
|
41
|
+
- **Migrations:** each layer ships its database changes. An admin applies them from `/admin/migrations`. See [Migrations](https://pluto.vulmix.dev/docs/supabase/migrations).
|
|
142
42
|
|
|
143
|
-
|
|
43
|
+
The public site reads the content through the [public API](https://pluto.vulmix.dev/docs/build/public-api) of core. The routes under `/api/_pluto/supabase/` serve the admin. They are not a public API.
|
|
144
44
|
|
|
145
45
|
## Development
|
|
146
46
|
|
|
@@ -43,7 +43,7 @@ async function copyEnvLine() {
|
|
|
43
43
|
<UAlert
|
|
44
44
|
:title="t('supabase.envWarning.title')"
|
|
45
45
|
color="warning"
|
|
46
|
-
variant="
|
|
46
|
+
variant="subtle"
|
|
47
47
|
icon="lucide:triangle-alert"
|
|
48
48
|
>
|
|
49
49
|
<template #description>
|
|
@@ -84,14 +84,16 @@ async function copyEnvLine() {
|
|
|
84
84
|
{{ envLine }}
|
|
85
85
|
</code>
|
|
86
86
|
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
<UTooltip :text="t('supabase.envWarning.copyLine')">
|
|
88
|
+
<UButton
|
|
89
|
+
:aria-label="t('supabase.envWarning.copyLine')"
|
|
90
|
+
icon="lucide:copy"
|
|
91
|
+
variant="soft"
|
|
92
|
+
color="neutral"
|
|
93
|
+
size="sm"
|
|
94
|
+
@click="copyEnvLine"
|
|
95
|
+
/>
|
|
96
|
+
</UTooltip>
|
|
95
97
|
</div>
|
|
96
98
|
</div>
|
|
97
99
|
</template>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The mark of Supabase, in its brand colors, for a page that connects Pluto to it (the database
|
|
3
|
+
// setup). Size it with a class, such as `size-6`.
|
|
4
|
+
const id = useId()
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<svg viewBox="0 0 109 113" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
9
|
+
<path
|
|
10
|
+
:fill="`url(#${id}-a)`"
|
|
11
|
+
d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z"
|
|
12
|
+
/>
|
|
13
|
+
<path
|
|
14
|
+
:fill="`url(#${id}-b)`"
|
|
15
|
+
d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z"
|
|
16
|
+
fill-opacity="0.2"
|
|
17
|
+
/>
|
|
18
|
+
<path
|
|
19
|
+
d="M45.317 2.07103C48.1765 -1.53037 53.9745 0.442937 54.0434 5.041L54.4849 72.2922H9.83113C1.64038 72.2922 -2.92775 62.8321 2.1655 56.4175L45.317 2.07103Z"
|
|
20
|
+
fill="#3ECF8E"
|
|
21
|
+
/>
|
|
22
|
+
<defs>
|
|
23
|
+
<linearGradient
|
|
24
|
+
:id="`${id}-a`"
|
|
25
|
+
x1="53.9738"
|
|
26
|
+
y1="54.974"
|
|
27
|
+
x2="94.1635"
|
|
28
|
+
y2="71.8295"
|
|
29
|
+
gradientUnits="userSpaceOnUse"
|
|
30
|
+
>
|
|
31
|
+
<stop stop-color="#249361" />
|
|
32
|
+
<stop offset="1" stop-color="#3ECF8E" />
|
|
33
|
+
</linearGradient>
|
|
34
|
+
<linearGradient
|
|
35
|
+
:id="`${id}-b`"
|
|
36
|
+
x1="36.1558"
|
|
37
|
+
y1="30.578"
|
|
38
|
+
x2="54.4844"
|
|
39
|
+
y2="65.0806"
|
|
40
|
+
gradientUnits="userSpaceOnUse"
|
|
41
|
+
>
|
|
42
|
+
<stop />
|
|
43
|
+
<stop offset="1" stop-opacity="0" />
|
|
44
|
+
</linearGradient>
|
|
45
|
+
</defs>
|
|
46
|
+
</svg>
|
|
47
|
+
</template>
|
|
@@ -72,7 +72,7 @@ const dashboardLinks = computed(() =>
|
|
|
72
72
|
key: 'project',
|
|
73
73
|
label: t('supabase.mail.authEmails.dashboardHome'),
|
|
74
74
|
to: links.value.project,
|
|
75
|
-
icon: 'lucide:
|
|
75
|
+
icon: 'lucide:layout-dashboard',
|
|
76
76
|
},
|
|
77
77
|
]
|
|
78
78
|
: []
|
|
@@ -95,7 +95,7 @@ const dashboardLinks = computed(() =>
|
|
|
95
95
|
:to="RESEND_SUPABASE_INTEGRATION_URL"
|
|
96
96
|
target="_blank"
|
|
97
97
|
rel="noopener noreferrer"
|
|
98
|
-
icon="lucide
|
|
98
|
+
trailing-icon="i-lucide-arrow-up-right"
|
|
99
99
|
color="neutral"
|
|
100
100
|
variant="outline"
|
|
101
101
|
>
|
|
@@ -149,6 +149,7 @@ const dashboardLinks = computed(() =>
|
|
|
149
149
|
:icon="link.icon"
|
|
150
150
|
target="_blank"
|
|
151
151
|
rel="noopener noreferrer"
|
|
152
|
+
trailing-icon="i-lucide-arrow-up-right"
|
|
152
153
|
color="neutral"
|
|
153
154
|
variant="outline"
|
|
154
155
|
size="sm"
|
|
@@ -188,17 +188,20 @@ async function applyMigrations(useForm: boolean) {
|
|
|
188
188
|
</script>
|
|
189
189
|
|
|
190
190
|
<template>
|
|
191
|
-
<
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
<PlutoViewAdminPanel>
|
|
192
|
+
<template #toolbar>
|
|
193
|
+
<PlutoViewToolbar
|
|
194
|
+
:title="t('supabase.migrations.title')"
|
|
195
|
+
icon="lucide:database-zap"
|
|
196
|
+
/>
|
|
197
|
+
</template>
|
|
195
198
|
|
|
196
199
|
<UAlert
|
|
197
200
|
v-if="isReconnecting"
|
|
198
201
|
:title="t('supabase.connection.restarting')"
|
|
199
202
|
:description="t('supabase.migrations.reconnectingBanner')"
|
|
200
203
|
color="info"
|
|
201
|
-
variant="
|
|
204
|
+
variant="subtle"
|
|
202
205
|
icon="lucide:refresh-cw"
|
|
203
206
|
/>
|
|
204
207
|
|
|
@@ -218,7 +221,7 @@ async function applyMigrations(useForm: boolean) {
|
|
|
218
221
|
:title="t('supabase.migrations.upToDate')"
|
|
219
222
|
:description="t('supabase.migrations.upToDateDescription')"
|
|
220
223
|
color="success"
|
|
221
|
-
variant="
|
|
224
|
+
variant="subtle"
|
|
222
225
|
icon="lucide:check-circle"
|
|
223
226
|
/>
|
|
224
227
|
|
|
@@ -284,14 +287,12 @@ async function applyMigrations(useForm: boolean) {
|
|
|
284
287
|
:message="t('supabase.connection.connectionStringHelp')"
|
|
285
288
|
>
|
|
286
289
|
<template #link>
|
|
287
|
-
<
|
|
290
|
+
<PlutoExternalLink
|
|
288
291
|
:to="`https://supabase.com/dashboard/project/${projectId}/database/settings?showConnect=true&connectTab=direct&method=transaction`"
|
|
289
|
-
target="_blank"
|
|
290
292
|
class="underline"
|
|
291
|
-
external
|
|
292
293
|
>
|
|
293
294
|
{{ t('supabase.connection.connectionStringHelpLink') }}
|
|
294
|
-
</
|
|
295
|
+
</PlutoExternalLink>
|
|
295
296
|
</template>
|
|
296
297
|
</SupabaseMessageParts>
|
|
297
298
|
</template>
|
|
@@ -310,19 +311,18 @@ async function applyMigrations(useForm: boolean) {
|
|
|
310
311
|
<UAlert
|
|
311
312
|
color="secondary"
|
|
312
313
|
icon="lucide:badge-info"
|
|
313
|
-
variant="
|
|
314
|
+
variant="subtle"
|
|
314
315
|
>
|
|
315
316
|
<template #description>
|
|
316
317
|
<SupabaseMessageParts
|
|
317
318
|
:message="t('supabase.connection.passwordInfo')"
|
|
318
319
|
>
|
|
319
320
|
<template #link>
|
|
320
|
-
<
|
|
321
|
+
<PlutoExternalLink
|
|
321
322
|
to="https://supabase.com/dashboard/project/_/database/settings"
|
|
322
|
-
target="_blank"
|
|
323
323
|
>
|
|
324
324
|
{{ t('supabase.connection.passwordInfoLink') }}
|
|
325
|
-
</
|
|
325
|
+
</PlutoExternalLink>
|
|
326
326
|
</template>
|
|
327
327
|
</SupabaseMessageParts>
|
|
328
328
|
</template>
|
|
@@ -332,7 +332,7 @@ async function applyMigrations(useForm: boolean) {
|
|
|
332
332
|
:title="t('supabase.connection.whyTitle')"
|
|
333
333
|
:description="t('supabase.migrations.why')"
|
|
334
334
|
color="warning"
|
|
335
|
-
variant="
|
|
335
|
+
variant="subtle"
|
|
336
336
|
icon="lucide:info"
|
|
337
337
|
/>
|
|
338
338
|
|
|
@@ -341,7 +341,7 @@ async function applyMigrations(useForm: boolean) {
|
|
|
341
341
|
:title="t('supabase.connection.restartTitle')"
|
|
342
342
|
:description="t('supabase.migrations.restartDescription')"
|
|
343
343
|
color="info"
|
|
344
|
-
variant="
|
|
344
|
+
variant="subtle"
|
|
345
345
|
icon="lucide:refresh-cw"
|
|
346
346
|
/>
|
|
347
347
|
|
|
@@ -374,11 +374,13 @@ async function applyMigrations(useForm: boolean) {
|
|
|
374
374
|
</UCard>
|
|
375
375
|
|
|
376
376
|
<UCard v-if="lastRun">
|
|
377
|
-
<
|
|
377
|
+
<template #header>
|
|
378
378
|
<h2 class="font-semibold">
|
|
379
379
|
{{ t('supabase.migrations.lastRun') }}
|
|
380
380
|
</h2>
|
|
381
|
+
</template>
|
|
381
382
|
|
|
383
|
+
<div class="flex flex-col gap-y-4">
|
|
382
384
|
<EnvPersistWarning
|
|
383
385
|
v-if="lastRun.success && lastRun.persisted === false"
|
|
384
386
|
:connection-string="lastConnectionString"
|
|
@@ -410,7 +412,7 @@ async function applyMigrations(useForm: boolean) {
|
|
|
410
412
|
:title="t('supabase.migrations.loadFailed')"
|
|
411
413
|
:description="t('supabase.migrations.loadFailedDescription')"
|
|
412
414
|
color="error"
|
|
413
|
-
variant="
|
|
415
|
+
variant="subtle"
|
|
414
416
|
icon="lucide:circle-x"
|
|
415
417
|
>
|
|
416
418
|
<template #actions>
|
|
@@ -424,5 +426,5 @@ async function applyMigrations(useForm: boolean) {
|
|
|
424
426
|
</UButton>
|
|
425
427
|
</template>
|
|
426
428
|
</UAlert>
|
|
427
|
-
</
|
|
429
|
+
</PlutoViewAdminPanel>
|
|
428
430
|
</template>
|