Skip to content

fix(dashboard): calculate usability percentage from real evaluator data#1854

Open
KishoreMuruganantham wants to merge 94 commits intoruxailab:masterfrom
KishoreMuruganantham:fix/usability-percentage-hardcoded-1852
Open

fix(dashboard): calculate usability percentage from real evaluator data#1854
KishoreMuruganantham wants to merge 94 commits intoruxailab:masterfrom
KishoreMuruganantham:fix/usability-percentage-hardcoded-1852

Conversation

@KishoreMuruganantham
Copy link

@KishoreMuruganantham KishoreMuruganantham commented Mar 10, 2026

Summary

  • Replace hardcoded return 75 in UsabilityResults.vue with a real calculation using the existing statistics() utility from statistics.js
  • Averages all evaluators' result values, returning 0 when no evaluators exist (prevents NaN)
  • Color coding and status text automatically correct themselves since they already derive from usabilityPercentage

Closes #1852

Before vs After

Before After
Always shows hardcoded 75% regardless of data Shows real averaged percentage from evaluator scores

before-after

The visual component (circular progress, colors, status text) is unchanged — only the underlying data source was fixed from a hardcoded value to a real calculation using statistics()

Test plan

  • Verify dashboard shows 0% usability when no evaluators have submitted answers
  • Verify dashboard shows correct averaged percentage when multiple evaluators have submitted
  • Verify color coding updates correctly: green (≥80%), yellow (≥60%), red (<60%)
  • Verify status text matches the percentage thresholds (Excellent/Acceptable/Needs improvement)

chanchal430 and others added 30 commits January 30, 2026 19:45
Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
… verification redirects. Prevents authenticated users from seeing signin page after refresh.

Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Refactor router logic to allow access to public pages for logged-in users with unverified emails.
…n should directly redirect to opeen room

Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
Signed-off-by: Rakshit Yadav <yadavrakshit60@gmail.com>
…existent test.answers

The four Beta manager dashboard components (StudyOverview, ParticipantsInfo,
TasksInfo, StorageInfo) read answer data from props.test.answers, but the
Study model does not define an answers property — it only stores answersDocId.
Answer data lives in the Answer Vuex store module at
store.getters.testAnswerDocument.taskAnswers.

Changes:
- Switch all four components to read from the Answer store module
- Fix task iteration to use Object.values() for Firestore maps
- Remove task.attempted gate that undercounted metrics
- Fix notStartedParticipants going negative when admin takes test
… align metrics with GeneralAnalytics

- Add allAnswersList getter to Answer.js to centralize duplicated answer-filtering logic
- Refactor StudyOverview, ParticipantsInfo, TasksInfo, StorageInfo to use the new getter
- Align overallCompletionRate with calculateEffectiveness (completed/all tasks, no attempted gate)
- Align taskSuccessRate with getTasksPerformance (task.completed && task.success !== false)
- Align averageTaskDuration and averageCompletionTime with averageTimePerTask (count all tasks, no gates)
…table sorting via stable keys and custom-key-sort

- Use locale.value from useI18n() instead of hardcoded 'es' for date formatting
- Move id out of baseFile, generate stable unique IDs per file using testId_answerIdx_taskIdx_fileType
- Store raw timestamp in date field for sorting, display formatted string via template slot
- Add custom-key-sort with natural string sort for names and numeric sort for date/size
- Add custom-filter for search on displayed date strings and study names
- Add item-value='id' to v-data-table for stable row key tracking
- Replaces underscores with hyphens in locale string
- Prevents Intl.DateTimeFormat from throwing on invalid BCP 47 tag
…ing-locale

# Conflicts:
#	src/shared/utils/dateUtils.js
jvJUCA and others added 15 commits March 6, 2026 11:12
…re-silent-errors

fix(store): add error handling to Answer.js actions that silently swa…
…ardize-cloud-functions-logging

