admin
This commit is contained in:
parent
c2a36842b6
commit
7604fe2004
2 changed files with 91 additions and 12 deletions
|
|
@ -3,7 +3,9 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|||
import { toast } from 'sonner'
|
||||
import { packsApi, isPacksApiError } from '@/api/packs'
|
||||
import { formatApiError, getDetailedErrorMessage } from '@/lib/error-utils'
|
||||
import type { EditCardPackDto, CardPackPreviewDto, PaginatedResponse, Question } from '@/types/models'
|
||||
import type { EditCardPackDto, CardPackPreviewDto, PaginatedResponse } from '@/types/models'
|
||||
import type { Question } from '@/types/questions'
|
||||
import { questionFromJson, questionToJson } from '@/types/questions'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
|
|
@ -37,6 +39,8 @@ import { Label } from '@/components/ui/label'
|
|||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Plus, Search, Edit, Trash2, ChevronLeft, ChevronRight } from 'lucide-react'
|
||||
import { TestQuestionsManager } from '@/components/TestQuestionsManager'
|
||||
import { QuestionEditorDialog } from '@/components/QuestionEditorDialog'
|
||||
|
||||
export default function PacksPage() {
|
||||
const queryClient = useQueryClient()
|
||||
|
|
@ -48,6 +52,13 @@ export default function PacksPage() {
|
|||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
|
||||
const [packToDelete, setPackToDelete] = useState<CardPackPreviewDto | null>(null)
|
||||
|
||||
// Question editor state
|
||||
const [isQuestionEditorOpen, setIsQuestionEditorOpen] = useState(false)
|
||||
const [editingQuestion, setEditingQuestion] = useState<{
|
||||
question: Question | null
|
||||
index: number
|
||||
} | null>(null)
|
||||
|
||||
// Form state
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
|
|
@ -139,12 +150,18 @@ export default function PacksPage() {
|
|||
try {
|
||||
const fullPack = await packsApi.getPack(pack.id)
|
||||
setSelectedPack(fullPack)
|
||||
|
||||
// Convert questions from backend format to frontend format
|
||||
const questions: Question[] = Array.isArray(fullPack.questions)
|
||||
? fullPack.questions.map((q: unknown) => questionFromJson(q))
|
||||
: []
|
||||
|
||||
setFormData({
|
||||
name: fullPack.name || '',
|
||||
description: fullPack.description || '',
|
||||
category: fullPack.category || '',
|
||||
isPublic: fullPack.isPublic ?? true,
|
||||
questions: fullPack.questions || [],
|
||||
questions,
|
||||
})
|
||||
|
||||
setIsDialogOpen(true)
|
||||
|
|
@ -180,6 +197,9 @@ export default function PacksPage() {
|
|||
return
|
||||
}
|
||||
|
||||
// Convert questions to backend format
|
||||
const questionsForBackend = formData.questions.map(q => questionToJson(q))
|
||||
|
||||
const packData: EditCardPackDto = {
|
||||
// Only include id for updates, not for new packs
|
||||
...(selectedPack && { id: selectedPack.id }),
|
||||
|
|
@ -187,7 +207,7 @@ export default function PacksPage() {
|
|||
description: formData.description.trim(),
|
||||
category: formData.category.trim(),
|
||||
isPublic: formData.isPublic,
|
||||
questions: formData.questions,
|
||||
questions: questionsForBackend as any, // Backend expects different format
|
||||
}
|
||||
|
||||
if (selectedPack) {
|
||||
|
|
@ -197,6 +217,42 @@ export default function PacksPage() {
|
|||
}
|
||||
}
|
||||
|
||||
// Question editor handlers
|
||||
const handleAddQuestion = () => {
|
||||
setEditingQuestion({ question: null, index: -1 })
|
||||
setIsQuestionEditorOpen(true)
|
||||
}
|
||||
|
||||
const handleEditQuestion = (question: Question, index: number) => {
|
||||
setEditingQuestion({ question, index })
|
||||
setIsQuestionEditorOpen(true)
|
||||
}
|
||||
|
||||
const handleSaveQuestion = (question: Question) => {
|
||||
if (editingQuestion) {
|
||||
const newQuestions = [...formData.questions]
|
||||
if (editingQuestion.index >= 0) {
|
||||
// Update existing question
|
||||
newQuestions[editingQuestion.index] = question
|
||||
} else {
|
||||
// Add new question
|
||||
newQuestions.push(question)
|
||||
}
|
||||
setFormData(prev => ({ ...prev, questions: newQuestions }))
|
||||
}
|
||||
setIsQuestionEditorOpen(false)
|
||||
setEditingQuestion(null)
|
||||
}
|
||||
|
||||
const handleCloseQuestionEditor = () => {
|
||||
setIsQuestionEditorOpen(false)
|
||||
setEditingQuestion(null)
|
||||
}
|
||||
|
||||
const handleQuestionsChange = (questions: Question[]) => {
|
||||
setFormData(prev => ({ ...prev, questions }))
|
||||
}
|
||||
|
||||
const handleDelete = (pack: CardPackPreviewDto) => {
|
||||
setPackToDelete(pack)
|
||||
setIsDeleteDialogOpen(true)
|
||||
|
|
@ -384,7 +440,7 @@ export default function PacksPage() {
|
|||
|
||||
{/* Create/Edit Dialog */}
|
||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||
<DialogContent className="sm:max-w-[600px] max-h-[80vh] overflow-y-auto">
|
||||
<DialogContent className="sm:max-w-[800px] max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{selectedPack ? 'Edit Pack' : 'Create New Pack'}
|
||||
|
|
@ -439,13 +495,23 @@ export default function PacksPage() {
|
|||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Questions</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{formData.questions.length} question(s) in this pack
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Questions can be managed through the question editor
|
||||
</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<Label>Questions</Label>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleAddQuestion}
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Add Question
|
||||
</Button>
|
||||
</div>
|
||||
<TestQuestionsManager
|
||||
questions={formData.questions}
|
||||
onChange={handleQuestionsChange}
|
||||
onEdit={handleEditQuestion}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -461,6 +527,14 @@ export default function PacksPage() {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Question Editor Dialog */}
|
||||
<QuestionEditorDialog
|
||||
open={isQuestionEditorOpen}
|
||||
question={editingQuestion?.question || null}
|
||||
onSave={handleSaveQuestion}
|
||||
onClose={handleCloseQuestionEditor}
|
||||
/>
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<AlertDialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
|
||||
<AlertDialogContent>
|
||||
|
|
|
|||
|
|
@ -251,7 +251,12 @@ const RoomPage = () => {
|
|||
>
|
||||
Показать QR-код
|
||||
</button>
|
||||
{isHost && room.status === 'WAITING' && (
|
||||
{isHost &&
|
||||
(room.status === 'WAITING' ||
|
||||
(room.status === 'PLAYING' &&
|
||||
(!room.questionPack ||
|
||||
room.questionPack.questionCount === 0 ||
|
||||
room.currentQuestionIndex === 0))) && (
|
||||
<button
|
||||
onClick={handleStartGame}
|
||||
className="primary"
|
||||
|
|
|
|||
Loading…
Reference in a new issue