sun-sword 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -2
  3. data/Gemfile +20 -0
  4. data/Gemfile.lock +252 -0
  5. data/README.md +74 -15
  6. data/Rakefile +6 -10
  7. data/lib/generators/sun_sword/USAGE +22 -3
  8. data/lib/generators/sun_sword/frontend_generator.rb +77 -39
  9. data/lib/generators/sun_sword/frontend_generator_spec.rb +539 -0
  10. data/lib/generators/sun_sword/init_generator.rb +2 -0
  11. data/lib/generators/sun_sword/init_generator_spec.rb +82 -0
  12. data/lib/generators/sun_sword/scaffold_generator.rb +212 -29
  13. data/lib/generators/sun_sword/scaffold_generator_spec.rb +1414 -0
  14. data/lib/generators/sun_sword/templates_frontend/{Procfile.dev → Procfile.dev.tt} +1 -0
  15. data/lib/generators/sun_sword/templates_frontend/bin/{watch → watch.tt} +2 -1
  16. data/lib/generators/sun_sword/templates_frontend/config/{vite.json → vite.json.tt} +2 -1
  17. data/lib/generators/sun_sword/templates_frontend/controllers/application_controller.rb.tt +2 -2
  18. data/lib/generators/sun_sword/templates_frontend/controllers/tests_controller.rb +36 -0
  19. data/lib/generators/sun_sword/templates_frontend/controllers/tests_controller_spec.rb +62 -0
  20. data/lib/generators/sun_sword/templates_frontend/env.development +1 -1
  21. data/lib/generators/sun_sword/templates_frontend/frontend/entrypoints/application.js +2 -2
  22. data/lib/generators/sun_sword/templates_frontend/frontend/pages/tests-stimulus.js +31 -0
  23. data/lib/generators/sun_sword/templates_frontend/frontend/pages/web.js +12 -0
  24. data/lib/generators/sun_sword/templates_frontend/frontend/stylesheets/application.css +1 -3
  25. data/lib/generators/sun_sword/templates_frontend/helpers/application_helper.rb +17 -0
  26. data/lib/generators/sun_sword/templates_frontend/helpers/application_helper_spec.rb +30 -0
  27. data/lib/generators/sun_sword/templates_frontend/package.json.tt +14 -0
  28. data/lib/generators/sun_sword/templates_frontend/views/components/_action_destroy.html.erb.tt +1 -1
  29. data/lib/generators/sun_sword/templates_frontend/views/components/_alert.html.erb.tt +1 -1
  30. data/lib/generators/sun_sword/templates_frontend/views/layouts/application.html.erb.tt +3 -3
  31. data/lib/generators/sun_sword/templates_frontend/views/tests/_frame_content.html.erb +9 -0
  32. data/lib/generators/sun_sword/templates_frontend/views/tests/_log_entry.html.erb +4 -0
  33. data/lib/generators/sun_sword/templates_frontend/views/tests/_updated_content.html.erb +6 -0
  34. data/lib/generators/sun_sword/templates_frontend/views/tests/stimulus.html.erb +45 -0
  35. data/lib/generators/sun_sword/templates_frontend/views/tests/turbo_drive.html.erb +55 -0
  36. data/lib/generators/sun_sword/templates_frontend/views/tests/turbo_frame.html.erb +87 -0
  37. data/lib/generators/sun_sword/templates_frontend/vite.config.ts.tt +1 -1
  38. data/lib/generators/sun_sword/templates_init/config/initializers/sun_sword.rb +1 -0
  39. data/lib/generators/sun_sword/templates_scaffold/controllers/controller.rb.tt +24 -24
  40. data/lib/generators/sun_sword/templates_scaffold/controllers/controller_spec.rb.tt +398 -0
  41. data/lib/generators/sun_sword/templates_scaffold/views/index.html.erb.tt +5 -5
  42. data/lib/generators/sun_sword/templates_scaffold/views/show.html.erb.tt +3 -0
  43. data/lib/generators/tmp/db/structures/test_structure.yaml +42 -0
  44. data/lib/sun-sword.rb +1 -0
  45. data/lib/sun_sword/configuration_spec.rb +77 -0
  46. data/lib/sun_sword/version.rb +1 -1
  47. metadata +177 -29
  48. data/.rspec +0 -3
  49. data/.rubocop.yml +0 -1146
  50. data/lib/generators/sun_sword/templates_frontend/controllers/site_controller.rb +0 -16
  51. data/lib/generators/sun_sword/templates_frontend/db/seeds.rb +0 -3
  52. data/lib/generators/sun_sword/templates_frontend/db/structures/example.yaml.tt +0 -106
  53. data/lib/generators/sun_sword/templates_frontend/frontend/pages/stimulus.js +0 -10
  54. data/lib/generators/sun_sword/templates_frontend/package.json +0 -7
  55. data/lib/generators/sun_sword/templates_frontend/views/site/_comment.html.erb.tt +0 -3
  56. data/lib/generators/sun_sword/templates_frontend/views/site/stimulus.html.erb.tt +0 -26