refactor(functions): replace raw console.log with project logger utility
fix:  Submenu text space fixed
…n-refresh

Fix: Admin redirected to test steps on refresh and after step completion in moderated sessions
…usertestview

chore: remove debug console logs from attachMediaToTasks
…ialog-ui

fix: dialog header spacing in heuristic edit dialogs
…n/develop/axios-1.13.6

chore(deps): bump axios from 1.13.5 to 1.13.6
…wer-errors-all-languages

feat(locales): add missing error translation keys to all languages
Replace hardcoded 75% return value with actual calculation using the
existing statistics() utility, averaging all evaluators' scores.

Closes ruxailab#1852

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 10, 2026 10:24
@github-actions github-actions bot added accessibility Issues related to accessibility method backend ci/cd Documentation Improvements or additions to documentation fix heuristic high-complexity size/XL testing ui/ux user-test Issues related to user test labels Mar 10, 2026
@sonarqubecloud
Copy link

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the heuristic manager dashboard’s usability percentage to be computed from real evaluator data, and also introduces a broad set of additional UX, analytics, auth/email-verification, routing, i18n tooling, and Firebase Functions updates across the app.

Changes:

  • Replace hardcoded heuristic dashboard usability % with a computed average from evaluator statistics.
  • Add time-spent aggregation/formatting (heuristic + user tests) and refactor heuristic answer/statistics UI into new components.
  • Introduce an email verification flow (new view/route, auth-store enforcement, email template/function changes) plus various UserTest/manager UI refinements and i18n tooling updates.

Reviewed changes

