Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions components/docs/eternalcore/commands/commands-table-rsc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { fetchEternalCoreData } from "@/lib/docs/eternalcore-data";
import DynamicCommandsTable from "./dynamic-commands-table";

export default async function CommandsTableRSC() {
const { commands } = await fetchEternalCoreData();
return <DynamicCommandsTable initialData={commands} />;
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
"use client";

import { useCommands } from "@/components/docs/eternalcore/commands/hooks/use-commands";
import type { CommandData } from "@/components/docs/eternalcore/commands/types";
import { CommandsSearchBar } from "./commands-search-bar";
import { CommandsTable } from "./commands-table";

export default function DynamicCommandsTable() {
interface DynamicCommandsTableProps {
initialData: CommandData[];
}

export default function DynamicCommandsTable({ initialData }: DynamicCommandsTableProps) {
const { commands, filteredCommands, searchQuery, loading, error, handleSearchChange } =
useCommands();
useCommands(initialData);

if (error) {
return <div className="p-6 text-center text-red-500">Error: {error}</div>;
Expand Down
62 changes: 9 additions & 53 deletions components/docs/eternalcore/commands/hooks/use-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import { create, insert, type Orama, search } from "@orama/orama";
import { type ChangeEvent, useEffect, useState } from "react";

import type { CommandData, EternalCoreData } from "@/components/docs/eternalcore/commands/types";
import type { CommandData } from "@/components/docs/eternalcore/commands/types";

const commandSchema = {
name: "string",
Expand All @@ -14,41 +13,34 @@ const commandSchema = {

type CommandDB = Orama<typeof commandSchema>;

const REGEX_LEADING_SLASH = /^\//;

export function useCommands() {
const [commands, setCommands] = useState<CommandData[]>([]);
const [filteredCommands, setFilteredCommands] = useState<CommandData[]>([]);
export function useCommands(initialData: CommandData[]) {
const [commands] = useState<CommandData[]>(initialData);
const [filteredCommands, setFilteredCommands] = useState<CommandData[]>(initialData);
const [searchQuery, setSearchQuery] = useState("");
const [db, setDb] = useState<CommandDB | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const load = async (): Promise<void> => {
const initDb = async () => {
try {
setLoading(true);
const all = await fetchAndParseCommands();

setCommands(all);
setFilteredCommands(all);

const orama = create({
schema: commandSchema,
});

await insertCommands(orama, all);
await insertCommands(orama, initialData);
setDb(orama);
} catch (err) {
const message = err instanceof Error ? err.message : "Unknown error";
const message = err instanceof Error ? err.message : "Failed to initialize search";
setError(message);
} finally {
setLoading(false);
}
};

load();
}, []);
initDb();
}, [initialData]);

const handleSearch = async (q: string): Promise<void> => {
setSearchQuery(q);
Expand Down Expand Up @@ -84,42 +76,6 @@ export function useCommands() {
};
}

async function fetchAndParseCommands(): Promise<CommandData[]> {
const res = await fetch(
"https://raw.githubusercontent.com/EternalCodeTeam/EternalCore/refs/heads/master/raw_eternalcore_documentation.json"
);

if (!res.ok) {
throw new Error("Failed to fetch data");
}

const data = (await res.json()) as unknown as EternalCoreData;

const commandsList =
data.commands?.map((c) => ({
name: `/${c.name.trim()}`,
permission: c.permissions?.[0] ?? "-",
description: c.descriptions?.[0] ?? "-",
arguments: c.arguments?.join(", ") ?? "-",
})) ?? [];

const permsList =
data.permissions?.map((p) => ({
name: p.name || "Unknown",
permission: p.permissions?.[0] ?? "-",
description: p.descriptions?.[0] ?? "-",
arguments: "-",
})) ?? [];

return [...commandsList, ...permsList].sort((a, b) =>
a.name
.replace(REGEX_LEADING_SLASH, "")
.localeCompare(b.name.replace(REGEX_LEADING_SLASH, ""), "pl", {
sensitivity: "base",
})
);
}

async function insertCommands(orama: CommandDB, commands: CommandData[]) {
for (const c of commands) {
await insert(orama, c);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
"use client";

import { usePlaceholders } from "@/components/docs/eternalcore/placeholder/hooks/use-placeholders";
import type { Placeholder } from "@/components/docs/eternalcore/placeholder/types";
import { DynamicNoPlaceholderMessage } from "./dynamic-no-placeholder-message";
import { PlaceholderCategoryButtons } from "./placeholder-category-buttons";
import { PlaceholderSearchBar } from "./placeholder-search-bar";
import { PlaceholderTable } from "./placeholder-table";

export default function DynamicPlaceholdersTable() {
interface DynamicPlaceholdersTableProps {
initialData: Placeholder[];
}

export default function DynamicPlaceholdersTable({ initialData }: DynamicPlaceholdersTableProps) {
const {
allPlaceholders,
viewablePlaceholders,
Expand All @@ -17,7 +22,7 @@ export default function DynamicPlaceholdersTable() {
loading,
handleSearchChange,
handleCategoryClick,
} = usePlaceholders();
} = usePlaceholders(initialData);

if (error) {
return <div className="p-6 text-center text-red-500">Error: {error}</div>;
Expand Down
27 changes: 6 additions & 21 deletions components/docs/eternalcore/placeholder/hooks/use-placeholders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ const placeholderSchema = {

type PlaceholderDB = Orama<typeof placeholderSchema>;

export function usePlaceholders() {
const [allPlaceholders, setAllPlaceholders] = useState<Placeholder[]>([]);
const [viewablePlaceholders, setViewablePlaceholders] = useState<Placeholder[]>([]);
export function usePlaceholders(initialData: Placeholder[]) {
const [allPlaceholders] = useState<Placeholder[]>(initialData);
const [viewablePlaceholders, setViewablePlaceholders] = useState<Placeholder[]>(initialData);
const [categories, setCategories] = useState<string[]>([]);
const [activeCategory, setActiveCategory] = useState("All");
const [searchQuery, setSearchQuery] = useState("");
Expand All @@ -31,26 +31,11 @@ export function usePlaceholders() {
try {
setLoading(true);

const response = await fetch(
"https://raw.githubusercontent.com/EternalCodeTeam/EternalCore/refs/heads/master/raw_eternalcore_placeholders.json"
);

if (!response.ok) {
setError(`Failed to fetch data: ${response.statusText}`);
return;
}

const data = (await response.json()) as Placeholder[];
const sortedData = [...data].sort((a, b) => a.name.localeCompare(b.name, "pl"));

setAllPlaceholders(sortedData);
setViewablePlaceholders(sortedData);

const uniqueCategories = Array.from(new Set(sortedData.map((p) => p.category))).sort();
const uniqueCategories = Array.from(new Set(initialData.map((p) => p.category))).sort();
setCategories(["All", ...uniqueCategories]);

const oramaDb = create({ schema: placeholderSchema });
await Promise.all(sortedData.map((p) => insert(oramaDb, p)));
await Promise.all(initialData.map((p) => insert(oramaDb, p)));

setDb(oramaDb);
} catch (exception) {
Expand All @@ -61,7 +46,7 @@ export function usePlaceholders() {
};

initializeData();
}, []);
}, [initialData]);

const filterPlaceholders = useCallback(
async (query = searchQuery, category = activeCategory) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { fetchEternalCoreData } from "@/lib/docs/eternalcore-data";
import DynamicPlaceholdersTable from "./dynamic-placeholders-table";

export default async function PlaceholdersTableRSC() {
const { placeholders } = await fetchEternalCoreData();
return <DynamicPlaceholdersTable initialData={placeholders} />;
}
14 changes: 9 additions & 5 deletions components/ui/mdx/mdx-components.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { MDXComponents } from "mdx/types";
import dynamic from "next/dynamic";
import type { ComponentProps, HTMLAttributes } from "react";

import DynamicCommandsTable from "@/components/docs/eternalcore/commands/dynamic-commands-table";
import DynamicPlaceholdersTable from "@/components/docs/eternalcore/placeholder/dynamic-placeholders-table";
import CommandsTableRSC from "@/components/docs/eternalcore/commands/commands-table-rsc";
import PlaceholdersTableRSC from "@/components/docs/eternalcore/placeholder/placeholders-table-rsc";
import { AlertBox } from "@/components/ui/alert-box";
import { Badge } from "@/components/ui/mdx/badge";
import { BeforeAfter, BeforeAfterItem } from "@/components/ui/mdx/before-after";
Expand All @@ -15,11 +16,14 @@ import { Divider } from "@/components/ui/mdx/divider";
import { FileTree, FileTreeItem } from "@/components/ui/mdx/file-tree";
import { Heading } from "@/components/ui/mdx/heading";
import { Inline } from "@/components/ui/mdx/inline";
import { LinkPreview } from "@/components/ui/mdx/link-preview";
import { MdxImage } from "@/components/ui/mdx/mdx-image";
import { MdxLink } from "@/components/ui/mdx/mdx-link";
import { Step, Steps } from "@/components/ui/mdx/steps";

const LinkPreview = dynamic(() =>
import("@/components/ui/mdx/link-preview").then((mod) => mod.LinkPreview)
);

type HeadingProps = HTMLAttributes<HTMLHeadingElement>;

export const components: MDXComponents = {
Expand Down Expand Up @@ -75,8 +79,8 @@ export const components: MDXComponents = {
FileTree,
FileTreeItem,
LinkPreview,
DynamicCommandsTable,
DynamicPlaceholdersTable,
DynamicCommandsTable: CommandsTableRSC,
DynamicPlaceholdersTable: PlaceholdersTableRSC,

code: ({ children, ...props }: ComponentProps<"code">) => {
if (typeof children !== "string") {
Expand Down
116 changes: 116 additions & 0 deletions lib/docs/eternalcore-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import "server-only";

export interface CommandData {
name: string;
permission: string;
description: string;
arguments: string;
}

export interface PlaceholderData {
name: string;
description: string;
category: string;
example: string;
returnType: string;
requiresPlayer: boolean;
}

interface RawEternalCoreData {
commands?: Array<{
name: string;
permissions?: string[];
descriptions?: string[];
arguments?: string[];
}>;
permissions?: Array<{
name: string;
permissions?: string[];
descriptions?: string[];
}>;
placeholders?: Array<{
name: string;
description: string;
category?: string;
}>;
}

const REGEX_LEADING_SLASH = /^\//;

export async function fetchEternalCoreData() {
const [commandsRes, placeholdersRes] = await Promise.all([
fetch(
"https://raw.githubusercontent.com/EternalCodeTeam/EternalCore/refs/heads/master/raw_eternalcore_documentation.json",
{
next: {
revalidate: 3600,
tags: ["eternalcore-data"],
},
}
),
fetch(
"https://raw.githubusercontent.com/EternalCodeTeam/EternalCore/refs/heads/master/raw_eternalcore_placeholders.json",
{
next: {
revalidate: 3600,
tags: ["eternalcore-placeholders"],
},
}
),
]);

if (!commandsRes.ok) {
throw new Error("Failed to fetch EternalCore commands data");
}

if (!placeholdersRes.ok) {
throw new Error("Failed to fetch EternalCore placeholders data");
}

const commandsData = (await commandsRes.json()) as RawEternalCoreData;
// biome-ignore lint/suspicious/noExplicitAny: External data source
const placeholdersData = (await placeholdersRes.json()) as any[];

const commandsList =
commandsData.commands?.map((c) => ({
name: `/${c.name.trim()}`,
permission: c.permissions?.[0] ?? "-",
description: c.descriptions?.[0] ?? "-",
arguments: c.arguments?.join(", ") ?? "-",
})) ?? [];

const permsList =
commandsData.permissions?.map((p) => ({
name: p.name || "Unknown",
permission: p.permissions?.[0] ?? "-",
description: p.descriptions?.[0] ?? "-",
arguments: "-",
})) ?? [];

const sortedCommands = [...commandsList, ...permsList].sort((a, b) =>
a.name
.replace(REGEX_LEADING_SLASH, "")
.localeCompare(b.name.replace(REGEX_LEADING_SLASH, ""), "pl", {
sensitivity: "base",
})
);

const placeholders =
placeholdersData?.map((p) => ({
name: p.name,
description: p.description,
category: p.category ?? "General",
example: p.example ?? "",
returnType: p.returnType ?? "String",
requiresPlayer: p.requiresPlayer ?? false,
})) ?? [];

const sortedPlaceholders = placeholders.sort((a, b: { name: string }) =>
a.name.localeCompare(b.name)
);
Comment on lines +108 to +110
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The explicit type annotation for the b parameter is redundant. If the placeholders array is properly typed (which would be the case after addressing the any[] cast on line 72), TypeScript can correctly infer the types of both a and b from the array. Removing the explicit type makes the code cleaner and more maintainable.

  const sortedPlaceholders = placeholders.sort((a, b) =>
    a.name.localeCompare(b.name)
  );


return {
commands: sortedCommands,
placeholders: sortedPlaceholders,
};
}