Skip to content

Fix(frontend)/apply feedback round 1 #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
81a550b
feat: add support for extra cards on curator tab
alcercu Mar 27, 2025
8d87ef0
fix(frontend): remove integrate section from ForBuilders
alcercu Mar 27, 2025
d72bff4
fix(frontend): remove elements from for lawyers page
alcercu Mar 27, 2025
e7cef8d
fix(frontend): remove stats from home page
alcercu Mar 27, 2025
c107b2e
Merge pull request #89 from kleros/feat(frontend)/add-extra-curator-c…
alcercu Mar 27, 2025
68bcee2
Merge branch 'fix(frontend)/apply-feedback-round-1' into fix(frontend…
alcercu Mar 27, 2025
d3e6ef8
Merge pull request #88 from kleros/fix(frontend)/remove-unwanted-elem…
alcercu Mar 27, 2025
0bf61ec
fix(frontend): remove home hero enter court button
alcercu Mar 28, 2025
b7681c0
fix(frontend): second round of feedback
alcercu Mar 31, 2025
2d10990
fix(frontend): rename path for-builders and for-lawyers to web-three …
alcercu Mar 31, 2025
1fb43e1
fix(frontend): reorder fellows section
alcercu Mar 31, 2025
38de70c
feat(frontend): add new home sections
alcercu Apr 2, 2025
5983784
feat(frontend): add pagination for mobile fellowship section
alcercu Apr 2, 2025
eea4ab4
fix(frontend): remove app list from home page
alcercu Apr 2, 2025
be7e2c0
feat(frontend): add svgr and arrows to mobile pagination
alcercu Apr 3, 2025
7e4090e
fix(frontend): remove unwanted sections
alcercu Apr 3, 2025
74d98a0
fix(frontend): remove unwanted sections
alcercu Apr 4, 2025
2a1857a
fix(frontend): footer subscribe link
alcercu Apr 4, 2025
644dc2e
feat(frontend): add industries cards
alcercu Apr 4, 2025
fdafe15
Merge branch 'master' into fix(frontend)/apply-feedback-round-1
alcercu Apr 4, 2025
0d3540b
feat(frontend): add for governemnts section
alcercu Apr 4, 2025
af89a8d
Merge branch 'master' into fix(frontend)/apply-feedback-round-1
alcercu Apr 7, 2025
f3b24be
fix(frontend): add more feedback
alcercu Apr 7, 2025
72adb30
feat(frontend): add swipable carousels R&D page
alcercu Apr 7, 2025
a624af6
fix(frontend): remove pnk token tokenomics section
alcercu Apr 7, 2025
eb916d6
fix(frontend): remove links r&d
alcercu Apr 8, 2025
28ebf5f
Merge branch 'master' into fix(frontend)/apply-feedback-round-1
alcercu Apr 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions frontend/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
webpack: (config) => {
const fileLoaderRule = config.module.rules.find((rule) =>
rule.test?.test?.(".svg"),
);
config.module.rules.push(
// Reapply the existing rule, but only for svg imports ending in ?url
{
...fileLoaderRule,
test: /\.svg$/i,
resourceQuery: /url/, // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
// exclude if *.svg?url
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] },
use: ["@svgr/webpack"],
},
);

fileLoaderRule.exclude = /\.svg$/i;

