Implement certificate progress feature in FalukantService and frontend: Add methods to calculate and retrieve certificate progress based on user attributes. Update localization files for English, German, and Spanish to include new terms related to certificate progress. Enhance OverviewView to display certificate details and requirements, improving user experience and clarity.
This commit is contained in:
@@ -96,6 +96,99 @@
|
||||
</button>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section v-if="certificateProgress" class="certificate-panel surface-card">
|
||||
<div class="certificate-panel__header">
|
||||
<div>
|
||||
<h3>{{ $t('falukant.overview.certificate.title') }}</h3>
|
||||
<p>{{ $t('falukant.overview.certificate.description') }}</p>
|
||||
</div>
|
||||
<div class="certificate-panel__badges">
|
||||
<span class="summary-card__label">{{ $t('falukant.overview.certificate.current') }}: {{ certificateProgress.currentCertificate }}</span>
|
||||
<span class="summary-card__label">{{ $t('falukant.overview.certificate.next') }}: {{ certificateProgress.nextCertificate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="certificate-panel__grid">
|
||||
<article class="certificate-panel__block">
|
||||
<h4>{{ $t('falukant.overview.certificate.factors') }}</h4>
|
||||
<div class="detail-list">
|
||||
<div class="detail-list__item">
|
||||
<span>{{ $t('falukant.overview.certificate.factor.avgKnowledge') }}</span>
|
||||
<strong>{{ formatCertificateValue(certificateProgress.currentValues.avgKnowledge, 1) }}</strong>
|
||||
</div>
|
||||
<div class="detail-list__item">
|
||||
<span>{{ $t('falukant.overview.certificate.factor.completedProductions') }}</span>
|
||||
<strong>{{ certificateProgress.currentValues.completedProductions }}</strong>
|
||||
</div>
|
||||
<div class="detail-list__item">
|
||||
<span>{{ $t('falukant.overview.certificate.factor.reputation') }}</span>
|
||||
<strong>{{ certificateProgress.currentValues.reputation }}</strong>
|
||||
</div>
|
||||
<div class="detail-list__item">
|
||||
<span>{{ $t('falukant.overview.certificate.factor.housePosition') }}</span>
|
||||
<strong>{{ certificateProgress.currentValues.housePosition }}</strong>
|
||||
</div>
|
||||
<div class="detail-list__item">
|
||||
<span>{{ $t('falukant.overview.certificate.factor.highestPoliticalOfficeRank') }}</span>
|
||||
<strong>{{ certificateProgress.currentValues.highestPoliticalOfficeRank }}</strong>
|
||||
</div>
|
||||
<div class="detail-list__item">
|
||||
<span>{{ $t('falukant.overview.certificate.factor.highestChurchOfficeRank') }}</span>
|
||||
<strong>{{ certificateProgress.currentValues.highestChurchOfficeRank }}</strong>
|
||||
</div>
|
||||
<div class="detail-list__item">
|
||||
<span>{{ $t('falukant.overview.certificate.factor.nobilityLevel') }}</span>
|
||||
<strong>{{ certificateProgress.currentValues.nobilityLevel }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="certificate-panel__block">
|
||||
<h4>{{ $t('falukant.overview.certificate.requirements') }}</h4>
|
||||
<div class="certificate-requirements">
|
||||
<div
|
||||
v-for="requirement in certificateProgress.nextRequirements"
|
||||
:key="requirement.type"
|
||||
class="certificate-requirement"
|
||||
:class="{ 'is-met': requirement.met }"
|
||||
>
|
||||
<span>{{ certificateRequirementLabel(requirement.type) }}</span>
|
||||
<strong>{{ formatCertificateRequirement(requirement.current, requirement.required) }}</strong>
|
||||
</div>
|
||||
<div
|
||||
v-if="certificateProgress.statusRequirement"
|
||||
class="certificate-requirement"
|
||||
:class="{ 'is-met': certificateProgress.statusRequirement.fulfilled }"
|
||||
>
|
||||
<span>{{ certificateStatusRequirementLabel(certificateProgress.statusRequirement.mode) }}</span>
|
||||
<strong>{{ certificateProgress.statusRequirement.metCount }}/{{ certificateProgress.statusRequirement.requiredCount }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul v-if="certificateProgress.statusRequirement?.options?.length" class="certificate-status-options">
|
||||
<li
|
||||
v-for="option in certificateProgress.statusRequirement.options"
|
||||
:key="option.type"
|
||||
:class="{ 'is-met': option.met }"
|
||||
>
|
||||
{{ certificateRequirementLabel(option.type) }}:
|
||||
{{ formatCertificateRequirement(option.current, option.required) }}
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Erben-Auswahl wenn kein Charakter vorhanden -->
|
||||
<div v-if="!falukantUser?.character" class="heir-selection-container">
|
||||
@@ -313,6 +406,9 @@ export default {
|
||||
stockEntryCount() {
|
||||
return this.allStock.length;
|
||||
},
|
||||
certificateProgress() {
|
||||
return this.falukantUser?.certificateProgress || null;
|
||||
},
|
||||
routineActions() {
|
||||
return [
|
||||
{
|
||||
@@ -553,6 +649,24 @@ export default {
|
||||
formatDate(timestamp) {
|
||||
return new Date(timestamp).toLocaleString();
|
||||
},
|
||||
formatCertificateValue(value, digits = 0) {
|
||||
if (value == null) return '---';
|
||||
return new Intl.NumberFormat(this.locale, {
|
||||
minimumFractionDigits: digits,
|
||||
maximumFractionDigits: digits,
|
||||
}).format(value);
|
||||
},
|
||||
formatCertificateRequirement(current, required) {
|
||||
const currentDigits = typeof current === 'number' && !Number.isInteger(current) ? 1 : 0;
|
||||
const requiredDigits = typeof required === 'number' && !Number.isInteger(required) ? 1 : 0;
|
||||
return `${this.formatCertificateValue(current, currentDigits)} / ${this.formatCertificateValue(required, requiredDigits)}`;
|
||||
},
|
||||
certificateRequirementLabel(type) {
|
||||
return this.$t(`falukant.overview.certificate.factor.${type}`);
|
||||
},
|
||||
certificateStatusRequirementLabel(mode) {
|
||||
return this.$t(`falukant.overview.certificate.statusMode.${mode}`);
|
||||
},
|
||||
async fetchPotentialHeirs() {
|
||||
this.loadingHeirs = true;
|
||||
try {
|
||||
@@ -649,6 +763,94 @@ export default {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.certificate-panel {
|
||||
margin-bottom: 16px;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.certificate-panel__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.certificate-panel__header p {
|
||||
margin: 6px 0 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.certificate-panel__badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.certificate-panel__score {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.certificate-panel__state {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
background: rgba(185, 99, 24, 0.12);
|
||||
color: #8d5412;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.certificate-panel__state.is-ready {
|
||||
background: rgba(68, 138, 86, 0.14);
|
||||
color: #2f6b3d;
|
||||
}
|
||||
|
||||
.certificate-panel__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.certificate-panel__block h4 {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.certificate-requirements {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.certificate-requirement {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius-md);
|
||||
background: rgba(185, 99, 24, 0.08);
|
||||
}
|
||||
|
||||
.certificate-requirement.is-met {
|
||||
background: rgba(68, 138, 86, 0.12);
|
||||
}
|
||||
|
||||
.certificate-status-options {
|
||||
margin: 12px 0 0;
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
.certificate-status-options li {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.certificate-status-options li.is-met {
|
||||
color: #2f6b3d;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.summary-card,
|
||||
.routine-card {
|
||||
padding: 18px;
|
||||
@@ -936,9 +1138,17 @@ export default {
|
||||
@media (max-width: 900px) {
|
||||
.falukant-summary-grid,
|
||||
.falukant-routine-grid,
|
||||
.certificate-panel__grid,
|
||||
.overviewcontainer {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.certificate-panel__header,
|
||||
.certificate-panel__score,
|
||||
.certificate-requirement {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.select-heir-button {
|
||||
|
||||
Reference in New Issue
Block a user