basic noita submissions
This commit is contained in:
+212
-15
@@ -1,23 +1,220 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
const userInfo = ref({
|
||||
username: "Player",
|
||||
rank: 42,
|
||||
score: 15420,
|
||||
runsSubmitted: 8,
|
||||
});
|
||||
|
||||
const uploadedFiles = ref<File[]>([]);
|
||||
const isUploading = ref(false);
|
||||
const isDragover = ref(false);
|
||||
|
||||
const handleFileUpload = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.files) {
|
||||
uploadedFiles.value = Array.from(input.files);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragover = (event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
isDragover.value = true;
|
||||
};
|
||||
|
||||
const handleDragleave = (event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
isDragover.value = false;
|
||||
};
|
||||
|
||||
const handleDrop = (event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
isDragover.value = false;
|
||||
|
||||
if (event.dataTransfer?.files) {
|
||||
uploadedFiles.value = Array.from(event.dataTransfer.files);
|
||||
}
|
||||
};
|
||||
|
||||
const submitRun = async () => {
|
||||
if (uploadedFiles.value.length === 0) return;
|
||||
|
||||
isUploading.value = true;
|
||||
try {
|
||||
for (const file of uploadedFiles.value) {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
const response = await fetch("/api/noita/submit", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
alert(`Error submitting ${file.name}: ${error.detail || "Unknown error"}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log("Submission successful:", result);
|
||||
}
|
||||
|
||||
uploadedFiles.value = [];
|
||||
alert("Run submitted successfully!");
|
||||
} catch (error) {
|
||||
console.error("Error submitting run:", error);
|
||||
alert("Error submitting run. Please try again.");
|
||||
} finally {
|
||||
isUploading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const goHome = () => {
|
||||
window.location.href = "/";
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen bg-base-200 flex items-center justify-center px-4">
|
||||
<div class="w-full max-w-2xl">
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="bg-gradient-to-br from-purple-600 to-purple-400 p-8 text-white">
|
||||
<i class="mdi mdi-wand-plus text-6xl"></i>
|
||||
<div class="min-h-screen bg-base-200">
|
||||
<!-- Header -->
|
||||
<div class="navbar bg-base-100 shadow-lg">
|
||||
<div class="container mx-auto w-full flex items-center gap-4">
|
||||
<button @click="goHome" class="btn btn-primary btn-sm">
|
||||
<i class="mdi mdi-arrow-left"></i>
|
||||
Back
|
||||
</button>
|
||||
<h1 class="text-xl font-bold">Noita Submitter</h1>
|
||||
<div class="flex-1"></div>
|
||||
<a href="/api/docs" class="btn btn-xs">API docs</a>
|
||||
<a href="/admin" class="btn btn-xs btn-warning">Admin panel</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<!-- Left Column: User Ranking -->
|
||||
<div class="lg:col-span-1">
|
||||
<div class="card bg-base-100 shadow-lg sticky top-8">
|
||||
<div class="bg-gradient-to-br from-purple-600 to-purple-400 p-6 text-white rounded-t-2xl">
|
||||
<i class="mdi mdi-trophy text-4xl"></i>
|
||||
<h2 class="text-2xl font-bold mt-2">Your Ranking</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="text-center mb-6">
|
||||
<p class="text-sm text-base-content/70">Player</p>
|
||||
<p class="text-3xl font-bold">{{ userInfo.username }}</p>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="text-center">
|
||||
<p class="text-sm text-base-content/70 mb-1">Current Rank</p>
|
||||
<p class="text-4xl font-bold text-primary">#{{ userInfo.rank }}</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<p class="text-sm text-base-content/70 mb-1">Total Score</p>
|
||||
<p class="text-2xl font-bold">{{ userInfo.score.toLocaleString() }}</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<p class="text-sm text-base-content/70 mb-1">Runs Submitted</p>
|
||||
<p class="text-2xl font-bold">{{ userInfo.runsSubmitted }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline btn-sm w-full mt-6">
|
||||
<i class="mdi mdi-refresh mr-1"></i>
|
||||
View Full Leaderboard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<h1 class="card-title text-4xl justify-center">Noita Submitter</h1>
|
||||
<p class="text-lg text-base-content/70 mt-4">
|
||||
Coming soon! Noita submission system is under development.
|
||||
</p>
|
||||
<div class="card-actions justify-center mt-8">
|
||||
<a href="/" class="btn btn-primary">
|
||||
<i class="mdi mdi-arrow-left mr-2"></i>
|
||||
Back to Home
|
||||
</a>
|
||||
|
||||
<!-- Right Column: Upload -->
|
||||
<div class="lg:col-span-2">
|
||||
<div class="card bg-base-100 shadow-lg">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-2xl mb-6">
|
||||
<i class="mdi mdi-cloud-upload text-purple-500 mr-2"></i>
|
||||
Submit Your Run
|
||||
</h2>
|
||||
|
||||
<!-- Upload Area -->
|
||||
<div
|
||||
@dragover="handleDragover"
|
||||
@dragleave="handleDragleave"
|
||||
@drop="handleDrop"
|
||||
:class="[
|
||||
'border-2 border-dashed rounded-lg p-8 text-center transition-colors cursor-pointer bg-base-200/50 mb-6',
|
||||
isDragover ? 'border-primary bg-primary/10' : 'border-base-300 hover:border-primary'
|
||||
]"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
multiple
|
||||
@change="handleFileUpload"
|
||||
class="hidden"
|
||||
id="file-upload"
|
||||
accept="video/*,image/*"
|
||||
/>
|
||||
<label for="file-upload" class="cursor-pointer flex flex-col items-center gap-3">
|
||||
<i :class="['mdi text-4xl', isDragover ? 'mdi-cloud-check text-primary' : 'mdi-file-upload text-base-content/50']"></i>
|
||||
<div>
|
||||
<p class="font-semibold">Click to upload or drag and drop</p>
|
||||
<p class="text-sm text-base-content/70">Video or image files (MP4, PNG, etc.)</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Uploaded Files List -->
|
||||
<div v-if="uploadedFiles.length > 0" class="mb-6">
|
||||
<p class="font-semibold mb-3">Selected Files:</p>
|
||||
<div class="space-y-2">
|
||||
<div v-for="(file, index) in uploadedFiles" :key="index" class="flex items-center gap-3 bg-base-200 p-3 rounded-lg">
|
||||
<i class="mdi mdi-file text-primary"></i>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-medium truncate">{{ file.name }}</p>
|
||||
<p class="text-xs text-base-content/70">{{ (file.size / 1024 / 1024).toFixed(2) }} MB</p>
|
||||
</div>
|
||||
<button
|
||||
@click="uploadedFiles.splice(index, 1)"
|
||||
class="btn btn-ghost btn-xs"
|
||||
>
|
||||
<i class="mdi mdi-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="flex gap-3">
|
||||
<label for="file-upload" class="btn btn-outline flex-1">
|
||||
<i class="mdi mdi-folder-open mr-2"></i>
|
||||
Choose Files
|
||||
</label>
|
||||
<button
|
||||
@click="submitRun"
|
||||
:disabled="uploadedFiles.length === 0 || isUploading"
|
||||
:class="['btn btn-primary flex-1', { 'loading': isUploading }]"
|
||||
>
|
||||
<i v-if="!isUploading" class="mdi mdi-send mr-2"></i>
|
||||
{{ isUploading ? 'Submitting...' : 'Submit Run' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-base-content/70 text-center mt-4">
|
||||
Maximum file size: 256 MB per file
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user