Copilot reviewed 66 out of 70 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
weight_function/README.md Markdown reformatting/normalization.
tests/unit/EmailController.spec.js Refactors axios mocking/env setup for EmailController tests.
src/ux/accessibility/view/automatic/Answers.vue Uses managed listener helper for iframe event handling + cleanup.
src/ux/UserTest/views/UserTestView.vue Adds pre/post test optionality and stepper/index logic updates; removes debug logs.
src/ux/UserTest/views/ModeratedTestView.vue Adjusts step progression and video call display behavior.
src/ux/UserTest/components/steps/WelcomeStep.vue Stepper display updated to reflect optional pre/post test steps.
src/ux/UserTest/components/steps/PreTestStep.vue Redesigns pre-test UI into paged questions with progress and navigation.
src/ux/UserTest/components/steps/PreTasksStep.vue Formatting/cleanup for script/template.
src/ux/UserTest/components/steps/PostTestStep.vue Redesigns post-test UI into paged questions with progress and navigation.
src/ux/UserTest/components/manager/TasksInfo.vue Switches manager analytics to centralized Answer store getter and recalculates rates/times.
src/ux/UserTest/components/manager/StudyOverview.vue Switches participant metrics to Answer store + accepted cooperators logic.
src/ux/UserTest/components/manager/StorageInfo.vue Switches to Answer store list getter for storage calculations.
src/ux/UserTest/components/manager/ParticipantsInfo.vue Recomputes participant counts from Answer store + accepted cooperators.
src/ux/UserTest/components/dialogs/SessionAnalyticsDialog.vue Uses managed listener helper + cleanup for video events/RAF.
src/ux/UserTest/components/VideoCall.vue Moves hardcoded UI strings to i18n keys; adds i18n usage in script.
src/ux/Heuristic/views/HeuristicTestView.vue Adds heuristic shuffling for fresh runs, deterministic order for resumed runs.
src/ux/Heuristic/views/EditTest.vue Makes WeightTable emit change events to mark unsaved changes.
src/ux/Heuristic/utils/statistics.js Adds time parsing/formatting helpers; attaches timeSpentMs into heuristic stats.
src/ux/Heuristic/store/Heuristic.js Simplifies getter signature (removes unused params).
src/ux/Heuristic/components/weights_evaluation/WeightTable.vue Switches to immediate persistence via watcher; adjusts v-model handling and initialization.
src/ux/Heuristic/components/statistics/StatisticsSummaryCard.vue New extracted statistics summary card component.
src/ux/Heuristic/components/statistics/HeuristicsDataCard.vue New extracted heuristics data card with tabs (incl. time-by-heuristics).
src/ux/Heuristic/components/statistics/EvaluatorsAndGraphicsCard.vue New extracted evaluators table/graph card component.
src/ux/Heuristic/components/manager/UsabilityResults.vue Replaces hardcoded 75% with computed average from evaluator statistics.
src/ux/Heuristic/components/HeuristicsTestAnswer.vue Refactors into new subcomponents; adds time-by-heuristics table.
src/ux/Heuristic/components/HeuristicsTable.vue Minor dialog style adjustments.
src/shared/views/ReportView.vue Adds “Total Time” column/section; computes totals for heuristic and user test reports.
src/shared/utils/dateUtils.js Normalizes locale strings (e.g. pt_BRpt-BR) before Intl formatting.
src/shared/store/Answer.js Adds allAnswersList getter; improves error handling + adds total time to evaluator stats.
src/shared/constants/methodDefinitions.js Adds study type normalization for legacy values and uses it in method resolution.
src/shared/composables/useManagedListeners.js New managed event-listener composable/factory with cleanup support.
src/router/modules/public.js Adds /verify-email public route.
src/features/ux_creation/StudyDetailsForm.vue Normalizes test type when instantiating/redirecting from template creation.
src/features/navigation/components/navbarSections/StorageSection.vue Adds custom sort/filter, stable IDs, and locale-aware date formatting in storage table.
src/features/navigation/components/DashboardSidebar.vue Adjusts subsection indentation/icon spacing styles.
src/features/dashboard/components/StatsCards.vue Formats storage MB value with fixed precision.
src/features/auth/views/VerifyEmailView.vue New email verification page (check status, resend, continue, sign out).
src/features/auth/views/SignUpView.vue Redirects to verify-email after signup.
src/features/auth/views/SignInView.vue Redirects to verify-email when EMAIL_NOT_VERIFIED; adds mobile logo styling.
src/features/auth/store/Auth.js Enforces email verification on sign-in; sends verification email on signup; adds action to resend verification.
src/features/auth/controllers/AuthController.js Adds sendVerificationEmail using EmailController.
src/controllers/StudyController.js Fixes notification removal call placement when removing cooperators.
src/app/router/index.js Adds email-verification gate/redirect logic in global beforeEach; removes template access checks.
src/app/plugins/locales/zh.json Adds auth/video-call related keys and some error keys.
src/app/plugins/locales/ru.json Adds auth/video-call related keys and some error keys.
src/app/plugins/locales/pt_br.json Adds auth/video-call related keys and some error keys.
src/app/plugins/locales/ja.json Adds auth/video-call related keys and some error keys.
src/app/plugins/locales/hi.json Adds auth/video-call related keys and some error keys.
src/app/plugins/locales/fr.json Adds auth/video-call related keys and some error keys.
src/app/plugins/locales/es.json Adds auth/video-call related keys and some error keys.
src/app/plugins/locales/en.json Adds auth/video-call related keys and some error keys.
src/app/plugins/locales/de.json Adds auth/video-call related keys and some error keys.
src/app/plugins/locales/ar.json Adds auth/video-call related keys and some error keys.
package.json Dependency bumps (axios, vue, vuetify, eslint ecosystem, etc.).
i18n-diff-guard.mjs Adds a diff-based i18n key validator script.
functions/src/templates/mails/invitations.html Updates logo asset URL.
functions/src/templates/mails/emailVerification.html Adds new email verification HTML template.
functions/src/scheduled/cleanupGhostSessions.js Refactors scheduled cleanup with structured logging.
functions/src/https/eyeTracking.js Refactors formatting and makes CORS origins configurable via env.
functions/src/https/email.js Extends email function to support email verification template and callable payload shape.
functions/src/helpers/addSubTypeInUser.js Refactors helper with structured logging + formatting.
functions/src/f.firebase.js Makes region getter dynamic; loads dotenv config.
functions/.gitignore Ignores functions env files, keeps example.
functions/.env.example Adds EYE_LAB_CORS_ORIGINS to example env.
README.md Updates support/contact links and repository link.
CONTRIBUTING.md Expands contributor guidance (labels, hooks, formatting/linting notes).
.husky/pre-commit Switches i18n guard to .mjs.
.github/workflows/i18n-diff-guard.yml Switches i18n guard to .mjs.
.firebaserc Formatting-only change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +10 to +19
beforeEach(() => {
jest.clearAllMocks()
emailController = new EmailController()
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
process.env.VUE_APP_CLOUD_FUNCTIONS_URL = 'http://localhost'
})

