Skip to content

Instantly share code, notes, and snippets.

@AndresRodH
Last active December 15, 2020 13:42
Show Gist options
  • Save AndresRodH/0de38302fe9f566c690e45b441490fef to your computer and use it in GitHub Desktop.
Save AndresRodH/0de38302fe9f566c690e45b441490fef to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
// Available variables:
// - Machine
// - interpret
// - assign
// - send
// - sendParent
// - spawn
// - raise
// - actions
// - XState (all XState exports)
const TaskStatus = {
OPEN: 0,
IN_PROGRESS: 1,
CLOSED: 2
}
const TransitionableState = {
TO_DO: 'TO_DO',
IN_PROGRESS: 'IN_PROGRESS',
COMPLETED: 'COMPLETED',
}
const testEntity = {
// current status of the entity
state: TransitionableState.TO_DO,
// the real date "this" stated
actualStartDate: null,
// the real date "this" was closed
actualStopDate: null,
// in minutes
duration: 10,
// date that this task/session could be done
estimatedDueDate: null,
// the actual time in minutes that took to complete this session/task
totalTimeToComplete: null,
}
const testOnTransition = (newState) => console.log(newState)
const isOpen = (status) => status === 'open' || status === TaskStatus.OPEN
const isInProgress = (status) => status === 'in_progress' || status === TaskStatus.IN_PROGRESS
/**
* @see https://xstate.js.org/viz/?gist=0de38302fe9f566c690e45b441490fef
* Tracks the status of a transitionable entity. This machine:
*
* 1. Manages the respective timestamps per transition
* 2. Calculates estimated due date if there's a duration available
* 3. Keeps track of how much time (in minutes) took to transition from open -> inProgress -> closed
*/
const transitionableEntityMachine = ({
entity,
onTransition,
}) => {
const {
actualStartDate,
actualStopDate,
estimatedDueDate,
duration,
totalTimeToComplete,
state: currentState,
} = entity;
return Machine(
{
id: 'transitionableEntity',
initial: currentState,
context: {
actualStopDate,
actualStartDate,
duration,
estimatedDueDate,
totalTimeToComplete,
},
states: {
[TransitionableState.TO_DO]: {
on: {
SET_IN_PROGRESS: {
target: TransitionableState.IN_PROGRESS,
actions: ['setActualStartDate', 'calculateEstimatedDueDate', 'notifyTransition'],
},
SET_COMPLETED: {
target: TransitionableState.COMPLETED,
actions: ['setActualStopDate', 'notifyTransition'],
},
UPDATE_DURATION: {
actions: ['setDuration'],
},
},
},
[TransitionableState.IN_PROGRESS]: {
on: {
SET_TO_DO: {
target: TransitionableState.TO_DO,
actions: ['clearActualStartDate', 'clearEstimatedDueDate', 'notifyTransition'],
},
SET_COMPLETED: {
target: TransitionableState.COMPLETED,
actions: ['setActualStopDate', 'calculateTotalTime', 'notifyTransition'],
},
UPDATE_DURATION: {
actions: ['setDuration', 'calculateEstimatedDueDate'],
},
},
},
[TransitionableState.COMPLETED]: {
on: {
SET_TO_DO: {
target: TransitionableState.TO_DO,
actions: [
'clearActualStopDate',
'clearActualStartDate',
'clearTotalTimeToComplete',
'clearEstimatedDueDate',
'notifyTransition',
],
},
SET_IN_PROGRESS: {
target: TransitionableState.IN_PROGRESS,
actions: [
'setActualStartDate',
'clearActualStopDate',
'calculateEstimatedDueDate',
'clearTotalTimeToComplete',
'notifyTransition',
],
},
},
},
},
},
{
actions: {
notifyTransition: (_ctx, event) =>
onTransition(
event.type === 'SET_TO_DO'
? TransitionableState.TO_DO
: event.type === 'SET_IN_PROGRESS'
? TransitionableState.IN_PROGRESS
: TransitionableState.COMPLETED
),
setActualStartDate: assign({ actualStartDate: () => new Date() }),
setActualStopDate: assign({ actualStopDate: () => new Date() }),
calculateEstimatedDueDate: assign({
estimatedDueDate: (ctx) =>
ctx.duration && ctx.actualStartDate
? new Date(ctx.actualStartDate.getTime() + ctx.duration * 60000)
: null,
}),
clearActualStartDate: assign({ actualStartDate: () => null }),
clearActualStopDate: assign({ actualStopDate: () => null }),
clearTotalTimeToComplete: assign({ totalTimeToComplete: () => null }),
clearEstimatedDueDate: assign({ estimatedDueDate: () => null }),
calculateTotalTime: assign({
totalTimeToComplete: (ctx) =>
// can only be calculated if there's both start and stop dates
// and sessions and tasks can be transitioned from open to closed directly
ctx.actualStartDate && ctx.actualStopDate
? Math.round((ctx.actualStopDate.getTime() - ctx.actualStartDate.getTime()) / 60000)
: null,
}),
setDuration: assign({
duration: (ctx, event) => {
let duration = ctx.duration;
if (event.type === 'UPDATE_DURATION') duration = event.duration;
return duration;
},
}),
},
}
);
};
transitionableEntityMachine({
entity: testEntity,
onTransition: (state) => {console.log(state)}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment