user info + max size

This commit is contained in:
2025-10-29 03:31:32 +01:00
parent 52723b200a
commit 961e14bd43
10 changed files with 210 additions and 53 deletions
+50 -5
View File
@@ -2,13 +2,15 @@
import { ref, onMounted, computed } from 'vue'
import PuzzleCard from './components/PuzzleCard.vue'
import SubmissionForm from './components/SubmissionForm.vue'
import { puzzleHelpers, submissionHelpers, errorHelpers } from './services/apiService'
import type { SteamCollection, SteamCollectionItem, Submission, PuzzleResponse } from './types'
import AdminPanel from './components/AdminPanel.vue'
import { puzzleHelpers, submissionHelpers, errorHelpers, apiService } from './services/apiService'
import type { SteamCollection, SteamCollectionItem, Submission, PuzzleResponse, UserInfo } from './types'
// API data
const collections = ref<SteamCollection[]>([])
const puzzles = ref<SteamCollectionItem[]>([])
const submissions = ref<Submission[]>([])
const userInfo = ref<UserInfo | null>(null)
const isLoading = ref(true)
const showSubmissionModal = ref(false)
const error = ref<string>('')
@@ -92,15 +94,22 @@ const mockPuzzles: SteamCollectionItem[] = [
}
]
// Computed properties
const isSuperuser = computed(() => {
return userInfo.value?.is_superuser || false
})
// Computed property to get responses grouped by puzzle
const responsesByPuzzle = computed(() => {
const grouped: Record<number, PuzzleResponse[]> = {}
submissions.value.forEach(submission => {
submission.responses.forEach(response => {
if (!grouped[response.puzzle_id]) {
grouped[response.puzzle_id] = []
// Handle both number and object types for puzzle field
const puzzleId = typeof response.puzzle === 'number' ? response.puzzle : response.puzzle.id
if (!grouped[puzzleId]) {
grouped[puzzleId] = []
}
grouped[response.puzzle_id].push(response)
grouped[puzzleId].push(response)
})
})
return grouped
@@ -111,9 +120,23 @@ onMounted(async () => {
isLoading.value = true
error.value = ''
console.log('Starting data load...')
// Load user info
console.log('Loading user info...')
const userResponse = await apiService.getUserInfo()
if (userResponse.data) {
userInfo.value = userResponse.data
console.log('User info loaded:', userResponse.data)
} else if (userResponse.error) {
console.warn('User info error:', userResponse.error)
}
// Load puzzles from API
console.log('Loading puzzles...')
const loadedPuzzles = await puzzleHelpers.loadPuzzles()
puzzles.value = loadedPuzzles
console.log('Puzzles loaded:', loadedPuzzles.length)
// Create mock collection from loaded puzzles for display
if (loadedPuzzles.length > 0) {
@@ -129,17 +152,23 @@ onMounted(async () => {
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
}]
console.log('Collection created')
}
// Load existing submissions
console.log('Loading submissions...')
const loadedSubmissions = await submissionHelpers.loadSubmissions()
submissions.value = loadedSubmissions
console.log('Submissions loaded:', loadedSubmissions.length)
console.log('Data load complete!')
} catch (err) {
error.value = errorHelpers.getErrorMessage(err)
console.error('Failed to load data:', err)
} finally {
isLoading.value = false
console.log('Loading state set to false')
}
})
@@ -208,6 +237,17 @@ const findPuzzleByName = (ocrPuzzleName: string): SteamCollectionItem | null =>
<div class="flex-1">
<h1 class="text-xl font-bold">Opus Magnum Puzzle Submitter</h1>
</div>
<div class="flex-none">
<div v-if="userInfo?.is_authenticated" class="flex items-center gap-2">
<div class="text-sm">
<span class="font-medium">{{ userInfo.username }}</span>
<span v-if="userInfo.is_superuser" class="badge badge-warning badge-xs ml-1">Admin</span>
</div>
</div>
<div v-else class="text-sm text-base-content/70">
Not logged in
</div>
</div>
</div>
</div>
@@ -255,6 +295,11 @@ const findPuzzleByName = (ocrPuzzleName: string): SteamCollectionItem | null =>
</div>
</div>
<!-- Admin Panel (only for superusers) -->
<div v-if="isSuperuser">
<AdminPanel />
</div>
<!-- Puzzles Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<PuzzleCard
+16 -4
View File
@@ -183,10 +183,22 @@ const loadData = async () => {
try {
isLoading.value = true
// Load stats
const statsResponse = await apiService.getStats()
if (statsResponse.data) {
stats.value = statsResponse.data
// Load stats (skip if endpoint doesn't exist)
try {
const statsResponse = await apiService.getStats()
if (statsResponse.data) {
stats.value = statsResponse.data
}
} catch (error) {
console.warn('Stats endpoint not available:', error)
// Set default stats
stats.value = {
total_submissions: 0,
total_responses: 0,
needs_validation: 0,
validated_submissions: 0,
validation_rate: 0
}
}
// Load responses needing validation
+4 -4
View File
@@ -37,7 +37,7 @@
</button>
</div>
<p class="text-xs text-base-content/50">
Supported formats: JPG, PNG, GIF (max 10MB each)
Supported formats: JPG, PNG, GIF (max 256MB each)
</p>
</div>
@@ -238,9 +238,9 @@ const isValidFile = (file: File): boolean => {
return false
}
// Check file size (10MB limit)
if (file.size > 10 * 1024 * 1024) {
error.value = `${file.name} is too large (max 10MB)`
// Check file size (256MB limit)
if (file.size > 256 * 1024 * 1024) {
error.value = `${file.name} is too large (max 256MB)`
return false
}
+1 -1
View File
@@ -48,7 +48,7 @@
<span class="text-sm font-medium">Solutions ({{ responses.length }})</span>
</div>
<div class="overflow-x-auto">
<div>
<table class="table table-xs">
<thead>
<tr>
+7 -1
View File
@@ -2,7 +2,8 @@ import type {
SteamCollectionItem,
Submission,
PuzzleResponse,
SubmissionFile
SubmissionFile,
UserInfo
} from '../types'
// API Configuration
@@ -179,6 +180,11 @@ export class ApiService {
async healthCheck(): Promise<ApiResponse<{ status: string; service: string }>> {
return this.request<{ status: string; service: string }>('/health')
}
// User info
async getUserInfo(): Promise<ApiResponse<UserInfo>> {
return this.request<UserInfo>('/user')
}
}
// Singleton instance
+12
View File
@@ -73,3 +73,15 @@ export interface Submission {
created_at?: string
updated_at?: string
}
export interface UserInfo {
id?: number
username?: string
first_name?: string
last_name?: string
email?: string
is_authenticated: boolean
is_staff: boolean
is_superuser: boolean
cas_groups?: string[]
}