From 34c93392ac6d443e85bf31d4ea3f5ac417cabe1a Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Sun, 30 Mar 2025 23:36:55 -0400 Subject: [PATCH 01/12] all results populate now --- .../reports-detail-activities.component.html | 20 ++++ .../reports-detail-activities.component.ts | 74 ++++++++++----- .../reports/reports-detail.component.ts | 95 +++++++++++++++---- 3 files changed, 146 insertions(+), 43 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.html b/src/app/manager-dashboard/reports/reports-detail-activities.component.html index 5eaa1b3e54..8528c8ab79 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.html +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.html @@ -66,6 +66,26 @@ Shared {{element?.shared}} + + Resource ID + {{element?.resourceId}} + + + Course ID + {{element?.courseId}} + + + Time + {{element?.timeFormatted || (element?.time | date:'medium')}} + + + Parent Code + {{element?.parentCode}} + + + Created On + {{element?.createdOn}} + diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts index 557e92ffef..c585e82b5f 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts @@ -53,34 +53,60 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte } ngOnChanges() { - this.matSortActive = this.activityType === 'health' ? 'weekOf' : ''; - this.displayedColumns = columns[this.activityType]; - const filterCourse = (activity: any) => (progress: any) => progress.courseId === activity.courseId; - - if (this.activityType === 'chat') { - this.activities.data = this.activitiesByDoc.map(activity => ({ - ...activity, - createdDate: new Date(activity.createdDate).getTime(), - hasAttachments: activity.context?.resource?.attachments ? 'True' : '', - assistant: activity.assistant ? 'True' : '', - shared: activity.shared ? 'True' : '', - conversationLength: activity.conversations.length - })); - } else { + // Set columns to include all useful fields for debugging + this.displayedColumns = this.activityType === 'resources' ? + [ 'resourceId', 'title', 'user', 'time', 'parentCode', 'createdOn' ] : + this.activityType === 'courses' ? + [ 'courseId', 'title', 'user', 'time', 'parentCode', 'createdOn' ] : + columns[this.activityType]; + + this.matSortActive = 'time'; + + // For debugging, log the raw data + console.log(`[${this.activityType}] Activities passed to component:`, this.activitiesByDoc.length); + + // Pass through all activities without transformation + if (this.activityType === 'resources' || this.activityType === 'courses') { + // Format the timestamp for display this.activities.data = this.activitiesByDoc.map(activity => { - if (activity.max) { - activity.max.title = this.truncateTitle(activity.max.title); - } return { - averageRating: (this.ratings.find((rating: any) => rating.item === (activity.resourceId || activity.courseId)) || {}).value, - enrollments: this.progress.enrollments.filteredData.filter(filterCourse(activity)).length, - completions: this.progress.completions.filteredData.filter(filterCourse(activity)).length, - stepsCompleted: this.progress.steps.filteredData.filter(filterCourse(activity)).length, - steps: activity.max?.steps, - exams: activity.max?.exams, - ...activity + ...activity, + timeFormatted: activity.time ? new Date(activity.time).toLocaleString() : '', + title: activity.title || activity.courseTitle || activity.resourceTitle || '(no title)' }; }); + console.log(`[${this.activityType}] Data in table:`, this.activities.data.length); + } else { + // Use original code for other tabs + this.matSortActive = this.activityType === 'health' ? 'weekOf' : ''; + this.displayedColumns = columns[this.activityType]; + const filterCourse = (activity: any) => (progress: any) => progress.courseId === activity.courseId; + + if (this.activityType === 'chat') { + this.activities.data = this.activitiesByDoc.map(activity => ({ + ...activity, + createdDate: new Date(activity.createdDate).getTime(), + hasAttachments: activity.context?.resource?.attachments ? 'True' : '', + assistant: activity.assistant ? 'True' : '', + shared: activity.shared ? 'True' : '', + conversationLength: activity.conversations.length + })); + } else { + this.activities.data = this.activitiesByDoc.map(activity => { + if (activity.max) { + activity.max.title = this.truncateTitle(activity.max.title); + } + return { + averageRating: (this.ratings.find((rating: any) => rating.item === (activity.resourceId || activity.courseId)) || {}).value, + enrollments: this.progress.enrollments.filteredData.filter(filterCourse(activity)).length, + completions: this.progress.completions.filteredData.filter(filterCourse(activity)).length, + stepsCompleted: this.progress.steps.filteredData.filter(filterCourse(activity)).length, + steps: activity.max?.steps, + exams: activity.max?.exams, + ...activity + }; + }); + } } } diff --git a/src/app/manager-dashboard/reports/reports-detail.component.ts b/src/app/manager-dashboard/reports/reports-detail.component.ts index 748c4b56e7..d45ea2f832 100644 --- a/src/app/manager-dashboard/reports/reports-detail.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail.component.ts @@ -154,6 +154,36 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { }); } + setDocVisits(type, isInit = false) { + const params = reportsDetailParams(type); + console.log(`[${type}] Processing data for charts: ${this[type].total.filteredData.length} records`); + + // For charts, we still need to group the data by month + const byMonth = this.activityService.groupByMonth( + this.activityService.appendGender(this[type].total.filteredData), + 'time' + ); + + // Set chart data + this.setChart({ ...this.setGenderDatasets(byMonth), chartName: params.chartName }); + + // For summary tab, group by resource/course ID to get top 5 + const groupedForSummary = this.activityService.groupBy( + this[type].byDoc, + [type.replace('Activities', 'Id')], + { maxField: 'time' } + ); + + this.reports[params.record] = groupedForSummary + .filter(item => item[type.replace('Activities', 'Id')]) + .sort((a, b) => b.count - a.count) + .slice(0, 5); + + if (isInit && type === 'courseActivities') { + this.getCourseProgress(); + } + } + setUserCounts({ count, byGender }) { this.reports.totalUsers = count; this.reports.usersByGender = byGender; @@ -199,14 +229,23 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { this.setLoginActivities(); this.ratings.total.filter(this.filter); this.setRatingInfo(); + + // Update these lines to use the existing byDoc data rather than calling setDocVisits this.resourceActivities.total.filter(this.filter); + this.resourceActivities.byDoc = this.resourceActivities.total.filteredData; + this.reports.totalResourceViews = this.resourceActivities.byDoc.length; this.setDocVisits('resourceActivities'); + this.courseActivities.total.filter(this.filter); + this.courseActivities.byDoc = this.courseActivities.total.filteredData; + this.reports.totalCourseViews = this.courseActivities.byDoc.length; this.setDocVisits('courseActivities'); + this.progress.enrollments.filter(this.filter); this.progress.completions.filter(this.filter); this.progress.steps.filter(this.filter); this.setStepCompletion(); + this.setUserCounts(this.activityService.groupUsers( this.users.filter( user => this.filter.members.length === 0 || this.filter.members.some( @@ -292,30 +331,48 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { getDocVisits(type) { const params = reportsDetailParams(type); - this.activityService.getAllActivities(params.db, activityParams(this.planetCode)) - .subscribe((activities: any) => { - // Filter out bad data caused by error found Mar 2 2020 where course id was sometimes undefined in database - // Also filter out bad data found Mar 29 2020 where resourceId included '_design' - this[type].total.data = activities.filter( - activity => (activity.resourceId || activity.courseId) && (activity.resourceId || activity.courseId).indexOf('_design') === -1 - && !activity.private + console.log(`Fetching data from ${params.db}...`); + + // Direct raw query to CouchDB to see all documents + this.couchService.findAll(params.db).subscribe((allActivities: any) => { + console.log(`[${type}] TOTAL RAW RECORDS IN DATABASE: ${allActivities.length}`); + console.log('Sample of 5 records:', allActivities.slice(0, 5)); + + // Filter out only design documents, nothing else + const filtered = allActivities.filter( + activity => (activity._id || '').indexOf('_design') === -1 ); + + console.log(`[${type}] After removing design docs: ${filtered.length}`); + + // Log all resourceIds to see if we have the expected ones + if (type === 'resourceActivities') { + const resourceIds = [...new Set(filtered.map(a => a.resourceId))]; + console.log(`Total unique resourceIds: ${resourceIds.length}`); + console.log('Sample of resource IDs:', resourceIds.slice(0, 5)); + + // Check for the specific resourceId you mentioned + const specificResource = 'e739e2bae321d6eaff6fd50d4606589a'; + const matchingActivities = filtered.filter(a => a.resourceId === specificResource); + console.log(`Activities for resource ${specificResource}: ${matchingActivities.length}`); + if (matchingActivities.length > 0) { + console.log('First matching activity:', matchingActivities[0]); + } + } + + // Store all activities + this[type].total.data = filtered; + this[type].total.filteredData = filtered; + this[type].byDoc = filtered; + + // For the UI + this.reports[params.views] = filtered.length; + + // Now call setDocVisits to set up charts and summary data this.setDocVisits(type, true); }); } - setDocVisits(type, isInit = false) { - const params = reportsDetailParams(type); - const { byDoc, byMonth } = this.activityService.groupDocVisits(this[type].total.filteredData, type.replace('Activities', 'Id')); - this[type].byDoc = byDoc; - this.reports[params.views] = byDoc.reduce((total, doc: any) => total + doc.count, 0); - this.reports[params.record] = byDoc.sort((a, b) => b.count - a.count).slice(0, 5); - this.setChart({ ...this.setGenderDatasets(byMonth), chartName: params.chartName }); - if (isInit && type === 'courseActivities') { - this.getCourseProgress(); - } - } - getPlanetCounts(local: boolean) { if (local) { this.activityService.getDatabaseCount('resources').subscribe(count => this.reports.totalResources = count); From ec78e9c4c6090c01875c92068a43235fff391101 Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Sun, 30 Mar 2025 23:37:58 -0400 Subject: [PATCH 02/12] linting fix --- .../reports-detail-activities.component.ts | 12 +++--- .../reports/reports-detail.component.ts | 42 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts index c585e82b5f..cfc8e10e9e 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts @@ -54,17 +54,17 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte ngOnChanges() { // Set columns to include all useful fields for debugging - this.displayedColumns = this.activityType === 'resources' ? - [ 'resourceId', 'title', 'user', 'time', 'parentCode', 'createdOn' ] : + this.displayedColumns = this.activityType === 'resources' ? + [ 'resourceId', 'title', 'user', 'time', 'parentCode', 'createdOn' ] : this.activityType === 'courses' ? [ 'courseId', 'title', 'user', 'time', 'parentCode', 'createdOn' ] : columns[this.activityType]; - + this.matSortActive = 'time'; - + // For debugging, log the raw data console.log(`[${this.activityType}] Activities passed to component:`, this.activitiesByDoc.length); - + // Pass through all activities without transformation if (this.activityType === 'resources' || this.activityType === 'courses') { // Format the timestamp for display @@ -81,7 +81,7 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte this.matSortActive = this.activityType === 'health' ? 'weekOf' : ''; this.displayedColumns = columns[this.activityType]; const filterCourse = (activity: any) => (progress: any) => progress.courseId === activity.courseId; - + if (this.activityType === 'chat') { this.activities.data = this.activitiesByDoc.map(activity => ({ ...activity, diff --git a/src/app/manager-dashboard/reports/reports-detail.component.ts b/src/app/manager-dashboard/reports/reports-detail.component.ts index d45ea2f832..f3c2c392ad 100644 --- a/src/app/manager-dashboard/reports/reports-detail.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail.component.ts @@ -157,28 +157,28 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { setDocVisits(type, isInit = false) { const params = reportsDetailParams(type); console.log(`[${type}] Processing data for charts: ${this[type].total.filteredData.length} records`); - + // For charts, we still need to group the data by month const byMonth = this.activityService.groupByMonth( - this.activityService.appendGender(this[type].total.filteredData), + this.activityService.appendGender(this[type].total.filteredData), 'time' ); - + // Set chart data this.setChart({ ...this.setGenderDatasets(byMonth), chartName: params.chartName }); - + // For summary tab, group by resource/course ID to get top 5 const groupedForSummary = this.activityService.groupBy( - this[type].byDoc, - [type.replace('Activities', 'Id')], + this[type].byDoc, + [ type.replace('Activities', 'Id') ], { maxField: 'time' } ); - + this.reports[params.record] = groupedForSummary .filter(item => item[type.replace('Activities', 'Id')]) .sort((a, b) => b.count - a.count) .slice(0, 5); - + if (isInit && type === 'courseActivities') { this.getCourseProgress(); } @@ -229,23 +229,23 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { this.setLoginActivities(); this.ratings.total.filter(this.filter); this.setRatingInfo(); - + // Update these lines to use the existing byDoc data rather than calling setDocVisits this.resourceActivities.total.filter(this.filter); this.resourceActivities.byDoc = this.resourceActivities.total.filteredData; this.reports.totalResourceViews = this.resourceActivities.byDoc.length; this.setDocVisits('resourceActivities'); - + this.courseActivities.total.filter(this.filter); this.courseActivities.byDoc = this.courseActivities.total.filteredData; this.reports.totalCourseViews = this.courseActivities.byDoc.length; this.setDocVisits('courseActivities'); - + this.progress.enrollments.filter(this.filter); this.progress.completions.filter(this.filter); this.progress.steps.filter(this.filter); this.setStepCompletion(); - + this.setUserCounts(this.activityService.groupUsers( this.users.filter( user => this.filter.members.length === 0 || this.filter.members.some( @@ -332,25 +332,25 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { getDocVisits(type) { const params = reportsDetailParams(type); console.log(`Fetching data from ${params.db}...`); - + // Direct raw query to CouchDB to see all documents this.couchService.findAll(params.db).subscribe((allActivities: any) => { console.log(`[${type}] TOTAL RAW RECORDS IN DATABASE: ${allActivities.length}`); console.log('Sample of 5 records:', allActivities.slice(0, 5)); - + // Filter out only design documents, nothing else const filtered = allActivities.filter( activity => (activity._id || '').indexOf('_design') === -1 ); - + console.log(`[${type}] After removing design docs: ${filtered.length}`); - + // Log all resourceIds to see if we have the expected ones if (type === 'resourceActivities') { - const resourceIds = [...new Set(filtered.map(a => a.resourceId))]; + const resourceIds = [ ...new Set(filtered.map(a => a.resourceId)) ]; console.log(`Total unique resourceIds: ${resourceIds.length}`); console.log('Sample of resource IDs:', resourceIds.slice(0, 5)); - + // Check for the specific resourceId you mentioned const specificResource = 'e739e2bae321d6eaff6fd50d4606589a'; const matchingActivities = filtered.filter(a => a.resourceId === specificResource); @@ -359,15 +359,15 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { console.log('First matching activity:', matchingActivities[0]); } } - + // Store all activities this[type].total.data = filtered; this[type].total.filteredData = filtered; this[type].byDoc = filtered; - + // For the UI this.reports[params.views] = filtered.length; - + // Now call setDocVisits to set up charts and summary data this.setDocVisits(type, true); }); From d0503c5551e75b7fbc5944bd9c1a9b443b71991a Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 00:19:58 -0400 Subject: [PATCH 03/12] titles display now --- .../reports-detail-activities.component.html | 40 +++---- .../reports-detail-activities.component.ts | 102 +++++++++--------- .../reports/reports-detail.component.ts | 61 ++++++++--- 3 files changed, 117 insertions(+), 86 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.html b/src/app/manager-dashboard/reports/reports-detail-activities.component.html index 8528c8ab79..b76afe1201 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.html +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.html @@ -1,7 +1,7 @@ Title - {{element?.max?.title}} + {{element.title || element.max?.title}} Week Starting @@ -12,79 +12,79 @@ Examinations Views - {{element?.count}} + {{element.count}} Patients Seen - {{element?.unique?.length}} + {{element.unique?.length}} Average Rating - {{element?.averageRating}} + {{element.averageRating}} Enrolled - {{element?.enrollments}} + {{element.enrollments}} Courses Completed - {{element?.completions}} + {{element.completions}} Steps - {{element?.max?.steps}} + {{element.steps || element.max?.steps}} Tests - {{element?.max?.exams}} + {{element.exams || element.max?.exams}} Steps Completed - {{element?.stepsCompleted}} + {{element.stepsCompleted}} AI Provider - {{element?.aiProvider}} + {{element.aiProvider}} User - {{element?.user}} + {{element.user}} Timestamp - {{element?.createdDate | date:'medium'}} + {{element.createdDate | date:'medium'}} Chat Responses - {{element?.conversationLength}} + {{element.conversationLength}} Assistant - {{element?.assistant}} + {{element.assistant}} Shared - {{element?.shared}} + {{element.shared}} Resource ID - {{element?.resourceId}} + {{element.resourceId}} Course ID - {{element?.courseId}} + {{element.courseId}} Time - {{element?.timeFormatted || (element?.time | date:'medium')}} + {{element.timeFormatted || (element.time | date:'medium')}} Parent Code - {{element?.parentCode}} + {{element.parentCode}} Created On - {{element?.createdOn}} + {{element.createdOn}} diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts index cfc8e10e9e..76d3f9f550 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts @@ -41,7 +41,7 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte ngOnInit() { this.activities.sortingDataAccessor = (item: any, property: string) => property === 'unique' ? - item.unique.length : + item.unique?.length || 0 : sortNumberOrString(this.sortingObject(item, property), property); } @@ -49,65 +49,64 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte if (title?.length > 150) { return title.slice(0, 150) + '...'; } - return title; + return title || ''; } ngOnChanges() { - // Set columns to include all useful fields for debugging - this.displayedColumns = this.activityType === 'resources' ? - [ 'resourceId', 'title', 'user', 'time', 'parentCode', 'createdOn' ] : - this.activityType === 'courses' ? - [ 'courseId', 'title', 'user', 'time', 'parentCode', 'createdOn' ] : - columns[this.activityType]; - - this.matSortActive = 'time'; - - // For debugging, log the raw data - console.log(`[${this.activityType}] Activities passed to component:`, this.activitiesByDoc.length); - - // Pass through all activities without transformation - if (this.activityType === 'resources' || this.activityType === 'courses') { - // Format the timestamp for display + console.log(`[${this.activityType}] Activities passed to component:`, this.activitiesByDoc); + + // Set appropriate columns based on activity type + this.displayedColumns = columns[this.activityType] || ['title', 'count']; + this.matSortActive = this.activityType === 'health' ? 'weekOf' : 'count'; + + if (this.activityType === 'chat') { + // Chat activities processing + this.activities.data = this.activitiesByDoc.map(activity => ({ + ...activity, + createdDate: activity.createdDate ? new Date(activity.createdDate).getTime() : '', + hasAttachments: activity.context?.resource?.attachments ? 'True' : '', + assistant: activity.assistant ? 'True' : '', + shared: activity.shared ? 'True' : '', + conversationLength: activity.conversations?.length || 0 + })); + } else if (this.activityType === 'resources') { + // Generate a proper display for resources this.activities.data = this.activitiesByDoc.map(activity => { + // We need to make sure title is accessible at the top level return { ...activity, - timeFormatted: activity.time ? new Date(activity.time).toLocaleString() : '', - title: activity.title || activity.courseTitle || activity.resourceTitle || '(no title)' + // For direct display in the table + title: activity.title || activity.max?.title || '', + averageRating: (this.ratings.find((rating: any) => rating.item === activity.resourceId) || {}).value || '' }; }); - console.log(`[${this.activityType}] Data in table:`, this.activities.data.length); - } else { - // Use original code for other tabs - this.matSortActive = this.activityType === 'health' ? 'weekOf' : ''; - this.displayedColumns = columns[this.activityType]; + console.log('Resources data processed:', this.activities.data); + } else if (this.activityType === 'courses') { + // For course activities const filterCourse = (activity: any) => (progress: any) => progress.courseId === activity.courseId; - - if (this.activityType === 'chat') { - this.activities.data = this.activitiesByDoc.map(activity => ({ + + this.activities.data = this.activitiesByDoc.map(activity => { + return { ...activity, - createdDate: new Date(activity.createdDate).getTime(), - hasAttachments: activity.context?.resource?.attachments ? 'True' : '', - assistant: activity.assistant ? 'True' : '', - shared: activity.shared ? 'True' : '', - conversationLength: activity.conversations.length - })); - } else { - this.activities.data = this.activitiesByDoc.map(activity => { - if (activity.max) { - activity.max.title = this.truncateTitle(activity.max.title); - } - return { - averageRating: (this.ratings.find((rating: any) => rating.item === (activity.resourceId || activity.courseId)) || {}).value, - enrollments: this.progress.enrollments.filteredData.filter(filterCourse(activity)).length, - completions: this.progress.completions.filteredData.filter(filterCourse(activity)).length, - stepsCompleted: this.progress.steps.filteredData.filter(filterCourse(activity)).length, - steps: activity.max?.steps, - exams: activity.max?.exams, - ...activity - }; - }); - } + title: activity.title || activity.max?.title || '', + steps: activity.max?.steps || 0, + exams: activity.max?.exams || 0, + averageRating: (this.ratings.find((rating: any) => rating.item === activity.courseId) || {}).value || '', + enrollments: this.progress.enrollments.filteredData.filter(filterCourse(activity)).length, + completions: this.progress.completions.filteredData.filter(filterCourse(activity)).length, + stepsCompleted: this.progress.steps.filteredData.filter(filterCourse(activity)).length + }; + }); + } else { + // Health activities + this.activities.data = this.activitiesByDoc.map(activity => ({ + ...activity, + count: activity.count || 0, + unique: activity.unique || [] + })); } + + console.log(`[${this.activityType}] Data in table:`, this.activities.data); } ngAfterViewInit() { @@ -116,7 +115,10 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte } sortingObject(item, property) { - return property === 'title' ? item.max : item; + if (property === 'title') { + return item.title || item.max?.title || ''; + } + return item; } rowClick(element) { diff --git a/src/app/manager-dashboard/reports/reports-detail.component.ts b/src/app/manager-dashboard/reports/reports-detail.component.ts index f3c2c392ad..173b8c2797 100644 --- a/src/app/manager-dashboard/reports/reports-detail.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail.component.ts @@ -345,25 +345,14 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { console.log(`[${type}] After removing design docs: ${filtered.length}`); - // Log all resourceIds to see if we have the expected ones - if (type === 'resourceActivities') { - const resourceIds = [ ...new Set(filtered.map(a => a.resourceId)) ]; - console.log(`Total unique resourceIds: ${resourceIds.length}`); - console.log('Sample of resource IDs:', resourceIds.slice(0, 5)); - - // Check for the specific resourceId you mentioned - const specificResource = 'e739e2bae321d6eaff6fd50d4606589a'; - const matchingActivities = filtered.filter(a => a.resourceId === specificResource); - console.log(`Activities for resource ${specificResource}: ${matchingActivities.length}`); - if (matchingActivities.length > 0) { - console.log('First matching activity:', matchingActivities[0]); - } - } - // Store all activities this[type].total.data = filtered; this[type].total.filteredData = filtered; - this[type].byDoc = filtered; + + // Group the filtered activities by resource/course ID + const idField = type.replace('Activities', 'Id'); + const grouped = this.groupActivities(filtered, idField); + this[type].byDoc = grouped; // For the UI this.reports[params.views] = filtered.length; @@ -373,6 +362,46 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { }); } + groupActivities(activities, idField) { + // Create a map to hold grouped activities + const groupMap = {}; + + activities.forEach(activity => { + const id = activity[idField]; + if (!id) { + return; + } + + if (!groupMap[id]) { + groupMap[id] = { + [idField]: id, + title: activity.title || activity.resourceTitle || activity.courseTitle || '', + count: 0, + unique: [], + time: activity.time || 0, + max: { + title: activity.title || activity.resourceTitle || activity.courseTitle || id, + steps: activity.steps || 0, + exams: activity.exams || 0 + } + }; + } + + groupMap[id].count++; + + if (activity.user && !groupMap[id].unique.includes(activity.user)) { + groupMap[id].unique.push(activity.user); + } + + if (activity.time > groupMap[id].time) { + groupMap[id].time = activity.time; + groupMap[id].max.title = activity.title || activity.resourceTitle || activity.courseTitle || id; + } + }); + + return Object.values(groupMap); + } + getPlanetCounts(local: boolean) { if (local) { this.activityService.getDatabaseCount('resources').subscribe(count => this.reports.totalResources = count); From cd9dcbc12c2be68904516e168e033a2dbe7a7515 Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 00:21:37 -0400 Subject: [PATCH 04/12] linting fix --- .../reports/reports-detail-activities.component.ts | 10 +++++----- .../reports/reports-detail.component.ts | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts index 76d3f9f550..e4653fafce 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts @@ -54,11 +54,11 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte ngOnChanges() { console.log(`[${this.activityType}] Activities passed to component:`, this.activitiesByDoc); - + // Set appropriate columns based on activity type - this.displayedColumns = columns[this.activityType] || ['title', 'count']; + this.displayedColumns = columns[this.activityType] || [ 'title', 'count' ]; this.matSortActive = this.activityType === 'health' ? 'weekOf' : 'count'; - + if (this.activityType === 'chat') { // Chat activities processing this.activities.data = this.activitiesByDoc.map(activity => ({ @@ -84,7 +84,7 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte } else if (this.activityType === 'courses') { // For course activities const filterCourse = (activity: any) => (progress: any) => progress.courseId === activity.courseId; - + this.activities.data = this.activitiesByDoc.map(activity => { return { ...activity, @@ -105,7 +105,7 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte unique: activity.unique || [] })); } - + console.log(`[${this.activityType}] Data in table:`, this.activities.data); } diff --git a/src/app/manager-dashboard/reports/reports-detail.component.ts b/src/app/manager-dashboard/reports/reports-detail.component.ts index 173b8c2797..2dcfa3fa56 100644 --- a/src/app/manager-dashboard/reports/reports-detail.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail.component.ts @@ -348,7 +348,7 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { // Store all activities this[type].total.data = filtered; this[type].total.filteredData = filtered; - + // Group the filtered activities by resource/course ID const idField = type.replace('Activities', 'Id'); const grouped = this.groupActivities(filtered, idField); @@ -365,13 +365,13 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { groupActivities(activities, idField) { // Create a map to hold grouped activities const groupMap = {}; - + activities.forEach(activity => { const id = activity[idField]; if (!id) { return; } - + if (!groupMap[id]) { groupMap[id] = { [idField]: id, @@ -386,19 +386,19 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { } }; } - + groupMap[id].count++; - + if (activity.user && !groupMap[id].unique.includes(activity.user)) { groupMap[id].unique.push(activity.user); } - + if (activity.time > groupMap[id].time) { groupMap[id].time = activity.time; groupMap[id].max.title = activity.title || activity.resourceTitle || activity.courseTitle || id; } }); - + return Object.values(groupMap); } From 3d759ba2cca093f5413bf665b19463e903782fc5 Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 00:30:05 -0400 Subject: [PATCH 05/12] collated data in resources --- .../reports/reports-detail.component.ts | 87 +++++++++++-------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail.component.ts b/src/app/manager-dashboard/reports/reports-detail.component.ts index 2dcfa3fa56..fce9e79e3a 100644 --- a/src/app/manager-dashboard/reports/reports-detail.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail.component.ts @@ -156,29 +156,44 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { setDocVisits(type, isInit = false) { const params = reportsDetailParams(type); - console.log(`[${type}] Processing data for charts: ${this[type].total.filteredData.length} records`); - + + // Filter the data based on user selections + this[type].total.filteredData = this[type].total.data.filter(item => { + const isCorrectApp = this.filter.app === '' || + ((this.filter.app === 'myplanet') !== (item.androidId === undefined)); + + const isInDateRange = !this.filter.startDate || !this.filter.endDate ? true : + (item.time >= this.filter.startDate.getTime() && item.time <= this.filter.endDate.getTime()); + + const isSelectedMember = this.filter.members.length === 0 || + this.filter.members.some(member => ( + member.userId === item.userId || member.userId.split(':')[1] === item.user + )); + + return isCorrectApp && isInDateRange && isSelectedMember; + }); + + // Group the filtered activities by resource/course ID + const idField = type.replace('Activities', 'Id'); + const grouped = this.groupActivities(this[type].total.filteredData, idField); + this[type].byDoc = grouped; + + // For the UI + this.reports[params.views] = this[type].total.filteredData.length; + + // Generate summary data with the properly grouped activities + this.reports[params.record] = grouped + .filter(item => item[idField]) // Make sure we have a valid ID + .sort((a, b) => b.count - a.count) + .slice(0, 5); + // For charts, we still need to group the data by month const byMonth = this.activityService.groupByMonth( this.activityService.appendGender(this[type].total.filteredData), 'time' ); - - // Set chart data this.setChart({ ...this.setGenderDatasets(byMonth), chartName: params.chartName }); - - // For summary tab, group by resource/course ID to get top 5 - const groupedForSummary = this.activityService.groupBy( - this[type].byDoc, - [ type.replace('Activities', 'Id') ], - { maxField: 'time' } - ); - - this.reports[params.record] = groupedForSummary - .filter(item => item[type.replace('Activities', 'Id')]) - .sort((a, b) => b.count - a.count) - .slice(0, 5); - + if (isInit && type === 'courseActivities') { this.getCourseProgress(); } @@ -277,13 +292,15 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { setStepCompletion() { const { byMonth } = this.activityService.groupStepCompletion(this.progress.steps.filteredData); - this.reports.totalStepCompleted = byMonth.reduce((total, doc: any) => total + doc.count, 0); + // Fix TypeScript error by adding type annotation to doc parameter + this.reports.totalStepCompleted = byMonth.reduce((total: number, doc: { count: number }) => total + doc.count, 0); this.setChart({ ...this.setGenderDatasets(byMonth), chartName: 'stepCompletedChart' }); } setLoginActivities() { const { byUser, byMonth } = this.activityService.groupLoginActivities(this.loginActivities.filteredData); - this.reports.totalMemberVisits = byUser.reduce((total, resource: any) => total + resource.count, 0); + // Fix TypeScript error by adding type annotation to resource parameter + this.reports.totalMemberVisits = byUser.reduce((total: number, resource: { count: number }) => total + resource.count, 0); const byUserWithProfile = byUser.map((activity) => ({ ...activity, userDoc: this.users.find((user) => user.doc.name === activity.user && user.doc.planetCode === this.planetCode) @@ -336,7 +353,6 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { // Direct raw query to CouchDB to see all documents this.couchService.findAll(params.db).subscribe((allActivities: any) => { console.log(`[${type}] TOTAL RAW RECORDS IN DATABASE: ${allActivities.length}`); - console.log('Sample of 5 records:', allActivities.slice(0, 5)); // Filter out only design documents, nothing else const filtered = allActivities.filter( @@ -348,30 +364,21 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { // Store all activities this[type].total.data = filtered; this[type].total.filteredData = filtered; - - // Group the filtered activities by resource/course ID - const idField = type.replace('Activities', 'Id'); - const grouped = this.groupActivities(filtered, idField); - this[type].byDoc = grouped; - - // For the UI - this.reports[params.views] = filtered.length; - + // Now call setDocVisits to set up charts and summary data this.setDocVisits(type, true); }); } groupActivities(activities, idField) { - // Create a map to hold grouped activities - const groupMap = {}; - + // Create a map to hold grouped activities - use an object for faster lookups by ID + const groupMap: { [key: string]: any } = {}; activities.forEach(activity => { const id = activity[idField]; if (!id) { return; } - + if (!groupMap[id]) { groupMap[id] = { [idField]: id, @@ -386,19 +393,23 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { } }; } - + groupMap[id].count++; - + if (activity.user && !groupMap[id].unique.includes(activity.user)) { groupMap[id].unique.push(activity.user); } - + if (activity.time > groupMap[id].time) { groupMap[id].time = activity.time; - groupMap[id].max.title = activity.title || activity.resourceTitle || activity.courseTitle || id; + // Only update the title if we have a better one + if (activity.title || activity.resourceTitle || activity.courseTitle) { + groupMap[id].max.title = activity.title || activity.resourceTitle || activity.courseTitle || id; + } } }); - + + // Convert the groupMap object back to an array return Object.values(groupMap); } From f0a3e77ae51c1b4bb0b39a4c97dba6fc8e3a1b22 Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 00:35:47 -0400 Subject: [PATCH 06/12] linting fix --- .../reports-detail-activities.component.html | 20 ------- .../reports-detail-activities.component.ts | 16 +----- .../reports/reports-detail.component.ts | 52 +++++-------------- 3 files changed, 14 insertions(+), 74 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.html b/src/app/manager-dashboard/reports/reports-detail-activities.component.html index b76afe1201..e4448afab2 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.html +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.html @@ -66,26 +66,6 @@ Shared {{element.shared}} - - Resource ID - {{element.resourceId}} - - - Course ID - {{element.courseId}} - - - Time - {{element.timeFormatted || (element.time | date:'medium')}} - - - Parent Code - {{element.parentCode}} - - - Created On - {{element.createdOn}} - diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts index e4653fafce..02312d1962 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts @@ -41,7 +41,7 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte ngOnInit() { this.activities.sortingDataAccessor = (item: any, property: string) => property === 'unique' ? - item.unique?.length || 0 : + item.unique.length : sortNumberOrString(this.sortingObject(item, property), property); } @@ -49,18 +49,14 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte if (title?.length > 150) { return title.slice(0, 150) + '...'; } - return title || ''; + return title; } ngOnChanges() { - console.log(`[${this.activityType}] Activities passed to component:`, this.activitiesByDoc); - - // Set appropriate columns based on activity type this.displayedColumns = columns[this.activityType] || [ 'title', 'count' ]; this.matSortActive = this.activityType === 'health' ? 'weekOf' : 'count'; if (this.activityType === 'chat') { - // Chat activities processing this.activities.data = this.activitiesByDoc.map(activity => ({ ...activity, createdDate: activity.createdDate ? new Date(activity.createdDate).getTime() : '', @@ -70,21 +66,16 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte conversationLength: activity.conversations?.length || 0 })); } else if (this.activityType === 'resources') { - // Generate a proper display for resources this.activities.data = this.activitiesByDoc.map(activity => { - // We need to make sure title is accessible at the top level return { ...activity, - // For direct display in the table title: activity.title || activity.max?.title || '', averageRating: (this.ratings.find((rating: any) => rating.item === activity.resourceId) || {}).value || '' }; }); console.log('Resources data processed:', this.activities.data); } else if (this.activityType === 'courses') { - // For course activities const filterCourse = (activity: any) => (progress: any) => progress.courseId === activity.courseId; - this.activities.data = this.activitiesByDoc.map(activity => { return { ...activity, @@ -98,15 +89,12 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte }; }); } else { - // Health activities this.activities.data = this.activitiesByDoc.map(activity => ({ ...activity, count: activity.count || 0, unique: activity.unique || [] })); } - - console.log(`[${this.activityType}] Data in table:`, this.activities.data); } ngAfterViewInit() { diff --git a/src/app/manager-dashboard/reports/reports-detail.component.ts b/src/app/manager-dashboard/reports/reports-detail.component.ts index fce9e79e3a..eedfe2b8d9 100644 --- a/src/app/manager-dashboard/reports/reports-detail.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail.component.ts @@ -156,44 +156,34 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { setDocVisits(type, isInit = false) { const params = reportsDetailParams(type); - - // Filter the data based on user selections this[type].total.filteredData = this[type].total.data.filter(item => { - const isCorrectApp = this.filter.app === '' || + const isCorrectApp = this.filter.app === '' || ((this.filter.app === 'myplanet') !== (item.androidId === undefined)); - - const isInDateRange = !this.filter.startDate || !this.filter.endDate ? true : + + const isInDateRange = !this.filter.startDate || !this.filter.endDate ? true : (item.time >= this.filter.startDate.getTime() && item.time <= this.filter.endDate.getTime()); - + const isSelectedMember = this.filter.members.length === 0 || this.filter.members.some(member => ( member.userId === item.userId || member.userId.split(':')[1] === item.user )); - + return isCorrectApp && isInDateRange && isSelectedMember; }); - - // Group the filtered activities by resource/course ID const idField = type.replace('Activities', 'Id'); const grouped = this.groupActivities(this[type].total.filteredData, idField); this[type].byDoc = grouped; - - // For the UI this.reports[params.views] = this[type].total.filteredData.length; - - // Generate summary data with the properly grouped activities this.reports[params.record] = grouped - .filter(item => item[idField]) // Make sure we have a valid ID + .filter(item => item[idField]) .sort((a, b) => b.count - a.count) .slice(0, 5); - - // For charts, we still need to group the data by month const byMonth = this.activityService.groupByMonth( this.activityService.appendGender(this[type].total.filteredData), 'time' ); this.setChart({ ...this.setGenderDatasets(byMonth), chartName: params.chartName }); - + if (isInit && type === 'courseActivities') { this.getCourseProgress(); } @@ -244,8 +234,6 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { this.setLoginActivities(); this.ratings.total.filter(this.filter); this.setRatingInfo(); - - // Update these lines to use the existing byDoc data rather than calling setDocVisits this.resourceActivities.total.filter(this.filter); this.resourceActivities.byDoc = this.resourceActivities.total.filteredData; this.reports.totalResourceViews = this.resourceActivities.byDoc.length; @@ -348,37 +336,24 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { getDocVisits(type) { const params = reportsDetailParams(type); - console.log(`Fetching data from ${params.db}...`); - - // Direct raw query to CouchDB to see all documents this.couchService.findAll(params.db).subscribe((allActivities: any) => { - console.log(`[${type}] TOTAL RAW RECORDS IN DATABASE: ${allActivities.length}`); - - // Filter out only design documents, nothing else const filtered = allActivities.filter( activity => (activity._id || '').indexOf('_design') === -1 ); - - console.log(`[${type}] After removing design docs: ${filtered.length}`); - - // Store all activities this[type].total.data = filtered; this[type].total.filteredData = filtered; - - // Now call setDocVisits to set up charts and summary data this.setDocVisits(type, true); }); } groupActivities(activities, idField) { - // Create a map to hold grouped activities - use an object for faster lookups by ID - const groupMap: { [key: string]: any } = {}; + const groupMap: { [key: string]: any } = {}; activities.forEach(activity => { const id = activity[idField]; if (!id) { return; } - + if (!groupMap[id]) { groupMap[id] = { [idField]: id, @@ -393,23 +368,20 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { } }; } - + groupMap[id].count++; - + if (activity.user && !groupMap[id].unique.includes(activity.user)) { groupMap[id].unique.push(activity.user); } - + if (activity.time > groupMap[id].time) { groupMap[id].time = activity.time; - // Only update the title if we have a better one if (activity.title || activity.resourceTitle || activity.courseTitle) { groupMap[id].max.title = activity.title || activity.resourceTitle || activity.courseTitle || id; } } }); - - // Convert the groupMap object back to an array return Object.values(groupMap); } From da211b179c0dd3b6b8fc0cf2a5f30108a7230030 Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:12:25 -0400 Subject: [PATCH 07/12] views update now --- .../reports/reports-detail.component.ts | 95 +++++++++++-------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail.component.ts b/src/app/manager-dashboard/reports/reports-detail.component.ts index eedfe2b8d9..d9bc86ab50 100644 --- a/src/app/manager-dashboard/reports/reports-detail.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail.component.ts @@ -155,37 +155,27 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { } setDocVisits(type, isInit = false) { + // This method is now only used for initial data setup, not for filtering const params = reportsDetailParams(type); - this[type].total.filteredData = this[type].total.data.filter(item => { - const isCorrectApp = this.filter.app === '' || - ((this.filter.app === 'myplanet') !== (item.androidId === undefined)); - - const isInDateRange = !this.filter.startDate || !this.filter.endDate ? true : - (item.time >= this.filter.startDate.getTime() && item.time <= this.filter.endDate.getTime()); - - const isSelectedMember = this.filter.members.length === 0 || - this.filter.members.some(member => ( - member.userId === item.userId || member.userId.split(':')[1] === item.user - )); - - return isCorrectApp && isInDateRange && isSelectedMember; - }); - const idField = type.replace('Activities', 'Id'); - const grouped = this.groupActivities(this[type].total.filteredData, idField); - this[type].byDoc = grouped; - this.reports[params.views] = this[type].total.filteredData.length; - this.reports[params.record] = grouped - .filter(item => item[idField]) - .sort((a, b) => b.count - a.count) - .slice(0, 5); - const byMonth = this.activityService.groupByMonth( - this.activityService.appendGender(this[type].total.filteredData), - 'time' - ); - this.setChart({ ...this.setGenderDatasets(byMonth), chartName: params.chartName }); + if (isInit) { + // Only if this is the initial setup, we'll set up the reports data + const idField = type.replace('Activities', 'Id'); + const grouped = this.groupActivities(this[type].total.filteredData, idField); + this[type].byDoc = grouped; + this.reports[params.views] = this[type].total.filteredData.length; + this.reports[params.record] = grouped + .filter(item => item[idField]) + .sort((a, b) => b.count - a.count) + .slice(0, 5); + const byMonth = this.activityService.groupByMonth( + this.activityService.appendGender(this[type].total.filteredData), + 'time' + ); + this.setChart({ ...this.setGenderDatasets(byMonth), chartName: params.chartName }); - if (isInit && type === 'courseActivities') { - this.getCourseProgress(); + if (isInit && type === 'courseActivities') { + this.getCourseProgress(); + } } } @@ -234,28 +224,59 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { this.setLoginActivities(); this.ratings.total.filter(this.filter); this.setRatingInfo(); + + // Filter resource activities and properly update both the data and the UI this.resourceActivities.total.filter(this.filter); - this.resourceActivities.byDoc = this.resourceActivities.total.filteredData; - this.reports.totalResourceViews = this.resourceActivities.byDoc.length; - this.setDocVisits('resourceActivities'); + // Generate proper groups after filtering + const resourceIdField = 'resourceId'; + const resourceGrouped = this.groupActivities(this.resourceActivities.total.filteredData, resourceIdField); + this.resourceActivities.byDoc = resourceGrouped; + this.reports.totalResourceViews = this.resourceActivities.total.filteredData.length; + this.reports.resources = resourceGrouped + .filter(item => item[resourceIdField]) + .sort((a, b) => b.count - a.count) + .slice(0, 5); + // Update resource charts + const resourceByMonth = this.activityService.groupByMonth( + this.activityService.appendGender(this.resourceActivities.total.filteredData), + 'time' + ); + this.setChart({ ...this.setGenderDatasets(resourceByMonth), chartName: 'resourceViewChart' }); + // Filter course activities and properly update both the data and the UI this.courseActivities.total.filter(this.filter); - this.courseActivities.byDoc = this.courseActivities.total.filteredData; - this.reports.totalCourseViews = this.courseActivities.byDoc.length; - this.setDocVisits('courseActivities'); + // Generate proper groups after filtering + const courseIdField = 'courseId'; + const courseGrouped = this.groupActivities(this.courseActivities.total.filteredData, courseIdField); + this.courseActivities.byDoc = courseGrouped; + this.reports.totalCourseViews = this.courseActivities.total.filteredData.length; + this.reports.courses = courseGrouped + .filter(item => item[courseIdField]) + .sort((a, b) => b.count - a.count) + .slice(0, 5); + // Update course charts + const courseByMonth = this.activityService.groupByMonth( + this.activityService.appendGender(this.courseActivities.total.filteredData), + 'time' + ); + this.setChart({ ...this.setGenderDatasets(courseByMonth), chartName: 'courseViewChart' }); + // Progress data this.progress.enrollments.filter(this.filter); this.progress.completions.filter(this.filter); this.progress.steps.filter(this.filter); this.setStepCompletion(); + // User data this.setUserCounts(this.activityService.groupUsers( this.users.filter( user => this.filter.members.length === 0 || this.filter.members.some( member => member.userId === user._id && member.userPlanetCode === user.doc.planetCode ) ) - )); + )); + + // Chat data this.chatActivities.filter(this.filter); this.setChatUsage(); } @@ -330,7 +351,7 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { }; }); this.setStepCompletion(); - this.setDocVisits('courseActivities', false); + this.filterData(); }); } From 2cd1c0c23b6f14287108262f7d366af381c347bf Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:14:47 -0400 Subject: [PATCH 08/12] linting fix --- src/app/manager-dashboard/reports/reports-detail.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail.component.ts b/src/app/manager-dashboard/reports/reports-detail.component.ts index d9bc86ab50..52cf485cc0 100644 --- a/src/app/manager-dashboard/reports/reports-detail.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail.component.ts @@ -233,7 +233,7 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { this.resourceActivities.byDoc = resourceGrouped; this.reports.totalResourceViews = this.resourceActivities.total.filteredData.length; this.reports.resources = resourceGrouped - .filter(item => item[resourceIdField]) + .filter(item => item[resourceIdField]) .sort((a, b) => b.count - a.count) .slice(0, 5); // Update resource charts @@ -275,7 +275,7 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { ) ) )); - + // Chat data this.chatActivities.filter(this.filter); this.setChatUsage(); From 31e980872651582aa03901ef8b0d01529bd0afd6 Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:23:53 -0400 Subject: [PATCH 09/12] added back truncation to titles --- .../reports-detail-activities.component.html | 4 +++- .../reports-detail-activities.component.ts | 10 ++++++---- .../reports/reports-detail.scss | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.html b/src/app/manager-dashboard/reports/reports-detail-activities.component.html index e4448afab2..a97c594120 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.html +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.html @@ -1,7 +1,9 @@ Title - {{element.title || element.max?.title}} + + {{element.title || element.max?.title}} + Week Starting diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts index 02312d1962..a9554f0022 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts @@ -46,8 +46,8 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte } truncateTitle(title: string) { - if (title?.length > 150) { - return title.slice(0, 150) + '...'; + if (title.length > 140) { + return title.slice(0, 140) + '…'; } return title; } @@ -67,9 +67,10 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte })); } else if (this.activityType === 'resources') { this.activities.data = this.activitiesByDoc.map(activity => { + const rawTitle = activity.title || activity.max?.title || ''; return { ...activity, - title: activity.title || activity.max?.title || '', + title: this.truncateTitle(rawTitle), averageRating: (this.ratings.find((rating: any) => rating.item === activity.resourceId) || {}).value || '' }; }); @@ -77,9 +78,10 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte } else if (this.activityType === 'courses') { const filterCourse = (activity: any) => (progress: any) => progress.courseId === activity.courseId; this.activities.data = this.activitiesByDoc.map(activity => { + const rawTitle = activity.title || activity.max?.title || ''; return { ...activity, - title: activity.title || activity.max?.title || '', + title: this.truncateTitle(rawTitle), steps: activity.max?.steps || 0, exams: activity.max?.exams || 0, averageRating: (this.ratings.find((rating: any) => rating.item === activity.courseId) || {}).value || '', diff --git a/src/app/manager-dashboard/reports/reports-detail.scss b/src/app/manager-dashboard/reports/reports-detail.scss index 0122323a47..7f815d8793 100644 --- a/src/app/manager-dashboard/reports/reports-detail.scss +++ b/src/app/manager-dashboard/reports/reports-detail.scss @@ -53,6 +53,24 @@ mat-toolbar mat-form-field { display: none; } +.wrap-text { + white-space: normal; + overflow-wrap: break-word; + word-break: break-word; + max-width: 300px; +} + +// Allow tooltip to display the full title without truncation +::ng-deep .mat-tooltip { + white-space: normal !important; + max-width: 500px !important; + overflow-wrap: break-word; + word-break: break-word; + text-align: left; + font-size: 14px !important; + line-height: 1.5 !important; +} + @media(max-width: #{$screen-md}) { .mobile-view-styles { display: block; From 74f787b55421bd8c35050a3883a09863ff14a89f Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:32:21 -0400 Subject: [PATCH 10/12] linting fix --- .../reports-detail-activities.component.html | 34 +++++++++---------- .../reports-detail-activities.component.ts | 6 ++-- .../reports/reports-detail.component.ts | 18 ---------- .../reports/reports-detail.scss | 20 +---------- 4 files changed, 20 insertions(+), 58 deletions(-) diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.html b/src/app/manager-dashboard/reports/reports-detail-activities.component.html index a97c594120..69214fcf3d 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.html +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.html @@ -1,9 +1,7 @@ Title - - {{element.title || element.max?.title}} - + {{element?.max?.title}} Week Starting @@ -14,59 +12,59 @@ Examinations Views - {{element.count}} + {{element?.count}} Patients Seen - {{element.unique?.length}} + {{element?.unique?.length}} Average Rating - {{element.averageRating}} + {{element?.averageRating}} Enrolled - {{element.enrollments}} + {{element?.enrollments}} Courses Completed - {{element.completions}} + {{element?.completions}} Steps - {{element.steps || element.max?.steps}} + {{element?.max?.steps}} Tests - {{element.exams || element.max?.exams}} + {{element?.max?.exams}} Steps Completed - {{element.stepsCompleted}} + {{element?.stepsCompleted}} AI Provider - {{element.aiProvider}} + {{element?.aiProvider}} User - {{element.user}} + {{element?.user}} Timestamp - {{element.createdDate | date:'medium'}} + {{element?.createdDate | date:'medium'}} Chat Responses - {{element.conversationLength}} + {{element?.conversationLength}} Assistant - {{element.assistant}} + {{element?.assistant}} Shared - {{element.shared}} + {{element?.shared}} @@ -74,4 +72,4 @@ - + \ No newline at end of file diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts index a9554f0022..f554cb6d82 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.ts @@ -46,8 +46,8 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte } truncateTitle(title: string) { - if (title.length > 140) { - return title.slice(0, 140) + '…'; + if (title?.length > 150) { + return title.slice(0, 150) + '...'; } return title; } @@ -63,7 +63,7 @@ export class ReportsDetailActivitiesComponent implements OnInit, OnChanges, Afte hasAttachments: activity.context?.resource?.attachments ? 'True' : '', assistant: activity.assistant ? 'True' : '', shared: activity.shared ? 'True' : '', - conversationLength: activity.conversations?.length || 0 + conversationLength: activity.conversations.length })); } else if (this.activityType === 'resources') { this.activities.data = this.activitiesByDoc.map(activity => { diff --git a/src/app/manager-dashboard/reports/reports-detail.component.ts b/src/app/manager-dashboard/reports/reports-detail.component.ts index 52cf485cc0..fe424e6018 100644 --- a/src/app/manager-dashboard/reports/reports-detail.component.ts +++ b/src/app/manager-dashboard/reports/reports-detail.component.ts @@ -155,10 +155,8 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { } setDocVisits(type, isInit = false) { - // This method is now only used for initial data setup, not for filtering const params = reportsDetailParams(type); if (isInit) { - // Only if this is the initial setup, we'll set up the reports data const idField = type.replace('Activities', 'Id'); const grouped = this.groupActivities(this[type].total.filteredData, idField); this[type].byDoc = grouped; @@ -224,10 +222,7 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { this.setLoginActivities(); this.ratings.total.filter(this.filter); this.setRatingInfo(); - - // Filter resource activities and properly update both the data and the UI this.resourceActivities.total.filter(this.filter); - // Generate proper groups after filtering const resourceIdField = 'resourceId'; const resourceGrouped = this.groupActivities(this.resourceActivities.total.filteredData, resourceIdField); this.resourceActivities.byDoc = resourceGrouped; @@ -236,16 +231,12 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { .filter(item => item[resourceIdField]) .sort((a, b) => b.count - a.count) .slice(0, 5); - // Update resource charts const resourceByMonth = this.activityService.groupByMonth( this.activityService.appendGender(this.resourceActivities.total.filteredData), 'time' ); this.setChart({ ...this.setGenderDatasets(resourceByMonth), chartName: 'resourceViewChart' }); - - // Filter course activities and properly update both the data and the UI this.courseActivities.total.filter(this.filter); - // Generate proper groups after filtering const courseIdField = 'courseId'; const courseGrouped = this.groupActivities(this.courseActivities.total.filteredData, courseIdField); this.courseActivities.byDoc = courseGrouped; @@ -254,20 +245,15 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { .filter(item => item[courseIdField]) .sort((a, b) => b.count - a.count) .slice(0, 5); - // Update course charts const courseByMonth = this.activityService.groupByMonth( this.activityService.appendGender(this.courseActivities.total.filteredData), 'time' ); this.setChart({ ...this.setGenderDatasets(courseByMonth), chartName: 'courseViewChart' }); - - // Progress data this.progress.enrollments.filter(this.filter); this.progress.completions.filter(this.filter); this.progress.steps.filter(this.filter); this.setStepCompletion(); - - // User data this.setUserCounts(this.activityService.groupUsers( this.users.filter( user => this.filter.members.length === 0 || this.filter.members.some( @@ -275,8 +261,6 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { ) ) )); - - // Chat data this.chatActivities.filter(this.filter); this.setChatUsage(); } @@ -301,14 +285,12 @@ export class ReportsDetailComponent implements OnInit, OnDestroy { setStepCompletion() { const { byMonth } = this.activityService.groupStepCompletion(this.progress.steps.filteredData); - // Fix TypeScript error by adding type annotation to doc parameter this.reports.totalStepCompleted = byMonth.reduce((total: number, doc: { count: number }) => total + doc.count, 0); this.setChart({ ...this.setGenderDatasets(byMonth), chartName: 'stepCompletedChart' }); } setLoginActivities() { const { byUser, byMonth } = this.activityService.groupLoginActivities(this.loginActivities.filteredData); - // Fix TypeScript error by adding type annotation to resource parameter this.reports.totalMemberVisits = byUser.reduce((total: number, resource: { count: number }) => total + resource.count, 0); const byUserWithProfile = byUser.map((activity) => ({ ...activity, diff --git a/src/app/manager-dashboard/reports/reports-detail.scss b/src/app/manager-dashboard/reports/reports-detail.scss index 7f815d8793..9cc4690e04 100644 --- a/src/app/manager-dashboard/reports/reports-detail.scss +++ b/src/app/manager-dashboard/reports/reports-detail.scss @@ -53,24 +53,6 @@ mat-toolbar mat-form-field { display: none; } -.wrap-text { - white-space: normal; - overflow-wrap: break-word; - word-break: break-word; - max-width: 300px; -} - -// Allow tooltip to display the full title without truncation -::ng-deep .mat-tooltip { - white-space: normal !important; - max-width: 500px !important; - overflow-wrap: break-word; - word-break: break-word; - text-align: left; - font-size: 14px !important; - line-height: 1.5 !important; -} - @media(max-width: #{$screen-md}) { .mobile-view-styles { display: block; @@ -96,4 +78,4 @@ mat-toolbar mat-form-field { .reports-table-container { grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); } -} +} \ No newline at end of file From 2487f1415212f9f8a7b0e78c70043c8b2a889aff Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:33:18 -0400 Subject: [PATCH 11/12] linting fix --- src/app/manager-dashboard/reports/reports-detail.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/manager-dashboard/reports/reports-detail.scss b/src/app/manager-dashboard/reports/reports-detail.scss index 9cc4690e04..0122323a47 100644 --- a/src/app/manager-dashboard/reports/reports-detail.scss +++ b/src/app/manager-dashboard/reports/reports-detail.scss @@ -78,4 +78,4 @@ mat-toolbar mat-form-field { .reports-table-container { grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); } -} \ No newline at end of file +} From 9b14484355ec24e3713fd5a84bfdbb044400ebd3 Mon Sep 17 00:00:00 2001 From: Jesse Washburn <142361664+jessewashburn@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:33:43 -0400 Subject: [PATCH 12/12] linting fix --- .../reports/reports-detail-activities.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/manager-dashboard/reports/reports-detail-activities.component.html b/src/app/manager-dashboard/reports/reports-detail-activities.component.html index 69214fcf3d..5eaa1b3e54 100644 --- a/src/app/manager-dashboard/reports/reports-detail-activities.component.html +++ b/src/app/manager-dashboard/reports/reports-detail-activities.component.html @@ -72,4 +72,4 @@ - \ No newline at end of file +