fixed some bugs and changed inputs on converters

main
alex 2024-09-15 13:19:03 +02:00
parent c40cba88d4
commit 73d5804f8c
20 changed files with 168 additions and 90 deletions

View File

@ -154,10 +154,17 @@
}, },
"lessonPage": { "lessonPage": {
"finishLessonButton": "Lektion beenden", "finishLessonButton": "Lektion beenden",
"questions": "Fragen" "questions": "Fragen",
"addContentButton": "Inhalt hinzufügen"
},
"lessonPageEditor": {
"previewParagraph": "Vorschau der Lektion",
"pageContentParagraph": "Seiteninhalt der Lektion",
"titlePlaceholder": "Titel hier eingeben..."
}, },
"lessonQuestions": { "lessonQuestions": {
"messageQuestionSuccessfullyCreated": "Frage erfolgreich erstellt", "messageQuestionSuccessfullyCreated": "Frage erfolgreich erstellt",
"messageReplySuccessfullyCreated": "Antwort erfolgreich erstellt",
"askAQuestion": "Frage stellen", "askAQuestion": "Frage stellen",
"typeSomething": "Etwas schreiben...", "typeSomething": "Etwas schreiben...",
"submitButton": "Absenden", "submitButton": "Absenden",
@ -184,6 +191,7 @@
} }
}, },
"lessonComponentsConverter": { "lessonComponentsConverter": {
"titlePlaceholder": "Titel hier eingeben...",
"textPlaceholder": "Text hier eingeben...", "textPlaceholder": "Text hier eingeben...",
"noImageProvided": "Kein Bild bereitgestellt", "noImageProvided": "Kein Bild bereitgestellt",
"gallery": "Galerie", "gallery": "Galerie",

View File

@ -154,10 +154,17 @@
}, },
"lessonPage": { "lessonPage": {
"finishLessonButton": "Finish lesson", "finishLessonButton": "Finish lesson",
"questions": "Questions" "questions": "Questions",
"addContentButton": "Add Content"
},
"lessonPageEditor": {
"previewParagraph": "Preview of the lesson",
"pageContentParagraph": "Page content of the lesson",
"titlePlaceholder": "Input title here..."
}, },
"lessonQuestions": { "lessonQuestions": {
"messageQuestionSuccessfullyCreated": "Question successfully created", "messageQuestionSuccessfullyCreated": "Question successfully created",
"messageReplySuccessfullyCreated": "Reply successfully created",
"askAQuestion": "Ask a question", "askAQuestion": "Ask a question",
"typeSomething": "Type something...", "typeSomething": "Type something...",
"submitButton": "Submit", "submitButton": "Submit",
@ -184,6 +191,7 @@
} }
}, },
"lessonComponentsConverter": { "lessonComponentsConverter": {
"titlePlaceholder": "Input title here...",
"textPlaceholder": "Input text here...", "textPlaceholder": "Input text here...",
"noImageProvided": "No image provided", "noImageProvided": "No image provided",
"gallery": "Gallery", "gallery": "Gallery",

View File

@ -1,16 +1,16 @@
import { Route, Routes } from "react-router-dom"; import { Route, Routes } from "react-router-dom";
import { MySupsenseFallback } from "../../../shared/components/MySupsenseFallback"; import { MySupsenseFallback } from "shared/components/MySupsenseFallback";
import { Constants } from "../../utils/utils"; import { Constants } from "../../utils/utils";
import Team from "../../../features/Team"; import Team from "features/Team";
import Roles from "../../../features/Roles"; import Roles from "features/Roles";
import WhatsNew from "../../../features/WhatsNew"; import WhatsNew from "features/WhatsNew";
import SuggestFeature from "../../../features/SuggestFeature"; import SuggestFeature from "features/SuggestFeature";
import ContactSupport from "../../../features/ContactSupport"; import ContactSupport from "features/ContactSupport";
import Lessons from "../../../features/Lessons"; import Lessons from "features/Lessons";
import Settings from "../../../features/Settings"; import Settings from "features/Settings";
import PageNotFound from "../../../features/PageNotFound"; import PageNotFound from "features/PageNotFound";
import LessonPage from "../../../features/Lessons/LessonPage"; import LessonPage from "features/Lessons/LessonPage";
import LessonPageEditor from "../../../features/Lessons/LessonPageEditor"; import LessonPageEditor from "features/Lessons/LessonPageEditor";
import TeamCreateUser from "features/Team/CreateUser"; import TeamCreateUser from "features/Team/CreateUser";
import Board from "features/Board"; import Board from "features/Board";
import UserProfile from "features/UserProfile"; import UserProfile from "features/UserProfile";

View File

@ -6,7 +6,7 @@ import { SideMenuContent, SideMenuEditorContent } from "../SideMenu";
import { useEffect } from "react"; import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { setIsSideMenuCollapsed } from "../SideMenu/sideMenuSlice"; import { setIsSideMenuCollapsed } from "../SideMenu/sideMenuSlice";
import { editorActive } from "../../../features/Lessons/LessonPageEditor/lessonPageEditorSlice"; import { editorActive } from "features/Lessons/LessonPageEditor/lessonPageEditorSlice";
import MyDndContext from "./MyDndContext"; import MyDndContext from "./MyDndContext";
import AiChat from "features/AiChat"; import AiChat from "features/AiChat";

View File

@ -55,7 +55,7 @@ export default function HeaderBar(props: HeaderBarProps = { theme: "light" }) {
zIndex: 999, zIndex: 999,
}} }}
> >
<Flex align="center" gap={16}> <Flex align="center">
<div <div
className={isDarkMode ? styles.containerDark : styles.containerLight} className={isDarkMode ? styles.containerDark : styles.containerLight}
style={{ borderRadius: 28, padding: 4 }} style={{ borderRadius: 28, padding: 4 }}

