feat(dashboard-v2): proper copies about api keys
This commit is contained in:
parent
53b0687b21
commit
efed2045af
|
@ -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) => (
|
||||
|
|
|
@ -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",
|
||||
})``;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from "./Tooltip";
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
Reference in New Issue