edit entries

This commit is contained in:
2026-01-30 01:43:58 +01:00
parent 530021960e
commit 3e36821e51
6 changed files with 148 additions and 148 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ class EntriesController < ApplicationController
end end
def entry_params def entry_params
params.require(:entry).permit(:category) params.require(:entry).permit(:category, :fi, :en, :sv, :no, :ru, :de, :notes)
end end
def validate_language_code(code) def validate_language_code(code)
+1 -44
View File
@@ -18,50 +18,7 @@
<% end %> <% end %>
<div class="px-6 pt-6 space-y-4"> <div class="px-6 pt-6 space-y-4">
<div> <%= render 'entries/form_fields', f: f %>
<%= f.label :category, "Category", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<%= f.select :category, Entry.categories.keys.map { |cat| [cat.humanize, cat] }, {}, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div class="border-t border-gray-200 pt-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Translations</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<%= f.label :fi, "🇫🇮 Finnish", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :fi, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :en, "🇬🇧 English", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :en, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :sv, "🇸🇪 Swedish", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :sv, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :no, "🇳🇴 Norwegian", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :no, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :ru, "🇷🇺 Russian", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :ru, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :de, "🇩🇪 German", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :de, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
</div>
</div>
<div>
<%= f.label :notes, "Additional Notes", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<%= f.text_area :notes, rows: 4, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition", placeholder: "Any additional context or information about this entry..." %>
</div>
<div class="border-t border-gray-200 pt-6"> <div class="border-t border-gray-200 pt-6">
<h3 class="text-sm font-semibold text-gray-700 mb-2">Requester (Read-only)</h3> <h3 class="text-sm font-semibold text-gray-700 mb-2">Requester (Read-only)</h3>
+57
View File
@@ -0,0 +1,57 @@
<%# Local variables expected:
# - f: form builder object
# - show_category: boolean (default: true) - whether to show category field
# - show_notes: boolean (default: true) - whether to show notes field
%>
<% show_category = local_assigns.fetch(:show_category, true) %>
<% show_notes = local_assigns.fetch(:show_notes, true) %>
<% if show_category %>
<div>
<%= f.label :category, "Category", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<%= f.select :category, Entry.categories.keys.map { |cat| [cat.humanize, cat] }, { prompt: "Select a category" }, { required: true, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" } %>
</div>
<% end %>
<div class="border-t border-gray-200 pt-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Translations (at least one required)</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<%= f.label :fi, "🇫🇮 Finnish", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :fi, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :en, "🇬🇧 English", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :en, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :sv, "🇸🇪 Swedish", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :sv, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :no, "🇳🇴 Norwegian", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :no, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :ru, "🇷🇺 Russian", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :ru, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :de, "🇩🇪 German", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :de, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
</div>
</div>
<% if show_notes %>
<div>
<%= f.label :notes, "Additional Notes (optional)", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<%= f.text_area :notes, rows: 4, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition", placeholder: "Any additional context or information about this entry..." %>
</div>
<% end %>
+76 -47
View File
@@ -1,70 +1,99 @@
<% content_for :title, "Edit Entry" %> <% content_for :title, "Edit Entry" %>
<nav class="sticky top-0 z-50 bg-white border-b border-slate-200"> <div class="min-h-screen flex flex-col">
<div class="max-w-5xl mx-auto px-4 h-16 flex items-center justify-between"> <header class="bg-white border-b border-slate-200">
<div class="flex items-center gap-2"> <div class="max-w-7xl mx-auto px-4">
<div class="h-16 flex items-center justify-between">
<%= link_to root_path, class: "flex items-center gap-2" do %>
<span class="text-xl font-bold tracking-tight text-indigo-600">Sanasto</span> <span class="text-xl font-bold tracking-tight text-indigo-600">Sanasto</span>
<span class="text-xl font-light text-slate-400">Wiki</span> <span class="text-xl font-light text-slate-400">Wiki</span>
</div> <% end %>
<div class="flex items-center gap-4"> <div class="flex items-center gap-3">
<%= link_to "Browse", entries_path, class: "text-sm font-medium text-slate-600 hover:text-indigo-600" %> <%= link_to "Browse", entries_path, class: "text-sm font-medium text-slate-600 hover:text-indigo-600 transition" %>
<%= link_to "Download XLSX", download_entries_path(format: :xlsx), class: "text-sm font-semibold text-indigo-700 px-3 py-2 rounded-lg border border-indigo-200 bg-indigo-50 hover:bg-indigo-100 transition" %> <%= link_to "Download XLSX", download_entries_path(format: :xlsx), class: "text-xs font-bold text-indigo-700 px-3 py-2 rounded-md border border-indigo-200 bg-indigo-50 hover:bg-indigo-100 transition" %>
</div> </div>
</div> </div>
</nav> </div>
</header>
<main class="max-w-5xl mx-auto px-4 py-8 space-y-6"> <div class="flex-1 bg-gradient-to-br from-indigo-50 via-white to-purple-50 flex items-center justify-center px-4 py-12">
<div class="flex items-center justify-between"> <div class="max-w-2xl w-full">
<%= link_to "← Back to entry", entry_path(@entry), class: "text-sm text-slate-500 hover:text-indigo-600" %> <div class="bg-white rounded-2xl shadow-xl p-8">
<%= link_to "Back to search", entries_path, class: "text-sm text-slate-500 hover:text-indigo-600" %> <div class="text-center mb-8">
</div> <div class="flex items-center justify-center gap-3 mb-2">
<h1 class="text-3xl font-bold text-gray-900">Edit Entry</h1>
<div class="bg-white border border-slate-200 rounded-xl shadow-sm overflow-hidden">
<div class="px-6 py-4 border-b border-slate-100 bg-slate-50/50 flex justify-between items-center">
<span class="text-[10px] font-black uppercase tracking-widest text-slate-400">Edit Category</span>
<% if @entry.verified? %> <% if @entry.verified? %>
<div class="flex items-center gap-1.5 text-emerald-600"> <div class="flex items-center gap-1.5 text-emerald-600">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>
<span class="text-xs font-bold">Verified</span> <span class="text-sm font-bold">Verified</span>
</div> </div>
<% else %> <% else %>
<span class="text-xs font-semibold text-amber-600">Unverified</span> <span class="text-sm font-semibold text-amber-600 px-3 py-1 rounded-full bg-amber-50">Unverified</span>
<% end %> <% end %>
</div> </div>
<p class="text-gray-600">Update the translations and details for this entry.</p>
</div>
<div class="p-6 space-y-6"> <%= form_with model: @entry, class: "space-y-6" do |f| %>
<%= form_with model: @entry, class: "space-y-4" do |form| %> <% if @entry.errors.any? %>
<div class="p-4 bg-red-50 border border-red-200 rounded-lg">
<h3 class="font-semibold text-red-800 mb-2">Please fix the following errors:</h3>
<ul class="list-disc list-inside text-red-700 text-sm space-y-1">
<% @entry.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="space-y-4">
<div> <div>
<%= form.label :category, "Category", class: "block text-xs font-bold text-slate-500 uppercase tracking-widest mb-2" %> <%= f.label :category, "Category", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<%= form.select :category, <%= f.select :category,
Entry.categories.keys.map { |key| [key.tr("_", " ").capitalize, key] }, Entry.categories.keys.map { |key| [key.tr("_", " ").capitalize, key] },
{}, {},
class: "block w-full border-slate-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" %> { class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" } %>
</div> </div>
<div class="flex justify-end"> </div>
<%= form.submit "Save Category", class: "bg-indigo-600 text-white px-4 py-2 rounded-lg text-sm font-semibold hover:bg-indigo-700 transition" %>
<div class="border-t border-gray-200 pt-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Translations (at least one required)</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<% %w[fi en sv no ru de].each do |lang_code| %>
<div>
<%= f.label lang_code.to_sym,
case lang_code
when 'fi' then '🇫🇮 Finnish'
when 'en' then '🇬🇧 English'
when 'sv' then '🇸🇪 Swedish'
when 'no' then '🇳🇴 Norwegian'
when 'ru' then '🇷🇺 Russian'
when 'de' then '🇩🇪 German'
end,
class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field lang_code.to_sym,
class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<% end %>
</div>
</div>
<div>
<%= f.label :notes, "Additional Notes (optional)", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<%= f.text_area :notes, rows: 4, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition", placeholder: "Any additional context or information about this entry..." %>
</div>
<div class="flex flex-col sm:flex-row gap-4 pt-4">
<%= f.submit "Save Changes", class: "flex-1 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-3 px-6 rounded-lg transition shadow-md hover:shadow-lg" %>
<%= link_to "Cancel", entry_path(@entry), class: "flex-1 text-center bg-gray-100 hover:bg-gray-200 text-gray-800 font-semibold py-3 px-6 rounded-lg transition" %>
</div> </div>
<% end %> <% end %>
<div class="grid grid-cols-1 md:grid-cols-2 gap-y-6 gap-x-12"> <div class="mt-6 text-center text-sm text-gray-600">
<% supported_languages.each do |language| %> <%= link_to "← Back to entry", entry_path(@entry), class: "text-indigo-600 hover:text-indigo-800 font-semibold" %>
<% translation = entry_translation_for(@entry, language.code) %> <%= link_to "Back to search", entries_path, class: "text-indigo-600 hover:text-indigo-800 font-semibold" %>
<% next if translation.blank? %> </div>
<div class="space-y-2"> </div>
<div class="grid grid-cols-2"> </div>
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-tight"><%= "#{language.name} (#{language.code.upcase})" %></span>
</div>
<p class="text-2xl font-semibold text-slate-800"><%= translation %></p>
</div>
<% end %>
</div>
<% if @entry.notes.present? %>
<div class="mt-6 pt-5 border-t border-slate-100">
<h4 class="text-xs font-bold text-slate-400 uppercase mb-2">Context & Notes</h4>
<p class="text-sm text-slate-600 italic"><%= @entry.notes %></p>
</div>
<% end %>
</div> </div>
</div> </div>
</main>
+1 -44
View File
@@ -65,52 +65,9 @@
<%= f.email_field :email, required: true, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %> <%= f.email_field :email, required: true, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div> </div>
<% end %> <% end %>
<div>
<%= f.label :category, "Category", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<%= f.select :category, Entry.categories.keys.map { |cat| [cat.humanize, cat] }, { prompt: "Select a category" }, { required: true, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" } %>
</div>
</div> </div>
<div class="border-t border-gray-200 pt-6"> <%= render 'entries/form_fields', f: f %>
<h3 class="text-lg font-semibold text-gray-900 mb-4">Translations (at least one required)</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<%= f.label :fi, "🇫🇮 Finnish", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :fi, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :en, "🇬🇧 English", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :en, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :sv, "🇸🇪 Swedish", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :sv, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :no, "🇳🇴 Norwegian", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :no, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :ru, "🇷🇺 Russian", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :ru, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
<div>
<%= f.label :de, "🇩🇪 German", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= f.text_field :de, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition" %>
</div>
</div>
</div>
<div>
<%= f.label :notes, "Additional Notes (optional)", class: "block text-sm font-semibold text-gray-700 mb-2" %>
<%= f.text_area :notes, rows: 4, class: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition", placeholder: "Any additional context or information about this entry..." %>
</div>
<div class="flex flex-col sm:flex-row gap-4 pt-4"> <div class="flex flex-col sm:flex-row gap-4 pt-4">
<%= f.submit "Submit Request", class: "flex-1 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-3 px-6 rounded-lg transition shadow-md hover:shadow-lg" %> <%= f.submit "Submit Request", class: "flex-1 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-3 px-6 rounded-lg transition shadow-md hover:shadow-lg" %>
+3 -3
View File
@@ -49,11 +49,11 @@
- [x] **Live search** (AJAX updates without page reload) - [x] **Live search** (AJAX updates without page reload)
### Entry Management ### Entry Management
- [ ] **Create entry form** with all language fields - [x] **Create entry form** with all language fields
- [ ] **Edit entry form** (contributors only) - [x] **Edit entry form** (contributors only)
- [ ] **Delete entries** (admin only, with confirmation) - [ ] **Delete entries** (admin only, with confirmation)
- [ ] **Bulk actions** (select multiple, bulk edit/delete) - [ ] **Bulk actions** (select multiple, bulk edit/delete)
- [ ] **Entry validation** (at least one language required, category required) - [x] **Entry validation** (at least one language required, category required)
### Suggested Meanings ### Suggested Meanings
- [ ] **Submit alternative translation** form - [ ] **Submit alternative translation** form