From 4f607eaececa9a4e80dac57c64d0826c845a2cac Mon Sep 17 00:00:00 2001
From: Hiroki Terashima <honchikun@gmail.com>
Date: Thu, 21 Sep 2023 09:54:52 -0700
Subject: [PATCH] refactor(ProjectAuthoringCompoonent): Extract
 ProjectAuthoringParentComponent (#1427)

---
 src/app/teacher/authoring-routing.module.ts   |   4 +-
 src/app/teacher/authoring-tool.module.ts      |   2 +
 .../project-authoring-parent.component.html   |  13 +
 .../project-authoring-parent.component.scss   |  17 +
 .../project-authoring-parent.component.ts     |  25 +
 .../project-authoring.component.html          | 601 +++++++++---------
 .../project-authoring.component.scss          |  18 -
 .../project-authoring.component.spec.ts       |  14 +-
 .../project-authoring.component.ts            |  48 +-
 src/messages.xlf                              |  56 +-
 10 files changed, 392 insertions(+), 406 deletions(-)
 create mode 100644 src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.html
 create mode 100644 src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.scss
 create mode 100644 src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.ts

diff --git a/src/app/teacher/authoring-routing.module.ts b/src/app/teacher/authoring-routing.module.ts
index 74663110805..636da4da99a 100644
--- a/src/app/teacher/authoring-routing.module.ts
+++ b/src/app/teacher/authoring-routing.module.ts
@@ -34,6 +34,7 @@ import { RecoveryAuthoringComponent } from '../../assets/wise5/authoringTool/rec
 import { ChooseImportComponentComponent } from '../../assets/wise5/authoringTool/importComponent/choose-import-component/choose-import-component.component';
 import { ChooseMoveNodeLocationComponent } from '../../assets/wise5/authoringTool/choose-node-location/choose-move-node-location/choose-move-node-location.component';
 import { ChooseCopyNodeLocationComponent } from '../../assets/wise5/authoringTool/choose-node-location/choose-copy-node-location/choose-copy-node-location.component';
+import { ProjectAuthoringParentComponent } from '../../assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component';
 
 const routes: Routes = [
   {
@@ -46,9 +47,10 @@ const routes: Routes = [
       { path: 'new-unit', component: AddProjectComponent },
       {
         path: 'unit/:unitId',
-        component: ProjectAuthoringComponent,
+        component: ProjectAuthoringParentComponent,
         resolve: { project: AuthoringProjectResolver },
         children: [
+          { path: '', component: ProjectAuthoringComponent },
           {
             path: 'add-lesson',
             children: [
diff --git a/src/app/teacher/authoring-tool.module.ts b/src/app/teacher/authoring-tool.module.ts
index cc7f3d66ea0..ead2aea6ae9 100644
--- a/src/app/teacher/authoring-tool.module.ts
+++ b/src/app/teacher/authoring-tool.module.ts
@@ -48,6 +48,7 @@ import { InsertNodeAfterButtonComponent } from '../../assets/wise5/authoringTool
 import { InsertNodeInsideButtonComponent } from '../../assets/wise5/authoringTool/choose-node-location/insert-node-inside-button/insert-node-inside-button.component';
 import { NodeIconAndTitleComponent } from '../../assets/wise5/authoringTool/choose-node-location/node-icon-and-title/node-icon-and-title.component';
 import { NodeWithMoveAfterButtonComponent } from '../../assets/wise5/authoringTool/choose-node-location/node-with-move-after-button/node-with-move-after-button.component';
+import { ProjectAuthoringParentComponent } from '../../assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component';
 
 @NgModule({
   declarations: [
@@ -81,6 +82,7 @@ import { NodeWithMoveAfterButtonComponent } from '../../assets/wise5/authoringTo
     NotebookAuthoringComponent,
     ProjectInfoAuthoringComponent,
     ProjectAuthoringComponent,
+    ProjectAuthoringParentComponent,
     RecoveryAuthoringComponent,
     RequiredErrorLabelComponent,
     RubricAuthoringComponent,
diff --git a/src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.html b/src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.html
new file mode 100644
index 00000000000..68e7f7f4451
--- /dev/null
+++ b/src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.html
@@ -0,0 +1,13 @@
+<div id="top" class="view-content view-content--with-sidemenu">
+  <div class="l-constrained" fxLayout="column">
+    <div class="node-content md-whiteframe-1dp" fxLayout="row wrap">
+      <div class="main-content">
+        <concurrent-authors-message
+          [projectId]="projectId"
+          class="concurrent-authors-message center app-background"
+        ></concurrent-authors-message>
+        <router-outlet></router-outlet>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.scss b/src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.scss
new file mode 100644
index 00000000000..7155e961a66
--- /dev/null
+++ b/src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.scss
@@ -0,0 +1,17 @@
+.node-content {
+  padding: 0px 16px;
+  position: relative;
+}
+
+.main-content {
+  margin-bottom: 20px;
+  width: 100%;
+}
+
+.concurrent-authors-message {
+  position: sticky;
+  top: 1px;
+  padding: 4px 0;
+  display: block;
+  z-index: 3;
+}
diff --git a/src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.ts b/src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.ts
new file mode 100644
index 00000000000..08a74051b27
--- /dev/null
+++ b/src/assets/wise5/authoringTool/project-authoring-parent/project-authoring-parent.component.ts
@@ -0,0 +1,25 @@
+import { Component } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { TeacherProjectService } from '../../services/teacherProjectService';
+
+@Component({
+  templateUrl: './project-authoring-parent.component.html',
+  styleUrls: ['./project-authoring-parent.component.scss']
+})
+export class ProjectAuthoringParentComponent {
+  protected projectId: number;
+
+  constructor(private projectService: TeacherProjectService, private route: ActivatedRoute) {}
+
+  ngOnInit(): void {
+    this.projectId = Number(this.route.snapshot.paramMap.get('unitId'));
+    this.projectService.notifyAuthorProjectBegin(this.projectId);
+    window.onbeforeunload = (event) => {
+      this.projectService.notifyAuthorProjectEnd(this.projectId);
+    };
+  }
+
+  ngOnDestroy(): void {
+    this.projectService.notifyAuthorProjectEnd(this.projectId);
+  }
+}
diff --git a/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html b/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html
index 0c723a9751b..1622181667d 100644
--- a/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html
+++ b/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html
@@ -1,338 +1,305 @@
-<div id="top" class="view-content view-content--with-sidemenu">
-  <div class="l-constrained" fxLayout="column">
-    <div class="node-content md-whiteframe-1dp" fxLayout="row wrap">
-      <div class="main-content">
-        <concurrent-authors-message
-          [projectId]="projectId"
-          class="concurrent-authors-message center app-background"
-        ></concurrent-authors-message>
-        <router-outlet></router-outlet>
-        <div *ngIf="showProjectView">
-          <div class="project-content">
-            <div fxLayout="row wrap" fxLayoutGap="15px">
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="goBackToProjectList()"
-                matTooltip="Back to Unit List"
-                matTooltipPosition="above"
-                i18n-matTooltip
-              >
-                <mat-icon>arrow_back</mat-icon>
-              </button>
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="createNewLesson()"
-                [disabled]="stepNodeSelected || groupNodeSelected"
-                matTooltip="Create New Lesson"
-                matTooltipPosition="above"
-                i18n-matTooltip
-              >
-                <mat-icon>queue</mat-icon>
-              </button>
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="createNewStep()"
-                [disabled]="stepNodeSelected || groupNodeSelected"
-                matTooltip="Create New Step"
-                matTooltipPosition="above"
-                i18n-matTooltip
-              >
-                <mat-icon>add_box</mat-icon>
-              </button>
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="addStructure()"
-                [disabled]="stepNodeSelected || groupNodeSelected"
-                matTooltip="Add Lesson Structure"
-                matTooltipPosition="above"
-                i18n-matTooltip
-              >
-                <mat-icon>widgets</mat-icon>
-              </button>
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="importStep()"
-                [disabled]="stepNodeSelected || groupNodeSelected"
-                matTooltip="Import Step"
-                matTooltipPosition="above"
-                i18n-matTooltip
-              >
-                <mat-icon>system_update_alt</mat-icon>
-              </button>
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="chooseLocation(false)"
-                [disabled]="!hasSelectedNodes()"
-                matTooltip="Move"
-                matTooltipPosition="above"
-                i18n-matTooltip
-              >
-                <mat-icon>redo</mat-icon>
-              </button>
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="chooseLocation(true)"
-                [disabled]="!hasSelectedStepsOnly()"
-                matTooltip="Copy"
-                matTooltipPosition="above"
+<div class="project-content">
+  <div fxLayout="row wrap" fxLayoutGap="15px">
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="goBackToProjectList()"
+      matTooltip="Back to Unit List"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>arrow_back</mat-icon>
+    </button>
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="createNewLesson()"
+      [disabled]="stepNodeSelected || groupNodeSelected"
+      matTooltip="Create New Lesson"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>queue</mat-icon>
+    </button>
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="createNewStep()"
+      [disabled]="stepNodeSelected || groupNodeSelected"
+      matTooltip="Create New Step"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>add_box</mat-icon>
+    </button>
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="addStructure()"
+      [disabled]="stepNodeSelected || groupNodeSelected"
+      matTooltip="Add Lesson Structure"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>widgets</mat-icon>
+    </button>
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="importStep()"
+      [disabled]="stepNodeSelected || groupNodeSelected"
+      matTooltip="Import Step"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>system_update_alt</mat-icon>
+    </button>
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="chooseLocation(false)"
+      [disabled]="!hasSelectedNodes()"
+      matTooltip="Move"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>redo</mat-icon>
+    </button>
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="chooseLocation(true)"
+      [disabled]="!hasSelectedStepsOnly()"
+      matTooltip="Copy"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>content_copy</mat-icon>
+    </button>
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="deleteSelectedNodes()"
+      [disabled]="!hasSelectedNodes()"
+      matTooltip="Delete"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>delete</mat-icon>
+    </button>
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="goToAdvancedAuthoring()"
+      [disabled]="stepNodeSelected || groupNodeSelected"
+      matTooltip="Advanced"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>build</mat-icon>
+    </button>
+    <span fxFlex></span>
+    <button
+      mat-raised-button
+      color="primary"
+      (click)="previewProject()"
+      matTooltip="Preview Unit"
+      matTooltipPosition="above"
+      i18n-matTooltip
+    >
+      <mat-icon>visibility</mat-icon>
+    </button>
+  </div>
+</div>
+<div class="all-nodes-div">
+  <ng-container *ngFor="let item of items">
+    <div *ngIf="item.order !== 0" class="projectItem">
+      <div
+        id="{{ item.key }}"
+        fxLayout="row"
+        fxLayoutAlign="start center"
+        [ngClass]="{
+          groupHeader: isGroupNode(item.key),
+          stepHeader: !isGroupNode(item.key),
+          branchPathStepHeader: isNodeInAnyBranchPath(item.key) && !isGroupNode(item.key)
+        }"
+        [ngStyle]="{ 'background-color': getStepBackgroundColor(item.key) }"
+      >
+        <div>
+          <mat-checkbox
+            color="primary"
+            [(ngModel)]="item.checked"
+            (ngModelChange)="selectNode()"
+            [disabled]="
+              (isGroupNode(item.key) && stepNodeSelected) ||
+              (!isGroupNode(item.key) && groupNodeSelected)
+            "
+            aria-label="{{ getNodePositionById(item.key) }} {{ getNodeTitle(item.key) }}"
+          >
+          </mat-checkbox>
+        </div>
+        <div class="projectItemTitleDiv" (click)="nodeClicked(item.key)">
+          <div class="pointer" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
+            <node-icon [nodeId]="item.key" size="18"></node-icon>
+            <h6 *ngIf="isGroupNode(item.key)">
+              {{ getNodePositionById(item.key) }}:
+              {{ getNodeTitle(item.key) }}
+            </h6>
+            <p *ngIf="!isGroupNode(item.key)" fxLayoutAlign="start center" fxLayoutGap="10px">
+              <span>{{ getNodePositionById(item.key) }}: {{ getNodeTitle(item.key) }}</span>
+              <mat-icon
+                *ngIf="isBranchPoint(item.key)"
+                class="rotate-180"
+                (click)="branchIconClicked(item.key); $event.stopPropagation()"
+                matTooltip="Branch point with {{
+                  getNumberOfBranchPaths(item.key)
+                }} paths based on {{ getBranchCriteriaDescription(item.key) }}"
+                matTooltipPosition="right"
                 i18n-matTooltip
+                >call_split</mat-icon
               >
-                <mat-icon>content_copy</mat-icon>
-              </button>
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="deleteSelectedNodes()"
-                [disabled]="!hasSelectedNodes()"
-                matTooltip="Delete"
-                matTooltipPosition="above"
+              <mat-icon
+                *ngIf="nodeHasConstraint(item.key) && getNumberOfConstraintsOnNode(item.key) == 1"
+                (click)="constraintIconClicked(item.key); $event.stopPropagation()"
+                matTooltip="{{ getNumberOfConstraintsOnNode(item.key) }} Constraint\n\n{{
+                  getConstraintDescriptions(item.key)
+                }}"
+                matTooltipPosition="right"
                 i18n-matTooltip
+                >block</mat-icon
               >
-                <mat-icon>delete</mat-icon>
-              </button>
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="goToAdvancedAuthoring()"
-                [disabled]="stepNodeSelected || groupNodeSelected"
-                matTooltip="Advanced"
-                matTooltipPosition="above"
+              <mat-icon
+                *ngIf="nodeHasConstraint(item.key) && getNumberOfConstraintsOnNode(item.key) > 1"
+                (click)="constraintIconClicked(item.key); $event.stopPropagation()"
+                matTooltip="{{ getNumberOfConstraintsOnNode(item.key) }} Constraints\n\n{{
+                  getConstraintDescriptions(item.key)
+                }}"
+                matTooltipPosition="right"
                 i18n-matTooltip
+                >block</mat-icon
               >
-                <mat-icon>build</mat-icon>
-              </button>
-              <span fxFlex></span>
-              <button
-                mat-raised-button
-                color="primary"
-                (click)="previewProject()"
-                matTooltip="Preview Unit"
-                matTooltipPosition="above"
+              <mat-icon
+                *ngIf="nodeHasRubric(item.key)"
+                matTooltip="Has Rubric"
+                matTooltipPosition="right"
                 i18n-matTooltip
+                >message</mat-icon
               >
-                <mat-icon>visibility</mat-icon>
-              </button>
+            </p>
+          </div>
+        </div>
+      </div>
+    </div>
+  </ng-container>
+  <div>
+    <h6 class="unused-header">Unused Lessons</h6>
+    <div *ngIf="getNumberOfInactiveGroups() === 0" i18n>There are no Unused Lessons</div>
+    <ng-container *ngFor="let inactiveNode of inactiveGroupNodes">
+      <div *ngIf="isGroupNode(inactiveNode.id)" id="{{ inactiveNode.id }}">
+        <div
+          fxLayout="row"
+          fxLayoutAlign="start center"
+          [ngClass]="{
+            groupHeader: isGroupNode(inactiveNode.id),
+            stepHeader: !isGroupNode(inactiveNode.id),
+            branchPathStepHeader:
+              isNodeInAnyBranchPath(inactiveNode.id) && !isGroupNode(inactiveNode.id)
+          }"
+          class="projectItem"
+        >
+          <div>
+            <mat-checkbox
+              color="primary"
+              [(ngModel)]="inactiveNode.checked"
+              (ngModelChange)="selectNode()"
+              [disabled]="stepNodeSelected"
+              aria-label="{{ getNodeTitle(inactiveNode.id) }}"
+            >
+            </mat-checkbox>
+          </div>
+          <div class="projectItemTitleDiv" (click)="nodeClicked(inactiveNode.id)">
+            <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
+              <node-icon [nodeId]="inactiveNode.id" size="18"></node-icon>&nbsp;
+              <h6 class="pointer">
+                {{ getNodeTitle(inactiveNode.id) }}
+              </h6>
             </div>
           </div>
-          <div class="all-nodes-div">
-            <ng-container *ngFor="let item of items">
-              <div *ngIf="item.order !== 0" class="projectItem">
-                <div
-                  id="{{ item.key }}"
-                  fxLayout="row"
-                  fxLayoutAlign="start center"
-                  [ngClass]="{
-                    groupHeader: isGroupNode(item.key),
-                    stepHeader: !isGroupNode(item.key),
-                    branchPathStepHeader: isNodeInAnyBranchPath(item.key) && !isGroupNode(item.key)
-                  }"
-                  [ngStyle]="{ 'background-color': getStepBackgroundColor(item.key) }"
+        </div>
+        <ng-container *ngFor="let inactiveChildId of inactiveNode.ids">
+          <div class="projectItem" id="{{ inactiveChildId }}">
+            <div
+              fxLayout="row"
+              [ngClass]="{
+                groupHeader: isGroupNode(inactiveChildId.id),
+                stepHeader: !isGroupNode(inactiveChildId.id),
+                branchPathStepHeader:
+                  isNodeInAnyBranchPath(inactiveChildId.id) && !isGroupNode(inactiveChildId.id)
+              }"
+              class="projectItem"
+            >
+              <div>
+                <mat-checkbox
+                  color="primary"
+                  [(ngModel)]="idToNode[inactiveChildId].checked"
+                  (ngModelChange)="selectNode()"
+                  [disabled]="groupNodeSelected"
+                  aria-label="{{ getNodeTitle(inactiveChildId) }}"
                 >
-                  <div>
-                    <mat-checkbox
-                      color="primary"
-                      [(ngModel)]="item.checked"
-                      (ngModelChange)="selectNode()"
-                      [disabled]="
-                        (isGroupNode(item.key) && stepNodeSelected) ||
-                        (!isGroupNode(item.key) && groupNodeSelected)
-                      "
-                      aria-label="{{ getNodePositionById(item.key) }} {{ getNodeTitle(item.key) }}"
-                    >
-                    </mat-checkbox>
-                  </div>
-                  <div class="projectItemTitleDiv" (click)="nodeClicked(item.key)">
-                    <div
-                      class="pointer"
-                      fxLayout="row"
-                      fxLayoutAlign="start center"
-                      fxLayoutGap="10px"
-                    >
-                      <node-icon [nodeId]="item.key" size="18"></node-icon>
-                      <h6 *ngIf="isGroupNode(item.key)">
-                        {{ getNodePositionById(item.key) }}:
-                        {{ getNodeTitle(item.key) }}
-                      </h6>
-                      <p
-                        *ngIf="!isGroupNode(item.key)"
-                        fxLayoutAlign="start center"
-                        fxLayoutGap="10px"
-                      >
-                        <span
-                          >{{ getNodePositionById(item.key) }}: {{ getNodeTitle(item.key) }}</span
-                        >
-                        <mat-icon
-                          *ngIf="isBranchPoint(item.key)"
-                          class="rotate-180"
-                          (click)="branchIconClicked(item.key); $event.stopPropagation()"
-                          matTooltip="Branch point with {{
-                            getNumberOfBranchPaths(item.key)
-                          }} paths based on {{ getBranchCriteriaDescription(item.key) }}"
-                          matTooltipPosition="right"
-                          i18n-matTooltip
-                          >call_split</mat-icon
-                        >
-                        <mat-icon
-                          *ngIf="
-                            nodeHasConstraint(item.key) &&
-                            getNumberOfConstraintsOnNode(item.key) == 1
-                          "
-                          (click)="constraintIconClicked(item.key); $event.stopPropagation()"
-                          matTooltip="{{ getNumberOfConstraintsOnNode(item.key) }} Constraint\n\n{{
-                            getConstraintDescriptions(item.key)
-                          }}"
-                          matTooltipPosition="right"
-                          i18n-matTooltip
-                          >block</mat-icon
-                        >
-                        <mat-icon
-                          *ngIf="
-                            nodeHasConstraint(item.key) &&
-                            getNumberOfConstraintsOnNode(item.key) > 1
-                          "
-                          (click)="constraintIconClicked(item.key); $event.stopPropagation()"
-                          matTooltip="{{ getNumberOfConstraintsOnNode(item.key) }} Constraints\n\n{{
-                            getConstraintDescriptions(item.key)
-                          }}"
-                          matTooltipPosition="right"
-                          i18n-matTooltip
-                          >block</mat-icon
-                        >
-                        <mat-icon
-                          *ngIf="nodeHasRubric(item.key)"
-                          matTooltip="Has Rubric"
-                          matTooltipPosition="right"
-                          i18n-matTooltip
-                          >message</mat-icon
-                        >
-                      </p>
-                    </div>
-                  </div>
+                </mat-checkbox>
+              </div>
+              <div class="projectItemTitleDiv" (click)="nodeClicked(inactiveChildId)">
+                <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
+                  <node-icon [nodeId]="inactiveChildId" size="18"></node-icon>&nbsp;
+                  <p class="pointer">
+                    {{ getNodeTitle(inactiveChildId) }}
+                  </p>
                 </div>
               </div>
