/*
Copyright (C) 2019 Cloudbase Solutions SRL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
import React from 'react'
import { observer } from 'mobx-react'
import styled, { css } from 'styled-components'
import InfoIcon from '@src/components/ui/InfoIcon'
import { Close as InputClose } from '@src/components/ui/TextInput'
import { Image as InstanceImage } from '@src/components/modules/WizardModule/WizardInstances'
import StatusIcon from '@src/components/ui/StatusComponents/StatusIcon'
import { ThemePalette, ThemeProps } from '@src/components/Theme'
import FileUtils from '@src/utils/FileUtils'
import type { Instance, InstanceScript } from '@src/@types/Instance'
import { UserScriptData } from '@src/@types/MainItem'
import DomUtils from '@src/utils/DomUtils'
import scriptItemImage from './images/script-item.svg'
const Wrapper = styled.div`
width: 100%;
display: flex;
overflow: auto;
flex-direction: column;
min-height: 0;
`
const Group = styled.div`
display: flex;
flex-direction: column;
width: 100%;
margin-bottom: 32px;
&:last-child {
margin-bottom: 0;
}
`
const Heading = styled.div`
margin-bottom: 16px;
font-size: ${props => (props.layout === 'modal' ? '16px' : '24px')};
font-weight: ${props => (props.layout === 'modal' ? ThemeProps.fontWeights.medium : ThemeProps.fontWeights.light)};
display: flex;
`
const InfoIconStyled = styled(InfoIcon)`
margin-top: ${props => (props.layout === 'modal' ? '1px' : '5px')};
margin-left: 8px;
`
const Scripts = styled.div`
width: 100%;
display: flex;
flex-direction: column;
`
const Script = styled.div`
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
border-top: 1px solid ${ThemePalette.grayscale[1]};
padding: 8px 0;
&:last-child {
border-bottom: 1px solid ${ThemePalette.grayscale[1]};
}
`
const Name = styled.div`
display: flex;
align-items: center;
`
const OsImage = styled.div`
${ThemeProps.exactSize('48px')}
background: url('${scriptItemImage}') center no-repeat;
`
const NameLabel = styled.div`
display: flex;
flex-direction: column;
margin-left: 16px;
`
const NameLabelTitle = styled.div`
font-size: 16px;
word-break: break-word;
`
const NameLabelSubtitle = styled.div`
font-size: 12px;
color: ${ThemePalette.grayscale[5]};
margin-top: 1px;
word-break: break-word;
`
const LinkButton = styled.div`
color: ${ThemePalette.primary};
flex-shrink: 0;
margin: 0 8px 0 16px;
cursor: pointer;
:hover {
text-decoration: underline;
}
`
const UploadedScript = styled.div`
display: flex;
position: relative;
`
const UploadedScriptFileName = styled.div`
max-width: 124px;
text-overflow: ellipsis;
overflow: hidden;
margin-right: 32px;
white-space: nowrap;
`
const InputCloseStyled = styled(InputClose)`
top: 0px;
`
const FakeFileInput = styled.input`
position: absolute;
opacity: 0;
top: -99999px;
`
const ScriptDataActions = styled.div`
display: flex;
margin-left: -8px;
margin-top: 8px;
> div {
margin-left: 8px;
}
`
const ScriptDataAction = styled.div<{ red?: boolean, disabled?: boolean }>`
color: ${props => (props.red ? ThemePalette.alert : ThemePalette.primary)};
cursor: pointer;
${props => (props.disabled ? css`
opacity: 0.6;
cursor: default;
` : '')}
font-size: 12px;
`
type Props = {
instances: Instance[],
uploadedScripts: InstanceScript[],
removedScripts: InstanceScript[],
layout?: 'modal' | 'page',
loadingInstances?: boolean,
userScriptData: UserScriptData | null | undefined
style?: React.CSSProperties
onScriptUpload: (instanceScript: InstanceScript) => void,
onCancelScript: (global: 'windows' | 'linux' | null, instanceName: string | null) => void,
onScrollableRef?: (ref: HTMLElement) => void,
scrollableRef?: (r: HTMLElement) => void
onScriptDataRemove: (script: InstanceScript) => void
}
type FileInputRefs = {
[prop: string]: {
inputRef: HTMLInputElement,
}
}
@observer
class WizardScripts extends React.Component {
fileInputRefs: FileInputRefs = {}
async handleFileUpload(
files: FileList | null,
global: 'windows' | 'linux' | null,
instanceId: string | null,
) {
if (!files || !files.length) {
return
}
const fileName = files[0].name
const scriptContent = await FileUtils.readTextFromFirstFile(files)
this.props.onScriptUpload({
instanceId,
global,
fileName,
scriptContent: scriptContent || '',
})
}
handleScriptDataDownload(scriptData: string, fileName: string) {
DomUtils.download(scriptData, fileName)
}
renderScriptItem(opts: {
global?: 'windows' | 'linux',
instanceId?: string,
title: string,
subtitle?: string,
}) {
const {
global, instanceId, title, subtitle,
} = opts
const uploadedScript = this.props.uploadedScripts.find(
s => (s.instanceId
? s.instanceId === instanceId : s.global ? s.global === global : false),
)
let scriptData: string | null | undefined = null
if (global) {
scriptData = this.props.userScriptData?.global?.[global]
} else if (instanceId) {
scriptData = this.props.userScriptData?.instances?.[instanceId]
}
const isRemoved: boolean = Boolean(this.props.removedScripts
.find(s => (global ? s.global === global : s.instanceId === instanceId)))
return (
)
}
renderScriptGroup(group: 'global' | 'instance') {
if (group === 'global') {
return (
Global Scripts
{this.renderScriptItem({ global: 'windows', title: 'Windows Script File' })}
{this.renderScriptItem({ global: 'linux', title: 'Linux Script File' })}
)
}
if (this.props.instances.length === 0 && !this.props.loadingInstances) {
return null
}
return (
Instance Scripts
{!this.props.loadingInstances ? (
) : null}
{this.props.loadingInstances ? (
) : null}
{this.props.instances.map(instance => {
const id = instance.instance_name || instance.id
const title = instance.name
const osLabel = instance.os_type ? instance.os_type === 'windows' ? 'Windows' : instance.os_type === 'linux' ? 'Linux' : instance.os_type : ''
const osType = osLabel ? `${osLabel} OS | ` : ''
const subtitle = `${osType}${instance.num_cpu} vCPU | ${instance.memory_mb} MB RAM`
return this.renderScriptItem({ instanceId: id, title, subtitle })
})}
)
}
render() {
return (
{
if (this.props.onScrollableRef) {
this.props.onScrollableRef(r)
}
}}
>
{this.renderScriptGroup('global')}
{this.renderScriptGroup('instance')}
)
}
}
export default WizardScripts