- Updated translation utility to improve consistency and reduce redundancy. - Removed deprecated useTrans hook and related code. - Introduced a new Zustand store for managing translations and locale settings. - Cleaned up unused hooks and files related to localization.
235 lines
7.2 KiB
TypeScript
235 lines
7.2 KiB
TypeScript
import { usePage } from "@inertiajs/react";
|
|
import { CheckIcon, XIcon } from "lucide-react";
|
|
import { type ReactNode } from "react";
|
|
|
|
import type {
|
|
ConfigField,
|
|
DynamicFieldType,
|
|
DynamicPageModelWithRelations,
|
|
} from "@/back/types/dynamic-pages";
|
|
import SafeHtml from "@/components/safe-html";
|
|
import AppLayout from "@/front/Layouts/AppLayout";
|
|
import { cn } from "@/lib/utils";
|
|
import { AppEnv } from "@/types";
|
|
import Head from "../Components/Base/Head";
|
|
import PageHeader from "../Components/Theme/PageHeader";
|
|
import Section from "../Components/Theme/Section";
|
|
|
|
interface DynamicPageDefaultProps {
|
|
dynamicPageModel: DynamicPageModelWithRelations;
|
|
}
|
|
|
|
export default function DynamicPageDefault({
|
|
dynamicPageModel,
|
|
}: Readonly<DynamicPageDefaultProps>) {
|
|
const { url, locale, fallbackLocale } = usePage().props.appEnv as AppEnv;
|
|
const enumDynamicFieldTypes = usePage().props
|
|
.enumDynamicFieldTypes as DynamicFieldType;
|
|
|
|
const currentLocaleContent =
|
|
dynamicPageModel.content?.[locale] ??
|
|
dynamicPageModel.content?.[fallbackLocale];
|
|
|
|
const currentLocaleConfig = dynamicPageModel.config
|
|
? (dynamicPageModel.config.config[locale] ??
|
|
dynamicPageModel.config.config[fallbackLocale])
|
|
: null;
|
|
|
|
const currentLocaleTitle =
|
|
dynamicPageModel.title[locale] ?? dynamicPageModel.title[fallbackLocale];
|
|
|
|
const joinUrl = (...parts: string[]): string =>
|
|
parts.map((part) => part.replace(/^\/+|\/+$/g, "")).join("/");
|
|
|
|
const renderField = (key: string, value: unknown): ReactNode => {
|
|
const configField = currentLocaleConfig?.find(
|
|
(item: ConfigField) => item.key === key,
|
|
);
|
|
|
|
if (!configField) {
|
|
return <span>{String(value)}</span>;
|
|
}
|
|
|
|
switch (configField.type) {
|
|
// Wysiwyg case.
|
|
case enumDynamicFieldTypes.wysiwyg.value:
|
|
return <SafeHtml html={String(value)} />;
|
|
// Number case.
|
|
case enumDynamicFieldTypes.number.value:
|
|
return <span>{Number(value).toLocaleString(locale)}</span>;
|
|
// Boolean case.
|
|
case enumDynamicFieldTypes.boolean.value:
|
|
return (
|
|
<span className={value ? "text-green-600" : "text-red-600"}>
|
|
{value ? <CheckIcon /> : <XIcon />}
|
|
</span>
|
|
);
|
|
// Link case.
|
|
case enumDynamicFieldTypes.url.value:
|
|
return (
|
|
<a
|
|
href={String(value)}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="underline text-blue-600"
|
|
>
|
|
{String(value)}
|
|
</a>
|
|
);
|
|
// E-mail case.
|
|
case enumDynamicFieldTypes.email.value:
|
|
return (
|
|
<a
|
|
href={`mailto:${String(value)}`}
|
|
className="underline text-blue-600"
|
|
>
|
|
{String(value)}
|
|
</a>
|
|
);
|
|
// Image case.
|
|
case enumDynamicFieldTypes.image.value:
|
|
return (
|
|
<img
|
|
src={joinUrl(url, String(value))}
|
|
alt={key}
|
|
className="max-w-xs max-h-48 object-contain"
|
|
/>
|
|
);
|
|
// File case.
|
|
case enumDynamicFieldTypes.file.value:
|
|
return (
|
|
<a
|
|
href={joinUrl(url, String(value))}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="underline text-blue-600"
|
|
>
|
|
{String(value).split("/").pop()}
|
|
</a>
|
|
);
|
|
// List case.
|
|
case enumDynamicFieldTypes.list.value: {
|
|
const currentValue = value as {
|
|
order: string;
|
|
[key: string]: string;
|
|
}[];
|
|
return (
|
|
<ul>
|
|
{currentValue.map((item, index) => (
|
|
<li
|
|
key={item.order}
|
|
className={cn({
|
|
"border-b pb-2 mb-2": index < currentValue.length - 1,
|
|
})}
|
|
>
|
|
{Object.entries(item).map((content) => (
|
|
<ol key="itemContent">
|
|
{content[0]}: {content[1]}
|
|
</ol>
|
|
))}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
);
|
|
}
|
|
// Vimeo ID case.
|
|
case enumDynamicFieldTypes.vimeoid.value:
|
|
return (
|
|
<a
|
|
href={`https://player.vimeo.com/video/${String(value)}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="underline text-blue-600"
|
|
>
|
|
{String(value).split("/").pop()}
|
|
</a>
|
|
);
|
|
// Youtube ID case.
|
|
case enumDynamicFieldTypes.youtubeid.value:
|
|
return (
|
|
<a
|
|
href={`https://www.youtube.com/embed/${String(value)}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="underline text-blue-600"
|
|
>
|
|
{String(value).split("/").pop()}
|
|
</a>
|
|
);
|
|
// Date case.
|
|
case enumDynamicFieldTypes.date.value:
|
|
return (
|
|
<span>{new Date(String(value)).toLocaleDateString(locale)}</span>
|
|
);
|
|
// Color case.
|
|
case enumDynamicFieldTypes.color.value:
|
|
return (
|
|
<span className="flex items-center gap-2">
|
|
<span
|
|
className="inline-block w-5 h-5 rounded-sm border"
|
|
style={{ backgroundColor: String(value) }}
|
|
/>
|
|
{String(value)}
|
|
</span>
|
|
);
|
|
// Model case.
|
|
case enumDynamicFieldTypes.model.value: {
|
|
const reservedValue = currentLocaleContent?.[
|
|
`reserved_${key}` as keyof typeof currentLocaleContent
|
|
] as unknown as string;
|
|
return (
|
|
<span>
|
|
{String(value)}{" "}
|
|
<span className="text-gray-500">({reservedValue})</span>
|
|
</span>
|
|
);
|
|
}
|
|
// Static route case.
|
|
case enumDynamicFieldTypes.staticroute.value:
|
|
return (value as string | null) ? (
|
|
<a href={String(value)} className="underline text-blue-600">
|
|
{String(value)}
|
|
</a>
|
|
) : (
|
|
<p>/</p>
|
|
);
|
|
// Default for string/textarea.
|
|
default:
|
|
return <span>{String(value)}</span>;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<AppLayout>
|
|
<Head seoTitle={currentLocaleTitle} />
|
|
<PageHeader
|
|
title={{
|
|
text: currentLocaleTitle,
|
|
}}
|
|
description={{
|
|
text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo mollitia minus cum obcaecati necessitatibus? Molestias, harum. Sapiente eos ipsa ullam esse laborum id libero consectetur voluptate labore eaque. Optio, corporis?",
|
|
}}
|
|
/>
|
|
<Section className="text-start tiptap">
|
|
{currentLocaleConfig && currentLocaleContent ? (
|
|
<table className="w-1/2 mx-auto border-collapse">
|
|
<tbody>
|
|
{Object.entries(currentLocaleContent)
|
|
.filter(([key]) => !key.startsWith("reserved_model_"))
|
|
.map(([key, value]) => (
|
|
<tr key={key} className="border-b">
|
|
<th className="text-left font-medium p-2 w-1/4 align-top">
|
|
{key}
|
|
</th>
|
|
<td className="p-2">{renderField(key, value)}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
) : (
|
|
<SafeHtml html={String(currentLocaleContent)} />
|
|
)}
|
|
</Section>
|
|
</AppLayout>
|
|
);
|
|
}
|