Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
958 changes: 93 additions & 865 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
},
"dependencies": {
"@ardrive/turbo-sdk": "^1.21.0",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"@fortawesome/fontawesome-svg-core": "^7.0.0",
"@fortawesome/free-solid-svg-icons": "^7.0.0",
"@fortawesome/react-fontawesome": "^0.2.3",
"@permaweb/aoconnect": "^0.0.77",
"ao-js-sdk": "^0.0.15",
"arweave": "^1.15.5",
Expand Down Expand Up @@ -46,4 +47,4 @@
"vite": "^5.0.8",
"vite-plugin-html": "^3.2.2"
}
}
}
213 changes: 98 additions & 115 deletions src/components/ActivityCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,15 @@ interface ActivityCardProps {
buttonText: string;
theme: any;
highlightSelectable?: boolean;
remainingTime?: string;
progress?: number;
className?: string;
}

export const ActivityCard: React.FC<ActivityCardProps> = ({
title,
badge,
badgeColor,
gradientFrom, // Kept for button, but not for top bar
gradientTo, // Kept for button, but not for top bar
gradientFrom,
gradientTo,
tokenLogo,
tokenBalance,
tokenRequired,
Expand All @@ -45,30 +44,43 @@ export const ActivityCard: React.FC<ActivityCardProps> = ({
isLoading,
isDisabled,
buttonText,
theme,
highlightSelectable = false,
remainingTime,
progress
}) => {

// Helper to get reward icons
const getRewardIcon = (cost: any) => {
if (cost.icon === '⚡') return '🔋';
if (cost.icon === '💝') return '❤️';
if (cost.icon === '🧭') return '🧭';
if (cost.icon === '🗡️') return '🗡️';
return cost.icon;
};

// Enhanced border effect for selectable items
const borderStyle = !isDisabled && highlightSelectable
? `border-2 border-${gradientFrom}`
: `border-2 ${theme.border}`;
? `border border-[#814E3355]`
: 'border border-[#e7dfd2]';

// Enhanced glow effect for selectable items
const glowEffect = !isDisabled && highlightSelectable
? `shadow-lg shadow-${gradientFrom}/20`
: '';

// Enhanced scale effect for selectable items
const hoverEffect = !isDisabled
? 'hover:scale-105'
: '';
? 'hover:scale-[1.02]'
: 'hover:shadow-none hover:scale-100';

// Enhanced button styling for better visual feedback
const buttonStyle = !isDisabled
? `bg-gradient-to-r from-${gradientFrom} to-${gradientTo} hover:from-${gradientFrom}-700 hover:to-${gradientTo}-700 transform hover:scale-105 hover:shadow-md`
: 'bg-gray-400 cursor-not-allowed';
// Button uses solid color based on badgeColor if active/enabled, else gray
const buttonBgMap: Record<string, string> = {
yellow: 'bg-[#facc15] hover:bg-[#eab308] text-white',
green: 'bg-[#22c55e] hover:bg-[#16a34a] text-white',
red: 'bg-[#ef4444] hover:bg-[#dc2626] text-white',
blue: 'bg-[#3b82f6] hover:bg-[#2563eb] text-white'
};
const buttonStyle = !isDisabled
? `${buttonBgMap[badgeColor] || 'bg-blue-500 hover:bg-blue-600 text-white'} font-medium shadow-lg hover:shadow-xl`
: 'bg-gray-300 cursor-not-allowed text-white hover:scale-100 hover:shadow-none';

// Add a highlight indicator for selectable items
const SelectableIndicator = () => {
Expand All @@ -80,118 +92,89 @@ export const ActivityCard: React.FC<ActivityCardProps> = ({
return null;
};

const badgeTextMap: Record<string, string> = {
yellow: 'text-[#78350f]',
green: 'text-[#166534]',
red: 'text-[#7f1d1d]',
blue: 'text-[#1e3a8a]'
};

return (
<div
className={`activity-card relative overflow-hidden rounded-xl
${theme.container} ${borderStyle} ${glowEffect} transform ${hoverEffect}
transition-all duration-300 h-[200px] w-[180px] flex flex-col`}>

{/* --- NEW Integrated Progress/Header Bar --- */}
{(() => {
const isActivityCompleted = progress === 100;
const isActivityInProgress = progress !== undefined && progress < 100;
const baseBgColor = theme.isDarkMode ? 'bg-[#2A1912]' : 'bg-[#F4E4C1]'; // Theme-aligned dark and light backgrounds

return (
<div
className={`absolute top-0 left-0 w-full h-3 flex items-center justify-center text-white text-xs font-bold overflow-hidden`}
>
{isActivityCompleted ? (
<div className="w-full h-full flex items-center justify-center bg-purple-600 shadow-lg shadow-purple-500/50 text-black">
COMPLETE
</div>
) : isActivityInProgress ? (
<div className={`w-full h-full relative ${baseBgColor} flex items-center justify-center`}>
<div
className="absolute top-0 left-0 h-full bg-gradient-to-r from-purple-500 to-pink-500 animate-magical-progress"
style={{ width: `${progress}%` }}
/>
{remainingTime && <span className="relative z-10 text-black text-xs font-bold">{remainingTime}</span>}
</div>
) : (
<div className={`w-full h-full ${baseBgColor}`} />
)}
</div>
);
})()}

{/* Selectable indicator (ensure it's visible on top of the new header) */}
className={`activity-card relative overflow-hidden bg-[#fcf8f3] ${borderStyle} rounded-lg hover:shadow-xl transition-all duration-300 ${hoverEffect} ${glowEffect}`}>
<SelectableIndicator />

{/* Main content - Adjusted pt-3 to account for h-2 header */}
<div className="p-3 pt-4 flex flex-col h-full">
{/* Header (Title and Badge) */}
<div className="flex justify-between items-center mb-1"> {/* Reduced mb from mb-2 */}
<h3 className={`text-base font-bold ${theme.text}`}>{title}</h3>
<span className={`px-1.5 py-0.5 bg-${badgeColor} text-${badgeColor}-900 rounded-full text-xs font-bold`}>
<div className="p-2 flex flex-col h-full">
{/* Header */}
<div className="flex items-center justify-between mb-1">
<div className="flex items-center gap-1">
<h3 className="text-sm font-semibold text-slate-800">{title}</h3>
</div>
<div
className={`xl:px-2 xl:py-1 px-1 py-0.5 rounded-full xl:text-xs text-[10px] font-bold ${badgeTextMap[badgeColor] || 'text-slate-800'}`}
style={{
background: badgeColor === 'yellow'
? '#facc15'
: badgeColor === 'green'
? '#22c55e'
: badgeColor === 'red'
? '#ef4444'
: badgeColor === 'blue'
? '#3b82f6'
: '#e5e7eb'
}}
>
{badge}
</span>
</div>
</div>

{/* Token info and requirements */}
<div className="flex-grow space-y-2">
{/* Token display */}
<div className="flex items-center gap-1.5 mb-2">
{tokenLogo && (
<img
{/* Stats */}
<div className="flex items-center gap-2 mb-4">
<div className="flex items-center gap-1 flex-wrap">
{tokenLogo ? (
<img
src={`${Gateway}${tokenLogo}`}
alt="Token"
className="w-5 h-5 rounded-full"
className="w-3 h-3 rounded-full"
/>
) : (
<span className="w-3 h-3 inline-block">🪙</span>
)}
<span className={`text-base font-medium ${tokenBalance >= tokenRequired ? 'text-green-500' : 'text-red-500'}`}>
{tokenBalance}/{tokenRequired}
</span>
</div>

{/* Costs and Rewards side by side */}
<div className="flex justify-between items-start">
{/* Costs section */}
<div className="space-y-1">
{costs.map((cost, index) => {
// Extract just the number from the text
const number = cost.text.match(/-?\d+/)?.[0] || '';
// Replace energy emoji with battery if present
const icon = cost.icon === '⚡' ? '🔋' : cost.icon;
return (
<div key={index} className="flex items-center gap-1">
<span>{icon}</span>
<span className={`text-base ${cost.isAvailable ? 'text-red-500' : 'text-red-700'}`}>{number}</span>
</div>
);
})}
</div>

{/* Rewards section */}
<div className="space-y-1">
{rewards.map((reward, index) => {
// Extract just the number from the text
const number = reward.text.match(/\d+/)?.[0] || '';
// Replace energy emoji with battery if present
const icon = reward.icon === '⚡' ? '🔋' : reward.icon;
return (
<div key={index} className="flex items-center gap-1">
<span>{icon}</span>
<span className={`text-base text-${reward.color}`}>+{number}</span>
</div>
);
})}
</div>
<span className="text-xs font-bold text-slate-800">{tokenBalance}/{tokenRequired}</span>
{/* Cost display */}
{costs.map((cost, index) => {
const number = cost.text.match(/-?\d+/)?.[0] || '';
const icon = getRewardIcon(cost);
return (
<div key={index} className="flex items-center gap-0.5">
<span className="text-xs">{icon}</span>
<span className={`text-xs font-semibold ${cost.isAvailable ? 'text-red-500' : 'text-red-400'}`}>
{number}
</span>
</div>
);
})}
{/* Reward display */}
{rewards.map((reward, index) => {
const number = reward.text.match(/\d+/)?.[0] || '';
const icon = getRewardIcon(reward);
return (
<div key={index} className="flex items-center gap-0.5">
<span className="text-xs">{icon}</span>
<span className="text-xs font-semibold text-green-500">+{number}</span>
</div>
);
})}
</div>
</div>

{/* Action button - always at bottom */}
<div className="mt-2">
<button
onClick={onAction}
disabled={isLoading || isDisabled}
className={`w-full px-3 py-1.5 rounded-lg font-bold text-white text-sm
${buttonStyle} transition-all duration-300
${!isDisabled && highlightSelectable ? 'ring-2 ring-offset-1 ring-' + gradientFrom : ''}`}
>
{buttonText}
</button>
</div>
{/* Action Button */}
<button
onClick={onAction}
disabled={isLoading || isDisabled}
className={`w-full ${buttonStyle} py-1.5 text-sm mt-auto rounded-lg transition-all duration-200 ${isLoading || isDisabled ? 'opacity-50 cursor-not-allowed' : ''}`}
>
{buttonText}
</button>
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Inventory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ const Inventory = () => {
const hasWallet = !!wallet?.address;

return (
<div className={`fixed right-4 top-32 ${theme.container} border ${theme.border} backdrop-blur-md transition-all duration-300 rounded-xl z-40 inventory-container max-w-[280px]`}>
<div className={`fixed right-4 top-32 ${theme.container} border ${theme.border} backdrop-blur-md transition-all duration-300 rounded-xl z-40 inventory-container w-full max-w-60`}>
<div className={`flex items-center justify-between p-3 ${theme.text}`}>
<div className="flex items-center gap-2 cursor-pointer" onClick={() => toggleSection('main')}>
<span className="text-xl">👜</span>
Expand Down
Loading