opus-submitter/polylan_submitter/src/components/PuzzleCard.vue

149 lines
6.0 KiB
Vue

<template>
<div class="card bg-base-100 shadow-lg hover:shadow-2xl transition-shadow duration-300"
:class="responses?.length == 0 ? 'shadow-red-900' : 'shadow-primary-300'">
<div class="card-body">
<div class="flex items-start justify-between">
<div class="flex-1">
<h3 class="card-title text-lg font-bold" :class="responses?.length == 0 ? 'text-error' : 'text-primary'">
{{ puzzle.title }}
</h3>
<p class="text-sm text-base-content/70 mb-2">
by {{ puzzle.author_name }}
</p>
<div class="flex items-center gap-2 mb-3">
<div class="badge badge-primary badge-sm">
{{ puzzle.steam_item_id }}
</div>
<div class="badge badge-ghost badge-sm">ID: {{ puzzle.id }}</div>
</div>
<p v-if="puzzle.description" class="text-sm text-base-content/80 mb-4">
{{ puzzle.description }}
</p>
<!-- Points Factor Coefficients -->
<div v-if="puzzle.points_factor" class="bg-base-200 p-3 rounded-lg mb-4">
<p class="text-xs text-base-content/70 font-semibold mb-2">Points Coefficients</p>
<div class="grid grid-cols-3 gap-2">
<div class="text-center">
<span class="font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cost }}</span>
<p class="text-xs text-base-content/70">Cost</p>
</div>
<div class="text-center">
<span class="font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cycles }}</span>
<p class="text-xs text-base-content/70">Cycles</p>
</div>
<div class="text-center">
<span class="font-bold text-primary"><small>x</small>{{ puzzle.points_factor.area }}</span>
<p class="text-xs text-base-content/70">Area</p>
</div>
</div>
</div>
<div v-if="puzzle.tags && puzzle.tags.length > 0" class="flex flex-wrap gap-1 mb-4">
<span v-for="tag in puzzle.tags.slice(0, 3)" :key="tag" class="badge badge-outline badge-xs">
{{ tag }}
</span>
<span v-if="puzzle.tags.length > 3" class="badge badge-outline badge-xs">
+{{ puzzle.tags.length - 3 }} more
</span>
</div>
</div>
<div class="flex flex-col items-end gap-2">
<div class="tooltip" data-tip="View on Steam Workshop">
<a :href="`https://steamcommunity.com/workshop/filedetails/?id=${puzzle.steam_item_id}`" target="_blank"
class="btn btn-ghost btn-sm btn-square">
<i class="mdi mdi-steam text-lg"></i>
</a>
</div>
</div>
</div>
<!-- Responses Table -->
<div v-if="responses && responses.length > 0" class="mt-1">
<div class="divider">
<span class="text-sm font-medium">Solutions ({{ responses.length }})</span>
</div>
<div>
<table class="table table-xs">
<thead>
<tr>
<th>Cost</th>
<th>Cycles</th>
<th>Area</th>
<th>Files</th>
</tr>
</thead>
<tbody>
<tr v-for="response in responses" :key="response.id" class="hover">
<td>
<span v-if="response.final_cost || response.cost" class="badge badge-success badge-xs">
{{ response.final_cost || response.cost }}
</span>
<span v-else class="text-base-content/50">-</span>
</td>
<td>
<span v-if="response.final_cycles || response.cycles" class="badge badge-info badge-xs">
{{ response.final_cycles || response.cycles }}
</span>
<span v-else class="text-base-content/50">-</span>
</td>
<td>
<span v-if="response.final_area || response.area" class="badge badge-warning badge-xs">
{{ response.final_area || response.area }}
</span>
<span v-else class="text-base-content/50">-</span>
</td>
<td>
<div class="flex items-center gap-1">
<span class="badge badge-ghost badge-xs">{{
response.files?.length || 0
}}</span>
<div v-if="response.files?.length" class="tooltip" :data-tip="response.files
.map((f) => f.original_filename || f.file?.name)
.join(', ')
">
<i class="mdi mdi-information-outline text-xs"></i>
</div>
<div v-if="response.needs_manual_validation" class="tooltip" data-tip="Needs manual validation">
<i class="mdi mdi-alert-circle text-xs text-warning"></i>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- No responses state -->
<div v-else
class="mt-6 text-center py-4 border-2 border-dashed border-base-300 rounded-lg hover:border-primary transition-colors duration-300 cursor-pointer"
@click="openSubmissionModal">
<i class="mdi mdi-upload text-2xl text-base-content/40"></i>
<p class="text-sm text-base-content/60 mt-2">No solutions yet</p>
<p class="text-xs text-base-content/40">
Upload solutions using the submit button
</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { SteamCollectionItem, PuzzleResponse } from "@/types";
import { useSubmissionsStore } from "@/stores/submissions";
interface Props {
puzzle: SteamCollectionItem;
responses?: PuzzleResponse[];
}
defineProps<Props>();
const { openSubmissionModal } = useSubmissionsStore();
</script>