@@ -1 +1,2 @@
1
1
  vite: bin/vite dev --clobber
2
+
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env sh
2
2
 
3
- vite dev --clobber
3
+ bun run dev
4
+
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "all": {
3
- "sourceCodeDir": "app/frontend",
3
+ "sourceCodeDir": "<%= source_code_dir %>",
4
4
  "watchAdditionalPaths": []
5
5
  },
6
6
  "development": {
@@ -15,3 +15,4 @@
15
15
  "port": 3037
16
16
  }
17
17
  }
18
+
@@ -8,8 +8,8 @@ class ApplicationController < ActionController::Base
8
8
  # Adjust the value of <resource_owner_id> in db/structures/<resource>_structure.yaml
9
9
  # Ignore this comment if you haven't generated the structure yet
10
10
 
11
- #def <resource_owner_id>
12
- # current_auth.<resource_owner_id>
11
+ #def <%=@resource_owner_id %>
12
+ # current_auth.<%=@resource_owner_id %>
13
13
  #end
14
14
 
15
15
  def build_form_errors(params, model, errors)
@@ -0,0 +1,36 @@
1
+ class TestsController < ApplicationController
2
+ layout :set_layouts
3
+
4
+ def stimulus
5
+ # Test halaman untuk Stimulus
6
+ end
7
+
8
+ def turbo_drive
9
+ @timestamp = Time.current
10
+ end
11
+
12
+ def turbo_frame
13
+ # Test halaman untuk Turbo Frames
14
+ end
15
+
16
+ def frame_content
17
+ # Render turbo frame content
18
+ render turbo_stream: turbo_stream.replace(
19
+ request.headers['Turbo-Frame'] || 'lazy-content',
20
+ partial: 'tests/frame_content'
21
+ )
22
+ end
23
+
24
+ def update_content
25
+ @timestamp = Time.current
26
+
27
+ respond_to do |format|
28
+ format.turbo_stream do
29
+ render turbo_stream: [
30
+ turbo_stream.update('dynamic-content', partial: 'tests/updated_content'),
31
+ turbo_stream.append('log', partial: 'tests/log_entry', locals: { timestamp: @timestamp })
32
+ ]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe TestsController, type: :controller do
4
+ routes { Web::Engine.routes }
5
+ describe '#stimulus' do
6
+ it 'renders the stimulus template' do
7
+ get :stimulus
8
+
9
+ expect(response).to render_template(:stimulus)
10
+ end
11
+ end
12
+
13
+ describe '#turbo_drive' do
14
+ it 'sets the timestamp' do
15
+ get :turbo_drive
16
+
17
+ expect(assigns(:timestamp)).to be_a(Time)
18
+ end
19
+
20
+ it 'renders the turbo_drive template' do
21
+ get :turbo_drive
22
+
23
+ expect(response).to render_template(:turbo_drive)
24
+ end
25
+ end
26
+
27
+ describe '#turbo_frame' do
28
+ it 'renders the turbo_frame template' do
29
+ get :turbo_frame
30
+
31
+ expect(response).to render_template(:turbo_frame)
32
+ end
33
+ end
34
+
35
+ describe '#frame_content' do
36
+ it 'renders turbo_stream response' do
37
+ get :frame_content
38
+
39
+ expect(response).to have_http_status(:success)
40
+ expect(response.content_type).to include('text/vnd.turbo-stream.html')
41
+ end
42
+ end
43
+
44
+ describe '#update_content' do
45
+ it 'sets the timestamp' do
46
+ post :update_content, format: :turbo_stream
47
+ expect(assigns(:timestamp)).to be_a(Time)
48
+ end
49
+
50
+ it 'responds with turbo_stream format' do
51
+ post :update_content, format: :turbo_stream
52
+ expect(response).to have_http_status(:success)
53
+ expect(response.content_type).to include('text/vnd.turbo-stream.html')
54
+ end
55
+ end
56
+
57
+ describe '#set_layouts' do
58
+ it 'returns application layout' do
59
+ expect(controller.send(:set_layouts)).to eq('application')
60
+ end
61
+ end
62
+ end
@@ -1,7 +1,7 @@
1
1
  BASE_URL="http://localhost:3000"
