Merge pull request #37 from Siphon880gh/add-category-search
Add Use Case Category Filter
This commit is contained in:
56
README.md
56
README.md
@@ -25,6 +25,7 @@ http://localhost:8000
|
|||||||
- 🌙 **Dark/light themes** with system preference detection
|
- 🌙 **Dark/light themes** with system preference detection
|
||||||
- 📊 **Live statistics** - 365 unique integrations, 29,445 total nodes
|
- 📊 **Live statistics** - 365 unique integrations, 29,445 total nodes
|
||||||
- 🎯 **Smart categorization** by trigger type and complexity
|
- 🎯 **Smart categorization** by trigger type and complexity
|
||||||
|
- 🎯 **Use case categorization** by service name mapped to categories
|
||||||
- 📄 **On-demand JSON viewing** and download
|
- 📄 **On-demand JSON viewing** and download
|
||||||
- 🔗 **Mermaid diagram generation** for workflow visualization
|
- 🔗 **Mermaid diagram generation** for workflow visualization
|
||||||
- 🔄 **Real-time workflow naming** with intelligent formatting
|
- 🔄 **Real-time workflow naming** with intelligent formatting
|
||||||
@@ -56,6 +57,61 @@ Our intelligent naming system converts technical filenames into readable titles:
|
|||||||
- **100% meaningful names** with smart capitalization
|
- **100% meaningful names** with smart capitalization
|
||||||
- **Automatic integration detection** from node analysis
|
- **Automatic integration detection** from node analysis
|
||||||
|
|
||||||
|
### Use Case Category ✨
|
||||||
|
|
||||||
|
The search interface includes a dropdown filter that lets you browse 2,000+ workflows by category.
|
||||||
|
|
||||||
|
The system includes an automated categorization feature that organizes workflows by service categories to make them easier to discover and filter.
|
||||||
|
|
||||||
|
### How Categorization Works
|
||||||
|
|
||||||
|
1. **Run the categorization script**
|
||||||
|
```
|
||||||
|
python create_categories.py
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Service Name Recognition**
|
||||||
|
The script analyzes each workflow JSON filename to identify recognized service names (e.g., "Twilio", "Slack", "Gmail", etc.)
|
||||||
|
|
||||||
|
3. **Category Mapping**
|
||||||
|
Each recognized service name is matched to its corresponding category using the definitions in `context/def_categories.json`. For example:
|
||||||
|
- Twilio → Communication & Messaging
|
||||||
|
- Gmail → Communication & Messaging
|
||||||
|
- Airtable → Data Processing & Analysis
|
||||||
|
- Salesforce → CRM & Sales
|
||||||
|
|
||||||
|
4. **Search Categories Generation**
|
||||||
|
The script produces a `search_categories.json` file that contains the categorized workflow data
|
||||||
|
|
||||||
|
5. **Filter Interface**
|
||||||
|
Users can then filter workflows by category in the search interface, making it easier to find workflows for specific use cases
|
||||||
|
|
||||||
|
### Available Categories
|
||||||
|
|
||||||
|
The categorization system includes the following main categories:
|
||||||
|
- AI Agent Development
|
||||||
|
- Business Process Automation
|
||||||
|
- Cloud Storage & File Management
|
||||||
|
- Communication & Messaging
|
||||||
|
- Creative Content & Video Automation
|
||||||
|
- Creative Design Automation
|
||||||
|
- CRM & Sales
|
||||||
|
- Data Processing & Analysis
|
||||||
|
- E-commerce & Retail
|
||||||
|
- Financial & Accounting
|
||||||
|
- Marketing & Advertising Automation
|
||||||
|
- Project Management
|
||||||
|
- Social Media Management
|
||||||
|
- Technical Infrastructure & DevOps
|
||||||
|
- Web Scraping & Data Extraction
|
||||||
|
|
||||||
|
### Contribute Categories
|
||||||
|
|
||||||
|
You can help expand the categorization by adding more service-to-category mappings (e.g., Twilio → Communication & Messaging) in context/defs_categories.json.
|
||||||
|
|
||||||
|
Many workflow JSON files are conveniently named with the service name, often separated by underscores (_).
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠 Usage Instructions
|
## 🛠 Usage Instructions
|
||||||
|
|||||||
@@ -361,13 +361,63 @@ async def get_integrations():
|
|||||||
|
|
||||||
@app.get("/api/categories")
|
@app.get("/api/categories")
|
||||||
async def get_categories():
|
async def get_categories():
|
||||||
"""Get available service categories for filtering."""
|
"""Get available workflow categories for filtering."""
|
||||||
try:
|
try:
|
||||||
categories = db.get_service_categories()
|
# Try to load from the generated unique categories file
|
||||||
return {"categories": categories}
|
categories_file = Path("context/unique_categories.json")
|
||||||
|
if categories_file.exists():
|
||||||
|
with open(categories_file, 'r', encoding='utf-8') as f:
|
||||||
|
categories = json.load(f)
|
||||||
|
return {"categories": categories}
|
||||||
|
else:
|
||||||
|
# Fallback: extract categories from search_categories.json
|
||||||
|
search_categories_file = Path("context/search_categories.json")
|
||||||
|
if search_categories_file.exists():
|
||||||
|
with open(search_categories_file, 'r', encoding='utf-8') as f:
|
||||||
|
search_data = json.load(f)
|
||||||
|
|
||||||
|
unique_categories = set()
|
||||||
|
for item in search_data:
|
||||||
|
if item.get('category'):
|
||||||
|
unique_categories.add(item['category'])
|
||||||
|
else:
|
||||||
|
unique_categories.add('Uncategorized')
|
||||||
|
|
||||||
|
categories = sorted(list(unique_categories))
|
||||||
|
return {"categories": categories}
|
||||||
|
else:
|
||||||
|
# Last resort: return basic categories
|
||||||
|
return {"categories": ["Uncategorized"]}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(f"Error loading categories: {e}")
|
||||||
raise HTTPException(status_code=500, detail=f"Error fetching categories: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error fetching categories: {str(e)}")
|
||||||
|
|
||||||
|
@app.get("/api/category-mappings")
|
||||||
|
async def get_category_mappings():
|
||||||
|
"""Get filename to category mappings for client-side filtering."""
|
||||||
|
try:
|
||||||
|
search_categories_file = Path("context/search_categories.json")
|
||||||
|
if not search_categories_file.exists():
|
||||||
|
return {"mappings": {}}
|
||||||
|
|
||||||
|
with open(search_categories_file, 'r', encoding='utf-8') as f:
|
||||||
|
search_data = json.load(f)
|
||||||
|
|
||||||
|
# Convert to a simple filename -> category mapping
|
||||||
|
mappings = {}
|
||||||
|
for item in search_data:
|
||||||
|
filename = item.get('filename')
|
||||||
|
category = item.get('category') or 'Uncategorized'
|
||||||
|
if filename:
|
||||||
|
mappings[filename] = category
|
||||||
|
|
||||||
|
return {"mappings": mappings}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading category mappings: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Error fetching category mappings: {str(e)}")
|
||||||
|
|
||||||
@app.get("/api/workflows/category/{category}", response_model=SearchResponse)
|
@app.get("/api/workflows/category/{category}", response_model=SearchResponse)
|
||||||
async def search_workflows_by_category(
|
async def search_workflows_by_category(
|
||||||
category: str,
|
category: str,
|
||||||
|
|||||||
726
context/def_categories.json
Normal file
726
context/def_categories.json
Normal file
@@ -0,0 +1,726 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"integration": "APITemplate.io",
|
||||||
|
"category": "Creative Design Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "AWS Transcribe",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "AWSComprehend",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "AWSLambda",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "AWSRekognition",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "AWSS3",
|
||||||
|
"category": "Cloud Storage & File Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "AWSSES",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "AWSSNS",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "AWSSQS",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "ActiveCampaign",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Affinity",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Agent",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Airtable",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Asana",
|
||||||
|
"category": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Automizy",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Autopilot",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Bannerbear",
|
||||||
|
"category": "Creative Design Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "BasicLLMChain",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Beeminder",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Bitly",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Box",
|
||||||
|
"category": "Cloud Storage & File Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Brandfetch",
|
||||||
|
"category": "Web Scraping & Data Extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "ChargeBee",
|
||||||
|
"category": "Financial & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "CircleCI",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Clearbit",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "ClickUp",
|
||||||
|
"category": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Clockify",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Cockpit",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Coda",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "CoinGecko",
|
||||||
|
"category": "Financial & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Contentful-delivery-api",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Contentful-preview-api",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "ConvertKit",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Copper",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Cortex",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "CrateDB",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Customerio",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Date&Time",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Deepl",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Demio",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Discord",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Discourse",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Disqus",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Drif",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "DropBox",
|
||||||
|
"category": "Cloud Storage & File Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "E-goi",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "EditImage",
|
||||||
|
"category": "Creative Design Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Emelia",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "ExecuteWorkflow",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "FTP",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Flow",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "FreshDesk",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "FunctionItem",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GetResponse",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Ghost",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Git",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GitLab",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Github",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Gmail",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GoogleBooks",
|
||||||
|
"category": "Web Scraping & Data Extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GoogleCalendar",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GoogleCloudFirestore",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GoogleContacts",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GoogleDrive",
|
||||||
|
"category": "Cloud Storage & File Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GoogleSheets",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GoogleSlides",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "GoogleTask",
|
||||||
|
"category": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Gotify",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "HTML Extract",
|
||||||
|
"category": "Web Scraping & Data Extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "HTTP",
|
||||||
|
"category": "Web Scraping & Data Extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Hackernews",
|
||||||
|
"category": "Web Scraping & Data Extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Harvest",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "HelpScout",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Hubspot",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Hunter",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "InMemoryVectorStore",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Intercom",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "InvoiceNinja",
|
||||||
|
"category": "Financial & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Iterable",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Keap",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Kitemaker",
|
||||||
|
"category": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Lemlist",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Line",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "LingvaNex",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Linkedin",
|
||||||
|
"category": "Social Media Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "MQTT",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "MailCheck",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Mailchimp",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Mailerlite",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Mailjet",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Mandrill",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Matrix",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Mattermost",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Mautic",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Medium",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "MessageBird",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Microsoft OneDrive",
|
||||||
|
"category": "Cloud Storage & File Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "MicrosoftExcel",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "MicrosoftOutlook",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "MicrosoftSQL",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Mindee",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Mocean",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Monday",
|
||||||
|
"category": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "MongoDB",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Move Binary Data",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "MySQL",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "NASA",
|
||||||
|
"category": "Web Scraping & Data Extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Nested sub-node errors",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "NextCloud",
|
||||||
|
"category": "Cloud Storage & File Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "OpenThesaurus",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "OpenWeatherMap",
|
||||||
|
"category": "Web Scraping & Data Extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Orbit",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Paddle",
|
||||||
|
"category": "Financial & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "PagerDuty",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Paypal",
|
||||||
|
"category": "Financial & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Peekalink",
|
||||||
|
"category": "Web Scraping & Data Extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "PhantomBuster",
|
||||||
|
"category": "Web Scraping & Data Extraction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "PineconeVectorStore",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Pipedrive",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "PostHog",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Postgres",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "ProfitWell",
|
||||||
|
"category": "Financial & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Pushbullet",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Pushover",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "QdrantVectorStore",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "QuestDB",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "QuickBase",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "QuickBooks",
|
||||||
|
"category": "Financial & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Rabbitmq",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Raindrop",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Reddit",
|
||||||
|
"category": "Social Media Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Redis",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "RocketChat",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Rundeck",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "S3",
|
||||||
|
"category": "Cloud Storage & File Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "SIGNL4",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Salesforce",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Salesmate",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Segment",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "SendGrid",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "SentryIo",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Shopify",
|
||||||
|
"category": "E-commerce & Retail"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Slack",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Spontit",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Spotify",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Stackby",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Storyblok",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Strapi",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Strava",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Sub-node errors",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "SummarizationChain",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Taiga",
|
||||||
|
"category": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Tapfiliate",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Telegram",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "TheHive[v3]",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "TheHive[v4]",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "TimescaleDB",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Todoist",
|
||||||
|
"category": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "TravisCI",
|
||||||
|
"category": "Technical Infrastructure & DevOps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Trello",
|
||||||
|
"category": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Twilio",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Twist",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Twitter",
|
||||||
|
"category": "Social Media Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "UnleashedSoftware",
|
||||||
|
"category": "Business Process Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Uplead",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Vero",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Vonage",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Webflow",
|
||||||
|
"category": "Creative Design Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Wekan",
|
||||||
|
"category": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Wise",
|
||||||
|
"category": "Financial & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Wordpress",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "XML",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Xero",
|
||||||
|
"category": "Financial & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Yourls",
|
||||||
|
"category": "Marketing & Advertising Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Youtube",
|
||||||
|
"category": "Creative Content & Video Automation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Zendesk",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "ZohoCRM",
|
||||||
|
"category": "CRM & Sales"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Zoom",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "Zulip",
|
||||||
|
"category": "Communication & Messaging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "uProc",
|
||||||
|
"category": "Data Processing & Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"integration": "vectorStorePGVector",
|
||||||
|
"category": "AI Agent Development"
|
||||||
|
}
|
||||||
|
]
|
||||||
8218
context/search_categories.json
Normal file
8218
context/search_categories.json
Normal file
File diff suppressed because it is too large
Load Diff
139
create_categories.py
Normal file
139
create_categories.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def load_def_categories():
|
||||||
|
"""Load the definition categories from def_categories.json"""
|
||||||
|
def_categories_path = Path("context/def_categories.json")
|
||||||
|
with open(def_categories_path, 'r', encoding='utf-8') as f:
|
||||||
|
categories_data = json.load(f)
|
||||||
|
|
||||||
|
# Create a mapping from integration name (lowercase) to category
|
||||||
|
integration_to_category = {}
|
||||||
|
for item in categories_data:
|
||||||
|
integration = item['integration'].lower()
|
||||||
|
category = item['category']
|
||||||
|
integration_to_category[integration] = category
|
||||||
|
|
||||||
|
return integration_to_category
|
||||||
|
|
||||||
|
def extract_tokens_from_filename(filename):
|
||||||
|
"""Extract tokens from filename by splitting on '_' and removing '.json'"""
|
||||||
|
# Remove .json extension
|
||||||
|
name_without_ext = filename.replace('.json', '')
|
||||||
|
|
||||||
|
# Split by underscore
|
||||||
|
tokens = name_without_ext.split('_')
|
||||||
|
|
||||||
|
# Convert to lowercase for matching
|
||||||
|
tokens = [token.lower() for token in tokens if token]
|
||||||
|
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
def find_matching_category(tokens, integration_to_category):
|
||||||
|
"""Find the first matching category for the given tokens"""
|
||||||
|
for token in tokens:
|
||||||
|
if token in integration_to_category:
|
||||||
|
return integration_to_category[token]
|
||||||
|
|
||||||
|
# Try partial matches for common variations
|
||||||
|
for token in tokens:
|
||||||
|
for integration in integration_to_category:
|
||||||
|
if token in integration or integration in token:
|
||||||
|
return integration_to_category[integration]
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Load definition categories
|
||||||
|
integration_to_category = load_def_categories()
|
||||||
|
|
||||||
|
# Get all JSON files from workflows directory
|
||||||
|
workflows_dir = Path("workflows")
|
||||||
|
json_files = list(workflows_dir.glob("*.json"))
|
||||||
|
|
||||||
|
# Process each file
|
||||||
|
search_categories = []
|
||||||
|
|
||||||
|
for json_file in json_files:
|
||||||
|
filename = json_file.name
|
||||||
|
tokens = extract_tokens_from_filename(filename)
|
||||||
|
category = find_matching_category(tokens, integration_to_category)
|
||||||
|
|
||||||
|
search_categories.append({
|
||||||
|
"filename": filename,
|
||||||
|
"category": category
|
||||||
|
})
|
||||||
|
|
||||||
|
# Sort by filename for consistency
|
||||||
|
search_categories.sort(key=lambda x: x['filename'])
|
||||||
|
|
||||||
|
# Write to search_categories.json
|
||||||
|
output_path = Path("context/search_categories.json")
|
||||||
|
with open(output_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(search_categories, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
print(f"Generated search_categories.json with {len(search_categories)} entries")
|
||||||
|
|
||||||
|
# Generate unique categories list for API
|
||||||
|
unique_categories = set()
|
||||||
|
for item in search_categories:
|
||||||
|
if item['category']:
|
||||||
|
unique_categories.add(item['category'])
|
||||||
|
|
||||||
|
# Always include 'Uncategorized' for workflows without categories
|
||||||
|
unique_categories.add('Uncategorized')
|
||||||
|
|
||||||
|
# Sort categories alphabetically
|
||||||
|
categories_list = sorted(list(unique_categories))
|
||||||
|
|
||||||
|
# Write unique categories to a separate file for API consumption
|
||||||
|
categories_output_path = Path("context/unique_categories.json")
|
||||||
|
with open(categories_output_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(categories_list, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
print(f"Generated unique_categories.json with {len(categories_list)} categories")
|
||||||
|
|
||||||
|
# Print some statistics
|
||||||
|
categorized = sum(1 for item in search_categories if item['category'])
|
||||||
|
uncategorized = len(search_categories) - categorized
|
||||||
|
print(f"Categorized: {categorized}, Uncategorized: {uncategorized}")
|
||||||
|
|
||||||
|
# Print detailed category statistics
|
||||||
|
print("\n" + "="*50)
|
||||||
|
print("CATEGORY DISTRIBUTION (Top 20)")
|
||||||
|
print("="*50)
|
||||||
|
|
||||||
|
# Count categories
|
||||||
|
category_counts = {}
|
||||||
|
for item in search_categories:
|
||||||
|
category = item['category'] if item['category'] else "Uncategorized"
|
||||||
|
category_counts[category] = category_counts.get(category, 0) + 1
|
||||||
|
|
||||||
|
# Sort by count (descending)
|
||||||
|
sorted_categories = sorted(category_counts.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
|
||||||
|
# Display top 20
|
||||||
|
for i, (category, count) in enumerate(sorted_categories[:20], 1):
|
||||||
|
print(f"{i:2d}. {category:<40} {count:>4} files")
|
||||||
|
|
||||||
|
if len(sorted_categories) > 20:
|
||||||
|
remaining = len(sorted_categories) - 20
|
||||||
|
print(f"\n... and {remaining} more categories")
|
||||||
|
|
||||||
|
# Write tips on uncategorized workflows
|
||||||
|
print("\n" + "="*50)
|
||||||
|
print("Tips on uncategorized workflows")
|
||||||
|
print("="*50)
|
||||||
|
print("1. At the search, you'll be able to list all uncategorized workflows.")
|
||||||
|
print("2. If the workflow JSON filename has a clear service name (eg. Twilio), it could just be we are missing its category definition at context/def_categories.json.")
|
||||||
|
print("3. You can contribute to the category definitions and then make a pull request to help improve the search experience.")
|
||||||
|
|
||||||
|
|
||||||
|
# Done message
|
||||||
|
print("\n" + "="*50)
|
||||||
|
print("Done! Search re-indexed with categories.")
|
||||||
|
print("="*50)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -302,6 +302,16 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.category-badge {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.workflow-title {
|
.workflow-title {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -623,6 +633,14 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-group">
|
||||||
|
<label for="categoryFilter">Category:</label>
|
||||||
|
<select id="categoryFilter">
|
||||||
|
<option value="all">All Categories</option>
|
||||||
|
<!-- Categories will be populated dynamically -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="activeOnly">
|
<input type="checkbox" id="activeOnly">
|
||||||
@@ -747,14 +765,18 @@
|
|||||||
filters: {
|
filters: {
|
||||||
trigger: 'all',
|
trigger: 'all',
|
||||||
complexity: 'all',
|
complexity: 'all',
|
||||||
|
category: 'all',
|
||||||
activeOnly: false
|
activeOnly: false
|
||||||
}
|
},
|
||||||
|
categories: [],
|
||||||
|
categoryMap: new Map()
|
||||||
};
|
};
|
||||||
|
|
||||||
this.elements = {
|
this.elements = {
|
||||||
searchInput: document.getElementById('searchInput'),
|
searchInput: document.getElementById('searchInput'),
|
||||||
triggerFilter: document.getElementById('triggerFilter'),
|
triggerFilter: document.getElementById('triggerFilter'),
|
||||||
complexityFilter: document.getElementById('complexityFilter'),
|
complexityFilter: document.getElementById('complexityFilter'),
|
||||||
|
categoryFilter: document.getElementById('categoryFilter'),
|
||||||
activeOnlyFilter: document.getElementById('activeOnly'),
|
activeOnlyFilter: document.getElementById('activeOnly'),
|
||||||
themeToggle: document.getElementById('themeToggle'),
|
themeToggle: document.getElementById('themeToggle'),
|
||||||
resultsCount: document.getElementById('resultsCount'),
|
resultsCount: document.getElementById('resultsCount'),
|
||||||
@@ -839,6 +861,16 @@
|
|||||||
this.resetAndSearch();
|
this.resetAndSearch();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.elements.categoryFilter.addEventListener('change', (e) => {
|
||||||
|
const selectedCategory = e.target.value;
|
||||||
|
console.log(`Category filter changed to: ${selectedCategory}`);
|
||||||
|
console.log('Current category map size:', this.state.categoryMap.size);
|
||||||
|
|
||||||
|
this.state.filters.category = selectedCategory;
|
||||||
|
this.state.currentPage = 1;
|
||||||
|
this.resetAndSearch();
|
||||||
|
});
|
||||||
|
|
||||||
this.elements.activeOnlyFilter.addEventListener('change', (e) => {
|
this.elements.activeOnlyFilter.addEventListener('change', (e) => {
|
||||||
this.state.filters.activeOnly = e.target.checked;
|
this.state.filters.activeOnly = e.target.checked;
|
||||||
this.state.currentPage = 1;
|
this.state.currentPage = 1;
|
||||||
@@ -942,18 +974,96 @@
|
|||||||
this.showState('loading');
|
this.showState('loading');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Load categories first, then stats and workflows
|
||||||
|
console.log('Loading categories...');
|
||||||
|
await this.loadCategories();
|
||||||
|
|
||||||
|
console.log('Categories loaded, populating filter...');
|
||||||
|
this.populateCategoryFilter();
|
||||||
|
|
||||||
// Load stats and workflows in parallel
|
// Load stats and workflows in parallel
|
||||||
const [stats, workflows] = await Promise.all([
|
console.log('Loading stats and workflows...');
|
||||||
|
const [stats] = await Promise.all([
|
||||||
this.apiCall('/stats'),
|
this.apiCall('/stats'),
|
||||||
this.loadWorkflows(true)
|
this.loadWorkflows(true)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.updateStatsDisplay(stats);
|
this.updateStatsDisplay(stats);
|
||||||
|
console.log('Initial data loading complete');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('Error during initial data loading:', error);
|
||||||
this.showError('Failed to load data: ' + error.message);
|
this.showError('Failed to load data: ' + error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadCategories() {
|
||||||
|
try {
|
||||||
|
console.log('Loading categories from API...');
|
||||||
|
|
||||||
|
// Load categories and mappings in parallel from API
|
||||||
|
const [categoriesResponse, mappingsResponse] = await Promise.all([
|
||||||
|
this.apiCall('/categories'),
|
||||||
|
this.apiCall('/category-mappings')
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Set categories from API
|
||||||
|
this.state.categories = categoriesResponse.categories || ['Uncategorized'];
|
||||||
|
|
||||||
|
// Build category map from API mappings
|
||||||
|
const categoryMap = new Map();
|
||||||
|
const mappings = mappingsResponse.mappings || {};
|
||||||
|
|
||||||
|
Object.entries(mappings).forEach(([filename, category]) => {
|
||||||
|
categoryMap.set(filename, category || 'Uncategorized');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.state.categoryMap = categoryMap;
|
||||||
|
|
||||||
|
console.log(`Successfully loaded ${this.state.categories.length} categories from API:`, this.state.categories);
|
||||||
|
console.log(`Loaded ${categoryMap.size} category mappings from API`);
|
||||||
|
|
||||||
|
return { categories: this.state.categories, mappings: mappings };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load categories from API:', error);
|
||||||
|
// Set default categories if loading fails
|
||||||
|
this.state.categories = ['Uncategorized'];
|
||||||
|
this.state.categoryMap = new Map();
|
||||||
|
return { categories: this.state.categories, mappings: {} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
populateCategoryFilter() {
|
||||||
|
const select = this.elements.categoryFilter;
|
||||||
|
|
||||||
|
if (!select) {
|
||||||
|
console.error('Category filter element not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Populating category filter with:', this.state.categories);
|
||||||
|
|
||||||
|
// Clear existing options except "All Categories"
|
||||||
|
while (select.children.length > 1) {
|
||||||
|
select.removeChild(select.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.categories.length === 0) {
|
||||||
|
console.warn('No categories available to populate filter');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add categories in alphabetical order
|
||||||
|
this.state.categories.forEach(category => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = category;
|
||||||
|
option.textContent = category;
|
||||||
|
select.appendChild(option);
|
||||||
|
console.log(`Added category option: ${category}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Category filter populated with ${select.options.length - 1} categories`);
|
||||||
|
}
|
||||||
|
|
||||||
async loadWorkflows(reset = false) {
|
async loadWorkflows(reset = false) {
|
||||||
if (reset) {
|
if (reset) {
|
||||||
this.state.currentPage = 1;
|
this.state.currentPage = 1;
|
||||||
@@ -963,25 +1073,65 @@
|
|||||||
this.state.isLoading = true;
|
this.state.isLoading = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params = new URLSearchParams({
|
// If category filtering is active, we need to load all workflows to filter properly
|
||||||
q: this.state.searchQuery,
|
const needsAllWorkflows = this.state.filters.category !== 'all' && reset;
|
||||||
trigger: this.state.filters.trigger,
|
|
||||||
complexity: this.state.filters.complexity,
|
|
||||||
active_only: this.state.filters.activeOnly,
|
|
||||||
page: this.state.currentPage,
|
|
||||||
per_page: this.state.perPage
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await this.apiCall(`/workflows?${params}`);
|
let allWorkflows = [];
|
||||||
|
let totalCount = 0;
|
||||||
|
let totalPages = 1;
|
||||||
|
|
||||||
if (reset) {
|
if (needsAllWorkflows) {
|
||||||
this.state.workflows = response.workflows;
|
// Load all workflows in batches for category filtering
|
||||||
|
console.log('Loading all workflows for category filtering...');
|
||||||
|
allWorkflows = await this.loadAllWorkflowsForCategoryFiltering();
|
||||||
|
|
||||||
|
// Apply client-side category filtering
|
||||||
|
console.log(`Filtering ${allWorkflows.length} workflows for category: ${this.state.filters.category}`);
|
||||||
|
console.log('Category map size:', this.state.categoryMap.size);
|
||||||
|
|
||||||
|
let matchCount = 0;
|
||||||
|
const filteredWorkflows = allWorkflows.filter(workflow => {
|
||||||
|
const workflowCategory = this.getWorkflowCategory(workflow.filename);
|
||||||
|
const matches = workflowCategory === this.state.filters.category;
|
||||||
|
|
||||||
|
// Debug: log first few matches/non-matches
|
||||||
|
if (matchCount < 5 || (!matches && matchCount < 3)) {
|
||||||
|
console.log(`${workflow.filename}: ${workflowCategory} ${matches ? '===' : '!=='} ${this.state.filters.category}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches) matchCount++;
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Filtered from ${allWorkflows.length} to ${filteredWorkflows.length} workflows`);
|
||||||
|
allWorkflows = filteredWorkflows;
|
||||||
|
totalCount = filteredWorkflows.length;
|
||||||
|
totalPages = 1; // All results loaded, no pagination needed
|
||||||
} else {
|
} else {
|
||||||
this.state.workflows.push(...response.workflows);
|
// Normal pagination
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
q: this.state.searchQuery,
|
||||||
|
trigger: this.state.filters.trigger,
|
||||||
|
complexity: this.state.filters.complexity,
|
||||||
|
active_only: this.state.filters.activeOnly,
|
||||||
|
page: this.state.currentPage,
|
||||||
|
per_page: this.state.perPage
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await this.apiCall(`/workflows?${params}`);
|
||||||
|
allWorkflows = response.workflows;
|
||||||
|
totalCount = response.total;
|
||||||
|
totalPages = response.pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.totalCount = response.total;
|
if (reset) {
|
||||||
this.state.totalPages = response.pages;
|
this.state.workflows = allWorkflows;
|
||||||
|
this.state.totalCount = totalCount;
|
||||||
|
this.state.totalPages = totalPages;
|
||||||
|
} else {
|
||||||
|
this.state.workflows.push(...allWorkflows);
|
||||||
|
}
|
||||||
|
|
||||||
this.updateUI();
|
this.updateUI();
|
||||||
|
|
||||||
@@ -992,6 +1142,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadAllWorkflowsForCategoryFiltering() {
|
||||||
|
const allWorkflows = [];
|
||||||
|
let currentPage = 1;
|
||||||
|
const maxPerPage = 100; // API limit
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
q: this.state.searchQuery,
|
||||||
|
trigger: this.state.filters.trigger,
|
||||||
|
complexity: this.state.filters.complexity,
|
||||||
|
active_only: this.state.filters.activeOnly,
|
||||||
|
page: currentPage,
|
||||||
|
per_page: maxPerPage
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await this.apiCall(`/workflows?${params}`);
|
||||||
|
allWorkflows.push(...response.workflows);
|
||||||
|
|
||||||
|
console.log(`Loaded page ${currentPage}/${response.pages} (${response.workflows.length} workflows)`);
|
||||||
|
|
||||||
|
if (currentPage >= response.pages) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Loaded total of ${allWorkflows.length} workflows for filtering`);
|
||||||
|
return allWorkflows;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorkflowCategory(filename) {
|
||||||
|
const category = this.state.categoryMap.get(filename);
|
||||||
|
const result = category && category.trim() ? category : 'Uncategorized';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
async loadMoreWorkflows() {
|
async loadMoreWorkflows() {
|
||||||
if (this.state.currentPage >= this.state.totalPages) return;
|
if (this.state.currentPage >= this.state.totalPages) return;
|
||||||
|
|
||||||
@@ -1025,12 +1212,19 @@
|
|||||||
updateResultsCount() {
|
updateResultsCount() {
|
||||||
const count = this.state.totalCount;
|
const count = this.state.totalCount;
|
||||||
const query = this.state.searchQuery;
|
const query = this.state.searchQuery;
|
||||||
|
const category = this.state.filters.category;
|
||||||
|
|
||||||
if (query) {
|
let text = `${count.toLocaleString()} workflows`;
|
||||||
this.elements.resultsCount.textContent = `${count.toLocaleString()} workflows found for "${query}"`;
|
|
||||||
} else {
|
if (query && category !== 'all') {
|
||||||
this.elements.resultsCount.textContent = `${count.toLocaleString()} workflows`;
|
text += ` found for "${query}" in "${category}"`;
|
||||||
|
} else if (query) {
|
||||||
|
text += ` found for "${query}"`;
|
||||||
|
} else if (category !== 'all') {
|
||||||
|
text += ` in "${category}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.elements.resultsCount.textContent = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderWorkflows() {
|
renderWorkflows() {
|
||||||
@@ -1048,6 +1242,7 @@
|
|||||||
createWorkflowCard(workflow) {
|
createWorkflowCard(workflow) {
|
||||||
const statusClass = workflow.active ? 'status-active' : 'status-inactive';
|
const statusClass = workflow.active ? 'status-active' : 'status-inactive';
|
||||||
const complexityClass = `complexity-${workflow.complexity}`;
|
const complexityClass = `complexity-${workflow.complexity}`;
|
||||||
|
const category = this.getWorkflowCategory(workflow.filename);
|
||||||
|
|
||||||
const integrations = workflow.integrations.slice(0, 5).map(integration =>
|
const integrations = workflow.integrations.slice(0, 5).map(integration =>
|
||||||
`<span class="integration-tag">${this.escapeHtml(integration)}</span>`
|
`<span class="integration-tag">${this.escapeHtml(integration)}</span>`
|
||||||
@@ -1064,6 +1259,7 @@
|
|||||||
<div class="status-dot ${statusClass}"></div>
|
<div class="status-dot ${statusClass}"></div>
|
||||||
<div class="complexity-dot ${complexityClass}"></div>
|
<div class="complexity-dot ${complexityClass}"></div>
|
||||||
<span>${workflow.node_count} nodes</span>
|
<span>${workflow.node_count} nodes</span>
|
||||||
|
<span class="category-badge">${this.escapeHtml(category)}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="trigger-badge">${this.escapeHtml(workflow.trigger_type)}</span>
|
<span class="trigger-badge">${this.escapeHtml(workflow.trigger_type)}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -1090,12 +1286,14 @@
|
|||||||
this.elements.modalDescription.textContent = workflow.description;
|
this.elements.modalDescription.textContent = workflow.description;
|
||||||
|
|
||||||
// Update stats
|
// Update stats
|
||||||
|
const category = this.getWorkflowCategory(workflow.filename);
|
||||||
this.elements.modalStats.innerHTML = `
|
this.elements.modalStats.innerHTML = `
|
||||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem;">
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem;">
|
||||||
<div><strong>Status:</strong> ${workflow.active ? 'Active' : 'Inactive'}</div>
|
<div><strong>Status:</strong> ${workflow.active ? 'Active' : 'Inactive'}</div>
|
||||||
<div><strong>Trigger:</strong> ${workflow.trigger_type}</div>
|
<div><strong>Trigger:</strong> ${workflow.trigger_type}</div>
|
||||||
<div><strong>Complexity:</strong> ${workflow.complexity}</div>
|
<div><strong>Complexity:</strong> ${workflow.complexity}</div>
|
||||||
<div><strong>Nodes:</strong> ${workflow.node_count}</div>
|
<div><strong>Nodes:</strong> ${workflow.node_count}</div>
|
||||||
|
<div><strong>Category:</strong> ${this.escapeHtml(category)}</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user