diff --git a/web/src/assets/svgs/icons/star.svg b/web/src/assets/svgs/icons/star.svg
new file mode 100644
index 000000000..055e853c0
--- /dev/null
+++ b/web/src/assets/svgs/icons/star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/components/CaseStarButton.tsx b/web/src/components/CaseStarButton.tsx
new file mode 100644
index 000000000..1ba097686
--- /dev/null
+++ b/web/src/components/CaseStarButton.tsx
@@ -0,0 +1,58 @@
+import React, { useMemo } from "react";
+import styled, { css } from "styled-components";
+
+import { Button, Tooltip } from "@kleros/ui-components-library";
+
+import Star from "svgs/icons/star.svg";
+
+import useIsDesktop from "hooks/useIsDesktop";
+import useStarredCases from "hooks/useStarredCases";
+
+const StyledButton = styled(Button)<{ starred: boolean }>`
+ background: none;
+ padding: 0 0 2px 0;
+
+ .button-svg {
+ width: 24px;
+ height: 24px;
+ margin: 0;
+ fill: none;
+
+ path {
+ stroke: ${({ theme }) => theme.secondaryPurple};
+ }
+ ${({ starred }) =>
+ starred &&
+ css`
+ fill: ${({ theme }) => theme.secondaryPurple};
+ `};
+ }
+
+ :hover {
+ background: none;
+ }
+`;
+
+const CaseStarButton: React.FC<{ id: string }> = ({ id }) => {
+ const { starredCases, starCase } = useStarredCases();
+ const isDesktop = useIsDesktop();
+ const starred = useMemo(() => Boolean(starredCases.has(id)), [id, starredCases]);
+ const text = starred ? "Remove from favorite" : "Add to favorite";
+ return (
+
+ {
+ e.stopPropagation();
+ starCase(id);
+ }}
+ />
+
+ );
+};
+
+export default CaseStarButton;
diff --git a/web/src/components/FavoriteCases.tsx b/web/src/components/FavoriteCases.tsx
new file mode 100644
index 000000000..c90ab4f5b
--- /dev/null
+++ b/web/src/components/FavoriteCases.tsx
@@ -0,0 +1,81 @@
+import React, { useMemo, useState } from "react";
+import styled from "styled-components";
+
+import { StandardPagination } from "@kleros/ui-components-library";
+
+import useStarredCases from "hooks/useStarredCases";
+import { isUndefined } from "utils/index";
+
+import { DisputeDetailsFragment, useCasesQuery } from "queries/useCasesQuery";
+
+import { responsiveSize } from "styles/responsiveSize";
+
+import DisputeView from "components/DisputeView";
+import { SkeletonDisputeCard } from "components/StyledSkeleton";
+
+const Container = styled.div`
+ margin-top: ${responsiveSize(48, 80)};
+`;
+
+const Title = styled.h1`
+ margin-bottom: 4px;
+`;
+
+const DisputeContainer = styled.div`
+ --gap: 16px;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(min(100%, max(312px, (100% - var(--gap) * 2)/3)), 1fr));
+ align-items: stretch;
+ gap: var(--gap);
+`;
+
+const StyledLabel = styled.label`
+ display: block;
+ color: ${({ theme }) => theme.primaryBlue};
+ cursor: pointer;
+ margin-bottom: ${responsiveSize(12, 16)};
+ :hover {
+ color: ${({ theme }) => theme.secondaryBlue};
+ }
+`;
+
+const StyledPagination = styled(StandardPagination)`
+ margin-top: 24px;
+ margin-left: auto;
+ margin-right: auto;
+`;
+
+const FavoriteCases: React.FC = () => {
+ const { starredCaseIds, clearAll } = useStarredCases();
+
+ const [currentPage, setCurrentPage] = useState(1);
+ const casesPerPage = 3;
+ const totalPages = Math.ceil(starredCaseIds.length / casesPerPage);
+
+ const { data } = useCasesQuery((currentPage - 1) * casesPerPage, casesPerPage, {
+ id_in: starredCaseIds,
+ });
+
+ const disputes: DisputeDetailsFragment[] = useMemo(() => data?.disputes as DisputeDetailsFragment[], [data]);
+
+ return starredCaseIds.length > 0 && (isUndefined(disputes) || disputes.length > 0) ? (
+
+ Favorite Cases
+ Clear all
+
+ {isUndefined(disputes)
+ ? Array.from({ length: 3 }).map((_, index) => )
+ : disputes.map((dispute) => )}
+
+ {totalPages > 1 ? (
+ setCurrentPage(page)}
+ />
+ ) : null}
+
+ ) : null;
+};
+
+export default FavoriteCases;
diff --git a/web/src/hooks/useStarredCases.tsx b/web/src/hooks/useStarredCases.tsx
new file mode 100644
index 000000000..eb6109306
--- /dev/null
+++ b/web/src/hooks/useStarredCases.tsx
@@ -0,0 +1,26 @@
+import { useMemo } from "react";
+
+import { useLocalStorage } from "./useLocalStorage";
+
+const useStarredCases = () => {
+ const initialValue = new Set();
+
+ const [localStarredCases, setLocalStarredCases] = useLocalStorage("starredCases", Array.from(initialValue));
+
+ const starredCases = useMemo(() => new Set(localStarredCases), [localStarredCases]);
+ const starredCaseIds = Array.from(starredCases.keys());
+
+ const starCase = (id: string) => {
+ if (starredCases.has(id)) starredCases.delete(id);
+ else starredCases.add(id);
+
+ setLocalStarredCases(Array.from(starredCases));
+ };
+
+ const clearAll = () => {
+ setLocalStarredCases(Array.from(initialValue));
+ };
+ return { starredCases, starredCaseIds, starCase, clearAll };
+};
+
+export default useStarredCases;
diff --git a/web/src/pages/Cases/CaseDetails/index.tsx b/web/src/pages/Cases/CaseDetails/index.tsx
index 28c97a060..64cc12424 100644
--- a/web/src/pages/Cases/CaseDetails/index.tsx
+++ b/web/src/pages/Cases/CaseDetails/index.tsx
@@ -12,6 +12,9 @@ import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
import { responsiveSize } from "styles/responsiveSize";
+import CaseStarButton from "components/CaseStarButton";
+import ScrollTop from "components/ScrollTop";
+
import Appeal from "./Appeal";
import Evidence from "./Evidence";
import MaintenanceButtons from "./MaintenanceButtons";
@@ -19,7 +22,6 @@ import Overview from "./Overview";
import Tabs from "./Tabs";
import Timeline from "./Timeline";
import Voting from "./Voting";
-import ScrollTop from "components/ScrollTop";
const Container = styled.div``;
@@ -38,8 +40,11 @@ const HeaderContainer = styled.div`
`;
const Header = styled.h1`
- margin: 0;
+ display: flex;
+ align-items: center;
flex: 1;
+ gap: 8px;
+ margin: 0;
`;
const CaseDetails: React.FC = () => {
@@ -53,7 +58,10 @@ const CaseDetails: React.FC = () => {
-
+
+ Case #{id} {id ? : null}
+
+
diff --git a/web/src/pages/Dashboard/index.tsx b/web/src/pages/Dashboard/index.tsx
index 37f06b6e6..e8a890e66 100644
--- a/web/src/pages/Dashboard/index.tsx
+++ b/web/src/pages/Dashboard/index.tsx
@@ -1,9 +1,6 @@
import React, { useMemo } from "react";
import styled from "styled-components";
-import { MAX_WIDTH_LANDSCAPE } from "styles/landscapeStyle";
-import { responsiveSize } from "styles/responsiveSize";
-
import { useNavigate, useParams } from "react-router-dom";
import { useAccount } from "wagmi";
@@ -15,8 +12,12 @@ import { useUserQuery } from "queries/useUser";
import { OrderDirection } from "src/graphql/graphql";
+import { MAX_WIDTH_LANDSCAPE } from "styles/landscapeStyle";
+import { responsiveSize } from "styles/responsiveSize";
+
import CasesDisplay from "components/CasesDisplay";
import ConnectWallet from "components/ConnectWallet";
+import FavoriteCases from "components/FavoriteCases";
import ScrollTop from "components/ScrollTop";
import Courts from "./Courts";
@@ -94,6 +95,7 @@ const Dashboard: React.FC = () => {
)}
+
);