Skip to content

Commit

Permalink
feat(client): Add structure project page (#3104)
Browse files Browse the repository at this point in the history
Add project page header, menu and project info

---------

Co-authored-by: Flora Thiebaut <flora.thiebaut@sdsc.ethz.ch>
  • Loading branch information
andre-code and leafty committed May 3, 2024
1 parent fdfa368 commit 3b6cae9
Show file tree
Hide file tree
Showing 32 changed files with 1,353 additions and 60 deletions.
2 changes: 1 addition & 1 deletion client/src/components/TimeCaption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
* limitations under the License.
*/

import { ReactNode, useEffect, useRef, useState } from "react";
import cx from "classnames";
import { DateTime, Duration } from "luxon";
import { ReactNode, useEffect, useRef, useState } from "react";
import { UncontrolledTooltip } from "reactstrap";
import {
ensureDateTime,
Expand Down
49 changes: 47 additions & 2 deletions client/src/components/buttons/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { faSyncAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import cx from "classnames";
import { Fragment, useState } from "react";
import { ChevronDown } from "react-bootstrap-icons";
import { Fragment, ReactNode, useRef, useState } from "react";
import { ArrowRight, ChevronDown, PencilSquare } from "react-bootstrap-icons";
import { Link } from "react-router-dom";
import {
Button,
Expand All @@ -41,6 +41,7 @@ import {

import { simpleHash } from "../../utils/helpers/HelperFunctions";
import { LoadingLabel, SuccessLabel } from "../formlabels/FormLabels";
import styles from "./Buttons.module.scss";

type ButtonWithMenuProps = {
children?:
Expand Down Expand Up @@ -287,11 +288,55 @@ function RoundButtonGroup({ children }: { children: React.ReactNode[] }) {
return <ButtonGroup className="round-button-group">{children}</ButtonGroup>;
}

/*
* underline Link with icon
*/
function UnderlineArrowLink({
to,
text,
tooltip,
}: {
text: string;
to: string;
tooltip: ReactNode;
}) {
const ref = useRef(null);
return (
<>
<span ref={ref} className={styles.LinkUnderline}>
<Link className="text-decoration-none" to={to}>
{text} <ArrowRight />
</Link>
</span>
<UncontrolledTooltip target={ref}>{tooltip}</UncontrolledTooltip>
</>
);
}

/*
* Edit button
*/
function EditButtonLink({ to, title }: { to: string; title: string }) {
const ref = useRef(null);
return (
<>
<span ref={ref} className={styles.LinkIcon}>
<Link className="text-decoration-none" to={to}>
<PencilSquare />
</Link>
</span>
<UncontrolledTooltip target={ref}>{title}</UncontrolledTooltip>
</>
);
}

export {
ButtonWithMenu,
CardButton,
EditButtonLink,
GoBackButton,
InlineSubmitButton,
RefreshButton,
RoundButtonGroup,
UnderlineArrowLink,
};
44 changes: 44 additions & 0 deletions client/src/components/buttons/Buttons.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.LinkUnderline {
border-bottom: solid 1px var(--bs-rk-text-light);

&:hover {
border-bottom: solid 1px var(--bs-rk-text);
}
a {
color: var(--bs-rk-text-light);
}
a:hover {
color: #000;
}
}
.LinkIcon {
a {
color: var(--bs-rk-text-light);
font-size: 20px;
}
a:hover {
color: #000;
}
}
.PlusIconButton {
color: var(--bs-rk-green);
display: flex;
justify-content: center;
align-items: center;
width: 24px;
height: 24px;
border-radius: 1000px;
border: solid 1px var(--bs-rk-green);
background-color: white;
padding: 0;

&:hover {
color: white;
background-color: var(--bs-rk-green-hover);
}
&:active,
&:focus {
color: white;
background-color: var(--bs-rk-green-active);
}
}
14 changes: 14 additions & 0 deletions client/src/components/buttons/ThreeDots.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.threeDots {
font-size: 34px;
color: var(--bs-rk-green) !important;

&:hover {
color: var(--bs-rk-green-hover) !important;
}

&:first-child:active,
&:active,
&:focus {
color: var(--bs-rk-green-active) !important;
}
}
36 changes: 36 additions & 0 deletions client/src/features/ProjectPageV2/LazyProjectPageV2Container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*!
* Copyright 2024 - 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 { Suspense, lazy } from "react";
import PageLoader from "../../components/PageLoader";
import { ProjectPageContentType } from "./ProjectPageContainer/ProjectPageContainer.tsx";

const ProjectPageV2Show = lazy(
() => import("./ProjectPageContainer/ProjectPageContainer.tsx")
);

export default function LazyProjectPageV2Show({
contentPage,
}: {
contentPage: ProjectPageContentType;
}) {
return (
<Suspense fallback={<PageLoader />}>
<ProjectPageV2Show contentPage={contentPage} />
</Suspense>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/variables-dark";
@import "~bootstrap/scss/maps";
@import "~bootstrap/scss/mixins";
.HeaderContainer {
background-color: var(--bs-white);

@include media-breakpoint-up(lg) {
& {
background-color: transparent;
}
}
}
.NavContainer {
background-color: var(--bs-gray-200);

@include media-breakpoint-up(lg) {
& {
background-color: transparent;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*!
* Copyright 2024 - 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 cx from "classnames";
import { Link, useParams } from "react-router-dom-v5-compat";
import { Col, Row } from "reactstrap";
import { Loader } from "../../../components/Loader.tsx";
import { RtkErrorAlert } from "../../../components/errors/RtkErrorAlert.tsx";
import { Url } from "../../../utils/helpers/url";
import { useGetProjectsByNamespaceAndSlugQuery } from "../../projectsV2/api/projectV2.api.ts";
import { isErrorResponse } from "../../projectsV2/api/projectV2.enhanced-api.ts";
import { ProjectV2ShowByProjectId } from "../../projectsV2/show/ProjectV2Show.tsx";
import ProjectInformation from "../ProjectPageContent/ProjectInformation/ProjectInformation.tsx";
import ProjectPageContent from "../ProjectPageContent/ProjectPageContent.tsx";
import ProjectPageHeader from "../ProjectPageHeader/ProjectPageHeader.tsx";
import ProjectPageNav from "../ProjectPageNav/ProjectPageNav.tsx";
import styles from "./ProjectPageContainer.module.scss";

export enum ProjectPageContentType {
Overview = "Overview",
Settings = "Settings",
ProjectInfo = "ProjectInfo",
}

export function ProjectPageContainer({
contentPage,
}: {
contentPage: ProjectPageContentType;
}) {
const { namespace, slug } = useParams<{
id: string | undefined;
namespace: string | undefined;
slug: string | undefined;
}>();
const { data, isLoading, error } = useGetProjectsByNamespaceAndSlugQuery({
namespace: namespace ?? "",
slug: slug ?? "",
});

if (isLoading) return <Loader />;
if (error) {
if (isErrorResponse(error) && error?.data?.error?.code == 404) {
return (
<Row>
<Col>
<div>
Project does not exist, or you are not authorized to access it.{" "}
<Link to={Url.get(Url.pages.projectV2.list)}>Return to list</Link>
</div>
</Col>
</Row>
);
}
return (
<Row>
<Col>
<p>Could not retrieve project</p>
<RtkErrorAlert error={error} />
</Col>
</Row>
);
}
if (data == null)
return (
<Row>
<Col>
<div>Could not retrieve project</div>
</Col>
</Row>
);
return (
<Row>
<Col
sm={12}
className={cx("py-4", "px-0", "px-lg-2", styles.HeaderContainer)}
>
<ProjectPageHeader project={data}></ProjectPageHeader>
</Col>
<Col sm={12} lg={1} className={cx(styles.NavContainer)}>
<ProjectPageNav namespace={namespace} slug={slug}></ProjectPageNav>
</Col>
<Col sm={12} lg={9}>
<ProjectPageContent
project={data}
selectedContent={contentPage}
></ProjectPageContent>
</Col>
<Col sm={12} lg={2} className={cx("d-none", "d-lg-block", " d-sm-none")}>
<ProjectInformation project={data}></ProjectInformation>
</Col>
</Row>
);
}

export default function ProjectPageV2Show({
contentPage,
}: {
contentPage?: ProjectPageContentType;
}) {
const { id: projectId } = useParams<{
id: string | undefined;
namespace: string | undefined;
slug: string | undefined;
}>();
if (projectId != null) {
return <ProjectV2ShowByProjectId />;
}
return (
<ProjectPageContainer
contentPage={contentPage || ProjectPageContentType.Overview}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.projectPageImgPlaceholder img {
max-width: 200px;
height: auto;
background-color: var(--bs-gray);
}
Loading

0 comments on commit 3b6cae9

Please sign in to comment.