| 
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -29,7 +29,7 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          v-if="newOffersToUser.length > 0" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          :icon="showOffersDetails ? 'chevron-down' : 'chevron-right'" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          class="cursor-pointer ml-4 mr-4 text-lg" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          @click="expandOffersToUserAndMarkRead()" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          @click.prevent="expandOffersToUserAndMarkRead()" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        /> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      <a class="text-blue-500 cursor-pointer" @click="handleSeeAllOffersToUser"> | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -48,7 +48,7 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            didInfo(offer.offeredByDid, activeDid, allMyDids, allContacts) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }}</span> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          offered | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <span v-if="offer.objectDescription">{{ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <span v-if="offer.objectDescription" class="truncate">{{ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            offer.objectDescription | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }}</span | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          >{{ offer.objectDescription && offer.amount ? ", and " : "" }} | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -67,10 +67,10 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <!-- New line that appears on hover or when the offer is clicked --> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <div | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            class="absolute left-0 w-full text-left text-gray-500 text-sm hidden group-hover:flex cursor-pointer items-center" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            @click="markOffersAsReadStartingWith(offer.jwtId)" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            @click.prevent="markOffersAsReadStartingWith(offer.jwtId)" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            <span class="inline-block w-8 h-px bg-gray-500 mr-2" /> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            Click to keep all above as new offers | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            Click to keep all above as unread offers | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        </li> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      </ul> | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -96,7 +96,7 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            showOffersToUserProjectsDetails ? 'chevron-down' : 'chevron-right' | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          " | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          class="cursor-pointer ml-4 mr-4 text-lg" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          @click="expandOffersToUserProjectsAndMarkRead()" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          @click.prevent="expandOffersToUserProjectsAndMarkRead()" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        /> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      <a | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -118,7 +118,7 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            didInfo(offer.offeredByDid, activeDid, allMyDids, allContacts) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }}</span> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          offered | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <span v-if="offer.objectDescription">{{ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <span v-if="offer.objectDescription" class="truncate">{{ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            offer.objectDescription | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }}</span | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          >{{ offer.objectDescription && offer.amount ? ", and " : "" }} | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -139,10 +139,153 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <!-- New line that appears on hover --> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <div | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            class="absolute left-0 w-full text-left text-gray-500 text-sm hidden group-hover:flex cursor-pointer items-center" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            @click="markOffersToUserProjectsAsReadStartingWith(offer.jwtId)" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            @click.prevent=" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              markOffersToUserProjectsAsReadStartingWith(offer.jwtId) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            " | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            <span class="inline-block w-8 h-px bg-gray-500 mr-2" /> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            Click to keep all above as new offers | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            Click to keep all above as unread offers | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        </li> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      </ul> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    <!-- Starred Projects with Changes Section --> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    <div | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      class="flex justify-between mt-6" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      data-testId="showStarredProjectChanges" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      <div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        <span class="text-lg font-medium" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          >{{ newStarredProjectChanges.length | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }}{{ newStarredProjectChangesHitLimit ? "+" : "" }}</span | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        <span class="text-lg font-medium ml-4" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          >Favorite Project{{ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            newStarredProjectChanges.length === 1 ? "" : "s" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          With Changes</span | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        <font-awesome | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          v-if="newStarredProjectChanges.length > 0" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          :icon=" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            showStarredProjectChangesDetails ? 'chevron-down' : 'chevron-right' | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          " | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          class="cursor-pointer ml-4 mr-4 text-lg" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          @click.prevent="expandStarredProjectChangesAndMarkRead()" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        /> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    <div v-if="showStarredProjectChangesDetails" class="ml-4 mt-4"> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      <ul class="list-disc ml-4"> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        <li | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          v-for="projectChange in newStarredProjectChanges" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          :key="projectChange.plan.handleId" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          class="mt-4 relative group" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <div class="flex items-center gap-2"> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            <div class="flex-1 min-w-0"> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              <span class="font-medium">{{ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                projectChange.plan.name || "Unnamed Project" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              }}</span> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              <span | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                v-if="projectChange.plan.description" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                class="text-gray-600 block truncate" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                {{ projectChange.plan.description }} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              </span> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            <router-link | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              :to="{ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                path: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                  '/project/' + encodeURIComponent(projectChange.plan.handleId), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              }" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              class="text-blue-500 flex-shrink-0" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              <font-awesome | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                icon="file-lines" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                class="text-blue-500 cursor-pointer" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              /> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            </router-link> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <!-- Show what changed --> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <div | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            v-if="getPlanDifferences(projectChange.plan.handleId)" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            class="text-sm mt-2" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            <div class="font-medium mb-2">Changes</div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            <div class="overflow-x-auto"> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              <table | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                class="w-full text-xs border-collapse border border-gray-300 rounded-lg shadow-sm bg-white" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                <thead> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                  <tr class="bg-gray-50"> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    <th | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      class="border border-gray-300 px-3 py-2 text-left font-semibold text-gray-700" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    ></th> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    <th | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      class="border border-gray-300 px-3 py-2 text-left font-semibold text-gray-700" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      Previous | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    </th> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    <th | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      class="border border-gray-300 px-3 py-2 text-left font-semibold text-gray-700" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      Current | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    </th> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                  </tr> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                </thead> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                <tbody> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                  <tr | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    v-for="(difference, field) in getPlanDifferences( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      projectChange.plan.handleId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    )" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    :key="field" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    class="hover:bg-gray-50" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                  > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    <td | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      class="border border-gray-300 px-3 py-2 font-medium text-gray-800 break-words" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      {{ getDisplayFieldName(field) }} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    </td> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    <td | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      class="border border-gray-300 px-3 py-2 text-gray-600 break-words align-top" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      <vue-markdown | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                        v-if="field === 'description' && difference.old" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                        :source="formatFieldValue(difference.old)" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                        class="text-sm markdown-content" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      /> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      <span v-else>{{ formatFieldValue(difference.old) }}</span> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    </td> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    <td | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      class="border border-gray-300 px-3 py-2 text-green-700 font-medium break-words align-top" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      <vue-markdown | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                        v-if="field === 'description' && difference.new" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                        :source="formatFieldValue(difference.new)" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                        class="text-sm markdown-content" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      /> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      <span v-else>{{ formatFieldValue(difference.new) }}</span> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                    </td> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                  </tr> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                </tbody> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              </table> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <div v-else>The changes did not affect essential project data.</div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <!-- New line that appears on hover --> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          <div | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            class="absolute left-0 w-full text-left text-gray-500 text-sm hidden group-hover:flex cursor-pointer items-center" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            @click.prevent=" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              markStarredProjectChangesAsReadStartingWith( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                projectChange.plan.jwtId!, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              ) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            " | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          > | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            <span class="inline-block w-8 h-px bg-gray-500 mr-2" /> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            Click to keep all above as unread changes | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          </div> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        </li> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      </ul> | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -152,6 +295,7 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					<script lang="ts"> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { Component, Vue } from "vue-facing-decorator"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import VueMarkdown from "vue-markdown-render"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import GiftedDialog from "../components/GiftedDialog.vue"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import QuickNav from "../components/QuickNav.vue"; | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -162,20 +306,28 @@ import { Router } from "vue-router"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  OfferSummaryRecord, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  OfferToPlanSummaryRecord, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  PlanSummaryAndPreviousClaim, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  PlanSummaryRecord, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} from "../interfaces/records"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  didInfo, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  didInfoOrNobody, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  displayAmount, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  getNewOffersToUser, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  getNewOffersToUserProjects, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  getStarredProjectsWithChanges, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} from "../libs/endorserServer"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { retrieveAccountDids } from "../libs/util"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { logger } from "../utils/logger"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import * as databaseUtil from "../db/databaseUtil"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import * as R from "ramda"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { PlanActionClaim } from "../interfaces/claims"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { GenericCredWrapper } from "@/interfaces"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					@Component({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  components: { GiftedDialog, QuickNav, EntityIcon }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  components: { GiftedDialog, QuickNav, EntityIcon, VueMarkdown }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  mixins: [PlatformServiceMixin], | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					}) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					export default class NewActivityView extends Vue { | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -189,13 +341,22 @@ export default class NewActivityView extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  apiServer = ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  lastAckedOfferToUserJwtId = ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  lastAckedOfferToUserProjectsJwtId = ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  lastAckedStarredPlanChangesJwtId = ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  newOffersToUser: Array<OfferSummaryRecord> = []; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  newOffersToUserHitLimit = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  newOffersToUserProjects: Array<OfferToPlanSummaryRecord> = []; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  newOffersToUserProjectsHitLimit = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  newStarredProjectChanges: Array<PlanSummaryAndPreviousClaim> = []; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  newStarredProjectChangesHitLimit = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  starredPlanHandleIds: Array<string> = []; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  planDifferences: Record< | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    string, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    Record<string, { old: unknown; new: unknown }> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  > = {}; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  showOffersDetails = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  showOffersToUserProjectsDetails = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  showStarredProjectChangesDetails = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  didInfo = didInfo; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  displayAmount = displayAmount; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -214,6 +375,12 @@ export default class NewActivityView extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.lastAckedOfferToUserProjectsJwtId = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        settings.lastAckedOfferToUserProjectsJwtId || ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.lastAckedStarredPlanChangesJwtId = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        settings.lastAckedStarredPlanChangesJwtId || ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.starredPlanHandleIds = databaseUtil.parseJsonField( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        settings.starredPlanHandleIds, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        [], | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.allContacts = await this.$getAllContacts(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -237,6 +404,29 @@ export default class NewActivityView extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.newOffersToUserProjects = offersToUserProjectsData.data; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.newOffersToUserProjectsHitLimit = offersToUserProjectsData.hitLimit; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Load starred project changes if user has starred projects | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (this.starredPlanHandleIds.length > 0) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        try { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          const starredProjectChangesData = await getStarredProjectsWithChanges( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            this.axios, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            this.apiServer, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            this.activeDid, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            this.starredPlanHandleIds, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            this.lastAckedStarredPlanChangesJwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.newStarredProjectChanges = starredProjectChangesData.data; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.newStarredProjectChangesHitLimit = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            starredProjectChangesData.hitLimit; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          // Analyze differences between current plans and previous claims | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.analyzePlanDifferences(this.newStarredProjectChanges); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } catch (error) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          logger.warn("Failed to load starred project changes:", error); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.newStarredProjectChanges = []; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.newStarredProjectChangesHitLimit = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // eslint-disable-next-line @typescript-eslint/no-explicit-any | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } catch (err: any) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      logger.error("Error retrieving settings & contacts:", err); | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -250,13 +440,13 @@ export default class NewActivityView extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  async expandOffersToUserAndMarkRead() { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    this.showOffersDetails = !this.showOffersDetails; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (this.showOffersDetails && this.newOffersToUser.length > 0) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$updateSettings({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$saveUserSettings(this.activeDid, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // note that we don't update this.lastAckedOfferToUserJwtId in case they | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // later choose the last one to keep the offers as new | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.notify.info( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        "The offers are marked as viewed. Click in the list to keep them as new.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        "The offers are marked read. Click in the list to keep them unread.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        TIMEOUTS.LONG, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -268,12 +458,12 @@ export default class NewActivityView extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (index !== -1 && index < this.newOffersToUser.length - 1) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Set to the next offer's jwtId | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$updateSettings({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$saveUserSettings(this.activeDid, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        lastAckedOfferToUserJwtId: this.newOffersToUser[index + 1].jwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } else { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // it's the last entry (or not found), so just keep it the same | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$updateSettings({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$saveUserSettings(this.activeDid, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        lastAckedOfferToUserJwtId: this.lastAckedOfferToUserJwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -290,14 +480,14 @@ export default class NewActivityView extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.showOffersToUserProjectsDetails && | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.newOffersToUserProjects.length > 0 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$updateSettings({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$saveUserSettings(this.activeDid, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        lastAckedOfferToUserProjectsJwtId: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.newOffersToUserProjects[0].jwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // note that we don't update this.lastAckedOfferToUserProjectsJwtId in case | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // they later choose the last one to keep the offers as new | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.notify.info( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        "The offers are marked as viewed. Click in the list to keep them as new.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        "The offers are now marked read. Click in the list to keep them unread.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        TIMEOUTS.LONG, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -309,13 +499,13 @@ export default class NewActivityView extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (index !== -1 && index < this.newOffersToUserProjects.length - 1) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Set to the next offer's jwtId | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$updateSettings({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$saveUserSettings(this.activeDid, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        lastAckedOfferToUserProjectsJwtId: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.newOffersToUserProjects[index + 1].jwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } else { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // it's the last entry (or not found), so just keep it the same | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$updateSettings({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$saveUserSettings(this.activeDid, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        lastAckedOfferToUserProjectsJwtId: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.lastAckedOfferToUserProjectsJwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -333,5 +523,382 @@ export default class NewActivityView extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  async handleSeeAllOffersToUserProjects() { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    this.$router.push("/recent-offers-to-user-projects"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  async expandStarredProjectChangesAndMarkRead() { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    this.showStarredProjectChangesDetails = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      !this.showStarredProjectChangesDetails; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if ( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.showStarredProjectChangesDetails && | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.newStarredProjectChanges.length > 0 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$saveUserSettings(this.activeDid, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        lastAckedStarredPlanChangesJwtId: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.newStarredProjectChanges[0].plan.jwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.notify.info( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        "The starred project changes are now marked read. Click in the list to keep them unread.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        TIMEOUTS.LONG, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  async markStarredProjectChangesAsReadStartingWith(jwtId: string) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const index = this.newStarredProjectChanges.findIndex( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      (change) => change.plan.jwtId === jwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (index !== -1 && index < this.newStarredProjectChanges.length - 1) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Set to the next change's jwtId | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$saveUserSettings(this.activeDid, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        lastAckedStarredPlanChangesJwtId: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.newStarredProjectChanges[index + 1].plan.jwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } else { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // it's the last entry (or not found), so just keep it the same | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      await this.$saveUserSettings(this.activeDid, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        lastAckedStarredPlanChangesJwtId: this.lastAckedStarredPlanChangesJwtId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    this.notify.info( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      "All starred project changes above that line are marked as unread.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      TIMEOUTS.STANDARD, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Analyzes differences between current plans and their previous claims | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Walks through a list of PlanSummaryAndPreviousClaim items and stores the | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * differences between the previous claim and the current plan. This method | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * extracts the claim from the wrappedClaimBefore object and compares relevant | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * fields with the current plan. | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @param planChanges Array of PlanSummaryAndPreviousClaim objects to analyze | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  analyzePlanDifferences(planChanges: Array<PlanSummaryAndPreviousClaim>) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    this.planDifferences = {}; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    for (const planChange of planChanges) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const currentPlan: PlanSummaryRecord = planChange.plan; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const wrappedClaim: GenericCredWrapper<PlanActionClaim> = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        planChange.wrappedClaimBefore; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Extract the actual claim from the wrapped claim | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      let previousClaim: PlanActionClaim; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const embeddedClaim: PlanActionClaim = wrappedClaim.claim; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if ( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        embeddedClaim && | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        typeof embeddedClaim === "object" && | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        "credentialSubject" in embeddedClaim | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // It's a Verifiable Credential | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        previousClaim = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          (embeddedClaim.credentialSubject as PlanActionClaim) || embeddedClaim; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } else { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // It's a direct claim | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        previousClaim = embeddedClaim; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!previousClaim || !currentPlan.handleId) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        continue; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const differences: Record<string, { old: unknown; new: unknown }> = {}; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Compare name | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedOldName = this.normalizeValueForComparison( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        previousClaim.name, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedNewName = this.normalizeValueForComparison( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        currentPlan.name, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!R.equals(normalizedOldName, normalizedNewName)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        differences.name = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          old: previousClaim.name, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          new: currentPlan.name, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Compare description | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedOldDescription = this.normalizeValueForComparison( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        previousClaim.description, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedNewDescription = this.normalizeValueForComparison( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        currentPlan.description, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!R.equals(normalizedOldDescription, normalizedNewDescription)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        differences.description = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          old: previousClaim.description, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          new: currentPlan.description, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Compare location (combine latitude and longitude into one row) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const oldLat = this.normalizeValueForComparison( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        previousClaim.location?.geo?.latitude, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const oldLon = this.normalizeValueForComparison( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        previousClaim.location?.geo?.longitude, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const newLat = this.normalizeValueForComparison(currentPlan.locLat); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const newLon = this.normalizeValueForComparison(currentPlan.locLon); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!R.equals(oldLat, newLat) || !R.equals(oldLon, newLon)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        differences.location = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          old: this.formatLocationValue(oldLat, oldLon, true), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          new: this.formatLocationValue(newLat, newLon, false), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Compare agent (issuer) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const oldAgent = didInfoOrNobody( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        previousClaim.agent?.identifier, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.activeDid, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.allMyDids, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.allContacts, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const newAgent = didInfoOrNobody( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        currentPlan.agentDid, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.activeDid, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.allMyDids, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.allContacts, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedOldAgent = this.normalizeValueForComparison(oldAgent); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedNewAgent = this.normalizeValueForComparison(newAgent); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!R.equals(normalizedOldAgent, normalizedNewAgent)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        differences.agent = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          old: oldAgent, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          new: newAgent, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Compare start time | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const oldStartTime = previousClaim.startTime; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const newStartTime = currentPlan.startTime; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedOldStartTime = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.normalizeDateForComparison(oldStartTime); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedNewStartTime = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.normalizeDateForComparison(newStartTime); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!R.equals(normalizedOldStartTime, normalizedNewStartTime)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        differences.startTime = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          old: oldStartTime, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          new: newStartTime, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Compare end time | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const oldEndTime = previousClaim.endTime; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const newEndTime = currentPlan.endTime; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedOldEndTime = this.normalizeDateForComparison(oldEndTime); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedNewEndTime = this.normalizeDateForComparison(newEndTime); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!R.equals(normalizedOldEndTime, normalizedNewEndTime)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        differences.endTime = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          old: oldEndTime, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          new: newEndTime, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Compare image | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const oldImage = previousClaim.image; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const newImage = currentPlan.image; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedOldImage = this.normalizeValueForComparison(oldImage); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedNewImage = this.normalizeValueForComparison(newImage); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!R.equals(normalizedOldImage, normalizedNewImage)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        differences.image = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          old: oldImage, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          new: newImage, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Compare url | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const oldUrl = previousClaim.url; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const newUrl = currentPlan.url; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedOldUrl = this.normalizeValueForComparison(oldUrl); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const normalizedNewUrl = this.normalizeValueForComparison(newUrl); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!R.equals(normalizedOldUrl, normalizedNewUrl)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        differences.url = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          old: oldUrl, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          new: newUrl, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Store differences if any were found | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!R.isEmpty(differences)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.planDifferences[currentPlan.handleId] = differences; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        logger.debug( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          "[NewActivityView] Plan differences found for", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          currentPlan.handleId, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          differences, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    logger.debug( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      "[NewActivityView] Analyzed", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      planChanges.length, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      "plan changes, found differences in", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      Object.keys(this.planDifferences).length, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      "plans", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Normalizes values for comparison - treats null, undefined, and empty string as equivalent | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @param value The value to normalize | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @returns The normalized value (null for null/undefined/empty, otherwise the original value) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  normalizeValueForComparison<T>(value: T | null | undefined): T | null { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (value === null || value === undefined || value === "") { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return null; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    return value; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Normalizes date values for comparison by converting strings to Date objects | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Returns null for null/undefined/empty values, Date objects for valid date strings | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  normalizeDateForComparison(value: unknown): Date | null { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (value === null || value === undefined || value === "") { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return null; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (typeof value === "string") { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const date = new Date(value); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Check if the date is valid | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return isNaN(date.getTime()) ? null : date; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (value instanceof Date) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return isNaN(value.getTime()) ? null : value; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    return null; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Gets the differences for a specific plan by handle ID | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @param handleId The handle ID of the plan to get differences for | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @returns The differences object or null if no differences found | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  getPlanDifferences( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    handleId: string, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  ): Record<string, { old: unknown; new: unknown }> | null { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    return this.planDifferences[handleId] || null; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Formats a field value for display in the UI | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @param value The value to format | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @returns A human-readable string representation | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  formatFieldValue(value: unknown): string { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (value === null || value === undefined) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return "Not set"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (typeof value === "string") { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const stringValue = value || "Empty"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Check if it's a date/time string | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (this.isDateTimeString(stringValue)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        return this.formatDateTime(stringValue); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Check if it's a URL | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (this.isUrl(stringValue)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        return stringValue; // Keep URLs as-is for now | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return stringValue; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (typeof value === "number") { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return value.toString(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (typeof value === "boolean") { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return value ? "Yes" : "No"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // For complex objects, stringify | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const stringified = JSON.stringify(value); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    return stringified; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Checks if a string appears to be a date/time string | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  isDateTimeString(value: string): boolean { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (!value) return false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // Check for ISO 8601 format or other common date formats | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const dateRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?(\.\d{3})?Z?$/; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    return dateRegex.test(value) || !isNaN(Date.parse(value)); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Checks if a string is a URL | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  isUrl(value: string): boolean { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (!value) return false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    try { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      new URL(value); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return true; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } catch { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Formats a date/time string for display | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  formatDateTime(value: string): string { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    try { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const date = new Date(value); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return date.toLocaleString(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } catch { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return value; // Return original if parsing fails | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Gets a human-readable field name for display | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @param fieldName The internal field name | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @returns A formatted field name for display | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  getDisplayFieldName(fieldName: string): string { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const fieldNameMap: Record<string, string> = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      name: "Name", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      description: "Description", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      location: "Location", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      agent: "Agent", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      startTime: "Start Time", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      endTime: "End Time", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      image: "Image", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      url: "URL", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    return fieldNameMap[fieldName] || fieldName; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  /** | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * Formats location values for display | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @param latitude The latitude value | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @param longitude The longitude value | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @param isOldValue Whether this is the old value (true) or new value (false) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   * @returns A formatted location string | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					   */ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  formatLocationValue( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    latitude: number | undefined | null, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    longitude: number | undefined | null, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    isOldValue: boolean = false, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  ): string { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (latitude == null && longitude == null) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return "Not set"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // If there's any location data, show generic labels instead of coordinates | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    if (isOldValue) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return "A Location"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } else { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      return "New Location"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					</script> | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
					 | 
				
				 | 
				
					
  |