diff --git a/packages/commercetools/api-client/src/api/addCustomerAddress/index.ts b/packages/commercetools/api-client/src/api/addCustomerAddress/index.ts new file mode 100644 index 000000000..37f2a1489 --- /dev/null +++ b/packages/commercetools/api-client/src/api/addCustomerAddress/index.ts @@ -0,0 +1,17 @@ +import { Customer, AddressInput } from '../../types/GraphQL'; +import { apolloClient } from '../../index'; +import updateMyCustomer from '../updateMyCustomer/defaultMutation'; +import { UpdateMyCustomerResponse } from '../../types/Api'; + +const addCustomerAddress = (customer: Customer, address: AddressInput): Promise => apolloClient.mutate({ + mutation: updateMyCustomer, + variables: { + version: customer.version, + actions: [{ + addAddress: { address } + }] + }, + fetchPolicy: 'no-cache' +}); + +export default addCustomerAddress; diff --git a/packages/commercetools/api-client/src/api/assignCustomerAddress/index.ts b/packages/commercetools/api-client/src/api/assignCustomerAddress/index.ts new file mode 100644 index 000000000..fbad25ac7 --- /dev/null +++ b/packages/commercetools/api-client/src/api/assignCustomerAddress/index.ts @@ -0,0 +1,29 @@ +import { Customer, Address } from '../../types/GraphQL'; +import { UpdateMyCustomerResponse } from '../../types/Api'; +import updateMyCustomer from '../updateMyCustomer/defaultMutation'; +import { apolloClient } from '../..'; + +const getActionKey = (type: 'billing' | 'shipping'): string => { + switch (type) { + case 'billing': + return 'addBillingAddressId'; + case 'shipping': + return 'addShippingAddressId'; + default: + throw new Error('Unknown action type'); + } +}; + +const assignCustomerAddress = (customer: Customer, address: Address, type: 'billing' | 'shipping'): Promise => apolloClient.mutate({ + mutation: updateMyCustomer, + variables: { + version: customer.version, + actions: [{ + [getActionKey(type)]: { + addressId: address.id + } + }] + } +}); + +export default assignCustomerAddress; diff --git a/packages/commercetools/api-client/src/api/getMe/defaultQuery.ts b/packages/commercetools/api-client/src/api/getMe/defaultQuery.ts index b501513b2..29d46aad5 100644 --- a/packages/commercetools/api-client/src/api/getMe/defaultQuery.ts +++ b/packages/commercetools/api-client/src/api/getMe/defaultQuery.ts @@ -1,5 +1,5 @@ import gql from 'graphql-tag'; -import { CartFragment, CustomerFragment } from './../../fragments'; +import { CartFragment, CustomerFragment, AddressFragment } from './../../fragments'; const basicProfile = gql` ${CartFragment} @@ -16,6 +16,7 @@ const basicProfile = gql` const fullProfile = gql` ${CartFragment} ${CustomerFragment} + ${AddressFragment} query getMe($locale: Locale!) { me { @@ -24,6 +25,17 @@ const fullProfile = gql` } customer { ...DefaultCustomer + addresses { + ...DefaultAddress + } + defaultBillingAddress { + ...DefaultAddress + } + defaultShippingAddress { + ...DefaultAddress + } + shippingAddressIds + billingAddressIds } } } diff --git a/packages/commercetools/api-client/src/api/removeCustomerAddress/index.ts b/packages/commercetools/api-client/src/api/removeCustomerAddress/index.ts new file mode 100644 index 000000000..708c0620d --- /dev/null +++ b/packages/commercetools/api-client/src/api/removeCustomerAddress/index.ts @@ -0,0 +1,18 @@ +import { Customer, AddressInput } from '../../types/GraphQL'; +import { UpdateMyCustomerResponse } from '../../types/Api'; +import { apolloClient } from '../../index'; +import updateMyCustomer from '../updateMyCustomer/defaultMutation'; + +const removeCustomerAddress = (customer: Customer, address: AddressInput): Promise => apolloClient.mutate({ + mutation: updateMyCustomer, + variables: { + version: customer.version, + actions: [{ + removeAddress: { + addressId: address.id + } + }] + } +}); + +export default removeCustomerAddress; diff --git a/packages/commercetools/api-client/src/api/updateCustomerAddress/index.ts b/packages/commercetools/api-client/src/api/updateCustomerAddress/index.ts new file mode 100644 index 000000000..fb0d8f04d --- /dev/null +++ b/packages/commercetools/api-client/src/api/updateCustomerAddress/index.ts @@ -0,0 +1,19 @@ +import { Customer, AddressInput } from '../../types/GraphQL'; +import { UpdateMyCustomerResponse } from '../../types/Api'; +import { apolloClient } from '../../index'; +import updateMyCustomer from '../updateMyCustomer/defaultMutation'; + +const updateCustomerAddress = (customer: Customer, address: AddressInput): Promise => apolloClient.mutate({ + mutation: updateMyCustomer, + variables: { + version: customer.version, + actions: [{ + changeAddress: { + addressId: address.id, + address + } + }] + } +}); + +export default updateCustomerAddress; diff --git a/packages/commercetools/api-client/src/api/updateCustomerPersonalDetails/index.ts b/packages/commercetools/api-client/src/api/updateCustomerPersonalDetails/index.ts new file mode 100644 index 000000000..5f32139c9 --- /dev/null +++ b/packages/commercetools/api-client/src/api/updateCustomerPersonalDetails/index.ts @@ -0,0 +1,44 @@ +import { Customer } from '../../types/GraphQL'; +import { UpdateMyCustomerResponse } from '../../types/Api'; +import { apolloClient } from '../../index'; +import updateMyCustomer from '../updateMyCustomer/defaultMutation'; + +interface PersonalDetails { + firstName?: string; + lastName?: string; + email?: string; +} + +const generateActions = (personalDetails: PersonalDetails): any[] => { + const actions = []; + + personalDetails.email && actions.push({ + changeEmail: { + email: personalDetails.email + } + }); + + personalDetails.firstName && actions.push({ + setFirstName: { + firstName: personalDetails.firstName + } + }); + + personalDetails.lastName && actions.push({ + setLastName: { + lastName: personalDetails.lastName + } + }); + + return actions; +}; + +const updateCustomerPersonalDetails = (customer: Customer, personalDetails: PersonalDetails): Promise => apolloClient.mutate({ + mutation: updateMyCustomer, + variables: { + version: customer.version, + actions: generateActions(personalDetails) + } +}); + +export default updateCustomerPersonalDetails; diff --git a/packages/commercetools/api-client/src/api/updateMyCustomer/defaultMutation.ts b/packages/commercetools/api-client/src/api/updateMyCustomer/defaultMutation.ts new file mode 100644 index 000000000..52c461e28 --- /dev/null +++ b/packages/commercetools/api-client/src/api/updateMyCustomer/defaultMutation.ts @@ -0,0 +1,18 @@ +import gql from 'graphql-tag'; +import { CustomerFragment, AddressFragment } from '../../fragments'; + +export default gql` + ${AddressFragment} + ${CustomerFragment} + + mutation updateMyCustomer($version: Long!, $actions: [MyCustomerUpdateAction!]!, $storeKey: KeyReferenceInput) { + updateMyCustomer(version: $version, actions: $actions, storeKey: $storeKey) { + shippingAddressIds + billingAddressIds + addresses { + ...DefaultAddress + } + ...DefaultCustomer + } + } +`; diff --git a/packages/commercetools/api-client/src/fragments/index.ts b/packages/commercetools/api-client/src/fragments/index.ts index 48e151c11..e8df739a1 100644 --- a/packages/commercetools/api-client/src/fragments/index.ts +++ b/packages/commercetools/api-client/src/fragments/index.ts @@ -1,6 +1,7 @@ export const AddressFragment = ` fragment DefaultAddress on Address { + id title firstName lastName @@ -11,6 +12,13 @@ export const AddressFragment = ` region country company + apartment + contactInfo { + phone + mobile + email + fax + } } `; diff --git a/packages/commercetools/api-client/src/index.ts b/packages/commercetools/api-client/src/index.ts index 8b82f0fe5..d594922ca 100644 --- a/packages/commercetools/api-client/src/index.ts +++ b/packages/commercetools/api-client/src/index.ts @@ -7,6 +7,11 @@ import getCategory from './api/getCategory'; import createCart from './api/createCart'; import updateCart from './api/updateCart'; import getCart from './api/getCart'; +import addCustomerAddress from './api/addCustomerAddress'; +import assignCustomerAddress from './api/assignCustomerAddress'; +import removeCustomerAddress from './api/removeCustomerAddress'; +import updateCustomerAddress from './api/updateCustomerAddress'; +import updateCustomerPersonalDetails from './api/updateCustomerPersonalDetails'; import addToCart from './api/addToCart'; import removeFromCart from './api/removeFromCart'; import updateCartQuantity from './api/updateCartQuantity'; @@ -72,6 +77,11 @@ export { updateCart, getCart, addToCart, + addCustomerAddress, + assignCustomerAddress, + removeCustomerAddress, + updateCustomerAddress, + updateCustomerPersonalDetails, removeFromCart, getMe, updateCartQuantity, diff --git a/packages/commercetools/api-client/src/types/Api.ts b/packages/commercetools/api-client/src/types/Api.ts index 97e237026..bf8155b81 100644 --- a/packages/commercetools/api-client/src/types/Api.ts +++ b/packages/commercetools/api-client/src/types/Api.ts @@ -41,3 +41,4 @@ export type OrderResponse = OrderQueryResponse | OrderMutationResponse export type ShippingMethodsResponse = QueryResponse<'shippingMethods', ShippingMethod> export type SignInResponse = QueryResponse<'user', CustomerSignInResult> export type ChangeMyPasswordResponse = QueryResponse<'user', Customer> +export type UpdateMyCustomerResponse = MutationResponse<'updateMyCustomer', Customer> diff --git a/packages/commercetools/composables/src/index.ts b/packages/commercetools/composables/src/index.ts index fedcae15f..5cfb7dd30 100644 --- a/packages/commercetools/composables/src/index.ts +++ b/packages/commercetools/composables/src/index.ts @@ -5,8 +5,10 @@ import useProduct from './useProduct'; import useCart from './useCart'; import useCheckout from './useCheckout'; import useUser from './useUser'; +import useUserAddress from './useUserAddress'; import useLocale from './useLocale'; import useUserOrders from './useUserOrders'; + import { cartGetters, categoryGetters, @@ -22,6 +24,7 @@ export { useCart, useCheckout, useUser, + useUserAddress, useLocale, useUserOrders, cartGetters, diff --git a/packages/commercetools/composables/src/useUser/factoryParams.ts b/packages/commercetools/composables/src/useUser/factoryParams.ts index 42947620a..355db71cb 100644 --- a/packages/commercetools/composables/src/useUser/factoryParams.ts +++ b/packages/commercetools/composables/src/useUser/factoryParams.ts @@ -1,7 +1,8 @@ -import {UseUserFactoryParams} from '@vue-storefront/factories'; -import {Customer} from '@vue-storefront/commercetools-api/lib//types/GraphQL'; +import { UseUserFactoryParams } from '@vue-storefront/factories'; +import { Customer } from '@vue-storefront/commercetools-api/lib//types/GraphQL'; import { authenticate } from './authenticate'; import { + updateCustomerPersonalDetails, customerSignMeUp as apiCustomerSignMeUp, customerSignMeIn as apiCustomerSignMeIn, customerSignOut as apiCustomerSignOut, @@ -10,7 +11,9 @@ import { } from '@vue-storefront/commercetools-api'; import useCart from '../useCart'; -export const params: UseUserFactoryParams = { +export type UpdateUserParams = Customer & { clientSideOnly?: boolean }; + +export const params: UseUserFactoryParams = { loadUser: async () => { try { const profile = await apiGetMe({ customer: true }); @@ -27,9 +30,19 @@ export const params: UseUserFactoryParams = { await useCart().refreshCart(); await apiCustomerSignOut(); }, - updateUser: async ({currentUser, updatedUserData}): Promise => { - // Change code below if the apiClient receive userUpdate method - return Promise.resolve({currentUser, updatedUserData} as any); + updateUser: async ({ currentUser, updatedUserData }): Promise => { + if (updatedUserData.clientSideOnly) { + return Promise.resolve({currentUser, updatedUserData} as any); + } + + const { firstName, lastName, email } = updatedUserData; + + const { data } = await updateCustomerPersonalDetails(currentUser, { firstName, lastName, email }); + + return { + ...currentUser, + ...data.updateMyCustomer + }; }, register: async ({email, password, firstName, lastName}) => { return await authenticate({email, password, firstName, lastName}, apiCustomerSignMeUp); diff --git a/packages/commercetools/composables/src/useUserAddress/factoryParams.ts b/packages/commercetools/composables/src/useUserAddress/factoryParams.ts new file mode 100644 index 000000000..1cd5992a8 --- /dev/null +++ b/packages/commercetools/composables/src/useUserAddress/factoryParams.ts @@ -0,0 +1,52 @@ +import { addCustomerAddress, assignCustomerAddress, removeCustomerAddress, updateCustomerAddress } from '@vue-storefront/commercetools-api'; +import { UseUserAddressFactoryParams, UpdatedUserAddresses } from '@vue-storefront/factories'; +import { Customer } from '@vue-storefront/commercetools-api/lib//types/GraphQL'; +import { Address } from '../types/GraphQL'; +import useUser from '../useUser'; +import { UpdateUserParams } from '../useUser/factoryParams'; +import { UserAddressType } from '@vue-storefront/interfaces'; + +const userComposable = useUser(); + +const transformAddresses = (customer: Customer): UpdatedUserAddresses => ({ + addresses: customer.addresses, + updateUserParams: { ...customer, clientSideOnly: true } +}); + +export const params: UseUserAddressFactoryParams = { + userComposable, + addresses: userComposable.user.value && userComposable.user.value.id ? userComposable.user.value.addresses : [], + addAddress: async (address, type) => { + const { data } = await addCustomerAddress(userComposable.user.value, address); + const newAddress = data.updateMyCustomer.addresses.pop(); + + const { data: { updateMyCustomer } } = await assignCustomerAddress( + { ...userComposable.user.value, ...data.updateMyCustomer }, + newAddress, + type + ); + + return transformAddresses(updateMyCustomer); + }, + updateAddress: async (address) => { + const { data } = await updateCustomerAddress(userComposable.user.value, address); + + return transformAddresses(data.updateMyCustomer); + }, + deleteAddress: async (address) => { + const { data } = await removeCustomerAddress(userComposable.user.value, address); + + return transformAddresses(data.updateMyCustomer); + }, + getBillingAddresses: () => { + const { addresses, billingAddressIds } = userComposable.user.value; + + return (addresses || []).filter(({ id }) => (billingAddressIds || []).includes(id)); + }, + getShippingAddresses: () => { + const { addresses, shippingAddressIds } = userComposable.user.value; + + return (addresses || []).filter(({ id }) => (shippingAddressIds || []).includes(id)); + }, + searchAddresses: async () => userComposable.user.value.addresses +}; diff --git a/packages/commercetools/composables/src/useUserAddress/index.ts b/packages/commercetools/composables/src/useUserAddress/index.ts new file mode 100644 index 000000000..5e1439da6 --- /dev/null +++ b/packages/commercetools/composables/src/useUserAddress/index.ts @@ -0,0 +1,9 @@ +import { UseUserAddress } from '@vue-storefront/interfaces'; +import { Address } from '../types/GraphQL'; +import { useUserAddressFactory } from '@vue-storefront/factories'; +import { Customer } from '@vue-storefront/commercetools-api/lib//types/GraphQL'; +import { params } from './factoryParams'; + +const useUserAddress: () => UseUserAddress
= useUserAddressFactory(params); + +export default useUserAddress; diff --git a/packages/commercetools/composables/tests/useUserAddress/useUserAddress.ts b/packages/commercetools/composables/tests/useUserAddress/useUserAddress.ts new file mode 100644 index 000000000..bd1ba7f4e --- /dev/null +++ b/packages/commercetools/composables/tests/useUserAddress/useUserAddress.ts @@ -0,0 +1,108 @@ +import useUserAddress from '../../src/useUserAddress'; + +const defaultUserAddresses = Object.freeze({ + billingAddressIds: ['1'], + shippingAddressIds: ['2', '3'], + addresses: [ + { + id: '1', + country: 'EN' + }, + { + id: '2', + country: 'EN' + }, + { + id: '3', + country: 'EN' + } + ] +}); + +const mockResponse = (customerData) => ({ + data: { + updateMyCustomer: customerData + } +}); + +jest.mock('@vue-storefront/commercetools-api', () => ({ + addCustomerAddress: async (customer, address) => mockResponse({ + ...defaultUserAddresses, + addresses: [ + ...defaultUserAddresses.addresses, + address + ] + }), + assignCustomerAddress: async (customer, address) => mockResponse({ + ...defaultUserAddresses, + billingAddressIds: [ + ...defaultUserAddresses.billingAddressIds, + address.id + ] + }), + removeCustomerAddress: async (customer, address) => mockResponse({ + ...defaultUserAddresses, + addresses: defaultUserAddresses.addresses.filter(a => a.id !== address.id) + }), + updateCustomerAddress: async (customer, address) => mockResponse({ + ...defaultUserAddresses, + addresses: defaultUserAddresses.addresses.map(a => a.id === address.id ? address : a) + }) +})); + +describe('[commercetools-composables] useUserAddress', () => { + it('creates properties', () => { + const { addresses, loading } = useUserAddress(); + + expect(Array.isArray(addresses.value)).toBeTruthy(); + expect(addresses.value).toHaveLength(0); + expect(loading.value).toBeFalsy(); + }); + + it('should add a new address', async () => { + const { addAddress } = useUserAddress(); + + const { user, addresses } = await addAddress({ id: '4', country: 'EN' } as any, 'billing'); + + expect(addresses).toHaveLength(4); + expect(addresses[3].id).toEqual('4'); + expect(user.billingAddressIds).toHaveLength(2); + expect(user.billingAddressIds[1]).toEqual('4'); + }); + + it('should remove an address', async () => { + const { deleteAddress } = useUserAddress(); + + const { addresses } = await deleteAddress({ id: '1' } as any); + + expect(addresses).toHaveLength(2); + }); + + it('should update an address', async () => { + const { updateAddress } = useUserAddress(); + + const { addresses } = await updateAddress({ id: '1', country: 'PL' } as any); + + expect(addresses).toHaveLength(3); + expect(addresses[0].country).toBe('PL'); + }); + + it('should return billing addresses', async () => { + const { getBillingAddresses } = useUserAddress(); + + const addresses = getBillingAddresses(); + + expect(addresses).toHaveLength(1); + expect(addresses[0].id).toEqual('1'); + }); + + it('should return shipping addresses', async () => { + const { getShippingAddresses } = useUserAddress(); + + const addresses = getShippingAddresses(); + + expect(addresses).toHaveLength(2); + expect(addresses[0].id).toEqual('2'); + expect(addresses[1].id).toEqual('3'); + }); +}); diff --git a/packages/core/factories/__tests__/useCartFactory.spec.ts b/packages/core/factories/__tests__/useCartFactory.spec.ts index aaee7eedd..22d82d100 100644 --- a/packages/core/factories/__tests__/useCartFactory.spec.ts +++ b/packages/core/factories/__tests__/useCartFactory.spec.ts @@ -1,8 +1,8 @@ -import { useCartFactory, UseCartFactoryParams } from '../src'; -import { UseCart } from '@vue-storefront/interfaces'; import Vue from 'vue'; import VueCompositionApi, { ref } from '@vue/composition-api'; Vue.use(VueCompositionApi); +import { useCartFactory, UseCartFactoryParams } from '../src'; +import { UseCart } from '@vue-storefront/interfaces'; let useCart: () => UseCart; let params: UseCartFactoryParams; diff --git a/packages/core/factories/__tests__/useUserAddress.spec.ts b/packages/core/factories/__tests__/useUserAddress.spec.ts new file mode 100644 index 000000000..20a13e21f --- /dev/null +++ b/packages/core/factories/__tests__/useUserAddress.spec.ts @@ -0,0 +1,144 @@ +import Vue from 'vue'; +import VueCompositionApi from '@vue/composition-api'; +Vue.use(VueCompositionApi); +import { useUserAddressFactory, UseUserAddressFactoryParams } from '../src'; +import { UseUserAddress } from '@vue-storefront/interfaces'; + +let useUserAddress: () => UseUserAddress; + +let params: UseUserAddressFactoryParams; +let inputAddresses: any = []; + +const transform = (addresses: any[]) => ({ + addresses, + updateUserParams: {} +}); + +const createComposable = (overrideParams?: UseUserAddressFactoryParams) => { + params = overrideParams || { + userComposable: { + updateUser: async () => {} + } as any, + addresses: inputAddresses, + searchAddresses: jest.fn().mockResolvedValueOnce(['address1', 'address2', 'address3']), + addAddress: jest.fn().mockResolvedValueOnce(transform(['address1', 'address2', 'address3', 'address4'])), + deleteAddress: jest.fn().mockResolvedValueOnce(transform(['address1', 'address2'])), + getBillingAddresses: jest.fn().mockReturnValueOnce(['address1']), + getShippingAddresses: jest.fn().mockReturnValueOnce(['address2', 'address3']), + updateAddress: jest.fn().mockResolvedValueOnce(transform(['address1', 'updatedAddress', 'address3'])) + }; + + useUserAddress = useUserAddressFactory(params); +}; + +describe('[CORE - factories] useUserAddressFactory', () => { + beforeEach(() => { + inputAddresses = []; + jest.clearAllMocks(); + createComposable(); + }); + + describe('initial setup', () => { + it('should have proper initial properties', () => { + const { addresses, loading } = useUserAddress(); + + expect(Array.isArray(addresses.value)).toBeTruthy(); + expect(addresses.value).toHaveLength(0); + expect(loading.value).toBeFalsy(); + }); + }); + + describe('error handling', () => { + it('should throw an error', async () => { + createComposable({ + ...params, + addAddress: async () => { + throw new Error('mocked error'); + } + }); + + const { addAddress } = useUserAddress(); + + let errorMessage = ''; + + try { + await addAddress('any', 'any'); + } catch (error) { + errorMessage = error.message; + } + + expect(errorMessage).toBe('mocked error'); + }); + }); + + describe('computed', () => { + it('should have correct totalAddresses', async () => { + const { searchAddresses, totalAddresses } = useUserAddress(); + + await searchAddresses(); + + expect(totalAddresses.value).toEqual(3); + }); + }); + + describe('methods', () => { + describe('addAddress', () => { + it('should add an address', async () => { + const { addAddress, addresses } = useUserAddress(); + + await addAddress('any address', 'billing'); + + expect(addresses.value).toHaveLength(4); + expect(addresses.value[3]).toEqual('address4'); + }); + }); + + describe('deleteAddress', () => { + it('should remove an address', async () => { + const { deleteAddress, addresses } = useUserAddress(); + + await deleteAddress('any adresss'); + + expect(addresses.value).toHaveLength(2); + }); + }); + + describe('updateAddress', () => { + it('should update an address', async () => { + const { updateAddress, addresses } = useUserAddress(); + + await updateAddress('any address'); + + expect(addresses.value).toHaveLength(3); + expect(addresses.value[1]).toEqual('updatedAddress'); + }); + }); + + describe('getBillingAddresses', () => { + it('should return billing addresses', async () => { + const { searchAddresses, getBillingAddresses } = useUserAddress(); + + await searchAddresses(); + + const addresses = getBillingAddresses(); + + expect(addresses).toHaveLength(1); + expect(addresses[0]).toEqual('address1'); + }); + }); + + describe('getShippingAddresses', () => { + it('should return shipping addresses', async () => { + const { searchAddresses, getShippingAddresses } = useUserAddress(); + + await searchAddresses(); + + const addresses = getShippingAddresses(); + + expect(addresses).toHaveLength(2); + expect(addresses[0]).toEqual('address2'); + expect(addresses[1]).toEqual('address3'); + }); + }); + }); +}); diff --git a/packages/core/factories/src/index.ts b/packages/core/factories/src/index.ts index d08fca17b..c41640473 100644 --- a/packages/core/factories/src/index.ts +++ b/packages/core/factories/src/index.ts @@ -1,6 +1,7 @@ export * from './useCartFactory'; export * from './useCategoryFactory'; export * from './useProductFactory'; +export * from './useUserAddressFactory'; export * from './useUserFactory'; export * from './useUserOrdersFactory'; export * from './apiClientFactory'; diff --git a/packages/core/factories/src/useUserAddressFactory.ts b/packages/core/factories/src/useUserAddressFactory.ts new file mode 100644 index 000000000..69a65f245 --- /dev/null +++ b/packages/core/factories/src/useUserAddressFactory.ts @@ -0,0 +1,97 @@ +import { UseUserAddress, UserAddressType, UseUser } from '@vue-storefront/interfaces'; +import { Ref, ref, computed } from '@vue/composition-api'; + +export interface UpdatedUserAddresses { + addresses: ADDRESS[]; + updateUserParams?: UPDATE_USER_PARAMS; +} + +export type UseUserAddressFactoryParams< + USER, + ADDRESS, + ADDRESS_TYPE = UserAddressType, + SEARCH_PARAMS = { [x: string]: any }, + UPDATE_USER_PARAMS = any, +> = { + addresses: ADDRESS[]; + searchAddresses: (params?: SEARCH_PARAMS) => Promise; + addAddress: (address: ADDRESS, type: ADDRESS_TYPE) => Promise>; + deleteAddress: (address: ADDRESS) => Promise>; + updateAddress: (address: ADDRESS) => Promise>; + getBillingAddresses: () => ADDRESS[]; + getShippingAddresses: () => ADDRESS[]; + userComposable?: UseUser; +} + +export function useUserAddressFactory (factoryParams: UseUserAddressFactoryParams) { + const loading: Ref = ref(false); + + const updateData = async (data: UpdatedUserAddresses) => { + factoryParams.addresses.length = 0; + data.addresses.forEach(address => factoryParams.addresses.push(address)); + + if (factoryParams.userComposable && data.updateUserParams) { + await factoryParams.userComposable.updateUser(data.updateUserParams); + } + + loading.value = false; + }; + + const logError = (error: any) => { + console.log(error); + loading.value = false; + + throw error; + }; + + const searchAddresses = async (params) => { + loading.value = true; + + const addresses = await factoryParams.searchAddresses(params); + + updateData({ addresses }); + + loading.value = false; + }; + + const getBillingAddresses = () => factoryParams.getBillingAddresses(); + + const getShippingAddresses = () => factoryParams.getShippingAddresses(); + + const addAddress = async (address: ADDRESS, type: ADDRESS_TYPE) => { + loading.value = true; + + await factoryParams.addAddress(address, type) + .then(updateData) + .catch(logError); + }; + + const deleteAddress = async (address) => { + loading.value = true; + + await factoryParams.deleteAddress(address) + .then(updateData) + .catch(logError); + }; + + const updateAddress = async (address) => { + loading.value = true; + await factoryParams.updateAddress(address) + .then(updateData) + .catch(logError); + }; + + return function useUserAddress(): UseUserAddress { + return { + addresses: computed(() => factoryParams.addresses), + totalAddresses: computed(() => factoryParams.addresses.length), + searchAddresses, + addAddress, + updateAddress, + deleteAddress, + getBillingAddresses, + getShippingAddresses, + loading + }; + }; +} diff --git a/packages/core/interfaces/src/index.ts b/packages/core/interfaces/src/index.ts index 18a0b5f0d..be754fe29 100644 --- a/packages/core/interfaces/src/index.ts +++ b/packages/core/interfaces/src/index.ts @@ -23,7 +23,7 @@ export interface UseUser UPDATE_USER_PARAMS > { user: ComputedProperty; - updateUser: (params: UPDATE_USER_PARAMS) => Promise; + updateUser: (params: UPDATE_USER_PARAMS) => void; register: (user: { email: string; password: string; @@ -57,14 +57,19 @@ export interface UseUserOrders { loading: ComputedProperty; } -export interface UseUserAddress
{ +export type UserAddressType = 'billing' | 'shipping'; + +/** check if we always have those addresses together or we need pagination */ +export interface UseUserAddress { addresses: ComputedProperty; totalAddresses: ComputedProperty; - addAddress: (address: ADDRESS) => Promise; - deleteAddress: (address: ADDRESS) => Promise; + addAddress: (address: ADDRESS, type: ADDRESS_TYPE) => Promise; updateAddress: (address: ADDRESS) => Promise; - searchAddresses: (params?: { [x: string]: any }) => Promise; - loading: ComputedProperty; + deleteAddress: (address: ADDRESS) => Promise; + getBillingAddresses: () => ADDRESS[]; + getShippingAddresses: () => ADDRESS[]; + searchAddresses: (params?: SEARCH_PARAMS) => Promise; + loading: Ref; } export interface UseCategory diff --git a/packages/core/theme-module/theme/pages/MyAccount.vue b/packages/core/theme-module/theme/pages/MyAccount.vue index 45ed6ca73..7d60d90b1 100644 --- a/packages/core/theme-module/theme/pages/MyAccount.vue +++ b/packages/core/theme-module/theme/pages/MyAccount.vue @@ -12,16 +12,10 @@ > - + - + @@ -44,6 +38,8 @@ diff --git a/packages/core/theme-module/theme/pages/MyAccount/MyProfile.vue b/packages/core/theme-module/theme/pages/MyAccount/MyProfile.vue index fd72ff0fa..c38d78f03 100644 --- a/packages/core/theme-module/theme/pages/MyAccount/MyProfile.vue +++ b/packages/core/theme-module/theme/pages/MyAccount/MyProfile.vue @@ -54,13 +54,13 @@

If you want to change the password to access your account, enter the following information:
Your current email address is - example@email.com + {{account.email}}

- + Update password
@@ -148,50 +148,48 @@ export default { default: () => ({}) } }, - setup() { - const resetPassForm = () => ({ currentPassword: '', newPassword: '', repeatPassword: '' }); - const { user, changePassword } = useUser(); - const error = ref(null); - const form = ref(resetPassForm()); + setup({ account }) { + const { user, changePassword, updateUser } = useUser(); + + const error = ref(false); + + const currentPassword = ref(''); + const repeatPassword = ref(''); + const newPassword = ref(''); + const firstName = ref(account.firstName || ''); + const lastName = ref(account.lastName || ''); + const email = ref(account.email || ''); + + const resetPassForm = () => { + currentPassword.value = ''; + repeatPassword.value = ''; + newPassword.value = ''; + }; const updatePassword = async () => { - await changePassword(form.value.currentPassword, form.value.newPassword); - form.value = resetPassForm(); + await changePassword(currentPassword.value, newPassword.value); + + resetPassForm(); }; + const updatePersonal = () => updateUser({ + firstName: firstName.value, + lastName: lastName.value, + email: email.value + }); + return { user, error, - form, - updatePassword - }; - }, - data() { - return { - firstName: '', - lastName: '', - email: '' + currentPassword, + newPassword, + repeatPassword, + firstName, + lastName, + email, + updatePassword, + updatePersonal }; - }, - watch: { - account: { - handler(value) { - this.firstName = value.firstName; - this.lastName = value.lastName; - this.email = value.email; - }, - immediate: true - } - }, - methods: { - updatePersonal() { - const personal = { - firstName: this.firstName, - lastName: this.lastName, - email: this.email - }; - this.$emit('update:personal', personal); - } } }; diff --git a/packages/core/theme-module/theme/pages/MyAccount/ShippingDetails.vue b/packages/core/theme-module/theme/pages/MyAccount/ShippingDetails.vue index c6cbd7b01..0a285e0da 100644 --- a/packages/core/theme-module/theme/pages/MyAccount/ShippingDetails.vue +++ b/packages/core/theme-module/theme/pages/MyAccount/ShippingDetails.vue @@ -1,18 +1,18 @@