2
2
 
3
3
  # database primary
4
- DATABASE_NAME=<%= Rails.application.class.module_parent_name.underscore %>_development
4
+ DATABASE_NAME=<%%= Rails.application.class.module_parent_name.underscore %>_development
5
5
  DATABASE_USERNAME=postgres
6
6
  DATABASE_PASSWORD=bayangan
7
7
  DATABASE_HOSTNAME=localhost
@@ -3,7 +3,7 @@ import "@hotwired/turbo-rails"
3
3
  import {stimulus} from "./stimulus"
4
4
 
5
5
  import WebController from "../pages/web"
6
- import SiteStimulusController from "../pages/stimulus.js"
6
+ import TestsStimulusController from "../pages/tests-stimulus.js"
7
7
 
8
8
  import "../stylesheets/application.css"
9
9
  console.log("Kotaro is here")
@@ -14,4 +14,4 @@ if(textElement !== null){
14
14
  textElement.scrollIntoView({behavior: "smooth"})
15
15
  }
16
16
  stimulus.register("web", WebController)
17
- stimulus.register("site-stimulus", SiteStimulusController)
17
+ stimulus.register("counter", TestsStimulusController)
@@ -0,0 +1,31 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['count', 'display']
5
+ static values = {
6
+ count: { type: Number, default: 0 }
7
+ }
8
+
9
+ connect() {
10
+ console.log('Counter controller connected')
11
+ this.updateDisplay()
12
+ }
13
+
14
+ increment() {
15
+ this.countValue++
16
+ this.updateDisplay()
17
+ }
18
+
19
+ decrement() {
20
+ this.countValue--
21
+ this.updateDisplay()
22
+ }
23
+
24
+ updateDisplay() {
25
+ this.displayTarget.textContent = this.countValue
26
+ }
27
+
28
+ countValueChanged() {
29
+ console.log('Count changed to:', this.countValue)
30
+ }
31
+ }
@@ -99,7 +99,19 @@ export default class extends Controller {
99
99
  }
100
100
  })
101
101
  }
102
+ confirmationDestroy(event) {
103
+ const chooseTypes = document.querySelectorAll(".confirmation-destroy-" + event.params.id);
104
+ chooseTypes.forEach((element) => {
105
+ element.classList.remove('hidden');
106
+ })
107
+ }
102
108
 
109
+ confirmationDestroyCancel(event) {
110
+ const chooseTypes = document.querySelectorAll(".confirmation-destroy-" + event.params.id);
111
+ chooseTypes.forEach((element) => {
112
+ element.classList.add('hidden');
113
+ })
114
+ }
103
115
  // Opsional: submenu & profil tetap bisa dipakai dari versi sebelumnya
