poetry-jumpstart_pro 0.1.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 (92) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +14 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +119 -0
  5. data/lib/generators/poetry_jumpstart_pro/install/files/poetry_jumpstart_pro_helper.rb +41 -0
  6. data/lib/generators/poetry_jumpstart_pro/install/install_generator.rb +196 -0
  7. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/account/passwords/edit.html.erb +46 -0
  8. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/account_invitations/show.html.erb +29 -0
  9. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/account_users/_form.html.erb +20 -0
  10. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/account_users/edit.html.erb +34 -0
  11. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/_form.html.erb +26 -0
  12. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/account_invitations/edit.html.erb +49 -0
  13. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/account_invitations/new.html.erb +37 -0
  14. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/edit.html.erb +51 -0
  15. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/index.html.erb +56 -0
  16. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/new.html.erb +18 -0
  17. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/show.html.erb +102 -0
  18. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/transfers/_form.html.erb +30 -0
  19. data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/application/_account_navbar.html.erb +40 -0
  20. data/lib/generators/poetry_jumpstart_pro/install/templates/announcements/announcements/_announcement.html.erb +28 -0
  21. data/lib/generators/poetry_jumpstart_pro/install/templates/announcements/announcements/index.html.erb +50 -0
  22. data/lib/generators/poetry_jumpstart_pro/install/templates/announcements/announcements/show.html.erb +32 -0
  23. data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/_form.html.erb +39 -0
  24. data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/edit.html.erb +16 -0
  25. data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/index.html.erb +48 -0
  26. data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/new.html.erb +15 -0
  27. data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/show.html.erb +69 -0
  28. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/application/_error_messages.html.erb +17 -0
  29. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/passwords/edit.html.erb +23 -0
  30. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/passwords/new.html.erb +26 -0
  31. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/registrations/edit.html.erb +103 -0
  32. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/registrations/new.html.erb +55 -0
  33. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/sessions/new.html.erb +39 -0
  34. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/sessions/otps/new.html.erb +24 -0
  35. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/shared/_oauth.html.erb +22 -0
  36. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/sudo/new.html.erb +31 -0
  37. data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/two_factor/verify.html.erb +53 -0
  38. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/_charges.html.erb +45 -0
  39. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/_email.html.erb +15 -0
  40. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/_info.html.erb +30 -0
  41. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/show.html.erb +26 -0
  42. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/_plan.html.erb +33 -0
  43. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/_subscription.html.erb +74 -0
  44. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/_summary.html.erb +21 -0
  45. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/cancels/show.html.erb +46 -0
  46. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/edit.html.erb +65 -0
  47. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/paddle_billing/edit.html.erb +17 -0
  48. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/payment_methods/forms/_fake_processor.html.erb +6 -0
  49. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/payment_methods/new.html.erb +26 -0
  50. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/resumes/show.html.erb +29 -0
  51. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/upcomings/show.html.erb +67 -0
  52. data/lib/generators/poetry_jumpstart_pro/install/templates/billing/pricing/show.html.erb +61 -0
  53. data/lib/generators/poetry_jumpstart_pro/install/templates/checkouts/checkouts/_testimonial.html.erb +15 -0
  54. data/lib/generators/poetry_jumpstart_pro/install/templates/checkouts/checkouts/show.html.erb +125 -0
  55. data/lib/generators/poetry_jumpstart_pro/install/templates/dashboard/dashboard/show.html.erb +60 -0
  56. data/lib/generators/poetry_jumpstart_pro/install/templates/errors/errors/404.html.erb +12 -0
  57. data/lib/generators/poetry_jumpstart_pro/install/templates/errors/errors/500.html.erb +12 -0
  58. data/lib/generators/poetry_jumpstart_pro/install/templates/errors/errors/show.html.erb +16 -0
  59. data/lib/generators/poetry_jumpstart_pro/install/templates/madmin/madmin/dashboard/show.html.erb +40 -0
  60. data/lib/generators/poetry_jumpstart_pro/install/templates/madmin/madmin/fields/array_field/_form.html.erb +29 -0
  61. data/lib/generators/poetry_jumpstart_pro/install/templates/madmin/madmin/fields/array_field/_index.html.erb +13 -0
  62. data/lib/generators/poetry_jumpstart_pro/install/templates/madmin/madmin/fields/array_field/_show.html.erb +13 -0
  63. data/lib/generators/poetry_jumpstart_pro/install/templates/notifications/notifications/_notification.html.erb +12 -0
  64. data/lib/generators/poetry_jumpstart_pro/install/templates/notifications/notifications/index.html.erb +31 -0
  65. data/lib/generators/poetry_jumpstart_pro/install/templates/notifications/notifications/nav.html.erb +17 -0
  66. data/lib/generators/poetry_jumpstart_pro/install/templates/public/public/about.html.erb +32 -0
  67. data/lib/generators/poetry_jumpstart_pro/install/templates/public/public/index.html.erb +71 -0
  68. data/lib/generators/poetry_jumpstart_pro/install/templates/public/public/privacy.html.erb +37 -0
  69. data/lib/generators/poetry_jumpstart_pro/install/templates/public/public/terms.html.erb +37 -0
  70. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_account_menu.html.erb +41 -0
  71. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_dev_menu.html.erb +13 -0
  72. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_flash.html.erb +37 -0
  73. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_footer.html.erb +18 -0
  74. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_left_nav.html.erb +11 -0
  75. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_navbar.html+native.erb +36 -0
  76. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_navbar.html.erb +32 -0
  77. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_notifications.html.erb +26 -0
  78. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_right_nav.html.erb +6 -0
  79. data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_user_menu.html.erb +34 -0
  80. data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/_user.html.erb +9 -0
  81. data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/agreements/show.html.erb +33 -0
  82. data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/connected_accounts/_connected_account.html.erb +25 -0
  83. data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/connected_accounts/index.html.erb +34 -0
  84. data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/referrals/index.html.erb +49 -0
  85. data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/two_factor/backup_codes.html.erb +32 -0
  86. data/lib/poetry/jumpstart_pro/css_scanner.rb +121 -0
  87. data/lib/poetry/jumpstart_pro/engine.rb +15 -0
  88. data/lib/poetry/jumpstart_pro/stylesheets.rb +155 -0
  89. data/lib/poetry/jumpstart_pro/test_edits.rb +51 -0
  90. data/lib/poetry/jumpstart_pro/version.rb +8 -0
  91. data/lib/poetry/jumpstart_pro.rb +43 -0
  92. metadata +194 -0
