diff --git a/backend/controllers/falukantController.js b/backend/controllers/falukantController.js index d1df872..a158980 100644 --- a/backend/controllers/falukantController.js +++ b/backend/controllers/falukantController.js @@ -155,7 +155,16 @@ class FalukantController { this.advanceNobility = this._wrapWithUser((userId) => this.service.advanceNobility(userId)); this.getHealth = this._wrapWithUser((userId) => this.service.getHealth(userId)); - this.healthActivity = this._wrapWithUser((userId, req) => this.service.healthActivity(userId, req.body.measureTr)); + this.healthActivity = this._wrapWithUser(async (userId, req) => { + try { + return await this.service.healthActivity(userId, req.body.measureTr); + } catch (e) { + if (e && e.name === 'PreconditionError' && e.message === 'tooClose') { + throw { status: 412, message: 'tooClose', retryAt: e.meta?.retryAt }; + } + throw e; + } + }); this.getPoliticsOverview = this._wrapWithUser((userId) => this.service.getPoliticsOverview(userId)); this.getOpenPolitics = this._wrapWithUser((userId) => this.service.getOpenPolitics(userId)); diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 5bcb743..9b07b63 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -3662,7 +3662,11 @@ class FalukantService extends BaseService { limit: 1 }); if (lastHealthActivity) { - throw new Error('too close'); + // Berechne, wann die nächste Maßnahme möglich ist (24 Stunden nach der letzten) + const retryAt = new Date(lastHealthActivity.createdAt.getTime() + 24 * 60 * 60 * 1000); + const err = new PreconditionError('tooClose'); + err.meta = { retryAt: retryAt.toISOString() }; + throw err; } const activityObject = FalukantService.HEALTH_ACTIVITIES.find((a) => a.tr === activity); if (!activityObject) { diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index bf8606b..6d1d99d 100644 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -895,7 +895,12 @@ "drunkOfLife": "Trunk des Lebens", "barber": "Barbier" }, - "choose": "Bitte auswählen" + "choose": "Bitte auswählen", + "errors": { + "tooClose": "Du kannst nicht so oft Maßnahmen durchführen.", + "generic": "Ein Fehler ist aufgetreten." + }, + "nextMeasureAt": "Nächste Maßnahme ab" }, "politics": { "title": "Politik", diff --git a/frontend/src/i18n/locales/en/falukant.json b/frontend/src/i18n/locales/en/falukant.json index 9a2d59f..823d430 100644 --- a/frontend/src/i18n/locales/en/falukant.json +++ b/frontend/src/i18n/locales/en/falukant.json @@ -197,7 +197,32 @@ }, "nobility": { "cooldown": "You can only advance again on {date}." - }, + }, + "healthview": { + "title": "Health", + "age": "Age", + "status": "Health Status", + "measuresTaken": "Measures Taken", + "measure": "Measure", + "date": "Date", + "cost": "Cost", + "success": "Success", + "selectMeasure": "Select Measure", + "perform": "Perform", + "measures": { + "pill": "Pill", + "doctor": "Doctor Visit", + "witch": "Witch", + "drunkOfLife": "Elixir of Life", + "barber": "Barber" + }, + "choose": "Please select", + "errors": { + "tooClose": "You cannot perform measures so often.", + "generic": "An error occurred." + }, + "nextMeasureAt": "Next measure from" + }, "branchProduction": { "storageAvailable": "Free storage" }, diff --git a/frontend/src/views/falukant/HealthView.vue b/frontend/src/views/falukant/HealthView.vue index 3cb7e2d..d756a18 100644 --- a/frontend/src/views/falukant/HealthView.vue +++ b/frontend/src/views/falukant/HealthView.vue @@ -142,9 +142,23 @@ export default { this.selectedTr = ''; } catch (err) { console.error('Error performing measure', err); - const title = this.$t('falukant.healthview.title'); - const remoteMsg = err?.response?.data?.error || err?.message || String(err); - this.$root.$refs.messageDialog?.open(remoteMsg, title); + if (err?.response?.status === 412) { + const retryAtIso = err.response?.data?.retryAt; + const code = err.response?.data?.error || err.response?.data?.message; + if (retryAtIso) { + const retryStr = new Date(retryAtIso).toLocaleString(navigator.language, { + year: 'numeric', month: '2-digit', day: '2-digit', + hour: '2-digit', minute: '2-digit' + }); + const baseMsg = this.$t(`falukant.healthview.errors.${code}`); + this.$root.$refs.errorDialog?.open(`${baseMsg} — ${this.$t('falukant.healthview.nextMeasureAt')}: ${retryStr}`); + } else { + this.$root.$refs.errorDialog?.open(this.$t(`falukant.healthview.errors.${code}`)); + } + } else { + const code = err?.response?.data?.error || err?.message || 'generic'; + this.$root.$refs.errorDialog?.open(this.$t(`falukant.healthview.errors.${code}`) || this.$t('falukant.healthview.errors.generic')); + } } }, handleDaemonMessage(evt) {