Skip to content

Commit

Permalink
Add category selection to subscriber modal.
Browse files Browse the repository at this point in the history
  • Loading branch information
allilevine committed Dec 10, 2024
1 parent 8a0fabd commit e0f8653
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 11 deletions.
1 change: 1 addition & 0 deletions packages/data-stores/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export * from './queries/use-site-query';
export * from './mutations/use-domains-bulk-actions-mutation';
export * from './queries/use-bulk-domain-update-status-query';
export * from './site-reset';
export { useNewsletterCategories } from './newsletter-categories';

const { SubscriptionManager } = Reader;

Expand Down
64 changes: 64 additions & 0 deletions packages/data-stores/src/newsletter-categories/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useQuery, UseQueryResult } from '@tanstack/react-query';
import request from 'wpcom-proxy-request';

export type NewsletterCategory = {
id: number;
name: string;
slug: string;
description: string;
parent: number;
subscribed?: boolean;
};

export type NewsletterCategories = {
enabled: boolean;
newsletterCategories: NewsletterCategory[];
};

type NewsletterCategoryQueryProps = {
siteId: number;
};

type NewsletterCategoryResponse = {
enabled: boolean;
newsletter_categories: NewsletterCategory[];
};

export const getNewsletterCategoriesKey = ( siteId?: string | number ) => [
'newsletter-categories',
siteId,
];

const convertNewsletterCategoryResponse = (
response: NewsletterCategoryResponse
): NewsletterCategories => ( {
enabled: response.enabled,
newsletterCategories: response.newsletter_categories,
} );

export const useNewsletterCategories = ( {
siteId,
}: NewsletterCategoryQueryProps ): UseQueryResult< NewsletterCategories > => {
const path = `/sites/${ siteId }/newsletter-categories`;

return useQuery( {
queryKey: getNewsletterCategoriesKey( siteId ),
queryFn: async () => {
try {
const response = await request< NewsletterCategoryResponse >( {
path,
apiVersion: '2',
apiNamespace: 'wpcom/v2',
} );
return convertNewsletterCategoryResponse( response );
} catch ( e ) {
return {
enabled: false,
newsletterCategories: [],
error: e instanceof Error ? e.message : 'Unknown error',
};
}
},
enabled: !! siteId,
} );
};
61 changes: 50 additions & 11 deletions packages/subscriber/src/components/add-form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* eslint-disable wpcalypso/jsx-classname-namespace */
import { FormInputValidation } from '@automattic/components';
import { Subscriber } from '@automattic/data-stores';
import { Subscriber, useNewsletterCategories } from '@automattic/data-stores';
import { localizeUrl } from '@automattic/i18n-utils';
import { Title, SubTitle, NextButton } from '@automattic/onboarding';
import { TextControl, FormFileUpload, Button } from '@wordpress/components';
import { TextControl, FormFileUpload, Button, ComboboxControl } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { createElement, createInterpolateElement } from '@wordpress/element';
import { sprintf } from '@wordpress/i18n';
Expand All @@ -18,6 +18,7 @@ import {
useEffect,
useRef,
useCallback,
useMemo,
} from 'react';
import { useActiveJobRecognition } from '../../hooks/use-active-job-recognition';
import { useInProgressState } from '../../hooks/use-in-progress-state';
Expand Down Expand Up @@ -50,6 +51,7 @@ interface Props {
hidden?: boolean;
isWPCOMSite?: boolean;
disabled?: boolean;
onCategorySelect?: ( email: string, category: string ) => void;
}

export const AddSubscriberForm: FunctionComponent< Props > = ( props ) => {
Expand Down Expand Up @@ -81,6 +83,7 @@ export const AddSubscriberForm: FunctionComponent< Props > = ( props ) => {
hidden = false,
isWPCOMSite = false,
disabled,
onCategorySelect,
} = props;

const {
Expand Down Expand Up @@ -109,6 +112,7 @@ export const AddSubscriberForm: FunctionComponent< Props > = ( props ) => {
const [ isDirtyEmails, setIsDirtyEmails ] = useState< boolean[] >( [] );
const [ emailFormControls, setEmailFormControls ] = useState( emailControlPlaceholder );
const [ submitAttemptCount, setSubmitAttemptCount ] = useState( 0 );
const [ emailCategories, setEmailCategories ] = useState< Record< string, string > >( {} );

const importSelector = useSelect(
( select ) => select( Subscriber.store ).getImportSubscribersSelector(),
Expand Down Expand Up @@ -431,6 +435,21 @@ export const AddSubscriberForm: FunctionComponent< Props > = ( props ) => {
);
}

const { data: newsletterCategoriesData } = useNewsletterCategories( {
siteId,
} );

const categoryOptions = useMemo(
() =>
newsletterCategoriesData?.newsletterCategories?.map(
( category: { id: number; name: string } ) => ( {
label: category.name,
value: category.id.toString(),
} )
) ?? [],
[ newsletterCategoriesData ]
);

if ( hidden ) {
return null;
}
Expand All @@ -455,6 +474,7 @@ export const AddSubscriberForm: FunctionComponent< Props > = ( props ) => {
<form onSubmit={ onFormSubmit } autoComplete="off">
{ emailFormControls.map( ( placeholder, i ) => {
const showError = isDirtyEmails[ i ] && ! isValidEmails[ i ] && emails[ i ];
const currentEmail = emails[ i ] || '';

return (
<div key={ i }>
Expand All @@ -463,15 +483,34 @@ export const AddSubscriberForm: FunctionComponent< Props > = ( props ) => {
<strong>{ __( 'Emails' ) }</strong>
</label>
) }
<TextControl
className={ showError ? 'is-error' : '' }
disabled={ inProgress || disabled }
placeholder={ placeholder }
value={ emails[ i ] || '' }
help={ isValidEmails[ i ] ? <Icon icon={ check } /> : undefined }
onChange={ ( value: string ) => onEmailChange( value, i ) }
onBlur={ () => setIsDirtyEmail( emails[ i ], i ) }
/>
<div className="add-subscriber__form-row">
<TextControl
className={ showError ? 'is-error' : '' }
disabled={ inProgress || disabled }
placeholder={ placeholder }
value={ currentEmail }
help={ isValidEmails[ i ] ? <Icon icon={ check } /> : undefined }
onChange={ ( value: string ) => onEmailChange( value, i ) }
onBlur={ () => setIsDirtyEmail( emails[ i ], i ) }
/>

{ currentEmail && isValidEmails[ i ] && (
<ComboboxControl
className="add-subscriber__category-select"
value={ emailCategories[ currentEmail ] || '' }
onChange={ ( value ) => {
setEmailCategories( ( prev ) => ( {
...prev,
[ currentEmail ]: value || '',
} ) );
onCategorySelect?.( currentEmail, value || '' );
} }
options={ categoryOptions }
allowReset
placeholder={ __( 'Select or create category' ) }
/>
) }
</div>

{ showError && (
<FormInputValidation
Expand Down

0 comments on commit e0f8653

Please sign in to comment.