feat(TrainingGroup): add excludeFromQuickDiaryCreate feature and update related logic
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s

- Introduced a new boolean field `excludeFromQuickDiaryCreate` in the TrainingGroup model to control group visibility in quick diary creation.
- Updated the `updateTrainingGroup` service method to handle the new field, allowing for dynamic updates based on user input.
- Enhanced the TrainingTimesTab component to include a checkbox for excluding groups from quick diary creation, improving user interaction.
- Updated localization files to include new strings related to the exclude feature, ensuring clarity in multiple languages.
- Refactored logic in DiaryView and mobile app to consider the new exclusion criteria when suggesting training slots.
This commit is contained in:
Torsten Schulz (local)
2026-05-14 22:47:02 +02:00
parent 83294406a4
commit bf3e1af084
12 changed files with 239 additions and 63 deletions

View File

@@ -32,8 +32,7 @@ export const updateTrainingGroup = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId, groupId } = req.params;
const { name, sortOrder } = req.body;
const group = await trainingGroupService.updateTrainingGroup(userToken, clubId, groupId, name, sortOrder);
const group = await trainingGroupService.updateTrainingGroup(userToken, clubId, groupId, req.body);
res.status(200).json(group);
} catch (error) {
console.error('[updateTrainingGroup] - Error:', error);

View File

@@ -0,0 +1,4 @@
-- Trainingsgruppen vom Tagebuch-Schnellanlegen ausnehmen (optional)
ALTER TABLE training_group
ADD COLUMN exclude_from_quick_diary_create TINYINT(1) NOT NULL DEFAULT 0
COMMENT '1 = Gruppe bei Schnellanlegen-Terminsuche ignorieren';

View File

@@ -38,7 +38,13 @@ const TrainingGroup = sequelize.define('TrainingGroup', {
allowNull: false,
defaultValue: 0,
comment: 'Order for displaying groups'
}
},
excludeFromQuickDiaryCreate: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
comment: 'If true, group is skipped when suggesting/creating diary dates via quick-create'
},
}, {
tableName: 'training_group',
underscored: true,

View File

@@ -159,9 +159,11 @@ class TrainingGroupService {
return group;
}
async updateTrainingGroup(userToken, clubId, groupId, name, sortOrder) {
async updateTrainingGroup(userToken, clubId, groupId, body = {}) {
await checkAccess(userToken, clubId);
const { name, sortOrder, excludeFromQuickDiaryCreate } = body;
const group = await TrainingGroup.findOne({
where: { id: groupId, clubId },
});
@@ -169,25 +171,31 @@ class TrainingGroupService {
throw new HttpError('Gruppe nicht gefunden', 404);
}
// Preset-Gruppen können nicht umbenannt werden
if (group.isPreset && name !== group.name) {
throw new HttpError('Vorgaben-Gruppen können nicht umbenannt werden', 400);
}
// Prüfe, ob bereits eine andere Gruppe mit diesem Namen existiert
if (name !== group.name) {
const existing = await TrainingGroup.findOne({
where: { clubId, name, id: { [Op.ne]: groupId } },
});
if (existing) {
throw new HttpError('Eine Gruppe mit diesem Namen existiert bereits', 409);
if (name !== undefined && name !== null) {
// Preset-Gruppen können nicht umbenannt werden
if (group.isPreset && name !== group.name) {
throw new HttpError('Vorgaben-Gruppen können nicht umbenannt werden', 400);
}
if (name !== group.name) {
const existing = await TrainingGroup.findOne({
where: { clubId, name, id: { [Op.ne]: groupId } },
});
if (existing) {
throw new HttpError('Eine Gruppe mit diesem Namen existiert bereits', 409);
}
}
group.name = name;
}
group.name = name;
if (sortOrder !== undefined) {
if (sortOrder !== undefined && sortOrder !== null) {
group.sortOrder = sortOrder;
}
if (excludeFromQuickDiaryCreate !== undefined) {
group.excludeFromQuickDiaryCreate = Boolean(excludeFromQuickDiaryCreate);
}
await group.save();
return group;
}

View File

@@ -10,12 +10,22 @@
>
<div class="group-header">
<h3>{{ group.name }}</h3>
<button
@click="showAddTimeForm(group.id)"
class="btn-primary btn-small"
>
{{ $t('trainingTimesTab.addTime') }}
</button>
<div class="group-header-actions">
<label class="exclude-quick-diary-label">
<input
type="checkbox"
:checked="Boolean(group.excludeFromQuickDiaryCreate)"
@change="onExcludeQuickChange(group, $event)"
/>
<span>{{ $t('trainingTimesTab.excludeFromQuickDiaryCreate') }}</span>
</label>
<button
@click="showAddTimeForm(group.id)"
class="btn-primary btn-small"
>
{{ $t('trainingTimesTab.addTime') }}
</button>
</div>
</div>
<!-- Add Time Form -->
@@ -165,7 +175,12 @@ export default {
this.loading = true;
try {
const response = await apiClient.get(`/training-times/${this.currentClub}`);
this.groups = Array.isArray(response.data) ? response.data : [];
this.groups = Array.isArray(response.data)
? response.data.map((g) => ({
...g,
excludeFromQuickDiaryCreate: Boolean(g.excludeFromQuickDiaryCreate),
}))
: [];
} catch (error) {
console.error('[loadTrainingTimes] Error:', error);
const msg = getSafeErrorMessage(error, 'Fehler beim Laden der Trainingszeiten');
@@ -176,6 +191,23 @@ export default {
}
},
async onExcludeQuickChange(group, event) {
const exclude = Boolean(event.target.checked);
try {
await apiClient.put(`/training-groups/${this.currentClub}/${group.id}`, {
name: group.name,
sortOrder: group.sortOrder,
excludeFromQuickDiaryCreate: exclude,
});
group.excludeFromQuickDiaryCreate = exclude;
} catch (error) {
console.error('[onExcludeQuickChange]', error);
event.target.checked = !exclude;
const msg = getSafeErrorMessage(error, this.$t('trainingTimesTab.excludeFromQuickDiarySaveError'));
alert(msg);
}
},
showAddTimeForm(groupId) {
this.addTimeFormGroupId = groupId;
this.newTime = {
@@ -267,7 +299,8 @@ export default {
getWeekdayName(weekday) {
const days = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'];
return days[weekday] || '';
const i = Number(weekday);
return days[i] || '';
},
formatTime(time) {
@@ -311,11 +344,37 @@ export default {
.group-header {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
align-items: flex-start;
gap: 12px;
margin-bottom: 16px;
}
.group-header-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
}
.exclude-quick-diary-label {
display: inline-flex;
align-items: flex-start;
gap: 8px;
font-size: 0.85rem;
color: #444;
cursor: pointer;
margin: 0;
max-width: 22rem;
line-height: 1.35;
}
.exclude-quick-diary-label input {
margin-top: 0.2rem;
flex-shrink: 0;
}
.group-header h3 {
margin: 0;
font-size: 1.2rem;

View File

@@ -2402,7 +2402,9 @@
"noTimes": "Keine Trainingszeiten definiert",
"editTime": "Trainingszeit bearbeiten",
"save": "Speichern",
"saveError": "Fehler beim Speichern der Trainingszeit"
"saveError": "Fehler beim Speichern der Trainingszeit",
"excludeFromQuickDiaryCreate": "Vom Schnellanlegen (Tagebuch) ausnehmen",
"excludeFromQuickDiarySaveError": "Die Einstellung konnte nicht gespeichert werden."
},
"trainingGroupsTab": {
"groups": "Gruppen",

View File

@@ -2464,7 +2464,9 @@
"noTimes": "Keine Trainingszeiten definiert",
"editTime": "Trainingszeit bearbeiten",
"save": "Speichern",
"saveError": "Fehler beim Speichern der Trainingszeit"
"saveError": "Fehler beim Speichern der Trainingszeit",
"excludeFromQuickDiaryCreate": "Vom Schnellanlegen (Tagebuch) ausnehmen",
"excludeFromQuickDiarySaveError": "Die Einstellung konnte nicht gespeichert werden."
},
"trainingGroupsTab": {
"groups": "Gruppen",

View File

@@ -1640,14 +1640,27 @@ export default {
return `${y}-${m}-${day}`;
},
/** Wochentag aus API (0=So … 6=Sa) zuverlässig als Zahl vermeidet Strict-===-Fehler bei String-Werten. */
trainingWeekdayNumber(tt) {
if (tt == null || tt.weekday === undefined || tt.weekday === null) return NaN;
const w = Number(tt.weekday);
return Number.isFinite(w) ? w : parseInt(String(tt.weekday), 10);
},
trainingStartToMinutes(start) {
const s = String(start || '').trim();
const base = s.length >= 5 ? s.substring(0, 5) : s;
const parts = base.split(':');
if (parts.length < 2) return null;
const h = parseInt(parts[0], 10);
const m = parseInt(parts[1], 10);
if (!Number.isFinite(h) || !Number.isFinite(m)) return null;
return h * 60 + m;
},
findNextQuickSlotAcrossGroups(groups) {
if (!Array.isArray(groups) || groups.length === 0) return null;
const sorted = [...groups].sort((a, b) => {
const oa = Number.isFinite(Number(a.sortOrder)) ? Number(a.sortOrder) : 1e9;
const ob = Number.isFinite(Number(b.sortOrder)) ? Number(b.sortOrder) : 1e9;
if (oa !== ob) return oa - ob;
return (Number(a.id) || 0) - (Number(b.id) || 0);
});
const eligible = groups.filter((g) => !g.excludeFromQuickDiaryCreate);
const existing = new Set((this.dates || []).map((d) => this.diaryDateKey(d)));
const today = new Date();
for (let dayOffset = 0; dayOffset <= 366; dayOffset++) {
@@ -1655,19 +1668,37 @@ export default {
const checkWeekday = checkDate.getDay();
const dateStr = this.localYyyyMmDd(checkDate);
if (existing.has(dateStr)) continue;
for (const g of sorted) {
const times = (g.trainingTimes || [])
.filter((tt) => tt.weekday === checkWeekday && tt.startTime && String(tt.startTime).trim());
if (!times.length) continue;
times.sort((a, b) => String(a.startTime).localeCompare(String(b.startTime))
|| (Number(a.id) || 0) - (Number(b.id) || 0));
const time = times[0];
return {
date: dateStr,
startTime: this.formatTimeForInput(time.startTime),
endTime: this.formatTimeForInput(time.endTime),
};
const candidates = [];
for (const g of eligible) {
for (const tt of g.trainingTimes || []) {
if (this.trainingWeekdayNumber(tt) !== checkWeekday) continue;
if (!tt.startTime || !String(tt.startTime).trim()) continue;
const startMin = this.trainingStartToMinutes(tt.startTime);
if (startMin == null) continue;
candidates.push({
startMin,
dateStr,
sortOrder: Number.isFinite(Number(g.sortOrder)) ? Number(g.sortOrder) : 1e9,
groupId: Number(g.id) || 0,
timeId: Number(tt.id) || 0,
startTime: this.formatTimeForInput(tt.startTime),
endTime: this.formatTimeForInput(tt.endTime),
});
}
}
if (!candidates.length) continue;
candidates.sort((a, b) => {
if (a.startMin !== b.startMin) return a.startMin - b.startMin;
if (a.sortOrder !== b.sortOrder) return a.sortOrder - b.sortOrder;
if (a.groupId !== b.groupId) return a.groupId - b.groupId;
return a.timeId - b.timeId;
});
const best = candidates[0];
return {
date: best.dateStr,
startTime: best.startTime,
endTime: best.endTime,
};
}
return null;
},
@@ -1848,7 +1879,7 @@ export default {
const checkWeekday = checkDate.getDay();
// Finde Trainingszeiten für diesen Wochentag
const timesForWeekday = sortedTimes.filter(tt => tt.weekday === checkWeekday);
const timesForWeekday = sortedTimes.filter(tt => this.trainingWeekdayNumber(tt) === checkWeekday);
if (timesForWeekday.length > 0) {
// Nimm die erste Trainingszeit für diesen Tag

View File

@@ -58,8 +58,8 @@ android {
applicationId = "de.tsschulz.tt_tagebuch"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 2
versionName = "1.0.1"
versionCode = 3
versionName = "1.1.0"
buildConfigField("String", "BACKEND_BASE_URL", "\"$backendBaseUrl\"")
buildConfigField("String", "SOCKET_BASE_URL", "\"$socketBaseUrl\"")
}

View File

@@ -6573,29 +6573,63 @@ private fun localDateToJsWeekday(d: java.time.LocalDate): Int =
java.time.DayOfWeek.SATURDAY -> 6
}
/** Nächster freier Kalendertag ab heute: erstes Datum ohne Tagebuch-Eintrag, dann erste passende Gruppe (Sortierung) mit Trainingszeit. */
private fun trainingTimeStartToMinutes(start: String): Int? {
val t = start.trim().take(5)
val parts = t.split(':')
if (parts.size < 2) return null
val h = parts[0].toIntOrNull() ?: return null
val m = parts[1].toIntOrNull() ?: return null
return h * 60 + m
}
/** Nächster freier Kalendertag: früheste Startzeit über alle nicht ausgeschlossenen Gruppen an diesem Wochentag (0=So … 6=Sa). */
private fun findNextQuickDiarySlotAcrossGroups(
groups: List<TrainingGroupDto>,
existingDiaryDatesYyyyMmDd: Set<String>,
): NextDiarySlotSuggestion? {
val sortedGroups = groups.sortedWith(compareBy<TrainingGroupDto> { it.sortOrder }.thenBy { it.id })
val eligible = groups.filter { !it.excludeFromQuickDiaryCreate }
val today = java.time.LocalDate.now()
for (offset in 0..366) {
val check = today.plusDays(offset.toLong())
val wd = localDateToJsWeekday(check)
val norm = check.toString().take(10)
if (norm in existingDiaryDatesYyyyMmDd) continue
for (g in sortedGroups) {
val timesForDay = g.trainingTimes
.filter { it.weekday == wd && it.startTime.isNotBlank() }
.sortedWith(compareBy<TrainingTimeDto> { it.startTime }.thenBy { it.id })
val time = timesForDay.firstOrNull() ?: continue
return NextDiarySlotSuggestion(
date = norm,
trainingStart = time.startTime.trim().take(5),
trainingEnd = time.endTime.trim().take(5),
)
data class SlotCand(
val startMin: Int,
val suggestion: NextDiarySlotSuggestion,
val groupOrder: Int,
val groupId: Int,
val timeId: Int,
)
val cands = buildList {
for (g in eligible) {
for (time in g.trainingTimes) {
if (time.weekday != wd || time.startTime.isBlank()) continue
val sm = trainingTimeStartToMinutes(time.startTime) ?: continue
add(
SlotCand(
startMin = sm,
suggestion = NextDiarySlotSuggestion(
date = norm,
trainingStart = time.startTime.trim().take(5),
trainingEnd = time.endTime.trim().take(5),
),
groupOrder = g.sortOrder,
groupId = g.id,
timeId = time.id,
),
)
}
}
}
if (cands.isEmpty()) continue
val best = cands.minWith(
compareBy<SlotCand> { it.startMin }
.thenBy { it.groupOrder }
.thenBy { it.groupId }
.thenBy { it.timeId },
)
return best.suggestion
}
return null
}

View File

@@ -25,6 +25,7 @@ data class TrainingGroupDto(
val clubId: Int? = null,
val name: String = "",
val sortOrder: Int = 0,
val excludeFromQuickDiaryCreate: Boolean = false,
val isPreset: Boolean = false,
val presetType: String? = null,
val trainingTimes: List<TrainingTimeDto> = emptyList(),

View File

@@ -2480,6 +2480,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -4967,6 +4969,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -7441,6 +7445,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -9915,6 +9921,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -12389,6 +12397,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -14863,6 +14873,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -17337,6 +17349,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -19811,6 +19825,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -22285,6 +22301,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -24759,6 +24777,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -27233,6 +27253,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -29707,6 +29729,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -32181,6 +32205,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -34655,6 +34681,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",
@@ -37129,6 +37157,8 @@ object MobileStrings {
"trainingTimesTab.delete" to "Löschen",
"trainingTimesTab.edit" to "Bearbeiten",
"trainingTimesTab.editTime" to "Trainingszeit bearbeiten",
"trainingTimesTab.excludeFromQuickDiaryCreate" to "Vom Schnellanlegen (Tagebuch) ausnehmen",
"trainingTimesTab.excludeFromQuickDiarySaveError" to "Die Einstellung konnte nicht gespeichert werden.",
"trainingTimesTab.friday" to "Freitag",
"trainingTimesTab.from" to "Von:",
"trainingTimesTab.loading" to "Lade Trainingszeiten...",