sun-sword 0.0.11 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +57 -2
- data/Gemfile +8 -0
- data/Gemfile.lock +114 -55
- data/README.md +35 -13
- data/Rakefile +6 -10
- data/lib/generators/sun_sword/USAGE +22 -3
- data/lib/generators/sun_sword/frontend_generator.rb +77 -39
- data/lib/generators/sun_sword/frontend_generator_spec.rb +539 -0
- data/lib/generators/sun_sword/init_generator.rb +2 -0
- data/lib/generators/sun_sword/init_generator_spec.rb +82 -0
- data/lib/generators/sun_sword/scaffold_generator.rb +189 -24
- data/lib/generators/sun_sword/scaffold_generator_spec.rb +1414 -0
- data/lib/generators/sun_sword/templates_frontend/{Procfile.dev → Procfile.dev.tt} +1 -0
- data/lib/generators/sun_sword/templates_frontend/bin/{watch → watch.tt} +2 -1
- data/lib/generators/sun_sword/templates_frontend/config/{vite.json → vite.json.tt} +2 -1
- data/lib/generators/sun_sword/templates_frontend/controllers/application_controller.rb.tt +2 -2
- data/lib/generators/sun_sword/templates_frontend/controllers/tests_controller.rb +36 -0
- data/lib/generators/sun_sword/templates_frontend/controllers/tests_controller_spec.rb +62 -0
- data/lib/generators/sun_sword/templates_frontend/env.development +1 -1
- data/lib/generators/sun_sword/templates_frontend/frontend/entrypoints/application.js +2 -2
- data/lib/generators/sun_sword/templates_frontend/frontend/pages/tests-stimulus.js +31 -0
- data/lib/generators/sun_sword/templates_frontend/frontend/pages/web.js +12 -0
- data/lib/generators/sun_sword/templates_frontend/frontend/stylesheets/application.css +1 -3
- data/lib/generators/sun_sword/templates_frontend/helpers/application_helper.rb +17 -0
- data/lib/generators/sun_sword/templates_frontend/helpers/application_helper_spec.rb +30 -0
- data/lib/generators/sun_sword/templates_frontend/package.json.tt +14 -0
- data/lib/generators/sun_sword/templates_frontend/views/components/_action_destroy.html.erb.tt +1 -1
- data/lib/generators/sun_sword/templates_frontend/views/components/_alert.html.erb.tt +1 -1
- data/lib/generators/sun_sword/templates_frontend/views/layouts/application.html.erb.tt +3 -3
- data/lib/generators/sun_sword/templates_frontend/views/tests/_frame_content.html.erb +9 -0
- data/lib/generators/sun_sword/templates_frontend/views/tests/_log_entry.html.erb +4 -0
- data/lib/generators/sun_sword/templates_frontend/views/tests/_updated_content.html.erb +6 -0
- data/lib/generators/sun_sword/templates_frontend/views/tests/stimulus.html.erb +45 -0
- data/lib/generators/sun_sword/templates_frontend/views/tests/turbo_drive.html.erb +55 -0
- data/lib/generators/sun_sword/templates_frontend/views/tests/turbo_frame.html.erb +87 -0
- data/lib/generators/sun_sword/templates_frontend/vite.config.ts.tt +1 -1
- data/lib/generators/sun_sword/templates_init/config/initializers/sun_sword.rb +1 -0
- data/lib/generators/sun_sword/templates_scaffold/controllers/controller.rb.tt +24 -24
- data/lib/generators/sun_sword/templates_scaffold/controllers/controller_spec.rb.tt +398 -0
- data/lib/generators/sun_sword/templates_scaffold/views/index.html.erb.tt +5 -5
- data/lib/generators/sun_sword/templates_scaffold/views/show.html.erb.tt +3 -0
- data/lib/generators/tmp/db/structures/test_structure.yaml +42 -0
- data/lib/sun-sword.rb +1 -0
- data/lib/sun_sword/configuration_spec.rb +77 -0
- data/lib/sun_sword/version.rb +1 -1
- metadata +84 -30
- data/lib/generators/sun_sword/templates_frontend/controllers/site_controller.rb +0 -16
- data/lib/generators/sun_sword/templates_frontend/db/seeds.rb +0 -3
- data/lib/generators/sun_sword/templates_frontend/db/structures/example.yaml.tt +0 -106
- data/lib/generators/sun_sword/templates_frontend/frontend/pages/stimulus.js +0 -10
- data/lib/generators/sun_sword/templates_frontend/package.json +0 -7
- data/lib/generators/sun_sword/templates_frontend/views/site/_comment.html.erb.tt +0 -3
- data/lib/generators/sun_sword/templates_frontend/views/site/stimulus.html.erb.tt +0 -26
|
@@ -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
|
|
12
|
-
# current_auth
|
|
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
|
|
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
|
|
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("
|
|
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 "
|
|
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
|
+
|
data/lib/generators/sun_sword/templates_frontend/views/components/_action_destroy.html.erb.tt
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
</button>
|
|
19
19
|
</div>
|
|
20
20
|
<div class="flex flex-row">
|
|
21
|
-
<div
|
|
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"/>
|
|
@@ -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-
|
|
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">© <%%= Date.today.year %> Kotaroisme. All rights
|
|
35
35
|
reserved.</p>
|
|
36
36
|
</div>
|
|
37
|
-
<p class="
|
|
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,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><body></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(['
|
|
11
|
+
FullReload(['**/routes.rb', '**/views/**/*.html.erb', 'app/frontend/**/*']),
|
|
12
12
|
StimulusHMR()
|
|
13
13
|
],
|
|
14
14
|
root: './app/frontend',
|