From 0ba2c5224e1d19b1f7c3d5c4b2ee90a251ffb42d Mon Sep 17 00:00:00 2001 From: Bimex Dev Date: Tue, 30 Jun 2026 01:36:18 +0100 Subject: [PATCH] feat: Add submission quality checklist to project form - Created SubmissionChecklist component with visual progress tracking - Tracks required fields (name, website, description) and optional quality indicators - Shows quality score (0-100%) based on completed items - Visual feedback with color-coded status (green=complete, orange=required, gray=optional) - Displays completion stats for required vs optional fields - Non-blocking: checklist guides submitters but doesn't prevent submission if required fields are filled - Integrated into both new project and edit project flows - Real-time updates as user fills out the form - Added comprehensive feature documentation --- dongle/SUBMISSION_CHECKLIST_FEATURE.md | 122 +++++++++ dongle/components/projects/ProjectForm.tsx | 18 ++ .../projects/SubmissionChecklist.tsx | 258 ++++++++++++++++++ 3 files changed, 398 insertions(+) create mode 100644 dongle/SUBMISSION_CHECKLIST_FEATURE.md create mode 100644 dongle/components/projects/SubmissionChecklist.tsx diff --git a/dongle/SUBMISSION_CHECKLIST_FEATURE.md b/dongle/SUBMISSION_CHECKLIST_FEATURE.md new file mode 100644 index 0000000..ffbf860 --- /dev/null +++ b/dongle/SUBMISSION_CHECKLIST_FEATURE.md @@ -0,0 +1,122 @@ +# Submission Quality Checklist Feature + +## Overview +Added a visual quality checklist to guide project submitters on creating high-quality listings. The checklist appears in both new project submission and project edit flows. + +## Implementation + +### New Component: `SubmissionChecklist` +Location: `dongle/components/projects/SubmissionChecklist.tsx` + +**Features:** +- Real-time progress tracking as users fill out the form +- Quality score calculation (0-100%) with visual progress bar +- Color-coded status indicators: + - ✅ Green: Completed items + - ⚠️ Orange: Required fields not yet completed + - ⭕ Gray: Optional fields not yet completed +- Separate tracking for required vs optional fields +- Non-blocking: guides users but doesn't prevent submission + +### Checklist Items + +**Required Fields:** +- ✅ Project Name (minimum 3 characters) +- ✅ Project Website (active website URL) +- ✅ Description (10-500 characters) + +**Optional Quality Indicators:** +- Logo URL (for better visibility) +- Documentation URL (helps users understand the project) +- Repository URL (GitHub/GitLab/Bitbucket for transparency) +- Audit Report URL (builds trust with security audit) +- Bug Bounty Program URL (shows commitment to security) + +### Quality Score Algorithm +- Required fields weight: 60% +- Optional fields weight: 40% +- Score ranges: + - 100%: Perfect - all items completed + - 80-99%: Excellent quality listing + - 60-79%: Good quality, consider adding optional items + - 40-59%: Basic listing, add more details for better visibility + - <40%: Complete required fields to submit + +### Integration Points +- Integrated into `ProjectForm` component +- Appears in both `/projects/new` and `/projects/[id]/edit` flows +- Updates in real-time as form fields change +- Works seamlessly with existing draft autosave feature + +## User Experience + +### Visual Feedback +1. **Progress Bar**: Shows overall completion percentage with color gradient +2. **Stats Cards**: Displays required (X/Y) and optional (X/Y) completion counts +3. **Item List**: Each checklist item shows: + - Completion status icon + - "Required" badge for mandatory fields + - Item label and description + - Color-coded background based on status + +### Quality Messages +- Provides contextual feedback based on quality score +- Encourages users to complete optional fields for better visibility +- Clear messaging about submission requirements + +## Acceptance Criteria Met + +✅ **Checklist appears near the submit/edit flow** +- Positioned prominently in the form, right after draft indicator +- Visible in both new submission and edit pages + +✅ **Completed items are visually tracked** +- Real-time updates as users type +- Visual progress bar with percentage +- Color-coded status for each item +- Separate counters for required vs optional + +✅ **Checklist does not block submission** +- Only enforces required fields (name, website, description) +- Optional items improve quality score but don't prevent submission +- Clear messaging distinguishes required from optional + +## Technical Details + +### Props Interface +```typescript +interface SubmissionChecklistProps { + formData: { + name?: string; + websiteUrl?: string; + githubUrl?: string; + logoUrl?: string; + docsUrl?: string; + auditReportUrl?: string; + bugBountyUrl?: string; + description?: string; + }; + className?: string; +} +``` + +### State Management +- Uses `useMemo` for efficient checklist calculation +- Watches form values via React Hook Form's `watch()` API +- No additional state management required + +## Benefits + +1. **Improved Listing Quality**: Guides submitters to provide comprehensive information +2. **User Education**: Teaches best practices for project submissions +3. **Better Discovery**: More complete listings improve user trust and engagement +4. **Non-Intrusive**: Doesn't block submission, just guides and encourages +5. **Visual Feedback**: Clear, immediate feedback on submission quality + +## Future Enhancements (Optional) + +- Add tooltips with examples for each field +- Link to documentation/guide on creating quality listings +- Add verification status integration when verification feature is used +- Export checklist as a shareable quality report +- Add contract ID fields when smart contract integration is ready diff --git a/dongle/components/projects/ProjectForm.tsx b/dongle/components/projects/ProjectForm.tsx index 152e22f..0143ae4 100644 --- a/dongle/components/projects/ProjectForm.tsx +++ b/dongle/components/projects/ProjectForm.tsx @@ -16,6 +16,7 @@ import TransactionProgressPanel from "@/components/transactions/TransactionProgr import { useOnChainTransaction } from "@/hooks/useOnChainTransaction"; import { useDraft } from "@/hooks/useDraft"; import { DraftIndicator } from "@/components/projects/DraftIndicator"; +import { SubmissionChecklist } from "@/components/projects/SubmissionChecklist"; import { Button } from "@/components/ui/Button"; import { Card } from "@/components/ui/Card"; @@ -145,6 +146,9 @@ export default function ProjectForm({ useUnsavedChanges(isDirty, isSubmitting); + // Watch form values for checklist + const watchedValues = watch(); + // Auto-save draft when form changes useEffect(() => { const subscription = watch((formData) => { @@ -290,6 +294,20 @@ export default function ProjectForm({ onDiscard={handleDiscardDraft} /> + {/* Quality Checklist */} + +
(() => { + const items: ChecklistItem[] = [ + { + id: "name", + label: "Project Name", + description: "Clear, unique name (minimum 3 characters)", + required: true, + completed: (formData.name?.trim().length ?? 0) >= 3, + }, + { + id: "website", + label: "Project Website", + description: "Active website with project information", + required: true, + completed: !!formData.websiteUrl && formData.websiteUrl.trim().length > 0, + }, + { + id: "description", + label: "Description", + description: "Clear explanation of what your project does (10-500 characters)", + required: true, + completed: (formData.description?.trim().length ?? 0) >= 10, + }, + { + id: "logo", + label: "Logo URL", + description: "High-quality project logo for better visibility", + required: false, + completed: !!formData.logoUrl && formData.logoUrl.trim().length > 0, + }, + { + id: "docs", + label: "Documentation", + description: "Developer or user documentation to help users understand your project", + required: false, + completed: !!formData.docsUrl && formData.docsUrl.trim().length > 0, + }, + { + id: "repository", + label: "Repository URL", + description: "Link to GitHub, GitLab, or Bitbucket repository for transparency", + required: false, + completed: !!formData.githubUrl && formData.githubUrl.trim().length > 0, + }, + { + id: "audit", + label: "Audit Report", + description: "Security audit report to build trust with users", + required: false, + completed: !!formData.auditReportUrl && formData.auditReportUrl.trim().length > 0, + }, + { + id: "bugBounty", + label: "Bug Bounty Program", + description: "Active bug bounty program showing commitment to security", + required: false, + completed: !!formData.bugBountyUrl && formData.bugBountyUrl.trim().length > 0, + }, + ]; + + return items; + }, [formData]); + + const stats = useMemo(() => { + const required = checklist.filter((item) => item.required); + const optional = checklist.filter((item) => !item.required); + const requiredCompleted = required.filter((item) => item.completed).length; + const optionalCompleted = optional.filter((item) => item.completed).length; + + return { + requiredTotal: required.length, + requiredCompleted, + optionalTotal: optional.length, + optionalCompleted, + totalCompleted: requiredCompleted + optionalCompleted, + total: checklist.length, + }; + }, [checklist]); + + const qualityScore = useMemo(() => { + const requiredWeight = 0.6; + const optionalWeight = 0.4; + + const requiredScore = + stats.requiredTotal > 0 ? stats.requiredCompleted / stats.requiredTotal : 1; + const optionalScore = + stats.optionalTotal > 0 ? stats.optionalCompleted / stats.optionalTotal : 0; + + return Math.round((requiredScore * requiredWeight + optionalScore * optionalWeight) * 100); + }, [stats]); + + const getQualityMessage = (score: number) => { + if (score === 100) return { text: "Perfect! All checklist items completed", color: "text-green-600 dark:text-green-400" }; + if (score >= 80) return { text: "Excellent quality listing", color: "text-green-600 dark:text-green-400" }; + if (score >= 60) return { text: "Good quality, consider adding optional items", color: "text-blue-600 dark:text-blue-400" }; + if (score >= 40) return { text: "Basic listing, add more details for better visibility", color: "text-yellow-600 dark:text-yellow-400" }; + return { text: "Complete required fields to submit", color: "text-orange-600 dark:text-orange-400" }; + }; + + const qualityMessage = getQualityMessage(qualityScore); + const canSubmit = stats.requiredCompleted === stats.requiredTotal; + + return ( + +
+ {/* Header */} +
+
+ +
+
+

Submission Quality Checklist

+

+ Complete all required fields to submit. Optional items improve visibility and trust. +

+
+
+ + {/* Quality Score */} +
+
+ + Quality Score + + + {qualityScore}% + +
+
+
= 80 + ? "bg-green-500" + : qualityScore >= 60 + ? "bg-blue-500" + : qualityScore >= 40 + ? "bg-yellow-500" + : "bg-orange-500" + }`} + style={{ width: `${qualityScore}%` }} + /> +
+

+ {qualityMessage.text} +

+
+ + {/* Status Summary */} +
+
+
Required
+
+ {stats.requiredCompleted}/{stats.requiredTotal} +
+
+
+
Optional
+
+ {stats.optionalCompleted}/{stats.optionalTotal} +
+
+
+ + {/* Checklist Items */} +
+ {checklist.map((item) => ( +
+
+ {item.completed ? ( + + ) : item.required ? ( + + ) : ( + + )} +
+
+
+ + {item.label} + + {item.required && ( + + Required + + )} +
+

+ {item.description} +

+
+
+ ))} +
+ + {/* Submission Status */} + {!canSubmit && ( +
+

+ + Complete all required fields to enable submission +

+
+ )} +
+ + ); +}