refactor: replace Wizard with CoopBuilder in navigation, enhance budget store structure, and streamline template components for improved user experience
This commit is contained in:
parent
eede87a273
commit
f67b138d95
33 changed files with 4970 additions and 2451 deletions
178
data/offerTemplates.ts
Normal file
178
data/offerTemplates.ts
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import type { OfferTemplate } from '~/composables/useOfferSuggestor';
|
||||
|
||||
export const offerTemplates: OfferTemplate[] = [
|
||||
{
|
||||
id: 'pitch-polish',
|
||||
name: 'Pitch Polish',
|
||||
type: 'clinic',
|
||||
skillRequirements: ['writing', 'design'],
|
||||
problemTargets: ['unclear-pitch', 'grant-budget-help'],
|
||||
scope: [
|
||||
'Comprehensive deck review and analysis',
|
||||
'Rewrite key sections for clarity and impact',
|
||||
'90-minute live presentation coaching session',
|
||||
'Final edit pass with visual polish'
|
||||
],
|
||||
defaultDays: 2,
|
||||
defaultHours: {
|
||||
writing: 6,
|
||||
design: 6,
|
||||
pm: 2
|
||||
},
|
||||
whyThisTemplate: [
|
||||
'Combines writing expertise with design polish',
|
||||
'Time-boxed format keeps scope manageable',
|
||||
'Live coaching session builds confidence',
|
||||
'Immediate impact on funding success'
|
||||
],
|
||||
riskTemplate: 'Client may resist feedback on core concept or messaging'
|
||||
},
|
||||
{
|
||||
id: 'brand-store-page-sprint',
|
||||
name: 'Brand/Store Page Sprint',
|
||||
type: 'sprint',
|
||||
skillRequirements: ['design', 'dev'],
|
||||
problemTargets: ['need-landing-store-page', 'marketing-assets'],
|
||||
scope: [
|
||||
'Develop clear messaging and brand voice',
|
||||
'Design and build one-page marketing site',
|
||||
'Create store page assets and layout',
|
||||
'Implement responsive design and basic SEO'
|
||||
],
|
||||
defaultDays: 7,
|
||||
defaultHours: {
|
||||
design: 12,
|
||||
writing: 6,
|
||||
dev: 10,
|
||||
pm: 4
|
||||
},
|
||||
whyThisTemplate: [
|
||||
'Full-stack approach from concept to deployment',
|
||||
'Combines brand strategy with technical execution',
|
||||
'Creates immediate market presence',
|
||||
'Scalable foundation for future marketing'
|
||||
],
|
||||
riskTemplate: 'Scope creep around additional pages or complex integrations'
|
||||
},
|
||||
{
|
||||
id: 'dev-sprint',
|
||||
name: 'Dev Sprint',
|
||||
type: 'sprint',
|
||||
skillRequirements: ['dev'],
|
||||
problemTargets: ['vertical-slice', 'tech-debt'],
|
||||
scope: [
|
||||
'Backlog triage and feature prioritization',
|
||||
'Implement 1-2 focused features or fixes',
|
||||
'Create demo build for stakeholder review',
|
||||
'Document changes and deployment process'
|
||||
],
|
||||
defaultDays: 7,
|
||||
defaultHours: {
|
||||
dev: 24,
|
||||
qa: 4,
|
||||
pm: 4
|
||||
},
|
||||
whyThisTemplate: [
|
||||
'Focused development with clear deliverables',
|
||||
'Includes quality assurance and project management',
|
||||
'Demo build provides immediate feedback opportunity',
|
||||
'Manageable scope reduces technical risk'
|
||||
],
|
||||
riskTemplate: 'Technical complexity may exceed initial estimates'
|
||||
},
|
||||
{
|
||||
id: 'maintenance-retainer',
|
||||
name: 'Maintenance Retainer',
|
||||
type: 'retainer',
|
||||
skillRequirements: ['dev', 'pm'],
|
||||
problemTargets: ['launch-checklist'],
|
||||
scope: [
|
||||
'Handle small fixes and bug reports',
|
||||
'Apply security and dependency updates',
|
||||
'Provide technical support and guidance',
|
||||
'Monthly progress reports and recommendations'
|
||||
],
|
||||
defaultDays: 30, // Monthly
|
||||
defaultHours: {
|
||||
dev: 6,
|
||||
pm: 2
|
||||
},
|
||||
whyThisTemplate: [
|
||||
'Predictable monthly income stream',
|
||||
'Builds long-term client relationships',
|
||||
'Low-risk work with defined boundaries',
|
||||
'Efficient use of development skills'
|
||||
],
|
||||
riskTemplate: 'Client expectations may exceed allocated hours'
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Lightweight matching rules for offer templates
|
||||
*/
|
||||
export function matchTemplateToInput(
|
||||
selectedSkills: string[],
|
||||
selectedProblems: string[]
|
||||
): OfferTemplate[] {
|
||||
const matches: OfferTemplate[] = [];
|
||||
|
||||
// Pitch Polish: Writing + Design + pitch/funding problems
|
||||
if (selectedSkills.includes('writing') && selectedSkills.includes('design')) {
|
||||
if (selectedProblems.includes('unclear-pitch') || selectedProblems.includes('grant-budget-help')) {
|
||||
matches.push(offerTemplates[0]); // Pitch Polish
|
||||
}
|
||||
}
|
||||
|
||||
// Brand/Store Page: Design + Dev + website/marketing problems
|
||||
if (selectedSkills.includes('design') && selectedSkills.includes('dev')) {
|
||||
if (selectedProblems.includes('need-landing-store-page') || selectedProblems.includes('marketing-assets')) {
|
||||
matches.push(offerTemplates[1]); // Brand/Store Page Sprint
|
||||
}
|
||||
}
|
||||
|
||||
// Dev Sprint: Dev + development-related problems
|
||||
if (selectedSkills.includes('dev')) {
|
||||
if (selectedProblems.includes('vertical-slice') || selectedProblems.includes('tech-debt')) {
|
||||
matches.push(offerTemplates[2]); // Dev Sprint
|
||||
}
|
||||
}
|
||||
|
||||
// Maintenance Retainer: Dev + PM + launch/maintenance problems
|
||||
if (selectedSkills.includes('dev') && selectedSkills.includes('pm')) {
|
||||
if (selectedProblems.includes('launch-checklist') || matches.length === 0) { // Also use as fallback
|
||||
matches.push(offerTemplates[3]); // Maintenance Retainer
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default hour allocation for a template based on available members
|
||||
*/
|
||||
export function getTemplateHours(
|
||||
template: OfferTemplate,
|
||||
availableSkills: string[]
|
||||
): Array<{ skill: string; hours: number }> {
|
||||
const allocations: Array<{ skill: string; hours: number }> = [];
|
||||
|
||||
// Convert template hours to skill-based allocations
|
||||
if (template.defaultHours) {
|
||||
Object.entries(template.defaultHours).forEach(([skill, hours]) => {
|
||||
if (availableSkills.includes(skill)) {
|
||||
allocations.push({ skill, hours });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return allocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total hours for a template
|
||||
*/
|
||||
export function getTemplateTotalHours(template: OfferTemplate): number {
|
||||
if (!template.defaultHours) return template.defaultDays * 8; // Fallback: 8 hours per day
|
||||
|
||||
return Object.values(template.defaultHours).reduce((sum, hours) => sum + hours, 0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue