portal-www
Version:
Nova Portal Website. Based on Next starter by Ueno
245 lines (232 loc) • 6.06 kB
text/typescript
import { Machine, assign } from 'xstate';
import { isPast } from 'date-fns';
type periodInput = {
variables: {
input: {
subscriptionId: string,
periodEnd: Date | null,
},
},
};
interface IPeriodMachineContext {
subscriptionId: string;
selectedDay: Date | null;
nextDueDate: Date | null;
calculatedPeriods: [any] | [];
getPeriods: () => any;
newPeriod: (({ }: periodInput) => any);
calculatePeriods: ({ }: periodInput) => any;
error: string | null;
}
const periodMachine = Machine<IPeriodMachineContext, any>(
{
id: 'periodMachine',
initial: 'initialize',
states: {
initialize: {
invoke: {
id: 'invokeGetPeriods',
src: (context, _) => {
return context.getPeriods();
},
},
on: {
SUCCESS: {
actions: 'setNextDueDate',
target: 'editing',
},
ERROR: {
actions: 'setError',
target: 'error',
},
},
},
editing: {
on: {
CONFIRM: {
target: 'setNewPeriod',
cond: 'canConfirm',
},
'selectedDay.UPDATE': [
{
actions: 'updateSelectedDay',
target: 'calculate',
cond: 'validEdit',
},
{
actions: 'updateSelectedDay',
},
],
},
},
calculate: {
initial: 'calculating',
states: {
calculating: {
invoke: {
id: 'calculatePeriods',
src: (context, _) => {
return context.calculatePeriods({
variables: {
input: {
subscriptionId: context.subscriptionId,
periodEnd: context.selectedDay,
},
},
});
},
onDone: {
actions: 'calculatePeriodsResult',
target: 'done',
},
onError: {
actions: 'setError',
target: '#periodMachine.error',
},
},
},
done: {
always: [
{ target: '#periodMachine.editing', cond: 'notError' },
{ target: '#periodMachine.error', cond: 'isError' },
],
},
},
},
setNewPeriod: {
initial: 'settingNewPeriod',
states: {
settingNewPeriod: {
invoke: {
id: 'setNewPeriodInvoke',
src: (context, _) => {
return context.newPeriod({
variables: {
input: {
subscriptionId: context.subscriptionId,
periodEnd: context.selectedDay,
},
},
});
},
onDone: {
actions: 'setNewPeriodResult',
target: 'done',
},
onError: {
actions: 'setError',
target: '#periodMachine.error',
},
},
},
done: {
always: [
{ target: '#periodMachine.success', cond: 'notError' },
{ target: '#periodMachine.error', cond: 'isError' },
],
},
},
},
success: {
on: {
RESET: {
actions: 'reset',
target: 'initialize',
},
},
},
error: {
on: {
RESET: {
actions: 'reset',
target: 'initialize',
},
},
},
},
},
{
actions: {
updateSelectedDay: assign({
selectedDay: (_, event) => {
return event.value ? event.value : null;
},
}),
setNextDueDate: assign(
(_context, event) => {
const res = ((periods) => {
const newestPeriod = periods?.[0];
if (!newestPeriod || isPast?.(new Date(newestPeriod?.periodEnd))) {
return null;
}
if (newestPeriod?.status === 'Paid') {
return new Date(newestPeriod?.periodEnd);
}
return new Date(newestPeriod?.periodStart);
})(event.value);
return {
nextDueDate: res,
};
},
),
calculatePeriodsResult: assign(
(_context, event) => {
if (event?.data?.data?.calculatePeriods?.data) {
return {
calculatedPeriods: event?.data?.data?.calculatePeriods?.data ?? [],
error: null,
};
}
return {
calculatedPeriods: [],
error: event?.data?.data?.calculatePeriods?.error.message,
};
},
),
setNewPeriodResult: assign(
(_context, event) => {
if (event?.data?.data?.changeSubscriptionPeriod?.error) {
return {
error: event?.data?.data?.changeSubscriptionPeriod?.error?.message,
};
}
return {
error: null,
};
},
),
setError: assign(
(_context, event) => {
const error = event?.data?.message ?? event.value;
return {
error,
};
},
),
reset: assign(
(_context, _event) => {
return {
selectedDay: null,
error: null,
nextDueDate: null,
calculatedPeriods: [],
};
},
),
},
guards: {
validEdit: (_context, event) => {
return !!event.value;
},
canConfirm: (context, _event) => {
return !!context.selectedDay;
},
isError: (context, _event) => {
return !!context.error;
},
notError: (context, _event) => {
return !context.error;
},
},
});
export { periodMachine };