2026-03-28 12:35:12 -07:00
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.entry-item {
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border-bottom: 1px solid #e9ecef;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: background-color 0.2s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.entry-item:hover {
|
|
|
|
|
background-color: #f8f9fa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.entry-item.active {
|
|
|
|
|
background-color: #e7f3ff;
|
|
|
|
|
border-left: 4px solid #0d6efd;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
2026-03-28 09:14:09 -07:00
|
|
|
<template>
|
2026-04-16 04:47:41 +00:00
|
|
|
<div v-show="loading">
|
|
|
|
|
<p>Loading</p>
|
|
|
|
|
</div>
|
2026-03-28 09:14:09 -07:00
|
|
|
<!-- Error Alert -->
|
|
|
|
|
<div v-if="error" class="mt-3 alert alert-danger alert-dismissible">
|
|
|
|
|
{{ error }}
|
|
|
|
|
<button type="button" class="btn-close" @click="error = null"></button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="p-3 border-bottom bg-primary text-white">
|
|
|
|
|
<h5 class="mb-0"><i class="bi bi-list-ul"></i> Review Queue</h5>
|
|
|
|
|
<small>{{ total }} entries pending</small>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Loading State -->
|
|
|
|
|
<div v-if="tasks == null" class="p-4 text-center">
|
|
|
|
|
<span class="spinner-border" role="status"></span>
|
|
|
|
|
<p class="mt-2">Loading tasks...</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Empty State -->
|
|
|
|
|
<div v-else-if="tasks.length === 0" class="p-4 text-center text-muted">
|
|
|
|
|
<i class="bi bi-check-circle" style="font-size: 48px"></i>
|
|
|
|
|
<p class="mt-2">No entries to review!</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Task List -->
|
|
|
|
|
<div
|
|
|
|
|
v-for="task in tasks"
|
|
|
|
|
:key="task.id"
|
|
|
|
|
class="entry-item"
|
|
|
|
|
:class="{ active: selectedTaskID === task.id }"
|
2026-04-16 05:36:54 +00:00
|
|
|
@click="doClick(task)"
|
2026-03-28 09:14:09 -07:00
|
|
|
>
|
|
|
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
|
|
|
<div>
|
|
|
|
|
<i class="bi bi-droplet"></i>
|
|
|
|
|
<strong>Pool {{ task.id }}</strong>
|
|
|
|
|
</div>
|
2026-03-31 14:52:53 +00:00
|
|
|
<small class="text-muted">{{ task.pool?.condition }}</small>
|
2026-03-28 09:14:09 -07:00
|
|
|
</div>
|
|
|
|
|
<small class="text-muted d-block mt-1">
|
|
|
|
|
{{ formatAddress(task.address) }}
|
|
|
|
|
</small>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { formatAddress } from "@/format";
|
2026-04-09 00:25:21 +00:00
|
|
|
import { ReviewTask } from "@/type/api";
|
2026-03-28 09:14:09 -07:00
|
|
|
|
2026-03-28 12:35:12 -07:00
|
|
|
interface Emits {
|
2026-04-16 05:36:54 +00:00
|
|
|
(e: "doDeselectTask", id: number): void;
|
2026-03-31 14:52:53 +00:00
|
|
|
(e: "doSelectTask", id: number): void;
|
2026-03-28 12:35:12 -07:00
|
|
|
}
|
2026-03-28 09:14:09 -07:00
|
|
|
interface Props {
|
|
|
|
|
error: string | null;
|
2026-04-16 04:47:41 +00:00
|
|
|
loading?: boolean;
|
2026-03-31 14:52:53 +00:00
|
|
|
selectedTaskID: number | null;
|
2026-03-28 09:14:09 -07:00
|
|
|
tasks: ReviewTask[];
|
2026-03-31 14:52:53 +00:00
|
|
|
total: number;
|
2026-03-28 09:14:09 -07:00
|
|
|
}
|
2026-03-28 12:35:12 -07:00
|
|
|
const emit = defineEmits<Emits>();
|
2026-04-16 04:47:41 +00:00
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
|
|
loading: false,
|
|
|
|
|
});
|
2026-04-16 05:36:54 +00:00
|
|
|
function doClick(task: ReviewTask) {
|
|
|
|
|
if (task.id == props.selectedTaskID) {
|
|
|
|
|
emit("doDeselectTask", task.id);
|
|
|
|
|
} else {
|
|
|
|
|
emit("doSelectTask", task.id);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-28 09:14:09 -07:00
|
|
|
</script>
|