feat: Adds the Ability to set Search Console Property type via Domain Settings.

- Previously only domain properties worked with SerpBear. This feature adds the ability to add URL properties as well.
- Adds a new field "search_console" in Domain Table.
- Adds a new Search Console option in Domain Settings Modal UI.
- When the  new "This is a URL Property" option is enabled, the exact Property URL should be provided.

closes #50
This commit is contained in:
towfiqi
2024-02-06 23:42:28 +06:00
parent 1041cb3c0b
commit b2e97b2ebe
12 changed files with 182 additions and 47 deletions

View File

@@ -0,0 +1,28 @@
type InputFieldProps = {
label: string;
value: string;
onChange: Function;
placeholder?: string;
classNames?: string;
hasError?: boolean;
}
const InputField = ({ label = '', value = '', placeholder = '', onChange, hasError = false }: InputFieldProps) => {
const labelStyle = 'mb-2 font-semibold inline-block text-sm text-gray-700 capitalize';
return (
<div className="field--input w-full relative">
<label className={labelStyle}>{label}</label>
<input
className={`w-full p-2 border border-gray-200 rounded mb-3 focus:outline-none
focus:border-blue-200 ${hasError ? ' border-red-400 focus:border-red-400' : ''} `}
type={'text'}
value={value}
onChange={(event) => onChange(event.target.value)}
autoComplete="off"
placeholder={placeholder}
/>
</div>
);
};
export default InputField;

View File

@@ -0,0 +1,32 @@
type ToggleFieldProps = {
label: string;
value: string;
onChange: (bool:boolean) => void ;
classNames?: string;
}
const ToggleField = ({ label = '', value = '', onChange, classNames = '' }: ToggleFieldProps) => {
return (
<div className={`field--toggle w-full relative ${classNames}`}>
<label className="relative inline-flex items-center cursor-pointer w-full justify-between">
<span className="text-sm font-medium text-gray-900 dark:text-gray-300 w-56">{label}</span>
<input
type="checkbox"
value={value}
checked={!!value}
className="sr-only peer"
onChange={() => onChange(!value)}
/>
<div className="relative rounded-3xl w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4
peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800rounded-full peer dark:bg-gray-700
peer-checked:after:translate-x-full peer-checked:after:border-white after:content-['']
after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300
after:border after:rounded-full after:h-4 after:w-4
after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
);
};
export default ToggleField;

View File

