Skip to content

Instantly share code, notes, and snippets.

@barucAlmaguer
Last active August 25, 2021 20:34
Show Gist options
  • Save barucAlmaguer/d4e43cbb57498dfc9f66d995c2da0c3d to your computer and use it in GitHub Desktop.
Save barucAlmaguer/d4e43cbb57498dfc9f66d995c2da0c3d to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
const timelineUiMachine = {
initial: 'loading_timeline',
states: {
loading_timeline: {
onEntry: ['clearExcelData', 'fetchTimelineData'],
on: {
SUCCESS: [
{
target: 'viewing_empty_timeline',
cond: 'isDataOutOfRange',
actions: ['setDbTimelineData', 'setHistoricProgram', 'setPreviousProgramDate', 'setRooms', 'setResourceCatalog']
}, {
target: 'viewing_timeline',
cond: 'isDataValid',
actions: ['setDbTimelineData', 'setHistoricProgram', 'setOptions', 'setExcelData', 'setRooms', 'setResourceCatalog']
}, {
target: 'error_loading_timeline'
}
],
FAILURE: {
target: 'error_loading_timeline'
// actions: ['setError', 'showError']
}
}
},
error_loading_timeline: {
on: {
RETRY: 'loading_timeline',
DATE_CHANGE: {
target: 'loading_timeline',
actions: ['setHistoric']
}
}
},
viewing_timeline: {
onEntry: ['calcLocalTimelineData'],
on: {
DATE_CHANGE: {
target: 'loading_timeline',
actions: ['setHistoric']
},
PLAN_FINISHED: {
target: 'loading_timeline',
actions: ['showAlertRefetchPlan']
}
}
},
viewing_empty_timeline: {
on: {
DATE_CHANGE: {
target: 'loading_timeline',
actions: ['setHistoric']
}
}
}
}
}
const timelineActionsMachine = {
initial: 'inactive',
states: {
inactive: {
onEntry: ['clearSelectedItem', 'calcLocalTimelineData'],
on: {
SHOW_ACTIONS: { target: 'active', actions: ['setSelectedItem'] }
}
},
active: {
onEntry: ['calcLocalTimelineData'],
on: {
LENGTH_ACTION: { target: 'active', actions: ['lengthAction'] },
BACK_ACTION: { target: 'active', actions: ['backAction'] },
FORWARD_ACTION: { target: 'active', actions: ['fwdAction'] },
UP_ACTION: { target: 'active', actions: ['upAction', 'updateSelectedItemInterval'] },
DOWN_ACTION: { target: 'active', actions: ['downAction', 'updateSelectedItemInterval'] },
APPLY_ACTION: { target: 'submitting', actions: ['submitSelectedItem'] },
CANCEL_ACTION: { target: 'inactive' }
}
},
submitting: {
on: {
SUCCESS: { target: 'inactive', actions: ['updateDbTimelineData', 'updateDbItemPlans', 'clearSelectedItem', 'calcLocalTimelineData'] },
FAILURE: { target: 'inactive', actions: ['showError'] }
}
},
error: {}
}
}
const timelineMachine = Machine({
id: 'timeline',
type: 'parallel',
context: {
dbTimelineData: [], // timeline synced with database
timelineData: [], // local timelineData (may be merged / edited in various ways)
options: {},
selectedItem: null,
originalSelectedItem: null, // allows simple recalculations of operations made to the selected item
resourceCatalog: [],
selectedResource: null,
originalSelectedResource: null,
historicDate: new Date(),
historicProgram: null,
previousProgramDate: null,
excelData: [],
rooms: []
},
states: {
ui: { ...timelineUiMachine },
timelineActions: { ...timelineActionsMachine }
}
}, {
guards: {
isDataValid: (ctx, evt) => true,
isDataOutOfRange: (ctx, evt) => true
},
actions: {
setHistoricProgram: assign({
historicProgram: (ctx, evt) => evt.historicProgram
}),
setPreviousProgramDate: assign({
previousProgramDate: (ctx, evt) => {
const initialDate = new Date(evt.timelineData)
// strip useless time info (keep only date from moment object)
return new Date(initialDate.year(), initialDate.month(), initialDate.date())
}
}),
setHistoric: assign({ historicDate: (ctx, evt) => (evt.date) }),
setDbTimelineData: assign({ dbTimelineData: (ctx, evt) => (evt.timelineData) }),
setSelectedItem: assign({
originalSelectedItem: (ctx, evt) => (
(!ctx.selectedItem || ctx.selectedItem.order !== evt.item.order)
? { ...evt.item }
: null
),
selectedItem: (ctx, evt) => (
(!ctx.selectedItem || ctx.selectedItem.order !== evt.item.order)
? { ...evt.item }
: null
),
selectedResource: (ctx, evt) => (
(!ctx.selectedItem || ctx.selectedItem.order !== evt.item.order)
? evt.resource
: null
),
originalSelectedResource: (ctx, evt) => (
(!ctx.selectedItem || ctx.selectedItem.order !== evt.item.order)
? evt.resource
: null
)
}),
clearSelectedItem: assign({
originalSelectedItem: (ctx, evt) => (null),
selectedItem: (ctx, evt) => (null),
selectedResource: (ctx, evt) => (null),
originalSelectedResource: (ctx, evt) => (null)
}),
setOptions: assign({
options: (ctx, evt) => {
let newOptions = { ...ctx.options, ...evt.options }
return newOptions
}
}),
setResourceCatalog: assign({
resourceCatalog: (ctx, evt) => evt.resourceCatalog
}),
// ! Timeline item actions
lengthAction: assign({
selectedItem: (ctx, evt) => {
const currentExtension = new Date(ctx.selectedItem.endAt)
const newExtension = evt.value
const diff = currentExtension - newExtension
return ({
...ctx.selectedItem,
endAt: new Date(ctx.selectedItem.endAt).toISOString()
})
}
}),
backAction: assign({
selectedItem: (ctx, evt) => ({
...ctx.selectedItem,
startAt: new Date(ctx.selectedItem.startAt).toISOString(),
endAt: new Date(ctx.selectedItem.endAt).toISOString()
})
}),
fwdAction: assign({
selectedItem: (ctx, evt) => ({
...ctx.selectedItem,
startAt: new Date(ctx.selectedItem.startAt).toISOString(),
endAt: new Date(ctx.selectedItem.endAt).toISOString()
})
}),
upAction: assign({
selectedResource: (ctx, evt) => {
// Find SelectedResource index, and move it upward if possible:
const resourceIndex = ctx.dbTimelineData.findIndex((res) => res.name === ctx.selectedResource.name)
const newResource = resourceIndex > 0 ? ctx.dbTimelineData[resourceIndex - 1] : ctx.selectedResource
return newResource
}
}),
downAction: assign({
selectedResource: (ctx, evt) => {
// Find SelectedResource index, and move it upward if possible:
const resourceIndex = ctx.dbTimelineData.findIndex((res) => res.name === ctx.selectedResource.name)
const lastIndex = ctx.dbTimelineData.length - 1
const newResource = resourceIndex < lastIndex ? ctx.dbTimelineData[resourceIndex + 1] : ctx.selectedResource
return newResource
}
}),
updateSelectedItemInterval: assign({
selectedItem: (ctx, evt) => ctx.selectedItem // ! Default, no scaling applied (scaling must be defined in component)
}),
calcLocalTimelineData: assign({
timelineData: (ctx, evt) => {
// No item selected, means nothing to map
if (!ctx.selectedItem) return ctx.dbTimelineData
const path = `${ctx.selectedItem.__type}s` // 'programs' | 'stops'
let localTimelineData = { ...ctx.dbTimelineData };
// * 1. remove selected item from original resouce
let srcResourceData = localTimelineData.find((res) => res.name === ctx.originalSelectedResource.name)
srcResourceData[path].filter((item) => (
ctx.selectedItem.__type === 'stop'
? item.id === ctx.selectedItem.id
: item.order === ctx.selectedItem.order
))
// * 2. re-insert into new location
let dstResourceData = localTimelineData.find((res) => res.name === ctx.selectedResource.name)
dstResourceData[path].push(ctx.selectedItem)
return localTimelineData
}
}),
updateDbTimelineData: assign({
dbTimelineData: (ctx, evt) => cloneDeep(ctx.timelineData)
}),
updateDbItemPlans: assign({
dbTimelineData: (ctx, evt) => ctx.dbTimelineData.map(res => (
// find updated resource and program, and replace its plans with the updated ones
res.name !== ctx.selectedResource.name
? res
: {
...res,
programs: res.programs.map(prog => (
prog.order !== ctx.selectedItem.order
? prog
: {
...prog,
plans: [...evt.updatedPlans]
}
))
}
))
}),
setExcelData: assign({
excelData: (ctx, evt) => evt.excelData
}),
clearExcelData: assign({
excelData: (ctx, evt) => ([])
}),
setRooms: assign({
rooms: (ctx, evt) => evt.rooms
}),
showError: (ctx, evt) => {
let err = evt.error
if (Array.isArray(evt.error) && evt.error.length > 0) {
err = evt.error[0]
} else {
err = evt.error
}
console.log(err)
},
showAlertRefetchPlan: (ctx, evt) => {
console.log('Obteniendo última planeación...')
}
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment