diff --git a/app/controllers/words_controller.rb b/app/controllers/words_controller.rb index 3d83a42..adec161 100644 --- a/app/controllers/words_controller.rb +++ b/app/controllers/words_controller.rb @@ -89,6 +89,32 @@ def destroy end end + # POST /words/ai_generate + def ai_generate + title = params[:title] + return render json: { success: false, error: "Title is required" }, status: :bad_request if title.blank? + + result = AiContentGenerator.new(title).call + + respond_to do |format| + if result.success? + format.json { + render json: { + success: true, + content: result.content + } + } + else + format.json { + render json: { + success: false, + error: result.error + }, status: :unprocessable_entity + } + end + end + end + # POST /words/1/ai_edit def ai_edit result = AiContentGenerator.new(@word.title).call diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index 63a4008..95770db 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -224,6 +224,55 @@ html lang="en" | }); | } | + | function handleAiEditNew() { + | const btn = document.getElementById('ai-edit-btn'); + | const trixEditor = document.querySelector('trix-editor'); + | const titleInput = document.querySelector('input[name="word[title]"]'); + | + | if (!trixEditor) { + | alert('エディターが見つかりません'); + | return; + | } + | + | if (!titleInput || !titleInput.value.trim()) { + | alert('まずタイトルを入力してください'); + | if (titleInput) titleInput.focus(); + | return; + | } + | + | const originalText = btn.innerHTML; + | btn.disabled = true; + | btn.innerHTML = '🔄 生成中...'; + | + | fetch('/ai_generate', { + | method: 'POST', + | headers: { + | 'Content-Type': 'application/json', + | 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content') + | }, + | body: JSON.stringify({ + | title: titleInput.value.trim() + | }) + | }) + | .then(response => response.json()) + | .then(data => { + | if (data.success) { + | trixEditor.value = data.content; + | showMessage('AIによるコンテンツ生成が完了しました', 'success'); + | } else { + | showMessage('エラー: ' + data.error, 'error'); + | } + | }) + | .catch(error => { + | console.error('Error:', error); + | showMessage('ネットワークエラーが発生しました', 'error'); + | }) + | .finally(() => { + | btn.disabled = false; + | btn.innerHTML = originalText; + | }); + | } + | | function showMessage(message, type) { | const existingMessage = document.querySelector('.ai-edit-message'); | if (existingMessage) { diff --git a/app/views/words/_form.html.slim b/app/views/words/_form.html.slim index 9f46b0c..4febacc 100644 --- a/app/views/words/_form.html.slim +++ b/app/views/words/_form.html.slim @@ -33,11 +33,10 @@ .mb-4 .d-flex.justify-content-between.align-items-center.mb-2 = f.label :body, "Content", class: 'form-label mb-0' - - if word.persisted? - = button_tag type: 'button', class: 'btn btn-outline-primary btn-sm ms-3', id: 'ai-edit-btn', - data: { word_id: word.id }, - onclick: "handleAiEdit(#{word.id})" do - | 🤖 AIで本文生成 + = button_tag type: 'button', class: 'btn btn-outline-primary btn-sm ms-3', id: 'ai-edit-btn', + data: { word_id: word.persisted? ? word.id : nil }, + onclick: word.persisted? ? "handleAiEdit(#{word.id})" : "handleAiEditNew()" do + | 🤖 AIで本文生成 = f.rich_text_area :body, class: 'form-control rich-text-editor' .form-text Use the toolbar above to format your content with rich text. diff --git a/config/routes.rb b/config/routes.rb index 8cdefbd..cfa3dd9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,6 +56,9 @@ get '/tag::tag_list', to: 'words#tag', as: 'word_tag' resources :words, path: '/' do + collection do + post :ai_generate + end member do post :ai_edit end