tt / index.html
Galaxydude2's picture
Upload folder using huggingface_hub
ae41e83 verified
raw
history blame
22.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Attractiveness Rating App</title>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet">
<!-- Font Awesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #ff4b6e;
--secondary-color: #ff8fa3;
--bg-gradient: linear-gradient(135deg, #fdfbfb 0%, #ebedee 100%);
--card-shadow: 0 10px 20px rgba(0,0,0,0.15);
--text-color: #333;
--success-color: #2ecc71;
--danger-color: #e74c3c;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Poppins', sans-serif;
background: var(--bg-gradient);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
color: var(--text-color);
overflow-x: hidden;
}
/* Header Section */
header {
width: 100%;
padding: 20px;
text-align: center;
background: white;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 600px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
h1 {
font-size: 1.5rem;
color: var(--primary-color);
font-weight: 700;
letter-spacing: 1px;
}
.attribution {
font-size: 0.75rem;
color: #888;
text-decoration: none;
}
.attribution:hover {
color: var(--primary-color);
}
/* Main Container */
main {
flex: 1;
width: 100%;
max-width: 450px;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
/* Card Stack Area */
.card-container {
position: relative;
width: 100%;
height: 500px;
perspective: 1000px;
margin-bottom: 30px;
}
.card {
position: absolute;
width: 100%;
height: 100%;
background: white;
border-radius: 20px;
box-shadow: var(--card-shadow);
overflow: hidden;
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
transform-origin: center bottom;
cursor: grab;
display: flex;
flex-direction: column;
}
.card-image {
width: 100%;
height: 65%;
object-fit: cover;
pointer-events: none; /* Prevents image drag interfering with card drag */
}
.card-info {
padding: 25px;
background: white;
text-align: center;
}
.card-info h2 {
font-size: 2rem;
margin-bottom: 5px;
color: #2d3436;
}
.card-info p {
color: #636e72;
font-size: 1rem;
line-height: 1.5;
}
.tags {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 15px;
flex-wrap: wrap;
}
.tag {
background: #f1f2f6;
padding: 5px 12px;
border-radius: 50px;
font-size: 0.8rem;
color: #576574;
font-weight: 600;
}
/* Rating Controls */
.controls {
display: flex;
gap: 20px;
align-items: center;
justify-content: center;
width: 100%;
}
.btn {
width: 70px;
height: 70px;
border-radius: 50%;
border: none;
cursor: pointer;
font-size: 2rem;
transition: transform 0.2s, box-shadow 0.2s;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
background: white;
color: #555;
}
.btn:hover {
transform: scale(1.1);
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
}
.btn-reject {
color: var(--danger-color);
}
.btn-reject:hover {
background-color: #fadbd8;
}
.btn-like {
color: var(--success-color);
}
.btn-like:hover {
background-color: #d4efdf;
}
.btn-pass {
color: var(--primary-color);
font-size: 1.2rem;
}
.btn-pass:hover {
background-color: #fadbd8;
}
/* Progress Bar */
.progress-container {
width: 100%;
max-width: 450px;
margin-bottom: 20px;
text-align: center;
}
.progress-text {
font-size: 0.9rem;
margin-bottom: 5px;
color: #636e72;
display: flex;
justify-content: space-between;
}
.progress-bar-bg {
width: 100%;
height: 8px;
background: #dfe6e9;
border-radius: 10px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
width: 0%;
transition: width 0.5s ease;
}
/* Results Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 200;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
backdrop-filter: blur(5px);
}
.modal-overlay.active {
opacity: 1;
pointer-events: all;
}
.modal-content {
background: white;
padding: 40px;
border-radius: 25px;
text-align: center;
max-width: 90%;
width: 400px;
transform: translateY(50px);
transition: transform 0.3s ease;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
}
.modal-overlay.active .modal-content {
transform: translateY(0);
}
.score-circle {
width: 120px;
height: 120px;
border-radius: 50%;
border: 8px solid var(--primary-color);
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 25px;
font-size: 3rem;
font-weight: 700;
color: var(--primary-color);
background: #fff;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 30px;
}
.stat-box {
background: #f8f9fa;
padding: 15px;
border-radius: 10px;
}
.stat-value {
font-size: 1.5rem;
font-weight: 700;
color: #2d3436;
}
.stat-label {
font-size: 0.85rem;
color: #636e72;
}
.btn-primary {
background: var(--primary-color);
color: white;
border: none;
padding: 15px 40px;
border-radius: 50px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: background 0.3s;
width: 100%;
}
.btn-primary:hover {
background: #e03e5b;
}
/* Animations for swiping */
.animating-out-left {
transform: translateX(-150%) rotate(-30deg) !important;
opacity: 0;
}
.animating-out-right {
transform: translateX(150%) rotate(30deg) !important;
opacity: 0;
}
/* Responsive adjustments */
@media (max-height: 700px) {
.card-container { height: 400px; }
.card-image { height: 60%; }
}
</style>
</head>
<body>
<header>
<div class="header-content">
<h1><i class="fa-solid fa-heart"></i> RateAttract</h1>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="attribution">Built with anycoder</a>
</div>
</header>
<main>
<div class="progress-container">
<div class="progress-text">
<span id="progress-text">Profile 1 of 10</span>
<span id="rating-count">0 Ratings</span>
</div>
<div class="progress-bar-bg">
<div class="progress-bar-fill" id="progress-bar"></div>
</div>
</div>
<div class="card-container" id="card-stack">
<!-- Cards will be injected here via JS -->
</div>
<div class="controls">
<button class="btn btn-reject" id="btn-reject" title="No">
<i class="fa-solid fa-xmark"></i>
</button>
<button class="btn btn-pass" id="btn-skip" title="Skip">
<i class="fa-solid fa-rotate-right"></i>
</button>
<button class="btn btn-like" id="btn-like" title="Yes">
<i class="fa-solid fa-heart"></i>
</button>
</div>
</main>
<!-- Results Modal -->
<div class="modal-overlay" id="results-modal">
<div class="modal-content">
<h2 style="color: var(--primary-color); margin-bottom: 10px;">Session Complete!</h2>
<p>Here is your rating summary</p>
<div class="score-circle" id="final-score">0</div>
<p>Average Attractiveness Score</p>
<div class="stats-grid">
<div class="stat-box">
<div class="stat-value" id="total-swipes">0</div>
<div class="stat-label">Total Profiles</div>
</div>
<div class="stat-box">
<div class="stat-value" id="total-likes">0</div>
<div class="stat-label">Likes (Hearts)</div>
</div>
<div class="stat-box">
<div class="stat-value" id="avg-rating">0.0</div>
<div class="stat-label">Avg Rating</div>
</div>
<div class="stat-box">
<div class="stat-value" id="highest-rating">0</div>
<div class="stat-label">Highest Score</div>
</div>
</div>
<button class="btn-primary" onclick="resetApp()">Start New Session</button>
</div>
</div>
<script>
// --- Data Generation ---
const generateProfiles = (count) => {
const names = ["Sarah", "James", "Elena", "Michael", "Liam", "Olivia", "Noah", "Emma", "Oliver", "Ava"];
const interests = ["Traveling", "Photography", "Cooking", "Hiking", "Music", "Art", "Fitness", "Gaming", "Reading", "Dancing"];
const profiles = [];
for (let i = 0; i < count; i++) {
const name = names[i % names.length] + (i >= names.length ? " " + (i + 1) : "");
// Random interests
const randomInterests = interests.sort(() => 0.5 - Math.random()).slice(0, 3);
// Random image from Picsum (reliable placeholder service)
const randomId = Math.floor(Math.random() * 1000);
profiles.push({
id: i,
name: name,
age: Math.floor(Math.random() * (35 - 22 + 1)) + 22,
bio: "Just living my life and looking for good vibes. I love exploring new places and trying different cuisines.",
interests: randomInterests,
image: `https://picsum.photos/id/${randomId}/400/500`
});
}
return profiles;
};
// --- State Management ---
const TOTAL_PROFILES = 10;
let profiles = [];
let currentIndex = 0;
let ratings = [];
let sessionStats = {
total: 0,
likes: 0,
ratingsSum: 0,
highestRating: 0
};
// --- DOM Elements ---
const cardStack = document.getElementById('card-stack');
const progressBar = document.getElementById('progress-bar');
const progressText = document.getElementById('progress-text');
const ratingCountText = document.getElementById('rating-count');
const btnReject = document.getElementById('btn-reject');
const btnLike = document.getElementById('btn-like');
const btnSkip = document.getElementById('btn-skip');
const modal = document.getElementById('results-modal');
const finalScoreEl = document.getElementById('final-score');
const totalSwipesEl = document.getElementById('total-swipes');
const totalLikesEl = document.getElementById('total-likes');
const avgRatingEl = document.getElementById('avg-rating');
const highestRatingEl = document.getElementById('highest-rating');
// --- Initialization ---
function initApp() {
profiles = generateProfiles(TOTAL_PROFILES);
currentIndex = 0;
ratings = [];
sessionStats = { total: 0, likes: 0, ratingsSum: 0, highestRating: 0 };
updateProgress();
renderCard();
modal.classList.remove('active');
}
// --- Rendering ---
function renderCard() {
if (currentIndex >= profiles.length) {
showResults();
return;
}
const profile = profiles[currentIndex];
// Create HTML structure
const card = document.createElement('div');
card.className = 'card';
card.id = 'current-card';
// Generate Interest Tags HTML
const tagsHtml = profile.interests.map(tag => `<span class="tag"><i class="fa-solid fa-tag"></i> ${tag}</span>`).join('');
card.innerHTML = `
<img src="${profile.image}" alt="${profile.name}" class="card-image" draggable="false">
<div class="card-info">
<h2>${profile.name}, ${profile.age}</h2>
<p>${profile.bio}</p>
<div class="tags">
${tagsHtml}
</div>
</div>
`;
// Clear stack and append new card
cardStack.innerHTML = '';
cardStack.appendChild(card);
// Attach Drag Logic
attachDragLogic(card);
}
function updateProgress() {
const percentage = (currentIndex / TOTAL_PROFILES) * 100;
progressBar.style.width = `${percentage}%`;
progressText.innerText = `Profile ${currentIndex + 1} of ${TOTAL_PROFILES}`;
ratingCountText.innerText = `${sessionStats.total} Profiles Rated`;
}
// --- Drag Logic (Touch & Mouse) ---
function attachDragLogic(card) {
let isDragging = false;
let startX = 0;
let currentX = 0;
const onStart = (e) => {
isDragging = true;
startX = e.type.includes('mouse') ? e.clientX : e.touches[0].clientX;
card.style.transition = 'none';
card.style.cursor = 'grabbing';
};
const onMove = (e) => {
if (!isDragging) return;
e.preventDefault(); // Prevent scrolling on mobile
currentX = (e.type.includes('mouse') ? e.clientX : e.touches[0].clientX);
const deltaX = currentX - startX;
// Rotation effect based on X movement
const rotate = deltaX * 0.1;
card.style.transform = `translateX(${deltaX}px) rotate(${rotate}deg)`;
// Visual feedback (opacity change)
const opacity = Math.max(0, 1 - Math.abs(deltaX) / 500);
card.style.opacity = opacity;
// Color overlay effect
if (deltaX > 0) {
card.style.borderRight = `5px solid var(--success-color)`;
card.style.borderLeft = 'none';
} else {
card.style.borderLeft = `5px solid var(--danger-color)`;
card.style.borderRight = 'none';
}
};
const onEnd = () => {
if (!isDragging) return;
isDragging = false;
card.style.transition = 'all 0.3s ease';
card.style.cursor = 'grab';
card.style.border = 'none';
const deltaX = currentX - startX;
const threshold = 100; // pixels to trigger swipe
if (deltaX > threshold) {
handleSwipe('right');
} else if (deltaX < -threshold) {
handleSwipe('left');
} else {
// Reset position
card.style.transform = 'translateX(0) rotate(0)';
card.style.opacity = '1';
}
currentX = 0;
};
// Mouse Events
card.addEventListener('mousedown', onStart);
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onEnd);
// Touch Events
card.addEventListener('touchstart', onStart);
card.addEventListener('touchmove', onMove);
card.addEventListener('touchend', onEnd);
}
// --- Actions ---
function handleSwipe(direction) {
const card = document.getElementById('current-card');
if (direction === 'right') {
card.classList.add('animating-out-right');
setTimeout(() => {
showRatingPrompt(true);
}, 300);
} else {
card.classList.add('animating-out-left');
setTimeout(() => {
nextProfile();
}, 300);
}
}
function showRatingPrompt(liked) {
// In this app, if you "Like" (Right swipe), you must rate it.
// If you Reject (Left swipe), you skip rating.
let rating = 0;
if (liked) {
// Simple prompt for rating (1-10)
let validRating = false;
while (!validRating) {
const input = prompt(`How would you rate ${profiles[currentIndex].name}? (1-10)\n(1 = Not Attractive, 10 = Extremely Attractive)`, "7");
if (input === null) {
// User cancelled, treat as a pass
nextProfile();
return;
}
const num = parseInt(input);
if (!isNaN(num) && num >= 1 && num <= 10) {
rating = num;
validRating = true;
} else {
alert("Please enter a number between 1 and 10.");
}
}
}
// Record Data
sessionStats.total++;
sessionStats.ratingsSum += rating;
if (rating > sessionStats.highestRating) {
sessionStats.highestRating = rating;
}
if (liked) sessionStats.likes++;
ratings.push({ name: profiles[currentIndex].name, score: rating });
updateProgress();
nextProfile();
}
function nextProfile() {
currentIndex++;
renderCard();
}
function showResults() {
finalScoreEl.innerText = sessionStats.total;
totalSwipesEl.innerText = sessionStats.total;
totalLikesEl.innerText = sessionStats.likes;
const avg = sessionStats.total > 0
? (sessionStats.ratingsSum / sessionStats.total).toFixed(1)
: 0;
avgRatingEl.innerText = avg;
highestRatingEl.innerText = sessionStats.highestRating;
modal.classList.add('active');
}
function resetApp() {
initApp();
}
// --- Button Listeners ---
btnLike.addEventListener('click', () => {
if (currentIndex < profiles.length) {
handleSwipe('right');
}
});
btnReject.addEventListener('click', () => {
if (currentIndex < profiles.length) {
handleSwipe('left');
}
});
btnSkip.addEventListener('click', () => {
if (currentIndex < profiles.length) {
nextProfile();
}
});
// Start the app
initApp();
</script>
</body>
</html>