View File

@ -3,9 +3,16 @@ import AppRoutes from "../AppRoutes";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { isSideMenuCollapsed } from "../SideMenu/sideMenuSlice"; import { isSideMenuCollapsed } from "../SideMenu/sideMenuSlice";
import { BreakpointLgWidth } from "../../utils/utils"; import { BreakpointLgWidth } from "../../utils/utils";
import { useLocation } from "react-router-dom";
import { useEffect } from "react";
export default function PageContent() { export default function PageContent() {
const isCollpased = useSelector(isSideMenuCollapsed); const isCollpased = useSelector(isSideMenuCollapsed);
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return ( return (
<Layout <Layout

View File

@ -218,7 +218,7 @@ export function SideMenuContent() {
}} }}
></div> ></div>
<div> <div style={{ overflowY: "auto" }}>
<Flex justify="center" style={{ paddingBottom: 24, width: "100%" }}> <Flex justify="center" style={{ paddingBottom: 24, width: "100%" }}>
{appLogoUrl !== null && ( {appLogoUrl !== null && (
<img <img

View File

@ -1,14 +1,10 @@
import { Button, Card, ConfigProvider, Flex, Form, Input } from "antd"; import { Button, Card, ConfigProvider, Flex, Form, Input } from "antd";
import { useForm } from "antd/es/form/Form"; import { useForm } from "antd/es/form/Form";
import styles from "./styles.module.css"; import styles from "./styles.module.css";
import { import { Constants, EncodeStringToBase64, myFetch } from "core/utils/utils";
Constants,
EncodeStringToBase64,
myFetch,
} from "../../../core/utils/utils";
import { useState } from "react"; import { useState } from "react";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { setUserAuthenticated } from "../../../core/reducers/appSlice"; import { setUserAuthenticated } from "core/reducers/appSlice";
type FieldType = { type FieldType = {
email: string; email: string;

View File

@ -1,5 +1,5 @@
import { Button, Flex } from "antd"; import { Button, Flex } from "antd";
import { CheckOutlined } from "@ant-design/icons"; import { CheckOutlined, PlusOutlined } from "@ant-design/icons";
import HeaderBar from "core/components/Header"; import HeaderBar from "core/components/Header";
import { useLocation, useNavigate, useParams } from "react-router-dom"; import { useLocation, useNavigate, useParams } from "react-router-dom";
import { Constants } from "core/utils/utils"; import { Constants } from "core/utils/utils";
@ -24,8 +24,10 @@ import MyCenteredSpin from "shared/components/MyCenteredSpin";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const LessonContents: React.FC = () => { const LessonContents: React.FC = () => {
const { t } = useTranslation();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { lessonId } = useParams(); const { lessonId } = useParams();
const navigate = useNavigate();
const lessonContents = useSelector(lessonPageContents); const lessonContents = useSelector(lessonPageContents);
@ -52,7 +54,22 @@ const LessonContents: React.FC = () => {
if (isLoading) return <MyCenteredSpin height="140px" />; if (isLoading) return <MyCenteredSpin height="140px" />;
if (error) return <MyErrorResult />; if (error) return <MyErrorResult />;
if (!lessonContents || lessonContents.length === 0) return <MyEmpty />; if (!lessonContents || lessonContents.length === 0)
return (
<MyEmpty style={{ paddingBottom: 64 }}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={() =>
navigate(
`${Constants.ROUTE_PATHS.LESSIONS.ROOT}/${lessonId}/editor`
)
}
>
{t("lessonPage.addContentButton")}
</Button>
</MyEmpty>
);
return ( return (
<> <>

View File

@ -79,8 +79,6 @@ const SortableEditorItem = (props: { item: LessonContent }) => {
mode="edititable" mode="edititable"
lessonContent={lnContent} lessonContent={lnContent}
onEdit={(data) => { onEdit={(data) => {
console.log("edit", lnContent.Id, data);
dispatch( dispatch(
updateLessonContent({ updateLessonContent({
id: lnContent.Id, id: lnContent.Id,

View File

@ -9,9 +9,9 @@ import {
setLessonThumbnailUrl, setLessonThumbnailUrl,
setPageEditorLessonState, setPageEditorLessonState,
} from "./lessonPageEditorSlice"; } from "./lessonPageEditorSlice";
import { useEffect } from "react"; import { useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { Card, Flex } from "antd"; import { Card, Flex, Typography } from "antd";
import { Constants } from "core/utils/utils"; import { Constants } from "core/utils/utils";
import HeaderBar from "core/components/Header"; import HeaderBar from "core/components/Header";
import Droppable from "./Droppable"; import Droppable from "./Droppable";
@ -28,10 +28,12 @@ import {
addWebSocketReconnectListener, addWebSocketReconnectListener,
removeWebSocketReconnectListener, removeWebSocketReconnectListener,
} from "core/services/websocketService"; } from "core/services/websocketService";
import { useTranslation } from "react-i18next";
const PreviewCard: React.FC = () => { const PreviewCard: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { lessonId } = useParams(); const { lessonId } = useParams();
const debounceRef = useRef<null | NodeJS.Timeout>(null);
const dataLessonThumbnail = useSelector(lessonThumbnail); const dataLessonThumbnail = useSelector(lessonThumbnail);
@ -69,17 +71,27 @@ const PreviewCard: React.FC = () => {
Title: dataLessonThumbnail.title || "", Title: dataLessonThumbnail.title || "",
ThumbnailUrl: dataLessonThumbnail.url || "", ThumbnailUrl: dataLessonThumbnail.url || "",
}} }}
onEditTitle={async (newTitle) => { onEditTitle={(newTitle) => {
try { try {
await updateLessonPreviewTitle({ if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
debounceRef.current = setTimeout(() => {
try {
updateLessonPreviewTitle({
lessonId: lessonId as string, lessonId: lessonId as string,
newTitle: newTitle, newTitle: newTitle,
}).unwrap(); });
dispatch(setLessonThumbnailTitle(newTitle)); dispatch(setLessonThumbnailTitle(newTitle));
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
}, 3000);
} catch (err) {
console.error(err);
}
}} }}
/> />
); );
@ -126,6 +138,7 @@ const LessonContentComponents: React.FC = () => {
}; };
export default function LessonPageEditor() { export default function LessonPageEditor() {
const { t } = useTranslation();
const { lessonId } = useParams(); const { lessonId } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
@ -159,15 +172,19 @@ export default function LessonPageEditor() {
sticky sticky
/> />
<Flex justify="center" style={{ paddingTop: 24 }}> <Flex justify="center" style={{ padding: 12 }}>
<Flex <Flex justify="center" vertical className={styles.cardContainer}>
justify="center" <Typography.Paragraph type="secondary" style={{ margin: 0 }}>
vertical {t("lessonPageEditor.previewParagraph")}
gap={16} </Typography.Paragraph>
className={styles.cardContainer}
>
<PreviewCard /> <PreviewCard />
<Typography.Paragraph
type="secondary"
style={{ margin: 0, paddingTop: 16 }}
>
{t("lessonPageEditor.pageContentParagraph")}
</Typography.Paragraph>
<LessonContentComponents /> <LessonContentComponents />
</Flex> </Flex>
</Flex> </Flex>

View File

@ -12,7 +12,7 @@ export const lessonPageEditorSlice = createSlice({
editorActive: false, editorActive: false,
currentLessonId: "", // required in sideMenu because has no access to useParams currentLessonId: "", // required in sideMenu because has no access to useParams
lessonThumbnail: { lessonThumbnail: {
title: "Test", title: "",
url: "", url: "",
}, },
lessonContents: [] as LessonContent[], lessonContents: [] as LessonContent[],

View File

@ -291,18 +291,19 @@ export function QuestionUIRaw({
return ( return (
<> <>
<Flex gap={16}> <Flex gap={16}>
<div style={{ marginTop: 4 }}>
<MyUserAvatar <MyUserAvatar
profilePictureUrl={user.ProfilePictureUrl} profilePictureUrl={user.ProfilePictureUrl}
firstName={user.FirstName} firstName={user.FirstName}
disableCursorPointer disableCursorPointer
size={42}
/> />
</div>
<Flex vertical style={{ width: "100%" }}> <Flex vertical style={{ width: "100%" }}>
<Typography style={{ fontSize: 24, fontWeight: 800 }}> <Typography.Title style={{ fontSize: 20, fontWeight: 800 }}>
{user.FirstName} {user.LastName} {user.FirstName} {user.LastName}
</Typography> </Typography.Title>
<Typography style={{ fontSize: 18, fontWeight: 500 }}> <Typography.Text>{message}</Typography.Text>
{message}
</Typography>
<Flex gap={0} align="center"> <Flex gap={0} align="center">
<Button <Button
type="text" type="text"

View File

@ -55,31 +55,37 @@ export function Converter({
} }
return ( return (
<Typography.Title <Input
editable={{ variant="borderless"
triggerType: "text" as any, style={{
onChange: (event) => { fontSize: 24,
onEdit?.(event); fontWeight: "bold",
wordBreak: "break-all",
padding: 0,
margin: 0,
}}
placeholder={t("lessonComponentsConverter.titlePlaceholder")}
value={lessonContent.Data}
onChange={(event) => {
onEdit?.(event.target.value);
if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
debounceRef.current = setTimeout(() => {
try { try {
reqUpdateLessonContent({ reqUpdateLessonContent({
lessonId: lessonId, lessonId: lessonId,
contentId: lessonContent.Id, contentId: lessonContent.Id,
data: event, data: event.target.value,
}); });
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
}, }, 1000);
}} }}
level={1} />
style={{
margin: 0,
width: "100%",
}}
>
{lessonContent.Data}
</Typography.Title>
); );
case getTypeByName("Text"): case getTypeByName("Text"):
if (mode === "view") { if (mode === "view") {
@ -321,6 +327,7 @@ export function Converter({
} }
}} }}
accept={Constants.ACCEPTED_VIDEO_FILE_TYPES} accept={Constants.ACCEPTED_VIDEO_FILE_TYPES}
fileType="video"
> >
<Button type="link">{t("lessonComponentsConverter.video")}</Button> <Button type="link">{t("lessonComponentsConverter.video")}</Button>
</MyUpload> </MyUpload>

View File

@ -1,7 +1,7 @@
import { Avatar, Checkbox, Collapse, Form, Tooltip } from "antd"; import { Avatar, Checkbox, Collapse, Form, Tooltip } from "antd";
import HeaderBar from "../../core/components/Header"; import HeaderBar from "core/components/Header";
import MyBanner from "../../shared/components/MyBanner"; import MyBanner from "shared/components/MyBanner";
import { MyContainer } from "../../shared/components/MyContainer"; import { MyContainer } from "shared/components/MyContainer";
import MyErrorResult from "shared/components/MyResult"; import MyErrorResult from "shared/components/MyResult";
import MyEmpty from "shared/components/MyEmpty"; import MyEmpty from "shared/components/MyEmpty";
import MyCenteredSpin from "shared/components/MyCenteredSpin"; import MyCenteredSpin from "shared/components/MyCenteredSpin";
@ -70,6 +70,7 @@ interface Permission {
// test data // test data
/*
const tmpI18nObj = [ const tmpI18nObj = [
{ {
id: 1, id: 1,
@ -98,7 +99,7 @@ const tmpI18nObj = [
description: description:
"Permission to invite a member. An email will be sent to the new member.", "Permission to invite a member. An email will be sent to the new member.",
}, },
]; ]; */
/* /*
export const tmpRoleNames = { export const tmpRoleNames = {
@ -174,7 +175,11 @@ function RoleComponent({ role }: { role: Role }) {
/> />
), ),
extra: ( extra: (
<> <div
onClick={(event) => {
event.stopPropagation();
}}
>
{role.Users.length > 0 && ( {role.Users.length > 0 && (
<Avatar.Group <Avatar.Group
size="small" size="small"
@ -192,14 +197,14 @@ function RoleComponent({ role }: { role: Role }) {
<MyUserAvatar <MyUserAvatar
profilePictureUrl={user.ProfilePictureUrl} profilePictureUrl={user.ProfilePictureUrl}
firstName={user.FirstName} firstName={user.FirstName}
size={32} size={24}
/> />
</div> </div>
</Tooltip> </Tooltip>
))} ))}
</Avatar.Group> </Avatar.Group>
)} )}
</> </div>
), ),
}, },
]} ]}

View File

@ -1,6 +1,6 @@
import { Button, Flex, Form, Input, Modal } from "antd"; import { Button, Flex, Form, Input, Modal } from "antd";
import HeaderBar from "../../core/components/Header"; import HeaderBar from "core/components/Header";
import MyBanner from "../../shared/components/MyBanner"; import MyBanner from "shared/components/MyBanner";
import { SaveOutlined } from "@ant-design/icons"; import { SaveOutlined } from "@ant-design/icons";
import MyUpload from "shared/components/MyUpload"; import MyUpload from "shared/components/MyUpload";
import { Constants } from "core/utils/utils"; import { Constants } from "core/utils/utils";

View File

@ -7,8 +7,8 @@ import {
Select, Select,
Typography, Typography,
} from "antd"; } from "antd";
import HeaderBar from "../../core/components/Header"; import HeaderBar from "core/components/Header";
import MyBanner from "../../shared/components/MyBanner"; import MyBanner from "shared/components/MyBanner";
import MyMiddleCard from "shared/components/MyMiddleCard"; import MyMiddleCard from "shared/components/MyMiddleCard";
import Meta from "antd/es/card/Meta"; import Meta from "antd/es/card/Meta";
import { useGetUserProfileQuery } from "core/services/userProfile"; import { useGetUserProfileQuery } from "core/services/userProfile";

View File

@ -1,5 +1,5 @@
import { CommentOutlined } from "@ant-design/icons"; import { CommentOutlined } from "@ant-design/icons";
import { Card, Flex, Typography } from "antd"; import { Card, Flex, Input } from "antd";
import { Constants, getImageUrl } from "core/utils/utils"; import { Constants, getImageUrl } from "core/utils/utils";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import MyUpload from "shared/components/MyUpload"; import MyUpload from "shared/components/MyUpload";
@ -81,6 +81,22 @@ export default function MyLessonPreviewCard({
{t("lessonPage.questions")} {t("lessonPage.questions")}
</div> </div>
) : ( ) : (
<Input
variant="outlined"
defaultValue={lessonSettings.Title}
onChange={(event) => onEditTitle?.(event.target.value)}
placeholder={t("lessonPageEditor.titlePlaceholder")}
style={{ width: "100%", fontSize: 24, fontWeight: "bold" }}
/>
)}
</Flex>
</div>
</Card>
</LinkWrapper>
);
}
/*
<Typography.Title <Typography.Title
level={2} level={2}
editable={{ editable={{
@ -90,14 +106,9 @@ export default function MyLessonPreviewCard({
}} }}
style={{ style={{
width: "100%", width: "100%",
margin: 0,
}} }}
> >
{lessonSettings.Title} {lessonSettings.Title}
</Typography.Title> </Typography.Title>
)} */
</Flex>
</div>
</Card>
</LinkWrapper>
);
}

View File

@ -8,6 +8,7 @@
.img { .img {
height: 240px; height: 240px;
object-fit: cover; object-fit: cover;
width: 100%;
} }
.title { .title {
@ -23,6 +24,8 @@
.img { .img {
height: 140px; height: 140px;
width: 200px;
min-width: 200px;
} }
.title { .title {

View File

@ -24,9 +24,9 @@ const MyUserAvatar: React.FC<MyUserAvatarProps> = ({
const isProfilePictureEmpty = profilePictureUrl === ""; const isProfilePictureEmpty = profilePictureUrl === "";
const avatarContent = const avatarContent =
isProfilePictureEmpty && firstName !== undefined isProfilePictureEmpty && firstName !== undefined ? (
? firstName.charAt(0) <span style={{ fontSize: 16 }}>{firstName.charAt(0)}</span>
: undefined; ) : undefined;
const iconContent = const iconContent =
isProfilePictureEmpty && firstName === undefined ? ( isProfilePictureEmpty && firstName === undefined ? (
<UserOutlined /> <UserOutlined />