noita update
This commit is contained in:
+108
-42
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
|
||||
interface Objective {
|
||||
objectiv_id: string;
|
||||
count: number;
|
||||
points_per_objectiv?: number;
|
||||
total_points?: number;
|
||||
}
|
||||
|
||||
const userInfo = ref({
|
||||
@@ -18,11 +20,62 @@ const uploadedFiles = ref<File[]>([]);
|
||||
const isUploading = ref(false);
|
||||
const isDragover = ref(false);
|
||||
const objectives = ref<Objective[]>([]);
|
||||
const isLoadingObjectives = ref(false);
|
||||
const objectiveSearchQuery = ref("");
|
||||
const objectiveSortBy = ref<"id" | "count" | "points_per" | "total_points">("id");
|
||||
const objectiveSortDesc = ref(false);
|
||||
const isLoadingLeaderboard = ref(false);
|
||||
const leaderboard = ref<any[]>([]);
|
||||
const isLeaderboardModalOpen = ref(false);
|
||||
|
||||
const filteredObjectives = computed(() => {
|
||||
const query = objectiveSearchQuery.value.toLowerCase();
|
||||
|
||||
let filtered = objectives.value;
|
||||
|
||||
if (query) {
|
||||
filtered = filtered.filter(
|
||||
(obj) =>
|
||||
obj.objectiv_id.toLowerCase().includes(query) ||
|
||||
obj.count.toString().includes(query)
|
||||
);
|
||||
}
|
||||
|
||||
const sorted = [...filtered].sort((a, b) => {
|
||||
let aValue: number | string;
|
||||
let bValue: number | string;
|
||||
|
||||
switch (objectiveSortBy.value) {
|
||||
case "points_per":
|
||||
aValue = a.points_per_objectiv || 0;
|
||||
bValue = b.points_per_objectiv || 0;
|
||||
break;
|
||||
case "total_points":
|
||||
aValue = a.total_points || 0;
|
||||
bValue = b.total_points || 0;
|
||||
break;
|
||||
case "id":
|
||||
default:
|
||||
aValue = a.objectiv_id.toLowerCase();
|
||||
bValue = b.objectiv_id.toLowerCase();
|
||||
}
|
||||
|
||||
if (aValue < bValue) return objectiveSortDesc.value ? 1 : -1;
|
||||
if (aValue > bValue) return objectiveSortDesc.value ? -1 : 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return sorted;
|
||||
});
|
||||
|
||||
const toggleObjectiveSort = (column: "id" | "points_per" | "total_points") => {
|
||||
if (objectiveSortBy.value === column) {
|
||||
objectiveSortDesc.value = !objectiveSortDesc.value;
|
||||
} else {
|
||||
objectiveSortBy.value = column;
|
||||
objectiveSortDesc.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.files) {
|
||||
@@ -81,7 +134,6 @@ const submitRun = async () => {
|
||||
|
||||
// Refresh objectives, score, and rank after successful submission
|
||||
await Promise.all([
|
||||
fetchObjectives(),
|
||||
fetchUserResults(),
|
||||
fetchLeaderboard(),
|
||||
]);
|
||||
@@ -97,20 +149,6 @@ const goHome = () => {
|
||||
window.location.href = "/";
|
||||
};
|
||||
|
||||
const fetchObjectives = async () => {
|
||||
isLoadingObjectives.value = true;
|
||||
try {
|
||||
const response = await fetch("/api/noita/objectives");
|
||||
if (!response.ok) throw new Error("Failed to fetch objectives");
|
||||
|
||||
objectives.value = await response.json();
|
||||
} catch (error) {
|
||||
console.error("Error fetching objectives:", error);
|
||||
} finally {
|
||||
isLoadingObjectives.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchUserResults = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/noita/results");
|
||||
@@ -119,6 +157,7 @@ const fetchUserResults = async () => {
|
||||
const results = await response.json();
|
||||
userInfo.value.score = results.total_score;
|
||||
userInfo.value.runsSubmitted = results.objectives.length;
|
||||
objectives.value = results.objectives;
|
||||
} catch (error) {
|
||||
console.error("Error fetching results:", error);
|
||||
}
|
||||
@@ -159,7 +198,6 @@ const clearCache = async () => {
|
||||
alert("Cache cleared successfully!");
|
||||
// Refresh data after clearing cache
|
||||
await Promise.all([
|
||||
fetchObjectives(),
|
||||
fetchUserResults(),
|
||||
fetchLeaderboard(),
|
||||
]);
|
||||
@@ -188,9 +226,8 @@ const loadUserData = async () => {
|
||||
console.error("Error fetching user info:", error);
|
||||
}
|
||||
|
||||
// Fetch objectives, results, and leaderboard
|
||||
// Fetch results and leaderboard
|
||||
await Promise.all([
|
||||
fetchObjectives(),
|
||||
fetchUserResults(),
|
||||
fetchLeaderboard(),
|
||||
]);
|
||||
@@ -343,32 +380,61 @@ onMounted(() => {
|
||||
Your Objectives
|
||||
</h2>
|
||||
|
||||
<div v-if="isLoadingObjectives" class="flex justify-center py-8">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
</div>
|
||||
|
||||
<div v-else-if="objectives.length === 0" class="text-center py-8">
|
||||
<div v-if="objectives.length === 0" class="text-center py-8">
|
||||
<p class="text-base-content/70 mb-2">No objectives completed yet</p>
|
||||
<p class="text-sm text-base-content/50">Submit your runs to unlock objectives!</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="overflow-x-auto">
|
||||
<table class="table table-zebra w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Objective ID</th>
|
||||
<th class="text-right">Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="obj in objectives" :key="obj.objectiv_id">
|
||||
<td class="font-medium">{{ obj.objectiv_id }}</td>
|
||||
<td class="text-right">
|
||||
<span class="badge badge-primary badge-lg">{{ obj.count }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-if="objectives.length > 0" class="space-y-4">
|
||||
<!-- Search Input -->
|
||||
<input v-model="objectiveSearchQuery" type="text" placeholder="Search objectives..."
|
||||
class="input input-bordered w-full" />
|
||||
|
||||
<!-- Results Summary -->
|
||||
<div class="text-sm text-base-content/70">
|
||||
Showing {{ filteredObjectives.length }} of {{ objectives.length }} objectives
|
||||
</div>
|
||||
|
||||
<!-- Objectives Table -->
|
||||
<div v-if="filteredObjectives.length > 0" class="overflow-x-auto">
|
||||
<table class="table table-zebra w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="cursor-pointer hover:bg-base-300" @click="toggleObjectiveSort('id')">
|
||||
Objective ID
|
||||
<i v-if="objectiveSortBy === 'id'"
|
||||
:class="['mdi ml-2', objectiveSortDesc ? 'mdi-arrow-down' : 'mdi-arrow-up']"></i>
|
||||
</th>
|
||||
<th class="text-right cursor-pointer hover:bg-base-300"
|
||||
@click="toggleObjectiveSort('total_points')">
|
||||
Total Points
|
||||
<i v-if="objectiveSortBy === 'total_points'"
|
||||
:class="['mdi ml-2', objectiveSortDesc ? 'mdi-arrow-down' : 'mdi-arrow-up']"></i>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="obj in filteredObjectives" :key="obj.objectiv_id">
|
||||
<td class="font-medium">
|
||||
<a :href="`https://noita.wiki.gg/wiki/${obj.objectiv_id}`" target="_blank">
|
||||
{{ obj.objectiv_id }}
|
||||
<i class="mdi mdi-open-in-new"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-right font-bold text-success">
|
||||
{{ obj.total_points || 0 }}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- No Results -->
|
||||
<div v-if="filteredObjectives.length === 0" class="text-center py-8">
|
||||
<p class="text-base-content/70">No objectives match your search</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user