return config;
},
images: {
minimumCacheTTL: 31536000,
deviceSizes: [640, 750, 828, 1080, 1200, 1920],
Expand Down
4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
"next": "^15.1.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-use": "^17.6.0"
"react-use": "^17.6.0",
"swiper": "^11.2.6"
},
"devDependencies": {
"@svgr/webpack": "^8.1.0",
"@types/node": "^22.10.5",
"@types/react": "^19.0.4",
"@types/react-dom": "^19.0.2",
Expand Down
37 changes: 37 additions & 0 deletions frontend/src/app/enterprise/components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react";

import Image from "next/image";

import { request } from "@/utils/graphQLClient";

import { heroQuery, HeroQueryType } from "../queries/hero";

const Hero: React.FC = async () => {
const { header, subtitle, background } = (
await request<HeroQueryType>(heroQuery)
).forLawyersPageHero;

return (
<div className="relative px-6 pb-64 pt-44 md:pt-52 lg:px-32 lg:pb-80">
<div className="space-y-8">
<h1
className={
"pt-1 text-2xl font-medium text-primary-text lg:pt-3 lg:text-4xl"
}
>
{header}
</h1>
<p className="max-w-[685px] text-lg text-primary-text">{subtitle}</p>
</div>
<Image
src={background.url}
alt="Hero Image Background"
fill
unoptimized
className="absolute left-0 top-0 z-[-1] h-full object-cover"
/>
</div>
);
};

export default Hero;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import clsx from "clsx";
import Image from "next/image";

import { ArbitrationMethodTableType } from "@/app/for-lawyers/queries/kleros-dispute-resolution-section";
import { ArbitrationMethodTableType } from "@/app/enterprise/queries/kleros-dispute-resolution-section";

const TableCardContent: React.FC<ArbitrationMethodTableType> = ({
name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import clsx from "clsx";

import { ArbitrationMethodTableType } from "@/app/for-lawyers/queries/kleros-dispute-resolution-section";
import { ArbitrationMethodTableType } from "@/app/enterprise/queries/kleros-dispute-resolution-section";

import TableCardContent from "./TabelCardContent";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,16 @@ import {
} from "../../queries/kleros-dispute-resolution-section";

import ArbitrationMethodTable from "./ArbitrationMethodTable";
import KlerosEscrowSection from "./KlerosEscrowSection";
import ResearchSection from "./ResearchSection";
// import ResearchSection from "./ResearchSection";

const KlerosDisputeResolutionSection: React.FC = async () => {
const {
headerSubtitle,
header,
subtitle,
secondHeader,
secondSubtitle,
thirdHeader,
thirdSubtitle,
arbitrationMethodTable,
publications,
} = (
await request<ForLawyersPageDisputeResolutionSectionQueryType>(
forLawyersPageDisputeResolutionSectionQuery,
Expand All @@ -46,16 +42,13 @@ const KlerosDisputeResolutionSection: React.FC = async () => {
</div>

<ArbitrationMethodTable table={arbitrationMethodTable} />
<ResearchSection {...{ secondHeader, secondSubtitle, publications }} />

<div className="my-4 lg:px-32">
<h2 className="mb-6 text-lg font-medium text-primary-text lg:text-xl">
{thirdHeader}
</h2>
<p className="text-secondary-text lg:text-lg">{thirdSubtitle}</p>
</div>

<KlerosEscrowSection />
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const DisputeResolutionProcess: React.FC<IDisputeResolutionProcess> = ({
<div className="flex flex-col gap-8 lg:px-32">
<HighlightedText
{...disputeResolutionProcessHeader}
fullTextStyle="!text-primary-text !text-lg !font-medium lg:!text-xl"
highlightedTextStyle="!text-lg !font-medium lg:!text-xl"
fullTextStyle="!text-primary-text !text-xl !font-medium lg:!text-2xl"
highlightedTextStyle="!text-xl !font-medium lg:!text-2xl"
/>

<div className="relative flex w-full justify-center">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import _HighlightedText from "@/components/HighlightedText";

import { HighlightedText as IHighlightedText } from "../../queries/kleros-enterprise-section";

const HighlightedText: React.FC<
IHighlightedText & { fullTextStyle?: string; highlightedTextStyle?: string }
> = ({ fullText, highlightedText, fullTextStyle, highlightedTextStyle }) => {
return (
<_HighlightedText
{...{ fullText, highlightedText, fullTextStyle, highlightedTextStyle }}
/>
);
};

export default HighlightedText;
Comment on lines +1 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider simplifying this component wrapper

This wrapper component doesn't add any functionality beyond passing props to the base _HighlightedText component. According to the retrieved learnings, the Kleros website prefers simple implementations that handle specific cases over complex solutions (YAGNI principle).

There are a few issues to address:

  1. The IHighlightedText type from the query only contains fullText and highlightedText, but you're extending it with additional style properties. This suggests a mismatch between the type definition and its usage.

  2. The naming convention is confusing - typically, a component prefixed with _ indicates a private/internal component, but here it's being wrapped by a public component with the same name.

Consider one of these approaches:

  1. Either add the style properties to the HighlightedText type in the query file
  2. Or import the component directly where needed, and avoid this wrapper

If you decide to keep this wrapper, I suggest renaming the imported component to avoid confusion:

-import _HighlightedText from "@/components/HighlightedText";
+import BaseHighlightedText from "@/components/HighlightedText";

import { HighlightedText as IHighlightedText } from "../../queries/kleros-enterprise-section";

const HighlightedText: React.FC<
  IHighlightedText & { fullTextStyle?: string; highlightedTextStyle?: string }
> = ({ fullText, highlightedText, fullTextStyle, highlightedTextStyle }) => {
  return (
    <BaseHighlightedText
      {...{ fullText, highlightedText, fullTextStyle, highlightedTextStyle }}
    />
  );
};

export default HighlightedText;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import _HighlightedText from "@/components/HighlightedText";
import { HighlightedText as IHighlightedText } from "../../queries/kleros-enterprise-section";
const HighlightedText: React.FC<
IHighlightedText & { fullTextStyle?: string; highlightedTextStyle?: string }
> = ({ fullText, highlightedText, fullTextStyle, highlightedTextStyle }) => {
return (
<_HighlightedText
{...{ fullText, highlightedText, fullTextStyle, highlightedTextStyle }}
/>
);
};
export default HighlightedText;
import BaseHighlightedText from "@/components/HighlightedText";
import { HighlightedText as IHighlightedText } from "../../queries/kleros-enterprise-section";
const HighlightedText: React.FC<
IHighlightedText & { fullTextStyle?: string; highlightedTextStyle?: string }
> = ({ fullText, highlightedText, fullTextStyle, highlightedTextStyle }) => {
return (
<BaseHighlightedText
{...{ fullText, highlightedText, fullTextStyle, highlightedTextStyle }}
/>
);
};
export default HighlightedText;

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import clsx from "clsx";
import Image from "next/image";

import { request } from "@/utils/graphQLClient";

import { industriesQuery, IIndustriesQuery } from "./queries";

const Industries: React.FC = async () => {
const industriesData = await request<IIndustriesQuery>(industriesQuery);

Comment on lines +8 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for the GraphQL request.

Your async component correctly fetches data, but there's no error handling. If the request fails, it would cause the entire component to fail rendering.

const Industries: React.FC = async () => {
-  const industriesData = await request<IIndustriesQuery>(industriesQuery);
+  try {
+    const industriesData = await request<IIndustriesQuery>(industriesQuery);
+    
+    return (
+      <div className="flex flex-wrap gap-6 lg:mx-32">
+        {industriesData.enterprise.industries.map(({ title, icon }) => (
+          // Component rendering...
+        ))}
+      </div>
+    );
+  } catch (error) {
+    console.error("Failed to fetch industries data:", error);
+    return <div className="text-center p-6">Unable to load industries information.</div>;
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const Industries: React.FC = async () => {
const industriesData = await request<IIndustriesQuery>(industriesQuery);
const Industries: React.FC = async () => {
try {
const industriesData = await request<IIndustriesQuery>(industriesQuery);
return (
<div className="flex flex-wrap gap-6 lg:mx-32">
{industriesData.enterprise.industries.map(({ title, icon }) => (
// Component rendering...
))}
</div>
);
} catch (error) {
console.error("Failed to fetch industries data:", error);
return <div className="text-center p-6">Unable to load industries information.</div>;
}
};

return (
<div className="flex flex-wrap gap-6 lg:mx-32">
{industriesData.enterprise.industries.map(({ title, icon }) => (
<div
className={clsx(
"flex flex-1 flex-col items-center rounded-2xl border",
"border-stroke p-6",
)}
key={title}
>
<Image
className="mx-10"
src={icon.url}
alt={title + " card image"}
width="72"
height="72"
/>
<p className="mx-auto w-max text-lg text-primary-text lg:text-xl">
{title}
</p>
</div>
))}
</div>
);
};

export default Industries;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { gql } from "graphql-request";

export const industriesQuery = gql`
{
enterprise {
industries {
title
icon {
url
}
}
}
}
`;

export type IIndustriesQuery = {
enterprise: {
industries: Array<{
title: string;
icon: {
url: string;
};
}>;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Quote from "@/components/Quote";
import { request } from "@/utils/graphQLClient";

import {
lemonCashSectionQuery,
lemonCashSectionQueryType,
} from "../../queries/lemon-cash";

const LemonCashSection: React.FC = async () => {
const quote = (
await request<lemonCashSectionQueryType>(lemonCashSectionQuery)
).lemonCashSection;

return <Quote className="lg:px-32" {...quote} />;
};

export default LemonCashSection;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState } from "react";

import clsx from "clsx";

import { Table } from "@/app/for-lawyers/queries/kleros-enterprise-section";
import { Table } from "@/app/enterprise/queries/kleros-enterprise-section";
import Divider from "@/components/Divider";
import Pagination from "@/components/Pagination";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { Table } from "@/app/for-lawyers/queries/kleros-enterprise-section";
import { Table } from "@/app/enterprise/queries/kleros-enterprise-section";
import { useScreenSize } from "@/hooks/useScreenSize";

import DesktopTable from "./DesktopTable";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@ import {
import Card from "../Card";

import DisputeResolutionProcess from "./DisputeResolutionProcess";
import HighlightedText from "./HighlightedText";
import Industries from "./Industries";
import LemonCashSection from "./LemonCashSection";
import MethodsTable from "./MethodsTable";

const KlerosEnterpriseSection: React.FC = async () => {
const {
headerSubtitle,
header,
subtitle,
cards,
disputeResolutionProcessHeader,
processDiagram,
Expand All @@ -37,14 +34,6 @@ const KlerosEnterpriseSection: React.FC = async () => {
"px-6 py-12 lg:py-24",
)}
>
<div className="flex flex-col gap-6 lg:px-32">
<h3 className="text-primary-purple lg:text-lg">{headerSubtitle}</h3>
<h1 className="text-xl font-medium text-primary-text lg:text-3xl">
{header}
</h1>
<HighlightedText {...subtitle} />
</div>

<div className="grid grid-cols-1 gap-6 lg:grid-cols-3 lg:px-32">
{cards.map((card) => (
<Card key={card.title} {...card} />
Expand All @@ -59,6 +48,9 @@ const KlerosEnterpriseSection: React.FC = async () => {
/>

<LemonCashSection />

<Industries />

<div className="mx-auto lg:px-32">
<ExternalLink url={arrowLink.link.url} text={arrowLink.text} />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import clsx from "clsx";

import Card from "@/components/CtaCard";
import HighlightedText from "@/components/HighlightedText";
import Quote from "@/components/Quote";
import { request } from "@/utils/graphQLClient";

import {
forGovernmentsQuery,
IForGovernmentsQuery,
getBlock,
ICCText,
ICCCardsSection,
ICCLongText,
ICCQuote,
ICCHightlightText,
} from "./queries";

const ForGovernments: React.FC = async () => {
const sections = (await request<IForGovernmentsQuery>(forGovernmentsQuery))
.enterprise.GovernmentSection;
const [{ fullText, highlightedText }] = getBlock<ICCHightlightText>(
sections,
"ComponentContentHighlightText",
);
const [{ longtext }] = getBlock<ICCLongText>(
sections,
"ComponentContentLongText",
);
const [{ text }] = getBlock<ICCText>(sections, "ComponentContentText");
const [{ cards: objectivesCards }, { cards: disputeTypesCards }] =
getBlock<ICCCardsSection>(sections, "ComponentContentCardsSection");
const [quote] = getBlock<ICCQuote>(sections, "ComponentContentQuote");

Comment on lines +19 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for GraphQL requests and data extraction

The component currently doesn't include any error handling for the GraphQL request or for scenarios where expected blocks might be missing from the response. This could lead to runtime errors if the API returns unexpected data.

Consider adding proper error handling:

const ForGovernments: React.FC = async () => {
+  try {
    const sections = (await request<IForGovernmentsQuery>(forGovernmentsQuery))
      .enterprise.GovernmentSection;
+    
+    // Check if we have the expected blocks
+    const highlightBlocks = getBlock<ICCHightlightText>(
+      sections,
+      "ComponentContentHighlightText",
+    );
+    if (highlightBlocks.length === 0) throw new Error("Missing highlight text block");
+    const [{ fullText, highlightedText }] = highlightBlocks;
    
-    const [{ fullText, highlightedText }] = getBlock<ICCHightlightText>(
-      sections,
-      "ComponentContentHighlightText",
-    );
    // Similar checks for other blocks
    
    // Rest of the component...
+  } catch (error) {
+    console.error("Error in ForGovernments component:", error);
+    return <div>Failed to load government section content. Please try again later.</div>;
+  }
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const ForGovernments: React.FC = async () => {
const sections = (await request<IForGovernmentsQuery>(forGovernmentsQuery))
.enterprise.GovernmentSection;
const [{ fullText, highlightedText }] = getBlock<ICCHightlightText>(
sections,
"ComponentContentHighlightText",
);
const [{ longtext }] = getBlock<ICCLongText>(
sections,
"ComponentContentLongText",
);
const [{ text }] = getBlock<ICCText>(sections, "ComponentContentText");
const [{ cards: objectivesCards }, { cards: disputeTypesCards }] =
getBlock<ICCCardsSection>(sections, "ComponentContentCardsSection");
const [quote] = getBlock<ICCQuote>(sections, "ComponentContentQuote");
const ForGovernments: React.FC = async () => {
try {
const sections = (await request<IForGovernmentsQuery>(forGovernmentsQuery))
.enterprise.GovernmentSection;
// Check if we have the expected blocks
const highlightBlocks = getBlock<ICCHightlightText>(
sections,
"ComponentContentHighlightText",
);
if (highlightBlocks.length === 0) throw new Error("Missing highlight text block");
const [{ fullText, highlightedText }] = highlightBlocks;
const [{ longtext }] = getBlock<ICCLongText>(
sections,
"ComponentContentLongText",
);
const [{ text }] = getBlock<ICCText>(
sections,
"ComponentContentText"
);
const [{ cards: objectivesCards }, { cards: disputeTypesCards }] =
getBlock<ICCCardsSection>(
sections,
"ComponentContentCardsSection"
);
const [quote] = getBlock<ICCQuote>(
sections,
"ComponentContentQuote"
);
// Similar checks for other blocks can be added here
// Rest of the component...
} catch (error) {
console.error("Error in ForGovernments component:", error);
return <div>Failed to load government section content. Please try again later.</div>;
}
};

return (
<div
className={"flex flex-col gap-20 px-6 py-12 lg:gap-28 lg:px-32 lg:py-24"}
>
<div className="space-y-6">
<HighlightedText
{...{ fullText, highlightedText }}
fullTextStyle="!text-primary-text !text-xl !font-medium lg:!text-2xl"
highlightedTextStyle="!text-xl !font-medium lg:!text-2xl"
/>
<p className="lg:text-lg">{longtext}</p>
</div>
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
{objectivesCards.map((card) => (
<Card
key={card.title}
title={card.title}
icon={card.icon}
description={card.subtitle}
/>
))}
</div>
<Quote {...quote} />
<div>
<h3 className="mb-12 text-lg font-medium text-primary-text lg:text-xl">
{text}
</h3>
<div className="flex flex-wrap gap-4">
{disputeTypesCards.map((card) => (
<div
key={card.title}
className={clsx(
"text-md rounded-2xl border border-stroke bg-background-2 p-6",
"text-primary-text lg:text-lg",
)}
>
{card.title}
</div>
))}
</div>
</div>
</div>
);
};

export default ForGovernments;
Loading