Skip to content

Commit a5e2f05

Browse files
committed
toil(front): minor UI adjustments
1 parent 64fe640 commit a5e2f05

File tree

9 files changed

+161
-67
lines changed

9 files changed

+161
-67
lines changed

front/assets/css/app-semaphore.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2795,6 +2795,8 @@ img { max-width: 100%; }
27952795
.b--indigo { border-color: #1570ff; }
27962796
.b--dark-indigo { border-color: #00359f; }
27972797
.b--orange { border-color: #fd7e14; }
2798+
.b--yellow { border-color: #FBC335; }
2799+
.b--blue { border-color: #2196F3; }
27982800
.b--purple { border-color: #8658d6; }
27992801
.b--dark-purple { border-color: #5122a5; }
28002802
.b--dark-brown { border-color: #974510; }
@@ -4585,6 +4587,7 @@ code, .code, pre {
45854587
.bg-washed-purple { background-color: #f3ecff; }
45864588
/* Yellows */
45874589
.yellow { color: #FBC335; }
4590+
.gold { color: #FBC335; }
45884591
.lightest-yellow { color: #fff3bf; }
45894592
.washed-yellow { color: #fffae4; }
45904593
.bg-yellow { background-color: #FBC335; }

front/assets/js/people/add_people/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const AddNewUsers = (props: { close: (reload: boolean) => void, }) => {
8585
const Link = (props: { icon: VNode, title: string, }) => {
8686
return (
8787
<ActiveShadowLink
88-
className={`btn btn-secondary ${
88+
className={`flex-grow-1 btn btn-secondary ${
8989
currentProvider === provider ? `active` : ``
9090
}`}
9191
disabled={loading}
@@ -136,7 +136,7 @@ const AddNewUsers = (props: { close: (reload: boolean) => void, }) => {
136136
return (
137137
<div className="pa4">
138138
{userProviders.length > 1 && (
139-
<div className="mb3 button-group w-100 items-center justify-center">
139+
<div className="mb3 button-group w-100 items-center">
140140
{userProviders.map(userProviderBox)}
141141
</div>
142142
)}

front/assets/js/service_accounts/components/ServiceAccountsList.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ export const ServiceAccountsList = ({
9393
</div>
9494
) : (
9595
<div id="service-accounts">
96-
{serviceAccounts.map((account) => (
97-
<div key={account.id} className="bg-white shadow-1 ph3 pv2">
96+
{serviceAccounts.map((account, idx) => (
97+
<div key={account.id} className={`bg-white shadow-1 ph3 pv2 ${idx == serviceAccounts.length - 1 ? `br2 br--bottom` : ``}`}>
9898
<div className="flex items-center justify-between" style={{ minHeight: `45px` }}>
9999
<div className="flex items-center">
100100
<span className="material-symbols-outlined mr2 f4 gray">key</span>
@@ -117,25 +117,27 @@ export const ServiceAccountsList = ({
117117
<div className="flex-shrink-0 pl2">
118118
<div className="button-group">
119119
<button
120-
className="btn btn-sm btn-secondary"
120+
className="flex items-center btn btn-sm btn-secondary"
121121
onClick={() => onEdit(account)}
122122
title="Edit"
123123
>
124-
<span className="material-symbols-outlined f6">edit</span>
124+
<span className="material-symbols-outlined mr1">edit</span>
125+
<span>Edit</span>
125126
</button>
126127
<button
127-
className="btn btn-sm btn-secondary"
128+
className="flex items-center btn btn-sm btn-secondary"
128129
onClick={() => onRegenerateToken(account)}
129130
title="Regenerate Token"
130131
>
131-
<span className="material-symbols-outlined f6">refresh</span>
132+
<span className="material-symbols-outlined mr1">refresh</span>
133+
<span>Refresh</span>
132134
</button>
133135
<button
134-
className="btn btn-sm btn-danger"
136+
className="flex items-center btn btn-sm btn-danger"
135137
onClick={() => onDelete(account)}
136138
title="Delete"
137139
>
138-
<span className="material-symbols-outlined f6">delete</span>
140+
×
139141
</button>
140142
</div>
141143
</div>

front/assets/js/service_accounts/components/TokenDisplay.tsx

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState } from "preact/hooks";
2+
import { Box } from "js/toolbox";
23

34
interface TokenDisplayProps {
45
token: string;
@@ -27,29 +28,15 @@ export const TokenDisplay = ({ token, onClose }: TokenDisplayProps) => {
2728
</p>
2829
</div>
2930

30-
<div className="bg-washed-yellow ba b--gold br2 pa3 mb3">
31-
<div className="flex items-center justify-between">
32-
<code className="f6 truncate mr2" style={{ maxWidth: `80%` }}>
33-
{token}
34-
</code>
35-
<button
36-
className="btn btn-secondary btn-sm flex items-center"
37-
onClick={() => void copyToClipboard()}
38-
>
39-
<span className="material-symbols-outlined mr1 f6">
40-
{copied ? `check` : `content_copy`}
41-
</span>
42-
{copied ? `Copied!` : `Copy`}
43-
</button>
44-
</div>
45-
</div>
31+
<Box type="info" className="mb3" showCopy={true} copyContent={token}>
32+
{token}
33+
</Box>
4634

47-
<div className="bg-washed-red ba b--red br2 pa2 mb3">
48-
<p className="f6 mb0 red">
49-
<strong>Warning:</strong> This token provides API access to your organization.
50-
Keep it secure and do not share it publicly.
51-
</p>
52-
</div>
35+
<Box type="warning" className="mb3">
36+
This token provides API access to your organization.
37+
<br/>
38+
Keep it secure and do not share it publicly.
39+
</Box>
5340

5441
<div className="flex justify-end">
5542
<button className="btn btn-primary" onClick={onClose}>

front/assets/js/service_accounts/index.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Fragment, render } from "preact";
22
import { useState, useContext, useEffect, useCallback } from "preact/hooks";
3-
import { Modal } from "js/toolbox";
3+
import { Modal, Box } from "js/toolbox";
44
import { AppConfig, ConfigContext } from "./config";
55
import { ServiceAccount, AppState, ModalState } from "./types";
66
import { ServiceAccountsAPI } from "./utils/api";
@@ -201,12 +201,11 @@ const App = () => {
201201
Are you sure you want to regenerate the API token for{` `}
202202
<strong>{state.selectedServiceAccount?.name}</strong>?
203203
</p>
204-
<div className="bg-washed-red ba b--red br2 pa3 mb3">
205-
<p className="f6 mb0 red">
206-
<strong>Warning:</strong> The current token will be immediately invalidated.
207-
Any systems using the old token will lose access.
208-
</p>
209-
</div>
204+
<Box type="warning" className="mb3">
205+
The current token will be immediately invalidated.
206+
<br/>
207+
Any systems using the old token will lose access.
208+
</Box>
210209
</div>
211210
<div className="flex justify-end items-center pa4 bt b--black-10">
212211
<button

front/assets/js/toolbox/box.tsx

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { h } from "preact";
2+
import { useState } from "preact/hooks";
3+
import * as toolbox from "js/toolbox";
4+
5+
interface BoxProps extends h.JSX.HTMLAttributes<HTMLDivElement> {
6+
type: `danger` | `warning` | `info`;
7+
copyContent?: string;
8+
showCopy?: boolean;
9+
}
10+
11+
export const Box = (props: BoxProps) => {
12+
const [copied, setCopied] = useState(false);
13+
const [anchorFocused, setAnchorFocused] = useState(false);
14+
15+
const { type, copyContent, showCopy = false, className = ``, children, ...rest } = props;
16+
17+
const onCopyClick = () => {
18+
if (copyContent) {
19+
void navigator.clipboard.writeText(copyContent);
20+
setCopied(true);
21+
setTimeout(() => setCopied(false), 2000);
22+
}
23+
};
24+
25+
const getColorClasses = () => {
26+
switch (type) {
27+
case `danger`:
28+
return `bg-washed-red b--red`;
29+
case `warning`:
30+
return `bg-washed-yellow b--yellow`;
31+
case `info`:
32+
return `bg-washed-blue b--blue`;
33+
}
34+
};
35+
36+
const getIcon = () => {
37+
switch (type) {
38+
case `danger`:
39+
return <toolbox.MaterializeIcon name="error" className="f4 red mr2"/>;
40+
case `warning`:
41+
return <toolbox.MaterializeIcon name="warning" className="f4 gold mr2"/>;
42+
case `info`:
43+
return <toolbox.MaterializeIcon name="info" className="f4 blue mr2"/>;
44+
}
45+
};
46+
47+
const copyButton = showCopy && copyContent && (
48+
<div
49+
className="absolute"
50+
style={{
51+
right: `0.5rem`,
52+
top: `0.5rem`,
53+
}}
54+
>
55+
<toolbox.Tooltip
56+
content="Copy to clipboard"
57+
anchor={
58+
<button
59+
className={`btn pa1 btn-secondary btn-tiny ${
60+
anchorFocused ? `` : `o-50`
61+
}`}
62+
onClick={onCopyClick}
63+
>
64+
{!copied && (
65+
<div className="flex items-center gray f6">
66+
<toolbox.MaterializeIcon name="content_copy" className="f4"/>
67+
</div>
68+
)}
69+
{copied && (
70+
<div className="flex items-center gray f6">
71+
<toolbox.MaterializeIcon name="done" className="f4"/>
72+
</div>
73+
)}
74+
</button>
75+
}
76+
placement="top"
77+
/>
78+
</div>
79+
);
80+
81+
return (
82+
<div
83+
className={`f6 ba br3 relative ${getColorClasses()} ${className as string}`}
84+
onMouseOver={() => setAnchorFocused(true)}
85+
onMouseOut={() => setAnchorFocused(false)}
86+
{...rest}
87+
>
88+
<div className="flex items-start pa3">
89+
{getIcon()}
90+
<div className="flex-auto">
91+
{typeof children === `string` ? (
92+
<div dangerouslySetInnerHTML={{ __html: children }}/>
93+
) : (
94+
children
95+
)}
96+
</div>
97+
</div>
98+
{copyButton}
99+
</div>
100+
);
101+
};

front/assets/js/toolbox/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import * as Formatter from "./formatter";
1212
import * as ChartHelpers from "./chart_helpers";
1313
import * as APIRequest from "./api_request";
1414
import { default as PreCopy } from "./pre_copy";
15+
import { Box } from "./box";
1516

1617
export {
1718
Asset,
@@ -28,4 +29,5 @@ export {
2829
Modal,
2930
YamlEditor,
3031
PreCopy,
32+
Box,
3133
};

front/assets/js/toolbox/modal.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import React, { createPortal, useEffect, useRef } from "preact/compat";
1+
import { createPortal, useEffect, useRef } from "preact/compat";
2+
import { h } from "preact";
23

3-
interface ModalProps extends React.HTMLAttributes<HTMLDivElement> {
4+
interface ModalProps extends h.JSX.HTMLAttributes<HTMLDivElement> {
45
isOpen: boolean;
56
close: () => void;
67
title: string;
@@ -41,16 +42,19 @@ export const Modal = (props: ModalProps) => {
4142
return createPortal(
4243
<div
4344
ref={modalRef}
44-
className="fixed flex items-center justify-center vh-100 w-100"
45-
style={{
46-
zIndex: 1000,
45+
className="fixed flex items-start justify-center vh-100 w-100"
46+
style={{
47+
zIndex: 1000,
4748
backgroundColor: `rgba(0, 0, 0, 0.5)`,
4849
left: 0,
4950
top: 0
5051
}}
5152
onClick={close}
5253
>
53-
<div className={`bg-white br3 shadow-1 w-90 ${props.width || `w-50-m`} mw6 relative`}>
54+
<div className={`bg-white br3 shadow-1 w-90 ${props.width || `w-50-m`} mw6 relative`}
55+
style={{
56+
top: `20vh`
57+
}}>
5458
{props.title && (
5559
<div className="pa3 bb b--black-10">
5660
<h2 className="f3 mb0">{props.title}</h2>

front/lib/front_web/controllers/service_account_controller.ex

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,14 @@ defmodule FrontWeb.ServiceAccountController do
33
require Logger
44

55
alias Front.{Audit, ServiceAccount}
6-
alias FrontWeb.Plugs
6+
alias FrontWeb.Plugs.{FetchPermissions, PageAccess, FeatureEnabled}
77

8-
plug(Plugs.FetchPermissions, scope: "org")
9-
plug(Plugs.PageAccess, permissions: "organization.service_accounts.view")
8+
@manage ~w(create update delete regenerate_token)a
109

11-
plug(
12-
Plugs.PageAccess,
13-
[permissions: "organization.service_accounts.manage"]
14-
when action in [:create, :update, :delete, :regenerate_token]
15-
)
16-
17-
plug(Plugs.FeatureEnabled, [:service_accounts])
10+
plug(FetchPermissions, scope: "org")
11+
plug(PageAccess, permissions: "organization.service_accounts.view")
12+
plug(PageAccess, [permissions: "organization.service_accounts.manage"] when action in @manage)
13+
plug(FeatureEnabled, [:service_accounts])
1814

1915
def index(conn, params) do
2016
org_id = conn.assigns.organization_id
@@ -36,6 +32,18 @@ defmodule FrontWeb.ServiceAccountController do
3632
end
3733
end
3834

35+
def show(conn, %{"id" => id}) do
36+
case ServiceAccount.describe(id) do
37+
{:ok, service_account} ->
38+
render(conn, "show.json", service_account: service_account)
39+
40+
{:error, message} ->
41+
conn
42+
|> put_status(422)
43+
|> json(%{error: message})
44+
end
45+
end
46+
3947
def create(conn, params) do
4048
org_id = conn.assigns.organization_id
4149
user_id = conn.assigns.user_id
@@ -64,18 +72,6 @@ defmodule FrontWeb.ServiceAccountController do
6472
end
6573
end
6674

67-
def show(conn, %{"id" => id}) do
68-
case ServiceAccount.describe(id) do
69-
{:ok, service_account} ->
70-
render(conn, "show.json", service_account: service_account)
71-
72-
{:error, message} ->
73-
conn
74-
|> put_status(422)
75-
|> json(%{error: message})
76-
end
77-
end
78-
7975
def update(conn, params = %{"id" => id}) do
8076
name = params["name"] || ""
8177
description = params["description"] || ""

0 commit comments

Comments
 (0)