2019-05-30 07:15:39 +00:00
/ * *
* External dependencies
* /
import { _ _ } from '@wordpress/i18n' ;
2020-07-21 00:12:19 +00:00
import {
Button ,
Card ,
CardBody ,
CardFooter ,
CheckboxControl ,
2020-12-21 01:57:55 +00:00
FlexItem as MaybeFlexItem ,
2021-08-31 04:39:04 +00:00
Spinner ,
2020-10-02 01:47:08 +00:00
Popover ,
2020-07-21 00:12:19 +00:00
} from '@wordpress/components' ;
2021-08-31 04:39:04 +00:00
import { Component , useRef } from '@wordpress/element' ;
2019-05-30 07:15:39 +00:00
import { compose } from '@wordpress/compose' ;
2020-03-25 03:20:17 +00:00
import { withDispatch , withSelect } from '@wordpress/data' ;
2021-08-31 04:39:04 +00:00
import { Form , TextControl } from '@woocommerce/components' ;
2021-08-10 18:58:01 +00:00
import {
2022-01-14 13:39:25 +00:00
COUNTRIES _STORE _NAME ,
2021-08-10 18:58:01 +00:00
ONBOARDING _STORE _NAME ,
OPTIONS _STORE _NAME ,
SETTINGS _STORE _NAME ,
} from '@woocommerce/data' ;
2020-08-20 04:59:52 +00:00
import { recordEvent } from '@woocommerce/tracks' ;
2021-01-07 23:57:09 +00:00
import { Text } from '@woocommerce/experimental' ;
2021-02-17 22:54:02 +00:00
import { Icon , info } from '@wordpress/icons' ;
2022-01-20 08:40:20 +00:00
import { isEmail } from '@wordpress/url' ;
2019-12-03 23:32:13 +00:00
/ * *
* Internal dependencies
* /
2020-10-02 01:47:08 +00:00
import { getCountryCode , getCurrencyRegion } from '../../../dashboard/utils' ;
2019-08-21 05:58:47 +00:00
import {
StoreAddress ,
2022-01-14 13:39:25 +00:00
getStoreAddressValidator ,
2020-10-02 01:47:08 +00:00
} from '../../../dashboard/components/settings/general/store-address' ;
import UsageModal from '../usage-modal' ;
import { CurrencyContext } from '../../../lib/currency-context' ;
2022-01-06 12:53:30 +00:00
import { getAdminSetting } from '~/utils/admin-settings' ;
2020-10-02 01:47:08 +00:00
import './style.scss' ;
2019-05-30 07:15:39 +00:00
2020-12-21 01:57:55 +00:00
// FlexItem is not available until WP version 5.5. This code is safe to remove
// once the minimum WP supported version becomes 5.5.
const FlextItemSubstitute = ( { children , align } ) => {
const style = {
display : 'flex' ,
'justify-content' : align ? 'center' : 'flex-start' ,
} ;
return < div style = { style } > { children } < / d i v > ;
} ;
const FlexItem = MaybeFlexItem || FlextItemSubstitute ;
2021-08-31 04:39:04 +00:00
const LoadingPlaceholder = ( ) => (
< div className = "woocommerce-admin__store-details__spinner" >
< Spinner / >
< / d i v >
) ;
2022-01-20 08:40:20 +00:00
export class StoreDetails extends Component {
2019-11-05 00:05:20 +00:00
constructor ( props ) {
2020-07-21 00:12:19 +00:00
super ( props ) ;
2019-05-30 07:15:39 +00:00
2019-10-10 14:05:13 +00:00
this . state = {
showUsageModal : false ,
2020-09-01 00:16:53 +00:00
skipping : false ,
2020-10-02 01:47:08 +00:00
isStoreDetailsPopoverVisible : false ,
isSkipSetupPopoverVisible : false ,
2019-10-10 14:05:13 +00:00
} ;
2019-05-30 07:15:39 +00:00
this . onContinue = this . onContinue . bind ( this ) ;
2019-10-10 14:05:13 +00:00
this . onSubmit = this . onSubmit . bind ( this ) ;
2022-01-14 13:39:25 +00:00
this . validateStoreDetails = this . validateStoreDetails . bind ( this ) ;
2022-02-17 19:15:11 +00:00
this . onFormValueChange = this . onFormValueChange . bind ( this ) ;
this . changedFormValues = { } ;
}
componentDidUpdate ( ) {
if (
this . props . isLoading === false &&
Object . keys ( this . changedFormValues ) . length === 0
) {
// Make a copy of the initialValues.
// The values in this object gets updated on onFormValueChange.
this . changedFormValues = { ... this . props . initialValues } ;
this . props . trackStepValueChanges (
this . props . step . key ,
this . props . initialValues ,
this . changedFormValues ,
( ) => {
this . onContinue ( this . changedFormValues ) ;
}
) ;
}
2019-10-10 14:05:13 +00:00
}
2019-10-29 18:34:04 +00:00
deriveCurrencySettings ( countryState ) {
if ( ! countryState ) {
return null ;
}
2021-01-04 18:04:58 +00:00
const Currency = this . context ;
const country = getCountryCode ( countryState ) ;
2022-01-06 12:53:30 +00:00
const { currencySymbols = { } , localeInfo = { } } = getAdminSetting (
2021-01-04 18:04:58 +00:00
'onboarding' ,
{ }
) ;
return Currency . getDataForCountry (
country ,
localeInfo ,
currencySymbols
) ;
2019-10-29 18:34:04 +00:00
}
2020-03-03 09:44:26 +00:00
onSubmit ( ) {
2020-09-01 00:16:53 +00:00
this . setState ( {
showUsageModal : true ,
skipping : false ,
} ) ;
2019-05-30 07:15:39 +00:00
}
2022-02-17 19:15:11 +00:00
onFormValueChange ( changedFormValue ) {
this . changedFormValues [ changedFormValue . name ] =
changedFormValue . value ;
}
2019-08-05 01:41:47 +00:00
async onContinue ( values ) {
2019-08-01 17:29:35 +00:00
const {
createNotice ,
updateProfileItems ,
2020-03-25 03:20:17 +00:00
updateAndPersistSettingsForGroup ,
2020-04-14 01:41:51 +00:00
profileItems ,
2020-06-23 02:47:02 +00:00
settings ,
2021-08-31 04:39:04 +00:00
errorsRef ,
2019-08-01 17:29:35 +00:00
} = this . props ;
2019-05-30 07:15:39 +00:00
2020-02-14 02:23:21 +00:00
const currencySettings = this . deriveCurrencySettings (
values . countryState
) ;
2020-04-02 21:54:38 +00:00
const Currency = this . context ;
Currency . setCurrency ( currencySettings ) ;
2019-10-29 18:34:04 +00:00
2019-07-01 18:13:29 +00:00
recordEvent ( 'storeprofiler_store_details_continue' , {
2019-08-26 05:49:04 +00:00
store _country : getCountryCode ( values . countryState ) ,
2022-01-20 00:19:20 +00:00
derived _currency : currencySettings . code ,
2021-08-31 04:39:04 +00:00
email _signup : values . isAgreeMarketing ,
2019-07-01 18:13:29 +00:00
} ) ;
2020-03-25 03:20:17 +00:00
await updateAndPersistSettingsForGroup ( 'general' , {
2019-05-30 07:15:39 +00:00
general : {
2020-06-23 02:47:02 +00:00
... settings ,
2019-08-05 01:41:47 +00:00
woocommerce _store _address : values . addressLine1 ,
woocommerce _store _address _2 : values . addressLine2 ,
woocommerce _default _country : values . countryState ,
woocommerce _store _city : values . city ,
woocommerce _store _postcode : values . postCode ,
2019-10-29 18:34:04 +00:00
woocommerce _currency : currencySettings . code ,
2019-12-03 23:32:13 +00:00
woocommerce _currency _pos : currencySettings . symbolPosition ,
2020-02-14 02:23:21 +00:00
woocommerce _price _thousand _sep :
currencySettings . thousandSeparator ,
woocommerce _price _decimal _sep :
currencySettings . decimalSeparator ,
2019-10-29 18:34:04 +00:00
woocommerce _price _num _decimals : currencySettings . precision ,
2019-05-30 07:15:39 +00:00
} ,
} ) ;
2021-08-31 04:39:04 +00:00
const profileItemsToUpdate = {
is _agree _marketing : values . isAgreeMarketing ,
store _email : values . storeEmail ,
} ;
2020-04-14 01:41:51 +00:00
const region = getCurrencyRegion ( values . countryState ) ;
/ * *
* If a user has already selected cdb industry and returns to change to a
* non US store , remove cbd industry .
*
* NOTE : the following call to ` updateProfileItems ` does not respect the
* ` await ` and performs an update aysnchronously . This means the following
* screen may not be initialized with correct profile settings .
*
* This comment may be removed when a refactor to wp . data datatores is complete .
* /
2020-04-14 18:46:41 +00:00
if (
region !== 'US' &&
profileItems . industry &&
profileItems . industry . length
) {
2020-04-14 01:41:51 +00:00
const cbdSlug = 'cbd-other-hemp-derived-products' ;
const trimmedIndustries = profileItems . industry . filter (
( industry ) => {
return cbdSlug !== industry && cbdSlug !== industry . slug ;
}
) ;
profileItemsToUpdate . industry = trimmedIndustries ;
}
2021-08-31 04:39:04 +00:00
let errorMessages = [ ] ;
try {
await updateProfileItems ( profileItemsToUpdate ) ;
} catch ( error ) {
// Array of error messages obtained from API response.
if ( error ? . data ? . params ) {
errorMessages = Object . values ( error . data . params ) ;
}
}
2019-08-01 17:29:35 +00:00
2021-08-31 04:39:04 +00:00
if (
! Boolean ( errorsRef . current . settings ) &&
! errorMessages . length
) {
2022-02-17 19:15:11 +00:00
return true ;
2019-05-30 07:15:39 +00:00
}
2022-02-17 19:15:11 +00:00
createNotice (
'error' ,
2022-03-30 09:00:04 +00:00
_ _ ( 'There was a problem saving your store details' , 'woocommerce' )
2022-02-17 19:15:11 +00:00
) ;
errorMessages . forEach ( ( message ) =>
createNotice ( 'error' , message )
) ;
2019-05-30 07:15:39 +00:00
}
2021-08-31 04:39:04 +00:00
validateStoreDetails ( values ) {
2022-01-14 13:39:25 +00:00
const { getLocale } = this . props ;
const locale = getLocale ( values . countryState ) ;
const validateAddress = getStoreAddressValidator ( locale ) ;
const errors = validateAddress ( values ) ;
2021-08-31 04:39:04 +00:00
2022-02-08 00:56:45 +00:00
if ( values . storeEmail && ! isEmail ( values . storeEmail ) ) {
2022-03-30 09:00:04 +00:00
errors . storeEmail = _ _ ( 'Invalid email address' , 'woocommerce' ) ;
2021-08-31 04:39:04 +00:00
}
2022-06-10 02:35:11 +00:00
if (
values . isAgreeMarketing &&
( ! values . storeEmail || ! values . storeEmail . trim ( ) . length )
) {
errors . storeEmail = _ _ (
'Please enter your email address to subscribe' ,
'woocommerce'
) ;
}
2021-08-31 04:39:04 +00:00
return errors ;
}
2019-05-30 07:15:39 +00:00
render ( ) {
2020-10-02 01:47:08 +00:00
const {
showUsageModal ,
skipping ,
isStoreDetailsPopoverVisible ,
isSkipSetupPopoverVisible ,
} = this . state ;
2021-10-13 16:15:47 +00:00
const {
skipProfiler ,
isLoading ,
isBusy ,
initialValues ,
invalidateResolutionForStoreSelector ,
} = this . props ;
2020-07-21 00:12:19 +00:00
2020-07-28 02:32:58 +00:00
/* eslint-disable @wordpress/i18n-no-collapsible-whitespace */
2020-07-21 00:12:19 +00:00
const skipSetupText = _ _ (
'Manual setup is only recommended for\n experienced WooCommerce users or developers.' ,
2022-03-30 09:00:04 +00:00
'woocommerce'
2020-07-21 00:12:19 +00:00
) ;
const configureCurrencyText = _ _ (
'Your store address will help us configure currency\n options and shipping rules automatically.\n This information will not be publicly visible and can\n easily be changed later.' ,
2022-03-30 09:00:04 +00:00
'woocommerce'
2020-07-21 00:12:19 +00:00
) ;
2020-07-28 02:32:58 +00:00
/* eslint-enable @wordpress/i18n-no-collapsible-whitespace */
2019-11-05 00:05:20 +00:00
2021-08-31 04:39:04 +00:00
if ( isLoading ) {
return (
< div className = "woocommerce-profile-wizard__store-details" >
< LoadingPlaceholder / >
< / d i v >
) ;
}
2019-05-30 07:15:39 +00:00
return (
2020-10-02 01:47:08 +00:00
< div className = "woocommerce-profile-wizard__store-details" >
2020-09-01 08:47:49 +00:00
< div className = "woocommerce-profile-wizard__step-header" >
2021-06-28 01:14:59 +00:00
< Text
variant = "title.small"
as = "h2"
size = "20"
lineHeight = "28px"
>
2022-03-30 09:00:04 +00:00
{ _ _ ( 'Welcome to WooCommerce' , 'woocommerce' ) }
2020-09-01 08:47:49 +00:00
< / T e x t >
2021-06-28 01:14:59 +00:00
< Text variant = "body" as = "p" >
2020-09-01 08:47:49 +00:00
{ _ _ (
"Tell us about your store and we'll get you set up in no time" ,
2022-03-30 09:00:04 +00:00
'woocommerce'
2020-09-01 08:47:49 +00:00
) }
2020-10-02 01:47:08 +00:00
< Button
isTertiary
label = { _ _ (
'Learn more about store details' ,
2022-03-30 09:00:04 +00:00
'woocommerce'
2020-10-02 01:47:08 +00:00
) }
onClick = { ( ) =>
this . setState ( {
isStoreDetailsPopoverVisible : true ,
} )
}
>
2021-02-17 22:54:02 +00:00
< Icon icon = { info } / >
2020-10-02 01:47:08 +00:00
< / B u t t o n >
2020-09-01 08:47:49 +00:00
< / T e x t >
2020-10-02 01:47:08 +00:00
{ isStoreDetailsPopoverVisible && (
< Popover
focusOnMount = "container"
position = "top center"
onClose = { ( ) =>
this . setState ( {
isStoreDetailsPopoverVisible : false ,
} )
}
>
{ configureCurrencyText }
< / P o p o v e r >
) }
2020-09-01 08:47:49 +00:00
< / d i v >
2019-05-30 07:15:39 +00:00
2020-07-21 00:12:19 +00:00
< Form
2021-08-31 04:39:04 +00:00
initialValues = { initialValues }
2021-05-18 04:24:24 +00:00
onSubmit = { this . onSubmit }
2021-08-31 04:39:04 +00:00
validate = { this . validateStoreDetails }
2022-02-17 19:15:11 +00:00
onChange = { this . onFormValueChange }
2020-07-21 00:12:19 +00:00
>
{ ( {
getInputProps ,
handleSubmit ,
values ,
isValidForm ,
setValue ,
} ) => (
< Card >
{ showUsageModal && (
< UsageModal
2020-09-01 00:16:53 +00:00
onContinue = { ( ) => {
if ( skipping ) {
skipProfiler ( ) ;
} else {
2022-06-21 08:37:34 +00:00
this . onContinue ( values ) . then (
( ) => this . props . goToNextStep ( )
2022-02-17 19:15:11 +00:00
) ;
2020-09-01 00:16:53 +00:00
}
} }
2020-07-21 00:12:19 +00:00
onClose = { ( ) =>
this . setState ( {
showUsageModal : false ,
2020-09-01 00:16:53 +00:00
skipping : false ,
2020-07-21 00:12:19 +00:00
} )
}
/ >
) }
< CardBody >
2020-02-14 02:23:21 +00:00
< StoreAddress
getInputProps = { getInputProps }
setValue = { setValue }
/ >
2021-08-31 04:39:04 +00:00
< TextControl
2022-01-10 19:18:19 +00:00
label = {
values . isAgreeMarketing
? _ _ (
'Email address' ,
2022-03-30 09:00:04 +00:00
'woocommerce'
2022-01-10 19:18:19 +00:00
)
: _ _ (
'Email address (Optional)' ,
2022-03-30 09:00:04 +00:00
'woocommerce'
2022-01-10 19:18:19 +00:00
)
}
required = { values . isAgreeMarketing }
2021-08-31 04:39:04 +00:00
autoComplete = "email"
{ ... getInputProps ( 'storeEmail' ) }
/ >
2020-12-21 01:57:55 +00:00
< FlexItem >
2021-09-03 11:13:31 +00:00
< div className = "woocommerce-profile-wizard__newsletter-signup" >
2020-07-21 00:12:19 +00:00
< CheckboxControl
2021-09-03 11:13:31 +00:00
label = {
< >
{ _ _ (
'Get tips, product updates and inspiration straight to your mailbox.' ,
2022-03-30 09:00:04 +00:00
'woocommerce'
2021-09-03 11:13:31 +00:00
) } { ' ' }
< span className = "woocommerce-profile-wizard__powered-by-mailchimp" >
{ _ _ (
'Powered by Mailchimp' ,
2022-03-30 09:00:04 +00:00
'woocommerce'
2021-09-03 11:13:31 +00:00
) }
< / s p a n >
< / >
}
2021-08-31 04:39:04 +00:00
{ ... getInputProps (
'isAgreeMarketing'
) }
2020-07-21 00:12:19 +00:00
/ >
< / d i v >
< / F l e x I t e m >
2022-01-12 22:56:19 +00:00
< / C a r d B o d y >
2020-07-21 00:12:19 +00:00
< CardFooter justify = "center" >
2020-12-21 19:34:22 +00:00
< Button
isPrimary
onClick = { handleSubmit }
2021-08-10 18:58:01 +00:00
isBusy = { isBusy }
disabled = { ! isValidForm || isBusy }
2020-12-21 19:34:22 +00:00
>
2022-03-30 09:00:04 +00:00
{ _ _ ( 'Continue' , 'woocommerce' ) }
2020-12-21 19:34:22 +00:00
< / B u t t o n >
2020-07-21 00:12:19 +00:00
< / C a r d F o o t e r >
< / C a r d >
) }
< / F o r m >
< div className = "woocommerce-profile-wizard__footer" >
< Button
isLink
className = "woocommerce-profile-wizard__footer-link"
onClick = { ( ) => {
2021-10-13 16:15:47 +00:00
invalidateResolutionForStoreSelector (
'getTaskLists'
) ;
2020-09-01 00:16:53 +00:00
this . setState ( {
showUsageModal : true ,
skipping : true ,
} ) ;
return false ;
2020-07-21 00:12:19 +00:00
} }
>
2022-03-30 09:00:04 +00:00
{ _ _ ( 'Skip setup store details' , 'woocommerce' ) }
2020-07-21 00:12:19 +00:00
< / B u t t o n >
2020-10-02 01:47:08 +00:00
< Button
isTertiary
label = { skipSetupText }
onClick = { ( ) =>
this . setState ( { isSkipSetupPopoverVisible : true } )
}
>
2021-02-17 22:54:02 +00:00
< Icon icon = { info } / >
2020-10-02 01:47:08 +00:00
< / B u t t o n >
{ isSkipSetupPopoverVisible && (
< Popover
focusOnMount = "container"
position = "top center"
onClose = { ( ) =>
this . setState ( {
isSkipSetupPopoverVisible : false ,
} )
}
>
{ skipSetupText }
< / P o p o v e r >
) }
2020-07-21 00:12:19 +00:00
< / d i v >
2020-10-02 01:47:08 +00:00
< / d i v >
2019-05-30 07:15:39 +00:00
) ;
}
}
2020-04-02 21:54:38 +00:00
StoreDetails . contextType = CurrencyContext ;
2019-05-30 07:15:39 +00:00
export default compose (
2020-04-02 21:54:38 +00:00
withSelect ( ( select ) => {
2022-06-21 08:37:34 +00:00
const { getSettings , getSettingsError , isUpdateSettingsRequesting } =
select ( SETTINGS _STORE _NAME ) ;
2020-12-01 22:56:17 +00:00
const {
getProfileItems ,
isOnboardingRequesting ,
2021-08-31 04:39:04 +00:00
getEmailPrefill ,
hasFinishedResolution : hasFinishedResolutionOnboarding ,
2020-12-01 22:56:17 +00:00
} = select ( ONBOARDING _STORE _NAME ) ;
2022-01-14 13:39:25 +00:00
const {
getLocale ,
getLocales ,
2022-03-01 12:33:41 +00:00
getCountries ,
2022-01-14 13:39:25 +00:00
hasFinishedResolution : hasFinishedResolutionCountries ,
} = select ( COUNTRIES _STORE _NAME ) ;
2021-08-10 18:58:01 +00:00
const { isResolving } = select ( OPTIONS _STORE _NAME ) ;
2020-05-28 08:51:40 +00:00
const profileItems = getProfileItems ( ) ;
2021-09-13 15:36:44 +00:00
const emailPrefill = getEmailPrefill ( ) ;
2020-03-25 03:20:17 +00:00
const { general : settings = { } } = getSettings ( 'general' ) ;
2021-08-10 18:58:01 +00:00
const isBusy =
2020-12-01 22:56:17 +00:00
isOnboardingRequesting ( 'updateProfileItems' ) ||
2021-08-10 18:58:01 +00:00
isUpdateSettingsRequesting ( 'general' ) ||
isResolving ( 'getOption' , [ 'woocommerce_allow_tracking' ] ) ;
2021-08-31 04:39:04 +00:00
const isLoading =
! hasFinishedResolutionOnboarding ( 'getProfileItems' ) ||
2022-01-14 13:39:25 +00:00
! hasFinishedResolutionOnboarding ( 'getEmailPrefill' ) ||
2022-03-01 12:33:41 +00:00
! hasFinishedResolutionCountries ( 'getLocales' ) ||
! hasFinishedResolutionCountries ( 'getCountries' ) ;
2021-08-31 04:39:04 +00:00
const errorsRef = useRef ( {
settings : null ,
} ) ;
errorsRef . current = {
settings : getSettingsError ( 'general' ) ,
} ;
// Check if a store address is set so that we don't default
// to WooCommerce's default country of the UK.
const countryState =
( settings . woocommerce _store _address &&
settings . woocommerce _default _country ) ||
'' ;
2022-03-01 12:33:41 +00:00
getCountries ( ) ;
2022-01-14 13:39:25 +00:00
getLocales ( ) ;
2021-08-31 04:39:04 +00:00
const initialValues = {
addressLine1 : settings . woocommerce _store _address || '' ,
addressLine2 : settings . woocommerce _store _address _2 || '' ,
city : settings . woocommerce _store _city || '' ,
countryState ,
postCode : settings . woocommerce _store _postcode || '' ,
2022-06-13 01:55:57 +00:00
// By default, the marketing checkbox should be unticked by default to comply with WordPress.org plugin review guidelines.
2021-08-31 04:39:04 +00:00
isAgreeMarketing :
typeof profileItems . is _agree _marketing === 'boolean'
? profileItems . is _agree _marketing
2022-06-13 01:55:57 +00:00
: false ,
2021-08-31 04:39:04 +00:00
storeEmail :
typeof profileItems . store _email === 'string'
? profileItems . store _email
2021-09-13 15:36:44 +00:00
: emailPrefill ,
2021-08-31 04:39:04 +00:00
} ;
2021-08-10 18:58:01 +00:00
2020-03-25 03:20:17 +00:00
return {
2022-01-14 13:39:25 +00:00
getLocale ,
2021-08-31 04:39:04 +00:00
initialValues ,
isLoading ,
2020-05-28 08:51:40 +00:00
profileItems ,
2021-08-10 18:58:01 +00:00
isBusy ,
2019-10-10 14:05:13 +00:00
settings ,
2021-08-31 04:39:04 +00:00
errorsRef ,
2019-10-10 14:05:13 +00:00
} ;
2019-05-30 07:15:39 +00:00
} ) ,
2020-02-14 02:23:21 +00:00
withDispatch ( ( dispatch ) => {
2019-07-23 03:26:46 +00:00
const { createNotice } = dispatch ( 'core/notices' ) ;
2022-06-21 08:37:34 +00:00
const { invalidateResolutionForStoreSelector , updateProfileItems } =
dispatch ( ONBOARDING _STORE _NAME ) ;
const { updateAndPersistSettingsForGroup } =
dispatch ( SETTINGS _STORE _NAME ) ;
2019-05-30 07:15:39 +00:00
return {
2019-07-23 03:26:46 +00:00
createNotice ,
2021-10-13 16:15:47 +00:00
invalidateResolutionForStoreSelector ,
2019-08-01 17:29:35 +00:00
updateProfileItems ,
2020-03-25 03:20:17 +00:00
updateAndPersistSettingsForGroup ,
2019-05-30 07:15:39 +00:00
} ;
} )
) ( StoreDetails ) ;