104
116
  profileSetting(event) {
105
117
  const container = event.currentTarget.closest("li")
@@ -4,9 +4,7 @@
4
4
  @plugin "@tailwindcss/typography";
5
5
  @plugin "@tailwindcss/aspect-ratio";
6
6
 
7
- @source "../../../app/views/**/*.html.erb";
8
- @source "../../../app/views/**/*.rb";
9
- @source "../../../app/helpers/**/*.rb";
7
+ @source "../../../**/app/views/**/*.html.erb";
10
8
  @source "./app/frontend/**/*.{js,ts,jsx,tsx,css,scss,ejs}";
11
9
 
12
10
 
@@ -1,5 +1,22 @@
1
1
  module ApplicationHelper
2
+ def post_to(path, options = {}, &block)
3
+ link_to path, { data: { turbo_method: :post } }.deep_merge(options), &block
4
+ end
5
+
6
+ def delete_to(path, options = {}, &block)
7
+ link_to path, { data: { turbo_method: :delete } }.deep_merge(options), &block
8
+ end
9
+
10
+ def patch_to(path, options = {}, &block)
11
+ link_to path, { data: { turbo_method: :patch } }.deep_merge(options), &block
12
+ end
13
+
2
14
  def flash_type(flash)
3
15
  flash.map { |type, msg| type }[0]
4
16
  end
17
+
18
+ def truncate_html(html, opts = {})
19
+ html_string = TruncateHtml::HtmlString.new(html)
20
+ TruncateHtml::HtmlTruncator.new(html_string, opts).truncate
21
+ end
5
22
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe ApplicationHelper, type: :helper do
4
+ describe '#flash_type' do
5
+ it 'returns the first flash type from a flash hash' do
6
+ flash = { 'notice' => 'Welcome!', 'error' => 'Something went wrong' }
7
+
8
+ expect(helper.flash_type(flash)).to eq('notice')
9
+ end
10
+
11
+ it 'returns nil for empty flash' do
12
+ flash = {}
13
+
14
+ expect(helper.flash_type(flash)).to be_nil
15
+ end
16
+
17
+ it 'returns the only flash type' do
18
+ flash = { 'success' => 'Operation successful' }
19
+
20
+ expect(helper.flash_type(flash)).to eq('success')
21
+ end
22
+
23
+ it 'returns the first type when multiple flashes exist' do
24
+ flash = { 'alert' => 'Warning', 'notice' => 'Info', 'error' => 'Error' }
25
+
26
+ # Hash order is preserved in Ruby 1.9+, so first key should be 'alert'
27
+ expect(helper.flash_type(flash)).to eq('alert')
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "<%= app_name %>",
3
+ "private": "true",
4
+ "type": "module",
5
+ "author": "kotarominami <kotaroisme@gmail.com>",
6
+ "license": "MIT",
7
+ "packageManager": "bun@1.0.0",
8
+ "scripts": {
9
+ "dev": "bun run vite dev",
10
+ "build": "bun run vite build",
11
+ "preview": "bun run vite preview"
12
+ }
13
+ }
14
+
@@ -18,7 +18,7 @@
18
18
  </button>
19
19
  </div>
20
20
  <div class="flex flex-row">
21
- <div class="basis-1/4">
21
+ <div>
22
22
  <div class="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
23
23
  <svg class="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
24
24
  <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"/>
@@ -1,5 +1,5 @@
1
1
  <%% if flash.present? %>
2
- <%% case flash_type flash %>
2
+ <%% case flash.map { |type, msg| type }[0] %>
3
3
  <%% when "success" %>
4
4
  <div class="rounded-md bg-green-50 p-4 mb-3">
5
5
  <div class="flex">
@@ -29,12 +29,12 @@
29
29
  </div>
30
30
  </div>
31
31
  </main>
32
- <footer class="mt-auto">
33
- <div class="mx-auto max-w-7xl overflow-hidden px-6 py-20 sm:py-24 lg:px-8">
32
+ <footer class="mt-auto mb-10">
33
+ <div class="mx-auto max-w-7xl overflow-hidden px-6 py-5 lg:px-8">
34
34
  <p class="mt-10 text-center text-xs leading-5 text-gray-500">&copy; <%%= Date.today.year %> Kotaroisme. All rights
35
35
  reserved.</p>
36
36
  </div>
37
- <p class="mt-3 text-center text-xs leading-5 text-gray-500">version <%%= <%= Rails.application.class.module_parent_name %>::VERSION %></p>
37
+ <p class="text-center text-xs leading-5 text-gray-500">version <%%= <%= Rails.application.class.module_parent_name %>::VERSION %></p>
38
38
  </footer>
39
39
  </div>
40
40
  </body>
