mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 03:45:06 +03:00
Update according to requirements
This commit is contained in:
1 parent
55654fc7c3
commit
e704ab5aaa
4 files changed
+159
-32
No files matched your search
@@ -0,0 +1,71 @@
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import { OAuthApp } from 'data/oauth/oauth-apps-query'
|
||||
import dayjs from 'dayjs'
|
||||
import { copyToClipboard } from 'lib/helpers'
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
IconCheck,
|
||||
IconClipboard,
|
||||
IconEdit,
|
||||
IconMoreVertical,
|
||||
IconTrash,
|
||||
} from 'ui'
|
||||
|
||||
export interface OAuthAppRowProps {
|
||||
app: OAuthApp
|
||||
onSelectEdit: () => void
|
||||
onSelectDelete: () => void
|
||||
}
|
||||
|
||||
const OAuthAppRow = ({ app, onSelectEdit, onSelectDelete }: OAuthAppRowProps) => {
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
|
||||
return (
|
||||
<Table.tr>
|
||||
<Table.td>{app.name}</Table.td>
|
||||
<Table.td>
|
||||
<span className="font-mono truncate">{app.client_id}</span>
|
||||
<Button
|
||||
type="default"
|
||||
icon={
|
||||
isCopied ? <IconCheck className="text-brand-900" strokeWidth={3} /> : <IconClipboard />
|
||||
}
|
||||
className="ml-2 px-1"
|
||||
onClick={() => {
|
||||
copyToClipboard(app.client_id)
|
||||
setIsCopied(true)
|
||||
setTimeout(() => {
|
||||
setIsCopied(false)
|
||||
}, 3000)
|
||||
}}
|
||||
/>
|
||||
</Table.td>
|
||||
<Table.td>
|
||||
<span className="font-mono">{app.client_secret_alias}...</span>
|
||||
</Table.td>
|
||||
<Table.td>{dayjs(app.created_at).format('DD/MM/YYYY, HH:mm:ss')}</Table.td>
|
||||
<Table.td align="right">
|
||||
<Dropdown
|
||||
size="tiny"
|
||||
align="end"
|
||||
side="bottom"
|
||||
overlay={[
|
||||
<Dropdown.Item key="edit" icon={<IconEdit />} onClick={() => onSelectEdit()}>
|
||||
Edit app
|
||||
</Dropdown.Item>,
|
||||
<Dropdown.Separator key="separator" />,
|
||||
<Dropdown.Item key="delete" icon={<IconTrash />} onClick={() => onSelectDelete()}>
|
||||
Delete app
|
||||
</Dropdown.Item>,
|
||||
]}
|
||||
>
|
||||
<Button type="default" icon={<IconMoreVertical />} className="px-1" />
|
||||
</Dropdown>
|
||||
</Table.td>
|
||||
</Table.tr>
|
||||
)
|
||||
}
|
||||
|
||||
export default OAuthAppRow
|
||||
@@ -2,16 +2,20 @@ import { useParams } from 'common'
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import { OAuthApp, useOAuthAppsQuery } from 'data/oauth/oauth-apps-query'
|
||||
import { useState } from 'react'
|
||||
import { Button, IconEdit, IconTrash } from 'ui'
|
||||
import { Alert, Button, IconClipboard, IconEdit, IconTrash, IconX, Input } from 'ui'
|
||||
import PublishAppModal from './PublishAppModal'
|
||||
import DeleteAppModal from './DeleteAppModal'
|
||||
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { copyToClipboard } from 'lib/helpers'
|
||||
import OAuthAppRow from './OAuthAppRow'
|
||||
import { OAuthAppCreateResponse } from 'data/oauth/oauth-app-create-mutation'
|
||||
|
||||
// [Joshen] Just FYI need to relook at the copy writing at the end
|
||||
|
||||
const OAuthApps = () => {
|
||||
const { slug } = useParams()
|
||||
const [createdApp, setCreatedApp] = useState<OAuthAppCreateResponse>()
|
||||
const [showPublishModal, setShowPublishModal] = useState(false)
|
||||
const [selectedAppToUpdate, setSelectedAppToUpdate] = useState<OAuthApp>()
|
||||
const [selectedAppToDelete, setSelectedAppToDelete] = useState<OAuthApp>()
|
||||
@@ -58,6 +62,47 @@ const OAuthApps = () => {
|
||||
|
||||
{isError && <AlertError subject="Unable to retrieve published OAuth apps" />}
|
||||
|
||||
{createdApp !== undefined && (
|
||||
<Alert withIcon variant="success" title="Successfully published a new application!">
|
||||
<div className="absolute top-4 right-4">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<IconX size={18} />}
|
||||
className="px-1"
|
||||
onClick={() => setCreatedApp(undefined)}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full space-y-4">
|
||||
<p className="text-sm">
|
||||
Ensure that you store the client secret securely - you will not be able to see
|
||||
it again.
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
<Input
|
||||
copy
|
||||
readOnly
|
||||
size="small"
|
||||
// layout="horizontal"
|
||||
label="Client ID"
|
||||
className="max-w-xl input-mono"
|
||||
value={createdApp.client_id}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
<Input
|
||||
copy
|
||||
readOnly
|
||||
size="small"
|
||||
// layout="horizontal"
|
||||
label="Client secret"
|
||||
className="max-w-xl input-mono"
|
||||
value={createdApp.client_secret}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{isSuccess && (
|
||||
<>
|
||||
{(publishedApps?.length ?? 0) === 0 ? (
|
||||
@@ -71,36 +116,20 @@ const OAuthApps = () => {
|
||||
<Table.th key="name">Name</Table.th>,
|
||||
<Table.th key="client-id">Client ID</Table.th>,
|
||||
<Table.th key="client-secret">Client Secret</Table.th>,
|
||||
<Table.th key="client-secret">Created at</Table.th>,
|
||||
<Table.th key="delete-action"></Table.th>,
|
||||
]}
|
||||
body={
|
||||
sortedPublishedApps?.map((app) => (
|
||||
<Table.tr key={app.id}>
|
||||
<Table.td>{app.name}</Table.td>
|
||||
<Table.td>{app.client_id}</Table.td>
|
||||
<Table.td>{app.client_secret_alias}</Table.td>
|
||||
<Table.td align="right">
|
||||
<div className="space-x-2">
|
||||
<Button
|
||||
type="default"
|
||||
title="Delete app"
|
||||
icon={<IconEdit />}
|
||||
className="px-1"
|
||||
onClick={() => {
|
||||
setShowPublishModal(true)
|
||||
setSelectedAppToUpdate(app)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="default"
|
||||
title="Delete app"
|
||||
icon={<IconTrash />}
|
||||
className="px-1"
|
||||
onClick={() => setSelectedAppToDelete(app)}
|
||||
/>
|
||||
</div>
|
||||
</Table.td>
|
||||
</Table.tr>
|
||||
<OAuthAppRow
|
||||
key={app.id}
|
||||
app={app}
|
||||
onSelectEdit={() => {
|
||||
setShowPublishModal(true)
|
||||
setSelectedAppToUpdate(app)
|
||||
}}
|
||||
onSelectDelete={() => setSelectedAppToDelete(app)}
|
||||
/>
|
||||
)) ?? []
|
||||
}
|
||||
/>
|
||||
@@ -151,6 +180,7 @@ const OAuthApps = () => {
|
||||
setSelectedAppToUpdate(undefined)
|
||||
setShowPublishModal(false)
|
||||
}}
|
||||
onCreateSuccess={setCreatedApp}
|
||||
/>
|
||||
<DeleteAppModal
|
||||
selectedApp={selectedAppToDelete}
|
||||
|
||||
@@ -2,7 +2,10 @@ import clsx from 'clsx'
|
||||
import { ChangeEvent, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { useOAuthAppCreateMutation } from 'data/oauth/oauth-app-create-mutation'
|
||||
import {
|
||||
OAuthAppCreateResponse,
|
||||
useOAuthAppCreateMutation,
|
||||
} from 'data/oauth/oauth-app-create-mutation'
|
||||
import { useOAuthAppUpdateMutation } from 'data/oauth/oauth-app-update-mutation'
|
||||
import { OAuthApp } from 'data/oauth/oauth-apps-query'
|
||||
import { useStore } from 'hooks'
|
||||
@@ -14,9 +17,15 @@ export interface PublishAppModalProps {
|
||||
visible: boolean
|
||||
selectedApp?: OAuthApp
|
||||
onClose: () => void
|
||||
onCreateSuccess: (app: OAuthAppCreateResponse) => void
|
||||
}
|
||||
|
||||
const PublishAppModal = ({ visible, selectedApp, onClose }: PublishAppModalProps) => {
|
||||
const PublishAppModal = ({
|
||||
visible,
|
||||
selectedApp,
|
||||
onClose,
|
||||
onCreateSuccess,
|
||||
}: PublishAppModalProps) => {
|
||||
const { ui } = useStore()
|
||||
const { slug } = useParams()
|
||||
const uploadButtonRef = useRef<any>()
|
||||
@@ -96,12 +105,19 @@ const PublishAppModal = ({ visible, selectedApp, onClose }: PublishAppModalProps
|
||||
if (selectedApp === undefined) {
|
||||
// Create application
|
||||
try {
|
||||
await createOAuthApp({ slug, name, website, redirect_uris, icon: uploadedIconUrl })
|
||||
const res = await createOAuthApp({
|
||||
slug,
|
||||
name,
|
||||
website,
|
||||
redirect_uris,
|
||||
icon: uploadedIconUrl,
|
||||
})
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Successfully created OAuth app "${name}"!`,
|
||||
})
|
||||
onClose()
|
||||
onCreateSuccess(res)
|
||||
} catch (error: any) {
|
||||
setSubmitting(false)
|
||||
ui.setNotification({
|
||||
@@ -163,7 +179,11 @@ const PublishAppModal = ({ visible, selectedApp, onClose }: PublishAppModalProps
|
||||
<Modal.Content>
|
||||
<div className="py-4 flex items-start justify-between gap-10">
|
||||
<div className="space-y-4 w-full">
|
||||
<Input id="name" label="Application name"></Input>
|
||||
<Input
|
||||
id="name"
|
||||
label="Application name"
|
||||
descriptionText={selectedApp?.id && `ID: ${selectedApp.id}`}
|
||||
></Input>
|
||||
<Input
|
||||
id="website"
|
||||
label="Website URL"
|
||||
|
||||
@@ -11,6 +11,12 @@ export type OAuthAppCreateVariables = {
|
||||
redirect_uris: string[]
|
||||
}
|
||||
|
||||
export type OAuthAppCreateResponse = {
|
||||
id: string
|
||||
client_id: string
|
||||
client_secret: string
|
||||
}
|
||||
|
||||
export async function createOAuthApp({
|
||||
slug,
|
||||
name,
|
||||
@@ -29,7 +35,7 @@ export async function createOAuthApp({
|
||||
redirect_uris,
|
||||
})
|
||||
if (response.error) throw response.error
|
||||
return response
|
||||
return response as OAuthAppCreateResponse
|
||||
}
|
||||
|
||||
type OAuthAppCreateData = Awaited<ReturnType<typeof createOAuthApp>>
|
||||
|
||||
Reference in new issue
Block a user