aboutsummaryrefslogtreecommitdiffstats
path: root/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/components/displays/DancerushScoreDisplay.tsx25
-rw-r--r--frontend/src/components/displays/GenericScoreDisplay.tsx25
-rw-r--r--frontend/src/pages/Game.tsx0
-rw-r--r--frontend/src/pages/Score.tsx32
-rw-r--r--frontend/src/types/constants.ts2
5 files changed, 79 insertions, 5 deletions
diff --git a/frontend/src/components/displays/DancerushScoreDisplay.tsx b/frontend/src/components/displays/DancerushScoreDisplay.tsx
index e99f6e9..b030db7 100644
--- a/frontend/src/components/displays/DancerushScoreDisplay.tsx
+++ b/frontend/src/components/displays/DancerushScoreDisplay.tsx
@@ -15,6 +15,7 @@ interface ScoreDisplayProps {
sortField: string;
sortDirection: "asc" | "desc";
onSort: (field: string) => void;
+ onDelete?: (scoreId: number) => void;
}
const DancerushScoreDisplay: React.FC<ScoreDisplayProps> = ({
@@ -23,6 +24,7 @@ const DancerushScoreDisplay: React.FC<ScoreDisplayProps> = ({
sortField,
sortDirection,
onSort,
+ onDelete,
}) => {
// Key mappings for better display names. Hit or miss
const keyDisplayNames: Record<string, string> = {
@@ -242,6 +244,9 @@ const DancerushScoreDisplay: React.FC<ScoreDisplayProps> = ({
"timestamp",
].filter((key) => allKeys.includes(key));
+ // Add actions column if delete function is provided
+ const showActions = onDelete && viewMode === "table";
+
if (scores.length === 0) {
return (
<div className="text-center py-16">
@@ -374,13 +379,18 @@ const DancerushScoreDisplay: React.FC<ScoreDisplayProps> = ({
)}
</th>
))}
+ {showActions && (
+ <th className="px-4 py-3 text-left text-slate-300 font-medium w-16">
+ Actions
+ </th>
+ )}
</tr>
</thead>
<tbody className="divide-y divide-slate-800/50">
{sortedScores.map((score, index) => (
<tr
key={score.id || index}
- className="hover:bg-slate-800/30 transition-colors"
+ className="hover:bg-slate-800/30 transition-colors group"
>
{tableKeys.map((key) => (
<td key={key} className="px-4 py-3">
@@ -411,6 +421,19 @@ const DancerushScoreDisplay: React.FC<ScoreDisplayProps> = ({
)}
</td>
))}
+ {showActions && (
+ <td className="px-4 py-3">
+ <button
+ onClick={() => onDelete(score.id)}
+ className="text-red-400 hover:text-red-300 opacity-100 transition-opacity duration-200 p-1 rounded bg-red-500/10"
+ title="Delete score"
+ >
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
+ </svg>
+ </button>
+ </td>
+ )}
</tr>
))}
</tbody>
diff --git a/frontend/src/components/displays/GenericScoreDisplay.tsx b/frontend/src/components/displays/GenericScoreDisplay.tsx
index c30e475..45bf1ca 100644
--- a/frontend/src/components/displays/GenericScoreDisplay.tsx
+++ b/frontend/src/components/displays/GenericScoreDisplay.tsx
@@ -13,6 +13,7 @@ interface ScoreDisplayProps {
sortField: string;
sortDirection: "asc" | "desc";
onSort: (field: string) => void;
+ onDelete?: (scoreId: number) => void;
}
const ScoreDisplay: React.FC<ScoreDisplayProps> = ({
@@ -21,6 +22,7 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({
sortField,
sortDirection,
onSort,
+ onDelete,
}) => {
// Key mappings for better display names. Hit or miss
const keyDisplayNames: Record<string, string> = {
@@ -238,6 +240,9 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({
"timestamp",
].filter((key) => allKeys.includes(key));
+ // Add actions column if delete function is provided
+ const showActions = onDelete && viewMode === "table";
+
if (scores.length === 0) {
return (
<div className="text-center py-16">
@@ -369,13 +374,18 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({
)}
</th>
))}
+ {showActions && (
+ <th className="px-4 py-3 text-left text-slate-300 font-medium w-16">
+ Actions
+ </th>
+ )}
</tr>
</thead>
<tbody className="divide-y divide-slate-800/50">
{sortedScores.map((score, index) => (
<tr
key={score.id || index}
- className="hover:bg-slate-800/30 transition-colors"
+ className="hover:bg-slate-800/30 transition-colors group"
>
{tableKeys.map((key) => (
<td key={key} className="px-4 py-3">
@@ -406,6 +416,19 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({
)}
</td>
))}
+ {showActions && (
+ <td className="px-4 py-3">
+ <button
+ onClick={() => onDelete(score.id)}
+ className="text-red-400 hover:text-red-300 opacity-100 transition-opacity duration-200 p-1 rounded bg-red-500/10"
+ title="Delete score"
+ >
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
+ </svg>
+ </button>
+ </td>
+ )}
</tr>
))}
</tbody>
diff --git a/frontend/src/pages/Game.tsx b/frontend/src/pages/Game.tsx
deleted file mode 100644
index e69de29..0000000
--- a/frontend/src/pages/Game.tsx
+++ /dev/null
diff --git a/frontend/src/pages/Score.tsx b/frontend/src/pages/Score.tsx
index e32f001..0d18d7a 100644
--- a/frontend/src/pages/Score.tsx
+++ b/frontend/src/pages/Score.tsx
@@ -5,7 +5,6 @@ import { NavBar } from "../components/NavBar";
import SessionExpiredPopup from "../components/SessionExpiredPopup";
import ScoreDisplay from "../components/displays/GenericScoreDisplay";
import DancerushScoreDisplay from "../components/displays/DancerushScoreDisplay";
-// TODO: selector for PB/Recent
type SortField = string;
type SortDirection = "asc" | "desc";
@@ -79,7 +78,7 @@ const Score = () => {
url.searchParams.append("sortKey", requestOrder);
url.searchParams.append("direction", "asc");
- const response = await fetch(url.toString());
+ const response = await fetch(url.toString(), {credentials: 'include'});
if (!response.ok) throw new Error("Failed to fetch scores");
const data = await response.json();
const flattened = data.scores.map(flattenScoreData);
@@ -96,6 +95,33 @@ const Score = () => {
[user, gameName, requestOrder],
);
+ const handleDeleteScore = async (scoreId: number) => {
+ if (!user) return;
+
+ if (!confirm("Are you sure you want to delete this score? This action cannot be undone.")) {
+ return;
+ }
+
+ try {
+ const url = new URL(import.meta.env.VITE_API_URL + "/scores");
+ url.searchParams.append("userId", user.id);
+ url.searchParams.append("internalGameName", gameName);
+ url.searchParams.append("scoreId", scoreId.toString());
+
+ const response = await fetch(url.toString(), {
+ method: "DELETE",
+ credentials: "include",
+ });
+
+ if (!response.ok) throw new Error("Failed to delete score");
+
+ await fetchScores(currentPage);
+ } catch (error) {
+ console.error("Failed to delete score:", error);
+ alert("Failed to delete score. Please try again.");
+ }
+ };
+
useEffect(() => {
if (user) fetchScores(1);
}, [user, fetchScores]);
@@ -175,6 +201,7 @@ const Score = () => {
sortField={sortField}
sortDirection={sortDirection}
onSort={handleSort}
+ onDelete={handleDeleteScore}
/>
);
default:
@@ -185,6 +212,7 @@ const Score = () => {
sortField={sortField}
sortDirection={sortDirection}
onSort={handleSort}
+ onDelete={handleDeleteScore}
/>
);
}
diff --git a/frontend/src/types/constants.ts b/frontend/src/types/constants.ts
index b1309e9..028545d 100644
--- a/frontend/src/types/constants.ts
+++ b/frontend/src/types/constants.ts
@@ -17,7 +17,7 @@ export function getFilterOptions(game: string): { value: string; label: string }
switch (game) {
case "dancerush":
return [
- { value: "timestamp", label: "Recent" },
+ { value: "timestamp", label: "Recently Played" },
{ value: "score", label: "Score" },
{ value: "lamp", label: "Rank" },
{ value: "lamp_diff", label: "Difficulty"}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage