From fc5782d59d15dee8d3007fe49fea2679ebfc536f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20K=C5=82osi=C5=84ski?= Date: Mon, 11 Nov 2024 18:35:43 +0100 Subject: [PATCH] feature: Add CardProfileTop Start adding basic profile structure including first card profile. Refs: CU-8696hrm29 Signed-off-by: Jimmy --- frontend/package-lock.json | 23 +++++++++++++ frontend/package.json | 2 ++ .../Cards/ProfileAboutMe/ProfileAboutMe.tsx | 11 +++++++ .../Components/Cards/ProfileAboutMe/index.tsx | 1 + .../ProfileMultimedia/ProfileMultimedia.tsx | 32 +++++++++++++++++++ .../Cards/ProfileMultimedia/index.tsx | 1 + .../Cards/ProfileTop/CardProfileTop.tsx | 32 +++++++++++++------ .../InnerHtmlHandler/InnerHtmlHandler.tsx | 22 +++++++++++++ frontend/src/Pages/Profile/Profile.tsx | 19 +++++++++-- .../Services/Constants/DummyMultimedia.tsx | 23 +++++++++++++ frontend/src/Services/Constants/DummyUser.tsx | 2 +- frontend/tsconfig.app.json | 3 +- 12 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 frontend/src/Components/Cards/ProfileAboutMe/ProfileAboutMe.tsx create mode 100644 frontend/src/Components/Cards/ProfileAboutMe/index.tsx create mode 100644 frontend/src/Components/Cards/ProfileMultimedia/ProfileMultimedia.tsx create mode 100644 frontend/src/Components/Cards/ProfileMultimedia/index.tsx create mode 100644 frontend/src/Components/InnerHtmlHandler/InnerHtmlHandler.tsx create mode 100644 frontend/src/Services/Constants/DummyMultimedia.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5bf86ad..808762e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,11 +28,13 @@ "@jest/globals": "^29.7.0", "@testing-library/jest-dom": "^6.6.2", "@testing-library/react": "^16.0.1", + "@types/dompurify": "^3.0.5", "@types/jest": "^29.5.13", "@types/react": "^18.3.10", "@types/react-dom": "^18.3.0", "@types/react-i18next": "^8.1.0", "@vitejs/plugin-react": "^4.3.2", + "dompurify": "^3.1.7", "eslint": "^9.11.1", "eslint-plugin-react-hooks": "^5.1.0-rc.0", "eslint-plugin-react-refresh": "^0.4.12", @@ -2677,6 +2679,15 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dev": true, + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -2799,6 +2810,12 @@ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -3970,6 +3987,12 @@ "node": ">=12" } }, + "node_modules/dompurify": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz", + "integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==", + "dev": true + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", diff --git a/frontend/package.json b/frontend/package.json index 1f9ca4d..66e67e4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,11 +31,13 @@ "@jest/globals": "^29.7.0", "@testing-library/jest-dom": "^6.6.2", "@testing-library/react": "^16.0.1", + "@types/dompurify": "^3.0.5", "@types/jest": "^29.5.13", "@types/react": "^18.3.10", "@types/react-dom": "^18.3.0", "@types/react-i18next": "^8.1.0", "@vitejs/plugin-react": "^4.3.2", + "dompurify": "^3.1.7", "eslint": "^9.11.1", "eslint-plugin-react-hooks": "^5.1.0-rc.0", "eslint-plugin-react-refresh": "^0.4.12", diff --git a/frontend/src/Components/Cards/ProfileAboutMe/ProfileAboutMe.tsx b/frontend/src/Components/Cards/ProfileAboutMe/ProfileAboutMe.tsx new file mode 100644 index 0000000..be8819d --- /dev/null +++ b/frontend/src/Components/Cards/ProfileAboutMe/ProfileAboutMe.tsx @@ -0,0 +1,11 @@ +import {Card, Text} from "@mantine/core"; +import {InnerHtmlHandler} from "../../InnerHtmlHandler/InnerHtmlHandler.tsx"; + +export const ProfileAboutMe = (props: { htmlContent: string }) => { + return ( + + O mnie + + + ); +} \ No newline at end of file diff --git a/frontend/src/Components/Cards/ProfileAboutMe/index.tsx b/frontend/src/Components/Cards/ProfileAboutMe/index.tsx new file mode 100644 index 0000000..e6b7b84 --- /dev/null +++ b/frontend/src/Components/Cards/ProfileAboutMe/index.tsx @@ -0,0 +1 @@ +export * from './ProfileAboutMe' \ No newline at end of file diff --git a/frontend/src/Components/Cards/ProfileMultimedia/ProfileMultimedia.tsx b/frontend/src/Components/Cards/ProfileMultimedia/ProfileMultimedia.tsx new file mode 100644 index 0000000..2ffb79c --- /dev/null +++ b/frontend/src/Components/Cards/ProfileMultimedia/ProfileMultimedia.tsx @@ -0,0 +1,32 @@ +import {Card, Grid, Group, Image, Text, UnstyledButton} from "@mantine/core"; +import {Multimedia} from "../../../Services/Constants/DummyMultimedia.tsx"; + +export const ProfileMultimedia = (props: { multimedia: Multimedia[] }) => { + + const handleButtonClick = (event) => { + event.preventDefault(); + + window.open(event.target.href, '_blank', 'noopener,noreferrer'); + } + + return ( + + + Multimedia + Wyƛwietl + wszystko + + + + {props.multimedia.map((media, index) => { + return ( + + {media.type} + + ); + })} + + + + ); +} \ No newline at end of file diff --git a/frontend/src/Components/Cards/ProfileMultimedia/index.tsx b/frontend/src/Components/Cards/ProfileMultimedia/index.tsx new file mode 100644 index 0000000..01dba4b --- /dev/null +++ b/frontend/src/Components/Cards/ProfileMultimedia/index.tsx @@ -0,0 +1 @@ +export * from './ProfileMultimedia' \ No newline at end of file diff --git a/frontend/src/Components/Cards/ProfileTop/CardProfileTop.tsx b/frontend/src/Components/Cards/ProfileTop/CardProfileTop.tsx index 00bab2d..36bc5d1 100644 --- a/frontend/src/Components/Cards/ProfileTop/CardProfileTop.tsx +++ b/frontend/src/Components/Cards/ProfileTop/CardProfileTop.tsx @@ -1,8 +1,12 @@ -import {Box, Card, Group, Image, Stack, Text} from "@mantine/core"; +import {Box, Card, Group, Image, Skeleton, Stack, Text} from "@mantine/core"; import {DummyUserType} from "../../../Services/Constants/DummyUser.tsx"; +import {useState} from "react"; export const CardProfileTop = (props: { userDetails: DummyUserType }) => { + + const [isProfilePictureLoaded, setIsProfilePictureLoaded] = useState(false); + return ( @@ -15,14 +19,24 @@ export const CardProfileTop = (props: { userDetails: DummyUserType }) => { - + + {!isProfilePictureLoaded && ( + + )} + setIsProfilePictureLoaded(true)} + radius={180} + bd={"2px solid"} + display={isProfilePictureLoaded ? "block" : "none"} + /> + {props.userDetails.name} {props.userDetails.surname} diff --git a/frontend/src/Components/InnerHtmlHandler/InnerHtmlHandler.tsx b/frontend/src/Components/InnerHtmlHandler/InnerHtmlHandler.tsx new file mode 100644 index 0000000..c0de892 --- /dev/null +++ b/frontend/src/Components/InnerHtmlHandler/InnerHtmlHandler.tsx @@ -0,0 +1,22 @@ +import {TypographyStylesProvider} from "@mantine/core"; +import DOMPurify from 'dompurify'; + +export const InnerHtmlHandler = (props: { innerHtml: string }) => { + + const allowedArgs = { + //TODO: Define allowed tags and attributes + ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li', 'br'], + ALLOWED_ATTR: ['href', 'title'], + } + + // Validate innerHtml to prevent XSS + const sanitizedHtml = DOMPurify.sanitize(props.innerHtml); + + return ( + +
+ + ); +} \ No newline at end of file diff --git a/frontend/src/Pages/Profile/Profile.tsx b/frontend/src/Pages/Profile/Profile.tsx index 34bfaf1..e1dca5e 100644 --- a/frontend/src/Pages/Profile/Profile.tsx +++ b/frontend/src/Pages/Profile/Profile.tsx @@ -1,7 +1,10 @@ import {useParams} from "react-router-dom"; import {DummyUser} from "../../Services/Constants/DummyUser.tsx"; -import {CardProfileTop} from "../../Components/Cards/ProfileTop/CardProfileTop.tsx"; -import {Box} from "@mantine/core"; +import {CardProfileTop} from "../../Components/Cards/ProfileTop"; +import {Box, Group, Stack} from "@mantine/core"; +import {ProfileAboutMe} from "../../Components/Cards/ProfileAboutMe"; +import {ProfileMultimedia} from "../../Components/Cards/ProfileMultimedia"; +import {DummyMultimedia} from "../../Services/Constants/DummyMultimedia.tsx"; export const Profile = () => { @@ -10,7 +13,17 @@ export const Profile = () => { if (DummyUser && DummyUser.tag === userTag) { return ( - + + + + + + {/*Here should be post map*/} + + + + + ); } diff --git a/frontend/src/Services/Constants/DummyMultimedia.tsx b/frontend/src/Services/Constants/DummyMultimedia.tsx new file mode 100644 index 0000000..867b2b3 --- /dev/null +++ b/frontend/src/Services/Constants/DummyMultimedia.tsx @@ -0,0 +1,23 @@ +export type Multimedia = { + url: string, + type?: string, +} + +export const DummyMultimedia: Multimedia[] = [ + { + type: "image", + url: "https://www.w3schools.com/w3images/avatar2.png", + }, + { + type: "image", + url: "https://www.w3schools.com/w3images/avatar6.png", + }, + { + type: "image", + url: "https://www.w3schools.com/w3images/avatar5.png", + }, + { + type: "image", + url: "https://www.w3schools.com/w3images/avatar4.png", + } +] \ No newline at end of file diff --git a/frontend/src/Services/Constants/DummyUser.tsx b/frontend/src/Services/Constants/DummyUser.tsx index 02aaa6a..12db031 100644 --- a/frontend/src/Services/Constants/DummyUser.tsx +++ b/frontend/src/Services/Constants/DummyUser.tsx @@ -43,6 +43,6 @@ export const DummyUser: DummyUserType = { tag: '@johndoe', pronouns: "he/him", profilePicture: "https://static-cdn.jtvnw.net/jtv_user_pictures/a7423251-6e4f-42f2-a33f-ca083eafae69-profile_image-300x300.png", - profileDetails: "

Test details

", + profileDetails: "

Test details

Hello world!

", friends: DummyUserFriends } \ No newline at end of file diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index f0a2350..fd906cc 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -18,7 +18,8 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true }, "include": ["src"] }