@@ -3,6 +3,8 @@ import { useState } from 'react';
import Icon from '../common/Icon';
import Modal from '../common/Modal';
import { useDeleteDomain, useUpdateDomain } from '../../services/domains';
import InputField from '../common/InputField';
import SelectField from '../common/SelectField';
type DomainSettingsProps = {
domain:DomainType|false,
@@ -16,11 +18,13 @@ type DomainSettingsError = {
const DomainSettings = ({ domain, closeModal }: DomainSettingsProps) => {
const router = useRouter();
const [currentTab, setCurrentTab] = useState<'notification'|'searchconsole'>('notification');
const [showRemoveDomain, setShowRemoveDomain] = useState<boolean>(false);
const [settingsError, setSettingsError] = useState<DomainSettingsError>({ type: '', msg: '' });
const [domainSettings, setDomainSettings] = useState<DomainSettings>(() => ({
notification_interval: domain && domain.notification_interval ? domain.notification_interval : 'never',
notification_emails: domain && domain.notification_emails ? domain.notification_emails : '',
search_console: domain && domain.search_console ? JSON.parse(domain.search_console) : { property_type: 'domain', url: '' },
}));
const { mutate: updateMutate } = useUpdateDomain(() => closeModal(false));
@@ -29,10 +33,6 @@ const DomainSettings = ({ domain, closeModal }: DomainSettingsProps) => {
router.push('/domains');
});
const updateNotiEmails = (event:React.FormEvent<HTMLInputElement>) => {
setDomainSettings({ ...domainSettings, notification_emails: event.currentTarget.value });
};
const updateDomain = () => {
console.log('Domain: ');
let error: DomainSettingsError | null = null;
@@ -55,24 +55,73 @@ const DomainSettings = ({ domain, closeModal }: DomainSettingsProps) => {
}
};
const tabStyle = 'inline-block px-4 py-1 rounded-full mr-3 cursor-pointer text-sm select-none';
return (
<div>
<Modal closeModal={() => closeModal(false)} title={'Domain Settings'} width="[500px]">
<div data-testid="domain_settings" className=" text-sm">
<div className="mb-6 flex justify-between items-center">
<h4>Notification Emails
{settingsError.type === 'email' && <span className="text-red-500 font-semibold ml-2">{settingsError.msg}</span>}
</h4>
<input
className={`border w-46 text-sm transition-all rounded p-1.5 px-4 outline-none ring-0
${settingsError.type === 'email' ? ' border-red-300' : ''}`}
type="text"
placeholder='Your Emails'
onChange={updateNotiEmails}
value={domainSettings.notification_emails || ''}
/>
<div className='mt-5 mb-5 '>
<ul>
<li
className={`${tabStyle} ${currentTab === 'notification' ? ' bg-blue-50 text-blue-600' : ''}`}
onClick={() => setCurrentTab('notification')}>
Notification
</li>
<li
className={`${tabStyle} ${currentTab === 'searchconsole' ? ' bg-blue-50 text-blue-600' : ''}`}
onClick={() => setCurrentTab('searchconsole')}>
Search Console
</li>
</ul>
</div>
<div>
{currentTab === 'notification' && (
<div className="mb-4 flex justify-between items-center w-full">
<InputField
label='Notification Emails'
onChange={(emails:string) => setDomainSettings({ ...domainSettings, notification_emails: emails })}
value={domainSettings.notification_emails || ''}
placeholder='Your Emails'
/>
</div>
)}
{currentTab === 'searchconsole' && (
<>
<div className="mb-4 flex justify-between items-center w-full">
<label className='mb-2 font-semibold inline-block text-sm text-gray-700 capitalize'>Property Type</label>
<SelectField
options={[{ label: 'Domain', value: 'domain' }, { label: 'URL', value: 'url' }]}
selected={[domainSettings.search_console?.property_type || 'domain']}
defaultLabel="Select Search Console Property Type"
updateField={(updated:['domain'|'url']) => setDomainSettings({
...domainSettings,
search_console: { ...domainSettings.search_console, property_type: updated[0] || 'domain' },
})}
multiple={false}
rounded={'rounded'}
/>
</div>
{domainSettings?.search_console?.property_type === 'url' && (
<div className="mb-4 flex justify-between items-center w-full">
<InputField
label='Property URL (Required)'
onChange={(url:string) => setDomainSettings({
...domainSettings,
search_console: { ...domainSettings.search_console, url },
})}
value={domainSettings?.search_console?.url || ''}
placeholder='Search Console Property URL. eg: https://mywebsite.com/'
/>
</div>
)}
</>
)}
</div>
</div>
<div className="flex justify-between border-t-[1px] border-gray-100 mt-8 pt-4 pb-0">
<button
className="text-sm font-semibold text-red-500"

View File

@@ -3,6 +3,7 @@ import { useClearFailedQueue } from '../../services/settings';
import Icon from '../common/Icon';
import SelectField, { SelectionOption } from '../common/SelectField';
import SecretField from '../common/SecretField';
import ToggleField from '../common/ToggleField';
type ScraperSettingsProps = {
settings: SettingsType,
@@ -108,23 +109,11 @@ const ScraperSettings = ({ settings, settingsError, updateSettings }:ScraperSett
<small className=' text-gray-500 pt-2 block'>This option requires Server/Docker Instance Restart to take Effect.</small>
</div>
<div className="settings__section__input mb-5">
<label className="relative inline-flex items-center cursor-pointer w-full justify-between">
<span className="text-sm font-medium text-gray-900 dark:text-gray-300 w-56">Auto Retry Failed Keyword Scrape</span>
<input
type="checkbox"
value={settings?.scrape_retry ? 'true' : '' }
checked={settings.scrape_retry || false}
className="sr-only peer"
onChange={() => updateSettings('scrape_retry', !settings.scrape_retry)}
/>
<div className="relative rounded-3xl w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4
peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800rounded-full peer dark:bg-gray-700
peer-checked:after:translate-x-full peer-checked:after:border-white after:content-['']
after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300
after:border after:rounded-full after:h-4 after:w-4
after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
<ToggleField
label='Auto Retry Failed Keyword Scrape'
value={settings?.scrape_retry ? 'true' : '' }
onChange={(val) => updateSettings('scrape_retry', val)}
/>
</div>
{settings?.scrape_retry && (settings.failed_queue?.length || 0) > 0 && (
<div className="settings__section__input mb-5">