feat: Enhance ChatAssistant component with dynamic privacy prompt and localization support; update various UI elements for improved accessibility and user experience
Fix product card width on mobile.
This commit is contained in:
@@ -52,7 +52,12 @@ class ChatAssistant extends Component {
|
||||
this.fileInputRef = React.createRef();
|
||||
this.recordingTimer = null;
|
||||
}
|
||||
|
||||
|
||||
buildPrivacyPromptHtml = () => {
|
||||
const { t } = this.props;
|
||||
return `${t('chat.privacyPromptBefore')}<a href="/datenschutz" target="_blank" rel="noopener noreferrer">${t('chat.privacyPolicyLink')}</a>${t('chat.privacyPromptAfter')}<button data-confirm-privacy="true">${t('chat.privacyRead')}</button>`;
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// Add socket listeners if socket is available and connected
|
||||
this.addSocketListeners();
|
||||
@@ -69,7 +74,7 @@ class ChatAssistant extends Component {
|
||||
const privacyMessage = {
|
||||
id: 'privacy-prompt',
|
||||
sender: 'bot',
|
||||
text: 'Bitte bestätigen Sie, dass Sie die <a href="/datenschutz" target="_blank" rel="noopener noreferrer">Datenschutzbestimmungen</a> gelesen haben und damit einverstanden sind. <button data-confirm-privacy="true">Gelesen & Akzeptiert</button>',
|
||||
text: this.buildPrivacyPromptHtml(),
|
||||
};
|
||||
const updatedMessages = [privacyMessage, ...prevState.messages];
|
||||
window.chatMessages = updatedMessages;
|
||||
@@ -84,6 +89,19 @@ class ChatAssistant extends Component {
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevProps.i18n?.language !== this.props.i18n?.language) {
|
||||
this.setState((prev) => {
|
||||
const idx = prev.messages.findIndex((m) => m.id === 'privacy-prompt');
|
||||
if (idx === -1) return null;
|
||||
const updatedMessages = [...prev.messages];
|
||||
updatedMessages[idx] = {
|
||||
...updatedMessages[idx],
|
||||
text: this.buildPrivacyPromptHtml(),
|
||||
};
|
||||
window.chatMessages = updatedMessages;
|
||||
return { messages: updatedMessages };
|
||||
});
|
||||
}
|
||||
if (prevState.messages !== this.state.messages || prevState.isTyping !== this.state.isTyping) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
@@ -244,7 +262,7 @@ class ChatAssistant extends Component {
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error accessing microphone:", err);
|
||||
alert("Could not access microphone. Please check your browser permissions.");
|
||||
alert(this.props.t('chat.micPermissionDenied'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -359,7 +377,7 @@ class ChatAssistant extends Component {
|
||||
const newUserMessage = {
|
||||
id: Date.now(),
|
||||
sender: 'user',
|
||||
text: `<img src="${imageUrl}" alt="Uploaded image" style="max-width: 100%; height: auto; border-radius: 8px;" />`,
|
||||
text: `<img src="${imageUrl}" alt="${this.props.t('chat.uploadedImageAlt')}" style="max-width: 100%; height: auto; border-radius: 8px;" />`,
|
||||
isImage: true
|
||||
};
|
||||
|
||||
@@ -451,7 +469,7 @@ class ChatAssistant extends Component {
|
||||
}
|
||||
|
||||
if (domNode.name === 'button' && domNode.attribs && domNode.attribs['data-confirm-privacy']) {
|
||||
return <Button variant="contained" size="small" onClick={this.handlePrivacyConfirm}>Gelesen & Akzeptiert</Button>;
|
||||
return <Button variant="contained" size="small" onClick={this.handlePrivacyConfirm}>{this.props.t('chat.privacyRead')}</Button>;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -505,12 +523,12 @@ class ChatAssistant extends Component {
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" component="div">
|
||||
Assistent
|
||||
{t('chat.assistantTitle')}
|
||||
<Typography component="span" color={this.state.aiThink ? "error" : "text.disabled"} sx={{ display: 'inline' }}>🧠</Typography>
|
||||
<Typography component="span" color={this.state.atDatabase ? "error" : "text.disabled"} sx={{ display: 'inline' }}>🛢</Typography>
|
||||
<Typography component="span" color={this.state.atWeb ? "error" : "text.disabled"} sx={{ display: 'inline' }}>🌐</Typography>
|
||||
</Typography>
|
||||
<IconButton onClick={onClose} size="small" aria-label="Assistent schließen" sx={{ color: 'primary.contrastText' }}>
|
||||
<IconButton onClick={onClose} size="small" aria-label={t('chat.closeAria')} sx={{ color: 'primary.contrastText' }}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
@@ -648,7 +666,7 @@ class ChatAssistant extends Component {
|
||||
autoFocus
|
||||
autoCapitalize="off"
|
||||
autoCorrect="off"
|
||||
placeholder={isRecording ? "Aufnahme läuft..." : "Du kannst mich nach Cannabissorten fragen..."}
|
||||
placeholder={isRecording ? t('chat.placeholderRecording') : t('chat.inputPlaceholder')}
|
||||
value={inputValue}
|
||||
onChange={this.handleInputChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
@@ -670,7 +688,7 @@ class ChatAssistant extends Component {
|
||||
<IconButton
|
||||
color="error"
|
||||
onClick={this.stopRecording}
|
||||
aria-label="Aufnahme stoppen"
|
||||
aria-label={t('chat.micStopAria')}
|
||||
sx={{ ml: { xs: 0, sm: 1 } }}
|
||||
>
|
||||
<StopIcon />
|
||||
@@ -679,7 +697,7 @@ class ChatAssistant extends Component {
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={this.startRecording}
|
||||
aria-label="Sprachaufnahme starten"
|
||||
aria-label={t('chat.micStartAria')}
|
||||
sx={{ ml: { xs: 0, sm: 1 } }}
|
||||
disabled={isTyping || inputsDisabled}
|
||||
>
|
||||
@@ -690,7 +708,7 @@ class ChatAssistant extends Component {
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={this.handleImageUpload}
|
||||
aria-label="Bild hochladen"
|
||||
aria-label={t('chat.uploadImageAria')}
|
||||
sx={{ ml: { xs: 0, sm: 1 } }}
|
||||
disabled={isTyping || isRecording || inputsDisabled}
|
||||
>
|
||||
@@ -703,7 +721,7 @@ class ChatAssistant extends Component {
|
||||
onClick={this.handleSendMessage}
|
||||
disabled={isTyping || isRecording || inputsDisabled}
|
||||
>
|
||||
Senden
|
||||
{t('chat.send')}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user