feat(dashboard-v2): add Files page
This commit is contained in:
parent
46a2ff44d5
commit
fab732c6cc
|
@ -0,0 +1,74 @@
|
|||
import * as React from "react";
|
||||
import useSWR from "swr";
|
||||
import { useMedia } from "react-use";
|
||||
|
||||
import theme from "../../lib/theme";
|
||||
|
||||
import { ContainerLoadingIndicator } from "../LoadingIndicator";
|
||||
import { Select, SelectOption } from "../Select";
|
||||
import { Switch } from "../Switch";
|
||||
import { TextInputIcon } from "../TextInputIcon/TextInputIcon";
|
||||
import { SearchIcon } from "../Icons";
|
||||
|
||||
import FileTable from "./FileTable";
|
||||
import useFormattedFilesData from "./useFormattedFilesData";
|
||||
|
||||
const FileList = ({ type }) => {
|
||||
const isMediumScreenOrLarger = useMedia(`(min-width: ${theme.screens.md})`);
|
||||
const { data, error } = useSWR(`user/${type}?pageSize=10`);
|
||||
const items = useFormattedFilesData(data?.items || []);
|
||||
|
||||
const setFilter = (name, value) => console.log("filter", name, "set to", value);
|
||||
|
||||
if (!items.length) {
|
||||
return (
|
||||
<div className="flex w-full h-full justify-center items-center text-palette-400">
|
||||
{/* TODO: proper error message */}
|
||||
{!data && !error && <ContainerLoadingIndicator />}
|
||||
{!data && error && <p>An error occurred while loading this data.</p>}
|
||||
{data && <p>No {type} found.</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 pt-4">
|
||||
<div className="flex flex-col gap-4 lg:flex-row justify-between items-center">
|
||||
<TextInputIcon
|
||||
className="w-full lg:w-[280px] xl:w-[420px]"
|
||||
placeholder="Search"
|
||||
icon={<SearchIcon size={20} />}
|
||||
onChange={console.log.bind(console)}
|
||||
/>
|
||||
<div className="flex flex-row items-center uppercase font-light text-sm gap-4">
|
||||
<Switch onChange={(value) => setFilter("showSmallFiles", value)} className="mr-8">
|
||||
<span className="underline decoration-dashed underline-offset-2 decoration-2 decoration-gray-300">
|
||||
Show small files
|
||||
</span>
|
||||
</Switch>
|
||||
<div>
|
||||
<span className="pr-2">File type:</span>
|
||||
<Select onChange={(value) => setFilter("type", value)}>
|
||||
<SelectOption value="all" label="All" />
|
||||
<SelectOption value="mp4" label=".mp4" />
|
||||
<SelectOption value="pdf" label=".pdf" />
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<span className="pr-2">Sort:</span>
|
||||
<Select onChange={(value) => setFilter("type", value)}>
|
||||
<SelectOption value="size-desc" label="Biggest size" />
|
||||
<SelectOption value="size-asc" label="Smallest size" />
|
||||
<SelectOption value="date-desc" label="Latest" />
|
||||
<SelectOption value="date-asc" label="Oldest" />
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* TODO: mobile view (it's not tabular) */}
|
||||
{isMediumScreenOrLarger ? <FileTable items={items} /> : "Mobile view"}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileList;
|
|
@ -0,0 +1,111 @@
|
|||
import { CogIcon, ShareIcon } from "../Icons";
|
||||
import { PopoverMenu } from "../PopoverMenu/PopoverMenu";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeadCell, TableRow } from "../Table";
|
||||
import { CopyButton } from "../CopyButton";
|
||||
|
||||
const buildShareMenu = (item) => {
|
||||
return [
|
||||
{
|
||||
label: "Facebook",
|
||||
callback: () => {
|
||||
console.info("share to Facebook", item);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Twitter",
|
||||
callback: () => {
|
||||
console.info("share to Twitter", item);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Discord",
|
||||
callback: () => {
|
||||
console.info("share to Discord", item);
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const buildOptionsMenu = (item) => {
|
||||
return [
|
||||
{
|
||||
label: "Preview",
|
||||
callback: () => {
|
||||
console.info("preview", item);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Download",
|
||||
callback: () => {
|
||||
console.info("download", item);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Unpin",
|
||||
callback: () => {
|
||||
console.info("unpin", item);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Report",
|
||||
callback: () => {
|
||||
console.info("report", item);
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export default function FileTable({ items }) {
|
||||
return (
|
||||
<Table style={{ tableLayout: "fixed" }}>
|
||||
<TableHead>
|
||||
<TableRow noHoverEffect>
|
||||
<TableHeadCell className="w-[240px] xl:w-[360px]">Name</TableHeadCell>
|
||||
<TableHeadCell className="w-[80px]">Type</TableHeadCell>
|
||||
<TableHeadCell className="w-[80px]" align="right">
|
||||
Size
|
||||
</TableHeadCell>
|
||||
<TableHeadCell className="w-[180px]">Uploaded</TableHeadCell>
|
||||
<TableHeadCell className="hidden lg:table-cell">Skylink</TableHeadCell>
|
||||
<TableHeadCell className="w-[100px]">Activity</TableHeadCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{items.map((item) => {
|
||||
const { id, name, type, size, date, skylink } = item;
|
||||
|
||||
return (
|
||||
<TableRow key={id}>
|
||||
<TableCell className="w-[240px] xl:w-[360px]">{name}</TableCell>
|
||||
<TableCell className="w-[80px]">{type}</TableCell>
|
||||
<TableCell className="w-[80px]" align="right">
|
||||
{size}
|
||||
</TableCell>
|
||||
<TableCell className="w-[180px]">{date}</TableCell>
|
||||
<TableCell className="hidden lg:table-cell pr-6 !overflow-visible">
|
||||
<div className="flex items-center">
|
||||
<CopyButton value={skylink} className="mr-2" />
|
||||
<span className="w-full inline-block truncate">{skylink}</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="w-[100px] !overflow-visible">
|
||||
<div className="flex text-palette-600 gap-4">
|
||||
<PopoverMenu options={buildShareMenu(item)} openClassName="text-primary">
|
||||
<button>
|
||||
<ShareIcon size={22} />
|
||||
</button>
|
||||
</PopoverMenu>
|
||||
<PopoverMenu options={buildOptionsMenu(item)} openClassName="text-primary">
|
||||
<button>
|
||||
<CogIcon />
|
||||
</button>
|
||||
</PopoverMenu>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from "./FileList";
|
|
@ -0,0 +1,26 @@
|
|||
import { useMemo } from "react";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const parseFileName = (fileName) => {
|
||||
const lastDotIndex = Math.max(0, fileName.lastIndexOf(".")) || Infinity;
|
||||
|
||||
return [fileName.substr(0, lastDotIndex), fileName.substr(lastDotIndex)];
|
||||
};
|
||||
|
||||
const formatItem = ({ size, name: rawFileName, uploadedOn, downloadedOn, ...rest }) => {
|
||||
const [name, type] = parseFileName(rawFileName);
|
||||
const date = dayjs(uploadedOn || downloadedOn).format("MM/DD/YYYY; HH:MM");
|
||||
|
||||
return {
|
||||
...rest,
|
||||
date,
|
||||
size: prettyBytes(size),
|
||||
type,
|
||||
name,
|
||||
};
|
||||
};
|
||||
|
||||
const useFormattedFilesData = (items) => useMemo(() => items.map(formatItem), [items]);
|
||||
|
||||
export default useFormattedFilesData;
|
|
@ -2,8 +2,28 @@ import * as React from "react";
|
|||
|
||||
import DashboardLayout from "../layouts/DashboardLayout";
|
||||
|
||||
import { Panel } from "../components/Panel";
|
||||
import { Tab, TabPanel, Tabs } from "../components/Tabs";
|
||||
import FileList from "../components/FileList/FileList";
|
||||
import { useSearchParam } from "react-use";
|
||||
|
||||
const FilesPage = () => {
|
||||
return <>FILES</>;
|
||||
const defaultTab = useSearchParam("tab");
|
||||
|
||||
return (
|
||||
<Panel title="Files">
|
||||
<Tabs defaultTab={defaultTab || "uploads"}>
|
||||
<Tab id="uploads" title="Uploads" />
|
||||
<Tab id="downloads" title="Downloads" />
|
||||
<TabPanel tabId="uploads" className="pt-4">
|
||||
<FileList type="uploads" />
|
||||
</TabPanel>
|
||||
<TabPanel tabId="downloads" className="pt-4">
|
||||
<FileList type="downloads" />
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
|
||||
FilesPage.Layout = DashboardLayout;
|
||||
|
|
Reference in New Issue