WIP: Various front-end improvements and bugfixes #76
@ -8,6 +8,7 @@ COPY Gemfile /akkounts/Gemfile
|
||||
COPY Gemfile.lock /akkounts/Gemfile.lock
|
||||
COPY package.json /akkounts/package.json
|
||||
RUN bundle install
|
||||
RUN gem install foreman
|
||||
RUN npm install -g yarn
|
||||
RUN yarn install
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
.btn-md {
|
||||
@apply btn;
|
||||
@apply py-2.5 px-5 shadow-md;
|
||||
@apply py-3 px-6;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<main class="w-full max-w-6xl mx-auto pb-12 px-4 md:px-6 lg:px-8">
|
||||
<div class="bg-white rounded-lg shadow">
|
||||
<div class="divide-y divide-gray-200 lg:grid lg:grid-cols-12 lg:divide-y-0 lg:divide-x">
|
||||
<div class="md:min-h-[50vh] divide-y divide-gray-200 lg:grid lg:grid-cols-12 lg:divide-y-0 lg:divide-x">
|
||||
<aside class="py-6 sm:py-8 lg:col-span-3">
|
||||
<nav class="space-y-1">
|
||||
<%= render partial: @sidenav_partial %>
|
||||
|
10
app/components/main_with_tabnav_component.html.erb
Normal file
10
app/components/main_with_tabnav_component.html.erb
Normal file
@ -0,0 +1,10 @@
|
||||
<main class="w-full max-w-6xl mx-auto pb-12 px-4 md:px-6 lg:px-8">
|
||||
<div class="bg-white rounded-lg shadow">
|
||||
<div class="px-6 sm:px-12 pt-2 sm:pt-4">
|
||||
<%= render partial: @tabnav_partial %>
|
||||
</div>
|
||||
<div class="px-6 sm:px-12 py-8 sm:py-12">
|
||||
<%= content %>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
7
app/components/main_with_tabnav_component.rb
Normal file
7
app/components/main_with_tabnav_component.rb
Normal file
@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class MainWithTabnavComponent < ViewComponent::Base
|
||||
def initialize(tabnav_partial:)
|
||||
@tabnav_partial = tabnav_partial
|
||||
end
|
||||
end
|
3
app/components/tabnav_link_component.html.erb
Normal file
3
app/components/tabnav_link_component.html.erb
Normal file
@ -0,0 +1,3 @@
|
||||
<%= link_to @path, class: @link_class do %>
|
||||
<%= @name %>
|
||||
<% end %>
|
21
app/components/tabnav_link_component.rb
Normal file
21
app/components/tabnav_link_component.rb
Normal file
@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TabnavLinkComponent < ViewComponent::Base
|
||||
def initialize(name:, path:, active: false, disabled: false)
|
||||
@name = name
|
||||
@path = path
|
||||
@active = active
|
||||
@disabled = disabled
|
||||
@link_class = class_names_link(path)
|
||||
end
|
||||
|
||||
def class_names_link(path)
|
||||
if @active
|
||||
"border-indigo-500 text-indigo-600 w-1/2 py-4 px-1 text-center border-b-2"
|
||||
elsif @disabled
|
||||
"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2"
|
||||
else
|
||||
"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2"
|
||||
end
|
||||
end
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
class DonationsController < ApplicationController
|
||||
class Contributions::DonationsController < ApplicationController
|
||||
before_action :require_user_signed_in
|
||||
|
||||
# GET /donations
|
8
app/controllers/contributions/projects_controller.rb
Normal file
8
app/controllers/contributions/projects_controller.rb
Normal file
@ -0,0 +1,8 @@
|
||||
class Contributions::ProjectsController < ApplicationController
|
||||
before_action :require_user_signed_in
|
||||
|
||||
# GET /contributions
|
||||
def index
|
||||
@current_section = :contributions
|
||||
end
|
||||
end
|
6
app/controllers/settings/profile_controller.rb
Normal file
6
app/controllers/settings/profile_controller.rb
Normal file
@ -0,0 +1,6 @@
|
||||
class Settings::ProfileController < SettingsController
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
end
|
13
app/controllers/settings/security_controller.rb
Normal file
13
app/controllers/settings/security_controller.rb
Normal file
@ -0,0 +1,13 @@
|
||||
class Settings::SecurityController < SettingsController
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
def reset_password
|
||||
current_user.send_reset_password_instructions
|
||||
sign_out current_user
|
||||
msg = "We have sent you an email with a link to reset your password."
|
||||
redirect_to check_your_email_path, notice: msg
|
||||
end
|
||||
|
||||
end
|
@ -1,13 +1,13 @@
|
||||
class SettingsController < ApplicationController
|
||||
before_action :require_user_signed_in
|
||||
before_action :set_current_section
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
def reset_password
|
||||
current_user.send_reset_password_instructions
|
||||
sign_out current_user
|
||||
msg = "We have sent you an email with a link to reset your password."
|
||||
redirect_to check_your_email_path, notice: msg
|
||||
private
|
||||
|
||||
def set_current_section
|
||||
@current_section = :settings
|
||||
end
|
||||
end
|
||||
|
18
app/controllers/turbo_controller.rb
Normal file
18
app/controllers/turbo_controller.rb
Normal file
@ -0,0 +1,18 @@
|
||||
class TurboController < ApplicationController
|
||||
class Responder < ActionController::Responder
|
||||
def to_turbo_stream
|
||||
controller.render(options.merge(formats: :html))
|
||||
rescue ActionView::MissingTemplate => error
|
||||
if get?
|
||||
raise error
|
||||
elsif has_errors? && default_action
|
||||
render rendering_options.merge(formats: :html, status: :unprocessable_entity)
|
||||
else
|
||||
redirect_to navigation_location
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.responder = Responder
|
||||
respond_to :html, :turbo_stream
|
||||
end
|
18
app/controllers/users/devise_controller.rb
Normal file
18
app/controllers/users/devise_controller.rb
Normal file
@ -0,0 +1,18 @@
|
||||
class Users::DeviseController < ApplicationController
|
||||
class Responder < ActionController::Responder
|
||||
def to_turbo_stream
|
||||
controller.render(options.merge(formats: :html))
|
||||
rescue ActionView::MissingTemplate => error
|
||||
if get?
|
||||
raise error
|
||||
elsif has_errors? && default_action
|
||||
render rendering_options.merge(formats: :html, status: :unprocessable_entity)
|
||||
else
|
||||
redirect_to navigation_location
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.responder = Responder
|
||||
respond_to :html, :turbo_stream
|
||||
end
|
@ -17,6 +17,7 @@
|
||||
<tr>
|
||||
<th>Token</th>
|
||||
<th>Accepted</th>
|
||||
<th>Inviter</th>
|
||||
<th>Invited user</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -24,7 +25,8 @@
|
||||
<% @invitations_used.each do |invitation| %>
|
||||
<tr>
|
||||
<td class="overflow-ellipsis font-mono"><%= invitation.token %></td>
|
||||
<td><%= invitation.used_at.strftime("%Y-%m-%d") %></td>
|
||||
<td><%= invitation.used_at.strftime("%Y-%m-%d (%H:%M UTC)") %></td>
|
||||
<td><%= invitation.user.address %></td>
|
||||
<td><%= User.find(invitation.invited_user_id).address %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<%= render HeaderComponent.new(title: "Donations") %>
|
||||
<%= render HeaderComponent.new(title: "Contributions") %>
|
||||
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<%= render MainWithTabnavComponent.new(tabnav_partial: "shared/tabnav_contributions") do %>
|
||||
<section>
|
||||
<p class="mb-12">
|
||||
Your financial contributions to the development and upkeep of Kosmos
|
||||
@ -34,7 +34,9 @@
|
||||
</ul>
|
||||
<% else %>
|
||||
<p class="text-gray-500">
|
||||
No donations to show.
|
||||
The donation process is not automated yet. Please
|
||||
<a href="https://wiki.kosmos.org/Main_Page#Community_.2F_Getting_in_touch_.2F_Getting_involved" class="ks-text-link" target="_blank">contact us</a>
|
||||
if you'd like to contribute this way right now.
|
||||
</p>
|
||||
<% end %>
|
||||
</section>
|
16
app/views/contributions/projects/index.html.erb
Normal file
16
app/views/contributions/projects/index.html.erb
Normal file
@ -0,0 +1,16 @@
|
||||
<%= render HeaderComponent.new(title: "Contributions") %>
|
||||
|
||||
<%= render MainWithTabnavComponent.new(tabnav_partial: "shared/tabnav_contributions") do %>
|
||||
<section>
|
||||
<p class="mb-8">
|
||||
Project contributions are how we develop and run all Kosmos software and
|
||||
services. Everything we create and provide is free and open-source
|
||||
software, even the page you're looking at right now!
|
||||
</p>
|
||||
<p>
|
||||
Soon you will find a summary of your contributions here. Until then,
|
||||
please refer to the
|
||||
<a href="https://kredits.kosmos.org/" class="ks-text-link" target="_blank">Kredits dashboard</a>.
|
||||
</p>
|
||||
</section>
|
||||
<% end %>
|
@ -2,7 +2,7 @@
|
||||
<div id="error_explanation">
|
||||
<ul>
|
||||
<% resource.errors.full_messages.each do |message| %>
|
||||
<li><%= message %></li>
|
||||
<li class="text-red-600"><%= message %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -1 +0,0 @@
|
||||
json.array! @donations, partial: "donations/donation", as: :donation
|
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-user"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-user <%= custom_class %>"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
|
||||
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 334 B |
@ -22,8 +22,8 @@
|
||||
</table>
|
||||
<% else %>
|
||||
<p>
|
||||
You do not have any invitations to give away yet. All good
|
||||
things come in time.
|
||||
You do not have any invitations to give away yet. We will notify you,
|
||||
as soon as you can invite others.
|
||||
</p>
|
||||
<% end %>
|
||||
</section>
|
||||
|
@ -1 +0,0 @@
|
||||
<h2>Settings</h2>
|
9
app/views/settings/profile/index.html.erb
Normal file
9
app/views/settings/profile/index.html.erb
Normal file
@ -0,0 +1,9 @@
|
||||
<%= render HeaderComponent.new(title: "Settings") %>
|
||||
|
||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/sidenav_settings') do %>
|
||||
<section>
|
||||
<h3>Profile</h3>
|
||||
<p class="mb-12">
|
||||
</p>
|
||||
</section>
|
||||
<% end %>
|
@ -3,7 +3,7 @@
|
||||
<%= render MainWithSidenavComponent.new(sidenav_partial: 'shared/sidenav_settings') do %>
|
||||
<section>
|
||||
<h3>Password</h3>
|
||||
<p class="mb-12">Use the following button to request an email with a password reset link:</p>
|
||||
<p class="mb-8">Use the following button to request an email with a password reset link:</p>
|
||||
<p>
|
||||
<%= form_with(url: settings_reset_password_path, method: :post) do %>
|
||||
<%= submit_tag("Send me a password reset link", class: 'btn-md btn-gray w-full sm:w-auto') %>
|
@ -1,10 +1,10 @@
|
||||
<%= link_to "Services", root_path,
|
||||
class: main_nav_class(@current_section, :dashboard) %>
|
||||
<%= link_to "Contributions", contributions_donations_path,
|
||||
class: main_nav_class(@current_section, :contributions) %>
|
||||
<%= link_to "Invitations", invitations_path,
|
||||
class: main_nav_class(@current_section, :invitations) %>
|
||||
<%= link_to "Donations", donations_path,
|
||||
class: main_nav_class(@current_section, :contributions) %>
|
||||
<%= link_to "Wallet", wallet_path,
|
||||
class: main_nav_class(@current_section, :wallet) %>
|
||||
<%= link_to "Settings", security_path,
|
||||
class: main_nav_class(@current_section, :security) %>
|
||||
<%= link_to "Settings", settings_profile_path,
|
||||
class: main_nav_class(@current_section, :settings) %>
|
||||
|
@ -1,9 +1,10 @@
|
||||
<%= render SidenavLinkComponent.new(
|
||||
name: "Account", path: "#", icon: "settings", disabled: true
|
||||
name: "Profile", path: settings_profile_path, icon: "user",
|
||||
active: current_page?(settings_profile_path)
|
||||
) %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
name: "Password", path: security_path, icon: "key",
|
||||
active: current_page?(security_path)
|
||||
name: "Security", path: settings_security_path, icon: "key",
|
||||
active: current_page?(settings_security_path)
|
||||
) %>
|
||||
<%= render SidenavLinkComponent.new(
|
||||
name: "Security", path: "#", icon: "shield", disabled: true
|
||||
|
12
app/views/shared/_tabnav_contributions.html.erb
Normal file
12
app/views/shared/_tabnav_contributions.html.erb
Normal file
@ -0,0 +1,12 @@
|
||||
<div class="border-b border-gray-200">
|
||||
<nav class="-mb-px flex" aria-label="Tabs">
|
||||
<%= render TabnavLinkComponent.new(
|
||||
name: "Donations", path: contributions_donations_path,
|
||||
active: current_page?(contributions_donations_path)
|
||||
) %>
|
||||
<%= render TabnavLinkComponent.new(
|
||||
name: "Projects", path: contributions_projects_path,
|
||||
active: current_page?(contributions_projects_path)
|
||||
) %>
|
||||
</nav>
|
||||
</div>
|
14
app/views/shared/_tabnav_wallet.html.erb
Normal file
14
app/views/shared/_tabnav_wallet.html.erb
Normal file
@ -0,0 +1,14 @@
|
||||
<section>
|
||||
<div class="border-b border-gray-200">
|
||||
<nav class="-mb-px flex" aria-label="Tabs">
|
||||
<%= render TabnavLinkComponent.new(
|
||||
name: "Info", path: wallet_path,
|
||||
active: current_page?(wallet_path)
|
||||
) %>
|
||||
<%= render TabnavLinkComponent.new(
|
||||
name: "Transactions", path: wallet_transactions_path,
|
||||
active: current_page?(wallet_transactions_path)
|
||||
) %>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
@ -3,14 +3,7 @@
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<%= render WalletSummaryComponent.new(balance: @balance) %>
|
||||
|
||||
<section>
|
||||
<div class="border-b border-gray-200">
|
||||
<nav class="-mb-px flex" aria-label="Tabs">
|
||||
<%= link_to "Info", wallet_path, class: "border-indigo-500 text-indigo-600 w-1/2 py-4 px-1 text-center border-b-2", "aria-current": "page" %>
|
||||
<%= link_to "Transactions", wallet_transactions_path, class: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2" %>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
<%= render partial: "shared/tabnav_wallet" %>
|
||||
|
||||
<section>
|
||||
<h3>Lightning Address</h3>
|
||||
|
@ -3,14 +3,7 @@
|
||||
<%= render MainSimpleComponent.new do %>
|
||||
<%= render WalletSummaryComponent.new(balance: @balance) %>
|
||||
|
||||
<section>
|
||||
<div class="border-b border-gray-200">
|
||||
<nav class="-mb-px flex" aria-label="Tabs">
|
||||
<%= link_to "Info", wallet_path, class: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 w-1/2 py-4 px-1 text-center border-b-2" %>
|
||||
<%= link_to "Transactions", wallet_transactions_path, class: "border-indigo-500 text-indigo-600 w-1/2 py-4 px-1 text-center border-b-2", "aria-current": "page" %>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
<%= render partial: "shared/tabnav_wallet" %>
|
||||
|
||||
<section>
|
||||
<h3 class="hidden">Transactions</h3>
|
||||
|
@ -3,6 +3,21 @@ require 'digest'
|
||||
require 'securerandom'
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Create custom failure for turbo
|
||||
class TurboFailureApp < Devise::FailureApp
|
||||
def respond
|
||||
if request_format == :turbo_stream
|
||||
redirect
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def skip_format?
|
||||
%w(html turbo_stream */*).include? request_format.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Assuming you have not yet modified this file, each configuration option below
|
||||
# is set to its default value. Note that some are commented out while others
|
||||
# are not: uncommented lines are intended to protect your configuration from
|
||||
@ -44,6 +59,7 @@ Devise.setup do |config|
|
||||
# ==> Controller configuration
|
||||
# Configure the parent class to the devise controllers.
|
||||
# config.parent_controller = 'DeviseController'
|
||||
config.parent_controller = 'TurboController'
|
||||
|
||||
# ==> Mailer Configuration
|
||||
# Configure the e-mail address which will be shown in Devise::Mailer,
|
||||
@ -289,6 +305,7 @@ Devise.setup do |config|
|
||||
#
|
||||
# The "*/*" below is required to match Internet Explorer requests.
|
||||
# config.navigational_formats = ['*/*', :html]
|
||||
config.navigational_formats = ['*/*', :html, :turbo_stream]
|
||||
|
||||
# The default HTTP method used to sign out a resource. Default is :delete.
|
||||
config.sign_out_via = :get
|
||||
@ -302,10 +319,11 @@ Devise.setup do |config|
|
||||
# If you want to use other strategies, that are not supported by Devise, or
|
||||
# change the failure app, you can configure them inside the config.warden block.
|
||||
#
|
||||
# config.warden do |manager|
|
||||
config.warden do |manager|
|
||||
manager.failure_app = TurboFailureApp
|
||||
# manager.intercept_401 = false
|
||||
# manager.default_strategies(scope: :user).unshift :some_external_strategy
|
||||
# end
|
||||
end
|
||||
|
||||
# ==> Mountable engine configurations
|
||||
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
|
||||
|
@ -10,15 +10,20 @@ Rails.application.routes.draw do
|
||||
match 'signup/:step', to: 'signup#steps', as: :signup_steps, via: [:get, :post]
|
||||
post 'signup_validate', to: 'signup#validate'
|
||||
|
||||
get 'settings', to: 'settings#index'
|
||||
post 'settings_reset_password', to: 'settings#reset_password'
|
||||
|
||||
namespace :settings do
|
||||
get 'profile', to: 'profile#index'
|
||||
get 'security', to: 'security#index'
|
||||
post 'reset_password', to: 'security#reset_password'
|
||||
end
|
||||
|
||||
namespace :contributions do
|
||||
root to: 'donations#index'
|
||||
get 'projects', to: 'projects#index'
|
||||
resources :donations, only: ['index']
|
||||
end
|
||||
|
||||
resources :invitations, only: ['index', 'show', 'create', 'destroy']
|
||||
|
||||
resources :donations
|
||||
|
||||
get 'wallet', to: 'wallet#index'
|
||||
get 'wallet/transactions', to: 'wallet#transactions'
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user