afterEach(() => {
consoleErrorSpy.mockRestore()
})
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test mutates process.env.VUE_APP_CLOUD_FUNCTIONS_URL in beforeEach but no longer restores the original environment in afterEach, which can leak state into other tests. Please snapshot/restore process.env (or at least this key) in afterEach like the previous version did.

Copilot uses AI. Check for mistakes.
Comment on lines 56 to 88
<script setup>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { statistics } from '@/ux/Heuristic/utils/statistics'

const props = defineProps({
test: {
type: Object,
required: true,
},
})

const router = useRouter()
const { t } = useI18n()

// Navigate to answers section
const navigateToAnswers = () => {
if (props.test?.id) {
router.push(`/heuristic/answer/${props.test.id}`)
}
}

// Computed properties
const usabilityPercentage = computed(() => {
// Por ahora devolvemos 75% como solicitado
// En el futuro esto se calculará basado en las respuestas reales
return 75
const resultEvaluator = statistics()
if (!resultEvaluator.length) return 0
const total = resultEvaluator.reduce(
(sum, ev) => sum + parseFloat(ev.result),
0,
)
return Math.round(total / resultEvaluator.length)
})
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description/title focus on fixing the hardcoded dashboard usability percentage, but this PR also introduces substantial unrelated changes (email verification flow, router guard changes, new i18n tooling, multiple UX/UserTest refactors). Please either narrow this PR to the usability calculation or update the PR description (and ideally split into separate PRs) so review and rollback risk are manageable.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +51
const authUser = await store.dispatch('autoSignIn')
user = store.state.Auth.user
// If user is logged in but email not verified, redirect to verify-email
if (authUser && authUser.emailVerified === false && !publicPages.includes(to.path)) {
return next('/verify-email')
}
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new email-verification redirect block has unbalanced braces/indentation, which will make the router guard invalid JS (the if (authUser && ...) block is not properly closed, and if (!user) is not closed cleanly). Please fix the braces so the guard compiles and executes as intended.

