Merge pull request #1881 from SkynetLabs/dashboard-v2-export-data-page
Dashboard V2 - API keys and export data UIs
This commit is contained in:
commit
4e7ee7848b
|
@ -4,18 +4,24 @@ import styled from "styled-components";
|
||||||
/**
|
/**
|
||||||
* Primary UI component for user interaction
|
* Primary UI component for user interaction
|
||||||
*/
|
*/
|
||||||
export const Button = styled.button.attrs(({ $primary }) => ({
|
export const Button = styled.button.attrs(({ disabled, $primary }) => ({
|
||||||
type: "button",
|
type: "button",
|
||||||
className: `px-6 py-2.5 rounded-full font-sans uppercase text-xs tracking-wide text-palette-600 transition-[filter] hover:brightness-90
|
className: `px-6 py-2.5 rounded-full font-sans uppercase text-xs tracking-wide text-palette-600 transition-[filter]
|
||||||
${$primary ? "bg-primary" : "bg-white border-2 border-black"}`,
|
${$primary ? "bg-primary" : "bg-white border-2 border-black"}
|
||||||
|
${disabled ? "saturate-50 brightness-125 cursor-default text-palette-400" : "hover:brightness-90"}`,
|
||||||
}))``;
|
}))``;
|
||||||
Button.propTypes = {
|
Button.propTypes = {
|
||||||
/**
|
/**
|
||||||
* Is this the principal call to action on the page?
|
* Is this the principal call to action on the page?
|
||||||
*/
|
*/
|
||||||
$primary: PropTypes.bool,
|
$primary: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Prevent interaction on the button
|
||||||
|
*/
|
||||||
|
disabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
Button.defaultProps = {
|
Button.defaultProps = {
|
||||||
$primary: false,
|
$primary: false,
|
||||||
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { withIconProps } from "../withIconProps";
|
||||||
|
|
||||||
|
export const TrashIcon = withIconProps(({ size, ...props }) => (
|
||||||
|
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14.01 15.33" {...props}>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M8.33,0a2.33,2.33,0,0,1,2.33,2.17v.5H13a1,1,0,0,1,.12,2h-.46V13a2.32,2.32,0,0,1-2.17,2.33H3.67a2.33,2.33,0,0,1-2.33-2.17V4.67H1a1,1,0,0,1-.12-2H3.33V2.33A2.33,2.33,0,0,1,5.51,0H8.33Zm-5,4.67V13a.34.34,0,0,0,.27.33h6.73a.33.33,0,0,0,.33-.26V4.67ZM8.33,2H5.67a.33.33,0,0,0-.33.27v.4H8.66V2.33A.33.33,0,0,0,8.4,2Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
));
|
|
@ -13,3 +13,4 @@ export * from "./icons/SearchIcon";
|
||||||
export * from "./icons/CopyIcon";
|
export * from "./icons/CopyIcon";
|
||||||
export * from "./icons/ShareIcon";
|
export * from "./icons/ShareIcon";
|
||||||
export * from "./icons/SimpleUploadIcon";
|
export * from "./icons/SimpleUploadIcon";
|
||||||
|
export * from "./icons/TrashIcon";
|
||||||
|
|
|
@ -45,7 +45,7 @@ const Sidebar = () => (
|
||||||
Notifications
|
Notifications
|
||||||
</SidebarLink>
|
</SidebarLink>
|
||||||
<SidebarLink activeClassName="!border-l-primary" to="/settings/export">
|
<SidebarLink activeClassName="!border-l-primary" to="/settings/export">
|
||||||
Import / Export
|
Export
|
||||||
</SidebarLink>
|
</SidebarLink>
|
||||||
<SidebarLink activeClassName="!border-l-primary" to="/settings/api-keys">
|
<SidebarLink activeClassName="!border-l-primary" to="/settings/api-keys">
|
||||||
API Keys
|
API Keys
|
||||||
|
|
|
@ -1,11 +1,65 @@
|
||||||
import * as React from "react";
|
import useSWR from "swr";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
import UserSettingsLayout from "../../layouts/UserSettingsLayout";
|
import UserSettingsLayout from "../../layouts/UserSettingsLayout";
|
||||||
|
|
||||||
|
import { TextInputBasic } from "../../components/TextInputBasic";
|
||||||
|
import { Button } from "../../components/Button";
|
||||||
|
import { TrashIcon } from "../../components/Icons";
|
||||||
|
|
||||||
const APIKeysPage = () => {
|
const APIKeysPage = () => {
|
||||||
|
const { data: apiKeys } = useSWR("user/apikeys");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h4>API Keys</h4>
|
<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]">
|
||||||
|
<section>
|
||||||
|
<h4>API Keys</h4>
|
||||||
|
<p>
|
||||||
|
At vero eos et caritatem, quae sine metu contineret, saluti prospexit civium, qua. Laudem et dolorem
|
||||||
|
aspernari ut ad naturam aut fu.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section className="flex flex-col gap-8">
|
||||||
|
<div className="grid sm:grid-cols-[1fr_min-content] w-full gap-y-2 gap-x-4 items-end">
|
||||||
|
<TextInputBasic label="API Key Name" placeholder="my_applications_statistics" />
|
||||||
|
<div className="flex mt-2 sm:mt-0 justify-center">
|
||||||
|
<Button onClick={() => console.info("TODO: generate ky")}>Generate</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{apiKeys?.length > 0 && (
|
||||||
|
<section className="mt-4">
|
||||||
|
<h6 className="text-palette-300">API Keys</h6>
|
||||||
|
<ul className="mt-4">
|
||||||
|
{apiKeys.map(({ id, name, createdAt }) => (
|
||||||
|
<li
|
||||||
|
key={id}
|
||||||
|
className="grid grid-cols-2 sm:grid-cols-[1fr_repeat(2,_max-content)] py-6 sm:py-4 px-4 gap-x-8 bg-white odd:bg-palette-100/50"
|
||||||
|
>
|
||||||
|
<span className="truncate col-span-2 sm:col-span-1">{name || id}</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="text-right">
|
||||||
|
<button
|
||||||
|
className="p-1 transition-colors hover:text-error"
|
||||||
|
onClick={() => window.confirm("TODO: confirmation modal")}
|
||||||
|
>
|
||||||
|
<TrashIcon size={14} />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="hidden xl:block w-full text-right pt-20 pr-6">
|
||||||
|
<img src="/images/import-export.svg" alt="" className="inline-block w-[200px]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,69 @@
|
||||||
import * as React from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import UserSettingsLayout from "../../layouts/UserSettingsLayout";
|
import UserSettingsLayout from "../../layouts/UserSettingsLayout";
|
||||||
|
|
||||||
|
import { Switch } from "../../components/Switch";
|
||||||
|
import { Button } from "../../components/Button";
|
||||||
|
|
||||||
|
const useExportOptions = () => {
|
||||||
|
const [pinnedFiles, setPinnedFiles] = useState(false);
|
||||||
|
const [uploadHistory, setUploadHistory] = useState(false);
|
||||||
|
const [downloadHistory, setDownloadHistory] = useState(false);
|
||||||
|
|
||||||
|
const selectedOptions = {
|
||||||
|
pinnedFiles,
|
||||||
|
uploadHistory,
|
||||||
|
downloadHistory,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedOptions: Object.keys(selectedOptions).filter((o) => selectedOptions[o]),
|
||||||
|
setPinnedFiles,
|
||||||
|
setUploadHistory,
|
||||||
|
setDownloadHistory,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const ExportPage = () => {
|
const ExportPage = () => {
|
||||||
|
const { selectedOptions, setPinnedFiles, setUploadHistory, setDownloadHistory } = useExportOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h4>Import / export</h4>
|
<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]">
|
||||||
|
<section>
|
||||||
|
<h4>Export</h4>
|
||||||
|
<p>
|
||||||
|
Et quidem exercitus quid ex eo delectu rerum, quem modo ista sis aequitate. Probabo, inquit, modo dixi,
|
||||||
|
constituto.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section className="flex flex-col gap-8">
|
||||||
|
<ul className="flex flex-col gap-2">
|
||||||
|
<li>
|
||||||
|
<Switch onChange={setUploadHistory}>Upload history</Switch>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Switch onChange={setDownloadHistory}>Download history</Switch>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Switch onChange={setPinnedFiles}>Pinned files</Switch>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<Button
|
||||||
|
$primary
|
||||||
|
disabled={selectedOptions.length === 0}
|
||||||
|
onClick={() => console.log("TODO: actually export:", selectedOptions)}
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div className="hidden xl:block w-full text-right pt-20 pr-6">
|
||||||
|
<img src="/images/import-export.svg" alt="" className="inline-block w-[200px]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 193 114"><defs><style>.cls-1{fill:#00c65e;}.cls-2,.cls-3{fill:none;stroke:#0d0d0d;stroke-width:2px;}.cls-2{stroke-linejoin:round;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><rect class="cls-1" x="79" y="10" width="40" height="40" rx="2"/><path class="cls-2" d="M149,49l-8,8-8-8M91,57h4m4,0h4m4,0h4m18,20V73H105V85"/><path class="cls-2" d="M105,85h16l8-8h32v36H105ZM61,5V1H37V13"/><path class="cls-2" d="M37,13H53l8-8H93V41H37Zm66,4h30a8,8,0,0,1,8,8V57M95,97H69a8,8,0,0,1-8-8V57"/><path class="cls-3" d="M29,69H0m34,9H15M44,58H25M164,28h29m-34,9h19M149,17h19"/></g></g></svg>
|
After Width: | Height: | Size: 674 B |
Reference in New Issue