Skip to content

Commit

Permalink
Add services list view
Browse files Browse the repository at this point in the history
  • Loading branch information
keigohtr committed Aug 10, 2018
1 parent a5022ac commit 73a6aae
Show file tree
Hide file tree
Showing 6 changed files with 750 additions and 1 deletion.
6 changes: 5 additions & 1 deletion frontend/src/apis/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export class Service {
public id: string = '',
public name: string = '',
public serviceLevel: string = '',
public modelId: string = null
public modelId: string = null,
public host: string = '',
public description: string = '',
) { }
}

Expand Down Expand Up @@ -323,6 +325,8 @@ export async function fetchAllServices(params: FetchServicesParam) {
name: variable.display_name,
serviceLevel: variable.service_level,
modelId: variable.model_id,
host: variable.host,
description: variable.description,
}
})
return APICore.getRequest(`${process.env.API_HOST}:${process.env.API_PORT}/api/applications/${params.applicationId}/services`, convert)
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/components/App/Application/SideMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ class SideMenu extends React.Component<any> {
path: 'dashboard',
icon: 'ship'
},
{
text: 'Services',
path: 'services',
icon: 'server'
},
{
text: 'Models',
path: 'models',
icon: 'database'
},
]
},
]
Expand Down
194 changes: 194 additions & 0 deletions frontend/src/components/App/Services/ServicesDeleteForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import * as React from 'react'
import { connect } from 'react-redux'
import { Button } from 'reactstrap'
import { reduxForm, InjectedFormProps } from 'redux-form'

import { APIRequest } from '@src/apis/Core'
import { Service } from '@src/apis'
import ServicesStatusTable from './ServicesStatusTable'
import { ControlMode } from './index'

class ServicesDeleteForm extends React.Component<ServicesDeleteFormProps, {}> {
constructor(props, context) {
super(props, context)

this.handleDiscardChanges = this.handleDiscardChanges.bind(this)
}

componentWillReceiveProps(nextProps: ServicesDeleteFormProps) {
const { mode, pristine, changeMode } = nextProps

if (mode === ControlMode.VIEW_SERVICES_STATUS && !pristine) {
changeMode(ControlMode.SELECT_TARGETS)
} else if (mode === ControlMode.SELECT_TARGETS && pristine) {
changeMode(ControlMode.VIEW_SERVICES_STATUS)
}
}

render() {
const {
onSubmit,
handleSubmit,
} = this.props

return (
<form onSubmit={handleSubmit(onSubmit)} >
<div className='mb-2'>
{this.renderDiscardButton()}
</div>
<ServicesStatusTable
{...this.props}
/>
<hr />
{this.renderSubmitButtons()}
</form>
)
}

renderDiscardButton = () => {
const { mode } = this.props

switch (mode) {
case ControlMode.SELECT_TARGETS:
return (
<Button outline color='danger' onClick={this.handleDiscardChanges}>
<i className={`fas fa-times fa-fw mr-2`}></i>
Discard changes
</Button>
)
default:
return (
<div>
</div>

This comment has been minimized.

Copy link
@yustoris

yustoris Aug 10, 2018

Member

You can just return null

)
}
}

/**
* Render submit button(s)
*
* Show delete button if selected targets exist
* Show save button if editing deploy status
*/
renderSubmitButtons(): JSX.Element {
const {
mode,
submitting,
pristine
} = this.props

const showSubmitButton: boolean = mode !== ControlMode.VIEW_SERVICES_STATUS

if (!showSubmitButton) {
return null
}

const paramsMap = {
[ControlMode.SELECT_TARGETS]: { color: 'danger', icon: 'trash', text: 'Delete Services' },
}

// Submit button element(s)
const buttons = (params) => (
<div className='mb-2'>
<Button
color={params.color}
className='mr-2'
disabled={pristine || submitting}
type='submit'
>
<i className={`fas fa-${params.icon} fa-fw mr-2`}></i>
{params.text}
</Button>
</div>
)

const submittingLoader = (
<div>
<div className='loader loader-primary loader-xs mr-2'/>
Submitting...
</div>
)

return submitting ? submittingLoader : buttons(paramsMap[mode])
}

renderSubmitButtonElements() {
const {
submitting,
pristine
} = this.props

const paramsMap = {
[ControlMode.SELECT_TARGETS]: { color: 'danger', icon: 'trash', text: 'Delete Services' },
}

return (
<div className='mb-2'>
<Button
color='danger'
className='mr-2'
disabled={pristine || submitting}
>
<i className='fas fa-trash fa-fw mr-2'></i>
Delete Services
</Button>
</div>
)
}

// Handle event methods

handleDiscardChanges(event): void {
const { changeMode, reset } = this.props
reset()
changeMode(ControlMode.VIEW_SERVICES_STATUS)
}
}

