Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions src/actions/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
import _ from 'lodash'
import {
LOAD_PROJECTS_METADATA, ADD_PROJECTS_METADATA, UPDATE_PROJECTS_METADATA, REMOVE_PROJECTS_METADATA,
PROJECT_TEMPLATES_SORT, PRODUCT_TEMPLATES_SORT, PROJECT_TYPES_SORT, CREATE_PROJECT_TEMPLATE, CREATE_PROJECT_TYPE,
PROJECT_TEMPLATES_SORT, PRODUCT_TEMPLATES_SORT, PROJECT_TYPES_SORT, MILESTONE_TEMPLATES_SORT, CREATE_PROJECT_TEMPLATE, CREATE_PROJECT_TYPE,
CREATE_PRODUCT_TEMPLATE,
CREATE_MILESTONE_TEMPLATE,
PRODUCT_CATEGORIES_SORT,
CREATE_PRODUCT_CATEGORY,
REMOVE_PROJECT_TYPE,
Expand Down Expand Up @@ -83,6 +84,15 @@ export function createProjectType(data) {
}
}

export function createMilestoneTemplate(data) {
return (dispatch) => {
return dispatch({
type: CREATE_MILESTONE_TEMPLATE,
payload: createProjectsMetadataAPI('milestoneTemplates', data)
})
}
}

