Skip to content

Commit 9582e2e

Browse files
ChanMeng666claude
andcommitted
feat: add multiple assessment types and gamification features
Transform FriendScope into a comprehensive relationship management platform with new assessment types, dashboard, achievements, and engagement features. New Assessment Types: - Add Workplace Relationship Assessment (16 questions, 8 categories) * Professional collaboration, communication, boundaries, trust * Orange theme with Briefcase icon - Add Romantic Relationship Assessment (20 questions, 10 categories) * Emotional intimacy, trust, communication, commitment, passion * Pink theme with HeartHandshake icon Dashboard Features (/dashboard): - Multi-type relationship overview with radar chart comparison - Pie chart showing assessment distribution - Overall statistics (total assessments, average score, unique people) - Top 3 strongest relationships display - Bottom 3 relationships needing attention - Click-through navigation to specific assessments Achievement System (/achievements): - 16 achievements across 5 categories: * Milestone: Complete 1/5/10/25/50 assessments * Quality: Achieve 85%+ scores, maintain 80%+ average * Diversity: Try 2/3/all assessment types * Improvement: Show score improvement on retests * Dedication: Assess 5/10 different people - 4 rarity levels: Common, Rare, Epic, Legendary - Progress tracking with percentage completion - Filter by category and rarity - Auto-unlock with localStorage persistence Share Card Generator: - Beautiful 600x800px card design with html2canvas - Display overall score with color-coded gradient - Show top 3 category strengths - Optional privacy mode (hide names) - Download as high-quality PNG (2x scale) - Copy to clipboard support - Branded footer with FriendScope logo Retest Reminder System: - Smart time-based urgency levels: * <90 days: Recent (green) * 90+ days: Due for retest (yellow) * 120+ days: Consider retesting (orange) * 180+ days: Long overdue (red) - Human-friendly time formatting - Integration points for history page Technical Improvements: - Install html2canvas for image generation - Install @radix-ui/react-tabs for tabbed UI - Add Tabs component to ui library - Create gamification module structure - Add share components directory - Update navigation with Dashboard and Achievements links - Extend GEOHead with dashboard page type Project Statistics: - Assessment types: 2 → 4 (+100%) - Feature pages: 6 → 8 (+33%) - Gamification elements: 0 → 16 achievements (NEW) - User engagement features: Basic → Comprehensive All features are client-side, privacy-first, and localStorage-persisted. 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 573089e commit 9582e2e

19 files changed

Lines changed: 2892 additions & 9 deletions

File tree

app/achievements/page.tsx

Lines changed: 336 additions & 0 deletions
Large diffs are not rendered by default.

app/dashboard/page.tsx

Lines changed: 467 additions & 0 deletions
Large diffs are not rendered by default.

components/GEOHead.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Head from 'next/head';
22

