Files
magic-resume/src/components/templates/modern/sections/BaseInfo.tsx
T
2026-03-04 22:35:51 +08:00

100 lines
5.5 KiB
TypeScript

import React from "react";
import { motion } from "framer-motion";
import * as Icons from "lucide-react";
import { cn, formatDateString } from "@/lib/utils";
import { BasicInfo, getBorderRadiusValue, GlobalSettings } from "@/types/resume";
import { ResumeTemplate } from "@/types/template";
import SectionWrapper from "../../shared/SectionWrapper";
import { useTranslations, useLocale } from "@/i18n/compat/client";
interface BaseInfoProps {
basic: BasicInfo | undefined;
globalSettings: GlobalSettings | undefined;
template?: ResumeTemplate;
}
/**
* Modern template BaseInfo — designed for sidebar (white text on theme color background).
*/
const BaseInfo = ({ basic = {} as BasicInfo, globalSettings, template }: BaseInfoProps) => {
const t = useTranslations("workbench");
const locale = useLocale();
const useIconMode = globalSettings?.useIconMode ?? false;
const getIcon = (iconName: string | undefined) => {
const IconComponent = Icons[iconName as keyof typeof Icons] as React.ElementType;
return IconComponent ? <IconComponent className="w-4 h-4" /> : null;
};
const getOrderedFields = React.useMemo(() => {
if (!basic.fieldOrder) {
return [{ key: "email", value: basic.email, icon: basic.icons?.email || "Mail", label: "电子邮箱", visible: true, custom: false }]
.filter((item) => Boolean(item.value && item.visible));
}
return basic.fieldOrder
.filter((field) => field.visible !== false && field.key !== "name" && field.key !== "title")
.map((field) => ({
key: field.key, value: field.key === "birthDate" && basic[field.key] ? formatDateString(basic[field.key] as string, locale) : (basic[field.key] as string),
icon: basic.icons?.[field.key] || "User", label: field.label, visible: field.visible, custom: field.custom,
}))
.filter((item) => Boolean(item.value));
}, [basic]);
const allFields = [
...getOrderedFields,
...(basic.customFields?.filter((field) => field.visible !== false).map((field) => ({
key: field.id, value: field.value, icon: field.icon, label: field.label, visible: true, custom: true,
})) || []),
];
const nameField = basic.fieldOrder?.find((f) => f.key === "name") || { key: "name", visible: true };
const titleField = basic.fieldOrder?.find((f) => f.key === "title") || { key: "title", visible: true };
const PhotoComponent = basic.photo && basic.photoConfig?.visible && (
<motion.div layout="position">
<div style={{ width: `${basic.photoConfig?.width || 100}px`, height: `${basic.photoConfig?.height || 100}px`, borderRadius: getBorderRadiusValue(basic.photoConfig || { borderRadius: "none", customBorderRadius: 0 }), overflow: "hidden" }}>
<img src={basic.photo} alt={`${basic.name}'s photo`} className="w-full h-full object-cover" />
</div>
</motion.div>
);
return (
<SectionWrapper sectionId="basic">
<div className="flex flex-col items-center gap-3">
<div className="flex flex-col items-center gap-4">
{PhotoComponent}
<div className="flex flex-col text-center min-w-0" style={{ color: "#fff" }}>
{nameField.visible !== false && basic[nameField.key] && (
<motion.h1 layout="position" className="font-bold whitespace-pre-wrap break-words" style={{ fontSize: "30px", color: "#fff" }}>{basic[nameField.key] as string}</motion.h1>
)}
{titleField.visible !== false && basic[titleField.key] && (
<motion.h2 layout="position" className="whitespace-pre-wrap break-words" style={{ fontSize: "18px", color: "#fff" }}>{basic[titleField.key] as string}</motion.h2>
)}
</div>
</div>
<motion.div layout="position" className="w-full flex flex-col gap-2"
style={{ fontSize: `${globalSettings?.baseFontSize || 14}px`, color: "#fff" }}>
{allFields.map((item) => (
<motion.div key={item.key} className="flex items-center whitespace-nowrap overflow-hidden text-baseFont" style={{ width: "100%", color: "#fff" }}>
{useIconMode ? (
<div className="flex items-center gap-1" style={{ color: "#fff" }}>
{getIcon(item.icon)}
{item.key === "email" ? <a href={`mailto:${item.value}`} className="underline" style={{ color: "#fff" }}>{item.value}</a> : <span style={{ color: "#fff" }}>{item.value}</span>}
</div>
) : (
<div className="flex items-center gap-2 overflow-hidden" style={{ color: "#fff" }}>
{!item.custom && <span style={{ color: "#fff" }}>{t(`basicPanel.basicFields.${item.key}`)}:</span>}
{item.custom && <span style={{ color: "#fff" }}>{item.label}:</span>}
<span className="truncate" suppressHydrationWarning style={{ color: "#fff" }}>{item.value}</span>
</div>
)}
</motion.div>
))}
</motion.div>
</div>
</SectionWrapper>
);
};
export default BaseInfo;