From 4e32e51f4c253e551242b0a31428d9cd23f9f26a Mon Sep 17 00:00:00 2001 From: Samuel Pull Date: Thu, 17 Oct 2024 13:46:43 +0200 Subject: [PATCH] wip --- frontend/src/pages/Common/Identifier.js | 46 +++++++------ frontend/src/pages/Common/LinkItems.js | 33 ++++++++++ frontend/src/pages/Common/TextWithLinks.js | 65 +++++++++++++++++++ .../src/pages/SubProjects/ProjectDetails.js | 7 +- .../pages/SubProjects/SubProjectContainer.js | 10 +++ 5 files changed, 135 insertions(+), 26 deletions(-) create mode 100644 frontend/src/pages/Common/LinkItems.js create mode 100644 frontend/src/pages/Common/TextWithLinks.js diff --git a/frontend/src/pages/Common/Identifier.js b/frontend/src/pages/Common/Identifier.js index ea4913a20..120b36c69 100644 --- a/frontend/src/pages/Common/Identifier.js +++ b/frontend/src/pages/Common/Identifier.js @@ -1,32 +1,30 @@ -import React, { Component } from "react"; +import React from "react"; import TextInput from "./TextInput"; import "./index.scss"; -class Identifier extends Component { - render() { - return ( -
- +const Identifier = (props) => { + return ( +
+ - -
- ); - } -} + +
+ ); +}; export default Identifier; diff --git a/frontend/src/pages/Common/LinkItems.js b/frontend/src/pages/Common/LinkItems.js new file mode 100644 index 000000000..ed9ade6a5 --- /dev/null +++ b/frontend/src/pages/Common/LinkItems.js @@ -0,0 +1,33 @@ +import React from "react"; +import { Link } from "react-router-dom"; + +import DatasetLinkedIcon from "@mui/icons-material/DatasetLinked"; +import { Avatar, ListItem, ListItemAvatar, Typography } from "@mui/material"; + +const displayLinks = (links) => { + return links.map((link, idx) => ( + + + {link} + + + )); +}; + +const LinkItems = ({ projectComment }) => { + const regex = /(?<=\s)\/projects[^\s,\.]*/g; + const matches = projectComment.match(regex); + + return matches && matches.length > 0 ? ( + + + + + + + <>{displayLinks(matches)} + + ) : null; +}; + +export default LinkItems; diff --git a/frontend/src/pages/Common/TextWithLinks.js b/frontend/src/pages/Common/TextWithLinks.js new file mode 100644 index 000000000..de7c24d54 --- /dev/null +++ b/frontend/src/pages/Common/TextWithLinks.js @@ -0,0 +1,65 @@ +import React, { useState } from "react"; + +import { Link, TextField, Typography } from "@mui/material"; + +const TextWithLinks = ({ + label, + helperText, + value, + onChange, + onBlur, + onFocus, + pattern, + multiline = false, + disabled = false, + id, + // eslint-disable-next-line no-useless-computed-key + ["data-test"]: dataTest +}) => { + const [inputValue, setInputValue] = useState(""); + const [url, setUrl] = useState(""); + + const handleInputChange = (event) => { + const value = event.target.value; + setInputValue(value); + + // Simple URL detection, could use a more robust regex for URL validation + const urlPattern = /https?:\/\/[^\s$.?#].[^\s]*/gi; + const detectedUrl = value.match(urlPattern); + setUrl(detectedUrl ? detectedUrl[0] : ""); + }; + + return ( + <> + {/* TextField for input */} + onChange(event.target.value)} + onBlur={onBlur} + pattern={pattern} + data-test={dataTest} + onChange={handleInputChange} + /> + + {/* Conditionally render the clickable URL below the TextField */} + {url && ( + + Clickable URL:{" "} + + {url} + + + )} + + ); +}; + +export default TextWithLinks; diff --git a/frontend/src/pages/SubProjects/ProjectDetails.js b/frontend/src/pages/SubProjects/ProjectDetails.js index 73081929e..79d551a91 100644 --- a/frontend/src/pages/SubProjects/ProjectDetails.js +++ b/frontend/src/pages/SubProjects/ProjectDetails.js @@ -35,15 +35,16 @@ import { import strings from "../../localizeStrings"; import ProjectAnalyticsDialog from "../Analytics/ProjectAnalyticsDialog"; import BudgetEmptyState from "../Common/BudgetEmptyState"; +import LinkItems from "../Common/LinkItems.js"; import ProjectAssigneeContainer from "./ProjectAssigneeContainer"; import "./ProjectDetails.scss"; const displayTags = (tags) => { - return tags.map((tag, i) => ( + return tags.map((tag, idx) => ( { const hasOpenSubprojects = !_isEmpty(subProjects.find((subproject) => subproject.data.status === "open")); const closeDisabled = !canClose || hasOpenSubprojects || projectStatus === "closed"; const tags = displayTags(projectTags || []); + return (
@@ -107,6 +109,7 @@ const ProjectDetails = (props) => { ) : null} +
{strings.common.total_budget} diff --git a/frontend/src/pages/SubProjects/SubProjectContainer.js b/frontend/src/pages/SubProjects/SubProjectContainer.js index e814e37e8..2619b1507 100644 --- a/frontend/src/pages/SubProjects/SubProjectContainer.js +++ b/frontend/src/pages/SubProjects/SubProjectContainer.js @@ -71,6 +71,16 @@ class SubProjectContainer extends Component { } componentDidUpdate(prevProps) { + if (this.props.router.location.pathname !== prevProps.router.location.pathname) { + const newProjectId = this.props.router.location.pathname.split("/")[2]; + if (newProjectId !== this.projectId) { + this.setState({ isDataFetched: false }); + this.projectId = newProjectId; + this.props.setSelectedView(this.projectId, "project"); + this.props.fetchAllProjectDetails(this.projectId, true); + this.setState({ isDataFetched: true }); + } + } const searchTermChanges = this.props.searchTerm !== prevProps.searchTerm; const projectsChange = !_isEqual(this.props.subProjects, prevProps.subProjects);