From de57d8a6bf81dfb42a7b9b2a42b5a0221354f9b1 Mon Sep 17 00:00:00 2001 From: Adeshina Mubaraq Date: Mon, 27 Jul 2026 19:45:54 +0000 Subject: [PATCH 1/3] feat: add contract template recommendation engine (#563) --- .../templates/TemplateRecommender.jsx | 153 ++++++++++++++ src/lib/templateFeedbackStore.ts | 134 ++++++++++++ src/lib/templateManager.ts | 198 +++++++++++++++++- src/lib/templateRecommendation.test.ts | 119 +++++++++++ src/lib/templateRecommendation.ts | 191 +++++++++++++++++ 5 files changed, 793 insertions(+), 2 deletions(-) create mode 100644 src/components/templates/TemplateRecommender.jsx create mode 100644 src/lib/templateFeedbackStore.ts create mode 100644 src/lib/templateRecommendation.test.ts create mode 100644 src/lib/templateRecommendation.ts diff --git a/src/components/templates/TemplateRecommender.jsx b/src/components/templates/TemplateRecommender.jsx new file mode 100644 index 00000000..e7aadca2 --- /dev/null +++ b/src/components/templates/TemplateRecommender.jsx @@ -0,0 +1,153 @@ +import React, { useEffect, useMemo, useState } from 'react' +import { + recommendTemplates, + requirementSignature, +} from '../../lib/templateRecommendation' +import { templateFeedbackStore } from '../../lib/templateFeedbackStore' +import TemplateCustomizer from './TemplateCustomizer' + +/** + * TemplateRecommender — Issue #563 + * + * Lets a user describe what they want to build, shows ranked contract-template + * suggestions with the reasons each was picked, and hands the chosen template + * off to the existing TemplateCustomizer. Choosing a template records feedback + * so future suggestions for similar needs improve. + */ +export default function TemplateRecommender() { + const [description, setDescription] = useState('') + const [category, setCategory] = useState('') + const [tagsInput, setTagsInput] = useState('') + const [results, setResults] = useState([]) + const [chosen, setChosen] = useState(null) + const [searched, setSearched] = useState(false) + + // Warm the feedback store once so getBoost has data to read. + useEffect(() => { + templateFeedbackStore.initialize().catch(() => { + // Non-fatal: recommender still works without persisted feedback. + }) + }, []) + + const requirement = useMemo(() => { + const tags = tagsInput + .split(',') + .map((t) => t.trim()) + .filter(Boolean) + return { + description: description.trim() || undefined, + category: category || undefined, + tags: tags.length ? tags : undefined, + } + }, [description, category, tagsInput]) + + const handleRecommend = () => { + const ranked = recommendTemplates(requirement, { + boost: templateFeedbackStore.getBoost, + limit: 5, + }) + setResults(ranked) + setSearched(true) + setChosen(null) + } + + const handleChoose = (template) => { + const signature = requirementSignature(requirement) + templateFeedbackStore.recordChoice(signature, template.id).catch(() => { + // Non-fatal: choosing still proceeds even if persistence fails. + }) + setChosen(template) + } + + if (chosen) { + return ( +
+ + +
+ ) + } + + return ( +
+

Find the right contract template

+

Describe what you want to build and we'll suggest a starting point.

+ + +