fixed sysuser and removed redundant fallbacks
This commit is contained in:
parent
d1f0b3c3d6
commit
994664f0b4
27 changed files with 140 additions and 177 deletions
|
|
@ -28,20 +28,13 @@ function _getModeOptions(t: (key: string) => string): { value: ScheduleMode; tit
|
|||
];
|
||||
}
|
||||
|
||||
const MONTH_NAMES_DE = [
|
||||
'Januar',
|
||||
'Februar',
|
||||
'März',
|
||||
'April',
|
||||
'Mai',
|
||||
'Juni',
|
||||
'Juli',
|
||||
'August',
|
||||
'September',
|
||||
'Oktober',
|
||||
'November',
|
||||
'Dezember',
|
||||
function _monthNames(t: (k: string) => string): string[] {
|
||||
return [
|
||||
t('Januar'), t('Februar'), t('März'), t('April'),
|
||||
t('Mai'), t('Juni'), t('Juli'), t('August'),
|
||||
t('September'), t('Oktober'), t('November'), t('Dezember'),
|
||||
];
|
||||
}
|
||||
|
||||
function _getIntervalUnits(t: (key: string) => string): { value: IntervalUnit; label: string; title: string }[] {
|
||||
return [
|
||||
|
|
@ -285,7 +278,7 @@ export const ScheduleStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ par
|
|||
<div className={styles.scheduleFieldCol}>
|
||||
<span className={styles.scheduleFieldLabel}>{t('Wochentage')}</span>
|
||||
<div className={styles.scheduleWeekdayToggles}>
|
||||
{WEEKDAYS_MO_SO.map(({ cronDow, label }) => (
|
||||
{WEEKDAYS_MO_SO.map(({ cronDow }) => (
|
||||
<button
|
||||
key={cronDow}
|
||||
type="button"
|
||||
|
|
@ -294,7 +287,7 @@ export const ScheduleStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ par
|
|||
}
|
||||
onClick={() => toggleWeekday(cronDow)}
|
||||
>
|
||||
{t(label)}
|
||||
{cronDow === 1 ? t('Mo') : cronDow === 2 ? t('Di') : cronDow === 3 ? t('Mi') : cronDow === 4 ? t('Do') : cronDow === 5 ? t('Fr') : cronDow === 6 ? t('Sa') : t('So')}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -365,9 +358,9 @@ export const ScheduleStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ par
|
|||
value={spec.monthIndex}
|
||||
onChange={(e) => push({ ...spec, monthIndex: Number(e.target.value) })}
|
||||
>
|
||||
{MONTH_NAMES_DE.map((name, i) => (
|
||||
{_monthNames(t).map((name, i) => (
|
||||
<option key={i + 1} value={i + 1}>
|
||||
{t(name)}
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ export const SwitchNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, up
|
|||
<option value="">{t('MIME-Typ wählen')}</option>
|
||||
{mimeTypeOptions.map((o) => (
|
||||
<option key={o.value} value={o.value}>
|
||||
{t(o.label)} ({o.value})
|
||||
{o.label} ({o.value})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
|
@ -221,7 +221,7 @@ export const SwitchNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, up
|
|||
>
|
||||
{operators.map((o) => (
|
||||
<option key={o.value} value={o.value}>
|
||||
{t(o.label)}
|
||||
{o.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export function CustomActionButton<T = any>({
|
|||
if (typeof title === 'function') {
|
||||
buttonTitle = title(row);
|
||||
} else if (typeof title === 'string') {
|
||||
buttonTitle = t(title, title); // Try to translate, fallback to original
|
||||
buttonTitle = title;
|
||||
}
|
||||
|
||||
// Determine the final tooltip
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ export const MandateNavigation: React.FC = () => {
|
|||
for (const sg of systemBlock.subgroups) {
|
||||
children.push({
|
||||
id: sg.id,
|
||||
label: t(sg.title),
|
||||
label: sg.title,
|
||||
children: sg.items.map(i => navigationItemToTreeNode(i, t)),
|
||||
defaultExpanded: true,
|
||||
});
|
||||
|
|
@ -288,7 +288,7 @@ export const MandateNavigation: React.FC = () => {
|
|||
if (items.length > 0) items.push({ type: 'separator' });
|
||||
const subgroupNodes: TreeNodeItem[] = adminSubgroups.map(sg => ({
|
||||
id: sg.id,
|
||||
label: t(sg.title),
|
||||
label: sg.title,
|
||||
children: sg.items.map(i => navigationItemToTreeNode(i, t)),
|
||||
defaultExpanded: false,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -87,9 +87,9 @@ const _PROVIDER_LABEL_KEYS: Record<string, string> = {
|
|||
internal: 'Internal',
|
||||
};
|
||||
|
||||
function _providerLabel(provider: string, t: (key: string) => string): string {
|
||||
function _providerLabel(provider: string): string {
|
||||
const key = _PROVIDER_LABEL_KEYS[provider];
|
||||
return key ? t(key) : provider;
|
||||
return key ? key : provider;
|
||||
}
|
||||
|
||||
const PROVIDER_ICONS: Record<string, string> = {
|
||||
|
|
@ -135,7 +135,7 @@ export const ProviderSelect: React.FC<ProviderSelectProps> = ({ value,
|
|||
const providerOptions = useMemo(() => {
|
||||
return allowedProviders.map((provider) => ({
|
||||
value: provider,
|
||||
label: `${PROVIDER_ICONS[provider] || '🔌'} ${_providerLabel(provider, t)}`,
|
||||
label: `${PROVIDER_ICONS[provider] || '🔌'} ${_providerLabel(provider)}`,
|
||||
}));
|
||||
}, [allowedProviders, t]);
|
||||
|
||||
|
|
@ -325,7 +325,7 @@ export const ProviderMultiSelect: React.FC<ProviderMultiSelectProps> = ({
|
|||
/>
|
||||
<span className={styles.icon}>{PROVIDER_ICONS[provider] || '🔌'}</span>
|
||||
<span className={styles.providerName}>
|
||||
{_providerLabel(provider, t)}
|
||||
{_providerLabel(provider)}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
|
|
@ -361,7 +361,7 @@ export const ProviderBadges: React.FC<ProviderBadgesProps> = ({
|
|||
<div className={`${styles.providerBadges} ${className || ''}`}>
|
||||
{providers.map((provider) => (
|
||||
<span key={provider} className={styles.badge}>
|
||||
{PROVIDER_ICONS[provider] || '🔌'} {_providerLabel(provider, t)}
|
||||
{PROVIDER_ICONS[provider] || '🔌'} {_providerLabel(provider)}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -144,21 +144,8 @@ const CreateButton: React.FC<CreateButtonProps> = ({
|
|||
|
||||
const isDisabled = disabled || loading || isCreating;
|
||||
|
||||
// Resolve language text for popup title
|
||||
const resolvedPopupTitle = typeof popupTitle === 'string'
|
||||
? t(popupTitle, popupTitle)
|
||||
: popupTitle;
|
||||
|
||||
// Resolve language text for attributes
|
||||
const resolvedAttributes: AttributeDefinition[] = useMemo(() => {
|
||||
return attributes.map(attr => ({
|
||||
...attr,
|
||||
label: typeof attr.label === 'string' ? t(attr.label, attr.label) : attr.label,
|
||||
placeholder: attr.placeholder
|
||||
? (typeof attr.placeholder === 'string' ? t(attr.placeholder, attr.placeholder) : attr.placeholder)
|
||||
: undefined
|
||||
}));
|
||||
}, [attributes, t]);
|
||||
const resolvedPopupTitle = popupTitle;
|
||||
const resolvedAttributes = attributes;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ const getStatusBadgeClass = (status?: string | null): string => {
|
|||
|
||||
const Log: React.FC<LogProps> = ({
|
||||
className = '',
|
||||
emptyMessage = 'Keine Log-Informationen verfügbar',
|
||||
emptyMessage,
|
||||
dashboardTree,
|
||||
onToggleOperationExpanded,
|
||||
getChildOperations
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const resolvedEmptyMessage = typeof emptyMessage === 'string' ? t(emptyMessage, emptyMessage) : emptyMessage;
|
||||
const resolvedEmptyMessage = emptyMessage || t('Keine Log-Informationen verfügbar');
|
||||
const formatLogTimestamp = (timestamp: number): string => {
|
||||
try {
|
||||
const formatted = formatUnixTimestamp(timestamp, undefined, {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const MapViewLeaflet: React.FC<MapViewProps> = ({ parcels = [],
|
|||
onParcelClick,
|
||||
height = '600px',
|
||||
className = '',
|
||||
emptyMessage = 'Klicken Sie auf die Karte, um einen Standort auszuwählen',
|
||||
emptyMessage,
|
||||
showWfsParcels = false,
|
||||
parcelsApiBaseUrl = ''
|
||||
}) => {
|
||||
|
|
@ -361,7 +361,7 @@ const MapViewLeaflet: React.FC<MapViewProps> = ({ parcels = [],
|
|||
<div ref={mapContainerRef} style={{ width: '100%', height: '100%' }} />
|
||||
{parcels.length === 0 && !center && (
|
||||
<div className={styles.emptyStateOverlay}>
|
||||
<p>{typeof emptyMessage === 'string' ? t(emptyMessage, emptyMessage) : emptyMessage}</p>
|
||||
<p>{emptyMessage || t('Klicken Sie auf die Karte, um einen Standort auszuwählen')}</p>
|
||||
</div>
|
||||
)}
|
||||
{showWfsParcels && isWfsLoading && (
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const Messages: React.FC<MessagesProps> = ({
|
|||
showProgress = true,
|
||||
renderMessage,
|
||||
renderDocument,
|
||||
emptyMessage = 'Noch keine Nachrichten',
|
||||
emptyMessage,
|
||||
onFileDelete,
|
||||
onFileRemove,
|
||||
onFileView,
|
||||
|
|
@ -30,7 +30,7 @@ const Messages: React.FC<MessagesProps> = ({
|
|||
deletingMessages
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const resolvedEmptyMessage = typeof emptyMessage === 'string' ? t(emptyMessage, emptyMessage) : emptyMessage;
|
||||
const resolvedEmptyMessage = emptyMessage || t('Noch keine Nachrichten');
|
||||
if (!messages || messages.length === 0) {
|
||||
return (
|
||||
<div className={`${styles.messagesContainer} ${styles.emptyContainer} ${className}`}>
|
||||
|
|
|
|||
|
|
@ -30,11 +30,14 @@ interface UnifiedDataBarProps {
|
|||
className?: string;
|
||||
}
|
||||
|
||||
const _TAB_KEYS: Record<UdbTab, string> = {
|
||||
chats: 'Chatverläufe',
|
||||
files: 'Dateien',
|
||||
sources: 'Quellen',
|
||||
};
|
||||
function _tabLabel(tab: UdbTab, t: (k: string) => string): string {
|
||||
switch (tab) {
|
||||
case 'chats': return t('Chatverläufe');
|
||||
case 'files': return t('Dateien');
|
||||
case 'sources': return t('Quellen');
|
||||
default: return tab;
|
||||
}
|
||||
}
|
||||
|
||||
const UnifiedDataBar: React.FC<UnifiedDataBarProps> = ({
|
||||
context,
|
||||
|
|
@ -72,7 +75,7 @@ const UnifiedDataBar: React.FC<UnifiedDataBarProps> = ({
|
|||
className={`${styles.tab} ${currentTab === tab ? styles.tabActive : ''}`}
|
||||
onClick={() => _handleTabChange(tab)}
|
||||
>
|
||||
{t(_TAB_KEYS[tab])}
|
||||
{_tabLabel(tab, t)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -82,23 +82,10 @@ export const SidebarProvider: React.FC<SidebarProviderProps> = ({ children }) =>
|
|||
}
|
||||
|
||||
// Helper function to resolve node name
|
||||
const resolveNodeName = (pathSegment: string, fullPath: string, page?: GenericPageData): string => {
|
||||
const resolveNodeName = (pathSegment: string, _fullPath: string, page?: GenericPageData): string => {
|
||||
if (page) {
|
||||
return resolveLanguageText(page.name, t);
|
||||
}
|
||||
// Try translation key (e.g., "start.real-estate.title")
|
||||
const translationKey = `${fullPath}.title`;
|
||||
const translated = t(translationKey);
|
||||
if (translated !== translationKey) {
|
||||
return translated;
|
||||
}
|
||||
// Try just the segment (e.g., "real-estate.title")
|
||||
const segmentKey = `${pathSegment}.title`;
|
||||
const segmentTranslated = t(segmentKey);
|
||||
if (segmentTranslated !== segmentKey) {
|
||||
return segmentTranslated;
|
||||
}
|
||||
// Fallback to capitalized segment
|
||||
return pathSegment.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ');
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ export const FeatureLayout: React.FC = () => {
|
|||
? navFeature.instances.find(i => i.id === instanceId)
|
||||
: undefined;
|
||||
return {
|
||||
mandate: t(navMandate.uiLabel),
|
||||
feature: navFeature ? t(navFeature.uiLabel) : undefined,
|
||||
instance: navInstance ? t(navInstance.uiLabel) : undefined,
|
||||
mandate: navMandate.uiLabel,
|
||||
feature: navFeature ? navFeature.uiLabel : undefined,
|
||||
instance: navInstance ? navInstance.uiLabel : undefined,
|
||||
};
|
||||
}, [dynamicBlock, mandateId, featureCode, instanceId, t]);
|
||||
}, [dynamicBlock, mandateId, featureCode, instanceId]);
|
||||
|
||||
// Warten bis Features geladen sind
|
||||
if (!initialized || loading || isLoading) {
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ export const AutomationsDashboardPage: React.FC = () => {
|
|||
filterable: true,
|
||||
formatter: (v: string) => (
|
||||
<span style={{ color: _STATUS_COLORS[v] || 'inherit', fontWeight: 600 }}>
|
||||
{t(v === 'completed' ? 'Abgeschlossen' : v === 'failed' ? 'Fehlgeschlagen' : v === 'running' ? 'Laufend' : v)}
|
||||
{v === 'completed' ? t('Abgeschlossen') : v === 'failed' ? t('Fehlgeschlagen') : v === 'running' ? t('Laufend') : v}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -29,11 +29,9 @@ const STORE_FEATURE_DESCRIPTION_FALLBACK: Record<string, string> = {
|
|||
commcoach: 'CommCoach: Kommunikation trainieren mit KI-gestütztem Coaching und Feedback.',
|
||||
};
|
||||
|
||||
function _storeCardDescription(feature: StoreFeature, t: (key: string, fallback?: string) => string): string {
|
||||
const raw =
|
||||
(feature.description && feature.description.trim()) ||
|
||||
STORE_FEATURE_DESCRIPTION_FALLBACK[feature.featureCode];
|
||||
return raw ? t(raw) : '';
|
||||
function _storeCardDescription(feature: StoreFeature): string {
|
||||
return (feature.description && feature.description.trim()) ||
|
||||
STORE_FEATURE_DESCRIPTION_FALLBACK[feature.featureCode] || '';
|
||||
}
|
||||
|
||||
interface FeatureCardProps {
|
||||
|
|
@ -62,13 +60,13 @@ const FeatureCard: React.FC<FeatureCardProps> = ({
|
|||
<div className={styles.cardHeader}>
|
||||
{icon && <span className={styles.cardIcon}>{icon}</span>}
|
||||
<h3 className={styles.cardTitle}>
|
||||
{t(feature.label)}
|
||||
{feature.label}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className={styles.cardBody}>
|
||||
<p className={styles.cardDescription}>
|
||||
{_storeCardDescription(feature, t)}
|
||||
{_storeCardDescription(feature)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ function getMandateName(mandate: Mandate): string {
|
|||
return mandate.label || mandate.name || mandate.id;
|
||||
}
|
||||
|
||||
function getFeatureLabel(feature: Feature, t: (k: string) => string): string {
|
||||
return t(feature.label || feature.code);
|
||||
function getFeatureLabel(feature: Feature): string {
|
||||
return feature.label || feature.code;
|
||||
}
|
||||
|
||||
export interface InstanceWithStats extends FeatureInstance {
|
||||
|
|
@ -165,7 +165,7 @@ export const AccessManagementHub: React.FC = () => {
|
|||
instance,
|
||||
mandateId: mandateId || '',
|
||||
mandateName: mandate ? getMandateName(mandate) : mandateId || '',
|
||||
featureLabel: feature ? getFeatureLabel(feature, t) : instance.featureCode,
|
||||
featureLabel: feature ? getFeatureLabel(feature) : instance.featureCode,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -294,7 +294,7 @@ export const AccessManagementHub: React.FC = () => {
|
|||
return {
|
||||
id: inst.id,
|
||||
label: inst.label,
|
||||
featureLabel: feature ? getFeatureLabel(feature, t) : inst.featureCode,
|
||||
featureLabel: feature ? getFeatureLabel(feature) : inst.featureCode,
|
||||
userCount: inst.userCount ?? 0,
|
||||
};
|
||||
}),
|
||||
|
|
@ -363,7 +363,7 @@ export const AccessManagementHub: React.FC = () => {
|
|||
<option value="">{t('Alle')}</option>
|
||||
{features.map((f) => (
|
||||
<option key={f.code} value={f.code}>
|
||||
{getFeatureLabel(f, t)}
|
||||
{getFeatureLabel(f)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
|
@ -424,7 +424,7 @@ export const AccessManagementHub: React.FC = () => {
|
|||
instancesByMandate={instancesByMandate}
|
||||
instanceUsersMap={instanceUsersMap}
|
||||
features={features}
|
||||
getFeatureLabel={(f) => getFeatureLabel(f, t)}
|
||||
getFeatureLabel={getFeatureLabel}
|
||||
loading={hierarchyUsersLoading}
|
||||
onOpenDetail={handleOpenDetail}
|
||||
/>
|
||||
|
|
@ -526,7 +526,7 @@ export const AccessManagementHub: React.FC = () => {
|
|||
</span>
|
||||
</div>
|
||||
<div className={hubStyles.instanceMeta}>
|
||||
<span>{getFeatureLabel(features.find((f) => f.code === inst.featureCode) || { code: inst.featureCode, label: inst.featureCode }, t)}</span>
|
||||
<span>{getFeatureLabel(features.find((f) => f.code === inst.featureCode) || { code: inst.featureCode, label: inst.featureCode })}</span>
|
||||
<span>
|
||||
{inst.userCount ?? '—'} {t('Benutzer')}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -92,10 +92,7 @@ export const AdminFeatureAccessPage: React.FC = () => {
|
|||
{ key: 'featureCode', label: t('Feature'), type: 'string' as const, sortable: true, filterable: true, width: 150,
|
||||
render: (value: string) => {
|
||||
const feature = features.find(f => f.code === value);
|
||||
if (feature) {
|
||||
return t(feature.label || value);
|
||||
}
|
||||
return value;
|
||||
return feature ? (feature.label || value) : value;
|
||||
}
|
||||
},
|
||||
{ key: 'enabled', label: t('Aktiv'), type: 'boolean' as const, sortable: true, filterable: true, width: 80 },
|
||||
|
|
@ -323,10 +320,7 @@ export const AdminFeatureAccessPage: React.FC = () => {
|
|||
// Get feature label
|
||||
const getFeatureLabel = (code: string) => {
|
||||
const feature = features.find(f => f.code === code);
|
||||
if (feature) {
|
||||
return t(feature.label || code);
|
||||
}
|
||||
return code;
|
||||
return feature ? (feature.label || code) : code;
|
||||
};
|
||||
|
||||
if (error && !selectedMandateId) {
|
||||
|
|
@ -514,7 +508,7 @@ export const AdminFeatureAccessPage: React.FC = () => {
|
|||
<DropdownSelect
|
||||
items={features.map(f => ({
|
||||
id: f.code,
|
||||
label: t(f.label || f.code),
|
||||
label: f.label || f.code,
|
||||
value: f.code
|
||||
}))}
|
||||
selectedItemId={createFeatureCode}
|
||||
|
|
|
|||
|
|
@ -367,10 +367,7 @@ export const AdminFeatureInstanceUsersPage: React.FC = () => {
|
|||
// Get feature label
|
||||
const getFeatureLabel = (code: string) => {
|
||||
const feature = features.find(f => f.code === code);
|
||||
if (feature) {
|
||||
return t(feature.label || code);
|
||||
}
|
||||
return code;
|
||||
return feature ? (feature.label || code) : code;
|
||||
};
|
||||
|
||||
// Get selected instance info from combined options
|
||||
|
|
|
|||
|
|
@ -260,10 +260,8 @@ export const AdminFeatureRolesPage: React.FC = () => {
|
|||
setEditingRole(role);
|
||||
};
|
||||
|
||||
// Get feature name - Backend uses 'label' field (German i18n key or legacy multilingual)
|
||||
const getFeatureName = (feature: Feature) => {
|
||||
const raw = getTextValue(feature.label || feature.name);
|
||||
return raw === '-' ? '-' : t(raw);
|
||||
return feature.label || feature.name || '-';
|
||||
};
|
||||
|
||||
if (error && !selectedFeatureCode) {
|
||||
|
|
|
|||
|
|
@ -109,10 +109,7 @@ export const AdminMandateWizardPage: React.FC = () => {
|
|||
|
||||
const getFeatureLabel = (code: string): string => {
|
||||
const f = features.find(feat => feat.code === code);
|
||||
if (f) {
|
||||
return t(f.label || code);
|
||||
}
|
||||
return code;
|
||||
return f ? (f.label || code) : code;
|
||||
};
|
||||
|
||||
const getUserDisplayName = (u: { fullName?: string; firstname?: string | null; lastname?: string | null; username: string }): string => {
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ export const FeatureInstanceWizard: React.FC<FeatureInstanceWizardProps> = ({ ma
|
|||
const [selectedUserRoles, setSelectedUserRoles] = useState<Array<{ userId: string; roleIds: string[] }>>([]);
|
||||
|
||||
const featureOptions = useMemo(
|
||||
() => features.map((f) => ({ value: f.code, label: t(f.label || f.code) })),
|
||||
[features, t]
|
||||
() => features.map((f) => ({ value: f.code, label: f.label || f.code })),
|
||||
[features]
|
||||
);
|
||||
const mandateOptions = useMemo(
|
||||
() => mandates.map((m) => ({ value: m.id, label: getMandateName(m) })),
|
||||
|
|
|
|||
|
|
@ -858,7 +858,7 @@ export const CommcoachDossierView: React.FC<CommcoachDossierViewProps> = ({ pers
|
|||
{_groupScoresByDimension(coach.scores).map(group => (
|
||||
<div key={group.dimension} className={styles.scoreGroup}>
|
||||
<div className={styles.scoreDimension}>
|
||||
<span className={styles.scoreDimensionLabel}>{t(_dimensionLabel(group.dimension))}</span>
|
||||
<span className={styles.scoreDimensionLabel}>{_dimensionLabel(group.dimension, t)}</span>
|
||||
<span className={styles.scoreLatest}>{Math.round(group.latest.score)}/100</span>
|
||||
<span className={`${styles.scoreTrend} ${styles[`trend_${group.latest.trend}`]}`}>
|
||||
{group.latest.trend === 'improving' ? 'steigend' : group.latest.trend === 'declining' ? 'sinkend' : 'stabil'}
|
||||
|
|
@ -926,13 +926,15 @@ function _groupScoresByDimension(scores: any[]): ScoreGroup[] {
|
|||
return Object.values(groups);
|
||||
}
|
||||
|
||||
function _dimensionLabel(dim: string): string {
|
||||
const labels: Record<string, string> = {
|
||||
empathy: 'Einfühlungsvermögen', clarity: 'Klarheit',
|
||||
assertiveness: 'Durchsetzung', listening: 'Zuhören',
|
||||
selfReflection: 'Selbstreflexion',
|
||||
};
|
||||
return labels[dim] || dim;
|
||||
function _dimensionLabel(dim: string, t: (k: string) => string): string {
|
||||
switch (dim) {
|
||||
case 'empathy': return t('Einfühlungsvermögen');
|
||||
case 'clarity': return t('Klarheit');
|
||||
case 'assertiveness': return t('Durchsetzung');
|
||||
case 'listening': return t('Zuhören');
|
||||
case 'selfReflection': return t('Selbstreflexion');
|
||||
default: return dim;
|
||||
}
|
||||
}
|
||||
|
||||
function _formatToolPayload(payload: Record<string, unknown>): string {
|
||||
|
|
|
|||
|
|
@ -30,15 +30,18 @@ import styles from './Automation2WorkflowsTasks.module.css';
|
|||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
const NODE_TYPE_LABELS: Record<string, string> = {
|
||||
'input.form': 'Formular',
|
||||
'input.approval': 'Genehmigung',
|
||||
'input.upload': 'Upload',
|
||||
'input.comment': 'Kommentar',
|
||||
'input.review': 'Prüfung',
|
||||
'input.selection': 'Auswahl',
|
||||
'input.confirmation': 'Bestätigung',
|
||||
};
|
||||
function _nodeTypeLabel(nodeType: string, t: (k: string) => string): string {
|
||||
switch (nodeType) {
|
||||
case 'input.form': return t('Formular');
|
||||
case 'input.approval': return t('Genehmigung');
|
||||
case 'input.upload': return t('Upload');
|
||||
case 'input.comment': return t('Kommentar');
|
||||
case 'input.review': return t('Prüfung');
|
||||
case 'input.selection': return t('Auswahl');
|
||||
case 'input.confirmation': return t('Bestätigung');
|
||||
default: return nodeType;
|
||||
}
|
||||
}
|
||||
|
||||
function formatTimestamp(ts?: number): string {
|
||||
if (ts == null || ts <= 0) return '—';
|
||||
|
|
@ -941,7 +944,7 @@ const TaskCard: React.FC<TaskCardProps> = ({
|
|||
<div className={styles.taskMetaRow}>
|
||||
<span className={styles.metaLabel}>{t('Typ')}</span>
|
||||
<span className={styles.metaValue}>
|
||||
{t(NODE_TYPE_LABELS[nodeType] ?? nodeType)}
|
||||
{_nodeTypeLabel(nodeType, t)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -30,14 +30,19 @@ const _TABS: TabDef[] = [
|
|||
{ id: 'year-end', templateTag: 'template:trustee-year-end-check', icon: '\u2705', color: '#795548' },
|
||||
];
|
||||
|
||||
const _TAB_LABEL_KEYS: Record<string, string> = {
|
||||
'year-end': 'Jahresabschluss prüfen',
|
||||
};
|
||||
function _tabLabel(tabId: string, t: (k: string) => string): string {
|
||||
switch (tabId) {
|
||||
case 'year-end': return t('Jahresabschluss prüfen');
|
||||
default: return tabId;
|
||||
}
|
||||
}
|
||||
|
||||
const _TAB_DESCRIPTION_KEYS: Record<string, string> = {
|
||||
'year-end':
|
||||
'Automatische Prüfungen für den Jahresabschluss: Saldovalidierung, Vorjahresvergleich, gesetzliche Checks.',
|
||||
};
|
||||
function _tabDescription(tabId: string, t: (k: string) => string): string {
|
||||
switch (tabId) {
|
||||
case 'year-end': return t('Automatische Prüfungen für den Jahresabschluss: Saldovalidierung, Vorjahresvergleich, gesetzliche Checks.');
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
|
|
@ -225,7 +230,7 @@ export const TrusteeAbschlussView: React.FC = () => {
|
|||
}}
|
||||
>
|
||||
<span style={{ marginRight: '0.375rem' }}>{tab.icon}</span>
|
||||
{t(_TAB_LABEL_KEYS[tab.id] || tab.id)}
|
||||
{_tabLabel(tab.id, t)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -234,7 +239,7 @@ export const TrusteeAbschlussView: React.FC = () => {
|
|||
{/* Tab content */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
||||
<p className={styles.sectionDescription}>
|
||||
{_TAB_DESCRIPTION_KEYS[activeTab] ? t(_TAB_DESCRIPTION_KEYS[activeTab]) : ''}
|
||||
{_tabDescription(activeTab, t)}
|
||||
</p>
|
||||
|
||||
{workflowsLoading ? (
|
||||
|
|
|
|||
|
|
@ -33,19 +33,25 @@ const _TABS: TabDef[] = [
|
|||
{ id: 'forecast', templateTag: 'template:trustee-forecast', icon: '\uD83D\uDCC8', color: '#E91E63' },
|
||||
];
|
||||
|
||||
const _TAB_LABEL_KEYS: Record<string, string> = {
|
||||
budget: 'Budget-Vergleich',
|
||||
kpi: 'KPI-Dashboard',
|
||||
cashflow: 'Cashflow-Rechnung',
|
||||
forecast: 'Prognose',
|
||||
};
|
||||
function _tabLabel(tabId: string, t: (k: string) => string): string {
|
||||
switch (tabId) {
|
||||
case 'budget': return t('Budget-Vergleich');
|
||||
case 'kpi': return t('KPI-Dashboard');
|
||||
case 'cashflow': return t('Cashflow-Rechnung');
|
||||
case 'forecast': return t('Prognose');
|
||||
default: return tabId;
|
||||
}
|
||||
}
|
||||
|
||||
const _TAB_DESCRIPTION_KEYS: Record<string, string> = {
|
||||
budget: 'Soll/Ist-Vergleich der Buchhaltung mit Budget-Excel',
|
||||
kpi: 'Kennzahlen berechnen und visualisieren',
|
||||
cashflow: 'Cashflow berechnen und analysieren',
|
||||
forecast: 'Trend-Analyse und Prognose der nächsten Monate',
|
||||
};
|
||||
function _tabDescription(tabId: string, t: (k: string) => string): string {
|
||||
switch (tabId) {
|
||||
case 'budget': return t('Soll/Ist-Vergleich der Buchhaltung mit Budget-Excel');
|
||||
case 'kpi': return t('Kennzahlen berechnen und visualisieren');
|
||||
case 'cashflow': return t('Cashflow berechnen und analysieren');
|
||||
case 'forecast': return t('Trend-Analyse und Prognose der nächsten Monate');
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
|
|
@ -240,7 +246,7 @@ export const TrusteeAnalyseView: React.FC = () => {
|
|||
}}
|
||||
>
|
||||
<span style={{ marginRight: '0.375rem' }}>{tab.icon}</span>
|
||||
{t(_TAB_LABEL_KEYS[tab.id] || tab.id)}
|
||||
{_tabLabel(tab.id, t)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -248,7 +254,7 @@ export const TrusteeAnalyseView: React.FC = () => {
|
|||
{/* Tab content */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
||||
<p className={styles.sectionDescription}>
|
||||
{_TAB_DESCRIPTION_KEYS[activeTab] ? t(_TAB_DESCRIPTION_KEYS[activeTab]) : ''}
|
||||
{_tabDescription(activeTab, t)}
|
||||
</p>
|
||||
|
||||
{workflowsLoading ? (
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* - Full details shown on error for debugging
|
||||
*/
|
||||
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import React, { useState} from 'react';
|
||||
import type { ToolActivity } from './useWorkspace';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ export const ToolActivityLog: React.FC<ToolActivityLogProps> = ({ activities })
|
|||
const { t } = useLanguage();
|
||||
const [expandedId, setExpandedId] = useState<string | null>(null);
|
||||
|
||||
const translate = useCallback<TranslateFn>((key, params) => t(key, params), [t]);
|
||||
const translate: TranslateFn = t;
|
||||
|
||||
if (!activities.length) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -25,16 +25,19 @@ import { formatBinaryDataSizeBytes } from '../../../utils/formatDataSize';
|
|||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
const MIME_LABELS: Record<string, string> = {
|
||||
pdf: 'PDF',
|
||||
office_doc: 'Office (Text)',
|
||||
office_sheet: 'Office (Tabellen)',
|
||||
office_slides: 'Office (Folien)',
|
||||
text: 'Text',
|
||||
image: 'Bild',
|
||||
html: 'HTML',
|
||||
other: 'Sonstige',
|
||||
};
|
||||
function _mimeLabel(key: string, t: (k: string) => string): string {
|
||||
switch (key) {
|
||||
case 'pdf': return t('PDF');
|
||||
case 'office_doc': return t('Office (Text)');
|
||||
case 'office_sheet': return t('Office (Tabellen)');
|
||||
case 'office_slides': return t('Office (Folien)');
|
||||
case 'text': return t('Text');
|
||||
case 'image': return t('Bild');
|
||||
case 'html': return t('HTML');
|
||||
case 'other': return t('Sonstige');
|
||||
default: return key;
|
||||
}
|
||||
}
|
||||
|
||||
const CHART_COLORS = ['#1976d2', '#00897b', '#6a1b9a', '#e65100', '#5d4037', '#455a64', '#c62828'];
|
||||
|
||||
|
|
@ -118,7 +121,7 @@ export const WorkspaceRagInsightsPage: React.FC = () => {
|
|||
const kpis = stats?.kpis;
|
||||
const timeline = stats?.timelineIndexedDocuments ?? [];
|
||||
const mimeRows = Object.entries(stats?.documentsByMimeCategory ?? {}).map(([key, value]) => ({
|
||||
name: t(MIME_LABELS[key] ?? key),
|
||||
name: _mimeLabel(key, t),
|
||||
value,
|
||||
}));
|
||||
const statusRows = Object.entries(stats?.indexedDocumentsByStatus ?? {}).map(([name, value]) => ({
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ type TranslateParams = Record<string, string | number | boolean | null | undefin
|
|||
interface LanguageContextType {
|
||||
currentLanguage: Language;
|
||||
setLanguage: (language: Language) => void;
|
||||
t: (key: string, paramsOrFallback?: TranslateParams | string) => string;
|
||||
t: (key: string, params?: TranslateParams) => string;
|
||||
isLoading: boolean;
|
||||
reloadLanguage: () => Promise<void>;
|
||||
availableLanguages: I18nCodeInfo[];
|
||||
|
|
@ -123,18 +123,8 @@ export const LanguageProvider: React.FC<LanguageProviderProps> = ({ children })
|
|||
return out;
|
||||
};
|
||||
|
||||
const t = (key: string, paramsOrFallback?: TranslateParams | string): string => {
|
||||
let params: TranslateParams | undefined;
|
||||
if (typeof paramsOrFallback === 'string') {
|
||||
params = undefined;
|
||||
} else {
|
||||
params = paramsOrFallback;
|
||||
}
|
||||
|
||||
const resolved =
|
||||
translations[key] ??
|
||||
(typeof paramsOrFallback === 'string' ? paramsOrFallback : undefined) ??
|
||||
`[${key}]`;
|
||||
const t = (key: string, params?: TranslateParams): string => {
|
||||
const resolved = translations[key] ?? `[${key}]`;
|
||||
return _applyParams(resolved, params);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue