- Add SkySection component for displaying Bluesky-specific file information - Add byteCountToHumanSize utility for formatting file sizes - Update PostFiles, FileCarousel, FileDetails, and DisplayedFile components - Enhance posts helper with file display logic - Update post model and view templates - Remove deprecated file details sky section partial
114 lines
3.1 KiB
TypeScript
114 lines
3.1 KiB
TypeScript
import * as React from 'react';
|
|
import { PostFileState } from './PostFiles';
|
|
import { byteCountToHumanSize } from '../utils/byteCountToHumanSize';
|
|
import SkySection from './SkySection';
|
|
|
|
export interface FileDetailsProps {
|
|
contentType: string;
|
|
fileSize: number;
|
|
responseTimeMs: number;
|
|
responseStatusCode: number;
|
|
postFileState: PostFileState;
|
|
logEntryId: number;
|
|
logEntryPath: string;
|
|
}
|
|
|
|
export const FileDetails: React.FC<FileDetailsProps> = ({
|
|
contentType,
|
|
fileSize,
|
|
responseTimeMs,
|
|
responseStatusCode,
|
|
postFileState,
|
|
logEntryId,
|
|
logEntryPath,
|
|
}) => {
|
|
return (
|
|
<SkySection
|
|
title="File Details"
|
|
contentClassName="grid grid-cols-3 sm:grid-cols-6 text-sm"
|
|
>
|
|
<TitleStat
|
|
label="Type"
|
|
value={contentType}
|
|
iconClass="fa-solid fa-file"
|
|
/>
|
|
<TitleStat
|
|
label="Size"
|
|
value={byteCountToHumanSize(fileSize)}
|
|
iconClass="fa-solid fa-weight-hanging"
|
|
/>
|
|
<TitleStat
|
|
label="Time"
|
|
value={responseTimeMs == -1 ? undefined : `${responseTimeMs}ms`}
|
|
iconClass="fa-solid fa-clock"
|
|
/>
|
|
<TitleStat
|
|
label="Status"
|
|
value={responseStatusCode}
|
|
textClass={
|
|
responseStatusCode == 200 ? 'text-green-600' : 'text-red-600'
|
|
}
|
|
iconClass="fa-solid fa-signal"
|
|
/>
|
|
<TitleStat
|
|
label="State"
|
|
value={postFileState}
|
|
textClass={postFileState == 'ok' ? 'text-green-600' : 'text-red-600'}
|
|
iconClass="fa-solid fa-circle-check"
|
|
/>
|
|
<TitleStat label="Log Entry" iconClass="fa-solid fa-file-pen">
|
|
<a
|
|
href={logEntryPath}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="font-medium text-blue-600 hover:text-blue-800"
|
|
>
|
|
#{logEntryId}
|
|
</a>
|
|
</TitleStat>
|
|
</SkySection>
|
|
);
|
|
};
|
|
|
|
const TitleStat: React.FC<{
|
|
label: string;
|
|
value?: string | number;
|
|
iconClass: string;
|
|
textClass?: string;
|
|
children?: React.ReactNode;
|
|
}> = ({ label, value, iconClass, textClass = 'text-slate-600', children }) => {
|
|
function valueElement(value: string | number | undefined) {
|
|
const defaultTextClass = 'font-normal';
|
|
if (value === undefined) {
|
|
return <span className="text-slate-500">—</span>;
|
|
} else if (typeof value === 'number') {
|
|
return (
|
|
<span className={`${textClass} ${defaultTextClass}`}>
|
|
{value.toLocaleString()}
|
|
</span>
|
|
);
|
|
} else {
|
|
return (
|
|
<span className={`${textClass} ${defaultTextClass}`}>{value}</span>
|
|
);
|
|
}
|
|
}
|
|
|
|
const gridInnerBorderClasses =
|
|
'border-r border-b border-slate-300 last:border-r-0 sm:last:border-r-0 [&:nth-child(3)]:border-r-0 sm:[&:nth-child(3)]:border-r [&:nth-last-child(-n+3)]:border-b-0 sm:[&:nth-last-child(-n+6)]:border-b-0';
|
|
|
|
return (
|
|
<div
|
|
className={`flex flex-col justify-center px-2 py-1 ${gridInnerBorderClasses}`}
|
|
>
|
|
<div className="flex items-center gap-2 font-light text-slate-600">
|
|
<i className={iconClass}></i>
|
|
<span>{label}</span>
|
|
</div>
|
|
{children || valueElement(value)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default FileDetails;
|