Skip to content

Commit

Permalink
Merge pull request #63 from goinvo/fermion/add-user-management
Browse files Browse the repository at this point in the history
Adds user management
  • Loading branch information
fermion authored Jan 29, 2024
2 parents 39f5918 + 3d8c42b commit 85d81a9
Show file tree
Hide file tree
Showing 80 changed files with 4,093 additions and 1,327 deletions.
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ gem "tailwindcss-rails"

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: %i[ windows jruby ]

gem 'paper_trail'
gem "passwordless", "~> 1.1"
gem "graphql", "~> 2.2"
gem "graphiql-rails"
Expand Down Expand Up @@ -56,6 +56,7 @@ group :development do
# gem "rack-mini-profiler"

gem "letter_opener"
gem "foreman"
end

group :test do
Expand Down
8 changes: 8 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ GEM
railties (>= 5.0.0)
faker (3.2.2)
i18n (>= 1.8.11, < 2)
foreman (0.87.2)
globalid (1.2.1)
activesupport (>= 6.1)
graphiql-rails (1.9.0)
Expand Down Expand Up @@ -175,6 +176,9 @@ GEM
racc (~> 1.4)
nokogiri (1.16.0-x86_64-linux)
racc (~> 1.4)
paper_trail (15.1.0)
activerecord (>= 6.1)
request_store (~> 1.4)
passwordless (1.2.0)
bcrypt (>= 3.1.11)
rails (>= 5.1.4)
Expand Down Expand Up @@ -232,6 +236,8 @@ GEM
regexp_parser (2.8.3)
reline (0.4.1)
io-console (~> 0.5)
request_store (1.5.1)
rack (>= 1.4)
rexml (3.2.6)
rspec-core (3.12.2)
rspec-support (~> 3.12.0)
Expand Down Expand Up @@ -327,11 +333,13 @@ DEPENDENCIES
dotenv-rails
factory_bot_rails
faker
foreman
graphiql-rails
graphql (~> 2.2)
importmap-rails
letter_opener
money
paper_trail
passwordless (~> 1.1)
pg (~> 1.1)
puma (>= 5.0)
Expand Down
33 changes: 27 additions & 6 deletions app/commands/add_user_to_company.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@

class AddUserToCompany

attr_accessor :user
attr_accessor :user, :membership
attr_reader :errors

def initialize(email:, name:, role: "member", company:)
class InvalidArguments < StandardError; end

def initialize(email:, name:, role: "member", company:, creating_new_company: false)
@email = email
@name = name
@role = role
@creating_new_company = creating_new_company
@company = company
end

def call
find_or_create_user
add_user_to_company
User.transaction do
find_or_create_user
add_user_to_company

unless @creating_new_company
send_welcome_email
update_stripe_subscription_count
end
end

@user
end
Expand All @@ -25,10 +36,20 @@ def find_or_create_user
user.name = @name
user.current_company = @company
end

@user.save!
end

def add_user_to_company
@company.memberships.build(user: @user, role: @role, status: 'active')
@company.save!
@membership = @company.memberships.build(user: @user, role: @role, status: 'active')
@membership.save!
end

def send_welcome_email
CompanyMailer.welcome(@company, @user).deliver_later
end

def update_stripe_subscription_count
SyncCustomerSubscriptionCountJob.perform_async(@company.id)
end
end
22 changes: 14 additions & 8 deletions app/commands/create_new_company.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
class CreateNewCompany

attr_reader :registration, :company, :user
attr_reader :email, :user, :registration_id, :company, :user

def initialize(registration)
@registration = registration
def initialize(email:, name:, registration_id:)
@email = email
@name = name
@registration_id = registration_id
end

def call
Expand All @@ -18,15 +20,16 @@ def call
private

def create_company_record
@company = Company.create(name: "#{@registration.name}'s' Company")
@company = Company.create(name: "#{@name}'s' Company")
end

def add_initial_owner
@user = AddUserToCompany.new(
email: @registration.email,
name: @registration.name,
email: @email,
name: @name,
company: @company,
role: "owner"
role: "owner",
creating_new_company: true
).call
end

Expand All @@ -35,6 +38,9 @@ def enqueue_create_stripe_customer_job
end

def claim_registration
@registration.update(user: @user, registered_at: Time.current)
Registration.find_by!(id: @registration_id).update!(
user: @user,
registered_at: Time.current
)
end
end
21 changes: 21 additions & 0 deletions app/components/settings/breadcrumb_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<li>
<div class="flex items-center">
<% if render_slash? %>
<svg class="h-5 w-5 flex-shrink-0 text-gray-300" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M5.555 17.776l8-16 .894.448-8 16-.894-.448z" />
</svg>
<% end %>
<% if @link %>
<a href="<%= @link %>" class="ml-2 text-sm font-medium text-gray-500 hover:text-gray-700">
<% if @first && svg? %>
<%= svg %>
<% end %>
<%= @title %>
</a>
<% else %>
<span class="ml-2 text-sm font-medium text-gray-500 hover:text-gray-700">
<%= @title %>
</span>
<% end %>
</div>
</li>
17 changes: 17 additions & 0 deletions app/components/settings/breadcrumb_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Settings
class BreadcrumbComponent < ViewComponent::Base

renders_one :svg

def initialize(title:, link: nil, first: false, last: false)
@title = title
@link = link
@first = first
@last = last
end

def render_slash?
!@first
end
end
end
7 changes: 7 additions & 0 deletions app/components/settings/breadcrumbs_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<nav class="flex py-4" aria-label="Breadcrumb">
<ol role="list" class="flex items-center space-x-4">
<% breadcrumbs.each do |breadcrumb| %>
<%= breadcrumb %>
<% end %>
</ol>
</nav>
9 changes: 9 additions & 0 deletions app/components/settings/breadcrumbs_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Settings
class BreadcrumbsComponent < ViewComponent::Base

renders_many :breadcrumbs, Settings::BreadcrumbComponent

def initialize()
end
end
end
8 changes: 8 additions & 0 deletions app/components/settings/section_heading_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div class="border-b border-gray-200 sm:flex sm:items-center sm:justify-between">

<%= breadcrumbs %>

<div class="mt-3 sm:ml-4 sm:mt-0">
<%= action_buttons %>
</div>
</div>
33 changes: 33 additions & 0 deletions app/components/settings/section_heading_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module Settings
class SectionHeadingComponent < ViewComponent::Base

LINK_CSS_CLASS = "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 whitespace-nowrap border-b-2 px-1 pb-4 text-sm font-medium"
SELECTED_LINK_CSS_CLASS = "border-indigo-500 text-indigo-600 whitespace-nowrap border-b-2 px-1 pb-4 text-sm font-medium"

renders_one :action_buttons
renders_one :breadcrumbs, Settings::BreadcrumbsComponent

def initialize(user:)
@user = user
end

def settings_link(text, path)
css_class = current_or_starts_with?(path) ? SELECTED_LINK_CSS_CLASS : LINK_CSS_CLASS

options = {}
options[:"aria-current"] = "page" if current_or_starts_with?(path)

link_to text, path, class: css_class, **options
end

def settings_option(text, path)
content_tag(:option, text, value: path, selected: current_or_starts_with?(path))
end

private

def current_or_starts_with?(path)
current_page?(path)
end
end
end
37 changes: 25 additions & 12 deletions app/components/settings/tabs_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
<div>
<div class="sm:hidden" data-controller="settings-tabs">
<label for="tabs" class="sr-only">Select a tab</label>
<!-- Use an "onChange" listener to redirect the user to the selected tab URL. -->
<select data-action="change->settings-tabs#handleChange" name="tabs" class="block w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm">
<%= settings_option "General", settings_path %>
<%= settings_option "Billing", settings_billing_information_path %>
</select>
<div class="relative border-b border-gray-200 pb-5" data-controller="settings-tabs">
<div class="md:flex md:items-center md:justify-between">
<% if header? %>
<h3 class="text-base font-semibold leading-6 text-gray-900">
<%= header %>
</h3>
<% end %>
<div class="mt-3 flex md:absolute md:right-0 md:top-3 md:mt-0">
<%= action_buttons %>
</div>
</div>
<div class="hidden sm:block">
<div class="border-b border-gray-200">
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
<div class="mt-4">
<!-- Dropdown menu on small screens -->
<div class="sm:hidden">
<label for="current-tab" class="sr-only">Select a tab</label>
<select data-action="change->settings-tabs#handleChange" id="current-tab" name="current-tab" class="block w-full rounded-md border-0 py-1.5 pl-3 pr-10 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600">
<%= settings_option "General", settings_path %>
<%= settings_option "User Management", settings_users_path %>
<%= settings_option "Billing", settings_billing_information_path %>
</select>
</div>
<!-- Tabs at small breakpoint and up -->
<div class="hidden sm:block">
<nav class="-mb-px flex space-x-8">
<%= settings_link "General", settings_path %>
<%= settings_link "User Management", settings_users_path %>
<%= settings_link "Billing", settings_billing_information_path %>
</nav>
</div>
</div>
</div>
</div>
19 changes: 14 additions & 5 deletions app/components/settings/tabs_component.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
module Settings
class TabsComponent < ViewComponent::Base

LINK_CSS_CLASS = "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 whitespace-nowrap border-b-2 py-4 px-1 text-sm font-medium"
SELECTED_LINK_CSS_CLASS = "border-indigo-500 text-indigo-600 whitespace-nowrap border-b-2 py-4 px-1 text-sm font-medium"
LINK_CSS_CLASS = "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 whitespace-nowrap border-b-2 px-1 pb-4 text-sm font-medium"
SELECTED_LINK_CSS_CLASS = "border-indigo-500 text-indigo-600 whitespace-nowrap border-b-2 px-1 pb-4 text-sm font-medium"

renders_one :action_buttons
renders_one :header

def initialize

end

def settings_link(text, path)
css_class = current_page?(path) ? SELECTED_LINK_CSS_CLASS : LINK_CSS_CLASS
css_class = current_or_starts_with?(path) ? SELECTED_LINK_CSS_CLASS : LINK_CSS_CLASS

options = {}
options[:"aria-current"] = "page" if current_page?(path)
options[:"aria-current"] = "page" if current_or_starts_with?(path)

link_to text, path, class: css_class, **options
end

def settings_option(text, path)
content_tag(:option, text, value: path, selected: current_page?(path))
content_tag(:option, text, value: path, selected: current_or_starts_with?(path))
end

private

def current_or_starts_with?(path)
current_page?(path)
end
end
end
31 changes: 31 additions & 0 deletions app/components/settings/users/list_item_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<li class="relative flex justify-between gap-x-6 py-5">
<div class="flex min-w-0 gap-x-4">
<%= helpers.user_gravatar(user: @user, css_classes: "h-12 w-12 flex-none rounded-full bg-gray-50") %>
<div class="min-w-0 flex-auto">
<p class="text-sm font-semibold leading-6 text-gray-900">
<%= link_to settings_user_path(@user) do %>
<span class="absolute inset-x-0 -top-px bottom-0"></span>
<%= user_name %>
<% end %>
</p>
<p class="mt-1 flex text-xs leading-5 text-gray-500">
<a href="mailto:<%= user_email %>" class="relative truncate hover:underline">
<%= user_email %>
</a>
</p>
</div>
</div>
<div class="flex shrink-0 items-center gap-x-4">
<div class="hidden sm:flex sm:flex-col sm:items-end">
<% if user_job_title %>
<p class="text-sm leading-6 text-gray-900"><%= user_job_title %></p>
<% end %>
<div class="mt-1 flex items-center gap-x-1.5">
<%= render(Settings::Users::StatusComponent.new(status: user_status)) %>
</div>
</div>
<svg class="h-5 w-5 flex-none text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
</svg>
</div>
</li>
Loading

0 comments on commit 85d81a9

Please sign in to comment.