@@ -0,0 +1,29 @@
1
+ <%# Poetry recreation of Jumpstart's madmin array_field/_form. The rows keep
2
+ the DOM contract of app/javascript/madmin/controllers/array_field_controller.js:
3
+ one .row per value, "add" inserting a copy of the template before the
4
+ clicked control (e.target), "remove" walking up to .row. The controls are
5
+ type="button" Poetry Buttons whose children ignore the pointer, so a
6
+ click always targets the button itself, never its label. The blank hidden
7
+ value lets an emptied list save as []. %>
8
+ <div data-controller="array-field" class="space-y-2">
9
+ <%= hidden_field_tag "#{form.object_name}[#{field.attribute_name}][]", "" %>
10
+
11
+ <% Array.wrap(form.object.send(field.attribute_name)).each do |item| %>
12
+ <div class="row flex items-center gap-2">
13
+ <%= poetry_input(name: "#{form.object_name}[#{field.attribute_name}][]", value: item.to_s, autocomplete: "off") %>
14
+ <%= poetry_button(type: :button, variant: :ghost, size: :sm, class: "*:pointer-events-none",
15
+ data: { action: "array-field#remove" }) { "Remove" } %>
16
+ </div>
17
+ <% end %>
18
+
19
+ <%= poetry_button(type: :button, variant: :outline, size: :sm, class: "*:pointer-events-none",
20
+ data: { action: "array-field#add" }) { "Add" } %>
21
+
22
+ <template data-array-field-target="template">
23
+ <div class="row flex items-center gap-2">
24
+ <%= poetry_input(name: "#{form.object_name}[#{field.attribute_name}][]", autocomplete: "off") %>
25
+ <%= poetry_button(type: :button, variant: :ghost, size: :sm, class: "*:pointer-events-none",
26
+ data: { action: "array-field#remove" }) { "Remove" } %>
27
+ </div>
28
+ </template>
29
+ </div>
@@ -0,0 +1,13 @@
1
+ <%# Poetry recreation of Jumpstart's madmin array_field/_index: each value as
2
+ an outline badge (the engine printed the array's inspect string), a muted
3
+ dash when there are none. %>
4
+ <% values = Array.wrap(field.value(record)).map(&:to_s).reject(&:blank?) %>
5
+ <% if values.any? %>
6
+ <span class="inline-flex flex-wrap gap-1">
7
+ <% values.each do |value| %>
8
+ <%= poetry_badge(variant: :outline) { value } %>
9
+ <% end %>
10
+ </span>
11
+ <% else %>
12
+ <span class="text-muted-foreground">&mdash;</span>
13
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <%# Poetry recreation of Jumpstart's madmin array_field/_show: each value as
2
+ an outline badge (the engine printed the array's inspect string), a muted
3
+ dash when there are none. %>
4
+ <% values = Array.wrap(field.value(record)).map(&:to_s).reject(&:blank?) %>
5
+ <% if values.any? %>
6
+ <span class="inline-flex flex-wrap gap-1.5">
7
+ <% values.each do |value| %>
8
+ <%= poetry_badge(variant: :outline) { value } %>
9
+ <% end %>
10
+ </span>
11
+ <% else %>
12
+ <span class="text-muted-foreground">&mdash;</span>
13
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <%# Poetry recreation of Jumpstart's notifications/_notification: a clickable
2
+ Item row that KEEPS the notifications controller's contract - the
3
+ notification target with data-id/seen-at/read-at on the anchor (nil
4
+ values render no attribute, which the CSS keys on) and the `unread`
5
+ marker, now a token dot, toggled by CSS from data-read-at. Rendered by
6
+ the index, the navbar frame, and Noticed's Action Cable push. %>
7
+ <%= poetry_item(tag: :a, href: notification_path(notification), size: :sm, class: "rounded-none px-4 py-3 hover:bg-accent",
8
+ data: { notifications_target: "notification", id: notification&.id, seen_at: notification&.seen_at, read_at: notification&.read_at }) do |item| %>
9
+ <% item.with_title { tag.span(notification.message, class: "line-clamp-3 text-wrap") } %>
10
+ <% item.with_description { local_time_ago notification&.created_at } %>
11
+ <% item.with_actions { tag.span(class: "unread size-2 shrink-0 rounded-full bg-primary", "aria-hidden": true) } %>
12
+ <% end %>
@@ -0,0 +1,31 @@
1
+ <%# Poetry recreation of Jumpstart's notifications/index. %>
2
+ <% content_for :title, t(".title") %>
3
+
4
+ <div class="mx-auto w-full max-w-2xl px-4 py-8 lg:px-6">
5
+ <header class="flex flex-wrap items-center justify-between gap-4">
6
+ <h1 class="text-2xl font-semibold tracking-tight"><%= t(".title") %></h1>
7
+ <% if @notifications.any? %>
8
+ <%= form_with(url: mark_as_read_notifications_path, method: :patch) do %>
9
+ <%= poetry_button(type: :submit, variant: :outline, size: :sm) { t(".mark_all_as_read") } %>
10
+ <% end %>
11
+ <% end %>
12
+ </header>
13
+
14
+ <% if @notifications.any? %>
15
+ <div class="mt-6 divide-y divide-border overflow-hidden rounded-lg border">
16
+ <%= render partial: "notifications/notification", collection: @notifications, cached: true %>
17
+ </div>
18
+
19
+ <% if @page.multiple_pages? %>
20
+ <div class="mt-8 flex justify-center">
21
+ <%= poetry_pagination_nav(@page) %>
22
+ </div>
23
+ <% end %>
24
+ <% else %>
25
+ <%= poetry_empty(media_variant: :icon, title_tag: :h2, class: "mt-6 rounded-lg border border-dashed py-12") do |empty| %>
26
+ <% empty.with_media { poetry_icon(name: :bell) } %>
27
+ <% empty.with_title { t(".empty_placeholder") } %>
28
+ <% empty.with_description { t("notifications.empty_description") } %>
29
+ <% end %>
30
+ <% end %>
31
+ </div>
@@ -0,0 +1,17 @@
1
+ <%# Poetry recreation of Jumpstart's notifications/nav: the lazily loaded
2
+ frame inside the navbar sheet. It stays turbo_frame_tag "notifications"
3
+ with the rows as direct children (the notifications controller inserts
4
+ pushed rows at the top of this frame) and the empty state as the
5
+ `only:` child. %>
6
+ <%= turbo_frame_tag "notifications" do %>
7
+ <%= render partial: "notifications/notification", collection: @notifications, cached: true %>
8
+
9
+ <% if @notifications.any? %>
10
+ <%= poetry_link(href: notifications_path, underline: :none, class: "block px-4 py-3 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-accent-foreground") { t("notifications.see_more") } %>
11
+ <% end %>
12
+
13
+ <div class="hidden p-6 text-center text-sm only:block">
14
+ <div class="mb-1 font-medium"><%= t("notifications.empty") %></div>
15
+ <div class="text-muted-foreground"><%= t("notifications.empty_description") %></div>
16
+ </div>
17
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <%# Poetry recreation of Jumpstart's public/about: a page header over the
2
+ facts the app already knows about itself (who to email, where the
3
+ changelog lives) so the stub page has a shape without inventing copy. %>
4
+ <% content_for :title, t(".title") %>
5
+
6
+ <div class="mx-auto w-full max-w-3xl px-4 py-10 lg:px-6">
7
+ <header class="flex flex-col gap-4">
8
+ <div class="space-y-1">
9
+ <h1 class="text-3xl font-semibold tracking-tight"><%= t(".title") %></h1>
10
+ <p class="text-sm text-muted-foreground"><%= t(".description", default: "Who runs %{name} and how to reach us.", name: Jumpstart.config.application_name) %></p>
11
+ </div>
12
+ <%= poetry_separator %>
13
+ </header>
14
+
15
+ <div class="mt-8 grid gap-4 sm:grid-cols-2">
16
+ <%= poetry_card(title_tag: :h2) do |card| %>
17
+ <% card.with_title { t(".contact", default: "Get in touch") } %>
18
+ <% card.with_description { t(".contact_description", default: "Questions or feedback about %{name}? Send %{business} an email.", name: Jumpstart.config.application_name, business: Jumpstart.config.business_name) } %>
19
+ <%= poetry_link(href: "mailto:#{Jumpstart.config.support_email}", underline: :always, class: "inline-flex items-center gap-1 text-sm") do %>
20
+ <%= poetry_icon(name: :mail, class: "size-3.5") %> <%= Jumpstart.config.support_email %>
21
+ <% end %>
22
+ <% end %>
23
+
24
+ <%= poetry_card(title_tag: :h2) do |card| %>
25
+ <% card.with_title { t("announcements.index.title") } %>
26
+ <% card.with_description { t("announcements.index.description", name: Jumpstart.config.application_name) } %>
27
+ <%= poetry_link(href: announcements_path, underline: :always, class: "inline-flex items-center gap-1 text-sm") do %>
28
+ <%= t(".read_updates", default: "Read the updates") %> <%= poetry_icon(name: :"arrow-right", class: "size-3.5") %>
29
+ <% end %>
30
+ <% end %>
31
+ </div>
32
+ </div>
@@ -0,0 +1,71 @@
1
+ <%# Poetry recreation of Jumpstart's public/index (the marketing landing):
2
+ a centered hero with placeholder copy to replace and the visitor calls
3
+ to action, a card row of the public destinations, and the development-only
4
+ Jumpstart links. The top navigation and footer come from the layout. %>
5
+ <div class="mx-auto w-full max-w-5xl px-4 py-10 lg:px-6">
6
+ <section class="mx-auto flex max-w-2xl flex-col items-center py-16 text-center sm:py-24" aria-labelledby="landing-title">
7
+ <h1 id="landing-title" class="text-4xl font-semibold tracking-tight sm:text-5xl"><%= t(".headline", default: "Welcome to %{name}", name: Jumpstart.config.application_name) %></h1>
8
+ <p class="mt-4 text-xl leading-normal text-muted-foreground"><%= t(".lede", default: "Say what %{name} does and who it is for, in a sentence or two.", name: Jumpstart.config.application_name) %></p>
9
+ <p class="mt-4 text-sm text-muted-foreground"><%= t(".customize", default: "This public homepage lives in") %> <%= tag.code("app/views/public/index.html.erb", class: "font-mono text-xs") %></p>
10
+
11
+ <% unless authenticated? %>
12
+ <div class="mt-8 flex flex-wrap items-center justify-center gap-3">
13
+ <%= poetry_button(size: :lg, href: signup_path) { t(".sign_up", default: "Sign up") } %>
14
+ <%= poetry_button(variant: :outline, size: :lg, href: login_path) { t(".log_in", default: "Log in") } %>
15
+ </div>
16
+ <% end %>
17
+ </section>
18
+
19
+ <section class="space-y-6" aria-labelledby="landing-explore">
20
+ <div class="space-y-1">
21
+ <h2 id="landing-explore" class="text-2xl font-semibold tracking-tight"><%= t(".explore", default: "Explore") %></h2>
22
+ <p class="text-sm text-muted-foreground"><%= t(".explore_description", default: "Where to go next on %{name}.", name: Jumpstart.config.application_name) %></p>
23
+ </div>
24
+
25
+ <%# Three columns only when the pricing card fills the third one. %>
26
+ <div class="<%= class_names("grid gap-4 sm:grid-cols-2", "lg:grid-cols-3": Jumpstart.config.payments_enabled?) %>">
27
+ <%= poetry_card do |card| %>
28
+ <% card.with_title { t("announcements.index.title") } %>
29
+ <% card.with_description { t("announcements.index.description", name: Jumpstart.config.application_name) } %>
30
+ <%= poetry_link(href: announcements_path, underline: :always, class: "inline-flex items-center gap-1 text-sm") do %>
31
+ <%= t(".read_updates", default: "Read the updates") %> <%= poetry_icon(name: :"arrow-right", class: "size-3.5") %>
32
+ <% end %>
33
+ <% end %>
34
+
35
+ <%= poetry_card do |card| %>
36
+ <% card.with_title { t("public.about.title") } %>
37
+ <% card.with_description { t(".about_description", default: "Who runs %{name} and how to reach us.", name: Jumpstart.config.application_name) } %>
38
+ <%= poetry_link(href: about_path, underline: :always, class: "inline-flex items-center gap-1 text-sm") do %>
39
+ <%= t(".about_us", default: "About us") %> <%= poetry_icon(name: :"arrow-right", class: "size-3.5") %>
40
+ <% end %>
41
+ <% end %>
42
+
43
+ <% if Jumpstart.config.payments_enabled? %>
44
+ <%= poetry_card do |card| %>
45
+ <% card.with_title { t(".pricing", default: "Pricing") } %>
46
+ <% card.with_description { t(".pricing_description", default: "Plans and what each one includes.") } %>
47
+ <%= poetry_link(href: pricing_path, underline: :always, class: "inline-flex items-center gap-1 text-sm") do %>
48
+ <%= t(".view_pricing", default: "View pricing") %> <%= poetry_icon(name: :"arrow-right", class: "size-3.5") %>
49
+ <% end %>
50
+ <% end %>
51
+ <% end %>
52
+ </div>
53
+ </section>
54
+
55
+ <% if Rails.env.development? %>
56
+ <section class="mt-12 rounded-lg border bg-muted/50 p-6" aria-labelledby="landing-development">
57
+ <div class="flex flex-wrap items-center justify-between gap-4">
58
+ <div class="space-y-1">
59
+ <h2 id="landing-development" class="text-lg font-semibold tracking-tight"><%= t(".development", default: "Development") %></h2>
60
+ <p class="text-sm text-muted-foreground"><%= t(".development_description", default: "Jumpstart's configuration and documentation, shown only in development.") %></p>
61
+ </div>
62
+ <div class="flex flex-wrap items-center gap-3">
63
+ <%= poetry_button(variant: :outline, href: jumpstart_path, data: { turbo: false }) { "Configure Jumpstart" } %>
64
+ <%= poetry_link(href: jumpstart.docs_path, external: true, underline: :always, data: { turbo: false }, class: "inline-flex items-center gap-1 text-sm font-medium") do %>
65
+ Read the Docs <%= poetry_icon(name: :"arrow-right", class: "size-3.5") %>
66
+ <% end %>
67
+ </div>
68
+ </div>
69
+ </section>
70
+ <% end %>
71
+ </div>
@@ -0,0 +1,37 @@
1
+ <%# Poetry recreation of Jumpstart's public/privacy: the policy prose from
2
+ users/agreements/_privacy_policy typeset for reading, with the agreement's
3
+ last-updated date when config/initializers/agreements.rb declares one. A
4
+ policy that is still unwritten (the partial renders blank) gets an empty
5
+ state with a way to ask, not a bare heading. %>
6
+ <% content_for :title, t(".title") %>
7
+ <% policy = capture { render "users/agreements/privacy_policy" } %>
8
+
9
+ <div class="mx-auto w-full max-w-3xl px-4 py-10 lg:px-6">
10
+ <header class="flex flex-col gap-4">
11
+ <div class="space-y-1">
12
+ <h1 class="text-3xl font-semibold tracking-tight"><%= t(".title") %></h1>
13
+ <% if @agreement %>
14
+ <p class="text-sm text-muted-foreground"><%= t("users.agreements.show.last_updated", date: l(@agreement.updated.to_date, format: :long)) %></p>
15
+ <% end %>
16
+ </div>
17
+ <%= poetry_separator %>
18
+ </header>
19
+
20
+ <% if policy.present? %>
21
+ <%= poetry_typeset(class: "mt-8") do %>
22
+ <%= policy %>
23
+ <% end %>
24
+ <% else %>
25
+ <div class="mt-8">
26
+ <%= poetry_empty(media_variant: :icon) do |empty| %>
27
+ <% empty.with_media { poetry_icon(name: :"file-text") } %>
28
+ <% empty.with_title { t(".empty_title", default: "Not published yet") } %>
29
+ <% empty.with_description { t(".empty_description", default: "The %{title} will appear here once it is published. Until then, email us with any questions.", title: t(".title")) } %>
30
+ <%= poetry_button(href: "mailto:#{Jumpstart.config.support_email}") do |button| %>
31
+ <% button.with_leading { poetry_icon(name: :mail) } %>
32
+ <%= t(".contact", default: "Email %{email}", email: Jumpstart.config.support_email) %>
33
+ <% end %>
34
+ <% end %>
35
+ </div>
36
+ <% end %>
37
+ </div>
@@ -0,0 +1,37 @@
1
+ <%# Poetry recreation of Jumpstart's public/terms: the terms prose from
2
+ users/agreements/_terms_of_service typeset for reading, with the
3
+ agreement's last-updated date when config/initializers/agreements.rb
4
+ declares one. Terms that are still unwritten (the partial renders blank)
5
+ get an empty state with a way to ask, not a bare heading. %>
6
+ <% content_for :title, t(".title") %>
7
+ <% terms = capture { render "users/agreements/terms_of_service" } %>
8
+
9
+ <div class="mx-auto w-full max-w-3xl px-4 py-10 lg:px-6">
10
+ <header class="flex flex-col gap-4">
11
+ <div class="space-y-1">
12
+ <h1 class="text-3xl font-semibold tracking-tight"><%= t(".title") %></h1>
13
+ <% if @agreement %>
14
+ <p class="text-sm text-muted-foreground"><%= t("users.agreements.show.last_updated", date: l(@agreement.updated.to_date, format: :long)) %></p>
15
+ <% end %>
16
+ </div>
17
+ <%= poetry_separator %>
18
+ </header>
19
+
20
+ <% if terms.present? %>
21
+ <%= poetry_typeset(class: "mt-8") do %>
22
+ <%= terms %>
23
+ <% end %>
24
+ <% else %>
25
+ <div class="mt-8">
26
+ <%= poetry_empty(media_variant: :icon) do |empty| %>
27
+ <% empty.with_media { poetry_icon(name: :"file-text") } %>
28
+ <% empty.with_title { t(".empty_title", default: "Not published yet") } %>
29
+ <% empty.with_description { t(".empty_description", default: "The %{title} will appear here once it is published. Until then, email us with any questions.", title: t(".title")) } %>
30
+ <%= poetry_button(href: "mailto:#{Jumpstart.config.support_email}") do |button| %>
31
+ <% button.with_leading { poetry_icon(name: :mail) } %>
32
+ <%= t(".contact", default: "Email %{email}", email: Jumpstart.config.support_email) %>
33
+ <% end %>
34
+ <% end %>
35
+ </div>
36
+ <% end %>
37
+ </div>
@@ -0,0 +1,41 @@
1
+ <%# Poetry recreation of Jumpstart's application/_account_menu (the team
2
+ account switcher) on poetry_popover. Subdomain and path multitenancy
3
+ switch by link; the default mode PATCHes switch_account_path, and the
4
+ accounts controller reconnects Action Cable when that form finishes.
5
+ The controller sits INSIDE the panel so it travels with the content when
6
+ the popover portals to body on open. %>
7
+ <%= poetry_popover(align: :start, content_class: "w-64 p-1", label: t(".switch_account", default: "Switch account"), class: "account-menu") do |popover| %>
8
+ <% popover.with_trigger(variant: :ghost, size: :sm, class: "gap-2") do %>
9
+ <%= account_avatar current_account, size: "size-6" %>
10
+ <span class="name max-w-32 truncate"><%= current_account.name %></span>
11
+ <%= poetry_icon(name: :"chevrons-up-down", class: "text-muted-foreground") %>
12
+ <% end %>
13
+
14
+ <div class="flex flex-col gap-0.5" data-controller="accounts" data-action="turbo:submit-end->accounts#reconnect">
15
+ <% Current.other_accounts.each do |account| %>
16
+ <% if Jumpstart::Multitenancy.subdomain? && account.subdomain? %>
17
+ <%= poetry_button(variant: :ghost, size: :sm, href: root_url(subdomain: account.subdomain), class: "w-full justify-start gap-2") do %>
18
+ <%= account_avatar account, size: "size-6" %>
19
+ <span class="truncate"><%= account.name %></span>
20
+ <% end %>
21
+ <% elsif Jumpstart::Multitenancy.path? %>
22
+ <%= poetry_button(variant: :ghost, size: :sm, href: root_url(script_name: "/#{account.id}"), class: "w-full justify-start gap-2") do %>
23
+ <%= account_avatar account, size: "size-6" %>
24
+ <span class="truncate"><%= account.name %></span>
25
+ <% end %>
26
+ <% else %>
27
+ <%= form_with url: switch_account_path(account), method: :patch do %>
28
+ <%= poetry_button(type: :submit, variant: :ghost, size: :sm, class: "w-full justify-start gap-2") do %>
29
+ <%= account_avatar account, size: "size-6" %>
30
+ <span class="truncate"><%= account.name %></span>
31
+ <% end %>
32
+ <% end %>
33
+ <% end %>
34
+ <% end %>
35
+
36
+ <%= poetry_separator(class: "my-1") %>
37
+ <%= poetry_button(variant: :ghost, size: :sm, href: accounts_path, class: "w-full justify-start gap-2") do %>
38
+ <%= poetry_icon(name: :users) %><%= t(".manage_accounts") %>
39
+ <% end %>
40
+ </div>
41
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <%# Poetry recreation of Jumpstart's application/_dev_menu (development only). %>
2
+ <%= poetry_dropdown_menu(align: :end) do |menu| %>
3
+ <% menu.with_trigger(variant: :ghost, size: :icon, label: "Jumpstart development") do %>
4
+ <%= poetry_icon(name: :code) %>
5
+ <% end %>
6
+
7
+ <% menu.with_label { "Jumpstart" } %>
8
+ <% menu.with_group do |group| %>
9
+ <% group.with_item(href: jumpstart_path(script_name: nil), data: { turbo: false }) { "Configuration" } %>
10
+ <% group.with_item(href: jumpstart.docs_path, external: true, data: { turbo: false }) { "Documentation" } %>
11
+ <% group.with_item(href: mailbin_path(script_name: nil), external: true, data: { turbo: false }) { "Mailbin" } %>
12
+ <% end %>
13
+ <% end %>
@@ -0,0 +1,37 @@
1
+ <%# Poetry recreation of Jumpstart's application/_flash: string flashes render
2
+ as edge-to-edge alerts; hash flashes (Jumpstart's toasts) stream into the
3
+ poetry_toaster below, which also carries them across Turbo visits. Every
4
+ Jumpstart layout renders this partial once, so the toaster lives here
5
+ and the layouts stay Jumpstart's own. %>
6
+ <div id="flash" data-turbo-temporary>
7
+ <%# The message is the alert's body, as a sanitized paragraph: Jumpstart's
8
+ banner was an icon and a <p>, and its tests look for the text in a <p>. %>
9
+ <% if alert %>
10
+ <%= poetry_alert(variant: :destructive, class: "rounded-none border-x-0 border-t-0") do |flash_alert| %>
11
+ <% flash_alert.with_icon(name: :"triangle-alert") %>
12
+ <p><%= sanitize(alert) %></p>
13
+ <% end %>
14
+ <% end %>
15
+ <% if notice %>
16
+ <%= poetry_alert(class: "rounded-none border-x-0 border-t-0") do |flash_notice| %>
17
+ <% flash_notice.with_icon(name: :info) %>
18
+ <p><%= sanitize(notice) %></p>
19
+ <% end %>
20
+ <% end %>
21
+ </div>
22
+
23
+ <div id="toasts" data-turbo-temporary>
24
+ <% toasts.each do |_, toast| %>
25
+ <% toast = toast.deep_symbolize_keys %>
26
+ <%= turbo_stream.poetry_toast(title: toast[:title], description: toast[:description],
27
+ variant: poetry_toast_variant(toast[:icon_name]),
28
+ duration: toast[:dismiss_after],
29
+ show_close_button: toast.fetch(:dismissable, true)) do |streamed| %>
30
+ <% if toast[:link].is_a?(Hash) %>
31
+ <% streamed.with_action(href: toast[:link][:url]) { toast[:link][:text] } %>
32
+ <% end %>
33
+ <% end %>
34
+ <% end %>
35
+ </div>
36
+
37
+ <%= poetry_toaster %>
@@ -0,0 +1,18 @@
1
+ <%# Poetry recreation of Jumpstart's application/_footer. %>
2
+ <footer class="w-full px-4 py-6 text-sm text-muted-foreground lg:px-6">
3
+ <div class="flex flex-col-reverse items-center gap-4 lg:flex-row lg:justify-between">
4
+ <div class="flex items-center gap-3">
5
+ <%= link_to root_path, class: "hidden sm:inline-block" do %>
6
+ <%= render_svg "mark", class: "size-6 fill-current" %>
7
+ <% end %>
8
+ <span>&copy; <%= Date.current.year %> <%= Jumpstart.config.business_name %></span>
9
+ </div>
10
+
11
+ <ul class="flex list-none flex-wrap items-center justify-center gap-x-6 gap-y-2">
12
+ <li><%= poetry_link(href: announcements_path, underline: :hover) { t(".announcements") } %></li>
13
+ <li><%= poetry_link(href: about_path, underline: :hover) { t(".about") } %></li>
14
+ <li><%= poetry_link(href: privacy_path, underline: :hover) { t(".privacy") } %></li>
15
+ <li><%= poetry_link(href: terms_path, underline: :hover) { t(".terms") } %></li>
16
+ </ul>
17
+ </div>
18
+ </footer>
@@ -0,0 +1,11 @@
1
+ <%# Poetry recreation of Jumpstart's application/_left_nav: Pricing for
2
+ visitors; for signed-in members of more than one account, the team
3
+ account switcher. %>
4
+ <% if authenticated? %>
5
+ <%= render "application/account_menu" if Jumpstart.config.team_accounts? && Current.other_accounts.any? %>
6
+ <% else %>
7
+ <% active = current_page?(pricing_path) %>
8
+ <%= poetry_link(href: pricing_path, current: active, underline: :none,
9
+ class: class_names("inline-flex h-9 items-center rounded-md px-3 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground",
10
+ active ? "bg-accent text-accent-foreground" : "text-muted-foreground")) { t(".pricing") } %>
11
+ <% end %>
@@ -0,0 +1,36 @@
1
+ <%# Poetry recreation of Jumpstart's application/_navbar for Hotwire Native.
2
+ The native app draws its own bar, so this is a panel its menu button
3
+ opens through the toggle-nav-bar window event: the left and right
4
+ navigation, the settings links, a bridged sign out, and the notification
5
+ badge bridge. A host _navbar override would shadow the engine's native
6
+ variant, so the gem ships both. %>
7
+ <nav class="fixed inset-x-0 top-0 z-50 border-b bg-background shadow-sm" aria-label="<%= Jumpstart.config.application_name %>"
8
+ data-controller="toggle" data-action="toggle-nav-bar@window->toggle#toggle">
9
+ <div class="hidden flex-col gap-4 p-4 lg:flex" data-toggle-target="toggleable">
10
+ <div class="flex flex-col gap-1"><%= render "application/left_nav" %></div>
11
+ <div class="flex flex-col gap-2"><%= render "application/right_nav" %></div>
12
+
13
+ <% if authenticated? %>
14
+ <%= poetry_separator %>
15
+ <div class="flex flex-col gap-1">
16
+ <%= poetry_button(variant: :ghost, href: edit_registration_path, class: "justify-start") { t(".profile") } %>
17
+ <%= poetry_button(variant: :ghost, href: edit_account_password_path, class: "justify-start") { t(".password") } %>
18
+ <%= poetry_button(variant: :ghost, href: accounts_path, class: "justify-start") { t(".accounts") } %>
19
+ <%= form_with url: session_path, method: :delete do %>
20
+ <%= poetry_button(type: :submit, variant: :ghost, class: "w-full justify-start",
21
+ data: { controller: "bridge--sign-out", action: "bridge--sign-out#signOut",
22
+ "bridge--sign-out-path-value": api_v1_auth_path }) { t(".sign_out") } %>
23
+ <% end %>
24
+ </div>
25
+
26
+ <%= tag.div data: {
27
+ controller: "notifications",
28
+ notifications_account_id_value: current_account.id,
29
+ notifications_account_unread_value: notification_counts[current_account.id],
30
+ notifications_total_unread_value: notification_counts.values.sum
31
+ } do %>
32
+ <%= tag.div data: { controller: "bridge--notification-badge", notifications_target: "nativeBadge" } %>
33
+ <% end %>
34
+ <% end %>
35
+ </div>
36
+ </nav>
@@ -0,0 +1,32 @@
1
+ <%# Poetry recreation of Jumpstart's application/_navbar: the brand mark, the
2
+ left and right navigation, and the signed-in controls. Below lg the two
3
+ navigation lists fold behind the menu button into a panel under the bar,
4
+ toggled by Jumpstart's toggle controller. The native variant is
5
+ _navbar.html+native. %>
6
+ <nav class="border-b bg-background" data-controller="toggle" aria-label="<%= Jumpstart.config.application_name %>">
7
+ <div class="flex min-h-14 flex-wrap items-center justify-between gap-x-4 px-4 lg:flex-nowrap lg:px-6">
8
+ <%= link_to root_path, class: "flex shrink-0 items-center py-3" do %>
9
+ <%= render_svg "logo", class: "h-6 fill-current" %>
10
+ <span class="sr-only"><%= Jumpstart.config.application_name %></span>
11
+ <% end %>
12
+
13
+ <div class="order-last hidden w-full flex-col gap-3 border-t py-3 lg:order-none lg:flex lg:w-auto lg:flex-1 lg:flex-row lg:items-center lg:justify-between lg:border-0 lg:py-0"
14
+ data-toggle-target="toggleable">
15
+ <div class="flex flex-col gap-1 lg:flex-row lg:items-center"><%= render "application/left_nav" %></div>
16
+ <div class="flex flex-col gap-2 lg:flex-row lg:items-center"><%= render "application/right_nav" %></div>
17
+ </div>
18
+
19
+ <div class="flex items-center gap-1">
20
+ <%= poetry_button(variant: :ghost, size: :icon, label: t(".menu", default: "Menu"), id: "sidebar-open", class: "lg:hidden",
21
+ data: { action: "click->toggle#toggle touch->toggle#toggle" }) do %>
22
+ <%= poetry_icon(name: :menu) %>
23
+ <% end %>
24
+
25
+ <%= render "application/dev_menu" if Rails.env.development? %>
26
+ <% if authenticated? %>
27
+ <%= render "application/notifications" %>
28
+ <%= render "application/user_menu" %>
29
+ <% end %>
30
+ </div>
31
+ </div>
32
+ </nav>
@@ -0,0 +1,26 @@
1
+ <%# Poetry recreation of Jumpstart's application/_notifications: the bell with
2
+ its unread badge opens a right sheet holding Jumpstart's lazy notification
3
+ frame. A sheet (dialog family) stays in the DOM, so the notifications
4
+ controller keeps its badge and list targets in scope; the open action on
5
+ the wrapper fires for the trigger click (marking everything seen, as
6
+ Jumpstart does) and harmlessly again for clicks inside the panel. %>
7
+ <%= tag.div class: "inline-flex", data: {
8
+ controller: "notifications",
9
+ action: "click->notifications#open",
10
+ notifications_account_id_value: current_account.id,
11
+ notifications_account_unread_value: notification_counts[current_account.id],
12
+ notifications_total_unread_value: notification_counts.values.sum
13
+ } do %>
14
+ <%= poetry_sheet(side: :right, content_class: "w-96 max-w-full") do |sheet| %>
15
+ <% sheet.with_trigger(variant: :ghost, size: :icon, label: t("notifications.index.title", default: "Notifications"), class: "relative") do %>
16
+ <%= poetry_icon(name: :bell) %>
17
+ <span data-notifications-target="badge" class="absolute right-1.5 top-1.5 hidden size-2 rounded-full bg-destructive" aria-hidden="true"></span>
18
+ <% end %>
19
+ <% sheet.with_title { t("notifications.index.title", default: "Notifications") } %>
20
+
21
+ <%= turbo_frame_tag "notifications", class: "block divide-y divide-border text-sm", loading: :lazy, target: :_top,
22
+ src: nav_notifications_path, data: { notifications_target: "list" } do %>
23
+ <p class="p-4 text-muted-foreground"><%= t("notifications.loading", default: "Loading...") %></p>
24
+ <% end %>
25
+ <% end %>
26
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <%# Poetry recreation of Jumpstart's application/_right_nav: the log in and
2
+ sign up actions for visitors. Signed-in controls live in _navbar. %>
3
+ <% unless authenticated? %>
4
+ <%= poetry_button(variant: :ghost, size: :sm, href: login_path) { t(".log_in") } %>
5
+ <%= poetry_button(size: :sm, href: signup_path) { t(".sign_up") } %>
6
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <%# Poetry recreation of Jumpstart's application/_user_menu on
2
+ poetry_dropdown_menu: link items for the pages, a submit item for the
3
+ sign-out DELETE (the item IS the control, never a nested link or form). %>
4
+ <%= poetry_dropdown_menu(align: :end, content_class: "w-56") do |menu| %>
5
+ <% menu.with_trigger(variant: :ghost, size: :icon, label: t(".user_menu", default: "User menu"), class: "rounded-full") do %>
6
+ <%= image_tag avatar_url_for(current_user), class: "size-6 shrink-0 rounded-full object-cover", alt: "" %>
7
+ <% end %>
8
+
9
+ <% menu.with_group do |group| %>
10
+ <% group.with_item(href: edit_registration_path) do %>
11
+ <%= poetry_icon(name: :settings) %><%= t(".settings") %>
12
+ <% end %>
13
+ <% if Jumpstart.config.payments_enabled? && Current.account_billing? %>
14
+ <% group.with_item(href: billing_path) do %>
15
+ <%= poetry_icon(name: :"credit-card") %><%= t(".billing") %>
16
+ <% end %>
17
+ <% end %>
18
+ <% end %>
19
+
20
+ <% if current_user.staff? %>
21
+ <% menu.with_separator %>
22
+ <% menu.with_item(href: madmin_root_path, external: true, data: { turbo: false }) do %>
23
+ <%= poetry_icon(name: :"shield-check") %><%= t(".admin") %>
24
+ <% end %>
25
+ <% end %>
26
+
27
+ <% menu.with_separator %>
28
+ <%# The sign-out item is a submit button inside a display:contents form, and
29
+ a button sizes to its label: w-full stretches it, and its hover, across
30
+ the row like the link items above. %>
31
+ <% menu.with_item(submit: session_path, method: :delete, class: "w-full") do %>
32
+ <%= poetry_icon(name: :"log-out") %><%= t(".sign_out") %>
33
+ <% end %>
34
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <%# Poetry recreation of Jumpstart's users/_user, the mention chip rich text
2
+ renders for an attached user: avatar and name on the secondary token
3
+ surface. Plain token classes rather than a component, because Action Text
4
+ sanitizes the rendered attachment and would strip a component's data
5
+ attributes. %>
6
+ <span class="inline-flex items-center gap-1.5 rounded-md bg-secondary px-1.5 py-0.5 align-middle text-sm font-medium text-secondary-foreground">
7
+ <%= image_tag avatar_url_for(user), alt: "", height: 14, width: 14, class: "size-3.5 rounded-full object-cover" %>
8
+ <%= user.name %>
9
+ </span>
@@ -0,0 +1,33 @@
1
+ <%# Poetry recreation of Jumpstart's users/agreements/show: read a changed
2
+ agreement, then accept (PATCH) or decline (DELETE) it. The agreement text
3
+ comes from the app's own partial (users/agreements/_<id>), rendered as
4
+ prose inside a Typeset. %>
5
+ <% content_for :title, t(".title", agreement: @agreement.title) %>
6
+
7
+ <div class="mx-auto w-full max-w-3xl px-4 py-10 lg:px-6">
8
+ <header class="space-y-1">
9
+ <h1 class="text-2xl font-semibold tracking-tight"><%= t(".title", agreement: @agreement.title) %></h1>
10
+ <p class="text-sm text-muted-foreground"><%= t(".last_updated", date: l(@agreement.updated.to_date, format: :long)) %></p>
11
+ </header>
12
+
13
+ <div class="mt-8">
14
+ <%= poetry_typeset do %>
15
+ <%= render partial: @agreement %>
16
+ <% end %>
17
+ </div>
18
+
19
+ <%= poetry_separator(class: "my-10") %>
20
+
21
+ <div class="text-center">
22
+ <p class="text-sm text-muted-foreground"><%= t(".description", agreement: @agreement.title) %></p>
23
+
24
+ <div class="mt-4 flex flex-wrap items-center justify-center gap-2">
25
+ <%= form_with url: agreement_path(@agreement), method: :delete do %>
26
+ <%= poetry_button(type: :submit, variant: :outline, size: :lg) { t(".decline") } %>
27
+ <% end %>
28
+ <%= form_with url: agreement_path(@agreement), method: :patch do %>
29
+ <%= poetry_button(type: :submit, size: :lg) { t(".accept") } %>
30
+ <% end %>
31
+ </div>
32
+ </div>
33
+ </div>