@@ -0,0 +1,9 @@
1
+ <div class="rounded-lg border-2 border-green-600 bg-green-100 p-6">
2
+ <p class="mb-2 font-semibold text-green-700">✅ Frame Loaded Successfully!</p>
3
+ <p class="mb-2 text-green-700">
4
+ Server time: <%= Time.current.strftime('%H:%M:%S.%L') %>
5
+ </p>
6
+ <p class="text-sm text-gray-600">
7
+ This content was loaded via Turbo Frame without reloading the page.
8
+ </p>
9
+ </div>
@@ -0,0 +1,4 @@
1
+ <div class="flex items-center gap-2 border-b border-gray-300 px-4 py-3">
2
+ <span class="text-xl text-green-600">●</span>
3
+ <span class="text-sm text-gray-700">Stream update at <%= timestamp.strftime('%H:%M:%S.%L') %></span>
4
+ </div>
@@ -0,0 +1,6 @@
1
+ <div class="rounded-lg border-2 border-green-600 bg-green-100 p-6">
2
+ <p class="mb-2 font-semibold text-green-700">✅ Content Updated via Turbo Stream!</p>
3
+ <p class="text-green-700">
4
+ Server time: <%= Time.current.strftime('%H:%M:%S.%L') %>
5
+ </p>
6
+ </div>
@@ -0,0 +1,45 @@
1
+ <div class="mx-auto max-w-3xl px-6 py-10">
2
+ <h1 class="text-3xl font-semibold text-gray-900">Stimulus + Turbo + Vite Test</h1>
3
+
4
+ <div data-controller="counter" class="mt-8 rounded-xl bg-white p-6 shadow-sm ring-1 ring-gray-200">
5
+ <h2 class="text-xl font-semibold text-gray-900">Counter Test</h2>
6
+
7
+ <div class="mt-4 text-center text-6xl font-semibold text-gray-900">
8
+ <span data-counter-target="display">0</span>
9
+ </div>
10
+
11
+ <div class="mt-6 flex justify-center gap-3">
12
+ <button
13
+ data-action="counter#decrement"
14
+ class="rounded-md bg-red-600 px-5 py-2 text-2xl font-semibold text-white shadow-sm transition hover:bg-red-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600">
15
+
16
+ </button>
17
+
18
+ <button
19
+ data-action="counter#increment"
20
+ class="rounded-md bg-green-600 px-5 py-2 text-2xl font-semibold text-white shadow-sm transition hover:bg-green-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600">
21
+ +
22
+ </button>
23
+ </div>
24
+ </div>
25
+
26
+ <div class="mt-8 rounded-xl bg-gray-50 p-6 ring-1 ring-gray-200">
27
+ <h3 class="text-base font-semibold text-gray-900">✅ Success Indicators:</h3>
28
+ <ol class="mt-4 list-decimal space-y-2 pl-6 text-sm leading-7 text-gray-700">
29
+ <li>Console shows "=== Stimulus and Turbo initialized ==="</li>
30
+ <li>Console shows "✅ Counter controller connected"</li>
31
+ <li>Clicking + button increases number</li>
32
+ <li>Clicking − button decreases number</li>
33
+ <li>Console shows "Count changed to: X" on each click</li>
34
+ </ol>
35
+
36
+ <p class="mt-4 rounded-md border-l-4 border-cyan-600 bg-white p-4 text-sm text-gray-700">
37
+ <strong>How to check:</strong> Open DevTools (F12) → Console tab
38
+ </p>
39
+ </div>
40
+
41
+ <div class="mt-8">
42
+ <%= link_to "Test Turbo Drive →", tests_turbo_drive_path,
43
+ class: "inline-flex items-center rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-blue-700" %>
44
+ </div>
45
+ </div>
@@ -0,0 +1,55 @@
1
+ <div class="mx-auto max-w-3xl px-6 py-10">
2
+ <h1 class="text-3xl font-semibold text-gray-900">Turbo Drive Test</h1>
3
+
4
+ <section class="mt-8 rounded-xl bg-white p-6 shadow-sm ring-1 ring-gray-200">
5
+ <h2 class="text-xl font-semibold text-gray-900">Current Server Timestamp</h2>
6
+ <p class="mt-4 text-4xl font-bold text-green-600">
7
+ <%= @timestamp.strftime('%H:%M:%S.%L') %>
8
+ </p>
9
+ <p class="mt-4 text-sm text-gray-600">
10
+ Page loaded at: <span id="client-load-time" class="font-semibold text-gray-900"></span>
11
+ </p>
12
+ </section>
13
+
14
+ <div class="mt-6 flex flex-col gap-3 sm:flex-row">
15
+ <%= link_to "↻ Reload (Turbo Drive)",
16
+ tests_turbo_drive_path,
17
+ class: "flex-1 rounded-md bg-green-600 px-4 py-2 text-center text-sm font-semibold text-white transition hover:bg-green-700" %>
18
+
19
+ <a href="<%= tests_turbo_drive_path %>"
20
+ data-turbo="false"
21
+ class="flex-1 rounded-md bg-red-600 px-4 py-2 text-center text-sm font-semibold text-white transition hover:bg-red-700">
22
+ ⟳ Full Page Reload
23
+ </a>
24
+ </div>
25
+
26
+ <section class="mt-8 rounded-xl bg-gray-50 p-6 ring-1 ring-gray-200">
27
+ <h3 class="text-base font-semibold text-gray-900">✅ Turbo Drive Working If:</h3>
28
+ <ol class="mt-4 list-decimal space-y-2 pl-6 text-sm leading-7 text-gray-700">
29
+ <li><strong>Green button:</strong> Server timestamp updates, client time stays same</li>
30
+ <li><strong>No flash/flicker</strong> during navigation</li>
31
+ <li><strong>Browser back/forward</strong> works smoothly</li>
32
+ <li><strong>Red button:</strong> Both timestamps change (full reload)</li>
33
+ </ol>
34
+
35
+ <div class="mt-4 rounded-md border-l-4 border-cyan-600 bg-white p-4 text-sm text-gray-700">
36
+ <strong>Expected behavior:</strong> Turbo Drive replaces <code>&lt;body&gt;</code> content without full page reload, keeping CSS/JS loaded.
37
+ </div>
38
+ </section>
39
+
40
+ <div class="mt-8 flex flex-wrap gap-3">
41
+ <%= link_to "← Stimulus Test", tests_stimulus_path, class: "inline-flex items-center rounded-md bg-gray-700 px-4 py-2 text-sm font-semibold text-white transition hover:bg-gray-800" %>
42
+ <%= link_to "Turbo Frames →", tests_turbo_frame_path, class: "inline-flex items-center rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-blue-700" %>
43
+ </div>
44
+ </div>
45
+
46
+ <script>
47
+ // Record client-side load time
48
+ document.getElementById('client-load-time').textContent = new Date().toLocaleTimeString('en-US', {
49
+ hour12: false,
50
+ hour: '2-digit',
51
+ minute: '2-digit',
52
+ second: '2-digit',
53
+ fractionalSecondDigits: 3
54
+ });
55
+ </script>
@@ -0,0 +1,87 @@
1
+ <div class="mx-auto max-w-3xl px-6 py-10">
2
+ <h1 class="text-3xl font-semibold text-gray-900">Turbo Frames Test</h1>
3
+
4
+ <section class="mt-8 space-y-4 rounded-xl bg-white p-6 shadow-sm ring-1 ring-gray-200">
5
+ <div>
6
+ <h2 class="text-xl font-semibold text-gray-900">1. Lazy Loading Frame</h2>
7
+ <p class="mt-2 text-sm text-gray-600">This frame loads content automatically when visible.</p>
8
+ </div>
9
+
10
+ <%= turbo_frame_tag "lazy-content",
11
+ src: tests_frame_content_path,
12
+ loading: :lazy,
13
+ class: "block min-h-[120px]" do %>
14
+ <div class="flex min-h-[120px] items-center justify-center rounded-lg border border-dashed border-gray-300 bg-gray-50 p-6">
15
+ <p class="text-sm text-gray-600">⏳ Loading frame content...</p>
16
+ </div>
17
+ <% end %>
18
+ </section>
19
+
20
+ <section class="mt-6 space-y-4 rounded-xl bg-white p-6 shadow-sm ring-1 ring-gray-200">
21
+ <div>
22
+ <h2 class="text-xl font-semibold text-gray-900">2. Manual Reload Frame</h2>
23
+ <p class="mt-2 text-sm text-gray-600">Click button to reload only this frame.</p>
24
+ </div>
25
+
26
+ <%= turbo_frame_tag "manual-content" do %>
27
+ <div class="rounded-lg border border-blue-400 bg-blue-50 p-6">
28
+ <p class="text-base font-semibold text-blue-700">Original Frame Content</p>
29
+ <p class="mt-2 text-sm text-blue-900">
30
+ Server time: <%= Time.current.strftime('%H:%M:%S.%L') %>
31
+ </p>
32
+ <div class="mt-4">
33
+ <%= link_to "🔄 Reload This Frame Only",
34
+ tests_frame_content_path,
35
+ data: { turbo_frame: "manual-content" },
36
+ class: "inline-flex items-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-blue-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600" %>
37
+ </div>
38
+ </div>
39
+ <% end %>
40
+ </section>
41
+
42
+ <section class="mt-6 space-y-4 rounded-xl bg-white p-6 shadow-sm ring-1 ring-gray-200">
43
+ <div>
44
+ <h2 class="text-xl font-semibold text-gray-900">3. Turbo Streams - Multiple Updates</h2>
45
+ <p class="mt-2 text-sm text-gray-600">Single request updates multiple parts of the page.</p>
46
+ </div>
47
+
48
+ <%= form_with url: tests_update_content_path,
49
+ data: { turbo_frame: "_top" },
50
+ html: { class: "mb-4" } do %>
51
+ <button type="submit"
52
+ class="rounded-md bg-cyan-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-cyan-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cyan-600">
53
+ ⚡ Trigger Stream Update
54
+ </button>
55
+ <% end %>
56
+
57
+ <div id="dynamic-content"
58
+ class="rounded-lg border border-dashed border-gray-300 bg-gray-50 p-6">
59
+ <p class="text-center text-sm text-gray-600">
60
+ 👆 Click button to update this area
61
+ </p>
62
+ </div>
63
+
64
+ <div class="space-y-2">
65
+ <h3 class="text-base font-semibold text-gray-900">Update Log:</h3>
66
+ <div id="log"
67
+ class="max-h-48 overflow-y-auto rounded-md border border-gray-200 bg-gray-50 p-3">
68
+ <p class="text-center text-sm text-gray-500">No updates yet</p>
69
+ </div>
70
+ </div>
71
+ </section>
72
+
73
+ <section class="mt-8 rounded-xl bg-gray-50 p-6 ring-1 ring-gray-200">
74
+ <h3 class="text-base font-semibold text-gray-900">✅ Success Indicators:</h3>
75
+ <ol class="mt-4 list-decimal space-y-2 pl-6 text-sm leading-7 text-gray-700">
76
+ <li><strong>Lazy frame:</strong> Loads automatically, replaces "Loading..." message</li>
77
+ <li><strong>Manual frame:</strong> Only that frame updates, rest of page unchanged</li>
78
+ <li><strong>Turbo Streams:</strong> Single click updates both content box AND log</li>
79
+ <li><strong>No page scroll</strong> or flash during updates</li>
80
+ </ol>
81
+ </section>
82
+
83
+ <div class="mt-8 flex flex-wrap gap-3">
84
+ <%= link_to "← Turbo Drive", tests_turbo_drive_path, class: "inline-flex items-center rounded-md bg-gray-700 px-4 py-2 text-sm font-semibold text-white transition hover:bg-gray-800" %>
85
+ <%= link_to "Back to Stimulus →", tests_stimulus_path, class: "inline-flex items-center rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-blue-700" %>
86
+ </div>
87
+ </div>
@@ -8,7 +8,7 @@ export default defineConfig({
8
8
  plugins: [
9
9
  RubyPlugin(),
10
10
  tailwindcss(),
11
- FullReload(['config/routes.rb', 'app/views/**/*', 'app/frontend/pages/**/*']),
11
+ FullReload(['**/routes.rb', '**/views/**/*.html.erb', 'app/frontend/**/*']),
12
12
  StimulusHMR()
13
13
  ],
14
14
  root: './app/frontend',
@@ -1,3 +1,4 @@
1
1
  SunSword.setup do |config|
2
2
  config.scope_owner_column = :account_id
3
+ config.scope_owner = :account
3
4
  end