Skip to content

Commit

Permalink
feat: add group page design
Browse files Browse the repository at this point in the history
  • Loading branch information
andre-code committed Dec 11, 2024
1 parent 0a879ac commit 406e7d4
Show file tree
Hide file tree
Showing 17 changed files with 866 additions and 410 deletions.
43 changes: 43 additions & 0 deletions client/src/components/EntityWatermark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*!
* 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 { CSSProperties } from "react";
import { Folder, People, Person } from "react-bootstrap-icons";

interface EntityWatermarkProps {
type: "project" | "user" | "group";
}
export function EntityWatermark({ type }: EntityWatermarkProps) {
const watermarkStyles: CSSProperties = {
right: "0",
fontSize: "150px",
lineHeight: "0",
color: "rgba(0, 0, 0, 0.1)",
};
return (
<div
style={watermarkStyles}
className={cx("d-none", "d-lg-block", "position-absolute", "top-0")}
>
{type === "group" && <People />}
{type === "user" && <Person />}
{type === "project" && <Folder />}
</div>
);
}
56 changes: 56 additions & 0 deletions client/src/components/PageNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*!
* 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 { Eye, Sliders } from "react-bootstrap-icons";
import { Nav, NavItem } from "reactstrap";
import RenkuNavLinkV2 from "./RenkuNavLinkV2";

