feat(falukant): implement score threshold logic and enhance UI feedback for certificate progression
All checks were successful
Deploy to production / deploy (push) Successful in 3m3s
All checks were successful
Deploy to production / deploy (push) Successful in 3m3s
- Added a new function to calculate score thresholds based on certificate levels, improving the logic for determining promotion eligibility. - Updated the FalukantService to include new properties for score and requirement checks, enhancing the decision-making process for certificate readiness. - Enhanced the OverviewView component to display detailed hints and states regarding certificate progression, providing users with clearer feedback on their status. - Localized new strings in multiple languages to support the updated UI elements and hints, improving user experience across different languages.
This commit is contained in:
@@ -208,6 +208,14 @@ function getTargetCertificateByScore(score) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
function getScoreThresholdForCertificate(level) {
|
||||
if (level >= 5) return 3.8;
|
||||
if (level === 4) return 2.8;
|
||||
if (level === 3) return 1.8;
|
||||
if (level === 2) return 0.9;
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function calcRegionalSellPrice(product, knowledgeFactor, regionId, worthPercent = null) {
|
||||
// Wenn worthPercent nicht übergeben wurde, hole es aus der Datenbank
|
||||
if (worthPercent === null) {
|
||||
@@ -2944,6 +2952,7 @@ class FalukantService extends BaseService {
|
||||
const targetCertificate = getTargetCertificateByScore(score);
|
||||
const nextCertificate = Math.min(5, currentCertificate + 1);
|
||||
const nextThreshold = CERTIFICATE_THRESHOLDS[nextCertificate] || null;
|
||||
const nextScoreThreshold = getScoreThresholdForCertificate(nextCertificate);
|
||||
|
||||
const currentValues = {
|
||||
avgKnowledge,
|
||||
@@ -3000,17 +3009,22 @@ class FalukantService extends BaseService {
|
||||
}] : []),
|
||||
] : [];
|
||||
|
||||
const minimumRequirementsMet = nextRequirements.every((requirement) => requirement.met)
|
||||
&& (!statusRequirement || statusRequirement.fulfilled);
|
||||
const scoreRequirementMet = nextCertificate <= targetCertificate;
|
||||
|
||||
return {
|
||||
currentCertificate,
|
||||
nextCertificate,
|
||||
score: Number(score.toFixed(2)),
|
||||
targetCertificate,
|
||||
nextScoreThreshold,
|
||||
currentValues,
|
||||
nextRequirements,
|
||||
statusRequirement,
|
||||
readyForNextCertificate: nextCertificate <= targetCertificate
|
||||
&& nextRequirements.every((requirement) => requirement.met)
|
||||
&& (!statusRequirement || statusRequirement.fulfilled),
|
||||
scoreRequirementMet,
|
||||
minimumRequirementsMet,
|
||||
readyForNextCertificate: scoreRequirementMet && minimumRequirementsMet,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -130,8 +130,21 @@
|
||||
"levelMatrix": "Mga produkto per certificate level",
|
||||
"levelLabel": "Level {level}",
|
||||
"score": "Puntos",
|
||||
"scoreGate": "Kinahanglan nga puntos para sa sunod nga level",
|
||||
"ready": "Andam na para sa sunod nga promotion",
|
||||
"notReady": "Wala pa maabot ang mga kinahanglanon",
|
||||
"state": {
|
||||
"ready": "Pwede na ang promotion sumala sa daemon",
|
||||
"minimumsMetScoreBlocked": "Naabot na ang minimum, pero gipugngan pa sa puntos",
|
||||
"scoreMetMinimumsMissing": "Sakto na ang puntos, pero kulang pa ang minimum nga kinahanglanon",
|
||||
"notReady": "Dili pa andam para sa promotion"
|
||||
},
|
||||
"hint": {
|
||||
"ready": "Para sa level {next}, naabot na ang minimum nga mga kinahanglanon ug ang score threshold nga {threshold}.",
|
||||
"minimumsMetScoreBlocked": "Naabot na ang makita nga minimum nga mga kinahanglanon para sa level {next}, pero motugot lang ang daemon sa promotion kung maabot sa weighted score ang threshold nga {threshold}. Sa pagkakaron, kutob ra sa level {target} ang score.",
|
||||
"scoreMetMinimumsMissing": "Ang weighted score igo na unta para sa level {next}, pero naa pay kulang nga minimum nga kinahanglanon.",
|
||||
"notReady": "Para sa level {next}, kinahanglan maabot ang minimum nga mga kinahanglanon ug ang score threshold nga {threshold}."
|
||||
},
|
||||
"factors": "Karon nga mga bili",
|
||||
"requirements": "Mga kinahanglanon sa sunod nga level"
|
||||
},
|
||||
|
||||
@@ -150,8 +150,21 @@
|
||||
"levelMatrix": "Produkte nach Zertifikatsstufe",
|
||||
"levelLabel": "Stufe {level}",
|
||||
"score": "Wertung",
|
||||
"scoreGate": "Wertungsgrenze für die nächste Stufe",
|
||||
"ready": "Für den nächsten Aufstieg bereit",
|
||||
"notReady": "Bedingungen noch nicht erfüllt",
|
||||
"state": {
|
||||
"ready": "Aufstieg aus Daemon-Sicht möglich",
|
||||
"minimumsMetScoreBlocked": "Mindestanforderungen erfüllt, Wertung blockiert noch",
|
||||
"scoreMetMinimumsMissing": "Wertung reicht, Mindestanforderungen fehlen noch",
|
||||
"notReady": "Noch nicht aufstiegsbereit"
|
||||
},
|
||||
"hint": {
|
||||
"ready": "Für Stufe {next} sind sowohl die Mindestanforderungen als auch die Wertungsgrenze von {threshold} erreicht.",
|
||||
"minimumsMetScoreBlocked": "Die sichtbaren Mindestanforderungen für Stufe {next} sind erfüllt, aber der Daemon lässt den Aufstieg erst zu, wenn die gewichtete Wertung die Schwelle {threshold} erreicht. Aktuell reicht die Wertung nur bis Stufe {target}.",
|
||||
"scoreMetMinimumsMissing": "Die gewichtete Wertung reicht grundsätzlich für Stufe {next}, aber mindestens eine Mindestanforderung ist noch nicht erfüllt.",
|
||||
"notReady": "Für Stufe {next} müssen sowohl die Mindestanforderungen als auch die Wertungsgrenze von {threshold} erfüllt sein."
|
||||
},
|
||||
"factors": "Aktuelle Werte",
|
||||
"requirements": "Bedingungen für die nächste Stufe",
|
||||
"factor": {
|
||||
|
||||
@@ -131,8 +131,21 @@
|
||||
"levelMatrix": "Products by certificate level",
|
||||
"levelLabel": "Level {level}",
|
||||
"score": "Score",
|
||||
"scoreGate": "Score threshold for the next level",
|
||||
"ready": "Ready for the next promotion",
|
||||
"notReady": "Requirements not met yet",
|
||||
"state": {
|
||||
"ready": "Promotion possible from the daemon's perspective",
|
||||
"minimumsMetScoreBlocked": "Minimum requirements met, but score still blocks promotion",
|
||||
"scoreMetMinimumsMissing": "Score is sufficient, minimum requirements still missing",
|
||||
"notReady": "Not ready for promotion yet"
|
||||
},
|
||||
"hint": {
|
||||
"ready": "For level {next}, both the minimum requirements and the score threshold of {threshold} are met.",
|
||||
"minimumsMetScoreBlocked": "The visible minimum requirements for level {next} are met, but the daemon will only allow promotion once the weighted score reaches the threshold {threshold}. Right now the score only reaches level {target}.",
|
||||
"scoreMetMinimumsMissing": "The weighted score is generally high enough for level {next}, but at least one minimum requirement is still missing.",
|
||||
"notReady": "For level {next}, both the minimum requirements and the score threshold of {threshold} must be met."
|
||||
},
|
||||
"factors": "Current values",
|
||||
"requirements": "Requirements for the next level",
|
||||
"factor": {
|
||||
|
||||
@@ -136,8 +136,21 @@
|
||||
"levelMatrix": "Productos por nivel de certificado",
|
||||
"levelLabel": "Nivel {level}",
|
||||
"score": "Puntuación",
|
||||
"scoreGate": "Umbral de puntuación para el siguiente nivel",
|
||||
"ready": "Listo para el siguiente ascenso",
|
||||
"notReady": "Condiciones aún no cumplidas",
|
||||
"state": {
|
||||
"ready": "Ascenso posible desde la perspectiva del daemon",
|
||||
"minimumsMetScoreBlocked": "Requisitos mínimos cumplidos, pero la puntuación aún bloquea el ascenso",
|
||||
"scoreMetMinimumsMissing": "La puntuación alcanza, pero aún faltan requisitos mínimos",
|
||||
"notReady": "Todavía no está listo para ascender"
|
||||
},
|
||||
"hint": {
|
||||
"ready": "Para el nivel {next} se cumplen tanto los requisitos mínimos como el umbral de puntuación de {threshold}.",
|
||||
"minimumsMetScoreBlocked": "Los requisitos mínimos visibles para el nivel {next} están cumplidos, pero el daemon solo permitirá el ascenso cuando la puntuación ponderada alcance el umbral {threshold}. Ahora mismo la puntuación solo alcanza hasta el nivel {target}.",
|
||||
"scoreMetMinimumsMissing": "La puntuación ponderada ya sería suficiente para el nivel {next}, pero todavía falta al menos un requisito mínimo.",
|
||||
"notReady": "Para el nivel {next} deben cumplirse tanto los requisitos mínimos como el umbral de puntuación de {threshold}."
|
||||
},
|
||||
"factors": "Valores actuales",
|
||||
"requirements": "Condiciones para el siguiente nivel",
|
||||
"factor": {
|
||||
|
||||
@@ -112,12 +112,13 @@
|
||||
<div class="certificate-panel__score">
|
||||
<span>{{ $t('falukant.overview.certificate.score') }}</span>
|
||||
<strong>{{ certificateProgress.score }}</strong>
|
||||
<span class="certificate-panel__state" :class="{ 'is-ready': certificateProgress.readyForNextCertificate }">
|
||||
{{ certificateProgress.readyForNextCertificate
|
||||
? $t('falukant.overview.certificate.ready')
|
||||
: $t('falukant.overview.certificate.notReady') }}
|
||||
<span class="certificate-panel__state" :class="certificateProgressStateClass">
|
||||
{{ $t(`falukant.overview.certificate.state.${certificateProgressStateKey}`) }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="certificate-panel__hint">
|
||||
{{ certificateProgressHint }}
|
||||
</p>
|
||||
|
||||
<div class="certificate-panel__grid">
|
||||
<article class="certificate-panel__block">
|
||||
@@ -157,6 +158,13 @@
|
||||
<article class="certificate-panel__block">
|
||||
<h4>{{ $t('falukant.overview.certificate.requirements') }}</h4>
|
||||
<div class="certificate-requirements">
|
||||
<div
|
||||
class="certificate-requirement"
|
||||
:class="{ 'is-met': certificateProgress.scoreRequirementMet }"
|
||||
>
|
||||
<span>{{ $t('falukant.overview.certificate.scoreGate') }}</span>
|
||||
<strong>{{ formatCertificateRequirement(certificateProgress.score, certificateProgress.nextScoreThreshold) }}</strong>
|
||||
</div>
|
||||
<div
|
||||
v-for="requirement in certificateProgress.nextRequirements"
|
||||
:key="requirement.type"
|
||||
@@ -438,6 +446,52 @@ export default {
|
||||
products: entry.products.map((productKey) => this.$t(`falukant.product.${productKey}`)),
|
||||
}));
|
||||
},
|
||||
certificateProgressStateKey() {
|
||||
const progress = this.certificateProgress;
|
||||
if (!progress) return 'notReady';
|
||||
if (progress.readyForNextCertificate) return 'ready';
|
||||
if (progress.minimumRequirementsMet && !progress.scoreRequirementMet) return 'minimumsMetScoreBlocked';
|
||||
if (!progress.minimumRequirementsMet && progress.scoreRequirementMet) return 'scoreMetMinimumsMissing';
|
||||
return 'notReady';
|
||||
},
|
||||
certificateProgressStateClass() {
|
||||
switch (this.certificateProgressStateKey) {
|
||||
case 'ready':
|
||||
return 'is-ready';
|
||||
case 'minimumsMetScoreBlocked':
|
||||
return 'is-warning';
|
||||
case 'scoreMetMinimumsMissing':
|
||||
return 'is-info';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
certificateProgressHint() {
|
||||
const progress = this.certificateProgress;
|
||||
if (!progress) return '';
|
||||
if (progress.readyForNextCertificate) {
|
||||
return this.$t('falukant.overview.certificate.hint.ready', {
|
||||
next: progress.nextCertificate,
|
||||
threshold: this.formatCertificateValue(progress.nextScoreThreshold, 1),
|
||||
});
|
||||
}
|
||||
if (progress.minimumRequirementsMet && !progress.scoreRequirementMet) {
|
||||
return this.$t('falukant.overview.certificate.hint.minimumsMetScoreBlocked', {
|
||||
next: progress.nextCertificate,
|
||||
target: progress.targetCertificate,
|
||||
threshold: this.formatCertificateValue(progress.nextScoreThreshold, 1),
|
||||
});
|
||||
}
|
||||
if (!progress.minimumRequirementsMet && progress.scoreRequirementMet) {
|
||||
return this.$t('falukant.overview.certificate.hint.scoreMetMinimumsMissing', {
|
||||
next: progress.nextCertificate,
|
||||
});
|
||||
}
|
||||
return this.$t('falukant.overview.certificate.hint.notReady', {
|
||||
next: progress.nextCertificate,
|
||||
threshold: this.formatCertificateValue(progress.nextScoreThreshold, 1),
|
||||
});
|
||||
},
|
||||
routineActions() {
|
||||
return [
|
||||
{
|
||||
@@ -820,7 +874,7 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.certificate-panel__state {
|
||||
@@ -838,6 +892,21 @@ export default {
|
||||
color: #2f6b3d;
|
||||
}
|
||||
|
||||
.certificate-panel__state.is-warning {
|
||||
background: rgba(185, 99, 24, 0.16);
|
||||
color: #8d5412;
|
||||
}
|
||||
|
||||
.certificate-panel__state.is-info {
|
||||
background: rgba(34, 96, 164, 0.14);
|
||||
color: #21598f;
|
||||
}
|
||||
|
||||
.certificate-panel__hint {
|
||||
margin: 0 0 16px;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.certificate-panel__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
Reference in New Issue
Block a user