ldc-laravel-base-project/resources/ts/back/Components/DynamicPages/DynamicPageDetails.tsx
2026-07-09 17:32:17 +02:00

152 lines
4.5 KiB
TypeScript

import { type ReactNode } from "react";
import type { ConfigField } from "@/back/types/dynamic-pages";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import str from "@/hooks/use-string";
import useTrans from "@/hooks/use-translations";
import { cn, GetTranslation } from "@/lib/utils";
type ChildItem = Record<string, unknown> & { id: string | number };
type FieldValue = string | number | boolean | null | undefined | ChildItem[];
const RenderRows = (
currentValue: Record<string, unknown>,
currentConfig: ConfigField[],
hasBorders = false,
path = "",
): ReactNode => {
const tableNotKnownTrans = str(useTrans("uwil-backend::back.table_not_known"))
.ucFirst()
.value();
return currentConfig.map((field, index) => {
const hasChildren = !!field.children?.length;
const accordionKey = path
? `${path}.${field.key}-${String(index)}`
: `${field.key}-${String(index)}`;
const rowContent = (currentHasChildren = false) => {
const cellClasses = "break-words whitespace-normal";
const placeholderClasses =
"text-sm text-muted-foreground break-words whitespace-normal";
const fieldValue = currentValue[field.key] as FieldValue;
let cellContent: ReactNode;
if (currentHasChildren) {
const count = Array.isArray(fieldValue) ? fieldValue.length : 0;
cellContent = (
<p className={placeholderClasses}>
{GetTranslation("uwil-dynamic-pages::trans.element", {
params: { count: String(count) },
choice: count > 1 ? 2 : 1,
})}
</p>
);
} else if (fieldValue !== null && fieldValue !== undefined) {
const displayValue = Array.isArray(fieldValue)
? null
: typeof fieldValue === "boolean"
? GetTranslation(
fieldValue ? "uwil-backend::back.yes" : "uwil-backend::back.no",
)
: String(fieldValue);
cellContent = displayValue ? (
<p className={cellClasses}>{displayValue}</p>
) : (
<p className={placeholderClasses}>{tableNotKnownTrans}</p>
);
} else {
cellContent = (
<p className={placeholderClasses}>{tableNotKnownTrans}</p>
);
}
return (
<div className="grid grid-cols-[1fr_1fr_auto] items-center w-full gap-4">
<p className={cellClasses}>{field.label}</p>
{cellContent}
{!currentHasChildren && <div className="w-[16px]" />}
</div>
);
};
const childItems = currentValue[field.key];
const hasChildItems =
hasChildren && Array.isArray(childItems) && childItems.length > 0;
return (
<AccordionItem
key={accordionKey}
value={accordionKey}
className="border-none"
>
{hasChildren ? (
<AccordionTrigger
className={cn(
"gap-0 px-4",
{ "rounded-none border-b mb-2": hasBorders },
{ "py-2": !hasBorders },
)}
>
{rowContent(hasChildren)}
</AccordionTrigger>
) : (
<div className="px-4 py-2">{rowContent()}</div>
)}
{hasChildItems && (
<AccordionContent className="px-4">
<div className="space-y-1">
{(childItems as ChildItem[]).map((childItem) => (
<div key={String(childItem.id)} className="border rounded-md">
{RenderRows(
childItem,
field.children ?? [],
false,
`${accordionKey}-${String(childItem.id)}`,
)}
</div>
))}
</div>
</AccordionContent>
)}
</AccordionItem>
);
});
};
interface DynamicPageDetailsProps {
value: Record<string, unknown>;
config: ConfigField[];
}
export default function DynamicPageDetails({
value,
config,
}: DynamicPageDetailsProps) {
const transLabel = str(useTrans("uwil-dynamic-pages::trans.attributes.label"))
.ucFirst()
.value();
const transValue = str(useTrans("uwil-dynamic-pages::trans.attributes.value"))
.ucFirst()
.value();
return (
<Accordion type="multiple" className="w-full divide-y">
<div className="grid grid-cols-[1fr_1fr_auto] text-sm font-medium border-b gap-4 px-4 py-2">
<div>{transLabel}</div>
<div>{transValue}</div>
<div className="w-[16px]" />
</div>
{RenderRows(value, config, true)}
</Accordion>
);
}