export function createProductCategory(data) {
return (dispatch) => {
return dispatch({
Expand Down Expand Up @@ -150,7 +160,7 @@ export function sortProjectTemplates(criteria) {
return (dispatch) => {
const fieldName = _.split(criteria, ' ')[0]
const order = _.split(criteria, ' ')[1] || 'asc'

return dispatch({
type: PROJECT_TEMPLATES_SORT,
payload: { fieldName, order }
Expand All @@ -162,7 +172,7 @@ export function sortProductTemplates(criteria) {
return (dispatch) => {
const fieldName = _.split(criteria, ' ')[0]
const order = _.split(criteria, ' ')[1] || 'asc'

return dispatch({
type: PRODUCT_TEMPLATES_SORT,
payload: { fieldName, order }
Expand All @@ -174,22 +184,34 @@ export function sortProjectTypes(criteria) {
return (dispatch) => {
const fieldName = _.split(criteria, ' ')[0]
const order = _.split(criteria, ' ')[1] || 'asc'

return dispatch({
type: PROJECT_TYPES_SORT,
payload: { fieldName, order }
})
}
}

export function sortMilestoneTemplates(criteria) {
return (dispatch) => {
const fieldName = _.split(criteria, ' ')[0]
const order = _.split(criteria, ' ')[1] || 'asc'

return dispatch({
type: MILESTONE_TEMPLATES_SORT,
payload: { fieldName, order }
})
}
}

export function sortProductCategories(criteria) {
return (dispatch) => {
const fieldName = _.split(criteria, ' ')[0]
const order = _.split(criteria, ' ')[1] || 'asc'

return dispatch({
type: PRODUCT_CATEGORIES_SORT,
payload: { fieldName, order }
})
}
}
}
7 changes: 7 additions & 0 deletions src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,11 @@ export const CREATE_PROJECT_TYPE_PENDING = 'CREATE_PROJECT_TYPE_PENDING'
export const CREATE_PROJECT_TYPE_SUCCESS = 'CREATE_PROJECT_TYPE_SUCCESS'
export const CREATE_PROJECT_TYPE_FAILURE = 'CREATE_PROJECT_TYPE_FAILURE'

export const CREATE_MILESTONE_TEMPLATE = 'CREATE_MILESTONE_TEMPLATE'
export const CREATE_MILESTONE_TEMPLATE_PENDING = 'CREATE_MILESTONE_TEMPLATE_PENDING'
export const CREATE_MILESTONE_TEMPLATE_SUCCESS = 'CREATE_MILESTONE_TEMPLATE_SUCCESS'
export const CREATE_MILESTONE_TEMPLATE_FAILURE = 'CREATE_MILESTONE_TEMPLATE_FAILURE'

export const CREATE_PRODUCT_CATEGORY = 'CREATE_PRODUCT_CATEGORY'
export const CREATE_PRODUCT_CATEGORY_PENDING = 'CREATE_PRODUCT_CATEGORY_PENDING'
export const CREATE_PRODUCT_CATEGORY_SUCCESS = 'CREATE_PRODUCT_CATEGORY_SUCCESS'
Expand Down Expand Up @@ -469,6 +474,8 @@ export const PROJECT_TEMPLATES_SORT = 'PROJECT_TEMPLATES_SORT'
export const PRODUCT_TEMPLATES_SORT = 'PRODUCT_TEMPLATES_SORT'
export const PROJECT_TYPES_SORT = 'PROJECT_TYPES_SORT'
export const PRODUCT_CATEGORIES_SORT = 'PRODUCT_CATEGORIES_SORT'
export const MILESTONE_TEMPLATES_SORT = 'MILESTONE_TEMPLATES_SORT'


export const THREAD_MESSAGES_PAGE_SIZE = 3
/*
Expand Down
17 changes: 16 additions & 1 deletion src/reducers/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
PROJECT_TEMPLATES_SORT,
PRODUCT_TEMPLATES_SORT,
PROJECT_TYPES_SORT,
MILESTONE_TEMPLATES_SORT,
CREATE_PROJECT_TEMPLATE_PENDING,
CREATE_PROJECT_TEMPLATE_FAILURE,
CREATE_PROJECT_TEMPLATE_SUCCESS,
Expand All @@ -25,6 +26,9 @@ import {
CREATE_PROJECT_TYPE_FAILURE,
CREATE_PRODUCT_TEMPLATE_SUCCESS,
CREATE_PROJECT_TYPE_SUCCESS,
CREATE_MILESTONE_TEMPLATE_SUCCESS,
CREATE_MILESTONE_TEMPLATE_PENDING,
CREATE_MILESTONE_TEMPLATE_FAILURE,
REMOVE_PRODUCT_CATEGORY_PENDING,
REMOVE_PROJECT_TYPE_PENDING,
REMOVE_PRODUCT_CATEGORY_FAILURE,
Expand Down Expand Up @@ -67,14 +71,15 @@ export default function(state = initialState, action) {
projectTypes: _.orderBy(projectTypes, ['updatedAt'], ['desc']),
productTemplates: _.orderBy(productTemplates, ['updatedAt'], ['desc']),
productCategories: _.orderBy(productCategories, ['updatedAt'], ['desc']),
milestoneTemplates,
milestoneTemplates: _.orderBy(milestoneTemplates, ['updatedAt'], ['desc']),
isLoading: false,
}
}
case ADD_PROJECTS_METADATA_PENDING:
case CREATE_PROJECT_TEMPLATE_PENDING:
case CREATE_PRODUCT_TEMPLATE_PENDING:
case CREATE_PROJECT_TYPE_PENDING:
case CREATE_MILESTONE_TEMPLATE_PENDING:
case UPDATE_PROJECTS_METADATA_PENDING:
return {
...state,
Expand All @@ -93,6 +98,7 @@ export default function(state = initialState, action) {
case CREATE_PROJECT_TEMPLATE_FAILURE:
case CREATE_PRODUCT_TEMPLATE_FAILURE:
case CREATE_PROJECT_TYPE_FAILURE:
case CREATE_MILESTONE_TEMPLATE_FAILURE:
Alert.error(`PROJECT METADATA CREATE FAILED: ${action.payload.response.data.result.content.message}`)
return {
...state,
Expand Down Expand Up @@ -121,6 +127,7 @@ export default function(state = initialState, action) {
case CREATE_PROJECT_TEMPLATE_SUCCESS:
case CREATE_PRODUCT_TEMPLATE_SUCCESS:
case CREATE_PROJECT_TYPE_SUCCESS:
case CREATE_MILESTONE_TEMPLATE_SUCCESS:
Alert.success('PROJECT METADATA CREATE SUCCESS')
return {
...state,
Expand Down Expand Up @@ -192,6 +199,14 @@ export default function(state = initialState, action) {
projectTypes: _.orderBy(state.projectTypes, [`${fieldName}`], [`${order}`]),
}
}
case MILESTONE_TEMPLATES_SORT: {
const fieldName = action.payload.fieldName
const order = action.payload.order
return {
...state,
milestoneTemplates: _.orderBy(state.milestoneTemplates, [`${fieldName}`], [`${order}`]),
}
}
case PRODUCT_CATEGORIES_SORT: {
const fieldName = action.payload.fieldName
const order = action.payload.order
Expand Down
33 changes: 31 additions & 2 deletions src/routes/metadata/components/MetaDataPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@ class MetaDataPanel extends React.Component {
if (metadataType === 'projectType') {
return { metadata: {} }
}
if (metadataType === 'milestoneTemplate') {
return { metadata: {} }
}
return {}
}
return metadata ? metadata : dirtyMetadata
Expand Down Expand Up @@ -366,6 +369,29 @@ class MetaDataPanel extends React.Component {
{ key: 'disabled', type: 'checkbox' },
{ key: 'hidden', type: 'checkbox' },
])
} else if (metadataType === 'milestoneTemplate') {
const productTemplateOptions = templates.productTemplates.map((item) => {
return {
value: item.id,
title: `(${item.id}) ${item.name}`
}
})

fields = fields.concat([
{ key: 'name', type: 'text' },
{ key: 'description', type: 'textarea' },
{ key: 'duration', type: 'number' },
{ key: 'type', type: 'text' },
{ key: 'order', type: 'number' },
{ key: 'plannedText', type: 'textarea' },
{ key: 'activeText', type: 'textarea' },
{ key: 'completedText', type: 'textarea' },
{ key: 'blockedText', type: 'textarea' },
{ key: 'reference', type: 'text', readonly: true, value: 'productTemplate' },
{ key: 'referenceId', type: 'dropdown', options: productTemplateOptions, value: String(productTemplateOptions[0].value) },
{ key: 'metadata', type: 'json' },
{ key: 'hidden', type: 'checkbox' },
])
}
return fields
}
Expand Down Expand Up @@ -433,13 +459,16 @@ class MetaDataPanel extends React.Component {
}
})
} else {
const payload = _.omit(data, omitKeys)
let payload = _.omit(data, omitKeys)
if (metadataType === 'milestoneTemplate') {
payload = _.omit(payload, ['key'])
}
const metadataResource = this.getResourceNameFromType(metadataType)
this.props.createProjectsMetadata(payload)
.then((res) => {
if (!res.error) {
const createdMetadata = res.action.payload
if (['projectTemplate', 'productTemplate'].indexOf(metadataType) !== -1) {
if (['projectTemplate', 'productTemplate', 'milestoneTemplate'].indexOf(metadataType) !== -1) {
window.location = `/metadata/${metadataResource}/${createdMetadata.id}`
} else {
window.location = `/metadata/${metadataResource}/${createdMetadata.key}`
Expand Down
152 changes: 152 additions & 0 deletions src/routes/metadata/components/MilestoneTemplatesGridView.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import React from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import { NavLink, Link } from 'react-router-dom'
import moment from 'moment'
import GridView from '../../../components/Grid/GridView'

import './MetaDataProjectTemplatesGridView.scss'

const MilestoneTemplatesGridView = props => {
const { totalCount, criteria, pageNum, pageSize, sortHandler,
error, isLoading, infiniteAutoload, setInfiniteAutoload,
applyFilters, milestoneTemplates } = props

const currentSortField = _.get(criteria, 'sort', '')
// This 'little' array is the heart of the list component.
// it defines what columns should be displayed and more importantly
// how they should be displayed.
const columns = [
{
id: 'id',
headerLabel: 'ID',
classes: 'item-key',
sortable: false,
renderText: item => {
const url = `/metadata/milestoneTemplates/${item.id}`
const recentlyCreated = moment().diff(item.createdAt, 'seconds') < 3600
return (
<Link to={url} className="spacing">
{recentlyCreated && <span className="blue-border" />}
{item.id}
</Link>
)
}
}, {
id: 'name',
headerLabel: 'Name',
classes: 'item-project-templates',
sortable: false,
renderText: item => {
const url = `/metadata/milestoneTemplates/${item.id}`
return (
<div className="spacing project-template-container">
<div className="template-title">
<Link to={url} className="link-title">{_.unescape(item.name)}</Link>
</div>
</div>
)
}
}, {
id: 'type',
headerLabel: 'Type',
classes: 'item-key',
sortable: false,
renderText: item => {
return (
<div className="spacing project-template-container">
<div className="template-title">
{item.type}
</div>
</div>
)
}
}, {
id: 'updatedAt',
headerLabel: 'Updated At',
sortable: true,
classes: 'item-status-date',
renderText: item => {
const time = moment(item.updatedAt)
return (
<div className="spacing time-container">
<div className="txt-normal">{time.year() === moment().year() ? time.format('MMM D, h:mm a') : time.format('MMM D YYYY, h:mm a')}</div>
</div>
)
}
}, {
id: 'createdAt',
headerLabel: 'Created At',
sortable: true,
classes: 'item-status-date',
renderText: item => {
const time = moment(item.createdAt)
return (
<div className="spacing time-container">
<div className="txt-normal">{time.year() === moment().year() ? time.format('MMM D, h:mm a') : time.format('MMM D YYYY, h:mm a')}</div>
</div>
)
}
}, {
id: 'hidden',
headerLabel: 'Hidden',
sortable: false,
classes: 'item-hidden-status',
renderText: item => {
return (
<div className="spacing">
{ item.hidden ? 'Hidden' : 'Visible' }
</div>
)
}
}
]

const gridProps = {
error,
isLoading,
columns,
onPageChange: () => {}, // dummy, as we are not expecting paging yet in metadata views
sortHandler,
currentSortField,
resultSet: milestoneTemplates.map((item) => ({
...item,
id: item.id,
})),
totalCount,
currentPageNum: pageNum,
pageSize,
infiniteAutoload,
infiniteScroll: true,
setInfiniteAutoload,
applyFilters,
entityName: 'project type',
entityNamePlural: 'project types'
}

return (
<div className="project-templates-grid-view">
<div className="project-templates-actions">
<NavLink to="/metadata/new-milestone-template" className="tc-btn tc-btn-primary align-button">
Create
</NavLink>
</div>
<GridView {...gridProps} />
</div>
)
}


MilestoneTemplatesGridView.propTypes = {
currentUser: PropTypes.object.isRequired,
totalCount: PropTypes.number.isRequired,
isLoading: PropTypes.bool.isRequired,
error: PropTypes.bool.isRequired,
// onPageChange: PropTypes.func.isRequired,
sortHandler: PropTypes.func.isRequired,
pageNum: PropTypes.number.isRequired,
criteria: PropTypes.object.isRequired,
milestoneTemplates: PropTypes.array.isRequired,
}

export default MilestoneTemplatesGridView
Loading