feat(dashboard-v2): proper copies about api keys

This commit is contained in:
Michał Leszczyk 2022-04-12 13:20:40 +02:00
parent 53b0687b21
commit efed2045af
No known key found for this signature in database
GPG Key ID: FA123CA8BAA2FBF4
7 changed files with 81 additions and 76 deletions

View File

@ -13,7 +13,7 @@ import { useAPIKeyRemoval } from "./useAPIKeyRemoval";
export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
const { id, name, createdAt, skylinks } = apiKey;
const isPublic = apiKey.public === "true";
const isSponsorKey = apiKey.public === "true";
const [error, setError] = useState(null);
const onSkylinkListEdited = useCallback(() => {
@ -53,9 +53,9 @@ export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
}, [abortEdit]);
const skylinksNumber = skylinks?.length ?? 0;
const isNotConfigured = isPublic && skylinksNumber === 0;
const isNotConfigured = isSponsorKey && skylinksNumber === 0;
const skylinksPhrasePrefix = skylinksNumber === 0 ? "No" : skylinksNumber;
const skylinksPhrase = `${skylinksPhrasePrefix} ${skylinksNumber === 1 ? "skylink" : "skylinks"} configured`;
const skylinksPhrase = `${skylinksPhrasePrefix} ${skylinksNumber === 1 ? "skylink" : "skylinks"} sponsored`;
return (
<li
@ -66,21 +66,23 @@ export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
<span className="col-span-2 sm:col-span-1 flex items-center">
<span className="flex flex-col">
<span className={cn("truncate", { "text-palette-300": !name })}>{name || "unnamed key"}</span>
<button
onClick={promptEdit}
className={cn("text-xs hover:underline decoration-dotted", {
"text-error": isNotConfigured,
"text-palette-400": !isNotConfigured,
})}
>
{skylinksPhrase}
</button>
{isSponsorKey && (
<button
onClick={promptEdit}
className={cn("text-xs hover:underline decoration-dotted", {
"text-error": isNotConfigured,
"text-palette-400": !isNotConfigured,
})}
>
{skylinksPhrase}
</button>
)}
</span>
</span>
<span className="col-span-2 my-4 border-t border-t-palette-200/50 sm:hidden" />
<span className="text-palette-400">{dayjs(createdAt).format("MMM DD, YYYY")}</span>
<span className="flex items-center justify-end">
{isPublic && (
{isSponsorKey && (
<button
title="Add or remove skylinks"
aria-label="Add or remove skylinks"
@ -119,7 +121,7 @@ export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
)}
{editInitiated && (
<Modal onClose={closeEditModal} className="flex flex-col gap-4 text-center sm:px-8 sm:py-6">
<h4>Covered skylinks</h4>
<h4>Sponsored skylinks</h4>
{skylinks?.length > 0 ? (
<ul className="text-xs flex flex-col gap-2">
{skylinks.map((skylink) => (

View File

@ -2,5 +2,5 @@ import { Link } from "gatsby";
import styled from "styled-components";
export default styled(Link).attrs({
className: "text-primary underline-offset-3 decoration-dotted hover:text-primary-light hover:underline",
className: "text-primary underline-offset-2 decoration-1 decoration-dotted hover:text-primary-light hover:underline",
})``;

View File

@ -0,0 +1 @@
export * from "./Tooltip";

View File

@ -2,6 +2,7 @@ import * as Yup from "yup";
import { forwardRef, useImperativeHandle, useState } from "react";
import PropTypes from "prop-types";
import { Formik, Form } from "formik";
import cn from "classnames";
import accountsService from "../../services/accountsService";
@ -9,7 +10,6 @@ import { Alert } from "../Alert";
import { Button } from "../Button";
import { CopyButton } from "../CopyButton";
import { TextField } from "../Form/TextField";
import { CircledProgressIcon, PlusIcon } from "../Icons";
const newAPIKeySchema = Yup.object().shape({
name: Yup.string(),
@ -22,7 +22,7 @@ const State = {
};
export const APIKeyType = {
Public: "public",
Sponsor: "sponsor",
General: "general",
};
@ -37,10 +37,10 @@ export const AddAPIKeyForm = forwardRef(({ onSuccess, type }, ref) => {
return (
<div ref={ref} className="flex flex-col gap-4">
{state === State.Success && (
<Alert $variant="success" className="text-center">
<Alert $variant="success">
<strong>Success!</strong>
<p>Please copy your new API key below. We'll never show it again!</p>
<div className="flex items-center gap-2 mt-4 justify-center">
<div className="flex items-center gap-2 mt-4">
<code className="p-2 rounded border border-palette-200 text-xs selection:bg-primary/30 truncate">
{generatedKey}
</code>
@ -62,8 +62,8 @@ export const AddAPIKeyForm = forwardRef(({ onSuccess, type }, ref) => {
.post("user/apikeys", {
json: {
name,
public: type === APIKeyType.Public ? "true" : "false",
skylinks: type === APIKeyType.Public ? [] : null,
public: type === APIKeyType.Sponsor ? "true" : "false",
skylinks: type === APIKeyType.Sponsor ? [] : null,
},
})
.json();
@ -78,26 +78,20 @@ export const AddAPIKeyForm = forwardRef(({ onSuccess, type }, ref) => {
}}
>
{({ errors, touched, isSubmitting }) => (
<Form className="grid grid-cols-[1fr_min-content] w-full gap-y-2 gap-x-4 items-start">
<div className="flex items-center">
<TextField
type="text"
id="name"
name="name"
label="New API Key Name"
placeholder="my_applications_statistics"
error={errors.name}
touched={touched.name}
/>
</div>
<div className="flex mt-5 justify-center">
{isSubmitting ? (
<CircledProgressIcon size={38} className="text-palette-300 animate-[spin_3s_linear_infinite]" />
) : (
<Button type="submit" className="px-2.5" aria-label="Create general API key">
<PlusIcon size={14} />
</Button>
)}
<Form className="flex flex-col gap-4">
<TextField
type="text"
id="name"
name="name"
label="New API Key Label"
placeholder="my_applications_statistics"
error={errors.name}
touched={touched.name}
/>
<div className="flex justify-center">
<Button type="submit" disabled={isSubmitting} className={cn({ "cursor-wait": isSubmitting })}>
{isSubmitting ? "Generating your API key..." : "Generate your API key"}
</Button>
</div>
</Form>
)}
@ -110,5 +104,5 @@ AddAPIKeyForm.displayName = "AddAPIKeyForm";
AddAPIKeyForm.propTypes = {
onSuccess: PropTypes.func.isRequired,
type: PropTypes.oneOf([APIKeyType.Public, APIKeyType.General]).isRequired,
type: PropTypes.oneOf([APIKeyType.Sponsor, APIKeyType.General]).isRequired,
};

View File

@ -52,10 +52,10 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => {
return (
<div ref={ref} className="flex flex-col gap-4">
{state === State.Success && (
<Alert $variant="success" className="text-center">
<Alert $variant="success">
<strong>Success!</strong>
<p>Please copy your new API key below. We'll never show it again!</p>
<div className="flex items-center gap-2 mt-4 justify-center">
<div className="flex items-center gap-2 mt-4">
<code className="p-2 rounded border border-palette-200 text-xs selection:bg-primary/30 truncate">
{generatedKey}
</code>
@ -101,14 +101,14 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => {
type="text"
id="name"
name="name"
label="Public API Key Name"
label="Sponsor API Key Name"
placeholder="my_applications_statistics"
error={errors.name}
touched={touched.name}
/>
</div>
<div>
<h6 className="text-palette-300 mb-2">Skylinks accessible with the new key</h6>
<h6 className="text-palette-300 mb-2">Skylinks sponsored by the new key</h6>
<FieldArray
name="skylinks"
render={({ push, remove }) => {
@ -182,7 +182,7 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => {
className={cn("px-2.5", { "cursor-wait": isSubmitting })}
disabled={!isValid || isSubmitting}
>
{isSubmitting ? "Generating" : "Generate"} your public key
{isSubmitting ? "Generating your sponsor key..." : "Generate your sponsor key"}
</Button>
</div>
</Form>
@ -192,7 +192,7 @@ export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => {
);
});
AddPublicAPIKeyForm.displayName = "AddAPIKeyForm";
AddPublicAPIKeyForm.displayName = "AddPublicAPIKeyForm";
AddPublicAPIKeyForm.propTypes = {
onSuccess: PropTypes.func.isRequired,

View File

@ -16,8 +16,8 @@ const Sidebar = () => (
<SidebarLink activeClassName="!border-l-primary" to="/settings/export">
Export
</SidebarLink>
<SidebarLink activeClassName="!border-l-primary" to="/settings/api-keys">
API Keys
<SidebarLink activeClassName="!border-l-primary" to="/settings/developer-settings">
Developer settings
</SidebarLink>
</nav>
</aside>

View File

@ -8,11 +8,12 @@ import { APIKeyList } from "../../components/APIKeyList/APIKeyList";
import { Alert } from "../../components/Alert";
import { AddPublicAPIKeyForm } from "../../components/forms/AddPublicAPIKeyForm";
import { Metadata } from "../../components/Metadata";
import HighlightedLink from "../../components/HighlightedLink";
const APIKeysPage = () => {
const { data: apiKeys = [], mutate: reloadKeys, error } = useSWR("user/apikeys");
const generalKeys = apiKeys.filter(({ public: isPublic }) => isPublic === "false");
const publicKeys = apiKeys.filter(({ public: isPublic }) => isPublic === "true");
const DeveloperSettingsPage = () => {
const { data: allKeys = [], mutate: reloadKeys, error } = useSWR("user/apikeys");
const apiKeys = allKeys.filter(({ public: isPublic }) => isPublic === "false");
const sponsorKeys = allKeys.filter(({ public: isPublic }) => isPublic === "true");
const publicFormRef = useRef();
const generalFormRef = useRef();
@ -31,53 +32,60 @@ const APIKeysPage = () => {
return (
<>
<Metadata>
<title>API Keys</title>
<title>Developer settings</title>
</Metadata>
<div className="flex flex-col xl:flex-row">
<div className="flex flex-col gap-10 lg:shrink-0 lg:max-w-[576px] xl:max-w-[524px]">
<div className="flex flex-col gap-10 lg:shrink-0 lg:max-w-[576px] xl:max-w-[524px] leading-relaxed">
<div>
<h4>API Keys</h4>
<p className="leading-relaxed">There are two types of API keys that you can generate for your account.</p>
<p>Make sure to use the appropriate type.</p>
<h4>Developer settings</h4>
<p>API keys allow developers and applications to extend the functionality of your portal account.</p>
<p>Skynet uses two types of API keys, explained below.</p>
</div>
<hr />
<section className="flex flex-col gap-2">
<h5>Public keys</h5>
<p className="text-palette-500">
Public keys provide read access to a selected list of skylinks. You can share them publicly.
<h5>Sponsor keys</h5>
<div className="text-palette-500"></div>
<p>
Sponsor keys allow users without an account on this portal to download skylinks covered by the API key.
</p>
<p>
Learn more about sponsoring content with Sponsor API Keys{" "}
<HighlightedLink as="a" href="#">
here
</HighlightedLink>
.
</p>{" "}
{/* TODO: missing documentation link */}
<div className="mt-4">
<AddPublicAPIKeyForm ref={publicFormRef} onSuccess={refreshState} />
</div>
{error ? (
<Alert $variant="error" className="mt-4">
An error occurred while loading your API keys. Please try again later.
An error occurred while loading your sponsor keys. Please try again later.
</Alert>
) : (
<div className="mt-4">
{publicKeys?.length > 0 ? (
<APIKeyList title="Your public keys" keys={publicKeys} reloadKeys={() => refreshState(true)} />
{sponsorKeys?.length > 0 ? (
<APIKeyList title="Your public keys" keys={sponsorKeys} reloadKeys={() => refreshState(true)} />
) : (
<Alert $variant="info">No public API keys found.</Alert>
<Alert $variant="info">No sponsor keys found.</Alert>
)}
</div>
)}
</section>
<hr />
<section className="flex flex-col gap-2">
<h5>General keys</h5>
<h5>API keys</h5>
<p className="text-palette-500">
These keys provide full access to <b>Accounts</b> service and are equivalent to using a JWT token.
</p>
<p className="underline">
<p className="font-bold">
This type of API keys needs to be kept secret and should never be shared with anyone.
</p>
<div className="mt-4">
<AddAPIKeyForm ref={generalFormRef} onSuccess={refreshState} type={APIKeyType.General} />
</div>
@ -88,10 +96,10 @@ const APIKeysPage = () => {
</Alert>
) : (
<div className="mt-4">
{generalKeys?.length > 0 ? (
<APIKeyList title="Your general keys" keys={generalKeys} reloadKeys={() => refreshState(true)} />
{apiKeys?.length > 0 ? (
<APIKeyList title="Your API keys" keys={apiKeys} reloadKeys={() => refreshState(true)} />
) : (
<Alert $variant="info">No general API keys found.</Alert>
<Alert $variant="info">No API keys found.</Alert>
)}
</div>
)}
@ -105,6 +113,6 @@ const APIKeysPage = () => {
);
};
APIKeysPage.Layout = UserSettingsLayout;
DeveloperSettingsPage.Layout = UserSettingsLayout;
export default APIKeysPage;
export default DeveloperSettingsPage;