-            </ng-container>
+            </div>
+          </div>
+        </ng-container>
+      </div>
+    </ng-container>
+    <div>
+      <h6 class="unused-header" i18n>Unused Steps</h6>
+      <div *ngIf="getNumberOfInactiveSteps() === 0" i18n>There are no Unused Steps</div>
+      <ng-container *ngFor="let inactiveNode of inactiveStepNodes">
+        <div
+          *ngIf="!isGroupNode(inactiveNode.id) && getParentGroup(inactiveNode.id) == null"
+          class="projectItem"
+        >
+          <div
+            fxLayout="row"
+            [ngClass]="{
+              groupHeader: isGroupNode(inactiveNode.id),
+              stepHeader: !isGroupNode(inactiveNode.id),
+              branchPathStepHeader:
+                isNodeInAnyBranchPath(inactiveNode.id) && !isGroupNode(inactiveNode.id)
+            }"
+          >
             <div>
-              <h6 class="unused-header">Unused Lessons</h6>
-              <div *ngIf="getNumberOfInactiveGroups() === 0" i18n>There are no Unused Lessons</div>
-              <ng-container *ngFor="let inactiveNode of inactiveGroupNodes">
-                <div *ngIf="isGroupNode(inactiveNode.id)" id="{{ inactiveNode.id }}">
-                  <div
-                    fxLayout="row"
-                    fxLayoutAlign="start center"
-                    [ngClass]="{
-                      groupHeader: isGroupNode(inactiveNode.id),
-                      stepHeader: !isGroupNode(inactiveNode.id),
-                      branchPathStepHeader:
-                        isNodeInAnyBranchPath(inactiveNode.id) && !isGroupNode(inactiveNode.id)
-                    }"
-                    class="projectItem"
-                  >
-                    <div>
-                      <mat-checkbox
-                        color="primary"
-                        [(ngModel)]="inactiveNode.checked"
-                        (ngModelChange)="selectNode()"
-                        [disabled]="stepNodeSelected"
-                        aria-label="{{ getNodeTitle(inactiveNode.id) }}"
-                      >
-                      </mat-checkbox>
-                    </div>
-                    <div class="projectItemTitleDiv" (click)="nodeClicked(inactiveNode.id)">
-                      <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
-                        <node-icon [nodeId]="inactiveNode.id" size="18"></node-icon>&nbsp;
-                        <h6 class="pointer">
-                          {{ getNodeTitle(inactiveNode.id) }}
-                        </h6>
-                      </div>
-                    </div>
-                  </div>
-                  <ng-container *ngFor="let inactiveChildId of inactiveNode.ids">
-                    <div class="projectItem" id="{{ inactiveChildId }}">
-                      <div
-                        fxLayout="row"
-                        [ngClass]="{
-                          groupHeader: isGroupNode(inactiveChildId.id),
-                          stepHeader: !isGroupNode(inactiveChildId.id),
-                          branchPathStepHeader:
-                            isNodeInAnyBranchPath(inactiveChildId.id) &&
-                            !isGroupNode(inactiveChildId.id)
-                        }"
-                        class="projectItem"
-                      >
-                        <div>
-                          <mat-checkbox
-                            color="primary"
-                            [(ngModel)]="idToNode[inactiveChildId].checked"
-                            (ngModelChange)="selectNode()"
-                            [disabled]="groupNodeSelected"
-                            aria-label="{{ getNodeTitle(inactiveChildId) }}"
-                          >
-                          </mat-checkbox>
-                        </div>
-                        <div class="projectItemTitleDiv" (click)="nodeClicked(inactiveChildId)">
-                          <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
-                            <node-icon [nodeId]="inactiveChildId" size="18"></node-icon>&nbsp;
-                            <p class="pointer">
-                              {{ getNodeTitle(inactiveChildId) }}
-                            </p>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  </ng-container>
-                </div>
-              </ng-container>
-              <div>
-                <h6 class="unused-header" i18n>Unused Steps</h6>
-                <div *ngIf="getNumberOfInactiveSteps() === 0" i18n>There are no Unused Steps</div>
-                <ng-container *ngFor="let inactiveNode of inactiveStepNodes">
-                  <div
-                    *ngIf="!isGroupNode(inactiveNode.id) && getParentGroup(inactiveNode.id) == null"
-                    class="projectItem"
-                  >
-                    <div
-                      fxLayout="row"
-                      [ngClass]="{
-                        groupHeader: isGroupNode(inactiveNode.id),
-                        stepHeader: !isGroupNode(inactiveNode.id),
-                        branchPathStepHeader:
-                          isNodeInAnyBranchPath(inactiveNode.id) && !isGroupNode(inactiveNode.id)
-                      }"
-                    >
-                      <div>
-                        <mat-checkbox
-                          color="primary"
-                          [(ngModel)]="inactiveNode.checked"
-                          (ngModelChange)="selectNode()"
-                          [disabled]="groupNodeSelected"
-                          aria-label="{{ getNodeTitle(inactiveNode.id) }}"
-                        >
-                        </mat-checkbox>
-                      </div>
-                      <div class="projectItemTitleDiv" (click)="nodeClicked(inactiveNode.id)">
-                        <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
-                          <node-icon [nodeId]="inactiveNode.id" size="18"></node-icon>
-                          <p class="pointer">
-                            {{ getNodeTitle(inactiveNode.id) }}
-                          </p>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </ng-container>
+              <mat-checkbox
+                color="primary"
+                [(ngModel)]="inactiveNode.checked"
+                (ngModelChange)="selectNode()"
+                [disabled]="groupNodeSelected"
+                aria-label="{{ getNodeTitle(inactiveNode.id) }}"
+              >
+              </mat-checkbox>
+            </div>
+            <div class="projectItemTitleDiv" (click)="nodeClicked(inactiveNode.id)">
+              <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px">
+                <node-icon [nodeId]="inactiveNode.id" size="18"></node-icon>
+                <p class="pointer">
+                  {{ getNodeTitle(inactiveNode.id) }}
+                </p>
               </div>
             </div>
           </div>