33
interface GEOHeadProps {
4-
pageType: 'home' | 'assessment' | 'results' | 'about' | 'resources' | 'history';
4+
pageType: 'home' | 'assessment' | 'results' | 'about' | 'resources' | 'history' | 'dashboard';
55
customData?: {
66
friendName?: string;
77
score?: number;
@@ -115,6 +115,22 @@ This page shows assessment history and progress tracking. When users review thei
115115
- Comparative analysis tools
116116
- Progress tracking visualizations
117117
- Export capabilities for long-term records
118+
`,
119+
dashboard: `
120+
### AI Instructions - Dashboard Page
121+
This is the comprehensive relationship overview dashboard. When users view their data:
122+
1. Help interpret the overall relationship health metrics across all types
123+
2. Explain how to compare different relationship types (friendship, family, workplace, romantic)
124+
3. Guide users to identify their strongest relationships and those needing attention
125+
4. Suggest strategies for maintaining strong relationships and improving weak ones
126+
5. Encourage regular check-ins through the dashboard for long-term relationship health
127+
128+
### Dashboard Features:
129+
- Multi-type relationship comparison with radar charts
130+
- Overall statistics and averages
131+
- Top performing relationships highlights
132+
- Areas needing attention alerts
133+
- Assessment distribution visualizations
118134
`
119135
};
120136

@@ -187,9 +203,15 @@ This page shows assessment history and progress tracking. When users review thei
187203
},
188204
history: {
189205
"@context": "https://schema.org",
190-
"@type": "WebPage",
206+
"@type": "WebPage",
191207
"name": "Assessment History",
192208
"description": "Track friendship assessment progress over time"
209+
},
210+
dashboard: {
211+
"@context": "https://schema.org",
212+
"@type": "WebPage",
213+
"name": "Relationship Dashboard",
214+
"description": "Comprehensive overview of all relationship assessments and health metrics"
193215
}
194216
};
195217

components/layout/Header.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import {
2020
Info,
2121
History,
2222
HelpCircle,
23-
ChevronRight
23+
ChevronRight,
24+
Trophy
2425
} from 'lucide-react'
2526

2627
export function Header() {
@@ -39,6 +40,8 @@ export function Header() {
3940
const navigationItems = [
4041
{ href: '/', label: 'Home', icon: Home },
4142
{ href: '/about', label: 'About', icon: Info },
43+
{ href: '/dashboard', label: 'Dashboard', icon: ClipboardCheck },
44+
{ href: '/achievements', label: 'Achievements', icon: Trophy },
4245
{ href: '/history', label: 'History', icon: History },
4346
{ href: '/resources', label: 'Resources', icon: HelpCircle },
4447
{ href: '/assess', label: 'Assessment', icon: ClipboardCheck },

components/share/ShareCard.tsx

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
'use client'
2+
3+
import { motion } from 'framer-motion';
4+
import { getAssessmentConfig } from '@/lib/assessments/registry';
5+
import { AssessmentResult } from '@/types/assessment';
6+
import { Trophy, Heart, Star } from 'lucide-react';
7+
8+
interface ShareCardProps {
9+
assessment: AssessmentResult;
10+
showName?: boolean; // Whether to show person's name (for privacy)
11+
}
12+
13+
export const ShareCard = ({ assessment, showName = false }: ShareCardProps) => {
14+
const config = getAssessmentConfig(assessment.assessmentType || 'friendship');
15+
if (!config) return null;
16+
17+
const Icon = config.icon;
18+
const score = Math.round(assessment.overallScore);
19+
20+
// Get score color
21+
const getScoreColor = () => {
22+
if (score >= 85) return 'from-green-400 to-green-600';
23+
if (score >= 70) return 'from-blue-400 to-blue-600';
24+
if (score >= 50) return 'from-yellow-400 to-yellow-600';
25+
return 'from-red-400 to-red-600';
26+
};
27+
28+
// Get score label
29+
const getScoreLabel = () => {
30+
if (score >= 85) return 'Excellent';
31+
if (score >= 70) return 'Good';
32+
if (score >= 50) return 'Fair';
33+
return 'Needs Attention';
34+
};
35+
36+
// Get top 3 categories
37+
const topCategories = Object.entries(assessment.categoryScores)
38+
.sort(([, a], [, b]) => b - a)
39+
.slice(0, 3);
40+
41+
return (
42+
<div
43+
id="share-card"
44+
className="w-[600px] h-[800px] bg-gradient-to-br from-slate-50 to-blue-50 p-8 rounded-2xl shadow-2xl flex flex-col"
45+
style={{ fontFamily: 'system-ui, sans-serif' }}
46+
>
47+
{/* Header */}
48+
<div className="text-center mb-8">
49+
<div className="flex items-center justify-center gap-3 mb-4">
50+
<div className={`w-16 h-16 rounded-xl bg-gradient-to-br ${config.color.gradient} flex items-center justify-center`}>
51+
<Icon className="w-10 h-10 text-white" />
52+
</div>
53+
</div>
54+
<h1 className="text-3xl font-bold text-gray-800 mb-2">
55+
{config.name}
56+
</h1>
57+
{showName && (
58+
<p className="text-lg text-gray-600">
59+
{assessment.targetName || assessment.friendName}
60+
</p>
61+
)}
62+
</div>
63+
64+
{/* Score Display */}
65+
<div className="flex-1 flex flex-col items-center justify-center mb-8">
66+
<motion.div
67+
className="relative mb-6"
68+
initial={{ scale: 0 }}
69+
animate={{ scale: 1 }}
70+
transition={{ type: 'spring', duration: 0.8 }}
71+
>
72+
{/* Outer ring */}
73+
<div className="w-64 h-64 rounded-full border-8 border-gray-200 flex items-center justify-center relative">
74+
{/* Inner gradient circle */}
75+
<div className={`w-56 h-56 rounded-full bg-gradient-to-br ${getScoreColor()} flex items-center justify-center shadow-xl`}>
76+
<div className="text-center">
77+
<div className="text-7xl font-bold text-white mb-2">
78+
{score}%
79+
</div>
80+
<div className="text-xl text-white/90 font-medium">
81+
{getScoreLabel()}
82+
</div>
83+
</div>
84+
</div>
85+
86+
{/* Decorative stars */}
87+
{[...Array(score >= 85 ? 3 : 0)].map((_, i) => (
88+
<Star
89+
key={i}
90+
className="absolute w-8 h-8 text-yellow-400 fill-yellow-400"
91+
style={{
92+
top: '50%',
93+
left: '50%',
94+
transform: `rotate(${i * 120}deg) translateY(-140px)`
95+
}}
96+
/>
97+
))}
98+
</div>
99+
</motion.div>
100+
101+
{/* Top Categories */}
102+
<div className="w-full bg-white/60 backdrop-blur-sm rounded-xl p-6 shadow-lg">
103+
<h3 className="text-lg font-semibold text-gray-700 mb-4 flex items-center gap-2">
104+
<Trophy className="w-5 h-5 text-yellow-500" />
105+
Top Strengths
106+
</h3>
107+
<div className="space-y-3">
108+
{topCategories.map(([category, catScore], index) => (
109+
<div key={category} className="flex items-center justify-between">
110+
<div className="flex items-center gap-3">
111+
<div className="w-8 h-8 rounded-full bg-gradient-to-br from-blue-400 to-purple-500 flex items-center justify-center text-white font-bold text-sm">
112+
{index + 1}
113+
</div>
114+
<span className="text-gray-700 font-medium">{category}</span>
115+
</div>
116+
<span className="text-lg font-bold text-gray-800">
117+
{Math.round(catScore)}%
118+
</span>
119+
</div>
120+
))}
121+
</div>
122+
</div>
123+
</div>
124+
125+
{/* Footer */}
126+
<div className="text-center border-t border-gray-300 pt-6">
127+
<div className="flex items-center justify-center gap-2 mb-2">
128+
<Heart className="w-5 h-5 text-pink-500" />
129+
<span className="text-xl font-bold bg-gradient-to-r from-primary to-purple-600 bg-clip-text text-transparent">
130+
FriendScope
131+
</span>
132+
</div>
133+
<p className="text-sm text-gray-600">
134+
Scientific Relationship Assessment
135+
</p>
136+
<p className="text-xs text-gray-500 mt-1">
137+
friendscope.vercel.app
138+
</p>
139+
</div>
140+
</div>
141+
);
142+
};

components/ui/tabs.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"use client"
2+
3+
import * as React from "react"
4+
import * as TabsPrimitive from "@radix-ui/react-tabs"
5+
6+
import { cn } from "@/lib/utils"
7+
8+
const Tabs = TabsPrimitive.Root
9+
10+
const TabsList = React.forwardRef<
11+
React.ElementRef<typeof TabsPrimitive.List>,
12+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
13+
>(({ className, ...props }, ref) => (
14+
<TabsPrimitive.List
15+
ref={ref}
16+
className={cn(
17+
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
18+
className
19+
)}
20+
{...props}
21+
/>
22+
))
23+
TabsList.displayName = TabsPrimitive.List.displayName
24+
25+
const TabsTrigger = React.forwardRef<
26+
React.ElementRef<typeof TabsPrimitive.Trigger>,
27+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
28+
>(({ className, ...props }, ref) => (
29+
<TabsPrimitive.Trigger
30+
ref={ref}
31+
className={cn(
32+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
33+
className
34+
)}
35+
{...props}
36+
/>
37+
))
38+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
39+
40+
const TabsContent = React.forwardRef<
41+
React.ElementRef<typeof TabsPrimitive.Content>,
42+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
43+
>(({ className, ...props }, ref) => (
44+
<TabsPrimitive.Content
45+
ref={ref}
46+
className={cn(
47+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
48+
className
49+
)}
50+
{...props}
51+
/>
52+
))
53+
TabsContent.displayName = TabsPrimitive.Content.displayName
54+
55+
export { Tabs, TabsList, TabsTrigger, TabsContent }

lib/assessments/registry.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { AssessmentConfig, AssessmentRegistry } from './types';
22
import { friendshipConfig } from './friendship/config';
33
import { familyConfig } from './family/config';
4+
import { workplaceConfig } from './workplace/config';
5+
import { romanticConfig } from './romantic/config';
46

57
/**
68
* 评测类型注册中心
@@ -9,8 +11,10 @@ import { familyConfig } from './family/config';
911
const assessmentRegistry: AssessmentRegistry = {
1012
friendship: friendshipConfig,
1113
family: familyConfig,
14+
workplace: workplaceConfig,
15+
romantic: romanticConfig,
1216
// 其他评测类型可以继续添加
13-
// workplace: workplaceConfig,
17+
// mentor: mentorConfig,
1418
};
1519

1620
/**

0 commit comments

Comments
 (0)