mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-03 16:10:05 +00:00
feat: add view to see custom user profile fields
This commit is contained in:
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_ExtendedProfileView {
|
||||||
|
/* Anpassung an das bestehende UserInfo Panel */
|
||||||
|
font-size: var(--cpd-font-size-body-sm);
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
/* Styling für das Accordion */
|
||||||
|
.mx_ExtendedProfileView_accordion {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Abstand zwischen den Feldern */
|
||||||
|
.mx_ExtendedProfileView_content {
|
||||||
|
padding: var(--cpd-space-2x) 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--cpd-space-4x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styling für die Felder */
|
||||||
|
.mx_ExtendedProfileView_field {
|
||||||
|
margin-bottom: var(--cpd-space-2x);
|
||||||
|
|
||||||
|
.mx_ExtendedProfileView_icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
color: var(--cpd-color-icon-secondary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels und Werte */
|
||||||
|
.mx_ExtendedProfileView_label {
|
||||||
|
color: var(--cpd-color-text-secondary);
|
||||||
|
font-size: var(--cpd-font-size-body-xs);
|
||||||
|
margin-bottom: var(--cpd-space-1x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ExtendedProfileView_value {
|
||||||
|
color: var(--cpd-color-text-primary);
|
||||||
|
word-break: break-word;
|
||||||
|
|
||||||
|
&.mx_ExtendedProfileView_multiline {
|
||||||
|
white-space: pre-line;
|
||||||
|
margin-top: var(--cpd-space-1x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spezielle Styles für Website-Links */
|
||||||
|
.mx_ExtendedProfileView_link {
|
||||||
|
color: var(--cpd-color-text-action-accent);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Für Bereich "Weitere Informationen" */
|
||||||
|
.mx_ExtendedProfileView_further_info {
|
||||||
|
padding: var(--cpd-space-1x) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading State */
|
||||||
|
.mx_ExtendedProfileView_loading {
|
||||||
|
padding: var(--cpd-space-4x) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error State */
|
||||||
|
.mx_ExtendedProfileView_error {
|
||||||
|
margin: var(--cpd-space-2x) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo {
|
||||||
|
padding: 16px;
|
||||||
|
background-color: $system;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo_loading {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo_heading {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo_content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo_row {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo_row .mx_UserExtendedProfileInfo_field {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo_field {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo_label {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
color: $tertiary-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo_value {
|
||||||
|
word-break: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme specific styles */
|
||||||
|
.mx_UserExtendedProfileInfo_value a {
|
||||||
|
color: $accent;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserExtendedProfileInfo_value a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
import { Alert } from "@vector-im/compound-web";
|
||||||
|
import GlobeIcon from "@vector-im/compound-design-tokens/assets/web/icons/globe";
|
||||||
|
import LocationIcon from "@vector-im/compound-design-tokens/assets/web/icons/location";
|
||||||
|
import BadgeIcon from "@vector-im/compound-design-tokens/assets/web/icons/badge";
|
||||||
|
import StarIcon from "@vector-im/compound-design-tokens/assets/web/icons/star";
|
||||||
|
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
|
import { SdkContextClass } from "../../../contexts/SDKContext";
|
||||||
|
import { Container } from "../right_panel/UserInfo";
|
||||||
|
import InlineSpinner from "../elements/InlineSpinner";
|
||||||
|
import {
|
||||||
|
EXT_PROFILE_KEY_ADDRESS,
|
||||||
|
EXT_PROFILE_KEY_FURTHER_INFO,
|
||||||
|
EXT_PROFILE_KEY_SPECIALIZATION,
|
||||||
|
EXT_PROFILE_KEY_TITLE,
|
||||||
|
EXT_PROFILE_KEY_WEBSITE,
|
||||||
|
} from "../settings/ExtendedProfileSettings";
|
||||||
|
import { Flex } from "../../utils/Flex";
|
||||||
|
|
||||||
|
interface ExtendedProfileViewProps {
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExtendedProfileData = {
|
||||||
|
title: string | null;
|
||||||
|
specialization: string | null;
|
||||||
|
address: string | null;
|
||||||
|
website: string | null;
|
||||||
|
furtherInfo: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Komponente zur Anzeige erweiterter Profilfelder eines anderen Benutzers
|
||||||
|
*/
|
||||||
|
const ExtendedProfileView: React.FC<ExtendedProfileViewProps> = ({ userId }) => {
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [error, setError] = useState<boolean>(false);
|
||||||
|
const [profileData, setProfileData] = useState<ExtendedProfileData | null>(null);
|
||||||
|
const [hasData, setHasData] = useState<boolean>(false);
|
||||||
|
|
||||||
|
//const client = useMatrixClientContext();
|
||||||
|
const userProfilesStore = SdkContextClass.instance.userProfilesStore;
|
||||||
|
|
||||||
|
// Laden der erweiterten Profilfelder für den angegebenen Benutzer
|
||||||
|
const fetchExtendedProfileData = useCallback(async () => {
|
||||||
|
if (!userId) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setError(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [
|
||||||
|
titleValue,
|
||||||
|
specializationValue,
|
||||||
|
addressValue,
|
||||||
|
websiteValue,
|
||||||
|
furtherInfoValue,
|
||||||
|
] = await Promise.all([
|
||||||
|
userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_TITLE),
|
||||||
|
userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_SPECIALIZATION),
|
||||||
|
userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_ADDRESS),
|
||||||
|
userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_WEBSITE),
|
||||||
|
userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_FURTHER_INFO),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
title: titleValue as string | null,
|
||||||
|
specialization: specializationValue as string | null,
|
||||||
|
address: addressValue as string | null,
|
||||||
|
website: websiteValue as string | null,
|
||||||
|
furtherInfo: furtherInfoValue as string | null,
|
||||||
|
};
|
||||||
|
|
||||||
|
setProfileData(data);
|
||||||
|
|
||||||
|
// Prüfen, ob mindestens ein Feld ausgefüllt ist
|
||||||
|
const hasAnyData = Object.values(data).some((value) => value !== null && value !== "");
|
||||||
|
setHasData(hasAnyData);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Fehler beim Laden der erweiterten Profildaten", e);
|
||||||
|
setError(true);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [userId, userProfilesStore]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchExtendedProfileData();
|
||||||
|
}, [fetchExtendedProfileData]);
|
||||||
|
|
||||||
|
// Wenn keine Daten vorhanden sind, zeigen wir nichts an
|
||||||
|
if (!loading && !hasData && !error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className="mx_ExtendedProfileView">
|
||||||
|
{loading ? (
|
||||||
|
<Flex justify="center" align="center" className="mx_ExtendedProfileView_loading">
|
||||||
|
<InlineSpinner />
|
||||||
|
</Flex>
|
||||||
|
) : error ? (
|
||||||
|
<Alert
|
||||||
|
type="critical"
|
||||||
|
title={_t("common|error")}
|
||||||
|
className="mx_ExtendedProfileView_error"
|
||||||
|
>
|
||||||
|
{_t("Fehler beim Laden der Profilinformationen")}
|
||||||
|
</Alert>
|
||||||
|
) : hasData ? (
|
||||||
|
<div className="mx_ExtendedProfileView_content">
|
||||||
|
{(profileData?.title || profileData?.specialization) && (
|
||||||
|
<div className="mx_ExtendedProfileView_section mx_ExtendedProfileView_professional">
|
||||||
|
{profileData.title && (
|
||||||
|
<div className="mx_ExtendedProfileView_field">
|
||||||
|
<Flex align="center" gap="var(--cpd-space-2x)">
|
||||||
|
<BadgeIcon className="mx_ExtendedProfileView_icon" />
|
||||||
|
<div>
|
||||||
|
<div className="mx_ExtendedProfileView_label">{_t("settings|extended_profile_settings|professional_title")}</div>
|
||||||
|
<div className="mx_ExtendedProfileView_value">{profileData.title}</div>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{profileData.specialization && (
|
||||||
|
<div className="mx_ExtendedProfileView_field">
|
||||||
|
<Flex align="center" gap="var(--cpd-space-2x)">
|
||||||
|
<StarIcon className="mx_ExtendedProfileView_icon" />
|
||||||
|
<div>
|
||||||
|
<div className="mx_ExtendedProfileView_label">{_t("settings|extended_profile_settings|specialization")}</div>
|
||||||
|
<div className="mx_ExtendedProfileView_value">{profileData.specialization}</div>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{profileData?.address && (
|
||||||
|
<div className="mx_ExtendedProfileView_field">
|
||||||
|
<Flex align="center" gap="var(--cpd-space-2x)">
|
||||||
|
<LocationIcon className="mx_ExtendedProfileView_icon" />
|
||||||
|
<div>
|
||||||
|
<div className="mx_ExtendedProfileView_label">{_t("settings|extended_profile_settings|address")}</div>
|
||||||
|
<div className="mx_ExtendedProfileView_value">{profileData.address}</div>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{profileData?.website && (
|
||||||
|
<div className="mx_ExtendedProfileView_field">
|
||||||
|
<Flex align="center" gap="var(--cpd-space-2x)">
|
||||||
|
<GlobeIcon className="mx_ExtendedProfileView_icon" />
|
||||||
|
<div>
|
||||||
|
<div className="mx_ExtendedProfileView_label">{_t("settings|extended_profile_settings|website")}</div>
|
||||||
|
<div className="mx_ExtendedProfileView_value">
|
||||||
|
<a
|
||||||
|
href={profileData.website.startsWith("http") ? profileData.website : `https://${profileData.website}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
className="mx_ExtendedProfileView_link"
|
||||||
|
>
|
||||||
|
{profileData.website}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{profileData?.furtherInfo && (
|
||||||
|
<div className="mx_ExtendedProfileView_field">
|
||||||
|
<div className="mx_ExtendedProfileView_further_info">
|
||||||
|
<div className="mx_ExtendedProfileView_label">{_t("settings|extended_profile_settings|further_information")}</div>
|
||||||
|
<div className="mx_ExtendedProfileView_value mx_ExtendedProfileView_multiline">{profileData.furtherInfo}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExtendedProfileView;
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Text } from "@vector-im/compound-web";
|
||||||
|
|
||||||
|
import { SdkContextClass } from "../../../contexts/SDKContext";
|
||||||
|
import Spinner from "../elements/Spinner";
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
|
// Extended profile property keys
|
||||||
|
import {
|
||||||
|
EXT_PROFILE_KEY_TITLE,
|
||||||
|
EXT_PROFILE_KEY_SPECIALIZATION,
|
||||||
|
EXT_PROFILE_KEY_ADDRESS,
|
||||||
|
EXT_PROFILE_KEY_WEBSITE,
|
||||||
|
EXT_PROFILE_KEY_FURTHER_INFO,
|
||||||
|
} from "../settings/ExtendedProfileSettings";
|
||||||
|
|
||||||
|
// Helper function to preserve line breaks
|
||||||
|
const renderMultilineText = (text: string): React.ReactNode[] => {
|
||||||
|
const lines = text.split("\n");
|
||||||
|
return lines.map((line, i) => (
|
||||||
|
<React.Fragment key={i}>
|
||||||
|
{line}
|
||||||
|
{i < lines.length - 1 && <br />}
|
||||||
|
</React.Fragment>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
interface UserExtendedProfileInfoProps {
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserExtendedProfileInfo: React.FC<UserExtendedProfileInfoProps> = ({ userId }) => {
|
||||||
|
//const client = useMatrixClientContext();
|
||||||
|
const userProfilesStore = SdkContextClass.instance.userProfilesStore;
|
||||||
|
|
||||||
|
const [title, setTitle] = useState<string>("");
|
||||||
|
const [specialization, setSpecialization] = useState<string>("");
|
||||||
|
const [address, setAddress] = useState<string>("");
|
||||||
|
const [website, setWebsite] = useState<string>("");
|
||||||
|
const [furtherInfo, setFurtherInfo] = useState<string>("");
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [error, setError] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchProfileData = async () => {
|
||||||
|
try {
|
||||||
|
setError(false);
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const titleValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_TITLE);
|
||||||
|
const specializationValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_SPECIALIZATION);
|
||||||
|
const addressValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_ADDRESS);
|
||||||
|
const websiteValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_WEBSITE);
|
||||||
|
const furtherInfoValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_FURTHER_INFO);
|
||||||
|
|
||||||
|
setTitle(titleValue as string || "");
|
||||||
|
setSpecialization(specializationValue as string || "");
|
||||||
|
setAddress(addressValue as string || "");
|
||||||
|
setWebsite(websiteValue as string || "");
|
||||||
|
setFurtherInfo(furtherInfoValue as string || "");
|
||||||
|
} catch {
|
||||||
|
setError(true);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchProfileData();
|
||||||
|
}, [userId, userProfilesStore]);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <Spinner />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <div className="mx_UserExtendedProfileInfo_error">{_t("user_info|extended_profile|error_loading_profile")}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't render anything if there's no data
|
||||||
|
if (!title && !specialization && !address && !website && !furtherInfo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_UserInfo_container mx_UserInfo_profileInfo">
|
||||||
|
{/* <div className="mx_UserInfo_container_header">
|
||||||
|
<Text weight="semibold">{_t("user_info|extended_profile|header")}</Text>
|
||||||
|
</div> */}
|
||||||
|
{title && (
|
||||||
|
<div className="mx_UserInfo_profileField">
|
||||||
|
<div className="mx_UserInfo_fieldLabel">
|
||||||
|
<Text size="sm" weight="semibold">
|
||||||
|
{_t("user_info|extended_profile|title")}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserInfo_fieldValue">
|
||||||
|
<Text size="sm">{title}</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{specialization && (
|
||||||
|
<div className="mx_UserInfo_profileField">
|
||||||
|
<div className="mx_UserInfo_fieldLabel">
|
||||||
|
<Text size="sm" weight="semibold">
|
||||||
|
{_t("user_info|extended_profile|specialization")}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserInfo_fieldValue">
|
||||||
|
<Text size="sm">{specialization}</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{address && (
|
||||||
|
<div className="mx_UserInfo_profileField">
|
||||||
|
<div className="mx_UserInfo_fieldLabel">
|
||||||
|
<Text size="sm" weight="semibold">
|
||||||
|
{_t("user_info|extended_profile|address")}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserInfo_fieldValue">
|
||||||
|
<Text size="sm">{renderMultilineText(address)}</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{website && (
|
||||||
|
<div className="mx_UserInfo_profileField">
|
||||||
|
<div className="mx_UserInfo_fieldLabel">
|
||||||
|
<Text size="sm" weight="semibold">
|
||||||
|
{_t("user_info|extended_profile|website")}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserInfo_fieldValue">
|
||||||
|
<Text size="sm">
|
||||||
|
<a href={website.startsWith('http') ? website : `https://${website}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener">
|
||||||
|
{website}
|
||||||
|
</a>
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{furtherInfo && (
|
||||||
|
<div className="mx_UserInfo_profileField">
|
||||||
|
<div className="mx_UserInfo_fieldLabel">
|
||||||
|
<Text size="sm" weight="semibold">
|
||||||
|
{_t("user_info|extended_profile|further_info")}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<div className="mx_UserInfo_fieldValue mx_UserInfo_fieldValue_multiline">
|
||||||
|
<Text size="sm">{renderMultilineText(furtherInfo)}</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserExtendedProfileInfo;
|
||||||
@@ -65,6 +65,7 @@ import { SdkContextClass } from "../../../contexts/SDKContext";
|
|||||||
import { UserInfoAdminToolsContainer } from "./user_info/UserInfoAdminToolsContainer";
|
import { UserInfoAdminToolsContainer } from "./user_info/UserInfoAdminToolsContainer";
|
||||||
import { PowerLevelSection } from "./user_info/UserInfoPowerLevels";
|
import { PowerLevelSection } from "./user_info/UserInfoPowerLevels";
|
||||||
import { UserInfoHeaderView } from "./user_info/UserInfoHeaderView";
|
import { UserInfoHeaderView } from "./user_info/UserInfoHeaderView";
|
||||||
|
import UserExtendedProfileInfo from "./UserExtendedProfileInfo";
|
||||||
|
|
||||||
export interface IDevice extends Device {
|
export interface IDevice extends Device {
|
||||||
ambiguous?: boolean;
|
ambiguous?: boolean;
|
||||||
@@ -731,6 +732,7 @@ const UserInfo: React.FC<IProps> = ({ user, room, onClose, phase = RightPanelPha
|
|||||||
devices={devices}
|
devices={devices}
|
||||||
roomId={room?.roomId}
|
roomId={room?.roomId}
|
||||||
/>
|
/>
|
||||||
|
{phase === RightPanelPhases.MemberInfo && <UserExtendedProfileInfo userId={member.userId} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -3829,6 +3829,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
|
"extended_profile": {
|
||||||
|
"header": "Weitere Informationen",
|
||||||
|
"title": "Titel",
|
||||||
|
"specialization": "Fachgebiet",
|
||||||
|
"address": "Adresse",
|
||||||
|
"website": "Webseite",
|
||||||
|
"further_info": "Weitere Infos / Qualifikationen",
|
||||||
|
"not_provided": "Nicht angegeben",
|
||||||
|
"error_loading_profile": "Fehler beim Laden des Profils"
|
||||||
|
},
|
||||||
"admin_tools_section": "Administrationswerkzeuge",
|
"admin_tools_section": "Administrationswerkzeuge",
|
||||||
"ban_button_room": "Bannen",
|
"ban_button_room": "Bannen",
|
||||||
"ban_button_space": "Bannen",
|
"ban_button_space": "Bannen",
|
||||||
|
|||||||
@@ -3865,6 +3865,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
|
"extended_profile": {
|
||||||
|
"header": "Additional infos",
|
||||||
|
"title": "Title",
|
||||||
|
"specialization": "Speciality",
|
||||||
|
"address": "Address",
|
||||||
|
"website": "Website",
|
||||||
|
"further_info": "Further infos / qualifications",
|
||||||
|
"not_provided": "Not provided",
|
||||||
|
"error_loading_profile": "Error loading profile"
|
||||||
|
},
|
||||||
"admin_tools_section": "Admin Tools",
|
"admin_tools_section": "Admin Tools",
|
||||||
"ban_button_room": "Ban from room",
|
"ban_button_room": "Ban from room",
|
||||||
"ban_button_space": "Ban from space",
|
"ban_button_space": "Ban from space",
|
||||||
|
|||||||
@@ -3864,6 +3864,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
|
"extended_profile": {
|
||||||
|
"header": "Informations supplémentaires",
|
||||||
|
"title": "Titre",
|
||||||
|
"specialization": "Spécialisation",
|
||||||
|
"address": "Adresse",
|
||||||
|
"website": "Site web",
|
||||||
|
"further_info": "Informations supplémentaires / qualifications",
|
||||||
|
"not_provided": "Non fourni",
|
||||||
|
"error_loading_profile": "Erreur lors du chargement du profil"
|
||||||
|
},
|
||||||
"admin_tools_section": "Outils d’administration",
|
"admin_tools_section": "Outils d’administration",
|
||||||
"ban_button_room": "Bannir du salon",
|
"ban_button_room": "Bannir du salon",
|
||||||
"ban_button_space": "Bannir de l'espace",
|
"ban_button_space": "Bannir de l'espace",
|
||||||
|
|||||||
@@ -2186,6 +2186,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
|
"extended_profile": {
|
||||||
|
"header": "Informazioni aggiuntive",
|
||||||
|
"title": "Titolo",
|
||||||
|
"specialization": "Specializzazione",
|
||||||
|
"address": "Indirizzo",
|
||||||
|
"website": "Sito web",
|
||||||
|
"further_info": "Ulteriori informazioni / qualifiche",
|
||||||
|
"not_provided": "Non fornito",
|
||||||
|
"error_loading_profile": "Errore nel caricamento del profilo"
|
||||||
|
},
|
||||||
"admin_tools_section": "Administratoriaus įrankiai",
|
"admin_tools_section": "Administratoriaus įrankiai",
|
||||||
"ban_button_room": "Užblokuoti iš kambario",
|
"ban_button_room": "Užblokuoti iš kambario",
|
||||||
"ban_button_space": "Užblokuoti iš erdvės",
|
"ban_button_space": "Užblokuoti iš erdvės",
|
||||||
|
|||||||
Reference in New Issue
Block a user