177b4e8fd8
- Full library home page: question list with per-user mastery stats,
search/source filter, sort (A-Z, weakest first, most-seen), source
breakdown in header, Take a test CTA.
- GET/POST /questions/{id}: view and edit question text, source, answers;
radio-select correct answer; shows seen×/correct% stat.
- POST /questions/{id}/delete: hard delete (cascades to answers via FK).
- repo: ListQuestions supports SortWeakest/SortMostSeen via LEFT JOIN;
added CountBySource, UpdateQuestion, UpdateAnswers, DeleteQuestion.
- render: added pct template func (correct*100/seen).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
72 lines
2.8 KiB
HTML
72 lines
2.8 KiB
HTML
{{define "content"}}
|
||
<div class="mb-5 flex items-center justify-between">
|
||
<a href="/" class="text-sm text-gray-500 hover:underline">← Library</a>
|
||
{{if .Stat}}
|
||
<p class="text-xs text-gray-500">
|
||
Seen {{.Stat.TimesSeen}}× · {{.Stat.TimesCorrect}} correct ({{pct .Stat.TimesCorrect .Stat.TimesSeen}}%)
|
||
</p>
|
||
{{else}}
|
||
<p class="text-xs text-gray-400">Not yet seen in a test</p>
|
||
{{end}}
|
||
</div>
|
||
|
||
<form method="post" action="/questions/{{.Question.ID}}" class="space-y-4">
|
||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
||
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
|
||
<textarea name="q_text" rows="4" required
|
||
class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm shadow-sm
|
||
focus:outline-none focus:ring-2 focus:ring-blue-500 resize-y">{{.Question.Text}}</textarea>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Source</label>
|
||
<input type="text" name="source" value="{{.Question.Source}}"
|
||
class="w-full border border-gray-300 rounded-md px-3 py-1.5 text-sm shadow-sm
|
||
focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">Answers</label>
|
||
<div class="space-y-2">
|
||
{{range $i, $a := .Answers}}
|
||
<div class="flex items-center gap-3">
|
||
<input type="radio" name="correct" value="{{$i}}"
|
||
{{if $a.IsCorrect}}checked{{end}}
|
||
class="text-blue-600 focus:ring-blue-500 flex-shrink-0">
|
||
<input type="text" name="a_text_{{$i}}" value="{{$a.Text}}" required
|
||
class="flex-1 border border-gray-300 rounded-md px-3 py-1.5 text-sm shadow-sm
|
||
focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
<p class="text-xs text-gray-400 mt-1">Select the radio button next to the correct answer.</p>
|
||
</div>
|
||
|
||
<div class="flex gap-3 pt-2">
|
||
<button type="submit"
|
||
class="flex-1 bg-blue-600 hover:bg-blue-700 text-white py-2.5 px-4 rounded-md
|
||
text-sm font-semibold shadow-sm">
|
||
Save changes
|
||
</button>
|
||
<a href="/"
|
||
class="px-4 py-2.5 border border-gray-300 rounded-md text-sm font-medium text-gray-700
|
||
hover:bg-gray-50 text-center">
|
||
Cancel
|
||
</a>
|
||
</div>
|
||
</form>
|
||
|
||
<div class="mt-6 pt-6 border-t border-gray-200">
|
||
<form method="post" action="/questions/{{.Question.ID}}/delete">
|
||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
||
<button type="submit"
|
||
onclick="return confirm('Delete this question? This cannot be undone.')"
|
||
class="text-sm text-red-600 hover:text-red-800 hover:underline">
|
||
Delete this question
|
||
</button>
|
||
</form>
|
||
</div>
|
||
{{end}}
|