| 1 | {{define "wine_form.html"}}{{template "base.html" .}}{{end}} |
| 2 | {{define "title"}}{{if .Wine}}Edit{{else}}New{{end}} Wine - Cellar{{end}} |
| 3 | {{define "content"}} |
| 4 | {{if .Error}}<p class="error">{{.Error}}</p>{{end}} |
| 5 | {{$w := .Wine}} |
| 6 | <form method="POST" enctype="multipart/form-data" |
| 7 | action="{{if $w}}/admin/edit/{{$w.ShortID}}{{else}}/admin/new{{end}}" |
| 8 | class="form"> |
| 9 | |
| 10 | <label for="image">image</label> |
| 11 | <div class="image-upload-row"> |
| 12 | <input type="file" id="image" name="image" accept="image/*"> |
| 13 | {{if .HasAnthropicKey}}<button type="button" id="analyze-btn" onclick="analyzeImage()">analyze</button>{{end}} |
| 14 | </div> |
| 15 | |
| 16 | <label for="name">name</label> |
| 17 | <input type="text" id="name" name="name" required value="{{if $w}}{{$w.Name}}{{end}}"> |
| 18 | |
| 19 | <label for="origin">origin</label> |
| 20 | <input type="text" id="origin" name="origin" value="{{if $w}}{{$w.Origin}}{{end}}"> |
| 21 | |
| 22 | <label for="grape">grape</label> |
| 23 | <input type="text" id="grape" name="grape" value="{{if $w}}{{$w.Grape}}{{end}}"> |
| 24 | |
| 25 | <label for="notes">notes</label> |
| 26 | <textarea id="notes" name="notes" rows="5">{{if $w}}{{$w.Notes}}{{end}}</textarea> |
| 27 | |
| 28 | <label for="background">background</label> |
| 29 | <textarea id="background" name="background" rows="5">{{if $w}}{{$w.Background}}{{end}}</textarea> |
| 30 | |
| 31 | <div class="score-group"> |
| 32 | <div class="score-section-label">appearance</div> |
| 33 | <div class="score-row"> |
| 34 | <label for="clarity">clarity</label> |
| 35 | <input type="range" id="clarity" name="clarity" min="1" max="5" value="{{if $w}}{{$w.Clarity}}{{else}}3{{end}}"> |
| 36 | <span class="score-value" data-for="clarity">{{if $w}}{{$w.Clarity}}{{else}}3{{end}}</span> |
| 37 | </div> |
| 38 | <div class="score-row"> |
| 39 | <label for="color_intensity">intensity</label> |
| 40 | <input type="range" id="color_intensity" name="color_intensity" min="1" max="5" value="{{if $w}}{{$w.ColorIntensity}}{{else}}3{{end}}"> |
| 41 | <span class="score-value" data-for="color_intensity">{{if $w}}{{$w.ColorIntensity}}{{else}}3{{end}}</span> |
| 42 | </div> |
| 43 | |
| 44 | <div class="score-section-label">nose</div> |
| 45 | <div class="score-row"> |
| 46 | <label for="aroma_intensity">aroma</label> |
| 47 | <input type="range" id="aroma_intensity" name="aroma_intensity" min="1" max="5" value="{{if $w}}{{$w.AromaIntensity}}{{else}}3{{end}}"> |
| 48 | <span class="score-value" data-for="aroma_intensity">{{if $w}}{{$w.AromaIntensity}}{{else}}3{{end}}</span> |
| 49 | </div> |
| 50 | <div class="score-row"> |
| 51 | <label for="nose_complexity">complexity</label> |
| 52 | <input type="range" id="nose_complexity" name="nose_complexity" min="1" max="5" value="{{if $w}}{{$w.NoseComplexity}}{{else}}3{{end}}"> |
| 53 | <span class="score-value" data-for="nose_complexity">{{if $w}}{{$w.NoseComplexity}}{{else}}3{{end}}</span> |
| 54 | </div> |
| 55 | |
| 56 | <div class="score-section-label">palate</div> |
| 57 | <div class="score-row"> |
| 58 | <label for="sweetness">sweetness</label> |
| 59 | <input type="range" id="sweetness" name="sweetness" min="1" max="5" value="{{if $w}}{{$w.Sweetness}}{{else}}3{{end}}"> |
| 60 | <span class="score-value" data-for="sweetness">{{if $w}}{{$w.Sweetness}}{{else}}3{{end}}</span> |
| 61 | </div> |
| 62 | <div class="score-row"> |
| 63 | <label for="acidity">acidity</label> |
| 64 | <input type="range" id="acidity" name="acidity" min="1" max="5" value="{{if $w}}{{$w.Acidity}}{{else}}3{{end}}"> |
| 65 | <span class="score-value" data-for="acidity">{{if $w}}{{$w.Acidity}}{{else}}3{{end}}</span> |
| 66 | </div> |
| 67 | <div class="score-row"> |
| 68 | <label for="tannin">tannin</label> |
| 69 | <input type="range" id="tannin" name="tannin" min="1" max="5" value="{{if $w}}{{$w.Tannin}}{{else}}3{{end}}"> |
| 70 | <span class="score-value" data-for="tannin">{{if $w}}{{$w.Tannin}}{{else}}3{{end}}</span> |
| 71 | </div> |
| 72 | <div class="score-row"> |
| 73 | <label for="alcohol">alcohol</label> |
| 74 | <input type="range" id="alcohol" name="alcohol" min="1" max="5" value="{{if $w}}{{$w.Alcohol}}{{else}}3{{end}}"> |
| 75 | <span class="score-value" data-for="alcohol">{{if $w}}{{$w.Alcohol}}{{else}}3{{end}}</span> |
| 76 | </div> |
| 77 | <div class="score-row"> |
| 78 | <label for="body">body</label> |
| 79 | <input type="range" id="body" name="body" min="1" max="5" value="{{if $w}}{{$w.Body}}{{else}}3{{end}}"> |
| 80 | <span class="score-value" data-for="body">{{if $w}}{{$w.Body}}{{else}}3{{end}}</span> |
| 81 | </div> |
| 82 | </div> |
| 83 | |
| 84 | <button type="submit">{{if $w}}update{{else}}create{{end}}</button> |
| 85 | </form> |
| 86 | |
| 87 | <script> |
| 88 | document.querySelectorAll('input[type="range"]').forEach(function(input) { |
| 89 | input.addEventListener('input', function() { |
| 90 | var span = document.querySelector('.score-value[data-for="' + this.id + '"]'); |
| 91 | if (span) span.textContent = this.value; |
| 92 | }); |
| 93 | }); |
| 94 | |
| 95 | {{if .HasAnthropicKey}} |
| 96 | async function analyzeImage() { |
| 97 | var fileInput = document.getElementById('image'); |
| 98 | if (!fileInput.files.length) return; |
| 99 | var formData = new FormData(); |
| 100 | formData.append('image', fileInput.files[0]); |
| 101 | var btn = document.getElementById('analyze-btn'); |
| 102 | btn.textContent = 'analyzing...'; |
| 103 | btn.disabled = true; |
| 104 | try { |
| 105 | var res = await fetch('/admin/analyze-image', { method: 'POST', body: formData }); |
| 106 | if (res.ok) { |
| 107 | var data = await res.json(); |
| 108 | if (data.name) document.getElementById('name').value = data.name; |
| 109 | if (data.origin) document.getElementById('origin').value = data.origin; |
| 110 | if (data.grape) document.getElementById('grape').value = data.grape; |
| 111 | if (data.background) document.getElementById('background').value = data.background; |
| 112 | } |
| 113 | } catch (e) { console.error('Analysis failed:', e); } |
| 114 | finally { btn.textContent = 'analyze'; btn.disabled = false; } |
| 115 | } |
| 116 | {{end}} |
| 117 | </script> |
| 118 | {{end}} |