+79
@@ -0,0 +1,79 @@
|
||||
import React, { useContext } from "react";
|
||||
import { format, formatDistanceToNow } from "date-fns";
|
||||
import FileSaver from "file-saver";
|
||||
|
||||
import { NotificationContext } from "context/notification";
|
||||
import scriptAPI, { IScript } from "services/entities/scripts";
|
||||
|
||||
import Icon from "components/Icon";
|
||||
import Button from "components/buttons/Button";
|
||||
|
||||
const baseClass = "script-list-item";
|
||||
|
||||
interface IScriptListItemProps {
|
||||
script: IScript;
|
||||
onDelete: (script: IScript) => void;
|
||||
}
|
||||
|
||||
const getFileIconName = (fileName: string) => {
|
||||
const fileExtension = fileName.split(".").pop();
|
||||
|
||||
switch (fileExtension) {
|
||||
case "py":
|
||||
return "file-python";
|
||||
case "zsh":
|
||||
return "file-zsh";
|
||||
case "sh":
|
||||
return "file-bash";
|
||||
default:
|
||||
return "file-generic";
|
||||
}
|
||||
};
|
||||
|
||||
const ScriptListItem = ({ script, onDelete }: IScriptListItemProps) => {
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
|
||||
const onClickDownload = async () => {
|
||||
try {
|
||||
const content = await scriptAPI.downloadScript(script.id);
|
||||
const formatDate = format(new Date(), "yyyy-MM-dd");
|
||||
const filename = `${formatDate}_${script.name}`;
|
||||
const file = new File([content], filename);
|
||||
FileSaver.saveAs(file);
|
||||
} catch {
|
||||
renderFlash("error", "Couldn’t Download. Please try again.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__value-group ${baseClass}__script-data`}>
|
||||
<Icon name={getFileIconName(script.name)} />
|
||||
<div className={`${baseClass}__script-info`}>
|
||||
<span className={`${baseClass}__script-name`}>{script.name}</span>
|
||||
<span className={`${baseClass}__script-uploaded`}>
|
||||
{`Uploaded ${formatDistanceToNow(new Date(script.created_at))} ago`}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${baseClass}__value-group ${baseClass}__script-actions`}>
|
||||
<Button
|
||||
className={`${baseClass}__download-button`}
|
||||
variant="text-icon"
|
||||
onClick={onClickDownload}
|
||||
>
|
||||
<Icon name="download" />
|
||||
</Button>
|
||||
<Button
|
||||
className={`${baseClass}__delete-button`}
|
||||
variant="text-icon"
|
||||
onClick={() => onDelete(script)}
|
||||
>
|
||||
<Icon name="trash" color="ui-fleet-black-75" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScriptListItem;
|
||||
@@ -0,0 +1,68 @@
|
||||
.script-list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__value-group {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__script-data {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__script-info {
|
||||
margin-left: $pad-medium;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__script-name {
|
||||
font-size: $x-small;
|
||||
}
|
||||
|
||||
&__script-uploaded {
|
||||
font-size: $xx-small;
|
||||
}
|
||||
|
||||
&__script-statuses {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
span {
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&__has-value {
|
||||
color: $core-vibrant-blue;
|
||||
}
|
||||
|
||||
&__script-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__refresh-button,
|
||||
&__download-button,
|
||||
&__delete-button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
&__refresh-button,
|
||||
&__download-button {
|
||||
margin-right: $pad-medium;
|
||||
}
|
||||
|
||||
@media (max-width: $break-md) {
|
||||
&__script-statuses {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__script-actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./ScriptListItem";
|
||||
Reference in New Issue
Block a user