import React, { useCallback, useEffect, useRef, useState } from "react"; import { PlusCircle, GripVertical, Trash2, Eye, EyeOff } from "lucide-react"; import { Reorder, AnimatePresence, motion } from "framer-motion"; import { useTranslations } from "@/i18n/compat/client"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import PhotoUpload from "@/components/shared/PhotoSelector"; import IconSelector from "../IconSelector"; import AlignSelector from "./AlignSelector"; import Field from "../Field"; import { cn } from "@/lib/utils"; import { DEFAULT_FIELD_ORDER } from "@/config"; import { useResumeStore } from "@/store/useResumeStore"; import { BasicFieldType, CustomFieldType } from "@/types/resume"; import { generateUUID } from "@/utils/uuid"; interface CustomFieldProps { field: CustomFieldType; onUpdate: (field: CustomFieldType) => void; onDelete: (id: string) => void; onReorderEnd: () => void; } const itemAnimations = { initial: { opacity: 0, y: 0 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: 0 }, transition: { type: "spring", stiffness: 500, damping: 50, mass: 1 }, }; const CustomField: React.FC = ({ field, onUpdate, onDelete, onReorderEnd, }) => { const t = useTranslations("workbench.basicPanel"); return (
onUpdate({ ...field, icon: value })} />
onUpdate({ ...field, label: value, }) } placeholder={t("customFields.placeholders.label")} className={cn( "bg-background/50", "border-border", "focus:border-primary", "placeholder-muted-foreground" )} /> onUpdate({ ...field, value: value, }) } placeholder={t("customFields.placeholders.value")} className={cn( "bg-background/50", "border-border", "focus:border-primary", "placeholder-muted-foreground" )} />
onUpdate({ ...field, displayLabel: checked, }) } /> {t("customFields.displayLabel")}
); }; const BasicPanel: React.FC = () => { const { activeResume, updateBasicInfo } = useResumeStore(); const { basic } = activeResume || {}; const [customFields, setCustomFields] = useState( basic?.customFields?.map((field) => ({ ...field, visible: field.visible ?? true, })) || [] ); const [basicFields, setBasicFields] = useState(() => { if (!basic?.fieldOrder) { return DEFAULT_FIELD_ORDER; } return basic.fieldOrder.map((field) => ({ ...field, visible: field.visible ?? true, })); }); const basicFieldsRef = useRef(basicFields); const customFieldsRef = useRef(customFields); const t = useTranslations("workbench.basicPanel"); useEffect(() => { basicFieldsRef.current = basicFields; }, [basicFields]); useEffect(() => { customFieldsRef.current = customFields; }, [customFields]); const handleBasicReorder = (newOrder: BasicFieldType[]) => { basicFieldsRef.current = newOrder; setBasicFields(newOrder); }; const commitBasicReorder = useCallback(() => { updateBasicInfo({ fieldOrder: basicFieldsRef.current, }); }, [updateBasicInfo]); const toggleFieldVisibility = (fieldId: string, isVisible: boolean) => { const newFields = basicFields.map((field) => field.id === fieldId ? { ...field, visible: isVisible } : field ); setBasicFields(newFields); updateBasicInfo({ ...basic, fieldOrder: newFields, }); }; const deleteBasicField = (fieldId: string) => { const fieldToDelete = basicFields.find((field) => field.id === fieldId); if ( fieldToDelete && (fieldToDelete.key === "name" || fieldToDelete.key === "title") ) { return; } const updatedFields = basicFields.filter((field) => field.id !== fieldId); setBasicFields(updatedFields); updateBasicInfo({ ...basic, fieldOrder: updatedFields, }); }; const addCustomField = () => { const fieldToAdd: CustomFieldType = { id: generateUUID(), label: "", value: "", icon: "User", visible: true, displayLabel: false, }; const updatedFields = [...customFields, fieldToAdd]; setCustomFields(updatedFields); updateBasicInfo({ ...basic, customFields: updatedFields, }); }; const updateCustomField = (updatedField: CustomFieldType) => { const updatedFields = customFields.map((field) => field.id === updatedField.id ? updatedField : field ); setCustomFields(updatedFields); updateBasicInfo({ ...basic, customFields: updatedFields, }); }; const deleteCustomField = (id: string) => { const updatedFields = customFields.filter((field) => field.id !== id); setCustomFields(updatedFields); updateBasicInfo({ ...basic, customFields: updatedFields, }); }; const handleCustomFieldsReorder = (newOrder: CustomFieldType[]) => { customFieldsRef.current = newOrder; setCustomFields(newOrder); }; const commitCustomFieldsReorder = useCallback(() => { updateBasicInfo({ customFields: customFieldsRef.current, }); }, [updateBasicInfo]); const renderBasicField = (field: BasicFieldType) => { const selectedIcon = basic?.icons?.[field.key] || "User"; return ( {field.key !== "name" && field.key !== "title" && (
)}
{field.key !== "name" && field.key !== "title" && ( { updateBasicInfo({ ...basic, icons: { ...(basic?.icons || {}), [field.key]: value, }, }); }} /> )}
{t(`basicFields.${field.key}`)}
updateBasicInfo({ ...basic, [field.key]: value, }) } placeholder={`请输入${field.label}`} type={field.type} />
{field.key !== "name" && field.key !== "title" && ( )}
); }; return (

{t("layout")}

updateBasicInfo({ ...basic, layout: value, }) } />

{t("title")}

{t("basicField")} {basicFields.map((field) => renderBasicField(field))} {t("customField")} {Array.isArray(customFields) && customFields.map((field) => ( ))}
{t("githubContributions")} updateBasicInfo({ ...basic, githubContributionsVisible: checked, }) } />
Access Token
updateBasicInfo({ ...basic, githubKey: e.target.value, }) } />
UseName
updateBasicInfo({ ...basic, githubUseName: e.target.value, }) } />
); }; export default BasicPanel;