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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +119 -0
- data/lib/generators/poetry_jumpstart_pro/install/files/poetry_jumpstart_pro_helper.rb +41 -0
- data/lib/generators/poetry_jumpstart_pro/install/install_generator.rb +196 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/account/passwords/edit.html.erb +46 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/account_invitations/show.html.erb +29 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/account_users/_form.html.erb +20 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/account_users/edit.html.erb +34 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/_form.html.erb +26 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/account_invitations/edit.html.erb +49 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/account_invitations/new.html.erb +37 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/edit.html.erb +51 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/index.html.erb +56 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/new.html.erb +18 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/show.html.erb +102 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/accounts/transfers/_form.html.erb +30 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/accounts/application/_account_navbar.html.erb +40 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/announcements/announcements/_announcement.html.erb +28 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/announcements/announcements/index.html.erb +50 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/announcements/announcements/show.html.erb +32 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/_form.html.erb +39 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/edit.html.erb +16 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/index.html.erb +48 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/new.html.erb +15 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/api_tokens/api_tokens/show.html.erb +69 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/application/_error_messages.html.erb +17 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/passwords/edit.html.erb +23 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/passwords/new.html.erb +26 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/registrations/edit.html.erb +103 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/registrations/new.html.erb +55 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/sessions/new.html.erb +39 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/sessions/otps/new.html.erb +24 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/shared/_oauth.html.erb +22 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/sudo/new.html.erb +31 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/two_factor/verify.html.erb +53 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/_charges.html.erb +45 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/_email.html.erb +15 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/_info.html.erb +30 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/show.html.erb +26 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/_plan.html.erb +33 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/_subscription.html.erb +74 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/_summary.html.erb +21 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/cancels/show.html.erb +46 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/edit.html.erb +65 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/paddle_billing/edit.html.erb +17 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/payment_methods/forms/_fake_processor.html.erb +6 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/payment_methods/new.html.erb +26 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/resumes/show.html.erb +29 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/billing/subscriptions/upcomings/show.html.erb +67 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/billing/pricing/show.html.erb +61 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/checkouts/checkouts/_testimonial.html.erb +15 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/checkouts/checkouts/show.html.erb +125 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/dashboard/dashboard/show.html.erb +60 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/errors/errors/404.html.erb +12 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/errors/errors/500.html.erb +12 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/errors/errors/show.html.erb +16 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/madmin/madmin/dashboard/show.html.erb +40 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/madmin/madmin/fields/array_field/_form.html.erb +29 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/madmin/madmin/fields/array_field/_index.html.erb +13 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/madmin/madmin/fields/array_field/_show.html.erb +13 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/notifications/notifications/_notification.html.erb +12 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/notifications/notifications/index.html.erb +31 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/notifications/notifications/nav.html.erb +17 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/public/public/about.html.erb +32 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/public/public/index.html.erb +71 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/public/public/privacy.html.erb +37 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/public/public/terms.html.erb +37 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_account_menu.html.erb +41 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_dev_menu.html.erb +13 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_flash.html.erb +37 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_footer.html.erb +18 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_left_nav.html.erb +11 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_navbar.html+native.erb +36 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_navbar.html.erb +32 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_notifications.html.erb +26 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_right_nav.html.erb +6 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/shell/application/_user_menu.html.erb +34 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/_user.html.erb +9 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/agreements/show.html.erb +33 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/connected_accounts/_connected_account.html.erb +25 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/connected_accounts/index.html.erb +34 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/referrals/index.html.erb +49 -0
- data/lib/generators/poetry_jumpstart_pro/install/templates/users/users/two_factor/backup_codes.html.erb +32 -0
- data/lib/poetry/jumpstart_pro/css_scanner.rb +121 -0
- data/lib/poetry/jumpstart_pro/engine.rb +15 -0
- data/lib/poetry/jumpstart_pro/stylesheets.rb +155 -0
- data/lib/poetry/jumpstart_pro/test_edits.rb +51 -0
- data/lib/poetry/jumpstart_pro/version.rb +8 -0
- data/lib/poetry/jumpstart_pro.rb +43 -0
- metadata +194 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's users/passwords/edit: choose a new
|
|
2
|
+
password from the emailed reset link. %>
|
|
3
|
+
<% content_for :title, t("users.passwords.edit.title") %>
|
|
4
|
+
|
|
5
|
+
<div class="mx-auto w-full max-w-md px-4 py-10 lg:px-6">
|
|
6
|
+
<header class="mb-8 space-y-2 text-center">
|
|
7
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= t("users.passwords.edit.change_your_password") %></h1>
|
|
8
|
+
</header>
|
|
9
|
+
|
|
10
|
+
<%= form_with model: @user, url: password_path(params[:token]), builder: Poetry::Ui::FormBuilder do |f| %>
|
|
11
|
+
<%= render "application/error_messages", resource: @user %>
|
|
12
|
+
|
|
13
|
+
<%= f.group do %>
|
|
14
|
+
<%= f.password_field :password, label: t("users.passwords.edit.new_password"), autofocus: true, autocomplete: "new-password",
|
|
15
|
+
hint: t("users.shared.minimum_password_length", count: User::MINIMUM_PASSWORD_LENGTH) %>
|
|
16
|
+
<%= f.password_field :password_confirmation, label: t("users.passwords.edit.confirm_new_password"), autocomplete: "new-password" %>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<div class="mt-6">
|
|
20
|
+
<%= f.submit t("users.passwords.edit.change_my_password"), class: "w-full" %>
|
|
21
|
+
</div>
|
|
22
|
+
<% end %>
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's users/passwords/new: request a password
|
|
2
|
+
reset link. Posts user[email] to passwords_path, as the original does. %>
|
|
3
|
+
<% content_for :title, t("users.passwords.new.title") %>
|
|
4
|
+
|
|
5
|
+
<div class="mx-auto w-full max-w-md px-4 py-10 lg:px-6">
|
|
6
|
+
<header class="mb-8 space-y-2 text-center">
|
|
7
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= t("users.passwords.new.forgot_your_password") %></h1>
|
|
8
|
+
</header>
|
|
9
|
+
|
|
10
|
+
<%= form_with url: passwords_path, scope: :user do %>
|
|
11
|
+
<%= poetry_field_group do %>
|
|
12
|
+
<%= poetry_field(id: "user_email", label_text: User.human_attribute_name(:email)) do |field| %>
|
|
13
|
+
<%= poetry_input(type: "email", name: "user[email]", autofocus: true, autocomplete: "email", placeholder: "you@example.com",
|
|
14
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
15
|
+
<% end %>
|
|
16
|
+
<% end %>
|
|
17
|
+
|
|
18
|
+
<div class="mt-6">
|
|
19
|
+
<%= poetry_button(type: :submit, class: "w-full") { t("users.passwords.new.send_me_reset_password_instructions") } %>
|
|
20
|
+
</div>
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
<p class="mt-4 text-center text-sm">
|
|
24
|
+
<%= poetry_link(href: login_path, class: "text-muted-foreground") { t("back") } %>
|
|
25
|
+
</p>
|
|
26
|
+
</div>
|
data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/registrations/edit.html.erb
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's users/registrations/edit: the profile
|
|
2
|
+
page inside the settings sidebar. Three forms, as in the original: the
|
|
3
|
+
profile (with the Hotwire Native bridge form wiring), the email change
|
|
4
|
+
request, and the guarded account deletion. %>
|
|
5
|
+
<% content_for :title, t("users.registrations.edit.title") %>
|
|
6
|
+
|
|
7
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= t("users.registrations.edit.title", resource: @user.model_name.human) %></h1>
|
|
8
|
+
|
|
9
|
+
<%= form_with(model: @user, url: registration_path, builder: Poetry::Ui::FormBuilder, class: "mt-6 max-w-md",
|
|
10
|
+
data: { controller: "bridge--form", action: "turbo:submit-start->bridge--form#submitStart turbo:submit-end->bridge--form#submitEnd" }) do |form| %>
|
|
11
|
+
<%= render "application/error_messages", resource: @user %>
|
|
12
|
+
|
|
13
|
+
<%= form.group do %>
|
|
14
|
+
<%# The builder's file_input makes the form multipart, and its block seats
|
|
15
|
+
the current avatar beside the control. %>
|
|
16
|
+
<%= form.file_input :avatar, accept: "image/png, image/jpeg, image/heic, image/heif, image/webp" do %>
|
|
17
|
+
<%= image_tag avatar_url_for(form.object), alt: "", class: "mt-3 size-16 shrink-0 rounded-full object-cover" %>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<%= form.text_field :name, autofocus: true, autocomplete: "name", required: true %>
|
|
21
|
+
|
|
22
|
+
<% if I18n.available_locales.length > 1 %>
|
|
23
|
+
<%= form.native_select :preferred_language, I18n.available_locales.map { |locale| [locale.to_s, locale.to_s] } %>
|
|
24
|
+
<% end %>
|
|
25
|
+
|
|
26
|
+
<%= form.native_select :theme, theme_options %>
|
|
27
|
+
<% end %>
|
|
28
|
+
|
|
29
|
+
<div class="mt-6">
|
|
30
|
+
<%= form.submit t("users.registrations.edit.update"),
|
|
31
|
+
data: { "bridge--form-target": "submit", "bridge-title": t("users.registrations.edit.update"),
|
|
32
|
+
"turbo-submits-with": t("users.registrations.edit.saving") } %>
|
|
33
|
+
</div>
|
|
34
|
+
<% end %>
|
|
35
|
+
|
|
36
|
+
<%= poetry_separator(class: "my-10") %>
|
|
37
|
+
|
|
38
|
+
<section class="max-w-md space-y-4" aria-labelledby="email-change-title">
|
|
39
|
+
<div class="space-y-1">
|
|
40
|
+
<h2 id="email-change-title" class="text-lg font-semibold"><%= t("users.email_changes.title") %></h2>
|
|
41
|
+
<p class="text-sm text-muted-foreground"><%= t("users.email_changes.description_html", email: tag.strong(@user.email)) %></p>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<%= form_with(model: @user, url: email_change_path, method: :post, builder: Poetry::Ui::FormBuilder) do |form| %>
|
|
45
|
+
<%= form.group do %>
|
|
46
|
+
<%= form.input :unconfirmed_email, as: :email, required: true, autocomplete: "email" %>
|
|
47
|
+
<% end %>
|
|
48
|
+
|
|
49
|
+
<div class="mt-4">
|
|
50
|
+
<%= form.submit t("users.email_changes.title"), variant: :outline %>
|
|
51
|
+
</div>
|
|
52
|
+
<% end %>
|
|
53
|
+
|
|
54
|
+
<% if @user.unconfirmed_email? %>
|
|
55
|
+
<%# Outside the form above, since cancelling is a form of its own. %>
|
|
56
|
+
<%= poetry_alert do |pending| %>
|
|
57
|
+
<% pending.with_icon(name: :mail) %>
|
|
58
|
+
<% pending.with_action do %>
|
|
59
|
+
<%= form_with url: email_change_path, method: :delete do %>
|
|
60
|
+
<%= poetry_button(type: :submit, variant: :outline, size: :sm) { t("cancel") } %>
|
|
61
|
+
<% end %>
|
|
62
|
+
<% end %>
|
|
63
|
+
<p><%= t("users.email_changes.pending_html", email: tag.strong(@user.unconfirmed_email)) %></p>
|
|
64
|
+
<% end %>
|
|
65
|
+
<% end %>
|
|
66
|
+
</section>
|
|
67
|
+
|
|
68
|
+
<%= poetry_separator(class: "my-10") %>
|
|
69
|
+
|
|
70
|
+
<section class="max-w-md space-y-4 rounded-lg border border-destructive/50 p-6" aria-labelledby="cancel-account-title">
|
|
71
|
+
<div class="space-y-1">
|
|
72
|
+
<h2 id="cancel-account-title" class="text-lg font-semibold"><%= t("users.registrations.edit.cancel_my_account") %></h2>
|
|
73
|
+
<p class="text-sm text-muted-foreground"><%= t("users.registrations.edit.cancel_my_account_description") %></p>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<%# A typed confirmation: the user types their email before deleting. The
|
|
77
|
+
dialog holds the DELETE form; its input demands the exact address
|
|
78
|
+
(pattern + required, enforced by the browser, no script) and the
|
|
79
|
+
destructive footer button submits the form. The bridge--sign-out
|
|
80
|
+
controller takes over the click inside the native apps. %>
|
|
81
|
+
<%= poetry_dialog do |dialog| %>
|
|
82
|
+
<% dialog.with_trigger(variant: :destructive) do %>
|
|
83
|
+
<%= poetry_icon(name: :trash) %>
|
|
84
|
+
<%= t("users.registrations.edit.cancel_my_account") %>
|
|
85
|
+
<% end %>
|
|
86
|
+
<% dialog.with_title { t("are_you_sure") } %>
|
|
87
|
+
<% dialog.with_description { t("users.registrations.edit.cancel_my_account_confirm_description_html") } %>
|
|
88
|
+
<%= form_with url: registration_path, method: :delete, id: "cancel-account-form" do %>
|
|
89
|
+
<%= poetry_field(id: "cancel-account-confirmation",
|
|
90
|
+
label_text: t("users.registrations.edit.cancel_my_account_confirm_label", default: "Type %{email} to confirm", email: current_user.email)) do |field| %>
|
|
91
|
+
<%= poetry_input(name: "confirmation", autocomplete: "off", pattern: Regexp.escape(current_user.email), required: true,
|
|
92
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
93
|
+
<% end %>
|
|
94
|
+
<% end %>
|
|
95
|
+
<% dialog.with_footer do %>
|
|
96
|
+
<%= poetry_button(type: :submit, form: "cancel-account-form", variant: :destructive,
|
|
97
|
+
data: { controller: "bridge--sign-out", action: "bridge--sign-out#signOut",
|
|
98
|
+
"bridge--sign-out-path-value": api_v1_me_path }) do %>
|
|
99
|
+
<%= t("users.registrations.edit.cancel_my_account") %>
|
|
100
|
+
<% end %>
|
|
101
|
+
<% end %>
|
|
102
|
+
<% end %>
|
|
103
|
+
</section>
|
data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/registrations/new.html.erb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's users/registrations/new: sign up. The
|
|
2
|
+
same params (user[name|email|password|terms_of_service]), the spam
|
|
3
|
+
protection fields, and the invitation notice when arriving from an
|
|
4
|
+
invite. %>
|
|
5
|
+
<% content_for :title, t("users.registrations.new.title") %>
|
|
6
|
+
|
|
7
|
+
<div class="mx-auto w-full max-w-md px-4 py-10 lg:px-6">
|
|
8
|
+
<header class="mb-8 space-y-2 text-center">
|
|
9
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= t("users.registrations.new.sign_up") %></h1>
|
|
10
|
+
<p class="text-sm text-muted-foreground">
|
|
11
|
+
<%= t("users.registrations.new.login_html", link: poetry_link(href: login_path, underline: :always) { t("users.registrations.new.login") }) %>
|
|
12
|
+
</p>
|
|
13
|
+
</header>
|
|
14
|
+
|
|
15
|
+
<% if @account_invitation %>
|
|
16
|
+
<%= poetry_alert(class: "mb-6") do |invitation| %>
|
|
17
|
+
<% invitation.with_icon(name: :"user-plus") %>
|
|
18
|
+
<% invitation.with_title do %>
|
|
19
|
+
<%= t("users.registrations.new.invited_you_to_join_html", inviter: @account_invitation.invited_by.name, account: @account_invitation.account.name) %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
22
|
+
<% end %>
|
|
23
|
+
|
|
24
|
+
<%= form_with model: @user, url: registration_path(invite: params[:invite]), builder: Poetry::Ui::FormBuilder do |f| %>
|
|
25
|
+
<%= render "application/error_messages", resource: @user %>
|
|
26
|
+
|
|
27
|
+
<%# Extra fields for creating an Account as part of registration belong
|
|
28
|
+
here, through f.fields_for :owned_accounts. %>
|
|
29
|
+
|
|
30
|
+
<%= f.group do %>
|
|
31
|
+
<%= f.text_field :name, autocomplete: "name" %>
|
|
32
|
+
<%= f.email_field :email, autocomplete: "email" %>
|
|
33
|
+
<%= f.password_field :password, autocomplete: "new-password",
|
|
34
|
+
hint: t("users.shared.minimum_password_length", count: User::MINIMUM_PASSWORD_LENGTH) %>
|
|
35
|
+
<% end %>
|
|
36
|
+
|
|
37
|
+
<%= spam_protection_fields %>
|
|
38
|
+
|
|
39
|
+
<%# The terms label carries two links: the translated _html string stays
|
|
40
|
+
html-safe through the builder's label:, so the Field renders the markup. %>
|
|
41
|
+
<div class="mt-6">
|
|
42
|
+
<%= f.check_box :terms_of_service,
|
|
43
|
+
label: t("users.registrations.new.terms_html",
|
|
44
|
+
terms: poetry_link(href: terms_path, external: true, underline: :always) { t("users.registrations.new.terms_of_service") },
|
|
45
|
+
privacy: poetry_link(href: privacy_path, external: true, underline: :always) { t("users.registrations.new.privacy_policy") }) %>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="mt-6">
|
|
49
|
+
<%= f.submit t("users.registrations.new.sign_up"), class: "w-full",
|
|
50
|
+
data: { "turbo-submits-with": t("users.registrations.new.submitting") } %>
|
|
51
|
+
</div>
|
|
52
|
+
<% end %>
|
|
53
|
+
|
|
54
|
+
<%= render "users/shared/oauth" %>
|
|
55
|
+
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's users/sessions/new: the log in form. It
|
|
2
|
+
posts user[email] and user[password] to session_path, as the original
|
|
3
|
+
does; the fields are composed from Field + Input by hand because the form
|
|
4
|
+
has no model behind it. %>
|
|
5
|
+
<% content_for :title, t("users.sessions.new.title") %>
|
|
6
|
+
|
|
7
|
+
<div class="mx-auto w-full max-w-md px-4 py-10 lg:px-6">
|
|
8
|
+
<header class="mb-8 space-y-2 text-center">
|
|
9
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= t("users.sessions.new.sign_in") %></h1>
|
|
10
|
+
<p class="text-sm text-muted-foreground">
|
|
11
|
+
<%= t("users.sessions.new.sign_up_html", link: poetry_link(href: signup_path, underline: :always) { t("users.sessions.new.sign_up") }) %>
|
|
12
|
+
</p>
|
|
13
|
+
</header>
|
|
14
|
+
|
|
15
|
+
<%= form_with url: session_path, scope: :user do %>
|
|
16
|
+
<%= poetry_field_group do %>
|
|
17
|
+
<%= poetry_field(id: "user_email", label_text: User.human_attribute_name(:email)) do |field| %>
|
|
18
|
+
<%= poetry_input(type: "email", name: "user[email]", value: params.dig(:user, :email),
|
|
19
|
+
autofocus: true, autocomplete: "email",
|
|
20
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
<%= poetry_field(id: "user_password", label_text: User.human_attribute_name(:password)) do |field| %>
|
|
24
|
+
<%= poetry_input(type: "password", name: "user[password]", autocomplete: "current-password",
|
|
25
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% end %>
|
|
28
|
+
|
|
29
|
+
<div class="mt-6">
|
|
30
|
+
<%= poetry_button(type: :submit, name: "commit", value: t("users.sessions.new.sign_in"), class: "w-full") { t("users.sessions.new.sign_in") } %>
|
|
31
|
+
</div>
|
|
32
|
+
<% end %>
|
|
33
|
+
|
|
34
|
+
<p class="mt-4 text-center text-sm">
|
|
35
|
+
<%= poetry_link(href: new_password_path, class: "text-muted-foreground") { t("users.shared.forgot_your_password") } %>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<%= render "users/shared/oauth" %>
|
|
39
|
+
</div>
|
data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/sessions/otps/new.html.erb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's users/sessions/otps/new: the two-factor
|
|
2
|
+
challenge after a successful password check. The field is a plain Input
|
|
3
|
+
on purpose: it accepts a six-digit app code or a ten-character backup
|
|
4
|
+
code, so a fixed-length code control would lock recovery out. %>
|
|
5
|
+
<% content_for :title, t("users.two_factor.title") %>
|
|
6
|
+
|
|
7
|
+
<div class="mx-auto w-full max-w-md px-4 py-10 lg:px-6">
|
|
8
|
+
<header class="mb-8 space-y-2 text-center">
|
|
9
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= t("users.two_factor.title") %></h1>
|
|
10
|
+
</header>
|
|
11
|
+
|
|
12
|
+
<%= form_with url: session_otp_path do %>
|
|
13
|
+
<%= poetry_field_group do %>
|
|
14
|
+
<%= poetry_field(id: "otp_attempt", label_text: t("users.two_factor.verification_code"), required: true) do |field| %>
|
|
15
|
+
<%= poetry_input(name: "otp_attempt", autocomplete: "one-time-code", inputmode: "numeric", autofocus: true,
|
|
16
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
17
|
+
<% end %>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<div class="mt-6">
|
|
21
|
+
<%= poetry_button(type: :submit, name: "commit", value: t("users.two_factor.submit"), class: "w-full") { t("users.two_factor.submit") } %>
|
|
22
|
+
</div>
|
|
23
|
+
<% end %>
|
|
24
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's users/shared/_oauth: the "or" divider
|
|
2
|
+
and one sign-in button per enabled OAuth provider under the log in and
|
|
3
|
+
sign up forms. Each form is a full-page submit (data-turbo="false"): the
|
|
4
|
+
OmniAuth request phase is a POST the middleware answers with a redirect. %>
|
|
5
|
+
<% if Jumpstart::Omniauth.enabled_providers.any? %>
|
|
6
|
+
<%= poetry_field_separator(class: "my-6") { t("users.shared.or") } %>
|
|
7
|
+
|
|
8
|
+
<div class="grid gap-2">
|
|
9
|
+
<% Jumpstart::Omniauth.enabled_providers.each_key do |provider| %>
|
|
10
|
+
<%# Google refuses OAuth inside a web view, so the native app's
|
|
11
|
+
google-sign-in bridge component takes this button over and posts a
|
|
12
|
+
native ID token instead. The bridge controller only loads in an app
|
|
13
|
+
that supports the component, so the attributes are inert on the web. %>
|
|
14
|
+
<% bridge = { controller: "bridge--google-sign-in", action: "bridge--google-sign-in#signIn" } if provider == :google_oauth2 && Jumpstart.config.native_sign_in? %>
|
|
15
|
+
<%= form_with url: omniauth_authorize_path(provider), method: :post, data: { turbo: false } do %>
|
|
16
|
+
<%= poetry_button(type: :submit, variant: :outline, class: "w-full", data: bridge.to_h) do %>
|
|
17
|
+
<%= t("oauth.sign_in_with", provider: t("oauth.#{provider}")) %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
</div>
|
|
22
|
+
<% end %>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's users/sudo/new: confirm the password
|
|
2
|
+
before a sensitive action. Rendered by the Users::Sudo concern from any
|
|
3
|
+
protected action and after a wrong password, so the redirect_to round
|
|
4
|
+
trip stays. %>
|
|
5
|
+
<% content_for :title, t("users.sudo.title") %>
|
|
6
|
+
|
|
7
|
+
<div class="mx-auto w-full max-w-md px-4 py-10 lg:px-6">
|
|
8
|
+
<header class="mb-8 space-y-2 text-center">
|
|
9
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= t("users.sudo.title") %></h1>
|
|
10
|
+
</header>
|
|
11
|
+
|
|
12
|
+
<%= poetry_card do %>
|
|
13
|
+
<%= form_with url: sudo_path do %>
|
|
14
|
+
<%= hidden_field_tag :redirect_to, params[:redirect_to] || request.url %>
|
|
15
|
+
|
|
16
|
+
<%= poetry_field_group do %>
|
|
17
|
+
<%= poetry_field(id: "password", label_text: User.human_attribute_name(:password)) do |field| %>
|
|
18
|
+
<% field.with_hint do %>
|
|
19
|
+
<%= poetry_link(href: new_password_path) { t("users.sudo.forgot_password") } %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<%= poetry_input(type: "password", name: "password", autocomplete: "current-password", autofocus: true,
|
|
22
|
+
**field.control_attributes.transform_keys(&:to_sym)) %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
25
|
+
|
|
26
|
+
<div class="mt-6">
|
|
27
|
+
<%= poetry_button(type: :submit, class: "w-full") { t("users.sudo.submit") } %>
|
|
28
|
+
</div>
|
|
29
|
+
<% end %>
|
|
30
|
+
<% end %>
|
|
31
|
+
</div>
|
data/lib/generators/poetry_jumpstart_pro/install/templates/auth/users/two_factor/verify.html.erb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's users/two_factor/verify: turning on
|
|
2
|
+
two-factor authentication. Scan the QR code (or copy the key), then
|
|
3
|
+
confirm with a six-digit code from the app; posts `code` to
|
|
4
|
+
user_two_factor_path, as the original does. %>
|
|
5
|
+
<% content_for :title, t(".title") %>
|
|
6
|
+
|
|
7
|
+
<div class="mx-auto w-full max-w-2xl px-4 py-10 lg:px-6">
|
|
8
|
+
<header class="space-y-1">
|
|
9
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= t(".title") %></h1>
|
|
10
|
+
<p class="text-sm text-muted-foreground"><%= t(".description") %></p>
|
|
11
|
+
</header>
|
|
12
|
+
|
|
13
|
+
<ol class="mt-6 list-inside list-decimal space-y-1 text-sm">
|
|
14
|
+
<li>
|
|
15
|
+
<%= t(".instructions_1_html",
|
|
16
|
+
android: poetry_link(href: "https://play.google.com/store/apps/details?id=com.authy.authy", external: true, underline: :always) { "Android" },
|
|
17
|
+
ios: poetry_link(href: "https://apps.apple.com/us/app/authy/id494168017", external: true, underline: :always) { "iOS" }) %>
|
|
18
|
+
</li>
|
|
19
|
+
<li><%= t(".instructions_2") %></li>
|
|
20
|
+
<li><%= t(".instructions_3") %></li>
|
|
21
|
+
</ol>
|
|
22
|
+
|
|
23
|
+
<%# The QR code sits on a white tile in both themes: scanners want dark
|
|
24
|
+
modules on a light ground, so this one surface does not follow the
|
|
25
|
+
theme tokens. %>
|
|
26
|
+
<div class="mx-auto my-8 w-48 max-w-full rounded-lg bg-white p-3 shadow-xs">
|
|
27
|
+
<%= current_user.two_factor_otp_qr_code(viewbox: true) %>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="space-y-2">
|
|
31
|
+
<p class="text-sm text-muted-foreground"><%= t(".instructions_4") %></p>
|
|
32
|
+
<%= poetry_clipboard_text(value: current_user.otp_app_code, label: t(".instructions_4")) %>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<%= poetry_separator(class: "my-8") %>
|
|
36
|
+
|
|
37
|
+
<h2 class="text-lg font-semibold"><%= t(".verify_header") %></h2>
|
|
38
|
+
<p class="mt-1 text-sm text-muted-foreground"><%= t(".verify_description") %></p>
|
|
39
|
+
|
|
40
|
+
<%= form_with url: user_two_factor_path, class: "mt-6" do %>
|
|
41
|
+
<%= poetry_field_group do %>
|
|
42
|
+
<%= poetry_field(id: "code", label_text: t("users.two_factor.verification_code"), required: true) do |field| %>
|
|
43
|
+
<%= poetry_input_otp(name: "code", length: 6, groups: [3, 3], required: true,
|
|
44
|
+
**field.control_attributes.slice("id", "aria-describedby").transform_keys(&:to_sym)) %>
|
|
45
|
+
<% end %>
|
|
46
|
+
<% end %>
|
|
47
|
+
|
|
48
|
+
<div class="mt-6 flex flex-wrap items-center justify-between gap-3">
|
|
49
|
+
<%= poetry_button(variant: :outline, href: edit_account_password_path) { t("cancel") } %>
|
|
50
|
+
<%= poetry_button(type: :submit) { t(".verify") } %>
|
|
51
|
+
</div>
|
|
52
|
+
<% end %>
|
|
53
|
+
</div>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's billing/_charges: payment history with
|
|
2
|
+
the invoice and receipt PDF links per charge; billing/show passes the
|
|
3
|
+
charges in. %>
|
|
4
|
+
<%# locals: (charges:) -%>
|
|
5
|
+
<%= poetry_card(title_tag: :h2) do |card| %>
|
|
6
|
+
<% card.with_title { t(".title") } %>
|
|
7
|
+
|
|
8
|
+
<% if charges.any? %>
|
|
9
|
+
<div class="overflow-hidden rounded-lg border">
|
|
10
|
+
<%= poetry_table do %>
|
|
11
|
+
<%= poetry_table_caption(class: "sr-only") { t(".title") } %>
|
|
12
|
+
<%= poetry_table_header do %>
|
|
13
|
+
<%= poetry_table_row do %>
|
|
14
|
+
<%= poetry_table_head { t(".date") } %>
|
|
15
|
+
<%= poetry_table_head { t(".amount") } %>
|
|
16
|
+
<%= poetry_table_head { t(".invoice") } %>
|
|
17
|
+
<%= poetry_table_head { t(".receipt") } %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<%= poetry_table_body do %>
|
|
21
|
+
<% charges.each do |charge| %>
|
|
22
|
+
<%= poetry_table_row(id: dom_id(charge)) do %>
|
|
23
|
+
<%= poetry_table_cell(class: "font-medium") { charge.created_at.strftime("%B %e, %Y") } %>
|
|
24
|
+
<%= poetry_table_cell(class: "tabular-nums") { pay_amount_to_currency(charge) } %>
|
|
25
|
+
<%= poetry_table_cell do %>
|
|
26
|
+
<%= poetry_link(href: invoice_billing_charge_path(charge, format: :pdf), external: true, class: "inline-flex items-center gap-1 font-medium") do %>
|
|
27
|
+
<%= poetry_icon(name: :"file-text", class: "size-4") %>
|
|
28
|
+
<%= t(".view") %><span class="sr-only"> <%= t(".invoice") %></span>
|
|
29
|
+
<% end %>
|
|
30
|
+
<% end %>
|
|
31
|
+
<%= poetry_table_cell do %>
|
|
32
|
+
<%= poetry_link(href: billing_charge_path(charge, format: :pdf), external: true, class: "inline-flex items-center gap-1 font-medium") do %>
|
|
33
|
+
<%= poetry_icon(name: :receipt, class: "size-4") %>
|
|
34
|
+
<%= t(".view") %><span class="sr-only"> <%= t(".receipt") %></span>
|
|
35
|
+
<% end %>
|
|
36
|
+
<% end %>
|
|
37
|
+
<% end %>
|
|
38
|
+
<% end %>
|
|
39
|
+
<% end %>
|
|
40
|
+
<% end %>
|
|
41
|
+
</div>
|
|
42
|
+
<% else %>
|
|
43
|
+
<p class="text-sm text-muted-foreground"><%= t(".empty") %></p>
|
|
44
|
+
<% end %>
|
|
45
|
+
<% end %>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's billing/_email: the billing email form
|
|
2
|
+
(PATCH /billing, account[billing_email]) as a section card. %>
|
|
3
|
+
<%= poetry_card(title_tag: :h2) do |card| %>
|
|
4
|
+
<% card.with_title { t(".billing_email") } %>
|
|
5
|
+
<% card.with_description { t(".description") } %>
|
|
6
|
+
|
|
7
|
+
<%= form_with model: current_account, url: billing_path, builder: Poetry::Ui::FormBuilder do |f| %>
|
|
8
|
+
<div class="flex flex-col gap-4 sm:flex-row sm:items-end">
|
|
9
|
+
<div class="w-full sm:max-w-80">
|
|
10
|
+
<%= f.field :billing_email, type: "email", label: t(".email_address", default: "Email address"), autocomplete: "email" %>
|
|
11
|
+
</div>
|
|
12
|
+
<%= f.submit t(".save"), variant: :outline, data: { turbo_submits_with: t(".saving") } %>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% end %>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's billing/_info: extra billing information
|
|
2
|
+
(PATCH /billing, account[extra_billing_info]) behind a disclosure, with the
|
|
3
|
+
saved text shown beneath. The collapsible replaces the toggle controller. %>
|
|
4
|
+
<%= poetry_card(title_tag: :h2) do |card| %>
|
|
5
|
+
<% card.with_title { t(".extra_billing_info") } %>
|
|
6
|
+
<% card.with_description { t(".additional_information") } %>
|
|
7
|
+
|
|
8
|
+
<div class="flex flex-col gap-4">
|
|
9
|
+
<% if current_account.extra_billing_info? %>
|
|
10
|
+
<blockquote class="whitespace-pre-line border-l-4 border-border py-1 pl-4 font-mono text-sm text-muted-foreground"><%= current_account.extra_billing_info %></blockquote>
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
<%= poetry_collapsible do |collapsible| %>
|
|
14
|
+
<% collapsible.with_trigger(compose: true) do |wiring| %>
|
|
15
|
+
<%= poetry_button(variant: :outline, **wiring) { t(".add_info") } %>
|
|
16
|
+
<% end %>
|
|
17
|
+
|
|
18
|
+
<div class="max-w-lg pt-4">
|
|
19
|
+
<%= form_with model: current_account, url: billing_path, builder: Poetry::Ui::FormBuilder do |f| %>
|
|
20
|
+
<div class="flex flex-col gap-4">
|
|
21
|
+
<%= f.field :extra_billing_info, as: :textarea, rows: 4, label: t(".information", default: "Information") %>
|
|
22
|
+
<div>
|
|
23
|
+
<%= f.submit t(".save_info"), variant: :outline, data: { turbo_submits_with: t(".saving") } %>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<% end %>
|
|
27
|
+
</div>
|
|
28
|
+
<% end %>
|
|
29
|
+
</div>
|
|
30
|
+
<% end %>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's billing/show: the account's current
|
|
2
|
+
plans, billing email, extra billing information and payment history.
|
|
3
|
+
Renders inside the sidebar layout column, which owns the container. %>
|
|
4
|
+
<% content_for :title, t(".title") %>
|
|
5
|
+
|
|
6
|
+
<div class="flex flex-col gap-8">
|
|
7
|
+
<h1 class="text-2xl font-semibold tracking-tight"><%= t(".title") %></h1>
|
|
8
|
+
|
|
9
|
+
<section class="flex flex-col gap-4" aria-labelledby="billing-subscriptions-title">
|
|
10
|
+
<h2 id="billing-subscriptions-title" class="text-lg font-semibold tracking-tight"><%= t(".subscriptions") %></h2>
|
|
11
|
+
|
|
12
|
+
<% if @subscriptions.any? %>
|
|
13
|
+
<%= render partial: "billing/subscriptions/subscription", collection: @subscriptions %>
|
|
14
|
+
<% else %>
|
|
15
|
+
<%= poetry_empty(media_variant: :icon, title_tag: :h3, class: "rounded-lg border py-10") do |empty| %>
|
|
16
|
+
<% empty.with_media { poetry_icon(name: :"credit-card") } %>
|
|
17
|
+
<% empty.with_title { t(".not_subscribed") } %>
|
|
18
|
+
<%= poetry_button(href: pricing_path) { t(".choose_plan") } %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<%= render partial: "billing/email" %>
|
|
24
|
+
<%= render partial: "billing/info" %>
|
|
25
|
+
<%= render "billing/charges", charges: @charges %>
|
|
26
|
+
</div>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's billing/subscriptions/_plan: one plan
|
|
2
|
+
card. Rendered as a layout partial - the caller's block (yield) is the
|
|
3
|
+
plan's action, evaluated here so its lazy t(".…") keys resolve under
|
|
4
|
+
billing.subscriptions.plan, exactly as the engine's callers rely on. %>
|
|
5
|
+
<%= poetry_card(key: plan, title_tag: :h2, class: "h-full", content_class: "flex flex-1 flex-col gap-6") do |card| %>
|
|
6
|
+
<% card.with_title { plan.name } %>
|
|
7
|
+
<% card.with_description { plan.description } if plan.description.present? %>
|
|
8
|
+
|
|
9
|
+
<p class="flex flex-wrap items-baseline gap-x-2">
|
|
10
|
+
<% if plan.contact_url? %>
|
|
11
|
+
<span class="text-4xl font-semibold tracking-tight"><%= t(".contact_us_price") %></span>
|
|
12
|
+
<% else %>
|
|
13
|
+
<span class="text-4xl font-semibold tracking-tight tabular-nums"><%= pay_amount_to_currency(plan, strip_insignificant_zeros: true) %></span>
|
|
14
|
+
<span class="text-sm font-medium text-muted-foreground">
|
|
15
|
+
<% if plan.unit_label? %>/ <%= plan.unit_label %> <% end %>/ <%= formatted_plan_interval(plan) %>
|
|
16
|
+
</span>
|
|
17
|
+
<% end %>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<%# The per-plan action supplied by the caller %>
|
|
21
|
+
<div><%= yield %></div>
|
|
22
|
+
|
|
23
|
+
<% if plan.features.any? %>
|
|
24
|
+
<ul class="flex flex-col gap-2 text-sm text-muted-foreground">
|
|
25
|
+
<% plan.features.each do |plan_feature| %>
|
|
26
|
+
<li class="flex items-start gap-2">
|
|
27
|
+
<%= poetry_icon(name: :check, class: "mt-0.5 size-4 shrink-0 text-primary") %>
|
|
28
|
+
<span><%= plan_feature %></span>
|
|
29
|
+
</li>
|
|
30
|
+
<% end %>
|
|
31
|
+
</ul>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% end %>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's billing/subscriptions/_subscription: one
|
|
2
|
+
subscription card - status badge, plan summary, trial/grace copy and the
|
|
3
|
+
state-driven actions. Same routes and conditions as the engine partial. %>
|
|
4
|
+
<%= tag.div id: dom_id(subscription) do %>
|
|
5
|
+
<%= poetry_card(key: subscription) do |card| %>
|
|
6
|
+
<% if subscription.generic_trial? %>
|
|
7
|
+
<div class="flex flex-col items-start gap-2">
|
|
8
|
+
<%= poetry_badge(variant: :info) { t(".free_trial") } %>
|
|
9
|
+
<p class="text-sm"><%= t(".free_trial_ends", date: l(subscription.trial_ends_at.to_date, format: :long)) %></p>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<% else %>
|
|
13
|
+
<div class="flex flex-col gap-6 lg:flex-row lg:items-start lg:justify-between">
|
|
14
|
+
<div class="flex flex-1 flex-col items-start gap-3">
|
|
15
|
+
<% if subscription.canceled? %>
|
|
16
|
+
<%= poetry_badge(variant: :outline) { t(".canceled") } %>
|
|
17
|
+
<% elsif subscription.incomplete? %>
|
|
18
|
+
<%= poetry_badge(variant: :outline) { t(".incomplete") } %>
|
|
19
|
+
<% elsif subscription.past_due? %>
|
|
20
|
+
<%= poetry_badge(variant: :warning) { t(".past_due") } %>
|
|
21
|
+
<% elsif subscription.unpaid? %>
|
|
22
|
+
<%= poetry_badge(variant: :warning) { t(".unpaid") } %>
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
<%= render partial: "billing/subscriptions/summary", locals: { subscription: subscription } %>
|
|
26
|
+
|
|
27
|
+
<% if subscription.metered? %>
|
|
28
|
+
<%= poetry_link(href: billing_subscription_upcoming_path(subscription), class: "text-sm font-medium") { t(".view_usage") } %>
|
|
29
|
+
<% elsif subscription.plan&.charge_per_unit? %>
|
|
30
|
+
<%= poetry_link(href: billing_subscription_upcoming_path(subscription), class: "text-sm font-medium") { t(".view_upcoming_invoice") } %>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<% if subscription.on_trial? && subscription.canceled? %>
|
|
34
|
+
<p class="text-sm text-muted-foreground"><%= t(".canceled_on_trial", date: l(subscription.trial_ends_at.to_date, format: :long)) %></p>
|
|
35
|
+
<% elsif subscription.on_trial? %>
|
|
36
|
+
<p class="text-sm text-muted-foreground"><%= t(".trial", date: l(subscription.trial_ends_at.to_date, format: :long)) %></p>
|
|
37
|
+
<% elsif subscription.on_grace_period? %>
|
|
38
|
+
<p class="flex items-center gap-2 text-sm text-muted-foreground">
|
|
39
|
+
<%= poetry_icon(name: :calendar, class: "size-4 shrink-0") %>
|
|
40
|
+
<% if subscription.ends_at? %>
|
|
41
|
+
<span><%= t(".ends_at", date: l(subscription.ends_at.to_date, format: :long)) %></span>
|
|
42
|
+
<% elsif subscription.pause_starts_at %>
|
|
43
|
+
<span><%= t(".paused_at", date: l(subscription.pause_starts_at.to_date, format: :long)) %></span>
|
|
44
|
+
<% end %>
|
|
45
|
+
</p>
|
|
46
|
+
<% elsif subscription.paused? %>
|
|
47
|
+
<p class="text-sm text-muted-foreground"><%= t(".paused") %></p>
|
|
48
|
+
<% end %>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div class="flex flex-col gap-2 lg:w-56">
|
|
52
|
+
<% if subscription.on_grace_period? %>
|
|
53
|
+
<%= poetry_button(href: billing_subscription_resume_path(subscription)) { t(".resume") } if subscription.resumable? %>
|
|
54
|
+
<% elsif subscription.active? %>
|
|
55
|
+
<%= poetry_button(variant: :outline, href: edit_billing_subscription_path(subscription)) { t(".change_plan") } %>
|
|
56
|
+
<%= poetry_button(variant: :outline, href: new_billing_subscription_payment_method_path(subscription)) { t(".update_payment_method") } %>
|
|
57
|
+
<%= poetry_button(variant: :outline, href: billing_subscription_cancel_path(subscription)) { t(".cancel_plan") } %>
|
|
58
|
+
<% elsif subscription.paused? %>
|
|
59
|
+
<%= poetry_button(href: billing_subscription_resume_path(subscription)) { t(".resume") } %>
|
|
60
|
+
<%= poetry_button(variant: :outline, href: billing_subscription_cancel_path(subscription)) { t(".cancel_plan") } %>
|
|
61
|
+
<% elsif subscription.incomplete? %>
|
|
62
|
+
<%= poetry_button(href: pay.payment_path(subscription.latest_payment.id)) { t(".confirm_payment") } %>
|
|
63
|
+
<% elsif subscription.past_due? %>
|
|
64
|
+
<%= poetry_button(href: new_billing_subscription_payment_method_path(subscription)) { t(".update_payment_method") } %>
|
|
65
|
+
<%= poetry_button(variant: :outline, href: billing_subscription_cancel_path(subscription)) { t(".cancel_plan") } %>
|
|
66
|
+
<% elsif subscription.unpaid? %>
|
|
67
|
+
<%= poetry_button(href: new_billing_subscription_payment_method_path(subscription)) { t(".update_payment_method") } %>
|
|
68
|
+
<%= poetry_button(variant: :outline, href: billing_subscription_cancel_path(subscription)) { t(".cancel_plan") } %>
|
|
69
|
+
<% end %>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
<% end %>
|
|
73
|
+
<% end %>
|
|
74
|
+
<% end %>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%# Poetry recreation of Jumpstart's billing/subscriptions/_summary: the plan
|
|
2
|
+
name, its price per interval, and a seat badge for per-unit or multi-seat
|
|
3
|
+
plans. heading_tag is an optional local (default :h3) so each page keeps a
|
|
4
|
+
sane outline. %>
|
|
5
|
+
<% heading_tag = local_assigns.fetch(:heading_tag, :h3) %>
|
|
6
|
+
<% plan = subscription.plan %>
|
|
7
|
+
<div class="flex flex-col items-start gap-1.5">
|
|
8
|
+
<%= tag.public_send(heading_tag, plan&.name.presence || subscription.name, class: "text-lg font-semibold tracking-tight") %>
|
|
9
|
+
<% if plan %>
|
|
10
|
+
<p class="flex items-baseline gap-1">
|
|
11
|
+
<span class="text-base font-medium tabular-nums"><%= pay_amount_to_currency(subscription) %></span>
|
|
12
|
+
<span class="text-sm text-muted-foreground">/ <%= formatted_plan_interval(plan) %></span>
|
|
13
|
+
</p>
|
|
14
|
+
<% if plan.charge_per_unit? || subscription.quantity > 1 %>
|
|
15
|
+
<% seat = plan.unit_label? ? t(".per_unit", unit: plan.unit_label) : t(".each") %>
|
|
16
|
+
<%= poetry_badge(variant: :outline, class: "tabular-nums") do %>
|
|
17
|
+
<%= t(".quantity", quantity: subscription.quantity, price: pay_amount_to_currency(plan), unit: seat) %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
</div>
|