157 lines
4.5 KiB
TypeScript
157 lines
4.5 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import { useEffect, useState, useCallback } from "react";
|
||
|
|
import { useAuthStore } from "@/features/auth/services/auth.store";
|
||
|
|
import { accountService } from "@/features/account/services/account.service";
|
||
|
|
import { logger } from "@customer-portal/logging";
|
||
|
|
|
||
|
|
// Use centralized profile types
|
||
|
|
import type { ProfileFormData } from "@customer-portal/domain";
|
||
|
|
export type { ProfileFormData };
|
||
|
|
|
||
|
|
// Address type moved to domain package
|
||
|
|
import type { Address } from "@customer-portal/domain";
|
||
|
|
|
||
|
|
export function useProfileData() {
|
||
|
|
const { user } = useAuthStore();
|
||
|
|
const [loading, setLoading] = useState(true);
|
||
|
|
const [error, setError] = useState<string | null>(null);
|
||
|
|
const [isSavingProfile, setIsSavingProfile] = useState(false);
|
||
|
|
const [isSavingAddress, setIsSavingAddress] = useState(false);
|
||
|
|
const [billingInfo, setBillingInfo] = useState<{ address: Address } | null>(null);
|
||
|
|
|
||
|
|
const [formData, setFormData] = useState<ProfileFormData>({
|
||
|
|
firstName: user?.firstName || "",
|
||
|
|
lastName: user?.lastName || "",
|
||
|
|
email: user?.email || "",
|
||
|
|
phone: user?.phone || "",
|
||
|
|
});
|
||
|
|
|
||
|
|
const [addressData, setAddress] = useState<Address>({
|
||
|
|
street: "",
|
||
|
|
streetLine2: "",
|
||
|
|
city: "",
|
||
|
|
state: "",
|
||
|
|
postalCode: "",
|
||
|
|
country: "",
|
||
|
|
});
|
||
|
|
|
||
|
|
const fetchBillingInfo = useCallback(async () => {
|
||
|
|
try {
|
||
|
|
setLoading(true);
|
||
|
|
const address = await accountService.getAddress().catch(() => null);
|
||
|
|
if (address)
|
||
|
|
setBillingInfo({
|
||
|
|
address: {
|
||
|
|
street: address.street || "",
|
||
|
|
streetLine2: address.streetLine2 || "",
|
||
|
|
city: address.city || "",
|
||
|
|
state: address.state || "",
|
||
|
|
postalCode: address.postalCode || "",
|
||
|
|
country: address.country || "",
|
||
|
|
},
|
||
|
|
});
|
||
|
|
if (address)
|
||
|
|
setAddress({
|
||
|
|
street: address.street || "",
|
||
|
|
streetLine2: address.streetLine2 || "",
|
||
|
|
city: address.city || "",
|
||
|
|
state: address.state || "",
|
||
|
|
postalCode: address.postalCode || "",
|
||
|
|
country: address.country || "",
|
||
|
|
});
|
||
|
|
} catch (err) {
|
||
|
|
setError(err instanceof Error ? err.message : "Failed to load address information");
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
void fetchBillingInfo();
|
||
|
|
}, [fetchBillingInfo]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (user) {
|
||
|
|
setFormData({
|
||
|
|
firstName: user.firstName || "",
|
||
|
|
lastName: user.lastName || "",
|
||
|
|
email: user.email || "",
|
||
|
|
phone: user.phone || "",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}, [user]);
|
||
|
|
|
||
|
|
const saveProfile = async (next: ProfileFormData) => {
|
||
|
|
setIsSavingProfile(true);
|
||
|
|
try {
|
||
|
|
const { tokens } = useAuthStore.getState();
|
||
|
|
if (!tokens?.accessToken) throw new Error("Authentication required");
|
||
|
|
const response = await fetch("/api/me", {
|
||
|
|
method: "PATCH",
|
||
|
|
headers: {
|
||
|
|
"Content-Type": "application/json",
|
||
|
|
Authorization: `Bearer ${tokens.accessToken}`,
|
||
|
|
},
|
||
|
|
body: JSON.stringify({
|
||
|
|
firstName: next.firstName,
|
||
|
|
lastName: next.lastName,
|
||
|
|
phone: next.phone,
|
||
|
|
}),
|
||
|
|
});
|
||
|
|
if (!response.ok) throw new Error("Failed to update profile");
|
||
|
|
const updatedUser = (await response.json()) as Partial<typeof user>;
|
||
|
|
useAuthStore.setState(state => ({
|
||
|
|
...state,
|
||
|
|
user: state.user ? { ...state.user, ...updatedUser } : state.user,
|
||
|
|
}));
|
||
|
|
setFormData(next);
|
||
|
|
return true;
|
||
|
|
} catch (err) {
|
||
|
|
logger.error(err, "Error updating profile");
|
||
|
|
setError(err instanceof Error ? err.message : "Failed to update profile");
|
||
|
|
return false;
|
||
|
|
} finally {
|
||
|
|
setIsSavingProfile(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const saveAddress = async (next: Address) => {
|
||
|
|
setIsSavingAddress(true);
|
||
|
|
setError(null);
|
||
|
|
try {
|
||
|
|
await accountService.updateAddress({
|
||
|
|
street: next.street,
|
||
|
|
streetLine2: next.streetLine2,
|
||
|
|
city: next.city,
|
||
|
|
state: next.state,
|
||
|
|
postalCode: next.postalCode,
|
||
|
|
country: next.country,
|
||
|
|
});
|
||
|
|
setBillingInfo({ address: next });
|
||
|
|
setAddress(next);
|
||
|
|
return true;
|
||
|
|
} catch (err) {
|
||
|
|
logger.error(err, "Error updating address");
|
||
|
|
setError(err instanceof Error ? err.message : "Failed to update address");
|
||
|
|
return false;
|
||
|
|
} finally {
|
||
|
|
setIsSavingAddress(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return {
|
||
|
|
loading,
|
||
|
|
error,
|
||
|
|
billingInfo,
|
||
|
|
formData,
|
||
|
|
setFormData,
|
||
|
|
addressData,
|
||
|
|
setAddressData: setAddress,
|
||
|
|
saveProfile,
|
||
|
|
saveAddress,
|
||
|
|
isSavingProfile,
|
||
|
|
isSavingAddress,
|
||
|
|
} as const;
|
||
|
|
}
|