Implements feedbacks on purchases table

This commit is contained in:
Arnaud 2024-09-10 18:49:16 +02:00
parent 1c2dc2c2f3
commit 8e3a4d91d9
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
9 changed files with 80 additions and 135 deletions

View File

@ -3,6 +3,10 @@
gap: 0.75rem;
}
.fileCell-cid {
white-space: nowrap;
}
.fileCell-subtitle {
display: flex;
align-items: center;

View File

@ -32,23 +32,29 @@ export function FileCell({ requestId, purchaseCid }: Props) {
});
}, [requestId]);
let name = metadata.name;
let name = metadata.name.slice(0, 10);
if (name.length > 10) {
const [filename, ext] = metadata.name.split(".");
name = filename.slice(0, 10) + "..." + ext;
if (metadata.name.length > 10) {
// const [filename, ext] = metadata.name.split(".");
// name = filename.slice(0, 10) + "..." + ext;
name += "...";
}
const cidTruncated = cid.slice(0, 5) + ".".repeat(5) + cid.slice(-5);
// const cidTruncated = cid.slice(0, 5) + ".".repeat(5) + cid.slice(-5);
const cidTruncated = cid.slice(0, 10) + "...";
return (
<>
<div className="fileCell">
<WebFileIcon type={metadata.mimetype} />
<div>
<span className="fileCell-title">{name}</span>
<span className="fileCell-title">
<Tooltip message={metadata.name}>{name}</Tooltip>
</span>
<span className="fileCell-subtitle">
<Tooltip message={cid}>{cidTruncated}</Tooltip>
<Tooltip message={cid}>
<span className="fileCell-cid">{cidTruncated}</span>
</Tooltip>
</span>
</div>
</div>

View File

@ -1,11 +1,3 @@
.fileDetails {
position: fixed;
transition: transform 0.25s;
background-color: var(--codex-background-secondary);
z-index: 2;
justify-content: space-between;
}
.fileDetails-header {
padding: 0.75rem 1.5rem;
border-bottom: 1px solid var(--codex-border-color);
@ -17,10 +9,6 @@
flex-grow: 1;
}
.files-backdrop {
z-index: 2;
}
.fileDetails-body {
padding: 0;
}
@ -43,36 +31,3 @@
gap: 0.75rem;
padding: 0.75rem 1.5rem;
}
@media (min-width: 1000px) {
.fileDetails {
width: 300px;
height: 100%;
bottom: 0;
top: 0;
transform: translatex(300px);
right: 0;
}
.fileDetails[aria-expanded] {
transform: translatex(0);
z-index: 10;
}
}
@media (max-width: 999px) {
.fileDetails {
width: 100%;
height: auto;
bottom: 0;
top: auto;
transform: translatey(1000px);
left: 0;
padding-bottom: 1.5rem;
}
.fileDetails[aria-expanded] {
transform: translatey(0);
z-index: 10;
}
}

View File

@ -1,13 +1,16 @@
import { ButtonIcon, Button } from "@codex-storage/marketplace-ui-components";
import {
ButtonIcon,
Button,
Sheets,
} from "@codex-storage/marketplace-ui-components";
import { CodexDataContent } from "@codex-storage/sdk-js";
import { X, DownloadIcon } from "lucide-react";
import { attributes } from "../../utils/attributes";
import { PrettyBytes } from "../../utils/bytes";
import { ICON_SIZE } from "../../utils/constants";
import { Dates } from "../../utils/dates";
import { CidCopyButton } from "./CidCopyButton";
import "./FileDetails.css";
import { FileMetadata } from "../../utils/file-storage";
import { DownloadIcon, X } from "lucide-react";
type Props = {
details: (CodexDataContent & FileMetadata) | undefined;
@ -16,7 +19,6 @@ type Props = {
};
export function FileDetails({ onClose, details, expanded }: Props) {
const attr = attributes({ "aria-expanded": expanded });
const url = import.meta.env.VITE_CODEX_API_URL + "/api/codex/v1/data/";
const Icon = () => <X size={ICON_SIZE} onClick={onClose} />;
@ -24,13 +26,8 @@ export function FileDetails({ onClose, details, expanded }: Props) {
const onDownload = () => window.open(url + details?.cid, "_target");
return (
<>
<div
className="files-backdrop backdrop"
onClick={onClose}
{...attr}></div>
<div className="fileDetails" {...attr}>
<Sheets open={expanded} onClose={onClose}>
<>
{details && (
<>
<div className="fileDetails-header">
@ -86,7 +83,7 @@ export function FileDetails({ onClose, details, expanded }: Props) {
</div>
</>
)}
</div>
</>
</>
</Sheets>
);
}

View File

@ -69,42 +69,3 @@
.files-header {
margin-bottom: 0.75rem;
}
.files-headerTabs {
display: flex;
margin-top: 1rem;
gap: 1rem;
position: relative;
}
.files-headerTabs::after {
width: 100%;
background-color: var(--codex-background-light);
content: " ";
position: absolute;
height: 2px;
top: 11px;
top: 31px;
}
.files-headerTab {
display: flex;
align-items: center;
gap: 0.25rem;
padding-bottom: 1rem;
cursor: pointer;
transition: 0.35s opacity;
z-index: 1;
}
.files-headerTab:not(.files-headerTab--active) {
opacity: 0.7;
}
.files-headerTab:hover {
opacity: 0.85;
}
.files-headerTab--active {
border-bottom: 2px solid var(--codex-color-contrast);
}

View File

@ -8,9 +8,9 @@ import {
ButtonIcon,
EmptyPlaceholder,
WebFileIcon,
Tabs,
} from "@codex-storage/marketplace-ui-components";
import { FileDetails } from "./FileDetails.tsx";
import { classnames } from "../../utils/classnames.ts";
import { FavoriteStorage } from "../../utils/favorite-storage.tsx";
import { useData } from "../../hooks/useData.tsx";
@ -33,7 +33,7 @@ export function Files() {
const cid = useRef<string | null>("");
const [expanded, setExpanded] = useState(false);
const [favorites, setFavorites] = useState<string[]>([]);
const [selected, setSelected] = useState<"all" | "favorites">("all");
const [index, setIndex] = useState(0);
useEffect(() => {
FavoriteStorage.list().then((cids) => setFavorites(cids));
@ -47,8 +47,7 @@ export function Files() {
}, SIDE_DURATION);
};
const onSelected = () =>
setSelected(selected === "all" ? "favorites" : "all");
const onTabChange = (i: number) => setIndex(i);
const onDetails = (id: string) => {
cid.current = id;
@ -67,7 +66,7 @@ export function Files() {
const items = [];
if (selected === "favorites") {
if (index === 1) {
items.push(...files.filter((f) => favorites.includes(f.cid)));
} else {
items.push(...files);
@ -81,27 +80,19 @@ export function Files() {
<div className="files">
<div className="files-header">
<div className="files-title">Files</div>
<div className="files-headerTabs">
<div
className={classnames(
["files-headerTab"],
["files-headerTab--active", selected === "all"]
)}
onClick={onSelected}>
<FilesIcon size={"1rem"}></FilesIcon>
<span>All files</span>
</div>
<div
className={classnames(
["files-headerTab"],
["files-headerTab--active", selected === "favorites"]
)}
onClick={onSelected}>
<Star size={"1rem"}></Star>
<span>Favorites</span>
</div>
</div>
<Tabs
onTabChange={onTabChange}
tabIndex={index}
tabs={[
{
label: "All files",
Icon: () => <FilesIcon size={"1rem"}></FilesIcon>,
},
{
label: "Favorites",
Icon: () => <Star size={"1rem"}></Star>,
},
]}></Tabs>
</div>
<div className="files-fileBody">

View File

@ -0,0 +1,7 @@
.truncateCell {
position: relative;
}
.truncateCell .tooltip:hover:after {
left: -33%;
}

View File

@ -0,0 +1,20 @@
import { Tooltip } from "@codex-storage/marketplace-ui-components";
import "./TruncateCell.css";
type Props = {
value: string;
};
export function TruncateCell({ value }: Props) {
if (value.length <= 10) {
return <span>{value}</span>;
}
return (
<div className="truncateCell">
<Tooltip message={value}>
<span>{value.slice(0, 10) + "..."}</span>
</Tooltip>
</div>
);
}

View File

@ -4,6 +4,7 @@ import { CodexSdk } from "../../sdk/codex";
import { Plus } from "lucide-react";
import { useState } from "react";
import {
BreakCell,
Button,
Cell,
Spinner,
@ -17,6 +18,7 @@ import { CustomStateCellRender } from "../../components/CustomStateCellRender/Cu
import prettyMilliseconds from "pretty-ms";
import { ErrorBoundary } from "../../components/ErrorBoundary/ErrorBoundary";
import { Promises } from "../../utils/promises";
import { TruncateCell } from "../../components/TruncateCell/TruncateCell";
const Purchases = () => {
const [open, setOpen] = useState(false);
@ -40,7 +42,8 @@ const Purchases = () => {
}
const headers = [
"cid",
"file",
"request id",
"duration",
"slots",
"reward",
@ -58,10 +61,11 @@ const Purchases = () => {
return [
<FileCell requestId={r.id} purchaseCid={r.content.cid} index={index} />,
<Cell value={prettyMilliseconds(duration)} />,
<Cell value={ask.slots + " hosts"} />,
<Cell value={ask.reward + " tokens"} />,
<Cell value={"Every " + prettyMilliseconds(pf)} />,
<TruncateCell value={r.id} />,
<Cell value={prettyMilliseconds(duration, { verbose: true })} />,
<Cell value={ask.slots.toString()} />,
<Cell value={ask.reward + " CDX"} />,
<Cell value={prettyMilliseconds(pf, { verbose: true })} />,
<CustomStateCellRender state={p.state} message={p.error} />,
];
}) || [];