Copilot uses AI. Check for mistakes.
Comment on lines 26 to 61
router.beforeEach(async (to, from, next) => {
const { authorize = [] } = to.meta || {}
let user = store.state.Auth.user

// Special handling for accessibility preview routes - allow complete public access
const isAccessibilityPreview =
to.path.includes('/accessibility/') && to.path.includes('/preview/')

if (isAccessibilityPreview) {
return next() // Allow immediate access without any checks
}

// Allow access to public pages even if user is logged in but email not verified
const publicPages = ['/signin', '/signup', '/verify-email', '/forgot-password']
if (publicPages.includes(to.path)) {
return next()
}

if (!user) {
await store.dispatch('autoSignIn')
const authUser = await store.dispatch('autoSignIn')
user = store.state.Auth.user
// If user is logged in but email not verified, redirect to verify-email
if (authUser && authUser.emailVerified === false && !publicPages.includes(to.path)) {
return next('/verify-email')
}
}

if (to.path === '/') return next(redirect())

if (authorize.length && to.path !== '/signin' && !to.params.token) {
if (!user || !authorize.includes(user.accessLevel)) {
return next(redirect())
}
}

if (to.meta?.templateAccess && to.params?.id) {
const template = await store.dispatch('getTemplateById', to.params.id)

if (!template) {
store.commit('SET_TOAST', {
type: 'error',
message: 'Template not found.',
})
return next('/admin')
}

const ownerId = template.header?.templateAuthor?.userDocId
const isOwner = ownerId && user?.id === ownerId
const isPublic = Boolean(template.header?.isTemplatePublic)

if (!isOwner && !isPublic) {
store.commit('SET_TOAST', {
type: 'error',
message: 'You do not have permission to access this template.',
})
return next('/admin')
}

const canonicalPath = getTemplateManagerPath(template)
if (canonicalPath && canonicalPath !== to.path) {
if (to.meta?.templateSection === 'manager') {
return next({ path: canonicalPath, query: to.query, replace: true })
}
}

if (to.meta?.templateOwnerOnly && !isOwner) {
store.commit('SET_TOAST', {
type: 'error',
message: 'Only template owner can access configuration.',
})
return next(canonicalPath || '/admin')
}
}

if (user && ['/signin', '/signup'].includes(to.path)) {
return next(redirect())
}

next()
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Routes in src/router/modules/admin.js still set meta.templateAccess / meta.templateOwnerOnly, but the corresponding access-control logic was removed from this global beforeEach guard. This looks like a security/authorization regression (template pages may become accessible without the intended checks). Please reintroduce the templateAccess handling (or move it into per-route guards) so those meta flags are enforced again.

Copilot uses AI. Check for mistakes.
Comment on lines +179 to +184
async logout() {
try {
await this.logout()
this.$router.push('/signin')
} catch {}
},
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

methods maps the Vuex logout action and also defines a logout() method that calls this.logout(), which will recurse into itself and never dispatch the action. Rename either the mapped action (e.g., mapActions({ doLogout: 'logout' })) or the method, and call the action explicitly before routing.

Copilot uses AI. Check for mistakes.
Comment on lines 1 to +30
import EmailController from '@/shared/controllers/EmailController'
import axios from 'axios'

jest.mock('axios', () => ({
post: jest.fn()
}))
jest.mock('axios')

describe('EmailController', () => {
let emailController
let consoleErrorSpy
const originalEnv = process.env

beforeEach(() => {
jest.clearAllMocks()
emailController = new EmailController()
process.env = {
...originalEnv,
VUE_APP_CLOUD_FUNCTIONS_URL: 'https://cloud-functions.example.com'
}
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
let emailController
let consoleErrorSpy

beforeEach(() => {
jest.clearAllMocks()
emailController = new EmailController()
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
process.env.VUE_APP_CLOUD_FUNCTIONS_URL = 'http://localhost'
})

afterEach(() => {
consoleErrorSpy.mockRestore()
})

describe('Structure', () => {
it('should have send method', () => {
expect(typeof emailController.send).toBe('function')
})
})

afterEach(() => {
process.env = originalEnv
consoleErrorSpy.mockRestore()
describe('send', () => {
it('should call axios.post with correct url and payload', async () => {
axios.post.mockResolvedValue({ data: {} })

Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jest.mock('axios') does not define axios.post unless you have a manual Jest mock for axios (none found in the repo). These tests call axios.post.mockResolvedValue(...), which will fail when post is undefined. Use an explicit factory mock (like in AuthController.spec.js) or set axios.post = jest.fn() in the test setup.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

accessibility Issues related to accessibility method backend ci/cd Documentation Improvements or additions to documentation fix heuristic high-complexity size/XL testing ui/ux user-test Issues related to user test

Projects

None yet

Development

Successfully merging this pull request may close these issues.