npm install
npm run dev
- After execute above commands, webapp run at
localhost:5173
. - You can test the UI, or open developer tool (
F12
orRight-click
>Inspect
) to watch result in Console tab. Employee ID
is used to define employee you want to move andSupervisor ID
is used to defined his/her new supervisor.
- Classes, interfaces, initial data included in
data
folder - Helper functions included in
utils
folder - Main App component of webapp is in
src/App.tsx
- Main logic app is in
src/data/EmployeeOrgApp.ts
export interface IEmployee {
uniqueId: number;
name: string;
subordinates: IEmployee[];
addChild: (child: IEmployee) => any;
removeChild: (child: IEmployee) => IEmployee[] | undefined;
undoRemoveChild: (child: IEmployee, oldSubordinates: IEmployee[]) => any;
}
export interface IEmployeeOrgApp {
ceo: Employee;
move(employeeId: number, supervisorId: number): void;
undo(): void;
redo(): void;
}
To remove a child(subordinate), there are 3 steps (E.g remove A from B)
- Remove A from B's children
- Move all A's children to B's
- Remove all children of A
Because A's children are moved to B, function returns old subordinates for undoing later.
this.subordinates.splice(index, 1);
this.subordinates.push(...child.subordinates);
const oldSubordinates = child.subordinates.splice(0);
return oldSubordinates;
In the other hand, there are also 3 steps to undo "remove a child" (E.g put A back to B)
- Add old subordinates to A
- Remove above subordinates from B's
- Add A to B's subordinates
child.subordinates.push(...oldSubordinates);
this.subordinates = this.subordinates.filter((value) => oldSubordinates.indexOf(value) < 0);
this.subordinates.push(child);
To handle action history, we use 2 stacks (undoStack
and redoStack
).
type Action = {
employeeId: number;
from: number;
to: number;
oldSubordinates: Employee[];
};
...
private undoStack: Action[] = [];
private redoStack: Action[] = [];
3 operations(move
, undo
and redo
) control those stacks.
- After a
move
, push new action in toundoStack
- After an
undo
, pop top ofundoStack
and push that action intoredoStack
- After a
redo
, pop top ofredoStack
and push that action intoundoStack
To handle a move, there are 4 steps (E.g A move from B to C)
- Find the employee A, old and new supervisors B, C
- Remove A from B
- Add A to C
- Return a new
Action
to handle history
const employee = findEmployeeResult.node;
const oldSupervisor = findEmployeeResult.parent;
const newSupervisor = findSupervisorResult.node;
const oldSubordinates = oldSupervisor.removeChild(employee);
newSupervisor.addChild(employee);
return {
employeeId: employee.uniqueId,
from: oldSupervisor.uniqueId,
to: newSupervisor.uniqueId,
oldSubordinates: oldSubordinates ?? new Array<Employee>()
};
To handle an undo, there are 3 steps (E.g still A, B, C from above example)
- Find the employee A, old and recent supervisors B, C
- Remove A from C
- Undo remove A from B
const employee = findEmployeeResult.node;
const recentSupervisor = findEmployeeResult.parent;
const oldSupervisor = findOldSupervisorResult.node;
recentSupervisor.removeChild(employee);
oldSupervisor.undoRemoveChild(employee, action.oldSubordinates);