interface ServicesDeleteFormCustomProps {
applicationType: string
applicationId
mode: ControlMode
services: Service[]
onSubmit: (e) => Promise<void>
changeMode: (mode: ControlMode) => void
}

interface StateProps {
initialValues: {
status
delete
}
}

const mapStateToProps = (state: any, extraProps: ServicesDeleteFormCustomProps) => {
// Map of service ID to delete flag
const initialDeleteStatus: { [x: string]: boolean } =
extraProps.services
.map((service) => ({[service.id]: false}))
.reduce((l, r) => Object.assign(l, r), {})

return {
...state.form,
initialValues: {
delete: {
services: initialDeleteStatus
}
}
}
}

const mapDispatchToProps = (dispatch): {} => {
return { }
}

type ServicesDeleteFormProps
= StateProps & ServicesDeleteFormCustomProps & InjectedFormProps<{}, ServicesDeleteFormCustomProps>

export default connect(mapStateToProps, mapDispatchToProps)(
reduxForm<{}, ServicesDeleteFormCustomProps>(
{
form: 'deployStatusForm'
}
)(ServicesDeleteForm)
)
161 changes: 161 additions & 0 deletions frontend/src/components/App/Services/ServicesStatusTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import * as React from 'react'
import { connect } from 'react-redux'
import { CustomInput, Table, Row } from 'reactstrap'
import { Field, InjectedFormProps } from 'redux-form'
import { Link } from 'react-router-dom'

import { Service } from '@src/apis'
import { ControlMode } from './index'

/**
* Table for showing services status
*/
class ServicesStatusTable extends React.Component<ServicesStatusProps, {tooltipOpen}> {
constructor(props, context) {
super(props, context)

this.state = {
tooltipOpen: {}
}
}

render() {
const { services } = this.props

return (
<Table hover className='mb-3'>
{this.renderTableHead()}
{this.renderTableBody(services)}
</Table>
)
}

toggleTooltip(tag) {
return () => {
const nextTooltipOpen = {
...this.state.tooltipOpen,
[tag]: !this.state.tooltipOpen[tag]
}

this.setState({
tooltipOpen: nextTooltipOpen
})
}
}

/**
* Render head row of the table
*/
renderTableHead = () => {
return (
<thead>
<tr className='bg-light text-primary'>
<th>Name</th><th>Service Level</th><th>Description</th><th>Host</th>
</tr>
</thead>
)
}

/**
* Render body of the table
*
* Render Service names
* Each Service is rendered with a deploy check box on viewing/deleting mode
* @param services Services to be shown (Currently show all, but should be filtered)
*/
renderTableBody = (services) => {
const { mode, applicationType, applicationId } = this.props

// Button to delete Service (for deleting k8s services)
const deleteCheckButton = (serviceName: string, serviceId: string) => {
return (
<Row>
{ applicationType === 'kubernetes' ?
<Field
name={`delete.services.${serviceId}`}
component={CustomCheckBox}
id={`service-${serviceId}`}
label=''
className='mr-1'
/>
: null }
<Link className='text-info' to={`/applications/${applicationId}/services/${serviceId}/edit`}>
{serviceName}
</Link>
</Row>
)
}

const renderButton = (serviceName, serviceId) => {
const renderMap = {
[ControlMode.VIEW_SERVICES_STATUS] : deleteCheckButton(serviceName, serviceId),
[ControlMode.SELECT_TARGETS] : deleteCheckButton(serviceName, serviceId),
}
return renderMap[mode]

This comment has been minimized.

Copy link
@yustoris

yustoris Aug 10, 2018

Member

Do you need to use renderMap ?

}

return (
<tbody>
{services.map(
(service) => (
<tr>
<td key={service.name} scope='col'>
{renderButton(service.name, service.id)}
</td>
<td>
{service.serviceLevel}
</td>
<td>
{service.description}
</td>
<td>
{service.host}
</td>
</tr>
)
)}
</tbody>
)
}

}

const CustomCheckBox = (props) => {
const { input, id, label } = props

return (
<CustomInput
{...input}
type='checkbox'
name={input.name}
id={id}
checked={input.value}
label={label}
/>
)
}

interface ServicesStatusFormCustomProps {
applicationType: string
applicationId
services: Service[],
mode: ControlMode,
}

export interface DispatchProps {
dispatchChange
}

const mapDispatchToProps = (dispatch): DispatchProps => {
return {
dispatchChange: (field, value, changeMethod) => dispatch(changeMethod(field, value))
}
}

const mapStateToProps = (state: any, extraProps: ServicesStatusFormCustomProps) => {
return {}
}

type ServicesStatusProps = DispatchProps & ServicesStatusFormCustomProps & InjectedFormProps<{}, ServicesStatusFormCustomProps>

export default connect(mapStateToProps, mapDispatchToProps)(ServicesStatusTable)
Loading

0 comments on commit 73a6aae

Please sign in to comment.