import React from 'react'
import * as antd from 'antd'
import { Icons as FIcons, createIconRender } from "components/Icons"
import * as MDIcons from "react-icons/md"
import loadable from "@loadable/component"
import "./index.less"
const Icons = {
...FIcons,
...MDIcons
}
const FormComponents = {
"input": antd.Input,
"textarea": antd.Input.TextArea,
"select": antd.Select,
"datepicker": antd.DatePicker,
}
const requestModifyByType = {
"vaultItem": {
"additions": ["essc"]
}
}
// FIELDS
const FieldsForms = {
description: {
label: "Description",
component: "input",
updateEvent: "onChange",
onUpdate: (update) => {
return update.target.value
},
style: {
minWidth: "300px",
},
props: {
placeholder: "Describe something...",
}
},
operations: {
label: "Operations",
component: "input",
updateEvent: "onChange",
onUpdate: (update) => {
return update.target.value
},
},
location: {
label: "Location",
component: "select",
updateEvent: "onChange",
children: async () => {
const api = window.app.request
const regions = await api.get.regions()
return regions.map(region => {
return {region.name}
})
},
props: {
placeholder: "Select a location",
}
},
vaultItemTypeSelector: {
label: "Type",
component: "select",
updateEvent: "onChange",
children: async () => {
let types = await import("schemas/vaultItemsTypes.json")
types = types.default || types
return Object.keys(types).map((group) => {
return
{types[group].map((type) => {
return {String(type).toTitleCase()}
})}
})
},
props: {
placeholder: "Select a type",
}
},
vaultItemSerial: {
label: "Serial number",
component: "input",
updateEvent: "onChange",
onUpdate: (update) => {
return update.target.value
},
props: {
placeholder: "S/N 00000000X",
}
},
vaultItemManufacturer: {
label: "Manufacturer",
component: "input",
updateEvent: "onChange",
onUpdate: (update) => {
return update.target.value
},
props: {
placeholder: "e.g. Hewlett Packard",
}
},
vaultItemManufacturedYear: {
label: "Manufactured Year",
component: "datepicker",
updateEvent: "onChange",
onUpdate: (update) => {
return update.year()
},
props: {
picker: "year"
}
},
}
//FORMULAS
const ProductFormula = {
defaultFields: [
"description",
"operations",
]
}
const OperationFormula = {
defaultFields: [
"description",
"task",
]
}
const PhaseFormula = {
defaultFields: [
"description",
"task",
]
}
const TaskFormula = {
defaultFields: [
"description",
"tasks",
]
}
const VaultItemFormula = {
defaultFields: [
// TODO: include location
"vaultItemTypeSelector",
"vaultItemSerial",
"vaultItemManufacturer",
"vaultItemManufacturedYear",
"location",
]
}
const FORMULAS = {
product: ProductFormula,
operation: OperationFormula,
phase: PhaseFormula,
task: TaskFormula,
vaultItem: VaultItemFormula,
}
// TYPES
const FabricItemTypesIcons = {
"product": "Box",
"operation": "Settings",
"phase": "GitCommit",
"task": "Tool",
"vaultItem": "Archive",
}
const FabricItemTypes = ["product", "operation", "phase", "task", "vaultItem"]
export default class FabricCreator extends React.Component {
state = {
loading: true,
submitting: false,
error: null,
name: null,
type: null,
fields: [],
values: {},
}
componentDidMount = async () => {
await this.setItemType(this.props.defaultType ?? "product")
this.setState({ loading: false })
}
toogleLoading = (to) => {
this.setState({ loading: to ?? !this.state.loading })
}
toogleSubmitting = (to) => {
this.setState({ submitting: to ?? !this.state.submitting })
}
clearError = () => {
if (this.state.error != null) {
this.setState({ error: null })
}
}
clearValues = async () => {
await this.setState({ values: {} })
}
clearFields = async () => {
await this.setState({ fields: [] })
}
setItemType = async (type) => {
const formulaKeys = Object.keys(FORMULAS)
if (formulaKeys.includes(type)) {
const formula = FORMULAS[type]
await this.clearValues()
await this.clearFields()
formula.defaultFields.forEach(field => {
this.appendFieldByType(field)
})
await this.setState({ type: type, name: "New item" })
} else {
console.error(`Cannot load default fields from formula with type ${type}`)
}
}
appendFieldByType = (fieldType) => {
const field = FieldsForms[fieldType]
if (typeof field === "undefined") {
console.error(`No form available for field [${fieldType}]`)
return null
}
const fields = this.state.fields
if (this.fieldsHasTypeKey(fieldType) && !field.allowMultiple) {
console.error(`Field [${fieldType}] already exists, and only can exists 1`)
return false
}
fields.push(this.generateFieldRender({ type: fieldType, ...field }))
this.setState({ fields: fields })
}
fieldsHasTypeKey = (key) => {
let isOnFields = false
const fields = this.state.fields
fields.forEach(field => {
field.props.type === key ? isOnFields = true : null
})
return isOnFields
}
renderFieldSelectorMenu = () => {
return {
this.appendFieldByType(e.key)
}}
>
{Object.keys(FieldsForms).map((key) => {
const field = FieldsForms[key]
const icon = field.icon && createIconRender(field.icon)
const disabled = this.fieldsHasTypeKey(key) && !field.allowMultiple
return
{icon ?? null}
{key.charAt(0).toUpperCase() + key.slice(1)}
})}
}
renderTypeMenuSelector = () => {
return {
this.setItemType(e.key)
}}
>
{FabricItemTypes.map((type) => {
const TypeIcon = FabricItemTypesIcons[type] && createIconRender(FabricItemTypesIcons[type])
return
{TypeIcon ?? null}
{type.charAt(0).toUpperCase() + type.slice(1)}
})}
}
onDone = async () => {
this.clearError()
this.toogleSubmitting(true)
const api = window.app.request
let properties = {}
this.getProperties().forEach((property) => {
if (typeof properties[property.type] !== "undefined") {
return properties[property.id] = property.value
}
return properties[property.type] = property.value
})
let payload = {
type: this.state.type,
name: this.state.name,
properties: properties,
}
if (typeof requestModifyByType[this.state.type] !== "undefined") {
payload = {
...payload,
...requestModifyByType[this.state.type],
}
}
await api.put.fabric(payload).catch((response) => {
console.error(response)
this.setState({ error: response })
return null
})
this.toogleSubmitting(false)
if (!this.state.error && typeof this.props.close === "function") {
this.props.close()
}
}
onChangeName = (event) => {
this.setState({ name: event.target.value })
}
onUpdateValue = (event, value) => {
const { updateEvent, key } = event
let state = this.state
state.values[key] = value
this.setState(state)
}
removeField = (key) => {
let values = this.state.values
let fields = this.state.fields.filter(field => field.key != key)
delete values[key]
this.setState({ fields: fields, values: values })
}
getProperties = () => {
return this.state.fields.map((field) => {
return {
type: field.props.type,
id: field.props.id,
value: this.state.values[field.key],
}
})
}
getKeyFromLatestFieldType = (type) => {
let latestByType = 0
this.state.fields.forEach((field) => {
field.props.type === type ? latestByType++ : null
})
return `${type}-${latestByType}`
}
generateFieldRender = (field) => {
if (!field.key) {
field.key = this.getKeyFromLatestFieldType(field.type)
}
if (typeof FormComponents[field.component] === "undefined") {
console.error(`No component type available for field [${field.key}]`)
return null
}
const getSubmittingState = () => {
return this.state.submitting
}
let fieldComponentProps = {
...field.props,
value: this.state.values[field.key],
disabled: getSubmittingState(),
[field.updateEvent]: (...args) => {
if (typeof field.onUpdate === "function") {
return this.onUpdateValue({ updateEvent: field.updateEvent, key: field.key }, field.onUpdate(...args))
}
return this.onUpdateValue({ updateEvent: field.updateEvent, key: field.key }, ...args)
},
}
let RenderComponent = null
if (typeof field.children === "function") {
RenderComponent = loadable(async () => {
try {
const children = await field.children()
return () => React.createElement(FormComponents[field.component], fieldComponentProps, children)
} catch (error) {
console.log(error)
return ()=>
Load Error
}
}, {
fallback: Loading...
,
})
} else {
RenderComponent = () => React.createElement(FormComponents[field.component], fieldComponentProps)
}
return
{ this.removeField(field.key) }}>
{field.icon && createIconRender(field.icon)}{field.label}
}
render() {
if (this.state.loading) {
return
}
const TypeIcon = FabricItemTypesIcons[this.state.type] && createIconRender(FabricItemTypesIcons[this.state.type])
return
{this.state.submitting ?
: this.state.fields}
{this.state.error &&
{this.state.error}
}
}
}