-          <div id="bottom"></div>
         </div>
-      </div>
+      </ng-container>
     </div>
   </div>
 </div>
+<div id="bottom"></div>
diff --git a/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.scss b/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.scss
index 3b6df0840ba..13e78e75400 100644
--- a/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.scss
+++ b/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.scss
@@ -1,21 +1,3 @@
-.node-content {
-  padding: 0px 16px;
-  position: relative;
-}
-
-.main-content {
-  margin-bottom: 20px;
-  width: 100%;
-}
-
-.concurrent-authors-message {
-  position: sticky;
-  top: 1px;
-  padding: 4px 0;
-  display: block;
-  z-index: 3;
-}
-
 .project-content {
   background-color: white;
   margin-bottom: 15px;
diff --git a/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.spec.ts b/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.spec.ts
index 565aec5a104..ba220142239 100644
--- a/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.spec.ts
+++ b/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.spec.ts
@@ -23,6 +23,7 @@ import { RouterTestingModule } from '@angular/router/testing';
 describe('ProjectAuthoringComponent', () => {
   let component: ProjectAuthoringComponent;
   let fixture: ComponentFixture<ProjectAuthoringComponent>;
+  let projectService: TeacherProjectService;
 
   beforeEach(async () => {
     await TestBed.configureTestingModule({
@@ -53,13 +54,12 @@ describe('ProjectAuthoringComponent', () => {
         TeacherWebSocketService
       ]
     }).compileComponents();
-    spyOn(TestBed.inject(TeacherProjectService), 'getFlattenedProjectAsNodeIds').and.returnValue([
-      'node1'
-    ]);
-    spyOn(TestBed.inject(TeacherProjectService), 'getNodeById').and.returnValue({
-      title: 'Step 1'
-    });
-    spyOn(TestBed.inject(TeacherProjectService), 'getInactiveNodes').and.returnValue([]);
+    projectService = TestBed.inject(TeacherProjectService);
+    spyOn(projectService, 'parseProject').and.callFake(() => {});
+    spyOn(projectService, 'getFlattenedProjectAsNodeIds').and.returnValue(['node1']);
+    spyOn(projectService, 'getNodeById').and.returnValue({ title: 'Step 1' });
+    spyOn(projectService, 'getInactiveNodes').and.returnValue([]);
+    window.history.pushState({}, '', '');
     fixture = TestBed.createComponent(ProjectAuthoringComponent);
     component = fixture.componentInstance;
     fixture.detectChanges();
diff --git a/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.ts b/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.ts
index 3814e2de197..dffaa1fd4b4 100644
--- a/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.ts
+++ b/src/assets/wise5/authoringTool/project-authoring/project-authoring.component.ts
@@ -4,9 +4,9 @@ import { DeleteNodeService } from '../../services/deleteNodeService';
 import { TeacherProjectService } from '../../services/teacherProjectService';
 import { TeacherDataService } from '../../services/teacherDataService';
 import * as $ from 'jquery';
-import { Subscription, filter } from 'rxjs';
+import { Subscription } from 'rxjs';
 import { temporarilyHighlightElement } from '../../common/dom/dom';
-import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 
 @Component({
   selector: 'project-authoring',
@@ -21,7 +21,6 @@ export class ProjectAuthoringComponent {
   protected inactiveStepNodes: any[];
   protected items: any;
   protected projectId: number;
-  protected showProjectView: boolean = true;
   protected stepNodeSelected: boolean = false;
   private subscriptions: Subscription = new Subscription();
 
@@ -32,42 +31,31 @@ export class ProjectAuthoringComponent {
     private dataService: TeacherDataService,
     private route: ActivatedRoute,
     private router: Router
-  ) {
-    this.subscriptions.add(
-      this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
-        this.updateShowProjectView();
-        this.temporarilyHighlightNewNodes(history.state.newNodes);
-      })
-    );
-  }
+  ) {}
 
   ngOnInit(): void {
-    this.updateShowProjectView();
     this.projectId = Number(this.route.snapshot.paramMap.get('unitId'));
-    this.items = this.projectService.getNodesInOrder();
-    this.inactiveGroupNodes = this.projectService.getInactiveGroupNodes();
-    this.inactiveStepNodes = this.projectService.getInactiveStepNodes();
-    this.inactiveNodes = this.projectService.getInactiveNodes();
-    this.idToNode = this.projectService.getIdToNode();
-    this.projectService.notifyAuthorProjectBegin(this.projectId);
+    this.refreshProject();
+    this.temporarilyHighlightNewNodes(history.state.newNodes);
     this.subscriptions.add(
       this.projectService.refreshProject$.subscribe(() => {
         this.refreshProject();
       })
     );
-
-    window.onbeforeunload = (event) => {
-      this.projectService.notifyAuthorProjectEnd(this.projectId);
-    };
   }
 
   ngOnDestroy(): void {
-    this.projectService.notifyAuthorProjectEnd(this.projectId);
     this.subscriptions.unsubscribe();
   }
 
-  private updateShowProjectView(): void {
-    this.showProjectView = /\/teacher\/edit\/unit\/(\d*)$/.test(this.router.url);
+  private refreshProject(): void {
+    this.projectService.parseProject();
+    this.items = this.projectService.getNodesInOrder();
+    this.inactiveGroupNodes = this.projectService.getInactiveGroupNodes();
+    this.inactiveStepNodes = this.projectService.getInactiveStepNodes();
+    this.inactiveNodes = this.projectService.getInactiveNodes();
+    this.idToNode = this.projectService.getIdToNode();
+    this.unselectAllItems();
   }
 
   protected previewProject(): void {
@@ -164,16 +152,6 @@ export class ProjectAuthoringComponent {
     this.router.navigate([`/teacher/edit/unit/${this.projectId}/structure/choose`]);
   }
 
-  private refreshProject(): void {
-    this.projectService.parseProject();
-    this.items = this.projectService.getNodesInOrder();
-    this.inactiveGroupNodes = this.projectService.getInactiveGroupNodes();
-    this.inactiveStepNodes = this.projectService.getInactiveStepNodes();
-    this.inactiveNodes = this.projectService.getInactiveNodes();
-    this.idToNode = this.projectService.getIdToNode();
-    this.unselectAllItems();
-  }
-
   protected importStep(): void {
     this.router.navigate([`/teacher/edit/unit/${this.projectId}/import-step/choose-step`]);
   }
diff --git a/src/messages.xlf b/src/messages.xlf
index 75c26056447..b73a239a5cb 100644
--- a/src/messages.xlf
+++ b/src/messages.xlf
@@ -697,7 +697,7 @@
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">94</context>
+          <context context-type="linenumber">84</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/components/animation/animation-authoring/animation-authoring.component.html</context>
@@ -1479,7 +1479,7 @@ Click &quot;Cancel&quot; to keep the invalid JSON open so you can fix it.</sourc
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">116</context>
+          <context context-type="linenumber">106</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/top-bar/top-bar.component.html</context>
@@ -5309,7 +5309,7 @@ Click &quot;Cancel&quot; to keep the invalid JSON open so you can fix it.</sourc
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">83</context>
+          <context context-type="linenumber">73</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-list/project-list.component.html</context>
@@ -9700,7 +9700,7 @@ Click &quot;Cancel&quot; to keep the invalid JSON open so you can fix it.</sourc
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">223</context>
+          <context context-type="linenumber">196</context>
         </context-group>
       </trans-unit>
       <trans-unit id="a10dba8eb1f22a5f90f90e79b8305963eec3ee38" datatype="html">
@@ -9715,7 +9715,7 @@ Click &quot;Cancel&quot; to keep the invalid JSON open so you can fix it.</sourc
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">293</context>
+          <context context-type="linenumber">265</context>
         </context-group>
       </trans-unit>
       <trans-unit id="0217500199a3e48a7b95762b14b79dad49e76beb" datatype="html">
@@ -9730,7 +9730,7 @@ Click &quot;Cancel&quot; to keep the invalid JSON open so you can fix it.</sourc
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">294</context>
+          <context context-type="linenumber">266</context>
         </context-group>
       </trans-unit>
       <trans-unit id="f951d80b53d5536f79142285e8ba09f190a3c723" datatype="html">
@@ -11486,7 +11486,7 @@ Click &quot;Cancel&quot; to keep the invalid JSON open so you can fix it.</sourc
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">105</context>
+          <context context-type="linenumber">95</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4f26c54d5c3edadf92756d7d3ce466dde1f01e2a" datatype="html">
@@ -12114,90 +12114,90 @@ Click &quot;Cancel&quot; to keep the invalid JSON open so you can fix it.</sourc
         <source>Back to Unit List</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">17</context>
+          <context context-type="linenumber">7</context>
         </context-group>
       </trans-unit>
       <trans-unit id="bcf0ed433ab4782db0764ae24fb015e22520f41f" datatype="html">
         <source>Create New Lesson</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">28</context>
+          <context context-type="linenumber">18</context>
         </context-group>
       </trans-unit>
       <trans-unit id="93c7d1ec242f9b1551ee0b4ac7dc133dbeab61cf" datatype="html">
         <source>Create New Step</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">39</context>
+          <context context-type="linenumber">29</context>
         </context-group>
       </trans-unit>
       <trans-unit id="2b3f9a010395471098728a566cd3849bed9f3301" datatype="html">
         <source>Add Lesson Structure</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">50</context>
+          <context context-type="linenumber">40</context>
         </context-group>
       </trans-unit>
       <trans-unit id="3538ada3bf07e331bafa52cf68b5862c1670a695" datatype="html">
         <source>Import Step</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">61</context>
+          <context context-type="linenumber">51</context>
         </context-group>
       </trans-unit>
       <trans-unit id="57cf305f9bfd681c70dc26e914108d06384ade9f" datatype="html">
         <source>Move</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">72</context>
+          <context context-type="linenumber">62</context>
         </context-group>
       </trans-unit>
-      <trans-unit id="ba598ed596aebc49e193351062110e4d64897ce2" datatype="html">
+      <trans-unit id="2babf1354d25bf42fc198b512e41efe699029adc" datatype="html">
         <source>Branch point with <x id="INTERPOLATION" equiv-text="{{
-                            getNumberOfBranchPaths(item.key)
-                          }}"/> paths based on <x id="INTERPOLATION_1" equiv-text="{{ getBranchCriteriaDescription(item.key) }}"/></source>
+                  getNumberOfBranchPaths(item.key)
+                }}"/> paths based on <x id="INTERPOLATION_1" equiv-text="{{ getBranchCriteriaDescription(item.key) }}"/></source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">175,177</context>
+          <context context-type="linenumber">154,156</context>
         </context-group>
       </trans-unit>
-      <trans-unit id="d2ccd4d6c7f329e359959ef05401ceb24408b711" datatype="html">
+      <trans-unit id="30e48681d6264197efea8455adfefd170a75e87f" datatype="html">
         <source><x id="INTERPOLATION" equiv-text="{{ getNumberOfConstraintsOnNode(item.key) }}"/> Constraint\n\n<x id="INTERPOLATION_1" equiv-text="{{
-                            getConstraintDescriptions(item.key)
-                          }}"/></source>
+                  getConstraintDescriptions(item.key)
+                }}"/></source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">188,190</context>
+          <context context-type="linenumber">164,166</context>
         </context-group>
       </trans-unit>
