feat(search): add comprehensive keyword coverage and intelligent phrase matching
- Added 10+ new keyword categories (task, cluster, billing, invoice, payment, plan, usage, schedule, wordpress, writing, picture, user, ai) - Implemented smart phrase normalization to strip filler words (how, to, what, is, etc.) - Added duplicate prevention using Set to avoid showing same question multiple times - Enhanced matching logic to check: direct keyword match, normalized term match, and question text match - Supports basic stemming (plurals -> singular: tasks -> task) - Now searches: 'how to import keywords' correctly matches 'import' in knowledge base - Fixed duplicate keywords field in Team Management navigation item This ensures all common search terms trigger relevant help suggestions with natural language support.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState, useRef } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import PageMeta from "../../components/common/PageMeta";
|
||||
import PageHeader from "../../components/common/PageHeader";
|
||||
import { Accordion, AccordionItem } from "../../components/ui/accordion";
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
GroupIcon,
|
||||
HelpCircleIcon
|
||||
} from "../../icons";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
interface TableOfContentsItem {
|
||||
id: string;
|
||||
@@ -143,7 +144,40 @@ function ModuleCard({ title, icon, color, children }: { title: string; icon: Rea
|
||||
|
||||
export default function Help() {
|
||||
const [activeSection, setActiveSection] = useState<string | null>(null);
|
||||
const [openAccordions, setOpenAccordions] = useState<Set<string>>(new Set());
|
||||
const sectionRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
||||
const location = useLocation();
|
||||
|
||||
// Handle URL hash navigation and auto-expand accordions
|
||||
useEffect(() => {
|
||||
const hash = location.hash.replace('#', '');
|
||||
if (hash) {
|
||||
// Small delay to ensure DOM is ready
|
||||
setTimeout(() => {
|
||||
scrollToSection(hash, true);
|
||||
}, 100);
|
||||
}
|
||||
}, [location.hash]);
|
||||
|
||||
const scrollToSection = (id: string, fromHash = false) => {
|
||||
const element = sectionRefs.current[id];
|
||||
if (element) {
|
||||
// Open the accordion if the section is inside one
|
||||
if (fromHash) {
|
||||
setOpenAccordions(prev => new Set([...prev, id]));
|
||||
}
|
||||
|
||||
const offset = 100;
|
||||
const elementPosition = element.getBoundingClientRect().top;
|
||||
const offsetPosition = elementPosition + window.pageYOffset - offset;
|
||||
|
||||
window.scrollTo({
|
||||
top: offsetPosition,
|
||||
behavior: "smooth"
|
||||
});
|
||||
setActiveSection(id);
|
||||
}
|
||||
};
|
||||
|
||||
const tableOfContents: TableOfContentsItem[] = [
|
||||
{ id: "getting-started", title: "Getting Started", level: 1 },
|
||||
@@ -177,21 +211,6 @@ export default function Help() {
|
||||
{ id: "faq", title: "Frequently Asked Questions", level: 1 },
|
||||
];
|
||||
|
||||
const scrollToSection = (id: string) => {
|
||||
const element = sectionRefs.current[id];
|
||||
if (element) {
|
||||
const offset = 100;
|
||||
const elementPosition = element.getBoundingClientRect().top;
|
||||
const offsetPosition = elementPosition + window.pageYOffset - offset;
|
||||
|
||||
window.scrollTo({
|
||||
top: offsetPosition,
|
||||
behavior: "smooth"
|
||||
});
|
||||
setActiveSection(id);
|
||||
}
|
||||
};
|
||||
|
||||
const faqItems = [
|
||||
{
|
||||
question: "How do I add keywords to my workflow?",
|
||||
@@ -551,8 +570,8 @@ export default function Help() {
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="Add Keywords">
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Add Keywords" forceOpen={openAccordions.has('importing-keywords')}>
|
||||
<div id="importing-keywords" ref={(el) => (sectionRefs.current["importing-keywords"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Browse and add keywords from our curated database organized by 100+ industry sectors.
|
||||
</p>
|
||||
@@ -578,8 +597,8 @@ export default function Help() {
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="Content Settings">
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Content Settings" forceOpen={openAccordions.has('content-settings')}>
|
||||
<div id="content-settings" ref={(el) => (sectionRefs.current["content-settings"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Configure how AI generates and publishes your content.
|
||||
</p>
|
||||
@@ -669,8 +688,8 @@ export default function Help() {
|
||||
</h2>
|
||||
|
||||
<Accordion>
|
||||
<AccordionItem title="Keywords Management" defaultOpen>
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Keywords Management" defaultOpen forceOpen={openAccordions.has('managing-keywords')}>
|
||||
<div id="managing-keywords" ref={(el) => (sectionRefs.current["managing-keywords"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Keywords are the foundation of your content strategy. Manage, filter, and organize your keywords here.
|
||||
</p>
|
||||
@@ -709,8 +728,8 @@ export default function Help() {
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="Keyword Clusters">
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Keyword Clusters" forceOpen={openAccordions.has('keyword-clustering')}>
|
||||
<div id="keyword-clustering" ref={(el) => (sectionRefs.current["keyword-clustering"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Clusters group related keywords for comprehensive content planning and topical authority building.
|
||||
</p>
|
||||
@@ -779,8 +798,8 @@ export default function Help() {
|
||||
</h2>
|
||||
|
||||
<Accordion>
|
||||
<AccordionItem title="Tasks Management" defaultOpen>
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Tasks Management" defaultOpen forceOpen={openAccordions.has('editing-content')}>
|
||||
<div id="editing-content" ref={(el) => (sectionRefs.current["editing-content"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Tasks are content ideas converted into actionable writing assignments with status tracking.
|
||||
</p>
|
||||
@@ -808,8 +827,8 @@ export default function Help() {
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="Content Generation">
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Content Generation" forceOpen={openAccordions.has('content-generation')}>
|
||||
<div id="content-generation" ref={(el) => (sectionRefs.current["content-generation"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Generate, edit, and manage your AI-created content.
|
||||
</p>
|
||||
@@ -858,8 +877,10 @@ export default function Help() {
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="Image Generation">
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Image Generation" forceOpen={openAccordions.has('image-generation') || openAccordions.has('image-settings') || openAccordions.has('managing-images')}>
|
||||
<div id="image-generation" ref={(el) => (sectionRefs.current["image-generation"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<div id="image-settings" ref={(el) => (sectionRefs.current["image-settings"] = el)}></div>
|
||||
<div id="managing-images" ref={(el) => (sectionRefs.current["managing-images"] = el)}></div>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Generate AI images for your content using DALL-E 3 (premium) or Runware (basic).
|
||||
</p>
|
||||
@@ -892,8 +913,8 @@ export default function Help() {
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="Review & Publish">
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Review & Publish" forceOpen={openAccordions.has('content-workflow')}>
|
||||
<div id="content-workflow" ref={(el) => (sectionRefs.current["content-workflow"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Final review stage before publishing to WordPress.
|
||||
</p>
|
||||
@@ -932,6 +953,8 @@ export default function Help() {
|
||||
|
||||
{/* Automation Section */}
|
||||
<div ref={(el) => (sectionRefs.current["automation"] = el)} className="mb-12 scroll-mt-24">
|
||||
<div id="automation-setup" ref={(el) => (sectionRefs.current["automation-setup"] = el)}></div>
|
||||
<div id="auto-publishing" ref={(el) => (sectionRefs.current["auto-publishing"] = el)}></div>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
|
||||
<BoltIcon className="size-8 text-warning-600 dark:text-warning-400" />
|
||||
Automation Pipeline
|
||||
@@ -1022,8 +1045,9 @@ export default function Help() {
|
||||
</h2>
|
||||
|
||||
<Accordion>
|
||||
<AccordionItem title="WordPress Integration" defaultOpen>
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="WordPress Integration" defaultOpen forceOpen={openAccordions.has('publishing-wordpress') || openAccordions.has('wordpress-integration')}>
|
||||
<div id="publishing-wordpress" ref={(el) => (sectionRefs.current["publishing-wordpress"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<div id="wordpress-integration" ref={(el) => (sectionRefs.current["wordpress-integration"] = el)}></div>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Connect your WordPress site for seamless content publishing.
|
||||
</p>
|
||||
@@ -1063,8 +1087,9 @@ export default function Help() {
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="AI Providers">
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="AI Providers" forceOpen={openAccordions.has('prompt-management') || openAccordions.has('author-profiles')}>
|
||||
<div id="prompt-management" ref={(el) => (sectionRefs.current["prompt-management"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<div id="author-profiles" ref={(el) => (sectionRefs.current["author-profiles"] = el)}></div>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
IGNY8 integrates with multiple AI providers for content and image generation.
|
||||
</p>
|
||||
@@ -1100,8 +1125,10 @@ export default function Help() {
|
||||
</h2>
|
||||
|
||||
<Accordion>
|
||||
<AccordionItem title="Credits System" defaultOpen>
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Credits System" defaultOpen forceOpen={openAccordions.has('credit-system') || openAccordions.has('purchasing-credits') || openAccordions.has('usage-tracking')}>
|
||||
<div id="credit-system" ref={(el) => (sectionRefs.current["credit-system"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<div id="purchasing-credits" ref={(el) => (sectionRefs.current["purchasing-credits"] = el)}></div>
|
||||
<div id="usage-tracking" ref={(el) => (sectionRefs.current["usage-tracking"] = el)}></div>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Credits are your currency for AI operations. Understand how credits work:
|
||||
</p>
|
||||
@@ -1212,8 +1239,9 @@ export default function Help() {
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem title="Team Management">
|
||||
<div className="space-y-4">
|
||||
<AccordionItem title="Team Management" forceOpen={openAccordions.has('team-collaboration') || openAccordions.has('user-roles')}>
|
||||
<div id="team-collaboration" ref={(el) => (sectionRefs.current["team-collaboration"] = el)} className="space-y-4 scroll-mt-24">
|
||||
<div id="user-roles" ref={(el) => (sectionRefs.current["user-roles"] = el)}></div>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Invite team members and manage roles in Account → Settings → Team.
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user