Skip to content

Commit

Permalink
feat: add user and group pages in Renku 2.0 (#3198)
Browse files Browse the repository at this point in the history
Closes #3197.

New features for Renku 2.0: add user pages, update group pages, update project info, update search page.

Changes:
- feat: add user pages in Renku 2.0 (#3200)
- feat: update group pages (#3206)
- feat: update the search page in Renku 2.0 (#3208)

**BREAKING CHANGE**: requires `renku-data-services >= 0.17.0`
**BREAKING CHANGE**: requires `renku-search >= 0.4.0`

---------

Co-authored-by: Chandrasekhar Ramakrishnan <[email protected]>
  • Loading branch information
leafty and ciyer authored Jul 10, 2024
1 parent c7f3cbc commit c598db5
Show file tree
Hide file tree
Showing 66 changed files with 2,741 additions and 694 deletions.
12 changes: 11 additions & 1 deletion client/package-lock.json

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

9 changes: 6 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
"storybook-wait-server": "wait-on http://127.0.0.1:6006",
"storybook-test": "test-storybook",
"storybook-compile-and-test": "concurrently -k -s first -n 'BUILD,TEST' -c 'magenta,blue' 'npm run storybook-build && npm run storybook-start-server' 'npm run storybook-wait-server && npm run storybook-test'",
"generate-api": "npm run generate-api:dataServicesUser && npm run generate-api:projectV2",
"generate-api": "npm run generate-api:dataServicesUser && npm run generate-api:namespaceV2 && npm run generate-api:projectV2 && npm run generate-api:searchV2",
"generate-api:dataServicesUser": "rtk-query-codegen-openapi src/features/user/dataServicesUser.api/dataServicesUser.api-config.ts",
"generate-api:projectV2": "rtk-query-codegen-openapi src/features/projectsV2/api/projectV2.api-config.ts"
"generate-api:namespaceV2": "rtk-query-codegen-openapi src/features/projectsV2/api/namespace.api-config.ts",
"generate-api:projectV2": "rtk-query-codegen-openapi src/features/projectsV2/api/projectV2.api-config.ts",
"generate-api:searchV2": "rtk-query-codegen-openapi src/features/searchV2/api/searchV2.api-config.ts"
},
"type": "module",
"dependencies": {
Expand Down Expand Up @@ -68,7 +70,7 @@
"react-helmet": "^6.1.0",
"react-hook-form": "^7.45.2",
"react-ipynb-renderer": "^2.1.2",
"react-js-pagination": "^3.0.2",
"react-js-pagination": "^3.0.3",
"react-katex": "^3.0.1",
"react-markdown": "^8.0.7",
"react-masonry-css": "^1.0.16",
Expand Down Expand Up @@ -125,6 +127,7 @@
"@types/react-avatar-editor": "^13.0.2",
"@types/react-dom": "^18.2.17",
"@types/react-helmet": "^6.1.9",
"@types/react-js-pagination": "^3.0.7",
"@types/react-router-dom": "^5.3.3",
"@types/react-router-hash-link": "^2.4.9",
"@types/react-test-renderer": "^18.0.7",
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/List.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
*/

import Masonry from "react-masonry-css";
import { Pagination } from "./Pagination";
import ListCard from "./list/ListCard";

import Pagination from "./Pagination";
import ListBar from "./list/ListBar";
import ListCard from "./list/ListCard";

/**
* This class receives a list of "items" and displays them either in a grid or in classic list.
Expand Down
73 changes: 0 additions & 73 deletions client/src/components/Pagination.jsx

This file was deleted.

94 changes: 94 additions & 0 deletions client/src/components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*!
* 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 ReactPagination from "react-js-pagination";

interface PaginationProps {
className?: string;
currentPage: number;
onPageChange: (pageNumber: number) => void;
perPage: number;
showDescription?: boolean;
totalInPage?: number;
totalItems?: number;
}

export default function Pagination({
className: className_,
currentPage,
onPageChange,
perPage,
showDescription,
totalInPage,
totalItems,
}: PaginationProps) {
// We do not display the pagination footer when there are no pages or only one page
if (totalItems == null || totalItems < 1 || totalItems <= perPage) {
return null;
}

const className = cx("pagination", className_);

return (
<div className={cx("d-flex", "align-items-center", "flex-column")}>
<ReactPagination
activePage={currentPage}
itemsCountPerPage={perPage}
totalItemsCount={totalItems}
onChange={onPageChange}
innerClass={className}
// Some defaults for the styling
itemClass={"page-item"}
linkClass={"page-link"}
activeClass={"page-item active"}
hideFirstLastPages={false}
hideDisabled
/>
{showDescription && totalInPage && (
<ExtraInfoPagination
currentPage={currentPage}
perPage={perPage}
totalInPage={totalInPage}
totalItems={totalItems}
/>
)}
</div>
);
}

interface ExtraInfoPaginationProps {
currentPage: number;
perPage: number;
totalInPage: number;
totalItems: number;
}
function ExtraInfoPagination({
currentPage,
perPage,
totalInPage,
totalItems,
}: ExtraInfoPaginationProps) {
const initialValue = currentPage * perPage - (perPage - 1);
const lastValue = initialValue + totalInPage - 1;
return (
<div className="pagination-label">
{initialValue} - {lastValue} of {totalItems} results
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@
* limitations under the License.
*/

import { Button } from "reactstrap";
import Masonry from "react-masonry-css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSadCry } from "@fortawesome/free-solid-svg-icons";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SerializedError } from "@reduxjs/toolkit";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import Masonry from "react-masonry-css";
import { Button } from "reactstrap";

import {
KgSearchResult,
ListResponse,
} from "../../features/kgSearch/KgSearch.types";
import { useKgSearchContext } from "../../features/kgSearch/KgSearchContext";
import {
FiltersProperties,
hasInitialFilterValues,
mapSearchResultToEntity,
} from "../../utils/helpers/KgSearchFunctions";
import { Loader } from "../Loader";
import Pagination from "../Pagination";
import ListCard from "../list/ListCard";
import { Pagination } from "../Pagination";
import { useKgSearchContext } from "../../features/kgSearch/KgSearchContext";

interface SearchResultProps {
data?: ListResponse<KgSearchResult>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
import { Loader } from "../../../../components/Loader";
import { RtkOrNotebooksError } from "../../../../components/errors/RtkErrorAlert";
import RenkuFrogIcon from "../../../../components/icons/RenkuIcon";
import { safeNewUrl } from "../../../../utils/helpers/safeNewUrl.utils";
import { Project } from "../../../projectsV2/api/projectV2.api";
import { usePatchProjectsByProjectIdMutation } from "../../../projectsV2/api/projectV2.enhanced-api";

Expand Down Expand Up @@ -386,24 +387,18 @@ export function RepositoryItem({
showMenu = true,
}: RepositoryItemProps) {
const canonicalUrlStr = useMemo(() => `${url.replace(/.git$/i, "")}`, [url]);
const canonicalUrl = useMemo(() => {
try {
return new URL(canonicalUrlStr);
} catch (error) {
if (error instanceof TypeError) {
return null;
}
throw error;
}
}, [canonicalUrlStr]);
const canonicalUrl = useMemo(
() => safeNewUrl(canonicalUrlStr),
[canonicalUrlStr]
);

const title = canonicalUrl?.pathname.split("/").pop() || canonicalUrlStr;
const title = canonicalUrl?.pathname.split("/").pop() ?? canonicalUrlStr;

const urlDisplay = (
<div className={cx("d-flex", "align-items-center", "gap-2")}>
<RepositoryIcon
className="flex-shrink-0"
provider={canonicalUrl?.origin}
provider={canonicalUrl?.origin ?? window.location.origin}
/>
<div className={cx("d-flex", "flex-column")}>
{canonicalUrl?.hostname && (
Expand Down
Loading

0 comments on commit c598db5

Please sign in to comment.