/**
* App UI editor: screen → keys mapping and tablet mocks matching current Android layouts.
* Screens with multiple states expose `variants` (chip picker in the editor).
*/
/** @typedef {{ id: string, label: string, focusKeys?: string[], mock: () => string }} AppUiVariant */
/** @typedef {{ id: string, label: string, hint?: string, keys: string[], mock?: () => string, variants?: AppUiVariant[] }} AppUiScreen */
function f(key, cls = '', tag = 'input') {
const safe = String(key).replace(/"/g, '');
if (tag === 'textarea') {
return ``;
}
return ``;
}
function iconHelp() {
return ``;
}
function iconRefresh() {
return ``;
}
function iconPerson() {
return ``;
}
function iconLock() {
return ``;
}
function iconBack() {
return ``;
}
function iconChevron() {
return ``;
}
function langChip() {
return `Deutsch ${iconChevron()}`;
}
function toastBanner(key, cls = '') {
return `
${f(key, 'aq-toast-field')}
`;
}
/** Client note row as on opening_screen (ClientNoteUi.bindRow). */
function clientNoteRow({ hasNote = false, pending = false } = {}) {
const preview = hasNote
? `Beispielnotiz…`
: f('client_note_add', 'aq-muted aq-sm');
const actionLabel = hasNote ? 'client_note_edit' : 'client_note_add_action';
const badge = pending
? `${f('client_note_pending', 'aq-xs')}`
: '';
return `
${f('client_note_for_client', 'aq-note-label')}
${badge}
${preview}
${f(actionLabel, 'aq-field-on-primary aq-xs')}
`;
}
/** Settings bottom sheet (HandlerOpeningScreen.showSettingsSheet — handle + logout only, no title). */
function settingsSheet() {
return `
${f('logout', 'aq-field-on-primary')}
`;
}
/** Client note editor bottom sheet (ClientNoteUi.showEditor — bottom_sheet_client_note.xml). */
function clientNoteEditorSheet({ errorKey = null } = {}) {
const err = errorKey
? `${f(errorKey, 'aq-sm')}
`
: '';
return `
${f('client_note_hint', 'aq-outlined-input aq-note-input', 'textarea')}
${err}
${f('save', 'aq-field-on-primary')}
${f('cancel', 'aq-sm')}
`;
}
function homeHeaderChrome({ status = 'online', note = 'empty' } = {}) {
const statusChip = status === 'offline'
? `
${f('offline', 'aq-status-text')}
`
: `
${f('online', 'aq-status-text')}
`;
const noteRow = note === 'none'
? ''
: clientNoteRow({
hasNote: note === 'filled' || note === 'pending',
pending: note === 'pending',
});
return `
`;
}
function homeDock() {
const iconReview = ``;
const iconUpload = ``;
return `
${iconReview}${f('review_scores', 'aq-field-on-primary aq-xs')}
${iconUpload}${f('upload', 'aq-field-on-primary aq-xs')}
`;
}
function homeNextStep(titleKey, bodyKey, actionKey) {
return `
${f('next_step_label', 'aq-eyebrow-field')}
${f(titleKey, 'aq-bold')}
${f(bodyKey, 'aq-muted aq-sm')}
${actionKey ? `
${f(actionKey, 'aq-field-on-primary aq-xs')}
` : ''}
`;
}
/** In-group Ampelkategorie / Punkteprüfung badge (view_category_review_badge.xml). */
function categoryReviewBadge({ band = 'yellow' } = {}) {
const lamp = band === 'green'
? ''
: band === 'red'
? ''
: '';
return `
${lamp}
${f('next_step_review_title', 'aq-category-review-title')}
${f('next_step_review_body', 'aq-category-review-body', 'textarea')}
`;
}
/** Process-complete / Ampelkategorie panel (view_process_complete_panel.xml). */
function processCompleteBadge({
mode = 'complete',
showUpload = false,
} = {}) {
const review = mode === 'review';
const titleKey = review ? 'next_step_review_title' : 'process_complete_title';
const bodyKey = review ? 'next_step_review_body' : 'process_complete_hub_body';
const actionKey = review ? 'process_complete_set_category' : (
mode === 'edit' ? 'process_complete_edit_category' : 'process_complete_set_category'
);
const tone = review ? 'review' : 'complete';
const icon = review
? `
`
: `✓
`;
const upload = showUpload
? `${f('upload', 'aq-xs')}
`
: '';
return `
${icon}
${f(titleKey, `aq-bold aq-process-complete-title`)}
${f(bodyKey, 'aq-sm aq-process-complete-body', 'textarea')}
${f(actionKey, 'aq-field-on-primary aq-xs')}
${upload}
`;
}
/** Save-only questionnaire footer (questionnaire_all_in_one has only btnSave, no previous). */
function qnFooter({ progressPct = 0, showProgress = true } = {}) {
const progress = showProgress
? `
${f('questionnaire_progress_answered', 'aq-muted aq-xs aq-progress-label-end')}
`
: '';
return `
`;
}
function helpShell(topicTitleKey, introKey, sections) {
const sectionHtml = sections.map((s, i) => `
${f(s.bodyKey, 'aq-muted aq-xs aq-mt', 'textarea')}
`).join('');
return `
‹
${f('help_title', 'aq-screen-title')}
${f('language', 'aq-muted aq-sm aq-picker-label')}
${langChip()}
${f('help_topic', 'aq-muted aq-sm aq-picker-label')}
${f(topicTitleKey, 'aq-sm')} ${iconChevron()}
${f('help_this_page', 'aq-xs')}
${f(topicTitleKey, 'aq-bold aq-mt')}
${f(introKey, 'aq-muted aq-sm', 'textarea')}
${sectionHtml}
`;
}
function syncPhaseMock(statusKey, subtitleKey, progressPct, finished = false) {
const stage = finished
? `✓
`
: ``;
// 7 checkpoint phases: preparing(10%), questionnaires(25%), client_info(40%), answers(55%), processing(70%), saving(85%), finished(100%)
const phases = [10, 25, 40, 55, 70, 85, 100];
const dots = phases.map(p => ``).join('');
return `
${f('sync_title', 'aq-muted aq-sync-title')}
${stage}
${f(statusKey, 'aq-sync-status')}
${f(subtitleKey, 'aq-muted aq-sm aq-sync-sub', 'textarea')}
${dots}
`;
}
/**
* Branded app dialog (dialog_app_message.xml).
* @param {'info'|'warning'|'logout'|'danger'} tone
*/
function dialogMock({
titleKey,
messageKey,
primaryKey,
secondaryKey = null,
detailsKeys = null,
tone = 'info',
}) {
const details = detailsKeys?.length
? `${detailsKeys.map(k => f(k, 'aq-sm aq-dialog-detail-line')).join('')}
`
: '';
const secondary = secondaryKey
? `${f(secondaryKey, 'aq-muted')}
`
: '';
const primaryCls = tone === 'logout' || tone === 'danger'
? 'aq-btn-pill aq-btn-danger'
: 'aq-btn-pill aq-btn-primary';
return `
${f(titleKey, 'aq-dialog-title')}
${f(messageKey, 'aq-muted aq-sm aq-dialog-msg', 'textarea')}
${details}
${f(primaryKey, 'aq-field-on-primary')}
${secondary}
`;
}
function scoreCalculatedMock() {
return `
✓
${f('score_calculated_eyebrow', 'aq-score-eyebrow')}
${f('score_calculated_title', 'aq-score-title')}
${f('score_calculated_result_label', 'aq-muted aq-xs')}
RHS Index · 27.0
${f('category_yellow', 'aq-xs')}
${f('score_calculated_band_hint', 'aq-muted aq-xs aq-mt')}
${f('score_calculated_next_label', 'aq-muted aq-xs')}
${f('score_calculated_next_title', 'aq-bold aq-mt')}
${f('score_calculated_body', 'aq-muted aq-sm', 'textarea')}
${f('score_calculated_continue', 'aq-field-on-primary')}
`;
}
/** @type {AppUiScreen[]} */
export const APP_UI_SCREENS = [
{
id: 'auth',
label: 'Anmeldung',
hint: 'activity_auth.xml',
keys: [
'auth_title', 'auth_subtitle_login', 'auth_subtitle_change_password',
'auth_footer_accounts', 'username_hint', 'password_hint', 'login_btn', 'exit_btn',
'new_password_hint', 'confirm_password_hint', 'save_password_btn',
'password_too_short', 'passwords_dont_match', 'please_username_password',
'password_needs_prefix', 'password_rule_min_length', 'password_rule_uppercase',
'password_rule_number', 'password_rule_special', 'login_failed_with_reason',
'language', 'help_title',
],
variants: [
{
id: 'login',
label: 'Login',
focusKeys: [
'auth_title', 'auth_subtitle_login', 'username_hint', 'password_hint',
'login_btn', 'exit_btn', 'auth_footer_accounts', 'language', 'help_title',
],
mock: () => `
${iconHelp()}${langChip()}
${f('auth_title', 'aq-auth-title')}
${f('auth_subtitle_login', 'aq-muted aq-auth-sub')}
`,
},
{
id: 'login_error',
label: 'Login Fehler',
focusKeys: [
'please_username_password', 'login_failed_with_reason',
'username_hint', 'password_hint', 'login_btn', 'exit_btn',
],
mock: () => `
${iconHelp()}${langChip()}
${f('auth_title', 'aq-auth-title')}
${f('auth_subtitle_login', 'aq-muted aq-auth-sub')}
${toastBanner('please_username_password')}
`,
},
{
id: 'login_failed',
label: 'Login fehlgeschlagen',
focusKeys: ['login_failed_with_reason', 'username_hint', 'password_hint', 'login_btn'],
mock: () => `
${iconHelp()}${langChip()}
${f('auth_title', 'aq-auth-title')}
${f('auth_subtitle_login', 'aq-muted aq-auth-sub')}
${toastBanner('login_failed_with_reason')}
`,
},
{
id: 'password',
label: 'Passwort ändern',
focusKeys: [
'auth_title', 'auth_subtitle_change_password', 'new_password_hint',
'confirm_password_hint', 'save_password_btn', 'auth_footer_accounts',
'password_needs_prefix', 'password_rule_min_length', 'password_rule_uppercase',
'password_rule_number', 'password_rule_special',
],
mock: () => `
${iconHelp()}${langChip()}
${f('auth_title', 'aq-auth-title')}
${f('auth_subtitle_change_password', 'aq-muted aq-auth-sub')}
`,
},
{
id: 'password_error',
label: 'Passwort Fehler',
focusKeys: ['password_too_short', 'passwords_dont_match', 'new_password_hint', 'confirm_password_hint'],
mock: () => `
${iconHelp()}${langChip()}
${f('auth_title', 'aq-auth-title')}
${f('auth_subtitle_change_password', 'aq-muted aq-auth-sub')}
${toastBanner('password_too_short')}
`,
},
],
},
{
id: 'home',
label: 'Startseite',
hint: 'opening_screen.xml + hub dock',
keys: [
'hub_welcome_back', 'language', 'active_client', 'online', 'offline',
'settings', 'logout', 'next_step_label', 'next_step_questionnaire_title',
'next_step_questionnaire_body', 'next_step_review_title', 'next_step_review_body',
'next_step_upload_title', 'next_step_upload_body', 'next_step_completed_title',
'next_step_completed_body', 'next_step_loading_title', 'next_step_loading_body',
'next_step_show_questionnaire', 'questionnaire_title', 'start', 'locked',
'questionnaire_chip_edit', 'questionnaire_chip_edit_pending',
'process_complete_title', 'process_complete_set_category', 'process_complete_edit_category',
'upload', 'review_scores', 'refresh_complete',
'help_title', 'client_note_for_client', 'client_note_add', 'client_note_edit',
'client_note_add_action', 'client_note_pending', 'process_complete_hub_body',
'questionnaire_unlock_2_rhs', 'questionnaire_unlock_3_integration_index',
'questionnaire_unlock_4_consultation_results', 'consultation_unlock_requires_rhs',
'review_required_before_continue', 'download_failed_use_offline', 'no_profile',
'cancel', 'save', 'client_note_title', 'client_note_hint', 'client_note_too_long',
'client_note_saved', 'client_note_saved_pending',
],
variants: [
{
id: 'questionnaire',
label: 'Nächster Fragebogen',
focusKeys: [
'hub_welcome_back', 'language', 'active_client', 'online', 'next_step_label',
'next_step_questionnaire_title', 'next_step_questionnaire_body',
'next_step_show_questionnaire', 'questionnaire_title', 'start', 'locked',
'questionnaire_unlock_2_rhs', 'questionnaire_unlock_3_integration_index',
'consultation_unlock_requires_rhs', 'review_scores', 'upload',
'client_note_for_client', 'client_note_add', 'client_note_add_action',
],
mock: () => `
`,
},
{
id: 'review',
label: 'Ampelkategorie',
focusKeys: [
'next_step_label', 'next_step_review_title', 'next_step_review_body',
'process_complete_set_category', 'review_scores', 'upload',
'questionnaire_chip_edit', 'questionnaire_title',
'review_required_before_continue',
],
mock: () => `
${homeDock()}
${toastBanner('review_required_before_continue')}
`,
},
{
id: 'upload',
label: 'Upload bereit',
focusKeys: [
'next_step_label', 'next_step_upload_title', 'next_step_upload_body',
'upload', 'review_scores', 'questionnaire_chip_edit_pending',
'client_note_pending', 'client_note_edit',
],
mock: () => `
`,
},
{
id: 'complete',
label: 'Abgeschlossen',
focusKeys: [
'process_complete_title', 'process_complete_hub_body',
'process_complete_set_category', 'upload',
],
mock: () => `
${homeHeaderChrome()}
${processCompleteBadge({ mode: 'complete', showUpload: true })}
${homeDock()}
`,
},
{
id: 'complete_edit',
label: 'Abgeschlossen · Kategorie',
focusKeys: [
'process_complete_title', 'process_complete_hub_body',
'process_complete_edit_category',
],
mock: () => `
${homeHeaderChrome()}
${processCompleteBadge({ mode: 'edit' })}
${homeDock()}
`,
},
{
id: 'set_category',
label: 'Ampelkategorie setzen',
focusKeys: [
'next_step_review_title', 'next_step_review_body',
'process_complete_set_category',
],
mock: () => `
${homeHeaderChrome()}
${processCompleteBadge({ mode: 'review' })}
${homeDock()}
`,
},
{
id: 'completed_banner',
label: 'Nächster Schritt · Fertig',
focusKeys: [
'next_step_label', 'next_step_completed_title', 'next_step_completed_body',
'review_scores', 'upload', 'questionnaire_chip_edit',
],
mock: () => `
`,
},
{
id: 'loading',
label: 'Laden',
focusKeys: [
'next_step_loading_title', 'next_step_loading_body', 'next_step_label',
],
mock: () => `
`,
},
{
id: 'offline',
label: 'Offline',
focusKeys: [
'offline', 'hub_welcome_back', 'active_client', 'download_failed_use_offline',
],
mock: () => `
${homeHeaderChrome({ status: 'offline' })}
${homeNextStep('next_step_questionnaire_title', 'next_step_questionnaire_body', 'next_step_show_questionnaire')}
${homeDock()}
${toastBanner('download_failed_use_offline')}
`,
},
{
id: 'settings',
label: 'Einstellungen',
focusKeys: ['settings', 'logout'],
mock: () => `
${homeHeaderChrome()}
${homeNextStep('next_step_questionnaire_title', 'next_step_questionnaire_body', 'next_step_show_questionnaire')}
${homeDock()}
${settingsSheet()}
`,
},
{
id: 'refresh_done',
label: 'Aktualisiert',
focusKeys: ['refresh_complete'],
mock: () => `
${homeHeaderChrome()}
${homeNextStep('next_step_questionnaire_title', 'next_step_questionnaire_body', 'next_step_show_questionnaire')}
${homeDock()}
${toastBanner('refresh_complete')}
`,
},
{
id: 'note_editor',
label: 'Notiz bearbeiten',
focusKeys: [
'client_note_title', 'client_note_for_client', 'client_note_hint',
'client_note_too_long', 'cancel', 'save',
],
mock: () => `
${homeHeaderChrome({ note: 'filled' })}
${homeNextStep('next_step_questionnaire_title', 'next_step_questionnaire_body', 'next_step_show_questionnaire')}
${homeDock()}
${clientNoteEditorSheet({ errorKey: 'client_note_too_long' })}
`,
},
{
id: 'note_saved',
label: 'Notiz gespeichert',
focusKeys: [
'client_note_saved', 'client_note_saved_pending',
'client_note_for_client', 'client_note_edit', 'client_note_pending',
],
mock: () => `
${homeHeaderChrome({ note: 'pending' })}
${homeNextStep('next_step_questionnaire_title', 'next_step_questionnaire_body', 'next_step_show_questionnaire')}
${homeDock()}
${toastBanner('client_note_saved_pending')}
`,
},
],
},
{
id: 'client',
label: 'Klient wählen',
hint: 'view_client_select_gate.xml',
keys: [
'client_select_gate_title', 'client_select_gate_body', 'client_select_gate_hint',
'client_select_gate_count', 'client_picker_title', 'client_picker_subtitle',
'client_picker_search', 'client_picker_current', 'client_picker_recent',
'client_picker_all', 'client_picker_empty', 'client_picker_status_finished',
'client_picker_status_started', 'client_picker_status_new',
'client_picker_status_finished_pending_upload', 'client_picker_status_pending_upload',
'client_picker_status_next_step_category', 'client_picker_status_next',
'client_picker_category_assigned', 'client_picker_category_calculated',
'client_picker_score', 'client_picker_loading_title', 'client_picker_loading_body',
'active_client', 'language', 'no_clients_assigned', 'client', 'client_note',
],
variants: [
{
id: 'gate',
label: 'Gate',
focusKeys: [
'client_select_gate_title', 'client_select_gate_body', 'client_select_gate_hint',
'client_select_gate_count', 'client_picker_search', 'language',
'client_picker_status_finished', 'client_picker_status_started',
'client_picker_status_new',
],
mock: () => `
${iconPerson()}
${f('client_picker_search', 'aq-outlined-input')}
${iconPerson()}
demoClient
${f('client_picker_status_finished', 'aq-xs')}
${iconPerson()}
demoClient2
${f('client_picker_status_new', 'aq-xs')}
`,
},
{
id: 'list',
label: 'Liste',
focusKeys: [
'client_picker_title', 'client_picker_subtitle', 'client_picker_recent',
'client_picker_all', 'client_picker_current', 'client_picker_score',
'client_picker_category_assigned', 'client_picker_category_calculated',
'client_picker_status_finished_pending_upload', 'client_picker_status_pending_upload',
'client_picker_status_next_step_category', 'client_picker_status_next',
'client', 'client_picker_search',
],
mock: () => `
${f('client_picker_current', 'aq-label-xs')}
${iconPerson()}
${f('client', 'aq-xs')} demoA
${f('client_picker_status_next', 'aq-xs')}
${f('client_picker_score', 'aq-xs')}: 27.0
${f('client_picker_category_calculated', 'aq-xs')}
${f('client_picker_recent', 'aq-label-xs')}
${f('client_picker_all', 'aq-label-xs')}
${iconPerson()}
demoB
${f('client_picker_status_pending_upload', 'aq-xs')}
${f('client_picker_status_finished_pending_upload', 'aq-xs')}
${f('client_picker_status_next_step_category', 'aq-xs')}
${f('client_picker_category_assigned', 'aq-xs')}
`,
},
{
id: 'loading',
label: 'Laden',
focusKeys: ['client_picker_loading_title', 'client_picker_loading_body'],
mock: () => `
`,
},
{
id: 'empty',
label: 'Leer',
focusKeys: ['client_picker_empty', 'no_clients_assigned'],
mock: () => `
`,
},
],
},
{
id: 'questionnaire',
label: 'Fragebogen',
hint: 'questionnaire_all_in_one.xml · pick a question type',
keys: [
'coach_code', 'client_code', 'client_note_for_client', 'client_note_add',
'client_note_edit', 'client_note_add_action', 'client_note_pending',
'client_note_title', 'client_note_hint', 'client_note_too_long', 'cancel',
'save', 'previous', 'choose_answer', 'fill_all_fields', 'select_one_answer',
'select_one_answer_per_row', 'choose_more_elements', 'questionnaire_progress_answered',
'no_questions_available', 'questionnaire_missing_options', 'help_title', 'language',
'please_client_code', 'fill_both_fields', 'client_code_exists', 'no_profile',
'other_option', 'other_country', 'january', 'february', 'march', 'april', 'may',
'june', 'july', 'august', 'september', 'october', 'november', 'december',
],
variants: [
{
id: 'codes',
label: 'Codes',
focusKeys: [
'client_code', 'coach_code', 'please_client_code', 'fill_both_fields',
'client_code_exists', 'no_profile', 'previous', 'save',
'questionnaire_progress_answered', 'client_note_for_client',
'client_note_add', 'client_note_add_action',
],
mock: () => `
${iconBack()}
${iconHelp()}${langChip()}
${clientNoteRow()}
${qnFooter({ progressPct: 5 })}
${toastBanner('fill_both_fields')}
`,
},
{
id: 'choice',
label: 'Auswahl',
focusKeys: [
'choose_answer', 'other_option', 'other_country', 'select_one_answer',
'choose_more_elements', 'previous', 'save', 'questionnaire_progress_answered',
],
mock: () => `
${iconBack()}
${iconHelp()}${langChip()}
${qnFooter({ progressPct: 35 })}
${toastBanner('select_one_answer')}
`,
},
{
id: 'multi',
label: 'Mehrfachauswahl',
focusKeys: [
'choose_more_elements', 'other_option', 'previous', 'save',
'questionnaire_progress_answered',
],
mock: () => `
${iconBack()}
${iconHelp()}${langChip()}
${qnFooter({ progressPct: 40 })}
${toastBanner('choose_more_elements')}
`,
},
{
id: 'date',
label: 'Datum',
focusKeys: [
'choose_answer', 'january', 'february', 'march', 'april', 'may', 'june',
'july', 'august', 'september', 'october', 'november', 'december',
'fill_all_fields', 'previous', 'save',
],
mock: () => `
${iconBack()}
${iconHelp()}${langChip()}
${qnFooter({ progressPct: 0, showProgress: false })}
${toastBanner('fill_all_fields')}
`,
},
{
id: 'empty',
label: 'Leer / Fehler',
focusKeys: [
'no_questions_available', 'questionnaire_missing_options', 'previous',
],
mock: () => `
${iconBack()}
${iconHelp()}${langChip()}
${qnFooter({ progressPct: 0, showProgress: false })}
${toastBanner('questionnaire_missing_options')}
`,
},
{
id: 'note',
label: 'Notiz',
focusKeys: [
'client_note_title', 'client_note_for_client', 'client_note_hint',
'client_note_too_long', 'cancel', 'save', 'client_note_edit',
],
mock: () => `
${iconBack()}
${iconHelp()}${langChip()}
${clientNoteRow({ hasNote: true })}
${clientNoteEditorSheet({ errorKey: 'client_note_too_long' })}
`,
},
],
},
{
id: 'review',
label: 'Ampelkategorie',
hint: 'review_scores_screen.xml',
keys: [
'review_scores', 'review_scores_subtitle', 'review_scores_agree',
'review_scores_calculated_category', 'review_scores_coach_category',
'review_scores_empty', 'review_scores_pending_upload', 'review_scores_complete_title',
'review_scores_complete_body', 'review_scores_complete_body_uploaded',
'review_scores_done', 'review_scores_save_failed', 'review_scores_saved',
'review_scores_saved_pending', 'review_scores_set_category',
'review_scores_supervisor_notice', 'review_scores_supervisor_confirm_title',
'review_scores_supervisor_confirm_body', 'review_scores_supervisor_confirm_action',
'review_scores_total', 'category_green', 'category_yellow', 'category_red',
'score_calculated_title', 'score_calculated_body', 'score_calculated_eyebrow',
'score_calculated_result_label', 'score_calculated_next_label',
'score_calculated_next_title', 'score_calculated_band_hint', 'score_calculated_continue',
'points', 'help_title', 'not_done', 'previous',
],
variants: [
{
id: 'active',
label: 'Aktiv',
focusKeys: [
'review_scores', 'review_scores_subtitle', 'review_scores_agree',
'review_scores_calculated_category', 'review_scores_coach_category',
'review_scores_total', 'review_scores_set_category', 'category_green',
'category_yellow', 'category_red', 'not_done', 'points', 'previous',
'review_scores_supervisor_notice',
],
mock: () => `
${f('review_scores_supervisor_notice', 'aq-sm')}
RHS Index
${f('review_scores_total', 'aq-xs')}: 27.0 ${f('points', 'aq-xs')}
${f('review_scores_calculated_category', 'aq-xs')}
${f('category_yellow', 'aq-xs')}
${f('review_scores_coach_category', 'aq-xs')}
${f('not_done', 'aq-xs')}
${f('review_scores_agree', 'aq-field-on-primary aq-xs')}
${f('review_scores_set_category', 'aq-label-xs')}
${f('category_green', 'aq-xs')}
${f('category_yellow', 'aq-xs')}
${f('category_red', 'aq-xs')}
${f('previous', 'aq-sm')}
`,
},
{
id: 'empty',
label: 'Leer',
focusKeys: ['review_scores_empty', 'review_scores'],
mock: () => `
`,
},
{
id: 'complete',
label: 'Abgeschlossen',
focusKeys: [
'review_scores_complete_title', 'review_scores_complete_body',
'review_scores_complete_body_uploaded', 'review_scores_done',
'review_scores_pending_upload', 'review_scores_saved_pending', 'previous',
],
mock: () => `
${f('review_scores_complete_title', 'aq-bold')}
${f('review_scores_complete_body', 'aq-muted aq-sm', 'textarea')}
${f('review_scores_pending_upload', 'aq-sm')}
${f('review_scores_done', 'aq-field-on-primary aq-xs')}
${f('previous', 'aq-sm')}
${toastBanner('review_scores_saved_pending')}
`,
},
],
},
{
id: 'upload',
label: 'Hochladen',
hint: 'upload_overview_screen.xml',
keys: [
'upload', 'start_upload', 'upload_pending_subtitle', 'upload_summary',
'upload_nothing_pending', 'upload_prepare_failed', 'upload_failed_title',
'upload_success_title', 'upload_success_message', 'upload_success_questionnaires',
'upload_success_scoring_reviews', 'upload_success_client_notes',
'upload_success_sync_next', 'upload_item_scoring_review', 'upload_scoring_review_summary',
'cancel', 'previous', 'help_title', 'questions_filled', 'done', 'client_code',
'upload_connect_network_first', 'no_questionnaires', 'login_required', 'client_note',
],
variants: [
{
id: 'pending',
label: 'Ausstehend',
focusKeys: [
'upload_pending_subtitle', 'start_upload', 'upload_summary', 'upload',
'cancel', 'previous', 'client_code', 'done', 'questions_filled',
'upload_item_scoring_review', 'upload_scoring_review_summary',
'upload_connect_network_first', 'client_note',
],
mock: () => `
Demografie
✓ ${f('done', 'aq-xs')} · 12 ${f('questions_filled', 'aq-xs')}
›
${f('upload_item_scoring_review', 'aq-sm')}
${f('upload_scoring_review_summary', 'aq-xs')}
${f('client_note', 'aq-sm')}
`,
},
{
id: 'empty',
label: 'Leer',
focusKeys: [
'upload_nothing_pending', 'no_questionnaires', 'start_upload',
'login_required', 'upload_connect_network_first',
],
mock: () => `
${toastBanner('login_required')}
`,
},
],
},
{
id: 'help',
label: 'Hilfe',
hint: 'activity_help.xml · pick a topic below',
keys: [
'help_title', 'help_topic', 'help_this_page', 'language',
'help_auth_title', 'help_auth_intro', 'help_auth_signin_title', 'help_auth_signin_body',
'help_auth_password_title', 'help_auth_password_body',
'help_opening_title', 'help_opening_intro', 'help_opening_pick_client_title',
'help_opening_pick_client_body', 'help_opening_home_title', 'help_opening_home_body',
'help_opening_client_lang_title', 'help_opening_client_lang_body',
'help_opening_questionnaires_title', 'help_opening_questionnaires_body',
'help_opening_complete_title', 'help_opening_complete_body',
'help_questionnaire_title', 'help_questionnaire_intro', 'help_questionnaire_header_title',
'help_questionnaire_header_body', 'help_questionnaire_answer_title',
'help_questionnaire_answer_body', 'help_questionnaire_save_title',
'help_questionnaire_save_body',
'help_category_title', 'help_category_intro', 'help_category_set_title',
'help_category_set_body', 'help_category_change_title', 'help_category_change_body',
'help_upload_title', 'help_upload_intro', 'help_upload_check_title',
'help_upload_check_body', 'help_upload_fix_title', 'help_upload_fix_body',
'help_upload_send_title', 'help_upload_send_body',
],
variants: [
{
id: 'auth',
label: 'Anmeldung',
focusKeys: [
'help_title', 'help_topic', 'help_this_page', 'language',
'help_auth_title', 'help_auth_intro', 'help_auth_signin_title',
'help_auth_signin_body', 'help_auth_password_title', 'help_auth_password_body',
],
mock: () => helpShell('help_auth_title', 'help_auth_intro', [
{ titleKey: 'help_auth_signin_title', bodyKey: 'help_auth_signin_body' },
{ titleKey: 'help_auth_password_title', bodyKey: 'help_auth_password_body' },
]),
},
{
id: 'opening',
label: 'Startseite',
focusKeys: [
'help_title', 'help_topic', 'help_this_page', 'language',
'help_opening_title', 'help_opening_intro', 'help_opening_pick_client_title',
'help_opening_pick_client_body', 'help_opening_home_title', 'help_opening_home_body',
'help_opening_client_lang_title', 'help_opening_client_lang_body',
'help_opening_questionnaires_title', 'help_opening_questionnaires_body',
'help_opening_complete_title', 'help_opening_complete_body',
],
mock: () => helpShell('help_opening_title', 'help_opening_intro', [
{ titleKey: 'help_opening_pick_client_title', bodyKey: 'help_opening_pick_client_body' },
{ titleKey: 'help_opening_home_title', bodyKey: 'help_opening_home_body' },
{ titleKey: 'help_opening_client_lang_title', bodyKey: 'help_opening_client_lang_body' },
{ titleKey: 'help_opening_questionnaires_title', bodyKey: 'help_opening_questionnaires_body' },
{ titleKey: 'help_opening_complete_title', bodyKey: 'help_opening_complete_body' },
]),
},
{
id: 'questionnaire',
label: 'Fragebogen',
focusKeys: [
'help_title', 'help_topic', 'help_this_page', 'language',
'help_questionnaire_title', 'help_questionnaire_intro',
'help_questionnaire_header_title', 'help_questionnaire_header_body',
'help_questionnaire_answer_title', 'help_questionnaire_answer_body',
'help_questionnaire_save_title', 'help_questionnaire_save_body',
],
mock: () => helpShell('help_questionnaire_title', 'help_questionnaire_intro', [
{ titleKey: 'help_questionnaire_header_title', bodyKey: 'help_questionnaire_header_body' },
{ titleKey: 'help_questionnaire_answer_title', bodyKey: 'help_questionnaire_answer_body' },
{ titleKey: 'help_questionnaire_save_title', bodyKey: 'help_questionnaire_save_body' },
]),
},
{
id: 'category',
label: 'Ampelkategorie',
focusKeys: [
'help_title', 'help_topic', 'help_this_page', 'language',
'help_category_title', 'help_category_intro',
'help_category_set_title', 'help_category_set_body',
'help_category_change_title', 'help_category_change_body',
],
mock: () => helpShell('help_category_title', 'help_category_intro', [
{ titleKey: 'help_category_set_title', bodyKey: 'help_category_set_body' },
{ titleKey: 'help_category_change_title', bodyKey: 'help_category_change_body' },
]),
},
{
id: 'upload',
label: 'Hochladen',
focusKeys: [
'help_title', 'help_topic', 'help_this_page', 'language',
'help_upload_title', 'help_upload_intro',
'help_upload_check_title', 'help_upload_check_body',
'help_upload_fix_title', 'help_upload_fix_body',
'help_upload_send_title', 'help_upload_send_body',
],
mock: () => helpShell('help_upload_title', 'help_upload_intro', [
{ titleKey: 'help_upload_check_title', bodyKey: 'help_upload_check_body' },
{ titleKey: 'help_upload_fix_title', bodyKey: 'help_upload_fix_body' },
{ titleKey: 'help_upload_send_title', bodyKey: 'help_upload_send_body' },
]),
},
],
},
{
id: 'sync',
label: 'Synchronisation',
hint: 'view_sync_progress_overlay.xml · pick a phase below',
keys: [
'sync_title',
'sync_status_preparing', 'sync_status_downloading_questionnaires',
'sync_status_downloading_client_info', 'sync_status_downloading_answers',
'sync_status_processing', 'sync_status_securely_saving', 'sync_status_finished',
'sync_subtitle_preparing', 'sync_subtitle_downloading_questionnaires',
'sync_subtitle_downloading_client_info', 'sync_subtitle_downloading_answers',
'sync_subtitle_processing', 'sync_subtitle_securely_saving', 'sync_subtitle_finished',
],
variants: [
{
id: 'preparing',
label: 'Vorbereitung',
focusKeys: ['sync_title', 'sync_status_preparing', 'sync_subtitle_preparing'],
mock: () => syncPhaseMock('sync_status_preparing', 'sync_subtitle_preparing', 10),
},
{
id: 'questionnaires',
label: 'Fragebögen',
focusKeys: [
'sync_title', 'sync_status_downloading_questionnaires',
'sync_subtitle_downloading_questionnaires',
],
mock: () => syncPhaseMock(
'sync_status_downloading_questionnaires',
'sync_subtitle_downloading_questionnaires',
25,
),
},
{
id: 'client_info',
label: 'Klienten',
focusKeys: [
'sync_title', 'sync_status_downloading_client_info',
'sync_subtitle_downloading_client_info',
],
mock: () => syncPhaseMock(
'sync_status_downloading_client_info',
'sync_subtitle_downloading_client_info',
40,
),
},
{
id: 'answers',
label: 'Antworten',
focusKeys: [
'sync_title', 'sync_status_downloading_answers',
'sync_subtitle_downloading_answers',
],
mock: () => syncPhaseMock(
'sync_status_downloading_answers',
'sync_subtitle_downloading_answers',
55,
),
},
{
id: 'processing',
label: 'Verarbeitung',
focusKeys: ['sync_title', 'sync_status_processing', 'sync_subtitle_processing'],
mock: () => syncPhaseMock('sync_status_processing', 'sync_subtitle_processing', 70),
},
{
id: 'saving',
label: 'Speichern',
focusKeys: [
'sync_title', 'sync_status_securely_saving', 'sync_subtitle_securely_saving',
],
mock: () => syncPhaseMock(
'sync_status_securely_saving',
'sync_subtitle_securely_saving',
85,
),
},
{
id: 'finished',
label: 'Fertig',
focusKeys: ['sync_title', 'sync_status_finished', 'sync_subtitle_finished'],
mock: () => syncPhaseMock('sync_status_finished', 'sync_subtitle_finished', 100, true),
},
],
},
{
id: 'dialogs',
label: 'Dialoge',
hint: 'dialog_app_message.xml + notices · pick a dialog below',
keys: [
'logout', 'logout_confirm_title', 'logout_confirm_message', 'logout_confirm_action',
'logout_offline_title', 'logout_offline_message', 'logout_pending_title',
'logout_pending_message', 'logout_pending_detail_questionnaires',
'logout_pending_detail_scoring', 'logout_pending_detail_notes',
'logout_pending_detail_in_progress',
'upload_pending_title', 'upload_pending_review_message', 'upload', 'later',
'review_scores_supervisor_confirm_title', 'review_scores_supervisor_confirm_body',
'review_scores_supervisor_confirm_action', 'cancel', 'ok',
'upload_success_title', 'upload_success_message', 'upload_success_questionnaires',
'upload_success_scoring_reviews', 'upload_success_client_notes',
'upload_success_sync_next', 'upload_failed_title', 'upload_nothing_pending',
'upload_prepare_failed', 'error', 'error_invalid_data', 'error_not_found',
'score_calculated_eyebrow', 'score_calculated_title', 'score_calculated_body',
'score_calculated_result_label', 'score_calculated_next_label',
'score_calculated_next_title', 'score_calculated_band_hint', 'score_calculated_continue',
'category_yellow', 'questionnaire_exit_title', 'questionnaire_exit_message', 'leave',
],
variants: [
{
id: 'logout_confirm',
label: 'Abmelden bestätigen',
focusKeys: [
'logout_confirm_title', 'logout_confirm_message', 'logout_confirm_action',
'cancel', 'logout',
],
mock: () => dialogMock({
titleKey: 'logout_confirm_title',
messageKey: 'logout_confirm_message',
primaryKey: 'logout_confirm_action',
secondaryKey: 'cancel',
tone: 'logout',
}),
},
{
id: 'logout_offline',
label: 'Abmelden offline',
focusKeys: ['logout_offline_title', 'logout_offline_message', 'ok'],
mock: () => dialogMock({
titleKey: 'logout_offline_title',
messageKey: 'logout_offline_message',
primaryKey: 'ok',
tone: 'warning',
}),
},
{
id: 'logout_pending',
label: 'Abmelden blockiert',
focusKeys: [
'logout_pending_title', 'logout_pending_message',
'logout_pending_detail_questionnaires', 'logout_pending_detail_scoring',
'logout_pending_detail_notes', 'logout_pending_detail_in_progress', 'ok',
],
mock: () => dialogMock({
titleKey: 'logout_pending_title',
messageKey: 'logout_pending_message',
primaryKey: 'ok',
detailsKeys: [
'logout_pending_detail_questionnaires',
'logout_pending_detail_scoring',
'logout_pending_detail_notes',
'logout_pending_detail_in_progress',
],
tone: 'warning',
}),
},
{
id: 'upload_pending',
label: 'Upload bereit',
focusKeys: [
'upload_pending_title', 'upload_pending_review_message', 'upload', 'later',
],
mock: () => dialogMock({
titleKey: 'upload_pending_title',
messageKey: 'upload_pending_review_message',
primaryKey: 'upload',
secondaryKey: 'later',
tone: 'info',
}),
},
{
id: 'supervisor',
label: 'Supervision',
focusKeys: [
'review_scores_supervisor_confirm_title',
'review_scores_supervisor_confirm_body',
'review_scores_supervisor_confirm_action', 'cancel',
],
mock: () => dialogMock({
titleKey: 'review_scores_supervisor_confirm_title',
messageKey: 'review_scores_supervisor_confirm_body',
primaryKey: 'review_scores_supervisor_confirm_action',
secondaryKey: 'cancel',
tone: 'warning',
}),
},
{
id: 'upload_success',
label: 'Upload OK',
focusKeys: [
'upload_success_title', 'upload_success_message',
'upload_success_questionnaires', 'upload_success_scoring_reviews',
'upload_success_client_notes', 'upload_success_sync_next', 'ok',
],
mock: () => dialogMock({
titleKey: 'upload_success_title',
messageKey: 'upload_success_message',
primaryKey: 'ok',
detailsKeys: [
'upload_success_questionnaires',
'upload_success_scoring_reviews',
'upload_success_client_notes',
'upload_success_sync_next',
],
tone: 'info',
}),
},
{
id: 'upload_failed',
label: 'Upload Fehler',
focusKeys: [
'upload_failed_title', 'upload_prepare_failed', 'ok', 'cancel',
],
mock: () => dialogMock({
titleKey: 'upload_failed_title',
messageKey: 'upload_prepare_failed',
primaryKey: 'ok',
secondaryKey: 'cancel',
tone: 'danger',
}),
},
{
id: 'exit_questionnaire',
label: 'Fragebogen verlassen',
focusKeys: [
'questionnaire_exit_title', 'questionnaire_exit_message', 'leave', 'cancel',
],
mock: () => `
${f('questionnaire_exit_title', 'aq-dialog-title')}
${f('questionnaire_exit_message', 'aq-muted aq-sm aq-dialog-msg', 'textarea')}
${f('leave', 'aq-field-on-primary')}
${f('cancel', 'aq-muted')}
`,
},
{
id: 'error_invalid',
label: 'Fehler (Daten)',
focusKeys: ['error', 'error_invalid_data', 'ok'],
mock: () => dialogMock({
titleKey: 'error',
messageKey: 'error_invalid_data',
primaryKey: 'ok',
tone: 'danger',
}),
},
{
id: 'error_not_found',
label: 'Fehler (Nicht gefunden)',
focusKeys: ['error', 'error_not_found', 'ok'],
mock: () => dialogMock({
titleKey: 'error',
messageKey: 'error_not_found',
primaryKey: 'ok',
tone: 'danger',
}),
},
{
id: 'score_notice',
label: 'Punktzahl berechnet',
focusKeys: [
'score_calculated_eyebrow', 'score_calculated_title', 'score_calculated_body',
'score_calculated_result_label', 'score_calculated_next_label',
'score_calculated_next_title', 'score_calculated_band_hint',
'score_calculated_continue', 'category_yellow',
],
mock: () => scoreCalculatedMock(),
},
],
},
];
export function screenById(id) {
return APP_UI_SCREENS.find(s => s.id === id) || APP_UI_SCREENS[0];
}
/** @returns {AppUiVariant[]} */
export function getVariants(screen) {
if (screen.variants?.length) return screen.variants;
return [{
id: 'default',
label: screen.label,
focusKeys: screen.keys,
mock: screen.mock || (() => ''),
}];
}
export function variantById(screen, variantId) {
const variants = getVariants(screen);
return variants.find(v => v.id === variantId) || variants[0];
}
export function allScreenKeys() {
const set = new Set();
APP_UI_SCREENS.forEach(s => s.keys.forEach(k => set.add(k)));
return set;
}