You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
4.9 KiB
TypeScript

import { useState, type CSSProperties } from 'react';
import type { DailyTaskResponse } from '../network/api';
interface DailyTasksProps {
tasks: DailyTaskResponse[];
onClaim: (taskId: string) => void;
}
const buttonStyle: CSSProperties = {
position: 'fixed',
top: 12,
right: 12,
zIndex: 50,
display: 'flex',
alignItems: 'center',
gap: 4,
padding: '5px 10px',
borderRadius: 8,
border: '1px solid rgba(255, 215, 0, 0.2)',
backgroundColor: 'rgba(0,0,0,0.55)',
color: '#daa520',
fontSize: 11,
fontWeight: 600,
cursor: 'pointer',
pointerEvents: 'auto',
userSelect: 'none',
};
const panelStyle: CSSProperties = {
position: 'fixed',
top: 12,
right: 12,
zIndex: 50,
width: 230,
borderRadius: 10,
border: '1px solid rgba(255, 215, 0, 0.15)',
backgroundColor: 'rgba(10, 10, 20, 0.88)',
backdropFilter: 'blur(6px)',
overflow: 'hidden',
pointerEvents: 'auto',
};
const panelHeaderStyle: CSSProperties = {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '7px 10px',
borderBottom: '1px solid rgba(255,255,255,0.08)',
fontSize: 12,
fontWeight: 700,
color: '#daa520',
};
const closeButtonStyle: CSSProperties = {
background: 'none',
border: 'none',
color: '#888',
fontSize: 14,
cursor: 'pointer',
padding: '0 4px',
lineHeight: 1,
};
const taskListStyle: CSSProperties = {
padding: '6px 10px 8px',
};
const taskRowStyle: CSSProperties = {
marginBottom: 8,
};
const taskLabelStyle: CSSProperties = {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: 11,
color: '#ccc',
marginBottom: 3,
};
const progressBarBgStyle: CSSProperties = {
width: '100%',
height: 6,
borderRadius: 3,
backgroundColor: 'rgba(255,255,255,0.08)',
overflow: 'hidden',
};
function progressBarFillStyle(pct: number, done: boolean): CSSProperties {
return {
width: `${Math.min(100, pct)}%`,
height: '100%',
borderRadius: 3,
backgroundColor: done ? '#44cc44' : '#daa520',
transition: 'width 300ms ease',
};
}
const claimButtonStyle: CSSProperties = {
marginTop: 4,
padding: '2px 10px',
borderRadius: 4,
border: '1px solid rgba(255, 215, 0, 0.4)',
backgroundColor: 'rgba(255, 215, 0, 0.15)',
color: '#ffd700',
fontSize: 10,
fontWeight: 600,
cursor: 'pointer',
display: 'block',
width: '100%',
textAlign: 'center',
};
export function DailyTasks({ tasks, onClaim }: DailyTasksProps) {
const [expanded, setExpanded] = useState(false);
const completedCount = tasks.filter((t) => t.completed).length;
const claimableCount = tasks.filter((t) => t.completed && !t.claimed).length;
if (!expanded) {
return (
<button style={buttonStyle} onClick={() => setExpanded(true)}>
<span style={{ fontSize: 13 }}>&#x2B50;</span>
<span>
Daily {completedCount}/{tasks.length}
{claimableCount > 0 && (
<span style={{ color: '#ffd700', marginLeft: 4 }}>!</span>
)}
</span>
</button>
);
}
return (
<div style={panelStyle}>
<div style={panelHeaderStyle}>
<span>&#x2B50; Daily Tasks</span>
<button style={closeButtonStyle} onClick={() => setExpanded(false)}>
&#x2715;
</button>
</div>
<div style={taskListStyle}>
{tasks.map((task) => {
const done = task.completed;
const pct = (task.progress / Math.max(1, task.objectiveCount)) * 100;
const canClaim = task.completed && !task.claimed;
return (
<div key={task.taskId} style={taskRowStyle}>
<div style={taskLabelStyle}>
<span style={{ opacity: task.claimed ? 0.5 : done ? 0.8 : 1 }}>
{task.claimed ? '\u2705 ' : done ? '\u2714 ' : ''}{task.title}
</span>
<span style={{
fontSize: 10,
color: task.claimed ? '#666' : done ? '#44cc44' : '#999',
fontWeight: 600,
}}>
{task.progress}/{task.objectiveCount}
</span>
</div>
<div style={progressBarBgStyle}>
<div style={progressBarFillStyle(pct, done)} />
</div>
{task.rewardAmount > 0 && (
<div style={{ fontSize: 9, color: '#b8860b', marginTop: 2 }}>
+{task.rewardAmount} {task.rewardType}
</div>
)}
{canClaim && (
<button
style={claimButtonStyle}
onClick={() => onClaim(task.taskId)}
>
Claim Reward
</button>
)}
</div>
);
})}
{tasks.length === 0 && (
<div style={{ textAlign: 'center', opacity: 0.5, padding: 8, fontSize: 11 }}>
No daily tasks available
</div>
)}
</div>
</div>
);
}