-
-
- Search Results ({searchResults.length})
-
-
-
-
-
-
- {searchResults.map((result, index) => (
-
- ))}
+ {searchResults.length > 0 && (
+
+
Search Results ({searchResults.length})
+
- )
- )}
-
+ )}
-
- {ruleCount && }
-
+
{ruleCount && }
diff --git a/components/LatestRulesCard.tsx b/components/LatestRulesCard.tsx
index 47ac4c01b..a10242018 100644
--- a/components/LatestRulesCard.tsx
+++ b/components/LatestRulesCard.tsx
@@ -1,10 +1,10 @@
"use client";
+import Link from "next/link";
+import { RiTimeFill } from "react-icons/ri";
import { Card } from "@/components/ui/card";
-import { LatestRule } from "@/models/LatestRule";
import { timeAgo } from "@/lib/dateUtils";
-import { RiTimeFill } from "react-icons/ri";
-import Link from "next/link";
+import { LatestRule } from "@/models/LatestRule";
interface LatestRulesProps {
rules: LatestRule[];
@@ -27,10 +27,7 @@ export default function LatestRulesCard({ rules }: LatestRulesProps) {
))}
-
+
See more
diff --git a/components/LatestRulesList.tsx b/components/LatestRulesList.tsx
deleted file mode 100644
index 6c4b3a696..000000000
--- a/components/LatestRulesList.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-"use client";
-
-import { LatestRule } from "@/models/LatestRule";
-import { useState } from "react";
-import { CgSortAz } from "react-icons/cg";
-import Dropdown from "./ui/dropdown";
-import RuleCard from "./RuleCard";
-
-interface LatestRulesListProps {
- rulesByUpdated: LatestRule[];
- rulesByCreated: LatestRule[];
- title?: string;
-}
-
-export default function LatestRulesList({
- rulesByUpdated,
- rulesByCreated,
- title,
-}: LatestRulesListProps) {
- const [currentSort, setCurrentSort] = useState<"lastUpdated" | "created">(
- "lastUpdated"
- );
-
- const handleSortChange = (newSort: "lastUpdated" | "created") => {
- setCurrentSort(newSort);
- };
-
- const currentRules =
- currentSort === "lastUpdated" ? rulesByUpdated : rulesByCreated;
-
- const sortOptions = [
- { value: "lastUpdated", label: "Last Updated" },
- { value: "created", label: "Recently Created" }
- ];
-
- return (
-
-
- {title &&
{title}
}
-
-
- handleSortChange(value as "lastUpdated" | "created")}
- />
-
-
-
- {currentRules.map((rule, index) => (
-
- ))}
-
- );
-}
diff --git a/components/LatestRulesTable.tsx b/components/LatestRulesTable.tsx
deleted file mode 100644
index de87bb742..000000000
--- a/components/LatestRulesTable.tsx
+++ /dev/null
@@ -1,173 +0,0 @@
-"use client";
-
-import Link from "next/link";
-import { LatestRule } from "@/models/LatestRule";
-import { timeAgo } from "@/lib/dateUtils";
-import { useState, useEffect } from "react";
-import { CgSortAz } from "react-icons/cg";
-import { RiTimeFill } from "react-icons/ri";
-import { Card } from "./ui/card";
-import Dropdown from "./ui/dropdown";
-
-interface LatestRulesTableProps {
- rulesByUpdated: LatestRule[];
- rulesByCreated: LatestRule[];
- title?: string;
-}
-
-export default function LatestRulesTable({
- rulesByUpdated,
- rulesByCreated,
- title,
-}: LatestRulesTableProps) {
- const [currentSort, setCurrentSort] = useState<"lastUpdated" | "created">(
- "lastUpdated"
- );
-
- const handleSortChange = (newSort: "lastUpdated" | "created") => {
- setCurrentSort(newSort);
- };
-
- const currentRules =
- currentSort === "lastUpdated" ? rulesByUpdated : rulesByCreated;
-
- // Map of ruleUri -> github username
- const [usernames, setUsernames] = useState
>({});
- const [loadingMap, setLoadingMap] = useState>({});
-
- // Prefetch usernames for visible rules
- useEffect(() => {
- let isMounted = true;
- const urisToFetch = currentRules
- .map((r) => r.uri)
- .filter((u) => u && usernames[u] === undefined);
-
- if (urisToFetch.length === 0) return;
-
- (async () => {
- for (const uri of urisToFetch) {
- if (!uri) continue;
- try {
- if (!isMounted) return;
- setLoadingMap((m) => ({ ...m, [uri]: true }));
- const params = new URLSearchParams();
- params.set('ruleUri', uri);
- const res = await fetch(`/api/github/rules/authors?${params.toString()}`);
- if (!res.ok) throw new Error('Failed to fetch GitHub username');
- const { authors } = await res.json();
- // re-use existing helper in other file? simple heuristic: pick last modified
- const lastModified = authors && authors.length ? authors[authors.length - 1] : null;
- if (!isMounted) return;
- setUsernames((m) => ({ ...m, [uri]: lastModified }));
- } catch (e) {
- // ignore prefetch errors
- setUsernames((m) => ({ ...m, [uri]: null }));
- } finally {
- if (isMounted) setLoadingMap((m) => ({ ...m, [uri]: false }));
- }
- }
- })();
-
- return () => {
- isMounted = false;
- };
- }, [currentRules, usernames]);
-
- const openGithubProfile = async (uri: string) => {
- if (!uri) return;
- const username = usernames[uri];
- if (username) {
- window.open(`https://github.com/${username}`, '_blank', 'noopener,noreferrer');
- return;
- }
- try {
- setLoadingMap((m) => ({ ...m, [uri]: true }));
- const params = new URLSearchParams();
- params.set('ruleUri', uri);
- const res = await fetch(`/api/github/rules/authors?${params.toString()}`);
- if (!res.ok) throw new Error('Failed to fetch GitHub username');
- const { authors } = await res.json();
- const lastModified = authors && authors.length ? authors[authors.length - 1] : null;
- setUsernames((m) => ({ ...m, [uri]: lastModified }));
- if (lastModified) window.open(`https://github.com/${lastModified}`, '_blank', 'noopener,noreferrer');
- } catch (e) {
- console.error('Failed to fetch GitHub username for latest rules:', e);
- } finally {
- setLoadingMap((m) => ({ ...m, [uri]: false }));
- }
- };
-
- const sortOptions = [
- { value: "lastUpdated", label: "Last Updated" },
- { value: "created", label: "Recently Created" },
- ];
-
- return (
-
-
- {title &&
{title}
}
-
-
-
- handleSortChange(value as "lastUpdated" | "created")
- }
- />
-
-
-
- {currentRules.map((rule, index) => (
-
-
-
#{index + 1}
-
-
-
- {rule.title}
-
-
-
-
-
-
- ))}
-
- );
-}
diff --git a/components/RuleActionButtons.tsx b/components/RuleActionButtons.tsx
index 0c0725673..8d50c453e 100644
--- a/components/RuleActionButtons.tsx
+++ b/components/RuleActionButtons.tsx
@@ -23,7 +23,7 @@ export default function RuleActionButtons({ rule, showBookmark = true, showOpenI
if (isAdminPage) return null;
return (
-
+
{showBookmark && (
...}>
diff --git a/components/radio-button/radio-button.tsx b/components/radio-button/radio-button.tsx
index 1841e13fa..da8e408e2 100644
--- a/components/radio-button/radio-button.tsx
+++ b/components/radio-button/radio-button.tsx
@@ -8,10 +8,10 @@ interface RadioButtonProps {
selectedOption: string;
handleOptionChange: (e: React.ChangeEvent) => void;
labelText: string;
- position?: "first" | "middle" | "last";
+ className?: string;
}
-const RadioButton: React.FC = ({ id, value, selectedOption, handleOptionChange, labelText, position }) => {
+const RadioButton: React.FC = ({ id, value, selectedOption, handleOptionChange, labelText, className = "" }) => {
const isSelected = selectedOption === value;
const handleButtonClick = () => {
@@ -27,27 +27,11 @@ const RadioButton: React.FC = ({ id, value, selectedOption, ha
handleOptionChange(syntheticEvent);
};
- const getBorderClasses = () => {
- if (!position) {
- return "border rounded";
- }
-
- if (position === "first") {
- return "border border-r-0 rounded-l-md rounded-r-none";
- } else if (position === "last") {
- return "border border-l-0 rounded-r-md rounded-l-none";
- } else if (position === "middle") {
- return "border rounded-none";
- }
-
- return "border rounded";
- };
-
return (