mirror of
https://github.com/towfiqi/serpbear
synced 2025-06-26 18:15:54 +00:00
feat: Adds the ability to show hide columns in tracked keywords table.
closes #224
This commit is contained in:
parent
7597210ca2
commit
d3e3760527
@ -14,6 +14,8 @@
|
||||
"max-len": ["error", {"code": 150, "ignoreComments": true, "ignoreUrls": true}],
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"no-unused-vars": "off",
|
||||
"implicit-arrow-linebreak": "off",
|
||||
"function-paren-newline": "off",
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
|
@ -10,10 +10,10 @@ type IconProps = {
|
||||
}
|
||||
|
||||
const Icon = ({ type, color = 'currentColor', size = 16, title = '', classes = '' }: IconProps) => {
|
||||
const xmlnsProps = { xmlns: 'http://www.w3.org/2000/svg', xmlnsXlink: 'http://www.w3.org/1999/xlink', preserveAspectRatio: 'xMidYMid meet' };
|
||||
const xmlnsProps = { title, xmlns: 'http://www.w3.org/2000/svg', xmlnsXlink: 'http://www.w3.org/1999/xlink', preserveAspectRatio: 'xMidYMid meet' };
|
||||
|
||||
return (
|
||||
<span className={`icon inline-block relative top-[2px] ${classes}`} title={title}>
|
||||
<span className={`icon inline-block relative top-[2px] ${classes}`}>
|
||||
{type === 'logo'
|
||||
&& <svg {...xmlnsProps} width={size} viewBox="0 0 1484.32 1348.5">
|
||||
<path fill={color} d="M1406.23,604.17s-44-158.18,40.43-192.67,195,97.52,195,97.52,314-65.41,534,0c0,0,122.16-105.61,214.68-80.28,99.9,27.36,32.7,181.38,32.7,181.38s228.36,384.15,239.06,737.38c0,0-346.1,346.09-746.9,406.75,0,0-527.47-106.44-737.38-449.57C1177.88,1304.68,1169.55,1008.54,1406.23,604.17Z" transform="translate(-1177.84 -405.75)"/>
|
||||
@ -308,6 +308,11 @@ const Icon = ({ type, color = 'currentColor', size = 16, title = '', classes = '
|
||||
<path fill={color} d="M7.328 43.504c.445 0 .914-.14 1.383-.469V17.957c0-.844.164-1.172.914-1.57L31.352 3.87c.07-1.547-.915-2.508-2.25-2.508c-.61 0-1.266.164-1.97.586L7.493 13.246c-2.297 1.336-2.578 1.758-2.578 4.43v22.43c0 2.015.96 3.398 2.414 3.398m9.375 5.414c.422 0 .89-.14 1.383-.469V23.371c0-.914.117-1.148.89-1.57L40.703 9.26c.07-1.523-.89-2.507-2.25-2.507c-.586 0-1.266.187-1.945.562L16.82 18.636c-2.297 1.313-2.555 1.805-2.555 4.43V45.52c0 2.015 1.008 3.398 2.438 3.398m10.031 5.719c.82 0 1.805-.328 2.977-.985l18.375-10.547c2.156-1.242 3-2.53 3-5.156l-.047-21.234c0-2.813-1.008-4.242-2.766-4.242c-.773 0-1.758.304-2.859.937L26.992 24.027c-2.203 1.29-2.977 2.602-2.977 5.157v21.234c0 2.719.961 4.219 2.72 4.219M28 50.067c-.117-.024-.164-.094-.164-.258L28 29.254c0-.89.258-1.36 1.055-1.805l17.742-10.43c.07-.046.14-.046.234-.023c.094.024.164.094.164.258l-.07 20.625c0 .773-.281 1.36-1.055 1.828L28.234 50.043a.284.284 0 0 1-.234.023"></path>
|
||||
</svg>
|
||||
}
|
||||
{type === 'lock'
|
||||
&& <svg {...xmlnsProps} width={size} viewBox="0 0 24 24">
|
||||
<path fill={color} d="M6 22q-.825 0-1.412-.587T4 20V10q0-.825.588-1.412T6 8h1V6q0-2.075 1.463-3.537T12 1t3.538 1.463T17 6v2h1q.825 0 1.413.588T20 10v10q0 .825-.587 1.413T18 22zm6-5q.825 0 1.413-.587T14 15t-.587-1.412T12 13t-1.412.588T10 15t.588 1.413T12 17M9 8h6V6q0-1.25-.875-2.125T12 3t-2.125.875T9 6z" />
|
||||
</svg>
|
||||
}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
@ -21,7 +21,9 @@ type KeywordProps = {
|
||||
lastItem?:boolean,
|
||||
showSCData: boolean,
|
||||
scDataType: string,
|
||||
style: Object
|
||||
style: Object,
|
||||
maxTitleColumnWidth: number,
|
||||
tableColumns? : string[]
|
||||
}
|
||||
|
||||
const Keyword = (props: KeywordProps) => {
|
||||
@ -39,12 +41,16 @@ const Keyword = (props: KeywordProps) => {
|
||||
style,
|
||||
index,
|
||||
scDataType = 'threeDays',
|
||||
tableColumns = [],
|
||||
maxTitleColumnWidth,
|
||||
} = props;
|
||||
const {
|
||||
keyword, domain, ID, city, position, url = '', lastUpdated, country, sticky, history = {}, updating = false, lastUpdateError = false, volume,
|
||||
} = keywordData;
|
||||
|
||||
const [showOptions, setShowOptions] = useState(false);
|
||||
const [showPositionError, setPositionError] = useState(false);
|
||||
|
||||
const turncatedURL = useMemo(() => {
|
||||
return url.replace(`https://${domain}`, '').replace(`https://www.${domain}`, '').replace(`http://${domain}`, '');
|
||||
}, [url, domain]);
|
||||
@ -91,7 +97,7 @@ const Keyword = (props: KeywordProps) => {
|
||||
className={`keyword relative py-5 px-4 text-gray-600 border-b-[1px] border-gray-200 lg:py-4 lg:px-6 lg:border-0
|
||||
lg:flex lg:justify-between lg:items-center ${selected ? ' bg-indigo-50 keyword--selected' : ''} ${lastItem ? 'border-b-0' : ''}`}>
|
||||
|
||||
<div className=' w-3/4 font-semibold cursor-pointer lg:flex-1 lg:basis-20 lg:w-auto lg:flex lg:items-center'>
|
||||
<div className=' w-3/4 font-semibold cursor-pointer lg:flex-1 lg:shrink-0 lg:basis-20 lg:w-auto lg:flex lg:items-center'>
|
||||
<button
|
||||
className={`p-0 mr-2 leading-[0px] inline-block rounded-sm pt-0 px-[1px] pb-[3px] border
|
||||
${selected ? ' bg-blue-700 border-blue-700 text-white' : 'text-transparent'}`}
|
||||
@ -100,10 +106,15 @@ const Keyword = (props: KeywordProps) => {
|
||||
<Icon type="check" size={10} />
|
||||
</button>
|
||||
<a
|
||||
className={`py-2 hover:text-blue-600 lg:flex lg:items-center lg:w-full ${showSCData ? 'lg:max-w-[180px]' : 'lg:max-w-[240px]'}`}
|
||||
onClick={() => showKeywordDetails()}>
|
||||
style={{ maxWidth: `${maxTitleColumnWidth - 35}px` }}
|
||||
className={'py-2 hover:text-blue-600 lg:flex lg:items-center w-full'}
|
||||
onClick={() => showKeywordDetails()}
|
||||
title={keyword}
|
||||
>
|
||||
<span className={`fflag fflag-${country} w-[18px] h-[12px] mr-2`} title={countries[country][0]} />
|
||||
<span className=' text-ellipsis overflow-hidden whitespace-nowrap w-[calc(100%-30px)]'>{keyword}{city ? ` (${city})` : ''}</span>
|
||||
<span className='inline-block text-ellipsis overflow-hidden whitespace-nowrap w-[calc(100%-50px)]'>
|
||||
{keyword}{city ? ` (${city})` : ''}
|
||||
</span>
|
||||
</a>
|
||||
{sticky && <button className='ml-2 relative top-[2px]' title='Favorite'><Icon type="star-filled" size={16} color="#fbd346" /></button>}
|
||||
{lastUpdateError && lastUpdateError.date
|
||||
@ -126,13 +137,15 @@ const Keyword = (props: KeywordProps) => {
|
||||
? new Date(bestPosition.date).toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'short', day: 'numeric' }) : ''
|
||||
}
|
||||
className={`keyword_best hidden bg-[#f8f9ff] w-fit min-w-[50px] h-12 p-2 text-base mt-[-20px] rounded right-5 lg:relative lg:block
|
||||
lg:bg-transparent lg:w-auto lg:h-auto lg:mt-0 lg:p-0 lg:text-sm lg:flex-1 lg:basis-16 lg:grow-0 lg:right-0 text-center font-semibold`}>
|
||||
lg:bg-transparent lg:w-auto lg:h-auto lg:mt-0 lg:p-0 lg:text-sm lg:flex-1 lg:basis-16 lg:grow-0 lg:right-0 text-center font-semibold
|
||||
${!tableColumns.includes('Best') ? 'lg:hidden' : ''}
|
||||
`}>
|
||||
{bestPosition ? bestPosition.position || '-' : (position || '-')}
|
||||
</div>
|
||||
|
||||
{chartData.labels.length > 0 && (
|
||||
<div
|
||||
className='hidden basis-20 grow-0 cursor-pointer lg:block'
|
||||
className={`hidden basis-20 grow-0 cursor-pointer lg:block ${!tableColumns.includes('History') ? 'lg:hidden' : ''}`}
|
||||
onClick={() => showKeywordDetails()}>
|
||||
<ChartSlim labels={chartData.labels} sreies={chartData.sreies} />
|
||||
</div>
|
||||
@ -140,26 +153,28 @@ const Keyword = (props: KeywordProps) => {
|
||||
|
||||
<div
|
||||
className={`keyword_best hidden bg-[#f8f9ff] w-fit min-w-[50px] h-12 p-2 text-base mt-[-20px] rounded right-5 lg:relative lg:block
|
||||
lg:bg-transparent lg:w-auto lg:h-auto lg:mt-0 lg:p-0 lg:text-sm lg:flex-1 lg:basis-24 lg:grow-0 lg:right-0 text-center`}>
|
||||
lg:bg-transparent lg:w-auto lg:h-auto lg:mt-0 lg:p-0 lg:text-sm lg:flex-1 lg:basis-24 lg:grow-0 lg:right-0 text-center
|
||||
${!tableColumns.includes('Volume') ? 'lg:hidden' : ''}
|
||||
`}>
|
||||
{formattedNum(volume)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`keyword_url inline-block mt-4 mr-5 ml-5 lg:flex-1 text-gray-400 lg:m-0 max-w-[70px]
|
||||
overflow-hidden text-ellipsis whitespace-nowrap lg:max-w-none lg:pr-5`}>
|
||||
overflow-hidden text-ellipsis whitespace-nowrap lg:max-w-none lg:pr-5 lg:pl-3`}>
|
||||
<a href={url} target="_blank" rel="noreferrer"><span className='mr-3 lg:hidden'>
|
||||
<Icon type="link-alt" size={14} color="#999" /></span>{turncatedURL || '-'}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className='inline-block mt-[4] top-[-5px] relative lg:flex-1 lg:m-0 lg:top-0'>
|
||||
className='inline-block mt-[4] top-[-5px] relative lg:flex-1 lg:m-0 lg:top-0 max-w-[150px]'>
|
||||
<span className='mr-2 lg:hidden'><Icon type="clock" size={14} color="#999" /></span>
|
||||
<TimeAgo title={dayjs(lastUpdated).format('DD-MMM-YYYY, hh:mm:ss A')} date={lastUpdated} />
|
||||
</div>
|
||||
|
||||
{showSCData && (
|
||||
<div className='keyword_sc_data min-w-[170px] text-xs mt-4 pt-2 border-t border-gray-100 top-[6px]
|
||||
{showSCData && tableColumns.includes('Search Console') && (
|
||||
<div className='keyword_sc_data min-w-[170px] lg:max-w-[170px] text-xs mt-4 pt-2 border-t border-gray-100 top-[6px]
|
||||
relative flex justify-between text-center lg:flex-1 lg:text-sm lg:m-0 lg:mt-0 lg:border-t-0 lg:pt-0 lg:top-0'>
|
||||
<span className='min-w-[40px]'>
|
||||
<span className='lg:hidden'>SC Position: </span>
|
||||
|
@ -15,6 +15,8 @@ type KeywordFilterProps = {
|
||||
integratedConsole?: boolean,
|
||||
isConsole?: boolean,
|
||||
SCcountries?: string[];
|
||||
updateColumns?: Function,
|
||||
tableColumns?: string[]
|
||||
}
|
||||
|
||||
const KeywordFilters = (props: KeywordFilterProps) => {
|
||||
@ -29,10 +31,13 @@ const KeywordFilters = (props: KeywordFilterProps) => {
|
||||
filterParams,
|
||||
isConsole = false,
|
||||
integratedConsole = false,
|
||||
updateColumns,
|
||||
SCcountries = [],
|
||||
tableColumns = [],
|
||||
} = props;
|
||||
const [sortOptions, showSortOptions] = useState(false);
|
||||
const [filterOptions, showFilterOptions] = useState(false);
|
||||
const [columnOptions, showColumnOptions] = useState(false);
|
||||
|
||||
const keywordCounts = useMemo(() => {
|
||||
const counts = { desktop: 0, mobile: 0 };
|
||||
@ -84,6 +89,17 @@ const KeywordFilters = (props: KeywordFilterProps) => {
|
||||
{ value: 'vol_asc', label: 'Lowest Search Volume' },
|
||||
{ value: 'vol_desc', label: 'Highest Search Volume' },
|
||||
];
|
||||
|
||||
const columnOptionChoices: {label: string, value: string, locked: boolean}[] = [
|
||||
{ value: 'Keyword', label: 'Keyword', locked: true },
|
||||
{ value: 'Position', label: 'Position', locked: true },
|
||||
{ value: 'URL', label: 'URL', locked: true },
|
||||
{ value: 'Updated', label: 'Updated', locked: true },
|
||||
{ value: 'Best', label: 'Best', locked: false },
|
||||
{ value: 'History', label: 'History', locked: false },
|
||||
{ value: 'Volume', label: 'Volume', locked: false },
|
||||
{ value: 'Search Console', label: 'Search Console', locked: false },
|
||||
];
|
||||
if (integratedConsole) {
|
||||
sortOptionChoices.push({ value: 'imp_desc', label: `Most Viewed${isConsole ? ' (Default)' : ''}` });
|
||||
sortOptionChoices.push({ value: 'imp_asc', label: 'Least Viewed' });
|
||||
@ -190,6 +206,43 @@ const KeywordFilters = (props: KeywordFilterProps) => {
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
{!isConsole && (
|
||||
<div className='relative'>
|
||||
<button
|
||||
data-testid="columns_button"
|
||||
className={`px-2 py-1 rounded ${columnOptions ? ' bg-indigo-100 text-blue-700' : ''}`}
|
||||
title='Show/Hide Columns'
|
||||
onClick={() => showColumnOptions(!columnOptions)}
|
||||
>
|
||||
<Icon type='eye-closed' size={18} />
|
||||
</button>
|
||||
{columnOptions && (
|
||||
<ul
|
||||
data-testid="sort_options"
|
||||
className='sort_options mt-2 border absolute w-48 min-w-[0] right-0 rounded-lg
|
||||
max-h-96 bg-white z-[9999] overflow-y-auto styled-scrollbar border-gray-200 '>
|
||||
{columnOptionChoices.map(({ value, label, locked }) => {
|
||||
return <li
|
||||
key={value}
|
||||
className={sortItemStyle(value) + (locked ? 'bg-gray-50 cursor-not-allowed pointer-events-none' : '') }
|
||||
onClick={() => { if (updateColumns) { updateColumns(value); } showColumnOptions(false); }}
|
||||
>
|
||||
<span className={' inline-block px-[3px] border border-gray-200 rounded-[4px] w-5'}>
|
||||
<Icon
|
||||
title={locked ? 'Cannot be Hidden' : ''}
|
||||
type={locked ? 'lock' : 'check'}
|
||||
color={!tableColumns.includes(value) && !locked ? 'transparent' : '#999' }
|
||||
size={12}
|
||||
/>
|
||||
</span>
|
||||
{' '}{label}
|
||||
|
||||
</li>;
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import React, { useState, useMemo, useCallback, useRef, useEffect } from 'react';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import { FixedSizeList as List, ListChildComponentProps } from 'react-window';
|
||||
import { filterKeywords, keywordsByDevice, sortKeywords } from '../../utils/client/sortFilter';
|
||||
@ -12,6 +12,8 @@ import KeywordTagManager from './KeywordTagManager';
|
||||
import AddTags from './AddTags';
|
||||
import useWindowResize from '../../hooks/useWindowResize';
|
||||
import useIsMobile from '../../hooks/useIsMobile';
|
||||
import { useUpdateSettings } from '../../services/settings';
|
||||
import { defaultSettings } from '../settings/Settings';
|
||||
|
||||
type KeywordsTableProps = {
|
||||
domain: DomainType | null,
|
||||
@ -20,10 +22,12 @@ type KeywordsTableProps = {
|
||||
showAddModal: boolean,
|
||||
setShowAddModal: Function,
|
||||
isConsoleIntegrated: boolean,
|
||||
settings?: SettingsType
|
||||
}
|
||||
|
||||
const KeywordsTable = (props: KeywordsTableProps) => {
|
||||
const { keywords = [], isLoading = true, isConsoleIntegrated = false } = props;
|
||||
const titleColumnRef = useRef(null);
|
||||
const { keywords = [], isLoading = true, isConsoleIntegrated = false, settings } = props;
|
||||
const showSCData = isConsoleIntegrated;
|
||||
const [device, setDevice] = useState<string>('desktop');
|
||||
const [selectedKeywords, setSelectedKeywords] = useState<number[]>([]);
|
||||
@ -36,11 +40,27 @@ const KeywordsTable = (props: KeywordsTableProps) => {
|
||||
const [sortBy, setSortBy] = useState<string>('date_asc');
|
||||
const [scDataType, setScDataType] = useState<string>('threeDays');
|
||||
const [showScDataTypes, setShowScDataTypes] = useState<boolean>(false);
|
||||
const [maxTitleColumnWidth, setMaxTitleColumnWidth] = useState(235);
|
||||
const { mutate: deleteMutate } = useDeleteKeywords(() => {});
|
||||
const { mutate: favoriteMutate } = useFavKeywords(() => {});
|
||||
const { mutate: refreshMutate } = useRefreshKeywords(() => {});
|
||||
const [isMobile] = useIsMobile();
|
||||
useWindowResize(() => setSCListHeight(window.innerHeight - (isMobile ? 200 : 400)));
|
||||
|
||||
useWindowResize(() => {
|
||||
setSCListHeight(window.innerHeight - (isMobile ? 200 : 400));
|
||||
if (titleColumnRef.current) {
|
||||
setMaxTitleColumnWidth((titleColumnRef.current as HTMLElement).clientWidth);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (titleColumnRef.current) {
|
||||
setMaxTitleColumnWidth((titleColumnRef.current as HTMLElement).clientWidth);
|
||||
}
|
||||
}, [titleColumnRef]);
|
||||
|
||||
const tableColumns = settings?.keywordsColumns || ['Best', 'History', 'Volume', 'Search Console'];
|
||||
const { mutate: updateMutate, isLoading: isUpdatingSettings } = useUpdateSettings(() => console.log(''));
|
||||
|
||||
const scDataObject:{ [k:string] : string} = {
|
||||
threeDays: 'Last Three Days',
|
||||
@ -71,6 +91,16 @@ const KeywordsTable = (props: KeywordsTableProps) => {
|
||||
}
|
||||
setSelectedKeywords(updatedSelectd);
|
||||
};
|
||||
|
||||
const updateColumns = (column:string) => {
|
||||
const newColumns = tableColumns.includes(column) ? tableColumns.filter((col) => col !== column) : [...tableColumns, column];
|
||||
updateMutate({ ...defaultSettings, ...settings, keywordsColumns: newColumns });
|
||||
};
|
||||
|
||||
const shouldHideColumn = useCallback((col:string) => {
|
||||
return settings?.keywordsColumns && !settings?.keywordsColumns.includes(col) ? 'lg:hidden' : '';
|
||||
}, [settings?.keywordsColumns]);
|
||||
|
||||
const Row = ({ data, index, style }:ListChildComponentProps) => {
|
||||
const keyword = data[index];
|
||||
return (
|
||||
@ -89,6 +119,8 @@ const KeywordsTable = (props: KeywordsTableProps) => {
|
||||
lastItem={index === (processedKeywords[device].length - 1)}
|
||||
showSCData={showSCData}
|
||||
scDataType={scDataType}
|
||||
tableColumns={tableColumns}
|
||||
maxTitleColumnWidth={maxTitleColumnWidth}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -136,15 +168,19 @@ const KeywordsTable = (props: KeywordsTableProps) => {
|
||||
keywords={keywords}
|
||||
device={device}
|
||||
setDevice={setDevice}
|
||||
updateColumns={updateColumns}
|
||||
tableColumns={tableColumns}
|
||||
integratedConsole={isConsoleIntegrated}
|
||||
/>
|
||||
)}
|
||||
<div className={`domkeywordsTable domkeywordsTable--keywords ${showSCData ? 'domkeywordsTable--hasSC' : ''}
|
||||
<div className={`domkeywordsTable domkeywordsTable--keywords
|
||||
${showSCData && tableColumns.includes('Search Console') ? 'domkeywordsTable--hasSC' : ''}
|
||||
styled-scrollbar w-full overflow-auto min-h-[60vh]`}>
|
||||
<div className=' lg:min-w-[800px]'>
|
||||
<div className={`domKeywords_head domKeywords_head--${sortBy} hidden lg:flex p-3 px-6 bg-[#FCFCFF]
|
||||
text-gray-600 justify-between items-center font-semibold border-y`}>
|
||||
<span className='domKeywords_head_keyword flex-1 basis-[4rem] w-auto '>
|
||||
<span ref={titleColumnRef} className={`domKeywords_head_keyword flex-1 basis-[4rem] w-auto lg:flex-1
|
||||
${showSCData && tableColumns.includes('Search Console') ? 'lg:basis-20' : 'lg:basis-10'} lg:w-auto lg:flex lg:items-center `}>
|
||||
{processedKeywords[device].length > 0 && (
|
||||
<button
|
||||
className={`p-0 mr-2 leading-[0px] inline-block rounded-sm pt-0 px-[1px] pb-[3px] border border-slate-300
|
||||
@ -154,16 +190,20 @@ const KeywordsTable = (props: KeywordsTableProps) => {
|
||||
<Icon type="check" size={10} />
|
||||
</button>
|
||||
)}
|
||||
Keyword
|
||||
{/* ${showSCData ? 'lg:min-w-[220px]' : 'lg:min-w-[280px]'} */}
|
||||
<span className={`inline-block lg:flex lg:items-center
|
||||
${showSCData && tableColumns.includes('Search Console') ? 'lg:max-w-[235px]' : ''}`}>
|
||||
Keyword
|
||||
</span>
|
||||
</span>
|
||||
<span className='domKeywords_head_position flex-1 basis-24 grow-0 text-center'>Position</span>
|
||||
<span className='domKeywords_head_best flex-1 basis-16 grow-0 text-center'>Best</span>
|
||||
<span className='domKeywords_head_history flex-1 basis-20 grow-0'>History (7d)</span>
|
||||
<span className='domKeywords_head_volume flex-1 basis-24 grow-0 text-center'>Volume</span>
|
||||
<span className={`domKeywords_head_best flex-1 basis-16 grow-0 text-center ${shouldHideColumn('Best')}`}>Best</span>
|
||||
<span className={`domKeywords_head_history flex-1 basis-20 grow-0 ${shouldHideColumn('History')}`}>History (7d)</span>
|
||||
<span className={`domKeywords_head_volume flex-1 basis-24 grow-0 text-center ${shouldHideColumn('Volume')}`}>Volume</span>
|
||||
<span className='domKeywords_head_url flex-1'>URL</span>
|
||||
<span className='domKeywords_head_updated flex-1 relative left-3'>Updated</span>
|
||||
{showSCData && (
|
||||
<div className='domKeywords_head_sc flex-1 min-w-[170px] mr-7 text-center'>
|
||||
<span className='domKeywords_head_updated flex-1 relative left-3 max-w-[150px]'>Updated</span>
|
||||
{showSCData && tableColumns.includes('Search Console') && (
|
||||
<div className='domKeywords_head_sc flex-1 min-w-[170px] lg:max-w-[170px] mr-7 text-center'>
|
||||
{/* Search Console */}
|
||||
<div>
|
||||
<div
|
||||
|
@ -17,7 +17,7 @@ type SettingsError = {
|
||||
msg: string
|
||||
}
|
||||
|
||||
const defaultSettings: SettingsType = {
|
||||
export const defaultSettings: SettingsType = {
|
||||
scraper_type: 'none',
|
||||
scrape_delay: 'none',
|
||||
scrape_retry: false,
|
||||
@ -32,6 +32,7 @@ const defaultSettings: SettingsType = {
|
||||
search_console: true,
|
||||
search_console_client_email: '',
|
||||
search_console_private_key: '',
|
||||
keywordsColumns: ['Best', 'History', 'Volume', 'Search Console'],
|
||||
};
|
||||
|
||||
const Settings = ({ closeSettings }:SettingsProps) => {
|
||||
|
@ -129,6 +129,7 @@ export const getAppSettings = async () : Promise<SettingsType> => {
|
||||
search_console: true,
|
||||
search_console_client_email: '',
|
||||
search_console_private_key: '',
|
||||
keywordsColumns: ['Best', 'History', 'Volume', 'Search Console'],
|
||||
};
|
||||
const otherSettings = {
|
||||
available_scapers: allScrapers.map((scraper) => ({ label: scraper.name, value: scraper.id })),
|
||||
|
@ -80,6 +80,7 @@ const SingleDomain: NextPage = () => {
|
||||
showAddModal={showAddKeywords}
|
||||
setShowAddModal={setShowAddKeywords}
|
||||
isConsoleIntegrated={!!(appSettings && appSettings.search_console_integrated) || domainHasScAPI }
|
||||
settings={appSettings}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
2
types.d.ts
vendored
2
types.d.ts
vendored
@ -85,6 +85,7 @@ type SettingsType = {
|
||||
notification_interval: string,
|
||||
notification_email: string,
|
||||
notification_email_from: string,
|
||||
notification_email_from_name: string,
|
||||
smtp_server: string,
|
||||
smtp_port: string,
|
||||
smtp_username?: string,
|
||||
@ -105,6 +106,7 @@ type SettingsType = {
|
||||
adwords_refresh_token?: string,
|
||||
adwords_developer_token?: string,
|
||||
adwords_account_id?: string,
|
||||
keywordsColumns: string[]
|
||||
}
|
||||
|
||||
type KeywordSCDataChild = {
|
||||
|
Loading…
Reference in New Issue
Block a user