Run lint and prettier
CI Pipeline / test (20) (pull_request) Successful in 3m0s
CI Pipeline / test (18) (pull_request) Successful in 3m18s
CI Pipeline / e2e (firefox) (pull_request) Successful in 3m20s
CI Pipeline / e2e (chromium) (pull_request) Successful in 3m54s
CI Pipeline / e2e (webkit) (pull_request) Successful in 3m41s
CI Pipeline / performance (pull_request) Successful in 3m3s
CI Pipeline / visual-regression (pull_request) Successful in 7m12s
CI Pipeline / storybook (pull_request) Successful in 1m29s
CI Pipeline / lint (pull_request) Failing after 1m7s
CI Pipeline / build (pull_request) Successful in 1m20s

This commit is contained in:
adilallo
2025-10-07 17:27:07 -06:00
parent c991e22f09
commit 6bd751957c
40 changed files with 96370 additions and 524 deletions
+6 -6
View File
@@ -46,7 +46,7 @@ export async function POST(request) {
// Log for monitoring
console.log(
`Web Vital received: ${metric} = ${data.value}ms (${data.rating})`
`Web Vital received: ${metric} = ${data.value}ms (${data.rating})`,
);
return NextResponse.json({ success: true });
@@ -54,7 +54,7 @@ export async function POST(request) {
console.error("Error processing web vital:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
{ status: 500 },
);
}
}
@@ -70,7 +70,7 @@ export async function GET() {
if (file.endsWith(".json")) {
const metric = file.replace(".json", "");
const data = JSON.parse(
fs.readFileSync(path.join(WEB_VITALS_DIR, file), "utf8")
fs.readFileSync(path.join(WEB_VITALS_DIR, file), "utf8"),
);
if (data.length > 0) {
@@ -86,14 +86,14 @@ export async function GET() {
average:
values.length > 0
? Math.round(
values.reduce((a, b) => a + b, 0) / values.length
values.reduce((a, b) => a + b, 0) / values.length,
)
: 0,
min: values.length > 0 ? Math.min(...values) : 0,
max: values.length > 0 ? Math.max(...values) : 0,
goodCount: ratings.filter((r) => r === "good").length,
needsImprovementCount: ratings.filter(
(r) => r === "needs-improvement"
(r) => r === "needs-improvement",
).length,
poorCount: ratings.filter((r) => r === "poor").length,
lastUpdated: data[data.length - 1]?.receivedAt,
@@ -108,7 +108,7 @@ export async function GET() {
console.error("Error fetching web vitals:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
{ status: 500 },
);
}
}
+1 -1
View File
@@ -106,7 +106,7 @@ const AskOrganizer = memo(
</div>
</section>
);
}
},
);
AskOrganizer.displayName = "AskOrganizer";
+1 -1
View File
@@ -12,7 +12,7 @@ const Avatar = memo(
const baseStyles = `rounded-[var(--radius-measures-radius-full)] object-cover ${sizeStyles[size]} ${className}`;
return <img src={src} alt={alt} className={baseStyles} {...props} />;
}
},
);
Avatar.displayName = "Avatar";
+1 -1
View File
@@ -16,7 +16,7 @@ const AvatarContainer = memo(
{children}
</div>
);
}
},
);
AvatarContainer.displayName = "AvatarContainer";
+1 -1
View File
@@ -108,7 +108,7 @@ const Button = memo(
{children}
</button>
);
}
},
);
Button.displayName = "Button";
+1 -1
View File
@@ -123,7 +123,7 @@ const ContentContainer = memo(
</div>
</div>
);
}
},
);
ContentContainer.displayName = "ContentContainer";
+1 -1
View File
@@ -179,7 +179,7 @@ const ContentLockup = memo(
)}
</div>
);
}
},
);
ContentLockup.displayName = "ContentLockup";
+1 -1
View File
@@ -91,7 +91,7 @@ const ContentThumbnailTemplate = memo(
</div>
</Link>
);
}
},
);
ContentThumbnailTemplate.displayName = "ContentThumbnailTemplate";
+1 -2
View File
@@ -28,7 +28,7 @@ class ErrorBoundary extends Component {
Something went wrong
</h2>
<p className="text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-016)]">
We're sorry, but something unexpected happened.
We&apos;re sorry, but something unexpected happened.
</p>
<button
onClick={() => this.setState({ hasError: false, error: null })}
@@ -46,4 +46,3 @@ class ErrorBoundary extends Component {
}
export default ErrorBoundary;
+1 -1
View File
@@ -42,7 +42,7 @@ const FeatureGrid = memo(({ title, subtitle, className = "" }) => {
href: "#conflict-resolution",
},
],
[]
[],
);
return (
<section
+1 -1
View File
@@ -33,7 +33,7 @@ const HeaderTab = memo(
/>
</div>
);
}
},
);
HeaderTab.displayName = "HeaderTab";
+1 -1
View File
@@ -46,7 +46,7 @@ const HeroBanner = memo(
</div>
</section>
);
}
},
);
HeroBanner.displayName = "HeroBanner";
+4 -4
View File
@@ -79,10 +79,10 @@ const HomeHeader = memo(() => {
? size === "home" || size === "homeMd"
? "homeMd"
: size === "large"
? "large"
: size === "homeXlarge"
? "homeXlarge"
: "xsmallUseCases"
? "large"
: size === "homeXlarge"
? "homeXlarge"
: "xsmallUseCases"
: size
}
variant={
+1 -1
View File
@@ -33,7 +33,7 @@ const ImagePlaceholder = memo(
{text}
</div>
);
}
},
);
ImagePlaceholder.displayName = "ImagePlaceholder";
+20 -20
View File
@@ -95,26 +95,26 @@ const Logo = memo(({ size = "default", showText = true }) => {
size === "homeHeaderXsmall"
? sizes.homeHeaderXsmall
: size === "homeHeaderSm"
? sizes.homeHeaderSm
: size === "homeHeaderMd"
? sizes.homeHeaderMd
: size === "homeHeaderLg"
? sizes.homeHeaderLg
: size === "homeHeaderXl"
? sizes.homeHeaderXl
: size === "header"
? sizes.header
: size === "headerMd"
? sizes.headerMd
: size === "headerLg"
? sizes.headerLg
: size === "headerXl"
? sizes.headerXl
: size === "footer"
? sizes.footer
: size === "footerLg"
? sizes.footerLg
: sizes.default;
? sizes.homeHeaderSm
: size === "homeHeaderMd"
? sizes.homeHeaderMd
: size === "homeHeaderLg"
? sizes.homeHeaderLg
: size === "homeHeaderXl"
? sizes.homeHeaderXl
: size === "header"
? sizes.header
: size === "headerMd"
? sizes.headerMd
: size === "headerLg"
? sizes.headerLg
: size === "headerXl"
? sizes.headerXl
: size === "footer"
? sizes.footer
: size === "footerLg"
? sizes.footerLg
: sizes.default;
return (
<Link href="/" className="block" aria-label="CommunityRule Logo">
+1 -1
View File
@@ -25,7 +25,7 @@ const MenuBar = memo(
{children}
</nav>
);
}
},
);
MenuBar.displayName = "MenuBar";
+1 -1
View File
@@ -158,7 +158,7 @@ const MenuBarItem = memo(
{children}
</a>
);
}
},
);
MenuBarItem.displayName = "MenuBarItem";
+1 -1
View File
@@ -116,7 +116,7 @@ const MiniCard = memo(
{cardContent}
</div>
);
}
},
);
MiniCard.displayName = "MiniCard";
+46 -43
View File
@@ -1,58 +1,61 @@
import React, { memo } from "react";
const NavigationItem = memo(({
href = "#",
children,
variant = "default",
size = "default",
className = "",
disabled = false,
...props
}) {
// Variant styles
const variantStyles = {
default:
"bg-transparent text-[var(--color-content-default-brand-primary)] border border-transparent hover:bg-[var(--color-surface-default-tertiary)] hover:text-[var(--color-content-default-brand-primary)] active:bg-transparent active:text-[var(--color-content-default-brand-primary)] active:border-[var(--color-content-default-brand-primary)] disabled:bg-[var(--color-surface-default-tertiary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-[var(--color-content-default-tertiary)] disabled:opacity-50 disabled:cursor-not-allowed",
};
const NavigationItem = memo(
({
href = "#",
children,
variant = "default",
size = "default",
className = "",
disabled = false,
...props
}) => {
// Variant styles
const variantStyles = {
default:
"bg-transparent text-[var(--color-content-default-brand-primary)] border border-transparent hover:bg-[var(--color-surface-default-tertiary)] hover:text-[var(--color-content-default-brand-primary)] active:bg-transparent active:text-[var(--color-content-default-brand-primary)] active:border-[var(--color-content-default-brand-primary)] disabled:bg-[var(--color-surface-default-tertiary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-[var(--color-content-default-tertiary)] disabled:opacity-50 disabled:cursor-not-allowed",
};
// Size styles
const sizeStyles = {
default:
"px-[var(--spacing-scale-016)] py-[var(--spacing-scale-016)] gap-[var(--spacing-scale-004)]",
xsmall:
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-002)] gap-[var(--spacing-scale-004)]",
};
// Size styles
const sizeStyles = {
default:
"px-[var(--spacing-scale-016)] py-[var(--spacing-scale-016)] gap-[var(--spacing-scale-004)]",
xsmall:
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-002)] gap-[var(--spacing-scale-004)]",
};
// Text styles based on size
const textStyles = {
default: "font-inter text-[10px] leading-[12px] font-medium tracking-[0%]",
xsmall: "font-inter text-[10px] leading-[12px] font-medium tracking-[0%]",
};
// Text styles based on size
const textStyles = {
default:
"font-inter text-[10px] leading-[12px] font-medium tracking-[0%]",
xsmall: "font-inter text-[10px] leading-[12px] font-medium tracking-[0%]",
};
const baseStyles = `inline-flex items-center ${sizeStyles[size]} rounded-[var(--radius-measures-radius-full)] ${textStyles[size]} transition-all duration-200 cursor-pointer`;
const baseStyles = `inline-flex items-center ${sizeStyles[size]} rounded-[var(--radius-measures-radius-full)] ${textStyles[size]} transition-all duration-200 cursor-pointer`;
// Determine which variant to use
let finalVariant = variant;
if (disabled) {
finalVariant = "default"; // The disabled state is handled by disabled: utilities
}
// Determine which variant to use
let finalVariant = variant;
if (disabled) {
finalVariant = "default"; // The disabled state is handled by disabled: utilities
}
const combinedStyles = `${baseStyles} ${variantStyles[finalVariant]} ${className}`;
const combinedStyles = `${baseStyles} ${variantStyles[finalVariant]} ${className}`;
if (disabled) {
return (
<span className={combinedStyles} {...props}>
{children}
</span>
);
}
if (disabled) {
return (
<span className={combinedStyles} {...props}>
<a href={href} className={combinedStyles} {...props}>
{children}
</span>
</a>
);
}
return (
<a href={href} className={combinedStyles} {...props}>
{children}
</a>
);
});
);
NavigationItem.displayName = "NavigationItem";
+1 -1
View File
@@ -20,7 +20,7 @@ const NumberedCards = memo(({ title, subtitle, cards }) => {
text: card.text,
})),
}),
[title, subtitle, cards]
[title, subtitle, cards],
);
return (
+2 -2
View File
@@ -80,7 +80,7 @@ const QuoteBlock = memo(
const handleImageError = (error) => {
console.warn(
`QuoteBlock: Failed to load avatar image for ${author}:`,
error
error,
);
setImageError(true);
setImageLoading(false);
@@ -244,7 +244,7 @@ const QuoteBlock = memo(
</div>
</section>
);
}
},
);
QuoteBlock.displayName = "QuoteBlock";
+6 -6
View File
@@ -8,7 +8,7 @@ const RelatedArticles = memo(
// Memoize filtered posts to prevent unnecessary re-computations
const filteredPosts = useMemo(
() => relatedPosts.filter((post) => post.slug !== currentPostSlug),
[relatedPosts, currentPostSlug]
[relatedPosts, currentPostSlug],
);
const [currentIndex, setCurrentIndex] = useState(0);
@@ -44,7 +44,7 @@ const RelatedArticles = memo(
: "none",
scrollBehavior: !isMobile ? "smooth" : "auto",
}),
[isMobile, currentIndex]
[isMobile, currentIndex],
);
// Memoize progress bar style calculation
@@ -54,10 +54,10 @@ const RelatedArticles = memo(
index === currentIndex
? `${progress}%`
: index < currentIndex
? "100%"
: "0%",
? "100%"
: "0%",
}),
[currentIndex, progress]
[currentIndex, progress],
);
// Check if we're on mobile (below lg breakpoint)
@@ -155,7 +155,7 @@ const RelatedArticles = memo(
</div>
</section>
);
}
},
);
RelatedArticles.displayName = "RelatedArticles";
+1 -1
View File
@@ -71,7 +71,7 @@ const RuleCard = memo(
)}
</div>
);
}
},
);
RuleCard.displayName = "RuleCard";
+1 -1
View File
@@ -52,7 +52,7 @@ const SectionHeader = memo(
</div>
</div>
);
}
},
);
SectionHeader.displayName = "SectionHeader";
+1 -1
View File
@@ -88,7 +88,7 @@ const WebVitalsDashboard = memo(() => {
},
}));
});
}
},
);
}
}, []);
+1 -1
View File
@@ -97,7 +97,7 @@ const OptimizedComponent = memo(({ data, onAction }) => {
(id) => {
onAction(id);
},
[onAction]
[onAction],
);
return <div onClick={handleClick}>{/* Component content */}</div>;
+1 -1
View File
@@ -194,7 +194,7 @@ test("components work together", () => {
<Header />
<MainContent />
<Footer />
</div>
</div>,
);
// Test that components complement each other
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -70,7 +70,7 @@ const nextConfig = {
reportFilename: isServer
? "../analyze/server.html"
: "../analyze/client.html",
})
}),
);
} catch (error) {
console.warn("Bundle analyzer not available:", error.message);
+2 -2
View File
@@ -114,7 +114,7 @@ class BundleAnalyzer {
Object.entries(this.results.bundles).forEach(([filename, bundle]) => {
const budget = budgets.find(
(b) => filename.includes(b.name) || b.name === "all"
(b) => filename.includes(b.name) || b.name === "all",
);
if (budget) {
@@ -175,7 +175,7 @@ class BundleAnalyzer {
// General recommendations
const totalSize = Object.values(this.results.bundles).reduce(
(sum, bundle) => sum + bundle.sizeKB,
0
0,
);
if (totalSize > 2000) {
+6 -6
View File
@@ -69,7 +69,7 @@ class PerformanceMonitor {
});
} catch (error) {
console.warn(
"⚠️ Development server not running, skipping Lighthouse CI..."
"⚠️ Development server not running, skipping Lighthouse CI...",
);
return;
}
@@ -99,23 +99,23 @@ class PerformanceMonitor {
if (resultFile) {
const results = JSON.parse(
fs.readFileSync(path.join(lhciResultsPath, resultFile), "utf8")
fs.readFileSync(path.join(lhciResultsPath, resultFile), "utf8"),
);
if (results.lhr && results.lhr.audits) {
this.metrics.coreWebVitals = {
lcp: this.getAuditScore(
results.lhr.audits,
"largest-contentful-paint"
"largest-contentful-paint",
),
fid: this.getAuditScore(results.lhr.audits, "max-potential-fid"),
cls: this.getAuditScore(
results.lhr.audits,
"cumulative-layout-shift"
"cumulative-layout-shift",
),
fcp: this.getAuditScore(
results.lhr.audits,
"first-contentful-paint"
"first-contentful-paint",
),
tti: this.getAuditScore(results.lhr.audits, "interactive"),
performance: results.lhr.categories.performance?.score * 100 || 0,
@@ -150,7 +150,7 @@ class PerformanceMonitor {
"..",
".next",
"static",
"chunks"
"chunks",
);
if (fs.existsSync(bundleStatsPath)) {
+12 -12
View File
@@ -90,11 +90,11 @@ class PerformanceTester {
"..",
".next",
"analyze",
"bundle-analysis.json"
"bundle-analysis.json",
);
if (fs.existsSync(bundleReportPath)) {
const bundleData = JSON.parse(
fs.readFileSync(bundleReportPath, "utf8")
fs.readFileSync(bundleReportPath, "utf8"),
);
this.results.bundleAnalysis = bundleData;
@@ -105,7 +105,7 @@ class PerformanceTester {
) {
this.results.summary.failed += bundleData.budgetViolations.length;
console.log(
`⚠️ Found ${bundleData.budgetViolations.length} budget violations`
`⚠️ Found ${bundleData.budgetViolations.length} budget violations`,
);
} else {
this.results.summary.passed += 1;
@@ -134,7 +134,7 @@ class PerformanceTester {
"..",
".next",
"monitoring",
"performance-report.json"
"performance-report.json",
);
if (fs.existsSync(perfReportPath)) {
const perfData = JSON.parse(fs.readFileSync(perfReportPath, "utf8"));
@@ -144,7 +144,7 @@ class PerformanceTester {
if (perfData.budgetViolations && perfData.budgetViolations.length > 0) {
this.results.summary.failed += perfData.budgetViolations.length;
console.log(
`⚠️ Found ${perfData.budgetViolations.length} performance violations`
`⚠️ Found ${perfData.budgetViolations.length} performance violations`,
);
} else {
this.results.summary.passed += 1;
@@ -173,11 +173,11 @@ class PerformanceTester {
"..",
".next",
"web-vitals",
"report.json"
"report.json",
);
if (fs.existsSync(vitalsReportPath)) {
const vitalsData = JSON.parse(
fs.readFileSync(vitalsReportPath, "utf8")
fs.readFileSync(vitalsReportPath, "utf8"),
);
this.results.webVitals = vitalsData;
console.log("✅ Web Vitals tracking setup complete");
@@ -204,7 +204,7 @@ class PerformanceTester {
});
} catch (error) {
console.warn(
"⚠️ Development server not running, skipping Lighthouse CI..."
"⚠️ Development server not running, skipping Lighthouse CI...",
);
this.results.summary.warnings += 1;
this.results.summary.total += 1;
@@ -221,7 +221,7 @@ class PerformanceTester {
if (resultFile) {
const lhciData = JSON.parse(
fs.readFileSync(path.join(lhciResultsPath, resultFile), "utf8")
fs.readFileSync(path.join(lhciResultsPath, resultFile), "utf8"),
);
this.results.lighthouse = lhciData;
console.log("✅ Lighthouse CI completed");
@@ -248,7 +248,7 @@ class PerformanceTester {
const reportPath = path.join(
TEST_RESULTS_DIR,
"performance-test-report.json"
"performance-test-report.json",
);
fs.writeFileSync(reportPath, JSON.stringify(this.results, null, 2));
@@ -262,7 +262,7 @@ class PerformanceTester {
generateMarkdownReport() {
const reportPath = path.join(
TEST_RESULTS_DIR,
"performance-test-report.md"
"performance-test-report.md",
);
let report = `# Performance Test Report\n\n`;
@@ -310,7 +310,7 @@ class PerformanceTester {
} (exceeds ${
violation.budget
}) - ${violation.severity.toUpperCase()}\n`;
}
},
);
} else {
report += `✅ No performance budget violations found\n\n`;
+2 -2
View File
@@ -289,7 +289,7 @@ export default WebVitalsDashboard;
if (file.endsWith(".json")) {
const metric = file.replace(".json", "");
const data = JSON.parse(
fs.readFileSync(path.join(WEB_VITALS_DIR, file), "utf8")
fs.readFileSync(path.join(WEB_VITALS_DIR, file), "utf8"),
);
if (data.length > 0) {
@@ -310,7 +310,7 @@ export default WebVitalsDashboard;
max: values.length > 0 ? Math.max(...values) : 0,
goodCount: ratings.filter((r) => r === "good").length,
needsImprovementCount: ratings.filter(
(r) => r === "needs-improvement"
(r) => r === "needs-improvement",
).length,
poorCount: ratings.filter((r) => r === "poor").length,
};
-2
View File
@@ -19,5 +19,3 @@ export default {
},
},
};
export default ErrorBoundary;
+6 -6
View File
@@ -47,7 +47,7 @@ describe("LogoWall Component", () => {
render(<LogoWall />);
expect(
screen.getByText("Trusted by leading cooperators")
screen.getByText("Trusted by leading cooperators"),
).toBeInTheDocument();
});
@@ -64,7 +64,7 @@ describe("LogoWall Component", () => {
const section = document.querySelector("section");
expect(section).toHaveClass(
"p-[var(--spacing-scale-032)]",
"md:px-[var(--spacing-scale-024)]"
"md:px-[var(--spacing-scale-024)]",
);
});
@@ -72,7 +72,7 @@ describe("LogoWall Component", () => {
render(<LogoWall />);
const grid = document.querySelector(
'[class*="grid grid-cols-2 grid-rows-3"]'
'[class*="grid grid-cols-2 grid-rows-3"]',
);
expect(grid).toBeInTheDocument();
expect(grid).toHaveClass("sm:grid-cols-3", "sm:grid-rows-2", "md:flex");
@@ -84,7 +84,7 @@ describe("LogoWall Component", () => {
const foodNotBombsLogo = screen.getByAltText("Food Not Bombs");
expect(foodNotBombsLogo).toHaveAttribute(
"src",
"/assets/Section/Logo_FoodNotBombs.png"
"/assets/Section/Logo_FoodNotBombs.png",
);
expect(foodNotBombsLogo).toHaveClass("h-11", "lg:h-14", "xl:h-[70px]");
});
@@ -109,7 +109,7 @@ describe("LogoWall Component", () => {
render(<LogoWall />);
const logoContainers = document.querySelectorAll(
'[class*="hover:opacity-100"]'
'[class*="hover:opacity-100"]',
);
expect(logoContainers.length).toBeGreaterThan(0);
});
@@ -129,7 +129,7 @@ describe("LogoWall Component", () => {
render(<LogoWall />);
const logoContainers = document.querySelectorAll(
'[class*="transition-opacity duration-500"]'
'[class*="transition-opacity duration-500"]',
);
expect(logoContainers.length).toBeGreaterThan(0);
});