export interface PageNavOptions {
overviewUrl: string;
settingsUrl: string;
}
export default function PageNav({ options }: { options: PageNavOptions }) {
return (
<>
<Nav tabs>
<NavItem>
<RenkuNavLinkV2
end
to={options.overviewUrl}
title="Overview"
data-cy="nav-link-overview"
>
<Eye className={cx("bi", "me-1")} />
Overview
</RenkuNavLinkV2>
</NavItem>
<NavItem>
<RenkuNavLinkV2
end
to={options.settingsUrl}
title="Settings"
data-cy="nav-link-settings"
>
<Sliders className={cx("bi", "me-1")} />
Settings
</RenkuNavLinkV2>
</NavItem>
</Nav>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,20 @@ function DataConnectorBoxContent({
totalConnectors={data.total}
/>
<CardBody>
{data.total === 0 && (
<p className="m-0">
Add published datasets from data repositories, and connect to
cloud storage to read and write custom data.
</p>
{data.total === 0 && namespaceKind === "group" && (
<AddEmptyListForGroupNamespace namespace={namespace} />
)}
{data.total === 0 && namespaceKind === "user" && (
<AddEmptyListForUserNamespace namespace={namespace} />
)}
{data.total > 0 && (
<ListGroup flush>
{data.dataConnectors?.map((dc) => (
<DataConnectorBoxListDisplay key={dc.id} dataConnector={dc} />
<DataConnectorBoxListDisplay
key={dc.id}
dataConnector={dc}
extendedPreview={true}
/>
))}
</ListGroup>
)}
Expand Down Expand Up @@ -320,3 +324,35 @@ function DataConnectorLoadingBoxContent() {
</Card>
);
}

function AddEmptyListForGroupNamespace({ namespace }: { namespace: string }) {
const { permissions } = useGroupPermissions({ groupSlug: namespace });

return (
<PermissionsGuard
disabled={<p>This group has no visible data connectors.</p>}
enabled={
<p>
Add published datasets from data repositories, and connect to cloud
storage to read and write custom data.
</p>
}
requestedPermission="write"
userPermissions={permissions}
/>
);
}

function AddEmptyListForUserNamespace({ namespace }: { namespace: string }) {
const { data: currentUser } = useGetUserQuery();

if (currentUser?.isLoggedIn && currentUser.username === namespace) {
return (
<p>
Add published datasets from data repositories, and connect to cloud
storage to read and write custom data.
</p>
);
}
return <p>This user has no visible data connectors.</p>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@

import cx from "classnames";
import { useCallback, useState } from "react";
import { Globe2, Lock } from "react-bootstrap-icons";
import { EyeFill, Globe2, Lock, Pencil } from "react-bootstrap-icons";
import { Col, ListGroupItem, Row } from "reactstrap";

import ClampedParagraph from "../../../components/clamped/ClampedParagraph";
import { TimeCaption } from "../../../components/TimeCaption";
import UserAvatar, { UserAvatarSize } from "../../usersV2/show/UserAvatar.tsx";
import type {
DataConnector,
DataConnectorToProjectLink,
Expand All @@ -33,23 +34,42 @@ import DataConnectorView from "./DataConnectorView";
interface DataConnectorBoxListDisplayProps {
dataConnector: DataConnector;
dataConnectorLink?: DataConnectorToProjectLink;
extendedPreview?: boolean;
}
export default function DataConnectorBoxListDisplay({
dataConnector,
dataConnectorLink,
extendedPreview,
}: DataConnectorBoxListDisplayProps) {
const {
name,
description,
visibility,
creation_date: creationDate,
storage,
namespace,
} = dataConnector;

const [showDetails, setShowDetails] = useState(false);
const toggleDetails = useCallback(() => {
setShowDetails((open) => !open);
}, []);

const type = `${storage?.configuration?.type?.toString() ?? ""} ${
storage?.configuration?.provider?.toString() ?? ""
}`;
const readOnly = storage?.readonly ? (
<div>
<EyeFill className={cx("bi", "me-1")} />
Read only
</div>
) : (
<div>
<Pencil className={cx("bi", "me-1")} />
Allow Read-write
</div>
);

return (
<>
<ListGroupItem
Expand All @@ -58,33 +78,56 @@ export default function DataConnectorBoxListDisplay({
onClick={toggleDetails}
>
<Row className={cx("align-items-center", "g-2")}>
<Col>
<Col className={cx("d-flex", "flex-column", "gap-1")}>
<span className="fw-bold" data-cy="data-connector-name">
{name}
</span>
<div
className={cx(
"d-flex",
"flex-row",
"text-truncate",
"gap-2",
"align-items-center"
)}
>
<UserAvatar username={namespace} size={UserAvatarSize.small} />
<p className={cx("mb-0", "text-truncate", "text-muted")}>
{namespace}
</p>
</div>
{description && <ClampedParagraph>{description}</ClampedParagraph>}
{extendedPreview && <div className="text-muted">{type}</div>}
<div
className={cx(
"align-items-center",
"d-flex",
"flex-wrap",
"gap-2",
"justify-content-between",
"mt-auto"
"justify-content-between"
)}
>
<div>
<div
className={cx(
"align-items-center",
"d-flex",
"flex-wrap",
"gap-3",
"mt-auto"
)}
>
{visibility.toLowerCase() === "private" ? (
<>
<div>
<Lock className={cx("bi", "me-1")} />
Private
</>
</div>
) : (
<>
<div>
<Globe2 className={cx("bi", "me-1")} />
Public
</>
</div>
)}
{extendedPreview && readOnly}
</div>
<TimeCaption
datetime={creationDate}
Expand Down
30 changes: 30 additions & 0 deletions client/src/features/groupsV2/LazyGroupContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*!
* 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";

const GroupV2Container = lazy(() => import("./show/GroupPageContainer"));

export default function LazyGroupContainer() {
return (
<Suspense fallback={<PageLoader />}>
<GroupV2Container />
</Suspense>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
import { Suspense, lazy } from "react";
import PageLoader from "../../components/PageLoader";

const GroupV2Show = lazy(() => import("./show/GroupV2Show"));
const GroupV2Overview = lazy(() => import("./show/GroupV2Show"));

export default function LazyGroupV2Show() {
export default function LazyGroupV2Overview() {
return (
<Suspense fallback={<PageLoader />}>
<GroupV2Show />
<GroupV2Overview />
</Suspense>
);
}
Loading

0 comments on commit 406e7d4

Please sign in to comment.