-      <trans-unit id="97508c2924a55e395236962196cecae6e0900e86" datatype="html">
+      <trans-unit id="352c3d81453aabe6f46a08ce422003e76f85f041" datatype="html">
         <source><x id="INTERPOLATION" equiv-text="{{ getNumberOfConstraintsOnNode(item.key) }}"/> Constraints\n\n<x id="INTERPOLATION_1" equiv-text="{{
-                            getConstraintDescriptions(item.key)
-                          }}"/></source>
+                  getConstraintDescriptions(item.key)
+                }}"/></source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">201,203</context>
+          <context context-type="linenumber">174,176</context>
         </context-group>
       </trans-unit>
       <trans-unit id="b3b4847175a6193afa85eb431bbe16d1f04470fa" datatype="html">
         <source>Has Rubric</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.html</context>
-          <context context-type="linenumber">210</context>
+          <context context-type="linenumber">183</context>
         </context-group>
       </trans-unit>
       <trans-unit id="791981839110791639" datatype="html">
         <source>Are you sure you want to delete the selected item?</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.ts</context>
-          <context context-type="linenumber">117</context>
+          <context context-type="linenumber">95</context>
         </context-group>
       </trans-unit>
       <trans-unit id="1189930234736223663" datatype="html">
         <source>Are you sure you want to delete the <x id="PH" equiv-text="selectedNodeIds.length"/> selected items?</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/assets/wise5/authoringTool/project-authoring/project-authoring.component.ts</context>
-          <context context-type="linenumber">118</context>
+          <context context-type="linenumber">96</context>
         </context-group>
       </trans-unit>
       <trans-unit id="e8fb2ceb6f8d4c3e90a6a688e9024461e67f44f0" datatype="html">