|
|
@@ -2,53 +2,106 @@
|
|
|
import React from "react";
|
|
|
import { useState } from "react";
|
|
|
import { useEffect } from "react";
|
|
|
-import {
|
|
|
- parseDate,
|
|
|
- CalendarDate,
|
|
|
- DateValue
|
|
|
-} from "@internationalized/date";
|
|
|
+import { parseDate, CalendarDate, DateValue } from "@internationalized/date";
|
|
|
import Link from "next/link";
|
|
|
import { DatePicker } from "@heroui/date-picker";
|
|
|
+import CustomerAddressPhone from "../../address/new/_components/CustomerAddressPhone";
|
|
|
+import { useForm, FormProvider,Controller } from "react-hook-form";
|
|
|
+import { useMutation, useApolloClient } from "@apollo/client/react";
|
|
|
+import { CustomerProfileResult } from "@components/customer/information/information";
|
|
|
+import {GET_CUSTOMER_PROFILE,UPDATE_CUSTOMER_PROFILE} from "@/graphql";
|
|
|
+import { UpdateCustomerProfileVars } from "@components/customer/information/information";
|
|
|
+import { useCustomToast } from "@/utils/hooks/useToast";
|
|
|
const AccountEditPage = () => {
|
|
|
interface UserForm {
|
|
|
- firstname: string;
|
|
|
- lastname: string;
|
|
|
- date: CalendarDate | string;
|
|
|
- email: string;
|
|
|
- current_password: string;
|
|
|
- password: string;
|
|
|
-}
|
|
|
- // 统一表单对象
|
|
|
- const [form, setForm] = useState<UserForm>({
|
|
|
- firstname: "",
|
|
|
- lastname: "",
|
|
|
- date: parseDate("2026-05-12"),
|
|
|
- email: "",
|
|
|
- current_password: "",
|
|
|
- password: "",
|
|
|
+ firstname: string;
|
|
|
+ lastname: string;
|
|
|
+ date: CalendarDate | string |null;
|
|
|
+ email: string;
|
|
|
+ current_password: string;
|
|
|
+ password: string;
|
|
|
+ phone: string;
|
|
|
+ }
|
|
|
+
|
|
|
+ const { showToast } = useCustomToast();
|
|
|
+ const addressForm = useForm<UserForm>({
|
|
|
+ mode: "onBlur",
|
|
|
+ reValidateMode: "onChange",
|
|
|
+ defaultValues: {
|
|
|
+ firstname: "",
|
|
|
+ lastname: "",
|
|
|
+ date: parseDate("2026-05-12"),
|
|
|
+ email: "",
|
|
|
+ current_password: "",
|
|
|
+ password: "",
|
|
|
+ phone: "",
|
|
|
+ },
|
|
|
});
|
|
|
+ const [hasBirthDate, setHasBirthDate] = useState(false);
|
|
|
+ const { reset,setValue } = addressForm;
|
|
|
+ const apolloClient = useApolloClient();
|
|
|
+ const fetchProducts = async () => {
|
|
|
+ try {
|
|
|
+ const res = await apolloClient.query<CustomerProfileResult>({
|
|
|
+ query: GET_CUSTOMER_PROFILE,
|
|
|
+ });
|
|
|
+ // 空值判断
|
|
|
+ if (!res.data) {
|
|
|
+ console.warn("没有返回用户数据");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- // 统一处理所有input变化
|
|
|
- const handleChange = (e:any) => {
|
|
|
- const { name, value } = e.target;
|
|
|
- setForm((prev) => ({
|
|
|
- ...prev,
|
|
|
- [name]: value,
|
|
|
- }));
|
|
|
- };
|
|
|
+ const profile = res.data.readCustomerProfile;
|
|
|
+
|
|
|
+ //接口数据映射到表单,reset回填
|
|
|
+ reset({
|
|
|
+ firstname: profile.firstName,
|
|
|
+ lastname: profile.lastName,
|
|
|
+ email: profile.email,
|
|
|
+ phone: profile.phone,
|
|
|
+ date: profile.dateOfBirth||null,
|
|
|
+ current_password: "",
|
|
|
+ password: "",
|
|
|
+ });
|
|
|
+ if(profile.dateOfBirth){
|
|
|
+ setHasBirthDate(true);
|
|
|
+ }else{
|
|
|
+ setHasBirthDate(false);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("fetch products error", error);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ useEffect(() => {
|
|
|
+
|
|
|
+ fetchProducts();
|
|
|
+
|
|
|
+ }, []);
|
|
|
+ const [updateProfileMutate] = useMutation(UPDATE_CUSTOMER_PROFILE, {
|
|
|
+ onCompleted: (res) => {
|
|
|
+ console.log("更新结果", res);
|
|
|
+ const result = res.createCustomerProfileUpdate.customerProfileUpdate;
|
|
|
+ if (result.success) {
|
|
|
+ // 更新成功后的逻辑,提示、刷新表单等
|
|
|
+ showToast(result.message, "success");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onError: (err) => {
|
|
|
+ console.error("更新用户资料失败:", err);
|
|
|
+ showToast(err.message, "danger");
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
// 1. 定义状态:控制修改密码显示隐藏
|
|
|
const [showContent, setShowContent] = useState(false);
|
|
|
// 日期选择方法
|
|
|
- const handleDateChange = (dateValue: DateValue | null) => {
|
|
|
+ const handleDateChange = (dateValue: DateValue | null) => {
|
|
|
if (!dateValue) return;
|
|
|
|
|
|
// 把 CalendarDate 转成 标准字符串:YYYY-MM-DD
|
|
|
const formattedDate = `${dateValue.year}-${String(dateValue.month).padStart(2, "0")}-${String(dateValue.day).padStart(2, "0")}`;
|
|
|
-
|
|
|
- setForm((prev) => ({
|
|
|
- ...prev,
|
|
|
- date: formattedDate, // 现在是正常字符串了
|
|
|
- }));
|
|
|
+ return formattedDate
|
|
|
+
|
|
|
};
|
|
|
// 勾选框改变时:隐藏时 → 清空密码
|
|
|
const handleCheckChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
@@ -57,88 +110,135 @@ const AccountEditPage = () => {
|
|
|
|
|
|
// ✅ 关键:不勾选时清空密码字段
|
|
|
if (!isChecked) {
|
|
|
- setForm((prev) => ({
|
|
|
- ...prev,
|
|
|
- current_password: "",
|
|
|
- password: "",
|
|
|
- }));
|
|
|
+ setValue("current_password", "");
|
|
|
+ setValue("password", "");
|
|
|
}
|
|
|
};
|
|
|
- // 统一提交
|
|
|
- const handleSubmit = (e:any) => {
|
|
|
- e.preventDefault();
|
|
|
- // 直接拿form所有数据
|
|
|
- console.log("表单数据:", form);
|
|
|
- };
|
|
|
- useEffect(() => {
|
|
|
- // console.log("form 已更新 →", form);
|
|
|
- }, [form]);
|
|
|
+
|
|
|
+
|
|
|
+ // 表单提交
|
|
|
+ const addressFormOnSubmit = async (formData: UserForm) => {
|
|
|
+ console.log("最终提交给后端的数据:", formData);
|
|
|
+ const inputPayload: UpdateCustomerProfileVars["input"] = {
|
|
|
+ firstName: formData.firstname,
|
|
|
+ lastName: formData.lastname,
|
|
|
+ email: formData.email,
|
|
|
+ phone: formData.phone || null,
|
|
|
+ dateOfBirth: formData.date || null,
|
|
|
+ };
|
|
|
+ // 勾选修改密码时附加密码字段
|
|
|
+ if (showContent) {
|
|
|
+ inputPayload.current_password = formData.current_password;
|
|
|
+ inputPayload.password = formData.password;
|
|
|
+ }
|
|
|
+ await updateProfileMutate({
|
|
|
+ variables: {
|
|
|
+ input: inputPayload,
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ const {
|
|
|
+ register,
|
|
|
+ handleSubmit,
|
|
|
+ control,
|
|
|
+ formState: { errors },
|
|
|
+ } = addressForm;
|
|
|
return (
|
|
|
<div className="w-full h-full">
|
|
|
- <form className="overflow-hidden" onSubmit={handleSubmit}>
|
|
|
- <div className="bg-[#fff] pr-2.5 pl-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
|
|
|
- <Link
|
|
|
- className="absolute left-2.5 text-2xl inline-block top-1/2 -translate-y-1/2"
|
|
|
- href="/customer/account"
|
|
|
- aria-label="Go to customer account page"
|
|
|
- >
|
|
|
+ <FormProvider {...addressForm}>
|
|
|
+ <form className="overflow-hidden" onSubmit={handleSubmit(addressFormOnSubmit)} >
|
|
|
+ <div className=" text-black py-4 px-4 flex items-center border-b border-[#F9F9F9FF]">
|
|
|
+ <Link
|
|
|
+ className="mr-4"
|
|
|
+ href="/customer/account"
|
|
|
+ aria-label="Go to customer account page"
|
|
|
+ >
|
|
|
<svg
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
|
- width="24"
|
|
|
- height="24"
|
|
|
+ className="h-6 w-6"
|
|
|
+ fill="#000"
|
|
|
viewBox="0 0 24 24"
|
|
|
- fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
>
|
|
|
<path
|
|
|
- stroke="rgba(0, 0, 0, 1)"
|
|
|
- stroke-width="1.2"
|
|
|
- stroke-linejoin="round"
|
|
|
- stroke-linecap="round"
|
|
|
- d="M15 18L9 12L15 6"
|
|
|
- ></path>
|
|
|
+ strokeLinecap="round"
|
|
|
+ strokeLinejoin="round"
|
|
|
+ strokeWidth={2}
|
|
|
+ fill="none"
|
|
|
+ d="M15 19l-7-7 7-7"
|
|
|
+ />
|
|
|
</svg>
|
|
|
</Link>
|
|
|
- <span className="vipReturnTitles">Account Information</span>
|
|
|
+ <h1 className="text-lg w-full text-center font-normal">
|
|
|
+ Information
|
|
|
+ </h1>
|
|
|
</div>
|
|
|
<input name="form_key" type="hidden" value="kW7wucXHATeeem75"></input>
|
|
|
- <div className="bg-[#f8f8f8] p-[20px_10px]">
|
|
|
- <div className="text-start">*Name</div>
|
|
|
+ <div className=" p-[20px_10px]">
|
|
|
+ <div className="text-start mb-3">*Name</div>
|
|
|
<div className="flex justify-between items-center">
|
|
|
<input
|
|
|
- name="firstname" // 必须和state字段对应
|
|
|
- value={form.firstname}
|
|
|
- onChange={handleChange}
|
|
|
+ // name="firstname" // 必须和state字段对应
|
|
|
+
|
|
|
+ {...register("firstname", {
|
|
|
+ required: "First name is required",
|
|
|
+ })}
|
|
|
placeholder="firstname"
|
|
|
- className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
|
|
|
+ className="w-43 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
|
|
|
/>
|
|
|
<input
|
|
|
- name="lastname" // 必须和state字段对应
|
|
|
- value={form.lastname}
|
|
|
- onChange={handleChange}
|
|
|
+ // name="lastname" // 必须和state字段对应
|
|
|
+
|
|
|
+ {...register("lastname", {
|
|
|
+ required: "Last name is required",
|
|
|
+ })}
|
|
|
placeholder="lastname"
|
|
|
- className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
|
|
|
+ className="w-43 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
|
|
|
/>
|
|
|
</div>
|
|
|
<div className="mt-7.5">
|
|
|
+ <Controller
|
|
|
+ name="date"
|
|
|
+ control={control}
|
|
|
+ rules={{
|
|
|
+ required: "date name is required",
|
|
|
+ }}
|
|
|
+
|
|
|
+ render={({ field: { onChange, value } }) => (
|
|
|
<DatePicker
|
|
|
size="md"
|
|
|
- onChange={handleDateChange}
|
|
|
+ label="Date Of Birth"
|
|
|
+ isRequired={hasBirthDate}
|
|
|
+ onChange={(dateValue) => {
|
|
|
+ // HeroUI DatePicker 返回 CalendarDate 对象,转成字符串 yyyy‑MM‑dd
|
|
|
+ onChange(handleDateChange(dateValue) );
|
|
|
+
|
|
|
+
|
|
|
+ }}
|
|
|
radius="sm"
|
|
|
- className="bg-[#fff]"
|
|
|
+ className="bg-white"
|
|
|
+ description={"This cannot be modified. Please fill in carefully."}
|
|
|
labelPlacement={"outside"}
|
|
|
- showMonthAndYearPickers
|
|
|
+ showMonthAndYearPickers={false}
|
|
|
aria-label="date picker"
|
|
|
/>
|
|
|
+ )}
|
|
|
+ />
|
|
|
{/* label={"Date of Birth"} */}
|
|
|
</div>
|
|
|
+ <CustomerAddressPhone />
|
|
|
<div className="mt-7.5">
|
|
|
- <label htmlFor="email">Email Address*</label>
|
|
|
+ <label >Email Address*</label>
|
|
|
<input
|
|
|
- name="email" // 必须和state字段对应
|
|
|
- value={form.email}
|
|
|
- onChange={handleChange}
|
|
|
+ // name="email" // 必须和state字段对应
|
|
|
+
|
|
|
+ {...register("email", {
|
|
|
+ required: "email name is required",
|
|
|
+ })}
|
|
|
placeholder="email"
|
|
|
- className="w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
|
|
|
+ className="w-full mt-3 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
|
|
|
/>
|
|
|
</div>
|
|
|
{/* 选择框(开关)*/}
|
|
|
@@ -155,30 +255,35 @@ const AccountEditPage = () => {
|
|
|
<div>
|
|
|
<input
|
|
|
type="password"
|
|
|
- name="current_password" // name对应key
|
|
|
- value={form.current_password}
|
|
|
- onChange={handleChange}
|
|
|
+ // name="current_password" // name对应key
|
|
|
+
|
|
|
+ {...register("current_password", {
|
|
|
+ required: "Password is required",
|
|
|
+ })}
|
|
|
placeholder="Old password"
|
|
|
- className="mt-7.5 w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
|
|
|
+ className="mt-7.5 w-full h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
|
|
|
/>
|
|
|
<input
|
|
|
type="password"
|
|
|
- name="password" // name对应key
|
|
|
- value={form.password}
|
|
|
- onChange={handleChange}
|
|
|
+ // name="password" // name对应key
|
|
|
+
|
|
|
+ {...register("password", {
|
|
|
+ required: "Password is required",
|
|
|
+ })}
|
|
|
placeholder="* New Password"
|
|
|
- className=" mt-7.5 w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
|
|
|
+ className=" mt-7.5 w-full h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
|
|
|
/>
|
|
|
</div>
|
|
|
)}
|
|
|
<button
|
|
|
- className="w-full h-11 leading-11 bg-[#1bbc9b] text-sm font-normal text-center mt-7.5"
|
|
|
+ className="w-full h-12 leading-12 rounded-[26px] bg-[rgba(3,97,65,1)] text-base text-white font-bold text-center mt-7.5"
|
|
|
type="submit"
|
|
|
>
|
|
|
- Save Changes
|
|
|
+ Save
|
|
|
</button>
|
|
|
</div>
|
|
|
</form>
|
|
|
+ </FormProvider>
|
|
|
</div>
|
|
|
);
|
|
|
};
|