feat: add use cases marketing product with AI-generated content

Transform qualifying scenarios into marketing-ready use cases with:
- AI-generated titles, descriptions, ROI estimates, business value
- Persona/industry/category taxonomy for targeting
- Browseable feed with filtering and ranking
- SEO-optimized case study pages
- Daily cron job for generation and ranking

Database:
- Add Persona, Industry, Category lookup tables
- Add UseCase model with marketing content fields
- Add junction tables for personas/industries/categories
- Add SocialProof model for cached metrics

API:
- GET /api/use-cases - Global directory with filtering
- GET /api/use-cases/[id] - Individual use case details
- GET /api/public/users/[username]/collections/[slug]/use-cases
- POST /api/cron/use-cases - Nightly generation job

Frontend:
- /use-cases - Global feed with persona dropdown
- /use-cases/[slug] - SEO case study page
- /[username]/collections/[slug]/use-cases - Collection feed
- UseCasesFeed component - Sortable table component
- UseCaseCaseStudy component - Full case study layout
This commit is contained in:
Ajax Davis 2026-01-20 16:17:35 +10:00
parent 5fafc90e3e
commit 48c41e8733
245 changed files with 4734 additions and 2075 deletions

View file

@ -76,14 +76,14 @@ function chunkBySentences(
if (overlap > 0) {
// Calculate how many sentences to include for overlap
let overlapText = '';
let overlapSentences = 0;
let _overlapSentences = 0;
for (let j = i - 1; j >= 0 && overlapText.length < overlap; j--) {
const overlapSentence = sentences[j];
if (!overlapSentence) continue;
if (overlapText.length + overlapSentence.length <= overlap) {
overlapText = `${overlapSentence} ${overlapText}`;
overlapSentences++;
_overlapSentences++;
} else {
break;
}
@ -221,7 +221,7 @@ export const textChunkTool = tool({
if (chunks.length === 0) {
chunks = chunkBySize(text, maxChunkSize, overlap);
}
} catch (error) {
} catch (_error) {
// Fall back to simple size-based chunking if sentence detection fails
chunks = chunkBySize(text, maxChunkSize, overlap);
}