Skip to content

Commit 1fbc3f3

Browse files
authored
Update auth admin design + add support for webhooks and openid connect providers (#369)
1 parent 3276196 commit 1fbc3f3

25 files changed

+2598
-1785
lines changed

shared/common/newui/button/index.tsx

+30-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import cn from "@edgedb/common/utils/classNames";
22

33
import styles from "./button.module.scss";
44
import Spinner from "../../ui/spinner";
5-
import {PropsWithChildren} from "react";
5+
import {CSSProperties, PropsWithChildren, useEffect, useState} from "react";
66

77
interface _BaseButtonProps {
88
className?: string;
@@ -12,6 +12,7 @@ interface _BaseButtonProps {
1212
rightIcon?: JSX.Element;
1313
disabled?: boolean;
1414
loading?: boolean;
15+
style?: CSSProperties;
1516
}
1617

1718
export interface ButtonProps extends _BaseButtonProps {
@@ -107,3 +108,31 @@ export function LinkButton({
107108
</a>
108109
);
109110
}
111+
112+
export function ConfirmButton({onClick, children, ...props}: ButtonProps) {
113+
const [confirming, setConfirming] = useState(false);
114+
115+
useEffect(() => {
116+
if (confirming) {
117+
const timer = setTimeout(() => setConfirming(false), 1000);
118+
119+
return () => clearTimeout(timer);
120+
}
121+
}, [confirming]);
122+
123+
return (
124+
<Button
125+
onClick={() => {
126+
if (confirming) {
127+
setConfirming(false);
128+
onClick?.();
129+
} else {
130+
setConfirming(true);
131+
}
132+
}}
133+
{...props}
134+
>
135+
{confirming ? "Confirm?" : children}
136+
</Button>
137+
);
138+
}

shared/common/newui/checkbox/checkbox.module.scss

+8
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@
4343
pointer-events: none;
4444
}
4545

46+
&.readonly {
47+
pointer-events: none;
48+
49+
.check {
50+
opacity: 0.5;
51+
}
52+
}
53+
4654
@include darkTheme {
4755
color: #ccc;
4856

shared/common/newui/checkbox/index.tsx

+15-7
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,40 @@ import cn from "@edgedb/common/utils/classNames";
22

33
import styles from "./checkbox.module.scss";
44

5-
export interface CheckboxProps {
5+
export type CheckboxProps<Readonly extends boolean> = {
66
className?: string;
77
label?: string | JSX.Element;
88
checked: boolean;
9-
onChange: (checked: boolean) => void;
109
disabled?: boolean;
11-
}
10+
readOnly?: Readonly;
11+
} & (Readonly extends true
12+
? {
13+
onChange?: (checked: boolean) => void;
14+
}
15+
: {
16+
onChange: (checked: boolean) => void;
17+
});
1218

13-
export function Checkbox({
19+
export function Checkbox<Readonly extends boolean = false>({
1420
className,
1521
label,
1622
checked,
1723
onChange,
1824
disabled,
19-
}: CheckboxProps) {
25+
readOnly,
26+
}: CheckboxProps<Readonly>) {
2027
return (
2128
<label
2229
className={cn(styles.checkbox, className, {
2330
[styles.disabled]: !!disabled,
31+
[styles.readonly]: !!readOnly,
2432
})}
2533
>
2634
<input
2735
type="checkbox"
2836
checked={checked}
29-
onChange={(e) => onChange(e.target.checked)}
30-
disabled={disabled}
37+
onChange={(e) => onChange?.(e.target.checked)}
38+
disabled={disabled || readOnly}
3139
/>
3240
<div className={styles.check} />
3341
{label}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import styles from "./loadingSkeleton.module.scss";
2+
3+
export function LoadingSkeleton({ className }: { className?: string }) {
4+
return <div className={`${styles.loadingSkeleton} ${className ?? ""}`} />;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
@import "@edgedb/common/mixins.scss";
2+
3+
.loadingSkeleton {
4+
border-radius: 8px;
5+
background: var(--skeletonBg, #ededed);
6+
background-image: linear-gradient(
7+
90deg,
8+
transparent,
9+
var(--skeletonShimmer, #fafafa) 66%,
10+
transparent
11+
);
12+
background-repeat: no-repeat;
13+
background-size: 100px 100%;
14+
animation: shimmer 2s infinite linear;
15+
16+
@include darkTheme {
17+
--skeletonBg: #2d2d2d;
18+
--skeletonShimmer: #3c3c3c;
19+
}
20+
}
21+
22+
@keyframes shimmer {
23+
0% {
24+
background-position: -100px;
25+
}
26+
50%,
27+
100% {
28+
background-position: calc(100% + 100px);
29+
}
30+
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import cn from "@edgedb/common/utils/classNames";
2+
3+
import styles from "./panelTabs.module.scss";
4+
5+
export interface PanelTabsProps<TabId extends string> {
6+
className?: string;
7+
tabs: {id: TabId; label: string | JSX.Element}[];
8+
selectedTabId: TabId;
9+
setSelectedTabId: (id: TabId) => void;
10+
}
11+
12+
export function PanelTabs<TabId extends string = string>({
13+
className,
14+
tabs,
15+
selectedTabId,
16+
setSelectedTabId,
17+
}: PanelTabsProps<TabId>) {
18+
return (
19+
<div className={cn(styles.panelTabs, className)}>
20+
{tabs.map((tab) => (
21+
<div
22+
key={tab.id}
23+
className={cn(styles.tab, {
24+
[styles.active]: selectedTabId === tab.id,
25+
})}
26+
onClick={() => setSelectedTabId(tab.id)}
27+
>
28+
{tab.label}
29+
</div>
30+
))}
31+
</div>
32+
);
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
@import "@edgedb/common/mixins.scss";
2+
3+
.panelTabs {
4+
grid-column: 1;
5+
grid-row: 1;
6+
display: flex;
7+
flex-direction: column;
8+
gap: 12px;
9+
align-self: start;
10+
position: sticky;
11+
top: 0;
12+
width: max-content;
13+
justify-self: end;
14+
text-align: end;
15+
padding: 64px 24px;
16+
z-index: 1;
17+
18+
.tab {
19+
position: relative;
20+
font-family: "Roboto Flex Variable", sans-serif;
21+
color: var(--secondary_text_color);
22+
text-decoration: none;
23+
font-weight: 500;
24+
padding: 12px 16px;
25+
border-radius: 8px;
26+
cursor: pointer;
27+
28+
&:hover {
29+
background: #fff;
30+
}
31+
32+
&.active {
33+
color: var(--main_text_color);
34+
35+
&:after {
36+
content: "";
37+
position: absolute;
38+
right: 1px;
39+
top: 4px;
40+
bottom: 4px;
41+
width: 2px;
42+
border-radius: 1px;
43+
background: #a565cd;
44+
}
45+
}
46+
}
47+
48+
@include darkTheme {
49+
.tab:hover {
50+
background: var(--Grey12);
51+
}
52+
}
53+
54+
@include isMobile {
55+
border-bottom: 1px solid var(--Grey93, #ededed);
56+
background: var(--Grey99, #fcfcfc);
57+
box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.08);
58+
flex-direction: row;
59+
padding: 0;
60+
width: 100%;
61+
align-items: center;
62+
justify-content: center;
63+
height: 44px;
64+
flex-shrink: 0;
65+
66+
.tab {
67+
padding: 10px 12px;
68+
border-radius: 6px;
69+
70+
&:hover {
71+
background: var(--Grey95, #f2f2f2);
72+
}
73+
74+
&.active:after {
75+
left: 4px;
76+
right: 4px;
77+
top: auto;
78+
bottom: -3px;
79+
height: 2px;
80+
width: auto;
81+
}
82+
}
83+
84+
@include darkTheme {
85+
border-bottom: 1px solid var(--Grey22, #363636);
86+
background: var(--Grey18, #2e2e2e);
87+
box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.2);
88+
89+
.tab:hover {
90+
background: var(--Grey14, #242424);
91+
}
92+
}
93+
}
94+
}

shared/common/newui/select/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Select as _Select,
44
SelectProps as _SelectProps,
55
} from "@edgedb/common/ui/select";
6+
export type {SelectItem, SelectItems} from "@edgedb/common/ui/select";
67

78
import styles from "./select.module.scss";
89

shared/common/newui/textInput/index.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface TextInputProps extends FieldHeaderProps {
1212
type?: "text" | "password" | "textarea";
1313
error?: string | null;
1414
prefix?: string;
15+
suffixEl?: JSX.Element;
1516
}
1617

1718
export const TextInput = forwardRef(function TextInput(
@@ -23,6 +24,7 @@ export const TextInput = forwardRef(function TextInput(
2324
headerNote,
2425
error,
2526
prefix,
27+
suffixEl,
2628
...props
2729
}: TextInputProps &
2830
Omit<InputHTMLAttributes<HTMLInputElement | HTMLTextAreaElement>, "type">,
@@ -53,6 +55,7 @@ export const TextInput = forwardRef(function TextInput(
5355
: undefined,
5456
}}
5557
/>
58+
{suffixEl}
5659
{error != null ? (
5760
<div className={styles.error}>
5861
<InfoIcon />

shared/studio/components/databasePage/databasePage.module.scss

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
.databasePage {
44
position: relative;
55
flex-grow: 1;
6-
overflow: hidden;
6+
min-height: 0;
77
display: grid;
8-
padding: 0 16px 16px 0;
8+
padding: 0 8px 8px 0;
99
grid-template-areas: "tabs session" "tabs content";
1010
grid-template-columns: auto 1fr;
1111
grid-template-rows: auto 1fr;

shared/studio/tabs/ai/index.tsx

+2-30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {useEffect, useLayoutEffect, useState} from "react";
1+
import {useEffect, useLayoutEffect} from "react";
22
import {Observer, observer} from "mobx-react-lite";
33

44
import cn from "@edgedb/common/utils/classNames";
@@ -15,7 +15,7 @@ import {PlaygroundTab} from "./playground";
1515
import {PromptsTab} from "./prompts";
1616

1717
import styles from "./aiAdmin.module.scss";
18-
import {Button, ButtonProps, WarningIcon} from "@edgedb/common/newui";
18+
import {WarningIcon} from "@edgedb/common/newui";
1919

2020
const AIAdminPage = observer(function AIAdminPage() {
2121
const state = useTabState(AIAdminState);
@@ -133,31 +133,3 @@ function AIAdminLayout() {
133133
</div>
134134
);
135135
}
136-
137-
export function ConfirmButton({onClick, children, ...props}: ButtonProps) {
138-
const [confirming, setConfirming] = useState(false);
139-
140-
useEffect(() => {
141-
if (confirming) {
142-
const timer = setTimeout(() => setConfirming(false), 1000);
143-
144-
return () => clearTimeout(timer);
145-
}
146-
}, [confirming]);
147-
148-
return (
149-
<Button
150-
onClick={() => {
151-
if (confirming) {
152-
setConfirming(false);
153-
onClick?.();
154-
} else {
155-
setConfirming(true);
156-
}
157-
}}
158-
{...props}
159-
>
160-
{confirming ? "Confirm?" : children}
161-
</Button>
162-
);
163-
}

shared/studio/tabs/ai/prompts.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {observer} from "mobx-react-lite";
44
import cn from "@edgedb/common/utils/classNames";
55
import {
66
Button,
7+
ConfirmButton,
78
ChevronDownIcon,
89
InfoIcon,
910
InfoTooltip,
@@ -16,7 +17,6 @@ import {AIAdminState, AIPromptDraft, PromptChatParticipantRole} from "./state";
1617

1718
import textStyles from "@edgedb/common/newui/textInput/textInput.module.scss";
1819
import styles from "./aiAdmin.module.scss";
19-
import {ConfirmButton} from ".";
2020

2121
export const PromptsTab = observer(function PromptTab() {
2222
const state = useTabState(AIAdminState);

0 commit comments

Comments
 (0)