mirror of
https://github.com/yusufipk/RepoHub.git
synced 2026-09-11 10:36:07 +00:00
refactor: Remove experience level from recommendation system
- Remove experienceLevel from UserProfile, RecommendationRequest, and PackagePreset types - Remove experience level validation and handling from GET/POST endpoints - Disable localStorage persistence in useRecommendationProfile hook (session-only storage) - Disable automatic onboarding modal on first visit - Set recommendations section to expanded by default - Add empty state card for recommendations with wizard button - Add translations
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { RecommendationService } from "@/services/recommendationService";
|
||||
import { RecommendationRequest, UserCategory, ExperienceLevel } from "@/types/recommendations";
|
||||
import { RecommendationRequest, UserCategory } from "@/types/recommendations";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
@@ -79,7 +79,6 @@ export async function POST(request: NextRequest) {
|
||||
{
|
||||
platform_id: body.platform_id,
|
||||
categories: body.categories,
|
||||
experienceLevel: body.experienceLevel,
|
||||
limit,
|
||||
}
|
||||
);
|
||||
@@ -90,7 +89,6 @@ export async function POST(request: NextRequest) {
|
||||
userProfile: {
|
||||
categories: body.categories,
|
||||
platform: body.platform_id,
|
||||
experienceLevel: body.experienceLevel,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -110,7 +108,6 @@ export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const platformId = searchParams.get("platform_id");
|
||||
const categoriesParam = searchParams.get("categories");
|
||||
const experienceLevel = searchParams.get("experience_level");
|
||||
const limit = searchParams.get("limit");
|
||||
|
||||
// Validate required fields
|
||||
@@ -174,17 +171,6 @@ export async function GET(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// Validate experience level if provided
|
||||
const validExperienceLevels: ExperienceLevel[] = ["beginner", "intermediate", "advanced"];
|
||||
if (experienceLevel && !validExperienceLevels.includes(experienceLevel as ExperienceLevel)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: `Invalid experience_level. Must be one of: ${validExperienceLevels.join(", ")}`,
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Set default limit
|
||||
const parsedLimit =
|
||||
limit && parseInt(limit) > 0 && parseInt(limit) <= 50
|
||||
@@ -196,7 +182,6 @@ export async function GET(request: NextRequest) {
|
||||
{
|
||||
platform_id: platformId,
|
||||
categories: categories as UserCategory[],
|
||||
experienceLevel: experienceLevel as ExperienceLevel | undefined,
|
||||
limit: parsedLimit,
|
||||
}
|
||||
);
|
||||
@@ -207,7 +192,6 @@ export async function GET(request: NextRequest) {
|
||||
userProfile: {
|
||||
categories,
|
||||
platform: platformId,
|
||||
experienceLevel,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -42,7 +42,7 @@ export function RecommendationsSection({
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('grid')
|
||||
const [filterCategory, setFilterCategory] = useState<FilterCategory>('all')
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const [isExpanded, setIsExpanded] = useState(true)
|
||||
|
||||
const fetchRecommendations = async () => {
|
||||
if (!isProfileComplete()) {
|
||||
@@ -61,7 +61,6 @@ export function RecommendationsSection({
|
||||
body: JSON.stringify({
|
||||
platform_id: getEffectiveOS(),
|
||||
categories: profile.categories,
|
||||
experienceLevel: profile.experienceLevel,
|
||||
limit: 1000
|
||||
})
|
||||
})
|
||||
@@ -104,7 +103,7 @@ export function RecommendationsSection({
|
||||
// But for now, let's rely on the cache key changing which includes profile data
|
||||
fetchRecommendations()
|
||||
}
|
||||
}, [profile.categories, profile.selectedOS, profile.experienceLevel])
|
||||
}, [profile.categories, profile.selectedOS])
|
||||
|
||||
const isPackageSelected = (pkg: RecommendedPackage) => {
|
||||
return selectedPackages.some(selected => selected.id === pkg.id)
|
||||
|
||||
@@ -13,7 +13,10 @@ import { generateScript } from '@/lib/scriptGenerator'
|
||||
import { useLocale } from '@/contexts/LocaleContext'
|
||||
import { useRecommendationProfile } from '@/hooks/useRecommendationProfile'
|
||||
import { Platform, Package, SelectedPackage, FilterOptions, GeneratedScript } from '@/types'
|
||||
import { UserCategory, ExperienceLevel } from '@/types/recommendations'
|
||||
import { UserCategory } from '@/types/recommendations'
|
||||
import { Sparkles, Settings, Package as PackageIcon } from 'lucide-react'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
function RepoHubAppContent({ cryptomusEnabled }: { cryptomusEnabled: boolean }) {
|
||||
const { t, locale } = useLocale()
|
||||
@@ -50,7 +53,8 @@ function RepoHubAppContent({ cryptomusEnabled }: { cryptomusEnabled: boolean })
|
||||
loadPlatforms()
|
||||
}, [])
|
||||
|
||||
// Show onboarding modal on first visit
|
||||
// Show onboarding modal on first visit - DISABLED as per user request
|
||||
/*
|
||||
useEffect(() => {
|
||||
if (!isProfileLoading && !hasCompletedOnboarding) {
|
||||
// Delay to allow page to render first
|
||||
@@ -60,18 +64,17 @@ function RepoHubAppContent({ cryptomusEnabled }: { cryptomusEnabled: boolean })
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [isProfileLoading, hasCompletedOnboarding])
|
||||
*/
|
||||
|
||||
const handleOnboardingComplete = (data: {
|
||||
categories: UserCategory[]
|
||||
selectedOS?: string
|
||||
experienceLevel: ExperienceLevel
|
||||
}) => {
|
||||
console.log('🎯 Onboarding completed with data:', data)
|
||||
|
||||
const success = saveProfile({
|
||||
categories: data.categories,
|
||||
selectedOS: data.selectedOS,
|
||||
experienceLevel: data.experienceLevel,
|
||||
hasCompletedOnboarding: true
|
||||
})
|
||||
|
||||
@@ -192,13 +195,32 @@ function RepoHubAppContent({ cryptomusEnabled }: { cryptomusEnabled: boolean })
|
||||
{/* Main Content */}
|
||||
<div className="space-y-8">
|
||||
{/* Recommendations Section - Show if profile is complete */}
|
||||
{hasCompletedOnboarding && profile.categories.length > 0 && (
|
||||
{hasCompletedOnboarding && profile.categories.length > 0 ? (
|
||||
<RecommendationsSection
|
||||
onPackageToggle={handlePackageToggle}
|
||||
selectedPackages={selectedPackages}
|
||||
onCustomizeClick={handleCustomizePreferences}
|
||||
profile={profile}
|
||||
/>
|
||||
) : (
|
||||
/* Empty State for Recommendations */
|
||||
<Card className="w-full bg-gradient-to-r from-primary/5 to-secondary/5 border-dashed">
|
||||
<CardContent className="flex flex-col items-center justify-center py-12 text-center">
|
||||
<div className="bg-background p-4 rounded-full shadow-sm mb-4">
|
||||
<Sparkles className="h-8 w-8 text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold mb-2">
|
||||
{t('recommendations.empty_title') || 'Get Personalized Recommendations'}
|
||||
</h3>
|
||||
<p className="text-muted-foreground max-w-md mb-6">
|
||||
{t('recommendations.empty_description') || 'Tell us about your role and platform to get a curated list of essential packages.'}
|
||||
</p>
|
||||
<Button onClick={handleCustomizePreferences} size="lg">
|
||||
<Sparkles className="h-4 w-4 mr-2" />
|
||||
{t('recommendations.start') || 'Start Recommendation Wizard'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
@@ -188,6 +188,9 @@ const translations = {
|
||||
reason: "Why recommended:",
|
||||
based_on: "Based on your interests in:",
|
||||
packages: "packages",
|
||||
empty_title: "Get Personalized Recommendations",
|
||||
empty_description: "Tell us about your role and platform to get a curated list of essential packages.",
|
||||
start: "Start Recommendation Wizard",
|
||||
sort: {
|
||||
recommended: "Best Match",
|
||||
popular: "Popular",
|
||||
@@ -378,6 +381,9 @@ const translations = {
|
||||
reason: "Neden önerildi:",
|
||||
based_on: "İlgi alanlarınıza göre:",
|
||||
packages: "paket",
|
||||
empty_title: "Kişiselleştirilmiş Öneriler Alın",
|
||||
empty_description: "Size temel paketlerden oluşan bir liste sunmamız için kategorileri ve platformunuzu belirtin.",
|
||||
start: "Öneri Sihirbazını Başlat",
|
||||
sort: {
|
||||
recommended: "En Uygun",
|
||||
popular: "Popüler",
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useState, useEffect, useCallback } from "react";
|
||||
import {
|
||||
UserProfile,
|
||||
UserCategory,
|
||||
ExperienceLevel,
|
||||
} from "@/types/recommendations";
|
||||
|
||||
const STORAGE_KEY = "repohub_user_profile";
|
||||
@@ -83,46 +82,16 @@ export function useRecommendationProfile() {
|
||||
|
||||
// Load profile from localStorage on mount
|
||||
useEffect(() => {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored) as UserProfile;
|
||||
// We intentionally do NOT load from localStorage anymore to reset on refresh
|
||||
// as requested by user preference change.
|
||||
|
||||
// Handle version migration
|
||||
if (!parsed.version || parsed.version < CURRENT_PROFILE_VERSION) {
|
||||
console.log(
|
||||
"Migrating profile from version",
|
||||
parsed.version || 0,
|
||||
"to",
|
||||
CURRENT_PROFILE_VERSION
|
||||
);
|
||||
// Add migration logic here when schema changes in the future
|
||||
parsed.version = CURRENT_PROFILE_VERSION;
|
||||
}
|
||||
|
||||
// Update detectedOS if it changed
|
||||
const currentOS = detectOS();
|
||||
if (parsed.detectedOS !== currentOS) {
|
||||
parsed.detectedOS = currentOS;
|
||||
}
|
||||
|
||||
setProfile(parsed);
|
||||
// Save migrated profile
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(parsed));
|
||||
} else {
|
||||
// First time user - save default profile
|
||||
// Initialize with default profile (detects OS)
|
||||
const defaultProfile = getDefaultProfile();
|
||||
setProfile(defaultProfile);
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(defaultProfile));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading user profile:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Save profile to localStorage
|
||||
// Save profile to state only (session persistence)
|
||||
const saveProfile = useCallback(
|
||||
(newProfile: Partial<UserProfile>) => {
|
||||
try {
|
||||
@@ -133,12 +102,10 @@ export function useRecommendationProfile() {
|
||||
lastUpdated: new Date().toISOString(),
|
||||
};
|
||||
|
||||
console.log("💾 Saving profile:", updated);
|
||||
console.log("💾 Saving profile (Session only):", updated);
|
||||
|
||||
setProfile(updated);
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
|
||||
|
||||
console.log("✅ Profile saved successfully to localStorage");
|
||||
// localStorage.setItem(STORAGE_KEY, JSON.stringify(updated)); // Disabled persistence
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -177,7 +144,7 @@ export function useRecommendationProfile() {
|
||||
try {
|
||||
const defaultProfile = getDefaultProfile();
|
||||
setProfile(defaultProfile);
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(defaultProfile));
|
||||
// localStorage.setItem(STORAGE_KEY, JSON.stringify(defaultProfile)); // Disabled persistence
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error resetting user profile:", error);
|
||||
|
||||
@@ -12,11 +12,6 @@ export type UserCategory =
|
||||
| "productivity"
|
||||
| "education";
|
||||
|
||||
/**
|
||||
* User experience level
|
||||
*/
|
||||
export type ExperienceLevel = "beginner" | "intermediate" | "advanced";
|
||||
|
||||
/**
|
||||
* User profile stored in localStorage
|
||||
*/
|
||||
@@ -25,7 +20,6 @@ export interface UserProfile {
|
||||
categories: UserCategory[];
|
||||
detectedOS?: string;
|
||||
selectedOS?: string; // Manual override
|
||||
experienceLevel?: ExperienceLevel;
|
||||
hasCompletedOnboarding: boolean;
|
||||
createdAt: string;
|
||||
lastUpdated: string;
|
||||
@@ -37,7 +31,6 @@ export interface UserProfile {
|
||||
export interface RecommendationRequest {
|
||||
platform_id: string;
|
||||
categories: UserCategory[];
|
||||
experienceLevel?: ExperienceLevel;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
@@ -76,7 +69,6 @@ export interface PackagePreset {
|
||||
platforms: string[]; // ['windows', 'macos', 'ubuntu', 'arch', 'fedora']
|
||||
priority: number; // 1-10, higher = more important
|
||||
reason: string; // Why this package is recommended
|
||||
experienceLevel?: ExperienceLevel[]; // Target experience levels
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user