SkateSpots/src/lib/skateboardRating.ts
shakespeare.diy e29d35f1e4 fix: restore full skate-spots source tree from misplaced src/src/ copy
The entire skate-spots codebase was accidentally copied into src/src/ and
public/public/ instead of src/ and public/ directly. This left the real
src/ directory as blank MKStack template stubs, explaining why the landing
page showed the default template instead of SkateSpots.

Changes made:
- Copied all files from src/src/ up to src/ (App.tsx, AppRouter.tsx, main.tsx,
  all pages, components, hooks, contexts, lib, i18n, test)
- Copied all files from public/public/ up to public/ (images, manifest, etc.)
- Deleted src/src/ and public/public/ nested directories
- Removed HelloWorld.tsx template stub from pages/
- Retained the previously fixed index.css (Tailwind v3 directives)

Full skate-spots feature set is now active:
- All 30+ routes (map, events, spots, coaches, marketplace, wallet, etc.)
- NWCProvider, LanguageProvider, PWAInstallPrompt, AmberCallbackHandler
- Skate community relays (goskatespots.nostr1.com, wotr.relatr.xyz, etc.)
- Outfit Variable font, Amber pre-check in main.tsx
- All hooks (useSkateSpots, useCoaches, useZaps, etc.)
- i18n support (en/es)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 22:18:28 +07:00

144 lines
3.8 KiB
TypeScript

/**
* Skateboard emoji rating system for SkateSpots
* Uses 🛹 emojis instead of stars, from 1 to 10
*/
/**
* Generate skateboard rating display (1-10 skateboards based on count)
* @param count - The count to convert to a rating (sales, reviews, etc.)
* @param maxDisplay - Maximum skateboards to display (default 10)
* @returns Object with emoji string and numeric rating
*/
export function getSkateboardRating(count: number, maxDisplay = 10): {
rating: number;
display: string;
label: string;
} {
// Scale: 1-10 based on count thresholds
let rating: number;
let label: string;
if (count === 0) {
rating = 0;
label = 'New';
} else if (count === 1) {
rating = 1;
label = 'Just starting';
} else if (count <= 3) {
rating = 2;
label = 'Getting known';
} else if (count <= 5) {
rating = 3;
label = 'Building rep';
} else if (count <= 8) {
rating = 4;
label = 'Solid';
} else if (count <= 12) {
rating = 5;
label = 'Reliable';
} else if (count <= 18) {
rating = 6;
label = 'Well known';
} else if (count <= 25) {
rating = 7;
label = 'Established';
} else if (count <= 35) {
rating = 8;
label = 'Trusted';
} else if (count <= 50) {
rating = 9;
label = 'Pro seller';
} else {
rating = 10;
label = 'Legendary';
}
// Clamp to max display
const displayRating = Math.min(rating, maxDisplay);
const display = '🛹'.repeat(displayRating);
return { rating, display, label };
}
/**
* Get skateboard rating for skate spots (based on reviews/ratings)
* @param reviewCount - Number of reviews
* @param averageRating - Average rating (1-5 scale typically)
* @returns Object with emoji string and numeric rating
*/
export function getSpotSkateboardRating(reviewCount: number, averageRating: number = 0): {
rating: number;
display: string;
label: string;
} {
// Combine review count and average rating for final score
// Weight: 60% average rating, 40% review count factor
let rating: number;
let label: string;
if (reviewCount === 0) {
rating = 0;
label = 'No reviews';
} else {
// Scale average rating (1-5) to (2-10)
const ratingScore = ((averageRating - 1) / 4) * 8 + 2;
// Review count bonus (up to +2 for 20+ reviews)
const countBonus = Math.min(reviewCount / 10, 2);
rating = Math.round(Math.min(ratingScore + countBonus, 10));
if (rating <= 2) label = 'Sketchy';
else if (rating <= 4) label = 'Decent';
else if (rating <= 6) label = 'Solid spot';
else if (rating <= 8) label = 'Classic';
else label = 'Legendary';
}
const display = rating > 0 ? '🛹'.repeat(rating) : '';
return { rating, display, label };
}
/**
* Compact display for tight spaces (shows count + single emoji)
*/
export function getCompactRating(count: number): string {
if (count === 0) return '🛹 0';
return `🛹 ${count}`;
}
/**
* Trust badge configurations matching the skateboard theme
*/
export const SELLER_TRUST_BADGES = {
verified: {
emoji: '✅',
label: 'Verified',
description: 'You follow them directly',
className: 'border-green-500/50 text-green-400 bg-green-500/10'
},
trusted: {
emoji: '🛡️',
label: 'Trusted',
description: 'Friend-of-friend or NIP-05',
className: 'border-blue-500/50 text-blue-400 bg-blue-500/10'
},
new: {
emoji: '⚠️',
label: 'New',
description: 'No trust signals yet',
className: 'border-yellow-500/50 text-yellow-400 bg-yellow-500/10'
},
} as const;
export type TrustLevel = keyof typeof SELLER_TRUST_BADGES;
/**
* Get trust level from trust score
*/
export function getTrustLevel(trustScore: number | undefined): TrustLevel {
if (trustScore === undefined || trustScore < 50) return 'new';
if (trustScore >= 90) return 'verified';
return 'trusted';
}