mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 11:55:05 +03:00
Merge branch 'master' into feat/studio-logs-page-query-params
This commit is contained in:
commit
73f22ee2d4
14 files changed
+199
-43
No files matched your search
@@ -0,0 +1,35 @@
|
||||
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Studio Unit Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'studio/**'
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- name: Install deps
|
||||
run: npm i
|
||||
working-directory: ./studio
|
||||
- name: Run tests
|
||||
run: npm test
|
||||
working-directory: ./studio
|
||||
@@ -5,6 +5,7 @@ import DataGrid from '@supabase/react-data-grid'
|
||||
|
||||
import LogSelection from './LogSelection'
|
||||
import { LogData } from './Logs.types'
|
||||
import { isNil } from 'lodash'
|
||||
|
||||
interface Props {
|
||||
isCustomQuery: boolean
|
||||
@@ -50,12 +51,13 @@ const LogTable = ({ isCustomQuery, data }: Props) => {
|
||||
}, {})
|
||||
}, [JSON.stringify(data)])
|
||||
|
||||
const strLogMap = JSON.stringify(logMap)
|
||||
useEffect(() => {
|
||||
if (!data) return
|
||||
if (isNil(data)) return
|
||||
if (focusedLog && !(focusedLog.id in logMap)) {
|
||||
setFocusedLog(null)
|
||||
}
|
||||
}, [Object.keys(logMap)])
|
||||
}, [strLogMap])
|
||||
|
||||
if (!data) return null
|
||||
|
||||
@@ -64,7 +66,7 @@ const LogTable = ({ isCustomQuery, data }: Props) => {
|
||||
|
||||
const logDataRows = useMemo(() => {
|
||||
return Object.values(logMap).sort((a, b) => a.timestamp - b.timestamp)
|
||||
}, [JSON.stringify(Object.keys(logMap))])
|
||||
}, [strLogMap])
|
||||
return (
|
||||
<section className="flex flex-1 flex-row" style={{ maxHeight }}>
|
||||
<DataGrid
|
||||
|
||||
@@ -3,7 +3,7 @@ import Link from 'next/link'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useRouter } from 'next/router'
|
||||
import { isUndefined } from 'lodash'
|
||||
import { Button, Divider, IconHome, Dropdown, IconUser, Toggle, Typography } from '@supabase/ui'
|
||||
import { Button, Divider, IconHome, Dropdown, IconUser, Select, Typography } from '@supabase/ui'
|
||||
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { useStore } from 'hooks'
|
||||
@@ -77,13 +77,16 @@ const NavigationBar: FC<Props> = ({}) => {
|
||||
: []),
|
||||
<Divider key="d1" light />,
|
||||
<Dropdown.Misc key="theme">
|
||||
<div className="w-[200px] pt-1">
|
||||
<Toggle
|
||||
size="small"
|
||||
label="Enable dark mode"
|
||||
checked={ui.isDarkTheme}
|
||||
onChange={() => ui.toggleTheme()}
|
||||
/>
|
||||
<div className="w-[240px] py-1 flex items-center justify-between">
|
||||
<Typography.Text>Theme</Typography.Text>
|
||||
<Select
|
||||
value={ui.themeOption}
|
||||
onChange={(e: any) => ui.onThemeOptionChange(e.target.value)}
|
||||
>
|
||||
<Select.Option value="system">System default</Select.Option>
|
||||
<Select.Option value="dark">Dark</Select.Option>
|
||||
<Select.Option value="light">Light</Select.Option>
|
||||
</Select>
|
||||
</div>
|
||||
</Dropdown.Misc>,
|
||||
]}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMonaco } from '@monaco-editor/react'
|
||||
import { autorun } from 'mobx'
|
||||
import { createContext, FC, useContext, useEffect } from 'react'
|
||||
import { createContext, FC, useContext, useEffect, useCallback } from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
import { IRootStore } from 'stores'
|
||||
@@ -33,8 +33,14 @@ export const StoreProvider: FC<StoreProvider> = ({ children, rootStore }) => {
|
||||
}
|
||||
}, [theme, monaco])
|
||||
|
||||
const matchMediaEvent = useCallback(() => {
|
||||
ui.themeOption === 'system' &&
|
||||
ui.setTheme(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
ui.load()
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', matchMediaEvent)
|
||||
|
||||
autorun(() => {
|
||||
if (ui.notification) {
|
||||
@@ -71,6 +77,10 @@ export const StoreProvider: FC<StoreProvider> = ({ children, rootStore }) => {
|
||||
}
|
||||
}
|
||||
})
|
||||
return () =>
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.removeEventListener('change', matchMediaEvent)
|
||||
}, [])
|
||||
|
||||
return <StoreContext.Provider value={rootStore}>{children}</StoreContext.Provider>
|
||||
|
||||
+13
-16
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { IconMoon, IconSun, Select, Typography } from '@supabase/ui'
|
||||
|
||||
@@ -77,15 +77,9 @@ const ProfileCard = observer(() => {
|
||||
)
|
||||
})
|
||||
|
||||
function ThemeSettings() {
|
||||
const [theme, setTheme] = useState('')
|
||||
const ThemeSettings = observer(() => {
|
||||
const { ui } = useStore()
|
||||
|
||||
useEffect(() => {
|
||||
const localStorageTheme = window.localStorage.getItem('theme')
|
||||
if (localStorageTheme) setTheme(localStorageTheme)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Panel
|
||||
title={[
|
||||
@@ -96,22 +90,25 @@ function ThemeSettings() {
|
||||
>
|
||||
<Panel.Content>
|
||||
<Select
|
||||
value={theme}
|
||||
value={ui.themeOption}
|
||||
label="Interface theme"
|
||||
descriptionText="Choose a theme preference"
|
||||
layout="horizontal"
|
||||
style={{ width: '50%' }}
|
||||
icon={theme === 'light' ? <IconSun /> : <IconMoon />}
|
||||
onChange={(e: any) => {
|
||||
setTheme(e.target.value)
|
||||
ui.setTheme(e.target.value)
|
||||
}}
|
||||
icon={
|
||||
ui.themeOption === 'light' ? (
|
||||
<IconSun />
|
||||
) : ui.themeOption === 'dark' ? (
|
||||
<IconMoon />
|
||||
) : undefined
|
||||
}
|
||||
onChange={(e: any) => ui.onThemeOptionChange(e.target.value)}
|
||||
>
|
||||
{/* <Select.Option value="system">System default</Select.Option> */}
|
||||
<Select.Option value="system">System default</Select.Option>
|
||||
<Select.Option value="dark">Dark</Select.Option>
|
||||
<Select.Option value="light">Light</Select.Option>
|
||||
</Select>
|
||||
</Panel.Content>
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -131,7 +131,6 @@ export const LogPage: NextPage = () => {
|
||||
setLatestRefresh(new Date().toISOString())
|
||||
setParams({ ...params, timestamp_start: '' })
|
||||
setSize(1)
|
||||
mutate()
|
||||
}
|
||||
|
||||
const handleModeToggle = () => {
|
||||
|
||||
+28
-11
@@ -7,6 +7,7 @@ import Telemetry from 'lib/telemetry'
|
||||
export interface IUiStore {
|
||||
language: 'en_US'
|
||||
theme: 'dark' | 'light'
|
||||
themeOption: 'dark' | 'light' | 'system'
|
||||
|
||||
isDarkTheme: boolean
|
||||
selectedProject?: Project
|
||||
@@ -15,8 +16,8 @@ export interface IUiStore {
|
||||
profile?: User
|
||||
|
||||
load: () => void
|
||||
toggleTheme: () => void
|
||||
setTheme: (theme: 'dark' | 'light') => void
|
||||
onThemeOptionChange: (themeOption: 'dark' | 'light' | 'system') => void
|
||||
setProjectRef: (ref?: string) => void
|
||||
setOrganizationSlug: (slug?: string) => void
|
||||
setNotification: (notification: Notification) => string
|
||||
@@ -26,6 +27,7 @@ export default class UiStore implements IUiStore {
|
||||
rootStore: IRootStore
|
||||
language: 'en_US' = 'en_US'
|
||||
theme: 'dark' | 'light' = 'dark'
|
||||
themeOption: 'dark' | 'light' | 'system' = 'dark'
|
||||
|
||||
selectedProjectRef?: string
|
||||
selectedOrganizationSlug?: string
|
||||
@@ -73,24 +75,39 @@ export default class UiStore implements IUiStore {
|
||||
|
||||
load() {
|
||||
if (typeof window === 'undefined') return
|
||||
const savedTheme = (window.localStorage.getItem('theme') ?? 'dark') as 'dark' | 'light'
|
||||
this.setTheme(savedTheme)
|
||||
}
|
||||
|
||||
toggleTheme() {
|
||||
if (this.theme === 'dark') {
|
||||
this.setTheme('light')
|
||||
} else {
|
||||
this.setTheme('dark')
|
||||
const localStorageThemeOption = window.localStorage.getItem('theme')
|
||||
if (localStorageThemeOption === 'system') {
|
||||
this.themeOption = localStorageThemeOption
|
||||
return this.setTheme(
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
)
|
||||
}
|
||||
if (localStorageThemeOption === 'light') {
|
||||
this.themeOption = localStorageThemeOption
|
||||
return this.setTheme('light')
|
||||
}
|
||||
window.localStorage.setItem('theme', 'dark')
|
||||
this.themeOption = 'dark'
|
||||
this.setTheme('dark')
|
||||
}
|
||||
|
||||
setTheme(theme: 'dark' | 'light') {
|
||||
this.theme = theme
|
||||
window.localStorage.setItem('theme', theme)
|
||||
document.body.className = theme
|
||||
}
|
||||
|
||||
onThemeOptionChange(themeOption: 'dark' | 'light' | 'system') {
|
||||
this.themeOption = themeOption
|
||||
if (themeOption === 'system') {
|
||||
window.localStorage.setItem('theme', 'system')
|
||||
return this.setTheme(
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
)
|
||||
}
|
||||
window.localStorage.setItem('theme', themeOption)
|
||||
this.setTheme(themeOption)
|
||||
}
|
||||
|
||||
setProjectRef(ref?: string) {
|
||||
this.selectedProjectRef = ref
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ test('Refreshpage', async () => {
|
||||
await waitFor(() => screen.getByText(/my_key/))
|
||||
|
||||
// simulate refresh
|
||||
await waitFor(() => userEvent.click(screen.getByText(/Refresh/)))
|
||||
userEvent.click(screen.getByText(/Refresh/))
|
||||
// when log line unmounts and it was focused, should close focus panel
|
||||
await waitFor(() => screen.queryByText(/my_key/) === null, { timeout: 1000 })
|
||||
await waitFor(() => screen.queryByText(/happened/) === null, { timeout: 1000 })
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
id: auth-azure
|
||||
title: 'Login with Azure'
|
||||
description: Add Azure OAuth to your Supabase project
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
To enable Azure Auth for your project, you need to set up an Azure OAuth application and add the application credentials to your Supabase Dashboard.
|
||||
|
||||
## Overview
|
||||
|
||||
Azure OAuth consists of six broad steps:
|
||||
|
||||
- Create an application under Azure Active Directory.
|
||||
- Obtain a `Application (client) ID` with “Sign In with Azure” capabilities. This will be used as the `client id`.
|
||||
- Create a `Secret ID` with “Sign In with Azure” capabilities. The value of the secret will be used as the `client secret`.
|
||||
- Whitelist the callback url of your application.
|
||||
|
||||
## Steps
|
||||
|
||||
### Access your Azure Developer account
|
||||
|
||||
- Go to [portal.azure.com](https://portal.azure.com/#home).
|
||||
- Login and select "Azure Active Directory" under the list of Azure Services.
|
||||
|
||||
### Register an application
|
||||
|
||||
- Under Azure Active Directory, select "App registrations" in the side panel.
|
||||
- Select "New registration".
|
||||
- Choose a name and select your preferred option for the supported account types.
|
||||
- Specify the "Redirect URI".
|
||||
- The redirect / callback URI should look like this: `https://<project-ref>.supabase.co/auth/v1/callback`
|
||||
- Click "Register" at the bottom of the form.
|
||||
|
||||

|
||||
|
||||
### Obtain a Client ID
|
||||
|
||||
This will serve as the `client_id` when you make API calls to authenticate the user.
|
||||
|
||||
- Once your app has been registered, the client id can be found under the [list of app registrations](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) under the column titled "Application (client) ID".
|
||||
|
||||

|
||||
|
||||
### Obtain a Secret ID
|
||||
|
||||
This will serve as the `client_secret` when you make API calls to authenticate the user.
|
||||
|
||||
- Click on the name of the app registered above.
|
||||
- Under "Essentials", click on "Client credentials".
|
||||
- Navigate to the "Client secrets" tab and select "New client secret".
|
||||
- Enter a description and choose your preferred expiry for the secret.
|
||||
- Once the secret is generated, save the value (not the secret ID).
|
||||
|
||||

|
||||
|
||||
### Add login code to your client app
|
||||
|
||||
The JavaScript client code is documented in the [Supabase OAuth Reference](/docs/reference/javascript/auth-signin#sign-in-using-third-party-providers).
|
||||
|
||||
```js
|
||||
const { user, session, error } = await supabase.auth.signIn({
|
||||
provider: 'Azure',
|
||||
})
|
||||
```
|
||||
|
||||
Add a function which you can call from a button, link, or UI element.
|
||||
|
||||
```js
|
||||
async function signInWithAzure() {
|
||||
const { user, session, error } = await supabase.auth.signIn({
|
||||
provider: 'Azure',
|
||||
}, {
|
||||
scopes: 'email',
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
To log out:
|
||||
|
||||
```js
|
||||
async function signout() {
|
||||
const { error } = await supabase.auth.signOut()
|
||||
}
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Azure Developer Account](https://portal.azure.com).
|
||||
- [Github Discussion](https://github.com/supabase/gotrue/pull/54#issuecomment-757043573).
|
||||
@@ -148,6 +148,7 @@ module.exports = {
|
||||
'guides/auth/auth-email',
|
||||
'guides/auth/auth-magic-link',
|
||||
'guides/auth/auth-apple',
|
||||
'guides/auth/auth-azure',
|
||||
'guides/auth/auth-bitbucket',
|
||||
'guides/auth/auth-discord',
|
||||
'guides/auth/auth-facebook',
|
||||
|
||||
@@ -29,10 +29,10 @@ const authProviders = [
|
||||
{
|
||||
name: 'Azure',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: 'https://github.com/supabase/gotrue/pull/54#issuecomment-757043573',
|
||||
href: '/docs/guides/auth/auth-azure',
|
||||
official: false,
|
||||
supporter: 'TBD',
|
||||
platform: false,
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 383 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 290 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 334 KiB |
Reference in new issue
Block a user