Skip to content

Commit

Permalink
feat: retrieve project metadata from KG on project access (#2337) (#2414
Browse files Browse the repository at this point in the history
)
  • Loading branch information
cramakri committed Mar 20, 2023
1 parent 777b2a6 commit 0ac090f
Show file tree
Hide file tree
Showing 14 changed files with 403 additions and 38 deletions.
19 changes: 19 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"@types/react-autosuggest": "^10.1.6",
"@types/react-avatar-editor": "^13.0.0",
"@types/react-dom": "^18.0.10",
"@types/react-helmet": "^6.1.6",
"@types/react-pdf": "^6.2.0",
"@types/react-router-dom": "^5.1.2",
"@types/styled-components": "^5.1.25",
Expand Down
2 changes: 1 addition & 1 deletion client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { Route, Switch } from "react-router-dom";
import { ToastContainer } from "react-toastify";
import { useSelector } from "react-redux";

import Project from "./project/Project";
import { Project } from "./project";
import { ProjectList } from "./project/list";
import { NewProject } from "./project/new";
import DatasetList from "./dataset/list/DatasetList.container";
Expand Down
45 changes: 37 additions & 8 deletions client/src/components/entityHeader/EntityHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
import * as React from "react";

import { StartSessionButton } from "../../project/Project.present";
import Creators, { EntityCreator } from "../entities/Creators";
import EntityDescription from "../entities/Description";
import EntityTags from "../entities/Tags";
Expand All @@ -32,7 +33,7 @@ import LinkedEntitiesByItemType, { EntityLinksHeader } from "../entities/LinkedE
import Slug from "../entities/Slug";
import VisibilityIcon from "../entities/VisibilityIcon";
import { EntityType } from "../entities/Entities";
import { StartSessionButton } from "../../project/Project.present";
import { Loader } from "../Loader";
import { TimeCaption } from "../TimeCaption";

import "./EntityHeader.scss";
Expand All @@ -41,7 +42,7 @@ import { stylesByItemType } from "../../utils/helpers/HelperFunctions";
export interface EntityHeaderProps {
client?: any;
creators: EntityCreator[];
description: string;
description?: {value: string, isLoading?: boolean};
devAccess: boolean;
email?: string;
fullPath?: string;
Expand All @@ -62,6 +63,37 @@ export interface EntityHeaderProps {
imageUrl?: string;
}

type EntityHeaderEntityDescriptionProps =
Pick<EntityHeaderProps, "description" | "devAccess" | "url">;
function EntityHeaderEntityDescription(
{ description,
devAccess,
url }: EntityHeaderEntityDescriptionProps
) {
if (!description) {
return <EntityDescription
description="" isHeightFixed={false}
showSuggestion={false} hasDevAccess={devAccess}
urlChangeDescription={`${url}/settings`}
className="text-rk-dark"
/>;
}
if (description.isLoading === true) {
return <div className="card-text text-rk-dark d-flex"
style={{ margin: "12px 0 0 0" }}>
<div>
<Loader size={16} />
</div>
</div>;
}
return <EntityDescription
description={description.value} isHeightFixed={false}
showSuggestion={description.isLoading === false} hasDevAccess={devAccess}
urlChangeDescription={`${url}/settings`}
className="text-rk-dark"
/>;
}

function EntityHeader({
creators, description, devAccess, fullPath, gitUrl, hideEmptyTags = false, imageUrl, itemType,
labelCaption, links, otherButtons, showFullHeader = true, slug, statusButton, tagList, timeCaption,
Expand All @@ -70,7 +102,6 @@ function EntityHeader({
const mainButton = fullPath && gitUrl ?
(<StartSessionButton fullPath={fullPath} gitUrl={gitUrl} />) :
null;

const imageStyles = imageUrl ? { backgroundImage: `url("${imageUrl}")` } : {};
const colorByType = stylesByItemType(itemType);

Expand Down Expand Up @@ -110,11 +141,9 @@ function EntityHeader({
<div className="entity-metadata">
<Creators display="list" creators={creators} itemType={itemType} includeIcon={true} />
<Slug multiline={true} slug={slug ?? ""} />
<EntityDescription
description={description} isHeightFixed={false}
showSuggestion={true} hasDevAccess={devAccess}
urlChangeDescription={`${url}/settings`}
className="text-rk-dark"
<EntityHeaderEntityDescription
description={description} devAccess={devAccess}
url={url}
/>
</div>
</div>
Expand Down
1 change: 0 additions & 1 deletion client/src/dataset/Dataset.present.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,6 @@ export default function DatasetView(props) {
<div className="mb-4">
<EntityHeader
creators={dataset?.published?.creator}
description=""
devAccess={false}
hideEmptyTags={true}
imageUrl={imageUrl}
Expand Down
92 changes: 92 additions & 0 deletions client/src/features/project/ProjectDescription.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from "react";
import { Form, FormGroup, FormText, Input, Label } from "reactstrap";

import { useProjectMetadataQuery } from "../projects/ProjectKgApi";
import { InlineSubmitButton } from "../../components/buttons/Button";

// class Nothing {
// constructor(props) {
// super(props);
// this.state = ProjectDescription.getDerivedStateFromProps(props, {});
// this.onValueChange = this.handleChange.bind(this);
// this.onSubmit = this.handleSubmit.bind(this);
// }

// static getDerivedStateFromProps(nextProps, prevState) {
// const update = { value: nextProps.metadata.description, pristine: true };
// return { ...update, ...prevState };
// }

// handleChange(e) {
// if (e.target.values !== this.state.value)
// this.setState({ value: e.target.value, updated: false, pristine: false });
// }

// handleSubmit(e) {
// e.preventDefault();
// this.setState({ value: this.state.value, updating: true });
// this.props.onProjectDescriptionChange(this.state.value)
// .then(() => {
// this.setState({ value: this.state.value, updated: true, updating: false });
// });
// }
// }

type ProjectDescriptionProps = {
projectPath?: string;
settingsReadOnly: boolean;
};

function ProjectDescription({ projectPath, settingsReadOnly }: ProjectDescriptionProps) {
const { data: kgData, isLoading } = useProjectMetadataQuery({ projectPath }, { skip: !projectPath });
const [isUpdating, setUpdating] = React.useState(false);
const [isUpdated, setUpdated] = React.useState(false);
const [isPristine, setPristine] = React.useState(false);
const [value, setValue] = React.useState(kgData?.description ?? "");

const inputField =
settingsReadOnly || isUpdating ? (
<Input id="projectDescription" readOnly value={value} />
) : (
<Input
id="projectDescription"
onChange={(event) => setValue(event.currentTarget.value)}
data-cy="description-input"
value={value}
/>
);

const submitButton = settingsReadOnly ? null : (
<InlineSubmitButton
className="updateProjectSettings"
doneText="Updated"
id="update-desc"
isDone={isUpdated}
isMainButton={true}
isReadOnly={isUpdating || isPristine}
isSubmitting={isUpdating}
onSubmit={() => {}}
pristine={isPristine}
text="Update"
tooltipPristine="Modify description to update value"
/>
);
return (
<Form
onSubmit={(e) => {
console.log("submit");
}}
>
<FormGroup>
<Label for="projectDescription">Project Description</Label>
<div className="d-flex">
{inputField}
{submitButton}
</div>
<FormText>A short description for the project</FormText>
</FormGroup>
</Form>
);
}

export default ProjectDescription;
36 changes: 36 additions & 0 deletions client/src/features/project/ProjectEntityHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*!
* Copyright 2023 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as React from "react";

import { useProjectMetadataQuery } from "../projects/ProjectKgApi";
import EntityHeader from "../../components/entityHeader/EntityHeader";
import type { EntityHeaderProps } from "../../components/entityHeader/EntityHeader";

function ProjectEntityHeader(props: EntityHeaderProps) {
const { fullPath } = props;
// eslint-disable-next-line no-unused-vars
const { data: _kgData } = useProjectMetadataQuery({ projectPath: fullPath }, { skip: !fullPath });
return <EntityHeader {...props} />;
// TODO: Return the description from the KG
// if (isError) return <EntityHeader {...props} />;
// const description = { value: kgData?.description ?? "", isLoading };
// return <EntityHeader {...props} description={description} />;
}

export default ProjectEntityHeader;
51 changes: 51 additions & 0 deletions client/src/features/project/ProjectPageTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*!
* Copyright 2023 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React from "react";
import { Helmet } from "react-helmet";

import { useProjectJsonLdQuery, useProjectMetadataQuery } from "../projects/ProjectKgApi";

type ProjectJsonLdProps = {
projectPathWithNamespace: string;
projectTitle: string;
};

function ProjectPageTitle({ projectPathWithNamespace, projectTitle }: ProjectJsonLdProps) {
const kgProjectQueryParams = {
projectPath: projectPathWithNamespace,
};
const { data, isFetching, isLoading } = useProjectJsonLdQuery(kgProjectQueryParams);
// eslint-disable-next-line no-unused-vars
const { data: kgData } = useProjectMetadataQuery(kgProjectQueryParams);

const projectDesc = kgData?.description;
const pageTitle = projectDesc
? `${projectTitle} • Project • ${projectPathWithNamespace}${projectDesc}`
: `${projectTitle} • Project • ${projectPathWithNamespace}`;
const jsonLd =
!isFetching && !isLoading && data ? <script type="application/ld+json">{JSON.stringify(data)}</script> : null;
return (
<Helmet key="page-title">
<title>{pageTitle}</title>
{jsonLd}
</Helmet>
);
}

export default ProjectPageTitle;
3 changes: 3 additions & 0 deletions client/src/features/project/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ProjectEntityHeader from "./ProjectEntityHeader";

export { ProjectEntityHeader };
Loading

0 comments on commit 0ac090f

Please sign in to comment.